From eeb14a60bc33f48cf3d6afa523e58ae0393b7907 Mon Sep 17 00:00:00 2001 From: Liam Andrew Date: Fri, 27 Sep 2024 10:06:29 +1000 Subject: [PATCH 1/2] fix: dialog usage pattern example --- apps/docs/guides/tailwind/slash-command.mdx | 33 +++++ .../components/tailwind/advanced-editor.tsx | 131 +++++++++--------- .../web/components/tailwind/slash-command.tsx | 13 +- 3 files changed, 107 insertions(+), 70 deletions(-) diff --git a/apps/docs/guides/tailwind/slash-command.mdx b/apps/docs/guides/tailwind/slash-command.mdx index d9b5a608..d5d64b65 100644 --- a/apps/docs/guides/tailwind/slash-command.mdx +++ b/apps/docs/guides/tailwind/slash-command.mdx @@ -193,3 +193,36 @@ Components are wrapper over cmdk ... ``` + +## Usage in Dialog + +Since a dialog is a portal, it isn't possible to scroll the command list when it's opened in a dialog since both are attached to the body but the default behaviour Radix's Dialog is to lock the scroll of the body when open. + +In order to fix this,we need to pass a ref to the `slashCommand` function so that it can render the command palette outside the editor's DOM hierarchy. + +We can do this by creating a `useRef` and passing it to the `slashCommand` function. + +```tsx +// Modify slashCommand to accept a ref +const slashCommand = (ref: React.RefObject | null) => Command.configure({ + suggestion: { + items: () => suggestionItems, + render: () => renderItems(ref), + }, +}); +``` + +```tsx +// Create a ref and pass it to the slashCommand +const ref = useRef(null); +const extensions = useRef([...defaultExtensions, slashCommand(ref)]).current; +``` + +```tsx +// Wrap the editor with a div and pass the ref +
+ + ... + +
+``` \ No newline at end of file diff --git a/apps/web/components/tailwind/advanced-editor.tsx b/apps/web/components/tailwind/advanced-editor.tsx index 526681f2..2b5d00a6 100644 --- a/apps/web/components/tailwind/advanced-editor.tsx +++ b/apps/web/components/tailwind/advanced-editor.tsx @@ -11,13 +11,13 @@ import { type JSONContent, } from "novel"; import { ImageResizer, handleCommandNavigation } from "novel/extensions"; -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { useDebouncedCallback } from "use-debounce"; import { defaultExtensions } from "./extensions"; import { ColorSelector } from "./selectors/color-selector"; import { LinkSelector } from "./selectors/link-selector"; -import { NodeSelector } from "./selectors/node-selector"; import { MathSelector } from "./selectors/math-selector"; +import { NodeSelector } from "./selectors/node-selector"; import { Separator } from "./ui/separator"; import { handleImageDrop, handleImagePaste } from "novel/plugins"; @@ -26,11 +26,12 @@ import { uploadFn } from "./image-upload"; import { TextButtons } from "./selectors/text-buttons"; import { slashCommand, suggestionItems } from "./slash-command"; -const hljs = require('highlight.js'); - -const extensions = [...defaultExtensions, slashCommand]; +const hljs = require("highlight.js"); const TailwindAdvancedEditor = () => { + const ref = useRef(null); + const extensions = useRef([...defaultExtensions, slashCommand(ref)]).current; + const [initialContent, setInitialContent] = useState(null); const [saveStatus, setSaveStatus] = useState("Saved"); const [charsCount, setCharsCount] = useState(); @@ -42,8 +43,8 @@ const TailwindAdvancedEditor = () => { //Apply Codeblock Highlighting on the HTML from editor.getHTML() const highlightCodeblocks = (content: string) => { - const doc = new DOMParser().parseFromString(content, 'text/html'); - doc.querySelectorAll('pre code').forEach((el) => { + const doc = new DOMParser().parseFromString(content, "text/html"); + doc.querySelectorAll("pre code").forEach((el) => { // @ts-ignore // https://highlightjs.readthedocs.io/en/latest/api.html?highlight=highlightElement#highlightelement hljs.highlightElement(el); @@ -76,65 +77,67 @@ const TailwindAdvancedEditor = () => { {charsCount} Words - - handleCommandNavigation(event), - }, - handlePaste: (view, event) => handleImagePaste(view, event, uploadFn), - handleDrop: (view, event, _slice, moved) => handleImageDrop(view, event, moved, uploadFn), - attributes: { - class: - "prose prose-lg dark:prose-invert prose-headings:font-title font-default focus:outline-none max-w-full", - }, - }} - onUpdate={({ editor }) => { - debouncedUpdates(editor); - setSaveStatus("Unsaved"); - }} - slotAfter={} - > - - No results - - {suggestionItems.map((item) => ( - item.command(val)} - className="flex w-full items-center space-x-2 rounded-md px-2 py-1 text-left text-sm hover:bg-accent aria-selected:bg-accent" - key={item.title} - > -
- {item.icon} -
-
-

{item.title}

-

{item.description}

-
-
- ))} -
-
+
+ + handleCommandNavigation(event), + }, + handlePaste: (view, event) => handleImagePaste(view, event, uploadFn), + handleDrop: (view, event, _slice, moved) => handleImageDrop(view, event, moved, uploadFn), + attributes: { + class: + "prose prose-lg dark:prose-invert prose-headings:font-title font-default focus:outline-none max-w-full", + }, + }} + onUpdate={({ editor }) => { + debouncedUpdates(editor); + setSaveStatus("Unsaved"); + }} + slotAfter={} + > + + No results + + {suggestionItems.map((item) => ( + item.command(val)} + className="flex w-full items-center space-x-2 rounded-md px-2 py-1 text-left text-sm hover:bg-accent aria-selected:bg-accent" + key={item.title} + > +
+ {item.icon} +
+
+

{item.title}

+

{item.description}

+
+
+ ))} +
+
- - - - + + + + - - - - - - - - -
-
+ + + + + + + + + + +
); }; diff --git a/apps/web/components/tailwind/slash-command.tsx b/apps/web/components/tailwind/slash-command.tsx index e82ea481..a7ccb94d 100644 --- a/apps/web/components/tailwind/slash-command.tsx +++ b/apps/web/components/tailwind/slash-command.tsx @@ -181,9 +181,10 @@ export const suggestionItems = createSuggestionItems([ }, ]); -export const slashCommand = Command.configure({ - suggestion: { - items: () => suggestionItems, - render: renderItems, - }, -}); +export const slashCommand = (ref: React.RefObject | null) => + Command.configure({ + suggestion: { + items: () => suggestionItems, + render: () => renderItems(ref), + }, + }); From c506856abd4890528a10cf4faa1259c926d9eab5 Mon Sep 17 00:00:00 2001 From: Liam Andrew Date: Fri, 27 Sep 2024 10:12:30 +1000 Subject: [PATCH 2/2] fix: update doc --- apps/docs/guides/tailwind/slash-command.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/docs/guides/tailwind/slash-command.mdx b/apps/docs/guides/tailwind/slash-command.mdx index d5d64b65..4977d894 100644 --- a/apps/docs/guides/tailwind/slash-command.mdx +++ b/apps/docs/guides/tailwind/slash-command.mdx @@ -196,9 +196,9 @@ Components are wrapper over cmdk ## Usage in Dialog -Since a dialog is a portal, it isn't possible to scroll the command list when it's opened in a dialog since both are attached to the body but the default behaviour Radix's Dialog is to lock the scroll of the body when open. +By default, it isn't possible to scroll the command list when it's opened in a dialog since both are attached to the document's body and the default behaviour of Radix's Dialog is to lock the scroll of the body when open. -In order to fix this,we need to pass a ref to the `slashCommand` function so that it can render the command palette outside the editor's DOM hierarchy. +In order to fix this, we need to pass a ref to the `slashCommand` function so that it can render the command palette outside the editor's DOM hierarchy. We can do this by creating a `useRef` and passing it to the `slashCommand` function.