@docusaurus/theme-classic 3.9.2 → 3.10.1-canary-6591

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 (51) hide show
  1. package/README.md +1 -1
  2. package/lib/getSwizzleConfig.js +7 -0
  3. package/lib/theme/Admonition/Layout/index.js +5 -4
  4. package/lib/theme/CodeBlock/Buttons/CopyButton/index.js +15 -1
  5. package/lib/theme/CodeBlock/Line/index.js +13 -5
  6. package/lib/theme/DocCard/Description/index.d.ts +9 -0
  7. package/lib/theme/DocCard/Description/index.js +23 -0
  8. package/lib/theme/DocCard/Description/styles.module.css +10 -0
  9. package/lib/theme/DocCard/Heading/Icon/index.d.ts +9 -0
  10. package/lib/theme/DocCard/Heading/Icon/index.js +18 -0
  11. package/lib/theme/DocCard/Heading/Icon/styles.module.css +11 -0
  12. package/lib/theme/DocCard/Heading/Text/index.d.ts +9 -0
  13. package/lib/theme/DocCard/Heading/Text/index.js +22 -0
  14. package/lib/theme/DocCard/Heading/Text/styles.module.css +10 -0
  15. package/lib/theme/DocCard/Heading/index.d.ts +9 -0
  16. package/lib/theme/DocCard/Heading/index.js +24 -0
  17. package/lib/theme/DocCard/Heading/styles.module.css +11 -0
  18. package/lib/theme/DocCard/Layout/index.d.ts +9 -0
  19. package/lib/theme/DocCard/Layout/index.js +42 -0
  20. package/lib/theme/DocCard/{styles.module.css → Layout/styles.module.css} +0 -8
  21. package/lib/theme/DocCard/index.js +24 -56
  22. package/lib/theme/Layout/index.js +0 -2
  23. package/lib/theme/Navbar/MobileSidebar/Layout/index.js +1 -0
  24. package/lib/theme/NavbarItem/DropdownNavbarItem/Desktop/index.js +1 -1
  25. package/lib/theme/SiteMetadata/index.js +1 -2
  26. package/lib/theme/TabItem/index.d.ts +1 -1
  27. package/lib/theme/TabItem/index.js +15 -1
  28. package/lib/theme/Tabs/index.js +24 -39
  29. package/package.json +24 -25
  30. package/src/getSwizzleConfig.ts +7 -0
  31. package/src/theme/Admonition/Layout/index.tsx +6 -4
  32. package/src/theme/CodeBlock/Buttons/CopyButton/index.tsx +16 -1
  33. package/src/theme/CodeBlock/Line/index.tsx +14 -8
  34. package/src/theme/DocCard/Description/index.tsx +27 -0
  35. package/src/theme/DocCard/Description/styles.module.css +10 -0
  36. package/src/theme/DocCard/Heading/Icon/index.tsx +22 -0
  37. package/src/theme/DocCard/Heading/Icon/styles.module.css +11 -0
  38. package/src/theme/DocCard/Heading/Text/index.tsx +27 -0
  39. package/src/theme/DocCard/Heading/Text/styles.module.css +10 -0
  40. package/src/theme/DocCard/Heading/index.tsx +28 -0
  41. package/src/theme/DocCard/Heading/styles.module.css +11 -0
  42. package/src/theme/DocCard/Layout/index.tsx +55 -0
  43. package/src/theme/DocCard/{styles.module.css → Layout/styles.module.css} +0 -8
  44. package/src/theme/DocCard/index.tsx +28 -79
  45. package/src/theme/Layout/index.tsx +0 -3
  46. package/src/theme/Navbar/MobileSidebar/Layout/index.tsx +2 -1
  47. package/src/theme/NavbarItem/DropdownNavbarItem/Desktop/index.tsx +1 -1
  48. package/src/theme/SiteMetadata/index.tsx +1 -2
  49. package/src/theme/TabItem/index.tsx +28 -3
  50. package/src/theme/Tabs/index.tsx +31 -50
  51. package/src/theme-classic.d.ts +69 -2
@@ -6,122 +6,71 @@
6
6
  */
7
7
 
8
8
  import React, {type ReactNode} from 'react';
9
- import clsx from 'clsx';
10
- import Link from '@docusaurus/Link';
11
9
  import {
12
10
  useDocById,
13
11
  findFirstSidebarItemLink,
14
12
  } from '@docusaurus/plugin-content-docs/client';
15
- import {usePluralForm} from '@docusaurus/theme-common';
13
+ import {
14
+ extractLeadingEmoji,
15
+ useDocCardDescriptionCategoryItemsPlural,
16
+ } from '@docusaurus/theme-common/internal';
16
17
  import isInternalUrl from '@docusaurus/isInternalUrl';
17
- import {translate} from '@docusaurus/Translate';
18
+ import Layout from '@theme/DocCard/Layout';
18
19
 
19
20
  import type {Props} from '@theme/DocCard';
20
- import Heading from '@theme/Heading';
21
21
  import type {
22
22
  PropSidebarItemCategory,
23
23
  PropSidebarItemLink,
24
24
  } from '@docusaurus/plugin-content-docs';
25
25
 
26
- import styles from './styles.module.css';
27
-
28
- function useCategoryItemsPlural() {
29
- const {selectMessage} = usePluralForm();
30
- return (count: number) =>
31
- selectMessage(
32
- count,
33
- translate(
34
- {
35
- message: '1 item|{count} items',
36
- id: 'theme.docs.DocCard.categoryDescription.plurals',
37
- description:
38
- 'The default description for a category card in the generated index about how many items this category includes',
39
- },
40
- {count},
41
- ),
42
- );
43
- }
44
-
45
- function CardContainer({
46
- className,
47
- href,
48
- children,
49
- }: {
50
- className?: string;
51
- href: string;
52
- children: ReactNode;
53
- }): ReactNode {
54
- return (
55
- <Link
56
- href={href}
57
- className={clsx('card padding--lg', styles.cardContainer, className)}>
58
- {children}
59
- </Link>
60
- );
26
+ function getFallbackEmojiIcon(
27
+ item: PropSidebarItemLink | PropSidebarItemCategory,
28
+ ): string {
29
+ if (item.type === 'category') {
30
+ return '🗃';
31
+ }
32
+ return isInternalUrl(item.href) ? '📄️' : '🔗';
61
33
  }
62
34
 
63
- function CardLayout({
64
- className,
65
- href,
66
- icon,
67
- title,
68
- description,
69
- }: {
70
- className?: string;
71
- href: string;
72
- icon: ReactNode;
73
- title: string;
74
- description?: string;
75
- }): ReactNode {
76
- return (
77
- <CardContainer href={href} className={className}>
78
- <Heading
79
- as="h2"
80
- className={clsx('text--truncate', styles.cardTitle)}
81
- title={title}>
82
- {icon} {title}
83
- </Heading>
84
- {description && (
85
- <p
86
- className={clsx('text--truncate', styles.cardDescription)}
87
- title={description}>
88
- {description}
89
- </p>
90
- )}
91
- </CardContainer>
92
- );
35
+ function getIconTitleProps(
36
+ item: PropSidebarItemLink | PropSidebarItemCategory,
37
+ ): {icon: ReactNode; title: string} {
38
+ const extracted = extractLeadingEmoji(item.label);
39
+ const emoji = extracted.emoji ?? getFallbackEmojiIcon(item);
40
+ return {
41
+ icon: emoji,
42
+ title: extracted.rest.trim(),
43
+ };
93
44
  }
94
45
 
95
46
  function CardCategory({item}: {item: PropSidebarItemCategory}): ReactNode {
96
47
  const href = findFirstSidebarItemLink(item);
97
- const categoryItemsPlural = useCategoryItemsPlural();
48
+ const categoryItemsPlural = useDocCardDescriptionCategoryItemsPlural();
98
49
 
99
50
  // Unexpected: categories that don't have a link have been filtered upfront
100
51
  if (!href) {
101
52
  return null;
102
53
  }
103
-
104
54
  return (
105
- <CardLayout
55
+ <Layout
56
+ item={item}
106
57
  className={item.className}
107
58
  href={href}
108
- icon="🗃️"
109
- title={item.label}
110
59
  description={item.description ?? categoryItemsPlural(item.items.length)}
60
+ {...getIconTitleProps(item)}
111
61
  />
112
62
  );
113
63
  }
114
64
 
115
65
  function CardLink({item}: {item: PropSidebarItemLink}): ReactNode {
116
- const icon = isInternalUrl(item.href) ? '📄️' : '🔗';
117
66
  const doc = useDocById(item.docId ?? undefined);
118
67
  return (
119
- <CardLayout
68
+ <Layout
69
+ item={item}
120
70
  className={item.className}
121
71
  href={item.href}
122
- icon={icon}
123
- title={item.label}
124
72
  description={item.description ?? doc?.description}
73
+ {...getIconTitleProps(item)}
125
74
  />
126
75
  );
127
76
  }
@@ -13,7 +13,6 @@ import {
13
13
  SkipToContentFallbackId,
14
14
  ThemeClassNames,
15
15
  } from '@docusaurus/theme-common';
16
- import {useKeyboardNavigation} from '@docusaurus/theme-common/internal';
17
16
  import SkipToContent from '@theme/SkipToContent';
18
17
  import AnnouncementBar from '@theme/AnnouncementBar';
19
18
  import Navbar from '@theme/Navbar';
@@ -33,8 +32,6 @@ export default function Layout(props: Props): ReactNode {
33
32
  description,
34
33
  } = props;
35
34
 
36
- useKeyboardNavigation();
37
-
38
35
  return (
39
36
  <LayoutProvider>
40
37
  <PageMetadata title={title} description={description} />
@@ -17,7 +17,8 @@ import type {Props} from '@theme/Navbar/MobileSidebar/Layout';
17
17
  function inertProps(inert: boolean) {
18
18
  const isBeforeReact19 = parseInt(version!.split('.')[0]!, 10) < 19;
19
19
  if (isBeforeReact19) {
20
- return {inert: inert ? '' : undefined};
20
+ // TODO Docusaurus v4: remove temporary inert workaround
21
+ return {inert: inert ? '' : undefined} as unknown as {inert: boolean};
21
22
  }
22
23
  return {inert};
23
24
  }
@@ -64,7 +64,7 @@ export default function DropdownNavbarItemDesktop({
64
64
  {...props}
65
65
  onClick={props.to ? undefined : (e) => e.preventDefault()}
66
66
  onKeyDown={(e) => {
67
- if (e.key === 'Enter') {
67
+ if (e.key === 'Enter' || e.key === ' ') {
68
68
  e.preventDefault();
69
69
  setShowDropdown(!showDropdown);
70
70
  }
@@ -13,7 +13,6 @@ import {PageMetadata, useThemeConfig} from '@docusaurus/theme-common';
13
13
  import {
14
14
  DEFAULT_SEARCH_TAG,
15
15
  useAlternatePageUtils,
16
- keyboardFocusedClassName,
17
16
  } from '@docusaurus/theme-common/internal';
18
17
  import {useLocation} from '@docusaurus/router';
19
18
  import {applyTrailingSlash} from '@docusaurus/utils-common';
@@ -130,7 +129,7 @@ export default function SiteMetadata(): ReactNode {
130
129
  <meta name="twitter:card" content="summary_large_image" />
131
130
  {/* The keyboard focus class name need to be applied when SSR so links
132
131
  are outlined when JS is disabled */}
133
- <body className={keyboardFocusedClassName} />
132
+ <body />
134
133
  </Head>
135
134
 
136
135
  {defaultImage && <PageMetadata image={defaultImage} />}
@@ -7,15 +7,20 @@
7
7
 
8
8
  import React, {type ReactNode} from 'react';
9
9
  import clsx from 'clsx';
10
+ import {useTabs} from '@docusaurus/theme-common/internal';
10
11
  import type {Props} from '@theme/TabItem';
11
12
 
12
13
  import styles from './styles.module.css';
13
14
 
14
- export default function TabItem({
15
+ function TabItemPanel({
15
16
  children,
16
- hidden,
17
17
  className,
18
- }: Props): ReactNode {
18
+ hidden,
19
+ }: {
20
+ children: ReactNode;
21
+ className?: string;
22
+ hidden?: boolean;
23
+ }) {
19
24
  return (
20
25
  <div
21
26
  role="tabpanel"
@@ -25,3 +30,23 @@ export default function TabItem({
25
30
  </div>
26
31
  );
27
32
  }
33
+
34
+ export default function TabItem({
35
+ children,
36
+ className,
37
+ value,
38
+ }: Props): ReactNode {
39
+ const {selectedValue, lazy} = useTabs();
40
+ const isSelected = value === selectedValue;
41
+
42
+ // TODO Docusaurus v4: use <Activity> ?
43
+ if (!isSelected && lazy) {
44
+ return null;
45
+ }
46
+
47
+ return (
48
+ <TabItemPanel className={className} hidden={!isSelected}>
49
+ {children}
50
+ </TabItemPanel>
51
+ );
52
+ }
@@ -5,26 +5,23 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import React, {cloneElement, type ReactElement, type ReactNode} from 'react';
8
+ import React, {type ReactNode} from 'react';
9
9
  import clsx from 'clsx';
10
10
  import {ThemeClassNames} from '@docusaurus/theme-common';
11
11
  import {
12
12
  useScrollPositionBlocker,
13
+ useTabsContextValue,
13
14
  useTabs,
14
15
  sanitizeTabsChildren,
15
- type TabItemProps,
16
+ TabsProvider,
16
17
  } from '@docusaurus/theme-common/internal';
17
18
  import useIsBrowser from '@docusaurus/useIsBrowser';
18
19
  import type {Props} from '@theme/Tabs';
19
20
  import styles from './styles.module.css';
20
21
 
21
- function TabList({
22
- className,
23
- block,
24
- selectedValue,
25
- selectValue,
26
- tabValues,
27
- }: Props & ReturnType<typeof useTabs>) {
22
+ function TabList({className}: {className?: string}) {
23
+ const {selectedValue, selectValue, tabValues, block} = useTabs();
24
+
28
25
  const tabRefs: (HTMLLIElement | null)[] = [];
29
26
  const {blockElementScrollPositionUntilNextRender} =
30
27
  useScrollPositionBlocker();
@@ -88,8 +85,8 @@ function TabList({
88
85
  tabIndex={selectedValue === value ? 0 : -1}
89
86
  aria-selected={selectedValue === value}
90
87
  key={value}
91
- ref={(tabControl) => {
92
- tabRefs.push(tabControl);
88
+ ref={(ref) => {
89
+ tabRefs.push(ref);
93
90
  }}
94
91
  onKeyDown={handleKeydown}
95
92
  onClick={handleTabChange}
@@ -109,40 +106,17 @@ function TabList({
109
106
  );
110
107
  }
111
108
 
112
- function TabContent({
113
- lazy,
114
- children,
115
- selectedValue,
116
- }: Props & ReturnType<typeof useTabs>) {
117
- const childTabs = (Array.isArray(children) ? children : [children]).filter(
118
- Boolean,
119
- ) as ReactElement<TabItemProps>[];
120
- if (lazy) {
121
- const selectedTabItem = childTabs.find(
122
- (tabItem) => tabItem.props.value === selectedValue,
123
- );
124
- if (!selectedTabItem) {
125
- // fail-safe or fail-fast? not sure what's best here
126
- return null;
127
- }
128
- return cloneElement(selectedTabItem, {
129
- className: clsx('margin-top--md', selectedTabItem.props.className),
130
- });
131
- }
132
- return (
133
- <div className="margin-top--md">
134
- {childTabs.map((tabItem, i) =>
135
- cloneElement(tabItem, {
136
- key: i,
137
- hidden: tabItem.props.value !== selectedValue,
138
- }),
139
- )}
140
- </div>
141
- );
109
+ function TabContent({children}: {children: ReactNode}) {
110
+ return <div className="margin-top--md">{children}</div>;
142
111
  }
143
112
 
144
- function TabsComponent(props: Props): ReactNode {
145
- const tabs = useTabs(props);
113
+ function TabsContainer({
114
+ className,
115
+ children,
116
+ }: {
117
+ className?: string;
118
+ children: ReactNode;
119
+ }): ReactNode {
146
120
  return (
147
121
  <div
148
122
  className={clsx(
@@ -152,21 +126,28 @@ function TabsComponent(props: Props): ReactNode {
152
126
  'tabs-container',
153
127
  styles.tabList,
154
128
  )}>
155
- <TabList {...tabs} {...props} />
156
- <TabContent {...tabs} {...props} />
129
+ <TabList
130
+ // Surprising but historical
131
+ // className is applied on TabList, not on TabsContainer
132
+ className={className}
133
+ />
134
+ <TabContent>{children}</TabContent>
157
135
  </div>
158
136
  );
159
137
  }
160
138
 
161
139
  export default function Tabs(props: Props): ReactNode {
162
140
  const isBrowser = useIsBrowser();
141
+ const value = useTabsContextValue(props);
163
142
  return (
164
- <TabsComponent
143
+ <TabsProvider
144
+ value={value}
165
145
  // Remount tabs after hydration
166
146
  // Temporary fix for https://github.com/facebook/docusaurus/issues/5653
167
- key={String(isBrowser)}
168
- {...props}>
169
- {sanitizeTabsChildren(props.children)}
170
- </TabsComponent>
147
+ key={String(isBrowser)}>
148
+ <TabsContainer className={props.className}>
149
+ {sanitizeTabsChildren(props.children)}
150
+ </TabsContainer>
151
+ </TabsProvider>
171
152
  );
172
153
  }
@@ -48,6 +48,7 @@ declare module '@theme/Admonition' {
48
48
  readonly icon?: ReactNode;
49
49
  readonly title?: ReactNode;
50
50
  readonly className?: string;
51
+ readonly id?: string;
51
52
  }
52
53
 
53
54
  export default function Admonition(props: Props): ReactNode;
@@ -123,6 +124,7 @@ declare module '@theme/Admonition/Layout' {
123
124
  readonly icon?: ReactNode;
124
125
  readonly title?: ReactNode;
125
126
  readonly className?: string;
127
+ readonly id?: string;
126
128
  }
127
129
  export default function AdmonitionLayout(props: Props): ReactNode;
128
130
  }
@@ -580,6 +582,71 @@ declare module '@theme/DocCard' {
580
582
  export default function DocCard(props: Props): ReactNode;
581
583
  }
582
584
 
585
+ declare module '@theme/DocCard/Heading' {
586
+ import type {ReactNode} from 'react';
587
+ import type {PropSidebarItem} from '@docusaurus/plugin-content-docs';
588
+
589
+ export interface Props {
590
+ readonly item: PropSidebarItem;
591
+ readonly icon: ReactNode;
592
+ readonly title: string;
593
+ }
594
+
595
+ export default function DocCardHeading(props: Props): ReactNode;
596
+ }
597
+
598
+ declare module '@theme/DocCard/Heading/Icon' {
599
+ import type {ReactNode} from 'react';
600
+ import type {PropSidebarItem} from '@docusaurus/plugin-content-docs';
601
+
602
+ export interface Props {
603
+ readonly item: PropSidebarItem;
604
+ readonly icon: ReactNode;
605
+ }
606
+
607
+ export default function DocCardHeadingIcon(props: Props): ReactNode;
608
+ }
609
+
610
+ declare module '@theme/DocCard/Heading/Text' {
611
+ import type {ReactNode} from 'react';
612
+ import type {PropSidebarItem} from '@docusaurus/plugin-content-docs';
613
+
614
+ export interface Props {
615
+ readonly item: PropSidebarItem;
616
+ readonly title: string;
617
+ }
618
+
619
+ export default function DocCardHeadingText(props: Props): ReactNode;
620
+ }
621
+
622
+ declare module '@theme/DocCard/Description' {
623
+ import type {ReactNode} from 'react';
624
+ import type {PropSidebarItem} from '@docusaurus/plugin-content-docs';
625
+
626
+ export interface Props {
627
+ readonly item: PropSidebarItem;
628
+ readonly description: string;
629
+ }
630
+
631
+ export default function DocCardDescription(props: Props): ReactNode;
632
+ }
633
+
634
+ declare module '@theme/DocCard/Layout' {
635
+ import type {ReactNode} from 'react';
636
+ import type {PropSidebarItem} from '@docusaurus/plugin-content-docs';
637
+
638
+ export interface Props {
639
+ readonly item: PropSidebarItem;
640
+ readonly className?: string;
641
+ readonly href: string;
642
+ readonly icon: ReactNode;
643
+ readonly title: string;
644
+ readonly description?: string;
645
+ }
646
+
647
+ export default function DocCardLayout(props: Props): ReactNode;
648
+ }
649
+
583
650
  declare module '@theme/DocCardList' {
584
651
  import type {ReactNode} from 'react';
585
652
  import type {PropSidebarItem} from '@docusaurus/plugin-content-docs';
@@ -1743,11 +1810,11 @@ declare module '@theme/Icon/LightMode' {
1743
1810
  }
1744
1811
 
1745
1812
  declare module '@theme/Icon/SystemColorMode' {
1746
- import type {ComponentProps} from 'react';
1813
+ import type {ComponentProps, ReactNode} from 'react';
1747
1814
 
1748
1815
  export interface Props extends ComponentProps<'svg'> {}
1749
1816
 
1750
- export default function IconSystemColorMode(props: Props): JSX.Element;
1817
+ export default function IconSystemColorMode(props: Props): ReactNode;
1751
1818
  }
1752
1819
 
1753
1820
  declare module '@theme/Icon/Menu' {