mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-24 20:36:40 +07:00
#57 added block-based content editor
This commit is contained in:
parent
d8c379de6a
commit
868d1f948a
9 changed files with 117 additions and 21 deletions
17
src/containers/editors/NewEditorBetweenBlocks/index.tsx
Normal file
17
src/containers/editors/NewEditorBetweenBlocks/index.tsx
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
import styles from './styles.module.scss';
|
||||||
|
import { Button } from '~/components/input/Button';
|
||||||
|
|
||||||
|
interface IProps {}
|
||||||
|
|
||||||
|
const NewEditorBetweenBlocks: FC<IProps> = () => {
|
||||||
|
return (
|
||||||
|
<div className={styles.buttons}>
|
||||||
|
<div className={styles.buttons_content}>
|
||||||
|
<Button iconLeft="plus" round />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { NewEditorBetweenBlocks };
|
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
height: 0;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&_content {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -10px;
|
||||||
|
right: 0;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
}
|
42
src/containers/editors/NewEditorBlock/index.tsx
Normal file
42
src/containers/editors/NewEditorBlock/index.tsx
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import React, { createElement, FC, Fragment, useCallback } from 'react';
|
||||||
|
import { IBlock } from '~/redux/types';
|
||||||
|
import { prop } from 'ramda';
|
||||||
|
import { EDITOR_BLOCKS } from '~/redux/node/constants';
|
||||||
|
import { useNodeFormContext } from '~/utils/hooks/useNodeFormFormik';
|
||||||
|
import { NewEditorBetweenBlocks } from '~/containers/editors/NewEditorBetweenBlocks';
|
||||||
|
import styles from './styles.module.scss';
|
||||||
|
import { Group } from '~/components/containers/Group';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
block: IBlock;
|
||||||
|
index: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NewEditorBlock: FC<IProps> = ({ block, index }) => {
|
||||||
|
const { values, setFieldValue } = useNodeFormContext();
|
||||||
|
|
||||||
|
const handler = useCallback(
|
||||||
|
(val: IBlock) =>
|
||||||
|
setFieldValue(
|
||||||
|
'blocks',
|
||||||
|
values.blocks.map((el, i) => (i === index ? val : el))
|
||||||
|
),
|
||||||
|
[setFieldValue, values.blocks]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!prop(block.type, EDITOR_BLOCKS)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.block}>
|
||||||
|
{prop(block.type, EDITOR_BLOCKS)
|
||||||
|
? createElement(prop(block.type, EDITOR_BLOCKS), { block, handler })
|
||||||
|
: null}
|
||||||
|
|
||||||
|
<NewEditorBetweenBlocks />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { NewEditorBlock };
|
8
src/containers/editors/NewEditorBlock/styles.module.scss
Normal file
8
src/containers/editors/NewEditorBlock/styles.module.scss
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
@import "~/styles/variables.scss";
|
||||||
|
|
||||||
|
.block {
|
||||||
|
}
|
||||||
|
|
||||||
|
.pad {
|
||||||
|
margin-bottom: $gap;
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import React, { FC, useCallback } from 'react';
|
import React, { FC, useCallback } from 'react';
|
||||||
import { BlockType, IBlockComponentProps } from '~/redux/types';
|
import { BlockType, IBlockComponentProps } from '~/redux/types';
|
||||||
import { InputText } from '~/components/input/InputText';
|
import { InputText } from '~/components/input/InputText';
|
||||||
|
import styles from './styles.module.scss';
|
||||||
|
|
||||||
const NewEditorBlockText: FC<IBlockComponentProps> = ({ block, handler }) => {
|
const NewEditorBlockText: FC<IBlockComponentProps> = ({ block, handler }) => {
|
||||||
const onChange = useCallback((text: string) => handler({ type: BlockType.text, text }), [
|
const onChange = useCallback((text: string) => handler({ type: BlockType.text, text }), [
|
||||||
|
@ -8,7 +9,7 @@ const NewEditorBlockText: FC<IBlockComponentProps> = ({ block, handler }) => {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className={styles.wrap}>
|
||||||
<InputText handler={onChange} value={block.text} />
|
<InputText handler={onChange} value={block.text} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
@import "~/styles/variables.scss";
|
||||||
|
|
||||||
|
.wrap {
|
||||||
|
padding: $gap;
|
||||||
|
background-color: transparentize($content_bg, 0.9);
|
||||||
|
}
|
|
@ -1,32 +1,31 @@
|
||||||
import React, { createElement, FC, useCallback, useMemo } from 'react';
|
import React, { FC } from 'react';
|
||||||
import { useNodeFormContext } from '~/utils/hooks/useNodeFormFormik';
|
import { useNodeFormContext } from '~/utils/hooks/useNodeFormFormik';
|
||||||
import { NODE_EDITOR_BLOCKS } from '~/redux/node/constants';
|
|
||||||
import { has, prop } from 'ramda';
|
|
||||||
import styles from './styles.module.scss';
|
import styles from './styles.module.scss';
|
||||||
import { Group } from '~/components/containers/Group';
|
import { Group } from '~/components/containers/Group';
|
||||||
|
import { NewEditorBlock } from '~/containers/editors/NewEditorBlock';
|
||||||
|
import { SortableContainer, SortableElement } from 'react-sortable-hoc';
|
||||||
import { IBlock } from '~/redux/types';
|
import { IBlock } from '~/redux/types';
|
||||||
|
|
||||||
interface IProps {}
|
interface IProps {}
|
||||||
|
|
||||||
const NewEditorContent: FC<IProps> = () => {
|
const SortableItem = SortableElement(({ value, index }: { value: IBlock; index: number }) => (
|
||||||
const { values, setFieldValue } = useNodeFormContext();
|
<NewEditorBlock index={index} block={value} />
|
||||||
|
));
|
||||||
|
|
||||||
const onChange = useCallback(
|
const SortableList = SortableContainer(({ items }: { items: IBlock[] }) => (
|
||||||
(index: number) => (val: IBlock) =>
|
<div>
|
||||||
setFieldValue(
|
{items.map((block, i) => (
|
||||||
'blocks',
|
<SortableItem key={i} index={i} value={block} />
|
||||||
values.blocks.map((el, i) => (i === index ? val : el))
|
))}
|
||||||
),
|
</div>
|
||||||
[setFieldValue, values.blocks]
|
));
|
||||||
);
|
|
||||||
|
const NewEditorContent: FC<IProps> = () => {
|
||||||
|
const { values } = useNodeFormContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group className={styles.wrap}>
|
<Group className={styles.wrap}>
|
||||||
{values.blocks.map((block, i) =>
|
<SortableList items={values.blocks} />
|
||||||
prop(block.type, NODE_EDITOR_BLOCKS)
|
|
||||||
? createElement(prop(block.type, NODE_EDITOR_BLOCKS), { block, handler: onChange(i) })
|
|
||||||
: null
|
|
||||||
)}
|
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
@import "~/styles/variables.scss";
|
@import "~/styles/variables.scss";
|
||||||
|
|
||||||
.wrap {
|
.wrap {
|
||||||
padding: $gap;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,11 +134,23 @@ export interface NodeEditorBlockConfig {
|
||||||
initial?: boolean;
|
initial?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NODE_EDITOR_BLOCKS: Record<BlockType, FC<IBlockComponentProps>> = {
|
export const EDITOR_BLOCKS: Record<BlockType, FC<IBlockComponentProps>> = {
|
||||||
[BlockType.video]: NewEditorBlockVideo,
|
[BlockType.video]: NewEditorBlockVideo,
|
||||||
[BlockType.text]: NewEditorBlockText,
|
[BlockType.text]: NewEditorBlockText,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const NODE_BLOCKS: Record<
|
||||||
|
string,
|
||||||
|
Partial<Record<BlockType, { limit?: number; initial?: boolean }>>
|
||||||
|
> = {
|
||||||
|
[NODE_TYPES.TEXT]: {
|
||||||
|
[BlockType.text]: {
|
||||||
|
limit: 1,
|
||||||
|
initial: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export const NODE_PANEL_COMPONENTS: Record<string, FC<IEditorComponentProps>[]> = {
|
export const NODE_PANEL_COMPONENTS: Record<string, FC<IEditorComponentProps>[]> = {
|
||||||
[NODE_TYPES.TEXT]: [EditorFiller, EditorUploadCoverButton, EditorPublicSwitch],
|
[NODE_TYPES.TEXT]: [EditorFiller, EditorUploadCoverButton, EditorPublicSwitch],
|
||||||
[NODE_TYPES.VIDEO]: [EditorFiller, EditorUploadCoverButton, EditorPublicSwitch],
|
[NODE_TYPES.VIDEO]: [EditorFiller, EditorUploadCoverButton, EditorPublicSwitch],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue