From c43ea8d801fb9111107a11beb2e9fbdb65ea6d16 Mon Sep 17 00:00:00 2001 From: zbeyens Date: Thu, 19 Dec 2024 15:49:56 +0100 Subject: [PATCH] fix --- .../(app)/_components/installation-tab.tsx | 5 +- apps/www/src/config/customizer-items.ts | 20 +- .../components/editor/use-create-editor.ts | 95 +++-- .../default/block/slate-to-html/page.tsx | 8 +- .../components/editor/plugins/dnd-plugins.tsx | 216 ++++++++++- .../default/components/editor/transforms.ts | 6 +- .../editor/use-create-editor-list.ts | 93 +++-- .../components/editor/use-create-editor.ts | 5 +- .../default/plate-ui/column-group-element.tsx | 55 ++- .../registry/default/plate-ui/draggable.tsx | 13 +- .../plate-ui/table-cell-element-static.tsx | 2 +- .../default/plate-ui/table-cell-element.tsx | 2 +- .../default/plate-ui/with-draggables.tsx | 148 -------- apps/www/src/registry/registry-ui.ts | 8 +- packages/layout/src/lib/index.ts | 1 + packages/layout/src/lib/transforms/index.ts | 3 +- .../src/lib/transforms/insertColumnGroup.ts | 21 +- .../src/lib/transforms/moveMiddleColumn.ts | 20 +- .../layout/src/lib/transforms/resizeColumn.ts | 66 ++++ .../src/lib/transforms/setColumnWidth.ts | 44 --- .../src/lib/transforms/setColumns.spec.tsx | 348 ++++++++++++++++++ .../layout/src/lib/transforms/setColumns.ts | 116 ++++++ .../lib/transforms/toggleColumnGroup.spec.tsx | 183 +++++++++ .../src/lib/transforms/toggleColumnGroup.ts | 62 ++-- packages/layout/src/lib/types.ts | 1 - .../layout/src/lib/utils/columnsToWidths.ts | 4 + packages/layout/src/lib/utils/index.ts | 5 + packages/layout/src/lib/withColumn.ts | 66 ++-- packages/layout/src/react/column-store.ts | 46 --- packages/layout/src/react/index.ts | 1 - 30 files changed, 1195 insertions(+), 468 deletions(-) delete mode 100644 apps/www/src/registry/default/plate-ui/with-draggables.tsx create mode 100644 packages/layout/src/lib/transforms/resizeColumn.ts delete mode 100644 packages/layout/src/lib/transforms/setColumnWidth.ts create mode 100644 packages/layout/src/lib/transforms/setColumns.spec.tsx create mode 100644 packages/layout/src/lib/transforms/setColumns.ts create mode 100644 packages/layout/src/lib/transforms/toggleColumnGroup.spec.tsx create mode 100644 packages/layout/src/lib/utils/columnsToWidths.ts create mode 100644 packages/layout/src/lib/utils/index.ts delete mode 100644 packages/layout/src/react/column-store.ts diff --git a/apps/www/src/app/(app)/_components/installation-tab.tsx b/apps/www/src/app/(app)/_components/installation-tab.tsx index f4e22a5853..be1bbd2137 100644 --- a/apps/www/src/app/(app)/_components/installation-tab.tsx +++ b/apps/www/src/app/(app)/_components/installation-tab.tsx @@ -309,7 +309,6 @@ export default function InstallationTab() { pluginsCode.push(formattedPlugin + ','); }); - const hasDraggable = components.some((comp) => comp.id === 'draggable'); const hasPlaceholder = components.some((comp) => comp.id === 'placeholder'); const usageCode = [ @@ -318,11 +317,11 @@ export default function InstallationTab() { pluginsCode.join('\n'), ' ],', ' override: {', - ` components: ${hasDraggable ? 'withDraggables(' : ''}${hasPlaceholder ? 'withPlaceholders(' : ''}({`, + ` components: ${hasPlaceholder ? 'withPlaceholders(' : ''}({`, ...componentsWithPluginKey.map( ({ pluginKey, usage }) => ` [${pluginKey}]: ${usage},` ), - ` })${hasPlaceholder ? ')' : ''}${hasDraggable ? ')' : ''},`, + ` })${hasPlaceholder ? ')' : ''},`, ' },', ' value: [', ' {', diff --git a/apps/www/src/config/customizer-items.ts b/apps/www/src/config/customizer-items.ts index b1d23434a4..aaf77be80d 100644 --- a/apps/www/src/config/customizer-items.ts +++ b/apps/www/src/config/customizer-items.ts @@ -417,16 +417,16 @@ export const customizerItems: Record = { [DndPlugin.key]: { id: DndPlugin.key, badges: [customizerBadges.handler, customizerBadges.ui], - components: [ - { - id: 'draggable', - filename: 'with-draggables', - label: 'Draggable', - registry: 'draggable', - route: getComponentNavItem('draggable').href, - usage: 'withDraggables', - }, - ], + // components: [ + // { + // id: 'draggable', + // filename: 'with-draggables', + // label: 'Draggable', + // registry: 'draggable', + // route: getComponentNavItem('draggable').href, + // usage: 'withDraggables', + // }, + // ], customImports: [ `import { DndProvider } from 'react-dnd';`, `import { HTML5Backend } from 'react-dnd-html5-backend';`, diff --git a/apps/www/src/registry/default/block/editor-ai/components/editor/use-create-editor.ts b/apps/www/src/registry/default/block/editor-ai/components/editor/use-create-editor.ts index 95cf5f8276..441b42ddd1 100644 --- a/apps/www/src/registry/default/block/editor-ai/components/editor/use-create-editor.ts +++ b/apps/www/src/registry/default/block/editor-ai/components/editor/use-create-editor.ts @@ -92,59 +92,56 @@ import { TableElement } from '@/registry/default/plate-ui/table-element'; import { TableRowElement } from '@/registry/default/plate-ui/table-row-element'; import { TocElement } from '@/registry/default/plate-ui/toc-element'; import { ToggleElement } from '@/registry/default/plate-ui/toggle-element'; -import { withDraggables } from '@/registry/default/plate-ui/with-draggables'; export const useCreateEditor = () => { return usePlateEditor({ override: { - components: withDraggables( - withPlaceholders({ - [AIPlugin.key]: AILeaf, - [AudioPlugin.key]: MediaAudioElement, - [BlockquotePlugin.key]: BlockquoteElement, - [BoldPlugin.key]: withProps(PlateLeaf, { as: 'strong' }), - [CodeBlockPlugin.key]: CodeBlockElement, - [CodeLinePlugin.key]: CodeLineElement, - [CodePlugin.key]: CodeLeaf, - [CodeSyntaxPlugin.key]: CodeSyntaxLeaf, - [ColumnItemPlugin.key]: ColumnElement, - [ColumnPlugin.key]: ColumnGroupElement, - [CommentsPlugin.key]: CommentLeaf, - [DatePlugin.key]: DateElement, - [EmojiInputPlugin.key]: EmojiInputElement, - [ExcalidrawPlugin.key]: ExcalidrawElement, - [FilePlugin.key]: MediaFileElement, - [HEADING_KEYS.h1]: withProps(HeadingElement, { variant: 'h1' }), - [HEADING_KEYS.h2]: withProps(HeadingElement, { variant: 'h2' }), - [HEADING_KEYS.h3]: withProps(HeadingElement, { variant: 'h3' }), - [HEADING_KEYS.h4]: withProps(HeadingElement, { variant: 'h4' }), - [HEADING_KEYS.h5]: withProps(HeadingElement, { variant: 'h5' }), - [HEADING_KEYS.h6]: withProps(HeadingElement, { variant: 'h6' }), - [HighlightPlugin.key]: HighlightLeaf, - [HorizontalRulePlugin.key]: HrElement, - [ImagePlugin.key]: ImageElement, - [ItalicPlugin.key]: withProps(PlateLeaf, { as: 'em' }), - [KbdPlugin.key]: KbdLeaf, - [LinkPlugin.key]: LinkElement, - [MediaEmbedPlugin.key]: MediaEmbedElement, - [MentionInputPlugin.key]: MentionInputElement, - [MentionPlugin.key]: MentionElement, - [ParagraphPlugin.key]: ParagraphElement, - [PlaceholderPlugin.key]: MediaPlaceholderElement, - [SlashInputPlugin.key]: SlashInputElement, - [StrikethroughPlugin.key]: withProps(PlateLeaf, { as: 's' }), - [SubscriptPlugin.key]: withProps(PlateLeaf, { as: 'sub' }), - [SuperscriptPlugin.key]: withProps(PlateLeaf, { as: 'sup' }), - [TableCellHeaderPlugin.key]: TableCellHeaderElement, - [TableCellPlugin.key]: TableCellElement, - [TablePlugin.key]: TableElement, - [TableRowPlugin.key]: TableRowElement, - [TocPlugin.key]: TocElement, - [TogglePlugin.key]: ToggleElement, - [UnderlinePlugin.key]: withProps(PlateLeaf, { as: 'u' }), - [VideoPlugin.key]: MediaVideoElement, - }) - ), + components: withPlaceholders({ + [AIPlugin.key]: AILeaf, + [AudioPlugin.key]: MediaAudioElement, + [BlockquotePlugin.key]: BlockquoteElement, + [BoldPlugin.key]: withProps(PlateLeaf, { as: 'strong' }), + [CodeBlockPlugin.key]: CodeBlockElement, + [CodeLinePlugin.key]: CodeLineElement, + [CodePlugin.key]: CodeLeaf, + [CodeSyntaxPlugin.key]: CodeSyntaxLeaf, + [ColumnItemPlugin.key]: ColumnElement, + [ColumnPlugin.key]: ColumnGroupElement, + [CommentsPlugin.key]: CommentLeaf, + [DatePlugin.key]: DateElement, + [EmojiInputPlugin.key]: EmojiInputElement, + [ExcalidrawPlugin.key]: ExcalidrawElement, + [FilePlugin.key]: MediaFileElement, + [HEADING_KEYS.h1]: withProps(HeadingElement, { variant: 'h1' }), + [HEADING_KEYS.h2]: withProps(HeadingElement, { variant: 'h2' }), + [HEADING_KEYS.h3]: withProps(HeadingElement, { variant: 'h3' }), + [HEADING_KEYS.h4]: withProps(HeadingElement, { variant: 'h4' }), + [HEADING_KEYS.h5]: withProps(HeadingElement, { variant: 'h5' }), + [HEADING_KEYS.h6]: withProps(HeadingElement, { variant: 'h6' }), + [HighlightPlugin.key]: HighlightLeaf, + [HorizontalRulePlugin.key]: HrElement, + [ImagePlugin.key]: ImageElement, + [ItalicPlugin.key]: withProps(PlateLeaf, { as: 'em' }), + [KbdPlugin.key]: KbdLeaf, + [LinkPlugin.key]: LinkElement, + [MediaEmbedPlugin.key]: MediaEmbedElement, + [MentionInputPlugin.key]: MentionInputElement, + [MentionPlugin.key]: MentionElement, + [ParagraphPlugin.key]: ParagraphElement, + [PlaceholderPlugin.key]: MediaPlaceholderElement, + [SlashInputPlugin.key]: SlashInputElement, + [StrikethroughPlugin.key]: withProps(PlateLeaf, { as: 's' }), + [SubscriptPlugin.key]: withProps(PlateLeaf, { as: 'sub' }), + [SuperscriptPlugin.key]: withProps(PlateLeaf, { as: 'sup' }), + [TableCellHeaderPlugin.key]: TableCellHeaderElement, + [TableCellPlugin.key]: TableCellElement, + [TablePlugin.key]: TableElement, + [TableRowPlugin.key]: TableRowElement, + [TocPlugin.key]: TocElement, + [TogglePlugin.key]: ToggleElement, + [UnderlinePlugin.key]: withProps(PlateLeaf, { as: 'u' }), + [VideoPlugin.key]: MediaVideoElement, + }), }, plugins: [ ...copilotPlugins, diff --git a/apps/www/src/registry/default/block/slate-to-html/page.tsx b/apps/www/src/registry/default/block/slate-to-html/page.tsx index 2edcc5ae73..36acdfae45 100644 --- a/apps/www/src/registry/default/block/slate-to-html/page.tsx +++ b/apps/www/src/registry/default/block/slate-to-html/page.tsx @@ -60,7 +60,6 @@ import { BaseTableRowPlugin, } from '@udecode/plate-table'; import { BaseTogglePlugin } from '@udecode/plate-toggle'; -import { cookies } from 'next/headers'; import fs from 'node:fs/promises'; import path from 'node:path'; import Prism from 'prismjs'; @@ -129,8 +128,6 @@ import { TableRowElementStatic } from '@/registry/default/plate-ui/table-row-ele import { TocElementStatic } from '@/registry/default/plate-ui/toc-element-static'; import { ToggleElementStatic } from '@/registry/default/plate-ui/toggle-element-static'; -export const dynamic = 'force-dynamic'; - export const description = 'Slate to HTML'; export const iframeHeight = '800px'; @@ -303,8 +300,9 @@ export default async function SlateToHtmlBlock() { const tailwindCss = await getCachedTailwindCss(); const prismCss = await getCachedPrismCss(); - const cookieStore = await cookies(); - const theme = cookieStore.get('theme')?.value; + // const cookieStore = await cookies(); + // const theme = cookieStore.get('theme')?.value; + const theme = 'light'; // Get the editor content HTML using EditorStatic const editorHtml = await serializeHtml(editor, { diff --git a/apps/www/src/registry/default/components/editor/plugins/dnd-plugins.tsx b/apps/www/src/registry/default/components/editor/plugins/dnd-plugins.tsx index f5569c0f58..e8b3b35dca 100644 --- a/apps/www/src/registry/default/components/editor/plugins/dnd-plugins.tsx +++ b/apps/www/src/registry/default/components/editor/plugins/dnd-plugins.tsx @@ -1,8 +1,217 @@ 'use client'; -import { DndPlugin } from '@udecode/plate-dnd'; -import { PlaceholderPlugin } from '@udecode/plate-media/react'; +import type { NodeWrapperComponent } from '@udecode/plate-common/react'; + +import { BlockquotePlugin } from '@udecode/plate-block-quote/react'; +import { CodeBlockPlugin } from '@udecode/plate-code-block/react'; +import { findNode } from '@udecode/plate-common'; +import { ParagraphPlugin } from '@udecode/plate-common/react'; +import { DndPlugin, useWithDraggable } from '@udecode/plate-dnd'; +import { ExcalidrawPlugin } from '@udecode/plate-excalidraw/react'; +import { HEADING_KEYS, HEADING_LEVELS } from '@udecode/plate-heading'; +import { ColumnPlugin } from '@udecode/plate-layout/react'; +import { + ImagePlugin, + MediaEmbedPlugin, + PlaceholderPlugin, +} from '@udecode/plate-media/react'; import { NodeIdPlugin } from '@udecode/plate-node-id'; +import { TablePlugin } from '@udecode/plate-table/react'; +import { TogglePlugin } from '@udecode/plate-toggle/react'; + +import { Draggable } from '@/registry/default/plate-ui/draggable'; + +const draggableKeys = [ + ParagraphPlugin.key, + 'ul', + 'ol', + ...HEADING_LEVELS, + BlockquotePlugin.key, + CodeBlockPlugin.key, + ImagePlugin.key, + MediaEmbedPlugin.key, + ExcalidrawPlugin.key, + TogglePlugin.key, + ColumnPlugin.key, + PlaceholderPlugin.key, + TablePlugin.key, +]; + +const options = [ + { + keys: draggableKeys, + filter: (editor, path) => { + if (path.length === 1) { + return false; + } + + const block = findNode(editor, { + at: path, + match: { + type: [editor.getType(ColumnPlugin), editor.getType(TablePlugin)], + }, + }); + + if (block?.[0].type === editor.getType(TablePlugin)) { + return path.length !== 4; + } + if (block?.[0].type === editor.getType(ColumnPlugin)) { + return path.length !== 3; + } + + return true; + }, + level: null, + }, + { + key: HEADING_KEYS.h1, + draggableProps: { + className: + '[&_.slate-blockToolbarWrapper]:h-[1.3em] [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-1 [&_.slate-gutterLeft]:text-[1.875em]', + }, + }, + { + key: HEADING_KEYS.h2, + draggableProps: { + className: + '[&_.slate-blockToolbarWrapper]:h-[1.3em] [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-1 [&_.slate-gutterLeft]:text-[1.5em]', + }, + }, + { + key: HEADING_KEYS.h3, + draggableProps: { + className: + '[&_.slate-blockToolbarWrapper]:h-[1.3em] [&_.slate-gutterLeft]:pt-[2px] [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-1 [&_.slate-gutterLeft]:text-[1.25em]', + }, + }, + { + keys: [HEADING_KEYS.h4, HEADING_KEYS.h5], + draggableProps: { + className: + '[&_.slate-blockToolbarWrapper]:h-[1.3em] [&_.slate-gutterLeft]:pt-[3px] [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-0 [&_.slate-gutterLeft]:text-[1.1em]', + }, + }, + { + keys: [ParagraphPlugin.key], + draggableProps: { + className: + '[&_.slate-gutterLeft]:pt-[3px] [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-0', + }, + }, + { + keys: [HEADING_KEYS.h6, 'ul', 'ol'], + draggableProps: { + className: '[&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-0', + }, + }, + { + key: BlockquotePlugin.key, + draggableProps: { + className: '[&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-0', + }, + }, + { + key: CodeBlockPlugin.key, + draggableProps: { + className: + '[&_.slate-gutterLeft]:pt-6 [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-0', + }, + }, + { + key: ImagePlugin.key, + draggableProps: { + className: + '[&_.slate-gutterLeft]:pt-0 [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-0', + }, + }, + { + key: MediaEmbedPlugin.key, + draggableProps: { + className: + '[&_.slate-gutterLeft]:pt-0 [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-0', + }, + }, + { + key: ExcalidrawPlugin.key, + draggableProps: { + className: + '[&_.slate-gutterLeft]:pt-0 [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-0', + }, + }, + { + key: TogglePlugin.key, + draggableProps: { + className: + '[&_.slate-gutterLeft]:pt-0 [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-0', + }, + }, + { + key: ColumnPlugin.key, + draggableProps: { + className: + '[&_.slate-gutterLeft]:pt-0 [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-0', + }, + }, + { + key: PlaceholderPlugin.key, + draggableProps: { + className: + '[&_.slate-gutterLeft]:pt-3 [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-0', + }, + }, + { + key: TablePlugin.key, + draggableProps: { + className: + '[&_.slate-gutterLeft]:pt-3 [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-0', + }, + }, + { + keys: draggableKeys, + draggableProps: { + className: '[&_.slate-gutterLeft]:w-3', + }, + filter: (editor, path) => { + if (path.length === 1) { + return false; + } + + const block = findNode(editor, { + at: path, + match: { + type: [editor.getType(ColumnPlugin), editor.getType(TablePlugin)], + }, + }); + + if (block?.[0].type === editor.getType(TablePlugin)) { + return path.length !== 4; + } + if (block?.[0].type === editor.getType(ColumnPlugin)) { + return path.length !== 3; + } + + return true; + }, + level: null, + }, +]; + +const RenderDraggableAboveNodes: NodeWrapperComponent = () => { + const { disabled, draggableProps } = useWithDraggable({ + ...options, + // ...props, + }); + + console.log({ disabled, draggableProps }); + + if (disabled) return; + + return ({ children, ...props }) => ( + + + + ); +}; export const dndPlugins = [ NodeIdPlugin, @@ -15,5 +224,8 @@ export const dndPlugins = [ .insert.media(dragItem.files, { at: target, nextBlock: false }); }, }, + render: { + aboveNodes: renderDraggableAboveNodes, + }, }), ] as const; diff --git a/apps/www/src/registry/default/components/editor/transforms.ts b/apps/www/src/registry/default/components/editor/transforms.ts index 26d7431155..bd1448eca8 100644 --- a/apps/www/src/registry/default/components/editor/transforms.ts +++ b/apps/www/src/registry/default/components/editor/transforms.ts @@ -53,7 +53,7 @@ import { } from '@udecode/plate-table/react'; import { Path } from 'slate'; -export const STRUCTURAL_TYPES = [ +export const STRUCTURAL_TYPES: string[] = [ ColumnPlugin.key, ColumnItemPlugin.key, TablePlugin.key, @@ -79,7 +79,7 @@ const insertBlockMap: Record< (editor: PlateEditor, type: string) => void > = { [ACTION_THREE_COLUMNS]: (editor) => - insertColumnGroup(editor, { layout: 3, select: true }), + insertColumnGroup(editor, { columns: 3, select: true }), [AudioPlugin.key]: (editor) => insertAudioPlaceholder(editor, { select: true }), [CalloutPlugin.key]: (editor) => insertCallout(editor, { select: true }), @@ -163,7 +163,7 @@ const setBlockMap: Record< string, (editor: PlateEditor, type: string, entry: TNodeEntry) => void > = { - [ACTION_THREE_COLUMNS]: (editor) => toggleColumnGroup(editor, { layout: 3 }), + [ACTION_THREE_COLUMNS]: (editor) => toggleColumnGroup(editor, { columns: 3 }), [INDENT_LIST_KEYS.todo]: setList, [ListStyleType.Decimal]: setList, [ListStyleType.Disc]: setList, diff --git a/apps/www/src/registry/default/components/editor/use-create-editor-list.ts b/apps/www/src/registry/default/components/editor/use-create-editor-list.ts index 1b90c14313..acacdacadc 100644 --- a/apps/www/src/registry/default/components/editor/use-create-editor-list.ts +++ b/apps/www/src/registry/default/components/editor/use-create-editor-list.ts @@ -88,58 +88,55 @@ import { TableElement } from '@/registry/default/plate-ui/table-element'; import { TableRowElement } from '@/registry/default/plate-ui/table-row-element'; import { TocElement } from '@/registry/default/plate-ui/toc-element'; import { ToggleElement } from '@/registry/default/plate-ui/toggle-element'; -import { withDraggables } from '@/registry/default/plate-ui/with-draggables'; export const useCreateEditor = () => { return usePlateEditor({ override: { - components: withDraggables( - withPlaceholders({ - [AIPlugin.key]: AILeaf, - [BlockquotePlugin.key]: BlockquoteElement, - [BoldPlugin.key]: withProps(PlateLeaf, { as: 'strong' }), - [BulletedListPlugin.key]: withProps(ListElement, { variant: 'ul' }), - [CodeBlockPlugin.key]: CodeBlockElement, - [CodeLinePlugin.key]: CodeLineElement, - [CodePlugin.key]: CodeLeaf, - [CodeSyntaxPlugin.key]: CodeSyntaxLeaf, - [ColumnItemPlugin.key]: ColumnElement, - [ColumnPlugin.key]: ColumnGroupElement, - [CommentsPlugin.key]: CommentLeaf, - [DatePlugin.key]: DateElement, - [EmojiInputPlugin.key]: EmojiInputElement, - [ExcalidrawPlugin.key]: ExcalidrawElement, - [HEADING_KEYS.h1]: withProps(HeadingElement, { variant: 'h1' }), - [HEADING_KEYS.h2]: withProps(HeadingElement, { variant: 'h2' }), - [HEADING_KEYS.h3]: withProps(HeadingElement, { variant: 'h3' }), - [HEADING_KEYS.h4]: withProps(HeadingElement, { variant: 'h4' }), - [HEADING_KEYS.h5]: withProps(HeadingElement, { variant: 'h5' }), - [HEADING_KEYS.h6]: withProps(HeadingElement, { variant: 'h6' }), - [HighlightPlugin.key]: HighlightLeaf, - [HorizontalRulePlugin.key]: HrElement, - [ImagePlugin.key]: ImageElement, - [ItalicPlugin.key]: withProps(PlateLeaf, { as: 'em' }), - [KbdPlugin.key]: KbdLeaf, - [LinkPlugin.key]: LinkElement, - [ListItemPlugin.key]: withProps(PlateElement, { as: 'li' }), - [MediaEmbedPlugin.key]: MediaEmbedElement, - [MentionInputPlugin.key]: MentionInputElement, - [MentionPlugin.key]: MentionElement, - [NumberedListPlugin.key]: withProps(ListElement, { variant: 'ol' }), - [ParagraphPlugin.key]: ParagraphElement, - [SlashInputPlugin.key]: SlashInputElement, - [StrikethroughPlugin.key]: withProps(PlateLeaf, { as: 's' }), - [SubscriptPlugin.key]: withProps(PlateLeaf, { as: 'sub' }), - [SuperscriptPlugin.key]: withProps(PlateLeaf, { as: 'sup' }), - [TableCellHeaderPlugin.key]: TableCellHeaderElement, - [TableCellPlugin.key]: TableCellElement, - [TablePlugin.key]: TableElement, - [TableRowPlugin.key]: TableRowElement, - [TocPlugin.key]: TocElement, - [TogglePlugin.key]: ToggleElement, - [UnderlinePlugin.key]: withProps(PlateLeaf, { as: 'u' }), - }) - ), + components: withPlaceholders({ + [AIPlugin.key]: AILeaf, + [BlockquotePlugin.key]: BlockquoteElement, + [BoldPlugin.key]: withProps(PlateLeaf, { as: 'strong' }), + [BulletedListPlugin.key]: withProps(ListElement, { variant: 'ul' }), + [CodeBlockPlugin.key]: CodeBlockElement, + [CodeLinePlugin.key]: CodeLineElement, + [CodePlugin.key]: CodeLeaf, + [CodeSyntaxPlugin.key]: CodeSyntaxLeaf, + [ColumnItemPlugin.key]: ColumnElement, + [ColumnPlugin.key]: ColumnGroupElement, + [CommentsPlugin.key]: CommentLeaf, + [DatePlugin.key]: DateElement, + [EmojiInputPlugin.key]: EmojiInputElement, + [ExcalidrawPlugin.key]: ExcalidrawElement, + [HEADING_KEYS.h1]: withProps(HeadingElement, { variant: 'h1' }), + [HEADING_KEYS.h2]: withProps(HeadingElement, { variant: 'h2' }), + [HEADING_KEYS.h3]: withProps(HeadingElement, { variant: 'h3' }), + [HEADING_KEYS.h4]: withProps(HeadingElement, { variant: 'h4' }), + [HEADING_KEYS.h5]: withProps(HeadingElement, { variant: 'h5' }), + [HEADING_KEYS.h6]: withProps(HeadingElement, { variant: 'h6' }), + [HighlightPlugin.key]: HighlightLeaf, + [HorizontalRulePlugin.key]: HrElement, + [ImagePlugin.key]: ImageElement, + [ItalicPlugin.key]: withProps(PlateLeaf, { as: 'em' }), + [KbdPlugin.key]: KbdLeaf, + [LinkPlugin.key]: LinkElement, + [ListItemPlugin.key]: withProps(PlateElement, { as: 'li' }), + [MediaEmbedPlugin.key]: MediaEmbedElement, + [MentionInputPlugin.key]: MentionInputElement, + [MentionPlugin.key]: MentionElement, + [NumberedListPlugin.key]: withProps(ListElement, { variant: 'ol' }), + [ParagraphPlugin.key]: ParagraphElement, + [SlashInputPlugin.key]: SlashInputElement, + [StrikethroughPlugin.key]: withProps(PlateLeaf, { as: 's' }), + [SubscriptPlugin.key]: withProps(PlateLeaf, { as: 'sub' }), + [SuperscriptPlugin.key]: withProps(PlateLeaf, { as: 'sup' }), + [TableCellHeaderPlugin.key]: TableCellHeaderElement, + [TableCellPlugin.key]: TableCellElement, + [TablePlugin.key]: TableElement, + [TableRowPlugin.key]: TableRowElement, + [TocPlugin.key]: TocElement, + [TogglePlugin.key]: ToggleElement, + [UnderlinePlugin.key]: withProps(PlateLeaf, { as: 'u' }), + }), }, plugins: [ ...copilotPlugins, diff --git a/apps/www/src/registry/default/components/editor/use-create-editor.ts b/apps/www/src/registry/default/components/editor/use-create-editor.ts index d452926a5c..f3820bcff5 100644 --- a/apps/www/src/registry/default/components/editor/use-create-editor.ts +++ b/apps/www/src/registry/default/components/editor/use-create-editor.ts @@ -91,7 +91,6 @@ import { TableElement } from '@/registry/default/plate-ui/table-element'; import { TableRowElement } from '@/registry/default/plate-ui/table-row-element'; import { TocElement } from '@/registry/default/plate-ui/toc-element'; import { ToggleElement } from '@/registry/default/plate-ui/toggle-element'; -import { withDraggables } from '@/registry/default/plate-ui/with-draggables'; import { editorPlugins, viewPlugins } from './plugins/editor-plugins'; @@ -162,9 +161,7 @@ export const useCreateEditor = ( { override: { components: { - ...(readOnly - ? viewComponents - : withPlaceholders(withDraggables(editorComponents))), + ...(readOnly ? viewComponents : withPlaceholders(editorComponents)), ...components, }, ...override, diff --git a/apps/www/src/registry/default/plate-ui/column-group-element.tsx b/apps/www/src/registry/default/plate-ui/column-group-element.tsx index 17293165dd..1e8fb6aff2 100644 --- a/apps/www/src/registry/default/plate-ui/column-group-element.tsx +++ b/apps/www/src/registry/default/plate-ui/column-group-element.tsx @@ -2,15 +2,23 @@ import React from 'react'; -import type { TColumnElement } from '@udecode/plate-layout'; - import { cn, withRef } from '@udecode/cn'; -import { useElement, useRemoveNodeButton } from '@udecode/plate-common/react'; +import { + useEditorRef, + useElement, + useRemoveNodeButton, +} from '@udecode/plate-common/react'; +import { + type TColumnElement, + type TColumnGroupElement, + setColumns, +} from '@udecode/plate-layout'; import { ColumnItemPlugin, - useColumnState, + ColumnPlugin, useDebouncePopoverOpen, } from '@udecode/plate-layout/react'; +import { findNodePath } from '@udecode/slate'; import { type LucideProps, Trash2Icon } from 'lucide-react'; import { useReadOnly } from 'slate-react'; @@ -32,22 +40,23 @@ export const ColumnGroupElement = withRef( ); export function ColumnFloatingToolbar({ children }: React.PropsWithChildren) { + const editor = useEditorRef(); const readOnly = useReadOnly(); - const { - setDoubleColumn, - setDoubleSideDoubleColumn, - setLeftSideDoubleColumn, - setRightSideDoubleColumn, - setThreeColumn, - } = useColumnState(); - const element = useElement(ColumnItemPlugin.key); + const columnGroupElement = useElement(ColumnPlugin.key); const { props: buttonProps } = useRemoveNodeButton({ element }); const isOpen = useDebouncePopoverOpen(); + const onColumnChange = (widths: string[]) => { + setColumns(editor, { + at: findNodePath(editor, columnGroupElement), + widths, + }); + }; + if (readOnly) return <>{children}; return ( @@ -61,26 +70,38 @@ export function ColumnFloatingToolbar({ children }: React.PropsWithChildren) { sideOffset={10} >
- - - diff --git a/apps/www/src/registry/default/plate-ui/draggable.tsx b/apps/www/src/registry/default/plate-ui/draggable.tsx index ec514ce65c..d08c0fbe7d 100644 --- a/apps/www/src/registry/default/plate-ui/draggable.tsx +++ b/apps/www/src/registry/default/plate-ui/draggable.tsx @@ -12,6 +12,7 @@ import { MemoizedChildren, useEditorPlugin, useEditorRef, + useElement, } from '@udecode/plate-common/react'; import { type DragItemNode, @@ -24,6 +25,8 @@ import { BlockSelectionPlugin } from '@udecode/plate-selection/react'; import { GripVertical } from 'lucide-react'; import { useSelected } from 'slate-react'; +import { STRUCTURAL_TYPES } from '@/registry/default/components/editor/transforms'; + import { Tooltip, TooltipContent, @@ -63,7 +66,9 @@ export const Draggable = withRef<'div', DraggableProps>( className={cn( 'relative', isDragging && 'opacity-50', - 'group', + STRUCTURAL_TYPES.includes(element.type) + ? 'group/structural' + : 'group', className )} > @@ -97,6 +102,7 @@ const Gutter = React.forwardRef< React.HTMLAttributes >(({ children, className, ...props }, ref) => { const { useOption } = useEditorPlugin(BlockSelectionPlugin); + const element = useElement(); const isSelectionAreaVisible = useOption('isSelectionAreaVisible'); const gutter = useDraggableGutter(); const selected = useSelected(); @@ -106,7 +112,10 @@ const Gutter = React.forwardRef< ref={ref} className={cn( 'slate-gutterLeft', - 'absolute -top-px z-50 flex h-full -translate-x-full cursor-text hover:opacity-100 sm:opacity-0 main-hover:group-hover:opacity-100', + 'absolute -top-px z-50 flex h-full -translate-x-full cursor-text hover:opacity-100 sm:opacity-0', + STRUCTURAL_TYPES.includes(element.type) + ? 'main-hover:group-hover/structural:opacity-100' + : 'main-hover:group-hover:opacity-100', isSelectionAreaVisible && 'hidden', !selected && 'opacity-0', className diff --git a/apps/www/src/registry/default/plate-ui/table-cell-element-static.tsx b/apps/www/src/registry/default/plate-ui/table-cell-element-static.tsx index 0040d0b9fa..f24382a23b 100644 --- a/apps/www/src/registry/default/plate-ui/table-cell-element-static.tsx +++ b/apps/www/src/registry/default/plate-ui/table-cell-element-static.tsx @@ -61,7 +61,7 @@ export function TableCellElementStatic({ } {...props} > -
+
{children}
diff --git a/apps/www/src/registry/default/plate-ui/table-cell-element.tsx b/apps/www/src/registry/default/plate-ui/table-cell-element.tsx index b0bacd0987..abd0f7e7a3 100644 --- a/apps/www/src/registry/default/plate-ui/table-cell-element.tsx +++ b/apps/www/src/registry/default/plate-ui/table-cell-element.tsx @@ -86,7 +86,7 @@ export const TableCellElement = withRef< } >
> - > -) => - withDraggablePrimitive(Draggable, Component, options as any); - -export const withDraggablesPrimitive = createNodesWithHOC(withDraggable); - -export const withDraggables = (components: any) => { - return withDraggablesPrimitive(components, [ - { - keys: [ParagraphPlugin.key, 'ul', 'ol'], - level: 0, - }, - { - key: HEADING_KEYS.h1, - draggableProps: { - className: - '[&_.slate-blockToolbarWrapper]:h-[1.3em] [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-1 [&_.slate-gutterLeft]:text-[1.875em]', - }, - }, - { - key: HEADING_KEYS.h2, - draggableProps: { - className: - '[&_.slate-blockToolbarWrapper]:h-[1.3em] [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-1 [&_.slate-gutterLeft]:text-[1.5em]', - }, - }, - { - key: HEADING_KEYS.h3, - draggableProps: { - className: - '[&_.slate-blockToolbarWrapper]:h-[1.3em] [&_.slate-gutterLeft]:pt-[2px] [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-1 [&_.slate-gutterLeft]:text-[1.25em]', - }, - }, - { - keys: [HEADING_KEYS.h4, HEADING_KEYS.h5], - draggableProps: { - className: - '[&_.slate-blockToolbarWrapper]:h-[1.3em] [&_.slate-gutterLeft]:pt-[3px] [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-0 [&_.slate-gutterLeft]:text-[1.1em]', - }, - }, - { - keys: [ParagraphPlugin.key], - draggableProps: { - className: - '[&_.slate-gutterLeft]:pt-[3px] [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-0', - }, - }, - { - keys: [HEADING_KEYS.h6, 'ul', 'ol'], - draggableProps: { - className: '[&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-0', - }, - }, - { - key: BlockquotePlugin.key, - draggableProps: { - className: '[&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-0', - }, - }, - { - key: CodeBlockPlugin.key, - draggableProps: { - className: - '[&_.slate-gutterLeft]:pt-6 [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-0', - }, - }, - { - key: ImagePlugin.key, - draggableProps: { - className: - '[&_.slate-gutterLeft]:pt-0 [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-0', - }, - }, - { - key: MediaEmbedPlugin.key, - draggableProps: { - className: - '[&_.slate-gutterLeft]:pt-0 [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-0', - }, - }, - { - key: ExcalidrawPlugin.key, - draggableProps: { - className: - '[&_.slate-gutterLeft]:pt-0 [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-0', - }, - }, - { - key: TogglePlugin.key, - draggableProps: { - className: - '[&_.slate-gutterLeft]:pt-0 [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-0', - }, - }, - { - key: ColumnPlugin.key, - draggableProps: { - className: - '[&_.slate-gutterLeft]:pt-0 [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-0', - }, - }, - { - key: PlaceholderPlugin.key, - draggableProps: { - className: - '[&_.slate-gutterLeft]:pt-3 [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-0', - }, - }, - { - key: TablePlugin.key, - draggableProps: { - className: - '[&_.slate-gutterLeft]:pt-3 [&_.slate-gutterLeft]:px-0 [&_.slate-gutterLeft]:pb-0', - }, - }, - ]); -}; diff --git a/apps/www/src/registry/registry-ui.ts b/apps/www/src/registry/registry-ui.ts index edbbbbd9b1..4052c3ef38 100644 --- a/apps/www/src/registry/registry-ui.ts +++ b/apps/www/src/registry/registry-ui.ts @@ -250,9 +250,7 @@ export const uiComponents: Registry = [ `import { DndPlugin } from '@udecode/plate-dnd'; import { NodeIdPlugin } from '@udecode/plate-node-id'; import { DndProvider } from 'react-dnd'; -import { HTML5Backend } from 'react-dnd-html5-backend'; - -import { withDraggables } from './withDraggables';`, +import { HTML5Backend } from 'react-dnd-html5-backend';`, `export function MyEditor() { const editor = usePlateEditor({ plugins: [ @@ -261,9 +259,9 @@ import { withDraggables } from './withDraggables';`, DndPlugin.configure({ options: { enableScroller: true } }), ], override: { - components: withDraggables({ + components: { // ...components - }), + }, } }); diff --git a/packages/layout/src/lib/index.ts b/packages/layout/src/lib/index.ts index 34b1e779d8..14119d4244 100644 --- a/packages/layout/src/lib/index.ts +++ b/packages/layout/src/lib/index.ts @@ -6,3 +6,4 @@ export * from './BaseColumnPlugin'; export * from './types'; export * from './withColumn'; export * from './transforms/index'; +export * from './utils/index'; diff --git a/packages/layout/src/lib/transforms/index.ts b/packages/layout/src/lib/transforms/index.ts index 5075b4c4a2..2a5fee6fed 100644 --- a/packages/layout/src/lib/transforms/index.ts +++ b/packages/layout/src/lib/transforms/index.ts @@ -5,5 +5,6 @@ export * from './insertColumn'; export * from './insertColumnGroup'; export * from './moveMiddleColumn'; -export * from './setColumnWidth'; +export * from './resizeColumn'; +export * from './setColumns'; export * from './toggleColumnGroup'; diff --git a/packages/layout/src/lib/transforms/insertColumnGroup.ts b/packages/layout/src/lib/transforms/insertColumnGroup.ts index 8f108769c2..bd81d110ca 100644 --- a/packages/layout/src/lib/transforms/insertColumnGroup.ts +++ b/packages/layout/src/lib/transforms/insertColumnGroup.ts @@ -14,27 +14,26 @@ import { BaseColumnItemPlugin, BaseColumnPlugin } from '../BaseColumnPlugin'; export const insertColumnGroup = ( editor: SlateEditor, { - layout = 2, + columns = 2, select: selectProp, ...options }: InsertNodesOptions & { - layout?: number[] | number; + columns?: number; } = {} ) => { - const columnLayout = Array.isArray(layout) - ? layout - : Array(layout).fill(Math.floor(100 / layout)); + const width = 100 / columns; withoutNormalizing(editor, () => { insertNodes( editor, { - children: columnLayout.map((width) => ({ - children: [editor.api.create.block()], - type: BaseColumnItemPlugin.key, - width: `${width}%`, - })), - layout: columnLayout, + children: Array(columns) + .fill(null) + .map(() => ({ + children: [editor.api.create.block()], + type: BaseColumnItemPlugin.key, + width: `${width}%`, + })), type: BaseColumnPlugin.key, }, options diff --git a/packages/layout/src/lib/transforms/moveMiddleColumn.ts b/packages/layout/src/lib/transforms/moveMiddleColumn.ts index 5ab1b2fff6..117253e33e 100644 --- a/packages/layout/src/lib/transforms/moveMiddleColumn.ts +++ b/packages/layout/src/lib/transforms/moveMiddleColumn.ts @@ -2,17 +2,19 @@ import { type SlateEditor, type TNode, type TNodeEntry, + getNode, + getNodeDescendant, + getNodeString, moveNodes, removeNodes, unwrapNodes, } from '@udecode/plate-common'; -import { Node } from 'slate'; import type { TColumnElement } from '../types'; /** - * Move the middle column to the left of right by options.direction. if the - * middle node is empty return false and remove it. + * Move the middle column to the left if direction is 'left', or to the right if + * 'right'. If the middle node is empty, return false and remove it. */ export const moveMiddleColumn = ( editor: SlateEditor, @@ -26,8 +28,12 @@ export const moveMiddleColumn = ( if (direction === 'left') { const DESCENDANT_PATH = [1]; - const middleChildNode = Node.get(node, DESCENDANT_PATH); - const isEmpty = editor.isEmpty(middleChildNode as any); + const middleChildNode = getNode(node, DESCENDANT_PATH); + + if (!middleChildNode) return false; + + // Check emptiness using Node.string + const isEmpty = getNodeString(middleChildNode) === ''; const middleChildPathRef = editor.pathRef(path.concat(DESCENDANT_PATH)); @@ -37,7 +43,9 @@ export const moveMiddleColumn = ( return false; } - const firstNode = Node.descendant(node, [0]) as TColumnElement; + const firstNode = getNodeDescendant(node, [0]); + + if (!firstNode) return false; const firstLast = path.concat([0, firstNode.children.length]); diff --git a/packages/layout/src/lib/transforms/resizeColumn.ts b/packages/layout/src/lib/transforms/resizeColumn.ts new file mode 100644 index 0000000000..39c3623ecc --- /dev/null +++ b/packages/layout/src/lib/transforms/resizeColumn.ts @@ -0,0 +1,66 @@ +import type { TColumnGroupElement } from '../types'; + +export function resizeColumn( + columnGroup: TColumnGroupElement, + columnId: string, + newWidthPercent: number +): TColumnGroupElement { + // Convert widths to numbers for easier math + const widths = columnGroup.children.map((col) => + col.width ? Number.parseFloat(col.width) : 0 + ); + + const totalBefore = widths.reduce((sum, w) => sum + w, 0); + + // fallback if columns do not sum to 100: normalize them + if (totalBefore === 0) { + // distribute evenly if no widths + const evenWidth = 100 / columnGroup.children.length; + columnGroup.children.forEach((col) => { + col.width = `${evenWidth}%`; + }); + + return columnGroup; + } + + const index = columnGroup.children.findIndex((col) => col.id === columnId); + + if (index === -1) return columnGroup; // Column not found + + // Set the new width for the target column + widths[index] = newWidthPercent; + + // Calculate the difference from total (ideally 100) + let totalAfter = widths.reduce((sum, w) => sum + w, 0); + + // If total is off from 100%, adjust siblings + // For simplicity, assume totalAfter < 100%. Add leftover to the next column. + // You can make this logic more balanced if needed. + const diff = 100 - totalAfter; + + if (diff !== 0) { + // Find a sibling to adjust. For a simple strategy, pick the next column. + const siblingIndex = (index + 1) % widths.length; + widths[siblingIndex] = Math.max(widths[siblingIndex] + diff, 0); + } + + // Normalize again if rounding introduced a small error + totalAfter = widths.reduce((sum, w) => sum + w, 0); + + if (Math.round(totalAfter) !== 100) { + // If you want a perfectly balanced approach: + // Scale all widths so they sum exactly to 100 + const scale = 100 / totalAfter; + + for (let i = 0; i < widths.length; i++) { + widths[i] = Number.parseFloat((widths[i] * scale).toFixed(2)); + } + } + + // Update the column widths + columnGroup.children.forEach((col, i) => { + col.width = `${widths[i]}%`; + }); + + return columnGroup; +} diff --git a/packages/layout/src/lib/transforms/setColumnWidth.ts b/packages/layout/src/lib/transforms/setColumnWidth.ts deleted file mode 100644 index f60abd4b82..0000000000 --- a/packages/layout/src/lib/transforms/setColumnWidth.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { PathRef } from 'slate'; - -import { - type SlateEditor, - getChildren, - getNodeEntry, - isElement, - setNodes, -} from '@udecode/plate-common'; - -import type { TColumnElement, TColumnGroupElement } from '../types'; - -import { BaseColumnItemPlugin } from '../BaseColumnPlugin'; - -export const setColumnWidth = ( - editor: SlateEditor, - groupPathRef: PathRef, - layout: Required['layout'] -) => { - const path = groupPathRef.unref()!; - - const columnGroup = getNodeEntry(editor, path); - - if (!columnGroup) throw new Error(`can not find the column group in ${path}`); - - const children = getChildren(columnGroup); - - const childPaths = Array.from(children, (item) => item[1]); - - childPaths.forEach((item, index) => { - const width = layout[index] + '%'; - - if (!width) return; - - setNodes( - editor, - { width: width }, - { - at: item, - match: (n) => isElement(n) && n.type === BaseColumnItemPlugin.key, - } - ); - }); -}; diff --git a/packages/layout/src/lib/transforms/setColumns.spec.tsx b/packages/layout/src/lib/transforms/setColumns.spec.tsx new file mode 100644 index 0000000000..a851c72dee --- /dev/null +++ b/packages/layout/src/lib/transforms/setColumns.spec.tsx @@ -0,0 +1,348 @@ +import type { Path } from 'slate'; + +import { insertNodes } from '@udecode/plate-common'; +import { createPlateEditor } from '@udecode/plate-common/react'; + +import { BaseColumnItemPlugin, BaseColumnPlugin } from '../BaseColumnPlugin'; +import { setColumns } from './setColumns'; + +describe('setColumns', () => { + let editor: ReturnType; + let columnGroupPath: Path; + + beforeEach(() => { + editor = createPlateEditor({ + plugins: [BaseColumnItemPlugin, BaseColumnPlugin], + // Initial value: a column_group with 2 columns + value: [ + { + children: [ + { + children: [{ children: [{ text: 'Column 1 text' }], type: 'p' }], + type: 'column', + width: '50%', + }, + { + children: [{ children: [{ text: 'Column 2 text' }], type: 'p' }], + type: 'column', + width: '50%', + }, + ], + type: 'column_group', + }, + ], + }); + columnGroupPath = [0]; + }); + + it('should update widths if same number of columns', () => { + // Currently 2 columns, set new widths for these 2 columns + setColumns(editor, { + at: columnGroupPath, + widths: ['30%', '70%'], + }); + + const node = editor.children[0] as any; + expect(node.children).toHaveLength(2); + expect(node.children[0].width).toBe('30%'); + expect(node.children[1].width).toBe('70%'); + // Content remains the same + expect(node.children[0].children[0].children[0].text).toBe('Column 1 text'); + expect(node.children[1].children[0].children[0].text).toBe('Column 2 text'); + }); + + it('should insert new columns if targetCount > currentCount', () => { + // Currently 2 columns, want 3 columns + setColumns(editor, { + at: columnGroupPath, + widths: ['33%', '33%', '33%'], + }); + + const node = editor.children[0] as any; + expect(node.children).toHaveLength(3); + + // First two columns updated + expect(node.children[0].width).toBe('33%'); + expect(node.children[1].width).toBe('33%'); + + // New column inserted + expect(node.children[2].width).toBe('33%'); + // Should have a default block inside + expect(node.children[2].children).toHaveLength(1); + expect(node.children[2].children[0].type).toBe('p'); + // Original content untouched in the first two columns + expect(node.children[0].children[0].children[0].text).toBe('Column 1 text'); + expect(node.children[1].children[0].children[0].text).toBe('Column 2 text'); + }); + + it('should merge columns and remove extras if targetCount < currentCount', () => { + // Setup initial state with 3 columns + editor.children = [ + { + children: [ + { + children: [{ children: [{ text: 'C1 text' }], type: 'p' }], + type: 'column', + width: '33%', + }, + { + children: [{ children: [{ text: 'C2 text' }], type: 'p' }], + type: 'column', + width: '33%', + }, + { + children: [{ children: [{ text: 'C3 text' }], type: 'p' }], + type: 'column', + width: '33%', + }, + ], + type: 'column_group', + }, + ]; + + // Now reduce to 2 columns + setColumns(editor, { + at: columnGroupPath, + widths: ['50%', '50%'], + }); + + const node = editor.children[0] as any; + expect(node.children).toHaveLength(2); + // Check widths updated + expect(node.children[0].width).toBe('50%'); + expect(node.children[1].width).toBe('50%'); + + // Content from column 3 should have moved into column 2 + const col1Text = node.children[0].children[0].children[0].text; + const col2TextChildren = node.children[1].children.flatMap( + (n: any) => n.children + ); + const col2Texts = col2TextChildren.map((t: any) => t.text); + + expect(col1Text).toBe('C1 text'); + expect(col2Texts).toContain('C2 text'); + expect(col2Texts).toContain('C3 text'); + + // Column 3 should now be removed + }); + + it('should do nothing if no path is provided', () => { + // Call without at + setColumns(editor, { widths: ['100%'] }); + + const node = editor.children[0] as any; + // Should remain unchanged + expect(node.children).toHaveLength(2); + expect(node.children[0].width).toBe('50%'); + expect(node.children[1].width).toBe('50%'); + }); + + it('should do nothing if node is not found at the given path', () => { + setColumns(editor, { at: [999], widths: ['100%'] }); + + const node = editor.children[0] as any; + // Should remain unchanged + expect(node.children).toHaveLength(2); + expect(node.children[0].width).toBe('50%'); + expect(node.children[1].width).toBe('50%'); + }); + + it('should do nothing if widths array is empty', () => { + setColumns(editor, { at: columnGroupPath, widths: [] }); + + const node = editor.children[0] as any; + expect(node.children).toHaveLength(2); + // Should remain unchanged + expect(node.children[0].width).toBe('50%'); + expect(node.children[1].width).toBe('50%'); + expect(node.children[0].children[0].children[0].text).toBe('Column 1 text'); + expect(node.children[1].children[0].children[0].text).toBe('Column 2 text'); + }); + + it('should handle decimal widths', () => { + setColumns(editor, { at: columnGroupPath, widths: ['33.3%', '66.7%'] }); + + const node = editor.children[0] as any; + expect(node.children).toHaveLength(2); + expect(node.children[0].width).toBe('33.3%'); + expect(node.children[1].width).toBe('66.7%'); + }); + + it('should handle widths that do not sum to 100%', () => { + setColumns(editor, { at: columnGroupPath, widths: ['40%', '40%'] }); + + const node = editor.children[0] as any; + expect(node.children).toHaveLength(2); + // Even though this sums to 80%, we do not enforce total width + expect(node.children[0].width).toBe('40%'); + expect(node.children[1].width).toBe('40%'); + }); + + it('should handle multiple toggles without losing content', () => { + // Start: 2 columns + // Toggle to 3 columns + setColumns(editor, { at: columnGroupPath, widths: ['33%', '33%', '34%'] }); + + let node = editor.children[0] as any; + expect(node.children).toHaveLength(3); + expect(node.children[0].children[0].children[0].text).toBe('Column 1 text'); + expect(node.children[1].children[0].children[0].text).toBe('Column 2 text'); + expect(node.children[2].width).toBe('34%'); + + // Add some new content in the third column + insertNodes( + editor, + { children: [{ text: 'Column 3 text' }], type: 'p' }, + { + at: [0, 2, 1], + } + ); + + // Toggle back to 2 columns + setColumns(editor, { at: columnGroupPath, widths: ['50%', '50%'] }); + + node = editor.children[0] as any; + expect(node.children).toHaveLength(2); + // Col3 content should have merged into column 2 + expect(node.children[1].children[0].children[0].text).toBe('Column 2 text'); + expect(node.children[1].children[1].children[0].text).toBe('Column 3 text'); + + // Toggle again to 3 columns + setColumns(editor, { at: columnGroupPath, widths: ['33%', '33%', '34%'] }); + + node = editor.children[0] as any; + expect(node.children).toHaveLength(3); + // Column 3 added again with empty content + expect(node.children[2].children.length).toBeGreaterThan(0); + // Original content is still preserved in columns 2 + expect(node.children[1].children[0].children[0].text).toBe('Column 2 text'); + expect(node.children[1].children[1].children[0].text).toBe('Column 3 text'); + expect(node.children[2].children[0].children[0].text).toBe(''); + }); + + it('should gracefully handle toggling to zero columns (though not practical)', () => { + // Set columns to an empty widths array (no columns) + setColumns(editor, { at: columnGroupPath, widths: [] }); + + // Should have done nothing as per previous test, but let's check stability + const node = editor.children[0] as any; + expect(node.children).toHaveLength(2); + }); + + it('should append content to the end when merging columns', () => { + // Setup initial state with 3 columns + editor.children = [ + { + children: [ + { + children: [{ children: [{ text: 'Col 1' }], type: 'p' }], + type: 'column', + width: '33%', + }, + { + children: [ + { children: [{ text: '21' }], type: 'p' }, + { children: [{ text: '22' }], type: 'p' }, + { children: [{ text: '23' }], type: 'p' }, + { children: [{ text: '24' }], type: 'p' }, + { children: [{ text: '25' }], type: 'p' }, + ], + type: 'column', + width: '33%', + }, + { + children: [ + { children: [{ text: 'Col 3 first' }], type: 'p' }, + { children: [{ text: 'Col 3 second' }], type: 'p' }, + ], + type: 'column', + width: '33%', + }, + ], + type: 'column_group', + }, + ]; + + // Reduce to 2 columns + setColumns(editor, { + at: columnGroupPath, + widths: ['50%', '50%'], + }); + + const node = editor.children[0] as any; + expect(node.children).toHaveLength(2); + + // Check column 2's content order + const col2Children = node.children[1].children; + expect(col2Children).toHaveLength(7); + expect(col2Children[0].children[0].text).toBe('21'); + expect(col2Children[1].children[0].text).toBe('22'); + expect(col2Children[2].children[0].text).toBe('23'); + expect(col2Children[3].children[0].text).toBe('24'); + expect(col2Children[4].children[0].text).toBe('25'); + expect(col2Children[5].children[0].text).toBe('Col 3 first'); + expect(col2Children[6].children[0].text).toBe('Col 3 second'); + }); + + it('should correctly merge multiple columns when reducing from 4 to 2', () => { + // Setup initial state with 4 columns + editor.children = [ + { + children: [ + { + children: [{ children: [{ text: 'Col 1' }], type: 'p' }], + type: 'column', + width: '25%', + }, + { + children: [ + { children: [{ text: 'Col 2 first' }], type: 'p' }, + { children: [{ text: 'Col 2 second' }], type: 'p' }, + ], + type: 'column', + width: '25%', + }, + { + children: [ + { children: [{ text: 'Col 3 first' }], type: 'p' }, + { children: [{ text: 'Col 3 second' }], type: 'p' }, + ], + type: 'column', + width: '25%', + }, + { + children: [ + { children: [{ text: 'Col 4 first' }], type: 'p' }, + { children: [{ text: 'Col 4 second' }], type: 'p' }, + ], + type: 'column', + width: '25%', + }, + ], + type: 'column_group', + }, + ]; + + // Reduce to 2 columns + setColumns(editor, { + at: columnGroupPath, + widths: ['50%', '50%'], + }); + + const node = editor.children[0] as any; + expect(node.children).toHaveLength(2); + + // Check column 1's content (should be unchanged) + expect(node.children[0].children[0].children[0].text).toBe('Col 1'); + + // Check column 2's content order (should have content from cols 2, 3, and 4) + const col2Children = node.children[1].children; + expect(col2Children).toHaveLength(6); + expect(col2Children[0].children[0].text).toBe('Col 2 first'); + expect(col2Children[1].children[0].text).toBe('Col 2 second'); + expect(col2Children[2].children[0].text).toBe('Col 3 first'); + expect(col2Children[3].children[0].text).toBe('Col 3 second'); + expect(col2Children[4].children[0].text).toBe('Col 4 first'); + expect(col2Children[5].children[0].text).toBe('Col 4 second'); + }); +}); diff --git a/packages/layout/src/lib/transforms/setColumns.ts b/packages/layout/src/lib/transforms/setColumns.ts new file mode 100644 index 0000000000..5ba2898e4f --- /dev/null +++ b/packages/layout/src/lib/transforms/setColumns.ts @@ -0,0 +1,116 @@ +import type { Path } from 'slate'; + +import { + type SlateEditor, + getNode, + getNodeEntry, + insertNodes, + moveChildren, + removeNodes, + setNodes, +} from '@udecode/plate-common'; + +import type { TColumnElement, TColumnGroupElement } from '../types'; + +import { BaseColumnItemPlugin } from '../BaseColumnPlugin'; +import { columnsToWidths } from '../utils/columnsToWidths'; + +export const setColumns = ( + editor: SlateEditor, + { + at, + columns, + widths, + }: { + /** Column group path */ + at?: Path; + columns?: number; + widths?: string[]; + } +) => { + editor.withoutNormalizing(() => { + if (!at) return; + + widths = widths ?? columnsToWidths({ columns }); + + // If widths is empty, do nothing. + if (widths.length === 0) { + return; + } + + const columnGroup = getNode(editor, at); + + if (!columnGroup) return; + + const { children } = columnGroup; + + const currentCount = children.length; + const targetCount = widths.length; + + if (currentCount === targetCount) { + // Same number of columns: just set widths directly + widths.forEach((width, i) => { + setNodes(editor, { width }, { at: at.concat([i]) }); + }); + + return; + } + if (targetCount > currentCount) { + // Need more columns than we have: insert extra columns at the end + const columnsToAdd = targetCount - currentCount; + const insertPath = at.concat([currentCount]); + + // Insert the extra columns + const newColumns = Array(columnsToAdd) + .fill(null) + .map((_, i) => ({ + children: [editor.api.create.block()], + type: editor.getType(BaseColumnItemPlugin), + width: widths![currentCount + i] || `${100 / targetCount}%`, + })); + + insertNodes(editor, newColumns, { at: insertPath }); + + // Just ensure final widths match exactly + widths.forEach((width, i) => { + setNodes(editor, { width }, { at: at.concat([i]) }); + }); + + return; + } + if (targetCount < currentCount) { + // Need fewer columns than we have: merge extra columns into the last kept column + const keepColumnIndex = targetCount - 1; + const keepColumnPath = at.concat([keepColumnIndex]); + const keepColumnNode = getNode(editor, keepColumnPath); + + if (!keepColumnNode) return; + + const to = keepColumnPath.concat([keepColumnNode.children.length]); + + // Move content from columns beyond keepIndex into keepIndex column + for (let i = currentCount - 1; i > keepColumnIndex; i--) { + const columnPath = at.concat([i]); + const columnEntry = getNodeEntry(editor, columnPath); + + if (!columnEntry) continue; + + moveChildren(editor, { + at: columnEntry[1], + to, + }); + } + + // Remove the now-empty extra columns + // Removing from the end to avoid path shifts + for (let i = currentCount - 1; i > keepColumnIndex; i--) { + removeNodes(editor, { at: at.concat([i]) }); + } + + // Set the final widths + widths.forEach((width, i) => { + setNodes(editor, { width }, { at: at.concat([i]) }); + }); + } + }); +}; diff --git a/packages/layout/src/lib/transforms/toggleColumnGroup.spec.tsx b/packages/layout/src/lib/transforms/toggleColumnGroup.spec.tsx new file mode 100644 index 0000000000..acdd11764e --- /dev/null +++ b/packages/layout/src/lib/transforms/toggleColumnGroup.spec.tsx @@ -0,0 +1,183 @@ +import type { Path } from 'slate'; + +import { getStartPoint, select } from '@udecode/plate-common'; +import { createPlateEditor } from '@udecode/plate-common/react'; + +import { BaseColumnItemPlugin, BaseColumnPlugin } from '../BaseColumnPlugin'; +import { toggleColumnGroup } from './toggleColumnGroup'; + +describe('toggleColumnGroup', () => { + let editor: ReturnType; + let initialValue: any[]; + + beforeEach(() => { + initialValue = [ + { + children: [{ text: 'Some paragraph text' }], + type: 'p', + }, + ]; + + editor = createPlateEditor({ + plugins: [BaseColumnItemPlugin, BaseColumnPlugin], + value: initialValue, + }); + }); + + it('should wrap a paragraph in a column group when toggling from a paragraph', () => { + const at: Path = [0, 0]; // Inside the paragraph text + select(editor, getStartPoint(editor, at)); + + // Toggle to 2 columns + toggleColumnGroup(editor, { columns: 2 }); + + const node: any = editor.children[0]; + expect(node.type).toBe('column_group'); + expect(node.children).toHaveLength(2); + expect(node.children[0].type).toBe('column'); + expect(node.children[1].type).toBe('column'); + expect(node.children[0].children[0].children[0].text).toBe( + 'Some paragraph text' + ); + // The second column should have a newly created block + expect(node.children[1].children[0].type).toBe('p'); + expect(node.children[1].children[0].children[0].text).toBe(''); + }); + + it('should update the number of columns if already a column group', () => { + // Start with a column group of 2 columns + editor.children = [ + { + children: [ + { + children: [{ children: [{ text: 'Col1 text' }], type: 'p' }], + type: 'column', + width: '50%', + }, + { + children: [{ children: [{ text: 'Col2 text' }], type: 'p' }], + type: 'column', + width: '50%', + }, + ], + type: 'column_group', + }, + ]; + + const columnGroupPath: Path = [0]; + select(editor, getStartPoint(editor, columnGroupPath.concat([0, 0, 0]))); + + // Toggle to 3 columns (from 2 columns) + toggleColumnGroup(editor, { columns: 3 }); + + const node: any = editor.children[0]; + expect(node.type).toBe('column_group'); + expect(node.children).toHaveLength(3); + + // All widths should be adjusted + expect(node.children[0].width).toContain('33.3333'); + expect(node.children[1].width).toContain('33.3333'); + expect(node.children[2].width).toContain('33.3333'); + + // Content from the original 2 columns should still exist + expect(node.children[0].children[0].children[0].text).toBe('Col1 text'); + expect(node.children[1].children[0].children[0].text).toBe('Col2 text'); + + // The new 3rd column should have one empty paragraph + expect(node.children[2].children).toHaveLength(1); + expect(node.children[2].children[0].type).toBe('p'); + expect(node.children[2].children[0].children[0].text).toBe(''); + }); + + it('should preserve content when toggling between different column counts', () => { + // Start with a column group of 2 columns + editor.children = [ + { + children: [ + { + children: [{ children: [{ text: 'Col1 text' }], type: 'p' }], + type: 'column', + width: '50%', + }, + { + children: [{ children: [{ text: 'Col2 text' }], type: 'p' }], + type: 'column', + width: '50%', + }, + ], + type: 'column_group', + }, + ]; + + const columnGroupPath: Path = [0]; + select(editor, getStartPoint(editor, columnGroupPath)); + + // Toggle to 3 columns + toggleColumnGroup(editor, { columns: 3 }); + let node: any = editor.children[0]; + expect(node.children).toHaveLength(3); + + // Insert content in the third column + editor.apply({ + node: { children: [{ text: 'Col3 extra text' }], type: 'p' }, + path: [0, 2, 1], + type: 'insert_node', + }); + + // Toggle back to 2 columns + toggleColumnGroup(editor, { columns: 2 }); + node = editor.children[0]; + expect(node.children).toHaveLength(2); + + // Col3 content should have merged into column 2 + const col2Texts = node.children[1].children + .flatMap((child: any) => child.children) + .map((child: any) => child.text); + + expect(col2Texts).toContain('Col2 text'); + expect(col2Texts).toContain('Col3 extra text'); + }); + + it('should do nothing if no selection is provided', () => { + // No selection + toggleColumnGroup(editor, { columns: 2 }); + // Should remain a single paragraph + const node = editor.children[0]; + expect(node.type).toBe('p'); + }); + + it('should handle toggling from a selection inside a column as well', () => { + // Start with a column group of 2 columns + editor.children = [ + { + children: [ + { + children: [{ children: [{ text: 'Col1 text' }], type: 'p' }], + type: 'column', + width: '50%', + }, + { + children: [{ children: [{ text: 'Col2 text' }], type: 'p' }], + type: 'column', + width: '50%', + }, + ], + type: 'column_group', + }, + ]; + const columnGroupPath: Path = [0]; + // Select inside second column's paragraph + select(editor, getStartPoint(editor, [0, 1, 0, 0])); + + // Toggle to 3 columns + toggleColumnGroup(editor, { columns: 3 }); + const node: any = editor.children[0]; + + expect(node.children).toHaveLength(3); + // Should keep Col1 text and Col2 text + expect(node.children[0].children[0].children[0].text).toBe('Col1 text'); + expect(node.children[1].children[0].children[0].text).toBe('Col2 text'); + // New column + expect(node.children[2].children[0].children[0].text).toBe(''); + }); +}); diff --git a/packages/layout/src/lib/transforms/toggleColumnGroup.ts b/packages/layout/src/lib/transforms/toggleColumnGroup.ts index 671cbfc85f..a5bc388795 100644 --- a/packages/layout/src/lib/transforms/toggleColumnGroup.ts +++ b/packages/layout/src/lib/transforms/toggleColumnGroup.ts @@ -9,40 +9,54 @@ import { } from '@udecode/plate-common'; import { BaseColumnItemPlugin, BaseColumnPlugin } from '../BaseColumnPlugin'; +import { columnsToWidths } from '../utils/columnsToWidths'; +import { setColumns } from './setColumns'; export const toggleColumnGroup = ( editor: SlateEditor, { at, - layout = 2, + columns = 2, + widths, }: Partial, 'nodes'>> & { - layout?: number[] | number; + columns?: number; + widths?: string[]; } = {} ) => { const entry = getBlockAbove(editor, { at }); + const columnGroupEntry = getBlockAbove(editor, { + at, + match: { type: editor.getType(BaseColumnPlugin) }, + }); if (!entry) return; - const [node] = entry; - - const columnLayout = Array.isArray(layout) - ? layout - : Array(layout).fill(Math.floor(100 / layout)); - - const nodes = { - children: columnLayout.map((width, index) => ({ - children: [index === 0 ? node : editor.api.create.block()], - type: BaseColumnItemPlugin.key, - width: `${width}%`, - })), - layout: columnLayout, - type: BaseColumnPlugin.key, - } as TElement; - - replaceNode(editor, { - at: entry[1], - nodes, - }); - - select(editor, getStartPoint(editor, entry[1].concat([0]))); + const [node, path] = entry; + + // Check if the node is already a column_group + if (columnGroupEntry) { + // Node is already a column_group, just update the columns using setColumns + setColumns(editor, { at: columnGroupEntry[1], columns, widths }); + } else { + // Node is not a column_group, wrap it in a column_group + const columnWidths = widths || columnsToWidths({ columns }); + + const nodes = { + children: Array(columns) + .fill(null) + .map((_, index) => ({ + children: [index === 0 ? node : editor.api.create.block()], + type: BaseColumnItemPlugin.key, + width: columnWidths[index], + })), + type: BaseColumnPlugin.key, + } as TElement; + + replaceNode(editor, { + at: path, + nodes, + }); + + select(editor, getStartPoint(editor, path.concat([0]))); + } }; diff --git a/packages/layout/src/lib/types.ts b/packages/layout/src/lib/types.ts index 4b391e159f..b4a757b5c9 100644 --- a/packages/layout/src/lib/types.ts +++ b/packages/layout/src/lib/types.ts @@ -10,5 +10,4 @@ export interface TColumnGroupElement extends TElement { children: TColumnElement[]; type: 'column_group'; id?: string; - layout?: number[]; } diff --git a/packages/layout/src/lib/utils/columnsToWidths.ts b/packages/layout/src/lib/utils/columnsToWidths.ts new file mode 100644 index 0000000000..db9e1e415b --- /dev/null +++ b/packages/layout/src/lib/utils/columnsToWidths.ts @@ -0,0 +1,4 @@ +export const columnsToWidths = ({ columns = 2 }: { columns?: number } = {}) => + Array(columns) + .fill(null) + .map((_, i) => `${100 / columns}%`); diff --git a/packages/layout/src/lib/utils/index.ts b/packages/layout/src/lib/utils/index.ts new file mode 100644 index 0000000000..0a70ca33f6 --- /dev/null +++ b/packages/layout/src/lib/utils/index.ts @@ -0,0 +1,5 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './columnsToWidths'; diff --git a/packages/layout/src/lib/withColumn.ts b/packages/layout/src/lib/withColumn.ts index 1c864e56d8..0a0450fdaf 100644 --- a/packages/layout/src/lib/withColumn.ts +++ b/packages/layout/src/lib/withColumn.ts @@ -1,8 +1,6 @@ import { type ExtendEditor, - createPathRef, getAboveNode, - getLastChildPath, isCollapsed, isElement, isStartPoint, @@ -13,17 +11,18 @@ import { import type { TColumnElement, TColumnGroupElement } from './types'; import { BaseColumnItemPlugin, BaseColumnPlugin } from './BaseColumnPlugin'; -import { insertColumn, moveMiddleColumn, setColumnWidth } from './transforms'; export const withColumn: ExtendEditor = ({ editor }) => { - const { deleteBackward, isEmpty, normalizeNode } = editor; + const { deleteBackward, normalizeNode } = editor; editor.normalizeNode = (entry) => { const [n, path] = entry; + // If it's a column group, ensure it has valid children if (isElement(n) && n.type === BaseColumnPlugin.key) { const node = n as TColumnGroupElement; + // If no columns found, unwrap the column group if ( !node.children.some( (child) => @@ -35,6 +34,7 @@ export const withColumn: ExtendEditor = ({ editor }) => { return; } + // If only one column remains, unwrap the group (optional logic) if (node.children.length < 2) { editor.withoutNormalizing(() => { unwrapNodes(editor, { at: path }); @@ -43,39 +43,41 @@ export const withColumn: ExtendEditor = ({ editor }) => { return; } + } - const prevChildrenCnt = node.children.length; - const currentLayout = node.layout; + // const prevChildrenCnt = node.children.length; + // const currentLayout = node.layout; - if (currentLayout) { - const currentChildrenCnt = currentLayout.length; + // if (currentLayout) { + // const currentChildrenCnt = currentLayout.length; - const groupPathRef = createPathRef(editor, path); + // const groupPathRef = createPathRef(editor, path); - if (prevChildrenCnt === 2 && currentChildrenCnt === 3) { - const lastChildPath = getLastChildPath(entry); + // if (prevChildrenCnt === 2 && currentChildrenCnt === 3) { + // const lastChildPath = getLastChildPath(entry); - insertColumn(editor, { - at: lastChildPath, - }); + // insertColumn(editor, { + // at: lastChildPath, + // }); - setColumnWidth(editor, groupPathRef, currentLayout); + // setColumnWidth(editor, groupPathRef, currentLayout); - return; - } - if (prevChildrenCnt === 3 && currentChildrenCnt === 2) { - moveMiddleColumn(editor, entry, { direction: 'left' }); - setColumnWidth(editor, groupPathRef, currentLayout); + // return; + // } + // if (prevChildrenCnt === 3 && currentChildrenCnt === 2) { + // moveMiddleColumn(editor, entry, { direction: 'left' }); + // setColumnWidth(editor, groupPathRef, currentLayout); - return; - } - if (prevChildrenCnt === currentChildrenCnt) { - setColumnWidth(editor, groupPathRef, currentLayout); + // return; + // } + // if (prevChildrenCnt === currentChildrenCnt) { + // setColumnWidth(editor, groupPathRef, currentLayout); - return; - } - } - } + // return; + // } + // } + + // If it's a column, ensure it has at least one block (optional) if (isElement(n) && n.type === BaseColumnItemPlugin.key) { const node = n as TColumnElement; @@ -109,13 +111,5 @@ export const withColumn: ExtendEditor = ({ editor }) => { deleteBackward(unit); }; - editor.isEmpty = (element: any) => { - if (element?.type && element.type === BaseColumnItemPlugin.key) { - return element.children.length === 1 && isEmpty(element.children[0]); - } - - return isEmpty(element); - }; - return editor; }; diff --git a/packages/layout/src/react/column-store.ts b/packages/layout/src/react/column-store.ts deleted file mode 100644 index 5cdbb13831..0000000000 --- a/packages/layout/src/react/column-store.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { setNodes } from '@udecode/plate-common'; -import { - findPath, - useEditorRef, - useElement, -} from '@udecode/plate-common/react'; - -import type { TColumnGroupElement } from '../lib/types'; - -import { ColumnPlugin } from './ColumnPlugin'; - -export const useColumnState = () => { - const editor = useEditorRef(); - - const columnGroupElement = useElement(ColumnPlugin.key); - - const columnPath = findPath(editor, columnGroupElement); - - const setDoubleColumn = () => { - setNodes(editor, { layout: [50, 50] }, { at: columnPath }); - }; - - const setThreeColumn = () => { - setNodes(editor, { layout: [33, 33, 33] }, { at: columnPath }); - }; - - const setRightSideDoubleColumn = () => { - setNodes(editor, { layout: [70, 30] }, { at: columnPath }); - }; - - const setLeftSideDoubleColumn = () => { - setNodes(editor, { layout: [30, 70] }, { at: columnPath }); - }; - - const setDoubleSideDoubleColumn = () => { - setNodes(editor, { layout: [25, 50, 25] }, { at: columnPath }); - }; - - return { - setDoubleColumn, - setDoubleSideDoubleColumn, - setLeftSideDoubleColumn, - setRightSideDoubleColumn, - setThreeColumn, - }; -}; diff --git a/packages/layout/src/react/index.ts b/packages/layout/src/react/index.ts index f9190c9f6e..757a6f621e 100644 --- a/packages/layout/src/react/index.ts +++ b/packages/layout/src/react/index.ts @@ -3,6 +3,5 @@ */ export * from './ColumnPlugin'; -export * from './column-store'; export * from './onKeyDownColumn'; export * from './hooks/index';