diff --git a/packages/rich-text/src/Toolbar/index.tsx b/packages/rich-text/src/Toolbar/index.tsx
index fb3233606..fbf25bdfb 100644
--- a/packages/rich-text/src/Toolbar/index.tsx
+++ b/packages/rich-text/src/Toolbar/index.tsx
@@ -119,6 +119,7 @@ const Toolbar = ({ isDisabled }: ToolbarProps) => {
const sdk = useSdkContext();
const editor = useContentfulEditor();
const canInsertBlocks = !isNodeTypeSelected(editor, BLOCKS.TABLE);
+ const canInsertListBlocks = !isNodeTypeSelected(editor, BLOCKS.TABLE_HEADER_CELL);
const validationInfo = React.useMemo(() => getValidationInfo(sdk.field), [sdk.field]);
const isListSelected =
isNodeTypeSelected(editor, BLOCKS.UL_LIST) || isNodeTypeSelected(editor, BLOCKS.OL_LIST);
@@ -183,7 +184,7 @@ const Toolbar = ({ isDisabled }: ToolbarProps) => {
{validationInfo.isAnyBlockFormattingEnabled && }
-
+
{isNodeTypeEnabled(sdk.field, BLOCKS.QUOTE) && (
diff --git a/packages/rich-text/src/internal/queries.ts b/packages/rich-text/src/internal/queries.ts
index 73bd7a18f..e615de548 100644
--- a/packages/rich-text/src/internal/queries.ts
+++ b/packages/rich-text/src/internal/queries.ts
@@ -44,14 +44,14 @@ export const isNode = (value: unknown): value is Node => {
export const isSelectionAtBlockEnd = (
editor: PlateEditor,
- options?: p.GetAboveNodeOptions,
+ options?: p.GetAboveNodeOptions
) => {
return p.isSelectionAtBlockEnd(editor, options);
};
export const isSelectionAtBlockStart = (
editor: PlateEditor,
- options?: p.GetAboveNodeOptions,
+ options?: p.GetAboveNodeOptions
) => {
return p.isSelectionAtBlockStart(editor, options);
};
@@ -72,7 +72,7 @@ export const getNodeEntries = (editor: PlateEditor, options?: p.GetNodeEntriesOp
export const getNodeChildren = (
root: Ancestor,
path: Path,
- options?: s.NodeChildrenOptions | undefined,
+ options?: s.NodeChildrenOptions | undefined
) => {
return p.getNodeChildren(root, path, options);
};
@@ -80,7 +80,7 @@ export const getNodeChildren = (
export const getParentNode = (
editor: PlateEditor,
at: Location,
- options?: s.EditorParentOptions,
+ options?: s.EditorParentOptions
) => {
return p.getParentNode(editor, at, options) as NodeEntry | undefined;
};
@@ -109,7 +109,7 @@ export const getDescendantNodeByPath = (root: Node, path: s.Path): Node => {
export const getNodeDescendants = (
root: PlateEditor | Node,
- options?: s.NodeDescendantsOptions,
+ options?: s.NodeDescendantsOptions
) => {
return p.getNodeDescendants(root, { ...options, pass: undefined });
};
@@ -123,7 +123,7 @@ export const isRangeAcrossBlocks = (
editor: p.TEditor,
options?:
| (Omit, 'at'> & { at?: s.BaseRange | null | undefined })
- | undefined,
+ | undefined
) => {
return p.isRangeAcrossBlocks(editor, options);
};
@@ -159,7 +159,7 @@ export const getNextNode = (editor: PlateEditor, opts?: p.GetNextNodeOptions {
return p.getCommonNode(root, path, another);
};
@@ -171,7 +171,7 @@ export const getNodeTexts = (
to?: s.Path;
pass?: (ne: NodeEntry) => boolean;
reverse?: boolean;
- },
+ }
) => {
return p.getNodeTexts(root, opts);
};
@@ -258,7 +258,7 @@ export const matchNode = (node: Node, path: s.Path, fn: p.Predicate boolean,
+ predicate: (node: HTMLElement) => boolean
) => {
return p.someHtmlElement(rootNode, predicate);
};
@@ -266,7 +266,7 @@ export const someHtmlElement = (
export const getPointBefore = (
editor: PlateEditor,
at: Location,
- options?: s.EditorBeforeOptions,
+ options?: s.EditorBeforeOptions
) => {
return p.getPointBefore(editor, at, options);
};
@@ -274,7 +274,7 @@ export const getPointBefore = (
export const getPointAfter = (
editor: PlateEditor,
at: Location,
- options?: s.EditorAfterOptions,
+ options?: s.EditorAfterOptions
) => {
return p.getPointAfter(editor, at, options);
};
@@ -282,7 +282,7 @@ export const getPointAfter = (
export const isEndPoint = (
editor: PlateEditor,
point: BasePoint | null | undefined,
- at: Location,
+ at: Location
) => {
return p.isEndPoint(editor, point, at);
};
diff --git a/packages/rich-text/src/internal/transforms.ts b/packages/rich-text/src/internal/transforms.ts
index 936195be9..38b879748 100644
--- a/packages/rich-text/src/internal/transforms.ts
+++ b/packages/rich-text/src/internal/transforms.ts
@@ -25,7 +25,7 @@ import {
*/
export const normalize = (
editor: PlateEditor,
- options: s.EditorNormalizeOptions = { force: true },
+ options: s.EditorNormalizeOptions = { force: true }
) => {
return p.normalizeEditor(editor, options);
};
@@ -60,7 +60,7 @@ export const collapseSelection = (editor: PlateEditor, options?: SelectionCollap
export const setNodes = (
editor: PlateEditor,
attrs: Partial>,
- opts?: p.SetNodesOptions,
+ opts?: p.SetNodesOptions
) => {
p.setNodes(editor, attrs, opts);
};
@@ -68,7 +68,7 @@ export const setNodes = (
export const unsetNodes = (
editor: PlateEditor,
props: string | number | (string | number)[],
- options?: p.UnsetNodesOptions | undefined,
+ options?: p.UnsetNodesOptions | undefined
) => {
p.unsetNodes(editor, props, options);
};
@@ -76,7 +76,7 @@ export const unsetNodes = (
export const insertNodes = (
editor: PlateEditor,
nodes: Node | Node[],
- opts?: p.InsertNodesOptions,
+ opts?: p.InsertNodesOptions
) => {
return p.insertNodes(editor, nodes, opts);
};
@@ -96,7 +96,7 @@ export const unwrapNodes = (editor: PlateEditor, options?: p.UnwrapNodesOptions<
export const wrapNodes = (
editor: PlateEditor,
element: Element,
- options?: p.WrapNodesOptions,
+ options?: p.WrapNodesOptions
) => {
return p.wrapNodes(editor, element, options);
};
@@ -104,7 +104,7 @@ export const wrapNodes = (
export const toggleNodeType = (
editor: PlateEditor,
options: ToggleNodeTypeOptions,
- editorOptions?: Omit,
+ editorOptions?: Omit
) => {
p.toggleNodeType(editor, options, editorOptions);
};
@@ -116,7 +116,7 @@ export const removeMark = (editor: PlateEditor, type: string, at: BaseRange) =>
export const unhangRange = (
editor: PlateEditor,
range?: Path | BasePoint | BaseRange | Span | null | undefined,
- options?: p.UnhangRangeOptions | undefined,
+ options?: p.UnhangRangeOptions | undefined
) => {
return p.unhangRange(editor, range, options);
};
@@ -147,7 +147,7 @@ export const moveNodes = (editor: PlateEditor, opts?: p.MoveNodesOptions)
export const deleteFragment = (
editor: PlateEditor,
- options?: s.EditorFragmentDeletionOptions | undefined,
+ options?: s.EditorFragmentDeletionOptions | undefined
) => {
return p.deleteFragment(editor, options);
};
@@ -178,3 +178,11 @@ export const setEditorValue = (editor: PlateEditor, nodes?: Node[]): void => {
}
});
};
+
+export const setElements = (
+ editor: PlateEditor,
+ props: Partial>,
+ options?: p.SetNodesOptions
+) => {
+ p.setElements(editor, props, options);
+};
diff --git a/packages/rich-text/src/plugins/List/transforms/unwrapList.ts b/packages/rich-text/src/plugins/List/transforms/unwrapList.ts
index 334f46fd1..a01a6829d 100644
--- a/packages/rich-text/src/plugins/List/transforms/unwrapList.ts
+++ b/packages/rich-text/src/plugins/List/transforms/unwrapList.ts
@@ -1,36 +1,97 @@
/**
* Credit: Modified version of Plate's list plugin
- * See: https://github.com/udecode/plate/blob/main/packages/nodes/list
+ * See: https://github.com/udecode/plate/blob/main/packages/list/src/transforms/unwrapList.ts
*/
+
import { BLOCKS } from '@contentful/rich-text-types';
+import { getListTypes } from '@udecode/plate-list';
-import { withoutNormalizing } from '../../../internal';
-import { getNodeEntries, isElement } from '../../../internal/queries';
-import { unwrapNodes, liftNodes } from '../../../internal/transforms';
-import { PlateEditor, Path } from '../../../internal/types';
+import {
+ getAboveNode,
+ getBlockAbove,
+ getCommonNode,
+ getPluginType,
+ isElement,
+} from '../../../internal/queries';
+import { withoutNormalizing, unwrapNodes, setElements } from '../../../internal/transforms';
+import { PlateEditor, Path, Descendant } from '../../../internal/types';
-function hasUnliftedListItems(editor: PlateEditor, at?: Path) {
- return getNodeEntries(editor, {
- at,
- match: (node, path) => isElement(node) && node.type === BLOCKS.LIST_ITEM && path.length >= 2,
- }).next().done;
+function handleEmbeddedType(
+ editor: PlateEditor,
+ blockType: BLOCKS.EMBEDDED_ASSET | BLOCKS.EMBEDDED_ENTRY,
+ at?: Path,
+ firstChild?: Descendant
+) {
+ if (firstChild) {
+ setElements(editor, {
+ at,
+ type: getPluginType(editor, blockType),
+ value: { ...firstChild },
+ });
+ }
}
+
export const unwrapList = (editor: PlateEditor, { at }: { at?: Path } = {}) => {
+ const ancestorListTypeCheck = () => {
+ if (getAboveNode(editor, { match: { at, type: getListTypes(editor) } })) {
+ return true;
+ }
+ // The selection's common node might be a list type
+ if (!at && editor.selection) {
+ const commonNode = getCommonNode(
+ editor,
+ editor.selection.anchor.path,
+ editor.selection.focus.path
+ );
+
+ if (isElement(commonNode[0]) && getListTypes(editor).includes(commonNode[0].type)) {
+ return true;
+ }
+ }
+
+ return false;
+ };
+
withoutNormalizing(editor, () => {
do {
- // lift list items to the root level
- liftNodes(editor, {
+ const licEntry = getBlockAbove(editor, {
at,
- match: (node) => isElement(node) && node.type === BLOCKS.LIST_ITEM,
- mode: 'lowest',
+ match: { type: getPluginType(editor, BLOCKS.LIST_ITEM) },
});
- } while (!hasUnliftedListItems(editor, at));
- // finally unwrap all lifted items
- unwrapNodes(editor, {
- at,
- match: { type: BLOCKS.LIST_ITEM },
- split: false,
- });
+ if (licEntry) {
+ // Special case for embedded entry and asset
+ // if we don't do these they will get replaced by a paragraph and lose
+ // their value.
+ const firstChild = licEntry[0]?.children[0];
+ if (
+ firstChild.type === BLOCKS.EMBEDDED_ENTRY ||
+ firstChild.type === BLOCKS.EMBEDDED_ASSET
+ ) {
+ handleEmbeddedType(editor, firstChild.type, at, firstChild);
+ } else {
+ setElements(editor, {
+ at,
+ type: getPluginType(editor, BLOCKS.PARAGRAPH),
+ });
+ }
+ }
+
+ unwrapNodes(editor, {
+ at,
+ match: { type: getPluginType(editor, BLOCKS.LIST_ITEM) },
+ // in original code split is set to true
+ // ommited here as we get an extra list item when switching off list blocks
+ });
+
+ unwrapNodes(editor, {
+ at,
+ match: {
+ type: [getPluginType(editor, BLOCKS.UL_LIST), getPluginType(editor, BLOCKS.OL_LIST)],
+ },
+ // in original code split is set to true
+ // ommited here as we get an extra list item when switching off list blocks
+ });
+ } while (ancestorListTypeCheck());
});
};
diff --git a/packages/rich-text/src/plugins/Table/createTablePlugin.ts b/packages/rich-text/src/plugins/Table/createTablePlugin.ts
index 57d66924c..d68a7452e 100644
--- a/packages/rich-text/src/plugins/Table/createTablePlugin.ts
+++ b/packages/rich-text/src/plugins/Table/createTablePlugin.ts
@@ -143,7 +143,12 @@ export const createTablePlugin = (): PlatePlugin =>
component: Cell,
normalizer: [
{
- validChildren: CONTAINERS[BLOCKS.TABLE_CELL],
+ validChildren: [
+ ...CONTAINERS[BLOCKS.TABLE_CELL],
+ ...CONTAINERS[BLOCKS.UL_LIST],
+ ...CONTAINERS[BLOCKS.OL_LIST],
+ ...CONTAINERS[BLOCKS.LIST_ITEM],
+ ],
transform: withInvalidCellChildrenTracking(transformParagraphs),
},
],