mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-21 01:53:26 +00:00
Merge branch 'main' into brett/RND-2311-openapi-enum
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'gitbook': patch
|
||||
---
|
||||
|
||||
Fix GitBook specific static assets not being served correctly when deployed on Cloudflare
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'gitbook': patch
|
||||
---
|
||||
|
||||
Automatically scroll to active item in TOC
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"version": 1,
|
||||
"exclude": ["/~gitbook/static/*"]
|
||||
}
|
||||
@@ -68,7 +68,7 @@
|
||||
"devDependencies": {
|
||||
"@argos-ci/playwright": "^2.0.0",
|
||||
"@playwright/test": "^1.42.1",
|
||||
"@cloudflare/next-on-pages": "^1.12.1",
|
||||
"@cloudflare/next-on-pages": "^1.13.3",
|
||||
"@cloudflare/workers-types": "^4.20240725.0",
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
"@types/jsontoxml": "^1.0.5",
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# GitBook immutable static assets
|
||||
/~gitbook/static/*
|
||||
cache-control: public,max-age=31536000,immutable
|
||||
@@ -0,0 +1,70 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { ClassValue, tcls } from '@/lib/tailwind';
|
||||
|
||||
const TOCScrollContainerRefContext = React.createContext<React.RefObject<HTMLDivElement> | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
function useTOCScrollContainerRefContext() {
|
||||
const ctx = React.useContext(TOCScrollContainerRefContext);
|
||||
if (!ctx) {
|
||||
throw new Error('Context `TOCScrollContainerRefContext` must be used within Provider');
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
export function TOCScrollContainer(props: { children: React.ReactNode; className?: ClassValue }) {
|
||||
const { children, className } = props;
|
||||
const scrollContainerRef = React.createRef<HTMLDivElement>();
|
||||
|
||||
return (
|
||||
<TOCScrollContainerRefContext.Provider value={scrollContainerRef}>
|
||||
<div ref={scrollContainerRef} className={tcls(className)}>
|
||||
{children}
|
||||
</div>
|
||||
</TOCScrollContainerRefContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
// Offset to scroll the table of contents item by.
|
||||
const TOC_ITEM_OFFSET = 200;
|
||||
|
||||
/**
|
||||
* Scrolls the table of contents container to the page item when it becomes active
|
||||
*/
|
||||
export function useScrollToActiveTOCItem(props: {
|
||||
isActive: boolean;
|
||||
linkRef: React.RefObject<HTMLAnchorElement>;
|
||||
}) {
|
||||
const { isActive, linkRef } = props;
|
||||
const scrollContainerRef = useTOCScrollContainerRefContext();
|
||||
const isScrolled = React.useRef(false);
|
||||
React.useLayoutEffect(() => {
|
||||
if (!isActive) {
|
||||
isScrolled.current = false;
|
||||
return;
|
||||
}
|
||||
if (isScrolled.current) {
|
||||
return;
|
||||
}
|
||||
const tocItem = linkRef.current;
|
||||
const tocContainer = scrollContainerRef.current;
|
||||
if (!tocItem || !tocContainer || !isOutOfView(tocItem, tocContainer)) {
|
||||
return;
|
||||
}
|
||||
tocContainer?.scrollTo({
|
||||
top: tocItem.offsetTop - TOC_ITEM_OFFSET,
|
||||
});
|
||||
isScrolled.current = true;
|
||||
}, [isActive, linkRef, scrollContainerRef]);
|
||||
}
|
||||
|
||||
function isOutOfView(tocItem: HTMLElement, tocContainer: HTMLElement) {
|
||||
const tocItemTop = tocItem.offsetTop;
|
||||
const containerTop = tocContainer.scrollTop;
|
||||
const containerBottom = containerTop + tocContainer.clientHeight;
|
||||
return tocItemTop < containerTop || tocItemTop > containerBottom;
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import { ContentRefContext } from '@/lib/references';
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
import { PagesList } from './PagesList';
|
||||
import { TOCScrollContainer } from './TOCScroller';
|
||||
import { Trademark } from './Trademark';
|
||||
|
||||
export function TableOfContents(props: {
|
||||
@@ -55,7 +56,7 @@ export function TableOfContents(props: {
|
||||
)}
|
||||
>
|
||||
{header ? header : null}
|
||||
<div
|
||||
<TOCScrollContainer
|
||||
className={tcls(
|
||||
withHeaderOffset ? 'pt-4' : ['pt-4', 'lg:pt-0'],
|
||||
'hidden',
|
||||
@@ -87,7 +88,7 @@ export function TableOfContents(props: {
|
||||
{customization.trademark.enabled ? (
|
||||
<Trademark space={space} customization={customization} />
|
||||
) : null}
|
||||
</div>
|
||||
</TOCScrollContainer>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import React from 'react';
|
||||
|
||||
import { tcls } from '@/lib/tailwind';
|
||||
|
||||
import { useScrollToActiveTOCItem } from './TOCScroller';
|
||||
import { useIsMounted } from '../hooks';
|
||||
import { Link } from '../primitives';
|
||||
|
||||
const show = {
|
||||
@@ -46,6 +48,7 @@ export function ToggleableLinkItem(props: {
|
||||
|
||||
const [scope, animate] = useAnimate();
|
||||
const [isVisible, setIsVisible] = React.useState(hasActiveDescendant);
|
||||
const isMounted = useIsMounted();
|
||||
|
||||
// Update the visibility of the children, if we are navigating to a descendant.
|
||||
React.useEffect(() => {
|
||||
@@ -59,10 +62,9 @@ export function ToggleableLinkItem(props: {
|
||||
// Animate the visibility of the children
|
||||
// only after the initial state.
|
||||
React.useEffect(() => {
|
||||
if (!mountedRef.current || !hasDescendants) {
|
||||
if (!isMounted || !hasDescendants) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
animate(scope.current, isVisible ? show : hide, {
|
||||
duration: 0.1,
|
||||
@@ -84,17 +86,15 @@ export function ToggleableLinkItem(props: {
|
||||
// The selector can crash in some browsers, we ignore it as the animation is not critical.
|
||||
console.error(error);
|
||||
}
|
||||
}, [isVisible, hasDescendants, animate, scope]);
|
||||
}, [isVisible, isMounted, hasDescendants, animate, scope]);
|
||||
|
||||
// Track if the component is mounted.
|
||||
const mountedRef = React.useRef(false);
|
||||
React.useEffect(() => {
|
||||
mountedRef.current = true;
|
||||
}, []);
|
||||
const linkRef = React.createRef<HTMLAnchorElement>();
|
||||
useScrollToActiveTOCItem({ linkRef, isActive });
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Link
|
||||
ref={linkRef}
|
||||
href={href}
|
||||
aria-selected={isActive}
|
||||
className={tcls(
|
||||
|
||||
Reference in New Issue
Block a user