-
#1500 by @zbeyens – Thanks @ianstormtaylor for the initial work on ianstormtaylor/slate#4177.
This release includes major changes to plate and slate types:
- Changing the
TEditortype to beTEditor<V>whereVrepresents the "value" being edited by Slate. In the most generic editor,Vwould be equivalent toTElement[](since that is what is accepted as children of the editor). But in a custom editor, you might haveTEditor<Array<Paragraph | Quote>>. - Other
TEditor-and-TNode-related methods have been also made generic, so for example if you usegetLeafNode(editor, path)it knows that the return value is aTTextnode. But more specifically, it knows that it is the text node of the type you've defined in your custom elements (with any marks you've defined). - This replaces the declaration merging approach, and provides some benefits. One of the drawbacks to declaration merging was that it was impossible to know whether you were dealing with an "unknown" or "known" element, since the underlying type was changed. Similarly, having two editors on the page with different schemas wasn't possible to represent. Hopefully this approach with generics will be able to smoothly replace the declaration merging approach. (While being easy to migrate to, since you can pass those same custom element definitions into
TEditorstill.)
- Changing the
Define your custom types
- Follow https://plate.udecode.io/docs/typescript example.
Slate types
Those Slate types should be replaced by the new types:
Editor->TEditor<V extends Value = Value>- Note that
TEditormethods are not typed based onValueas it would introduce a circular dependency. You can usegetTEditor(editor)to get the editor with typed methods.
- Note that
ReactEditor->TReactEditor<V extends Value = Value>HistoryEditor->THistoryEditor<V extends Value = Value>EditableProps->TEditableProps<V extends Value = Value>Node->TNodeElement->TElementText->TTextNodeEntry->TNodeEntryNodeProps->TNodeProps
Slate functions
Those Slate functions should be replaced by the new typed ones:
- As the new editor type is not matching the slate ones, all
Transforms,Editor,Node,Element,Text,HistoryEditor,ReactEditorfunctions should be replaced: The whole API has been typed into Plate core. See https://github.com/udecode/plate/packages/core/src/slate createEditor->createTEditorwithReact->withTReactwithHistory->withTHistory
Generic types
-
<T = {}>could be used to extend the editor type. It is now replaced by<E extends PlateEditor<V> = PlateEditor<V>>to customize the whole editor type. -
When the plugin type is customizable, these generics are used:
<P = PluginOptions, V extends Value = Value, E extends PlateEditor<V> = PlateEditor<V>>, wherePis the plugin options type. -
Editorfunctions are using<V extends Value>generic, whereVcan be a custom editor value type used inPlateEditor<V>. -
Editorfunctions returning a node are using<N extends ENode<V>, V extends Value = Value>generics, whereNcan be a custom returned node type. -
Editorcallbacks (e.g. a plugin option) are using<V extends Value = Value, E extends PlateEditor<V> = PlateEditor<V>>generics, whereEcan be a custom editor type. -
Nodefunctions returning a node are using<N extends Node, R extends TNode = TNode>generics. -
These generics are used by
<V extends Value, K extends keyof EMarks<V>>:getMarks,isMarkActive,removeMark,setMarks,ToggleMarkPlugin,addMark,removeEditorMark -
WithOverrideis a special type case as it can return a new editor type:// before export type WithOverride<T = {}, P = {}> = ( editor: PlateEditor<T>, plugin: WithPlatePlugin<T, P> ) => PlateEditor<T>; // after - where E is the Editor type (input), and EE is the Extended Editor type (output) export type WithOverride< P = PluginOptions, V extends Value = Value, E extends PlateEditor<V> = PlateEditor<V>, EE extends E = E > = (editor: E, plugin: WithPlatePlugin<P, V, E>) => EE;
-
type TEditor<V extends Value> -
type PlateEditor<V extends Value>
Renamed functions
getAbove->getAboveNodegetParent->getParentNodegetText->getEditorStringgetLastNode->getLastNodeByLevelgetPointBefore->getPointBeforeLocationgetNodes->getNodeEntriesgetNodes->getNodeEntriesisStart->isStartPointisEnd->isEndPoint
Replaced types
Removing node props types in favor of element types (same props + extends TElement). You can use TNodeProps to get the node data (props).
LinkNodeData->TLinkElementImageNodeData->TImageElementTableNodeData->TTableElementMentionNodeData->TMentionElementMentionNode->TMentionElementMentionInputNodeData->TMentionInputElementMentionInputNode->TMentionInputElementCodeBlockNodeData->TCodeBlockElementMediaEmbedNodeData->TMediaEmbedElementTodoListItemNodeData->TTodoListItemElementExcalidrawNodeData->TExcalidrawElement
Utils
matchsignature change:
<T extends TNode>(
obj: T,
path: TPath,
predicate?: Predicate<T>
)
Generic types
type StyledElementProps<V extends Value, N extends TElement = EElement<V>, TStyles = {}>
- #1377 by @zbeyens – Before,
BalloonToolbarcould be outsidePlate. Now,BallonToolbarshould be a child ofPlateto support multiple editors.
- #1303 by @zbeyens –
Plateeditorprop can now be fully controlled: Plate is not applyingwithPlateon it anymore
PlatePlugin.deserializeHtml- can't be an array anymore
- moved
validAttribute,validClassName,validNodeName,validStyletodeserializeHtml.rulesproperty
- renamed
plateStoretoplatesStore platesStoreis now a zustood storeeventEditorStoreis now a zustood storegetPlateIdnow gets the last editor id if not focused or blurred- used by
usePlateEditorRefandusePlateEditorState
- used by
- removed:
usePlateEnabledforusePlateSelectors(id).enabled()usePlateValueforusePlateSelectors(id).value()usePlateActions:resetEditorforgetPlateActions(id).resetEditor()clearStateforplatesActions.unset()setInitialStateforplatesActions.set(id)setEditorforgetPlateActions(id).editor(value)setEnabledforgetPlateActions(id).enabled(value)setValueforgetPlateActions(id).value(value)
getPlateStateusePlateStateusePlateKey
- #1303 by @zbeyens –
- renamed
plate-x-uitoplate-ui-x: all packages depending onstyled-componentshasplate-uiprefix - renamed
plate-x-serializertoplate-serializer-x - is now exporting only these (new) packages:
@udecode/plate-headless: all unstyled packages@udecode/plate-ui: all styled packages
- renamed
PlateStatetoPlateStoreState
-
IndentListPluginOptionsforPlatePlugin
Rename:
getIndentListInjectComponenttoinjectIndentListComponent
-
#1234 by @zbeyens – Breaking changes:
- removed
componentsprop:
// Before <Plate plugins={plugins} components={components} />; // After // option 1: use the plugin factory let plugins = [ createParagraphPlugin({ component: ParagraphElement, }), ]; // option 2: use createPlugins plugins = createPlugins(plugins, { components: { [ELEMENT_PARAGRAPH]: ParagraphElement, }, }); <Plate plugins={plugins} />;
- removed
optionsprop:
// Before <Plate plugins={plugins} options={options} />; // After // option 1: use the plugin factory let plugins = [ createParagraphPlugin({ type: 'paragraph', }), ]; // option 2: use createPlugins plugins = createPlugins(plugins, { overrideByKey: { [ELEMENT_PARAGRAPH]: { type: 'paragraph', }, }, }); <Plate plugins={plugins} />;
key- replacing
pluginKey - is now required: each plugin needs a key to be retrieved by key.
- replacing
- all handlers have
pluginas a second parameter:
// Before export type X<T = {}> = (editor: PlateEditor<T>) => Y; // After export type X<T = {}, P = {}> = ( editor: PlateEditor<T>, plugin: WithPlatePlugin<T, P> ) => Y;
serializeno longer haselementandleafproperties:
type SerializeHtml = RenderFunction< PlateRenderElementProps | PlateRenderLeafProps >;
Renamed:
injectParentComponenttoinject.aboveComponentinjectChildComponenttoinject.belowComponentoverridePropstoinject.propstransformClassName,transformNodeValue,transformStylefirst parameter is no longereditoras it's provided bythenif needed.- the previously
getOverridePropsis now the core behavior ifinject.propsis defined.
serializetoserializeHtmldeserializetodeserializeHtml- can be an array
- the old deserializer options are merged to
deserializeHtml
type DeserializeHtml = { /** * List of HTML attribute names to store their values in `node.attributes`. */ attributeNames?: string[]; /** * Deserialize an element. * Use this instead of plugin.isElement if you don't want the plugin to renderElement. * @default plugin.isElement */ isElement?: boolean; /** * Deserialize a leaf. * Use this instead of plugin.isLeaf if you don't want the plugin to renderLeaf. * @default plugin.isLeaf */ isLeaf?: boolean; /** * Deserialize html element to slate node. */ getNode?: (element: HTMLElement) => AnyObject | undefined; query?: (element: HTMLElement) => boolean; /** * Deserialize an element: * - if this option (string) is in the element attribute names. * - if this option (object) values match the element attributes. */ validAttribute?: string | { [key: string]: string | string[] }; /** * Valid element `className`. */ validClassName?: string; /** * Valid element `nodeName`. * Set '*' to allow any node name. */ validNodeName?: string | string[]; /** * Valid element style values. * Can be a list of string (only one match is needed). */ validStyle?: Partial< Record<keyof CSSStyleDeclaration, string | string[] | undefined> >; /** * Whether or not to include deserialized children on this node */ withoutChildren?: boolean; };
- handlers starting by
on...are moved tohandlersproperty.
// Before onDrop: handler; // After handlers: { onDrop: handler; }
Removed:
renderElementis favor of:isElementis a boolean that enables element rendering.- the previously
getRenderElementis now the core behavior.
renderLeafis favor of:isLeafis a boolean that enables leaf rendering.- the previously
getRenderLeafis now the core behavior.
inlineTypesandvoidTypesfor:isInlineis a boolean that enables inline rendering.isVoidis a boolean that enables void rendering.
- the following plugins are now part of the core plugins, so you need to remove these from your
pluginsprop:
const corePlugins = [ createReactPlugin(), createHistoryPlugin(), createEventEditorPlugin(), createInlineVoidPlugin(), createInsertDataPlugin(), createDeserializeAstPlugin(), createDeserializeHtmlPlugin(), ];
pluginsis not a parameter anymore as it can be retrieved ineditor.pluginswithInlineVoidis now using pluginsisInlineandisVoidplugin properties.
Renamed:
getPlatePluginTypetogetPluginTypegetEditorOptionstogetPluginsgetPlatePluginOptionstogetPluginpipeOverridePropstopipeInjectPropsgetOverridePropstopluginInjectPropsserializeHTMLFromNodestoserializeHtmlgetLeaftoleafToHtmlgetNodetoelementToHtml
xDeserializerIdtoKEY_DESERIALIZE_XdeserializeHTMLToTexttohtmlTextNodeToStringdeserializeHTMLToMarkstohtmlElementToLeafandpipeDeserializeHtmlLeafdeserializeHTMLToElementtohtmlElementToElementandpipeDeserializeHtmlElementdeserializeHTMLToFragmenttohtmlBodyToFragmentdeserializeHTMLToDocumentFragmenttodeserializeHtmldeserializeHTMLToBreaktohtmlBrToNewLinedeserializeHTMLNodetodeserializeHtmlNodedeserializeHTMLElementtodeserializeHtmlElement
Removed:
usePlateKeys,getPlateKeysusePlateOptionsforgetPlugingetPlateSelectionforgetPlateEditorRef().selectionflatMapByKeygetEditableRenderElementandgetRenderElementforpipeRenderElementandpluginRenderElementgetEditableRenderLeafandgetRenderLeafforpipeRenderLeafandpluginRenderLeafgetInlineTypesgetVoidTypesgetPlatePluginTypesgetPlatePluginWithOverridesmapPlatePluginKeysToOptionswithDeserializeXforPlatePlugin.editor.insertData
Changed types:
PlateEditor:- removed
optionsforpluginsByKey
- removed
WithOverrideis not returning an extended editor anymore (input and output editors are assumed to be the same types for simplicity).PlateState- renamed
keyChangetokeyEditor - removed
pluginsforeditor.plugins - removed
pluginKeys - removed
selectionforeditor.selection - actions:
- removed
setSelection,setPlugins,setPluginKeys - removed
incrementKeyChangefor
- removed
- renamed
Renamed types:
XHTMLYtoXHtmlYDeserializetoDeseralizeHtml
Removed types:
PlatePluginOptions:typetoPlatePlugin.typecomponenttoPlatePlugin.componentdeserializetoPlatePlugin.deserializeHtmlgetNodePropstoPlatePlugin.props.nodePropshotkeytoHotkeyPlugincleartoToggleMarkPlugindefaultTypeis hardcoded top.type
OverridePropsforPlatePlugin.inject.propsSerializeforPlatePlugin.serializeHtmlNodePropsforAnyObjectOnKeyDownElementOptionsforHotkeyPluginOnKeyDownMarkOptionsforToggleMarkPluginWithInlineVoidOptionsGetNodePropsforPlatePluginPropsDeserializeOptions,GetLeafDeserializerOptions,GetElementDeserializerOptions,GetNodeDeserializerOptions,GetNodeDeserializerRule,DeserializeNodeforPlatePlugin.deserializeHtmlPlateOptionsRenderNodeOptionsDeserializedHTMLElement
- removed
- #1234 by @zbeyens – Removed:
getCodeBlockPluginOptionsforgetPlugingetCodeLinePluginOptionsforgetPlugin
- #1234 by @zbeyens – Removed:
getMentionInputPluginOptionsforgetPlugingetMentionInputTypeforgetPluginTypeCOMBOBOX_TRIGGER_MENTION
-
#1234 by @zbeyens – Breaking changes:
- all plugins options are now defined in the plugin itself
- plugins which now have nested plugins instead of array:
createBasicElementsPlugincreateCodeBlockPlugincreateHeadingPlugincreateListPlugincreateTablePlugincreateBasicMarksPlugin
Removed:
createEditorPluginsforcreatePlateEditor(without components) andcreatePlateEditorUI(with Plate components)createPlateOptionsforcreatePlugins- all
DEFAULTS_X: these are defined in the plugins - all
getXDeserialize: these are defined in the plugins - all
WithXOptionsfor extended plugins - all
getXRenderElement - some plugin option types are removed for
PlatePlugin
Renamed:
createPlateComponentstocreatePlateUI- all
getXYhandlers toyX(e.g.getXOnKeyDowntoonKeyDownX) - all
XPluginOptionstoXPlugin - all
pluginKeyparameter tokeyexcept in components
Renamed types:
DecorateSearchHighlightOptionstoFindReplacePlugin
Updated deps:
"slate": "0.70.0""slate-react": "0.70.1"
Removed deps (merged to core):
plate-commonplate-ast-serializerplate-html-serializerplate-serializer
- #1234 by @zbeyens – Renamed:
createDeserializeCSVPlugintocreateDeserializeCsvPlugindeserializeCSVtodeserializeCsv
-
createDeserializeMdPlugin:- is now disabled if there is html data in the data transfer.
Renamed:
createDeserializeMDPlugintocreateDeserializeMdPlugindeserializeMDtodeserializeMd
- renamed:
SPEditortoPEditor(note thatPlateEditoris the new default)SPRenderNodePropstoPlateRenderNodePropsSPRenderElementPropstoPlateRenderElementPropsSPRenderLeafPropstoPlateRenderLeafPropsuseEventEditorIdtousePlateEventIduseStoreEditorOptionstousePlateOptionsuseStoreEditorReftousePlateEditorRefuseStoreEditorSelectiontousePlateSelectionuseStoreEditorStatetousePlateEditorStateuseStoreEditorValuetousePlateValueuseStoreEnabledtousePlateEnableduseStorePlatetousePlatePluginsuseStorePlatePluginKeystousePlateKeysuseStoreStatetousePlateState
getPlateId: Get the last focused editor id, else get the last blurred editor id, else get the first editor id, elsenullgetPlateState:- removed first parameter
state - previously when giving no parameter, it was returning the first editor. Now it's returning the editor with id =
getPlateId(). It meansuseEventEditorId('focus')is no longer needed forusePlateEditorRefusePlateEditorStateusePlateX...
- removed first parameter
setAlign: optionalignrenamed tovalue- removed
getAlignOverrideProps()in favor ofgetOverrideProps(KEY_ALIGN)
- removed
getIndentOverrideProps()in favor ofgetOverrideProps(KEY_INDENT) - rename
onKeyDownHandlertogetIndentOnKeyDown() IndentPluginOptions- rename
typestovalidTypes - rename
cssPropNametostyleKey - rename
transformCssValuetotransformNodeValue
- rename
setLineHeight: optionlineHeightrenamed tovalue- removed
getLineHeightOverridePropsin favor ofgetOverrideProps(KEY_LINE_HEIGHT)
getMentionOnSelectItem:- removed
createMentionNodein favor of plugin options - removed
insertSpaceAfterMentionin favor of plugin options
- removed
MentionComboboxprops:- removed
triggerin favor of plugin options - removed
insertSpaceAfterMentionin favor of plugin options - removed
createMentionNodein favor of plugin options
- removed
- renamed
ToolbarAligntoAlignToolbarButton - renamed
ToolbarCodeBlocktoCodeBlockToolbarButton - renamed
ToolbarElementtoBlockToolbarButton - renamed
ToolbarImagetoImageToolbarButton - renamed
ToolbarLinktoLinkToolbarButton - renamed
ToolbarListtoListToolbarButton - renamed
ToolbarLineHeighttoLineHeightToolbarDropdown - renamed
ToolbarMarktoMarkToolbarButton - renamed
ToolbarMediaEmbedtoMediaEmbedToolbarButton - renamed
ToolbarSearchHighlighttoSearchHighlightToolbar - renamed
ToolbarTabletoTableToolbarButton
The align plugin is no longer wrapping a block, but instead setting an align property to an existing block.
createAlignPlugin:- removed
pluginKeys,renderElementanddeserialize
- removed
- removed:
ELEMENT_ALIGN_LEFTELEMENT_ALIGN_CENTERELEMENT_ALIGN_RIGHTELEMENT_ALIGN_JUSTIFYKEYS_ALIGNin favor ofKEY_ALIGNgetAlignDeserializeupsertAlignin favor ofsetAlign
Migration (normalizer):
- for each node:
- run
parent = getParent(editor, path), ifparent[0].typeis one of the alignment values:- run
setAlign(editor, { align }, { at: path }) - run
unwrapNodes(editor, { at: path })
- run
- run
ToolbarAlignProps:- removed
typein favor ofalign - removed
unwrapTypes - added
align
- removed
The mention plugin is now using the combobox.
- removed
useMentionPluginin favor ofcreateMentionPlugin- migration: replace
useMentionPlugin().pluginbycreateMentionPlugin()
- migration: replace
- removed options:
mentionableSearchPatterninsertSpaceAfterMentionmaxSuggestions: moved tocomboboxStoretrigger: moved tocomboboxStorementionables: moved toitemsincomboboxStorementionableFilter: moved tofilterincomboboxStore
- removed
matchesTriggerAndPatternin favor ofgetTextFromTrigger - removed
MentionNodeDatain favor ofComboboxItemData
export interface ComboboxItemData {
/**
* Unique key.
*/
key: string;
/**
* Item text.
*/
text: any;
/**
* Whether the item is disabled.
* @default false
*/
disabled?: boolean;
/**
* Data available to `onRenderItem`.
*/
data?: unknown;
}- removed
MentionSelectin favor ofMentionCombobox
- removed
setPositionAtSelectionin favor ofuseBalloonToolbarPopper - removed
useBalloonMovein favor ofuseBalloonToolbarPopper - removed
usePopupPositionin favor ofuseBalloonToolbarPopper - removed
useBalloonShowin favor ofuseBalloonToolbarPopperBalloonToolbarprops: - removed
directionin favor ofpopperOptions.placement - renamed
scrollContainertopopperContainer
BalloonToolbar: removedhiddenDelayprop.
There was multiple instances of styled-components across all the packages.
So we moved styled-components from dependencies to peer dependencies.
styled-components was not listed in your dependencies
Add styled-components to your dependencies
autoformatBlock:- signatude changed
// Before
(
editor: TEditor,
type: string,
at: Location,
options: Pick<AutoformatRule, 'preFormat' | 'format'>
)// After
(editor: TEditor, options: AutoformatBlockOptions)- moved the checks from
withAutoformat autoformatInline:- renamed to
autoformatMark - signatured changed
- renamed to
// Before
(
editor: TEditor,
options: Pick<AutoformatRule, 'type' | 'between' | 'markup' | 'ignoreTrim'>
) // After
(
editor: TEditor,
options: AutoformatMarkOptions
) AutoformatRuleis nowAutoformatBlockRule | AutoformatMarkRule | AutoformatTextRule;mode: 'inline'renamed tomode: 'mark'markupandbetweenhave been replaced bymatch: string | string[] | MatchRange | MatchRange[]: The rule applies when the trigger and the text just before the cursor matches. Formode: 'block': lookup for the end match(es) before the cursor. Formode: 'text': lookup for the end match(es) before the cursor. Ifformatis an array, also lookup for the start match(es). Formode: 'mark': lookup for the start and end matches. Note:'_*',['_*']and{ start: '_*', end: '*_' }are equivalent.triggernow defaults to the last character ofmatchormatch.end(previously' ')
- the plugin now checks that there is no character before the start match to apply autoformatting. For example, nothing will happen by typing
a*text*.