Docs
Typescript
Typescript
Generic types for Plate.
The core types will be the default of Slate in the near future, see this PR.
Plate delivers a structured typing layer built on top of Slate, which primarily employs generic types.
Differences with Slate types:
Editor
type: Plate introducesTEditor<V>
, whereV
symbolizes the "value" that Slate edits. In a generic editor,V
would correspond toTElement[]
, as this is what the editor accepts as children. However, in a custom editor,V
might be defined asTEditor<Array<Paragraph | Quote>>
.- Methods related to
TEditor
andTNode
: These methods have also been made generic. For instance, when you callgetLeafNode(editor, path)
, it is recognized that the return value is aTText
node. More specifically, it recognizes the text node of the type defined in your custom elements, along with any marks you've defined. TheTEditor
type in Plate does not correspond to theEditor
type in Slate, hence, Plate has introduced type-only forks of all Slate methods that useEditor
, which you should utilize. See the full list in Slate and Slate React. - Declaration merging approach: Plate's use of generic types supersedes the declaration merging approach, offering several benefits. One issue with declaration merging was the inability to distinguish between an "unknown" and a "known" element as the underlying type was changed. Similarly, managing two editors on a page with different schemas was not possible. The use of generics in Plate, however, can smoothly replace the declaration merging approach while facilitating an easy migration. You can continue to pass the same custom element definitions into
TEditor
.
Example of Defining Types in plate-types.ts
:
The following example outlines the types in plate-types.ts
:
- The
MyValue
type, which will serve as the type of oureditor.children
. MyValue
is a crucial type since most core types derive from it.- Types following
MyValue
are optional, yet highly recommended. The process of writing generic types can be repetitive, so aim to do it just once. - A set of typed functions are also included for your use.
Naming Convention:
- We use
T...Element
because...Element
is already utilized by Plate UI components, and we wish to avoid naming conflicts among imports. TheT...
prefix also differentiates Plate types from Slate types. My
is used for consumer types of the library. It's succinct and unambiguous. You could substitute it with the tag name of your project.
Usage
import React from 'react';
import { AutoformatRule } from '@udecode/plate-autoformat';
import { ELEMENT_BLOCKQUOTE } from '@udecode/plate-block-quote';
import {
ELEMENT_CODE_BLOCK,
ELEMENT_CODE_LINE,
} from '@udecode/plate-code-block';
import { TCommentText } from '@udecode/plate-comments';
import {
createPlateEditor,
CreatePlateEditorOptions,
createPluginFactory,
createPlugins,
createTEditor,
Decorate,
DecorateEntry,
DOMHandler,
EDescendant,
EElement,
EElementEntry,
EElementOrText,
EMarks,
ENode,
ENodeEntry,
EText,
ETextEntry,
getTEditor,
InjectComponent,
InjectProps,
KeyboardHandler,
NoInfer,
OnChange,
OverrideByKey,
PlateEditor,
PlateId,
PlatePlugin,
PlatePluginComponent,
PlatePluginInsertData,
PlatePluginProps,
PlateProps,
PluginOptions,
SerializeHtml,
TElement,
TNodeEntry,
TReactEditor,
useEditorRef,
useEditorState,
usePlateActions,
usePlateEditorRef,
usePlateEditorState,
usePlateSelectors,
usePlateStates,
WithOverride,
} from '@udecode/plate-common';
import {
ELEMENT_EXCALIDRAW,
TExcalidrawElement,
} from '@udecode/plate-excalidraw';
import {
ELEMENT_H1,
ELEMENT_H2,
ELEMENT_H3,
ELEMENT_H4,
ELEMENT_H5,
ELEMENT_H6,
} from '@udecode/plate-heading';
import { ELEMENT_HR } from '@udecode/plate-horizontal-rule';
import { ELEMENT_LINK, TLinkElement } from '@udecode/plate-link';
import {
ELEMENT_LI,
ELEMENT_OL,
ELEMENT_TODO_LI,
ELEMENT_UL,
TTodoListItemElement,
} from '@udecode/plate-list';
import {
ELEMENT_IMAGE,
ELEMENT_MEDIA_EMBED,
TImageElement,
TMediaEmbedElement,
} from '@udecode/plate-media';
import {
ELEMENT_MENTION,
ELEMENT_MENTION_INPUT,
TMentionElement,
TMentionInputElement,
} from '@udecode/plate-mention';
import { ELEMENT_PARAGRAPH } from '@udecode/plate-paragraph';
import {
ELEMENT_TABLE,
ELEMENT_TD,
ELEMENT_TR,
TTableElement,
} from '@udecode/plate-table';
import { TText } from '@udecode/slate';
/**
* Text
*/
export type EmptyText = {
text: '';
};
export type PlainText = {
text: string;
};
export interface RichText extends TText, TCommentText {
bold?: boolean;
italic?: boolean;
underline?: boolean;
strikethrough?: boolean;
code?: boolean;
kbd?: boolean;
subscript?: boolean;
backgroundColor?: React.CSSProperties['backgroundColor'];
fontFamily?: React.CSSProperties['fontFamily'];
color?: React.CSSProperties['color'];
fontSize?: React.CSSProperties['fontSize'];
fontWeight?: React.CSSProperties['fontWeight'];
}
/**
* Inline Elements
*/
export interface MyLinkElement extends TLinkElement {
type: typeof ELEMENT_LINK;
children: RichText[];
}
export interface MyMentionInputElement extends TMentionInputElement {
type: typeof ELEMENT_MENTION_INPUT;
children: [PlainText];
}
export interface MyMentionElement extends TMentionElement {
type: typeof ELEMENT_MENTION;
children: [EmptyText];
}
export type MyInlineElement =
| MyLinkElement
| MyMentionElement
| MyMentionInputElement;
export type MyInlineDescendant = MyInlineElement | RichText;
export type MyInlineChildren = MyInlineDescendant[];
/**
* Block props
*/
export interface MyIndentProps {
indent?: number;
}
export interface MyIndentListProps extends MyIndentProps {
listStart?: number;
listRestart?: number;
listStyleType?: string;
}
export interface MyLineHeightProps {
lineHeight?: React.CSSProperties['lineHeight'];
}
export interface MyAlignProps {
align?: React.CSSProperties['textAlign'];
}
export interface MyBlockElement
extends TElement,
MyIndentListProps,
MyLineHeightProps {
id?: PlateId;
}
/**
* Blocks
*/
export interface MyParagraphElement extends MyBlockElement {
type: typeof ELEMENT_PARAGRAPH;
children: MyInlineChildren;
}
export interface MyH1Element extends MyBlockElement {
type: typeof ELEMENT_H1;
children: MyInlineChildren;
}
export interface MyH2Element extends MyBlockElement {
type: typeof ELEMENT_H2;
children: MyInlineChildren;
}
export interface MyH3Element extends MyBlockElement {
type: typeof ELEMENT_H3;
children: MyInlineChildren;
}
export interface MyH4Element extends MyBlockElement {
type: typeof ELEMENT_H4;
children: MyInlineChildren;
}
export interface MyH5Element extends MyBlockElement {
type: typeof ELEMENT_H5;
children: MyInlineChildren;
}
export interface MyH6Element extends MyBlockElement {
type: typeof ELEMENT_H6;
children: MyInlineChildren;
}
export interface MyBlockquoteElement extends MyBlockElement {
type: typeof ELEMENT_BLOCKQUOTE;
children: MyInlineChildren;
}
export interface MyCodeBlockElement extends MyBlockElement {
type: typeof ELEMENT_CODE_BLOCK;
children: MyCodeLineElement[];
}
export interface MyCodeLineElement extends TElement {
type: typeof ELEMENT_CODE_LINE;
children: PlainText[];
}
export interface MyTableElement extends TTableElement, MyBlockElement {
type: typeof ELEMENT_TABLE;
children: MyTableRowElement[];
}
export interface MyTableRowElement extends TElement {
type: typeof ELEMENT_TR;
children: MyTableCellElement[];
}
export interface MyTableCellElement extends TElement {
type: typeof ELEMENT_TD;
children: MyNestableBlock[];
}
export interface MyBulletedListElement extends TElement, MyBlockElement {
type: typeof ELEMENT_UL;
children: MyListItemElement[];
}
export interface MyNumberedListElement extends TElement, MyBlockElement {
type: typeof ELEMENT_OL;
children: MyListItemElement[];
}
export interface MyListItemElement extends TElement, MyBlockElement {
type: typeof ELEMENT_LI;
children: MyInlineChildren;
}
export interface MyTodoListElement
extends TTodoListItemElement,
MyBlockElement {
type: typeof ELEMENT_TODO_LI;
children: MyInlineChildren;
}
export interface MyImageElement extends TImageElement, MyBlockElement {
type: typeof ELEMENT_IMAGE;
children: [EmptyText];
}
export interface MyMediaEmbedElement
extends TMediaEmbedElement,
MyBlockElement {
type: typeof ELEMENT_MEDIA_EMBED;
children: [EmptyText];
}
export interface MyHrElement extends MyBlockElement {
type: typeof ELEMENT_HR;
children: [EmptyText];
}
export interface MyExcalidrawElement
extends TExcalidrawElement,
MyBlockElement {
type: typeof ELEMENT_EXCALIDRAW;
children: [EmptyText];
}
export type MyNestableBlock = MyParagraphElement;
export type MyBlock = Exclude<MyElement, MyInlineElement>;
export type MyBlockEntry = TNodeEntry<MyBlock>;
export type MyRootBlock =
| MyParagraphElement
| MyH1Element
| MyH2Element
| MyH3Element
| MyH4Element
| MyH5Element
| MyH6Element
| MyBlockquoteElement
| MyCodeBlockElement
| MyTableElement
| MyBulletedListElement
| MyNumberedListElement
| MyTodoListElement
| MyImageElement
| MyMediaEmbedElement
| MyHrElement
| MyExcalidrawElement;
export type MyValue = MyRootBlock[];
/**
* Editor types
*/
export type MyEditor = PlateEditor<MyValue> & { isDragging?: boolean };
export type MyReactEditor = TReactEditor<MyValue>;
export type MyNode = ENode<MyValue>;
export type MyNodeEntry = ENodeEntry<MyValue>;
export type MyElement = EElement<MyValue>;
export type MyElementEntry = EElementEntry<MyValue>;
export type MyText = EText<MyValue>;
export type MyTextEntry = ETextEntry<MyValue>;
export type MyElementOrText = EElementOrText<MyValue>;
export type MyDescendant = EDescendant<MyValue>;
export type MyMarks = EMarks<MyValue>;
export type MyMark = keyof MyMarks;
/**
* Plate types
*/
export type MyDecorate<P = PluginOptions> = Decorate<P, MyValue, MyEditor>;
export type MyDecorateEntry = DecorateEntry<MyValue>;
export type MyDOMHandler<P = PluginOptions> = DOMHandler<P, MyValue, MyEditor>;
export type MyInjectComponent = InjectComponent<MyValue>;
export type MyInjectProps = InjectProps<MyValue>;
export type MyKeyboardHandler<P = PluginOptions> = KeyboardHandler<
P,
MyValue,
MyEditor
>;
export type MyOnChange<P = PluginOptions> = OnChange<P, MyValue, MyEditor>;
export type MyOverrideByKey = OverrideByKey<MyValue, MyEditor>;
export type MyPlatePlugin<P = PluginOptions> = PlatePlugin<
P,
MyValue,
MyEditor
>;
export type MyPlatePluginInsertData = PlatePluginInsertData<MyValue>;
export type MyPlatePluginProps = PlatePluginProps<MyValue>;
export type MyPlateProps = PlateProps<MyValue, MyEditor>;
export type MySerializeHtml = SerializeHtml<MyValue>;
export type MyWithOverride<P = PluginOptions> = WithOverride<
P,
MyValue,
MyEditor
>;
/**
* Plate store, Slate context
*/
export const getMyEditor = (editor: MyEditor) =>
getTEditor<MyValue, MyEditor>(editor);
export const useMyEditorRef = () => useEditorRef<MyValue, MyEditor>();
export const useMyEditorState = () => useEditorState<MyValue, MyEditor>();
export const useMyPlateEditorRef = (id?: PlateId) =>
usePlateEditorRef<MyValue, MyEditor>(id);
export const useMyPlateEditorState = (id?: PlateId) =>
usePlateEditorState<MyValue, MyEditor>(id);
export const useMyPlateSelectors = (id?: PlateId) =>
usePlateSelectors<MyValue, MyEditor>(id);
export const useMyPlateActions = (id?: PlateId) =>
usePlateActions<MyValue, MyEditor>(id);
export const useMyPlateStates = (id?: PlateId) =>
usePlateStates<MyValue, MyEditor>(id);
/**
* Utils
*/
export const createMyEditor = () => createTEditor() as MyEditor;
export const createMyPlateEditor = (
options: CreatePlateEditorOptions<MyValue, MyEditor> = {}
) => createPlateEditor<MyValue, MyEditor>(options);
export const createMyPluginFactory = <P = PluginOptions>(
defaultPlugin: PlatePlugin<NoInfer<P>, MyValue, MyEditor>
) => createPluginFactory(defaultPlugin);
export const createMyPlugins = (
plugins: MyPlatePlugin[],
options?: {
components?: Record<string, PlatePluginComponent>;
overrideByKey?: MyOverrideByKey;
}
) => createPlugins<MyValue, MyEditor>(plugins, options);
export type MyAutoformatRule = AutoformatRule<MyValue, MyEditor>;