Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DEV-19141] Refactor - Find a way to make hoc func plugins introducing new editor #582

Draft
wants to merge 8 commits into
base: feature/dev-19130-upgrade-plate-to-v42
Choose a base branch
from
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"dependencies": {
"@types/react": "^18.3.0",
"@types/react-dom": "^18.3.0",
"@udecode/plate": "^42.1.2",
"react": "^18.3.0",
"react-dom": "^18.3.0"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { PlaceholdersManager } from '../PlaceholdersManager';
import { InputPlaceholderElement } from './InputPlaceholderElement';

const extensions = [PlaceholdersExtension()];
const editor = createEditor(createPlateEditor(), () => extensions);
const editor = createEditor({ editor: createPlateEditor(), getExtensions: () => extensions });

const placeholder: PlaceholderNode = {
type: PlaceholderNode.Type.EMBED,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { PlaceholderNode } from '../PlaceholderNode';
import { PlaceholderElement } from './PlaceholderElement';

const extensions = [PlaceholdersExtension()];
const editor = createEditor(createPlateEditor(), () => extensions);
const editor = createEditor({ editor: createPlateEditor(), getExtensions: () => extensions });

const placeholder: PlaceholderNode = {
type: PlaceholderNode.Type.ATTACHMENT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { PlaceholderNode } from '../PlaceholderNode';
import { AttachmentPlaceholderElement } from './AttachmentPlaceholderElement';

const extensions = [PlaceholdersExtension()];
const editor = createEditor(createPlateEditor(), () => extensions);
const editor = createEditor({ editor: createPlateEditor(), getExtensions: () => extensions });

const placeholder: PlaceholderNode<PlaceholderNode.Type.ATTACHMENT> = {
type: PlaceholderNode.Type.ATTACHMENT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { PlaceholderNode } from '../PlaceholderNode';
import { ContactPlaceholderElement } from './ContactPlaceholderElement';

const extensions = [PlaceholdersExtension()];
const editor = createEditor(createPlateEditor(), () => extensions);
const editor = createEditor({ editor: createPlateEditor(), getExtensions: () => extensions });

const placeholder: PlaceholderNode<PlaceholderNode.Type.CONTACT> = {
type: PlaceholderNode.Type.CONTACT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { PlaceholderNode } from '../PlaceholderNode';
import { EmbedPlaceholderElement } from './EmbedPlaceholderElement';

const extensions = [PlaceholdersExtension()];
const editor = createEditor(createPlateEditor(), () => extensions);
const editor = createEditor({ editor: createPlateEditor(), getExtensions: () => extensions });

const placeholder: PlaceholderNode<PlaceholderNode.Type.EMBED> = {
type: PlaceholderNode.Type.EMBED,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { PlaceholderNode } from '../PlaceholderNode';
import { GalleryPlaceholderElement } from './GalleryPlaceholderElement';

const extensions = [PlaceholdersExtension()];
const editor = createEditor(createPlateEditor(), () => extensions);
const editor = createEditor({ editor: createPlateEditor(), getExtensions: () => extensions });

const placeholder: PlaceholderNode<PlaceholderNode.Type.GALLERY> = {
type: PlaceholderNode.Type.GALLERY,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { PlaceholderNode } from '../PlaceholderNode';
import { ImagePlaceholderElement } from './ImagePlaceholderElement';

const extensions = [PlaceholdersExtension()];
const editor = createEditor(createPlateEditor(), () => extensions);
const editor = createEditor({ editor: createPlateEditor(), getExtensions: () => extensions });

const placeholder: PlaceholderNode<PlaceholderNode.Type.IMAGE> = {
type: PlaceholderNode.Type.IMAGE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { PlaceholderNode } from '../PlaceholderNode';
import { MediaPlaceholderElement } from './MediaPlaceholderElement';

const extensions = [PlaceholdersExtension()];
const editor = createEditor(createPlateEditor(), () => extensions);
const editor = createEditor({ editor: createPlateEditor(), getExtensions: () => extensions });

const placeholder: PlaceholderNode<PlaceholderNode.Type.MEDIA> = {
type: PlaceholderNode.Type.MEDIA,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { PlaceholderNode } from '../PlaceholderNode';
import { SocialPostPlaceholderElement } from './SocialPostPlaceholderElement';

const extensions = [PlaceholdersExtension()];
const editor = createEditor(createPlateEditor(), () => extensions);
const editor = createEditor({ editor: createPlateEditor(), getExtensions: () => extensions });

const placeholder: PlaceholderNode<PlaceholderNode.Type.SOCIAL_POST> = {
type: PlaceholderNode.Type.SOCIAL_POST,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { PlaceholderNode } from '../PlaceholderNode';
import { StoryBookmarkPlaceholderElement } from './StoryBookmarkPlaceholderElement';

const extensions = [PlaceholdersExtension()];
const editor = createEditor(createPlateEditor(), () => extensions);
const editor = createEditor({ editor: createPlateEditor(), getExtensions: () => extensions });

const placeholder: PlaceholderNode<PlaceholderNode.Type.STORY_BOOKMARK> = {
type: PlaceholderNode.Type.STORY_BOOKMARK,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { PlaceholderNode } from '../PlaceholderNode';
import { StoryEmbedPlaceholderElement } from './StoryEmbedPlaceholderElement';

const extensions = [PlaceholdersExtension()];
const editor = createEditor(createPlateEditor(), () => extensions);
const editor = createEditor({ editor: createPlateEditor(), getExtensions: () => extensions });

const placeholder: PlaceholderNode<PlaceholderNode.Type.STORY_EMBED> = {
type: PlaceholderNode.Type.STORY_EMBED,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { PlaceholderNode } from '../PlaceholderNode';
import { VideoPlaceholderElement } from './VideoPlaceholderElement';

const extensions = [PlaceholdersExtension()];
const editor = createEditor(createPlateEditor(), () => extensions);
const editor = createEditor({ editor: createPlateEditor(), getExtensions: () => extensions });

const placeholder: PlaceholderNode<PlaceholderNode.Type.VIDEO> = {
type: PlaceholderNode.Type.VIDEO,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { PlaceholderNode } from '../PlaceholderNode';
import { WebBookmarkPlaceholderElement } from './WebBookmarkPlaceholderElement';

const extensions = [PlaceholdersExtension()];
const editor = createEditor(createPlateEditor(), () => extensions);
const editor = createEditor({ editor: createPlateEditor(), getExtensions: () => extensions });

const placeholder: PlaceholderNode<PlaceholderNode.Type.WEB_BOOKMARK> = {
type: PlaceholderNode.Type.WEB_BOOKMARK,
Expand Down
6 changes: 5 additions & 1 deletion packages/slate-editor/src/hyperscript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,11 @@ const extensions = [

const creators: Record<keyof HElements, HyperscriptCreators[string]> = {
editor: createEditorFactory(() =>
createEditor(createSlateEditor(), () => extensions, [withReact, withHistory]),
createEditor({
editor: createSlateEditor(),
getExtensions: () => extensions,
withOverrides: [withReact, withHistory],
}),
),
'editor-pure': createEditorFactory(() => withReact(createSlateEditor())),
'h:text': createText,
Expand Down
72 changes: 58 additions & 14 deletions packages/slate-editor/src/modules/editor/createEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,76 @@ import {
import type { Extension, WithOverrides } from '@prezly/slate-commons';
import { isNotUndefined } from '@technically/is-not-undefined';
import { flow } from '@technically/lodash';
import type { SlateEditor } from '@udecode/plate';
import { type SlateEditor } from '@udecode/plate';
import { createPlateEditor } from '@udecode/plate/react';
import { type PlatePlugin } from '@udecode/plate/react';

import { createParagraph } from '#extensions/paragraphs';
import { withNodesHierarchy, hierarchySchema } from '#modules/nodes-hierarchy';

import { withDeserializeHtml, withElementsEqualityCheck, withRichBlocks } from './plugins';
import {
DefaultTextBlockPlugin,
withDeserializeHtml,
RichBlocksPlugin,
ElementsEqualityCheckPlugin,
} from './plugins';
import { type Value } from './types';

type Params = {
initialValue?: Value;
plugins?: PlatePlugin[];
baseEditor?: SlateEditor;
/**
* @deprecated It is planned to migrate extensions to become Plate Plugins
*/
getExtensions?: () => Extension[];
/**
* @deprecated It is recommended to migrate these overrides to become Plate Plugins
*/
withOverrides?: WithOverrides[];
};

export function createEditor({
initialValue,
plugins = [],
baseEditor,
getExtensions = noExtensions,
withOverrides = [],
}: Params) {
const editor = createPlateEditor({
editor: baseEditor,
plugins: [
...plugins,
DefaultTextBlockPlugin.configure({
options: {
createDefaultTextBlock: createParagraph,
},
}),
ElementsEqualityCheckPlugin,
RichBlocksPlugin.configure({
options: { getExtensions },
}),
],
value: initialValue,
});

export function createEditor(
baseEditor: SlateEditor,
getExtensions: () => Extension[],
plugins: WithOverrides[] = [],
) {
const overrides = getExtensions()
.map(({ withOverrides }) => withOverrides)
const extensionsOverrides = getExtensions()
.map((extension) => extension.withOverrides)
.filter(isNotUndefined);

return flow([
...plugins,
...withOverrides,
withNodesHierarchy(hierarchySchema),
withBreaksOnExpandedSelection,
withBreaksOnVoidNodes,
withInlineVoid(getExtensions),
withNormalization(getExtensions),
withUserFriendlyDeleteBehavior,
withDeserializeHtml(getExtensions),
withRichBlocks(getExtensions),
withElementsEqualityCheck,
...overrides,
])(baseEditor);
...extensionsOverrides,
])(editor);
}

function noExtensions(): Extension[] {
return [];
}
2 changes: 0 additions & 2 deletions packages/slate-editor/src/modules/editor/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
export { Editor } from './Editor';
export { createEditor } from './createEditor';
export { createEmptyValue } from './lib';
export { withDeserializeHtml, withElementsEqualityCheck, withRichBlocks } from './plugins';
export type { ElementsEqualityCheckEditor, RichBlocksAwareEditor } from './plugins';
export type { EditorRef, EditorProps, Value } from './types';
export { useEditorEvents } from './useEditorEvents';
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { isEditorValueEqual } from './isEditorValueEqual';

describe('isEditorValueEqual', () => {
it('should return true for equivalent values', () => {
const editor = createEditor(createSlateEditor(), () => []);
const editor = createEditor({ editor: createSlateEditor() });

const a = [
{
Expand All @@ -25,7 +25,7 @@ describe('isEditorValueEqual', () => {
});

it('should return false for non-equivalent values', () => {
const editor = createEditor(createSlateEditor(), () => []);
const editor = createEditor({ editor: createSlateEditor() });

const a = [
{
Expand All @@ -44,7 +44,7 @@ describe('isEditorValueEqual', () => {
});

it('should consider structural equality', () => {
const editor = createEditor(createSlateEditor(), () => []);
const editor = createEditor({ editor: createSlateEditor() });

const a = [
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { type TElement } from '@udecode/plate';
import { createPlatePlugin } from '@udecode/plate/react';

type TextBlockFactory<T extends TElement> = (props?: Partial<T>) => T;

type Options<T> = {
createDefaultTextBlock(): TextBlockFactory<T>;
};

export const DefaultTextBlockPlugin = {
configure<T extends TElement>(config: { options: Options<T> }) {
return createPlatePlugin<'DefaultTextBlock', Options<T>>({
key: 'DefaultTextBlock',
options: config.options,
}).overrideEditor(({ getOptions }) => {
const { createDefaultTextBlock } = getOptions();
return {
api: {
createDefaultTextBlock,
},
};
});
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,30 @@ import {
VideoNode,
} from '@prezly/slate-types';
import { isEqual } from '@technically/lodash';
import type { Element, SlateEditor } from '@udecode/plate';
import type { Element } from '@udecode/plate';
import { createPlatePlugin } from '@udecode/plate/react';

import { EmbedNode } from '#extensions/embed';
import { PlaceholderNode } from '#extensions/placeholders';

export interface ElementsEqualityCheckEditor {
/**
* Compare two elements.
*
* This is useful to implement smarter comparison rules to,
* for example, ignore data-independent properties like `uuid`.
*
* `children` arrays can be omitted from the comparison,
* as the outer code will compare them anyway.
*/
isElementEqual(node: Element, another: Element): boolean | undefined;
}

export function withElementsEqualityCheck<T extends SlateEditor>(
editor: T,
): T & ElementsEqualityCheckEditor {
return Object.assign(editor, {
isElementEqual,
});
}
/**
* Compare two elements.
*
* This is useful to implement smarter comparison rules to,
* for example, ignore data-independent properties like `uuid`.
*
* `children` arrays can be omitted from the comparison,
* as the outer code will compare them anyway.
*/
export const ElementsEqualityCheckPlugin = createPlatePlugin({
key: 'withElementsEqualityCheck',
}).overrideEditor(() => {
return {
api: {
isElementEqual,
},
};
});

function isElementEqual(node: Element, another: Element): boolean | undefined {
if (isAttachmentNode(node) && isAttachmentNode(another)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { Extension } from '@prezly/slate-commons';
import type { Node } from '@udecode/plate';
import { createPlatePlugin, type PlatePlugin } from '@udecode/plate/react';

type Configuration = {
key: 'RichBlocks';
options: {
getExtensions(): Extension[];
};
api: never;
transforms: never;
};

export const RichBlocksPlugin: PlatePlugin<Configuration> = createPlatePlugin<
Configuration['key'],
Configuration['options'],
Configuration['api'],
Configuration['transforms']
>({
key: 'RichBlocks',
options: {
getExtensions: () => [],
},
}).overrideEditor(({ getOptions }) => {
const { getExtensions } = getOptions();
function isRichBlock(node: Node) {
for (const extension of getExtensions()) {
if (extension.isRichBlock?.(node)) return true;
}
return false;
}
return {
api: {
isRichBlock,
},
};
});
9 changes: 3 additions & 6 deletions packages/slate-editor/src/modules/editor/plugins/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
export { type DefaultTextBlockEditor, withDefaultTextBlock } from './withDefaultTextBlock';
export { DefaultTextBlockPlugin } from './DefaultTextBlockPlugin';
export { ElementsEqualityCheckPlugin } from './ElementsEqualityCheckPlugin';
export { RichBlocksPlugin } from './RichBlocksPlugin';
export { withDeserializeHtml } from './withDeserializeHtml';
export {
type ElementsEqualityCheckEditor,
withElementsEqualityCheck,
} from './withElementsEqualityCheck';
export { type RichBlocksAwareEditor, withRichBlocks } from './withRichBlocks';
Loading
Loading