From 2db833aded69999150a51d2e18191d1fd01771eb Mon Sep 17 00:00:00 2001 From: zbeyens Date: Thu, 10 Oct 2024 05:01:15 +0200 Subject: [PATCH 1/3] feat --- .changeset/serious-baboons-study.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/serious-baboons-study.md diff --git a/.changeset/serious-baboons-study.md b/.changeset/serious-baboons-study.md new file mode 100644 index 0000000000..226c6268f2 --- /dev/null +++ b/.changeset/serious-baboons-study.md @@ -0,0 +1,5 @@ +--- +'@udecode/slate-utils': patch +--- + +a From b622f6605b04476403cb6023c31c1baa4919ea21 Mon Sep 17 00:00:00 2001 From: zbeyens Date: Thu, 10 Oct 2024 05:01:59 +0200 Subject: [PATCH 2/3] feat --- .changeset/afraid-coins-share.md | 5 + .changeset/great-bottles-exist.md | 7 + .changeset/honest-lamps-work.md | 5 + .changeset/lovely-ads-push.md | 17 + .changeset/quick-swans-repeat.md | 9 + .changeset/rotten-hornets-fly.md | 7 + .changeset/serious-baboons-study.md | 4 +- .changeset/sixty-wombats-roll.md | 5 + apps/www/content/docs/api/core.mdx | 85 +++++ apps/www/content/docs/api/slate-react.mdx | 41 +++ apps/www/content/docs/api/slate-utils.mdx | 146 ++++++++ apps/www/content/docs/block-selection.mdx | 66 +++- .../www/content/docs/components/changelog.mdx | 6 + .../playground-more-dropdown-menu.tsx | 2 +- .../playground-turn-into-dropdown-menu.tsx | 71 ++-- .../default/plate-ui/dropdown-menu.tsx | 8 +- .../default/plate-ui/floating-toolbar.tsx | 6 +- .../plate-ui/turn-into-dropdown-menu.tsx | 56 ++- config/eslint/bases/tailwind.cjs | 1 + packages/ai/package.json | 1 - packages/ai/src/react/ai/AIPlugin.ts | 3 +- packages/ai/src/react/ai/hook/useAI.ts | 40 +-- packages/ai/src/react/ai/types.ts | 10 +- packages/ai/src/react/ai/utils/getContent.ts | 2 +- .../src/react/components/PlateContent.tsx | 1 + .../core/src/react/hooks/useEditableProps.ts | 32 +- .../floating/src/hooks/useFloatingToolbar.ts | 21 +- .../floating/src/hooks/useVirtualFloating.ts | 8 +- .../utils/getSelectionBoundingClientRect.ts | 20 +- .../FloatingLink/useFloatingLinkEdit.ts | 2 +- .../FloatingLink/useFloatingLinkInsert.ts | 2 +- packages/menu/.npmignore | 3 - packages/menu/CHANGELOG.md | 7 - packages/menu/README.md | 1 - packages/menu/package.json | 71 ---- packages/menu/src/hooks/index.ts | 6 - packages/menu/src/hooks/useMenu.tsx | 96 ------ packages/menu/src/hooks/useMenuItem.tsx | 113 ------ packages/menu/src/index.ts | 8 - packages/menu/src/lib/Ariakit.ts | 3 - packages/menu/src/lib/index.ts | 5 - packages/menu/src/types/action.ts | 17 - packages/menu/src/types/index.ts | 7 - packages/menu/src/types/menu.ts | 37 -- packages/menu/src/types/menuItem.ts | 13 - packages/menu/src/utils/buildMenuTree.ts | 23 -- .../menu/src/utils/filterAndBuildMenuTree.ts | 21 -- packages/menu/src/utils/flattenMenuTree.ts | 20 -- packages/menu/src/utils/index.ts | 8 - packages/menu/src/utils/useOnClickOutside.ts | 120 ------- packages/menu/tsconfig.build.json | 8 - packages/menu/tsconfig.json | 5 - packages/plate-utils/src/react/index.ts | 2 + .../src/react/useMarkToolbarButton.ts | 2 + .../plate-utils/src/react/useSelection.ts | 22 ++ .../src/react/useSelectionFragment.ts | 24 ++ .../selection/src/internal/SelectionArea.ts | 1 + .../src/react/BlockContextMenuPlugin.tsx | 80 ----- .../selection/src/react/BlockMenuPlugin.tsx | 79 +++++ .../src/react/BlockSelectionAfterEditable.tsx | 198 ----------- .../src/react/BlockSelectionPlugin.tsx | 325 +++++++++++++++--- .../selection/src/react/context-menu/index.ts | 7 - .../selection/src/react/context-menu/types.ts | 14 - .../react/context-menu/useBlockContextMenu.ts | 40 --- .../react/context-menu/useBlockMenuItems.ts | 23 -- packages/selection/src/react/hooks/index.ts | 8 + .../src/react/{ => hooks}/useBlockSelected.ts | 2 +- .../src/react/hooks/useBlockSelectionNodes.ts | 39 +++ .../src/react/{ => hooks}/useIsSelecting.ts | 4 +- .../src/react/{ => hooks}/useSelectionArea.ts | 4 +- packages/selection/src/react/index.ts | 9 +- .../duplicateBlockSelectionNodes.ts | 38 ++ .../selection/src/react/transforms/index.ts | 8 + .../transforms/removeBlockSelectionNodes.ts | 14 + .../transforms/selectBlockSelectionNodes.ts | 11 + .../transforms/setBlockSelectionNodes.ts | 42 +++ .../src/react/utils/copySelectedBlocks.ts | 14 +- packages/selection/src/react/utils/index.ts | 1 - .../src/react/utils/onChangeBlockSelection.ts | 10 +- .../src/react/utils/openContextMenu.ts | 29 -- .../src/react/utils/pasteSelectedBlocks.ts | 2 +- packages/slate-react/src/utils/index.ts | 1 + packages/slate-react/src/utils/setNode.ts | 24 ++ packages/slate-utils/src/queries/getBlocks.ts | 18 + .../src/queries/getFragmentProp.ts | 59 ++++ .../src/queries/getSelectionFragment.ts | 25 ++ packages/slate-utils/src/queries/index.ts | 3 + .../src/transforms/duplicateBlocks.ts | 13 + packages/slate-utils/src/transforms/index.ts | 5 + .../transforms/removeEmptyPreviousBlock.ts | 26 ++ .../slate-utils/src/transforms/selectNodes.ts | 11 + .../src/transforms/setBlockAboveNode.ts | 37 ++ .../src/transforms/setBlockNodes.ts | 27 ++ packages/slate-utils/src/utils/index.ts | 1 + .../src/utils/unwrapStructuralNodes.ts | 22 ++ yarn.lock | 208 ++++------- 96 files changed, 1452 insertions(+), 1361 deletions(-) create mode 100644 .changeset/afraid-coins-share.md create mode 100644 .changeset/great-bottles-exist.md create mode 100644 .changeset/honest-lamps-work.md create mode 100644 .changeset/lovely-ads-push.md create mode 100644 .changeset/quick-swans-repeat.md create mode 100644 .changeset/rotten-hornets-fly.md create mode 100644 .changeset/sixty-wombats-roll.md delete mode 100644 packages/menu/.npmignore delete mode 100644 packages/menu/CHANGELOG.md delete mode 100644 packages/menu/README.md delete mode 100644 packages/menu/package.json delete mode 100644 packages/menu/src/hooks/index.ts delete mode 100644 packages/menu/src/hooks/useMenu.tsx delete mode 100644 packages/menu/src/hooks/useMenuItem.tsx delete mode 100644 packages/menu/src/index.ts delete mode 100644 packages/menu/src/lib/Ariakit.ts delete mode 100644 packages/menu/src/lib/index.ts delete mode 100644 packages/menu/src/types/action.ts delete mode 100644 packages/menu/src/types/index.ts delete mode 100644 packages/menu/src/types/menu.ts delete mode 100644 packages/menu/src/types/menuItem.ts delete mode 100644 packages/menu/src/utils/buildMenuTree.ts delete mode 100644 packages/menu/src/utils/filterAndBuildMenuTree.ts delete mode 100644 packages/menu/src/utils/flattenMenuTree.ts delete mode 100644 packages/menu/src/utils/index.ts delete mode 100644 packages/menu/src/utils/useOnClickOutside.ts delete mode 100644 packages/menu/tsconfig.build.json delete mode 100644 packages/menu/tsconfig.json create mode 100644 packages/plate-utils/src/react/useSelection.ts create mode 100644 packages/plate-utils/src/react/useSelectionFragment.ts delete mode 100644 packages/selection/src/react/BlockContextMenuPlugin.tsx create mode 100644 packages/selection/src/react/BlockMenuPlugin.tsx delete mode 100644 packages/selection/src/react/BlockSelectionAfterEditable.tsx delete mode 100644 packages/selection/src/react/context-menu/index.ts delete mode 100644 packages/selection/src/react/context-menu/types.ts delete mode 100644 packages/selection/src/react/context-menu/useBlockContextMenu.ts delete mode 100644 packages/selection/src/react/context-menu/useBlockMenuItems.ts create mode 100644 packages/selection/src/react/hooks/index.ts rename packages/selection/src/react/{ => hooks}/useBlockSelected.ts (83%) create mode 100644 packages/selection/src/react/hooks/useBlockSelectionNodes.ts rename packages/selection/src/react/{ => hooks}/useIsSelecting.ts (85%) rename packages/selection/src/react/{ => hooks}/useSelectionArea.ts (91%) create mode 100644 packages/selection/src/react/transforms/duplicateBlockSelectionNodes.ts create mode 100644 packages/selection/src/react/transforms/index.ts create mode 100644 packages/selection/src/react/transforms/removeBlockSelectionNodes.ts create mode 100644 packages/selection/src/react/transforms/selectBlockSelectionNodes.ts create mode 100644 packages/selection/src/react/transforms/setBlockSelectionNodes.ts delete mode 100644 packages/selection/src/react/utils/openContextMenu.ts create mode 100644 packages/slate-react/src/utils/setNode.ts create mode 100644 packages/slate-utils/src/queries/getBlocks.ts create mode 100644 packages/slate-utils/src/queries/getFragmentProp.ts create mode 100644 packages/slate-utils/src/queries/getSelectionFragment.ts create mode 100644 packages/slate-utils/src/transforms/duplicateBlocks.ts create mode 100644 packages/slate-utils/src/transforms/removeEmptyPreviousBlock.ts create mode 100644 packages/slate-utils/src/transforms/selectNodes.ts create mode 100644 packages/slate-utils/src/transforms/setBlockAboveNode.ts create mode 100644 packages/slate-utils/src/transforms/setBlockNodes.ts create mode 100644 packages/slate-utils/src/utils/unwrapStructuralNodes.ts diff --git a/.changeset/afraid-coins-share.md b/.changeset/afraid-coins-share.md new file mode 100644 index 0000000000..98c628a6c1 --- /dev/null +++ b/.changeset/afraid-coins-share.md @@ -0,0 +1,5 @@ +--- +'@udecode/plate-ai': patch +--- + +WIP diff --git a/.changeset/great-bottles-exist.md b/.changeset/great-bottles-exist.md new file mode 100644 index 0000000000..cf1486c7b6 --- /dev/null +++ b/.changeset/great-bottles-exist.md @@ -0,0 +1,7 @@ +--- +'@udecode/plate-floating': patch +--- + +- `getSelectionBoundingClientRect` is now returning the bounding client rect of the editor selection instead of the dom selection. This is more robust for cases like floating toolbar. +- Update floating toolbar position on value change, in addition to selection change. +- Return `clickOutsideRef` from `useFloatingToolbar` so it can be used to close the toolbar when clicking outside of it. Use `ignore-click-outside/toolbar` class to ignore clicks outside of the toolbar. diff --git a/.changeset/honest-lamps-work.md b/.changeset/honest-lamps-work.md new file mode 100644 index 0000000000..4d8d0c13d9 --- /dev/null +++ b/.changeset/honest-lamps-work.md @@ -0,0 +1,5 @@ +--- +'@udecode/slate-react': patch +--- + +- Add `setNode` diff --git a/.changeset/lovely-ads-push.md b/.changeset/lovely-ads-push.md new file mode 100644 index 0000000000..104f33febc --- /dev/null +++ b/.changeset/lovely-ads-push.md @@ -0,0 +1,17 @@ +--- +'@udecode/plate-selection': minor +--- + +- Add `useBlockSelectionNodes`, `useBlockSelectionFragment`, `useBlockSelectionFragmentProp` +- `BlockSelectionPlugin`: + - Make `setSelectedIds` options optional + - Rename option `getSelectedBlocks` -> `getNodes` + - Extend api: `duplicate`, `removeNodes`, `select`, `setNodes`, `setTexts` +- Rename `BlockContextMenuPlugin` to `BlockMenuPlugin` +- `BlockMenuPlugin` options: + - `position` + - `openId` +- `BlockMenuPlugin` api: + - `hide` + - `show` + - `showContextMenu` diff --git a/.changeset/quick-swans-repeat.md b/.changeset/quick-swans-repeat.md new file mode 100644 index 0000000000..1ac09d771a --- /dev/null +++ b/.changeset/quick-swans-repeat.md @@ -0,0 +1,9 @@ +--- +'@udecode/plate-core': patch +--- + +`PlateContent`: + +- When `disabled=true`, `readOnly` should be `true` +- Add prop `aria-disabled=true` and `data-readonly=true` when `readOnly=true` +- Add class `slate-editor`, `ignore-click-outside/toolbar` (used by floating toolbar) diff --git a/.changeset/rotten-hornets-fly.md b/.changeset/rotten-hornets-fly.md new file mode 100644 index 0000000000..66a2afe70b --- /dev/null +++ b/.changeset/rotten-hornets-fly.md @@ -0,0 +1,7 @@ +--- +'@udecode/plate-utils': patch +--- + +- Update `useMarkToolbarButton().props.onClick`: focus editor after toggle mark +- Add `useSelectionCollapsed`, `useSelectionExpanded`, `useSelectionWithinBlock`, `useSelectionAcrossBlocks` +- Add `useSelectionFragment`, `useSelectionFragmentProp` diff --git a/.changeset/serious-baboons-study.md b/.changeset/serious-baboons-study.md index 226c6268f2..28a8370cb9 100644 --- a/.changeset/serious-baboons-study.md +++ b/.changeset/serious-baboons-study.md @@ -2,4 +2,6 @@ '@udecode/slate-utils': patch --- -a +- Add queries `getBlocks`, `getFragmentProp`, `getSelectionFragment` +- Add transforms `duplicateBlocks`, `removeEmptyPreviousBlock`, `selectNodes`, `setBlockAboveNode`, `setBlockNodes` +- Add utils `unwrapStructuralNodes` diff --git a/.changeset/sixty-wombats-roll.md b/.changeset/sixty-wombats-roll.md new file mode 100644 index 0000000000..0c7b557d9f --- /dev/null +++ b/.changeset/sixty-wombats-roll.md @@ -0,0 +1,5 @@ +--- +'@udecode/plate-link': patch +--- + +Floating patch diff --git a/apps/www/content/docs/api/core.mdx b/apps/www/content/docs/api/core.mdx index d67188445b..f033d49df0 100644 --- a/apps/www/content/docs/api/core.mdx +++ b/apps/www/content/docs/api/core.mdx @@ -308,6 +308,91 @@ Get the version of the editor selection. That version is incremented on each sel The current version of the editor selection. +### useSelectionCollapsed + +Returns whether the current selection is collapsed (i.e., the selection is a single point). + + + A boolean value indicating if the selection is collapsed. + + +### useSelectionExpanded + +Returns whether the current selection is expanded (i.e., the selection has a non-zero range). + + + A boolean value indicating if the selection is expanded. + + +### useSelectionWithinBlock + +Returns whether the current selection is within a single block. + + + A boolean value indicating if the selection is within a single block. + + +### useSelectionAcrossBlocks + +Returns whether the current selection spans across multiple blocks. + + + A boolean value indicating if the selection spans across multiple blocks. + + +### useSelectionFragment + +Returns the fragment of the current selection, optionally unwrapping structural nodes. + + + + Options for getting the selection fragment. + + + Array of structural types to unwrap from the fragment. + + + + + + + An array of `TElement` representing the fragment of the current selection. Returns an empty array if the selection is not expanded or if no fragment is found. + + +### useSelectionFragmentProp + +Returns a prop value derived from the current selection fragment. + + + + + + Array of structural types to unwrap from the fragment. + + + The key of the property to extract from each node. + + + The default value to return if no valid prop is found. + + + Custom function to extract the prop value from a node. + + + Determines how to traverse the fragment: + - 'all': Check both block and text nodes + - 'block': Only check block nodes + - 'text': Only check text nodes + Default is 'block'. + + + + + + + A value derived from the fragment nodes, or undefined if no consistent value is found across the specified nodes. + + ## Core plugins ### DebugPlugin diff --git a/apps/www/content/docs/api/slate-react.mdx b/apps/www/content/docs/api/slate-react.mdx index dfd4b2f09b..cb45fbf4de 100644 --- a/apps/www/content/docs/api/slate-react.mdx +++ b/apps/www/content/docs/api/slate-react.mdx @@ -58,3 +58,44 @@ Find the corresponding documentation in the + The editor instance. + + + + + The edge to focus on. + - 'start': Focus at the beginning of the editor. + - 'end': Focus at the end of the editor. + Default is 'start'. + + + + + +### setNode + +Sets properties on a specific node in the editor. + + + + The editor instance. + + + The node to update. + + + The properties to set on the node. + + + Options for setting nodes, excluding the 'at' property. + + \ No newline at end of file diff --git a/apps/www/content/docs/api/slate-utils.mdx b/apps/www/content/docs/api/slate-utils.mdx index 11958f99d5..1d93561ca5 100644 --- a/apps/www/content/docs/api/slate-utils.mdx +++ b/apps/www/content/docs/api/slate-utils.mdx @@ -45,6 +45,23 @@ Returns the block above the specified location. +### getBlocks + +Retrieves block-level node entries from the editor. + + + + The editor to search for block-level nodes. + + + Options for getting node entries. + + + + + An array of block-level node entries. + + ### getChildren Returns the children node entries of a node entry. @@ -79,6 +96,40 @@ Returns the edge blocks above a specified location. location, or `null` if not found. +### getFragmentProp + +Retrieves a consistent property value from a fragment of nodes. + + + + The fragment of nodes to search for the property. + + + + + The key of the property to extract from each node. + + + The default value to return if no valid prop is found. + + + Custom function to extract the prop value from a node. + + + Determines how to traverse the fragment: + - 'all': Check both block and text nodes + - 'block': Only check block nodes + - 'text': Only check text nodes + Default is 'block'. + + + + + + + The consistent property value found in the fragment, or undefined if no consistent value is found. + + ### getLastChild Returns the last child of a node or `null` if no children. @@ -356,6 +407,27 @@ Gets the range from the start of the block above a location to the location. `undefined` if no such block exists. +### getSelectionFragment + +Retrieves the fragment of the current selection, optionally unwrapping structural nodes. + + + + The editor to get the selection fragment from. + + + + + Array of structural types to unwrap from the fragment. + + + + + + + An array of `TElement` representing the fragment of the current selection. Returns an empty array if the selection is not expanded or if no fragment is found. + + ### getSelectionText Gets the selected text from the editor. @@ -635,6 +707,19 @@ Queries the editor state. ## Transforms +### duplicateBlocks + +Duplicates the given blocks and inserts them after the last block in the selection. + + + + The editor instance. + + + An array of node entries representing the blocks to duplicate. + + + ### insertElements Inserts nodes at a location in the document. @@ -750,6 +835,67 @@ Selects the end point of the block above the selection. +### selectNodes + +Selects the range encompassing the given nodes. + + + + The editor instance. + + + An array of node entries to select. + + + +### setBlockAboveNode + +Sets properties on the block above the current selection. + + + + The editor instance. + + + The properties to set on the block. + + + Options for setting nodes, excluding the 'at' property. + + + +### setBlockAboveTexts + +Sets properties on the lowest-level nodes within the block above the current selection. + + + + The editor instance. + + + The properties to set on the text nodes. + + + Options for setting nodes, excluding the 'at' property. + + + +### setBlockNodes + +Sets properties on all block nodes that match the given options. + + + + The editor instance. + + + The properties to set on the matching block nodes. + + + Options for getting node entries to update. + + + ### setMarks Sets marks to selected text. diff --git a/apps/www/content/docs/block-selection.mdx b/apps/www/content/docs/block-selection.mdx index 13ff71097d..1dee154020 100644 --- a/apps/www/content/docs/block-selection.mdx +++ b/apps/www/content/docs/block-selection.mdx @@ -187,9 +187,7 @@ A set of IDs for the currently selected blocks. -### BlockContextMenuPlugin - -This plugin is used by `BlockSelectionPlugin` and doesn't need to be added manually. +### BlockMenuPlugin ## API @@ -214,7 +212,7 @@ Adds a selected row to the block selection. -### editor.api.blockSelection.getSelectedBlocks +### editor.api.blockSelection.getNodes Gets the selected blocks in the editor. @@ -253,6 +251,52 @@ Sets the selected IDs based on added and removed elements. Unselects all blocks and sets the `isSelecting` flag to false. +## Transforms + +### editor.tf.blockSelection.duplicate + +Duplicates the selected blocks. + + + + An array of node entries to duplicate. + + + +### editor.tf.blockSelection.removeNodes + +Removes the selected nodes from the editor. + +### editor.tf.blockSelection.select + +Selects the nodes returned by `getNodes()` and resets the selected IDs. + +### editor.tf.blockSelection.setNodes + +Sets properties on the selected nodes. + + + + The properties to set on the selected nodes. + + + Options for setting nodes. + + + +### editor.tf.blockSelection.setTexts + +Sets text properties on the selected nodes. + + + + The text properties to set on the selected nodes. + + + Options for setting text nodes, excluding the 'at' property. + + + ## API Components ### BlockSelectable @@ -313,4 +357,16 @@ Returns true if context block is selected. ### useSelectionArea -A hook that initializes and manages the selection area functionality. \ No newline at end of file +A hook that initializes and manages the selection area functionality. + +### useBlockSelectionNodes + +Returns an array of node entries for the currently selected blocks. + +### useBlockSelectionFragment + +Returns an array of nodes for the currently selected blocks. + +### useBlockSelectionFragmentProp + +Returns fragment prop for the currently selected blocks. diff --git a/apps/www/content/docs/components/changelog.mdx b/apps/www/content/docs/components/changelog.mdx index 054d33430b..0c46a8ebf9 100644 --- a/apps/www/content/docs/components/changelog.mdx +++ b/apps/www/content/docs/components/changelog.mdx @@ -11,6 +11,12 @@ Use the [CLI](https://platejs.org/docs/components/cli) to install the latest ver ## October 2024 #15 +### October 10 #15.3 + +- `dropdown-menu`(`DropdownMenuContent`): prevent default on `onCloseAutoFocus` +- `floating-toolbar`(`FloatingToolbar`): remove portal, hide on click outside +- `turn-into-dropdown-menu`(`TurnIntoDropdownMenu`): add indent list items + ### October 4 #15.2 - feat `emoji-picker`: adjust ui emoji picker diff --git a/apps/www/src/components/plate-ui/playground-more-dropdown-menu.tsx b/apps/www/src/components/plate-ui/playground-more-dropdown-menu.tsx index 7a697afe24..1f11624020 100644 --- a/apps/www/src/components/plate-ui/playground-more-dropdown-menu.tsx +++ b/apps/www/src/components/plate-ui/playground-more-dropdown-menu.tsx @@ -34,7 +34,7 @@ export function PlaygroundMoreDropdownMenu(props: DropdownMenuProps) { item.value === value) ?? defaultItem; const { icon: SelectedItemIcon, label: selectedItemLabel } = selectedItem; - const onCloseAutoFocus = React.useCallback((e: Event) => { - focusEditor(editor); - - return e.preventDefault(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - return ( @@ -161,8 +148,7 @@ export function PlaygroundTurnIntoDropdownMenu(props: DropdownMenuProps) { Turn into @@ -170,21 +156,20 @@ export function PlaygroundTurnIntoDropdownMenu(props: DropdownMenuProps) { { - if (type === 'ul' || type === 'ol') { - if (settingsStore.get.checkedId(IndentListPlugin.key)) { - toggleIndentList(editor, { - listStyleType: type === 'ul' ? 'disc' : 'decimal', - }); - } else if (settingsStore.get.checkedId('list')) { - editor.tf.toggle.list({ type }); - } + onValueChange={(type: any) => { + if (type === ListStyleType.Disc || type === ListStyleType.Decimal) { + setNodes(editor, { type: 'p' }); + toggleIndentList(editor, { + listStyleType: type, + }); + } else if (type === 'ul' || type === 'ol') { + editor.tf.toggle.list({ type }); } else { unwrapList(editor); + unsetNodes(editor, ['indent', 'listStyleType']); editor.tf.toggle.block({ type }); } - collapseSelection(editor); focusEditor(editor); }} > diff --git a/apps/www/src/registry/default/plate-ui/dropdown-menu.tsx b/apps/www/src/registry/default/plate-ui/dropdown-menu.tsx index f3b9fb9483..ba3ae7b40f 100644 --- a/apps/www/src/registry/default/plate-ui/dropdown-menu.tsx +++ b/apps/www/src/registry/default/plate-ui/dropdown-menu.tsx @@ -65,7 +65,13 @@ export const DropdownMenuContent = withRef< typeof DropdownMenuPrimitive.Content >(({ ...props }, ref) => ( - + { + e.preventDefault(); + }} + {...props} + /> )); diff --git a/apps/www/src/registry/default/plate-ui/floating-toolbar.tsx b/apps/www/src/registry/default/plate-ui/floating-toolbar.tsx index a9fda8e243..e9344cf87c 100644 --- a/apps/www/src/registry/default/plate-ui/floating-toolbar.tsx +++ b/apps/www/src/registry/default/plate-ui/floating-toolbar.tsx @@ -4,7 +4,6 @@ import React from 'react'; import { cn, withRef } from '@udecode/cn'; import { - PortalBody, useComposedRef, useEditorId, useEventEditorSelectors, @@ -51,6 +50,7 @@ export const FloatingToolbar = withRef< }); const { + clickOutsideRef, hidden, props: rootProps, ref: floatingRef, @@ -61,7 +61,7 @@ export const FloatingToolbar = withRef< if (hidden) return null; return ( - +
{children} - +
); }); diff --git a/apps/www/src/registry/default/plate-ui/turn-into-dropdown-menu.tsx b/apps/www/src/registry/default/plate-ui/turn-into-dropdown-menu.tsx index 9b878e6b6a..2525458725 100644 --- a/apps/www/src/registry/default/plate-ui/turn-into-dropdown-menu.tsx +++ b/apps/www/src/registry/default/plate-ui/turn-into-dropdown-menu.tsx @@ -3,11 +3,7 @@ import React from 'react'; import type { DropdownMenuProps } from '@radix-ui/react-dropdown-menu'; import { BlockquotePlugin } from '@udecode/plate-block-quote/react'; -import { - collapseSelection, - getNodeEntries, - isBlock, -} from '@udecode/plate-common'; +import { getNodeEntries, isBlock } from '@udecode/plate-common'; import { ParagraphPlugin, focusEditor, @@ -15,6 +11,7 @@ import { useEditorSelector, } from '@udecode/plate-common/react'; import { HEADING_KEYS } from '@udecode/plate-heading'; +import { ListStyleType, toggleIndentList } from '@udecode/plate-indent-list'; import { Icons } from '@/components/icons'; @@ -60,18 +57,18 @@ const items = [ label: 'Quote', value: BlockquotePlugin.key, }, - // { - // value: 'ul', - // label: 'Bulleted list', - // description: 'Bulleted list', - // icon: Icons.ul, - // }, - // { - // value: 'ol', - // label: 'Numbered list', - // description: 'Numbered list', - // icon: Icons.ol, - // }, + { + description: 'Bulleted list', + icon: Icons.ul, + label: 'Bulleted list', + value: ListStyleType.Disc, + }, + { + description: 'Numbered list', + icon: Icons.ol, + label: 'Numbered list', + value: ListStyleType.Decimal, + }, ]; const defaultItem = items.find((item) => item.value === ParagraphPlugin.key)!; @@ -119,27 +116,24 @@ export function TurnIntoDropdownMenu(props: DropdownMenuProps) { - + Turn into { - // if (type === 'ul' || type === 'ol') { - // if (settingsStore.get.checkedId(IndentListPlugin.key)) { - // toggleIndentList(editor, { - // listStyleType: type === 'ul' ? 'disc' : 'decimal', - // }); - // } else if (settingsStore.get.checkedId('list')) { - // toggleList(editor, { type }); - // } - // } else { - // unwrapList(editor); + onValueChange={(type: any) => { + if (type === ListStyleType.Disc || type === ListStyleType.Decimal) { + toggleIndentList(editor, { + listStyleType: type, + }); + } + editor.tf.toggle.block({ type }); - // } - collapseSelection(editor); focusEditor(editor); }} > diff --git a/config/eslint/bases/tailwind.cjs b/config/eslint/bases/tailwind.cjs index da130f5d69..d1c2cabd56 100644 --- a/config/eslint/bases/tailwind.cjs +++ b/config/eslint/bases/tailwind.cjs @@ -27,6 +27,7 @@ module.exports = { 'error', { whitelist: [ + 'ignore-click-outside/toolbar', 'no-scrollbar', 'chunk-mode', 'preview', diff --git a/packages/ai/package.json b/packages/ai/package.json index d18c3c7ec4..e591fdf808 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -52,7 +52,6 @@ "dependencies": { "@udecode/plate-combobox": "39.0.0", "@udecode/plate-markdown": "39.0.0", - "@udecode/plate-menu": "39.1.0", "@udecode/plate-selection": "39.1.2", "lodash": "^4.17.21" }, diff --git a/packages/ai/src/react/ai/AIPlugin.ts b/packages/ai/src/react/ai/AIPlugin.ts index ed56e328f7..b8404de9b4 100644 --- a/packages/ai/src/react/ai/AIPlugin.ts +++ b/packages/ai/src/react/ai/AIPlugin.ts @@ -1,5 +1,4 @@ import type { ExtendConfig } from '@udecode/plate-core'; -import type { AriakitTypes } from '@udecode/plate-menu'; import type { NodeEntry, Path } from 'slate'; import { @@ -61,7 +60,7 @@ export type AIPluginConfig = ExtendConfig< lastWorkPath: Path | null; menuType: 'cursor' | 'selection' | null; openEditorId: string | null; - store: AriakitTypes.MenuStore | null; + store: any | null; } & ExposeOptions & AIApi & AISelectors, diff --git a/packages/ai/src/react/ai/hook/useAI.ts b/packages/ai/src/react/ai/hook/useAI.ts index 840e1b78c8..cfc01a45ba 100644 --- a/packages/ai/src/react/ai/hook/useAI.ts +++ b/packages/ai/src/react/ai/hook/useAI.ts @@ -9,11 +9,6 @@ import React, { import { isHotkey } from '@udecode/plate-common'; import { focusEditor, useEditorPlugin } from '@udecode/plate-common/react'; -import { - type Action, - Ariakit, - filterAndBuildMenuTree, -} from '@udecode/plate-menu'; import type { AIActions, AICommands } from '../types'; @@ -28,7 +23,7 @@ interface UseAIStateProps { defaultValues: Record; } -export type AICommandsAction = Record; +export type AICommandsAction = Record; export const useAI = ({ aiActions, @@ -49,8 +44,7 @@ export const useAI = ({ SelectionSuggestions, } = aiCommands; - const { api, editor, setOption, setOptions, useOption } = - useEditorPlugin(AIPlugin); + const { api, editor, setOption, useOption } = useEditorPlugin(AIPlugin); const isOpen = useOption('isOpen', editor.id); const action = useOption('action'); @@ -61,13 +55,13 @@ export const useAI = ({ const { aiEditor } = editor.useOptions(AIPlugin); - const menu = Ariakit.useMenuStore(); - useEffect(() => { - setOptions({ - store: menu, - }); - // eslint-disable-next`-line react-hooks/exhaustive-deps - }, [isOpen, menu, setOptions]); + // const menu = Ariakit.useMenuStore(); + // useEffect(() => { + // setOptions({ + // store: menu, + // }); + // // eslint-disable-next`-line react-hooks/exhaustive-deps + // }, [isOpen, menu, setOptions]); const [values, setValues] = useState(defaultValues); const [searchValue, setSearchValue] = useState(''); @@ -141,11 +135,11 @@ export const useAI = ({ /** IME */ const [isComposing, setIsComposing] = useState(false); - const searchItems = useMemo(() => { - return isComposing - ? [] - : filterAndBuildMenuTree(Object.values(CurrentActions), searchValue); - }, [CurrentActions, isComposing, searchValue]); + // const searchItems = useMemo(() => { + // return isComposing + // ? [] + // : filterAndBuildMenuTree(Object.values(CurrentActions), searchValue); + // }, [CurrentActions, isComposing, searchValue]); /** Props */ @@ -155,7 +149,7 @@ export const useAI = ({ loading: aiState === 'generating' || aiState === 'requesting', open: isOpen, setAction: setAction, - store: menu, + // store: menu, values: values, onClickOutside: () => { return editor.getApi(AIPlugin).ai.hide(); @@ -166,7 +160,7 @@ export const useAI = ({ setValues(values); }, }; - }, [aiState, editor, isOpen, menu, setAction, values]); + }, [aiState, editor, isOpen, setAction, values]); const comboboxProps = useMemo(() => { return { @@ -197,7 +191,7 @@ export const useAI = ({ comboboxProps, menuProps, menuType, - searchItems, + // searchItems, submitButtonProps, onCloseMenu, }; diff --git a/packages/ai/src/react/ai/types.ts b/packages/ai/src/react/ai/types.ts index 7a7e087bfd..c01e5f8673 100644 --- a/packages/ai/src/react/ai/types.ts +++ b/packages/ai/src/react/ai/types.ts @@ -1,10 +1,8 @@ -import type { Action } from '@udecode/plate-menu'; - export interface AIActions { - CursorCommandsActions: Record; - CursorSuggestionActions: Record; - SelectionCommandsActions: Record; - SelectionSuggestionActions: Record; + CursorCommandsActions: Record; + CursorSuggestionActions: Record; + SelectionCommandsActions: Record; + SelectionSuggestionActions: Record; } export interface AICommands { diff --git a/packages/ai/src/react/ai/utils/getContent.ts b/packages/ai/src/react/ai/utils/getContent.ts index 18348e7e26..d6c26ebf4f 100644 --- a/packages/ai/src/react/ai/utils/getContent.ts +++ b/packages/ai/src/react/ai/utils/getContent.ts @@ -24,7 +24,7 @@ export const getContent = (editor: PlateEditor, aiEditor: PlateEditor) => { ) { const entries = editor .getApi(BlockSelectionPlugin) - .blockSelection.getSelectedBlocks(); + .blockSelection.getNodes(); const nodes = Array.from(entries, (entry) => entry[0]); diff --git a/packages/core/src/react/components/PlateContent.tsx b/packages/core/src/react/components/PlateContent.tsx index a5ca6a2c04..a59e551a32 100644 --- a/packages/core/src/react/components/PlateContent.tsx +++ b/packages/core/src/react/components/PlateContent.tsx @@ -16,6 +16,7 @@ import { PlateSlate } from './PlateSlate'; export type PlateContentProps = Omit & { decorate?: PlateStoreState['decorate']; + disabled?: boolean; /** R enders the editable content. */ renderEditable?: (editable: React.ReactElement) => React.ReactNode; }; diff --git a/packages/core/src/react/hooks/useEditableProps.ts b/packages/core/src/react/hooks/useEditableProps.ts index 5ff1f49dc0..270b8fe4bf 100644 --- a/packages/core/src/react/hooks/useEditableProps.ts +++ b/packages/core/src/react/hooks/useEditableProps.ts @@ -2,7 +2,7 @@ import React from 'react'; import type { TEditableProps } from '@udecode/slate-react'; -import { isDefined } from '@udecode/utils'; +import clsx from 'clsx'; import omit from 'lodash/omit.js'; import { useDeepCompareMemo } from 'use-deep-compare'; @@ -15,16 +15,18 @@ import { pipeHandler } from '../utils/pipeHandler'; import { pipeRenderElement } from '../utils/pipeRenderElement'; import { pipeRenderLeaf } from '../utils/pipeRenderLeaf'; -export const useEditableProps = ( - editableProps: Omit & - Pick = {} -): TEditableProps => { +export const useEditableProps = ({ + disabled, + readOnly: readOnlyProp, + ...editableProps +}: Omit & + Pick = {}): TEditableProps => { const { id } = editableProps; const editor = useEditorRef(id); const selectors = usePlateSelectors(id); const versionDecorate = selectors.versionDecorate(); - const readOnly = selectors.readOnly(); + const storeReadOnly = selectors.readOnly(); const storeDecorate = selectors.decorate(); const storeRenderLeaf = selectors.renderLeaf(); const storeRenderElement = selectors.renderElement(); @@ -57,10 +59,6 @@ export const useEditableProps = ( renderLeaf, }; - if (isDefined(readOnly)) { - _props.readOnly = readOnly!; - } - DOM_HANDLERS.forEach((handlerKey) => { const handler = pipeHandler(editor, { editableProps, @@ -73,7 +71,9 @@ export const useEditableProps = ( }); return _props; - }, [decorate, editableProps, renderElement, renderLeaf, readOnly]); + }, [decorate, editableProps, renderElement, renderLeaf]); + + const readOnly = storeReadOnly || readOnlyProp || disabled; return useDeepCompareMemo( () => ({ @@ -84,7 +84,15 @@ export const useEditableProps = ( 'decorate', ]), ...props, + 'aria-disabled': disabled, + className: clsx( + 'slate-editor', + 'ignore-click-outside/toolbar', + editableProps.className + ), + 'data-readonly': readOnly, + readOnly, }), - [editableProps, props] + [editableProps, props, readOnly] ); }; diff --git a/packages/floating/src/hooks/useFloatingToolbar.ts b/packages/floating/src/hooks/useFloatingToolbar.ts index 1c26c79a8c..4195cd368e 100644 --- a/packages/floating/src/hooks/useFloatingToolbar.ts +++ b/packages/floating/src/hooks/useFloatingToolbar.ts @@ -7,7 +7,9 @@ import { } from '@udecode/plate-common'; import { useEditorReadOnly, + useEditorRef, useEditorSelector, + useOnClickOutside, } from '@udecode/plate-common/react'; import { useFocused } from 'slate-react'; @@ -33,6 +35,7 @@ export const useFloatingToolbarState = ({ editorId: string; focusedEditorId: string | null; } & FloatingToolbarState) => { + const editor = useEditorRef(); const selectionExpanded = useEditorSelector(isSelectionExpanded, []); const selectionText = useEditorSelector(getSelectionText, []); const readOnly = useEditorReadOnly(); @@ -47,7 +50,7 @@ export const useFloatingToolbarState = ({ const floating = useVirtualFloating( mergeProps( { - getBoundingClientRect: getSelectionBoundingClientRect, + getBoundingClientRect: () => getSelectionBoundingClientRect(editor), open, onOpenChange: setOpen, }, @@ -153,15 +156,21 @@ export const useFloatingToolbar = ({ const { update } = floating; - const selectionTextLength = selectionText?.length ?? 0; + useEditorSelector(() => { + update?.(); + }, [update]) - React.useEffect(() => { - if (selectionTextLength > 0) { - update?.(); + const clickOutsideRef = useOnClickOutside( + () => { + setOpen(false); + }, + { + ignoreClass: 'ignore-click-outside/toolbar', } - }, [selectionTextLength, update]); + ); return { + clickOutsideRef, hidden: !open, props: { style: floating.style, diff --git a/packages/floating/src/hooks/useVirtualFloating.ts b/packages/floating/src/hooks/useVirtualFloating.ts index a9434d07ae..4448f59c4c 100644 --- a/packages/floating/src/hooks/useVirtualFloating.ts +++ b/packages/floating/src/hooks/useVirtualFloating.ts @@ -4,7 +4,10 @@ import type { ClientRectObject } from '@floating-ui/core'; import { useIsomorphicLayoutEffect } from '@udecode/plate-common/react'; -import { createVirtualElement } from '../createVirtualElement'; +import { + createVirtualElement, + getDefaultBoundingClientRect, +} from '../createVirtualElement'; import { type ReferenceType, type UseFloatingOptions, @@ -13,7 +16,6 @@ import { autoUpdate, useFloating, } from '../libs/floating-ui'; -import { getSelectionBoundingClientRect } from '../utils/index'; export interface UseVirtualFloatingOptions extends Partial { getBoundingClientRect?: () => ClientRectObject; @@ -49,7 +51,7 @@ export interface UseVirtualFloatingReturn< * @see https://floating-ui.com/docs/react-dom#virtual-element */ export const useVirtualFloating = ({ - getBoundingClientRect = getSelectionBoundingClientRect, + getBoundingClientRect = getDefaultBoundingClientRect, ...floatingOptions }: UseVirtualFloatingOptions): UseVirtualFloatingReturn => { const virtualElementRef = React.useRef(createVirtualElement() as RT); diff --git a/packages/floating/src/utils/getSelectionBoundingClientRect.ts b/packages/floating/src/utils/getSelectionBoundingClientRect.ts index e0905bd974..46b559a1cc 100644 --- a/packages/floating/src/utils/getSelectionBoundingClientRect.ts +++ b/packages/floating/src/utils/getSelectionBoundingClientRect.ts @@ -1,16 +1,18 @@ import type { ClientRectObject } from '@floating-ui/core'; +import type { PlateEditor } from '@udecode/plate-common/react'; -import { getDefaultBoundingClientRect } from '../createVirtualElement'; +import { isSelectionExpanded } from '@udecode/plate-common'; -/** Get bounding client rect of the window selection */ -export const getSelectionBoundingClientRect = (): ClientRectObject => { - const domSelection = window.getSelection(); +import { getDefaultBoundingClientRect } from '../createVirtualElement'; +import { getRangeBoundingClientRect } from './getRangeBoundingClientRect'; - if (!domSelection || domSelection.rangeCount < 1) { - return getDefaultBoundingClientRect(); +/** Get bounding client rect of the editor selection */ +export const getSelectionBoundingClientRect = ( + editor: PlateEditor +): ClientRectObject => { + if (isSelectionExpanded(editor)) { + return getRangeBoundingClientRect(editor, editor.selection); } - const domRange = domSelection.getRangeAt(0); - - return domRange.getBoundingClientRect(); + return getDefaultBoundingClientRect(); }; diff --git a/packages/link/src/react/components/FloatingLink/useFloatingLinkEdit.ts b/packages/link/src/react/components/FloatingLink/useFloatingLinkEdit.ts index 66a86231a2..9f1abae7e5 100644 --- a/packages/link/src/react/components/FloatingLink/useFloatingLinkEdit.ts +++ b/packages/link/src/react/components/FloatingLink/useFloatingLinkEdit.ts @@ -52,7 +52,7 @@ export const useFloatingLinkEditState = ({ }); } - return getSelectionBoundingClientRect(); + return getSelectionBoundingClientRect(editor); }, [editor, type]); const isOpen = open && mode === 'edit'; diff --git a/packages/link/src/react/components/FloatingLink/useFloatingLinkInsert.ts b/packages/link/src/react/components/FloatingLink/useFloatingLinkInsert.ts index 2f723f52c7..f59c264e7a 100644 --- a/packages/link/src/react/components/FloatingLink/useFloatingLinkInsert.ts +++ b/packages/link/src/react/components/FloatingLink/useFloatingLinkInsert.ts @@ -36,7 +36,7 @@ export const useFloatingLinkInsertState = ({ const floating = useVirtualFloatingLink({ editorId: editor.id, - getBoundingClientRect: getSelectionBoundingClientRect, + getBoundingClientRect: () => getSelectionBoundingClientRect(editor), open: isOpen && mode === 'insert', whileElementsMounted: () => () => {}, ...floatingOptions, diff --git a/packages/menu/.npmignore b/packages/menu/.npmignore deleted file mode 100644 index 7d3b305b17..0000000000 --- a/packages/menu/.npmignore +++ /dev/null @@ -1,3 +0,0 @@ -__tests__ -__test-utils__ -__mocks__ diff --git a/packages/menu/CHANGELOG.md b/packages/menu/CHANGELOG.md deleted file mode 100644 index 5a83584992..0000000000 --- a/packages/menu/CHANGELOG.md +++ /dev/null @@ -1,7 +0,0 @@ -# @udecode/plate-menu - -## 39.1.0 - -### Minor Changes - -- [#3604](https://github.com/udecode/plate/pull/3604) by [@felixfeng33](https://github.com/felixfeng33) – Release package diff --git a/packages/menu/README.md b/packages/menu/README.md deleted file mode 100644 index 6d3f66599c..0000000000 --- a/packages/menu/README.md +++ /dev/null @@ -1 +0,0 @@ -WIP \ No newline at end of file diff --git a/packages/menu/package.json b/packages/menu/package.json deleted file mode 100644 index a22f45a6eb..0000000000 --- a/packages/menu/package.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "name": "@udecode/plate-menu", - "version": "39.1.0", - "description": "Menu for Plate", - "keywords": [ - "plate", - "menu", - "slate" - ], - "homepage": "https://platejs.org", - "bugs": { - "url": "https://github.com/udecode/plate/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/udecode/plate.git", - "directory": "packages/menu" - }, - "license": "MIT", - "sideEffects": false, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.mjs", - "module": "./dist/index.mjs", - "require": "./dist/index.js" - }, - "./react": { - "types": "./dist/react/index.d.ts", - "import": "./dist/react/index.mjs", - "module": "./dist/react/index.mjs", - "require": "./dist/react/index.js" - } - }, - "main": "dist/index.js", - "module": "dist/index.mjs", - "types": "dist/index.d.ts", - "files": [ - "dist/**/*" - ], - "scripts": { - "brl": "yarn p:brl", - "build": "yarn p:build", - "build:watch": "yarn p:build:watch", - "clean": "yarn p:clean", - "lint": "yarn p:lint", - "lint:fix": "yarn p:lint:fix", - "test": "yarn p:test", - "test:watch": "yarn p:test:watch", - "typecheck": "yarn p:typecheck" - }, - "dependencies": { - "@ariakit/react": "0.4.7", - "match-sorter": "6.3.4" - }, - "devDependencies": { - "@udecode/plate-common": "workspace:^" - }, - "peerDependencies": { - "@udecode/plate-common": ">=39.1.3", - "react": ">=16.8.0", - "react-dom": ">=16.8.0", - "slate": ">=0.103.0", - "slate-history": ">=0.93.0", - "slate-hyperscript": ">=0.66.0", - "slate-react": ">=0.108.0" - }, - "publishConfig": { - "access": "public" - } -} diff --git a/packages/menu/src/hooks/index.ts b/packages/menu/src/hooks/index.ts deleted file mode 100644 index 097a2db989..0000000000 --- a/packages/menu/src/hooks/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * @file Automatically generated by barrelsby. - */ - -export * from './useMenu'; -export * from './useMenuItem'; diff --git a/packages/menu/src/hooks/useMenu.tsx b/packages/menu/src/hooks/useMenu.tsx deleted file mode 100644 index 9f52266d0a..0000000000 --- a/packages/menu/src/hooks/useMenu.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import React from 'react'; - -import type { MenuProps, setAction } from '../types'; - -import { Ariakit } from '../lib'; -import { useOnClickOutside } from '../utils/useOnClickOutside'; - -export const SearchableContext = React.createContext(false); - -export const ActionContext = React.createContext(null); - -export const useMenu = ({ - children, - combobox, - comboboxClassName, - comboboxListClassName, - comboboxSubmitButton, - dragButton, - flip = true, - getAnchorRect, - icon, - injectAboveMenu, - label, - loading, - loadingPlaceholder, - open, - placement, - portal, - ref, - searchValue, - setAction, - store, - values, - onClickOutside, - onOpenChange, - onRootMenuClose, - onValueChange, - onValuesChange, - ...props -}: MenuProps & { ref: React.ForwardedRef }) => { - const parent = Ariakit.useMenuContext(); - const searchable = searchValue != null || !!onValueChange || !!combobox; - const ParentSetAction = React.useContext(ActionContext); - - const isRootMenu = !parent; - const isDraggleButtonMenu = !!dragButton; - const menuRef = React.useRef(null); - - useOnClickOutside(menuRef, onClickOutside); - - const menuProviderProps = { - open, - placement: isRootMenu ? placement : 'right', - setOpen: (v: boolean) => { - onOpenChange?.(v); - - if (!v && !parent && !dragButton) onRootMenuClose?.(); - }, - setValues: onValuesChange, - showTimeout: 100, - store, - values, - }; - - const menuButtonProps = { - ref, - ...props, - }; - - const menuProps = { - flip, - getAnchorRect, - gutter: isRootMenu ? 0 : 4, - portal, - ref: isRootMenu ? menuRef : undefined, - unmountOnHide: true, - }; - - const comboboxProviderProps = { - includesBaseElement: false, - resetValueOnHide: true, - setValue: onValueChange, - value: searchValue, - }; - - return { - ParentSetAction, - comboboxProviderProps, - isDraggleButtonMenu, - isRootMenu, - menuButtonProps, - menuProps, - menuProviderProps, - searchable, - }; -}; diff --git a/packages/menu/src/hooks/useMenuItem.tsx b/packages/menu/src/hooks/useMenuItem.tsx deleted file mode 100644 index 1368fc0483..0000000000 --- a/packages/menu/src/hooks/useMenuItem.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import React from 'react'; - -import type { MenuItemProps } from '../types'; - -import { Ariakit } from '../lib'; -import { ActionContext, SearchableContext } from './useMenu'; - -type UseMenuItemProps = MenuItemProps & { - ref: React.ForwardedRef; -}; - -export const useMenuItem = ({ - className, - group, - icon, - label, - name, - parentGroup, - preventClose, - ref, - shortcut, - value, - ...props -}: UseMenuItemProps) => { - const menu = Ariakit.useMenuContext(); - - if (!menu) throw new Error('MenuItem should be used inside a Menu'); - - const setAction = React.useContext(ActionContext); - - const searchable = React.useContext(SearchableContext); - - const baseOnClick = (event: React.MouseEvent) => { - props.onClick?.(event); - - if (event.isDefaultPrevented()) return; - if (setAction === null) - console.warn('Did you forget to pass the setAction prop?'); - - setAction?.({ group, value }); - }; - - const baseProps: MenuItemProps = { - blurOnHoverEnd: false, - focusOnHover: true, - label, - ref, - ...props, - group: parentGroup, - name: group, - value: value || label, - onClick: baseOnClick, - }; - - const isCheckable = menu.useState((state) => { - if (!group) return false; - if (value == null) return false; - - return state.values[group] != null; - }); - - const isChecked = menu.useState((state) => { - if (!group) return false; - - return state.values[group] === value; - }); - - const isRadio = name != null && value != null; - - const radioProps = { - ...baseProps, - hideOnClick: true, - name: name as string, - value: value as string, - }; - - const hideOnClick = (event: React.MouseEvent) => { - const expandable = event.currentTarget.hasAttribute('aria-expanded'); - - if (expandable) return false; - if (preventClose) return false; - - menu.hideAll(); - - return false; - }; - - const selectValueOnClick = () => { - if (name == null || value == null) return false; - - menu.setValue(name, value); - - return true; - }; - - const comboboxProps = { - ...baseProps, - hideOnClick: hideOnClick, - selectValueOnClick: selectValueOnClick, - setValueOnClick: selectValueOnClick, - value: isCheckable ? value : undefined, - }; - - return { - baseProps, - comboboxProps, - isCheckable, - isChecked, - isRadio, - radioProps, - searchable, - }; -}; diff --git a/packages/menu/src/index.ts b/packages/menu/src/index.ts deleted file mode 100644 index cad68a5708..0000000000 --- a/packages/menu/src/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @file Automatically generated by barrelsby. - */ - -export * from './hooks/index'; -export * from './lib/index'; -export * from './types/index'; -export * from './utils/index'; diff --git a/packages/menu/src/lib/Ariakit.ts b/packages/menu/src/lib/Ariakit.ts deleted file mode 100644 index 20cabed108..0000000000 --- a/packages/menu/src/lib/Ariakit.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * as Ariakit from '@ariakit/react'; - -export type * as AriakitTypes from '@ariakit/react'; diff --git a/packages/menu/src/lib/index.ts b/packages/menu/src/lib/index.ts deleted file mode 100644 index dc5cc8d196..0000000000 --- a/packages/menu/src/lib/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** - * @file Automatically generated by barrelsby. - */ - -export * from './Ariakit'; diff --git a/packages/menu/src/types/action.ts b/packages/menu/src/types/action.ts deleted file mode 100644 index 1cb6173db2..0000000000 --- a/packages/menu/src/types/action.ts +++ /dev/null @@ -1,17 +0,0 @@ -export interface Action { - group?: string; - groupName?: string; - icon?: React.ReactNode; - items?: Action[]; - keywords?: string[]; - label?: string; - shortcut?: string; - value?: string; -} - -export type actionGroup = { - group?: string; - value?: string; -}; - -export type setAction = (actionGroup: actionGroup) => void; diff --git a/packages/menu/src/types/index.ts b/packages/menu/src/types/index.ts deleted file mode 100644 index cfc956a561..0000000000 --- a/packages/menu/src/types/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * @file Automatically generated by barrelsby. - */ - -export * from './action'; -export * from './menu'; -export * from './menuItem'; diff --git a/packages/menu/src/types/menu.ts b/packages/menu/src/types/menu.ts deleted file mode 100644 index 3b33a8a8c8..0000000000 --- a/packages/menu/src/types/menu.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { Ariakit } from '../lib'; -import type { setAction } from './action'; - -export interface MenuProps extends Ariakit.MenuButtonProps<'div'> { - combobox?: Ariakit.ComboboxProps['render']; - comboboxClassName?: string; - comboboxListClassName?: string; - comboboxSubmitButton?: React.ReactElement; - dragButton?: Ariakit.MenuButtonProps['render']; - flip?: boolean; - getAnchorRect?: (anchor: HTMLElement | null) => AnchorRect | null; - icon?: React.ReactNode; - injectAboveMenu?: React.ReactElement; - label?: React.ReactNode; - loading?: boolean; - loadingPlaceholder?: React.ReactNode; - onClickOutside?: (event: MouseEvent) => void; - onOpenChange?: (open: boolean) => void; - onRootMenuClose?: () => void; - onValueChange?: (value: string) => void; - onValuesChange?: Ariakit.MenuProviderProps['setValues']; - open?: boolean; - placement?: Ariakit.MenuProviderProps['placement']; - portal?: Ariakit.MenuProps['portal']; - searchValue?: string; - setAction?: setAction; - values?: Ariakit.MenuProviderProps['values']; -} - -export interface AnchorRect { - bottom: number; - height: number; - left: number; - right: number; - top: number; - width: number; -} diff --git a/packages/menu/src/types/menuItem.ts b/packages/menu/src/types/menuItem.ts deleted file mode 100644 index d078457492..0000000000 --- a/packages/menu/src/types/menuItem.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { Ariakit } from '../lib'; - -export interface MenuItemProps - extends Omit { - group?: string; - icon?: React.ReactNode; - label?: string; - name?: string; - parentGroup?: string; - preventClose?: boolean; - shortcut?: string; - value?: string; -} diff --git a/packages/menu/src/utils/buildMenuTree.ts b/packages/menu/src/utils/buildMenuTree.ts deleted file mode 100644 index d456a17c49..0000000000 --- a/packages/menu/src/utils/buildMenuTree.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { Action } from '../types'; - -export function buildMenuTree(actions: Action[] | null) { - if (!actions) return null; - - return actions.reduce((actions, option) => { - if (option.groupName) { - const groupName = actions.find( - (action) => action.label === option.groupName - ); - - if (groupName) { - groupName.items!.push(option); - } else { - actions.push({ items: [option], label: option.groupName }); - } - } else { - actions.push(option); - } - - return actions; - }, []); -} diff --git a/packages/menu/src/utils/filterAndBuildMenuTree.ts b/packages/menu/src/utils/filterAndBuildMenuTree.ts deleted file mode 100644 index 21c477132f..0000000000 --- a/packages/menu/src/utils/filterAndBuildMenuTree.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { matchSorter } from 'match-sorter'; - -import type { Action } from '../types'; - -import { buildMenuTree } from './buildMenuTree'; -import { flattenMenuTree } from './flattenMenuTree'; - -export function filterAndBuildMenuTree( - actions: Action[], - searchValue: string -): Action[] | null { - if (!searchValue) return null; - - const options = flattenMenuTree(actions); - - const matches = matchSorter(options, searchValue, { - keys: ['label', 'group', 'value', 'keywords'], - }); - - return buildMenuTree(matches.slice(0, 15)); -} diff --git a/packages/menu/src/utils/flattenMenuTree.ts b/packages/menu/src/utils/flattenMenuTree.ts deleted file mode 100644 index fcb39ee254..0000000000 --- a/packages/menu/src/utils/flattenMenuTree.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { Action } from '../types'; - -export function flattenMenuTree(actions: Action[]): Action[] { - return actions.flatMap((item) => { - if (item.items) { - const parentGroup = item.group ?? item.label; - const groupName = item.label; - - return flattenMenuTree( - item.items.map(({ group, ...item }) => ({ - ...item, - group: group ?? parentGroup, - groupName, - })) - ); - } - - return item; - }); -} diff --git a/packages/menu/src/utils/index.ts b/packages/menu/src/utils/index.ts deleted file mode 100644 index b37821fcc7..0000000000 --- a/packages/menu/src/utils/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @file Automatically generated by barrelsby. - */ - -export * from './buildMenuTree'; -export * from './filterAndBuildMenuTree'; -export * from './flattenMenuTree'; -export * from './useOnClickOutside'; diff --git a/packages/menu/src/utils/useOnClickOutside.ts b/packages/menu/src/utils/useOnClickOutside.ts deleted file mode 100644 index ea829c96aa..0000000000 --- a/packages/menu/src/utils/useOnClickOutside.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { useEffect, useLayoutEffect, useRef } from 'react'; - -/** - * Determines the appropriate effect hook to use based on the environment. If - * the code is running on the client-side (browser), it uses the - * `useLayoutEffect` hook, otherwise, it uses the `useEffect` hook. - */ -export const useIsomorphicLayoutEffect = - typeof window === 'undefined' ? useEffect : useLayoutEffect; - -// MediaQueryList Event based useEventListener interface -function useEventListener( - eventName: K, - handler: (event: MediaQueryListEventMap[K]) => void, - element: React.RefObject, - options?: AddEventListenerOptions | boolean -): void; - -// Window Event based useEventListener interface -function useEventListener( - eventName: K, - handler: (event: WindowEventMap[K]) => void, - element?: undefined, - options?: AddEventListenerOptions | boolean -): void; - -// Element Event based useEventListener interface -function useEventListener< - K extends keyof HTMLElementEventMap, - T extends HTMLElement = HTMLDivElement, ->( - eventName: K, - handler: (event: HTMLElementEventMap[K]) => void, - element: React.RefObject, - options?: AddEventListenerOptions | boolean -): void; - -// Document Event based useEventListener interface -function useEventListener( - eventName: K, - handler: (event: DocumentEventMap[K]) => void, - element: React.RefObject, - options?: AddEventListenerOptions | boolean -): void; - -// https://usehooks-ts.com/react-hook/use-event-listener -function useEventListener< - KW extends keyof WindowEventMap, - KH extends keyof HTMLElementEventMap, - KM extends keyof MediaQueryListEventMap, - T extends HTMLElement | MediaQueryList | void = void, ->( - eventName: KH | KM | KW, - handler: ( - event: - | Event - | HTMLElementEventMap[KH] - | MediaQueryListEventMap[KM] - | WindowEventMap[KW] - ) => void, - element?: React.RefObject, - options?: AddEventListenerOptions | boolean -) { - // Create a ref that stores handler - const savedHandler = useRef(handler); - - useIsomorphicLayoutEffect(() => { - savedHandler.current = handler; - }, [handler]); - - useEffect(() => { - // Define the listening target - const targetElement: T | Window = element?.current ?? window; - - if (!(targetElement && targetElement.addEventListener)) return; - - // Create event listener that calls handler function stored in ref - const listener: typeof handler = (event) => savedHandler.current(event); - - targetElement.addEventListener(eventName, listener, options); - - // Remove event listener on cleanup - return () => { - targetElement.removeEventListener(eventName, listener, options); - }; - }, [eventName, element, options]); -} - -type Handler = (event: MouseEvent) => void; - -/** - * Attaches an event listener to detect clicks that occur outside a given - * element. - * - * @template T - The type of HTMLElement that the ref is referring to. - * @param {RefObject} ref - A React ref object that points to the element to - * listen for clicks outside of. - * @param {Handler} handler - The callback function to be executed when a click - * occurs outside the element. - * @param {string} [mouseEvent='mousedown'] - The type of mouse event to listen - * for (e.g., 'mousedown', 'mouseup'). Default is `'mousedown'` - */ -export const useOnClickOutside = ( - ref: React.RefObject, - handler?: Handler, - mouseEvent: 'mousedown' | 'mouseup' = 'mousedown' -): void => { - useEventListener(mouseEvent, (event) => { - if (!handler) return; - - const el = ref?.current; - - // Do nothing if clicking ref's element or descendent elements - if (!el || el.contains(event.target as Node)) { - return; - } - - handler(event); - }); -}; diff --git a/packages/menu/tsconfig.build.json b/packages/menu/tsconfig.build.json deleted file mode 100644 index 425481e027..0000000000 --- a/packages/menu/tsconfig.build.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "../../config/tsconfig.build.json", - "compilerOptions": { - "declarationDir": "./dist", - "outDir": "./dist" - }, - "include": ["src"] -} diff --git a/packages/menu/tsconfig.json b/packages/menu/tsconfig.json deleted file mode 100644 index ad83d092a5..0000000000 --- a/packages/menu/tsconfig.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "../../config/tsconfig.base.json", - "include": ["src"], - "exclude": [] -} diff --git a/packages/plate-utils/src/react/index.ts b/packages/plate-utils/src/react/index.ts index 54f3d6284c..8d996f1c02 100644 --- a/packages/plate-utils/src/react/index.ts +++ b/packages/plate-utils/src/react/index.ts @@ -11,3 +11,5 @@ export * from './useFormInputProps'; export * from './useMarkToolbarButton'; export * from './usePlaceholder'; export * from './useRemoveNodeButton'; +export * from './useSelection'; +export * from './useSelectionFragment'; diff --git a/packages/plate-utils/src/react/useMarkToolbarButton.ts b/packages/plate-utils/src/react/useMarkToolbarButton.ts index af345c0eed..c801e9209b 100644 --- a/packages/plate-utils/src/react/useMarkToolbarButton.ts +++ b/packages/plate-utils/src/react/useMarkToolbarButton.ts @@ -1,4 +1,5 @@ import { useEditorRef, useEditorSelector } from '@udecode/plate-core/react'; +import { focusEditor } from '@udecode/slate-react'; import { isMarkActive } from '@udecode/slate-utils'; export const useMarkToolbarButtonState = ({ @@ -30,6 +31,7 @@ export const useMarkToolbarButton = ( pressed: state.pressed, onClick: () => { editor.tf.toggle.mark({ key: state.nodeType, clear: state.clear }); + focusEditor(editor); }, onMouseDown: (e: React.MouseEvent) => { e.preventDefault(); diff --git a/packages/plate-utils/src/react/useSelection.ts b/packages/plate-utils/src/react/useSelection.ts new file mode 100644 index 0000000000..5ca3df3a17 --- /dev/null +++ b/packages/plate-utils/src/react/useSelection.ts @@ -0,0 +1,22 @@ +import { useEditorSelector } from '@udecode/plate-core/react'; +import { + isRangeAcrossBlocks, + isRangeInSameBlock, + isSelectionExpanded, +} from '@udecode/slate-utils'; + +export function useSelectionCollapsed() { + return useEditorSelector((editor) => !isSelectionExpanded(editor), []); +} + +export function useSelectionExpanded() { + return useEditorSelector((editor) => isSelectionExpanded(editor), []); +} + +export function useSelectionWithinBlock() { + return useEditorSelector((editor) => isRangeInSameBlock(editor), []); +} + +export function useSelectionAcrossBlocks() { + return useEditorSelector((editor) => isRangeAcrossBlocks(editor), []); +} diff --git a/packages/plate-utils/src/react/useSelectionFragment.ts b/packages/plate-utils/src/react/useSelectionFragment.ts new file mode 100644 index 0000000000..870a6b6a5b --- /dev/null +++ b/packages/plate-utils/src/react/useSelectionFragment.ts @@ -0,0 +1,24 @@ +import { useEditorSelector } from '@udecode/plate-core/react'; +import { + type GetFragmentPropOptions, + type GetSelectionFragmentOptions, + getFragmentProp, + getSelectionFragment, +} from '@udecode/slate-utils'; + +export const useSelectionFragment = (options?: GetSelectionFragmentOptions) => { + return useEditorSelector((editor) => { + return getSelectionFragment(editor, options); + }, []); +}; + +export const useSelectionFragmentProp = ({ + structuralTypes, + ...options +}: GetSelectionFragmentOptions & GetFragmentPropOptions = {}) => { + return useEditorSelector((editor) => { + const fragment = getSelectionFragment(editor, { structuralTypes }); + + return getFragmentProp(fragment, options); + }, []); +}; diff --git a/packages/selection/src/internal/SelectionArea.ts b/packages/selection/src/internal/SelectionArea.ts index 42d9d35ee1..5fa3920eb0 100644 --- a/packages/selection/src/internal/SelectionArea.ts +++ b/packages/selection/src/internal/SelectionArea.ts @@ -501,6 +501,7 @@ export class SelectionArea extends EventTarget { if ( this._container.contains(target) && + target.dataset.slateEditor !== 'true' && target.dataset.plateSelectable !== 'true' ) return; diff --git a/packages/selection/src/react/BlockContextMenuPlugin.tsx b/packages/selection/src/react/BlockContextMenuPlugin.tsx deleted file mode 100644 index ee4814e9e2..0000000000 --- a/packages/selection/src/react/BlockContextMenuPlugin.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import type { PluginConfig } from '@udecode/plate-common'; - -import { createTPlatePlugin } from '@udecode/plate-common/react'; - -export type BlockContextMenuConfig = PluginConfig< - 'blockContextMenu', - { - action: { - group: string | null; - value: string | null; - } | null; - anchorRect: { x: number; y: number }; - openEditorId: string | null; - store: any | null; - }, - { - blockContextMenu: BlockContextMenuApi; - } ->; - -export type BlockContextMenuApi = { - show: ( - editorId: string, - event: React.MouseEvent - ) => void; - hide: () => void; - reset: () => void; -}; - -export const BlockContextMenuPlugin = - createTPlatePlugin({ - key: 'blockContextMenu', - options: { - action: { group: null, value: null }, - anchorRect: { x: 0, y: 0 }, - openEditorId: null, - store: null, - }, - }) - .extendApi>(({ getOptions, setOptions }) => ({ - hide: () => { - setOptions({ - anchorRect: { x: 0, y: 0 }, - openEditorId: null, - }); - }, - reset: () => { - setOptions({ anchorRect: { x: 0, y: 0 } }); - }, - show: ( - editorId: string, - event: React.MouseEvent - ) => { - const { store } = getOptions(); - setOptions({ - anchorRect: { x: event.clientX, y: event.clientY }, - openEditorId: editorId, - }); - - if (store) { - store.show(); - store.setAutoFocusOnShow(true); - store.setInitialFocus('first'); - } - }, - })) - .extendOptions(({ getOptions }) => ({ - isOpen: (editorId: string) => getOptions().openEditorId === editorId, - })) - .extend(({ api }) => ({ - handlers: { - onMouseDown: ({ editor, event, getOption }) => { - if (event.button === 0 && getOption('isOpen', editor.id)) { - event.preventDefault(); - api.blockContextMenu.hide(); - } - if (event.button === 2) event.preventDefault(); - }, - }, - })); diff --git a/packages/selection/src/react/BlockMenuPlugin.tsx b/packages/selection/src/react/BlockMenuPlugin.tsx new file mode 100644 index 0000000000..dd5a7b9967 --- /dev/null +++ b/packages/selection/src/react/BlockMenuPlugin.tsx @@ -0,0 +1,79 @@ +import type { PluginConfig } from '@udecode/plate-common'; + +import { createTPlatePlugin } from '@udecode/plate-common/react'; + +import type { BlockSelectionConfig } from './BlockSelectionPlugin'; + +export const BLOCK_CONTEXT_MENU_ID = 'context'; + +type OpenId = (string & {}) | typeof BLOCK_CONTEXT_MENU_ID; + +export type BlockMenuConfig = PluginConfig< + 'blockMenu', + { + position: { + x: number; + y: number; + }; + openId: OpenId | null; + }, + { + blockMenu: BlockMenuApi; + } +>; + +export type BlockMenuApi = { + hide: () => void; + show: (id: OpenId, position?: { x: number; y: number }) => void; +}; + +export const BlockMenuPlugin = createTPlatePlugin({ + key: 'blockMenu', + options: { + openId: null, + position: { + x: -10_000, + y: -10_000, + }, + }, +}) + .extendApi>(({ setOption, setOptions }) => ({ + hide: () => { + setOptions({ + openId: null, + position: { + x: -10_000, + y: -10_000, + }, + }); + }, + show: (id, position) => { + if (position) { + setOptions({ + openId: id, + position, + }); + } else { + setOption('openId', id); + } + }, + })) + .extendApi(({ api, editor }) => ({ + showContextMenu: (blockId: string, position: { x: number; y: number }) => { + editor + .getApi({ key: 'blockSelection' }) + .blockSelection?.addSelectedRow(blockId); + api.blockMenu.show(BLOCK_CONTEXT_MENU_ID, position); + }, + })) + .extend(({ api }) => ({ + handlers: { + onMouseDown: ({ event, getOptions }) => { + if (event.button === 0 && getOptions().openId) { + event.preventDefault(); + api.blockMenu.hide(); + } + if (event.button === 2) event.preventDefault(); + }, + }, + })); diff --git a/packages/selection/src/react/BlockSelectionAfterEditable.tsx b/packages/selection/src/react/BlockSelectionAfterEditable.tsx deleted file mode 100644 index f9c84c2e88..0000000000 --- a/packages/selection/src/react/BlockSelectionAfterEditable.tsx +++ /dev/null @@ -1,198 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; - -import { - findNode, - getEndPoint, - getNextNode, - getPreviousNode, - isHotkey, - removeNodes, -} from '@udecode/plate-common'; -import { - type EditableSiblingComponent, - focusEditor, - isEditorReadOnly, - useEditorPlugin, - useEditorRef, -} from '@udecode/plate-common/react'; - -import { BlockContextMenuPlugin } from './BlockContextMenuPlugin'; -import { - type BlockSelectionConfig, - BlockSelectionPlugin, -} from './BlockSelectionPlugin'; -import { useSelectionArea } from './useSelectionArea'; -import { - copySelectedBlocks, - pasteSelectedBlocks, - selectInsertedBlocks, -} from './utils'; - -export const BlockSelectionAfterEditable: EditableSiblingComponent = () => { - const editor = useEditorRef(); - const { api, getOption, getOptions, useOption } = - useEditorPlugin(BlockSelectionPlugin); - const isSelecting = useOption('isSelecting'); - const selectedIds = useOption('selectedIds'); - const blockContextMenu = useEditorPlugin(BlockContextMenuPlugin); - const isOpen = blockContextMenu.useOption('isOpen', editor.id); - - useSelectionArea(); - - const inputRef = React.useRef(null); - const [isMounted, setIsMounted] = React.useState(false); - - React.useEffect(() => { - setIsMounted(true); - - return () => { - setIsMounted(false); - }; - }, []); - - React.useEffect(() => { - if (isSelecting && !isOpen && inputRef.current) { - inputRef.current.focus(); - } else if (inputRef.current) { - inputRef.current.blur(); - } - }, [isSelecting, isOpen]); - - const handleKeyDown = React.useCallback( - (e: React.KeyboardEvent) => { - const isReadonly = isEditorReadOnly(editor); - getOptions().onKeyDownSelecting?.(e.nativeEvent); - - // selecting commands - if (!getOptions().isSelecting) return; - if (isHotkey('escape')(e)) { - api.blockSelection.unselect(); - } - if (isHotkey('mod+z')(e)) { - editor.undo(); - selectInsertedBlocks(editor); - } - if (isHotkey('mod+shift+z')(e)) { - editor.redo(); - selectInsertedBlocks(editor); - } - // selecting some commands - if (!getOption('isSelectingSome')) return; - if (isHotkey('enter')(e)) { - // get the first block in the selection - const entry = findNode(editor, { - at: [], - match: (n) => selectedIds!.has(n.id), - }); - - if (entry) { - const [, path] = entry; - - // focus the end of that block - focusEditor(editor, getEndPoint(editor, path)); - e.preventDefault(); - } - } - if (isHotkey(['backspace', 'delete'])(e) && !isReadonly) { - removeNodes(editor, { - at: [], - match: (n) => selectedIds!.has(n.id), - }); - } - // TODO: skip toggle child - if (isHotkey('up')(e)) { - const firstId = [...selectedIds!][0]; - const node = findNode(editor, { - at: [], - match: (n) => n.id === firstId, - }); - const prev = getPreviousNode(editor, { - at: node?.[1], - }); - - const prevId = prev?.[0].id; - api.blockSelection.addSelectedRow(prevId); - } - if (isHotkey('down')(e)) { - const lastId = [...selectedIds!].pop(); - const node = findNode(editor, { - at: [], - match: (n) => n.id === lastId, - }); - const next = getNextNode(editor, { - at: node?.[1], - }); - const nextId = next?.[0].id; - api.blockSelection.addSelectedRow(nextId); - } - }, - [editor, selectedIds, api, getOptions, getOption] - ); - - const handleCopy = React.useCallback( - (e: React.ClipboardEvent) => { - e.preventDefault(); - - if (getOption('isSelectingSome')) { - copySelectedBlocks(editor); - } - }, - [editor, getOption] - ); - - const handleCut = React.useCallback( - (e: React.ClipboardEvent) => { - e.preventDefault(); - - if (getOption('isSelectingSome')) { - copySelectedBlocks(editor); - - if (!isEditorReadOnly(editor)) { - removeNodes(editor, { - at: [], - match: (n) => selectedIds!.has(n.id), - }); - - focusEditor(editor); - } - } - }, - [editor, selectedIds, getOption] - ); - - const handlePaste = React.useCallback( - (e: React.ClipboardEvent) => { - e.preventDefault(); - - if (!isEditorReadOnly(editor)) { - pasteSelectedBlocks(editor, e.nativeEvent); - } - }, - [editor] - ); - - if (!isMounted || typeof window === 'undefined') { - return null; - } - - return ReactDOM.createPortal( - , - document.body - ); -}; diff --git a/packages/selection/src/react/BlockSelectionPlugin.tsx b/packages/selection/src/react/BlockSelectionPlugin.tsx index 0ac37fa3ef..d941f614b5 100644 --- a/packages/selection/src/react/BlockSelectionPlugin.tsx +++ b/packages/selection/src/react/BlockSelectionPlugin.tsx @@ -1,22 +1,53 @@ import type { CSSProperties } from 'react'; +import React from 'react'; +import ReactDOM from 'react-dom'; import { type PluginConfig, type QueryNodeOptions, + type TElement, type TNodeEntry, + bindFirst, getNodeEntries, } from '@udecode/plate-common'; +import { + findNode, + getEndPoint, + getNextNode, + getPreviousNode, + isHotkey, + removeNodes, +} from '@udecode/plate-common'; import { createTPlatePlugin } from '@udecode/plate-common/react'; +import { + type EditableSiblingComponent, + focusEditor, + isEditorReadOnly, + useEditorPlugin, + useEditorRef, +} from '@udecode/plate-common/react'; import type { ChangedElements, PartialSelectionOptions } from '../internal'; import { getAllSelectableDomNode, getSelectedDomNode } from '../lib'; import { extractSelectableIds } from '../lib/extractSelectableIds'; -import { BlockContextMenuPlugin } from './BlockContextMenuPlugin'; -import { BlockSelectionAfterEditable } from './BlockSelectionAfterEditable'; +import { BlockMenuPlugin } from './BlockMenuPlugin'; import { BlockSelectable } from './components/BlockSelectable'; +import { useSelectionArea } from './hooks/useSelectionArea'; import { onKeyDownSelection } from './onKeyDownSelection'; -import { onChangeBlockSelection } from './utils'; +import { duplicateBlockSelectionNodes } from './transforms/duplicateBlockSelectionNodes'; +import { removeBlockSelectionNodes } from './transforms/removeBlockSelectionNodes'; +import { selectBlockSelectionNodes } from './transforms/selectBlockSelectionNodes'; +import { + setBlockSelectionNodes, + setBlockSelectionTexts, +} from './transforms/setBlockSelectionNodes'; +import { + copySelectedBlocks, + onChangeBlockSelection, + pasteSelectedBlocks, + selectInsertedBlocks, +} from './utils'; export type BlockSelectionConfig = PluginConfig< 'blockSelection', @@ -46,13 +77,181 @@ export type BlockSelectionApi = { id: string, options?: { aboveHtmlNode?: HTMLDivElement; clear?: boolean } ) => void; - getSelectedBlocks: () => TNodeEntry[]; + setSelectedIds: ( + options: Partial & { ids?: string[] } + ) => void; + getNodes: () => TNodeEntry[]; resetSelectedIds: () => void; selectedAll: () => void; - setSelectedIds: (options: ChangedElements & { ids?: string[] }) => void; unselect: () => void; }; +export const BlockSelectionAfterEditable: EditableSiblingComponent = () => { + const editor = useEditorRef(); + const { api, getOption, getOptions, useOption } = + useEditorPlugin({ key: 'blockSelection' }); + const isSelecting = useOption('isSelecting'); + const selectedIds = useOption('selectedIds'); + + useSelectionArea(); + + const inputRef = React.useRef(null); + const [isMounted, setIsMounted] = React.useState(false); + + React.useEffect(() => { + setIsMounted(true); + + return () => { + setIsMounted(false); + }; + }, []); + + React.useEffect(() => { + if (isSelecting && inputRef.current) { + inputRef.current.focus(); + } else if (inputRef.current) { + inputRef.current.blur(); + } + }, [isSelecting]); + + const handleKeyDown = React.useCallback( + (e: React.KeyboardEvent) => { + const isReadonly = isEditorReadOnly(editor); + getOptions().onKeyDownSelecting?.(e.nativeEvent); + + // selecting commands + if (!getOptions().isSelecting) return; + if (isHotkey('escape')(e)) { + api.blockSelection.unselect(); + } + if (isHotkey('mod+z')(e)) { + editor.undo(); + selectInsertedBlocks(editor); + } + if (isHotkey('mod+shift+z')(e)) { + editor.redo(); + selectInsertedBlocks(editor); + } + // selecting some commands + if (!getOption('isSelectingSome')) return; + if (isHotkey('enter')(e)) { + // get the first block in the selection + const entry = findNode(editor, { + at: [], + match: (n) => selectedIds!.has(n.id), + }); + + if (entry) { + const [, path] = entry; + + // focus the end of that block + focusEditor(editor, getEndPoint(editor, path)); + e.preventDefault(); + } + } + if (isHotkey(['backspace', 'delete'])(e) && !isReadonly) { + removeNodes(editor, { + at: [], + match: (n) => selectedIds!.has(n.id), + }); + } + // TODO: skip toggle child + if (isHotkey('up')(e)) { + const firstId = [...selectedIds!][0]; + const node = findNode(editor, { + at: [], + match: (n) => n.id === firstId, + }); + const prev = getPreviousNode(editor, { + at: node?.[1], + }); + + const prevId = prev?.[0].id; + api.blockSelection.addSelectedRow(prevId); + } + if (isHotkey('down')(e)) { + const lastId = [...selectedIds!].pop(); + const node = findNode(editor, { + at: [], + match: (n) => n.id === lastId, + }); + const next = getNextNode(editor, { + at: node?.[1], + }); + const nextId = next?.[0].id; + api.blockSelection.addSelectedRow(nextId); + } + }, + [editor, selectedIds, api, getOptions, getOption] + ); + + const handleCopy = React.useCallback( + (e: React.ClipboardEvent) => { + e.preventDefault(); + + if (getOption('isSelectingSome')) { + copySelectedBlocks(editor); + } + }, + [editor, getOption] + ); + + const handleCut = React.useCallback( + (e: React.ClipboardEvent) => { + e.preventDefault(); + + if (getOption('isSelectingSome')) { + copySelectedBlocks(editor); + + if (!isEditorReadOnly(editor)) { + removeNodes(editor, { + at: [], + match: (n) => selectedIds!.has(n.id), + }); + + focusEditor(editor); + } + } + }, + [editor, selectedIds, getOption] + ); + + const handlePaste = React.useCallback( + (e: React.ClipboardEvent) => { + e.preventDefault(); + + if (!isEditorReadOnly(editor)) { + pasteSelectedBlocks(editor, e.nativeEvent); + } + }, + [editor] + ); + + if (!isMounted || typeof window === 'undefined') { + return null; + } + + return ReactDOM.createPortal( + , + document.body + ); +}; + export const BlockSelectionPlugin = createTPlatePlugin({ key: 'blockSelection', options: { @@ -78,7 +277,7 @@ export const BlockSelectionPlugin = createTPlatePlugin({ }, selectedIds: new Set(), }, - plugins: [BlockContextMenuPlugin], + plugins: [BlockMenuPlugin], render: { aboveNodes: () => @@ -100,64 +299,78 @@ export const BlockSelectionPlugin = createTPlatePlugin({ isSelected: (id?: string) => !!id && getOptions().selectedIds!.has(id), isSelectingSome: () => getOptions().selectedIds!.size > 0, })) - .extendApi>(({ getOptions, setOption }) => ({ - resetSelectedIds: () => { - setOption('selectedIds', new Set()); - }, - setSelectedIds: ({ added, ids, removed }) => { - if (ids) { - setOption('selectedIds', new Set(ids)); - } - if (added || removed) { - const { selectedIds: prev } = getOptions(); - const next = new Set(prev); - extractSelectableIds(added).forEach((id) => next.add(id)); - extractSelectableIds(removed).forEach((id) => next.delete(id)); - - setOption('selectedIds', next); - } - - setOption('isSelecting', true); - }, - unselect: () => { - setOption('selectedIds', new Set()); - setOption('isSelecting', false); - }, - })) .extendApi>( - ({ api, editor, getOption, getOptions, setOption }) => ({ - addSelectedRow: (id, options = {}) => { - const { aboveHtmlNode, clear = true } = options; - - const element = aboveHtmlNode ?? getSelectedDomNode(id); - - if (!element) return; - if (!getOptions().selectedIds!.has(id) && clear) { - setOption('selectedIds', new Set()); - } + ({ editor, getOption, getOptions, setOption }) => ({ + getNodes: () => { + const selectedIds = getOption('selectedIds'); - api.blockSelection.setSelectedIds({ - added: [element], - removed: [], - }); - }, - getSelectedBlocks: () => { return [ - ...getNodeEntries(editor, { + ...getNodeEntries(editor, { at: [], - match: (n) => getOption('isSelected', n.id), + match: (n) => selectedIds?.has(n.id), }), ]; }, - - selectedAll: () => { - const all = getAllSelectableDomNode(); + resetSelectedIds: () => { setOption('selectedIds', new Set()); + }, + setSelectedIds: ({ added, ids, removed }) => { + if (ids) { + setOption('selectedIds', new Set(ids)); + } + if (added || removed) { + const { selectedIds: prev } = getOptions(); + const next = new Set(prev); - api.blockSelection.setSelectedIds({ - added: Array.from(all), - removed: [], - }); + if (added) { + extractSelectableIds(added).forEach((id) => next.add(id)); + } + if (removed) { + extractSelectableIds(removed).forEach((id) => next.delete(id)); + } + + setOption('selectedIds', next); + } + + setOption('isSelecting', true); + }, + unselect: () => { + setOption('selectedIds', new Set()); + setOption('isSelecting', false); }, }) - ); + ) + .extendApi>(({ api, getOptions, setOption }) => ({ + addSelectedRow: (id, options = {}) => { + const { aboveHtmlNode, clear = true } = options; + + const element = aboveHtmlNode ?? getSelectedDomNode(id); + + if (!element) return; + if (!getOptions().selectedIds!.has(id) && clear) { + setOption('selectedIds', new Set()); + } + + api.blockSelection.setSelectedIds({ + added: [element], + removed: [], + }); + }, + + selectedAll: () => { + const all = getAllSelectableDomNode(); + setOption('selectedIds', new Set()); + + api.blockSelection.setSelectedIds({ + added: Array.from(all), + removed: [], + }); + }, + })) + .extendTransforms(({ editor }) => ({ + duplicate: bindFirst(duplicateBlockSelectionNodes, editor), + removeNodes: bindFirst(removeBlockSelectionNodes, editor), + select: bindFirst(selectBlockSelectionNodes, editor), + setNodes: bindFirst(setBlockSelectionNodes, editor), + setTexts: bindFirst(setBlockSelectionTexts, editor), + })); diff --git a/packages/selection/src/react/context-menu/index.ts b/packages/selection/src/react/context-menu/index.ts deleted file mode 100644 index 866fb2c896..0000000000 --- a/packages/selection/src/react/context-menu/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * @file Automatically generated by barrelsby. - */ - -export * from './types'; -export * from './useBlockContextMenu'; -export * from './useBlockMenuItems'; diff --git a/packages/selection/src/react/context-menu/types.ts b/packages/selection/src/react/context-menu/types.ts deleted file mode 100644 index 22a0d2553b..0000000000 --- a/packages/selection/src/react/context-menu/types.ts +++ /dev/null @@ -1,14 +0,0 @@ -export type CommandItem = { - title: string; - value: string; - shortcut?: string; -}; - -export interface Menu { - heading: string; - items: CommandItem[]; -} - -export const ACTION_DELETE = 'context_menu_delete'; - -export const ACTION_COPY = 'context_menu_copy'; diff --git a/packages/selection/src/react/context-menu/useBlockContextMenu.ts b/packages/selection/src/react/context-menu/useBlockContextMenu.ts deleted file mode 100644 index 6626efe2a2..0000000000 --- a/packages/selection/src/react/context-menu/useBlockContextMenu.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { useMemo } from 'react'; - -import { useEditorPlugin } from '@udecode/plate-common/react'; - -import { BlockContextMenuPlugin } from '../BlockContextMenuPlugin'; -import { BlockSelectionPlugin } from '../BlockSelectionPlugin'; - -export const useBlockContextMenuState = () => { - const { api, editor, useOption } = useEditorPlugin(BlockSelectionPlugin); - const blockContextMenu = useEditorPlugin(BlockContextMenuPlugin); - - const isOpen = blockContextMenu.useOption('isOpen', editor.id); - const selectedIds = useOption('selectedIds'); - - // eslint-disable-next-line react-hooks/exhaustive-deps - const action = useMemo(() => blockContextMenu.getOptions().action, [isOpen]); - const selectedBlocks = api.blockSelection.getSelectedBlocks(); - - return { - action, - editor, - isOpen, - selectedBlocks, - selectedIds, - }; -}; - -export const useBlockContextMenu = () => { - return { - props: { - // onOpenChange: (value: boolean) => { - // if (value) { - // api.show(editor.id); - // } else { - // api.hide(); - // } - // }, - }, - }; -}; diff --git a/packages/selection/src/react/context-menu/useBlockMenuItems.ts b/packages/selection/src/react/context-menu/useBlockMenuItems.ts deleted file mode 100644 index b5384ba2c9..0000000000 --- a/packages/selection/src/react/context-menu/useBlockMenuItems.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { useEditorPlugin } from '@udecode/plate-common/react'; - -import { BlockContextMenuPlugin } from '../BlockContextMenuPlugin'; -import { BlockSelectionPlugin } from '../BlockSelectionPlugin'; - -export const useBlockMenuItemsState = () => { - const { api, editor, useOption } = useEditorPlugin(BlockSelectionPlugin); - const blockContextMenu = useEditorPlugin(BlockContextMenuPlugin); - const isOpen = blockContextMenu.useOption('isOpen', editor.id); - const selectedIds = useOption('selectedIds'); - - const selectedBlocks = api.blockSelection.getSelectedBlocks(); - - return { - isOpen, - selectedBlocks, - selectedIds, - }; -}; - -export const useBlockMenuItems = () => { - return {}; -}; diff --git a/packages/selection/src/react/hooks/index.ts b/packages/selection/src/react/hooks/index.ts new file mode 100644 index 0000000000..3d9c2f8267 --- /dev/null +++ b/packages/selection/src/react/hooks/index.ts @@ -0,0 +1,8 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './useBlockSelected'; +export * from './useBlockSelectionNodes'; +export * from './useIsSelecting'; +export * from './useSelectionArea'; diff --git a/packages/selection/src/react/useBlockSelected.ts b/packages/selection/src/react/hooks/useBlockSelected.ts similarity index 83% rename from packages/selection/src/react/useBlockSelected.ts rename to packages/selection/src/react/hooks/useBlockSelected.ts index abefd290cf..c28da78d02 100644 --- a/packages/selection/src/react/useBlockSelected.ts +++ b/packages/selection/src/react/hooks/useBlockSelected.ts @@ -1,6 +1,6 @@ import { useEditorPlugin, useElement } from '@udecode/plate-common/react'; -import { BlockSelectionPlugin } from './BlockSelectionPlugin'; +import { BlockSelectionPlugin } from '../BlockSelectionPlugin'; export const useBlockSelected = (_id?: string) => { const { useOption } = useEditorPlugin(BlockSelectionPlugin); diff --git a/packages/selection/src/react/hooks/useBlockSelectionNodes.ts b/packages/selection/src/react/hooks/useBlockSelectionNodes.ts new file mode 100644 index 0000000000..f4543c1890 --- /dev/null +++ b/packages/selection/src/react/hooks/useBlockSelectionNodes.ts @@ -0,0 +1,39 @@ +import { useMemo } from 'react'; + +import { + type GetFragmentPropOptions, + type TElement, + getFragmentProp, + getNodeEntries, +} from '@udecode/plate-common'; +import { useEditorPlugin } from '@udecode/plate-common/react'; + +import { BlockSelectionPlugin } from '../BlockSelectionPlugin'; + +export function useBlockSelectionNodes() { + const { editor, useOption } = useEditorPlugin(BlockSelectionPlugin); + const selectedIds = useOption('selectedIds'); + + return useMemo(() => { + return [ + ...getNodeEntries(editor, { + at: [], + match: (n) => selectedIds?.has(n.id), + }), + ]; + }, [editor, selectedIds]); +} + +export function useBlockSelectionFragment() { + const nodes = useBlockSelectionNodes(); + + return useMemo(() => nodes.map(([node]) => node), [nodes]); +} + +export function useBlockSelectionFragmentProp( + options?: GetFragmentPropOptions +) { + const fragment = useBlockSelectionFragment(); + + return useMemo(() => getFragmentProp(fragment, options), [fragment, options]); +} diff --git a/packages/selection/src/react/useIsSelecting.ts b/packages/selection/src/react/hooks/useIsSelecting.ts similarity index 85% rename from packages/selection/src/react/useIsSelecting.ts rename to packages/selection/src/react/hooks/useIsSelecting.ts index 71446a5b99..40dd38b126 100644 --- a/packages/selection/src/react/useIsSelecting.ts +++ b/packages/selection/src/react/hooks/useIsSelecting.ts @@ -6,8 +6,8 @@ import { useEditorSelector, } from '@udecode/plate-common/react'; -import { isSelecting } from '../lib'; -import { BlockSelectionPlugin } from './BlockSelectionPlugin'; +import { isSelecting } from '../../lib'; +import { BlockSelectionPlugin } from '../BlockSelectionPlugin'; export const isSelectingOrFocused = (editor: PlateEditor) => { return isSelecting(editor) || isEditorFocused(editor); diff --git a/packages/selection/src/react/useSelectionArea.ts b/packages/selection/src/react/hooks/useSelectionArea.ts similarity index 91% rename from packages/selection/src/react/useSelectionArea.ts rename to packages/selection/src/react/hooks/useSelectionArea.ts index 023d2ccc0f..33fc750362 100644 --- a/packages/selection/src/react/useSelectionArea.ts +++ b/packages/selection/src/react/hooks/useSelectionArea.ts @@ -2,8 +2,8 @@ import React from 'react'; import { deselectEditor, useEditorPlugin } from '@udecode/plate-common/react'; -import { SelectionArea } from '../internal'; -import { BlockSelectionPlugin } from './BlockSelectionPlugin'; +import { SelectionArea } from '../../internal'; +import { BlockSelectionPlugin } from '../BlockSelectionPlugin'; export const useSelectionArea = () => { const { api, editor, getOptions, setOption } = diff --git a/packages/selection/src/react/index.ts b/packages/selection/src/react/index.ts index 5313c72d15..7401039991 100644 --- a/packages/selection/src/react/index.ts +++ b/packages/selection/src/react/index.ts @@ -2,13 +2,10 @@ * @file Automatically generated by barrelsby. */ -export * from './BlockContextMenuPlugin'; +export * from './BlockMenuPlugin'; export * from './BlockSelectionPlugin'; export * from './onKeyDownSelection'; -export * from './BlockSelectionAfterEditable'; -export * from './useSelectionArea'; export * from './components/index'; -export * from './context-menu/index'; -export * from './useBlockSelected'; -export * from './useIsSelecting'; +export * from './hooks/index'; +export * from './transforms/index'; export * from './utils/index'; diff --git a/packages/selection/src/react/transforms/duplicateBlockSelectionNodes.ts b/packages/selection/src/react/transforms/duplicateBlockSelectionNodes.ts new file mode 100644 index 0000000000..1685a08a8f --- /dev/null +++ b/packages/selection/src/react/transforms/duplicateBlockSelectionNodes.ts @@ -0,0 +1,38 @@ +import type { PlateEditor } from '@udecode/plate-common/react'; + +import { + type TNodeEntry, + duplicateBlocks, + getNodeEntry, +} from '@udecode/plate-common'; +import { Path } from 'slate'; + +import { BlockSelectionPlugin } from '../BlockSelectionPlugin'; + +export const duplicateBlockSelectionNodes = ( + editor: PlateEditor, + blocks: TNodeEntry[] +) => { + duplicateBlocks(editor, blocks); + + const lastBlock = blocks.at(-1); + + if (!lastBlock) return; + + const path = Path.next(lastBlock[1]); + + const ids = blocks + .map((_, index) => { + const targetPath = [path[0] + index]; + const targetNode = getNodeEntry(editor, targetPath); + + return targetNode?.[0].id; + }) + .filter(Boolean); + + setTimeout(() => { + editor + .getApi(BlockSelectionPlugin) + .blockSelection.setSelectedIds({ ids } as any); + }, 0); +}; diff --git a/packages/selection/src/react/transforms/index.ts b/packages/selection/src/react/transforms/index.ts new file mode 100644 index 0000000000..d50706e0fd --- /dev/null +++ b/packages/selection/src/react/transforms/index.ts @@ -0,0 +1,8 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './duplicateBlockSelectionNodes'; +export * from './removeBlockSelectionNodes'; +export * from './selectBlockSelectionNodes'; +export * from './setBlockSelectionNodes'; diff --git a/packages/selection/src/react/transforms/removeBlockSelectionNodes.ts b/packages/selection/src/react/transforms/removeBlockSelectionNodes.ts new file mode 100644 index 0000000000..c2f2100eb6 --- /dev/null +++ b/packages/selection/src/react/transforms/removeBlockSelectionNodes.ts @@ -0,0 +1,14 @@ +import type { SlateEditor } from '@udecode/plate-common'; + +import { BlockSelectionPlugin } from '../BlockSelectionPlugin'; + +export const removeBlockSelectionNodes = (editor: SlateEditor) => { + const selectedIds = editor.getOption(BlockSelectionPlugin, 'selectedIds'); + + if (!selectedIds) return; + + editor.removeNodes({ + at: [], + match: (n) => selectedIds.has((n as any).id), + }); +}; diff --git a/packages/selection/src/react/transforms/selectBlockSelectionNodes.ts b/packages/selection/src/react/transforms/selectBlockSelectionNodes.ts new file mode 100644 index 0000000000..a66200c15b --- /dev/null +++ b/packages/selection/src/react/transforms/selectBlockSelectionNodes.ts @@ -0,0 +1,11 @@ +import { type SlateEditor, selectNodes } from '@udecode/plate-common'; + +import { BlockSelectionPlugin } from '../BlockSelectionPlugin'; + +export const selectBlockSelectionNodes = (editor: SlateEditor) => { + selectNodes( + editor, + editor.getApi(BlockSelectionPlugin).blockSelection.getNodes() + ); + editor.getApi(BlockSelectionPlugin).blockSelection.resetSelectedIds(); +}; diff --git a/packages/selection/src/react/transforms/setBlockSelectionNodes.ts b/packages/selection/src/react/transforms/setBlockSelectionNodes.ts new file mode 100644 index 0000000000..0dc5569912 --- /dev/null +++ b/packages/selection/src/react/transforms/setBlockSelectionNodes.ts @@ -0,0 +1,42 @@ +import type { PlateEditor } from '@udecode/plate-common/react'; + +import { + type SetNodesOptions, + type TElement, + type TNodeProps, + type TText, + setNodes, + withoutNormalizing, +} from '@udecode/plate-common'; + +import { BlockSelectionPlugin } from '../BlockSelectionPlugin'; + +export const setBlockSelectionNodes = ( + editor: PlateEditor, + props: Partial>, + options?: SetNodesOptions +) => { + withoutNormalizing(editor, () => { + const blocks = editor + .getApi(BlockSelectionPlugin) + .blockSelection.getNodes(); + + blocks.forEach(([, path]) => { + setNodes(editor, props, { + ...options, + at: path, + }); + }); + }); +}; + +export const setBlockSelectionTexts = ( + editor: PlateEditor, + props: Partial>, + options?: Omit +) => { + setBlockSelectionNodes(editor, props, { + mode: 'lowest', + ...options, + }); +}; diff --git a/packages/selection/src/react/utils/copySelectedBlocks.ts b/packages/selection/src/react/utils/copySelectedBlocks.ts index d2537c3f3d..c5c29deb05 100644 --- a/packages/selection/src/react/utils/copySelectedBlocks.ts +++ b/packages/selection/src/react/utils/copySelectedBlocks.ts @@ -1,7 +1,6 @@ import { type SlateEditor, deselect, - getEditorPlugin, getEndPoint, getStartPoint, select, @@ -12,13 +11,10 @@ import copyToClipboard from 'copy-to-clipboard'; import { BlockSelectionPlugin } from '../BlockSelectionPlugin'; export const copySelectedBlocks = (editor: SlateEditor) => { - const { api, getOptions, setOption } = getEditorPlugin( - editor, - BlockSelectionPlugin - ); - - const { selectedIds } = getOptions(); - const selectedEntries = api.blockSelection.getSelectedBlocks(); + const { selectedIds } = editor.getOptions(BlockSelectionPlugin); + const selectedEntries = editor + .getApi(BlockSelectionPlugin) + .blockSelection.getNodes(); const selectedFragment = selectedEntries.map(([node]) => node); copyToClipboard(' ', { @@ -52,7 +48,7 @@ export const copySelectedBlocks = (editor: SlateEditor) => { // deselect and select back selectedIds deselect(editor); - setOption('selectedIds', selectedIds); + editor.setOption(BlockSelectionPlugin, 'selectedIds', selectedIds); }); data.setData('text/plain', textPlain); diff --git a/packages/selection/src/react/utils/index.ts b/packages/selection/src/react/utils/index.ts index 0e0ad49409..2458ea1fcb 100644 --- a/packages/selection/src/react/utils/index.ts +++ b/packages/selection/src/react/utils/index.ts @@ -4,6 +4,5 @@ export * from './copySelectedBlocks'; export * from './onChangeBlockSelection'; -export * from './openContextMenu'; export * from './pasteSelectedBlocks'; export * from './selectInsertedBlocks'; diff --git a/packages/selection/src/react/utils/onChangeBlockSelection.ts b/packages/selection/src/react/utils/onChangeBlockSelection.ts index 29105efa49..2e8f353337 100644 --- a/packages/selection/src/react/utils/onChangeBlockSelection.ts +++ b/packages/selection/src/react/utils/onChangeBlockSelection.ts @@ -1,24 +1,20 @@ import type { OnChange } from '@udecode/plate-common/react'; -import { getEditorPlugin } from '@udecode/plate-common'; - import type { BlockSelectionConfig } from '../BlockSelectionPlugin'; -import { BlockContextMenuPlugin } from '../BlockContextMenuPlugin'; +import { BlockMenuPlugin } from '../BlockMenuPlugin'; export const onChangeBlockSelection: OnChange = ({ api, editor, getOptions, }) => { - const blockContextMenu = getEditorPlugin(editor, BlockContextMenuPlugin); - if ( editor.selection && getOptions().isSelecting && - !blockContextMenu.getOption('isOpen', editor.id) + !editor.getOption(BlockMenuPlugin, 'openId') ) { api.blockSelection.unselect(); - blockContextMenu.api.blockContextMenu.hide(); + editor.getApi(BlockMenuPlugin).blockMenu.hide(); } }; diff --git a/packages/selection/src/react/utils/openContextMenu.ts b/packages/selection/src/react/utils/openContextMenu.ts deleted file mode 100644 index 0c11aaa808..0000000000 --- a/packages/selection/src/react/utils/openContextMenu.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { - type SlateEditor, - collapseSelection, - getAncestorNode, - getEditorPlugin, -} from '@udecode/plate-common'; - -import { BlockContextMenuPlugin } from '../BlockContextMenuPlugin'; -import { BlockSelectionPlugin } from '../BlockSelectionPlugin'; - -export const openContextMenu = ( - editor: SlateEditor, - e: any, - selectedId?: string -) => { - const { api } = getEditorPlugin(editor, BlockSelectionPlugin); - const blockContextMenu = getEditorPlugin(editor, BlockContextMenuPlugin); - - const id = selectedId ?? getAncestorNode(editor)?.[0].id; - - if (!id) return; - - api.blockSelection.addSelectedRow(id); - blockContextMenu.api.blockContextMenu.show(editor.id, e); - - collapseSelection(editor); - - return true; -}; diff --git a/packages/selection/src/react/utils/pasteSelectedBlocks.ts b/packages/selection/src/react/utils/pasteSelectedBlocks.ts index e926de3a6e..f4ef1e017e 100644 --- a/packages/selection/src/react/utils/pasteSelectedBlocks.ts +++ b/packages/selection/src/react/utils/pasteSelectedBlocks.ts @@ -15,7 +15,7 @@ import { selectInsertedBlocks } from './selectInsertedBlocks'; export const pasteSelectedBlocks = (editor: SlateEditor, e: ClipboardEvent) => { const { api } = getEditorPlugin(editor, BlockSelectionPlugin); - const entries = api.blockSelection.getSelectedBlocks(); + const entries = api.blockSelection.getNodes(); if (entries.length > 0) { const entry = entries.at(-1)!; diff --git a/packages/slate-react/src/utils/index.ts b/packages/slate-react/src/utils/index.ts index 4c38dec562..df80ea5dce 100644 --- a/packages/slate-react/src/utils/index.ts +++ b/packages/slate-react/src/utils/index.ts @@ -3,3 +3,4 @@ */ export * from './focusEditorEdge'; +export * from './setNode'; diff --git a/packages/slate-react/src/utils/setNode.ts b/packages/slate-react/src/utils/setNode.ts new file mode 100644 index 0000000000..12f266d350 --- /dev/null +++ b/packages/slate-react/src/utils/setNode.ts @@ -0,0 +1,24 @@ +import type { + NodeOf, + SetNodesOptions, + TEditor, + TNodeProps, +} from '@udecode/slate'; + +import { findNodePath } from '../react-editor'; + +export const setNode = , E extends TEditor = TEditor>( + editor: E, + node: N, + props: Partial>, + options?: Omit, 'at'> +) => { + const path = findNodePath(editor, node); + + if (!path) return; + + editor.setNodes(props, { + ...options, + at: path, + } as any); +}; diff --git a/packages/slate-utils/src/queries/getBlocks.ts b/packages/slate-utils/src/queries/getBlocks.ts new file mode 100644 index 0000000000..b92f203d84 --- /dev/null +++ b/packages/slate-utils/src/queries/getBlocks.ts @@ -0,0 +1,18 @@ +import { + type ElementOf, + type GetNodeEntriesOptions, + type TEditor, + getNodeEntries, +} from '@udecode/slate'; + +export const getBlocks = , E extends TEditor = TEditor>( + editor: E, + options?: GetNodeEntriesOptions +) => { + return [ + ...getNodeEntries(editor, { + ...options, + block: true, + }), + ]; +}; diff --git a/packages/slate-utils/src/queries/getFragmentProp.ts b/packages/slate-utils/src/queries/getFragmentProp.ts new file mode 100644 index 0000000000..cba0f3db9d --- /dev/null +++ b/packages/slate-utils/src/queries/getFragmentProp.ts @@ -0,0 +1,59 @@ +import { type TElement, type TText, getNodeTexts } from '@udecode/slate'; + +export type GetFragmentPropOptions = { + key?: string; + defaultValue?: string; + getProp?: (node: TElement | TText) => any; + mode?: 'all' | 'block' | 'text'; +}; + +export function getFragmentProp( + fragment: TElement[], + { key, defaultValue, getProp, mode = 'block' }: GetFragmentPropOptions = {} +): string | undefined { + if (fragment.length === 0) return defaultValue; + + const getNodeValue = + getProp ?? + ((node) => { + return node[key!]!; + }); + + let value: string | undefined; + + for (const node of fragment) { + if (mode === 'block' || mode === 'all') { + const nodeValue = getNodeValue(node); + + if (nodeValue !== undefined) { + if (value === undefined) { + value = nodeValue; + } else if (value !== nodeValue) { + return; + } + if (mode === 'block') continue; + } else if (mode === 'block') { + return defaultValue; + } + } + if (mode === 'text' || mode === 'all') { + const textEntries = Array.from(getNodeTexts(node)); + + for (const [text] of textEntries) { + const textValue = getNodeValue(text); + + if (textValue !== undefined) { + if (value === undefined) { + value = textValue; + } else if (value !== textValue) { + return; + } + } else if (mode === 'text') { + return defaultValue; + } + } + } + } + + return value; +} diff --git a/packages/slate-utils/src/queries/getSelectionFragment.ts b/packages/slate-utils/src/queries/getSelectionFragment.ts new file mode 100644 index 0000000000..45982706cb --- /dev/null +++ b/packages/slate-utils/src/queries/getSelectionFragment.ts @@ -0,0 +1,25 @@ +import { + type TEditor, + type TElement, + getFragment, + isExpanded, +} from '@udecode/slate'; + +import { unwrapStructuralNodes } from '../utils/unwrapStructuralNodes'; + +export type GetSelectionFragmentOptions = { + structuralTypes?: string[]; +}; + +export const getSelectionFragment = ( + editor: TEditor, + options?: GetSelectionFragmentOptions +) => { + if (!isExpanded(editor.selection)) return []; + + const fragment = getFragment(editor, editor.selection!) as TElement[]; + + if (fragment.length === 0) return []; + + return unwrapStructuralNodes(fragment, options); +}; diff --git a/packages/slate-utils/src/queries/index.ts b/packages/slate-utils/src/queries/index.ts index ad25bffcab..0ba55547b0 100644 --- a/packages/slate-utils/src/queries/index.ts +++ b/packages/slate-utils/src/queries/index.ts @@ -5,8 +5,10 @@ export * from './findDescendant'; export * from './getAncestorNode'; export * from './getBlockAbove'; +export * from './getBlocks'; export * from './getChildren'; export * from './getEdgeBlocksAbove'; +export * from './getFragmentProp'; export * from './getLastChild'; export * from './getLastNodeByLevel'; export * from './getMark'; @@ -23,6 +25,7 @@ export * from './getPreviousPath'; export * from './getPreviousSiblingNode'; export * from './getRangeBefore'; export * from './getRangeFromBlockStart'; +export * from './getSelectionFragment'; export * from './getSelectionText'; export * from './isAncestorEmpty'; export * from './isBlockAboveEmpty'; diff --git a/packages/slate-utils/src/transforms/duplicateBlocks.ts b/packages/slate-utils/src/transforms/duplicateBlocks.ts new file mode 100644 index 0000000000..2f4f802218 --- /dev/null +++ b/packages/slate-utils/src/transforms/duplicateBlocks.ts @@ -0,0 +1,13 @@ +import type { TEditor, TNodeEntry } from '@udecode/slate'; + +import { Path } from 'slate'; + +export const duplicateBlocks = (editor: TEditor, blocks: TNodeEntry[]) => { + const lastBlock = blocks.at(-1); + + if (!lastBlock) return; + + editor.insertNodes(blocks.map((item) => item[0]) as any, { + at: Path.next(lastBlock[1]), + }); +}; diff --git a/packages/slate-utils/src/transforms/index.ts b/packages/slate-utils/src/transforms/index.ts index 8e2e0d77a0..b300ba5767 100644 --- a/packages/slate-utils/src/transforms/index.ts +++ b/packages/slate-utils/src/transforms/index.ts @@ -2,15 +2,20 @@ * @file Automatically generated by barrelsby. */ +export * from './duplicateBlocks'; export * from './insertElements'; export * from './insertEmptyElement'; export * from './moveChildren'; +export * from './removeEmptyPreviousBlock'; export * from './removeMark'; export * from './removeNodeChildren'; export * from './removeSelectionMark'; export * from './replaceNode'; export * from './replaceNodeChildren'; export * from './selectEndOfBlockAboveSelection'; +export * from './selectNodes'; +export * from './setBlockAboveNode'; +export * from './setBlockNodes'; export * from './setMarks'; export * from './toggleMark'; export * from './toggleWrapNodes'; diff --git a/packages/slate-utils/src/transforms/removeEmptyPreviousBlock.ts b/packages/slate-utils/src/transforms/removeEmptyPreviousBlock.ts new file mode 100644 index 0000000000..988d37d6ce --- /dev/null +++ b/packages/slate-utils/src/transforms/removeEmptyPreviousBlock.ts @@ -0,0 +1,26 @@ +import { + type GetAboveNodeOptions, + type TEditor, + type TElement, + type TNodeEntry, + isElementEmpty, +} from '@udecode/slate'; + +import { getBlockAbove, getPreviousSiblingNode } from '../queries'; + +export const removeEmptyPreviousBlock = ( + editor: E, + options: GetAboveNodeOptions = {} +) => { + const entry = getBlockAbove(editor, options); + + if (!entry) return; + + const prevEntry = getPreviousSiblingNode(editor, entry[1]) as + | TNodeEntry + | undefined; + + if (prevEntry && isElementEmpty(editor, prevEntry[0] as TElement)) { + editor.removeNodes({ at: prevEntry[1] }); + } +}; diff --git a/packages/slate-utils/src/transforms/selectNodes.ts b/packages/slate-utils/src/transforms/selectNodes.ts new file mode 100644 index 0000000000..984c2e7409 --- /dev/null +++ b/packages/slate-utils/src/transforms/selectNodes.ts @@ -0,0 +1,11 @@ +import { type TEditor, type TNodeEntry, setSelection } from '@udecode/slate'; + +import { getNodesRange } from '../queries'; + +export const selectNodes = (editor: TEditor, nodes: TNodeEntry[]) => { + const range = getNodesRange(editor, nodes); + + if (!range) return; + + setSelection(editor, range); +}; diff --git a/packages/slate-utils/src/transforms/setBlockAboveNode.ts b/packages/slate-utils/src/transforms/setBlockAboveNode.ts new file mode 100644 index 0000000000..a960922d65 --- /dev/null +++ b/packages/slate-utils/src/transforms/setBlockAboveNode.ts @@ -0,0 +1,37 @@ +import type { + ElementOf, + SetNodesOptions, + TEditor, + TNodeProps, +} from '@udecode/slate'; + +import { getBlockAbove } from '../queries'; + +export const setBlockAboveNode = < + N extends ElementOf, + E extends TEditor = TEditor, +>( + editor: E, + props: Partial>, + options?: Omit, 'at'> +) => { + const at = getBlockAbove(editor)?.[1]; + + if (!at) return; + + editor.setNodes(props, { + ...options, + at: getBlockAbove(editor)![1], + } as any); +}; + +export const setBlockAboveTexts = < + N extends ElementOf, + E extends TEditor = TEditor, +>( + editor: E, + props: Partial>, + options?: Omit, 'at'> +) => { + setBlockAboveNode(editor, props, { ...options, mode: 'lowest' }); +}; diff --git a/packages/slate-utils/src/transforms/setBlockNodes.ts b/packages/slate-utils/src/transforms/setBlockNodes.ts new file mode 100644 index 0000000000..a454236acd --- /dev/null +++ b/packages/slate-utils/src/transforms/setBlockNodes.ts @@ -0,0 +1,27 @@ +import type { + ElementOf, + GetNodeEntriesOptions, + TEditor, + TNodeProps, +} from '@udecode/slate'; + +import { getBlocks } from '../queries/getBlocks'; + +export const setBlockNodes = < + N extends ElementOf, + E extends TEditor = TEditor, +>( + editor: E, + props: Partial>, + options?: GetNodeEntriesOptions +) => { + editor.withoutNormalizing(() => { + const blocks = getBlocks(editor, options); + + blocks.forEach(([, path]) => { + editor.setNodes(props as any, { + at: path, + }); + }); + }); +}; diff --git a/packages/slate-utils/src/utils/index.ts b/packages/slate-utils/src/utils/index.ts index fcfe3d51db..21112456c2 100644 --- a/packages/slate-utils/src/utils/index.ts +++ b/packages/slate-utils/src/utils/index.ts @@ -4,3 +4,4 @@ export * from './createDocumentNode'; export * from './createNode'; +export * from './unwrapStructuralNodes'; diff --git a/packages/slate-utils/src/utils/unwrapStructuralNodes.ts b/packages/slate-utils/src/utils/unwrapStructuralNodes.ts new file mode 100644 index 0000000000..0059e978a0 --- /dev/null +++ b/packages/slate-utils/src/utils/unwrapStructuralNodes.ts @@ -0,0 +1,22 @@ +import type { TElement } from '@udecode/slate'; + +import type { GetSelectionFragmentOptions } from '../queries/getSelectionFragment'; + +export const unwrapStructuralNodes = ( + nodes: TElement[], + { structuralTypes }: GetSelectionFragmentOptions = {} +) => { + const unwrap = (nodes: TElement[], acc: TElement[] = []): TElement[] => { + nodes.forEach((node) => { + if (structuralTypes?.includes(node.type)) { + return unwrap(node.children as TElement[], acc); + } + + acc.push(node); + }); + + return acc; + }; + + return unwrap(nodes); +}; diff --git a/yarn.lock b/yarn.lock index e6bc81f195..e37f0dfe41 100644 --- a/yarn.lock +++ b/yarn.lock @@ -58,13 +58,6 @@ __metadata: languageName: node linkType: hard -"@ariakit/core@npm:0.4.7": - version: 0.4.7 - resolution: "@ariakit/core@npm:0.4.7" - checksum: 10c0/107f0cf197f2674c0e0b11660a8b5e4a966704914f49726f72ffa81c04ef6d8144c2df0a9f7ac23481b9598d1b10b73cf9e00f211c611172b4946ed024ee4460 - languageName: node - linkType: hard - "@ariakit/react-core@npm:0.4.11": version: 0.4.11 resolution: "@ariakit/react-core@npm:0.4.11" @@ -79,20 +72,6 @@ __metadata: languageName: node linkType: hard -"@ariakit/react-core@npm:0.4.7": - version: 0.4.7 - resolution: "@ariakit/react-core@npm:0.4.7" - dependencies: - "@ariakit/core": "npm:0.4.7" - "@floating-ui/dom": "npm:^1.0.0" - use-sync-external-store: "npm:^1.2.0" - peerDependencies: - react: ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 - checksum: 10c0/8cbd462bed3f328d523c05eee12424b2f70e35e585772d333d6c4513631f10bfab39cc0e36acca09afad4d3e50c44c2305fdbf917574e4e3ab084b425eb8a245 - languageName: node - linkType: hard - "@ariakit/react@npm:0.4.11": version: 0.4.11 resolution: "@ariakit/react@npm:0.4.11" @@ -105,18 +84,6 @@ __metadata: languageName: node linkType: hard -"@ariakit/react@npm:0.4.7": - version: 0.4.7 - resolution: "@ariakit/react@npm:0.4.7" - dependencies: - "@ariakit/react-core": "npm:0.4.7" - peerDependencies: - react: ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 - checksum: 10c0/9f919d72d3a57cc270747d83e10e3a0ef057cdaa038f2af07e16284892802bb365cff58fb0a287f6233ae24cf2d2f38c315411b16a68a6b4c212922b921a63cc - languageName: node - linkType: hard - "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.21.4, @babel/code-frame@npm:^7.23.5, @babel/code-frame@npm:^7.24.1, @babel/code-frame@npm:^7.24.2": version: 7.24.2 resolution: "@babel/code-frame@npm:7.24.2" @@ -502,15 +469,6 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.23.8": - version: 7.25.6 - resolution: "@babel/runtime@npm:7.25.6" - dependencies: - regenerator-runtime: "npm:^0.14.0" - checksum: 10c0/d6143adf5aa1ce79ed374e33fdfd74fa975055a80bc6e479672ab1eadc4e4bfd7484444e17dd063a1d180e051f3ec62b357c7a2b817e7657687b47313158c3d2 - languageName: node - linkType: hard - "@babel/template@npm:^7.22.15, @babel/template@npm:^7.24.0, @babel/template@npm:^7.3.3": version: 7.24.0 resolution: "@babel/template@npm:7.24.0" @@ -5919,8 +5877,7 @@ __metadata: dependencies: "@udecode/plate-combobox": "npm:39.0.0" "@udecode/plate-markdown": "npm:39.0.0" - "@udecode/plate-menu": "npm:39.1.0" - "@udecode/plate-selection": "npm:39.0.0" + "@udecode/plate-selection": "npm:39.1.2" lodash: "npm:^4.17.21" peerDependencies: "@udecode/plate-common": ">=39.0.0" @@ -5939,7 +5896,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -5956,7 +5913,7 @@ __metadata: "@udecode/plate-common": "workspace:^" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -5975,7 +5932,7 @@ __metadata: "@udecode/plate-common": "workspace:^" "@udecode/plate-heading": "npm:39.0.0" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -5991,7 +5948,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6007,7 +5964,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6023,7 +5980,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6039,7 +5996,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6056,7 +6013,7 @@ __metadata: "@udecode/plate-common": "workspace:^" react-textarea-autosize: "npm:^8.5.3" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6075,7 +6032,7 @@ __metadata: delay: "npm:5.0.0" p-defer: "npm:^4.0.1" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6092,7 +6049,7 @@ __metadata: "@udecode/plate-common": "workspace:^" prismjs: "npm:^1.29.0" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6108,7 +6065,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6125,7 +6082,7 @@ __metadata: "@udecode/plate-common": "workspace:^" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6135,12 +6092,12 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-common@npm:39.0.0, @udecode/plate-common@workspace:^, @udecode/plate-common@workspace:packages/common": +"@udecode/plate-common@npm:39.1.3, @udecode/plate-common@workspace:^, @udecode/plate-common@workspace:packages/common": version: 0.0.0-use.local resolution: "@udecode/plate-common@workspace:packages/common" dependencies: - "@udecode/plate-core": "npm:39.0.0" - "@udecode/plate-utils": "npm:39.0.0" + "@udecode/plate-core": "npm:39.1.3" + "@udecode/plate-utils": "npm:39.1.3" "@udecode/react-hotkeys": "npm:37.0.0" "@udecode/react-utils": "npm:39.0.0" "@udecode/slate": "npm:38.0.4" @@ -6157,7 +6114,7 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-core@npm:39.0.0, @udecode/plate-core@workspace:^, @udecode/plate-core@workspace:packages/core": +"@udecode/plate-core@npm:39.1.3, @udecode/plate-core@workspace:^, @udecode/plate-core@workspace:packages/core": version: 0.0.0-use.local resolution: "@udecode/plate-core@workspace:packages/core" dependencies: @@ -6197,7 +6154,7 @@ __metadata: "@udecode/plate-table": "npm:39.0.1" papaparse: "npm:^5.4.1" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6213,7 +6170,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6229,7 +6186,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.94.0" @@ -6247,7 +6204,7 @@ __metadata: diff-match-patch-ts: "npm:^0.6.0" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6265,7 +6222,7 @@ __metadata: lodash: "npm:^4.17.21" raf: "npm:^3.4.1" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dnd: ">=14.0.0" react-dnd-html5-backend: ">=14.0.0" @@ -6289,7 +6246,7 @@ __metadata: "@udecode/plate-table": "npm:39.0.1" validator: "npm:^13.12.0" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6307,7 +6264,7 @@ __metadata: "@udecode/plate-combobox": "npm:39.0.0" "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6324,7 +6281,7 @@ __metadata: "@excalidraw/excalidraw": "npm:0.16.4" "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6340,7 +6297,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6358,7 +6315,7 @@ __metadata: "@floating-ui/react": "npm:^0.26.23" "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6375,7 +6332,7 @@ __metadata: "@udecode/plate-common": "workspace:^" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6391,7 +6348,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6407,7 +6364,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6423,7 +6380,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6441,7 +6398,7 @@ __metadata: "@udecode/plate-common": "workspace:^" html-entities: "npm:^2.5.2" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6460,7 +6417,7 @@ __metadata: "@udecode/plate-list": "npm:39.0.0" clsx: "npm:^2.1.1" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6476,7 +6433,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6493,7 +6450,7 @@ __metadata: "@udecode/plate-common": "workspace:^" juice: "npm:^8.1.0" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6509,7 +6466,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6525,7 +6482,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6541,7 +6498,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6551,7 +6508,7 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-link@npm:39.0.0, @udecode/plate-link@workspace:^, @udecode/plate-link@workspace:packages/link": +"@udecode/plate-link@npm:39.1.1, @udecode/plate-link@workspace:^, @udecode/plate-link@workspace:packages/link": version: 0.0.0-use.local resolution: "@udecode/plate-link@workspace:packages/link" dependencies: @@ -6559,7 +6516,7 @@ __metadata: "@udecode/plate-floating": "npm:39.0.0" "@udecode/plate-normalizers": "npm:39.0.0" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6577,7 +6534,7 @@ __metadata: "@udecode/plate-reset-node": "npm:39.0.0" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6596,7 +6553,7 @@ __metadata: remark-parse: "npm:^9.0.0" unified: "npm:^11.0.5" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6614,7 +6571,7 @@ __metadata: "@udecode/plate-common": "workspace:^" katex: "npm:0.16.11" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6631,7 +6588,7 @@ __metadata: "@udecode/plate-common": "workspace:^" js-video-url-parser: "npm:^0.5.1" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6648,25 +6605,7 @@ __metadata: "@udecode/plate-combobox": "npm:39.0.0" "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=39.0.0" - react: ">=16.8.0" - react-dom: ">=16.8.0" - slate: ">=0.103.0" - slate-history: ">=0.93.0" - slate-hyperscript: ">=0.66.0" - slate-react: ">=0.108.0" - languageName: unknown - linkType: soft - -"@udecode/plate-menu@npm:39.1.0, @udecode/plate-menu@workspace:packages/menu": - version: 0.0.0-use.local - resolution: "@udecode/plate-menu@workspace:packages/menu" - dependencies: - "@ariakit/react": "npm:0.4.7" - "@udecode/plate-common": "workspace:^" - match-sorter: "npm:6.3.4" - peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6683,7 +6622,7 @@ __metadata: "@udecode/plate-common": "workspace:^" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6700,7 +6639,7 @@ __metadata: "@udecode/plate-common": "workspace:^" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6717,7 +6656,7 @@ __metadata: "@udecode/plate-common": "workspace:^" peerDependencies: "@playwright/test": ">=1.42.1" - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6733,7 +6672,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6749,7 +6688,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6765,7 +6704,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6775,14 +6714,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-selection@npm:39.0.0, @udecode/plate-selection@workspace:^, @udecode/plate-selection@workspace:packages/selection": +"@udecode/plate-selection@npm:39.1.2, @udecode/plate-selection@workspace:^, @udecode/plate-selection@workspace:packages/selection": version: 0.0.0-use.local resolution: "@udecode/plate-selection@workspace:packages/selection" dependencies: "@udecode/plate-common": "workspace:^" copy-to-clipboard: "npm:^3.3.3" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6799,7 +6738,7 @@ __metadata: "@udecode/plate-combobox": "npm:39.0.0" "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6817,7 +6756,7 @@ __metadata: "@udecode/plate-diff": "npm:39.0.0" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6834,7 +6773,7 @@ __metadata: "@udecode/plate-common": "workspace:^" tabbable: "npm:^6.2.0" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6852,7 +6791,7 @@ __metadata: "@udecode/plate-resizable": "npm:39.0.0" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6879,7 +6818,7 @@ __metadata: "@udecode/plate-node-id": "npm:39.0.0" lodash: "npm:^4.17.21" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6895,7 +6834,7 @@ __metadata: dependencies: "@udecode/plate-common": "workspace:^" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6936,11 +6875,11 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-utils@npm:39.0.0, @udecode/plate-utils@workspace:^, @udecode/plate-utils@workspace:packages/plate-utils": +"@udecode/plate-utils@npm:39.1.3, @udecode/plate-utils@workspace:^, @udecode/plate-utils@workspace:packages/plate-utils": version: 0.0.0-use.local resolution: "@udecode/plate-utils@workspace:packages/plate-utils" dependencies: - "@udecode/plate-core": "npm:39.0.0" + "@udecode/plate-core": "npm:39.1.3" "@udecode/react-utils": "npm:39.0.0" "@udecode/slate": "npm:38.0.4" "@udecode/slate-react": "npm:39.0.0" @@ -6967,7 +6906,7 @@ __metadata: "@udecode/plate-common": "workspace:^" yjs: "npm:^13.6.19" peerDependencies: - "@udecode/plate-common": ">=39.0.0" + "@udecode/plate-common": ">=39.1.3" react: ">=16.8.0" react-dom: ">=16.8.0" slate: ">=0.103.0" @@ -6990,7 +6929,7 @@ __metadata: "@udecode/plate-code-block": "npm:39.0.0" "@udecode/plate-combobox": "npm:39.0.0" "@udecode/plate-comments": "npm:39.0.0" - "@udecode/plate-common": "npm:39.0.0" + "@udecode/plate-common": "npm:39.1.3" "@udecode/plate-csv": "npm:39.0.1" "@udecode/plate-diff": "npm:39.0.0" "@udecode/plate-docx": "npm:39.0.1" @@ -7006,7 +6945,7 @@ __metadata: "@udecode/plate-kbd": "npm:39.0.0" "@udecode/plate-layout": "npm:39.0.0" "@udecode/plate-line-height": "npm:39.0.0" - "@udecode/plate-link": "npm:39.0.0" + "@udecode/plate-link": "npm:39.1.1" "@udecode/plate-list": "npm:39.0.0" "@udecode/plate-markdown": "npm:39.0.0" "@udecode/plate-media": "npm:39.0.0" @@ -7016,7 +6955,7 @@ __metadata: "@udecode/plate-reset-node": "npm:39.0.0" "@udecode/plate-resizable": "npm:39.0.0" "@udecode/plate-select": "npm:39.0.0" - "@udecode/plate-selection": "npm:39.0.0" + "@udecode/plate-selection": "npm:39.1.2" "@udecode/plate-slash-command": "npm:39.0.0" "@udecode/plate-suggestion": "npm:39.0.0" "@udecode/plate-tabbable": "npm:39.0.0" @@ -14351,16 +14290,6 @@ __metadata: languageName: node linkType: hard -"match-sorter@npm:6.3.4": - version: 6.3.4 - resolution: "match-sorter@npm:6.3.4" - dependencies: - "@babel/runtime": "npm:^7.23.8" - remove-accents: "npm:0.5.0" - checksum: 10c0/35d2a6b6df003c677d9ec87ecd4683657638f5bce856f43f9cf90b03e357ed2f09813ebbac759defa7e7438706936dd34dc2bfe1a18771f7d2541f14d639b4ad - languageName: node - linkType: hard - "mdast-util-find-and-replace@npm:^3.0.0": version: 3.0.1 resolution: "mdast-util-find-and-replace@npm:3.0.1" @@ -18174,13 +18103,6 @@ __metadata: languageName: node linkType: hard -"remove-accents@npm:0.5.0": - version: 0.5.0 - resolution: "remove-accents@npm:0.5.0" - checksum: 10c0/a75321aa1b53d9abe82637115a492770bfe42bb38ed258be748bf6795871202bc8b4badff22013494a7029f5a241057ad8d3f72adf67884dbe15a9e37e87adc4 - languageName: node - linkType: hard - "repeat-string@npm:^1.6.1": version: 1.6.1 resolution: "repeat-string@npm:1.6.1" From 344994a007e99ce40df7b00b081b11fc3828e85d Mon Sep 17 00:00:00 2001 From: Felix Feng Date: Thu, 10 Oct 2024 17:34:15 +0800 Subject: [PATCH 3/3] fix ci --- .../public/r/styles/default/code-line-element.json | 2 +- .../www/public/r/styles/default/dropdown-menu.json | 2 +- .../r/styles/default/emoji-dropdown-menu.json | 14 +++++++------- .../r/styles/default/emoji-input-element.json | 2 +- .../r/styles/default/excalidraw-element.json | 2 +- .../public/r/styles/default/floating-toolbar.json | 2 +- apps/www/public/r/styles/default/hr-element.json | 2 +- .../r/styles/default/mention-input-element.json | 2 +- .../public/r/styles/default/todo-list-element.json | 2 +- .../r/styles/default/turn-into-dropdown-menu.json | 2 +- .../default/plate-ui/turn-into-dropdown-menu.tsx | 1 + 11 files changed, 17 insertions(+), 16 deletions(-) diff --git a/apps/www/public/r/styles/default/code-line-element.json b/apps/www/public/r/styles/default/code-line-element.json index 1d5b20b5b5..123f4d63eb 100644 --- a/apps/www/public/r/styles/default/code-line-element.json +++ b/apps/www/public/r/styles/default/code-line-element.json @@ -4,7 +4,7 @@ ], "files": [ { - "content": "'use client';\n\nimport React from 'react';\n\nimport { withRef } from '@udecode/cn';\nimport { PlateElement } from './plate-element';\n\nexport const CodeLineElement = withRef((props, ref) => (\n \n));\n", + "content": "'use client';\n\nimport React from 'react';\n\nimport { withRef } from '@udecode/cn';\n\nimport { PlateElement } from './plate-element';\n\nexport const CodeLineElement = withRef((props, ref) => (\n \n));\n", "path": "plate-ui/code-line-element.tsx", "target": "", "type": "registry:ui" diff --git a/apps/www/public/r/styles/default/dropdown-menu.json b/apps/www/public/r/styles/default/dropdown-menu.json index a402ada7cd..b05c6f1e7e 100644 --- a/apps/www/public/r/styles/default/dropdown-menu.json +++ b/apps/www/public/r/styles/default/dropdown-menu.json @@ -4,7 +4,7 @@ ], "files": [ { - "content": "'use client';\n\nimport * as React from 'react';\nimport { useCallback, useState } from 'react';\n\nimport * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';\nimport {\n cn,\n createPrimitiveElement,\n withCn,\n withProps,\n withRef,\n withVariants,\n} from '@udecode/cn';\nimport { cva } from 'class-variance-authority';\n\nimport { Icons } from '@/components/icons';\n\nexport const DropdownMenu = DropdownMenuPrimitive.Root;\n\nexport const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;\n\nexport const DropdownMenuGroup = DropdownMenuPrimitive.Group;\n\nexport const DropdownMenuPortal = DropdownMenuPrimitive.Portal;\n\nexport const DropdownMenuSub = DropdownMenuPrimitive.Sub;\n\nexport const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;\n\nexport const DropdownMenuSubTrigger = withRef<\n typeof DropdownMenuPrimitive.SubTrigger,\n {\n inset?: boolean;\n }\n>(({ children, className, inset, ...props }, ref) => (\n \n {children}\n \n \n));\n\nexport const DropdownMenuSubContent = withCn(\n DropdownMenuPrimitive.SubContent,\n 'z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2'\n);\n\nconst DropdownMenuContentVariants = withProps(DropdownMenuPrimitive.Content, {\n className: cn(\n 'z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2'\n ),\n sideOffset: 4,\n});\n\nexport const DropdownMenuContent = withRef<\n typeof DropdownMenuPrimitive.Content\n>(({ ...props }, ref) => (\n \n \n \n));\n\nconst menuItemVariants = cva(\n cn(\n 'relative flex h-9 cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors',\n 'focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50'\n ),\n {\n variants: {\n inset: {\n true: 'pl-8',\n },\n },\n }\n);\n\nexport const DropdownMenuItem = withVariants(\n DropdownMenuPrimitive.Item,\n menuItemVariants,\n ['inset']\n);\n\nexport const DropdownMenuCheckboxItem = withRef<\n typeof DropdownMenuPrimitive.CheckboxItem\n>(({ children, className, ...props }, ref) => (\n \n \n \n \n \n \n {children}\n \n));\n\nexport const DropdownMenuRadioItem = withRef<\n typeof DropdownMenuPrimitive.RadioItem,\n {\n hideIcon?: boolean;\n }\n>(({ children, className, hideIcon, ...props }, ref) => (\n \n {!hideIcon && (\n \n \n \n \n \n )}\n {children}\n \n));\n\nconst dropdownMenuLabelVariants = cva(\n cn('select-none px-2 py-1.5 text-sm font-semibold'),\n {\n variants: {\n inset: {\n true: 'pl-8',\n },\n },\n }\n);\n\nexport const DropdownMenuLabel = withVariants(\n DropdownMenuPrimitive.Label,\n dropdownMenuLabelVariants,\n ['inset']\n);\n\nexport const DropdownMenuSeparator = withCn(\n DropdownMenuPrimitive.Separator,\n '-mx-1 my-1 h-px bg-muted'\n);\n\nexport const DropdownMenuShortcut = withCn(\n createPrimitiveElement('span'),\n 'ml-auto text-xs tracking-widest opacity-60'\n);\n\nexport const useOpenState = () => {\n const [open, setOpen] = useState(false);\n\n const onOpenChange = useCallback(\n (_value = !open) => {\n setOpen(_value);\n },\n [open]\n );\n\n return {\n open,\n onOpenChange,\n };\n};\n", + "content": "'use client';\n\nimport * as React from 'react';\nimport { useCallback, useState } from 'react';\n\nimport * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';\nimport {\n cn,\n createPrimitiveElement,\n withCn,\n withProps,\n withRef,\n withVariants,\n} from '@udecode/cn';\nimport { cva } from 'class-variance-authority';\n\nimport { Icons } from '@/components/icons';\n\nexport const DropdownMenu = DropdownMenuPrimitive.Root;\n\nexport const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;\n\nexport const DropdownMenuGroup = DropdownMenuPrimitive.Group;\n\nexport const DropdownMenuPortal = DropdownMenuPrimitive.Portal;\n\nexport const DropdownMenuSub = DropdownMenuPrimitive.Sub;\n\nexport const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;\n\nexport const DropdownMenuSubTrigger = withRef<\n typeof DropdownMenuPrimitive.SubTrigger,\n {\n inset?: boolean;\n }\n>(({ children, className, inset, ...props }, ref) => (\n \n {children}\n \n \n));\n\nexport const DropdownMenuSubContent = withCn(\n DropdownMenuPrimitive.SubContent,\n 'z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2'\n);\n\nconst DropdownMenuContentVariants = withProps(DropdownMenuPrimitive.Content, {\n className: cn(\n 'z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2'\n ),\n sideOffset: 4,\n});\n\nexport const DropdownMenuContent = withRef<\n typeof DropdownMenuPrimitive.Content\n>(({ ...props }, ref) => (\n \n {\n e.preventDefault();\n }}\n {...props}\n />\n \n));\n\nconst menuItemVariants = cva(\n cn(\n 'relative flex h-9 cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors',\n 'focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50'\n ),\n {\n variants: {\n inset: {\n true: 'pl-8',\n },\n },\n }\n);\n\nexport const DropdownMenuItem = withVariants(\n DropdownMenuPrimitive.Item,\n menuItemVariants,\n ['inset']\n);\n\nexport const DropdownMenuCheckboxItem = withRef<\n typeof DropdownMenuPrimitive.CheckboxItem\n>(({ children, className, ...props }, ref) => (\n \n \n \n \n \n \n {children}\n \n));\n\nexport const DropdownMenuRadioItem = withRef<\n typeof DropdownMenuPrimitive.RadioItem,\n {\n hideIcon?: boolean;\n }\n>(({ children, className, hideIcon, ...props }, ref) => (\n \n {!hideIcon && (\n \n \n \n \n \n )}\n {children}\n \n));\n\nconst dropdownMenuLabelVariants = cva(\n cn('select-none px-2 py-1.5 text-sm font-semibold'),\n {\n variants: {\n inset: {\n true: 'pl-8',\n },\n },\n }\n);\n\nexport const DropdownMenuLabel = withVariants(\n DropdownMenuPrimitive.Label,\n dropdownMenuLabelVariants,\n ['inset']\n);\n\nexport const DropdownMenuSeparator = withCn(\n DropdownMenuPrimitive.Separator,\n '-mx-1 my-1 h-px bg-muted'\n);\n\nexport const DropdownMenuShortcut = withCn(\n createPrimitiveElement('span'),\n 'ml-auto text-xs tracking-widest opacity-60'\n);\n\nexport const useOpenState = () => {\n const [open, setOpen] = useState(false);\n\n const onOpenChange = useCallback(\n (_value = !open) => {\n setOpen(_value);\n },\n [open]\n );\n\n return {\n open,\n onOpenChange,\n };\n};\n", "path": "plate-ui/dropdown-menu.tsx", "target": "", "type": "registry:ui" diff --git a/apps/www/public/r/styles/default/emoji-dropdown-menu.json b/apps/www/public/r/styles/default/emoji-dropdown-menu.json index e67936adb2..4753db7388 100644 --- a/apps/www/public/r/styles/default/emoji-dropdown-menu.json +++ b/apps/www/public/r/styles/default/emoji-dropdown-menu.json @@ -16,43 +16,43 @@ "type": "registry:ui" }, { - "content": "import type React from 'react';\n\nimport type { EmojiCategoryList } from '@udecode/plate-emoji';\n\nimport {\n AppleIcon,\n ClockIcon,\n CompassIcon,\n DeleteIcon,\n FlagIcon,\n LeafIcon,\n LightbulbIcon,\n MusicIcon,\n SearchIcon,\n SmileIcon,\n StarIcon,\n} from 'lucide-react';\n\nexport const emojiCategoryIcons: Record<\n EmojiCategoryList,\n { outline: React.ReactElement; solid: React.ReactElement }\n> = {\n activity: {\n outline: (\n \n \n \n \n \n \n ),\n // Needed to add another solid variant - outline will be used for now\n solid: (\n \n \n \n \n \n \n ),\n },\n\n custom: {\n outline: ,\n // Needed to add another solid variant - outline will be used for now\n solid: ,\n },\n\n flags: {\n outline: ,\n // Needed to add another solid variant - outline will be used for now\n solid: ,\n },\n\n foods: {\n outline: ,\n // Needed to add another solid variant - outline will be used for now\n solid: ,\n },\n\n frequent: {\n outline: ,\n // Needed to add another solid variant - outline will be used for now\n solid: ,\n },\n\n nature: {\n outline: ,\n // Needed to add another solid variant - outline will be used for now\n solid: ,\n },\n\n objects: {\n outline: ,\n // Needed to add another solid variant - outline will be used for now\n solid: ,\n },\n\n people: {\n outline: ,\n // Needed to add another solid variant - outline will be used for now\n solid: ,\n },\n\n places: {\n outline: ,\n // Needed to add another solid variant - outline will be used for now\n solid: ,\n },\n\n symbols: {\n outline: ,\n // Needed to add another solid variant - outline will be used for now\n solid: ,\n },\n};\n\nexport const emojiSearchIcons = {\n delete: ,\n\n loupe: ,\n};\n", + "content": "import type React from 'react';\n\nimport type { EmojiCategoryList } from '@udecode/plate-emoji';\n\nimport {\n AppleIcon,\n ClockIcon,\n CompassIcon,\n FlagIcon,\n LeafIcon,\n LightbulbIcon,\n MusicIcon,\n SearchIcon,\n SmileIcon,\n StarIcon,\n XIcon,\n} from 'lucide-react';\n\nexport const emojiCategoryIcons: Record<\n EmojiCategoryList,\n {\n outline: React.ReactElement;\n solid: React.ReactElement; // Needed to add another solid variant - outline will be used for now\n }\n> = {\n activity: {\n outline: (\n \n \n \n \n \n \n ),\n solid: (\n \n \n \n \n \n \n ),\n },\n\n custom: {\n outline: ,\n solid: ,\n },\n\n flags: {\n outline: ,\n solid: ,\n },\n\n foods: {\n outline: ,\n solid: ,\n },\n\n frequent: {\n outline: ,\n solid: ,\n },\n\n nature: {\n outline: ,\n solid: ,\n },\n\n objects: {\n outline: ,\n solid: ,\n },\n\n people: {\n outline: ,\n solid: ,\n },\n\n places: {\n outline: ,\n solid: ,\n },\n\n symbols: {\n outline: ,\n solid: ,\n },\n};\n\nexport const emojiSearchIcons = {\n delete: ,\n loupe: ,\n};\n", "path": "plate-ui/emoji-icons.tsx", "target": "", "type": "registry:ui" }, { - "content": "import React from 'react';\n\nimport type { UseEmojiPickerType } from '@udecode/plate-emoji/react';\n\nimport { cn } from '@udecode/cn';\nimport { EmojiSettings } from '@udecode/plate-emoji';\n\nimport { EmojiPickerContent } from './emoji-picker-content';\nimport { EmojiPickerNavigation } from './emoji-picker-navigation';\nimport { EmojiPickerPreview } from './emoji-picker-preview';\nimport { EmojiPickerSearchAndClear } from './emoji-picker-search-and-clear';\nimport { EmojiPickerSearchBar } from './emoji-picker-search-bar';\n\nexport function EmojiPicker({\n clearSearch,\n emoji,\n emojiLibrary,\n focusedCategory,\n hasFound,\n i18n,\n icons,\n isSearching,\n refs,\n searchResult,\n searchValue,\n setSearch,\n settings = EmojiSettings,\n visibleCategories,\n handleCategoryClick,\n onMouseOver,\n onSelectEmoji,\n}: UseEmojiPickerType) {\n return (\n \n \n \n \n \n \n \n \n );\n}\n", + "content": "import React from 'react';\n\nimport type { UseEmojiPickerType } from '@udecode/plate-emoji/react';\n\nimport { cn } from '@udecode/cn';\nimport { EmojiSettings } from '@udecode/plate-emoji';\n\nimport { EmojiPickerContent } from './emoji-picker-content';\nimport { EmojiPickerNavigation } from './emoji-picker-navigation';\nimport { EmojiPickerPreview } from './emoji-picker-preview';\nimport { EmojiPickerSearchAndClear } from './emoji-picker-search-and-clear';\nimport { EmojiPickerSearchBar } from './emoji-picker-search-bar';\n\nexport function EmojiPicker({\n clearSearch,\n emoji,\n emojiLibrary,\n focusedCategory,\n hasFound,\n i18n,\n icons,\n isSearching,\n refs,\n searchResult,\n searchValue,\n setSearch,\n settings = EmojiSettings,\n visibleCategories,\n handleCategoryClick,\n onMouseOver,\n onSelectEmoji,\n}: UseEmojiPickerType) {\n return (\n \n \n \n \n \n \n \n \n );\n}\n", "path": "plate-ui/emoji-picker.tsx", "target": "", "type": "registry:ui" }, { - "content": "import { memo, useCallback } from 'react';\n\nimport type { UseEmojiPickerType } from '@udecode/plate-emoji/react';\n\nimport { cn } from '@udecode/cn';\nimport { type Emoji, type GridRow, EmojiSettings } from '@udecode/plate-emoji';\n\nexport type EmojiPickerContentProps = Pick<\n UseEmojiPickerType,\n | 'emojiLibrary'\n | 'i18n'\n | 'isSearching'\n | 'onMouseOver'\n | 'onSelectEmoji'\n | 'refs'\n | 'searchResult'\n | 'settings'\n | 'visibleCategories'\n>;\n\nexport type EmojiButtonProps = {\n emoji: Emoji;\n index: number;\n onMouseOver: (emoji?: Emoji) => void;\n onSelect: (emoji: Emoji) => void;\n};\n\nexport type RowOfButtonsProps = {\n row: GridRow;\n} & Pick;\n\nconst Button = memo(\n ({ emoji, index, onMouseOver, onSelect }: EmojiButtonProps) => {\n return (\n onSelect(emoji)}\n onMouseEnter={() => onMouseOver(emoji)}\n onMouseLeave={() => onMouseOver()}\n aria-label={emoji.skins[0].native}\n data-index={index}\n tabIndex={-1}\n type=\"button\"\n >\n \n \n {emoji.skins[0].native}\n \n \n );\n }\n);\nButton.displayName = 'Button';\n\nconst RowOfButtons = memo(\n ({ emojiLibrary, row, onMouseOver, onSelectEmoji }: RowOfButtonsProps) => (\n
\n {row.elements.map((emojiId, index) => (\n \n ))}\n
\n )\n);\nRowOfButtons.displayName = 'RowOfButtons';\n\nexport function EmojiPickerContent({\n emojiLibrary,\n i18n,\n isSearching = false,\n refs,\n searchResult,\n settings = EmojiSettings,\n visibleCategories,\n onMouseOver,\n onSelectEmoji,\n}: EmojiPickerContentProps) {\n const getRowWidth = settings.perLine.value * settings.buttonSize.value;\n\n const isCategoryVisible = useCallback(\n (categoryId: any) => {\n return visibleCategories.has(categoryId)\n ? visibleCategories.get(categoryId)\n : false;\n },\n [visibleCategories]\n );\n\n const EmojiList = useCallback(() => {\n return emojiLibrary\n .getGrid()\n .sections()\n .map(({ id: categoryId }) => {\n const section = emojiLibrary.getGrid().section(categoryId);\n const { buttonSize } = settings;\n\n return (\n \n
\n {i18n.categories[categoryId]}\n
\n \n {isCategoryVisible(categoryId) &&\n section\n .getRows()\n .map((row: GridRow) => (\n \n ))}\n \n \n );\n });\n }, [\n emojiLibrary,\n getRowWidth,\n i18n.categories,\n isCategoryVisible,\n onSelectEmoji,\n onMouseOver,\n settings,\n ]);\n\n const SearchList = useCallback(() => {\n return (\n
\n
\n {i18n.searchResult}\n
\n
\n {searchResult.map((emoji: Emoji, index: number) => (\n \n ))}\n
\n
\n );\n }, [\n emojiLibrary,\n getRowWidth,\n i18n.searchResult,\n searchResult,\n onSelectEmoji,\n onMouseOver,\n ]);\n\n return (\n \n
\n {isSearching ? SearchList() : EmojiList()}\n
\n \n );\n}\n", + "content": "import { memo, useCallback } from 'react';\n\nimport type { UseEmojiPickerType } from '@udecode/plate-emoji/react';\n\nimport { cn } from '@udecode/cn';\nimport { type Emoji, type GridRow, EmojiSettings } from '@udecode/plate-emoji';\n\nexport type EmojiPickerContentProps = Pick<\n UseEmojiPickerType,\n | 'emojiLibrary'\n | 'i18n'\n | 'isSearching'\n | 'onMouseOver'\n | 'onSelectEmoji'\n | 'refs'\n | 'searchResult'\n | 'settings'\n | 'visibleCategories'\n>;\n\nexport type EmojiButtonProps = {\n emoji: Emoji;\n index: number;\n onMouseOver: (emoji?: Emoji) => void;\n onSelect: (emoji: Emoji) => void;\n};\n\nexport type RowOfButtonsProps = {\n row: GridRow;\n} & Pick;\n\nconst Button = memo(\n ({ emoji, index, onMouseOver, onSelect }: EmojiButtonProps) => {\n return (\n onSelect(emoji)}\n onMouseEnter={() => onMouseOver(emoji)}\n onMouseLeave={() => onMouseOver()}\n aria-label={emoji.skins[0].native}\n data-index={index}\n tabIndex={-1}\n type=\"button\"\n >\n \n \n {emoji.skins[0].native}\n \n \n );\n }\n);\nButton.displayName = 'Button';\n\nconst RowOfButtons = memo(\n ({ emojiLibrary, row, onMouseOver, onSelectEmoji }: RowOfButtonsProps) => (\n
\n {row.elements.map((emojiId, index) => (\n \n ))}\n
\n )\n);\nRowOfButtons.displayName = 'RowOfButtons';\n\nexport function EmojiPickerContent({\n emojiLibrary,\n i18n,\n isSearching = false,\n refs,\n searchResult,\n settings = EmojiSettings,\n visibleCategories,\n onMouseOver,\n onSelectEmoji,\n}: EmojiPickerContentProps) {\n const getRowWidth = settings.perLine.value * settings.buttonSize.value;\n\n const isCategoryVisible = useCallback(\n (categoryId: any) => {\n return visibleCategories.has(categoryId)\n ? visibleCategories.get(categoryId)\n : false;\n },\n [visibleCategories]\n );\n\n const EmojiList = useCallback(() => {\n return emojiLibrary\n .getGrid()\n .sections()\n .map(({ id: categoryId }) => {\n const section = emojiLibrary.getGrid().section(categoryId);\n const { buttonSize } = settings;\n\n return (\n \n
\n {i18n.categories[categoryId]}\n
\n \n {isCategoryVisible(categoryId) &&\n section\n .getRows()\n .map((row: GridRow) => (\n \n ))}\n \n \n );\n });\n }, [\n emojiLibrary,\n getRowWidth,\n i18n.categories,\n isCategoryVisible,\n onSelectEmoji,\n onMouseOver,\n settings,\n ]);\n\n const SearchList = useCallback(() => {\n return (\n
\n
\n {i18n.searchResult}\n
\n
\n {searchResult.map((emoji: Emoji, index: number) => (\n \n ))}\n
\n
\n );\n }, [\n emojiLibrary,\n getRowWidth,\n i18n.searchResult,\n searchResult,\n onSelectEmoji,\n onMouseOver,\n ]);\n\n return (\n \n
\n {isSearching ? SearchList() : EmojiList()}\n
\n \n );\n}\n", "path": "plate-ui/emoji-picker-content.tsx", "target": "", "type": "registry:ui" }, { - "content": "import React from 'react';\n\nimport type { EmojiCategoryList } from '@udecode/plate-emoji';\nimport type {\n IEmojiFloatingLibrary,\n UseEmojiPickerType,\n} from '@udecode/plate-emoji/react';\n\nimport { cn } from '@udecode/cn';\n\nimport { Button } from './button';\n\nexport type EmojiPickerNavigationProps = {\n onClick: (id: EmojiCategoryList) => void;\n} & Pick<\n UseEmojiPickerType,\n 'emojiLibrary' | 'focusedCategory' | 'i18n' | 'icons'\n>;\n\nconst getBarProperty = (\n emojiLibrary: IEmojiFloatingLibrary,\n focusedCategory?: EmojiCategoryList\n) => {\n let width = 0;\n let position = 0;\n\n if (focusedCategory) {\n width = 100 / emojiLibrary.getGrid().size;\n position = focusedCategory\n ? emojiLibrary.indexOf(focusedCategory) * 100\n : 0;\n }\n\n return { position, width };\n};\n\nexport function EmojiPickerNavigation({\n emojiLibrary,\n focusedCategory,\n i18n,\n icons,\n onClick,\n}: EmojiPickerNavigationProps) {\n const { position, width } = getBarProperty(emojiLibrary, focusedCategory);\n\n return (\n \n
\n {emojiLibrary\n .getGrid()\n .sections()\n .map(({ id }) => (\n onClick(id)}\n title={i18n.categories[id]}\n aria-label={i18n.categories[id]}\n type=\"button\"\n >\n {icons.categories[id].outline}\n \n ))}\n \n
\n \n );\n}\n", + "content": "import { useMemo } from 'react';\n\nimport type { EmojiCategoryList } from '@udecode/plate-emoji';\nimport type {\n IEmojiFloatingLibrary,\n UseEmojiPickerType,\n} from '@udecode/plate-emoji/react';\n\nimport { cn } from '@udecode/cn';\n\nimport { Button } from './button';\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from './tooltip';\n\nexport type EmojiPickerNavigationProps = {\n onClick: (id: EmojiCategoryList) => void;\n} & Pick<\n UseEmojiPickerType,\n 'emojiLibrary' | 'focusedCategory' | 'i18n' | 'icons'\n>;\n\nconst getBarProperty = (\n emojiLibrary: IEmojiFloatingLibrary,\n focusedCategory?: EmojiCategoryList\n) => {\n let width = 0;\n let position = 0;\n\n if (focusedCategory) {\n width = 100 / emojiLibrary.getGrid().size;\n position = focusedCategory\n ? emojiLibrary.indexOf(focusedCategory) * 100\n : 0;\n }\n\n return { position, width };\n};\n\nexport function EmojiPickerNavigation({\n emojiLibrary,\n focusedCategory,\n i18n,\n icons,\n onClick,\n}: EmojiPickerNavigationProps) {\n const { position, width } = useMemo(\n () => getBarProperty(emojiLibrary, focusedCategory),\n [emojiLibrary, focusedCategory]\n );\n\n return (\n \n \n
\n {emojiLibrary\n .getGrid()\n .sections()\n .map(({ id }) => (\n \n \n {\n onClick(id);\n }}\n aria-label={i18n.categories[id]}\n type=\"button\"\n >\n \n {icons.categories[id].outline}\n \n \n \n \n {i18n.categories[id]}\n \n \n ))}\n \n
\n \n
\n );\n}\n", "path": "plate-ui/emoji-picker-navigation.tsx", "target": "", "type": "registry:ui" }, { - "content": "import type { UseEmojiPickerType } from '@udecode/plate-emoji/react';\n\nexport type EmojiPickerPreviewProps = Pick<\n UseEmojiPickerType,\n 'emoji' | 'hasFound' | 'i18n' | 'isSearching'\n>;\n\nexport type EmojiPreviewProps = Pick;\n\nexport type NoEmojiPreviewProps = Pick;\n\nexport type PickAnEmojiPreviewProps = NoEmojiPreviewProps;\n\nfunction EmojiPreview({ emoji }: EmojiPreviewProps) {\n return (\n
\n
\n {emoji?.skins[0].native}\n
\n
\n
{emoji?.name}
\n
{`:${emoji?.id}:`}
\n
\n
\n );\n}\n\nfunction NoEmoji({ i18n }: NoEmojiPreviewProps) {\n return (\n
\n
😢
\n
\n
\n {i18n.searchNoResultsTitle}\n
\n
{i18n.searchNoResultsSubtitle}
\n
\n
\n );\n}\n\nfunction PickAnEmoji({ i18n }: PickAnEmojiPreviewProps) {\n return (\n
\n
☝️
\n
\n
{i18n.pick}
\n
\n
\n );\n}\n\nexport function EmojiPickerPreview({\n emoji,\n hasFound = true,\n i18n,\n isSearching = false,\n ...props\n}: EmojiPickerPreviewProps) {\n const showPickEmoji = !emoji && !(isSearching && !hasFound);\n const showNoEmoji = isSearching && !hasFound;\n const showPreview = emoji;\n\n return (\n <>\n {showPreview && }\n {showPickEmoji && }\n {showNoEmoji && }\n \n );\n}\n", + "content": "import type { UseEmojiPickerType } from '@udecode/plate-emoji/react';\n\nexport type EmojiPickerPreviewProps = Pick<\n UseEmojiPickerType,\n 'emoji' | 'hasFound' | 'i18n' | 'isSearching'\n>;\n\nexport type EmojiPreviewProps = Pick;\n\nexport type NoEmojiPreviewProps = Pick;\n\nexport type PickAnEmojiPreviewProps = NoEmojiPreviewProps;\n\nfunction EmojiPreview({ emoji }: EmojiPreviewProps) {\n return (\n
\n
\n {emoji?.skins[0].native}\n
\n
\n
{emoji?.name}
\n
{`:${emoji?.id}:`}
\n
\n
\n );\n}\n\nfunction NoEmoji({ i18n }: NoEmojiPreviewProps) {\n return (\n
\n
😢
\n
\n
\n {i18n.searchNoResultsTitle}\n
\n
{i18n.searchNoResultsSubtitle}
\n
\n
\n );\n}\n\nfunction PickAnEmoji({ i18n }: PickAnEmojiPreviewProps) {\n return (\n
\n
☝️
\n
\n
{i18n.pick}
\n
\n
\n );\n}\n\nexport function EmojiPickerPreview({\n emoji,\n hasFound = true,\n i18n,\n isSearching = false,\n ...props\n}: EmojiPickerPreviewProps) {\n const showPickEmoji = !emoji && (!isSearching || hasFound);\n const showNoEmoji = isSearching && !hasFound;\n const showPreview = emoji && !showNoEmoji && !showNoEmoji;\n\n return (\n <>\n {showPreview && }\n {showPickEmoji && }\n {showNoEmoji && }\n \n );\n}\n", "path": "plate-ui/emoji-picker-preview.tsx", "target": "", "type": "registry:ui" }, { - "content": "import type { UseEmojiPickerType } from '@udecode/plate-emoji/react';\n\nimport { cn } from '@udecode/cn';\nimport { DeleteIcon, SearchIcon } from 'lucide-react';\n\nimport { Button } from './button';\n\nexport type EmojiPickerSearchAndClearProps = Pick<\n UseEmojiPickerType,\n 'clearSearch' | 'i18n' | 'searchValue'\n>;\n\nexport function EmojiPickerSearchAndClear({\n clearSearch,\n i18n,\n searchValue,\n}: EmojiPickerSearchAndClearProps) {\n return (\n
\n \n \n
\n {searchValue && (\n \n \n \n )}\n \n );\n}\n", + "content": "import type { UseEmojiPickerType } from '@udecode/plate-emoji/react';\n\nimport { cn } from '@udecode/cn';\n\nimport { Button } from './button';\nimport { emojiSearchIcons } from './emoji-icons';\n\nexport type EmojiPickerSearchAndClearProps = Pick<\n UseEmojiPickerType,\n 'clearSearch' | 'i18n' | 'searchValue'\n>;\n\nexport function EmojiPickerSearchAndClear({\n clearSearch,\n i18n,\n searchValue,\n}: EmojiPickerSearchAndClearProps) {\n return (\n
\n \n {emojiSearchIcons.loupe}\n
\n {searchValue && (\n \n {emojiSearchIcons.delete}\n \n )}\n \n );\n}\n", "path": "plate-ui/emoji-picker-search-and-clear.tsx", "target": "", "type": "registry:ui" }, { - "content": "import type { ReactNode } from 'react';\n\nimport type { UseEmojiPickerType } from '@udecode/plate-emoji/react';\n\nexport type EmojiPickerSearchBarProps = {\n children: ReactNode;\n} & Pick;\n\nexport function EmojiPickerSearchBar({\n children,\n i18n,\n searchValue,\n setSearch,\n}: EmojiPickerSearchBarProps) {\n return (\n
\n
\n setSearch(event.target.value)}\n placeholder={i18n.search}\n aria-label=\"Search\"\n autoComplete=\"off\"\n type=\"text\"\n />\n {children}\n
\n
\n );\n}\n", + "content": "import type { ReactNode } from 'react';\n\nimport type { UseEmojiPickerType } from '@udecode/plate-emoji/react';\n\nexport type EmojiPickerSearchBarProps = {\n children: ReactNode;\n} & Pick;\n\nexport function EmojiPickerSearchBar({\n children,\n i18n,\n searchValue,\n setSearch,\n}: EmojiPickerSearchBarProps) {\n return (\n
\n
\n setSearch(event.target.value)}\n placeholder={i18n.search}\n aria-label=\"Search\"\n autoComplete=\"off\"\n type=\"text\"\n />\n {children}\n
\n
\n );\n}\n", "path": "plate-ui/emoji-picker-search-bar.tsx", "target": "", "type": "registry:ui" diff --git a/apps/www/public/r/styles/default/emoji-input-element.json b/apps/www/public/r/styles/default/emoji-input-element.json index 5964810e4a..bdca2c1528 100644 --- a/apps/www/public/r/styles/default/emoji-input-element.json +++ b/apps/www/public/r/styles/default/emoji-input-element.json @@ -4,7 +4,7 @@ ], "files": [ { - "content": "import React, { useMemo, useState } from 'react';\n\nimport { withRef } from '@udecode/cn';\nimport { PlateElement } from './plate-element';\nimport { EmojiInlineIndexSearch, insertEmoji } from '@udecode/plate-emoji';\n\nimport { useDebounce } from '@/registry/default/hooks/use-debounce';\n\nimport {\n InlineCombobox,\n InlineComboboxContent,\n InlineComboboxEmpty,\n InlineComboboxInput,\n InlineComboboxItem,\n} from './inline-combobox';\n\nexport const EmojiInputElement = withRef(\n ({ className, ...props }, ref) => {\n const { children, editor, element } = props;\n const [value, setValue] = useState('');\n const debouncedValue = useDebounce(value, 100);\n const isPending = value !== debouncedValue;\n\n const filteredEmojis = useMemo(() => {\n if (debouncedValue.trim().length === 0) return [];\n\n return EmojiInlineIndexSearch.getInstance()\n .search(debouncedValue.replace(/:$/, ''))\n .get();\n }, [debouncedValue]);\n\n return (\n \n \n \n\n \n {!isPending && (\n No matching emoji found\n )}\n\n {filteredEmojis.map((emoji) => (\n insertEmoji(editor, emoji)}\n >\n {emoji.skins[0].native} {emoji.name}\n \n ))}\n \n \n\n {children}\n
\n );\n }\n);\n", + "content": "import React, { useMemo, useState } from 'react';\n\nimport { withRef } from '@udecode/cn';\nimport { EmojiInlineIndexSearch, insertEmoji } from '@udecode/plate-emoji';\n\nimport { useDebounce } from '@/registry/default/hooks/use-debounce';\n\nimport {\n InlineCombobox,\n InlineComboboxContent,\n InlineComboboxEmpty,\n InlineComboboxInput,\n InlineComboboxItem,\n} from './inline-combobox';\nimport { PlateElement } from './plate-element';\n\nexport const EmojiInputElement = withRef(\n ({ className, ...props }, ref) => {\n const { children, editor, element } = props;\n const [value, setValue] = useState('');\n const debouncedValue = useDebounce(value, 100);\n const isPending = value !== debouncedValue;\n\n const filteredEmojis = useMemo(() => {\n if (debouncedValue.trim().length === 0) return [];\n\n return EmojiInlineIndexSearch.getInstance()\n .search(debouncedValue.replace(/:$/, ''))\n .get();\n }, [debouncedValue]);\n\n return (\n \n \n \n\n \n {!isPending && (\n No matching emoji found\n )}\n\n {filteredEmojis.map((emoji) => (\n insertEmoji(editor, emoji)}\n >\n {emoji.skins[0].native} {emoji.name}\n \n ))}\n \n \n\n {children}\n
\n );\n }\n);\n", "path": "plate-ui/emoji-input-element.tsx", "target": "", "type": "registry:ui" diff --git a/apps/www/public/r/styles/default/excalidraw-element.json b/apps/www/public/r/styles/default/excalidraw-element.json index 7e0e94cf94..1c46cf946d 100644 --- a/apps/www/public/r/styles/default/excalidraw-element.json +++ b/apps/www/public/r/styles/default/excalidraw-element.json @@ -4,7 +4,7 @@ ], "files": [ { - "content": "import React from 'react';\n\nimport { withRef } from '@udecode/cn';\nimport { PlateElement } from './plate-element';\nimport { useExcalidrawElement } from '@udecode/plate-excalidraw/react';\n\nexport const ExcalidrawElement = withRef(\n ({ nodeProps, ...props }, ref) => {\n const { children, element } = props;\n\n const { Excalidraw, excalidrawProps } = useExcalidrawElement({\n element,\n });\n\n return (\n \n
\n
\n {Excalidraw && (\n \n )}\n
\n
\n {children}\n
\n );\n }\n);\n", + "content": "import React from 'react';\n\nimport { withRef } from '@udecode/cn';\nimport { useExcalidrawElement } from '@udecode/plate-excalidraw/react';\n\nimport { PlateElement } from './plate-element';\n\nexport const ExcalidrawElement = withRef(\n ({ nodeProps, ...props }, ref) => {\n const { children, element } = props;\n\n const { Excalidraw, excalidrawProps } = useExcalidrawElement({\n element,\n });\n\n return (\n \n
\n
\n {Excalidraw && (\n \n )}\n
\n
\n {children}\n
\n );\n }\n);\n", "path": "plate-ui/excalidraw-element.tsx", "target": "", "type": "registry:ui" diff --git a/apps/www/public/r/styles/default/floating-toolbar.json b/apps/www/public/r/styles/default/floating-toolbar.json index 9de2dc03c0..18be358a5c 100644 --- a/apps/www/public/r/styles/default/floating-toolbar.json +++ b/apps/www/public/r/styles/default/floating-toolbar.json @@ -4,7 +4,7 @@ ], "files": [ { - "content": "'use client';\n\nimport React from 'react';\n\nimport { cn, withRef } from '@udecode/cn';\nimport {\n PortalBody,\n useComposedRef,\n useEditorId,\n useEventEditorSelectors,\n} from '@udecode/plate-common/react';\nimport {\n type FloatingToolbarState,\n flip,\n offset,\n useFloatingToolbar,\n useFloatingToolbarState,\n} from '@udecode/plate-floating';\n\nimport { Toolbar } from './toolbar';\n\nexport const FloatingToolbar = withRef<\n typeof Toolbar,\n {\n state?: FloatingToolbarState;\n }\n>(({ children, state, ...props }, componentRef) => {\n const editorId = useEditorId();\n const focusedEditorId = useEventEditorSelectors.focus();\n\n const floatingToolbarState = useFloatingToolbarState({\n editorId,\n focusedEditorId,\n ...state,\n floatingOptions: {\n middleware: [\n offset(12),\n flip({\n fallbackPlacements: [\n 'top-start',\n 'top-end',\n 'bottom-start',\n 'bottom-end',\n ],\n padding: 12,\n }),\n ],\n placement: 'top',\n ...state?.floatingOptions,\n },\n });\n\n const {\n hidden,\n props: rootProps,\n ref: floatingRef,\n } = useFloatingToolbar(floatingToolbarState);\n\n const ref = useComposedRef(componentRef, floatingRef);\n\n if (hidden) return null;\n\n return (\n \n \n {children}\n \n \n );\n});\n", + "content": "'use client';\n\nimport React from 'react';\n\nimport { cn, withRef } from '@udecode/cn';\nimport {\n useComposedRef,\n useEditorId,\n useEventEditorSelectors,\n} from '@udecode/plate-common/react';\nimport {\n type FloatingToolbarState,\n flip,\n offset,\n useFloatingToolbar,\n useFloatingToolbarState,\n} from '@udecode/plate-floating';\n\nimport { Toolbar } from './toolbar';\n\nexport const FloatingToolbar = withRef<\n typeof Toolbar,\n {\n state?: FloatingToolbarState;\n }\n>(({ children, state, ...props }, componentRef) => {\n const editorId = useEditorId();\n const focusedEditorId = useEventEditorSelectors.focus();\n\n const floatingToolbarState = useFloatingToolbarState({\n editorId,\n focusedEditorId,\n ...state,\n floatingOptions: {\n middleware: [\n offset(12),\n flip({\n fallbackPlacements: [\n 'top-start',\n 'top-end',\n 'bottom-start',\n 'bottom-end',\n ],\n padding: 12,\n }),\n ],\n placement: 'top',\n ...state?.floatingOptions,\n },\n });\n\n const {\n clickOutsideRef,\n hidden,\n props: rootProps,\n ref: floatingRef,\n } = useFloatingToolbar(floatingToolbarState);\n\n const ref = useComposedRef(componentRef, floatingRef);\n\n if (hidden) return null;\n\n return (\n
\n \n {children}\n \n
\n );\n});\n", "path": "plate-ui/floating-toolbar.tsx", "target": "", "type": "registry:ui" diff --git a/apps/www/public/r/styles/default/hr-element.json b/apps/www/public/r/styles/default/hr-element.json index 9c8a34ebf8..e5eb2ddbc0 100644 --- a/apps/www/public/r/styles/default/hr-element.json +++ b/apps/www/public/r/styles/default/hr-element.json @@ -4,7 +4,7 @@ ], "files": [ { - "content": "import React from 'react';\n\nimport { cn, withRef } from '@udecode/cn';\nimport { PlateElement } from './plate-element';\nimport { useFocused, useSelected } from 'slate-react';\n\nexport const HrElement = withRef(\n ({ className, nodeProps, ...props }, ref) => {\n const { children } = props;\n\n const selected = useSelected();\n const focused = useFocused();\n\n return (\n \n
\n \n
\n {children}\n
\n );\n }\n);\n", + "content": "import React from 'react';\n\nimport { cn, withRef } from '@udecode/cn';\nimport { useFocused, useSelected } from 'slate-react';\n\nimport { PlateElement } from './plate-element';\n\nexport const HrElement = withRef(\n ({ className, nodeProps, ...props }, ref) => {\n const { children } = props;\n\n const selected = useSelected();\n const focused = useFocused();\n\n return (\n \n
\n \n
\n {children}\n
\n );\n }\n);\n", "path": "plate-ui/hr-element.tsx", "target": "", "type": "registry:ui" diff --git a/apps/www/public/r/styles/default/mention-input-element.json b/apps/www/public/r/styles/default/mention-input-element.json index 95320fac62..92e3464db5 100644 --- a/apps/www/public/r/styles/default/mention-input-element.json +++ b/apps/www/public/r/styles/default/mention-input-element.json @@ -4,7 +4,7 @@ ], "files": [ { - "content": "import React, { useState } from 'react';\n\nimport { cn, withRef } from '@udecode/cn';\nimport { PlateElement } from './plate-element';\nimport { getMentionOnSelectItem } from '@udecode/plate-mention';\n\nimport {\n InlineCombobox,\n InlineComboboxContent,\n InlineComboboxEmpty,\n InlineComboboxInput,\n InlineComboboxItem,\n} from './inline-combobox';\n\nconst onSelectItem = getMentionOnSelectItem();\n\nexport const MentionInputElement = withRef(\n ({ className, ...props }, ref) => {\n const { children, editor, element } = props;\n const [search, setSearch] = useState('');\n\n return (\n \n \n \n \n \n\n \n No results found\n\n {MENTIONABLES.map((item) => (\n onSelectItem(editor, item, search)}\n >\n {item.text}\n \n ))}\n \n \n\n {children}\n \n );\n }\n);\n\nexport const MENTIONABLES = [\n { key: '0', text: 'Aayla Secura' },\n { key: '1', text: 'Adi Gallia' },\n {\n key: '2',\n text: 'Admiral Dodd Rancit',\n },\n {\n key: '3',\n text: 'Admiral Firmus Piett',\n },\n {\n key: '4',\n text: 'Admiral Gial Ackbar',\n },\n { key: '5', text: 'Admiral Ozzel' },\n { key: '6', text: 'Admiral Raddus' },\n {\n key: '7',\n text: 'Admiral Terrinald Screed',\n },\n { key: '8', text: 'Admiral Trench' },\n {\n key: '9',\n text: 'Admiral U.O. Statura',\n },\n { key: '10', text: 'Agen Kolar' },\n { key: '11', text: 'Agent Kallus' },\n {\n key: '12',\n text: 'Aiolin and Morit Astarte',\n },\n { key: '13', text: 'Aks Moe' },\n { key: '14', text: 'Almec' },\n { key: '15', text: 'Alton Kastle' },\n { key: '16', text: 'Amee' },\n { key: '17', text: 'AP-5' },\n { key: '18', text: 'Armitage Hux' },\n { key: '19', text: 'Artoo' },\n { key: '20', text: 'Arvel Crynyd' },\n { key: '21', text: 'Asajj Ventress' },\n { key: '22', text: 'Aurra Sing' },\n { key: '23', text: 'AZI-3' },\n { key: '24', text: 'Bala-Tik' },\n { key: '25', text: 'Barada' },\n { key: '26', text: 'Bargwill Tomder' },\n { key: '27', text: 'Baron Papanoida' },\n { key: '28', text: 'Barriss Offee' },\n { key: '29', text: 'Baze Malbus' },\n { key: '30', text: 'Bazine Netal' },\n { key: '31', text: 'BB-8' },\n { key: '32', text: 'BB-9E' },\n { key: '33', text: 'Ben Quadinaros' },\n { key: '34', text: 'Berch Teller' },\n { key: '35', text: 'Beru Lars' },\n { key: '36', text: 'Bib Fortuna' },\n {\n key: '37',\n text: 'Biggs Darklighter',\n },\n { key: '38', text: 'Black Krrsantan' },\n { key: '39', text: 'Bo-Katan Kryze' },\n { key: '40', text: 'Boba Fett' },\n { key: '41', text: 'Bobbajo' },\n { key: '42', text: 'Bodhi Rook' },\n { key: '43', text: 'Borvo the Hutt' },\n { key: '44', text: 'Boss Nass' },\n { key: '45', text: 'Bossk' },\n {\n key: '46',\n text: 'Breha Antilles-Organa',\n },\n { key: '47', text: 'Bren Derlin' },\n { key: '48', text: 'Brendol Hux' },\n { key: '49', text: 'BT-1' },\n];\n", + "content": "import React, { useState } from 'react';\n\nimport { cn, withRef } from '@udecode/cn';\nimport { getMentionOnSelectItem } from '@udecode/plate-mention';\n\nimport {\n InlineCombobox,\n InlineComboboxContent,\n InlineComboboxEmpty,\n InlineComboboxInput,\n InlineComboboxItem,\n} from './inline-combobox';\nimport { PlateElement } from './plate-element';\n\nconst onSelectItem = getMentionOnSelectItem();\n\nexport const MentionInputElement = withRef(\n ({ className, ...props }, ref) => {\n const { children, editor, element } = props;\n const [search, setSearch] = useState('');\n\n return (\n \n \n \n \n \n\n \n No results found\n\n {MENTIONABLES.map((item) => (\n onSelectItem(editor, item, search)}\n >\n {item.text}\n \n ))}\n \n \n\n {children}\n \n );\n }\n);\n\nexport const MENTIONABLES = [\n { key: '0', text: 'Aayla Secura' },\n { key: '1', text: 'Adi Gallia' },\n {\n key: '2',\n text: 'Admiral Dodd Rancit',\n },\n {\n key: '3',\n text: 'Admiral Firmus Piett',\n },\n {\n key: '4',\n text: 'Admiral Gial Ackbar',\n },\n { key: '5', text: 'Admiral Ozzel' },\n { key: '6', text: 'Admiral Raddus' },\n {\n key: '7',\n text: 'Admiral Terrinald Screed',\n },\n { key: '8', text: 'Admiral Trench' },\n {\n key: '9',\n text: 'Admiral U.O. Statura',\n },\n { key: '10', text: 'Agen Kolar' },\n { key: '11', text: 'Agent Kallus' },\n {\n key: '12',\n text: 'Aiolin and Morit Astarte',\n },\n { key: '13', text: 'Aks Moe' },\n { key: '14', text: 'Almec' },\n { key: '15', text: 'Alton Kastle' },\n { key: '16', text: 'Amee' },\n { key: '17', text: 'AP-5' },\n { key: '18', text: 'Armitage Hux' },\n { key: '19', text: 'Artoo' },\n { key: '20', text: 'Arvel Crynyd' },\n { key: '21', text: 'Asajj Ventress' },\n { key: '22', text: 'Aurra Sing' },\n { key: '23', text: 'AZI-3' },\n { key: '24', text: 'Bala-Tik' },\n { key: '25', text: 'Barada' },\n { key: '26', text: 'Bargwill Tomder' },\n { key: '27', text: 'Baron Papanoida' },\n { key: '28', text: 'Barriss Offee' },\n { key: '29', text: 'Baze Malbus' },\n { key: '30', text: 'Bazine Netal' },\n { key: '31', text: 'BB-8' },\n { key: '32', text: 'BB-9E' },\n { key: '33', text: 'Ben Quadinaros' },\n { key: '34', text: 'Berch Teller' },\n { key: '35', text: 'Beru Lars' },\n { key: '36', text: 'Bib Fortuna' },\n {\n key: '37',\n text: 'Biggs Darklighter',\n },\n { key: '38', text: 'Black Krrsantan' },\n { key: '39', text: 'Bo-Katan Kryze' },\n { key: '40', text: 'Boba Fett' },\n { key: '41', text: 'Bobbajo' },\n { key: '42', text: 'Bodhi Rook' },\n { key: '43', text: 'Borvo the Hutt' },\n { key: '44', text: 'Boss Nass' },\n { key: '45', text: 'Bossk' },\n {\n key: '46',\n text: 'Breha Antilles-Organa',\n },\n { key: '47', text: 'Bren Derlin' },\n { key: '48', text: 'Brendol Hux' },\n { key: '49', text: 'BT-1' },\n];\n", "path": "plate-ui/mention-input-element.tsx", "target": "", "type": "registry:ui" diff --git a/apps/www/public/r/styles/default/todo-list-element.json b/apps/www/public/r/styles/default/todo-list-element.json index e14dd33c28..6d075256e4 100644 --- a/apps/www/public/r/styles/default/todo-list-element.json +++ b/apps/www/public/r/styles/default/todo-list-element.json @@ -4,7 +4,7 @@ ], "files": [ { - "content": "import React from 'react';\n\nimport { cn, withRef } from '@udecode/cn';\nimport { PlateElement } from './plate-element';\nimport {\n useTodoListElement,\n useTodoListElementState,\n} from '@udecode/plate-list/react';\n\nimport { Checkbox } from './checkbox';\n\nexport const TodoListElement = withRef(\n ({ children, className, ...props }, ref) => {\n const { element } = props;\n const state = useTodoListElementState({ element });\n const { checkboxProps } = useTodoListElement(state);\n\n return (\n \n \n \n \n \n {children}\n \n \n );\n }\n);\n", + "content": "import React from 'react';\n\nimport { cn, withRef } from '@udecode/cn';\nimport {\n useTodoListElement,\n useTodoListElementState,\n} from '@udecode/plate-list/react';\n\nimport { Checkbox } from './checkbox';\nimport { PlateElement } from './plate-element';\n\nexport const TodoListElement = withRef(\n ({ children, className, ...props }, ref) => {\n const { element } = props;\n const state = useTodoListElementState({ element });\n const { checkboxProps } = useTodoListElement(state);\n\n return (\n \n \n \n \n \n {children}\n \n \n );\n }\n);\n", "path": "plate-ui/todo-list-element.tsx", "target": "", "type": "registry:ui" diff --git a/apps/www/public/r/styles/default/turn-into-dropdown-menu.json b/apps/www/public/r/styles/default/turn-into-dropdown-menu.json index 0ccbce78a8..57bfebd012 100644 --- a/apps/www/public/r/styles/default/turn-into-dropdown-menu.json +++ b/apps/www/public/r/styles/default/turn-into-dropdown-menu.json @@ -5,7 +5,7 @@ ], "files": [ { - "content": "import React from 'react';\n\nimport type { DropdownMenuProps } from '@radix-ui/react-dropdown-menu';\n\nimport { BlockquotePlugin } from '@udecode/plate-block-quote/react';\nimport {\n collapseSelection,\n getNodeEntries,\n isBlock,\n} from '@udecode/plate-common';\nimport {\n ParagraphPlugin,\n focusEditor,\n useEditorRef,\n useEditorSelector,\n} from '@udecode/plate-common/react';\nimport { HEADING_KEYS } from '@udecode/plate-heading';\n\nimport { Icons } from '@/components/icons';\n\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuLabel,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuTrigger,\n useOpenState,\n} from './dropdown-menu';\nimport { ToolbarButton } from './toolbar';\n\nconst items = [\n {\n description: 'Paragraph',\n icon: Icons.paragraph,\n label: 'Paragraph',\n value: ParagraphPlugin.key,\n },\n {\n description: 'Heading 1',\n icon: Icons.h1,\n label: 'Heading 1',\n value: HEADING_KEYS.h1,\n },\n {\n description: 'Heading 2',\n icon: Icons.h2,\n label: 'Heading 2',\n value: HEADING_KEYS.h2,\n },\n {\n description: 'Heading 3',\n icon: Icons.h3,\n label: 'Heading 3',\n value: HEADING_KEYS.h3,\n },\n {\n description: 'Quote (⌘+⇧+.)',\n icon: Icons.blockquote,\n label: 'Quote',\n value: BlockquotePlugin.key,\n },\n // {\n // value: 'ul',\n // label: 'Bulleted list',\n // description: 'Bulleted list',\n // icon: Icons.ul,\n // },\n // {\n // value: 'ol',\n // label: 'Numbered list',\n // description: 'Numbered list',\n // icon: Icons.ol,\n // },\n];\n\nconst defaultItem = items.find((item) => item.value === ParagraphPlugin.key)!;\n\nexport function TurnIntoDropdownMenu(props: DropdownMenuProps) {\n const value: string = useEditorSelector((editor) => {\n let initialNodeType: string = ParagraphPlugin.key;\n let allNodesMatchInitialNodeType = false;\n const codeBlockEntries = getNodeEntries(editor, {\n match: (n) => isBlock(editor, n),\n mode: 'highest',\n });\n const nodes = Array.from(codeBlockEntries);\n\n if (nodes.length > 0) {\n initialNodeType = nodes[0][0].type as string;\n allNodesMatchInitialNodeType = nodes.every(([node]) => {\n const type: string = (node?.type as string) || ParagraphPlugin.key;\n\n return type === initialNodeType;\n });\n }\n\n return allNodesMatchInitialNodeType ? initialNodeType : ParagraphPlugin.key;\n }, []);\n\n const editor = useEditorRef();\n const openState = useOpenState();\n\n const selectedItem =\n items.find((item) => item.value === value) ?? defaultItem;\n const { icon: SelectedItemIcon, label: selectedItemLabel } = selectedItem;\n\n return (\n \n \n \n \n {selectedItemLabel}\n \n \n\n \n Turn into\n\n {\n // if (type === 'ul' || type === 'ol') {\n // if (settingsStore.get.checkedId(IndentListPlugin.key)) {\n // toggleIndentList(editor, {\n // listStyleType: type === 'ul' ? 'disc' : 'decimal',\n // });\n // } else if (settingsStore.get.checkedId('list')) {\n // toggleList(editor, { type });\n // }\n // } else {\n // unwrapList(editor);\n editor.tf.toggle.block({ type });\n // }\n\n collapseSelection(editor);\n focusEditor(editor);\n }}\n >\n {items.map(({ icon: Icon, label, value: itemValue }) => (\n \n \n {label}\n \n ))}\n
\n
\n
\n );\n}\n", + "content": "import React from 'react';\n\nimport type { DropdownMenuProps } from '@radix-ui/react-dropdown-menu';\n\nimport { BlockquotePlugin } from '@udecode/plate-block-quote/react';\nimport { getNodeEntries, isBlock } from '@udecode/plate-common';\nimport {\n ParagraphPlugin,\n focusEditor,\n useEditorRef,\n useEditorSelector,\n} from '@udecode/plate-common/react';\nimport { HEADING_KEYS } from '@udecode/plate-heading';\nimport { ListStyleType, toggleIndentList } from '@udecode/plate-indent-list';\n\nimport { Icons } from '@/components/icons';\n\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuLabel,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuTrigger,\n useOpenState,\n} from './dropdown-menu';\nimport { ToolbarButton } from './toolbar';\n\nconst items = [\n {\n description: 'Paragraph',\n icon: Icons.paragraph,\n label: 'Paragraph',\n value: ParagraphPlugin.key,\n },\n {\n description: 'Heading 1',\n icon: Icons.h1,\n label: 'Heading 1',\n value: HEADING_KEYS.h1,\n },\n {\n description: 'Heading 2',\n icon: Icons.h2,\n label: 'Heading 2',\n value: HEADING_KEYS.h2,\n },\n {\n description: 'Heading 3',\n icon: Icons.h3,\n label: 'Heading 3',\n value: HEADING_KEYS.h3,\n },\n {\n description: 'Quote (⌘+⇧+.)',\n icon: Icons.blockquote,\n label: 'Quote',\n value: BlockquotePlugin.key,\n },\n {\n description: 'Bulleted list',\n icon: Icons.ul,\n label: 'Bulleted list',\n value: ListStyleType.Disc,\n },\n {\n description: 'Numbered list',\n icon: Icons.ol,\n label: 'Numbered list',\n value: ListStyleType.Decimal,\n },\n];\n\nconst defaultItem = items.find((item) => item.value === ParagraphPlugin.key)!;\n\nexport function TurnIntoDropdownMenu(props: DropdownMenuProps) {\n const value: string = useEditorSelector((editor) => {\n let initialNodeType: string = ParagraphPlugin.key;\n let allNodesMatchInitialNodeType = false;\n const codeBlockEntries = getNodeEntries(editor, {\n match: (n) => isBlock(editor, n),\n mode: 'highest',\n });\n const nodes = Array.from(codeBlockEntries);\n\n if (nodes.length > 0) {\n initialNodeType = nodes[0][0].type as string;\n allNodesMatchInitialNodeType = nodes.every(([node]) => {\n const type: string = (node?.type as string) || ParagraphPlugin.key;\n\n return type === initialNodeType;\n });\n }\n\n return allNodesMatchInitialNodeType ? initialNodeType : ParagraphPlugin.key;\n }, []);\n\n const editor = useEditorRef();\n const openState = useOpenState();\n\n const selectedItem =\n // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison\n items.find((item) => item.value === value) ?? defaultItem;\n const { icon: SelectedItemIcon, label: selectedItemLabel } = selectedItem;\n\n return (\n \n \n \n \n {selectedItemLabel}\n \n \n\n \n Turn into\n\n {\n if (type === ListStyleType.Disc || type === ListStyleType.Decimal) {\n toggleIndentList(editor, {\n listStyleType: type,\n });\n }\n\n editor.tf.toggle.block({ type });\n\n focusEditor(editor);\n }}\n >\n {items.map(({ icon: Icon, label, value: itemValue }) => (\n \n \n {label}\n \n ))}\n \n
\n \n );\n}\n", "path": "plate-ui/turn-into-dropdown-menu.tsx", "target": "", "type": "registry:ui" diff --git a/apps/www/src/registry/default/plate-ui/turn-into-dropdown-menu.tsx b/apps/www/src/registry/default/plate-ui/turn-into-dropdown-menu.tsx index 2525458725..96a75b8823 100644 --- a/apps/www/src/registry/default/plate-ui/turn-into-dropdown-menu.tsx +++ b/apps/www/src/registry/default/plate-ui/turn-into-dropdown-menu.tsx @@ -99,6 +99,7 @@ export function TurnIntoDropdownMenu(props: DropdownMenuProps) { const openState = useOpenState(); const selectedItem = + // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison items.find((item) => item.value === value) ?? defaultItem; const { icon: SelectedItemIcon, label: selectedItemLabel } = selectedItem;