@docusaurus/theme-classic 3.8.1 → 3.9.0-canary-6403
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/index.js +0 -1
- package/lib/inlineScripts.d.ts +1 -1
- package/lib/inlineScripts.js +21 -20
- package/lib/theme/Blog/Components/Author/Socials/index.js +3 -1
- package/lib/theme/Blog/Components/Author/index.js +6 -2
- package/lib/theme/CodeBlock/Buttons/CopyButton/index.js +6 -6
- package/lib/theme/DocSidebarItem/Category/index.d.ts +1 -1
- package/lib/theme/DocSidebarItem/Category/index.js +96 -33
- package/lib/theme/DocSidebarItem/Category/styles.module.css +26 -0
- package/lib/theme/DocSidebarItem/Link/index.js +8 -1
- package/lib/theme/DocSidebarItem/Link/styles.module.css +8 -0
- package/lib/theme/Heading/index.js +2 -1
- package/lib/theme/Icon/ExternalLink/index.js +6 -1
- package/lib/theme/Icon/Socials/Bluesky/index.js +6 -5
- package/lib/theme/{MDXComponents/A.js → Icon/Socials/Bluesky/styles.module.css} +7 -4
- package/lib/theme/Icon/Socials/Default/index.js +6 -6
- package/lib/theme/Icon/Socials/Email/index.d.ts +9 -0
- package/lib/theme/Icon/Socials/Email/index.js +25 -0
- package/lib/theme/Icon/Socials/LinkedIn/index.d.ts +1 -1
- package/lib/theme/Icon/Socials/LinkedIn/index.js +6 -5
- package/lib/theme/Icon/Socials/LinkedIn/styles.module.css +14 -0
- package/lib/theme/MDXComponents/A/index.js +36 -0
- package/lib/theme/MDXComponents/A/styles.module.css +20 -0
- package/lib/theme/Navbar/Search/styles.module.css +1 -2
- package/lib/theme/NavbarItem/DocsVersionDropdownNavbarItem.js +3 -2
- package/lib/theme/NavbarItem/LocaleDropdownNavbarItem/index.js +61 -15
- package/lib/theme/Tabs/index.js +9 -1
- package/package.json +16 -17
- package/src/index.ts +0 -1
- package/src/inlineScripts.ts +27 -20
- package/src/theme/Blog/Components/Author/Socials/index.tsx +3 -1
- package/src/theme/Blog/Components/Author/index.tsx +6 -2
- package/src/theme/CodeBlock/Buttons/CopyButton/index.tsx +6 -6
- package/src/theme/DocSidebarItem/Category/index.tsx +104 -32
- package/src/theme/DocSidebarItem/Category/styles.module.css +26 -0
- package/src/theme/DocSidebarItem/Link/index.tsx +9 -1
- package/src/theme/DocSidebarItem/Link/styles.module.css +8 -0
- package/src/theme/Heading/index.tsx +2 -1
- package/src/theme/Icon/ExternalLink/index.tsx +6 -1
- package/src/theme/Icon/Socials/Bluesky/index.tsx +7 -6
- package/src/theme/Icon/Socials/Bluesky/styles.module.css +14 -0
- package/src/theme/Icon/Socials/Default/index.tsx +6 -6
- package/src/theme/Icon/Socials/Email/index.tsx +28 -0
- package/src/theme/Icon/Socials/LinkedIn/index.tsx +7 -6
- package/src/theme/Icon/Socials/LinkedIn/styles.module.css +14 -0
- package/src/theme/MDXComponents/A/index.tsx +42 -0
- package/src/theme/MDXComponents/A/styles.module.css +20 -0
- package/src/theme/Navbar/Search/styles.module.css +1 -2
- package/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx +3 -2
- package/src/theme/NavbarItem/LocaleDropdownNavbarItem/index.tsx +66 -16
- package/src/theme/Tabs/index.tsx +9 -1
- package/src/theme-classic.d.ts +11 -1
- package/src/theme/MDXComponents/A.tsx +0 -14
- /package/lib/theme/MDXComponents/{A.d.ts → A/index.d.ts} +0 -0
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
useDocsPreferredVersion,
|
|
13
13
|
} from '@docusaurus/plugin-content-docs/client';
|
|
14
14
|
import {translate} from '@docusaurus/Translate';
|
|
15
|
-
import {
|
|
15
|
+
import {useHistorySelector} from '@docusaurus/theme-common';
|
|
16
16
|
import DefaultNavbarItem from '@theme/NavbarItem/DefaultNavbarItem';
|
|
17
17
|
import DropdownNavbarItem from '@theme/NavbarItem/DropdownNavbarItem';
|
|
18
18
|
function getVersionItems(versions, configs) {
|
|
@@ -73,7 +73,8 @@ export default function DocsVersionDropdownNavbarItem({
|
|
|
73
73
|
versions: configs,
|
|
74
74
|
...props
|
|
75
75
|
}) {
|
|
76
|
-
const
|
|
76
|
+
const search = useHistorySelector((history) => history.location.search);
|
|
77
|
+
const hash = useHistorySelector((history) => history.location.hash);
|
|
77
78
|
const activeDocContext = useActiveDocContext(docsPluginId);
|
|
78
79
|
const {savePreferredVersionName} = useDocsPreferredVersion(docsPluginId);
|
|
79
80
|
const versionItems = useVersionItems({docsPluginId, configs});
|
|
@@ -8,33 +8,79 @@ import React from 'react';
|
|
|
8
8
|
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
|
9
9
|
import {useAlternatePageUtils} from '@docusaurus/theme-common/internal';
|
|
10
10
|
import {translate} from '@docusaurus/Translate';
|
|
11
|
-
import {
|
|
11
|
+
import {mergeSearchStrings, useHistorySelector} from '@docusaurus/theme-common';
|
|
12
12
|
import DropdownNavbarItem from '@theme/NavbarItem/DropdownNavbarItem';
|
|
13
13
|
import IconLanguage from '@theme/Icon/Language';
|
|
14
14
|
import styles from './styles.module.css';
|
|
15
|
+
function useLocaleDropdownUtils() {
|
|
16
|
+
const {
|
|
17
|
+
siteConfig,
|
|
18
|
+
i18n: {localeConfigs},
|
|
19
|
+
} = useDocusaurusContext();
|
|
20
|
+
const alternatePageUtils = useAlternatePageUtils();
|
|
21
|
+
const search = useHistorySelector((history) => history.location.search);
|
|
22
|
+
const hash = useHistorySelector((history) => history.location.hash);
|
|
23
|
+
const getLocaleConfig = (locale) => {
|
|
24
|
+
const localeConfig = localeConfigs[locale];
|
|
25
|
+
if (!localeConfig) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
`Docusaurus bug, no locale config found for locale=${locale}`,
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
return localeConfig;
|
|
31
|
+
};
|
|
32
|
+
const getBaseURLForLocale = (locale) => {
|
|
33
|
+
const localeConfig = getLocaleConfig(locale);
|
|
34
|
+
const isSameDomain = localeConfig.url === siteConfig.url;
|
|
35
|
+
if (isSameDomain) {
|
|
36
|
+
// Shorter paths if localized sites are hosted on the same domain
|
|
37
|
+
// This reduces HTML size a bit
|
|
38
|
+
return `pathname://${alternatePageUtils.createUrl({
|
|
39
|
+
locale,
|
|
40
|
+
fullyQualified: false,
|
|
41
|
+
})}`;
|
|
42
|
+
}
|
|
43
|
+
return alternatePageUtils.createUrl({
|
|
44
|
+
locale,
|
|
45
|
+
fullyQualified: true,
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
return {
|
|
49
|
+
getURL: (locale, options) => {
|
|
50
|
+
// We have 2 query strings because
|
|
51
|
+
// - there's the current one
|
|
52
|
+
// - there's one user can provide through navbar config
|
|
53
|
+
// see https://github.com/facebook/docusaurus/pull/8915
|
|
54
|
+
const finalSearch = mergeSearchStrings(
|
|
55
|
+
[search, options.queryString],
|
|
56
|
+
'append',
|
|
57
|
+
);
|
|
58
|
+
return `${getBaseURLForLocale(locale)}${finalSearch}${hash}`;
|
|
59
|
+
},
|
|
60
|
+
getLabel: (locale) => {
|
|
61
|
+
return getLocaleConfig(locale).label;
|
|
62
|
+
},
|
|
63
|
+
getLang: (locale) => {
|
|
64
|
+
return getLocaleConfig(locale).htmlLang;
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
15
68
|
export default function LocaleDropdownNavbarItem({
|
|
16
69
|
mobile,
|
|
17
70
|
dropdownItemsBefore,
|
|
18
71
|
dropdownItemsAfter,
|
|
19
|
-
queryString
|
|
72
|
+
queryString,
|
|
20
73
|
...props
|
|
21
74
|
}) {
|
|
75
|
+
const utils = useLocaleDropdownUtils();
|
|
22
76
|
const {
|
|
23
|
-
i18n: {currentLocale, locales
|
|
77
|
+
i18n: {currentLocale, locales},
|
|
24
78
|
} = useDocusaurusContext();
|
|
25
|
-
const alternatePageUtils = useAlternatePageUtils();
|
|
26
|
-
const {search, hash} = useLocation();
|
|
27
79
|
const localeItems = locales.map((locale) => {
|
|
28
|
-
const baseTo = `pathname://${alternatePageUtils.createUrl({
|
|
29
|
-
locale,
|
|
30
|
-
fullyQualified: false,
|
|
31
|
-
})}`;
|
|
32
|
-
// preserve ?search#hash suffix on locale switches
|
|
33
|
-
const to = `${baseTo}${search}${hash}${queryString}`;
|
|
34
80
|
return {
|
|
35
|
-
label:
|
|
36
|
-
lang:
|
|
37
|
-
to,
|
|
81
|
+
label: utils.getLabel(locale),
|
|
82
|
+
lang: utils.getLang(locale),
|
|
83
|
+
to: utils.getURL(locale, {queryString}),
|
|
38
84
|
target: '_self',
|
|
39
85
|
autoAddBaseUrl: false,
|
|
40
86
|
className:
|
|
@@ -57,7 +103,7 @@ export default function LocaleDropdownNavbarItem({
|
|
|
57
103
|
id: 'theme.navbar.mobileLanguageDropdown.label',
|
|
58
104
|
description: 'The label for the mobile language switcher dropdown',
|
|
59
105
|
})
|
|
60
|
-
:
|
|
106
|
+
: utils.getLabel(currentLocale);
|
|
61
107
|
return (
|
|
62
108
|
<DropdownNavbarItem
|
|
63
109
|
{...props}
|
package/lib/theme/Tabs/index.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import React, {cloneElement} from 'react';
|
|
8
8
|
import clsx from 'clsx';
|
|
9
|
+
import {ThemeClassNames} from '@docusaurus/theme-common';
|
|
9
10
|
import {
|
|
10
11
|
useScrollPositionBlocker,
|
|
11
12
|
useTabs,
|
|
@@ -111,7 +112,14 @@ function TabContent({lazy, children, selectedValue}) {
|
|
|
111
112
|
function TabsComponent(props) {
|
|
112
113
|
const tabs = useTabs(props);
|
|
113
114
|
return (
|
|
114
|
-
<div
|
|
115
|
+
<div
|
|
116
|
+
className={clsx(
|
|
117
|
+
ThemeClassNames.tabs.container,
|
|
118
|
+
// former name kept for backward compatibility
|
|
119
|
+
// see https://github.com/facebook/docusaurus/pull/4086
|
|
120
|
+
'tabs-container',
|
|
121
|
+
styles.tabList,
|
|
122
|
+
)}>
|
|
115
123
|
<TabList {...tabs} {...props} />
|
|
116
124
|
<TabContent {...tabs} {...props} />
|
|
117
125
|
</div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docusaurus/theme-classic",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.9.0-canary-6403",
|
|
4
4
|
"description": "Classic theme for Docusaurus",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "src/theme-classic.d.ts",
|
|
@@ -20,22 +20,21 @@
|
|
|
20
20
|
"copy:watch": "node ../../admin/scripts/copyUntypedFiles.js --watch"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@docusaurus/core": "3.
|
|
24
|
-
"@docusaurus/logger": "3.
|
|
25
|
-
"@docusaurus/mdx-loader": "3.
|
|
26
|
-
"@docusaurus/module-type-aliases": "3.
|
|
27
|
-
"@docusaurus/plugin-content-blog": "3.
|
|
28
|
-
"@docusaurus/plugin-content-docs": "3.
|
|
29
|
-
"@docusaurus/plugin-content-pages": "3.
|
|
30
|
-
"@docusaurus/theme-common": "3.
|
|
31
|
-
"@docusaurus/theme-translations": "3.
|
|
32
|
-
"@docusaurus/types": "3.
|
|
33
|
-
"@docusaurus/utils": "3.
|
|
34
|
-
"@docusaurus/utils-common": "3.
|
|
35
|
-
"@docusaurus/utils-validation": "3.
|
|
23
|
+
"@docusaurus/core": "3.9.0-canary-6403",
|
|
24
|
+
"@docusaurus/logger": "3.9.0-canary-6403",
|
|
25
|
+
"@docusaurus/mdx-loader": "3.9.0-canary-6403",
|
|
26
|
+
"@docusaurus/module-type-aliases": "3.9.0-canary-6403",
|
|
27
|
+
"@docusaurus/plugin-content-blog": "3.9.0-canary-6403",
|
|
28
|
+
"@docusaurus/plugin-content-docs": "3.9.0-canary-6403",
|
|
29
|
+
"@docusaurus/plugin-content-pages": "3.9.0-canary-6403",
|
|
30
|
+
"@docusaurus/theme-common": "3.9.0-canary-6403",
|
|
31
|
+
"@docusaurus/theme-translations": "3.9.0-canary-6403",
|
|
32
|
+
"@docusaurus/types": "3.9.0-canary-6403",
|
|
33
|
+
"@docusaurus/utils": "3.9.0-canary-6403",
|
|
34
|
+
"@docusaurus/utils-common": "3.9.0-canary-6403",
|
|
35
|
+
"@docusaurus/utils-validation": "3.9.0-canary-6403",
|
|
36
36
|
"@mdx-js/react": "^3.0.0",
|
|
37
37
|
"clsx": "^2.0.0",
|
|
38
|
-
"copy-text-to-clipboard": "^3.2.0",
|
|
39
38
|
"infima": "0.2.0-alpha.45",
|
|
40
39
|
"lodash": "^4.17.21",
|
|
41
40
|
"nprogress": "^0.2.0",
|
|
@@ -60,7 +59,7 @@
|
|
|
60
59
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
61
60
|
},
|
|
62
61
|
"engines": {
|
|
63
|
-
"node": ">=
|
|
62
|
+
"node": ">=20.0"
|
|
64
63
|
},
|
|
65
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "22c95f8cb5c070ce47e115be376fa9b828dd54df"
|
|
66
65
|
}
|
package/src/index.ts
CHANGED
package/src/inlineScripts.ts
CHANGED
|
@@ -15,7 +15,7 @@ const ThemeQueryStringKey = 'docusaurus-theme';
|
|
|
15
15
|
const DataQueryStringPrefixKey = 'docusaurus-data-';
|
|
16
16
|
|
|
17
17
|
export function getThemeInlineScript({
|
|
18
|
-
colorMode: {defaultMode, respectPrefersColorScheme},
|
|
18
|
+
colorMode: {disableSwitch, defaultMode, respectPrefersColorScheme},
|
|
19
19
|
siteStorage,
|
|
20
20
|
}: {
|
|
21
21
|
colorMode: ThemeConfig['colorMode'];
|
|
@@ -25,28 +25,35 @@ export function getThemeInlineScript({
|
|
|
25
25
|
// Make sure the key is the same as the one in the color mode React context
|
|
26
26
|
// Currently defined in: `docusaurus-theme-common/src/contexts/colorMode.tsx`
|
|
27
27
|
const themeStorageKey = `theme${siteStorage.namespace}`;
|
|
28
|
+
const isThemeUserConfigurable = !disableSwitch;
|
|
28
29
|
|
|
29
30
|
/* language=js */
|
|
30
31
|
return `(function() {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
function getQueryStringTheme() {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
32
|
+
function getSystemColorMode() {
|
|
33
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
34
|
+
}
|
|
35
|
+
${
|
|
36
|
+
isThemeUserConfigurable
|
|
37
|
+
? ` function getQueryStringTheme() {
|
|
38
|
+
try {
|
|
39
|
+
return new URLSearchParams(window.location.search).get('${ThemeQueryStringKey}')
|
|
40
|
+
} catch (e) {}
|
|
41
|
+
}
|
|
42
|
+
function getStoredTheme() {
|
|
43
|
+
try {
|
|
44
|
+
return window['${siteStorage.type}'].getItem('${themeStorageKey}');
|
|
45
|
+
} catch (err) {}
|
|
46
|
+
}
|
|
47
|
+
var initialTheme = getQueryStringTheme() || getStoredTheme();`
|
|
48
|
+
: ' var initialTheme;'
|
|
49
|
+
}
|
|
50
|
+
document.documentElement.setAttribute('data-theme', initialTheme || ${
|
|
51
|
+
respectPrefersColorScheme ? 'getSystemColorMode()' : `'${defaultMode}'`
|
|
52
|
+
});
|
|
53
|
+
document.documentElement.setAttribute('data-theme-choice', initialTheme || ${
|
|
54
|
+
respectPrefersColorScheme ? `'system'` : `'${defaultMode}'`
|
|
55
|
+
});
|
|
56
|
+
})();`;
|
|
50
57
|
}
|
|
51
58
|
|
|
52
59
|
/* language=js */
|
|
@@ -23,6 +23,7 @@ import Threads from '@theme/Icon/Socials/Threads';
|
|
|
23
23
|
import Youtube from '@theme/Icon/Socials/YouTube';
|
|
24
24
|
import Mastodon from '@theme/Icon/Socials/Mastodon';
|
|
25
25
|
import Twitch from '@theme/Icon/Socials/Twitch';
|
|
26
|
+
import Email from '@theme/Icon/Socials/Email';
|
|
26
27
|
|
|
27
28
|
import styles from './styles.module.css';
|
|
28
29
|
|
|
@@ -42,6 +43,7 @@ const SocialPlatformConfigs: Record<string, SocialPlatformConfig> = {
|
|
|
42
43
|
mastodon: {Icon: Mastodon, label: 'Mastodon'},
|
|
43
44
|
youtube: {Icon: Youtube, label: 'YouTube'},
|
|
44
45
|
twitch: {Icon: Twitch, label: 'Twitch'},
|
|
46
|
+
email: {Icon: Email, label: 'Email'},
|
|
45
47
|
};
|
|
46
48
|
|
|
47
49
|
function getSocialPlatformConfig(platformKey: string): SocialPlatformConfig {
|
|
@@ -57,7 +59,7 @@ function SocialLink({platform, link}: {platform: string; link: string}) {
|
|
|
57
59
|
const {Icon, label} = getSocialPlatformConfig(platform);
|
|
58
60
|
return (
|
|
59
61
|
<Link className={styles.authorSocialLink} href={link} title={label}>
|
|
60
|
-
<Icon className={clsx(styles.
|
|
62
|
+
<Icon className={clsx(styles.authorSocialIcon)} />
|
|
61
63
|
</Link>
|
|
62
64
|
);
|
|
63
65
|
}
|
|
@@ -30,10 +30,14 @@ function AuthorTitle({title}: {title: string}) {
|
|
|
30
30
|
|
|
31
31
|
function AuthorName({name, as}: {name: string; as: Props['as']}) {
|
|
32
32
|
if (!as) {
|
|
33
|
-
return
|
|
33
|
+
return (
|
|
34
|
+
<span className={styles.authorName} translate="no">
|
|
35
|
+
{name}
|
|
36
|
+
</span>
|
|
37
|
+
);
|
|
34
38
|
} else {
|
|
35
39
|
return (
|
|
36
|
-
<Heading as={as} className={styles.authorName}>
|
|
40
|
+
<Heading as={as} className={styles.authorName} translate="no">
|
|
37
41
|
{name}
|
|
38
42
|
</Heading>
|
|
39
43
|
);
|
|
@@ -13,7 +13,6 @@ import React, {
|
|
|
13
13
|
type ReactNode,
|
|
14
14
|
} from 'react';
|
|
15
15
|
import clsx from 'clsx';
|
|
16
|
-
import copy from 'copy-text-to-clipboard';
|
|
17
16
|
import {translate} from '@docusaurus/Translate';
|
|
18
17
|
import {useCodeBlockContext} from '@docusaurus/theme-common/internal';
|
|
19
18
|
import Button from '@theme/CodeBlock/Buttons/Button';
|
|
@@ -53,11 +52,12 @@ function useCopyButton() {
|
|
|
53
52
|
const copyTimeout = useRef<number | undefined>(undefined);
|
|
54
53
|
|
|
55
54
|
const copyCode = useCallback(() => {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
navigator.clipboard.writeText(code).then(() => {
|
|
56
|
+
setIsCopied(true);
|
|
57
|
+
copyTimeout.current = window.setTimeout(() => {
|
|
58
|
+
setIsCopied(false);
|
|
59
|
+
}, 1000);
|
|
60
|
+
});
|
|
61
61
|
}, [code]);
|
|
62
62
|
|
|
63
63
|
useEffect(() => () => window.clearTimeout(copyTimeout.current), []);
|
|
@@ -24,31 +24,51 @@ import {
|
|
|
24
24
|
isActiveSidebarItem,
|
|
25
25
|
findFirstSidebarItemLink,
|
|
26
26
|
useDocSidebarItemsExpandedState,
|
|
27
|
+
useVisibleSidebarItems,
|
|
27
28
|
} from '@docusaurus/plugin-content-docs/client';
|
|
28
29
|
import Link from '@docusaurus/Link';
|
|
29
30
|
import {translate} from '@docusaurus/Translate';
|
|
30
31
|
import useIsBrowser from '@docusaurus/useIsBrowser';
|
|
31
32
|
import DocSidebarItems from '@theme/DocSidebarItems';
|
|
33
|
+
import DocSidebarItemLink from '@theme/DocSidebarItem/Link';
|
|
32
34
|
import type {Props} from '@theme/DocSidebarItem/Category';
|
|
33
35
|
|
|
36
|
+
import type {
|
|
37
|
+
PropSidebarItemCategory,
|
|
38
|
+
PropSidebarItemLink,
|
|
39
|
+
} from '@docusaurus/plugin-content-docs';
|
|
40
|
+
import styles from './styles.module.css';
|
|
41
|
+
|
|
34
42
|
// If we navigate to a category and it becomes active, it should automatically
|
|
35
43
|
// expand itself
|
|
36
44
|
function useAutoExpandActiveCategory({
|
|
37
45
|
isActive,
|
|
38
46
|
collapsed,
|
|
39
47
|
updateCollapsed,
|
|
48
|
+
activePath,
|
|
40
49
|
}: {
|
|
41
50
|
isActive: boolean;
|
|
42
51
|
collapsed: boolean;
|
|
43
52
|
updateCollapsed: (b: boolean) => void;
|
|
53
|
+
activePath: string;
|
|
44
54
|
}) {
|
|
45
55
|
const wasActive = usePrevious(isActive);
|
|
56
|
+
const previousActivePath = usePrevious(activePath);
|
|
46
57
|
useEffect(() => {
|
|
47
58
|
const justBecameActive = isActive && !wasActive;
|
|
48
|
-
|
|
59
|
+
const stillActiveButPathChanged =
|
|
60
|
+
isActive && wasActive && activePath !== previousActivePath;
|
|
61
|
+
if ((justBecameActive || stillActiveButPathChanged) && collapsed) {
|
|
49
62
|
updateCollapsed(false);
|
|
50
63
|
}
|
|
51
|
-
}, [
|
|
64
|
+
}, [
|
|
65
|
+
isActive,
|
|
66
|
+
wasActive,
|
|
67
|
+
collapsed,
|
|
68
|
+
updateCollapsed,
|
|
69
|
+
activePath,
|
|
70
|
+
previousActivePath,
|
|
71
|
+
]);
|
|
52
72
|
}
|
|
53
73
|
|
|
54
74
|
/**
|
|
@@ -114,7 +134,56 @@ function CollapseButton({
|
|
|
114
134
|
);
|
|
115
135
|
}
|
|
116
136
|
|
|
117
|
-
|
|
137
|
+
function CategoryLinkLabel({label}: {label: string}) {
|
|
138
|
+
return (
|
|
139
|
+
<span title={label} className={styles.categoryLinkLabel}>
|
|
140
|
+
{label}
|
|
141
|
+
</span>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export default function DocSidebarItemCategory(props: Props): ReactNode {
|
|
146
|
+
const visibleChildren = useVisibleSidebarItems(
|
|
147
|
+
props.item.items,
|
|
148
|
+
props.activePath,
|
|
149
|
+
);
|
|
150
|
+
if (visibleChildren.length === 0) {
|
|
151
|
+
return <DocSidebarItemCategoryEmpty {...props} />;
|
|
152
|
+
} else {
|
|
153
|
+
return <DocSidebarItemCategoryCollapsible {...props} />;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function isCategoryWithHref(
|
|
158
|
+
category: PropSidebarItemCategory,
|
|
159
|
+
): category is PropSidebarItemCategory & {href: string} {
|
|
160
|
+
return typeof category.href === 'string';
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// If a category doesn't have any visible children, we render it as a link
|
|
164
|
+
function DocSidebarItemCategoryEmpty({item, ...props}: Props): ReactNode {
|
|
165
|
+
// If the category has no link, we don't render anything
|
|
166
|
+
// It's not super useful to render a category you can't open nor click
|
|
167
|
+
if (!isCategoryWithHref(item)) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
// We remove props that don't make sense for a link and forward the rest
|
|
171
|
+
const {
|
|
172
|
+
type,
|
|
173
|
+
collapsed,
|
|
174
|
+
collapsible,
|
|
175
|
+
items,
|
|
176
|
+
linkUnlisted,
|
|
177
|
+
...forwardableProps
|
|
178
|
+
} = item;
|
|
179
|
+
const linkItem: PropSidebarItemLink = {
|
|
180
|
+
type: 'link',
|
|
181
|
+
...forwardableProps,
|
|
182
|
+
};
|
|
183
|
+
return <DocSidebarItemLink item={linkItem} {...props} />;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function DocSidebarItemCategoryCollapsible({
|
|
118
187
|
item,
|
|
119
188
|
onItemClick,
|
|
120
189
|
activePath,
|
|
@@ -150,7 +219,12 @@ export default function DocSidebarItemCategory({
|
|
|
150
219
|
setExpandedItem(toCollapsed ? null : index);
|
|
151
220
|
setCollapsed(toCollapsed);
|
|
152
221
|
};
|
|
153
|
-
useAutoExpandActiveCategory({
|
|
222
|
+
useAutoExpandActiveCategory({
|
|
223
|
+
isActive,
|
|
224
|
+
collapsed,
|
|
225
|
+
updateCollapsed,
|
|
226
|
+
activePath,
|
|
227
|
+
});
|
|
154
228
|
useEffect(() => {
|
|
155
229
|
if (
|
|
156
230
|
collapsible &&
|
|
@@ -162,6 +236,29 @@ export default function DocSidebarItemCategory({
|
|
|
162
236
|
}
|
|
163
237
|
}, [collapsible, expandedItem, index, setCollapsed, autoCollapseCategories]);
|
|
164
238
|
|
|
239
|
+
const handleItemClick: ComponentProps<'a'>['onClick'] = (e) => {
|
|
240
|
+
onItemClick?.(item);
|
|
241
|
+
if (collapsible) {
|
|
242
|
+
if (href) {
|
|
243
|
+
// When already on the category's page, we collapse it
|
|
244
|
+
// We don't use "isActive" because it would collapse the
|
|
245
|
+
// category even when we browse a children element
|
|
246
|
+
// See https://github.com/facebook/docusaurus/issues/11213
|
|
247
|
+
if (isCurrentPage) {
|
|
248
|
+
e.preventDefault();
|
|
249
|
+
updateCollapsed();
|
|
250
|
+
} else {
|
|
251
|
+
// When navigating to a new category, we always expand
|
|
252
|
+
// see https://github.com/facebook/docusaurus/issues/10854#issuecomment-2609616182
|
|
253
|
+
updateCollapsed(false);
|
|
254
|
+
}
|
|
255
|
+
} else {
|
|
256
|
+
e.preventDefault();
|
|
257
|
+
updateCollapsed();
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
|
|
165
262
|
return (
|
|
166
263
|
<li
|
|
167
264
|
className={clsx(
|
|
@@ -178,43 +275,18 @@ export default function DocSidebarItemCategory({
|
|
|
178
275
|
'menu__list-item-collapsible--active': isCurrentPage,
|
|
179
276
|
})}>
|
|
180
277
|
<Link
|
|
181
|
-
className={clsx('menu__link', {
|
|
278
|
+
className={clsx(styles.categoryLink, 'menu__link', {
|
|
182
279
|
'menu__link--sublist': collapsible,
|
|
183
280
|
'menu__link--sublist-caret': !href && collapsible,
|
|
184
281
|
'menu__link--active': isActive,
|
|
185
282
|
})}
|
|
186
|
-
onClick={
|
|
187
|
-
collapsible
|
|
188
|
-
? (e) => {
|
|
189
|
-
onItemClick?.(item);
|
|
190
|
-
if (href) {
|
|
191
|
-
// When already on the category's page, we collapse it
|
|
192
|
-
// We don't use "isActive" because it would collapse the
|
|
193
|
-
// category even when we browse a children element
|
|
194
|
-
// See https://github.com/facebook/docusaurus/issues/11213
|
|
195
|
-
if (isCurrentPage) {
|
|
196
|
-
e.preventDefault();
|
|
197
|
-
updateCollapsed();
|
|
198
|
-
} else {
|
|
199
|
-
// When navigating to a new category, we always expand
|
|
200
|
-
// see https://github.com/facebook/docusaurus/issues/10854#issuecomment-2609616182
|
|
201
|
-
updateCollapsed(false);
|
|
202
|
-
}
|
|
203
|
-
} else {
|
|
204
|
-
e.preventDefault();
|
|
205
|
-
updateCollapsed();
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
: () => {
|
|
209
|
-
onItemClick?.(item);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
283
|
+
onClick={handleItemClick}
|
|
212
284
|
aria-current={isCurrentPage ? 'page' : undefined}
|
|
213
285
|
role={collapsible && !href ? 'button' : undefined}
|
|
214
286
|
aria-expanded={collapsible && !href ? !collapsed : undefined}
|
|
215
287
|
href={collapsible ? hrefWithSSRFallback ?? '#' : hrefWithSSRFallback}
|
|
216
288
|
{...props}>
|
|
217
|
-
{label}
|
|
289
|
+
<CategoryLinkLabel label={label} />
|
|
218
290
|
</Link>
|
|
219
291
|
{href && collapsible && (
|
|
220
292
|
<CollapseButton
|
|
@@ -0,0 +1,26 @@
|
|
|
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
|
+
.categoryLink {
|
|
9
|
+
overflow: hidden;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
TODO merge this logic back in Infima?
|
|
14
|
+
*/
|
|
15
|
+
:global(.menu__link--sublist-caret)::after {
|
|
16
|
+
margin-left: var(--ifm-menu-link-padding-vertical);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.categoryLinkLabel {
|
|
20
|
+
flex: 1;
|
|
21
|
+
overflow: hidden;
|
|
22
|
+
display: -webkit-box;
|
|
23
|
+
line-clamp: 2;
|
|
24
|
+
-webkit-box-orient: vertical;
|
|
25
|
+
-webkit-line-clamp: 2;
|
|
26
|
+
}
|
|
@@ -16,6 +16,14 @@ import type {Props} from '@theme/DocSidebarItem/Link';
|
|
|
16
16
|
|
|
17
17
|
import styles from './styles.module.css';
|
|
18
18
|
|
|
19
|
+
function LinkLabel({label}: {label: string}) {
|
|
20
|
+
return (
|
|
21
|
+
<span title={label} className={styles.linkLabel}>
|
|
22
|
+
{label}
|
|
23
|
+
</span>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
19
27
|
export default function DocSidebarItemLink({
|
|
20
28
|
item,
|
|
21
29
|
onItemClick,
|
|
@@ -51,7 +59,7 @@ export default function DocSidebarItemLink({
|
|
|
51
59
|
onClick: onItemClick ? () => onItemClick(item) : undefined,
|
|
52
60
|
})}
|
|
53
61
|
{...props}>
|
|
54
|
-
{label}
|
|
62
|
+
<LinkLabel label={label} />
|
|
55
63
|
{!isInternalLink && <IconExternalLink />}
|
|
56
64
|
</Link>
|
|
57
65
|
</li>
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import React, {type ReactNode} from 'react';
|
|
9
|
+
import {translate} from '@docusaurus/Translate';
|
|
9
10
|
import type {Props} from '@theme/Icon/ExternalLink';
|
|
10
11
|
|
|
11
12
|
import styles from './styles.module.css';
|
|
@@ -22,7 +23,11 @@ export default function IconExternalLink({
|
|
|
22
23
|
<svg
|
|
23
24
|
width={width}
|
|
24
25
|
height={height}
|
|
25
|
-
aria-
|
|
26
|
+
aria-label={translate({
|
|
27
|
+
id: 'theme.IconExternalLink.ariaLabel',
|
|
28
|
+
message: '(opens in new tab)',
|
|
29
|
+
description: 'The ARIA label for the external link icon',
|
|
30
|
+
})}
|
|
26
31
|
className={styles.iconExternalLink}>
|
|
27
32
|
<use href={svgSprite} />
|
|
28
33
|
</svg>
|
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type {SVGProps, ReactNode} from 'react';
|
|
8
|
+
import type {SVGProps, ReactNode, CSSProperties} from 'react';
|
|
9
|
+
import clsx from 'clsx';
|
|
10
|
+
import styles from './styles.module.css';
|
|
9
11
|
|
|
10
12
|
// SVG Source: https://svgl.app/
|
|
11
13
|
function Bluesky(props: SVGProps<SVGSVGElement>): ReactNode {
|
|
@@ -16,11 +18,10 @@ function Bluesky(props: SVGProps<SVGSVGElement>): ReactNode {
|
|
|
16
18
|
height="1em"
|
|
17
19
|
preserveAspectRatio="xMidYMid"
|
|
18
20
|
viewBox="0 0 256 226"
|
|
19
|
-
{
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
/>
|
|
21
|
+
style={{'--dark': '#0085ff', '--light': '#0085ff'} as CSSProperties}
|
|
22
|
+
{...props}
|
|
23
|
+
className={clsx(props.className, styles.blueskySvg)}>
|
|
24
|
+
<path d="M55.491 15.172c29.35 22.035 60.917 66.712 72.509 90.686 11.592-23.974 43.159-68.651 72.509-90.686C221.686-.727 256-13.028 256 26.116c0 7.818-4.482 65.674-7.111 75.068-9.138 32.654-42.436 40.983-72.057 35.942 51.775 8.812 64.946 38 36.501 67.187-54.021 55.433-77.644-13.908-83.696-31.676-1.11-3.257-1.63-4.78-1.637-3.485-.008-1.296-.527.228-1.637 3.485-6.052 17.768-29.675 87.11-83.696 31.676-28.445-29.187-15.274-58.375 36.5-67.187-29.62 5.041-62.918-3.288-72.056-35.942C4.482 91.79 0 33.934 0 26.116 0-13.028 34.314-.727 55.491 15.172Z" />
|
|
24
25
|
</svg>
|
|
25
26
|
);
|
|
26
27
|
}
|