u
This commit is contained in:
1 parent
ad87788261
commit
17e07df91a
12 files changed
+604
-26
No files matched your search
@@ -138,9 +138,3 @@ const config: Config = {
|
||||
}
|
||||
|
||||
export default config
|
||||
|
||||
/**
|
||||
*
|
||||
* <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path d="M16 12C16 14.2091 14.2091 16 12 16C9.79086 16 8 14.2091 8 12C8 9.79086 9.79086 8 12 8C14.2091 8 16 9.79086 16 12ZM16 12V13.5C16 14.8807 17.1193 16 18.5 16V16C19.8807 16 21 14.8807 21 13.5V12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21H16" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path> </g></svg>
|
||||
*
|
||||
*/
|
||||
@@ -7,4 +7,7 @@ export default antfu({
|
||||
html: true,
|
||||
markdown: 'prettier',
|
||||
},
|
||||
extends: [
|
||||
'plugin:@docusaurus/recommended',
|
||||
],
|
||||
})
|
||||
@@ -28,6 +28,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^6.7.3",
|
||||
"@docusaurus/eslint-plugin": "^3.9.2",
|
||||
"@docusaurus/module-type-aliases": "3.9.2",
|
||||
"@docusaurus/tsconfig": "3.9.2",
|
||||
"@docusaurus/types": "3.9.2",
|
||||
|
||||
Generated
+107
@@ -33,6 +33,9 @@ importers:
|
||||
'@antfu/eslint-config':
|
||||
specifier: ^6.7.3
|
||||
version: 6.7.3(@eslint-react/eslint-plugin@2.5.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.6.3))(@vue/compiler-sfc@3.5.26)(eslint-plugin-format@1.2.0(eslint@9.39.2(jiti@1.21.7)))(eslint-plugin-react-hooks@7.0.1(eslint@9.39.2(jiti@1.21.7)))(eslint-plugin-react-refresh@0.4.26(eslint@9.39.2(jiti@1.21.7)))(eslint@9.39.2(jiti@1.21.7))(typescript@5.6.3)
|
||||
'@docusaurus/eslint-plugin':
|
||||
specifier: ^3.9.2
|
||||
version: 3.9.2(eslint@9.39.2(jiti@1.21.7))(typescript@5.6.3)
|
||||
'@docusaurus/module-type-aliases':
|
||||
specifier: 3.9.2
|
||||
version: 3.9.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
@@ -1164,6 +1167,12 @@ packages:
|
||||
resolution: {integrity: sha512-8gBKup94aGttRduABsj7bpPFTX7kbwu+xh3K9NMCF5K4bWBqTFYW+REKHF6iBVDHRJ4grZdIPbvkiHd/XNKRMQ==}
|
||||
engines: {node: '>=20.0'}
|
||||
|
||||
'@docusaurus/eslint-plugin@3.9.2':
|
||||
resolution: {integrity: sha512-LnCrmrR4EtzpSiq6aoSfiY0Lf8P0WslGbBFZJ0olKXJIMxey8dpKevT1K/+tN87Lbn2H/VrdGGSPGlfVKmihAQ==}
|
||||
engines: {node: '>=20.0'}
|
||||
peerDependencies:
|
||||
eslint: '>=6'
|
||||
|
||||
'@docusaurus/logger@3.9.2':
|
||||
resolution: {integrity: sha512-/SVCc57ByARzGSU60c50rMyQlBuMIJCjcsJlkphxY6B0GV4UH3tcA1994N8fFfbJ9kX3jIBe/xg3XP5qBtGDbA==}
|
||||
engines: {node: '>=20.0'}
|
||||
@@ -1799,6 +1808,9 @@ packages:
|
||||
'@types/sax@1.2.7':
|
||||
resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==}
|
||||
|
||||
'@types/semver@7.7.1':
|
||||
resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==}
|
||||
|
||||
'@types/send@0.17.6':
|
||||
resolution: {integrity: sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==}
|
||||
|
||||
@@ -1850,6 +1862,10 @@ packages:
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
'@typescript-eslint/scope-manager@5.62.0':
|
||||
resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
|
||||
'@typescript-eslint/scope-manager@8.52.0':
|
||||
resolution: {integrity: sha512-ixxqmmCcc1Nf8S0mS0TkJ/3LKcC8mruYJPOU6Ia2F/zUUR4pApW7LzrpU3JmtePbRUTes9bEqRc1Gg4iyRnDzA==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
@@ -1867,16 +1883,35 @@ packages:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
'@typescript-eslint/types@5.62.0':
|
||||
resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
|
||||
'@typescript-eslint/types@8.52.0':
|
||||
resolution: {integrity: sha512-LWQV1V4q9V4cT4H5JCIx3481iIFxH1UkVk+ZkGGAV1ZGcjGI9IoFOfg3O6ywz8QqCDEp7Inlg6kovMofsNRaGg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/typescript-estree@5.62.0':
|
||||
resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
'@typescript-eslint/typescript-estree@8.52.0':
|
||||
resolution: {integrity: sha512-XP3LClsCc0FsTK5/frGjolyADTh3QmsLp6nKd476xNI9CsSsLnmn4f0jrzNoAulmxlmNIpeXuHYeEQv61Q6qeQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
'@typescript-eslint/utils@5.62.0':
|
||||
resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||
|
||||
'@typescript-eslint/utils@8.52.0':
|
||||
resolution: {integrity: sha512-wYndVMWkweqHpEpwPhwqE2lnD2DxC6WVLupU/DOt/0/v+/+iQbbzO3jOHjmBMnhu0DgLULvOaU4h4pwHYi2oRQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
@@ -1884,6 +1919,10 @@ packages:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
'@typescript-eslint/visitor-keys@5.62.0':
|
||||
resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
|
||||
'@typescript-eslint/visitor-keys@8.52.0':
|
||||
resolution: {integrity: sha512-ink3/Zofus34nmBsPjow63FP5M7IGff0RKAgqR6+CFpdk22M7aLwC9gOcLGYqr7MczLPzZVERW9hRog3O4n1sQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
@@ -5588,9 +5627,18 @@ packages:
|
||||
ts-pattern@5.9.0:
|
||||
resolution: {integrity: sha512-6s5V71mX8qBUmlgbrfL33xDUwO0fq48rxAu2LBE11WBeGdpCPOsXksQbZJHvHwhrd3QjUusd3mAOM5Gg0mFBLg==}
|
||||
|
||||
tslib@1.14.1:
|
||||
resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
|
||||
|
||||
tslib@2.8.1:
|
||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||
|
||||
tsutils@3.21.0:
|
||||
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
|
||||
engines: {node: '>= 6'}
|
||||
peerDependencies:
|
||||
typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
|
||||
|
||||
type-check@0.4.0:
|
||||
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
@@ -7354,6 +7402,15 @@ snapshots:
|
||||
postcss-sort-media-queries: 5.2.0(postcss@8.5.6)
|
||||
tslib: 2.8.1
|
||||
|
||||
'@docusaurus/eslint-plugin@3.9.2(eslint@9.39.2(jiti@1.21.7))(typescript@5.6.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/utils': 5.62.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.6.3)
|
||||
eslint: 9.39.2(jiti@1.21.7)
|
||||
tslib: 2.8.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
'@docusaurus/logger@3.9.2':
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
@@ -8572,6 +8629,8 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/node': 17.0.45
|
||||
|
||||
'@types/semver@7.7.1': {}
|
||||
|
||||
'@types/send@0.17.6':
|
||||
dependencies:
|
||||
'@types/mime': 1.3.5
|
||||
@@ -8646,6 +8705,11 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/scope-manager@5.62.0':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 5.62.0
|
||||
'@typescript-eslint/visitor-keys': 5.62.0
|
||||
|
||||
'@typescript-eslint/scope-manager@8.52.0':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.52.0
|
||||
@@ -8667,8 +8731,24 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/types@5.62.0': {}
|
||||
|
||||
'@typescript-eslint/types@8.52.0': {}
|
||||
|
||||
'@typescript-eslint/typescript-estree@5.62.0(typescript@5.6.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 5.62.0
|
||||
'@typescript-eslint/visitor-keys': 5.62.0
|
||||
debug: 4.4.3
|
||||
globby: 11.1.0
|
||||
is-glob: 4.0.3
|
||||
semver: 7.7.3
|
||||
tsutils: 3.21.0(typescript@5.6.3)
|
||||
optionalDependencies:
|
||||
typescript: 5.6.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/typescript-estree@8.52.0(typescript@5.6.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/project-service': 8.52.0(typescript@5.6.3)
|
||||
@@ -8684,6 +8764,21 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/utils@5.62.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.6.3)':
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@1.21.7))
|
||||
'@types/json-schema': 7.0.15
|
||||
'@types/semver': 7.7.1
|
||||
'@typescript-eslint/scope-manager': 5.62.0
|
||||
'@typescript-eslint/types': 5.62.0
|
||||
'@typescript-eslint/typescript-estree': 5.62.0(typescript@5.6.3)
|
||||
eslint: 9.39.2(jiti@1.21.7)
|
||||
eslint-scope: 5.1.1
|
||||
semver: 7.7.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
'@typescript-eslint/utils@8.52.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.6.3)':
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@1.21.7))
|
||||
@@ -8695,6 +8790,11 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/visitor-keys@5.62.0':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 5.62.0
|
||||
eslint-visitor-keys: 3.4.3
|
||||
|
||||
'@typescript-eslint/visitor-keys@8.52.0':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.52.0
|
||||
@@ -13052,8 +13152,15 @@ snapshots:
|
||||
|
||||
ts-pattern@5.9.0: {}
|
||||
|
||||
tslib@1.14.1: {}
|
||||
|
||||
tslib@2.8.1: {}
|
||||
|
||||
tsutils@3.21.0(typescript@5.6.3):
|
||||
dependencies:
|
||||
tslib: 1.14.1
|
||||
typescript: 5.6.3
|
||||
|
||||
type-check@0.4.0:
|
||||
dependencies:
|
||||
prelude-ls: 1.2.1
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import type { ReactNode } from "react";
|
||||
import clsx from "clsx";
|
||||
import Heading from "@theme/Heading";
|
||||
import styles from "./styles.module.css";
|
||||
import type { ReactNode } from 'react'
|
||||
import Heading from '@theme/Heading'
|
||||
import clsx from 'clsx'
|
||||
import styles from './styles.module.css'
|
||||
|
||||
type FeatureItem = {
|
||||
title: string;
|
||||
Svg: React.ComponentType<React.ComponentProps<"svg">>;
|
||||
description: ReactNode;
|
||||
};
|
||||
interface FeatureItem {
|
||||
title: string
|
||||
Svg: React.ComponentType<React.ComponentProps<'svg'>>
|
||||
description: ReactNode
|
||||
}
|
||||
|
||||
const FeatureList: FeatureItem[] = [
|
||||
{
|
||||
title: "Easy to Use",
|
||||
Svg: require("@site/static/img/undraw_docusaurus_mountain.svg").default,
|
||||
title: 'Easy to Use',
|
||||
Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default,
|
||||
description: (
|
||||
<>
|
||||
Docusaurus was designed from the ground up to be easily installed and used to get your
|
||||
@@ -21,18 +21,22 @@ const FeatureList: FeatureItem[] = [
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Focus on What Matters",
|
||||
Svg: require("@site/static/img/undraw_docusaurus_tree.svg").default,
|
||||
title: 'Focus on What Matters',
|
||||
Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default,
|
||||
description: (
|
||||
<>
|
||||
Docusaurus lets you focus on your docs, and we'll do the chores. Go ahead and move your
|
||||
docs into the <code>docs</code> directory.
|
||||
docs into the
|
||||
{' '}
|
||||
<code>docs</code>
|
||||
{' '}
|
||||
directory.
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Powered by React",
|
||||
Svg: require("@site/static/img/undraw_docusaurus_react.svg").default,
|
||||
title: 'Powered by React',
|
||||
Svg: require('@site/static/img/undraw_docusaurus_react.svg').default,
|
||||
description: (
|
||||
<>
|
||||
Extend or customize your website layout by reusing React. Docusaurus can be extended while
|
||||
@@ -40,11 +44,11 @@ const FeatureList: FeatureItem[] = [
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
]
|
||||
|
||||
function Feature({ title, Svg, description }: FeatureItem) {
|
||||
return (
|
||||
<div className={clsx("col col--4")}>
|
||||
<div className={clsx('col col--4')}>
|
||||
<div className="text--center">
|
||||
<Svg className={styles.featureSvg} role="img" />
|
||||
</div>
|
||||
@@ -53,7 +57,7 @@ function Feature({ title, Svg, description }: FeatureItem) {
|
||||
<p>{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
export default function HomepageFeatures(): ReactNode {
|
||||
@@ -67,5 +71,5 @@ export default function HomepageFeatures(): ReactNode {
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import React, {useState, useRef, useEffect} from 'react';
|
||||
import clsx from 'clsx';
|
||||
import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink';
|
||||
import NavbarItem from '@theme/NavbarItem';
|
||||
export default function DropdownNavbarItemDesktop({
|
||||
items,
|
||||
position,
|
||||
className,
|
||||
onClick,
|
||||
...props
|
||||
}) {
|
||||
const dropdownRef = useRef(null);
|
||||
const [showDropdown, setShowDropdown] = useState(false);
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event) => {
|
||||
if (!dropdownRef.current || dropdownRef.current.contains(event.target)) {
|
||||
return;
|
||||
}
|
||||
setShowDropdown(false);
|
||||
};
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
document.addEventListener('touchstart', handleClickOutside);
|
||||
document.addEventListener('focusin', handleClickOutside);
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside);
|
||||
document.removeEventListener('touchstart', handleClickOutside);
|
||||
document.removeEventListener('focusin', handleClickOutside);
|
||||
};
|
||||
}, [dropdownRef]);
|
||||
return (
|
||||
<div
|
||||
ref={dropdownRef}
|
||||
className={clsx('navbar__item', 'dropdown', 'dropdown--hoverable', {
|
||||
'dropdown--right': position === 'right',
|
||||
'dropdown--show': showDropdown,
|
||||
})}>
|
||||
<NavbarNavLink
|
||||
aria-haspopup="true"
|
||||
aria-expanded={showDropdown}
|
||||
role="button"
|
||||
// # hash permits to make the <a> tag focusable in case no link target
|
||||
// See https://github.com/facebook/docusaurus/pull/6003
|
||||
// There's probably a better solution though...
|
||||
href={props.to ? undefined : '#'}
|
||||
className={clsx('navbar__link', className)}
|
||||
{...props}
|
||||
onClick={props.to ? undefined : (e) => e.preventDefault()}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
setShowDropdown(!showDropdown);
|
||||
}
|
||||
}}>
|
||||
{props.children ?? props.label}
|
||||
</NavbarNavLink>
|
||||
<ul className="dropdown__menu">
|
||||
{items.map((childItemProps, i) => (
|
||||
<NavbarItem
|
||||
isDropdownItem
|
||||
activeClassName="dropdown__link--active"
|
||||
{...childItemProps}
|
||||
key={i}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import type { Props } from '@theme/NavbarItem/DropdownNavbarItem/Desktop'
|
||||
import type { ReactNode } from 'react'
|
||||
import NavbarItem from '@theme/NavbarItem'
|
||||
import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink'
|
||||
import clsx from 'clsx'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
|
||||
export default function DropdownNavbarItemDesktop({
|
||||
items,
|
||||
position,
|
||||
className,
|
||||
onClick,
|
||||
...props
|
||||
}: Props): ReactNode {
|
||||
const dropdownRef = useRef<HTMLDivElement>(null)
|
||||
const [showDropdown, setShowDropdown] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (
|
||||
event: MouseEvent | TouchEvent | FocusEvent,
|
||||
) => {
|
||||
if (
|
||||
!dropdownRef.current
|
||||
|| dropdownRef.current.contains(event.target as Node)
|
||||
) {
|
||||
return
|
||||
}
|
||||
setShowDropdown(false)
|
||||
}
|
||||
|
||||
document.addEventListener('mousedown', handleClickOutside)
|
||||
document.addEventListener('touchstart', handleClickOutside)
|
||||
document.addEventListener('focusin', handleClickOutside)
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside)
|
||||
document.removeEventListener('touchstart', handleClickOutside)
|
||||
document.removeEventListener('focusin', handleClickOutside)
|
||||
}
|
||||
}, [dropdownRef])
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={dropdownRef}
|
||||
className={clsx('navbar__item', 'dropdown', 'dropdown--hoverable', {
|
||||
'dropdown--right': position === 'right',
|
||||
'dropdown--show': showDropdown,
|
||||
})}
|
||||
>
|
||||
<NavbarNavLink
|
||||
aria-haspopup="true"
|
||||
aria-expanded={showDropdown}
|
||||
role="button"
|
||||
// # hash permits to make the <a> tag focusable in case no link target
|
||||
// See https://github.com/facebook/docusaurus/pull/6003
|
||||
// There's probably a better solution though...
|
||||
href={props.to ? undefined : '#'}
|
||||
className={clsx('navbar__link', className)}
|
||||
{...props}
|
||||
onClick={props.to ? undefined : e => e.preventDefault()}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault()
|
||||
setShowDropdown(!showDropdown)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{props.children ?? props.label}
|
||||
</NavbarNavLink>
|
||||
<ul className="dropdown__menu">
|
||||
{items.map((childItemProps, i) => (
|
||||
<NavbarItem
|
||||
isDropdownItem
|
||||
activeClassName="dropdown__link--active"
|
||||
{...childItemProps}
|
||||
key={i}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
import React, {useEffect} from 'react';
|
||||
import clsx from 'clsx';
|
||||
import {
|
||||
isRegexpStringMatch,
|
||||
useCollapsible,
|
||||
Collapsible,
|
||||
} from '@docusaurus/theme-common';
|
||||
import {isSamePath, useLocalPathname} from '@docusaurus/theme-common/internal';
|
||||
import {translate} from '@docusaurus/Translate';
|
||||
import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink';
|
||||
import NavbarItem from '@theme/NavbarItem';
|
||||
import styles from './styles.module.css';
|
||||
function isItemActive(item, localPathname) {
|
||||
if (isSamePath(item.to, localPathname)) {
|
||||
return true;
|
||||
}
|
||||
if (isRegexpStringMatch(item.activeBaseRegex, localPathname)) {
|
||||
return true;
|
||||
}
|
||||
if (item.activeBasePath && localPathname.startsWith(item.activeBasePath)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function containsActiveItems(items, localPathname) {
|
||||
return items.some((item) => isItemActive(item, localPathname));
|
||||
}
|
||||
function CollapseButton({collapsed, onClick}) {
|
||||
return (
|
||||
<button
|
||||
aria-label={
|
||||
collapsed
|
||||
? translate({
|
||||
id: 'theme.navbar.mobileDropdown.collapseButton.expandAriaLabel',
|
||||
message: 'Expand the dropdown',
|
||||
description:
|
||||
'The ARIA label of the button to expand the mobile dropdown navbar item',
|
||||
})
|
||||
: translate({
|
||||
id: 'theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel',
|
||||
message: 'Collapse the dropdown',
|
||||
description:
|
||||
'The ARIA label of the button to collapse the mobile dropdown navbar item',
|
||||
})
|
||||
}
|
||||
aria-expanded={!collapsed}
|
||||
type="button"
|
||||
className="clean-btn menu__caret"
|
||||
onClick={onClick}
|
||||
/>
|
||||
);
|
||||
}
|
||||
function useItemCollapsible({active}) {
|
||||
const {collapsed, toggleCollapsed, setCollapsed} = useCollapsible({
|
||||
initialState: () => !active,
|
||||
});
|
||||
// Expand if any item active after a navigation
|
||||
useEffect(() => {
|
||||
if (active) {
|
||||
setCollapsed(false);
|
||||
}
|
||||
}, [active, setCollapsed]);
|
||||
return {
|
||||
collapsed,
|
||||
toggleCollapsed,
|
||||
};
|
||||
}
|
||||
export default function DropdownNavbarItemMobile({
|
||||
items,
|
||||
className,
|
||||
position, // Need to destructure position from props so that it doesn't get passed on.
|
||||
onClick,
|
||||
...props
|
||||
}) {
|
||||
const localPathname = useLocalPathname();
|
||||
const isActive = isSamePath(props.to, localPathname);
|
||||
const containsActive = containsActiveItems(items, localPathname);
|
||||
const {collapsed, toggleCollapsed} = useItemCollapsible({
|
||||
active: isActive || containsActive,
|
||||
});
|
||||
// # hash permits to make the <a> tag focusable in case no link target
|
||||
// See https://github.com/facebook/docusaurus/pull/6003
|
||||
// There's probably a better solution though...
|
||||
const href = props.to ? undefined : '#';
|
||||
return (
|
||||
<li
|
||||
className={clsx('menu__list-item', {
|
||||
'menu__list-item--collapsed': collapsed,
|
||||
})}>
|
||||
<div
|
||||
className={clsx('menu__list-item-collapsible', {
|
||||
'menu__list-item-collapsible--active': isActive,
|
||||
})}>
|
||||
<NavbarNavLink
|
||||
role="button"
|
||||
className={clsx(
|
||||
styles.dropdownNavbarItemMobile,
|
||||
'menu__link menu__link--sublist',
|
||||
className,
|
||||
)}
|
||||
href={href}
|
||||
{...props}
|
||||
onClick={(e) => {
|
||||
// Prevent navigation when link is "#"
|
||||
if (href === '#') {
|
||||
e.preventDefault();
|
||||
}
|
||||
// Otherwise we let navigation eventually happen, and/or collapse
|
||||
toggleCollapsed();
|
||||
}}>
|
||||
{props.children ?? props.label}
|
||||
</NavbarNavLink>
|
||||
<CollapseButton
|
||||
collapsed={collapsed}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
toggleCollapsed();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Collapsible lazy as="ul" className="menu__list" collapsed={collapsed}>
|
||||
{items.map((childItemProps, i) => (
|
||||
<NavbarItem
|
||||
mobile
|
||||
isDropdownItem
|
||||
onClick={onClick}
|
||||
activeClassName="menu__link--active"
|
||||
{...childItemProps}
|
||||
key={i}
|
||||
/>
|
||||
))}
|
||||
</Collapsible>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
import React, {useEffect, type ReactNode, type ComponentProps} from 'react';
|
||||
import clsx from 'clsx';
|
||||
import {
|
||||
isRegexpStringMatch,
|
||||
useCollapsible,
|
||||
Collapsible,
|
||||
} from '@docusaurus/theme-common';
|
||||
import {isSamePath, useLocalPathname} from '@docusaurus/theme-common/internal';
|
||||
import {translate} from '@docusaurus/Translate';
|
||||
import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink';
|
||||
import NavbarItem, {type LinkLikeNavbarItemProps} from '@theme/NavbarItem';
|
||||
import type {Props} from '@theme/NavbarItem/DropdownNavbarItem/Mobile';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
function isItemActive(
|
||||
item: LinkLikeNavbarItemProps,
|
||||
localPathname: string,
|
||||
): boolean {
|
||||
if (isSamePath(item.to, localPathname)) {
|
||||
return true;
|
||||
}
|
||||
if (isRegexpStringMatch(item.activeBaseRegex, localPathname)) {
|
||||
return true;
|
||||
}
|
||||
if (item.activeBasePath && localPathname.startsWith(item.activeBasePath)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function containsActiveItems(
|
||||
items: readonly LinkLikeNavbarItemProps[],
|
||||
localPathname: string,
|
||||
): boolean {
|
||||
return items.some((item) => isItemActive(item, localPathname));
|
||||
}
|
||||
|
||||
function CollapseButton({
|
||||
collapsed,
|
||||
onClick,
|
||||
}: {
|
||||
collapsed: boolean;
|
||||
onClick: ComponentProps<'button'>['onClick'];
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
aria-label={
|
||||
collapsed
|
||||
? translate({
|
||||
id: 'theme.navbar.mobileDropdown.collapseButton.expandAriaLabel',
|
||||
message: 'Expand the dropdown',
|
||||
description:
|
||||
'The ARIA label of the button to expand the mobile dropdown navbar item',
|
||||
})
|
||||
: translate({
|
||||
id: 'theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel',
|
||||
message: 'Collapse the dropdown',
|
||||
description:
|
||||
'The ARIA label of the button to collapse the mobile dropdown navbar item',
|
||||
})
|
||||
}
|
||||
aria-expanded={!collapsed}
|
||||
type="button"
|
||||
className="clean-btn menu__caret"
|
||||
onClick={onClick}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function useItemCollapsible({active}: {active: boolean}) {
|
||||
const {collapsed, toggleCollapsed, setCollapsed} = useCollapsible({
|
||||
initialState: () => !active,
|
||||
});
|
||||
|
||||
// Expand if any item active after a navigation
|
||||
useEffect(() => {
|
||||
if (active) {
|
||||
setCollapsed(false);
|
||||
}
|
||||
}, [active, setCollapsed]);
|
||||
|
||||
return {
|
||||
collapsed,
|
||||
toggleCollapsed,
|
||||
};
|
||||
}
|
||||
|
||||
export default function DropdownNavbarItemMobile({
|
||||
items,
|
||||
className,
|
||||
position, // Need to destructure position from props so that it doesn't get passed on.
|
||||
onClick,
|
||||
...props
|
||||
}: Props): ReactNode {
|
||||
const localPathname = useLocalPathname();
|
||||
const isActive = isSamePath(props.to, localPathname);
|
||||
const containsActive = containsActiveItems(items, localPathname);
|
||||
|
||||
const {collapsed, toggleCollapsed} = useItemCollapsible({
|
||||
active: isActive || containsActive,
|
||||
});
|
||||
|
||||
// # hash permits to make the <a> tag focusable in case no link target
|
||||
// See https://github.com/facebook/docusaurus/pull/6003
|
||||
// There's probably a better solution though...
|
||||
const href = props.to ? undefined : '#';
|
||||
|
||||
return (
|
||||
<li
|
||||
className={clsx('menu__list-item', {
|
||||
'menu__list-item--collapsed': collapsed,
|
||||
})}>
|
||||
<div
|
||||
className={clsx('menu__list-item-collapsible', {
|
||||
'menu__list-item-collapsible--active': isActive,
|
||||
})}>
|
||||
<NavbarNavLink
|
||||
role="button"
|
||||
className={clsx(
|
||||
styles.dropdownNavbarItemMobile,
|
||||
'menu__link menu__link--sublist',
|
||||
className,
|
||||
)}
|
||||
href={href}
|
||||
{...props}
|
||||
onClick={(e) => {
|
||||
// Prevent navigation when link is "#"
|
||||
if (href === '#') {
|
||||
e.preventDefault();
|
||||
}
|
||||
// Otherwise we let navigation eventually happen, and/or collapse
|
||||
toggleCollapsed();
|
||||
}}>
|
||||
{props.children ?? props.label}
|
||||
</NavbarNavLink>
|
||||
<CollapseButton
|
||||
collapsed={collapsed}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
toggleCollapsed();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Collapsible lazy as="ul" className="menu__list" collapsed={collapsed}>
|
||||
{items.map((childItemProps, i) => (
|
||||
<NavbarItem
|
||||
mobile
|
||||
isDropdownItem
|
||||
onClick={onClick}
|
||||
activeClassName="menu__link--active"
|
||||
{...childItemProps}
|
||||
key={i}
|
||||
/>
|
||||
))}
|
||||
</Collapsible>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
.dropdownNavbarItemMobile {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import DropdownNavbarItemDesktop from '@theme/NavbarItem/DropdownNavbarItem/Desktop'
|
||||
import DropdownNavbarItemMobile from '@theme/NavbarItem/DropdownNavbarItem/Mobile'
|
||||
import React from 'react'
|
||||
|
||||
export default function DropdownNavbarItem({ mobile = false, ...props }) {
|
||||
const Comp = mobile ? DropdownNavbarItemMobile : DropdownNavbarItemDesktop
|
||||
return <Comp {...props} />
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { Props } from '@theme/NavbarItem/DropdownNavbarItem'
|
||||
import type { ReactNode } from 'react'
|
||||
import DropdownNavbarItemDesktop from '@theme/NavbarItem/DropdownNavbarItem/Desktop'
|
||||
import DropdownNavbarItemMobile from '@theme/NavbarItem/DropdownNavbarItem/Mobile'
|
||||
import React from 'react'
|
||||
|
||||
export default function DropdownNavbarItem({
|
||||
mobile = false,
|
||||
...props
|
||||
}: Props): ReactNode {
|
||||
const Comp = mobile ? DropdownNavbarItemMobile : DropdownNavbarItemDesktop
|
||||
return <Comp {...props} />
|
||||
}
|
||||
Reference in new issue
Block a user