@widlarzgroup/docusaurus-ui 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +36 -0
  2. package/lib/components/PlatformsList/PlatformsList.d.ts +7 -0
  3. package/lib/components/PlatformsList/PlatformsList.d.ts.map +1 -0
  4. package/lib/components/PlatformsList/PlatformsList.js +14 -0
  5. package/lib/components/PlatformsList/PlatformsList.js.map +1 -0
  6. package/lib/components/PlatformsList/PlatformsList.module.css +10 -0
  7. package/lib/components/PlusBadge/PlusBadge.d.ts +6 -0
  8. package/lib/components/PlusBadge/PlusBadge.d.ts.map +1 -0
  9. package/lib/components/PlusBadge/PlusBadge.js +12 -0
  10. package/lib/components/PlusBadge/PlusBadge.js.map +1 -0
  11. package/lib/components/PlusBadge/PlusBadge.module.css +17 -0
  12. package/lib/components/ProBadge/ProBadge.d.ts +2 -0
  13. package/lib/components/ProBadge/ProBadge.d.ts.map +1 -0
  14. package/lib/components/ProBadge/ProBadge.js +12 -0
  15. package/lib/components/ProBadge/ProBadge.js.map +1 -0
  16. package/lib/components/ProBadge/ProBadge.module.css +8 -0
  17. package/lib/components/ProFeature/ProFeature.d.ts +7 -0
  18. package/lib/components/ProFeature/ProFeature.d.ts.map +1 -0
  19. package/lib/components/ProFeature/ProFeature.js +12 -0
  20. package/lib/components/ProFeature/ProFeature.js.map +1 -0
  21. package/lib/components/ProFeature/ProFeature.module.css +89 -0
  22. package/lib/components/StatusBadge/StatusBadge.d.ts +7 -0
  23. package/lib/components/StatusBadge/StatusBadge.d.ts.map +1 -0
  24. package/lib/components/StatusBadge/StatusBadge.js +26 -0
  25. package/lib/components/StatusBadge/StatusBadge.js.map +1 -0
  26. package/lib/components/StatusBadge/StatusBadge.module.css +26 -0
  27. package/lib/components/TWGBadge/TWGBadge.d.ts +6 -0
  28. package/lib/components/TWGBadge/TWGBadge.d.ts.map +1 -0
  29. package/lib/components/TWGBadge/TWGBadge.js +15 -0
  30. package/lib/components/TWGBadge/TWGBadge.js.map +1 -0
  31. package/lib/components/TWGBadge/TWGBadge.module.css +107 -0
  32. package/lib/css/custom.css +614 -0
  33. package/lib/index.d.ts +4 -0
  34. package/lib/index.d.ts.map +1 -0
  35. package/lib/index.js +37 -0
  36. package/lib/index.js.map +1 -0
  37. package/lib/theme/DocItem/Footer/index.d.ts +3 -0
  38. package/lib/theme/DocItem/Footer/index.d.ts.map +1 -0
  39. package/lib/theme/DocItem/Footer/index.js +24 -0
  40. package/lib/theme/DocItem/Footer/index.js.map +1 -0
  41. package/lib/theme/DocItem/TOC/Desktop/index.d.ts +3 -0
  42. package/lib/theme/DocItem/TOC/Desktop/index.d.ts.map +1 -0
  43. package/lib/theme/DocItem/TOC/Desktop/index.js +15 -0
  44. package/lib/theme/DocItem/TOC/Desktop/index.js.map +1 -0
  45. package/lib/theme/DocSidebarItem/Category/index.d.ts +5 -0
  46. package/lib/theme/DocSidebarItem/Category/index.d.ts.map +1 -0
  47. package/lib/theme/DocSidebarItem/Category/index.js +224 -0
  48. package/lib/theme/DocSidebarItem/Category/index.js.map +1 -0
  49. package/lib/theme/DocSidebarItem/Category/styles.module.css +119 -0
  50. package/lib/theme/DocSidebarItem/Link/index.d.ts +5 -0
  51. package/lib/theme/DocSidebarItem/Link/index.d.ts.map +1 -0
  52. package/lib/theme/DocSidebarItem/Link/index.js +35 -0
  53. package/lib/theme/DocSidebarItem/Link/index.js.map +1 -0
  54. package/lib/theme/DocSidebarItem/Link/styles.module.css +18 -0
  55. package/lib/theme/TOC/index.d.ts +4 -0
  56. package/lib/theme/TOC/index.d.ts.map +1 -0
  57. package/lib/theme/TOC/index.js +19 -0
  58. package/lib/theme/TOC/index.js.map +1 -0
  59. package/lib/theme/TOC/styles.module.css +16 -0
  60. package/lib/types/sidebar.d.ts +17 -0
  61. package/lib/types/sidebar.d.ts.map +1 -0
  62. package/lib/types/sidebar.js +3 -0
  63. package/lib/types/sidebar.js.map +1 -0
  64. package/package.json +41 -0
  65. package/src/components/PlatformsList/PlatformsList.module.css +10 -0
  66. package/src/components/PlatformsList/PlatformsList.tsx +28 -0
  67. package/src/components/PlusBadge/PlusBadge.module.css +17 -0
  68. package/src/components/PlusBadge/PlusBadge.tsx +23 -0
  69. package/src/components/ProBadge/ProBadge.module.css +8 -0
  70. package/src/components/ProBadge/ProBadge.tsx +21 -0
  71. package/src/components/ProFeature/ProFeature.module.css +89 -0
  72. package/src/components/ProFeature/ProFeature.tsx +51 -0
  73. package/src/components/StatusBadge/StatusBadge.module.css +26 -0
  74. package/src/components/StatusBadge/StatusBadge.tsx +32 -0
  75. package/src/components/TWGBadge/TWGBadge.module.css +107 -0
  76. package/src/components/TWGBadge/TWGBadge.tsx +27 -0
  77. package/src/css/custom.css +614 -0
  78. package/src/index.ts +22 -0
  79. package/src/theme/DocItem/Footer/index.tsx +51 -0
  80. package/src/theme/DocSidebarItem/Category/index.tsx +403 -0
  81. package/src/theme/DocSidebarItem/Category/styles.module.css +119 -0
  82. package/src/theme/DocSidebarItem/Link/index.tsx +85 -0
  83. package/src/theme/DocSidebarItem/Link/styles.module.css +18 -0
  84. package/src/theme/TOC/index.tsx +25 -0
  85. package/src/theme/TOC/styles.module.css +16 -0
  86. package/src/types/sidebar.ts +23 -0
@@ -0,0 +1,403 @@
1
+ import React, {
2
+ type ComponentProps,
3
+ type ReactNode,
4
+ useEffect,
5
+ useMemo,
6
+ } from 'react';
7
+ import clsx from 'clsx';
8
+ import {
9
+ ThemeClassNames,
10
+ useThemeConfig,
11
+ usePrevious,
12
+ Collapsible,
13
+ useCollapsible,
14
+ } from '@docusaurus/theme-common';
15
+ import { isSamePath } from '@docusaurus/theme-common/internal';
16
+ import {
17
+ isActiveSidebarItem,
18
+ findFirstSidebarItemLink,
19
+ useDocSidebarItemsExpandedState,
20
+ useVisibleSidebarItems,
21
+ } from '@docusaurus/plugin-content-docs/client';
22
+ import Link from '@docusaurus/Link';
23
+ import { translate } from '@docusaurus/Translate';
24
+ import useIsBrowser from '@docusaurus/useIsBrowser';
25
+ import DocSidebarItems from '@theme-original/DocSidebarItems';
26
+ import DocSidebarItemLink from '@theme-original/DocSidebarItem/Link';
27
+ import type { Props } from '@theme/DocSidebarItem/Category';
28
+ import ProBadge from '@/components/ProBadge/ProBadge';
29
+ import StatusBadge from '@/components/StatusBadge/StatusBadge';
30
+ import type {
31
+ WithCustomProps,
32
+ CustomSidebarProps,
33
+ } from '@/types/sidebar';
34
+
35
+ import type {
36
+ PropSidebarItemCategory,
37
+ PropSidebarItemLink,
38
+ } from '@docusaurus/plugin-content-docs';
39
+ import styles from './styles.module.css';
40
+
41
+ // If we navigate to a category and it becomes active, it should automatically
42
+ // expand itself
43
+ function useAutoExpandActiveCategory({
44
+ isActive,
45
+ collapsed,
46
+ updateCollapsed,
47
+ activePath,
48
+ }: {
49
+ isActive: boolean;
50
+ collapsed: boolean;
51
+ updateCollapsed: (b: boolean) => void;
52
+ activePath: string;
53
+ }) {
54
+ const wasActive = usePrevious(isActive);
55
+ const previousActivePath = usePrevious(activePath);
56
+ useEffect(() => {
57
+ const justBecameActive = isActive && !wasActive;
58
+ const stillActiveButPathChanged =
59
+ isActive && wasActive && activePath !== previousActivePath;
60
+ if ((justBecameActive || stillActiveButPathChanged) && collapsed) {
61
+ updateCollapsed(false);
62
+ }
63
+ }, [
64
+ isActive,
65
+ wasActive,
66
+ collapsed,
67
+ updateCollapsed,
68
+ activePath,
69
+ previousActivePath,
70
+ ]);
71
+ }
72
+
73
+ /**
74
+ * When a collapsible category has no link, we still link it to its first child
75
+ * during SSR as a temporary fallback. This allows to be able to navigate inside
76
+ * the category even when JS fails to load, is delayed or simply disabled
77
+ * React hydration becomes an optional progressive enhancement
78
+ * see https://github.com/facebookincubator/infima/issues/36#issuecomment-772543188
79
+ * see https://github.com/facebook/docusaurus/issues/3030
80
+ */
81
+ function useCategoryHrefWithSSRFallback(
82
+ item: Props['item']
83
+ ): string | undefined {
84
+ const isBrowser = useIsBrowser();
85
+ return useMemo(() => {
86
+ if (item.href && !item.linkUnlisted) {
87
+ return item.href;
88
+ }
89
+ // In these cases, it's not necessary to render a fallback
90
+ // We skip the "findFirstCategoryLink" computation
91
+ if (isBrowser || !item.collapsible) {
92
+ return undefined;
93
+ }
94
+ return findFirstSidebarItemLink(item);
95
+ }, [item, isBrowser]);
96
+ }
97
+
98
+ function CollapseButton({
99
+ collapsed,
100
+ categoryLabel,
101
+ onClick,
102
+ }: {
103
+ collapsed: boolean;
104
+ categoryLabel: string;
105
+ onClick: ComponentProps<'button'>['onClick'];
106
+ }) {
107
+ return (
108
+ <button
109
+ aria-label={
110
+ collapsed
111
+ ? translate(
112
+ {
113
+ id: 'theme.DocSidebarItem.expandCategoryAriaLabel',
114
+ message: "Expand sidebar category '{label}'",
115
+ description: 'The ARIA label to expand the sidebar category',
116
+ },
117
+ { label: categoryLabel }
118
+ )
119
+ : translate(
120
+ {
121
+ id: 'theme.DocSidebarItem.collapseCategoryAriaLabel',
122
+ message: "Collapse sidebar category '{label}'",
123
+ description: 'The ARIA label to collapse the sidebar category',
124
+ },
125
+ { label: categoryLabel }
126
+ )
127
+ }
128
+ aria-expanded={!collapsed}
129
+ type="button"
130
+ className="clean-btn menu__caret"
131
+ onClick={onClick}
132
+ />
133
+ );
134
+ }
135
+
136
+ function CategoryLinkLabel({
137
+ label,
138
+ isRootCategory,
139
+ customProps,
140
+ }: {
141
+ label: string;
142
+ isRootCategory: boolean;
143
+ customProps?: CustomSidebarProps;
144
+ }) {
145
+ return (
146
+ <>
147
+ <span
148
+ title={label}
149
+ className={clsx(styles.categoryLinkLabel, {
150
+ [styles.categoryLinkLabelRoot]: isRootCategory,
151
+ [styles.categoryLinkLabelNested]: !isRootCategory,
152
+ })}
153
+ >
154
+ {label}
155
+ </span>
156
+ {customProps?.plan === 'pro' && <ProBadge />}
157
+ {customProps?.badgeType && <StatusBadge type={customProps.badgeType} />}
158
+ </>
159
+ );
160
+ }
161
+
162
+ export default function DocSidebarItemCategory(
163
+ props: WithCustomProps<Props>
164
+ ): ReactNode {
165
+ const visibleChildren = useVisibleSidebarItems(
166
+ props.item.items,
167
+ props.activePath
168
+ );
169
+ if (visibleChildren.length === 0) {
170
+ return <DocSidebarItemCategoryEmpty {...props} />;
171
+ } else {
172
+ return <DocSidebarItemCategoryCollapsible {...props} />;
173
+ }
174
+ }
175
+
176
+ function isCategoryWithHref(
177
+ category: PropSidebarItemCategory
178
+ ): category is PropSidebarItemCategory & { href: string } {
179
+ return typeof category.href === 'string';
180
+ }
181
+
182
+ // If a category doesn't have any visible children, we render it as a link
183
+ function DocSidebarItemCategoryEmpty({
184
+ item,
185
+ ...props
186
+ }: WithCustomProps<Props>): ReactNode {
187
+ // If the category has no link, we don't render anything
188
+ // It's not super useful to render a category you can't open nor click
189
+ if (!isCategoryWithHref(item)) {
190
+ return null;
191
+ }
192
+ // We remove props that don't make sense for a link and forward the rest
193
+ const {
194
+ type: _type,
195
+ collapsed: _collapsed,
196
+ collapsible: _collapsible,
197
+ items: _items,
198
+ linkUnlisted: _linkUnlisted,
199
+ ...forwardableProps
200
+ } = item;
201
+ const linkItem: PropSidebarItemLink = {
202
+ type: 'link',
203
+ ...forwardableProps,
204
+ };
205
+ return <DocSidebarItemLink item={linkItem} {...props} />;
206
+ }
207
+
208
+ function DocSidebarItemCategoryCollapsible({
209
+ item,
210
+ onItemClick,
211
+ activePath,
212
+ level,
213
+ index,
214
+ ...props
215
+ }: WithCustomProps<Props>): ReactNode {
216
+ const { items, label, collapsible, className, href, customProps } = item;
217
+ const {
218
+ docs: {
219
+ sidebar: { autoCollapseCategories },
220
+ },
221
+ } = useThemeConfig();
222
+ const hrefWithSSRFallback = useCategoryHrefWithSSRFallback(item);
223
+
224
+ const isActive = isActiveSidebarItem(item, activePath);
225
+ const isCurrentPage = isSamePath(href, activePath);
226
+
227
+ // Root categories (level === 1) are always expanded and non-collapsible
228
+ const isRootCategory = level === 1;
229
+
230
+ const { collapsed, setCollapsed } = useCollapsible({
231
+ // Root categories (level === 1) are always expanded
232
+ // Level 2 categories respect the collapsed prop from sidebars.ts
233
+ // Deeper nested categories (level > 2) are collapsed by default, unless they are active
234
+ initialState: () => {
235
+ if (isRootCategory) {
236
+ return false; // Always expanded for root categories
237
+ }
238
+ if (!collapsible) {
239
+ return false;
240
+ }
241
+ // Level 2 categories: respect the collapsed prop from sidebars.ts
242
+ if (level === 2) {
243
+ return item.collapsed ?? false; // Use sidebar config, default to open
244
+ }
245
+ // Deeper nested categories (level > 2): only expand if active, otherwise collapse
246
+ return isActive ? false : true;
247
+ },
248
+ });
249
+
250
+ const { expandedItem, setExpandedItem } = useDocSidebarItemsExpandedState();
251
+ // Use this instead of `setCollapsed`, because it is also reactive
252
+ const updateCollapsed = (toCollapsed: boolean = !collapsed) => {
253
+ // Root categories cannot be collapsed
254
+ if (isRootCategory) {
255
+ return;
256
+ }
257
+ setExpandedItem(toCollapsed ? null : index);
258
+ setCollapsed(toCollapsed);
259
+ };
260
+
261
+ // Only auto-expand for nested categories, not root
262
+ // We need to call the hook unconditionally (React rules), but make it a no-op for root categories
263
+ useAutoExpandActiveCategory({
264
+ isActive: isActive && !isRootCategory,
265
+ collapsed: isRootCategory ? false : collapsed,
266
+ updateCollapsed: isRootCategory ? () => {} : updateCollapsed,
267
+ activePath,
268
+ });
269
+
270
+ useEffect(() => {
271
+ // Root categories should never collapse - always keep them expanded
272
+ if (isRootCategory) {
273
+ setCollapsed(false);
274
+ return;
275
+ }
276
+ // Auto-collapse when other categories expand (only if autoCollapseCategories is enabled)
277
+ if (
278
+ collapsible &&
279
+ expandedItem != null &&
280
+ expandedItem !== index &&
281
+ autoCollapseCategories
282
+ ) {
283
+ setCollapsed(true);
284
+ }
285
+ }, [
286
+ collapsible,
287
+ expandedItem,
288
+ index,
289
+ setCollapsed,
290
+ autoCollapseCategories,
291
+ isRootCategory,
292
+ ]);
293
+
294
+ const handleItemClick: ComponentProps<'a'>['onClick'] = (e) => {
295
+ onItemClick?.(item);
296
+ // Root categories cannot be collapsed, so skip collapse logic
297
+ if (isRootCategory) {
298
+ return;
299
+ }
300
+ if (collapsible) {
301
+ if (href) {
302
+ // When already on the category's page, we collapse it
303
+ // We don't use "isActive" because it would collapse the
304
+ // category even when we browse a children element
305
+ // See https://github.com/facebook/docusaurus/issues/11213
306
+ if (isCurrentPage) {
307
+ e.preventDefault();
308
+ updateCollapsed();
309
+ } else {
310
+ // When navigating to a new category, we always expand
311
+ // see https://github.com/facebook/docusaurus/issues/10854#issuecomment-2609616182
312
+ updateCollapsed(false);
313
+ }
314
+ } else {
315
+ e.preventDefault();
316
+ updateCollapsed();
317
+ }
318
+ }
319
+ };
320
+
321
+ // Dynamic indent for nested categories (level 3+)
322
+ const nestedIndent = level >= 3 ? (level - 2) * 0.05 : 0;
323
+
324
+ return (
325
+ <li
326
+ className={clsx(
327
+ ThemeClassNames.docs.docSidebarItemCategory,
328
+ ThemeClassNames.docs.docSidebarItemCategoryLevel(level),
329
+ 'menu__list-item',
330
+ {
331
+ 'menu__list-item--collapsed': collapsed,
332
+ [styles.rootCategoryContainer]: isRootCategory,
333
+ },
334
+ className
335
+ )}
336
+ >
337
+ <div
338
+ className={clsx('menu__list-item-collapsible', {
339
+ 'menu__list-item-collapsible--active': isCurrentPage,
340
+ })}
341
+ >
342
+ <Link
343
+ className={clsx(styles.categoryLink, 'menu__link', {
344
+ [styles.categoryLinkRoot]: isRootCategory,
345
+ 'menu__link--sublist': collapsible && !isRootCategory,
346
+ 'menu__link--sublist-caret':
347
+ !href && collapsible && !isRootCategory,
348
+ 'menu__link--active': isActive,
349
+ })}
350
+ style={
351
+ nestedIndent > 0
352
+ ? { paddingLeft: `${nestedIndent + 0.5}rem` }
353
+ : undefined
354
+ }
355
+ onClick={handleItemClick}
356
+ aria-current={isCurrentPage ? 'page' : undefined}
357
+ role={collapsible && !href && !isRootCategory ? 'button' : undefined}
358
+ aria-expanded={
359
+ collapsible && !href && !isRootCategory ? !collapsed : undefined
360
+ }
361
+ href={
362
+ collapsible && !isRootCategory
363
+ ? (hrefWithSSRFallback ?? '#')
364
+ : hrefWithSSRFallback
365
+ }
366
+ {...props}
367
+ >
368
+ {/* Only show collapse button for nested categories (not root) */}
369
+ {href && collapsible && !isRootCategory && (
370
+ <CollapseButton
371
+ collapsed={collapsed}
372
+ categoryLabel={label}
373
+ onClick={(e) => {
374
+ e.preventDefault();
375
+ updateCollapsed();
376
+ }}
377
+ />
378
+ )}
379
+ <CategoryLinkLabel
380
+ label={label}
381
+ isRootCategory={isRootCategory}
382
+ customProps={customProps}
383
+ />
384
+ </Link>
385
+ </div>
386
+
387
+ <Collapsible
388
+ lazy
389
+ as="ul"
390
+ className={clsx('menu__list', isRootCategory && styles.categoryList)}
391
+ collapsed={collapsed}
392
+ >
393
+ <DocSidebarItems
394
+ items={items}
395
+ tabIndex={collapsed ? -1 : 0}
396
+ onItemClick={onItemClick}
397
+ activePath={activePath}
398
+ level={level + 1}
399
+ />
400
+ </Collapsible>
401
+ </li>
402
+ );
403
+ }
@@ -0,0 +1,119 @@
1
+ .categoryLink {
2
+ overflow: hidden;
3
+ display: flex;
4
+ align-items: center;
5
+ padding-left: 0;
6
+ gap: 0.1;
7
+ position: relative;
8
+ }
9
+
10
+ .categoryLink::after {
11
+ order: -1;
12
+ margin-left: 1rem;
13
+ min-width: 1rem;
14
+ width: 1rem;
15
+ height: 1rem;
16
+ flex-shrink: 0;
17
+ background-size: contain;
18
+ }
19
+
20
+ .categoryLinkLabel {
21
+ overflow: hidden;
22
+ display: -webkit-box;
23
+ line-clamp: 2;
24
+ -webkit-box-orient: vertical;
25
+ -webkit-line-clamp: 2;
26
+ color: var(--color-text-primary);
27
+ }
28
+
29
+ .categoryLinkRoot {
30
+ padding-left: 1.5rem;
31
+ }
32
+
33
+ /* Root category container with full-height border */
34
+ .rootCategoryContainer {
35
+ position: relative;
36
+ margin-left: 1rem;
37
+ }
38
+
39
+ /* Circle outline at the top */
40
+ .rootCategoryContainer::before {
41
+ content: '';
42
+ position: absolute;
43
+ left: calc(0.5rem - 4px);
44
+ top: 0.85rem;
45
+ width: 8px;
46
+ height: 8px;
47
+ border-radius: 50%;
48
+ border: 1px solid var(--ifm-toc-border-color);
49
+ background-color: var(--ifm-background-color);
50
+ z-index: 2;
51
+ pointer-events: none;
52
+ transition: border-color 0.15s ease, background-color 0.15s ease;
53
+ }
54
+
55
+ /* Circle outline turns blue on hover */
56
+ .rootCategoryContainer:has(> :global(.menu__list-item-collapsible) > :global(.menu__link):hover)::before {
57
+ border-color: var(--ifm-color-primary-light);
58
+ }
59
+
60
+ /* Circle outline turns blue when category has active item */
61
+ .rootCategoryContainer:has(:global(.menu__link--active))::before {
62
+ border-color: var(--ifm-color-primary-light);
63
+ }
64
+
65
+ /* Vertical line starting from the circle */
66
+ .rootCategoryContainer::after {
67
+ content: '';
68
+ position: absolute;
69
+ left: 0.5rem;
70
+ top: calc(0.85rem + 8px);
71
+ bottom: 0;
72
+ width: 1px;
73
+ background-color: var(--ifm-toc-border-color);
74
+ z-index: 1;
75
+ pointer-events: none;
76
+ }
77
+
78
+ .categoryLinkLabelRoot {
79
+ font-size: 1rem;
80
+ font-weight: 600;
81
+ }
82
+
83
+ .categoryLinkLabelNested {
84
+ font-size: 0.85rem;
85
+ font-weight: 500;
86
+ }
87
+
88
+ .categoryList {
89
+ position: relative;
90
+ margin-left: 0;
91
+ padding-left: 0;
92
+ }
93
+
94
+ /* Reset margin for nested lists inside root category */
95
+ .categoryList :global(.menu__list) {
96
+ margin-left: 0;
97
+ padding-left: 0;
98
+ }
99
+
100
+ /* Border indicator on nested items only (not on root category link itself) */
101
+ .rootCategoryContainer .categoryList :global(.menu__link) {
102
+ position: relative;
103
+ }
104
+
105
+ .rootCategoryContainer .categoryList :global(.menu__link)::before {
106
+ content: '';
107
+ position: absolute;
108
+ left: 0.5rem;
109
+ top: 0;
110
+ bottom: 0;
111
+ width: 2px;
112
+ background-color: transparent;
113
+ z-index: 2;
114
+ transition: background-color 0.15s ease;
115
+ }
116
+
117
+ .rootCategoryContainer .categoryList :global(.menu__link:hover)::before {
118
+ background-color: var(--ifm-color-primary-light);
119
+ }
@@ -0,0 +1,85 @@
1
+ import React, { type ReactNode } from 'react';
2
+ import clsx from 'clsx';
3
+ import { ThemeClassNames } from '@docusaurus/theme-common';
4
+ import { isActiveSidebarItem } from '@docusaurus/plugin-content-docs/client';
5
+ import Link from '@docusaurus/Link';
6
+ import isInternalUrl from '@docusaurus/isInternalUrl';
7
+ import IconExternalLink from '@theme/Icon/ExternalLink';
8
+ import type { Props } from '@theme/DocSidebarItem/Link';
9
+ import ProBadge from '@/components/ProBadge/ProBadge';
10
+ import StatusBadge from '@/components/StatusBadge/StatusBadge';
11
+ import type { WithCustomProps } from '@/types/sidebar';
12
+
13
+ import styles from './styles.module.css';
14
+
15
+ function LinkLabel({ label }: { label: string }) {
16
+ return (
17
+ <span title={label} className={styles.linkLabel}>
18
+ {label}
19
+ </span>
20
+ );
21
+ }
22
+
23
+ export default function DocSidebarItemLink({
24
+ item,
25
+ onItemClick,
26
+ activePath,
27
+ level,
28
+ index: _index,
29
+ ...props
30
+ }: WithCustomProps<Props>): ReactNode {
31
+ const { href, label, className, autoAddBaseUrl, customProps } = item;
32
+ const isActive = isActiveSidebarItem(item, activePath);
33
+ const isInternalLink = isInternalUrl(href);
34
+
35
+ const isLink = item.type === 'link';
36
+
37
+ // Dynamic indent for nested items (level 3+)
38
+ const nestedIndent = level >= 3 ? (level - 2) * 0.05 : 0;
39
+
40
+ return (
41
+ <li
42
+ className={clsx(
43
+ ThemeClassNames.docs.docSidebarItemLink,
44
+ ThemeClassNames.docs.docSidebarItemLinkLevel(level),
45
+ 'menu__list-item',
46
+ className
47
+ )}
48
+ key={label}
49
+ >
50
+ <Link
51
+ className={clsx(
52
+ 'menu__link',
53
+ styles.menuLink,
54
+ !isInternalLink && styles.menuExternalLink,
55
+ {
56
+ 'menu__link--active': isActive,
57
+ }
58
+ )}
59
+ style={
60
+ nestedIndent > 0
61
+ ? { paddingLeft: `${nestedIndent + 2.5}rem` }
62
+ : undefined
63
+ }
64
+ autoAddBaseUrl={autoAddBaseUrl}
65
+ aria-current={isActive ? 'page' : undefined}
66
+ to={href}
67
+ {...(isInternalLink && {
68
+ onClick: onItemClick ? () => onItemClick(item) : undefined,
69
+ })}
70
+ {...props}
71
+ >
72
+ <LinkLabel label={label} />
73
+ {!isInternalLink && <IconExternalLink />}
74
+ {isLink && (
75
+ <>
76
+ {customProps?.plan === 'pro' && <ProBadge />}
77
+ {customProps?.badgeType && (
78
+ <StatusBadge type={customProps.badgeType} />
79
+ )}
80
+ </>
81
+ )}
82
+ </Link>
83
+ </li>
84
+ );
85
+ }
@@ -0,0 +1,18 @@
1
+ .menuExternalLink {
2
+ align-items: center;
3
+ }
4
+
5
+ .menuLink {
6
+ padding-left: 2rem;
7
+ padding-top: 0.25rem;
8
+ padding-bottom: 0.25rem;
9
+ }
10
+
11
+ .linkLabel {
12
+ overflow: hidden;
13
+ display: -webkit-box;
14
+ line-clamp: 2;
15
+ -webkit-box-orient: vertical;
16
+ -webkit-line-clamp: 2;
17
+ font-size: 0.8rem;
18
+ }
@@ -0,0 +1,25 @@
1
+ import React, {type ReactNode} from 'react';
2
+ import clsx from 'clsx';
3
+ import TOCItems from '@theme/TOCItems';
4
+ import type {Props} from '@theme/TOC';
5
+
6
+ import styles from './styles.module.css';
7
+ import TWGBadge from '@/components/TWGBadge/TWGBadge';
8
+
9
+ // Using a custom className
10
+ // This prevents TOCInline/TOCCollapsible getting highlighted by mistake
11
+ const LINK_CLASS_NAME = 'table-of-contents__link toc-highlight';
12
+ const LINK_ACTIVE_CLASS_NAME = 'table-of-contents__link--active';
13
+
14
+ export default function TOC({className, ...props}: Props): ReactNode {
15
+ return (
16
+ <div className={clsx(styles.tableOfContents, 'thin-scrollbar', className)}>
17
+ <TOCItems
18
+ {...props}
19
+ linkClassName={LINK_CLASS_NAME}
20
+ linkActiveClassName={LINK_ACTIVE_CLASS_NAME}
21
+ />
22
+ <TWGBadge visibleOnLarge={true} />
23
+ </div>
24
+ );
25
+ }
@@ -0,0 +1,16 @@
1
+ .tableOfContents {
2
+ max-height: calc(100vh - (var(--ifm-navbar-height) + 2rem));
3
+ overflow-y: auto;
4
+ position: sticky;
5
+ top: calc(var(--ifm-navbar-height) + 1rem);
6
+ }
7
+
8
+ @media (max-width: 996px) {
9
+ .tableOfContents {
10
+ display: none;
11
+ }
12
+
13
+ .docItemContainer {
14
+ padding: 0 0.3rem;
15
+ }
16
+ }
@@ -0,0 +1,23 @@
1
+ import type { SidebarsConfig } from '@docusaurus/plugin-content-docs';
2
+
3
+ export interface CustomSidebarProps {
4
+ plan?: 'pro';
5
+ badgeType?: 'planned' | 'new';
6
+ }
7
+
8
+ export type AddCustomProps<T> = T extends readonly (infer U)[]
9
+ ? Array<AddCustomProps<U>>
10
+ : T extends object
11
+ ? Omit<{ [K in keyof T]: AddCustomProps<T[K]> }, 'customProps'> & {
12
+ customProps?: CustomSidebarProps;
13
+ }
14
+ : T;
15
+
16
+ export type TypedSidebarsConfig = AddCustomProps<SidebarsConfig>;
17
+
18
+ export type WithCustomProps<Props extends { item: unknown }> = Omit<
19
+ Props,
20
+ 'item'
21
+ > & {
22
+ item: AddCustomProps<Props['item']> & Props['item'];
23
+ };