mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-24 20:36:40 +07:00
video node editor
This commit is contained in:
parent
12e366fce9
commit
8612cc5ce7
10 changed files with 105 additions and 6 deletions
|
@ -18,6 +18,7 @@ type IProps = typeof mapDispatchToProps & {};
|
|||
const SubmitBarUnconnected: FC<IProps> = ({ nodeCreate }) => {
|
||||
const onOpenImageEditor = useCallback(() => nodeCreate(NODE_TYPES.IMAGE), [nodeCreate]);
|
||||
const onOpenTextEditor = useCallback(() => nodeCreate(NODE_TYPES.TEXT), [nodeCreate]);
|
||||
const onOpenVideoEditor = useCallback(() => nodeCreate(NODE_TYPES.VIDEO), [nodeCreate]);
|
||||
|
||||
return (
|
||||
<div className={styles.wrap}>
|
||||
|
@ -27,7 +28,11 @@ const SubmitBarUnconnected: FC<IProps> = ({ nodeCreate }) => {
|
|||
</div>
|
||||
|
||||
<div onClick={onOpenTextEditor}>
|
||||
<Icon icon="image" />
|
||||
<Icon icon="text" />
|
||||
</div>
|
||||
|
||||
<div onClick={onOpenVideoEditor}>
|
||||
<Icon icon="video" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
40
src/components/editors/VideoEditor/index.tsx
Normal file
40
src/components/editors/VideoEditor/index.tsx
Normal file
|
@ -0,0 +1,40 @@
|
|||
import React, { FC, useCallback, useMemo } from 'react';
|
||||
import { INode } from '~/redux/types';
|
||||
import * as styles from './styles.scss';
|
||||
import path from 'ramda/es/path';
|
||||
import { InputText } from '~/components/input/InputText';
|
||||
|
||||
interface IProps {
|
||||
data: INode;
|
||||
setData: (val: INode) => void;
|
||||
}
|
||||
|
||||
const VideoEditor: FC<IProps> = ({ data, setData }) => {
|
||||
const setUrl = useCallback(
|
||||
(url: string) => setData({ ...data, blocks: [{ type: 'video', url }] }),
|
||||
[data, setData]
|
||||
);
|
||||
|
||||
const url = (path(['blocks', 0, 'url'], data) as string) || '';
|
||||
const preview = useMemo(() => {
|
||||
const match =
|
||||
url &&
|
||||
url.match(
|
||||
/http(?:s?):\/\/(?:www\.)?youtu(?:be\.com\/watch\?v=|\.be\/)([\w\-\_]*)(&(amp;)?[\w\?=]*)?/
|
||||
);
|
||||
|
||||
return match && match[1] ? `http://img.youtube.com/vi/${match[1]}/maxresdefault.jpg` : null;
|
||||
}, [url]);
|
||||
|
||||
return (
|
||||
<div className={styles.preview} style={{ backgroundImage: preview && `url("${preview}")` }}>
|
||||
<div className={styles.input_wrap}>
|
||||
<div className={styles.input}>
|
||||
<InputText value={url} handler={setUrl} placeholder="Адрес видео" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { VideoEditor };
|
30
src/components/editors/VideoEditor/styles.scss
Normal file
30
src/components/editors/VideoEditor/styles.scss
Normal file
|
@ -0,0 +1,30 @@
|
|||
.preview {
|
||||
padding-top: 56.25%;
|
||||
position: relative;
|
||||
// background: darken($color: $content_bg, $amount: 2%);
|
||||
}
|
||||
|
||||
.input_wrap {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.input {
|
||||
// @include outer_shadow();
|
||||
|
||||
flex: 1 0 50%;
|
||||
padding: $gap * 2 $gap $gap * 2 $gap;
|
||||
// border-radius: $radius;
|
||||
background: $content_bg;
|
||||
// margin: 20px;
|
||||
|
||||
input {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
10
src/containers/editors/EditorDialogVideo/index.tsx
Normal file
10
src/containers/editors/EditorDialogVideo/index.tsx
Normal file
|
@ -0,0 +1,10 @@
|
|||
import React, { FC } from 'react';
|
||||
import { EditorDialog } from '~/containers/dialogs/EditorDialog';
|
||||
import { IDialogProps } from '~/redux/types';
|
||||
import { NODE_TYPES } from '~/redux/node/constants';
|
||||
|
||||
type IProps = IDialogProps & {};
|
||||
|
||||
const EditorDialogVideo: FC<IProps> = props => <EditorDialog type={NODE_TYPES.VIDEO} {...props} />;
|
||||
|
||||
export { EditorDialogVideo };
|
0
src/containers/editors/EditorDialogVideo/styles.scss
Normal file
0
src/containers/editors/EditorDialogVideo/styles.scss
Normal file
|
@ -13,13 +13,11 @@ function* onGetFlow() {
|
|||
}: IResultWithStatus<{ nodes: INode[] }> = yield call(getNodes, {});
|
||||
|
||||
if (!nodes || !nodes.length) {
|
||||
// todo: set error empty response
|
||||
yield put(flowSetNodes([]));
|
||||
return;
|
||||
}
|
||||
|
||||
// todo: write nodes
|
||||
yield put(flowSetNodes(nodes));
|
||||
|
||||
// console.log('flow', { nodes, error });
|
||||
}
|
||||
|
||||
export default function* nodeSaga() {
|
||||
|
|
|
@ -2,6 +2,7 @@ import { ValueOf } from '~/redux/types';
|
|||
import { LoginDialog } from '~/containers/dialogs/LoginDialog';
|
||||
import { EditorDialogImage } from '~/containers/editors/EditorDialogImage';
|
||||
import { EditorDialogText } from '~/containers/editors/EditorDialogText';
|
||||
import { EditorDialogVideo } from '~/containers/editors/EditorDialogVideo';
|
||||
import { NODE_TYPES } from '../node/constants';
|
||||
|
||||
export const MODAL_ACTIONS = {
|
||||
|
@ -13,18 +14,21 @@ export const MODAL_ACTIONS = {
|
|||
export const DIALOGS = {
|
||||
EDITOR_IMAGE: 'EDITOR_IMAGE',
|
||||
EDITOR_TEXT: 'EDITOR_TEXT',
|
||||
EDITOR_VIDEO: 'EDITOR_VIDEO',
|
||||
LOGIN: 'LOGIN',
|
||||
};
|
||||
|
||||
export const DIALOG_CONTENT = {
|
||||
[DIALOGS.EDITOR_IMAGE]: EditorDialogImage,
|
||||
[DIALOGS.EDITOR_TEXT]: EditorDialogText,
|
||||
[DIALOGS.EDITOR_VIDEO]: EditorDialogVideo,
|
||||
[DIALOGS.LOGIN]: LoginDialog,
|
||||
};
|
||||
|
||||
export const NODE_EDITOR_DIALOGS = {
|
||||
[NODE_TYPES.IMAGE]: DIALOGS.EDITOR_IMAGE,
|
||||
[NODE_TYPES.TEXT]: DIALOGS.EDITOR_TEXT,
|
||||
[NODE_TYPES.VIDEO]: DIALOGS.EDITOR_VIDEO,
|
||||
};
|
||||
|
||||
export interface IDialogProps {
|
||||
|
|
|
@ -4,6 +4,7 @@ import { NodeImageSlideBlock } from '~/components/node/NodeImageSlideBlock';
|
|||
import { NodeTextBlock } from '~/components/node/NodeTextBlock';
|
||||
import { ImageEditor } from '~/components/editors/ImageEditor';
|
||||
import { TextEditor } from '~/components/editors/TextEditor';
|
||||
import { VideoEditor } from '~/components/editors/VideoEditor';
|
||||
|
||||
const prefix = 'NODE.';
|
||||
export const NODE_ACTIONS = {
|
||||
|
@ -82,6 +83,7 @@ export const EMPTY_COMMENT: IComment = {
|
|||
export const NODE_EDITORS = {
|
||||
[NODE_TYPES.IMAGE]: ImageEditor,
|
||||
[NODE_TYPES.TEXT]: TextEditor,
|
||||
[NODE_TYPES.VIDEO]: VideoEditor,
|
||||
};
|
||||
|
||||
export const NODE_EDITOR_DATA: Record<
|
||||
|
|
|
@ -100,7 +100,7 @@ export interface IBlockText {
|
|||
}
|
||||
|
||||
export interface IBlockEmbed {
|
||||
type: 'embed';
|
||||
type: 'video';
|
||||
url: string;
|
||||
}
|
||||
|
||||
|
|
|
@ -98,6 +98,16 @@ const Sprites: FC<{}> = () => (
|
|||
<path fill="none" d="M0 0h24v24H0V0z" />
|
||||
<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" />
|
||||
</g>
|
||||
|
||||
<g id="text" stroke="none">
|
||||
<path fill="none" d="M0 0h24v24H0V0z" />
|
||||
<path d="M14 17H4v2h10v-2zm6-8H4v2h16V9zM4 15h16v-2H4v2zM4 5v2h16V5H4z" />
|
||||
</g>
|
||||
|
||||
<g id="video" stroke="none">
|
||||
<path fill="none" d="M0 0h24v24H0V0z" />
|
||||
<path d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z" />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue