@cypress-design/react-docmenu 0.4.0 → 0.4.2
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +18 -0
- package/DocMenu.tsx +42 -24
- package/DocMenuReact.cy.tsx +111 -4
- package/ReadMe.md +9 -10
- package/_DocGroup.tsx +220 -98
- package/_DocLink.tsx +60 -46
- package/dist/DocMenu.d.ts +2 -1
- package/dist/DocMenu.d.ts.map +1 -1
- package/dist/_DocGroup.d.ts +31 -5
- package/dist/_DocGroup.d.ts.map +1 -1
- package/dist/_DocLink.d.ts +9 -2
- package/dist/_DocLink.d.ts.map +1 -1
- package/dist/index.es.mjs +149 -88
- package/dist/index.es.mjs.map +1 -1
- package/dist/index.umd.js +149 -88
- package/dist/index.umd.js.map +1 -1
- package/package.json +2 -2
package/.turbo/turbo-build.log
CHANGED
|
@@ -2,5 +2,5 @@ $ yarn build:module && yarn build:types
|
|
|
2
2
|
$ rollup -c ./rollup.config.mjs
|
|
3
3
|
[36m
|
|
4
4
|
[1m./index.ts[22m → [1m./dist/index.umd.js, ./dist/index.es.mjs[22m...[39m
|
|
5
|
-
[32mcreated [1m./dist/index.umd.js, ./dist/index.es.mjs[22m in [
|
|
5
|
+
[32mcreated [1m./dist/index.umd.js, ./dist/index.es.mjs[22m in [1m15.2s[22m[39m
|
|
6
6
|
$ tsc --project ./tsconfig.build.json
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @cypress-design/react-docmenu
|
|
2
2
|
|
|
3
|
+
## 0.4.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`a58be93`](https://github.com/cypress-io/cypress-design/commit/a58be9322cfc64c64533ff8f8414a45ba90be751) Thanks [@elevatebart](https://github.com/elevatebart)! - fix(docgen): make buttons left-aligned (logical)
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`a58be93`](https://github.com/cypress-io/cypress-design/commit/a58be9322cfc64c64533ff8f8414a45ba90be751)]:
|
|
10
|
+
- @cypress-design/constants-docmenu@0.1.3
|
|
11
|
+
|
|
12
|
+
## 0.4.1
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- [#353](https://github.com/cypress-io/cypress-design/pull/353) [`b382b9e`](https://github.com/cypress-io/cypress-design/commit/b382b9ecc99e3ab6288eee406831199edb661c19) Thanks [@elevatebart](https://github.com/elevatebart)! - feat: Docgen wrapping active links
|
|
17
|
+
|
|
18
|
+
- Updated dependencies [[`b382b9e`](https://github.com/cypress-io/cypress-design/commit/b382b9ecc99e3ab6288eee406831199edb661c19)]:
|
|
19
|
+
- @cypress-design/constants-docmenu@0.1.2
|
|
20
|
+
|
|
3
21
|
## 0.4.0
|
|
4
22
|
|
|
5
23
|
### Minor Changes
|
package/DocMenu.tsx
CHANGED
|
@@ -1,45 +1,63 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
2
|
import { NavGroup, NavItemLink } from '@cypress-design/constants-docmenu'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import clsx from 'clsx'
|
|
3
|
+
import type { LinkComponentType } from './_DocLink'
|
|
4
|
+
import { DocGroupElements } from './_DocGroup'
|
|
6
5
|
|
|
7
6
|
export type NavItem = NavGroup | NavItemLink
|
|
8
7
|
|
|
9
8
|
export interface DocMenuProps extends React.HTMLAttributes<HTMLUListElement> {
|
|
10
9
|
items: NavItem[]
|
|
10
|
+
activePath?: string
|
|
11
11
|
collapsible?: boolean
|
|
12
12
|
LinkComponent?: LinkComponentType
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export const DocMenu: React.FC<DocMenuProps> = ({
|
|
16
16
|
items,
|
|
17
|
+
activePath = '<unknown>',
|
|
17
18
|
collapsible = true,
|
|
18
19
|
LinkComponent = 'a',
|
|
19
20
|
...rest
|
|
20
21
|
}) => {
|
|
22
|
+
const [activeTop, setActiveTop] = React.useState(0)
|
|
23
|
+
const [activeHeight, setActiveHeight] = React.useState(36)
|
|
24
|
+
const [showMarker, setShowMarker] = React.useState(false)
|
|
25
|
+
|
|
26
|
+
const container = React.useRef<HTMLDivElement>(null)
|
|
27
|
+
|
|
28
|
+
const setActivePosition = React.useCallback(
|
|
29
|
+
(opts: { top: number; height: number }) => {
|
|
30
|
+
const containerTop = container.current?.getBoundingClientRect().top || 0
|
|
31
|
+
setShowMarker(true)
|
|
32
|
+
setActiveTop(opts.top - containerTop)
|
|
33
|
+
setActiveHeight(opts.height)
|
|
34
|
+
},
|
|
35
|
+
[],
|
|
36
|
+
)
|
|
37
|
+
|
|
21
38
|
return (
|
|
22
|
-
<
|
|
23
|
-
{
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
<div ref={container} className="relative">
|
|
40
|
+
{showMarker && collapsible ? (
|
|
41
|
+
<div
|
|
42
|
+
className="absolute h-[36px] w-[4px] z-50 rounded-full bg-indigo-500 transition-all duration-300 ml-[6.5px] mt-[4px]"
|
|
43
|
+
style={{
|
|
44
|
+
top: `${activeTop + 4}px`,
|
|
45
|
+
height: `${activeHeight - 8}px`,
|
|
46
|
+
}}
|
|
47
|
+
/>
|
|
48
|
+
) : null}
|
|
49
|
+
<DocGroupElements
|
|
50
|
+
{...rest}
|
|
51
|
+
className={!open ? 'hidden' : undefined}
|
|
52
|
+
items={items}
|
|
53
|
+
activePath={activePath}
|
|
54
|
+
collapsible={collapsible}
|
|
55
|
+
depth={-1}
|
|
56
|
+
onActivePosition={setActivePosition}
|
|
57
|
+
LinkComponent={LinkComponent}
|
|
58
|
+
hideMarker={() => setShowMarker(false)}
|
|
59
|
+
/>
|
|
60
|
+
</div>
|
|
43
61
|
)
|
|
44
62
|
}
|
|
45
63
|
|
package/DocMenuReact.cy.tsx
CHANGED
|
@@ -25,13 +25,117 @@ describe('<DocMenu/>', () => {
|
|
|
25
25
|
items={[
|
|
26
26
|
{
|
|
27
27
|
label: 'Baaaaaaz',
|
|
28
|
-
items: [
|
|
28
|
+
items: [
|
|
29
|
+
{
|
|
30
|
+
label: 'Bar',
|
|
31
|
+
items: [{ href: '/foo', label: 'Foo' }],
|
|
32
|
+
},
|
|
33
|
+
],
|
|
29
34
|
},
|
|
30
35
|
]}
|
|
31
36
|
/>,
|
|
32
37
|
)
|
|
33
38
|
})
|
|
34
39
|
|
|
40
|
+
it(
|
|
41
|
+
'wraps items correctly',
|
|
42
|
+
{ viewportHeight: 950, viewportWidth: 350 },
|
|
43
|
+
() => {
|
|
44
|
+
const Sut = () => {
|
|
45
|
+
const [activeHref, setActiveHref] = React.useState<string | undefined>()
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div className="w-[200px] mt-[60px] absolute">
|
|
49
|
+
<DocMenu
|
|
50
|
+
activePath={activeHref}
|
|
51
|
+
LinkComponent={({ href, className, children }) => (
|
|
52
|
+
<div className={className} onClick={() => setActiveHref(href)}>
|
|
53
|
+
{children}
|
|
54
|
+
</div>
|
|
55
|
+
)}
|
|
56
|
+
items={[
|
|
57
|
+
{
|
|
58
|
+
href: '/fooTop',
|
|
59
|
+
label: 'Foo Top',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
href: '/test',
|
|
63
|
+
label: 'Getting started',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
href: '/faaz',
|
|
67
|
+
label:
|
|
68
|
+
'lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam,',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
label: 'Baaaaaaz',
|
|
72
|
+
items: [
|
|
73
|
+
{
|
|
74
|
+
label: 'Bar',
|
|
75
|
+
items: [
|
|
76
|
+
{
|
|
77
|
+
href: '/foo',
|
|
78
|
+
label: 'Foo',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
href: '/test',
|
|
82
|
+
label: 'Getting started',
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
label: 'Code',
|
|
88
|
+
collapsed: true,
|
|
89
|
+
items: [
|
|
90
|
+
{
|
|
91
|
+
href: '/faa',
|
|
92
|
+
label: 'sasassa',
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
href: '/faa1',
|
|
96
|
+
label: 'sasassa2',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
href: '/faa2',
|
|
100
|
+
label: 'sasassa3',
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
href: '/faa3',
|
|
104
|
+
label: 'sasassa4',
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
href: '/faa4',
|
|
108
|
+
label:
|
|
109
|
+
'lorem ipsum dolor sit amet consectetur adipisicing elit',
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
href: '/faa5',
|
|
113
|
+
label: 'sasassa5',
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
href: '/faa6',
|
|
117
|
+
label:
|
|
118
|
+
'lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos',
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
]}
|
|
125
|
+
/>
|
|
126
|
+
</div>
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
mount(<Sut />)
|
|
130
|
+
|
|
131
|
+
cy.findByText('Bar').click()
|
|
132
|
+
cy.findByText('Code').click()
|
|
133
|
+
cy.findByText(
|
|
134
|
+
'lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos',
|
|
135
|
+
).click()
|
|
136
|
+
},
|
|
137
|
+
)
|
|
138
|
+
|
|
35
139
|
it('renders the custom link components', () => {
|
|
36
140
|
mount(
|
|
37
141
|
<DocMenu
|
|
@@ -48,12 +152,15 @@ describe('<DocMenu/>', () => {
|
|
|
48
152
|
)}
|
|
49
153
|
/>,
|
|
50
154
|
)
|
|
51
|
-
|
|
155
|
+
|
|
52
156
|
cy.findByText('Foo + href: /foo', { selector: 'div' }).should('be.visible')
|
|
53
157
|
})
|
|
54
158
|
|
|
55
|
-
function mountStory(items: (NavItemLink | NavGroup)[] = []) {
|
|
56
|
-
mount(<DocMenu items={items} />)
|
|
159
|
+
function mountStory(items: (NavItemLink | NavGroup)[] = [], activePath = '') {
|
|
160
|
+
mount(<DocMenu items={items} activePath={activePath} />)
|
|
57
161
|
}
|
|
58
162
|
assertions(mountStory)
|
|
59
163
|
})
|
|
164
|
+
|
|
165
|
+
// pseudo text
|
|
166
|
+
// lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos
|
package/ReadMe.md
CHANGED
|
@@ -23,22 +23,22 @@ import DocMenu from '@cypress-design/react-docmenu'
|
|
|
23
23
|
```tsx live
|
|
24
24
|
export default () => (
|
|
25
25
|
<DocMenu
|
|
26
|
+
activePath="#group-item-3"
|
|
26
27
|
items={[
|
|
27
28
|
{
|
|
28
29
|
label: 'Get Started',
|
|
29
|
-
href: '#',
|
|
30
|
+
href: '#get-started',
|
|
30
31
|
},
|
|
31
32
|
{
|
|
32
33
|
label: 'Overview',
|
|
33
34
|
items: [
|
|
34
35
|
{
|
|
35
36
|
label: 'Overview Item 1',
|
|
36
|
-
href: '#',
|
|
37
|
+
href: '#overview-item-1',
|
|
37
38
|
},
|
|
38
39
|
{
|
|
39
40
|
label: 'Overview Item 2',
|
|
40
|
-
href: '#',
|
|
41
|
-
active: true,
|
|
41
|
+
href: '#overview-item-2',
|
|
42
42
|
},
|
|
43
43
|
],
|
|
44
44
|
},
|
|
@@ -47,33 +47,32 @@ export default () => (
|
|
|
47
47
|
items: [
|
|
48
48
|
{
|
|
49
49
|
label: 'Item 1',
|
|
50
|
-
href: '#',
|
|
50
|
+
href: '#item-1',
|
|
51
51
|
},
|
|
52
52
|
{
|
|
53
53
|
label: 'Group',
|
|
54
54
|
items: [
|
|
55
55
|
{
|
|
56
56
|
label: 'Item 1',
|
|
57
|
-
href: '#',
|
|
57
|
+
href: '#group-item-1',
|
|
58
58
|
},
|
|
59
59
|
{
|
|
60
60
|
label: 'Item 2',
|
|
61
|
-
href: '#',
|
|
61
|
+
href: '#group-item-2',
|
|
62
62
|
},
|
|
63
63
|
{
|
|
64
64
|
label: 'Item 3',
|
|
65
|
-
href: '#',
|
|
65
|
+
href: '#group-item-3',
|
|
66
66
|
},
|
|
67
67
|
{
|
|
68
68
|
label: 'Item 4',
|
|
69
|
-
href: '#',
|
|
69
|
+
href: '#group-item-4',
|
|
70
70
|
},
|
|
71
71
|
],
|
|
72
72
|
},
|
|
73
73
|
{
|
|
74
74
|
label: 'Item 5',
|
|
75
75
|
href: '#',
|
|
76
|
-
active: true,
|
|
77
76
|
},
|
|
78
77
|
],
|
|
79
78
|
},
|
package/_DocGroup.tsx
CHANGED
|
@@ -1,140 +1,262 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
2
|
import { IconChevronDownSmall } from '@cypress-design/react-icon'
|
|
3
3
|
import clsx from 'clsx'
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import {
|
|
5
|
+
type NavGroup,
|
|
6
|
+
type NavItemLink,
|
|
7
|
+
classes,
|
|
8
|
+
} from '@cypress-design/constants-docmenu'
|
|
9
|
+
import {
|
|
10
|
+
DocLink,
|
|
11
|
+
type DocLinkForward,
|
|
12
|
+
type LinkComponentType,
|
|
13
|
+
} from './_DocLink'
|
|
6
14
|
|
|
7
15
|
export interface DocGroupProps {
|
|
8
|
-
index: number
|
|
9
16
|
group: NavGroup
|
|
17
|
+
activePath: string
|
|
10
18
|
collapsible: boolean
|
|
19
|
+
onActivePosition: (opts: { top: number; height: number }) => void
|
|
20
|
+
updateMarkerPosition?: () => void
|
|
11
21
|
depth?: number
|
|
12
|
-
setHeight?: (height: number) => void
|
|
13
22
|
LinkComponent?: LinkComponentType
|
|
23
|
+
hideMarker: () => void
|
|
14
24
|
}
|
|
15
25
|
|
|
16
|
-
export
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
depth = 0,
|
|
20
|
-
setHeight,
|
|
21
|
-
index,
|
|
22
|
-
LinkComponent = 'a',
|
|
23
|
-
}) => {
|
|
24
|
-
const [open, setOpen] = React.useState(depth === 0)
|
|
25
|
-
const [itemsHeights, setItemsHeights] = React.useState<number[]>(
|
|
26
|
-
Array(group.items.length).fill(1),
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
React.useEffect(() => {
|
|
30
|
-
// if this group contains other groups, ignore this effect
|
|
31
|
-
// it will be triggered by a leaf and trickle up
|
|
32
|
-
if (group.items.some((item) => 'items' in item)) {
|
|
33
|
-
return
|
|
34
|
-
}
|
|
26
|
+
export interface DocGroupForward {
|
|
27
|
+
reTriggerSetActiveGroup: () => void
|
|
28
|
+
}
|
|
35
29
|
|
|
36
|
-
|
|
30
|
+
export const DocGroup = React.forwardRef<DocGroupForward, DocGroupProps>(
|
|
31
|
+
(
|
|
32
|
+
{
|
|
33
|
+
group,
|
|
34
|
+
activePath,
|
|
35
|
+
collapsible,
|
|
36
|
+
depth = 0,
|
|
37
|
+
onActivePosition,
|
|
38
|
+
updateMarkerPosition,
|
|
39
|
+
LinkComponent = 'a',
|
|
40
|
+
hideMarker,
|
|
41
|
+
},
|
|
42
|
+
ref,
|
|
43
|
+
) => {
|
|
44
|
+
const [open, setOpen] = React.useState(group.collapsed !== true)
|
|
45
|
+
const $groupElements = React.useRef<DocGroupElementsForward>(null)
|
|
37
46
|
|
|
38
|
-
|
|
39
|
-
|
|
47
|
+
function toggleMenu(open: boolean) {
|
|
48
|
+
if (!collapsible) return
|
|
49
|
+
setOpen(open)
|
|
50
|
+
}
|
|
40
51
|
|
|
41
|
-
|
|
52
|
+
const hasActiveItemRecursively = React.useCallback(
|
|
53
|
+
(items = group.items): boolean => {
|
|
54
|
+
return items.some((item) => {
|
|
55
|
+
if ('items' in item) {
|
|
56
|
+
return hasActiveItemRecursively(item.items)
|
|
57
|
+
}
|
|
58
|
+
return item.href === activePath
|
|
59
|
+
})
|
|
60
|
+
},
|
|
61
|
+
[group.items, activePath],
|
|
62
|
+
)
|
|
42
63
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
64
|
+
const Head = collapsible ? 'button' : group.href ? 'a' : 'div'
|
|
65
|
+
|
|
66
|
+
const setActivePosition = React.useCallback(
|
|
67
|
+
({ top, height }: { top: number; height: number }) => {
|
|
68
|
+
if (open) {
|
|
69
|
+
onActivePosition({ top, height })
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
[onActivePosition, open],
|
|
46
73
|
)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
)
|
|
74
|
+
|
|
75
|
+
function reTriggerSetActiveGroup() {
|
|
76
|
+
$groupElements.current?.reTriggerSetActiveGroup()
|
|
51
77
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
78
|
+
|
|
79
|
+
React.useEffect(() => {
|
|
80
|
+
if (hasActiveItemRecursively()) {
|
|
81
|
+
if (open) {
|
|
82
|
+
reTriggerSetActiveGroup()
|
|
83
|
+
} else {
|
|
84
|
+
hideMarker()
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
updateMarkerPosition?.()
|
|
88
|
+
}
|
|
89
|
+
}, [open, updateMarkerPosition, hideMarker, hasActiveItemRecursively])
|
|
90
|
+
|
|
91
|
+
React.useImperativeHandle(ref, () => ({
|
|
92
|
+
reTriggerSetActiveGroup,
|
|
93
|
+
}))
|
|
94
|
+
|
|
95
|
+
const onUpdateMarkerPosition = React.useCallback(() => {
|
|
96
|
+
if (hasActiveItemRecursively()) {
|
|
97
|
+
reTriggerSetActiveGroup()
|
|
98
|
+
} else {
|
|
99
|
+
updateMarkerPosition?.()
|
|
100
|
+
}
|
|
101
|
+
}, [hasActiveItemRecursively, updateMarkerPosition])
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<>
|
|
105
|
+
<Head
|
|
106
|
+
onClick={() => toggleMenu(!open)}
|
|
107
|
+
href={group.href}
|
|
108
|
+
className={clsx(classes.button, {
|
|
109
|
+
[classes.topButton]: depth === 0,
|
|
110
|
+
[classes.leafButton]: depth,
|
|
111
|
+
'text-indigo-500': activePath === group.href,
|
|
112
|
+
})}
|
|
113
|
+
>
|
|
114
|
+
{collapsible ? (
|
|
115
|
+
<IconChevronDownSmall
|
|
116
|
+
stroke-color="gray-400"
|
|
117
|
+
size={depth ? '8' : '16'}
|
|
118
|
+
className={clsx(
|
|
119
|
+
'absolute transform transition-transform left-0',
|
|
120
|
+
{
|
|
121
|
+
'rotate-0': open,
|
|
122
|
+
'-rotate-90': !open,
|
|
123
|
+
'ml-[16px]': depth,
|
|
124
|
+
},
|
|
125
|
+
)}
|
|
126
|
+
/>
|
|
127
|
+
) : null}
|
|
128
|
+
{group.label}
|
|
129
|
+
</Head>
|
|
130
|
+
<DocGroupElements
|
|
131
|
+
ref={$groupElements}
|
|
132
|
+
className={!open ? 'hidden' : undefined}
|
|
133
|
+
items={group.items}
|
|
134
|
+
activePath={activePath}
|
|
135
|
+
collapsible={collapsible}
|
|
136
|
+
depth={depth}
|
|
137
|
+
onActivePosition={setActivePosition}
|
|
138
|
+
updateMarkerPosition={onUpdateMarkerPosition}
|
|
139
|
+
LinkComponent={LinkComponent}
|
|
140
|
+
hideMarker={hideMarker}
|
|
106
141
|
/>
|
|
107
|
-
|
|
142
|
+
</>
|
|
143
|
+
)
|
|
144
|
+
},
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
export interface DocGroupElementsProps
|
|
148
|
+
extends React.HTMLAttributes<HTMLUListElement> {
|
|
149
|
+
items: (NavGroup | NavItemLink)[]
|
|
150
|
+
activePath: string
|
|
151
|
+
collapsible: boolean
|
|
152
|
+
onActivePosition: (opts: { top: number; height: number }) => void
|
|
153
|
+
updateMarkerPosition?: () => void
|
|
154
|
+
depth: number
|
|
155
|
+
LinkComponent: LinkComponentType
|
|
156
|
+
hideMarker: () => void
|
|
157
|
+
className: string | undefined
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface DocGroupElementsForward {
|
|
161
|
+
reTriggerSetActiveGroup: () => void
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export const DocGroupElements = React.forwardRef<
|
|
165
|
+
DocGroupElementsForward,
|
|
166
|
+
DocGroupElementsProps
|
|
167
|
+
>(
|
|
168
|
+
(
|
|
169
|
+
{
|
|
170
|
+
items,
|
|
171
|
+
activePath,
|
|
172
|
+
collapsible,
|
|
173
|
+
depth = 0,
|
|
174
|
+
onActivePosition,
|
|
175
|
+
updateMarkerPosition,
|
|
176
|
+
LinkComponent,
|
|
177
|
+
hideMarker,
|
|
178
|
+
className,
|
|
179
|
+
...rest
|
|
180
|
+
},
|
|
181
|
+
ref,
|
|
182
|
+
) => {
|
|
183
|
+
const $groups = React.useRef<DocGroupForward[]>([])
|
|
184
|
+
const $items = React.useRef<DocLinkForward[]>([])
|
|
185
|
+
|
|
186
|
+
const hasActiveItemRecursively = React.useCallback(
|
|
187
|
+
(localItems = items): boolean => {
|
|
188
|
+
return localItems.some((item) => {
|
|
189
|
+
if ('items' in item) {
|
|
190
|
+
return hasActiveItemRecursively(item.items)
|
|
191
|
+
}
|
|
192
|
+
return item.href === activePath
|
|
193
|
+
})
|
|
194
|
+
},
|
|
195
|
+
[items, activePath],
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
function reTriggerSetActiveGroup() {
|
|
199
|
+
$groups.current.forEach((group) => {
|
|
200
|
+
group?.reTriggerSetActiveGroup()
|
|
201
|
+
})
|
|
202
|
+
$items.current.forEach((item) => {
|
|
203
|
+
item?.setActiveMarkerPosition()
|
|
204
|
+
})
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
React.useImperativeHandle(ref, () => ({
|
|
208
|
+
reTriggerSetActiveGroup,
|
|
209
|
+
}))
|
|
210
|
+
|
|
211
|
+
const onUpdateMarkerPosition = React.useCallback(() => {
|
|
212
|
+
if (hasActiveItemRecursively()) {
|
|
213
|
+
reTriggerSetActiveGroup()
|
|
214
|
+
} else {
|
|
215
|
+
updateMarkerPosition?.()
|
|
216
|
+
}
|
|
217
|
+
}, [hasActiveItemRecursively, updateMarkerPosition])
|
|
218
|
+
|
|
219
|
+
return (
|
|
108
220
|
<ul
|
|
109
|
-
|
|
221
|
+
{...rest}
|
|
222
|
+
className={clsx('list-none p-0', className, {
|
|
110
223
|
'border-l border-gray-100': depth === 0 && collapsible,
|
|
111
|
-
|
|
224
|
+
'ml-[8px]': depth >= 0,
|
|
112
225
|
})}
|
|
113
226
|
>
|
|
114
|
-
{
|
|
227
|
+
{items.map((item, index) =>
|
|
115
228
|
'items' in item ? (
|
|
116
229
|
<li key={index} className="relative list-none p-0">
|
|
117
230
|
<DocGroup
|
|
231
|
+
ref={(el: DocGroupForward) => {
|
|
232
|
+
$groups.current[index] = el
|
|
233
|
+
}}
|
|
118
234
|
group={item}
|
|
235
|
+
activePath={activePath}
|
|
119
236
|
depth={depth + 1}
|
|
120
|
-
setHeight={onSetHeightCallback}
|
|
121
|
-
index={index}
|
|
122
237
|
collapsible={collapsible}
|
|
123
238
|
LinkComponent={LinkComponent}
|
|
239
|
+
hideMarker={hideMarker}
|
|
240
|
+
onActivePosition={onActivePosition}
|
|
241
|
+
updateMarkerPosition={onUpdateMarkerPosition}
|
|
124
242
|
/>
|
|
125
243
|
</li>
|
|
126
244
|
) : (
|
|
127
245
|
<DocLink
|
|
246
|
+
ref={(el: DocLinkForward) => {
|
|
247
|
+
$items.current[index] = el
|
|
248
|
+
}}
|
|
128
249
|
key={index}
|
|
129
250
|
item={item}
|
|
251
|
+
active={item.href === activePath}
|
|
130
252
|
collapsible={collapsible}
|
|
131
253
|
depth={depth}
|
|
132
|
-
onActive={
|
|
254
|
+
onActive={onActivePosition}
|
|
133
255
|
LinkComponent={LinkComponent}
|
|
134
256
|
/>
|
|
135
257
|
),
|
|
136
258
|
)}
|
|
137
259
|
</ul>
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
260
|
+
)
|
|
261
|
+
},
|
|
262
|
+
)
|