diff --git a/changelog.md b/changelog.md index 1ea46f0176..022137b65e 100644 --- a/changelog.md +++ b/changelog.md @@ -14,6 +14,7 @@ * [VirtualList]: fixed estimatedHeight calculations in components with pagination * [RTE]: fixed working of old iframe data structure with 'src' prop * [VerticalTabButton]: reverted paddings & gaps to previous values for all sizes +* [RTE]: fixed migration of old table element data.cellSizes to the new colSizes format # 5.10.2 - 24.10.2024 diff --git a/public/docs/content/examples-richTextEditor-Basic.json b/public/docs/content/examples-richTextEditor-Basic.json index e51b584768..b217e7a3e2 100644 --- a/public/docs/content/examples-richTextEditor-Basic.json +++ b/public/docs/content/examples-richTextEditor-Basic.json @@ -4,7 +4,7 @@ "type": "paragraph", "children": [ { - "text": "Rich Text Editor is a powerful content editor, styled to match UI, and extensible to host any React component." + "text": "Rich Text Editor is a powerful content editor, styled to match UUI theme style, and extensible by plugins." } ] }, @@ -13,44 +13,62 @@ "type": "paragraph", "children": [ { - "text": "Rich Text Editor is based on Slate.JS — the framework to build such editors. Read more on " + "text": "Rich Text Editor is based on " }, { - "data": { - "url": "https://docs.slatejs.org/" - }, + "data": {}, "type": "link", "url": "https://docs.slatejs.org/", "children": [ { - "text": "Slate.JS" + "text": "Slate.js" } ] }, { - "text": " documentation site." + "text": " and " + }, + { + "type": "link", + "url": "https://platejs.org/", + "target": "_blank", + "children": [ + { + "text": "Plate.js" + } + ] + }, + { + "text": " — the frameworks to build such editors." } ] }, { "data": {}, - "type": "paragraph", + "type": "note-error", "children": [ { - "text": "UUI RichTextEditor is built on top of Slate to bring UUI styles, add our extensions, panels to interact with, and to wrap them with convenient API." + "text": "It's required to import CCS from the editor package. Please add the following code to your app entry file: \n\n" + }, + { + "text": "import '@epam/uui-editor/styles.css';", + "uui-richTextEditor-code": true } ] }, { "data": {}, - "type": "note-error", + "type": "note-warning", "children": [ { - "text": "It's required to import CCS from the editor package. Please add the following code to your app entry file: \n" + "text": "Please note that the RTE component is uncontrolled because the UUI RTE is based on Slate.js, which is also an uncontrolled editor. \n\nAlthough the UUI implementation does have limited support for updating the editor's value from the outside, such as in cases where the value changes from empty to a new one (as might occur after loading a state from the backend), we still do not recommend relying on this mechanism and advise avoiding external updates to the editor state. \n\nAs a workaround, you can update the " }, { - "text": "import '@epam/uui-editor/styles.css';", + "text": "key", "uui-richTextEditor-code": true + }, + { + "text": " prop on the component to trigger a full remount cycle, ensuring the value is safely updated." } ] } diff --git a/uui-editor/package.json b/uui-editor/package.json index 56ef54b393..a6706fd7fc 100644 --- a/uui-editor/package.json +++ b/uui-editor/package.json @@ -48,7 +48,8 @@ "slate-history": "0.100.0", "slate-hyperscript": "0.100.0", "slate-react": "0.102.0", - "unified": "9.2.2" + "unified": "9.2.2", + "react-fast-compare": "3.2.2" }, "devDependencies": { "@types/lodash.debounce": "4.0.7", diff --git a/uui-editor/src/SlateEditor.tsx b/uui-editor/src/SlateEditor.tsx index 96e9e983c3..517ae3ea27 100644 --- a/uui-editor/src/SlateEditor.tsx +++ b/uui-editor/src/SlateEditor.tsx @@ -19,6 +19,7 @@ import { useFocusEvents } from './plugins/eventEditorPlugin'; import { isEditorValueEmpty } from './helpers'; import { getMigratedPlateValue, isPlateValue } from './migrations'; import { PlateProps } from '@udecode/plate-core'; +import isEqual from 'react-fast-compare'; export interface PlateEditorProps extends IEditable, @@ -42,6 +43,7 @@ export const SlateEditor = memo(forwardRef((pr const [currentId] = useState(String(Date.now())); const editorRef = useRef(null); const editableWrapperRef = useRef(); + const prevChangedValue = useRef(props.value); /** value */ /** consider legacy slate to plate content migraions once. should be deprecated in the near future */ @@ -64,7 +66,7 @@ export const SlateEditor = memo(forwardRef((pr if (isReadonly) { return; } - + prevChangedValue.current = v; onValueChange(v); }, [isReadonly, onValueChange]); @@ -131,14 +133,18 @@ export const SlateEditor = memo(forwardRef((pr ? { renderContent() } : renderContent(); - /** force update of uncontrolled component */ + /** force update of uncontrolled component. Danger part, because Slate component is uncontrolled by default and doesn't support value change after init. + * This code can produce unexpected effects and bugs, e.g. updating value in a such value doesn't call value normalizers. + * We just guarantee that we can apply value change from empty state to the new one, like after content loading. Try to avoid updating editor value in other cases. + */ const forceUpdate = useForceUpdate(); useEffect(() => { - if (isPlateValue(plateValue) && editorRef.current && editorRef.current.children !== plateValue) { + if (isPlateValue(plateValue) && editorRef.current && !isEqual(prevChangedValue.current, props.value)) { editorRef.current.children = plateValue; + prevChangedValue.current = props.value; forceUpdate(); } - }, [forceUpdate, plateValue]); + }, [props.value]); return ( , entry: TNo } }; -/** deprecate data properties */ +/** Migrate data.cellSizes to the colSizes */ export const normalizeTableElement = (entry: TNodeEntry): TTableElement => { const [node] = entry; const tableNode = node as DepreactedTTableElement; @@ -48,7 +48,7 @@ export const normalizeTableElement = (entry: TNodeEntry): TTableElement => { return tableNode; } - return { ...tableNode, data: { ...otherData } }; + return { ...tableNode, colSizes: cellSizes, data: { ...otherData } }; } return tableNode; }; diff --git a/uui-editor/src/plugins/tablePlugin/tablePlugin.tsx b/uui-editor/src/plugins/tablePlugin/tablePlugin.tsx index 237790e82b..c5e7c237c5 100644 --- a/uui-editor/src/plugins/tablePlugin/tablePlugin.tsx +++ b/uui-editor/src/plugins/tablePlugin/tablePlugin.tsx @@ -158,16 +158,17 @@ export const tablePlugin = (): PlatePlugin => if (isElement(node) && node.type === TABLE_TYPE) { const normalized = initDefaultTableColWidth(normalizeTableElement(entry) as DeprecatedTTableCellElement); - setNodes( editor, normalized, { at: path }, ); + return; } if (isElement(node) && (TABLE_CELL_TYPE === node.type || TABLE_CELL_TYPE === node.type)) { normalizeTableCellElement(editor, entry); + return; } normalizeNode(entry); diff --git a/yarn.lock b/yarn.lock index 573e51d177..6d5b41d6c6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14685,7 +14685,7 @@ react-error-overlay@^6.0.11: resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb" integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg== -react-fast-compare@^3.0.1, react-fast-compare@^3.2.2: +react-fast-compare@3.2.2, react-fast-compare@^3.0.1, react-fast-compare@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49" integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==