@antv/dumi-theme-antv 0.1.0

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.
Files changed (111) hide show
  1. package/README.md +9 -0
  2. package/es/antv/404/index.js +20 -0
  3. package/es/antv/Banner/Banner.module.less +412 -0
  4. package/es/antv/Banner/Notification.js +44 -0
  5. package/es/antv/Banner/Notification.module.less +108 -0
  6. package/es/antv/Banner/index.js +115 -0
  7. package/es/antv/Cases/Cases.js +124 -0
  8. package/es/antv/Cases/Cases.module.less +203 -0
  9. package/es/antv/Features/FeatureCard.js +25 -0
  10. package/es/antv/Features/FeatureCard.module.less +51 -0
  11. package/es/antv/Features/Features.module.less +169 -0
  12. package/es/antv/Features/index.js +102 -0
  13. package/es/antv/Footer/Footer.module.less +36 -0
  14. package/es/antv/Footer/index.js +232 -0
  15. package/es/antv/Header/Logo.js +130 -0
  16. package/es/antv/Products/Product.js +61 -0
  17. package/es/antv/Products/Product.module.less +146 -0
  18. package/es/antv/Products/getNewProducts.js +41 -0
  19. package/es/antv/Products/getProducts.js +466 -0
  20. package/es/antv/Products/index.js +81 -0
  21. package/es/antv/hooks.js +81 -0
  22. package/es/antv/mixins.less +21 -0
  23. package/es/antv/utils.js +49 -0
  24. package/es/builtins/API.js +37 -0
  25. package/es/builtins/Alert.js +9 -0
  26. package/es/builtins/Alert.less +62 -0
  27. package/es/builtins/Badge.js +9 -0
  28. package/es/builtins/Badge.less +31 -0
  29. package/es/builtins/Example.js +48 -0
  30. package/es/builtins/Example.less +47 -0
  31. package/es/builtins/Previewer.js +225 -0
  32. package/es/builtins/Previewer.less +406 -0
  33. package/es/builtins/SourceCode.js +72 -0
  34. package/es/builtins/SourceCode.less +103 -0
  35. package/es/builtins/Table.js +56 -0
  36. package/es/builtins/Table.less +43 -0
  37. package/es/builtins/Tree.js +219 -0
  38. package/es/builtins/Tree.less +159 -0
  39. package/es/components/Dark.js +125 -0
  40. package/es/components/Dark.less +121 -0
  41. package/es/components/LocaleSelect.js +53 -0
  42. package/es/components/LocaleSelect.less +59 -0
  43. package/es/components/Navbar.js +155 -0
  44. package/es/components/Navbar.less +180 -0
  45. package/es/components/SearchBar.js +83 -0
  46. package/es/components/SearchBar.less +165 -0
  47. package/es/components/SideMenu.js +99 -0
  48. package/es/components/SideMenu.less +379 -0
  49. package/es/components/SlugList.js +33 -0
  50. package/es/components/SlugList.less +18 -0
  51. package/es/layout.js +276 -0
  52. package/es/style/layout.less +402 -0
  53. package/es/style/markdown.less +240 -0
  54. package/es/style/variables.less +37 -0
  55. package/package.json +58 -0
  56. package/src/antv/404/index.tsx +25 -0
  57. package/src/antv/Banner/Banner.module.less +412 -0
  58. package/src/antv/Banner/Notification.module.less +108 -0
  59. package/src/antv/Banner/Notification.tsx +45 -0
  60. package/src/antv/Banner/index.tsx +121 -0
  61. package/src/antv/Cases/Cases.module.less +203 -0
  62. package/src/antv/Cases/Cases.tsx +116 -0
  63. package/src/antv/Features/FeatureCard.module.less +51 -0
  64. package/src/antv/Features/FeatureCard.tsx +24 -0
  65. package/src/antv/Features/Features.module.less +169 -0
  66. package/src/antv/Features/index.tsx +86 -0
  67. package/src/antv/Footer/Footer.module.less +36 -0
  68. package/src/antv/Footer/index.tsx +272 -0
  69. package/src/antv/Header/Logo.tsx +85 -0
  70. package/src/antv/Products/Product.module.less +146 -0
  71. package/src/antv/Products/Product.tsx +80 -0
  72. package/src/antv/Products/getNewProducts.tsx +53 -0
  73. package/src/antv/Products/getProducts.tsx +626 -0
  74. package/src/antv/Products/index.tsx +70 -0
  75. package/src/antv/hooks.ts +87 -0
  76. package/src/antv/mixins.less +21 -0
  77. package/src/antv/utils.ts +44 -0
  78. package/src/builtins/API.tsx +57 -0
  79. package/src/builtins/Alert.less +62 -0
  80. package/src/builtins/Alert.tsx +4 -0
  81. package/src/builtins/Badge.less +31 -0
  82. package/src/builtins/Badge.tsx +4 -0
  83. package/src/builtins/Example.less +47 -0
  84. package/src/builtins/Example.tsx +34 -0
  85. package/src/builtins/Previewer.less +406 -0
  86. package/src/builtins/Previewer.tsx +264 -0
  87. package/src/builtins/SourceCode.less +103 -0
  88. package/src/builtins/SourceCode.tsx +55 -0
  89. package/src/builtins/Table.less +43 -0
  90. package/src/builtins/Table.tsx +42 -0
  91. package/src/builtins/Tree.less +159 -0
  92. package/src/builtins/Tree.tsx +130 -0
  93. package/src/components/Dark.less +121 -0
  94. package/src/components/Dark.tsx +78 -0
  95. package/src/components/LocaleSelect.less +59 -0
  96. package/src/components/LocaleSelect.tsx +53 -0
  97. package/src/components/Navbar.less +180 -0
  98. package/src/components/Navbar.tsx +152 -0
  99. package/src/components/SearchBar.less +165 -0
  100. package/src/components/SearchBar.tsx +68 -0
  101. package/src/components/SideMenu.less +379 -0
  102. package/src/components/SideMenu.tsx +148 -0
  103. package/src/components/SlugList.less +18 -0
  104. package/src/components/SlugList.tsx +20 -0
  105. package/src/layout.tsx +225 -0
  106. package/src/style/layout.less +402 -0
  107. package/src/style/markdown.less +240 -0
  108. package/src/style/variables.less +37 -0
  109. package/src/test/SearchBar.test.ts +32 -0
  110. package/src/test/Table.test.tsx +41 -0
  111. package/src/test/index.test.tsx +377 -0
@@ -0,0 +1,68 @@
1
+ import React, { useState, useEffect, useRef } from 'react';
2
+ import { useSearch, AnchorLink } from 'dumi/theme';
3
+ import './SearchBar.less';
4
+
5
+ export const highlight = (key: string, title: string) => {
6
+ const index = title.toLowerCase().indexOf(key.toLowerCase());
7
+ const l = key.length;
8
+ return (
9
+ <>
10
+ {title.substring(0, index)}
11
+ <span className="__dumi-default-search-highlight">{title.substring(index, index + l)}</span>
12
+ {title.substring(index + l, title.length)}
13
+ </>
14
+ );
15
+ };
16
+
17
+ export default () => {
18
+ const [keywords, setKeywords] = useState<string>('');
19
+ const [items, setItems] = useState([]);
20
+ const input = useRef<HTMLInputElement>();
21
+ const result = useSearch(keywords);
22
+
23
+ const emptySvg = (
24
+ <svg
25
+ className="__dumi-default-search-empty"
26
+ viewBox="0 0 1024 1024"
27
+ version="1.1"
28
+ xmlns="http://www.w3.org/2000/svg"
29
+ p-id="2347"
30
+ width="32"
31
+ height="32"
32
+ >
33
+ <path d="M855.6 427.2H168.5c-12.7 0-24.4 6.9-30.6 18L4.4 684.7C1.5 689.9 0 695.8 0 701.8v287.1c0 19.4 15.7 35.1 35.1 35.1H989c19.4 0 35.1-15.7 35.1-35.1V701.8c0-6-1.5-11.8-4.4-17.1L886.2 445.2c-6.2-11.1-17.9-18-30.6-18zM673.4 695.6c-16.5 0-30.8 11.5-34.3 27.7-12.7 58.5-64.8 102.3-127.2 102.3s-114.5-43.8-127.2-102.3c-3.5-16.1-17.8-27.7-34.3-27.7H119c-26.4 0-43.3-28-31.1-51.4l81.7-155.8c6.1-11.6 18-18.8 31.1-18.8h622.4c13 0 25 7.2 31.1 18.8l81.7 155.8c12.2 23.4-4.7 51.4-31.1 51.4H673.4zM819.9 209.5c-1-1.8-2.1-3.7-3.2-5.5-9.8-16.6-31.1-22.2-47.8-12.6L648.5 261c-17 9.8-22.7 31.6-12.6 48.4 0.9 1.4 1.7 2.9 2.5 4.4 9.5 17 31.2 22.8 48 13L807 257.3c16.7-9.7 22.4-31 12.9-47.8zM375.4 261.1L255 191.6c-16.7-9.6-38-4-47.8 12.6-1.1 1.8-2.1 3.6-3.2 5.5-9.5 16.8-3.8 38.1 12.9 47.8L337.3 327c16.9 9.7 38.6 4 48-13.1 0.8-1.5 1.7-2.9 2.5-4.4 10.2-16.8 4.5-38.6-12.4-48.4zM512 239.3h2.5c19.5 0.3 35.5-15.5 35.5-35.1v-139c0-19.3-15.6-34.9-34.8-35.1h-6.4C489.6 30.3 474 46 474 65.2v139c0 19.5 15.9 35.4 35.5 35.1h2.5z"></path>
34
+ </svg>
35
+ );
36
+
37
+ useEffect(() => {
38
+ if (Array.isArray(result)) {
39
+ setItems(result);
40
+ } else if (typeof result === 'function') {
41
+ result(`.${input.current.className}`);
42
+ }
43
+ }, [result]);
44
+
45
+ return (
46
+ <div className="__dumi-default-search">
47
+ <input
48
+ className="__dumi-default-search-input"
49
+ type="search"
50
+ ref={input}
51
+ {...(Array.isArray(result)
52
+ ? { value: keywords, onChange: ev => setKeywords(ev.target.value) }
53
+ : {})}
54
+ />
55
+ <ul>
56
+ {items.length > 0 && items.map(meta => (
57
+ <li key={meta.path} onClick={() => setKeywords('')}>
58
+ <AnchorLink to={meta.path}>
59
+ {meta.parent?.title && <span>{meta.parent.title}</span>}
60
+ {highlight(keywords, meta.title)}
61
+ </AnchorLink>
62
+ </li>
63
+ ))}
64
+ {items.length === 0 && keywords && <li style={{ textAlign: 'center' }}>{emptySvg}</li>}
65
+ </ul>
66
+ </div>
67
+ );
68
+ };
@@ -0,0 +1,379 @@
1
+ @import (reference) '../style/variables.less';
2
+
3
+ .@{prefix}-menu {
4
+ position: fixed;
5
+ z-index: 100;
6
+ top: 0;
7
+ left: 0;
8
+ bottom: 0;
9
+ width: @s-menu-width;
10
+ background-color: #f2f5fa;
11
+ box-sizing: border-box;
12
+ transition: left 0.3s;
13
+
14
+ &[data-hidden] {
15
+ display: none;
16
+ }
17
+
18
+ @media @mobile {
19
+ left: -@s-menu-mobile-width;
20
+ top: @s-mobile-nav-height;
21
+ display: block !important;
22
+ width: @s-menu-mobile-width;
23
+ background-color: #fff;
24
+
25
+ &[data-mobile-show] {
26
+ left: 0;
27
+ }
28
+
29
+ [data-prefers-color=dark] & {
30
+ background-color: @c-bg-dark;
31
+ }
32
+ }
33
+
34
+ // shadow
35
+ &::after {
36
+ content: '';
37
+ position: absolute;
38
+ top: 0;
39
+ right: 0;
40
+ bottom: 0;
41
+ display: block;
42
+ width: 20px;
43
+ background: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.03));
44
+ pointer-events: none;
45
+
46
+ // use border on mobile devices
47
+ @media @mobile {
48
+ width: 1px;
49
+ background: @c-border;
50
+ }
51
+ }
52
+
53
+ &-header {
54
+ position: relative;
55
+ padding-top: 40px;
56
+ text-align: center;
57
+ border-bottom: 1px solid @c-border;
58
+
59
+ [data-prefers-color=dark] & {
60
+ border-color: @c-border-dark;
61
+ }
62
+
63
+ @media @mobile {
64
+ display: none;
65
+ }
66
+
67
+ .@{prefix}-menu-logo {
68
+ display: inline-block;
69
+ width: 66px;
70
+ height: 65px;
71
+ background: url(@img-logo) no-repeat 0 / contain;
72
+ }
73
+
74
+ h1 {
75
+ margin: 10px 0 0;
76
+ color: @c-heading;
77
+ font-weight: 500;
78
+ line-height: 1.40625;
79
+ }
80
+
81
+ p {
82
+ margin: 0 0 5px;
83
+ color: lighten(@c-secondary, 10%);
84
+
85
+ // badges
86
+ > object[data^='https://img.shields.io'] {
87
+ max-height: 20px;
88
+ }
89
+
90
+ + p {
91
+ margin-bottom: 10px;
92
+ }
93
+ }
94
+ }
95
+
96
+ &-doc-locale {
97
+ padding: 16px 0;
98
+ text-align: center;
99
+ border-bottom: 1px solid @c-border;
100
+ display: flex;
101
+ justify-content: space-evenly;
102
+
103
+ [data-prefers-color=dark] & {
104
+ border-color: @c-border-dark;
105
+ }
106
+
107
+ [data-mode=doc][data-mobile-show=true] & {
108
+ display: grid;
109
+ }
110
+
111
+ &:empty {
112
+ display: none;
113
+ }
114
+ }
115
+
116
+ &-inner {
117
+ width: 100%;
118
+ height: 100%;
119
+ overflow: auto;
120
+ overscroll-behavior: contain;
121
+
122
+ [data-prefers-color=dark] & {
123
+ background-color: #262626;
124
+ }
125
+
126
+ // common list styles
127
+ ul {
128
+ list-style: none;
129
+ margin: 0;
130
+ padding: 0;
131
+ font-size: 16px;
132
+
133
+ li {
134
+ color: @c-text;
135
+ a,
136
+ > span {
137
+ position: relative;
138
+ display: block;
139
+ padding-right: 24px;
140
+ color: @c-heading;
141
+ line-height: 2.4;
142
+ text-decoration: none;
143
+ outline: none;
144
+ transition: color 0.3s, background 0.3s;
145
+
146
+ [data-prefers-color=dark] & {
147
+ color: @c-heading-dark;
148
+ }
149
+
150
+ span {
151
+ display: block;
152
+ overflow: hidden;
153
+ white-space: nowrap;
154
+ text-overflow: ellipsis;
155
+ }
156
+
157
+ &:hover,
158
+ &.active {
159
+ color: @c-primary;
160
+
161
+ [data-prefers-color=dark] & {
162
+ color: @c-primary-dark;
163
+ }
164
+ }
165
+
166
+ &::before {
167
+ content: '';
168
+ position: absolute;
169
+ top: 50%;
170
+ left: -10px;
171
+ margin-top: -2.5px;
172
+ display: inline-block;
173
+ width: 5px;
174
+ height: 5px;
175
+ background-color: @c-primary;
176
+ border-radius: 50%;
177
+ opacity: 0;
178
+ transition: transform 0.2s, opacity 0.2s;
179
+ transform: scale(0) translateX(-10px);
180
+ }
181
+ }
182
+
183
+ &.active a,
184
+ a.active {
185
+ &::before {
186
+ opacity: 1;
187
+ transform: scale(1) translateX(0);
188
+ }
189
+ }
190
+
191
+ // level larger, offset larger, font size smaller
192
+ ul {
193
+ font-size: 0.9em;
194
+ padding-left: 1em;
195
+ }
196
+ }
197
+ }
198
+
199
+ // 1-level list styles
200
+ > ul {
201
+ > li > a {
202
+ line-height: 2.875;
203
+
204
+ &:not([href]) {
205
+ padding-top: 24px;
206
+ line-height: 1;
207
+ font-weight: 500;
208
+ color: @c-heading !important;
209
+ background: transparent !important;
210
+ cursor: default;
211
+
212
+ [data-prefers-color=dark] & {
213
+ color: @c-heading-dark !important;
214
+ }
215
+ }
216
+ }
217
+
218
+ > li:first-child > a:not([href]) {
219
+ padding-top: 0;
220
+ }
221
+ }
222
+
223
+ // n-level list styles
224
+ > ul ul {
225
+ a {
226
+ color: @c-secondary;
227
+ [data-prefers-color=dark] & {
228
+ color: @c-secondary-dark;
229
+ }
230
+ &.active {
231
+ color: @c-primary;
232
+ [data-prefers-color=dark] & {
233
+ color: @c-primary-dark;
234
+ }
235
+ }
236
+ }
237
+ }
238
+
239
+ .@{prefix}-menu-mobile-area {
240
+ display: none;
241
+ padding-bottom: 16px;
242
+ margin-bottom: 16px;
243
+ text-align: center;
244
+ border-bottom: 1px solid @c-border;
245
+
246
+ [data-prefers-color=dark] & {
247
+ border-color: @c-border-dark;
248
+ }
249
+
250
+ @media @mobile {
251
+ display: block;
252
+ }
253
+ }
254
+
255
+ // mobile nav list
256
+ .@{prefix}-menu-nav-list {
257
+ padding: 16px 0 0 0;
258
+
259
+ > li,
260
+ > li > a {
261
+ padding-right: 0;
262
+ line-height: 2.4;
263
+
264
+ ul {
265
+ padding-left: 0;
266
+
267
+ a {
268
+ padding-right: 0;
269
+ font-size: 90%;
270
+ }
271
+ }
272
+ }
273
+ }
274
+
275
+ // menu list
276
+ .@{prefix}-menu-list {
277
+ padding: 8px 0;
278
+ margin-bottom: 40px;
279
+
280
+ > li > a {
281
+ @c-active-bg: #e8ecf4;
282
+
283
+ padding-left: 28px;
284
+
285
+ &.active {
286
+ background: linear-gradient(to left, #e8ecf4, rgba(232, 236, 244, 0));
287
+
288
+ [data-prefers-color=dark] & {
289
+ background: linear-gradient(to left, #3d3d3e, rgba(255, 255, 255, 0.06));
290
+ }
291
+ }
292
+
293
+ ~ ul {
294
+ margin-top: 8px;
295
+ margin-left: 28px;
296
+ }
297
+
298
+ @media @mobile {
299
+ padding-left: 16px;
300
+
301
+ ~ ul {
302
+ margin-left: 16px;
303
+ }
304
+ }
305
+ }
306
+ }
307
+ }
308
+
309
+ &[data-mode='site'] {
310
+ &::after {
311
+ width: 1px;
312
+ background: @c-border;
313
+
314
+ [data-prefers-color=dark] & {
315
+ background: @c-border-dark;
316
+ }
317
+ }
318
+
319
+ .@{prefix}-menu-list {
320
+ padding: 0;
321
+
322
+ > li > a {
323
+ position: relative;
324
+
325
+ &::after {
326
+ content: '';
327
+ position: absolute;
328
+ top: 0;
329
+ bottom: 0;
330
+ right: 0;
331
+ display: block;
332
+ width: 3px;
333
+ background-color: @c-primary;
334
+ visibility: hidden;
335
+ opacity: 0;
336
+ transition: all 0.3s;
337
+ border-radius: 1px;
338
+ }
339
+
340
+ &.active {
341
+ z-index: 1;
342
+ background: linear-gradient(to left, #f8faff, rgba(248, 250, 255, 0));
343
+
344
+ [data-prefers-color=dark] & {
345
+ background: linear-gradient(to left, #3d3d3e, rgba(255, 255, 255, 0.06));
346
+ }
347
+ &::after {
348
+ opacity: 1;
349
+ visibility: visible;
350
+ }
351
+ }
352
+ }
353
+ }
354
+
355
+ @media @desktop {
356
+ top: @s-nav-height;
357
+ width: @s-site-menu-width;
358
+ padding-top: 50px;
359
+ background: transparent;
360
+
361
+ [data-prefers-color=dark] & {
362
+ background: @c-light-bg-dark;
363
+ }
364
+
365
+ .@{prefix}-menu-nav,
366
+ .@{prefix}-menu-header {
367
+ display: none;
368
+ }
369
+
370
+ .@{prefix}-menu-list > li > a {
371
+ padding-left: 58px;
372
+
373
+ ~ ul {
374
+ margin-left: 58px;
375
+ }
376
+ }
377
+ }
378
+ }
379
+ }
@@ -0,0 +1,148 @@
1
+ import type { FC } from 'react';
2
+ import React, { useContext } from 'react';
3
+ import { context, Link, NavLink } from 'dumi/theme';
4
+ import LocaleSelect from './LocaleSelect';
5
+ import SlugList from './SlugList';
6
+ import './SideMenu.less';
7
+
8
+ interface INavbarProps {
9
+ mobileMenuCollapsed: boolean;
10
+ location: any;
11
+ darkPrefix?: React.ReactNode;
12
+ }
13
+
14
+ const SideMenu: FC<INavbarProps> = ({ mobileMenuCollapsed, location, darkPrefix }) => {
15
+ const {
16
+ config: {
17
+ logo,
18
+ title,
19
+ description,
20
+ mode,
21
+ repository: { url: repoUrl },
22
+ },
23
+ menu,
24
+ nav: navItems,
25
+ base,
26
+ meta,
27
+ } = useContext(context);
28
+ const isHiddenMenus =
29
+ Boolean((meta.hero || meta.features || meta.gapless) && mode === 'site') ||
30
+ meta.sidemenu === false ||
31
+ undefined;
32
+
33
+ return (
34
+ <div
35
+ className="__dumi-default-menu"
36
+ data-mode={mode}
37
+ data-hidden={isHiddenMenus}
38
+ data-mobile-show={!mobileMenuCollapsed || undefined}
39
+ >
40
+ <div className="__dumi-default-menu-inner">
41
+ <div className="__dumi-default-menu-header">
42
+ <Link
43
+ to={base}
44
+ className="__dumi-default-menu-logo"
45
+ style={{
46
+ backgroundImage: logo && `url('${logo}')`,
47
+ }}
48
+ />
49
+ <h1>{title}</h1>
50
+ <p>{description}</p>
51
+ {/* github star badge */}
52
+ {/github\.com/.test(repoUrl) && mode === 'doc' && (
53
+ <p>
54
+ <object
55
+ type="image/svg+xml"
56
+ data={`https://img.shields.io/github/stars${repoUrl.match(/((\/[^\/]+){2})$/)[1]
57
+ }?style=social`}
58
+ />
59
+ </p>
60
+ )}
61
+ </div>
62
+ {/* mobile nav list */}
63
+
64
+ <div className="__dumi-default-menu-mobile-area">
65
+ {!!navItems.length && (
66
+ <ul className="__dumi-default-menu-nav-list">
67
+ {navItems.map(nav => {
68
+ const child = Boolean(nav.children?.length) && (
69
+ <ul>
70
+ {nav.children.map(item => (
71
+ <li key={item.path || item.title}>
72
+ <NavLink to={item.path}>{item.title}</NavLink>
73
+ </li>
74
+ ))}
75
+ </ul>
76
+ );
77
+
78
+ return (
79
+ <li key={nav.path || nav.title}>
80
+ {nav.path ? <NavLink to={nav.path}>{nav.title}</NavLink> : nav.title}
81
+ {child}
82
+ </li>
83
+ );
84
+ })}
85
+ </ul>
86
+ )}
87
+ {/* site mode locale select */}
88
+ <LocaleSelect location={location} />
89
+ {darkPrefix}
90
+ </div>
91
+ {/* menu list */}
92
+ <ul className="__dumi-default-menu-list">
93
+ {!isHiddenMenus &&
94
+ menu.map(item => {
95
+ // always use meta from routes to reduce menu data size
96
+ const hasSlugs = Boolean(meta.slugs?.length);
97
+ const hasChildren = item.children && Boolean(item.children.length);
98
+ const show1LevelSlugs =
99
+ meta.toc === 'menu' && !hasChildren && hasSlugs && item.path === location.pathname.replace(/([^^])\/$/, '$1');
100
+ const menuPaths = hasChildren
101
+ ? item.children.map(i => i.path)
102
+ : [
103
+ item.path,
104
+ // handle menu group which has no index route and no valid children
105
+ location.pathname.startsWith(`${item.path}/`) && meta.title === item.title
106
+ ? location.pathname
107
+ : null,
108
+ ];
109
+
110
+ return (
111
+ <li key={item.path || item.title}>
112
+ <NavLink
113
+ to={item.path}
114
+ isActive={() => menuPaths.includes(location.pathname)}
115
+ >
116
+ {item.title}
117
+ </NavLink>
118
+ {/* group children */}
119
+ {Boolean(item.children && item.children.length) && (
120
+ <ul>
121
+ {item.children.map(child => (
122
+ <li key={child.path}>
123
+ <NavLink to={child.path} exact>
124
+ <span>{child.title}</span>
125
+ </NavLink>
126
+ {/* group children slugs */}
127
+ {Boolean(
128
+ meta.toc === 'menu' &&
129
+ typeof window !== 'undefined' &&
130
+ child.path === location.pathname &&
131
+ hasSlugs,
132
+ ) && <SlugList slugs={meta.slugs} />}
133
+ </li>
134
+ ))}
135
+ </ul>
136
+ )}
137
+ {/* group slugs */}
138
+ {show1LevelSlugs && <SlugList slugs={meta.slugs} />}
139
+ </li>
140
+ );
141
+ })}
142
+ </ul>
143
+ </div>
144
+ </div>
145
+ );
146
+ };
147
+
148
+ export default SideMenu;
@@ -0,0 +1,18 @@
1
+ @import (reference) '../style/variables.less';
2
+
3
+ ul[role='slug-list'] {
4
+ &:empty {
5
+ margin: 0 !important;
6
+ padding: 0 !important;
7
+ }
8
+
9
+ li {
10
+ > a.active {
11
+ color: darken(@c-primary, 2%);
12
+ }
13
+
14
+ &[data-depth='3'] {
15
+ padding-left: 12px;
16
+ }
17
+ }
18
+ }
@@ -0,0 +1,20 @@
1
+ import type { FC } from 'react';
2
+ import React from 'react';
3
+ import { AnchorLink } from 'dumi/theme';
4
+ import './SlugList.less';
5
+
6
+ const SlugsList: FC<{ slugs: any; className?: string }> = ({ slugs, ...props }) => (
7
+ <ul role="slug-list" {...props}>
8
+ {slugs
9
+ .filter(({ depth }) => depth > 1 && depth < 4)
10
+ .map(slug => (
11
+ <li key={slug.heading} title={slug.value} data-depth={slug.depth}>
12
+ <AnchorLink to={`#${slug.heading}`}>
13
+ <span>{slug.value}</span>
14
+ </AnchorLink>
15
+ </li>
16
+ ))}
17
+ </ul>
18
+ );
19
+
20
+ export default SlugsList;