mirror of
https://github.com/facebook/docusaurus.git
synced 2025-07-26 21:18:47 +02:00
chore: clean up ESLint config, enable a few rules (#6514)
* chore: clean up ESLint config, enable a few rules * enable max-len for comments * fix build
This commit is contained in:
parent
b8ccb869f1
commit
aa446b7a9c
167 changed files with 1157 additions and 960 deletions
|
@ -67,7 +67,8 @@ function applyCollapsedStyle(el: HTMLElement, collapsed: boolean) {
|
|||
}
|
||||
|
||||
/*
|
||||
Lex111: Dynamic transition duration is used in Material design, this technique is good for a large number of items.
|
||||
Lex111: Dynamic transition duration is used in Material design, this technique
|
||||
is good for a large number of items.
|
||||
https://material.io/archive/guidelines/motion/duration-easing.html#duration-easing-dynamic-durations
|
||||
https://github.com/mui-org/material-ui/blob/e724d98eba018e55e1a684236a2037e24bcf050c/packages/material-ui/src/styles/createTransitions.js#L40-L43
|
||||
*/
|
||||
|
@ -151,7 +152,8 @@ type CollapsibleElementType = React.ElementType<
|
|||
Pick<React.HTMLAttributes<unknown>, 'className' | 'onTransitionEnd' | 'style'>
|
||||
>;
|
||||
|
||||
// Prevent hydration layout shift before animations are handled imperatively with JS
|
||||
// Prevent hydration layout shift before animations are handled imperatively
|
||||
// with JS
|
||||
function getSSRStyle(collapsed: boolean) {
|
||||
if (ExecutionEnvironment.canUseDOM) {
|
||||
return undefined;
|
||||
|
@ -167,8 +169,9 @@ type CollapsibleBaseProps = {
|
|||
onCollapseTransitionEnd?: (collapsed: boolean) => void;
|
||||
className?: string;
|
||||
|
||||
// This is mostly useful for details/summary component where ssrStyle is not needed (as details are hidden natively)
|
||||
// and can mess-up with the default native behavior of the browser when JS fails to load or is disabled
|
||||
// This is mostly useful for details/summary component where ssrStyle is not
|
||||
// needed (as details are hidden natively) and can mess up with the default
|
||||
// native behavior of the browser when JS fails to load or is disabled
|
||||
disableSSRStyle?: boolean;
|
||||
};
|
||||
|
||||
|
@ -189,7 +192,8 @@ function CollapsibleBase({
|
|||
|
||||
return (
|
||||
<As
|
||||
// @ts-expect-error: the "too complicated type" is produced from "CollapsibleElementType" being a huge union
|
||||
// @ts-expect-error: the "too complicated type" is produced from
|
||||
// "CollapsibleElementType" being a huge union
|
||||
ref={collapsibleRef}
|
||||
style={disableSSRStyle ? undefined : getSSRStyle(collapsed)}
|
||||
onTransitionEnd={(e: React.TransitionEvent) => {
|
||||
|
@ -215,7 +219,7 @@ function CollapsibleLazy({collapsed, ...props}: CollapsibleBaseProps) {
|
|||
}
|
||||
}, [collapsed]);
|
||||
|
||||
// lazyCollapsed updated in effect so that the first expansion transition can work
|
||||
// lazyCollapsed updated in effect so that first expansion transition can work
|
||||
const [lazyCollapsed, setLazyCollapsed] = useState(collapsed);
|
||||
useLayoutEffect(() => {
|
||||
if (mounted) {
|
||||
|
@ -229,9 +233,10 @@ function CollapsibleLazy({collapsed, ...props}: CollapsibleBaseProps) {
|
|||
}
|
||||
|
||||
type CollapsibleProps = CollapsibleBaseProps & {
|
||||
// Lazy allows to delay the rendering when collapsed => it will render children only after hydration, on first expansion
|
||||
// Required prop: it forces to think if content should be server-rendered or not!
|
||||
// This has perf impact on the SSR output and html file sizes
|
||||
// Lazy allows to delay the rendering when collapsed => it will render
|
||||
// children only after hydration, on first expansion
|
||||
// Required prop: it forces to think if content should be server-rendered
|
||||
// or not! This has perf impact on the SSR output and html file sizes
|
||||
// See https://github.com/facebook/docusaurus/issues/4753
|
||||
lazy: boolean;
|
||||
};
|
||||
|
|
|
@ -41,7 +41,7 @@ function Details({summary, children, ...props}: DetailsProps): JSX.Element {
|
|||
const {collapsed, setCollapsed} = useCollapsible({
|
||||
initialState: !props.open,
|
||||
});
|
||||
// We use a separate prop because it must be set only after animation completes
|
||||
// Use a separate prop because it must be set only after animation completes
|
||||
// Otherwise close anim won't work
|
||||
const [open, setOpen] = useState(props.open);
|
||||
|
||||
|
|
|
@ -15,7 +15,8 @@ const windowSizes = {
|
|||
|
||||
// This "ssr" value is very important to handle hydration FOUC / layout shifts
|
||||
// You have to handle server-rendering explicitly on the call-site
|
||||
// On the server, you may need to render BOTH the mobile/desktop elements (and hide one of them with mediaquery)
|
||||
// On the server, you may need to render BOTH the mobile/desktop elements (and
|
||||
// hide one of them with mediaquery)
|
||||
// We don't return "undefined" on purpose, to make it more explicit
|
||||
ssr: 'ssr',
|
||||
} as const;
|
||||
|
@ -33,7 +34,8 @@ function getWindowSize() {
|
|||
: windowSizes.mobile;
|
||||
}
|
||||
|
||||
// Simulate the SSR window size in dev, so that potential hydration FOUC/layout shift problems can be seen in dev too!
|
||||
// Simulate the SSR window size in dev, so that potential hydration FOUC/layout
|
||||
// shift problems can be seen in dev too!
|
||||
const DevSimulateSSR = process.env.NODE_ENV === 'development' && true;
|
||||
|
||||
// This hook returns an enum value on purpose!
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
|
||||
// These class names are used to style page layouts in Docusaurus
|
||||
// Those are meant to be targeted by user-provided custom CSS selectors
|
||||
// /!\ Please do not modify the classnames! This is a breaking change, and annoying for users!
|
||||
// Please do not modify the classnames! This is a breaking change, and annoying
|
||||
// for users!
|
||||
export const ThemeClassNames = {
|
||||
page: {
|
||||
blogListPage: 'blog-list-page',
|
||||
|
|
|
@ -112,8 +112,9 @@ describe('filterTOC', () => {
|
|||
]);
|
||||
});
|
||||
|
||||
// It's not 100% clear exactly how the TOC should behave under weird heading levels provided by the user
|
||||
// Adding a test so that behavior stays the same over time
|
||||
// It's not 100% clear exactly how the TOC should behave under weird heading
|
||||
// levels provided by the user. Adding a test so that behavior stays the same
|
||||
// over time
|
||||
test('filter invalid heading levels (but possible) TOC', () => {
|
||||
const toc: TOCItem[] = [
|
||||
{
|
||||
|
|
|
@ -76,10 +76,9 @@ function readStorageState({
|
|||
);
|
||||
if (versionExists) {
|
||||
return {preferredVersionName: preferredVersionNameUnsafe};
|
||||
} else {
|
||||
DocsPreferredVersionStorage.clear(pluginId, versionPersistence);
|
||||
return {preferredVersionName: null};
|
||||
}
|
||||
DocsPreferredVersionStorage.clear(pluginId, versionPersistence);
|
||||
return {preferredVersionName: null};
|
||||
}
|
||||
|
||||
const initialState: DocsPreferredVersionState = {};
|
||||
|
@ -144,9 +143,8 @@ export function DocsPreferredVersionContextProvider({
|
|||
{children}
|
||||
</DocsPreferredVersionContextProviderUnsafe>
|
||||
);
|
||||
} else {
|
||||
return children;
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
||||
function DocsPreferredVersionContextProviderUnsafe({
|
||||
|
|
|
@ -20,7 +20,7 @@ import {useLocation} from '@docusaurus/router';
|
|||
// TODO not ideal, see also "useDocs"
|
||||
export const isDocsPluginEnabled: boolean = !!useAllDocsData;
|
||||
|
||||
// Using a Symbol because null is a valid context value (a doc can have no sidebar)
|
||||
// Using a Symbol because null is a valid context value (a doc with no sidebar)
|
||||
// Inspired by https://github.com/jamiebuilds/unstated-next/blob/master/src/unstated-next.tsx
|
||||
const EmptyContextValue: unique symbol = Symbol('EmptyContext');
|
||||
|
||||
|
@ -101,11 +101,10 @@ export function findSidebarCategory(
|
|||
if (item.type === 'category') {
|
||||
if (predicate(item)) {
|
||||
return item;
|
||||
} else {
|
||||
const subItem = findSidebarCategory(item.items, predicate);
|
||||
if (subItem) {
|
||||
return subItem;
|
||||
}
|
||||
}
|
||||
const subItem = findSidebarCategory(item.items, predicate);
|
||||
if (subItem) {
|
||||
return subItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,9 +11,10 @@ import type {Location, Action} from '@docusaurus/history';
|
|||
|
||||
type HistoryBlockHandler = (location: Location, action: Action) => void | false;
|
||||
|
||||
/*
|
||||
Permits to register a handler that will be called on history actions (pop,push,replace)
|
||||
If the handler returns false, the navigation transition will be blocked/cancelled
|
||||
/**
|
||||
* Permits to register a handler that will be called on history actions (pop,
|
||||
* push, replace) If the handler returns false, the navigation transition will
|
||||
* be blocked/cancelled
|
||||
*/
|
||||
export function useHistoryActionHandler(handler: HistoryBlockHandler): void {
|
||||
const {block} = useHistory();
|
||||
|
@ -32,11 +33,11 @@ export function useHistoryActionHandler(handler: HistoryBlockHandler): void {
|
|||
);
|
||||
}
|
||||
|
||||
/*
|
||||
Permits to register a handler that will be called on history pop navigation (backward/forward)
|
||||
If the handler returns false, the backward/forward transition will be blocked
|
||||
|
||||
Unfortunately there's no good way to detect the "direction" (backward/forward) of the POP event.
|
||||
/**
|
||||
* Permits to register a handler that will be called on history pop navigation
|
||||
* (backward/forward) If the handler returns false, the backward/forward
|
||||
* transition will be blocked. Unfortunately there's no good way to detect the
|
||||
* "direction" (backward/forward) of the POP event.
|
||||
*/
|
||||
export function useHistoryPopHandler(handler: HistoryBlockHandler): void {
|
||||
useHistoryActionHandler((location, action) => {
|
||||
|
|
|
@ -10,8 +10,11 @@
|
|||
/**
|
||||
* Gets the duplicate values in an array.
|
||||
* @param arr The array.
|
||||
* @param comparator Compares two values and returns `true` if they are equal (duplicated).
|
||||
* @returns Value of the elements `v` that have a preceding element `u` where `comparator(u, v) === true`. Values within the returned array are not guaranteed to be unique.
|
||||
* @param comparator Compares two values and returns `true` if they are equal
|
||||
* (duplicated).
|
||||
* @returns Value of the elements `v` that have a preceding element `u` where
|
||||
* `comparator(u, v) === true`. Values within the returned array are not
|
||||
* guaranteed to be unique.
|
||||
*/
|
||||
export function duplicates<T>(
|
||||
arr: readonly T[],
|
||||
|
|
|
@ -16,12 +16,13 @@ import React, {
|
|||
} from 'react';
|
||||
|
||||
/*
|
||||
The idea behind all this is that a specific component must be able to fill a placeholder in the generic layout
|
||||
The doc page should be able to fill the secondary menu of the main mobile navbar.
|
||||
This permits to reduce coupling between the main layout and the specific page.
|
||||
The idea behind all this is that a specific component must be able to fill a
|
||||
placeholder in the generic layout. The doc page should be able to fill the
|
||||
secondary menu of the main mobile navbar. This permits to reduce coupling
|
||||
between the main layout and the specific page.
|
||||
|
||||
This kind of feature is often called portal/teleport/gateway... various unmaintained React libs exist
|
||||
Most up-to-date one: https://github.com/gregberge/react-teleporter
|
||||
This kind of feature is often called portal/teleport/gateway... various
|
||||
unmaintained React libs exist. Most up-to-date one: https://github.com/gregberge/react-teleporter
|
||||
Not sure any of those is safe regarding concurrent mode.
|
||||
*/
|
||||
|
||||
|
|
|
@ -7,19 +7,27 @@
|
|||
|
||||
import {useCallback, useEffect, useLayoutEffect, useRef} from 'react';
|
||||
|
||||
// This hook is like useLayoutEffect, but without the SSR warning
|
||||
// It seems hacky but it's used in many React libs (Redux, Formik...)
|
||||
// Also mentioned here: https://github.com/facebook/react/issues/16956
|
||||
// It is useful when you need to update a ref as soon as possible after a React render (before useEffect)
|
||||
/**
|
||||
* This hook is like useLayoutEffect, but without the SSR warning
|
||||
* It seems hacky but it's used in many React libs (Redux, Formik...)
|
||||
* Also mentioned here: https://github.com/facebook/react/issues/16956
|
||||
* It is useful when you need to update a ref as soon as possible after a React
|
||||
* render (before `useEffect`)
|
||||
*/
|
||||
export const useIsomorphicLayoutEffect =
|
||||
typeof window !== 'undefined' ? useLayoutEffect : useEffect;
|
||||
|
||||
// Permits to transform an unstable callback (like an arrow function provided as props)
|
||||
// to a "stable" callback that is safe to use in a useEffect dependency array
|
||||
// Useful to avoid React stale closure problems + avoid useless effect re-executions
|
||||
//
|
||||
// Workaround until the React team recommends a good solution, see https://github.com/facebook/react/issues/16956
|
||||
// This generally works has some potential drawbacks, such as https://github.com/facebook/react/issues/16956#issuecomment-536636418
|
||||
/**
|
||||
* Permits to transform an unstable callback (like an arrow function provided as
|
||||
* props) to a "stable" callback that is safe to use in a useEffect dependency
|
||||
* array. Useful to avoid React stale closure problems + avoid useless effect
|
||||
* re-executions
|
||||
*
|
||||
* Workaround until the React team recommends a good solution, see
|
||||
* https://github.com/facebook/react/issues/16956
|
||||
* This generally works but has some potential drawbacks, such as
|
||||
* https://github.com/facebook/react/issues/16956#issuecomment-536636418
|
||||
*/
|
||||
export function useDynamicCallback<T extends (...args: never[]) => unknown>(
|
||||
callback: T,
|
||||
): T {
|
||||
|
@ -29,6 +37,7 @@ export function useDynamicCallback<T extends (...args: never[]) => unknown>(
|
|||
ref.current = callback;
|
||||
}, [callback]);
|
||||
|
||||
// @ts-expect-error: TS is right that this callback may be a supertype of T, but good enough for our use
|
||||
// @ts-expect-error: TS is right that this callback may be a supertype of T,
|
||||
// but good enough for our use
|
||||
return useCallback<T>((...args) => ref.current(...args), []);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* Utility to convert an optional string into a Regex case insensitive and global
|
||||
* Converts an optional string into a Regex case insensitive and global
|
||||
*/
|
||||
export function isRegexpStringMatch(
|
||||
regexAsString?: string,
|
||||
|
|
|
@ -11,8 +11,8 @@ export type StorageType = typeof StorageTypes[number];
|
|||
|
||||
const DefaultStorageType: StorageType = 'localStorage';
|
||||
|
||||
// Will return null browser storage is unavailable (like running Docusaurus in iframe)
|
||||
// See https://github.com/facebook/docusaurus/pull/4501
|
||||
// Will return null browser storage is unavailable (like running Docusaurus in
|
||||
// iframe) See https://github.com/facebook/docusaurus/pull/4501
|
||||
function getBrowserStorage(
|
||||
storageType: StorageType = DefaultStorageType,
|
||||
): Storage | null {
|
||||
|
@ -23,13 +23,12 @@ function getBrowserStorage(
|
|||
}
|
||||
if (storageType === 'none') {
|
||||
return null;
|
||||
} else {
|
||||
try {
|
||||
return window[storageType];
|
||||
} catch (e) {
|
||||
logOnceBrowserStorageNotAvailableWarning(e as Error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
try {
|
||||
return window[storageType];
|
||||
} catch (e) {
|
||||
logOnceBrowserStorageNotAvailableWarning(e as Error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,10 +78,10 @@ Please only call storage APIs in effects and event handlers.`);
|
|||
|
||||
/**
|
||||
* Creates an object for accessing a particular key in localStorage.
|
||||
* The API is fail-safe, and usage of browser storage should be considered unreliable
|
||||
* Local storage might simply be unavailable (iframe + browser security) or operations might fail individually
|
||||
* Please assume that using this API can be a NO-OP
|
||||
* See also https://github.com/facebook/docusaurus/issues/6036
|
||||
* The API is fail-safe, and usage of browser storage should be considered
|
||||
* unreliable. Local storage might simply be unavailable (iframe + browser
|
||||
* security) or operations might fail individually. Please assume that using
|
||||
* this API can be a NO-OP. See also https://github.com/facebook/docusaurus/issues/6036
|
||||
*/
|
||||
export const createStorageSlot = (
|
||||
key: string,
|
||||
|
|
|
@ -36,9 +36,8 @@ export function filterTOC({
|
|||
children: filteredChildren,
|
||||
},
|
||||
];
|
||||
} else {
|
||||
return filteredChildren;
|
||||
}
|
||||
return filteredChildren;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -39,14 +39,15 @@ export function useAlternatePageUtils(): {
|
|||
: `${baseUrlUnlocalized}${locale}/`;
|
||||
}
|
||||
|
||||
// TODO support correct alternate url when localized site is deployed on another domain
|
||||
// TODO support correct alternate url when localized site is deployed on
|
||||
// another domain
|
||||
function createUrl({
|
||||
locale,
|
||||
fullyQualified,
|
||||
}: {
|
||||
locale: string;
|
||||
// For hreflang SEO headers, we need it to be fully qualified (full protocol/domain/path...)
|
||||
// For locale dropdown, using a path is good enough
|
||||
// For hreflang SEO headers, we need it to be fully qualified (full
|
||||
// protocol/domain/path...) or locale dropdown, using a path is good enough
|
||||
fullyQualified: boolean;
|
||||
}) {
|
||||
return `${fullyQualified ? url : ''}${getLocalizedBaseUrl(
|
||||
|
|
|
@ -18,8 +18,8 @@ export type useContextualSearchFiltersReturns = {
|
|||
tags: string[];
|
||||
};
|
||||
|
||||
// We may want to support multiple search engines, don't couple that to Algolia/DocSearch
|
||||
// Maybe users will want to use its own search engine solution
|
||||
// We may want to support multiple search engines, don't couple that to
|
||||
// Algolia/DocSearch. Maybe users want to use their own search engine solution
|
||||
export function useContextualSearchFilters(): useContextualSearchFiltersReturns {
|
||||
const {i18n} = useDocusaurusContext();
|
||||
const allDocsData = useAllDocsData();
|
||||
|
|
|
@ -49,23 +49,29 @@ function createLocalePluralForms(locale: string): LocalePluralForms {
|
|||
};
|
||||
}
|
||||
|
||||
// Poor man's PluralSelector implementation, using an english fallback.
|
||||
// We want a lightweight, future-proof and good-enough solution.
|
||||
// We don't want a perfect and heavy solution.
|
||||
//
|
||||
// Docusaurus classic theme has only 2 deeply nested labels requiring complex plural rules
|
||||
// We don't want to use Intl + PluralRules polyfills + full ICU syntax (react-intl) just for that.
|
||||
//
|
||||
// Notes:
|
||||
// - 2021: 92+% Browsers support Intl.PluralRules, and support will increase in the future
|
||||
// - NodeJS >= 13 has full ICU support by default
|
||||
// - In case of "mismatch" between SSR and Browser ICU support, React keeps working!
|
||||
/**
|
||||
* Poor man's PluralSelector implementation, using an english fallback. We want
|
||||
* a lightweight, future-proof and good-enough solution. We don't want a perfect
|
||||
* and heavy solution.
|
||||
*
|
||||
* Docusaurus classic theme has only 2 deeply nested labels requiring complex
|
||||
* plural rules. We don't want to use Intl + PluralRules polyfills + full ICU
|
||||
* syntax (react-intl) just for that.
|
||||
*
|
||||
* Notes:
|
||||
* - 2021: 92+% Browsers support Intl.PluralRules, and support will increase in
|
||||
* the future
|
||||
* - NodeJS >= 13 has full ICU support by default
|
||||
* - In case of "mismatch" between SSR and Browser ICU support, React keeps
|
||||
* working!
|
||||
*/
|
||||
function useLocalePluralForms(): LocalePluralForms {
|
||||
const {
|
||||
i18n: {currentLocale},
|
||||
} = useDocusaurusContext();
|
||||
return useMemo(() => {
|
||||
// @ts-expect-error checking Intl.PluralRules in case browser doesn't have it (e.g Safari 12-)
|
||||
// @ts-expect-error checking Intl.PluralRules in case browser doesn't
|
||||
// have it (e.g Safari 12-)
|
||||
if (Intl.PluralRules) {
|
||||
try {
|
||||
return createLocalePluralForms(currentLocale);
|
||||
|
@ -94,17 +100,17 @@ function selectPluralMessage(
|
|||
|
||||
if (parts.length === 1) {
|
||||
return parts[0];
|
||||
} else {
|
||||
if (parts.length > localePluralForms.pluralForms.length) {
|
||||
console.error(
|
||||
`For locale=${localePluralForms.locale}, a maximum of ${localePluralForms.pluralForms.length} plural forms are expected (${localePluralForms.pluralForms}), but the message contains ${parts.length} plural forms: ${pluralMessages} `,
|
||||
);
|
||||
}
|
||||
const pluralForm = localePluralForms.select(count);
|
||||
const pluralFormIndex = localePluralForms.pluralForms.indexOf(pluralForm);
|
||||
// In case of not enough plural form messages, we take the last one (other) instead of returning undefined
|
||||
return parts[Math.min(pluralFormIndex, parts.length - 1)];
|
||||
}
|
||||
if (parts.length > localePluralForms.pluralForms.length) {
|
||||
console.error(
|
||||
`For locale=${localePluralForms.locale}, a maximum of ${localePluralForms.pluralForms.length} plural forms are expected (${localePluralForms.pluralForms}), but the message contains ${parts.length} plural forms: ${pluralMessages} `,
|
||||
);
|
||||
}
|
||||
const pluralForm = localePluralForms.select(count);
|
||||
const pluralFormIndex = localePluralForms.pluralForms.indexOf(pluralForm);
|
||||
// In case of not enough plural form messages, we take the last one (other)
|
||||
// instead of returning undefined
|
||||
return parts[Math.min(pluralFormIndex, parts.length - 1)];
|
||||
}
|
||||
|
||||
export function usePluralForm(): {
|
||||
|
|
|
@ -13,7 +13,8 @@ TODO make the hardcoded theme-classic classnames configurable
|
|||
(or add them to ThemeClassNames?)
|
||||
*/
|
||||
|
||||
// If the anchor has no height and is just a "marker" in the dom; we'll use the parent (normally the link text) rect boundaries instead
|
||||
// If the anchor has no height and is just a "marker" in the dom; we'll use the
|
||||
// parent (normally the link text) rect boundaries instead
|
||||
function getVisibleBoundingClientRect(element: HTMLElement): DOMRect {
|
||||
const rect = element.getBoundingClientRect();
|
||||
const hasNoHeight = rect.top === rect.bottom;
|
||||
|
@ -23,8 +24,10 @@ function getVisibleBoundingClientRect(element: HTMLElement): DOMRect {
|
|||
return rect;
|
||||
}
|
||||
|
||||
// Considering we divide viewport into 2 zones of each 50vh
|
||||
// This returns true if an element is in the first zone (ie, appear in viewport, near the top)
|
||||
/**
|
||||
* Considering we divide viewport into 2 zones of each 50vh, this returns true
|
||||
* if an element is in the first zone (ie, appear in viewport, near the top)
|
||||
*/
|
||||
function isInViewportTopHalf(boundingRect: DOMRect) {
|
||||
return boundingRect.top > 0 && boundingRect.bottom < window.innerHeight / 2;
|
||||
}
|
||||
|
@ -54,9 +57,10 @@ function getActiveAnchor(
|
|||
anchorTopOffset: number;
|
||||
},
|
||||
): Element | null {
|
||||
// Naming is hard
|
||||
// The "nextVisibleAnchor" is the first anchor that appear under the viewport top boundary
|
||||
// Note: it does not mean this anchor is visible yet, but if user continues scrolling down, it will be the first to become visible
|
||||
// Naming is hard: The "nextVisibleAnchor" is the first anchor that appear
|
||||
// under the viewport top boundary. It does not mean this anchor is visible
|
||||
// yet, but if user continues scrolling down, it will be the first to become
|
||||
// visible
|
||||
const nextVisibleAnchor = anchors.find((anchor) => {
|
||||
const boundingRect = getVisibleBoundingClientRect(anchor);
|
||||
return boundingRect.top >= anchorTopOffset;
|
||||
|
@ -64,23 +68,22 @@ function getActiveAnchor(
|
|||
|
||||
if (nextVisibleAnchor) {
|
||||
const boundingRect = getVisibleBoundingClientRect(nextVisibleAnchor);
|
||||
// If anchor is in the top half of the viewport: it is the one we consider "active"
|
||||
// (unless it's too close to the top and and soon to be scrolled outside viewport)
|
||||
// If anchor is in the top half of the viewport: it is the one we consider
|
||||
// "active" (unless it's too close to the top and and soon to be scrolled
|
||||
// outside viewport)
|
||||
if (isInViewportTopHalf(boundingRect)) {
|
||||
return nextVisibleAnchor;
|
||||
}
|
||||
// If anchor is in the bottom half of the viewport, or under the viewport, we consider the active anchor is the previous one
|
||||
// This is because the main text appearing in the user screen mostly belong to the previous anchor
|
||||
else {
|
||||
// Returns null for the first anchor, see https://github.com/facebook/docusaurus/issues/5318
|
||||
return anchors[anchors.indexOf(nextVisibleAnchor) - 1] ?? null;
|
||||
}
|
||||
// If anchor is in the bottom half of the viewport, or under the viewport,
|
||||
// we consider the active anchor is the previous one. This is because the
|
||||
// main text appearing in the user screen mostly belong to the previous
|
||||
// anchor. Returns null for the first anchor, see
|
||||
// https://github.com/facebook/docusaurus/issues/5318
|
||||
return anchors[anchors.indexOf(nextVisibleAnchor) - 1] ?? null;
|
||||
}
|
||||
// no anchor under viewport top? (ie we are at the bottom of the page)
|
||||
// => highlight the last anchor found
|
||||
else {
|
||||
return anchors[anchors.length - 1];
|
||||
}
|
||||
return anchors[anchors.length - 1];
|
||||
}
|
||||
|
||||
function getLinkAnchorValue(link: HTMLAnchorElement): string {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue