Element nodes behave differently depending on the Plite editor's schema. An element can be:
state.schema.isInline(element)state.schema.isVoid(element)A "block" element can only be siblings with other "block" elements. An "inline" node can be siblings with Text nodes or other "inline" elements.
In a not "void" element, Plite handles the rendering of its children (e.g. in a paragraph where the Text and Inline children are rendered by Plite). In a "void" element, Plite owns the DOM shell and selection anchor while app code renders only the visible content.
Some void elements are effectively stand-ins for text, such as with the
mentions example, where the
mention element renders the character's name. Users might want to format Void
elements like this with bold, or set their font and size, so
state.schema.isMarkableVoid(element) tells Plite whether or not to apply Marks
to the text children of void elements.
Void elements still contain a text child in the Plite document model so
selection and marks have a stable model location. The React runtime renders the
hidden anchor and browser shell for you. App renderers should use
Editable's renderVoid prop and return visible content only.
Typical rendering code will resemble this image element:
import type { RenderVoidProps } from "@platejs/plite-react";
type ImageElement = {
type: "image";
url: string;
children: [{ text: "" }];
};
const Image = ({ element }: RenderVoidProps<ImageElement>) => {
return <img alt="" src={element.url} />;
};
<Editable
renderVoid={(props) => {
switch (props.element.type) {
case "image":
return <Image {...props} />;
default:
return null;
}
}}
/>;import type { RenderVoidProps } from "@platejs/plite-react";
type ImageElement = {
type: "image";
url: string;
children: [{ text: "" }];
};
const Image = ({ element }: RenderVoidProps<ImageElement>) => {
return <img alt="" src={element.url} />;
};
<Editable
renderVoid={(props) => {
For a "markable" void such as a mention element, marks on the text child can
still be used to determine how the visible content is rendered. Selection UI is
an opt-in target subscription:
import { useElementSelected, type RenderVoidProps } from "@platejs/plite-react";
type MentionElement = {
type: "mention";
character: string;
children: [{ bold?: true; italic?: true; text: "" }];
};
const Mention = ({ element }: RenderVoidProps<MentionElement>) => {
const selected = useElementSelected();
const text = element.children[0] ?? {};
const style = {
padding: "3px 3px 2px",
margin: "0 1px",
verticalAlign: "baseline",
display: "inline-block",
borderRadius: "4px",
backgroundColor: "#eee",
fontSize: "0.9em",
boxShadow: selected ? "0 0 0 2px #B4D5FF" : "none",
};
if (text.bold) {
style.fontWeight = "bold";
}
if (text.italic) {
style.fontStyle = "italic";
}
return (
<span
data-cy={`mention-${element.character.replace(" ", "-")}`}
style={style}
>
@{element.character}
</span>
);
};import { useElementSelected, type RenderVoidProps } from "@platejs/plite-react";
type MentionElement = {
type: "mention";
character: string;
children: [{ bold?: true; italic?: true; text: "" }];
};
const Mention = ({ element }: RenderVoidProps<MentionElement>) => {
const selected = useElementSelected();
const text = element.children[0] ?? {};
Check if an element matches a set of props. Note: This checks custom properties, but it does not ensure that any children are equivalent.
Check if a value implements the 'Ancestor' interface.
Check if a value implements the Element interface.
Check if a value is an array of Element objects.
Check if a value is an object that can be used as partial Element props.
ElementApi.isElementType<T extends Element>(value: unknown, elementVal: string, elementKey: string = 'type'): value is TCheck if a value implements the Element interface and has elementKey with
the selected value. The default key is type.