@docusaurus/theme-common 2.0.0-beta.ff31de0ff → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/Collapsible/index.d.ts +64 -0
- package/lib/components/Collapsible/index.d.ts.map +1 -0
- package/lib/components/Collapsible/index.js +150 -0
- package/lib/components/Collapsible/index.js.map +1 -0
- package/lib/components/Details/index.d.ts +17 -0
- package/lib/components/Details/index.d.ts.map +1 -0
- package/lib/components/Details/index.js +76 -0
- package/lib/components/Details/index.js.map +1 -0
- package/lib/components/Details/styles.module.css +62 -0
- package/lib/contexts/announcementBar.d.ts +22 -0
- package/lib/contexts/announcementBar.d.ts.map +1 -0
- package/lib/contexts/announcementBar.js +72 -0
- package/lib/contexts/announcementBar.js.map +1 -0
- package/lib/contexts/blogPost.d.ts +33 -0
- package/lib/contexts/blogPost.d.ts.map +1 -0
- package/lib/contexts/blogPost.js +46 -0
- package/lib/contexts/blogPost.js.map +1 -0
- package/lib/contexts/colorMode.d.ts +27 -0
- package/lib/contexts/colorMode.d.ts.map +1 -0
- package/lib/contexts/colorMode.js +132 -0
- package/lib/contexts/colorMode.js.map +1 -0
- package/lib/contexts/doc.d.ts +30 -0
- package/lib/contexts/doc.d.ts.map +1 -0
- package/lib/contexts/doc.js +48 -0
- package/lib/contexts/doc.js.map +1 -0
- package/lib/contexts/docSidebarItemsExpandedState.d.ts +31 -0
- package/lib/contexts/docSidebarItemsExpandedState.d.ts.map +1 -0
- package/lib/contexts/docSidebarItemsExpandedState.js +28 -0
- package/lib/contexts/docSidebarItemsExpandedState.js.map +1 -0
- package/lib/contexts/docsPreferredVersion.d.ts +30 -0
- package/lib/contexts/docsPreferredVersion.d.ts.map +1 -0
- package/lib/contexts/docsPreferredVersion.js +130 -0
- package/lib/contexts/docsPreferredVersion.js.map +1 -0
- package/lib/contexts/docsSidebar.d.ts +26 -0
- package/lib/contexts/docsSidebar.d.ts.map +1 -0
- package/lib/contexts/docsSidebar.js +30 -0
- package/lib/contexts/docsSidebar.js.map +1 -0
- package/lib/contexts/docsVersion.d.ts +20 -0
- package/lib/contexts/docsVersion.d.ts.map +1 -0
- package/lib/contexts/docsVersion.js +26 -0
- package/lib/contexts/docsVersion.js.map +1 -0
- package/lib/contexts/navbarMobileSidebar.d.ts +31 -0
- package/lib/contexts/navbarMobileSidebar.d.ts.map +1 -0
- package/lib/contexts/navbarMobileSidebar.js +56 -0
- package/lib/contexts/navbarMobileSidebar.js.map +1 -0
- package/lib/contexts/navbarSecondaryMenu/content.d.ts +37 -0
- package/lib/contexts/navbarSecondaryMenu/content.d.ts.map +1 -0
- package/lib/contexts/navbarSecondaryMenu/content.js +50 -0
- package/lib/contexts/navbarSecondaryMenu/content.js.map +1 -0
- package/lib/contexts/navbarSecondaryMenu/display.d.ts +24 -0
- package/lib/contexts/navbarSecondaryMenu/display.d.ts.map +1 -0
- package/lib/contexts/navbarSecondaryMenu/display.js +62 -0
- package/lib/contexts/navbarSecondaryMenu/display.js.map +1 -0
- package/lib/contexts/tabGroupChoice.d.ts +21 -0
- package/lib/contexts/tabGroupChoice.d.ts.map +1 -0
- package/lib/contexts/tabGroupChoice.js +49 -0
- package/lib/contexts/tabGroupChoice.js.map +1 -0
- package/lib/{utils/useChangeRoute.d.ts → hooks/styles.css} +4 -1
- package/lib/hooks/useBackToTopButton.d.ts +27 -0
- package/lib/hooks/useBackToTopButton.d.ts.map +1 -0
- package/lib/hooks/useBackToTopButton.js +50 -0
- package/lib/hooks/useBackToTopButton.js.map +1 -0
- package/lib/hooks/useCodeWordWrap.d.ts +14 -0
- package/lib/hooks/useCodeWordWrap.d.ts.map +1 -0
- package/lib/hooks/useCodeWordWrap.js +67 -0
- package/lib/hooks/useCodeWordWrap.js.map +1 -0
- package/lib/hooks/useHideableNavbar.d.ts +17 -0
- package/lib/hooks/useHideableNavbar.d.ts.map +1 -0
- package/lib/hooks/useHideableNavbar.js +60 -0
- package/lib/hooks/useHideableNavbar.js.map +1 -0
- package/lib/hooks/useKeyboardNavigation.d.ts +20 -0
- package/lib/hooks/useKeyboardNavigation.d.ts.map +1 -0
- package/lib/hooks/useKeyboardNavigation.js +39 -0
- package/lib/hooks/useKeyboardNavigation.js.map +1 -0
- package/lib/hooks/useLockBodyScroll.d.ts +12 -0
- package/lib/hooks/useLockBodyScroll.d.ts.map +1 -0
- package/lib/hooks/useLockBodyScroll.js +20 -0
- package/lib/hooks/useLockBodyScroll.js.map +1 -0
- package/lib/hooks/useMutationObserver.d.ts +4 -0
- package/lib/hooks/useMutationObserver.d.ts.map +1 -0
- package/lib/hooks/useMutationObserver.js +29 -0
- package/lib/hooks/useMutationObserver.js.map +1 -0
- package/lib/hooks/usePrismTheme.d.ts +13 -0
- package/lib/hooks/usePrismTheme.d.ts.map +1 -0
- package/lib/hooks/usePrismTheme.js +21 -0
- package/lib/hooks/usePrismTheme.js.map +1 -0
- package/lib/hooks/useSearchPage.d.ts +25 -0
- package/lib/hooks/useSearchPage.d.ts.map +1 -0
- package/lib/hooks/useSearchPage.js +43 -0
- package/lib/hooks/useSearchPage.js.map +1 -0
- package/lib/hooks/useSkipToContent.d.ts +25 -0
- package/lib/hooks/useSkipToContent.d.ts.map +1 -0
- package/lib/hooks/useSkipToContent.js +35 -0
- package/lib/hooks/useSkipToContent.js.map +1 -0
- package/lib/hooks/useTOCHighlight.d.ts +25 -0
- package/lib/hooks/useTOCHighlight.d.ts.map +1 -0
- package/lib/hooks/useTOCHighlight.js +130 -0
- package/lib/hooks/useTOCHighlight.js.map +1 -0
- package/lib/hooks/useWindowSize.d.ts +28 -0
- package/lib/hooks/useWindowSize.d.ts.map +1 -0
- package/lib/hooks/useWindowSize.js +59 -0
- package/lib/hooks/useWindowSize.js.map +1 -0
- package/lib/index.d.ts +16 -11
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +23 -10
- package/lib/index.js.map +1 -0
- package/lib/internal.d.ts +41 -0
- package/lib/internal.d.ts.map +1 -0
- package/lib/internal.js +52 -0
- package/lib/internal.js.map +1 -0
- package/lib/utils/ThemeClassNames.d.ts +49 -12
- package/lib/utils/ThemeClassNames.d.ts.map +1 -0
- package/lib/utils/ThemeClassNames.js +47 -4
- package/lib/utils/ThemeClassNames.js.map +1 -0
- package/lib/utils/codeBlockUtils.d.ts +63 -0
- package/lib/utils/codeBlockUtils.d.ts.map +1 -0
- package/lib/utils/codeBlockUtils.js +159 -3
- package/lib/utils/codeBlockUtils.js.map +1 -0
- package/lib/utils/docsUtils.d.ts +91 -0
- package/lib/utils/docsUtils.d.ts.map +1 -0
- package/lib/utils/docsUtils.js +217 -1
- package/lib/utils/docsUtils.js.map +1 -0
- package/lib/utils/footerUtils.d.ts +13 -0
- package/lib/utils/footerUtils.d.ts.map +1 -0
- package/lib/utils/footerUtils.js +14 -0
- package/lib/utils/footerUtils.js.map +1 -0
- package/lib/utils/generalUtils.d.ts +11 -1
- package/lib/utils/generalUtils.d.ts.map +1 -0
- package/lib/utils/generalUtils.js +9 -5
- package/lib/utils/generalUtils.js.map +1 -0
- package/lib/utils/historyUtils.d.ts +17 -0
- package/lib/utils/historyUtils.d.ts.map +1 -0
- package/lib/utils/historyUtils.js +38 -0
- package/lib/utils/historyUtils.js.map +1 -0
- package/lib/utils/jsUtils.d.ts +23 -0
- package/lib/utils/jsUtils.d.ts.map +1 -0
- package/lib/utils/jsUtils.js +29 -0
- package/lib/utils/jsUtils.js.map +1 -0
- package/lib/utils/metadataUtils.d.ts +38 -0
- package/lib/utils/metadataUtils.d.ts.map +1 -0
- package/lib/utils/metadataUtils.js +70 -0
- package/lib/utils/metadataUtils.js.map +1 -0
- package/lib/utils/navbarUtils.d.ts +21 -0
- package/lib/utils/navbarUtils.d.ts.map +1 -0
- package/lib/utils/navbarUtils.js +36 -0
- package/lib/utils/navbarUtils.js.map +1 -0
- package/lib/utils/reactUtils.d.ts +69 -0
- package/lib/utils/reactUtils.d.ts.map +1 -0
- package/lib/utils/reactUtils.js +98 -0
- package/lib/utils/reactUtils.js.map +1 -0
- package/lib/utils/regexpUtils.d.ts +12 -0
- package/lib/utils/regexpUtils.d.ts.map +1 -0
- package/lib/utils/regexpUtils.js +18 -0
- package/lib/utils/regexpUtils.js.map +1 -0
- package/lib/utils/routesUtils.d.ts +26 -0
- package/lib/utils/routesUtils.d.ts.map +1 -0
- package/lib/utils/routesUtils.js +54 -0
- package/lib/utils/routesUtils.js.map +1 -0
- package/lib/utils/scrollUtils.d.ts +83 -0
- package/lib/utils/scrollUtils.d.ts.map +1 -0
- package/lib/utils/scrollUtils.js +200 -0
- package/lib/utils/scrollUtils.js.map +1 -0
- package/lib/utils/searchUtils.d.ts +13 -0
- package/lib/utils/searchUtils.d.ts.map +1 -0
- package/lib/utils/searchUtils.js +37 -0
- package/lib/utils/searchUtils.js.map +1 -0
- package/lib/utils/storageUtils.d.ts +15 -7
- package/lib/utils/storageUtils.d.ts.map +1 -0
- package/lib/utils/storageUtils.js +58 -25
- package/lib/utils/storageUtils.js.map +1 -0
- package/lib/utils/tagsUtils.d.ts +18 -0
- package/lib/utils/tagsUtils.d.ts.map +1 -0
- package/lib/utils/tagsUtils.js +36 -0
- package/lib/utils/tagsUtils.js.map +1 -0
- package/lib/utils/tocUtils.d.ts +36 -0
- package/lib/utils/tocUtils.d.ts.map +1 -0
- package/lib/utils/tocUtils.js +84 -0
- package/lib/utils/tocUtils.js.map +1 -0
- package/lib/utils/useAlternatePageUtils.d.ts +21 -1
- package/lib/utils/useAlternatePageUtils.d.ts.map +1 -0
- package/lib/utils/useAlternatePageUtils.js +9 -4
- package/lib/utils/useAlternatePageUtils.js.map +1 -0
- package/lib/utils/useLocalPathname.d.ts +13 -0
- package/lib/utils/useLocalPathname.d.ts.map +1 -0
- package/lib/utils/useLocalPathname.js +19 -0
- package/lib/utils/useLocalPathname.js.map +1 -0
- package/lib/utils/useLocationChange.d.ts +17 -0
- package/lib/utils/useLocationChange.d.ts.map +1 -0
- package/lib/utils/useLocationChange.js +31 -0
- package/lib/utils/useLocationChange.js.map +1 -0
- package/lib/utils/usePluralForm.d.ts +12 -0
- package/lib/utils/usePluralForm.d.ts.map +1 -0
- package/lib/utils/usePluralForm.js +36 -37
- package/lib/utils/usePluralForm.js.map +1 -0
- package/lib/utils/useThemeConfig.d.ts +56 -27
- package/lib/utils/useThemeConfig.d.ts.map +1 -0
- package/lib/utils/useThemeConfig.js +4 -0
- package/lib/utils/useThemeConfig.js.map +1 -0
- package/package.json +35 -13
- package/src/components/Collapsible/index.tsx +263 -0
- package/src/components/Details/index.tsx +109 -0
- package/src/components/Details/styles.module.css +62 -0
- package/src/contexts/announcementBar.tsx +119 -0
- package/src/contexts/blogPost.tsx +80 -0
- package/src/contexts/colorMode.tsx +198 -0
- package/src/contexts/doc.tsx +71 -0
- package/src/contexts/docSidebarItemsExpandedState.tsx +55 -0
- package/src/contexts/docsPreferredVersion.tsx +251 -0
- package/src/contexts/docsSidebar.tsx +50 -0
- package/src/contexts/docsVersion.tsx +36 -0
- package/src/contexts/navbarMobileSidebar.tsx +99 -0
- package/src/contexts/navbarSecondaryMenu/content.tsx +100 -0
- package/src/contexts/navbarSecondaryMenu/display.tsx +102 -0
- package/src/contexts/tabGroupChoice.tsx +85 -0
- package/{lib/utils/pathUtils.d.ts → src/hooks/styles.css} +4 -1
- package/src/hooks/useBackToTopButton.ts +73 -0
- package/src/hooks/useCodeWordWrap.ts +105 -0
- package/src/hooks/useHideableNavbar.ts +75 -0
- package/src/hooks/useKeyboardNavigation.ts +45 -0
- package/src/hooks/useLockBodyScroll.ts +21 -0
- package/src/hooks/useMutationObserver.ts +38 -0
- package/src/hooks/usePrismTheme.ts +24 -0
- package/src/hooks/useSearchPage.ts +79 -0
- package/src/hooks/useSkipToContent.ts +58 -0
- package/src/hooks/useTOCHighlight.ts +189 -0
- package/src/hooks/useWindowSize.ts +72 -0
- package/src/index.ts +55 -23
- package/src/internal.ts +122 -0
- package/src/types.d.ts +0 -2
- package/src/utils/ThemeClassNames.ts +54 -5
- package/src/utils/codeBlockUtils.ts +241 -2
- package/src/utils/docsUtils.tsx +334 -0
- package/src/utils/footerUtils.ts +18 -0
- package/src/utils/generalUtils.ts +9 -5
- package/src/utils/historyUtils.ts +45 -0
- package/src/utils/jsUtils.ts +36 -0
- package/src/utils/metadataUtils.tsx +115 -0
- package/src/utils/navbarUtils.tsx +45 -0
- package/src/utils/reactUtils.tsx +129 -0
- package/src/utils/regexpUtils.ts +24 -0
- package/src/utils/routesUtils.ts +75 -0
- package/src/utils/scrollUtils.tsx +304 -0
- package/src/utils/searchUtils.ts +51 -0
- package/src/utils/storageUtils.ts +59 -26
- package/src/utils/tagsUtils.ts +50 -0
- package/src/utils/tocUtils.ts +119 -0
- package/src/utils/useAlternatePageUtils.ts +28 -7
- package/src/utils/useLocalPathname.ts +22 -0
- package/src/utils/useLocationChange.ts +41 -0
- package/src/utils/usePluralForm.ts +53 -39
- package/src/utils/useThemeConfig.ts +55 -29
- package/lib/.tsbuildinfo +0 -4115
- package/lib/utils/docsPreferredVersion/DocsPreferredVersionProvider.d.ts +0 -21
- package/lib/utils/docsPreferredVersion/DocsPreferredVersionProvider.js +0 -94
- package/lib/utils/docsPreferredVersion/DocsPreferredVersionStorage.d.ts +0 -13
- package/lib/utils/docsPreferredVersion/DocsPreferredVersionStorage.js +0 -20
- package/lib/utils/docsPreferredVersion/useDocsPreferredVersion.d.ts +0 -5
- package/lib/utils/docsPreferredVersion/useDocsPreferredVersion.js +0 -41
- package/lib/utils/pathUtils.js +0 -13
- package/lib/utils/useChangeRoute.js +0 -18
- package/src/utils/__tests__/codeBlockUtils.test.ts +0 -54
- package/src/utils/__tests__/pathUtils.test.ts +0 -32
- package/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx +0 -165
- package/src/utils/docsPreferredVersion/DocsPreferredVersionStorage.ts +0 -34
- package/src/utils/docsPreferredVersion/useDocsPreferredVersion.ts +0 -66
- package/src/utils/docsUtils.ts +0 -11
- package/src/utils/pathUtils.ts +0 -17
- package/src/utils/useChangeRoute.ts +0 -21
- package/tsconfig.json +0 -10
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, {
|
|
9
|
+
useCallback,
|
|
10
|
+
useEffect,
|
|
11
|
+
useState,
|
|
12
|
+
useMemo,
|
|
13
|
+
type ReactNode,
|
|
14
|
+
} from 'react';
|
|
15
|
+
import {useNavbarSecondaryMenuContent} from './navbarSecondaryMenu/content';
|
|
16
|
+
import {useWindowSize} from '../hooks/useWindowSize';
|
|
17
|
+
import {useHistoryPopHandler} from '../utils/historyUtils';
|
|
18
|
+
import {useThemeConfig} from '../utils/useThemeConfig';
|
|
19
|
+
import {ReactContextError} from '../utils/reactUtils';
|
|
20
|
+
|
|
21
|
+
type ContextValue = {
|
|
22
|
+
/**
|
|
23
|
+
* Mobile sidebar should be disabled in case it's empty, i.e. no secondary
|
|
24
|
+
* menu + no navbar items). If disabled, the toggle button should not be
|
|
25
|
+
* displayed at all.
|
|
26
|
+
*/
|
|
27
|
+
disabled: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Signals whether the actual sidebar should be displayed (contrary to
|
|
30
|
+
* `disabled` which is about the toggle button). Sidebar should not visible
|
|
31
|
+
* until user interaction to avoid SSR rendering.
|
|
32
|
+
*/
|
|
33
|
+
shouldRender: boolean;
|
|
34
|
+
/** The displayed state. Can be toggled with the `toggle` callback. */
|
|
35
|
+
shown: boolean;
|
|
36
|
+
/** Toggle the `shown` attribute. */
|
|
37
|
+
toggle: () => void;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const Context = React.createContext<ContextValue | undefined>(undefined);
|
|
41
|
+
|
|
42
|
+
function useIsNavbarMobileSidebarDisabled() {
|
|
43
|
+
const secondaryMenuContent = useNavbarSecondaryMenuContent();
|
|
44
|
+
const {items} = useThemeConfig().navbar;
|
|
45
|
+
return items.length === 0 && !secondaryMenuContent.component;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function useContextValue(): ContextValue {
|
|
49
|
+
const disabled = useIsNavbarMobileSidebarDisabled();
|
|
50
|
+
const windowSize = useWindowSize();
|
|
51
|
+
|
|
52
|
+
const shouldRender = !disabled && windowSize === 'mobile';
|
|
53
|
+
|
|
54
|
+
const [shown, setShown] = useState(false);
|
|
55
|
+
|
|
56
|
+
// Close mobile sidebar on navigation pop
|
|
57
|
+
// Most likely firing when using the Android back button (but not only)
|
|
58
|
+
useHistoryPopHandler(() => {
|
|
59
|
+
if (shown) {
|
|
60
|
+
setShown(false);
|
|
61
|
+
// Prevent pop navigation; seems desirable enough
|
|
62
|
+
// See https://github.com/facebook/docusaurus/pull/5462#issuecomment-911699846
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
return undefined;
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const toggle = useCallback(() => {
|
|
69
|
+
setShown((s) => !s);
|
|
70
|
+
}, []);
|
|
71
|
+
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (windowSize === 'desktop') {
|
|
74
|
+
setShown(false);
|
|
75
|
+
}
|
|
76
|
+
}, [windowSize]);
|
|
77
|
+
|
|
78
|
+
return useMemo(
|
|
79
|
+
() => ({disabled, shouldRender, toggle, shown}),
|
|
80
|
+
[disabled, shouldRender, toggle, shown],
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function NavbarMobileSidebarProvider({
|
|
85
|
+
children,
|
|
86
|
+
}: {
|
|
87
|
+
children: ReactNode;
|
|
88
|
+
}): JSX.Element {
|
|
89
|
+
const value = useContextValue();
|
|
90
|
+
return <Context.Provider value={value}>{children}</Context.Provider>;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function useNavbarMobileSidebar(): ContextValue {
|
|
94
|
+
const context = React.useContext(Context);
|
|
95
|
+
if (context === undefined) {
|
|
96
|
+
throw new ReactContextError('NavbarMobileSidebarProvider');
|
|
97
|
+
}
|
|
98
|
+
return context;
|
|
99
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, {
|
|
9
|
+
useState,
|
|
10
|
+
useContext,
|
|
11
|
+
useEffect,
|
|
12
|
+
type ReactNode,
|
|
13
|
+
type ComponentType,
|
|
14
|
+
} from 'react';
|
|
15
|
+
import {ReactContextError, useShallowMemoObject} from '../../utils/reactUtils';
|
|
16
|
+
|
|
17
|
+
// This context represents a "global layout store". A component (usually a
|
|
18
|
+
// layout component) can request filling this store through
|
|
19
|
+
// `NavbarSecondaryMenuFiller`. It doesn't actually control rendering by itself,
|
|
20
|
+
// and this context should be considered internal implementation. The user-
|
|
21
|
+
// facing value comes from `display.tsx`, which takes the `component` and
|
|
22
|
+
// `props` stored here and renders the actual element.
|
|
23
|
+
|
|
24
|
+
export type NavbarSecondaryMenuComponent<Props> = ComponentType<Props>;
|
|
25
|
+
|
|
26
|
+
/** @internal */
|
|
27
|
+
export type Content =
|
|
28
|
+
| {
|
|
29
|
+
component: NavbarSecondaryMenuComponent<object>;
|
|
30
|
+
props: object;
|
|
31
|
+
}
|
|
32
|
+
| {component: null; props: null};
|
|
33
|
+
|
|
34
|
+
type ContextValue = [
|
|
35
|
+
content: Content,
|
|
36
|
+
setContent: React.Dispatch<React.SetStateAction<Content>>,
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const Context = React.createContext<ContextValue | null>(null);
|
|
40
|
+
|
|
41
|
+
/** @internal */
|
|
42
|
+
export function NavbarSecondaryMenuContentProvider({
|
|
43
|
+
children,
|
|
44
|
+
}: {
|
|
45
|
+
children: ReactNode;
|
|
46
|
+
}): JSX.Element {
|
|
47
|
+
const value = useState({component: null, props: null});
|
|
48
|
+
return (
|
|
49
|
+
// @ts-expect-error: this context is hard to type
|
|
50
|
+
<Context.Provider value={value}>{children}</Context.Provider>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** @internal */
|
|
55
|
+
export function useNavbarSecondaryMenuContent(): Content {
|
|
56
|
+
const value = useContext(Context);
|
|
57
|
+
if (!value) {
|
|
58
|
+
throw new ReactContextError('NavbarSecondaryMenuContentProvider');
|
|
59
|
+
}
|
|
60
|
+
return value[0];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* This component renders nothing by itself, but it fills the placeholder in the
|
|
65
|
+
* generic secondary menu layout. This reduces coupling between the main layout
|
|
66
|
+
* and the specific page.
|
|
67
|
+
*
|
|
68
|
+
* This kind of feature is often called portal/teleport/gateway/outlet...
|
|
69
|
+
* Various unmaintained React libs exist. Most up-to-date one:
|
|
70
|
+
* https://github.com/gregberge/react-teleporter
|
|
71
|
+
* Not sure any of those is safe regarding concurrent mode.
|
|
72
|
+
*/
|
|
73
|
+
export function NavbarSecondaryMenuFiller<P extends object>({
|
|
74
|
+
component,
|
|
75
|
+
props,
|
|
76
|
+
}: {
|
|
77
|
+
component: NavbarSecondaryMenuComponent<P>;
|
|
78
|
+
props: P;
|
|
79
|
+
}): JSX.Element | null {
|
|
80
|
+
const context = useContext(Context);
|
|
81
|
+
if (!context) {
|
|
82
|
+
throw new ReactContextError('NavbarSecondaryMenuContentProvider');
|
|
83
|
+
}
|
|
84
|
+
const [, setContent] = context;
|
|
85
|
+
|
|
86
|
+
// To avoid useless context re-renders, props are memoized shallowly
|
|
87
|
+
const memoizedProps = useShallowMemoObject(props);
|
|
88
|
+
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
// @ts-expect-error: this context is hard to type
|
|
91
|
+
setContent({component, props: memoizedProps});
|
|
92
|
+
}, [setContent, component, memoizedProps]);
|
|
93
|
+
|
|
94
|
+
useEffect(
|
|
95
|
+
() => () => setContent({component: null, props: null}),
|
|
96
|
+
[setContent],
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, {
|
|
9
|
+
useState,
|
|
10
|
+
useContext,
|
|
11
|
+
useEffect,
|
|
12
|
+
useMemo,
|
|
13
|
+
useCallback,
|
|
14
|
+
type ReactNode,
|
|
15
|
+
} from 'react';
|
|
16
|
+
import {ReactContextError, usePrevious} from '../../utils/reactUtils';
|
|
17
|
+
import {useNavbarMobileSidebar} from '../navbarMobileSidebar';
|
|
18
|
+
import {useNavbarSecondaryMenuContent, type Content} from './content';
|
|
19
|
+
|
|
20
|
+
type ContextValue = [
|
|
21
|
+
shown: boolean,
|
|
22
|
+
setShown: React.Dispatch<React.SetStateAction<boolean>>,
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
const Context = React.createContext<ContextValue | null>(null);
|
|
26
|
+
|
|
27
|
+
function useContextValue(): ContextValue {
|
|
28
|
+
const mobileSidebar = useNavbarMobileSidebar();
|
|
29
|
+
const content = useNavbarSecondaryMenuContent();
|
|
30
|
+
|
|
31
|
+
const [shown, setShown] = useState(false);
|
|
32
|
+
|
|
33
|
+
const hasContent = content.component !== null;
|
|
34
|
+
const previousHasContent = usePrevious(hasContent);
|
|
35
|
+
|
|
36
|
+
// When content is become available for the first time (set in useEffect)
|
|
37
|
+
// we set this content to be shown!
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
const contentBecameAvailable = hasContent && !previousHasContent;
|
|
40
|
+
if (contentBecameAvailable) {
|
|
41
|
+
setShown(true);
|
|
42
|
+
}
|
|
43
|
+
}, [hasContent, previousHasContent]);
|
|
44
|
+
|
|
45
|
+
// On sidebar close, secondary menu is set to be shown on next re-opening
|
|
46
|
+
// (if any secondary menu content available)
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
if (!hasContent) {
|
|
49
|
+
setShown(false);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (!mobileSidebar.shown) {
|
|
53
|
+
setShown(true);
|
|
54
|
+
}
|
|
55
|
+
}, [mobileSidebar.shown, hasContent]);
|
|
56
|
+
|
|
57
|
+
return useMemo(() => [shown, setShown], [shown]);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** @internal */
|
|
61
|
+
export function NavbarSecondaryMenuDisplayProvider({
|
|
62
|
+
children,
|
|
63
|
+
}: {
|
|
64
|
+
children: ReactNode;
|
|
65
|
+
}): JSX.Element {
|
|
66
|
+
const value = useContextValue();
|
|
67
|
+
return <Context.Provider value={value}>{children}</Context.Provider>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function renderElement(content: Content): JSX.Element | undefined {
|
|
71
|
+
if (content.component) {
|
|
72
|
+
const Comp = content.component;
|
|
73
|
+
return <Comp {...content.props} />;
|
|
74
|
+
}
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Wires the logic for rendering the mobile navbar secondary menu. */
|
|
79
|
+
export function useNavbarSecondaryMenu(): {
|
|
80
|
+
/** Whether secondary menu is displayed. */
|
|
81
|
+
shown: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Hide the secondary menu; fired either when hiding the entire sidebar, or
|
|
84
|
+
* when going back to the primary menu.
|
|
85
|
+
*/
|
|
86
|
+
hide: () => void;
|
|
87
|
+
/** The content returned from the current secondary menu filler. */
|
|
88
|
+
content: JSX.Element | undefined;
|
|
89
|
+
} {
|
|
90
|
+
const value = useContext(Context);
|
|
91
|
+
if (!value) {
|
|
92
|
+
throw new ReactContextError('NavbarSecondaryMenuDisplayProvider');
|
|
93
|
+
}
|
|
94
|
+
const [shown, setShown] = value;
|
|
95
|
+
const hide = useCallback(() => setShown(false), [setShown]);
|
|
96
|
+
const content = useNavbarSecondaryMenuContent();
|
|
97
|
+
|
|
98
|
+
return useMemo(
|
|
99
|
+
() => ({shown, hide, content: renderElement(content)}),
|
|
100
|
+
[hide, content, shown],
|
|
101
|
+
);
|
|
102
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, {
|
|
9
|
+
useState,
|
|
10
|
+
useCallback,
|
|
11
|
+
useEffect,
|
|
12
|
+
useMemo,
|
|
13
|
+
useContext,
|
|
14
|
+
type ReactNode,
|
|
15
|
+
} from 'react';
|
|
16
|
+
import {createStorageSlot, listStorageKeys} from '../utils/storageUtils';
|
|
17
|
+
import {ReactContextError} from '../utils/reactUtils';
|
|
18
|
+
|
|
19
|
+
const TAB_CHOICE_PREFIX = 'docusaurus.tab.';
|
|
20
|
+
|
|
21
|
+
type ContextValue = {
|
|
22
|
+
/** A map from `groupId` to the `value` of the saved choice. */
|
|
23
|
+
readonly tabGroupChoices: {readonly [groupId: string]: string};
|
|
24
|
+
/** Set the new choice value of a group. */
|
|
25
|
+
readonly setTabGroupChoices: (groupId: string, newChoice: string) => void;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const Context = React.createContext<ContextValue | undefined>(undefined);
|
|
29
|
+
|
|
30
|
+
function useContextValue(): ContextValue {
|
|
31
|
+
const [tabGroupChoices, setChoices] = useState<{
|
|
32
|
+
readonly [groupId: string]: string;
|
|
33
|
+
}>({});
|
|
34
|
+
const setChoiceSyncWithLocalStorage = useCallback(
|
|
35
|
+
(groupId: string, newChoice: string) => {
|
|
36
|
+
createStorageSlot(`${TAB_CHOICE_PREFIX}${groupId}`).set(newChoice);
|
|
37
|
+
},
|
|
38
|
+
[],
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
try {
|
|
43
|
+
const localStorageChoices: {[groupId: string]: string} = {};
|
|
44
|
+
listStorageKeys().forEach((storageKey) => {
|
|
45
|
+
if (storageKey.startsWith(TAB_CHOICE_PREFIX)) {
|
|
46
|
+
const groupId = storageKey.substring(TAB_CHOICE_PREFIX.length);
|
|
47
|
+
localStorageChoices[groupId] = createStorageSlot(storageKey).get()!;
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
setChoices(localStorageChoices);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
console.error(err);
|
|
53
|
+
}
|
|
54
|
+
}, []);
|
|
55
|
+
|
|
56
|
+
const setTabGroupChoices = useCallback(
|
|
57
|
+
(groupId: string, newChoice: string) => {
|
|
58
|
+
setChoices((oldChoices) => ({...oldChoices, [groupId]: newChoice}));
|
|
59
|
+
setChoiceSyncWithLocalStorage(groupId, newChoice);
|
|
60
|
+
},
|
|
61
|
+
[setChoiceSyncWithLocalStorage],
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
return useMemo(
|
|
65
|
+
() => ({tabGroupChoices, setTabGroupChoices}),
|
|
66
|
+
[tabGroupChoices, setTabGroupChoices],
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function TabGroupChoiceProvider({
|
|
71
|
+
children,
|
|
72
|
+
}: {
|
|
73
|
+
children: ReactNode;
|
|
74
|
+
}): JSX.Element {
|
|
75
|
+
const value = useContextValue();
|
|
76
|
+
return <Context.Provider value={value}>{children}</Context.Provider>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function useTabGroupChoice(): ContextValue {
|
|
80
|
+
const context = useContext(Context);
|
|
81
|
+
if (context == null) {
|
|
82
|
+
throw new ReactContextError('TabGroupChoiceProvider');
|
|
83
|
+
}
|
|
84
|
+
return context;
|
|
85
|
+
}
|
|
@@ -4,4 +4,7 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
body:not(.navigation-with-keyboard) *:not(input):focus {
|
|
9
|
+
outline: none;
|
|
10
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {useRef, useState} from 'react';
|
|
9
|
+
import {useScrollPosition, useSmoothScrollTo} from '../utils/scrollUtils';
|
|
10
|
+
import {useLocationChange} from '../utils/useLocationChange';
|
|
11
|
+
|
|
12
|
+
/** Wires the logic for the back to top button. */
|
|
13
|
+
export function useBackToTopButton({
|
|
14
|
+
threshold,
|
|
15
|
+
}: {
|
|
16
|
+
/**
|
|
17
|
+
* The minimum vertical scroll position, above which a scroll-up would not
|
|
18
|
+
* cause `shown` to become `true`. This is because BTT is only useful if the
|
|
19
|
+
* user is far down the page.
|
|
20
|
+
*/
|
|
21
|
+
threshold: number;
|
|
22
|
+
}): {
|
|
23
|
+
/**
|
|
24
|
+
* Whether the button should be displayed. We only show if the user has
|
|
25
|
+
* scrolled up and is on a vertical position greater than `threshold`.
|
|
26
|
+
*/
|
|
27
|
+
shown: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* A (memoized) handle for starting the scroll, which you can directly plug
|
|
30
|
+
* into the props.
|
|
31
|
+
*/
|
|
32
|
+
scrollToTop: () => void;
|
|
33
|
+
} {
|
|
34
|
+
const [shown, setShown] = useState(false);
|
|
35
|
+
const isFocusedAnchor = useRef(false);
|
|
36
|
+
const {startScroll, cancelScroll} = useSmoothScrollTo();
|
|
37
|
+
|
|
38
|
+
useScrollPosition(({scrollY: scrollTop}, lastPosition) => {
|
|
39
|
+
const lastScrollTop = lastPosition?.scrollY;
|
|
40
|
+
// Component is just being mounted. Not really a scroll event from the user.
|
|
41
|
+
// Ignore it.
|
|
42
|
+
if (!lastScrollTop) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (isFocusedAnchor.current) {
|
|
46
|
+
// This scroll position change is triggered by navigating to an anchor.
|
|
47
|
+
// Ignore it.
|
|
48
|
+
isFocusedAnchor.current = false;
|
|
49
|
+
} else if (scrollTop >= lastScrollTop) {
|
|
50
|
+
// The user has scrolled down to "fight against" the animation. Cancel any
|
|
51
|
+
// animation under progress.
|
|
52
|
+
cancelScroll();
|
|
53
|
+
setShown(false);
|
|
54
|
+
} else if (scrollTop < threshold) {
|
|
55
|
+
// Scrolled to the minimum position; hide the button.
|
|
56
|
+
setShown(false);
|
|
57
|
+
} else if (
|
|
58
|
+
scrollTop + window.innerHeight <
|
|
59
|
+
document.documentElement.scrollHeight
|
|
60
|
+
) {
|
|
61
|
+
setShown(true);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
useLocationChange((locationChangeEvent) => {
|
|
66
|
+
if (locationChangeEvent.location.hash) {
|
|
67
|
+
isFocusedAnchor.current = true;
|
|
68
|
+
setShown(false);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return {shown, scrollToTop: () => startScroll(0)};
|
|
73
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
import type {RefObject} from 'react';
|
|
8
|
+
import {useState, useCallback, useEffect, useRef} from 'react';
|
|
9
|
+
import {useMutationObserver} from './useMutationObserver';
|
|
10
|
+
|
|
11
|
+
// Callback fires when the "hidden" attribute of a tabpanel changes
|
|
12
|
+
// See https://github.com/facebook/docusaurus/pull/7485
|
|
13
|
+
function useTabBecameVisibleCallback(
|
|
14
|
+
codeBlockRef: RefObject<HTMLPreElement>,
|
|
15
|
+
callback: () => void,
|
|
16
|
+
) {
|
|
17
|
+
const [hiddenTabElement, setHiddenTabElement] = useState<
|
|
18
|
+
Element | null | undefined
|
|
19
|
+
>();
|
|
20
|
+
|
|
21
|
+
const updateHiddenTabElement = useCallback(() => {
|
|
22
|
+
// No need to observe non-hidden tabs
|
|
23
|
+
// + we want to force a re-render when a tab becomes visible
|
|
24
|
+
setHiddenTabElement(
|
|
25
|
+
codeBlockRef.current?.closest('[role=tabpanel][hidden]'),
|
|
26
|
+
);
|
|
27
|
+
}, [codeBlockRef, setHiddenTabElement]);
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
updateHiddenTabElement();
|
|
31
|
+
}, [updateHiddenTabElement]);
|
|
32
|
+
|
|
33
|
+
useMutationObserver(
|
|
34
|
+
hiddenTabElement,
|
|
35
|
+
(mutations: MutationRecord[]) => {
|
|
36
|
+
mutations.forEach((mutation) => {
|
|
37
|
+
if (
|
|
38
|
+
mutation.type === 'attributes' &&
|
|
39
|
+
mutation.attributeName === 'hidden'
|
|
40
|
+
) {
|
|
41
|
+
callback();
|
|
42
|
+
updateHiddenTabElement();
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
attributes: true,
|
|
48
|
+
characterData: false,
|
|
49
|
+
childList: false,
|
|
50
|
+
subtree: false,
|
|
51
|
+
},
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function useCodeWordWrap(): {
|
|
56
|
+
readonly codeBlockRef: RefObject<HTMLPreElement>;
|
|
57
|
+
readonly isEnabled: boolean;
|
|
58
|
+
readonly isCodeScrollable: boolean;
|
|
59
|
+
readonly toggle: () => void;
|
|
60
|
+
} {
|
|
61
|
+
const [isEnabled, setIsEnabled] = useState(false);
|
|
62
|
+
const [isCodeScrollable, setIsCodeScrollable] = useState<boolean>(false);
|
|
63
|
+
const codeBlockRef = useRef<HTMLPreElement>(null);
|
|
64
|
+
|
|
65
|
+
const toggle = useCallback(() => {
|
|
66
|
+
const codeElement = codeBlockRef.current!.querySelector('code')!;
|
|
67
|
+
|
|
68
|
+
if (isEnabled) {
|
|
69
|
+
codeElement.removeAttribute('style');
|
|
70
|
+
} else {
|
|
71
|
+
codeElement.style.whiteSpace = 'pre-wrap';
|
|
72
|
+
// When code wrap is enabled, we want to avoid a scrollbar in any case
|
|
73
|
+
// Ensure that very very long words/strings/tokens still wrap
|
|
74
|
+
codeElement.style.overflowWrap = 'anywhere';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
setIsEnabled((value) => !value);
|
|
78
|
+
}, [codeBlockRef, isEnabled]);
|
|
79
|
+
|
|
80
|
+
const updateCodeIsScrollable = useCallback(() => {
|
|
81
|
+
const {scrollWidth, clientWidth} = codeBlockRef.current!;
|
|
82
|
+
const isScrollable =
|
|
83
|
+
scrollWidth > clientWidth ||
|
|
84
|
+
codeBlockRef.current!.querySelector('code')!.hasAttribute('style');
|
|
85
|
+
setIsCodeScrollable(isScrollable);
|
|
86
|
+
}, [codeBlockRef]);
|
|
87
|
+
|
|
88
|
+
useTabBecameVisibleCallback(codeBlockRef, updateCodeIsScrollable);
|
|
89
|
+
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
updateCodeIsScrollable();
|
|
92
|
+
}, [isEnabled, updateCodeIsScrollable]);
|
|
93
|
+
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
window.addEventListener('resize', updateCodeIsScrollable, {
|
|
96
|
+
passive: true,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return () => {
|
|
100
|
+
window.removeEventListener('resize', updateCodeIsScrollable);
|
|
101
|
+
};
|
|
102
|
+
}, [updateCodeIsScrollable]);
|
|
103
|
+
|
|
104
|
+
return {codeBlockRef, isEnabled, isCodeScrollable, toggle};
|
|
105
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {useState, useCallback, useRef} from 'react';
|
|
9
|
+
import {useLocationChange} from '../utils/useLocationChange';
|
|
10
|
+
import {useScrollPosition} from '../utils/scrollUtils';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Wires the imperative logic of a hideable navbar.
|
|
14
|
+
* @param hideOnScroll If `false`, this hook is basically a no-op.
|
|
15
|
+
*/
|
|
16
|
+
export function useHideableNavbar(hideOnScroll: boolean): {
|
|
17
|
+
/** A ref to the navbar component. Plug this into the actual element. */
|
|
18
|
+
readonly navbarRef: (node: HTMLElement | null) => void;
|
|
19
|
+
/** If `false`, the navbar component should not be rendered. */
|
|
20
|
+
readonly isNavbarVisible: boolean;
|
|
21
|
+
} {
|
|
22
|
+
const [isNavbarVisible, setIsNavbarVisible] = useState(hideOnScroll);
|
|
23
|
+
const isFocusedAnchor = useRef(false);
|
|
24
|
+
const navbarHeight = useRef(0);
|
|
25
|
+
const navbarRef = useCallback((node: HTMLElement | null) => {
|
|
26
|
+
if (node !== null) {
|
|
27
|
+
navbarHeight.current = node.getBoundingClientRect().height;
|
|
28
|
+
}
|
|
29
|
+
}, []);
|
|
30
|
+
|
|
31
|
+
useScrollPosition(({scrollY: scrollTop}, lastPosition) => {
|
|
32
|
+
if (!hideOnScroll) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Needed mostly for handling rubber band scrolling.
|
|
37
|
+
// See https://github.com/facebook/docusaurus/pull/5721
|
|
38
|
+
if (scrollTop < navbarHeight.current) {
|
|
39
|
+
setIsNavbarVisible(true);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (isFocusedAnchor.current) {
|
|
44
|
+
isFocusedAnchor.current = false;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const lastScrollTop = lastPosition?.scrollY;
|
|
49
|
+
const documentHeight =
|
|
50
|
+
document.documentElement.scrollHeight - navbarHeight.current;
|
|
51
|
+
const windowHeight = window.innerHeight;
|
|
52
|
+
|
|
53
|
+
if (lastScrollTop && scrollTop >= lastScrollTop) {
|
|
54
|
+
setIsNavbarVisible(false);
|
|
55
|
+
} else if (scrollTop + windowHeight < documentHeight) {
|
|
56
|
+
setIsNavbarVisible(true);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
useLocationChange((locationChangeEvent) => {
|
|
61
|
+
if (!hideOnScroll) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (locationChangeEvent.location.hash) {
|
|
66
|
+
isFocusedAnchor.current = true;
|
|
67
|
+
setIsNavbarVisible(false);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
setIsNavbarVisible(true);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return {navbarRef, isNavbarVisible};
|
|
75
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {useEffect} from 'react';
|
|
9
|
+
|
|
10
|
+
import './styles.css';
|
|
11
|
+
|
|
12
|
+
export const keyboardFocusedClassName = 'navigation-with-keyboard';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Side-effect that adds the `keyboardFocusedClassName` to the body element when
|
|
16
|
+
* the keyboard has been pressed, or removes it when the mouse is clicked.
|
|
17
|
+
*
|
|
18
|
+
* The presence of this class name signals that the user may be using keyboard
|
|
19
|
+
* for navigation, and the theme **must** add focus outline when this class name
|
|
20
|
+
* is present. (And optionally not if it's absent, for design purposes)
|
|
21
|
+
*
|
|
22
|
+
* Inspired by https://hackernoon.com/removing-that-ugly-focus-ring-and-keeping-it-too-6c8727fefcd2
|
|
23
|
+
*/
|
|
24
|
+
export function useKeyboardNavigation(): void {
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
function handleOutlineStyles(e: MouseEvent | KeyboardEvent) {
|
|
27
|
+
if (e.type === 'keydown' && (e as KeyboardEvent).key === 'Tab') {
|
|
28
|
+
document.body.classList.add(keyboardFocusedClassName);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (e.type === 'mousedown') {
|
|
32
|
+
document.body.classList.remove(keyboardFocusedClassName);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
document.addEventListener('keydown', handleOutlineStyles);
|
|
37
|
+
document.addEventListener('mousedown', handleOutlineStyles);
|
|
38
|
+
|
|
39
|
+
return () => {
|
|
40
|
+
document.body.classList.remove(keyboardFocusedClassName);
|
|
41
|
+
document.removeEventListener('keydown', handleOutlineStyles);
|
|
42
|
+
document.removeEventListener('mousedown', handleOutlineStyles);
|
|
43
|
+
};
|
|
44
|
+
}, []);
|
|
45
|
+
}
|