From 88ceb03ec343f39a3848dcac3da50e91922fb44c Mon Sep 17 00:00:00 2001 From: Felix Feng Date: Sat, 28 Sep 2024 20:16:25 +0800 Subject: [PATCH 1/3] refactor --- .../playground-insert-dropdown-menu.tsx | 14 ++++- apps/www/src/lib/plate/create-plate-ui.ts | 3 + .../default/example/playground-demo.tsx | 9 ++- .../registry/default/plate-ui/toc-element.tsx | 63 +++++++++++++++++++ packages/heading/src/lib/BaseTocPlugin.ts | 8 +++ .../heading/src/react/hooks/useTocElement.ts | 12 ++-- .../src/react/hooks/useTocSideBarState.ts | 17 +++-- 7 files changed, 113 insertions(+), 13 deletions(-) create mode 100644 apps/www/src/registry/default/plate-ui/toc-element.tsx diff --git a/apps/www/src/components/plate-ui/playground-insert-dropdown-menu.tsx b/apps/www/src/components/plate-ui/playground-insert-dropdown-menu.tsx index 1725abe31d..4ee8aeee70 100644 --- a/apps/www/src/components/plate-ui/playground-insert-dropdown-menu.tsx +++ b/apps/www/src/components/plate-ui/playground-insert-dropdown-menu.tsx @@ -10,7 +10,8 @@ import { CodeBlockPlugin } from '@udecode/plate-code-block/react'; import { insertEmptyElement } from '@udecode/plate-common'; import { ParagraphPlugin, focusEditor } from '@udecode/plate-common/react'; import { ExcalidrawPlugin } from '@udecode/plate-excalidraw/react'; -import { HEADING_KEYS } from '@udecode/plate-heading'; +import { HEADING_KEYS, insertToc } from '@udecode/plate-heading'; +import { TocPlugin } from '@udecode/plate-heading/react'; import { HorizontalRulePlugin } from '@udecode/plate-horizontal-rule/react'; import { toggleIndentList } from '@udecode/plate-indent-list'; import { IndentListPlugin } from '@udecode/plate-indent-list/react'; @@ -117,6 +118,12 @@ const items = [ label: 'Columns', value: ColumnPlugin.key, }, + { + description: 'Table of Contents', + icon: Icons.h3, + label: 'Table of Contents', + value: TocPlugin.key, + }, ], label: 'Basic blocks', }, @@ -241,6 +248,11 @@ export function PlaygroundInsertDropdownMenu(props: DropdownMenuProps) { break; } + case TocPlugin.key: { + insertToc(editor); + + break; + } default: { insertEmptyElement(editor, type, { nextBlock: true, diff --git a/apps/www/src/lib/plate/create-plate-ui.ts b/apps/www/src/lib/plate/create-plate-ui.ts index 1d694ac2ef..a3ce01f4cf 100644 --- a/apps/www/src/lib/plate/create-plate-ui.ts +++ b/apps/www/src/lib/plate/create-plate-ui.ts @@ -28,6 +28,7 @@ import { EmojiInputPlugin } from '@udecode/plate-emoji/react'; import { ExcalidrawPlugin } from '@udecode/plate-excalidraw/react'; import { FindReplacePlugin } from '@udecode/plate-find-replace'; import { HEADING_KEYS } from '@udecode/plate-heading'; +import { TocPlugin } from '@udecode/plate-heading/react'; import { HighlightPlugin } from '@udecode/plate-highlight/react'; import { HorizontalRulePlugin } from '@udecode/plate-horizontal-rule/react'; import { KbdPlugin } from '@udecode/plate-kbd/react'; @@ -84,6 +85,7 @@ import { } from '@/registry/default/plate-ui/table-cell-element'; import { TableElement } from '@/registry/default/plate-ui/table-element'; import { TableRowElement } from '@/registry/default/plate-ui/table-row-element'; +import { TocElement } from '@/registry/default/plate-ui/toc-element'; import { TodoListElement } from '@/registry/default/plate-ui/todo-list-element'; import { ToggleElement } from '@/registry/default/plate-ui/toggle-element'; import { withDraggables } from '@/registry/default/plate-ui/with-draggables'; @@ -133,6 +135,7 @@ export const createPlateUI = ({ [TableCellPlugin.key]: TableCellElement, [TablePlugin.key]: TableElement, [TableRowPlugin.key]: TableRowElement, + [TocPlugin.key]: TocElement, [TodoListPlugin.key]: TodoListElement, [TogglePlugin.key]: ToggleElement, [UnderlinePlugin.key]: withProps(PlateLeaf, { as: 'u' }), diff --git a/apps/www/src/registry/default/example/playground-demo.tsx b/apps/www/src/registry/default/example/playground-demo.tsx index 6b6b5e10f5..02f9813445 100644 --- a/apps/www/src/registry/default/example/playground-demo.tsx +++ b/apps/www/src/registry/default/example/playground-demo.tsx @@ -39,7 +39,7 @@ import { FontSizePlugin, } from '@udecode/plate-font/react'; import { HEADING_KEYS } from '@udecode/plate-heading'; -import { HeadingPlugin } from '@udecode/plate-heading/react'; +import { HeadingPlugin, TocPlugin } from '@udecode/plate-heading/react'; import { HighlightPlugin } from '@udecode/plate-highlight/react'; import { HorizontalRulePlugin } from '@udecode/plate-horizontal-rule/react'; import { IndentPlugin } from '@udecode/plate-indent/react'; @@ -120,6 +120,13 @@ export const usePlaygroundEditor = (id: any = '', scrollSelector?: string) => { plugins: [ // Nodes HeadingPlugin, + TocPlugin.configure({ + options: { + isScroll: true, + scrollContainerSelector: `#${scrollSelector}`, + topOffset: 80, + }, + }), BlockquotePlugin, CodeBlockPlugin.configure({ options: { diff --git a/apps/www/src/registry/default/plate-ui/toc-element.tsx b/apps/www/src/registry/default/plate-ui/toc-element.tsx new file mode 100644 index 0000000000..e5088574e4 --- /dev/null +++ b/apps/www/src/registry/default/plate-ui/toc-element.tsx @@ -0,0 +1,63 @@ +import { cn } from '@udecode/cn'; +import { PlateElement } from '@udecode/plate-common/react'; +import { + useTocElement, + useTocElementState, +} from '@udecode/plate-heading/react'; +import { withRef } from '@udecode/react-utils'; +import { cva } from 'class-variance-authority'; + +import { Button } from './button'; + +const headingItemVariants = cva( + 'block h-auto w-full cursor-pointer truncate rounded-none px-0.5 py-1.5 text-left font-medium text-muted-foreground underline decoration-[0.5px] underline-offset-4 hover:bg-accent hover:text-muted-foreground', + { + variants: { + depth: { + 1: 'pl-0.5', + 2: 'pl-[26px]', + 3: 'pl-[50px]', + }, + }, + } +); + +// const options: useTocElementStateProps = { +// // if editor container have a fixed height set isScroll to TRUE. +// isScroll: true, +// scrollContainerSelector: '#scroll_container', +// topOffset: 80, +// }; + +export const TocElement = withRef( + ({ children, className, ...props }, ref) => { + const state = useTocElementState(); + + const { props: btnProps } = useTocElement(state); + + const { headingList } = state; + + return ( + + + {children} + + ); + } +); diff --git a/packages/heading/src/lib/BaseTocPlugin.ts b/packages/heading/src/lib/BaseTocPlugin.ts index 74f9963622..f33ec14141 100644 --- a/packages/heading/src/lib/BaseTocPlugin.ts +++ b/packages/heading/src/lib/BaseTocPlugin.ts @@ -9,11 +9,19 @@ import type { Heading } from './types'; export type TocConfig = PluginConfig< 'toc', { + isScroll: boolean; + topOffset: number; queryHeading?: (editor: SlateEditor) => Heading[]; + scrollContainerSelector?: string; } >; export const BaseTocPlugin = createTSlatePlugin({ key: 'toc', node: { isElement: true, isVoid: true }, + options: { + isScroll: true, + scrollContainerSelector: '#scroll_container', + topOffset: 80, + }, }); diff --git a/packages/heading/src/react/hooks/useTocElement.ts b/packages/heading/src/react/hooks/useTocElement.ts index 355af77b7f..48dc2f2758 100644 --- a/packages/heading/src/react/hooks/useTocElement.ts +++ b/packages/heading/src/react/hooks/useTocElement.ts @@ -3,13 +3,14 @@ import React, { useEffect } from 'react'; import { getNode } from '@udecode/plate-common'; import { toDOMNode, - useEditorRef, + useEditorPlugin, useEditorSelector, } from '@udecode/plate-common/react'; import type { Heading } from '../../lib/types'; import { getHeadingList } from '../../internal/getHeadingList'; +import { TocPlugin } from '../TocPlugin'; import { heightToTop } from '../utils'; export type useTocElementStateProps = { @@ -18,12 +19,9 @@ export type useTocElementStateProps = { scrollContainerSelector?: string; }; -export const useTocElementState = ({ - isScroll, - scrollContainerSelector, - topOffset, -}: useTocElementStateProps) => { - const editor = useEditorRef(); +export const useTocElementState = () => { + const { editor, getOptions } = useEditorPlugin(TocPlugin); + const { isScroll, scrollContainerSelector, topOffset } = getOptions(); const headingList = useEditorSelector(getHeadingList, []); diff --git a/packages/heading/src/react/hooks/useTocSideBarState.ts b/packages/heading/src/react/hooks/useTocSideBarState.ts index ead5bffe16..5fabf42421 100644 --- a/packages/heading/src/react/hooks/useTocSideBarState.ts +++ b/packages/heading/src/react/hooks/useTocSideBarState.ts @@ -3,7 +3,7 @@ import React from 'react'; import { getNode } from '@udecode/plate-common'; import { toDOMNode, - useEditorRef, + useEditorPlugin, useEditorSelector, } from '@udecode/plate-common/react'; @@ -12,10 +12,10 @@ import type { TocSideBarProps } from '../types'; import { useContentController, useTocController } from '.'; import { getHeadingList } from '../../internal/getHeadingList'; +import { TocPlugin } from '../TocPlugin'; import { checkIn } from '../utils'; export const useTocSideBarState = ({ - containerRef, open = true, rootMargin = '0px 0px 0px 0px', showHeader = true, @@ -23,8 +23,17 @@ export const useTocSideBarState = ({ topOffset = 0, onOpenChange, }: TocSideBarProps) => { - const editor = useEditorRef(); + const { editor, getOptions } = useEditorPlugin(TocPlugin); + const { scrollContainerSelector } = getOptions(); const headingList = useEditorSelector(getHeadingList, []); + const scrollContainerRef = React.useRef(null); + + React.useEffect(() => { + scrollContainerRef.current = document.querySelector( + scrollContainerSelector ?? '#scroll_container' + )!; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); const tocRef = React.useRef(null); @@ -33,7 +42,7 @@ export const useTocSideBarState = ({ const [isObserve, setIsObserve] = React.useState(open); const { activeContentId, onContentScroll } = useContentController({ - containerRef, + containerRef: scrollContainerRef, isObserve, rootMargin, topOffset, From f711b60c0bfcfb6d369920f59c3a9e872cba07bd Mon Sep 17 00:00:00 2001 From: Felix Feng Date: Sat, 28 Sep 2024 20:34:23 +0800 Subject: [PATCH 2/3] Remove useless props --- .../src/registry/default/plate-ui/toc-element.tsx | 7 ------- .../heading/src/react/hooks/useTocController.ts | 3 --- packages/heading/src/react/hooks/useTocElement.ts | 6 ------ packages/heading/src/react/hooks/useTocObserver.ts | 1 - .../heading/src/react/hooks/useTocSideBarState.ts | 7 ------- packages/heading/src/react/types.ts | 5 ----- yarn.lock | 14 +++++++------- 7 files changed, 7 insertions(+), 36 deletions(-) diff --git a/apps/www/src/registry/default/plate-ui/toc-element.tsx b/apps/www/src/registry/default/plate-ui/toc-element.tsx index e5088574e4..b5d454bb44 100644 --- a/apps/www/src/registry/default/plate-ui/toc-element.tsx +++ b/apps/www/src/registry/default/plate-ui/toc-element.tsx @@ -22,13 +22,6 @@ const headingItemVariants = cva( } ); -// const options: useTocElementStateProps = { -// // if editor container have a fixed height set isScroll to TRUE. -// isScroll: true, -// scrollContainerSelector: '#scroll_container', -// topOffset: 80, -// }; - export const TocElement = withRef( ({ children, className, ...props }, ref) => { const state = useTocElementState(); diff --git a/packages/heading/src/react/hooks/useTocController.ts b/packages/heading/src/react/hooks/useTocController.ts index c20b83e8ec..43f2f10755 100644 --- a/packages/heading/src/react/hooks/useTocController.ts +++ b/packages/heading/src/react/hooks/useTocController.ts @@ -5,21 +5,18 @@ import { useTocObserver } from './useTocObserver'; interface UseTocController { activeId: string; isObserve: boolean; - showHeader: boolean; tocRef: React.RefObject; } export const useTocController = ({ activeId, isObserve, - showHeader, tocRef, }: UseTocController) => { const [activeTocId, setActiveTocId] = React.useState(''); const { offset, visible } = useTocObserver({ activeId: activeTocId, isObserve, - showHeader, tocRef, }); diff --git a/packages/heading/src/react/hooks/useTocElement.ts b/packages/heading/src/react/hooks/useTocElement.ts index 48dc2f2758..aa96a361e4 100644 --- a/packages/heading/src/react/hooks/useTocElement.ts +++ b/packages/heading/src/react/hooks/useTocElement.ts @@ -13,12 +13,6 @@ import { getHeadingList } from '../../internal/getHeadingList'; import { TocPlugin } from '../TocPlugin'; import { heightToTop } from '../utils'; -export type useTocElementStateProps = { - isScroll: boolean; - topOffset: number; - scrollContainerSelector?: string; -}; - export const useTocElementState = () => { const { editor, getOptions } = useEditorPlugin(TocPlugin); const { isScroll, scrollContainerSelector, topOffset } = getOptions(); diff --git a/packages/heading/src/react/hooks/useTocObserver.ts b/packages/heading/src/react/hooks/useTocObserver.ts index 2aa93135a8..252694bd88 100644 --- a/packages/heading/src/react/hooks/useTocObserver.ts +++ b/packages/heading/src/react/hooks/useTocObserver.ts @@ -3,7 +3,6 @@ import React from 'react'; interface UseTocObserver { activeId: string; isObserve: boolean; - showHeader: boolean; tocRef: React.RefObject; } diff --git a/packages/heading/src/react/hooks/useTocSideBarState.ts b/packages/heading/src/react/hooks/useTocSideBarState.ts index 5fabf42421..f788e20345 100644 --- a/packages/heading/src/react/hooks/useTocSideBarState.ts +++ b/packages/heading/src/react/hooks/useTocSideBarState.ts @@ -18,10 +18,7 @@ import { checkIn } from '../utils'; export const useTocSideBarState = ({ open = true, rootMargin = '0px 0px 0px 0px', - showHeader = true, - style, topOffset = 0, - onOpenChange, }: TocSideBarProps) => { const { editor, getOptions } = useEditorPlugin(TocPlugin); const { scrollContainerSelector } = getOptions(); @@ -51,7 +48,6 @@ export const useTocSideBarState = ({ useTocController({ activeId: activeContentId, isObserve, - showHeader, tocRef, }); @@ -63,11 +59,8 @@ export const useTocSideBarState = ({ open, setIsObserve, setMouseInToc, - showHeader, - style, tocRef, onContentScroll, - onOpenChange, }; }; diff --git a/packages/heading/src/react/types.ts b/packages/heading/src/react/types.ts index 26a08f3634..7b9415bd8a 100644 --- a/packages/heading/src/react/types.ts +++ b/packages/heading/src/react/types.ts @@ -1,11 +1,6 @@ export interface TocSideBarProps { - containerRef: React.RefObject; - className?: string; - onOpenChange?: (open: boolean) => void; open?: boolean; rootMargin?: string; - showHeader?: boolean; - style?: React.CSSProperties; topOffset?: number; } diff --git a/yarn.lock b/yarn.lock index d226bcd939..e1e47a8326 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6215,14 +6215,14 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-docx@npm:38.0.8, @udecode/plate-docx@workspace:^, @udecode/plate-docx@workspace:packages/docx": +"@udecode/plate-docx@npm:38.0.10, @udecode/plate-docx@workspace:^, @udecode/plate-docx@workspace:packages/docx": version: 0.0.0-use.local resolution: "@udecode/plate-docx@workspace:packages/docx" dependencies: "@udecode/plate-common": "workspace:^" "@udecode/plate-heading": "npm:38.0.1" "@udecode/plate-indent": "npm:38.0.1" - "@udecode/plate-indent-list": "npm:38.0.1" + "@udecode/plate-indent-list": "npm:38.0.10" "@udecode/plate-media": "npm:38.0.6" "@udecode/plate-table": "npm:38.0.8" validator: "npm:^13.12.0" @@ -6389,7 +6389,7 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-indent-list@npm:38.0.1, @udecode/plate-indent-list@workspace:^, @udecode/plate-indent-list@workspace:packages/indent-list": +"@udecode/plate-indent-list@npm:38.0.10, @udecode/plate-indent-list@workspace:^, @udecode/plate-indent-list@workspace:packages/indent-list": version: 0.0.0-use.local resolution: "@udecode/plate-indent-list@workspace:packages/indent-list" dependencies: @@ -6695,7 +6695,7 @@ __metadata: languageName: unknown linkType: soft -"@udecode/plate-selection@npm:38.0.9, @udecode/plate-selection@workspace:^, @udecode/plate-selection@workspace:packages/selection": +"@udecode/plate-selection@npm:38.0.11, @udecode/plate-selection@workspace:^, @udecode/plate-selection@workspace:packages/selection": version: 0.0.0-use.local resolution: "@udecode/plate-selection@workspace:packages/selection" dependencies: @@ -6913,7 +6913,7 @@ __metadata: "@udecode/plate-common": "npm:38.0.6" "@udecode/plate-csv": "npm:38.0.8" "@udecode/plate-diff": "npm:38.0.0" - "@udecode/plate-docx": "npm:38.0.8" + "@udecode/plate-docx": "npm:38.0.10" "@udecode/plate-find-replace": "npm:38.0.0" "@udecode/plate-floating": "npm:38.0.1" "@udecode/plate-font": "npm:38.0.1" @@ -6922,7 +6922,7 @@ __metadata: "@udecode/plate-horizontal-rule": "npm:38.0.1" "@udecode/plate-html": "npm:38.0.1" "@udecode/plate-indent": "npm:38.0.1" - "@udecode/plate-indent-list": "npm:38.0.1" + "@udecode/plate-indent-list": "npm:38.0.10" "@udecode/plate-kbd": "npm:38.0.1" "@udecode/plate-layout": "npm:38.0.1" "@udecode/plate-line-height": "npm:38.0.1" @@ -6936,7 +6936,7 @@ __metadata: "@udecode/plate-reset-node": "npm:38.0.1" "@udecode/plate-resizable": "npm:38.0.0" "@udecode/plate-select": "npm:38.0.1" - "@udecode/plate-selection": "npm:38.0.9" + "@udecode/plate-selection": "npm:38.0.11" "@udecode/plate-slash-command": "npm:38.0.1" "@udecode/plate-suggestion": "npm:38.0.1" "@udecode/plate-tabbable": "npm:38.0.1" From 8cffd67a5f22a7e1f73c6e499ebfb0307b4d3083 Mon Sep 17 00:00:00 2001 From: Felix Feng Date: Sat, 28 Sep 2024 20:35:51 +0800 Subject: [PATCH 3/3] Changeset --- .changeset/giant-deers-marry.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/giant-deers-marry.md diff --git a/.changeset/giant-deers-marry.md b/.changeset/giant-deers-marry.md new file mode 100644 index 0000000000..30c79c6ae4 --- /dev/null +++ b/.changeset/giant-deers-marry.md @@ -0,0 +1,5 @@ +--- +'@udecode/plate-heading': patch +--- + +Remove useless props move config from `useTocElementState` to TocPlugin `options`