refactor, added slate, added editor, added book

This commit is contained in:
lnn0q 2025-05-28 23:25:46 +03:00
parent bc250e73bb
commit 029b7e9353
33 changed files with 714 additions and 496 deletions

View file

@ -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;
};
}