refactor, added slate, added editor, added book
This commit is contained in:
parent
bc250e73bb
commit
029b7e9353
33 changed files with 714 additions and 496 deletions
|
|
@ -1,6 +1,6 @@
|
|||
import { Button, Card, Stack, Text, Wrap } from '@chakra-ui/react';
|
||||
import isHotkey from 'is-hotkey';
|
||||
import React, { KeyboardEvent, MouseEvent, useCallback, useMemo, useState } from 'react';
|
||||
import React, { KeyboardEvent, MouseEvent, useCallback, useMemo } from 'react';
|
||||
import { createEditor, Descendant, Editor, Element as SlateElement, Transforms } from 'slate';
|
||||
import { withHistory } from 'slate-history';
|
||||
import {
|
||||
|
|
@ -44,7 +44,7 @@ export function EditorSlate() {
|
|||
}
|
||||
}}
|
||||
>
|
||||
<RichTextExample />
|
||||
<EditorViewport />
|
||||
</Card.Body>
|
||||
</Card.Root>
|
||||
</>
|
||||
|
|
@ -65,7 +65,7 @@ type AlignType = (typeof TEXT_ALIGN_TYPES)[number];
|
|||
type ListType = (typeof LIST_TYPES)[number];
|
||||
type CustomElementFormat = CustomElementType | AlignType | ListType;
|
||||
|
||||
const RichTextExample = () => {
|
||||
function EditorViewport() {
|
||||
const renderElement = useCallback((props: RenderElementProps) => <Element {...props} />, []);
|
||||
const renderLeaf = useCallback((props: RenderLeafProps) => <Leaf {...props} />, []);
|
||||
const editor = useMemo(() => withHistory(withReact(createEditor())), []);
|
||||
|
|
@ -123,12 +123,17 @@ const RichTextExample = () => {
|
|||
<BlockButton format="right" icon="nf nf-fa-align_right" />
|
||||
<BlockButton format="justify" icon="nf nf-fa-align_justify" />
|
||||
</Wrap>
|
||||
<Stack width="100%" display="flex" justifyContent="center" alignItems="center">
|
||||
<Stack
|
||||
width="100%"
|
||||
maxWidth="100%"
|
||||
paddingX={3}
|
||||
display="flex"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
<Prose
|
||||
width="100%"
|
||||
height="100%"
|
||||
maxWidth="unset"
|
||||
maxHeight="unset"
|
||||
minWidth="100%"
|
||||
display="flex"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
|
|
@ -137,7 +142,11 @@ const RichTextExample = () => {
|
|||
className="slate-editor"
|
||||
style={{
|
||||
outline: 'none',
|
||||
minWidth: '100%'
|
||||
minWidth: '100%',
|
||||
maxWidth: 'unset',
|
||||
maxHeight: '75vh',
|
||||
padding: 0,
|
||||
overflowY: 'auto'
|
||||
}}
|
||||
renderElement={renderElement}
|
||||
renderLeaf={renderLeaf}
|
||||
|
|
@ -158,9 +167,9 @@ const RichTextExample = () => {
|
|||
</Stack>
|
||||
</Slate>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const toggleBlock = (editor: CustomEditor, format: CustomElementFormat) => {
|
||||
function toggleBlock(editor: CustomEditor, format: CustomElementFormat) {
|
||||
const isActive = isBlockActive(editor, format, isAlignType(format) ? 'align' : 'type');
|
||||
const isList = isListType(format);
|
||||
|
||||
|
|
@ -188,9 +197,9 @@ const toggleBlock = (editor: CustomEditor, format: CustomElementFormat) => {
|
|||
const block = { type: format, children: [] };
|
||||
Transforms.wrapNodes(editor, block);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const toggleMark = (editor: CustomEditor, format: CustomTextKey) => {
|
||||
function toggleMark(editor: CustomEditor, format: CustomTextKey) {
|
||||
const isActive = isMarkActive(editor, format);
|
||||
|
||||
if (isActive) {
|
||||
|
|
@ -198,13 +207,13 @@ const toggleMark = (editor: CustomEditor, format: CustomTextKey) => {
|
|||
} else {
|
||||
Editor.addMark(editor, format, true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const isBlockActive = (
|
||||
function isBlockActive(
|
||||
editor: CustomEditor,
|
||||
format: CustomElementFormat,
|
||||
blockType: 'type' | 'align' = 'type'
|
||||
) => {
|
||||
) {
|
||||
const { selection } = editor;
|
||||
if (!selection) return false;
|
||||
|
||||
|
|
@ -223,15 +232,15 @@ const isBlockActive = (
|
|||
})
|
||||
);
|
||||
|
||||
return !!match;
|
||||
};
|
||||
return Boolean(match);
|
||||
}
|
||||
|
||||
const isMarkActive = (editor: CustomEditor, format: CustomTextKey) => {
|
||||
function isMarkActive(editor: CustomEditor, format: CustomTextKey) {
|
||||
const marks = Editor.marks(editor);
|
||||
return marks ? marks[format] === true : false;
|
||||
};
|
||||
}
|
||||
|
||||
const Element = ({ attributes, children, element }: RenderElementProps) => {
|
||||
function Element({ attributes, children, element }: RenderElementProps) {
|
||||
const style: React.CSSProperties = {};
|
||||
if (isAlignElement(element)) {
|
||||
style.textAlign = element.align as AlignType;
|
||||
|
|
@ -280,9 +289,9 @@ const Element = ({ attributes, children, element }: RenderElementProps) => {
|
|||
</p>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const Leaf = ({ attributes, children, leaf }: RenderLeafProps) => {
|
||||
function Leaf({ attributes, children, leaf }: RenderLeafProps) {
|
||||
if (leaf.bold) {
|
||||
children = <strong>{children}</strong>;
|
||||
}
|
||||
|
|
@ -300,7 +309,7 @@ const Leaf = ({ attributes, children, leaf }: RenderLeafProps) => {
|
|||
}
|
||||
|
||||
return <span {...attributes}>{children}</span>;
|
||||
};
|
||||
}
|
||||
|
||||
interface BlockButtonProps {
|
||||
format: CustomElementFormat;
|
||||
|
|
@ -308,7 +317,7 @@ interface BlockButtonProps {
|
|||
text?: string;
|
||||
}
|
||||
|
||||
const BlockButton = ({ format, icon, text }: BlockButtonProps) => {
|
||||
function BlockButton({ format, icon, text }: BlockButtonProps) {
|
||||
const editor = useSlate();
|
||||
return (
|
||||
<Button
|
||||
|
|
@ -325,7 +334,7 @@ const BlockButton = ({ format, icon, text }: BlockButtonProps) => {
|
|||
</Stack>
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
interface MarkButtonProps {
|
||||
format: CustomTextKey;
|
||||
|
|
@ -333,7 +342,7 @@ interface MarkButtonProps {
|
|||
text?: string;
|
||||
}
|
||||
|
||||
const MarkButton = ({ format, icon, text }: MarkButtonProps) => {
|
||||
function MarkButton({ format, icon, text }: MarkButtonProps) {
|
||||
const editor = useSlate();
|
||||
return (
|
||||
<Button
|
||||
|
|
@ -347,16 +356,16 @@ const MarkButton = ({ format, icon, text }: MarkButtonProps) => {
|
|||
{text}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const isAlignType = (format: CustomElementFormat): format is AlignType => {
|
||||
function isAlignType(format: CustomElementFormat): format is AlignType {
|
||||
return TEXT_ALIGN_TYPES.includes(format as AlignType);
|
||||
};
|
||||
}
|
||||
|
||||
const isListType = (format: CustomElementFormat): format is ListType => {
|
||||
function isListType(format: CustomElementFormat): format is ListType {
|
||||
return LIST_TYPES.includes(format as ListType);
|
||||
};
|
||||
}
|
||||
|
||||
const isAlignElement = (element: CustomElement): element is CustomElementWithAlign => {
|
||||
function isAlignElement(element: CustomElement): element is CustomElementWithAlign {
|
||||
return 'align' in element;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue