diff --git a/packages/gitbook/src/components/AIChat/AIChat.tsx b/packages/gitbook/src/components/AIChat/AIChat.tsx index d36ebe0e9..a5470d61d 100644 --- a/packages/gitbook/src/components/AIChat/AIChat.tsx +++ b/packages/gitbook/src/components/AIChat/AIChat.tsx @@ -83,7 +83,7 @@ export function AIChat() { data-testid="ai-chat" withScrim={true} className={tcls( - 'ai-chat mx-auto not-hydrated:hidden w-96 max-w-full pl-8 transition-[width] duration-300 ease-quint lg:max-xl:w-80' + 'ai-chat mx-auto ml-8 not-hydrated:hidden w-96 transition-[width] duration-300 ease-quint lg:max-xl:w-80' )} > @@ -226,7 +226,7 @@ export function AIChatBody(props: { contentClassName="p-4 gutter-stable flex flex-col gap-4" orientation="vertical" fadeEdges={['leading']} - active={`message-group-${chat.messages.filter((message) => message.role === 'user').length - 1}`} + active={`#message-group-${chat.messages.filter((message) => message.role === 'user').length - 1}`} > {isEmpty ? (
diff --git a/packages/gitbook/src/components/SiteSections/SiteSectionList.tsx b/packages/gitbook/src/components/SiteSections/SiteSectionList.tsx index 5254a7606..1b68da643 100644 --- a/packages/gitbook/src/components/SiteSections/SiteSectionList.tsx +++ b/packages/gitbook/src/components/SiteSections/SiteSectionList.tsx @@ -40,7 +40,7 @@ export function SiteSectionList(props: { sections: ClientSiteSections; className orientation="vertical" style={{ maxHeight: `${MAX_ITEMS * 3 + 2}rem` }} className="pb-4" - active={currentSection.id} + active={`#${currentSection.id}`} >
{sectionsAndGroups.map((item) => { diff --git a/packages/gitbook/src/components/SiteSections/SiteSectionTabs.tsx b/packages/gitbook/src/components/SiteSections/SiteSectionTabs.tsx index e176a3f82..ede6ca5b1 100644 --- a/packages/gitbook/src/components/SiteSections/SiteSectionTabs.tsx +++ b/packages/gitbook/src/components/SiteSections/SiteSectionTabs.tsx @@ -76,7 +76,7 @@ export function SiteSectionTabs(props: { ? 'md:-mr-8 -mr-4 sm:-mr-6' : 'after:contents[] after:absolute after:inset-y-2 after:right-0 after:border-transparent after:border-r after:transition-colors' )} - active={currentSection.id} + active={`#${currentSection.id}`} trailingEdgeScrollClassName={children ? 'after:border-tint' : ''} > void) => () => void; -} - -const TOCScrollContainerContext = React.createContext(null); - -function useTOCScrollContainerContext() { - const ctx = React.useContext(TOCScrollContainerContext); - assert(ctx); - return ctx; -} - -/** - * Table of contents scroll container. - */ -export function TOCScrollContainer(props: ComponentPropsWithoutRef<'div'>) { - const ref = useRef(null); - const listeners = useRef<((element: HTMLDivElement) => void)[]>([]); - const getContainer: TOCScrollContainerContextType['getContainer'] = useCallback((listener) => { - if (ref.current) { - listener(ref.current); - return () => {}; - } - listeners.current.push(listener); - return () => { - listeners.current = listeners.current.filter((l) => l !== listener); - }; - }, []); - const value: TOCScrollContainerContextType = useMemo(() => ({ getContainer }), [getContainer]); - useEffect(() => { - const element = ref.current; - if (!element) { - return; - } - listeners.current.forEach((listener) => listener(element)); - return () => { - listeners.current = []; - }; - }, []); - - return ( - -
- - ); -} - -// Offset to scroll the table of contents item by. -const TOC_ITEM_OFFSET = 100; - -/** - * Scrolls the table of contents container to the page item when it's initially active. - */ -export function useScrollToActiveTOCItem(props: { - anchorRef: React.RefObject; - isActive: boolean; -}) { - const { isActive, anchorRef } = props; - const { getContainer } = useTOCScrollContainerContext(); - useEffect(() => { - const anchor = anchorRef.current; - if (isActive && anchor) { - return getContainer((container) => { - if (isOutOfView(anchor, container)) { - container.scrollTo({ top: anchor.offsetTop - TOC_ITEM_OFFSET }); - } - }); - } - }, [isActive, getContainer, anchorRef]); -} - -function isOutOfView(element: HTMLElement, container: HTMLElement) { - const tocItemTop = element.offsetTop; - const containerTop = container.scrollTop; - const containerBottom = containerTop + container.clientHeight; - return ( - tocItemTop < containerTop + TOC_ITEM_OFFSET || - tocItemTop > containerBottom - TOC_ITEM_OFFSET - ); -} diff --git a/packages/gitbook/src/components/TableOfContents/TableOfContents.tsx b/packages/gitbook/src/components/TableOfContents/TableOfContents.tsx index 85bce6158..4aaa978de 100644 --- a/packages/gitbook/src/components/TableOfContents/TableOfContents.tsx +++ b/packages/gitbook/src/components/TableOfContents/TableOfContents.tsx @@ -3,9 +3,9 @@ import { SiteInsightsTrademarkPlacement } from '@gitbook/api'; import type React from 'react'; import { tcls } from '@/lib/tailwind'; +import { ScrollContainer } from '../primitives/ScrollContainer'; import { SideSheet } from '../primitives/SideSheet'; import { PagesList } from './PagesList'; -import { TOCScrollContainer } from './TOCScroller'; import { TableOfContentsScript } from './TableOfContentsScript'; import { Trademark } from './Trademark'; import { encodeClientTableOfContents } from './encodeClientTableOfContents'; @@ -74,7 +74,7 @@ export async function TableOfContents(props: { 'lg:page-no-toc:[html[style*="--outline-top-offset"]_&]:top-(--outline-top-offset)!', 'lg:page-no-toc:[html[style*="--outline-height"]_&]:top-(--outline-height)!', - 'py-6', + 'pt-6 pb-4', 'lg:sidebar-filled:pr-6', 'lg:page-no-toc:pr-0', 'max-lg:pl-8', @@ -106,21 +106,24 @@ export async function TableOfContents(props: { )} > {innerHeader ? innerHeader : null} - - {customization.trademark.enabled ? ( - - ) : null} - + + {customization.trademark.enabled ? ( + + ) : null}
diff --git a/packages/gitbook/src/components/TableOfContents/ToggleableLinkItem.tsx b/packages/gitbook/src/components/TableOfContents/ToggleableLinkItem.tsx index 4d222da04..a907cd3dd 100644 --- a/packages/gitbook/src/components/TableOfContents/ToggleableLinkItem.tsx +++ b/packages/gitbook/src/components/TableOfContents/ToggleableLinkItem.tsx @@ -8,7 +8,6 @@ import { tcls } from '@/lib/tailwind'; import { useCurrentPagePath } from '../hooks'; import { Link, type LinkInsightsProps, type LinkProps } from '../primitives'; -import { useScrollToActiveTOCItem } from './TOCScroller'; /** * Client component for a page document to toggle its children and be marked as active. @@ -81,8 +80,6 @@ function LinkItem( } ) { const { isActive, href, insights, children, onActiveClick } = props; - const anchorRef = useRef(null); - useScrollToActiveTOCItem({ anchorRef, isActive }); const handleClick = (event: React.MouseEvent) => { if (isActive && onActiveClick) { @@ -93,7 +90,7 @@ function LinkItem( return ( +
); diff --git a/packages/gitbook/src/components/TableOfContents/index.ts b/packages/gitbook/src/components/TableOfContents/index.ts index 6eeff9269..03420d7a0 100644 --- a/packages/gitbook/src/components/TableOfContents/index.ts +++ b/packages/gitbook/src/components/TableOfContents/index.ts @@ -1,4 +1,3 @@ export { TableOfContents } from './TableOfContents'; export { PagesList } from './PagesList'; -export { TOCScrollContainer } from './TOCScroller'; export { Trademark } from './Trademark'; diff --git a/packages/gitbook/src/components/primitives/ScrollContainer.tsx b/packages/gitbook/src/components/primitives/ScrollContainer.tsx index 0e6051f29..aea4079a2 100644 --- a/packages/gitbook/src/components/primitives/ScrollContainer.tsx +++ b/packages/gitbook/src/components/primitives/ScrollContainer.tsx @@ -102,7 +102,9 @@ export function ScrollContainer(props: ScrollContainerProps) { return; } const activeItem = - typeof active === 'string' ? document.getElementById(active) : active.current; + typeof active === 'string' + ? containerRef.current?.querySelector(active) + : active.current; if (!activeItem || !container.contains(activeItem)) { return; } @@ -180,7 +182,7 @@ export function ScrollContainer(props: ScrollContainerProps) { orientation === 'horizontal' ? '-translate-y-1/2! top-1/2 left-0 ml-2' : '-translate-x-1/2! top-0 left-1/2 mt-2', - 'absolute not-pointer-none:block hidden scale-0 opacity-0 transition-[scale,opacity]', + 'absolute z-10 not-pointer-none:block hidden scale-0 opacity-0 transition-[scale,opacity]', scrollPosition > 0 ? 'not-pointer-none:group-hover/scroll-container:scale-100 not-pointer-none:group-hover/scroll-container:opacity-11' : 'pointer-events-none' @@ -199,7 +201,7 @@ export function ScrollContainer(props: ScrollContainerProps) { orientation === 'horizontal' ? '-translate-y-1/2! top-1/2 right-0 mr-2' : '-translate-x-1/2! bottom-0 left-1/2 mb-2', - 'absolute not-pointer-none:block hidden scale-0 transition-[scale,opacity]', + 'absolute z-10 not-pointer-none:block hidden scale-0 transition-[scale,opacity]', scrollPosition < scrollSize ? 'not-pointer-none:group-hover/scroll-container:scale-100 not-pointer-none:group-hover/scroll-container:opacity-11' : 'pointer-events-none' @@ -214,7 +216,7 @@ export function ScrollContainer(props: ScrollContainerProps) { /** * Scroll to an element in a container. */ -function scrollToElementInContainer(element: HTMLElement, container: HTMLElement) { +function scrollToElementInContainer(element: Element, container: HTMLElement) { const containerRect = container.getBoundingClientRect(); const rect = element.getBoundingClientRect(); @@ -229,6 +231,6 @@ function scrollToElementInContainer(element: HTMLElement, container: HTMLElement (rect.left - containerRect.left) - container.clientWidth / 2 + rect.width / 2, - behavior: 'smooth', + behavior: 'auto', }); }