- #4025 by @e1himself – Fixed props injection match check for elements
-
#4001 by @martin-keeper – Add new param to HTML deserializer for changing the default element
-
#4002 by @zbeyens – Pass plugin context to
plugin.node.props
- #3986 by @felixfeng33 – Fix lodash import
-
#3974 by @felixfeng33 – fix import html
-
#3974 by @felixfeng33 – Remove useless html parser.
-
#3943 by @felixfeng33 –
editor.api.html.deserialize
: Support deserialization from PlateStatic.New:
getEditorDOMFromHtmlString
returns the editor element in html string (the one withdata-slate-editor="true"
).New utilities for checking Slate nodes in HTML:
isSlateVoid
: Check if an HTML element is a Slate void nodeisSlateElement
: Check if an HTML element is a Slate element nodeisSlateString
: Check if an HTML element is a Slate string nodeisSlateLeaf
: Check if an HTML element is a Slate leaf nodeisSlateNode
: Check if an HTML element is any type of Slate nodeisSlatePluginElement
: Check if an HTML element is a Slate element node with a specific plugin keyisSlatePluginNode
: Check if an HTML element has a specific plugin key classgetSlateElements
: Get all Slate element nodes in an HTML element
- #3952 by @zbeyens –
- Fix
tf.reset
missingoptions
argument. Fixes editor reset on select all > backspace usingResetNodePlugin
. PlateStatic
element and leaf rendering is now memoized withReact.memo
so you can safely updateeditor.children
. For elements, it compares theelement
reference orelement._memo
value. The latter can be used to memoize based on the markdown string instead of theelement
reference. For example,deserializeMd
withmemoize: true
will setelement._memo
for that purpose.
- Fix
-
-
Plugin
normalizeInitialValue
now returnsvoid
instead ofValue
. When mutating nodes, keep their references (e.g., useObject.assign
instead of spread). -
Editor methods have moved to
editor.tf
andeditor.api
. They still exist at the top level for slate backward compatibility, but are no longer redundantly typed. If you truly need the top-level method types, extend your editor type withLegacyEditorMethods
(e.g.editor as Editor & LegacyEditorMethods
). Since these methods can be overridden byextendEditor
,with...
, or slate plugins, consider migrating to the following approaches:// For overriding existing methods only: overrideEditor(({ editor, tf: { deleteForward }, api: { isInline } }) => ({ transforms: { deleteForward(options) { // ...conditional override deleteForward(options); }, }, api: { isInline(element) { // ...conditional override return isInline(element); }, }, }));
This was previously done in
extendEditor
using top-level methods, which still works but now throws a type error due to the move toeditor.tf/editor.api
. A workaround is to extend your editor withLegacyEditorMethods
.Why? Having all methods at the top-level (next to
children
,marks
, etc.) would clutter the editor interface. Slate splits transforms in three places (editor
,Editor
, andTransforms
), which is also confusing. We've reorganized them intotf
andapi
for better DX, but also to support transform-only middlewares in the future. This also lets us leverageextendEditorTransforms
,extendEditorApi
, andoverrideEditor
to modify those methods.Migration example:
// From: export const withInlineVoid: ExtendEditor = ({ editor }) => { const { isInline, isSelectable, isVoid, markableVoid } = editor; const voidTypes: string[] = []; const inlineTypes: string[] = []; editor.pluginList.forEach((plugin) => { if (plugin.node.isInline) { inlineTypes.push(plugin.node.type); } if (plugin.node.isVoid) { voidTypes.push(plugin.node.type); } }); editor.isInline = (element) => { return inlineTypes.includes(element.type as any) ? true : isInline(element); }; editor.isVoid = (element) => { return voidTypes.includes(element.type as any) ? true : isVoid(element); }; return editor; }; export const InlineVoidPlugin = createSlatePlugin({ key: 'inlineVoid', extendEditor: withInlineVoid, }); // After (using overrideEditor since we're only overriding existing methods): export const withInlineVoid: OverrideEditor = ({ api: { isInline, isSelectable, isVoid, markableVoid }, editor, }) => { const voidTypes: string[] = []; const inlineTypes: string[] = []; editor.pluginList.forEach((plugin) => { if (plugin.node.isInline) { inlineTypes.push(plugin.node.type); } if (plugin.node.isVoid) { voidTypes.push(plugin.node.type); } }); return { api: { isInline(element) { return inlineTypes.includes(element.type as any) ? true : isInline(element); }, isVoid(element) { return voidTypes.includes(element.type as any) ? true : isVoid(element); }, }, }; }; export const InlineVoidPlugin = createSlatePlugin({ key: 'inlineVoid', }).overrideEditor(withInlineVoid);
- Move
editor.redecorate
toeditor.api.redecorate
Types:
- Rename
TRenderElementProps
toRenderElementProps
- Rename
TRenderLeafProps
toRenderLeafProps
- Rename
TEditableProps
toEditableProps
-
-
- Import the following from
@udecode/plate-core/react
(or@udecode/plate/react
) instead ofslate-react
:RenderPlaceholderProps
,DefaultElement
,DefaultPlaceholder
,Editable
,Slate
,useComposing
,useFocused
,useReadOnly
,useSelected
,withReact
. useNodePath
is now memoized: it will re-render only when the actual path changes (PathApi.equals
). This includesusePath
andpath
element prop.- New hook
useElementSelector(([node, path]) => selector(node, path), deps, { equalityFn, key })
: re-render only when the selector result changes. We highly recommend using this hook over useElement(key) when subscribing to an ancestor element (e.g. table element from a cell element). For example, subscribe to the row size from a cell element without affecting the re-rendering of all row cells:
const rowSize = useElementSelector(([node]) => node.size, [], { key: TableRowPlugin.key, });
- Added a new plugin attribute:
SlatePlugin.node.isSelectable
. If set tofalse
, the node cannot be selected. - The plugin context
tf
andapi
now includeEditor
methods.
- Import the following from
- #3932 by @felixfeng33 – Each
PlateElement
andSlateElement
comes with a defaultposition: relative
style. Removerelative
className from all components
- #3878 by @zbeyens –
- Add
useNodePath(node: TNode)
: memoizedfindPath
(useMemo
) - Add
usePath(pluginKey?: string)
: memoizedfindPath
(context) PlateElementProps
now includespath
prop, also accessible usingusePath
- Add
-
#3830 by @felixfeng33 – ## @udecode/plate-core@40.1.0
- #3744 by @zbeyens –
- Add
PlateStatic
,SlateElement
,SlateLeaf
components for static rendering and server-side HTML serialization - Add
serializeHtml
function to serialize editor content to HTML. Deprecating@udecode/plate-html
in favor of core serialization. - Move from
PlatePlugin
(/react
) toBasePlugin
(/
):node.component
,render.aboveEditable
,render.aboveSlate
,render.node
- Add to
SlatePlugin
:node.props
,render.aboveNodes
,render.belowNodes
,render.afterEditable
,render.beforeEditable
,render.node
- Add
- #3744 by @zbeyens –
- #3809 by @zbeyens –
PlateContent
new prop -autoFocusOnEditable
: Autofocus when it becomes editable (readOnly false -> readOnly true)
0682bb02329d6cf09d96fdf9a226e85925b8ce54
by @zbeyens – Fix scrollRef
- #3759 by @zbeyens –
- Add
scrollRef
in Plate store - Add
useEditorScrollRef
to get the scroll container ref, that can be used in plugins to control the scroll position
- Add
195163e6e3d612c1d016112b982e9d49213efb3d
by @zbeyens –Plate
store: addcontainerRef
. This is used by some plugins likeCursorOverlay
.- Add
useEditorContainerRef
selector hook. You can pass the returned ref to your editor scroll container. usePlateEditor
options:value
can now be a callback function to get the value from the editoreditor.key
is now usingnanoid()
editor.uid
: new property added byPlate
to uniquely identify the editor. The difference witheditor.key
is thatuid
supports SSR hydration. This can be passed to the editor container asid
prop.render.aboveNodes
andrender.belowNodes
now supportuseElement
PlatePlugin.inject
new properties:excludePlugins?: string[]
excludeBelowPlugins?: string[]
maxLevel?: number
isLeaf?: boolean
isBlock?: boolean
isElement?: boolean
- Add
getInjectMatch(editor, plugin)
to get a plugin inject match function.
- #3744 by @zbeyens –
Plate
now warns if multiple instances of@udecode/plate-core
are detected. UsesuppressInstanceWarning
to suppress the warning.
- #3675 by @felixfeng33 – Fix
DefaultLeaf
missing style attribute
- #3469 by @felixfeng33 – Fix
DefaultLeaf
andDefaultElement
props
86487a3357dbe6005a0b4e37c2510c97f2ad4d96
by @zbeyens – Remove debug warning on missing plugin
a17b84f1aa09ac5bcc019823b5d0dfea581ada57
by @zbeyens – Use slate-history fork
-
#3616 by @zbeyens –
PlateContent
:- When
disabled=true
,readOnly
should betrue
- Add prop
aria-disabled=true
anddata-readonly=true
whenreadOnly=true
- Add class
slate-editor
,ignore-click-outside/toolbar
(used by floating toolbar)
- When
-
d30471cb19577e53c20944ab66eab2a7ef3b3ad2
by @12joan – Mitigate XSS inelement.attributes
by requiring all attribute names to be allowlisted in thenode.dangerouslyAllowAttributes
plugin configuration option.Migration:
For each plugin that needs to support passing DOM attributes using
element.attributes
, add the list of allowed attributes to thenode.dangerouslyAllowAttributes
option of the plugin.const ImagePlugin = createPlatePlugin({ key: 'image', node: { isElement: true, isVoid: true, dangerouslyAllowAttributes: ['alt'], }, });
To modify existing plugins, use the
extend
method as follows:const MyImagePlugin = ImagePlugin.extend({ node: { dangerouslyAllowAttributes: ['alt'], }, });
WARNING: Improper use of
dangerouslyAllowAttributes
WILL make your application vulnerable to cross-site scripting (XSS) or information exposure attacks. Ensure you carefully research the security implications of any attribute before adding it. For example, thesrc
andhref
attributes will allow attackers to execute arbitrary code, and thestyle
andbackground
attributes will allow attackers to leak users' IP addresses.
- #3526 by @zbeyens –
- Rename all base plugins that have a React plugin counterpart to be prefixed with
Base
. This change improves clarity and distinguishes base implementations from potential React extensions. Use base plugins only for server-side environments or to extend your own DOM layer. - Import the following plugins from
/react
entry:AlignPlugin
,CalloutPlugin
,EquationPlugin
,FontBackgroundColorPlugin
,FontColorPlugin
,FontFamilyPlugin
,FontSizePlugin
,FontWeightPlugin
,InlineEquationPlugin
,LineHeightPlugin
,TextIndentPlugin
,TocPlugin
- Upgrade dependencies
- Rename all base plugins that have a React plugin counterpart to be prefixed with
-
- Change
plugin.options
merging behavior from deep merge to shallow merge. - This affects
.extend()
,.configure()
, and other methods that modify plugin options. - This update addresses a performance regression introduced in v37 that affected editor creation.
Before:
const plugin = createSlatePlugin({ key: 'test', options: { nested: { a: 1 } }, }).extend({ options: { nested: { b: 1 } }, }); // Result: { nested: { a: 1, b: 1 } }
After:
const plugin = createSlatePlugin({ key: 'test', options: { nested: { a: 1 } }, }).extend(({ getOptions }) => ({ options: { ...getOptions(), nested: { ...getOptions().nested, b: 1 }, }, })); // Result: { nested: { a: 1, b: 1 } }
Migration:
- If you're using nested options and want to preserve the previous behavior, you need to manually spread both the top-level options and the nested objects.
- If you're not using nested options, no changes are required.
- Change
- #3512 by @zbeyens –
- Add
editor.tf.setValue
to replace the editor value - Fix: move
editor.api.reset
toeditor.tf.reset
- Add
e9f1bbaeaf6e4c38372f7dd8427c20e1d8eec6e6
by @zbeyens – Addid?: string
inuseEditorPlugin
params
fd8ba6260022cfdc3ac370ad9e49cbeb2896fb71
by @zbeyens – Add id?: string in useEditorPlugin param
- #3495 by @zbeyens – Add string value support for
createSlateEditor
,createPlateEditor
,usePlateEditor
-
#3420 by @zbeyens – Plugin System:
Decoupling React in all packages:
- Split build into
@udecode/plate-core
and@udecode/plate-core/react
- NEW
SlatePlugin
as the foundation for all plugins PlatePlugin
extendsSlatePlugin
with React-specific plugin features
Plugin Creation:
- Remove
createPluginFactory
- NEW
createSlatePlugin
: vanilla - NEW
createTSlatePlugin
: vanilla explicitly typed - NEW
createPlatePlugin
: React - NEW
createTPlatePlugin
: React explicitly typed - NEW
toPlatePlugin
: extend a vanilla plugin into a React plugin - NEW
toTPlatePlugin
: extend a vanilla plugin into a React plugin explicitly typed - Rename all plugins starting with
createNamePlugin()
toNamePlugin
Before:
const MyPluginFactory = createPluginFactory({ key: 'myPlugin', isElement: true, component: MyComponent, }); const plugin = MyPluginFactory();
After:
const plugin = createSlatePlugin({ key: 'myPlugin', node: { isElement: true, component: MyComponent, }, }); const reactPlugin = toPlatePlugin(plugin);
Plugin Configuration:
- Remove all
NamePlugin
option types, useNameConfig
instead. NameConfig
as the new naming convention for plugin configurations.
Before:
createPluginFactory<HotkeyPlugin>({ handlers: { onKeyDown: onKeyDownToggleElement, }, options: { hotkey: ['mod+opt+0', 'mod+shift+0'], }, });
After:
export const ParagraphPlugin = createPlatePlugin({ key: 'p', node: { isElement: true }, }).extend({ editor, type }) => ({ shortcuts: { toggleParagraph: { handler: () => { editor.tf.toggle.block({ type }); }, keys: [ [Key.Mod, Key.Alt, '0'], [Key.Mod, Key.Shift, '0'], ], preventDefault: true, }, }, })
toggleParagraph
is now a shortcut foreditor.tf.toggle.block({ type: 'p' })
for the given keys- Multiple shortcuts can be defined per plugin, and any shortcut can be disabled by setting
shortcuts.toggleParagraph = null
- Note the typing support using
Key
Plugin Properties:
Rename
SlatePlugin
/PlatePlugin
properties:type
->node.type
isElement
->node.isElement
isLeaf
->node.isLeaf
isInline
->node.isInline
isMarkableVoid
->node.isMarkableVoid
isVoid
->node.isVoid
component
->node.component
orrender.node
props
->node.props
overrideByKey
->override.plugins
renderAboveEditable
->render.aboveEditable
renderAboveSlate
->render.aboveSlate
renderAfterEditable
->render.afterEditable
renderBeforeEditable
->render.beforeEditable
inject.props
->inject.nodeProps
inject.props.validTypes
->inject.targetPlugins
inject.aboveComponent
->render.aboveNodes
inject.belowComponent
->render.belowNodes
inject.pluginsByKey
->inject.plugins
editor.insertData
->parser
- NEW
parser.format
now supportsstring[]
- NEW
parser.mimeTypes: string[]
- NEW
deserializeHtml
->parsers.html.deserializer
deserializeHtml.getNode
->parsers.html.deserializer.parse
serializeHtml
->parsers.htmlReact.serializer
withOverride
->extendEditor
- All methods now have a single parameter:
SlatePluginContext<C>
orPlatePluginContext<C>
, in addition to the method specific options. Some of the affected methods are:decorate
handlers
, includingonChange
. Returns({ event, ...ctx }) => void
instead of(editor, plugin) => (event) => void
handlers.onChange
:({ value, ...ctx }) => void
instead of(editor, plugin) => (value) => void
normalizeInitialValue
editor.insertData.preInsert
editor.insertData.transformData
editor.insertData.transformFragment
deserializeHtml.getNode
deserializeHtml.query
inject.props.query
inject.props.transformProps
useHooks
withOverrides
NEW
SlatePlugin
properties:api
: API methods provided by this plugindependencies
: An array of plugin keys that this plugin depends onnode
: Node-specific configuration for this pluginparsers
: Now acceptstring
keys to add custom parserspriority
: Plugin priority for registration and execution ordershortcuts
: Plugin-specific hotkeysinject.targetPluginToInject
: Function to inject plugin config into other plugins specified byinject.targetPlugins
Before:
export const createAlignPlugin = createPluginFactory({ key: KEY_ALIGN, inject: { props: { defaultNodeValue: 'start', nodeKey: KEY_ALIGN, styleKey: 'textAlign', validNodeValues: ['start', 'left', 'center', 'right', 'end', 'justify'], validTypes: ['p'], }, }, then: (_, plugin) => mapInjectPropsToPlugin(editor, plugin, { deserializeHtml: { getNode: (el, node) => { if (el.style.textAlign) { node[plugin.key] = el.style.textAlign; } }, }, }), });
After:
export const AlignPlugin = createSlatePlugin({ inject: { nodeProps: { defaultNodeValue: 'start', nodeKey: 'align', styleKey: 'textAlign', validNodeValues: ['start', 'left', 'center', 'right', 'end', 'justify'], }, targetPluginToInject: ({ editor, plugin }) => ({ parsers: { html: { deserializer: { parse: ({ element, node }) => { if (element.style.textAlign) { node[editor.getType(plugin)] = element.style.textAlign; } }, }, }, }, }), targetPlugins: [ParagraphPlugin.key], }, key: 'align', });
Plugin Shortcuts:
- NEW
shortcuts
to add custom hotkeys to a plugin. - Remove
hotkey
option from all plugins
Before:
type LinkPlugin = { hotkey?: string; };
After:
type LinkConfig = PluginConfig< // key 'p', // options { defaultLinkAttributes?: any }, // api { link: { getAttributes: (editor: PlateEditor) => LinkAttributes } }, // transforms { floatingLink: { hide: () => void } } >;
Shortcuts API:
handler
is called with the editor, event, and event details.keys
is an array of keys to trigger the shortcut.priority
is the priority of the shortcut over other shortcuts....HotkeysOptions
from@udecode/react-hotkeys
Plugin Types:
- Update
SlatePlugin
,PlatePlugin
generics.P, V, E
->C extends AnyPluginConfig = PluginConfig
- Remove
PluginOptions
- Remove
PlatePluginKey
- Remove
HotkeyPlugin
,ToggleMarkPlugin
in favor ofplugin.shortcuts
WithPlatePlugin
->EditorPlugin
,EditorPlatePlugin
PlatePluginComponent
->NodeComponent
InjectComponent*
->NodeWrapperComponent*
PlatePluginInsertData
->Parser
PlatePluginProps
->NodeProps
RenderAfterEditable
->EditableSiblingComponent
WithOverride
->ExtendEditor
SerializeHtml
->HtmlReactSerializer
Plugin Store:
- NEW each plugin has its own store, accessible via
plugin.optionsStore
andplugin.useOptionsStore
editor
has many methods to get, set and subscribe to plugin options
Plugin Methods:
- All plugin methods return a new plugin instance with the extended types.
- Remove
then
, useextend
instead - NEW
extend
method to deep merge a plugin configuration- If you pass an object, it will be directly merged with the plugin config.
- If you pass a function, it will be called with the plugin config once the editor is resolved and should return the new plugin config.
- Object extensions always have the priority over function extensions.
- Extend multiple times to derive from the result of the previous extension.
- NEW
configure
method to configure the properties of existing plugins. The difference withextend
is thatconfigure
with not add new properties to the plugin, it will only modify existing ones. - NEW
extendPlugin
method to extend a nested plugin configuration. - NEW
configurePlugin
method to configure the properties of a nested plugin. - NEW
extendApi
method to extend the plugin API. The API is then merged intoeditor.api[plugin.key]
. - NEW
extendTransforms
method to extend the plugin transforms. The transforms is then merged intoeditor.transforms[plugin.key]
. - NEW
extendEditorApi
method to extend the editor API. The API is then merged intoeditor.api
. Use this to add or override top-level methods to the editor. - NEW
extendEditorTransforms
method to extend the editor transforms. The transforms is then merged intoeditor.transforms
. - NEW
extendOptions
method to extend the plugin options with selectors. Useeditor.useOption(plugin, 'optionKey')
to subscribe to an (extended) option. - NEW
withComponent
to replaceplugin.node.component
Plugin Context
Each plugin method now receive the plugin context created with
getEditorPlugin(editor, plugin)
as parameter:api
editor
getOption
getOptions
plugin
setOption
setOptions
tf
type
useOption
Core Plugins:
- NEW
ParagraphPlugin
is now part ofcore
- NEW
DebugPlugin
is now part ofcore
- NEW
api.debug.log
,api.debug.info
,api.debug.warn
,api.debug.error
methods options.isProduction
to control logging in production environmentsoptions.logLevel
to set the minimum log leveloptions.logger
to customize logging behavioroptions.throwErrors
to control error throwing behavior, by default aPlateError
will be thrown onapi.debug.error
- NEW
- NEW - You can now override a core plugin by adding it to
editor.plugins
. Last plugin wins. createDeserializeHtmlPlugin
->HtmlPlugin
- NEW
api.html.deserialize
- NEW
createEventEditorPlugin
->EventEditorPlugin
eventEditorStore
->EventEditorStore
createDeserializeAstPlugin
->AstPlugin
createEditorProtocolPlugin
->SlateNextPlugin
- NEW
editor.tf.toggle.block
- NEW
editor.tf.toggle.mark
- Remove
createNodeFactoryPlugin
, included inSlateNextPlugin
. - Remove
createPrevSelectionPlugin
, included inSlateNextPlugin
.
- NEW
createHistoryPlugin
->HistoryPlugin
createInlineVoidPlugin
->InlineVoidPlugin
createInsertDataPlugin
->ParserPlugin
createLengthPlugin
->LengthPlugin
createReactPlugin
->ReactPlugin
Editor Creation:
NEW
withSlate
:- Extends an editor into a vanilla Plate editor
- NEW
rootPlugin
option for configuring the root plugin
NEW
withPlate
:- Extends an editor into a React Plate editor
- Now extends
withSlate
with React-specific enhancements - NEW
useOptions
anduseOption
methods to the editor
NEW
createSlateEditor
:- Create a vanilla Plate editor with server-side support
createPlateEditor
:-
Plugin replacement mechanism: using
plugins
, any plugin with the same key that a previous plugin will replace it. That means you can now override core plugins that way, likeReactPlugin
-
root
plugin is now created fromcreatePlateEditor
option as a quicker way to configure the editor than passingplugins
. Since plugins can have nested plugins (think as a recursive tree),plugins
option will be passed toroot
pluginplugins
option. -
Centralized editor resolution. Before, both
createPlateEditor
andPlate
component were resolving the editor. Now, onlycreatePlateEditor
takes care of that. That meansid
,value
, and other options are now controlled bycreatePlateEditor
. -
Remove
createPlugins
, pass plugins directly:components
->override.components
overrideByKey
->override.plugins
createPlateEditor
options:- Rename
normalizeInitialValue
option toshouldNormalizeEditor
- Move
components
tooverride.components
to override components by key - Move
overrideByKey
tooverride.plugins
to override plugins by key - Remove
disableCorePlugins
, useoverride.enabled
instead - NEW
value
to set the initial value of the editor. - NEW
autoSelect?: 'end' | 'start' | boolean
to auto select the start of end of the editor. This is decoupled fromautoFocus
. - NEW
selection
to control the initial selection. - NEW
override.enabled
to disable plugins by key - NEW
rootPlugin?: (plugin: AnyPlatePlugin) => AnyPlatePlugin
to configure the root plugin. From here, you can for example callconfigurePlugin
to configure any plugin. - NEW
api
,decorate
,extendEditor
,handlers
,inject
,normalizeInitialValue
,options
,override
,priority
,render
,shortcuts
,transforms
,useHooks
. These options will be passed to the very firstrootPlugin
.
NEW
usePlateEditor()
hook to create aPlateEditor
in a React component:- Uses
createPlateEditor
anduseMemo
to avoid re-creating the editor on every render. - Dependencies can be added to the hook to re-create the editor on demand.
id
option is always used as dependency.
Editor Methods:
editor: PlateEditor
:- Move
redecorate
toeditor.api.redecorate
- Move
reset
toeditor.tf.reset
- Move
plate.set
toeditor.setPlateState
- Move
blockFactory
toeditor.api.create.block
- Move
childrenFactory
toeditor.api.create.value
- Rename
plugins
topluginList
- Rename
pluginsByKey
toplugins
- NEW
getApi()
to get the editor API - NEW
getTransforms()
to get the editor transforms - Remove
getPlugin(editor, key)
, useeditor.getPlugin(plugin) or editor.getPlugin({ key })
- Remove
getPluginType
, useeditor.getType(plugin)
to get node type - Remove
getPluginInjectProps(editor, key)
, useeditor.getPlugin(plugin).inject.props
- NEW
getOptionsStore()
to get a plugin options store - Remove
getPluginOptions
, usegetOptions()
- NEW
getOption()
to get a plugin option - NEW
setOption()
to set a plugin option - NEW
setOptions()
to set multiple plugin options. Pass a function to use Immer. Pass an object to merge the options. - NEW
useOption
to subscribe to a plugin option in a React component - NEW
useOptions
to subscribe to a plugin options store in a React component - Remove
getPlugins
, useeditor.pluginList
- Remove
getPluginsByKey
, useeditor.plugins
- Remove
mapInjectPropsToPlugin
Editor Types:
The new generic types are:
V extends Value = Value
,P extends AnyPluginConfig = PlateCorePlugin
- That means this function will infer all plugin configurations from the options passed to it:
key
options
api
transforms
- Can't infer for some reason? Use
createTPlateEditor
for explicit typing.
const editor = createPlateEditor({ plugins: [TablePlugin] }); editor.api.htmlReact.serialize(); // core plugin is automatically inferred editor.tf.insert.tableRow(); // table plugin is automatically inferred
Plate Component
PlateProps
:editor
is now required. Ifnull
,Plate
will not render anything. As before,Plate
remounts onid
change.- Remove
id
,plugins
,maxLength
, pass these tocreatePlateEditor
instead - Remove
initialValue
,value
, passvalue
tocreatePlateEditor
instead - Remove
editorRef
- Remove
disableCorePlugins
, overrideplugins
increatePlateEditor
instead
Utils:
- Remove
useReplaceEditor
sinceeditor
is now always controlled - NEW
useEditorPlugin
to get the editor and the plugin context.
Types:
PlateRenderElementProps
,PlateRenderLeafProps
generics:V, N
->N, C
Plate Store:
- Remove
plugins
andrawPlugins
, useuseEditorRef().plugins
instead, or listen to plugin changes witheditor.useOption(plugin, <optionKey>)
- Remove
value
, useuseEditorValue()
instead - Remove
editorRef
, useuseEditorRef()
instead
Miscellaneous Changes
slate >=0.103.0
peer dependencyslate-react >=0.108.0
peer dependency- New dependency
@udecode/react-hotkeys
- Remove
ELEMENT_
,MARK_
andKEY_
constants. UseNamePlugin.key
instead. - Replace
ELEMENT_DEFAULT
withParagraphPlugin.key
. - Remove
getTEditor
- Rename
withTReact
towithPlateReact
- Rename
withTHistory
towithPlateHistory
- Rename
usePlateId
touseEditorId
- Remove
usePlateSelectors().id()
,usePlateSelectors().value()
,usePlateSelectors().plugins()
, use insteaduseEditorRef().<key>
- Rename
toggleNodeType
totoggleBlock
toggleBlock
options:- Rename
activeType
totype
- Rename
inactiveType
todefaultType
- Rename
- Remove
react-hotkeys-hook
re-exports. Use@udecode/react-hotkeys
instead.
Types:
- Move
TEditableProps
,TRenderElementProps
to@udecode/slate-react
- Remove
<V extends Value>
generic in all functions where not used - Remove
PlatePluginKey
- Remove
OverrideByKey
- Remove
PlateId
- Split build into
- #3418 by @beeant0512 – fix cannot copy a row/column format from table
b74fc734be04266af0e147b7f7e78cc39ccbc98e
by @zbeyens – Fix rsc: remove useFocusEditorEvents from server bundle
- #3346 by @yf-yang – feat: expose onValueChange and onSelectionChange from Slate component, following ianstormtaylor/slate#5526
- #3241 by @felixfeng33 – Fix:
toggleNodeType
not working usingat
.
- #3194 by @KorovinQuantori – Export plugin keys for easier access plugin options by key
- #3125 by @zbeyens –
- Use
editor.reset
instead ofresetEditor
to focus the editor after reset so it's decoupled fromslate-react
. - Add a server bundle including
createPlateEditor
. It can be imported usingimport { createPlateEditor } from '@udecode/plate-core/server'
.
- Use
- #3155 by @felixfeng33 – Export
KeyboardEventHandler
type
- #2881 by @johnrazeur – fix plate store id when plate use the editor prop.
-
- Introduce
PlateController
as a way of accessing the active editor from an ancestor or sibling ofPlate
(see Accessing the Editor). - Add
primary
prop toPlate
(default true) - Add
isFallback
toeditor
instance (default false) - The following hooks now throw a runtime error when used outside of either a
Plate
orPlateController
, and accept adebugHookName
option to customize this error message:useIncrementVersion
useRedecorate
useReplaceEditor
useEditorMounted
(new)useEditorReadOnly
useEditorRef
useEdtiorSelection
useEditorSelector
useEditorState
useEditorVersion
useSelectionVersion
- Change the default
id
of aPlate
editor from'plate'
to a random value generated withnanoid/non-secure
- Introduce
- #2854 by @MarcosPereira1 – Ensure that beforeinput event is handled as a React.SyntheticEvent rather than a native DOM event
822f6f56b
by @12joan –- Upgrade to
jotai-x@1.1.0
- Add
useEditorSelector
hook to only re-render when a specific property ofeditor
changes - Remove
{ fn: ... }
workaround for jotai stores that contain functions - Breaking change:
usePlateSelectors
,usePlateActions
andusePlateStates
no longer accept generic type arguments. If custom types are required, cast the resulting values at the point of use, or use hooks likeuseEditorRef
that still provide generics. - Fix:
readOnly
on Plate store defaults to false and overridesreadOnly
on PlateContent - Fix: Plate ignores plugins passed via
editor
- Upgrade to
- #2814 by @12joan –
- Fix
renderBeforeEditable
andrenderAfterEditable
- Like
renderAboveEditable
andrenderAboveSlate
, the given component is now rendered using JSX syntax, separately from the parent component.
- Like
- Fix
- #2763 by @12joan –
- Migrate store from
jotai@1
tojotai@2
- New dependency:
jotai-x
. See https://github.com/udecode/jotai-x - Accessing a store without an explicit provider component is no longer supported. Attempting to do so will result in a warning in the console:
Tried to access jotai store '${storeName}' outside of a matching provider.
- New dependency:
- Upgraded from
zustand@3
tozustand@4
- Rename
zustand-x
exportsStateActions
->ZustandStateActions
StoreApi
->ZustandStoreApi
createStore
->createZustandStore
- Note that these exports are deprecated and should not be used in new code. They may be removed in a future version of Plate.
renderAboveEditable
andrenderAboveSlate
- The given component is now rendered using JSX syntax, separately from the parent component. Previously, the component was called as a function, which affected how hooks were handled by React.
withHOC
- Add support for
ref
prop, which is forwarded to the inner component - Add
hocRef
argument, which is forwarded to theHOC
- Strengthen the type of
hocProps
- Add support for
- Migrate store from
- #2729 by @12joan – This is a breaking change meant to be part of v25, hence the patch.
On
deserializeHtml
, replacestripWhitespace
withcollapseWhiteSpace
, defaulting to true. ThecollapseWhiteSpace
option aims to parse white space in HTML according to the HTML specification, ensuring greater accuracy when pasting HTML from browsers.
- #2635 by @zbeyens –
- Fix: set Plate
id
prop type tostring
to satisfy HTML specs.
- Fix: set Plate
-
- [Breaking] Rename
Plate
toPlateContent
. - [Breaking] Rename
PlateProvider
toPlate
. - [Breaking] Rendering
PlateContent
is now required inPlate
. This allows you to choose where to render the editor next to other components like toolbar. Example:
// Before <Plate /> // or <PlateProvider> <Plate /> </PlateProvider> // After <Plate> <PlateContent /> </Plate>
- [Breaking] Remove provider props such as
plugins
fromPlateContent
. These props should be passed toPlate
. - [Breaking] Remove
editableProps
prop fromPlateContent
. Move these asPlateContent
props. - [Breaking] Remove
children
prop fromPlateContent
. Render instead these components afterPlateContent
. - [Breaking] Remove
firstChildren
prop fromPlateContent
. Render instead these components beforePlateContent
. - [Breaking] Remove
editableRef
prop fromPlateContent
. Useref
instead. - [Breaking] Remove
withPlateProvider
. - [Breaking] Rename
usePlateEditorRef
touseEditorRef
. - [Breaking] Rename
usePlateEditorState
touseEditorState
. - [Breaking] Rename
usePlateReadOnly
touseEditorReadOnly
. This hook can be used belowPlate
whileuseReadOnly
can only be used in node components. - [Breaking] Rename
usePlateSelection
touseEditorSelection
. - [Breaking] Rename store attributes
keyDecorate
,keyEditor
andkeySelection
toversionDecorate
,versionEditor
andversionSelection
. These are now numbers incremented on each change. - [Breaking] Rename store attribute
isRendered
toisMounted
. - Add
maxLength
prop toPlate
. Specifies the maximum number of characters allowed in the editor. This is a new core plugin (createLengthPlugin
). - Add
useEditorVersion
hook. Version incremented on each editor change. - Add
useSelectionVersion
hook. Version incremented on each selection change. - Fix
editor.reset
should now reset the editor without mutating the ref so it does not remountPlateContent
. Default is usingresetEditor
. If you need to replace the editor ref, useuseReplaceEditor
. - [Type] Remove generic from
TEditableProps
,RenderElementFn
,RenderAfterEditable
- [Breaking] Rename
- #2588 by @zbeyens –
PlatePlugin
inject.props.query
(new): Whether to inject the props. If true, overrides all other checks.inject.props.transformProps
(new): Transform the injected props.
- #2464 by @12joan –
- Add
editorRef
prop to Plate/PlateProvider- Works with
useRef<PlateEditor | null>
oruseState<PlateEditor | null>
- The editor instance is passed to the ref on mount and whenever the editor is reset
- The ref is set to
null
when the editor unmounts
- Works with
- Add various new methods to
editor
:editor.reset()
- Equivalent touseResetPlateEditor()()
editor.redecorate()
- Equivalent touseRedecorate()()
editor.plate.<key>.set(value)
- Sets the value of<key>
in the Plate store. The following keys are currently supported:- readOnly
- plugins
- onChange
- decorate
- renderElement
- renderLeaf
- Add
- #2415 by @santialbo – support new prop name initialValue on Slate after 0.94.1
- #2358 by @etienne-dldc – fix readOnly not being properly updated on Editable
0077402
by @zbeyens –- This package has been split into multiple packages for separation of concerns and decoupled versioning:
@udecode/utils
is a collection of miscellaneous utilities. Can be used by any project.@udecode/slate
is a collection ofslate
experimental features and bug fixes that may be moved intoslate
one day. It's essentially composed of the generic types. Can be used by vanillaslate
consumers without plate.@udecode/slate-react
is a collection ofslate-react
experimental features and bug fixes that that may be moved intoslate-react
one day. It's essentially composed of the generic types. Can be used by vanillaslate-react
consumers without plate.@udecode/plate-core
is the minimalistic core of plate. It essentially includesPlate
,PlateProvider
and their dependencies. Note this package is not dependent on the*-utils
packages.@udecode/slate-utils
is a collection of utils depending on@udecode/slate
. Can be used by vanillaslate
consumers without plate.@udecode/plate-utils
is a collection of utils depending on@udecode/slate-react
and@udecode/plate-core
@udecode/plate-common
re-exports the 6 previous packages and is a dependency of all the other packages. It's basically replacing@udecore/plate-core
as a bundle.
- Removed
getPreventDefaultHandler
since it is no longer needed. Migration:- If using
@udecode/plate
or@udecode/plate-headless
: none - Else: find & replace
@udecode/plate-core
by@udecode/plate-common
- If using
- This package has been split into multiple packages for separation of concerns and decoupled versioning:
- #2240 by @OliverWales –
- Add
sanitizeUrl
util to check if URL has an allowed scheme
- Add
- #2237 by @TomMorane –
createHOC
: deep merge props
- #2225 by @TomMorane – vendor: upgrade react-hotkeys-hook to v4
- #2211 by @zbeyens –
- support
slate/slate-react@0.90.0
- add
isElement(n)
toisBlock
as it has been removed by slate - Fixes #2203
- Fixes #2197
- support
- #2194 by @zbeyens – fix:
useElement
should not throw an error if the element is not found. It can happen when the document is not yet normalized. This patch replaces thethrow
by aconsole.warn
.
- #2185 by @zbeyens – fix:
getEditorString
should not throw an error when a node is not found. Returns an empty string in that case.
- #2142 by @zbeyens –
- New core plugin:
editorProtocol
following https://github.com/udecode/editor-protocol core specs - Slate types: replaced editor mark types by
string
. Derived types fromEMarks<V>
are often unusable.
- New core plugin:
- #2097 by @zbeyens –
- upgrade deps, including typescript support for the new editor methods:
// from "slate": "0.78.0", "slate-history": "0.66.0", "slate-react": "0.79.0" // to "slate": "0.87.0", "slate-history": "0.86.0", "slate-react": "0.88.0"
-
- new
Plate
/PlateProvider
prop:readOnly
- it's also stored in plate store, useful when
readOnly
is needed betweenPlateProvider
andPlate
. - new selector:
usePlateReadOnly
- (not mandatory) migration:
// from <Plate editableProps={{readOnly: true}} /> // to <Plate readOnly />
- new
- #1829 by @osamatanveer –
- new queries:
getPreviousSiblingNode
isDocumentEnd
- new utils:
getJotaiProviderInitialValues
: get jotai provider initial values from props- exports
nanoid
- new dependency:
nanoid
- new queries:
- #1978 by @zbeyens – Plugin fields
renderBeforeEditable
andrenderAfterEditable
now haveTEditableProps
passed as the first parameter.
- #1960 by @zbeyens –
- Default editor value is now overridable with
editor.childrenFactory()
- New core plugin
nodeFactory
, extends the editor with:blockFactory: (node) => TElement
, can be used to create the default editor blockchildrenFactory: () => Value
- New transform
resetEditorChildren
: Replace editor children byeditor.childrenFactory()
.
- Default editor value is now overridable with
- #1959 by @zbeyens –
- Default editor value is now overridable with
editor.childrenFactory()
- New core plugin
nodeFactory
, extends the editor with:blockFactory: (node) => TElement
, can be used to create the default editor blockchildrenFactory: () => Value
- New transform
resetEditorChildren
: Replace editor children byeditor.childrenFactory()
.
- Default editor value is now overridable with
-
#1957 by @tmilewski – fix: update
@radix-ui/react-slot
to eliminate conflicting peer dependencies
- #1888 by @zbeyens –
- new
PlatePlugin
property:renderAboveSlate
– Render a component aboveSlate
id
is no longer required in plate hooks:usePlateId()
is getting the closest editor id- it's used in all store hooks if no store is found with the omitted id
- note that
id
is not needed if you don't have nestedPlate
/PlateProvider
id
prop change should remountPlate
- new
- #1896 by @charrondev – Fix
PrevSelectionPlugin
event persistence on React 16.x
- #1885 by @zbeyens – fix: Plate without
initialValue
orvalue
prop should useeditor.children
as value. Ifeditor.children
is empty, use default value (empty paragraph).
- #1878 by @zbeyens –
- Fix:
Maximum call stack size exceeded
after many changes - Fix: Plate props that are functions are now working (e.g.
onChange
)
- Fix:
-
usePlateStore
:- Plate no longer has a global store containing all the editor states (zustand). Each editor store is now defined in a React context tree (jotai). If you need to access all the editor states at once (as you could do before), you'll need to build that layer yourself.
- Plate store is now accessible only below
PlateProvider
orPlate
(provider-less mode). It means it's no longer accessible outside of a Plate React tree. If you have such use-case, you'll need to build your own layer to share the state between your components. - You can nest many
PlateProvider
with different scopes (id
prop). Default scope isPLATE_SCOPE
- Hook usage:
const value = usePlateSelectors(id).value()
const setValue = usePlateActions(id).value()
const [value, setValue] = usePlateStates(id).value()
- removed from the store:
editableProps
, use the props insteadenabled
, use conditional rendering insteadisReady
, no point anymore as it's now directly ready
useEventPlateId
is still used to get the last focused editor id.- Functions are stored in an object
{ fn: <here> }
-const setOnChange = usePlateActions(id).onChange()
-setOnChange({ fn: newOnChange })
Plate
- if rendered below
PlateProvider
, it will renderPlateSlate > PlateEditable
- if rendered without
PlateProvider
, it will renderPlateProvider > PlateSlate > PlateEditable
- default
id
is no longermain
, it's nowPLATE_SCOPE
- if rendered below
PlateProvider
- Each provider has an optional
scope
, so you can have multiple providers in the same React tree and use the plate hooks with the correspondingscope
. - Plate effects are now run in
PlateProvider
initialValue, value, editor, normalizeInitialValue, normalizeEditor
are no longer defined in an effect (SSR support)
- Props:
- now extends the previous
Plate
props - if using
PlateProvider
, set the provider props on it instead ofPlate
.Plate
would only neededitableProps
andPlateEditableExtendedProps
- if not using it, set the provider props on
Plate
- now extends the previous
- Each provider has an optional
// Before <PlateProvider> <Toolbar> <AlignToolbarButtons /> </Toolbar> <Plate<MyValue> editableProps={editableProps} <MyValue> initialValue={alignValue} plugins={plugins} /> </PlateProvider> // After <PlateProvider<MyValue> initialValue={alignValue} plugins={plugins}> <Toolbar> <AlignToolbarButtons /> </Toolbar> <Plate<MyValue> editableProps={editableProps} /> </PlateProvider> // After (provider-less mode) <Plate<MyValue> editableProps={editableProps} initialValue={alignValue} plugins={plugins} />
- types:
- store
editor
is no longer nullable - store
value
is no longer nullable id
type is nowPlateId
- store
- renamed:
SCOPE_PLATE
toPLATE_SCOPE
getEventEditorId
togetEventPlateId
getPlateActions().resetEditor
touseResetPlateEditor()
- removed:
plateIdAtom
usePlateId
forusePlateSelectors().id()
EditablePlugins
forPlateEditable
SlateChildren
PlateEventProvider
forPlateProvider
withPlateEventProvider
forwithPlateProvider
usePlate
usePlatesStoreEffect
useEventEditorId
foruseEventPlateId
platesStore, platesActions, platesSelectors, usePlatesSelectors
getPlateActions
forusePlateActions
getPlateSelectors
forusePlateSelectors
getPlateEditorRef
forusePlateEditorRef
getPlateStore, usePlateStore
EditorId
forPlateId
-
- SSR support
useEventPlateId
returns:id
if defined- focused editor id if defined
- blurred editor id if defined
- last editor id if defined
- provider id if defined
PLATE_SCOPE
otherwise
- new dep:
nanoid
PlateProvider
export interface PlateProviderProps< V extends Value = Value, E extends PlateEditor<V> = PlateEditor<V>, > extends PlateProviderEffectsProps<V, E>, Partial<Pick<PlateStoreState<V, E>, 'id' | 'editor'>> { /** * Initial value of the editor. * * @default [{ children: [{ text: '' }] }] */ initialValue?: PlateStoreState<V>['value']; /** * When `true`, it will normalize the initial value passed to the `editor` * once it gets created. This is useful when adding normalization rules on * already existing content. * * @default false */ normalizeInitialValue?: boolean; scope?: Scope; }
PlateProviderEffects
PlateSlate
PlateEditable
export interface PlateEditableExtendedProps { id?: PlateId; /** The children rendered inside `Slate`, after `Editable`. */ children?: ReactNode; /** Ref to the `Editable` component. */ editableRef?: Ref<HTMLDivElement>; /** * The first children rendered inside `Slate`, before `Editable`. Slate DOM is * not yet resolvable on first render, for that case use `children` instead. */ firstChildren?: ReactNode; /** Custom `Editable` node. */ renderEditable?: (editable: ReactNode) => ReactNode; } export interface PlateEditableProps<V extends Value = Value> extends Omit<TEditableProps<V>, 'id'>, PlateEditableExtendedProps {}
- #1856 by @zbeyens –
- core plugin
createSelectionPlugin
renamed tocreatePrevSelectionPlugin
queryNode
- new options:level
: Valid path levelsmaxLevel
: Paths above that value are invalid
- core plugin
- #1832 by @zbeyens – New editor prop:
currentKeyboardEvent
: is set inonKeyDown
and unset after applyingset_selection
operation. Useful to override the selection depending on the keyboard event.
- #1778 by @zbeyens –
isRangeAcrossBlocks
: Now returns true if one of the block above is found but not the other and returns undefined if no block is found.isRangeInSameBlock
: Whether the range is in the same block.removeNodeChildren
: Remove node children.replaceNodeChildren
: Replace node children: remove then insert.
- #1776 by @davisg123 – Autoformatter will incorrectly match on text that contains one additional character of text
-
#1755 by @mouradmourafiq – Add
options
parameter toisSelectionAtBlockEnd
- #1721 by @zbeyens –
ElementProvider
now hasSCOPE_ELEMENT='element'
scope in addition to the plugin key, souseElement()
can be called without parameter (default =SCOPE_ELEMENT
). You'll need to use the plugin key scope only to get an ancestor element.- upgrade peerDeps:
"slate": ">=0.78.0"
"slate-react": ">=0.79.0"
- #1677 by @zbeyens –
- new dep + re-exports
"react-hotkeys-hook": "^3.4.6"
- new core plugin
createSelectionPlugin
- stores the previous selection in
editor.prevSelection
(default isnull
) - enabled by default, can be disabled using
selection
key
- stores the previous selection in
- new
PlatePlugin
props:renderAboveEditable
: Render a component aboveEditable
.renderAfterEditable
: Render a component afterEditable
.renderBeforeEditable
: Render a component beforeEditable
.
Plate
:- pipes plugins
renderAboveEditable
and render the result aboveEditable
- pipes plugins
renderAfterEditable
and render the result afterEditable
, beforechildren
- pipes plugins
renderBeforeEditable
and render the result beforeEditable
, afterfirstChildren
- pipes plugins
- new queries
getNextNodeStartPoint
getPreviousNodeEndPoint
- new hooks
useOnClickOutside
PlateEditor
new prop:prevSelection: TRange | null;
- new dep + re-exports
- #1633 by @tjramage – Moved
serializeHtml
and its utils to@udecode/plate-serializer-html
as it has a new dependency: html-entities.- If you're using
@udecode/plate
, no migration is needed - Otherwise, import it from
@udecode/plate-serializer-html
- If you're using
- #1650 by @zbeyens –
PlatePlugin
has a new option:normalizeInitialValue
: filter the value before it's passed into the editor
bed47ae
by @zbeyens –focusEditor
new option to set selection before focusing the editortarget
: if defined:- deselect the editor (otherwise it will focus the start of the editor)
- select the editor
- focus the editor
- re-exports
createStore
from@udecode/zustood
, so the other packages don't have to install it
- #1616 by @zbeyens –
useElement
: Plate is now storingelement
in a context provided in each rendered element. Required parameter: the plugin key is used as a scope as it's needed for nested elements.
Plate
children are now rendered as last children ofSlate
(previously first children). To reproduce the previous behavior, movechildren
tofirstChildren
- #1592 by @zbeyens –
- fix:
Plate
children were rendered beforeEditable
, making slate DOM not resolvable on first render. Fixed by movingEditable
as the first child ofSlate
andchildren
as the last children ofSlate
. Plate
new props:firstChildren
: replaces the previous behavior ofchildren
, rendered as the first children ofSlate
editableRef
: Ref to theEditable
component.
- Plate store - new field:
isRendered
: WhetherEditable
is rendered so slate DOM is resolvable. Subscribe to this value when you query the slate DOM outsidePlate
.
- fix:
- #1560 by @zbeyens –
- exports
isComposing
fromReactEditor
- exports
Hotkeys
from slate - types:
- use slate type options when defined
- exports
- #1546 by @zbeyens –
getEdgeBlocksAbove
: Get the edge blocks above a location (default: selection).getPluginTypes
: Get plugin types option by plugin keys.
- #1534 by @zbeyens – types:
createPluginFactory
: use genericP
type in first parameter- add
Value
default type in place it can't be inferred - replace
EditorNodesOptions
byGetNodeEntriesOptions
- #1526 by @zbeyens –
unhangRange
: return the range instead of void- add default generic types to many places
- add generic types to:
WithOverride
functionsDecorate
functionsOnChange
functionsKeyboardHandler
functions
- #1523 by @zbeyens –
createPluginFactory
type: default plugin has types (e.g.Value
) which can be overriden using generics (e.g.MyValue
).- Plugin types are now using
Value
generic type when it's using the editor. - replace plugin options generic type
P = {}
byP = PluginOptions
wherePluginOptions = AnyObject
. That fixes a type error happening when a list of plugins has customP
, which don't match{}
.
-
#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
TEditor
type to beTEditor<V>
whereV
represents the "value" being edited by Slate. In the most generic editor,V
would 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 aTText
node. 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
TEditor
still.)
- Changing the
Define your custom types
- Follow https://platejs.org/docs/typescript example.
Slate types
Those Slate types should be replaced by the new types:
Editor
->TEditor<V extends Value>
- Note that
TEditor
methods are not typed based onValue
as it would introduce a circular dependency. You can usegetTEditor(editor)
to get the editor with typed methods.
- Note that
ReactEditor
->TReactEditor<V extends Value>
HistoryEditor
->THistoryEditor<V extends Value>
EditableProps
->TEditableProps<V extends Value>
Node
->TNode
Element
->TElement
Text
->TText
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
,ReactEditor
functions should be replaced: The whole API has been typed into Plate core. See https://github.com/udecode/plate/packages/core/src/slate createEditor
->createTEditor
withReact
->withTReact
withHistory
->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>>
, whereP
is the plugin options type. -
Editor
functions are using<V extends Value>
generic, whereV
can be a custom editor value type used inPlateEditor<V>
. -
Editor
functions returning a node are using<N extends ENode<V>, V extends Value = Value>
generics, whereN
can be a custom returned node type. -
Editor
callbacks (e.g. a plugin option) are using<V extends Value, E extends PlateEditor<V> = PlateEditor<V>>
generics, whereE
can be a custom editor type. -
Node
functions 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
-
WithOverride
is 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
->getAboveNode
getParent
->getParentNode
getText
->getEditorString
getLastNode
->getLastNodeByLevel
getPointBefore
->getPointBeforeLocation
getNodes
->getNodeEntries
getNodes
->getNodeEntries
isStart
->isStartPoint
isEnd
->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
->TLinkElement
ImageNodeData
->TImageElement
TableNodeData
->TTableElement
MentionNodeData
->TMentionElement
MentionNode
->TMentionElement
MentionInputNodeData
->TMentionInputElement
MentionInputNode
->TMentionInputElement
CodeBlockNodeData
->TCodeBlockElement
MediaEmbedNodeData
->TMediaEmbedElement
TodoListItemNodeData
->TTodoListItemElement
ExcalidrawNodeData
->TExcalidrawElement
Utils
match
signature change:
<T extends TNode>(
obj: T,
path: TPath,
predicate?: Predicate<T>
)
-
#1500 by @zbeyens – Transforms:
insertElements
:insertNodes
where node type isTElement
setElements
:setNodes
where node type isTElement
Types:
- General type improvements to all plate packages.
Value = TElement[]
: Default value of an editor.TNode = TEditor<Value> | TElement | TText
TElement
: Note thattype: string
is included as it's the standard in Plate.TText
: it now accepts unknown props.TDescendant = TElement | TText
TAncestor = TEditor<Value> | TElement
ENode<V extends Value>
: Node of an editor valueEElement<V extends Value>
: Element of an editor valueEText<V extends Value>
: Text of an editor valueEDescendant<V extends Value>
: Descendant of an editor valueEAncestor<V extends Value>
: Ancestor of an editor valueNodeOf<N extends TNode>
: A utility type to get all the node types from a root node type.ElementOf<N extends TNode>
: A utility type to get all the element nodes type from a root node.TextOf<N extends TNode>
: A utility type to get all the text node types from a root node type.DescendantOf<N extends TNode>
: A utility type to get all the descendant node types from a root node type.ChildOf<N extends TNode, I extends number = number>
: A utility type to get the child node types from a root node type.AncestorOf<N extends TNode>
: A utility type to get all the ancestor node types from a root node type.ValueOf<E extends TEditor<Value>>
: A helper type for getting the value of an editor.MarksOf<N extends TNode>
: A utility type to get all the mark types from a root node type.EMarks<V extends Value>
TNodeProps<N extends TNode>
: Convenience type for returning the props of a node.TNodeEntry<N extends TNode = TNode>
ENodeEntry<V extends Value>
: Node entry from an editor.TElementEntry<N extends TNode = TNode>
: Element entry from a node.TTextEntry<N extends TNode = TNode>
: Text node entry from a node.ETextEntry<V extends Value>
: Text node entry of a value.TAncestorEntry<N extends TNode = TNode>
: Ancestor entry from a node.EAncestorEntry<V extends Value>
: Ancestor entry from an editor.TDescendantEntry<N extends TNode = TNode>
: Descendant entry from a node.TOperation<N extends TDescendant = TDescendant>
: operation types now accept unknown props.
Updated deps:
"@udecode/zustood": "^1.1.1", "jotai": "^1.6.6", "lodash": "^4.17.21", "zustand": "^3.7.2"
- #1476 by @chrishyle – Fixed an error in toggleMark that caused removeMark to be called when there is no mark to remove
- #1472 by @m9rc1n – Fix Url encoded HTML nodes on adding an image #1189.
Updated function
serializeHtml
to usedecodeURIComponent
per node, instead of complete text. This is fixing problem when combination of image and i.e. paragraph nodes would result in paragraph node not decoded.
- #1465 by @zbeyens –
withoutNormalizing
:Editor.withoutNormalizing
which returns true if normalizedcreatePlateEditor
: addnormalizeInitialValue
optioncreatePlateTestEditor
- #1440 by @zbeyens – Critical fix: plate hooks without id.
usePlateId
(used to get plate store) is now working belowPlateProvider
and outsidePlate
.
- #1435 by @zbeyens – Fix a critical issue when using multiple editors #1352
withHOC
: 3rd parameter can be used to add props to HOC.usePlateId
now just gets plate id atom value and no longer gets event editor id as fallback.useEventEditorId
: Get last event editor id: focus, blur or last.useEventPlateId
: Get provider plate id or event editor id.PlateEventProvider
:PlateProvider
where id is the event editor id (used for toolbar buttons).withPlateEventProvider
15e64184
by @zbeyens – Revert plugins memoization fix #1415 (comment)
- #1415 by @chaseadamsio – fix useEditableProps plugins memoization
-
#1388 by @zbeyens – fix for docs only: use
Array.from
instead of destructuring generators -
#1392 by @zbeyens – fix: using
PlateProvider
was not setting the initial value
-
- vendor:
- upgrade slate to "0.72.8"
- upgrade slate-react to "0.72.9"
- upgrade zustand to "3.7.0"
- new component for testing:
PlateTest
- vendor:
-
Plate
props are merged into the initial store state to override the default values.- the initial value will be
editor.children
ifeditor
prop is defined.
- the initial value will be
PlateProvider
acceptsPlateProps
so set the initial store state
- #1377 by @zbeyens –
- new dep: jotai
Plate
:- set the store only if it's not already set (e.g. controlled use-case)
- there is now a jotai provider with plate id so it can be used by plate selectors if no id is given as parameter.
PlateProvider
: Create plate store and mount/unmount ifid
prop updates.id
can bestring[]
. Use this component on top of components using plate hook selectors, otherwise your components would not rerender on change. Not needed for plate non-hook selectors (getters).useCreatePlateStore
: hook that creates a plate store into the plates store, if not defined.usePlateId
: returns the provider plate id (if any).usePlateStore
: if the hook is used before the plate store is created, it will console warn "The plate hooks must be used inside the<PlateProvider id={id}>
component's context."
- #1377 by @zbeyens –
eventEditorSelectors.focus()
should now return the currently focused editor id, andnull
if no editor is focused.
- #1367 by @zbeyens – Fix: "Adding new Editor instances after render of another instance causes a bad setState error". We were setting the plate store anytime
getPlateStore
was called, so it could be called outside auseEffect
.Plate
now returnsnull
until the plate store is set in the plates store, sogetPlateStore
always returns a defined store. Note that you'd need the same check on your end above any component using plate selectors.
- #1341 by @zbeyens – Fix components using
usePlateEditorState
by introducingwithEditor
/EditorProvider
hoc
- #1303 by @zbeyens –
Plate
editor
prop can now be fully controlled: Plate is not applyingwithPlate
on it anymore
PlatePlugin.deserializeHtml
- can't be an array anymore
- moved
validAttribute
,validClassName
,validNodeName
,validStyle
todeserializeHtml.rules
property
- renamed
plateStore
toplatesStore
platesStore
is now a zustood storeeventEditorStore
is now a zustood storegetPlateId
now gets the last editor id if not focused or blurred- used by
usePlateEditorRef
andusePlateEditorState
- used by
- removed:
usePlateEnabled
forusePlateSelectors(id).enabled()
usePlateValue
forusePlateSelectors(id).value()
usePlateActions
:resetEditor
forgetPlateActions(id).resetEditor()
clearState
forplatesActions.unset()
setInitialState
forplatesActions.set(id)
setEditor
forgetPlateActions(id).editor(value)
setEnabled
forgetPlateActions(id).enabled(value)
setValue
forgetPlateActions(id).value(value)
getPlateState
usePlateState
usePlateKey
- #1303 by @zbeyens –
- new packages
@udecode/zustood
use-deep-compare
Plate
- renders a new component:
EditorRefEffect
- it renders
plugin.useHooks(editor, plugin)
for alleditor.plugins
- note that it will unmount and remount the hooks on
plugins
change
- it renders
useEditableProps
- subscribes to the store
editableProps
,decorate
,renderLeaf
,renderElement
decorate
,renderLeaf
,renderElement
are now separately memoizeduseDeepCompareMemo
instead ofuseMemo
for performance
- subscribes to the store
useSlateProps
- subscribes to the store
onChange
,value
- subscribes to the store
usePlateEffects
- update the plate store on props change:
editableProps
onChange
value
enabled
plugins
decorate
renderElement
renderLeaf
- update the plate store on props change:
- renders a new component:
PlatePlugin
useHooks
: new property to use hooks once the editor is initialized.deserializeHtml
getNode
has a new parameternode
getNode
can be injected by other plugins
createPlateStore
: create a plate zustood store- actions:
resetEditor
,incrementKey
- new properties:
plugins
decorate
renderElement
renderLeaf
editableProps
onChange
- actions:
platesStore
:- actions:
set
,unset
- selectors:
get
- actions:
usePlateId
: hook version ofgetPlateId
platesActions
getPlateActions
getPlateSelectors
usePlateSelectors
getPlateStore
usePlateStore
eventEditorActions
eventEditorSelectors
useEventEditorSelectors
mapInjectPropsToPlugin
: Map plugin inject props to injected plugin
- new packages
- #1303 by @zbeyens –
- fix performance issue with hundreds of Plate editors
- fix a bug where
editor.plugins
was reversed Plate
editor.plugins
were missing plugins onplugins
prop change
withInlineVoid
:- use
plugin.type
instead ofplugin.key
- use
-
- HTML deserializer:
- parent attributes does not override child leaf attributes anymore. For example, if a span has fontSize style = 16px, and its child span has fontSize style = 18px, it's now deserializing to 18px instead of 16px.
- Inject props:
- does not inject props when node value =
inject.props.defaultNodeValue
anymore.
- does not inject props when node value =
- HTML deserializer:
-
- fix link upsert on space
getPointBefore
: will return early if the point before is in another block. RemovedmultiPaths
option as it's not used anymore.
-
#1234 by @zbeyens – Breaking changes:
- removed
components
prop:
// 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
options
prop:
// 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
plugin
as 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;
serialize
no longer haselement
andleaf
properties:
type SerializeHtml = RenderFunction< PlateRenderElementProps | PlateRenderLeafProps >;
Renamed:
injectParentComponent
toinject.aboveComponent
injectChildComponent
toinject.belowComponent
overrideProps
toinject.props
transformClassName
,transformNodeValue
,transformStyle
first parameter is no longereditor
as it's provided bythen
if needed.- the previously
getOverrideProps
is now the core behavior ifinject.props
is defined.
serialize
toserializeHtml
deserialize
todeserializeHtml
- 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 tohandlers
field.
// Before onDrop: handler; // After handlers: { onDrop: handler; }
Removed:
renderElement
is favor of:isElement
is a boolean that enables element rendering.- the previously
getRenderElement
is now the core behavior.
renderLeaf
is favor of:isLeaf
is a boolean that enables leaf rendering.- the previously
getRenderLeaf
is now the core behavior.
inlineTypes
andvoidTypes
for:isInline
is a boolean that enables inline rendering.isVoid
is a boolean that enables void rendering.
plugins
is not a parameter anymore as it can be retrieved ineditor.plugins
withInlineVoid
is now using pluginsisInline
andisVoid
plugin fields.
Renamed:
getPlatePluginType
togetPluginType
getEditorOptions
togetPlugins
getPlatePluginOptions
togetPlugin
pipeOverrideProps
topipeInjectProps
getOverrideProps
topluginInjectProps
serializeHTMLFromNodes
toserializeHtml
getLeaf
toleafToHtml
getNode
toelementToHtml
xDeserializerId
toKEY_DESERIALIZE_X
deserializeHTMLToText
tohtmlTextNodeToString
deserializeHTMLToMarks
tohtmlElementToLeaf
andpipeDeserializeHtmlLeaf
deserializeHTMLToElement
tohtmlElementToElement
andpipeDeserializeHtmlElement
deserializeHTMLToFragment
tohtmlBodyToFragment
deserializeHTMLToDocumentFragment
todeserializeHtml
deserializeHTMLToBreak
tohtmlBrToNewLine
deserializeHTMLNode
todeserializeHtmlNode
deserializeHTMLElement
todeserializeHtmlElement
Removed:
usePlateKeys
,getPlateKeys
usePlateOptions
forgetPlugin
getPlateSelection
forgetPlateEditorRef().selection
flatMapByKey
getEditableRenderElement
andgetRenderElement
forpipeRenderElement
andpluginRenderElement
getEditableRenderLeaf
andgetRenderLeaf
forpipeRenderLeaf
andpluginRenderLeaf
getInlineTypes
getVoidTypes
getPlatePluginTypes
getPlatePluginWithOverrides
mapPlatePluginKeysToOptions
withDeserializeX
forPlatePlugin.editor.insertData
Changed types:
PlateEditor
:- removed
options
forpluginsByKey
- removed
WithOverride
is not returning an extended editor anymore (input and output editors are assumed to be the same types for simplicity).PlateState
- renamed
keyChange
tokeyEditor
- removed
plugins
foreditor.plugins
- removed
pluginKeys
- removed
selection
foreditor.selection
- actions:
- removed
setSelection
,setPlugins
,setPluginKeys
- removed
incrementKeyChange
for
- removed
- renamed
Renamed types:
XHTMLY
toXHtmlY
Deserialize
toDeseralizeHtml
Removed types:
PlatePluginOptions
:type
toPlatePlugin.type
component
toPlatePlugin.component
deserialize
toPlatePlugin.deserializeHtml
getNodeProps
toPlatePlugin.props.nodeProps
hotkey
toHotkeyPlugin
clear
toToggleMarkPlugin
defaultType
is hardcoded top.type
OverrideProps
forPlatePlugin.inject.props
Serialize
forPlatePlugin.serializeHtml
NodeProps
forAnyObject
OnKeyDownElementOptions
forHotkeyPlugin
OnKeyDownMarkOptions
forToggleMarkPlugin
WithInlineVoidOptions
GetNodeProps
forPlatePluginProps
DeserializeOptions
,GetLeafDeserializerOptions
,GetElementDeserializerOptions
,GetNodeDeserializerOptions
,GetNodeDeserializerRule
,DeserializeNode
forPlatePlugin.deserializeHtml
PlateOptions
RenderNodeOptions
DeserializedHTMLElement
- removed
-
#1234 by @zbeyens –
PlatePlugin
extended:- These fields are used by
withInsertData
plugin.
interface PlatePlugin { editor?: Nullable<{ insertData?: { /** * Format to get data. Example data types are text/plain and * text/uri-list. */ format?: string; /** Query to skip this plugin. */ query?: (options: PlatePluginInsertDataOptions) => boolean; /** Deserialize data to fragment */ getFragment?: ( options: PlatePluginInsertDataOptions ) => TDescendant[] | undefined; // injected /** * Function called on `editor.insertData` just before * `editor.insertFragment`. Default: if the block above the selection is * empty and the first fragment node type is not inline, set the selected * node type to the first fragment node type. * * @returns If true, the next handlers will be skipped. */ preInsert?: ( fragment: TDescendant[], options: PlatePluginInsertDataOptions ) => HandlerReturnType; /** Transform the inserted data. */ transformData?: ( data: string, options: { dataTransfer: DataTransfer } ) => string; /** Transform the fragment to insert. */ transformFragment?: ( fragment: TDescendant[], options: PlatePluginInsertDataOptions ) => TDescendant[]; }; }>; }
inject.pluginsByKey
:
interface PlatePlugin { inject?: { /** * Any plugin can use this field to inject code into a stack. For example, * if multiple plugins have defined `inject.editor.insertData.transformData` * for `key=KEY_DESERIALIZE_HTML`, `insertData` plugin will call all of * these `transformData` for `KEY_DESERIALIZE_HTML` plugin. Differs from * `overrideByKey` as this is not overriding any plugin. */ pluginsByKey?: Record<PluginKey, Partial<PlatePlugin<T>>>; }; }
options
: any plugin can use the second generic type to type this field. It means that each plugin can be extended using this field.type
is now optionalcomponent
: no longer need ofoptions
to customize the component.overrideByKey
: a plugin can override other plugins by key (deep merge).plugins
:- Can be used to pack multiple plugins, like the heading plugin.
- Plate eventually flats all the plugins into
editor.plugins
. - nesting support (recursive)
props
: Override nodecomponent
props. Props object or function with props parameters returning the new props. Previously done byoverrideProps
andgetNodeProps
options.then
: a function that is called after the plugin is loaded.- this is very powerful as it allows you to have plugin fields derived from the editor and/or the loaded plugin.
- nesting support (recursive)
interface PlatePlugin { /** * Recursive plugin merging. Can be used to derive plugin fields from * `editor`, `plugin`. The returned value will be deeply merged to the * plugin. */ then?: ( editor: PlateEditor<T>, plugin: WithPlatePlugin<T, P> ) => Partial<PlatePlugin<T, P>>; }
New plugins:
createEventEditorPlugin
(core)createInsertDataPlugin
withInsertData
- all plugins using
editor.insertData
field will be used here - it first gets the data with
format
- then it pipes
query
- then it pipes
transformData
- then it calls
getFragment
- then it pipes
transformFragment
- then it pipes
insertFragment
- all plugins using
New utils:
@udecode/plate-common
has been merged into this package as both packages were dependencies of the exact same packages.@udecode/plate-html-serializer
has been merged into this package.@udecode/plate-ast-serializer
has been merged into this package.@udecode/plate-serializer
has been merged into this package.createPlateEditor
: Create a plate editor with:createEditor
or customeditor
withPlate
- custom
components
createPluginFactory
: Create plugin factory with a default plugin.- The plugin factory:
- param 1
override
can be used to (deeply) override the default plugin. - param 2
overrideByKey
can be used to (deeply) override a nested plugin (in plugin.plugins) by key.
- param 1
- The plugin factory:
createPlugins
: Creates a new array of plugins by overriding the plugins in the original array.- Components can be overridden by key using
components
in the second param. - Any other properties can be overridden by key using
overrideByKey
in the second param.
- Components can be overridden by key using
findHtmlParentElement
flattenDeepPlugins
: Recursively mergeplugin.plugins
intoeditor.plugins
andeditor.pluginsByKey
mergeDeepPlugins
: Recursively merge nested plugins into the root plugins.getInjectedPlugins
:- Get all plugins having a defined
inject.pluginsByKey[plugin.key]
. - It includes
plugin
itself.
- Get all plugins having a defined
getPluginInjectProps
getPluginOptions
getPluginsByKey
mockPlugin
overridePluginsByKey
: Recursive deep merge of each plugin fromoverrideByKey
into plugin with same key (plugin
>plugin.plugins
).pipeInsertDataQuery
pipeInsertFragment
pipeTransformData
pipeTransformFragment
setDefaultPlugin
setPlatePlugins
: Flatten deep plugins then set editor.plugins and editor.pluginsByKeydeserializeHtmlNodeChildren
isHtmlComment
isHtmlElement
isHtmlText
pluginDeserializeHtml
New selectors:
usePlateKey
New types:
HotkeyPlugin
–hotkey
ToggleMarkPlugin
–hotkey
,mark
OverrideByKey
WithPlatePlugin
:PlatePlugin
with requiredtype
,options
,inject
andeditor
.Plate
will create default values if not defined.
Extended types:
PlateEditor
:plugins
: list of the editor pluginspluginsByKey
: map of the editor plugins
PlateState
:keyPlugins
: A key that is incremented on eacheditor.plugins
change.keySelection
: A key that is incremented on eacheditor.selection
change.
WithPlateOptions
:disableCorePlugins
- disable core plugins if you'd prefer to have more control over the plugins order.
- These fields are used by
- #1190 by @zbeyens –
- renamed:
SPEditor
toPEditor
(note thatPlateEditor
is the new default)SPRenderNodeProps
toPlateRenderNodeProps
SPRenderElementProps
toPlateRenderElementProps
SPRenderLeafProps
toPlateRenderLeafProps
useEventEditorId
tousePlateEventId
useStoreEditorOptions
tousePlateOptions
useStoreEditorRef
tousePlateEditorRef
useStoreEditorSelection
tousePlateSelection
useStoreEditorState
tousePlateEditorState
useStoreEditorValue
tousePlateValue
useStoreEnabled
tousePlateEnabled
useStorePlate
tousePlatePlugins
useStorePlatePluginKeys
tousePlateKeys
useStoreState
tousePlateState
getPlateId
: Get the last focused editor id, else get the last blurred editor id, else get the first editor id, elsenull
getPlateState
:- 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 forusePlateEditorRef
usePlateEditorState
usePlateX
...
- removed first parameter
- renamed:
-
getEditableRenderElement
: now uses pluginsinjectChildComponent
to wrapchildren
(lowest)getEditableRenderElement
: now uses pluginsinjectParentComponent
to wrapcomponent
(highest)- new store selectors:
getPlateEditorRef
getPlateEnabled
getPlateKeys
getPlatePlugins
getPlateSelection
getPlateValue
getPlateEventId
Types:
PlatePlugin
,PlatePluginEditor
new fields:injectChildComponent
: Inject child component around any node children.injectParentComponent
: Inject parent component around any nodecomponent
.overrideProps
supports arrays.
SPRenderNodeProps
new fields:editor: PlateEditor
plugins: PlatePlugin
- new types:
PlateEditor<T = {}>
: default editor type used in Plate, assuming we all use history and react editors.InjectComponent
type InjectComponent = <T = AnyObject>( props: PlateRenderElementProps & T ) => RenderFunction<PlateRenderElementProps> | undefined;
87b133ce
by @zbeyens –- slate
DefaultLeaf
does not spread the props to the rendered span so we're using our ownDefaultLeaf
component which does it. It enables us to override the props leaves without having to register a component (e.g. fontColor)
- slate
-
#1154 by @zbeyens – generic type support:
getEditorOptions
getPlatePluginOptions
PlatePluginOptions
PlateOptions
-
#1150 by @jeffsee55 –
- Fixes dependencie issue for React<17 users by using the classic
React.createElement
function rather than the newerjsx-runtime
transform. - Per babel docs: https://babeljs.io/docs/en/babel-preset-react#with-a-configuration-file-recommended
- Fixes dependencie issue for React<17 users by using the classic
- #1126
7ee21356
Thanks @zbeyens! - feat:PlatePlugin
- new field:
overrideProps
- Overrides rendered node props (shallow merge).
- This enables controlling the props of any node component (use cases: indent, align,...).
- used by
pipeRenderElement
andpipeRenderLeaf
- new field:
getRenderElement
andgetRenderLeaf
:- pass the rest of the props to the component
getRenderNodeProps
:- computes slate class and
nodeProps
- computes slate class and
- new dependency:
clsx
- new types:
OverrideProps
PlatePluginEditor
PlatePluginSerialize
PlatePluginNode
PlatePluginElement
PlatePluginLeaf
- #1063
6af469cd
Thanks @ghingis! - addnormalizeInitialValue
prop toPlate
. Whentrue
, it will normalize the initial value passed to theeditor
once it's created. This is useful when adding normalization rules on already existing content. Default isfalse
.
- #1022
35caf35d
Thanks @zbeyens! -overrideProps
: new plate option used bygetRenderElement
andgetRenderLeaf
- If it's a function, its return value will override the component props.
- If it's an object, it will override the component props.
🎉 The Slate Plugins project has evolved to Plate 🎉
To migrate, find and replace all occurrences of:
slate-plugins
toplate
SlatePlugins
toPlate
SlatePlugin
toPlatePlugin
This is the last version of
@udecode/slate-plugins[-x]
, please install@udecode/plate[-x]
.
- #869
7c26cf32
Thanks @zbeyens! - - New plugin optiondeserialize.getFragment
: Function called oneditor.insertData
to filter the fragment to insert.- New plugin option
deserialize.preInsert
: Function called oneditor.insertData
just beforeeditor.insertFragment
. Default: if the block above the selection is empty and the first fragment node type is not inline, set the selected node type to the first fragment node type. If returns true, the next handlers will be skipped.
- New plugin option
- #855
75b39f18
Thanks @zbeyens! - Sometimes we want to preventDefault without stopping the handler pipeline, so we remove this check. In summary, to stop the pipeline, a handler has to returntrue
or runevent.stopPropagation()
- #853
abaf4a11
Thanks @zbeyens! - Before, the handlers had to returnfalse
to prevent the next handlers to be called. Now, we reuseisEventHandled
internally used byslate@0.65.0
which has the opposite behavior: a handler has to returntrue
to stop the pipeline. Additionally, the pipeline stops if at any momentevent.isDefaultPrevented()
orevent.isPropagationStopped()
returnstrue
, except if the handler returnsfalse
. See the updated docs in "Creating Plugins".
- #840
42360b44
Thanks @zbeyens! - fix:- Plugin handlers are now run when a handler is passed to
editableProps
- If one handler returns
true
, slate internal corresponding handler is not called anymore
- Plugin handlers are now run when a handler is passed to
- #773
15048e6f
Thanks @zbeyens! - fix: before, store setValue was called at the start ofonChange
pipeline. Now, it's called at the end of the pipeline so we can make use of this value as the "previous value" in pluginsonChange
.
- #723
806e1632
Thanks @Aedron! - feat: newSlatePlugins
option -renderEditable
: CustomEditable
node
- #687
dfbde8bd
Thanks @zbeyens! - changes:- renamed:
useTSlate
touseEditorState
useTSlateStatic
touseEditorRef
useStoreEditor
touseStoreEditorRef
- removed:
useEditorId
in favor ofuseEditorRef().id
useEditorOptions
in favor ofuseEditorRef().options
useSlatePluginOptions
in favor ofgetSlatePluginOptions(useEditorRef(), pluginKey)
useSlatePluginType
in favor ofgetSlatePluginType(useEditorRef(), pluginKey)
pipeOnDOMBeforeInput
in favor ofpipeHandler
pipeOnKeyDown
in favor ofpipeHandler
- types:
- renamed:
SlatePluginsState
toSlatePluginsStates
State
toSlatePluginsState
- removed:
OnDOMBeforeInput
in favor ofDOMHandler<'onDOMBeforeInput'>
OnKeyDown
in favor ofKeyboardHandler
- renamed:
- renamed:
- #687
dfbde8bd
Thanks @zbeyens! - changes:useEditableProps
(used bySlatePlugins
):- new fields returned: all handler props from the plugins (if defined)
- new core plugins with the following fields:
onFocus: setEventEditorId('focus', id)
onBlur: setEventEditorId('blur', id)
- You can add your own handlers in a plugin
EditorStateEffect
: a new component used bySlatePlugins
to update the editor state.setEventEditorId
: a new action. Set an editor id by event key.eventEditorStore
,useEventEditorStore
: a new store. Store where the keys are event names and the values are editor ids.usePlateEventId
: a new selector. Get the editor id byevent
key.useStoreEditorSelection
: a new selector. Get the editor selection which is updated on editor change.useStoreEditorState
: a new selector. Get editor state which is updated on editor change. Similar touseSlate
.SlatePlugin
: the previous plugin could implement the following handlers:onChange
,onDOMBeforeInput
andonKeyDown
. The plugins now implement all DOM handlers: clipboard, composition, focus, form, image, keyboard, media, mouse, selection, touch, pointer, ui, wheel animation and transition events.SlatePluginsState
(store interface):- a new field
keyChange
incremented bySlatePlugins
onuseSlate
update. - a new field
selection = editor.selection
updated onuseSlate
update.
- a new field
pipeHandler
: a new function. Generic pipe for handlers.