Merge branch 'main' into brett/RND-2311-openapi-enum

This commit is contained in:
Brett Jephson
2024-09-24 16:26:13 +01:00
committed by GitHub
9 changed files with 99 additions and 11 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'gitbook': patch
---
Fix GitBook specific static assets not being served correctly when deployed on Cloudflare
+5
View File
@@ -0,0 +1,5 @@
---
'gitbook': patch
---
Automatically scroll to active item in TOC
BIN
View File
Binary file not shown.
+4
View File
@@ -0,0 +1,4 @@
{
"version": 1,
"exclude": ["/~gitbook/static/*"]
}
+1 -1
View File
@@ -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",
+3
View File
@@ -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(