diff --git a/package.json b/package.json index 69d568117..db75bdd3f 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ ], "private": true, "scripts": { - "start": "yarn lerna run start --scope @yoopta/editor --scope @yoopta/embed --scope @yoopta/action-menu-list --scope @yoopta/toolbar --scope @yoopta/paragraph --scope @yoopta/blockquote --scope @yoopta/headings --parallel --ignore development", + "start": "yarn lerna run start --scope @yoopta/editor --scope @yoopta/renderer --scope @yoopta/image --scope @yoopta/video --scope @yoopta/link --scope @yoopta/file --scope @yoopta/lists --scope @yoopta/paragraph --scope @yoopta/callout --scope @yoopta/blockquote --scope @yoopta/marks --scope @yoopta/headings --parallel --ignore development", "build": "yarn clean && yarn lerna run build --parallel --ignore development", "clean": "find ./packages -type d -name dist ! -path './packages/development/*' -exec rm -rf {} +", "serve": "cd ./packages/development && yarn dev", diff --git a/packages/action-menu/package.json b/packages/action-menu/package.json index a574d9b88..5cb88ee22 100644 --- a/packages/action-menu/package.json +++ b/packages/action-menu/package.json @@ -14,7 +14,7 @@ "dist/" ], "peerDependencies": { - "@yoopta/editor": ">=4.0.0-rc.0", + "@yoopta/editor": ">=4.0.0", "react": ">=17.0.2", "react-dom": ">=17.0.2" }, diff --git a/packages/blockquote/package.json b/packages/blockquote/package.json index b78146c07..27d99ffd5 100644 --- a/packages/blockquote/package.json +++ b/packages/blockquote/package.json @@ -14,7 +14,7 @@ "dist/" ], "peerDependencies": { - "@yoopta/editor": ">=4.0.0-rc.0", + "@yoopta/editor": ">=4.0.0", "react": ">=17.0.2", "react-dom": ">=17.0.2" }, diff --git a/packages/blockquote/src/index.ts b/packages/blockquote/src/index.ts index 9a17008aa..f81098a9d 100644 --- a/packages/blockquote/src/index.ts +++ b/packages/blockquote/src/index.ts @@ -1,4 +1,5 @@ import { Blockquote } from './plugin'; +import { BlockquoteRenderer } from './render/BlockquoteRenderer'; import { BlockquoteElement } from './types'; import './styles.css'; @@ -9,4 +10,4 @@ declare module 'slate' { } export default Blockquote; -export { BlockquoteElement }; +export { BlockquoteElement, Blockquote, BlockquoteRenderer }; diff --git a/packages/blockquote/src/plugin/index.tsx b/packages/blockquote/src/plugin/index.tsx index c81bf71a9..1d746f1bc 100644 --- a/packages/blockquote/src/plugin/index.tsx +++ b/packages/blockquote/src/plugin/index.tsx @@ -1,11 +1,15 @@ import { YooptaPlugin } from '@yoopta/editor'; -import { BlockquoteRender } from '../ui/Blockquote'; +import { BlockquoteRenderer } from '../render/BlockquoteRenderer'; const Blockquote = new YooptaPlugin({ type: 'Blockquote', elements: { blockquote: { - render: BlockquoteRender, + render: (props) => ( + + {props.children} + + ), }, }, options: { diff --git a/packages/blockquote/src/ui/Blockquote.tsx b/packages/blockquote/src/render/BlockquoteRenderer.tsx similarity index 55% rename from packages/blockquote/src/ui/Blockquote.tsx rename to packages/blockquote/src/render/BlockquoteRenderer.tsx index 7c625b467..3a13c701f 100644 --- a/packages/blockquote/src/ui/Blockquote.tsx +++ b/packages/blockquote/src/render/BlockquoteRenderer.tsx @@ -1,4 +1,4 @@ -import { PluginElementRenderProps } from '@yoopta/editor'; +import type { ElementRendererProps } from '@yoopta/editor'; const blockquoteStyles = { margin: '8px 0 0 0', @@ -6,20 +6,19 @@ const blockquoteStyles = { borderLeft: '2px solid #e5e7eb', }; -const BlockquoteRender = (props: PluginElementRenderProps) => { - const { className = '', style, ...htmlAttrs } = props.HTMLAttributes || {}; +const BlockquoteRenderer = (props: ElementRendererProps) => { + const { className = '', style, ...attrs } = props.attributes || {}; return (
{props.children}
); }; -export { BlockquoteRender }; +export { BlockquoteRenderer }; diff --git a/packages/callout/package.json b/packages/callout/package.json index cdedd0e3c..407017d4f 100644 --- a/packages/callout/package.json +++ b/packages/callout/package.json @@ -14,7 +14,7 @@ "dist/" ], "peerDependencies": { - "@yoopta/editor": ">=4.0.0-rc.0", + "@yoopta/editor": ">=4.0.0", "react": ">=17.0.2", "react-dom": ">=17.0.2" }, diff --git a/packages/callout/src/ui/CalloutBlockOptions.tsx b/packages/callout/src/components/CalloutBlockOptions.tsx similarity index 100% rename from packages/callout/src/ui/CalloutBlockOptions.tsx rename to packages/callout/src/components/CalloutBlockOptions.tsx diff --git a/packages/callout/src/editor/CalloutEditor.tsx b/packages/callout/src/editor/CalloutEditor.tsx new file mode 100644 index 000000000..fb5f956df --- /dev/null +++ b/packages/callout/src/editor/CalloutEditor.tsx @@ -0,0 +1,20 @@ +import { PluginElementRenderProps, useYooptaEditor, useYooptaReadOnly } from '@yoopta/editor'; +import { CalloutBlockOptions } from '../components/CalloutBlockOptions'; +import { CalloutRenderer } from '../render/CalloutRenderer'; + +const CalloutEditor = ({ element, attributes, children, blockId, block }: PluginElementRenderProps) => { + const editor = useYooptaEditor(); + const isReadOnly = useYooptaReadOnly(); + const { theme = 'default' } = element.props || {}; + + return ( + + {!isReadOnly && } + {children} + + ); +}; + +CalloutEditor.displayName = 'Callout'; + +export { CalloutEditor }; diff --git a/packages/callout/src/index.ts b/packages/callout/src/index.ts index f57ac9cd2..988616d59 100644 --- a/packages/callout/src/index.ts +++ b/packages/callout/src/index.ts @@ -1,4 +1,5 @@ import { Callout } from './plugin'; +import { CalloutRenderer } from './render/CalloutRenderer'; import { CalloutElement } from './types'; import './styles.css'; @@ -9,4 +10,4 @@ declare module 'slate' { } export default Callout; -export { CalloutElement }; +export { CalloutElement, CalloutRenderer }; diff --git a/packages/callout/src/plugin/index.tsx b/packages/callout/src/plugin/index.tsx index 50094bc8d..06e623b38 100644 --- a/packages/callout/src/plugin/index.tsx +++ b/packages/callout/src/plugin/index.tsx @@ -1,12 +1,16 @@ import { YooptaPlugin } from '@yoopta/editor'; import { CalloutElementProps, CalloutPluginElementKeys } from '../types'; -import { CalloutRender } from '../ui/Callout'; +import { CalloutEditor } from '../editor/CalloutEditor'; +import { CalloutRenderer } from '../render/CalloutRenderer'; const Callout = new YooptaPlugin({ type: 'Callout', elements: { callout: { - render: CalloutRender, + render: { + editor: CalloutEditor, + renderer: CalloutRenderer, + }, props: { theme: 'default', }, diff --git a/packages/callout/src/render/CalloutRenderer.tsx b/packages/callout/src/render/CalloutRenderer.tsx new file mode 100644 index 000000000..acae8cd3c --- /dev/null +++ b/packages/callout/src/render/CalloutRenderer.tsx @@ -0,0 +1,18 @@ +import { ElementRendererProps } from '@yoopta/editor'; +import { CALLOUT_THEME_STYLES } from '../utils'; + +export const CalloutRenderer = ({ element, attributes, children }: ElementRendererProps) => { + const { theme = 'default' } = element.props || {}; + const style = CALLOUT_THEME_STYLES[theme]; + + return ( +
+ {children} +
+ ); +}; diff --git a/packages/callout/src/ui/Callout.tsx b/packages/callout/src/ui/Callout.tsx deleted file mode 100644 index e015860c8..000000000 --- a/packages/callout/src/ui/Callout.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { PluginElementRenderProps, useBlockData, useYooptaEditor, useYooptaReadOnly } from '@yoopta/editor'; -import { CALLOUT_THEME_STYLES } from '../utils'; -import { CalloutBlockOptions } from './CalloutBlockOptions'; - -const CalloutRender = ({ element, attributes, children, blockId, HTMLAttributes = {} }: PluginElementRenderProps) => { - const { className = '', ...htmlAttrs } = HTMLAttributes; - - const block = useBlockData(blockId); - const editor = useYooptaEditor(); - const isReadOnly = useYooptaReadOnly(); - const { theme = 'default' } = element.props || {}; - const styles = CALLOUT_THEME_STYLES[theme]; - - return ( -
- {!isReadOnly && } - {children} -
- ); -}; - -CalloutRender.displayName = 'Callout'; - -export { CalloutRender }; diff --git a/packages/code/package.json b/packages/code/package.json index 0f933b9d3..9fd2826b4 100644 --- a/packages/code/package.json +++ b/packages/code/package.json @@ -14,7 +14,7 @@ "dist/" ], "peerDependencies": { - "@yoopta/editor": ">=4.0.0-rc.0", + "@yoopta/editor": ">=4.0.0", "react": ">=17.0.2", "react-dom": ">=17.0.2" }, diff --git a/packages/core/src/components/Editor/dnd.ts b/packages/core/src/components/Editor/dnd.ts index 69ea0e894..f0edea864 100644 --- a/packages/core/src/components/Editor/dnd.ts +++ b/packages/core/src/components/Editor/dnd.ts @@ -7,7 +7,7 @@ export const useYooptaDragDrop = ({ editor }: { editor: YooEditor }) => { const sensors = useSensors( useSensor(PointerSensor, { activationConstraint: { - delay: 100, + delay: 10, tolerance: 0, }, }), diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index b683918ce..885217d7b 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -30,6 +30,7 @@ export { PluginElementRenderProps, PluginEventHandlerOptions, PluginCustomEditorRenderProps, + ElementRendererProps, YooptaMarkProps, } from './plugins/types'; diff --git a/packages/core/src/plugins/SlateEditorComponent.tsx b/packages/core/src/plugins/SlateEditorComponent.tsx index 8d2b2af71..d506a6153 100644 --- a/packages/core/src/plugins/SlateEditorComponent.tsx +++ b/packages/core/src/plugins/SlateEditorComponent.tsx @@ -1,10 +1,16 @@ -import React, { memo, useCallback, useMemo, useRef } from 'react'; +import React, { memo, ReactComponentElement, ReactElement, ReactNode, useCallback, useMemo, useRef } from 'react'; import { DefaultElement, Editable, RenderElementProps, Slate } from 'slate-react'; import { useYooptaEditor, useBlockData } from '../contexts/YooptaContext/YooptaContext'; import { EVENT_HANDLERS } from '../handlers'; import { YooptaMark } from '../marks'; -import { ExtendedLeafProps, PluginCustomEditorRenderProps, PluginEventHandlerOptions, Plugin } from './types'; +import { + ExtendedLeafProps, + PluginCustomEditorRenderProps, + PluginEventHandlerOptions, + Plugin, + PluginElementsMap, +} from './types'; import { EditorEventHandlers } from '../types/eventHandlers'; import { HOTKEYS } from '../utils/hotkeys'; import { Editor, NodeEntry, Range } from 'slate'; @@ -24,9 +30,13 @@ type Props = Plugin { +const getMappedElements = (elements: PluginElementsMap) => { const mappedElements = {}; - Object.keys(elements).forEach((type) => (mappedElements[type] = elements[type].render)); + Object.keys(elements).forEach((type) => { + const render = elements[type].render?.editor ? elements[type].render?.editor : elements[type].render; + mappedElements[type] = render; + }); + return mappedElements; }; @@ -50,7 +60,6 @@ const SlateEditorComponent = ({ const editor = useYooptaEditor(); const block = useBlockData(id); const initialValue = useRef(block.value).current; - const type = block.type; const ELEMENTS_MAP = useMemo(() => getMappedElements(elements), [elements]); const MARKS_MAP = useMemo(() => getMappedMarks(marks), [marks]); @@ -113,9 +122,12 @@ const SlateEditorComponent = ({ const renderElement = useCallback( (props: RenderElementProps) => { const ElementComponent = ELEMENTS_MAP[props.element.type]; + const { attributes: slateElementAttributes, ...elementProps } = props; + + const attributes = { ...options?.HTMLAttributes, ...slateElementAttributes }; if (!ElementComponent) return ; - return ; + return ; }, [elements], ); @@ -247,7 +259,7 @@ const SlateEditorComponent = ({ ); return ( -
+
& { blockId: string; - HTMLAttributes?: HTMLAttributes; + block: YooptaBlockData; + attributes: RenderSlateElementProps['attributes'] & HTMLAttributes; }; +export type ElementRendererProps = Omit; + export type PluginCustomEditorRenderProps = { blockId: string; }; @@ -39,8 +42,14 @@ export type PluginCustomEditorRenderProps = { export type PluginDefaultProps = { nodeType?: 'block' | 'inline' | 'void' | 'inlineVoid' }; export type PluginElementProps = PluginDefaultProps & T; +type PluginElementRenderJustFn = (props: PluginElementRenderProps) => JSX.Element; +type PluginElementRenderWithEditor = { + editor: (props: PluginElementRenderProps) => JSX.Element; + renderer: (props: ElementRendererProps) => JSX.Element; +}; + export type PluginElement = { - render: (props: PluginElementRenderProps) => JSX.Element; + render: PluginElementRenderWithEditor | PluginElementRenderJustFn; props?: PluginElementProps; options?: PluginElementOptions; asRoot?: boolean; diff --git a/packages/development/package.json b/packages/development/package.json index 3ef9f855c..32fb1a0e2 100644 --- a/packages/development/package.json +++ b/packages/development/package.json @@ -27,6 +27,7 @@ "@yoopta/table": "*", "@yoopta/toolbar": "*", "@yoopta/video": "*", + "@yoopta/renderer": "*", "classnames": "^2.5.1", "katex": "^0.16.10", "lucide-react": "^0.365.0", diff --git a/packages/development/src/pages/dev/index.tsx b/packages/development/src/pages/dev/index.tsx index db7948e84..2a760c30c 100644 --- a/packages/development/src/pages/dev/index.tsx +++ b/packages/development/src/pages/dev/index.tsx @@ -1,148 +1,15 @@ import YooptaEditor, { createYooptaEditor, Tools, YooEditor, YooptaBlockData } from '@yoopta/editor'; -import Blockquote from '@yoopta/blockquote'; -import Paragraph from '@yoopta/paragraph'; -import Headings from '@yoopta/headings'; -import Image from '@yoopta/image'; -import { Bold, Italic, Highlight, CodeMark, Strike, Underline } from '@yoopta/marks'; -import Callout from '@yoopta/callout'; -import Lists from '@yoopta/lists'; -import Link from '@yoopta/link'; -import Video from '@yoopta/video'; -import File from '@yoopta/file'; -import Embed from '@yoopta/embed'; + import ActionMenuList, { DefaultActionMenuRender } from '@yoopta/action-menu-list'; import LinkTool, { DefaultLinkToolRender } from '@yoopta/link-tool'; import Toolbar, { DefaultToolbarRender } from '@yoopta/toolbar'; import { useEffect, useMemo, useRef, useState } from 'react'; -import { uploadToCloudinary } from '../../utils/cloudinary'; -import Code from '@yoopta/code'; -import { BaseElement } from 'slate'; -import { ActionNotionMenuExample } from '../../components/ActionMenuExamples/NotionExample/ActionNotionMenuExample'; -import { SlackChat } from '../../components/Chats/SlackChat/SlackChat'; import { NotionToolbar } from '../../components/Toolbars/NotionToolbar/NotionToolbar'; +import { value } from '../../utils/defaultValue'; +import { MARKS, plugins } from '../../utils/plugins'; // import Mention from '@yoopta/mention'; -const plugins = [ - Code, - File.extend({ - options: { - onUpload: async (file: File) => { - const data = await uploadToCloudinary(file, 'auto'); - - return { - src: data.secure_url, - format: data.format, - name: data.name, - size: data.bytes, - }; - }, - }, - }), - Paragraph.extend({ - options: { - HTMLAttributes: { - className: 'paragraph-element', - }, - display: { - title: 'THIS IS CHANGED TITLE', - description: 'Ooh.. It seems extending plugin is works!', - }, - }, - }), - Image.extend({ - // renders: { - // image: ({ attributes, children, element, blockId }) => { - // return ( - //
- // - // {children} - //
- // ); - // }, - // }, - options: { - HTMLAttributes: { - className: 'image-element', - }, - onUpload: async (file: File) => { - const data = await uploadToCloudinary(file); - - return { - src: data.secure_url, - alt: 'cloudinary', - sizes: { - width: data.width, - height: data.height, - }, - }; - }, - }, - }), - Headings.HeadingOne.extend({ - options: { - HTMLAttributes: { - className: 'heading-one-element', - style: { - color: 'red !important', - }, - }, - }, - }), - Headings.HeadingTwo.extend({ - options: { - HTMLAttributes: { - className: 'heading-two-element-extended', - }, - }, - }), - Headings.HeadingThree, - Blockquote, - Callout, - Lists.BulletedList.extend({ - options: { - HTMLAttributes: { - className: 'bulleted-list-element', - }, - }, - }), - Lists.NumberedList, - Lists.TodoList, - Embed, - Video.extend({ - options: { - HTMLAttributes: { - className: 'video-element', - }, - onUpload: async (file: File) => { - const data = await uploadToCloudinary(file, 'video'); - return { - src: data.secure_url, - alt: 'cloudinary', - sizes: { - width: data.width, - height: data.height, - }, - }; - }, - }, - }), - Link.extend({ - options: { - HTMLAttributes: { - className: 'link-element', - }, - }, - }), -]; - -const MARKS = [Bold, Italic, Highlight, CodeMark, Strike, Underline]; - const TOOLS: Tools = { ActionMenu: { // render: ActionNotionMenuExample, @@ -153,8 +20,8 @@ const TOOLS: Tools = { }, }, Toolbar: { - // render: DefaultToolbarRender, - render: NotionToolbar, + render: DefaultToolbarRender, + // render: NotionToolbar, tool: Toolbar, }, LinkTool: { @@ -165,444 +32,33 @@ const TOOLS: Tools = { export type YooptaChildrenValue = Record; -const value = { - 'f2cc1b99-1c77-49d4-90b9-f76cf7f28757': { - id: 'f2cc1b99-1c77-49d4-90b9-f76cf7f28757', - value: [ - { - id: '9183fd7d-1c8b-4653-b0d5-01bc27f07d95', - type: 'heading-two', - children: [ - { - text: 'Description', - }, - ], - props: { - nodeType: 'block', - }, - }, - ], - type: 'HeadingTwo', - meta: { - order: 0, - depth: 0, - }, - }, - 'cec7abb6-bff8-4911-bfda-a6c05227621f': { - id: 'cec7abb6-bff8-4911-bfda-a6c05227621f', - value: [ - { - id: '0ce2e21f-15a6-4102-916a-4ed74cee3669', - type: 'paragraph', - children: [ - { - text: 'Fixes', - }, - { - text: ' ', - }, - { - text: '#27481', - }, - { - text: '.', - }, - { - text: '\n', - }, - { - text: 'See also', - }, - { - text: ' ', - }, - { - text: '#3629', - }, - { - text: '.', - }, - ], - props: { - nodeType: 'block', - }, - }, - ], - type: 'Paragraph', - meta: { - order: 1, - depth: 0, - }, - }, - '53b44347-9c9a-408e-834d-269d0fa17a48': { - id: '53b44347-9c9a-408e-834d-269d0fa17a48', - value: [ - { - id: '6c53740d-3688-44d2-b6db-e0f182224e9d', - type: 'paragraph', - children: [ - { - text: 'These changes allow the user to select multiple blocks without selecting the blocks as a whole. This is done by allowing native selection across block boundaries, detecting the selection start and end in the appropriate rich text instances, and handling actions for this selection.', - }, - ], - props: { - nodeType: 'block', - }, - }, - ], - type: 'Paragraph', - meta: { - order: 2, - depth: 0, - }, - }, - '5c5e2bf6-50d1-4f72-bb33-7cfb38be480f': { - id: '5c5e2bf6-50d1-4f72-bb33-7cfb38be480f', - value: [ - { - id: 'f70411a6-661b-4c87-9bc2-90b9c474b670', - type: 'paragraph', - children: [ - { - text: 'This PR implements', - }, - { - text: ' ', - }, - { - code: true, - text: 'Enter', - }, - { - text: ',', - }, - { - text: ' ', - }, - { - code: true, - text: 'Backspace', - }, - { - text: ',', - }, - { - text: ' ', - }, - { - code: true, - text: 'Delete', - }, - { - text: ' ', - }, - { - text: '(forward merge) and any input (typing) over the selection.', - }, - ], - props: { - nodeType: 'block', - }, - }, - ], - type: 'Paragraph', - meta: { - order: 3, - depth: 0, - }, - }, - '1908fd68-e42f-485b-b4ca-7cbbc2626eae': { - id: '1908fd68-e42f-485b-b4ca-7cbbc2626eae', - value: [ - { - id: '13172da8-afef-42fa-a5d1-0b138260195f', - type: 'paragraph', - children: [ - { - text: 'You can select partially by mouse (drag), shift + click, and also the keyboard (shift + arrow).', - }, - ], - props: { - nodeType: 'block', - }, - }, - ], - type: 'Paragraph', - meta: { - order: 4, - depth: 0, - }, - }, - 'be87a81a-0e17-4efe-bebd-17d56f122df3': { - id: 'be87a81a-0e17-4efe-bebd-17d56f122df3', - value: [ - { - id: '41b3e0d9-0d81-4ceb-b85c-79ea1212a1d9', - type: 'paragraph', - children: [ - { - text: 'It works in all browsers including Firefox.', - }, - ], - props: { - nodeType: 'block', - }, - }, - ], - type: 'Paragraph', - meta: { - order: 5, - depth: 0, - }, - }, - 'b2572c5f-8258-466f-af7e-631ce00f9e10': { - id: 'b2572c5f-8258-466f-af7e-631ce00f9e10', - value: [ - { - id: 'c223492e-1abe-41df-a305-9a6a04d9025a', - type: 'paragraph', - children: [ - { - text: 'We use the', - }, - { - text: ' ', - }, - { - code: true, - text: 'merge', - }, - { - text: ' ', - }, - { - text: 'function on the block type to handle merges and detect if two blocks are mergeable. If two blocks are not mergeable, we fall back to the current behaviour, which is to select the blocks entirely.', - }, - ], - props: { - nodeType: 'block', - }, - }, - ], - type: 'Paragraph', - meta: { - order: 6, - depth: 0, - }, - }, - '391fd7a6-efc1-4c6e-b5ad-06449d447296': { - id: '391fd7a6-efc1-4c6e-b5ad-06449d447296', - value: [ - { - id: '6a7c25ba-253b-4e07-844c-c161c41fdaf5', - type: 'heading-two', - children: [ - { - text: 'Checklist:', - }, - ], - props: { - nodeType: 'block', - }, - }, - ], - type: 'HeadingTwo', - meta: { - order: 7, - depth: 0, - }, - }, - '223c0a79-e9f6-4892-b314-46abae2b3f10': { - id: '223c0a79-e9f6-4892-b314-46abae2b3f10', - value: [ - { - id: 'a010ac22-228f-48a1-83b9-85c50916208e', - type: 'bulleted-list', - children: [ - { - text: ' My code is tested.', - }, - ], - props: { - nodeType: 'block', - }, - }, - ], - type: 'BulletedList', - meta: { - order: 8, - depth: 0, - }, - }, - '7ea2f516-59d3-4cda-aa03-86b77ddf9f82': { - id: '7ea2f516-59d3-4cda-aa03-86b77ddf9f82', - value: [ - { - id: 'e230a425-7cac-4bcf-bd21-bd565f5e03de', - type: 'bulleted-list', - children: [ - { - text: ' My code follows the WordPress code style.', - }, - ], - props: { - nodeType: 'block', - }, - }, - ], - type: 'BulletedList', - meta: { - order: 9, - depth: 0, - }, - }, - '1a199ea1-7120-4c10-9b41-c2c0fffcdecc': { - id: '1a199ea1-7120-4c10-9b41-c2c0fffcdecc', - value: [ - { - id: '5bffabf2-d08c-4cef-acff-c6a799ac1fe4', - type: 'bulleted-list', - children: [ - { - text: ' My code follows the accessibility standards.', - }, - ], - props: { - nodeType: 'block', - }, - }, - ], - type: 'BulletedList', - meta: { - order: 10, - depth: 0, - }, - }, - '65263b95-2037-4eec-a079-0a8896bf558f': { - id: '65263b95-2037-4eec-a079-0a8896bf558f', - value: [ - { - id: '28098640-a649-4f04-b5ee-5a45bccfe904', - type: 'bulleted-list', - children: [ - { - text: " I've tested my changes with keyboard and screen readers.", - }, - ], - props: { - nodeType: 'block', - }, - }, - ], - type: 'BulletedList', - meta: { - order: 11, - depth: 0, - }, - }, - '9a469643-c29c-457d-92ad-b03a6f9adeec': { - id: '9a469643-c29c-457d-92ad-b03a6f9adeec', - value: [ - { - id: '5b82ca2f-87d1-40fc-98ea-23b6826174c6', - type: 'bulleted-list', - children: [ - { - text: ' My code has proper inline documentation.', - }, - ], - props: { - nodeType: 'block', - }, - }, - ], - type: 'BulletedList', - meta: { - order: 12, - depth: 0, - }, - }, - 'b89f8ac1-6760-4294-b1a3-c77b8cc01a10': { - id: 'b89f8ac1-6760-4294-b1a3-c77b8cc01a10', - value: [ - { - id: '27148ba4-41cf-4203-924f-6a03108dc1ca', - type: 'bulleted-list', - children: [ - { - text: " I've included developer documentation if appropriate.", - }, - ], - props: { - nodeType: 'block', - }, - }, - ], - type: 'BulletedList', - meta: { - order: 13, - depth: 0, - }, - }, - 'f907d9d2-1026-4266-a7ba-d9be081ffb1d': { - id: 'f907d9d2-1026-4266-a7ba-d9be081ffb1d', - value: [ - { - id: 'fc0ee172-9a9c-4304-877d-7dffec711a51', - type: 'bulleted-list', - children: [ - { - text: " I've updated all React Native files affected by any refactorings/renamings in this PR (please manually search all *.native.js files for terms that need renaming or removal).", - }, - ], - props: { - nodeType: 'block', - }, - }, - ], - type: 'BulletedList', - meta: { - order: 14, - depth: 0, - }, - }, - 'f96f1b75-282f-405d-a5ae-69d0aafde8d1': { - id: 'f96f1b75-282f-405d-a5ae-69d0aafde8d1', - value: [ - { - id: 'ab2c7a10-33b1-4283-a33c-4b77c4634cd0', - type: 'bulleted-list', - children: [ - { - text: " I've updated related schemas if appropriate.", - }, - ], - props: { - nodeType: 'block', - }, - }, - ], - type: 'BulletedList', - meta: { - order: 15, - depth: 0, - }, - }, -}; const BasicExample = () => { const editor: YooEditor = useMemo(() => createYooptaEditor(), []); const rectangleSelectionRef = useRef(null); const [readOnly, setReadOnly] = useState(false); + const [content, setContent] = useState(null); + + useEffect(() => { + const onSaveToLocalStorage = (val: any) => { + localStorage.setItem('editorData', JSON.stringify(val)); + }; + + editor.on('change', onSaveToLocalStorage); + return () => editor.off('change', onSaveToLocalStorage); + }, [content]); useEffect(() => { - editor.on('block:copy', (value) => console.log('BLOCK COPY', value)); + const editorData = localStorage.getItem('editorData') || '{}'; + setContent(JSON.parse(editorData)); }, []); const onSubmit = () => { const editorData = editor.getEditorValue(); - console.log('EDITOR DATA', editorData); + console.log('editorData', editorData); }; + if (!content) return null; + return (
{ tools={TOOLS} readOnly={readOnly} width={750} - value={value} + value={content} >