Skip to content

Commit

Permalink
Merge pull request #3273 from udecode/fix/toc
Browse files Browse the repository at this point in the history
toc controller optimize
  • Loading branch information
felixfeng33 authored Jun 12, 2024
2 parents 28a2655 + 72e0b8d commit 384b890
Show file tree
Hide file tree
Showing 8 changed files with 47 additions and 29 deletions.
4 changes: 4 additions & 0 deletions .changeset/old-crews-dream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
"@udecode/plate-heading": patch
---
Fix closure issue,incorrect height calculation and expose behavior parameters
2 changes: 1 addition & 1 deletion packages/heading/src/toc/createTocPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createPluginFactory } from '@udecode/plate-common';

export const ELEMENT_TOC = 'toc';

export const createTocPlugin = createPluginFactory<{ continerRef: any }>({
export const createTocPlugin = createPluginFactory({
isElement: true,
isVoid: true,
key: ELEMENT_TOC,
Expand Down
13 changes: 7 additions & 6 deletions packages/heading/src/toc/hooks/useTocElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
useEditorSelector,
} from '@udecode/plate-common';

import type { HeadingList } from '../types';
import type { Heading } from '../types';

import { getHeadingList, heightToTop } from '../../utils';

Expand Down Expand Up @@ -39,16 +39,16 @@ export const useTocElementState = ({
}, [editor]);

const onContentScroll = React.useCallback(
(el: HTMLElement, id: string) => {
(el: HTMLElement, id: string, behavior: ScrollBehavior = 'instant') => {
if (!containerRef.current) return;
if (isScroll) {
containerRef.current?.scrollTo({
behavior: 'instant',
behavior,
top: heightToTop(el, containerRef as any) - topOffset,
});
} else {
const top = heightToTop(el) - topOffset;
window.scrollTo({ behavior: 'instant', top });
window.scrollTo({ behavior, top });
}

setTimeout(() => {
Expand All @@ -70,7 +70,8 @@ export const useTocElement = ({
props: {
onClick: (
e: React.MouseEvent<HTMLElement, globalThis.MouseEvent>,
item: HeadingList
item: Heading,
behavior: ScrollBehavior
) => {
e.preventDefault();
const { id, path } = item;
Expand All @@ -82,7 +83,7 @@ export const useTocElement = ({

if (!el) return;

onContentScroll(el, id);
onContentScroll(el, id, behavior);
},
},
};
Expand Down
20 changes: 16 additions & 4 deletions packages/heading/src/toc/tocSideBar/useContentController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,14 @@ export const useContentController = ({
const editor = useEditorRef();
const [editorContentRef, setEditorContentRef] = React.useState(containerRef);

const isScrollRef = React.useRef(false);

const isScroll =
(editorContentRef.current?.scrollHeight || 0) >
(editorContentRef.current?.clientHeight || 0);

isScrollRef.current = isScroll;

const scrollContainer = React.useMemo(() => {
if (typeof window !== 'object') return;

Expand All @@ -41,18 +45,26 @@ export const useContentController = ({

const [activeContentId, setActiveContentId] = React.useState(activeId);

const onContentScroll = (el: HTMLElement, id: string) => {
const onContentScroll = ({
behavior = 'instant',
el,
id,
}: {
behavior?: ScrollBehavior;
el: HTMLElement;
id: string;
}) => {
setActiveContentId(id);

if (isScroll) {
if (isScrollRef.current) {
editorContentRef.current?.scrollTo({
behavior: 'instant',
behavior,
top: heightToTop(el, editorContentRef) - topOffset,
});
} else {
const top = heightToTop(el) - topOffset;
// Note: if behavior === smooth,scrolling the toc then click the title immediately will scroll to the wrong position.It should be a chrome bug.
window.scrollTo({ behavior: 'instant', top });
window.scrollTo({ behavior, top });
}

addSelectedRow(editor, id);
Expand Down
11 changes: 6 additions & 5 deletions packages/heading/src/toc/tocSideBar/useTocSideBarState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
useEditorSelector,
} from '@udecode/plate-common';

import type { HeadingList, TocSideBarProps } from '../types';
import type { Heading, TocSideBarProps } from '../types';

import { useContentController, useTocController } from '.';
import { checkIn, getHeadingList } from '../../utils';
Expand Down Expand Up @@ -78,10 +78,11 @@ export const useTocSideBar = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [mouseInToc]);

const onConentClick = React.useCallback(
const onContentClick = React.useCallback(
(
e: React.MouseEvent<HTMLElement, globalThis.MouseEvent>,
item: HeadingList
item: Heading,
behavior?: ScrollBehavior
) => {
e.preventDefault();
const { id, path } = item;
Expand All @@ -93,7 +94,7 @@ export const useTocSideBar = ({

if (!el) return;

onContentScroll(el, id);
onContentScroll({ behavior, el, id });
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
Expand All @@ -114,6 +115,6 @@ export const useTocSideBar = ({
},
ref: tocRef,
},
onConentClick,
onContentClick,
};
};
4 changes: 2 additions & 2 deletions packages/heading/src/toc/types.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { Path } from 'slate';

export interface HeadingList {
export interface Heading {
depth: number;
id: string;
path: Path;
title: string;
type: string;
depth?: number;
}

export interface TocSideBarProps {
Expand Down
4 changes: 2 additions & 2 deletions packages/heading/src/utils/getHeadingList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
getNodeString,
} from '@udecode/plate-common';

import type { HeadingList } from '../toc';
import type { Heading } from '../toc';

import {
ELEMENT_H1,
Expand All @@ -27,7 +27,7 @@ const headingDepth: Record<string, number> = {
};

export const getHeadingList = (editor: PlateEditor) => {
const headingList: HeadingList[] = [];
const headingList: Heading[] = [];

const values = getNodeEntries(editor, {
at: [],
Expand Down
18 changes: 9 additions & 9 deletions packages/heading/src/utils/heightToTop.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import type React from 'react';

export const heightToTop = (
ele: Element | HTMLElement,
ele: HTMLElement,
editorContentRef?: React.RefObject<HTMLDivElement>
) => {
// ele为指定跳转到该位置的DOM节点
const root = editorContentRef ? editorContentRef.current : document.body;
let height = 0;

do {
height += (ele as HTMLElement).offsetTop;
// eslint-disable-next-line no-param-reassign
ele = (ele as HTMLElement).offsetParent as unknown as Element;
} while (ele !== root);
if (!root || !ele) return 0;

return height;
const containerRect = root.getBoundingClientRect();
const elementRect = ele.getBoundingClientRect();

const scrollY = root.scrollTop;
const absoluteElementTop = elementRect.top + scrollY - containerRect.top;

return absoluteElementTop;
};

0 comments on commit 384b890

Please sign in to comment.