@redocly/theme 0.58.0-next.9 → 0.59.0-next.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 (84) hide show
  1. package/lib/components/Catalog/CatalogEntity/CatalogEntity.d.ts +5 -1
  2. package/lib/components/Catalog/CatalogEntity/CatalogEntity.js +4 -4
  3. package/lib/components/Catalog/CatalogEntity/CatalogEntityMetadata.js +3 -3
  4. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityApiDescriptionRelations.js +1 -1
  5. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityTeamRelations.js +1 -1
  6. package/lib/components/Catalog/CatalogEntity/CatalogEntitySchema.d.ts +5 -1
  7. package/lib/components/Catalog/CatalogEntity/CatalogEntitySchema.js +9 -7
  8. package/lib/components/CodeBlock/CodeBlock.d.ts +5 -12
  9. package/lib/components/CodeBlock/CodeBlockControls.d.ts +3 -3
  10. package/lib/components/CodeBlock/CodeBlockControls.js +1 -1
  11. package/lib/components/CodeBlock/CodeBlockDropdown.d.ts +2 -2
  12. package/lib/components/CodeBlock/CodeBlockDropdown.js +4 -13
  13. package/lib/components/CodeBlock/CodeBlockTabs.d.ts +2 -2
  14. package/lib/components/CodeBlock/CodeBlockTabs.js +4 -3
  15. package/lib/components/JsonViewer/JsonViewer.d.ts +1 -1
  16. package/lib/components/JsonViewer/JsonViewer.js +9 -10
  17. package/lib/components/PageActions/PageActions.d.ts +4 -1
  18. package/lib/components/PageActions/PageActions.js +2 -2
  19. package/lib/components/Panel/variables.js +1 -0
  20. package/lib/components/Tag/Tag.d.ts +3 -2
  21. package/lib/components/Tag/Tag.js +21 -5
  22. package/lib/components/Tag/variables.dark.js +135 -0
  23. package/lib/components/Tag/variables.js +120 -58
  24. package/lib/core/constants/catalog.js +4 -0
  25. package/lib/core/contexts/CodeSnippetContext.d.ts +14 -6
  26. package/lib/core/contexts/CodeSnippetContext.js +57 -14
  27. package/lib/core/hooks/use-codeblock-tabs-controls.d.ts +2 -2
  28. package/lib/core/hooks/use-local-state.js +22 -18
  29. package/lib/core/hooks/use-page-actions.d.ts +2 -1
  30. package/lib/core/hooks/use-page-actions.js +48 -6
  31. package/lib/core/hooks/use-tabs.d.ts +11 -6
  32. package/lib/core/hooks/use-tabs.js +117 -207
  33. package/lib/core/openapi/index.d.ts +1 -0
  34. package/lib/core/openapi/index.js +3 -1
  35. package/lib/core/types/l10n.d.ts +1 -1
  36. package/lib/core/types/open-api-server.d.ts +1 -0
  37. package/lib/core/utils/index.d.ts +1 -0
  38. package/lib/core/utils/index.js +1 -0
  39. package/lib/core/utils/tabs.d.ts +1 -0
  40. package/lib/core/utils/tabs.js +8 -0
  41. package/lib/icons/CursorIcon/CursorIcon.d.ts +9 -0
  42. package/lib/icons/CursorIcon/CursorIcon.js +22 -0
  43. package/lib/layouts/DocumentationLayout.js +1 -3
  44. package/lib/markdoc/components/CodeGroup/CodeGroup.js +49 -27
  45. package/lib/markdoc/components/Tabs/Tab.js +1 -1
  46. package/lib/markdoc/components/Tabs/TabList.d.ts +2 -14
  47. package/lib/markdoc/components/Tabs/TabList.js +65 -16
  48. package/lib/markdoc/components/Tabs/Tabs.d.ts +2 -2
  49. package/lib/markdoc/components/Tabs/Tabs.js +11 -87
  50. package/lib/markdoc/tags/tabs.js +5 -0
  51. package/package.json +4 -4
  52. package/src/components/Catalog/CatalogEntity/CatalogEntity.tsx +15 -2
  53. package/src/components/Catalog/CatalogEntity/CatalogEntityMetadata.tsx +3 -3
  54. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityApiDescriptionRelations.tsx +1 -1
  55. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityTeamRelations.tsx +1 -1
  56. package/src/components/Catalog/CatalogEntity/CatalogEntitySchema.tsx +27 -18
  57. package/src/components/CodeBlock/CodeBlock.tsx +5 -11
  58. package/src/components/CodeBlock/CodeBlockControls.tsx +4 -7
  59. package/src/components/CodeBlock/CodeBlockDropdown.tsx +11 -20
  60. package/src/components/CodeBlock/CodeBlockTabs.tsx +8 -8
  61. package/src/components/JsonViewer/JsonViewer.tsx +16 -9
  62. package/src/components/PageActions/PageActions.tsx +6 -4
  63. package/src/components/Panel/variables.ts +1 -0
  64. package/src/components/Tag/Tag.tsx +33 -8
  65. package/src/components/Tag/variables.dark.ts +135 -0
  66. package/src/components/Tag/variables.ts +120 -58
  67. package/src/core/constants/catalog.ts +4 -0
  68. package/src/core/contexts/CodeSnippetContext.tsx +54 -18
  69. package/src/core/hooks/use-codeblock-tabs-controls.ts +2 -2
  70. package/src/core/hooks/use-local-state.ts +28 -19
  71. package/src/core/hooks/use-page-actions.ts +63 -6
  72. package/src/core/hooks/use-tabs.ts +160 -238
  73. package/src/core/openapi/index.ts +1 -0
  74. package/src/core/types/l10n.ts +13 -0
  75. package/src/core/types/open-api-server.ts +1 -0
  76. package/src/core/utils/index.ts +1 -0
  77. package/src/core/utils/tabs.ts +4 -0
  78. package/src/icons/CursorIcon/CursorIcon.tsx +35 -0
  79. package/src/layouts/DocumentationLayout.tsx +3 -10
  80. package/src/markdoc/components/CodeGroup/CodeGroup.tsx +81 -52
  81. package/src/markdoc/components/Tabs/Tab.tsx +1 -0
  82. package/src/markdoc/components/Tabs/TabList.tsx +85 -30
  83. package/src/markdoc/components/Tabs/Tabs.tsx +12 -125
  84. package/src/markdoc/tags/tabs.ts +5 -0
@@ -5,65 +5,55 @@ import {
5
5
  type CodeBlockProps,
6
6
  } from '@redocly/theme/components/CodeBlock/CodeBlock';
7
7
  import { langToName } from '@redocly/theme/core/utils';
8
- import { useActiveCodeSnippetName } from '@redocly/theme/core/contexts';
8
+ import { useActiveCodeSnippetId } from '@redocly/theme/core/contexts';
9
+
10
+ type SnippetData = {
11
+ name: string;
12
+ languageName: string;
13
+ lang: string;
14
+ props: CodeBlockProps;
15
+ id: string;
16
+ };
9
17
 
10
18
  export function CodeGroup(props: React.PropsWithChildren<{ mode?: 'tabs' | 'dropdown' }>) {
11
19
  const mode = props.mode || 'tabs';
12
20
  const isTabsMode = mode === 'tabs';
13
21
 
14
22
  const rawSnippets = React.useMemo(
15
- () =>
16
- React.Children.toArray(props.children).map((child, idx) => {
17
- const childProps = child as React.ReactElement<CodeBlockProps>;
18
- return {
19
- name: getTabName(childProps.props, idx),
20
- languageName: langToName(childProps.props.lang || 'Default'),
21
- lang: childProps.props.lang || '',
22
- props: childProps.props,
23
- };
24
- }),
23
+ () => parseSnippetsFromChildren(props.children),
25
24
  [props.children],
26
25
  );
27
26
 
28
- const [activeSnippetName, setActiveSnippetName] = useActiveCodeSnippetName(mode);
29
-
30
- const snippets: Record<string, CodeBlockProps> = React.useMemo(
31
- () =>
32
- Object.fromEntries(
33
- rawSnippets.map((snippet) => {
34
- const getItemName = (snippet: (typeof rawSnippets)[number]) =>
35
- isTabsMode ? snippet?.name : snippet?.languageName || '';
36
-
37
- const name = getItemName(snippet);
38
-
39
- const items = rawSnippets.map((item) => ({
40
- name: getItemName(item),
41
- lang: item.lang,
42
- }));
43
- const itemsProps = {
44
- items,
45
- onChange: (name: string | string[]) => {
46
- setActiveSnippetName(name as string);
47
- },
48
- value: activeSnippetName || getItemName(rawSnippets[0]),
49
- };
50
- const snippetProps = {
51
- ...snippet.props,
52
- header: {
53
- ...snippet.props.header,
54
- title: isTabsMode ? undefined : snippet.name,
55
- },
56
- ...(isTabsMode ? { tabs: itemsProps } : { dropdown: itemsProps }),
57
- };
58
-
59
- return [name, snippetProps];
60
- }),
61
- ),
62
- [rawSnippets, activeSnippetName, isTabsMode, setActiveSnippetName],
63
- );
27
+ const groupId = React.useMemo(() => generateGroupId(rawSnippets, mode), [rawSnippets, mode]);
28
+
29
+ const [activeSnippetId, setActiveSnippetId] = useActiveCodeSnippetId(groupId, rawSnippets);
30
+
31
+ const snippets = React.useMemo(() => {
32
+ const items = createItemsFromSnippets(rawSnippets, isTabsMode);
64
33
 
65
- const firstName = Object.keys(snippets)[0];
66
- const activeSnippet = snippets[activeSnippetName] || snippets[firstName];
34
+ const itemsProps = {
35
+ items,
36
+ onChange: (id: string | string[]) => setActiveSnippetId(id as string),
37
+ value: activeSnippetId,
38
+ };
39
+
40
+ return Object.fromEntries(
41
+ rawSnippets.map((snippet: SnippetData) => {
42
+ const snippetProps = {
43
+ ...snippet.props,
44
+ header: {
45
+ ...snippet.props.header,
46
+ title: isTabsMode ? undefined : snippet.name,
47
+ },
48
+ ...(isTabsMode ? { tabs: itemsProps } : { dropdown: itemsProps }),
49
+ };
50
+
51
+ return [snippet.id, snippetProps];
52
+ }),
53
+ );
54
+ }, [rawSnippets, activeSnippetId, isTabsMode, setActiveSnippetId]);
55
+
56
+ const activeSnippet = snippets[activeSnippetId];
67
57
  if (!activeSnippet) {
68
58
  return null;
69
59
  }
@@ -71,8 +61,47 @@ export function CodeGroup(props: React.PropsWithChildren<{ mode?: 'tabs' | 'drop
71
61
  return <CodeBlockComponent {...activeSnippet} />;
72
62
  }
73
63
 
64
+ function generateContentHash(content: string): number {
65
+ let hash = 0;
66
+ for (let i = 0; i < content.length; i++) {
67
+ hash = content.charCodeAt(i) + ((hash << 5) - hash);
68
+ }
69
+ return Math.abs(hash);
70
+ }
71
+
72
+ // Generate unique group ID for CodeGroup instance
73
+ // Examples: "dropdown-8901234", "tabs-1234567"
74
+ function generateGroupId(rawSnippets: SnippetData[], mode: string): string {
75
+ const content = rawSnippets.map((s) => s.id + (s.props.source || '')).join('|') + `|${mode}`;
76
+ const hash = generateContentHash(content);
77
+
78
+ return `${mode}-${hash}`;
79
+ }
80
+
74
81
  function getTabName(props: CodeBlockProps, idx: number): string {
75
- return String(
76
- props.header?.title || props.file || langToName(props.lang || '') || 'Tab ' + String(idx + 1),
77
- );
82
+ const fallbackName = `Tab ${idx + 1}`;
83
+ return String(props.header?.title || props.file || langToName(props.lang || '') || fallbackName);
84
+ }
85
+
86
+ function parseSnippetsFromChildren(children: React.ReactNode): SnippetData[] {
87
+ return React.Children.toArray(children).map((child, idx) => {
88
+ const childProps = child as React.ReactElement<CodeBlockProps>;
89
+ const props = childProps.props;
90
+
91
+ return {
92
+ name: getTabName(props, idx),
93
+ languageName: String(langToName(props.lang || 'Default') || ''),
94
+ lang: props.lang || '',
95
+ props,
96
+ id: `${props.lang || ''}-${idx}`,
97
+ };
98
+ });
99
+ }
100
+
101
+ function createItemsFromSnippets(snippets: SnippetData[], isTabsMode: boolean) {
102
+ return snippets.map((snippet) => ({
103
+ name: isTabsMode ? snippet.name : snippet.languageName || '',
104
+ lang: snippet.lang,
105
+ id: snippet.id,
106
+ }));
78
107
  }
@@ -34,6 +34,7 @@ export function TabComponent({
34
34
  <TabItem data-component-name="Markdoc/Tabs/Tab" size={size} tabIndex={0}>
35
35
  <TabButtonLink
36
36
  id={`tab-${tabId}`}
37
+ data-label={label}
37
38
  role="tab"
38
39
  aria-selected="false"
39
40
  aria-controls={`panel-${tabId}`}
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useEffect, useRef } from 'react';
2
2
  import styled, { css } from 'styled-components';
3
3
 
4
4
  import type { JSX } from 'react';
@@ -9,41 +9,43 @@ import { Dropdown } from '@redocly/theme/components/Dropdown/Dropdown';
9
9
  import { DropdownMenu } from '@redocly/theme/components/Dropdown/DropdownMenu';
10
10
  import { DropdownMenuItem } from '@redocly/theme/components/Dropdown/DropdownMenuItem';
11
11
  import { Button } from '@redocly/theme/components/Button/Button';
12
+ import { useTabs } from '@redocly/theme/core/hooks';
13
+ import { getTabId } from '@redocly/theme/core/utils';
12
14
 
13
15
  type TabListProps = {
14
16
  childrenArray: React.ReactElement<TabItemProps>[];
15
17
  size: TabsSize;
16
- overflowTabs: number[];
17
- visibleTabs: number[];
18
- setTabRef: (element: HTMLButtonElement | null, index: number) => void;
19
- onTabClick: (labelOrIndex: string | number) => void;
20
- handleKeyboard: (event: React.KeyboardEvent<HTMLButtonElement>, index: number) => void;
21
- getTabId: (label: string, index: number) => string;
22
18
  activeTab: string;
23
- isAnimating: boolean;
24
- highlightStyle: { left: number; width: number };
25
- allTabsHidden: boolean;
26
- tabsContainerRef: React.RefObject<HTMLUListElement | null>;
19
+ onTabChange: (tab: string) => void;
27
20
  };
28
21
 
29
22
  export function TabList({
30
23
  childrenArray,
31
24
  size,
32
- overflowTabs,
33
- visibleTabs,
34
- setTabRef,
35
- onTabClick,
36
- handleKeyboard,
37
- getTabId,
38
25
  activeTab,
39
- isAnimating,
40
- highlightStyle,
41
- allTabsHidden,
42
- tabsContainerRef,
26
+ onTabChange,
43
27
  }: TabListProps): JSX.Element {
28
+ const tabsContainerRef = useRef<HTMLUListElement>(null);
29
+
30
+ const { allTabsHidden, overflowTabs, visibleTabs, handleKeyboard, onTabClick, setTabRef } =
31
+ useTabs({
32
+ activeTab,
33
+ onTabChange,
34
+ containerRef: tabsContainerRef,
35
+ totalTabs: childrenArray.length,
36
+ });
37
+
38
+ const { highlightStyle } = useHighlightBarAnimation({
39
+ activeTab,
40
+ childrenArray,
41
+ overflowTabs,
42
+ tabsContainerRef,
43
+ visibleTabs,
44
+ });
45
+
44
46
  return (
45
- <TabListContainer role="tablist" ref={tabsContainerRef} data-animating={isAnimating}>
46
- <HighlightBar size={size} style={{ left: highlightStyle.left, width: highlightStyle.width }}>
47
+ <TabListContainer role="tablist" ref={tabsContainerRef}>
48
+ <HighlightBar size={size} style={highlightStyle}>
47
49
  <div />
48
50
  </HighlightBar>
49
51
  {childrenArray.map((child, index) => {
@@ -58,13 +60,7 @@ export function TabList({
58
60
  icon={icon}
59
61
  size={size}
60
62
  disabled={child.props.disable}
61
- setRef={(el: HTMLButtonElement | null) => {
62
- setTabRef(el, index);
63
- if (el) {
64
- el.setAttribute('data-label', label);
65
- el.setAttribute('data-animating', isAnimating.toString());
66
- }
67
- }}
63
+ setRef={(el: HTMLButtonElement | null) => setTabRef(el, index)}
68
64
  onKeyDown={(event) => handleKeyboard(event, index)}
69
65
  onClick={() => {
70
66
  child.props.onClick?.();
@@ -104,6 +100,7 @@ export function TabList({
104
100
  key={`more-${tabId}`}
105
101
  active={activeTab === label}
106
102
  onAction={() => {
103
+ childrenArray[index].props.onClick?.();
107
104
  onTabClick(index);
108
105
  }}
109
106
  disabled={childrenArray[index].props.disable}
@@ -120,6 +117,64 @@ export function TabList({
120
117
  );
121
118
  }
122
119
 
120
+ type UseHighlightBarAnimationProps = {
121
+ childrenArray: React.ReactElement<TabItemProps>[];
122
+ activeTab: string;
123
+ tabsContainerRef: React.RefObject<HTMLElement | null>;
124
+ visibleTabs: number[];
125
+ overflowTabs: number[];
126
+ };
127
+ const useHighlightBarAnimation = (props: UseHighlightBarAnimationProps) => {
128
+ const { childrenArray, activeTab, tabsContainerRef, visibleTabs, overflowTabs } = props;
129
+
130
+ const [highlightStyle, setHighlightStyle] = React.useState<{ left: number; width: number }>({
131
+ left: 0,
132
+ width: 0,
133
+ });
134
+
135
+ useEffect(() => {
136
+ const activeIndex = childrenArray.findIndex((child) => child.props.label === activeTab);
137
+ const container = tabsContainerRef.current;
138
+
139
+ if (!container || activeIndex === -1) {
140
+ setHighlightStyle({ left: 0, width: 0 });
141
+ return;
142
+ }
143
+
144
+ const activeTabElement: HTMLElement | null = container.querySelector(
145
+ `[data-label="${activeTab}"]`,
146
+ );
147
+ if (!activeTabElement) return;
148
+
149
+ container.querySelectorAll('[data-label]').forEach((el) => {
150
+ el.classList.remove('active');
151
+ });
152
+
153
+ const { offsetLeft, offsetWidth } = activeTabElement;
154
+
155
+ if (visibleTabs.includes(activeIndex)) {
156
+ activeTabElement.classList.add('active');
157
+ setHighlightStyle({ left: offsetLeft, width: offsetWidth });
158
+ return;
159
+ }
160
+
161
+ if (overflowTabs.includes(activeIndex)) {
162
+ const moreButton = container.querySelector('button');
163
+ if (!moreButton) return;
164
+
165
+ const moreButtonRect = moreButton.getBoundingClientRect();
166
+ const containerRect = container.getBoundingClientRect();
167
+ setHighlightStyle({
168
+ left: moreButtonRect.left - containerRect.left,
169
+ width: moreButtonRect.width,
170
+ });
171
+ return;
172
+ }
173
+ }, [activeTab, childrenArray, visibleTabs, overflowTabs, tabsContainerRef]);
174
+
175
+ return { highlightStyle };
176
+ };
177
+
123
178
  export const TabListContainer = styled.ul`
124
179
  position: relative;
125
180
  display: flex;
@@ -1,10 +1,11 @@
1
- import React, { useRef, useState, useEffect } from 'react';
2
- import styled, { css } from 'styled-components';
1
+ import React, { useState, useEffect } from 'react';
2
+ import styled from 'styled-components';
3
3
 
4
4
  import type { JSX } from 'react';
5
5
 
6
- import { useTabs } from '@redocly/theme/core/hooks';
6
+ import { useActiveTab } from '@redocly/theme/core/hooks';
7
7
  import { TabList } from '@redocly/theme/markdoc/components/Tabs/TabList';
8
+ import { getTabId } from '@redocly/theme/core/utils';
8
9
 
9
10
  export enum TabsSize {
10
11
  SMALL = 'small',
@@ -20,140 +21,38 @@ export type TabItemProps = {
20
21
  };
21
22
 
22
23
  type TabsProps = {
24
+ id?: string;
23
25
  children: React.ReactElement<TabItemProps>[];
24
26
  className?: string;
25
27
  size: TabsSize;
26
- forceReady?: boolean;
27
28
  initialTab?: string;
28
29
  };
29
30
 
30
31
  export function Tabs({
32
+ id,
31
33
  children,
32
34
  className,
33
35
  size,
34
- forceReady = false,
35
36
  initialTab: propInitialTab,
36
37
  }: TabsProps): JSX.Element {
37
38
  const [childrenArray, setChildrenArray] = useState<React.ReactElement<TabItemProps>[]>(
38
39
  React.Children.toArray(children) as React.ReactElement<TabItemProps>[],
39
40
  );
40
41
 
42
+ const initialTab = propInitialTab ?? childrenArray[0]?.props.label ?? '';
43
+ const { activeTab, setActiveTab } = useActiveTab({ tabsId: id, initialTab });
44
+
41
45
  useEffect(() => {
42
46
  setChildrenArray(React.Children.toArray(children) as React.ReactElement<TabItemProps>[]);
43
47
  }, [children]);
44
- const tabsContainerRef = useRef<HTMLUListElement>(null);
45
- const [isAnimating, setIsAnimating] = useState<boolean>(false);
46
- const defaultInitialTab = childrenArray[0]?.props.label ?? '';
47
- const initialTab = propInitialTab ?? defaultInitialTab;
48
- const {
49
- activeTab,
50
- setTabRef,
51
- onTabClick,
52
- handleKeyboard,
53
- getTabId,
54
- visibleTabs,
55
- overflowTabs,
56
- ready,
57
- allTabsHidden,
58
- } = useTabs({
59
- initialTab,
60
- totalTabs: childrenArray.length,
61
- containerRef: tabsContainerRef,
62
- });
63
-
64
- const [prevTab, setPrevTab] = React.useState(initialTab);
65
- const [highlightStyle, setHighlightStyle] = React.useState<{ left: number; width: number }>({
66
- left: 0,
67
- width: 0,
68
- });
69
-
70
- useEffect(() => {
71
- setPrevTab(activeTab);
72
- setIsAnimating(true);
73
-
74
- const activeIndex = childrenArray.findIndex((child) => child.props.label === activeTab);
75
- const container = tabsContainerRef.current;
76
-
77
- if (container) {
78
- container.querySelectorAll('[data-label]').forEach((el) => {
79
- el.classList.remove('active');
80
- });
81
-
82
- container.getBoundingClientRect();
83
-
84
- requestAnimationFrame(() => {
85
- if (activeIndex >= 0) {
86
- let activeTabElement: HTMLElement | null = null;
87
- let startPosition = { left: 0, width: 0 };
88
-
89
- if (visibleTabs.includes(activeIndex)) {
90
- activeTabElement = container.querySelector(
91
- `[data-label="${activeTab}"]`,
92
- ) as HTMLElement;
93
- } else if (overflowTabs.includes(activeIndex)) {
94
- const moreButton = container.querySelector('button') as HTMLElement;
95
- if (moreButton) {
96
- const moreButtonRect = moreButton.getBoundingClientRect();
97
- const containerRect = container.getBoundingClientRect();
98
- startPosition = {
99
- left: moreButtonRect.left - containerRect.left,
100
- width: moreButtonRect.width,
101
- };
102
- }
103
- }
104
-
105
- if (activeTabElement) {
106
- const { offsetLeft, offsetWidth } = activeTabElement;
107
-
108
- if (overflowTabs.includes(activeIndex)) {
109
- setHighlightStyle(startPosition);
110
- requestAnimationFrame(() => {
111
- setHighlightStyle({ left: offsetLeft, width: offsetWidth });
112
- });
113
- } else {
114
- setHighlightStyle({ left: offsetLeft, width: offsetWidth });
115
- }
116
-
117
- if (visibleTabs.includes(activeIndex)) {
118
- activeTabElement?.classList.add('active');
119
- }
120
-
121
- return () => {
122
- container.querySelectorAll('[data-label]').forEach((el) => {
123
- el.classList.remove('active');
124
- });
125
- };
126
- }
127
- }
128
- setHighlightStyle({ left: 0, width: 0 });
129
- setIsAnimating(false);
130
- });
131
- } else {
132
- setHighlightStyle({ left: 0, width: 0 });
133
- setIsAnimating(false);
134
- }
135
- }, [activeTab, prevTab, childrenArray, visibleTabs, overflowTabs]);
136
48
 
137
49
  return (
138
- <TabsContainer
139
- data-component-name="Markdoc/Tabs/Tabs"
140
- className={className}
141
- isReady={ready || forceReady}
142
- >
50
+ <TabsContainer data-component-name="Markdoc/Tabs/Tabs" className={className} key={id}>
143
51
  <TabList
144
52
  size={size}
145
53
  childrenArray={childrenArray}
146
- overflowTabs={overflowTabs}
147
- setTabRef={setTabRef}
148
- onTabClick={onTabClick}
149
- handleKeyboard={handleKeyboard}
150
- getTabId={getTabId}
151
54
  activeTab={activeTab}
152
- isAnimating={isAnimating}
153
- highlightStyle={highlightStyle}
154
- visibleTabs={visibleTabs}
155
- allTabsHidden={allTabsHidden}
156
- tabsContainerRef={tabsContainerRef}
55
+ onTabChange={setActiveTab}
157
56
  />
158
57
  {childrenArray.map((child, index) => {
159
58
  const { label } = child.props;
@@ -173,8 +72,7 @@ export function Tabs({
173
72
  </TabsContainer>
174
73
  );
175
74
  }
176
-
177
- const TabsContainer = styled.div<{ isReady: boolean }>`
75
+ const TabsContainer = styled.div`
178
76
  position: relative;
179
77
  color: var(--md-tabs-container-text-color);
180
78
  font-size: var(--md-tabs-container-font-size);
@@ -186,17 +84,6 @@ const TabsContainer = styled.div<{ isReady: boolean }>`
186
84
  padding: var(--md-tabs-container-padding);
187
85
  border: var(--md-tabs-container-border);
188
86
 
189
- ${({ isReady }) =>
190
- !isReady
191
- ? css`
192
- visibility: hidden;
193
- overflow: hidden;
194
- `
195
- : css`
196
- visibility: visible;
197
- overflow: visible;
198
- `}
199
-
200
87
  ol[class^='Tabs__TabList'] {
201
88
  margin: 0;
202
89
  padding: 0;
@@ -22,6 +22,11 @@ export const tabs: MarkdocSchemaWrapper = {
22
22
  return new markdoc.Tag('Tabs', attributes, tabsContent);
23
23
  },
24
24
  attributes: {
25
+ /*
26
+ A unique persistent identifier assigned to a component.
27
+ This value is used as a key for the query parameter that stores the active tab to enable deep linking.
28
+ */
29
+ id: { type: String },
25
30
  size: { type: String, matches: ['small', 'medium'], default: 'medium' },
26
31
  },
27
32
  },