@redocly/theme 0.59.0-next.0 → 0.59.0-next.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/lib/components/Accordion/Accordion.d.ts +12 -0
- package/lib/components/Accordion/Accordion.js +85 -0
- package/lib/components/Accordion/AccordionBody.d.ts +8 -0
- package/lib/components/Accordion/AccordionBody.js +73 -0
- package/lib/components/Accordion/AccordionHeader.d.ts +10 -0
- package/lib/components/Accordion/AccordionHeader.js +37 -0
- package/lib/components/Accordion/AccordionTitle.d.ts +6 -0
- package/lib/components/Accordion/AccordionTitle.js +20 -0
- package/lib/components/Accordion/variables.d.ts +1 -0
- package/lib/components/Accordion/variables.js +59 -0
- package/lib/components/Admonition/Admonition.js +17 -7
- package/lib/components/Badge/Badge.js +17 -7
- package/lib/components/Breadcrumbs/Breadcrumb.js +17 -7
- package/lib/components/Breadcrumbs/BreadcrumbDropdown.js +17 -7
- package/lib/components/Button/Button.js +17 -7
- package/lib/components/Buttons/AIAssistantButton.d.ts +2 -0
- package/lib/components/Buttons/AIAssistantButton.js +135 -0
- package/lib/components/Buttons/CopyButton.js +17 -7
- package/lib/components/Buttons/variables.d.ts +1 -0
- package/lib/components/Buttons/variables.dark.d.ts +1 -0
- package/lib/components/Buttons/variables.dark.js +10 -0
- package/lib/components/Buttons/variables.js +51 -0
- package/lib/components/Catalog/Catalog.d.ts +6 -0
- package/lib/components/Catalog/Catalog.js +9 -8
- package/lib/components/Catalog/CatalogEntities.js +17 -7
- package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsGraph.js +17 -7
- package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsGraph.lazy.js +17 -7
- package/lib/components/Catalog/CatalogEntity/CatalogEntityMetadata.js +17 -7
- package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityApiDescriptionRelations.js +1 -1
- package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityRelations.js +17 -7
- package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityTeamRelations.js +1 -1
- package/lib/components/Catalog/CatalogEntity/CatalogEntitySchema.js +17 -7
- package/lib/components/Catalog/CatalogFilter/CatalogFilter.d.ts +6 -0
- package/lib/components/Catalog/CatalogFilter/CatalogFilter.js +35 -0
- package/lib/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.d.ts +6 -0
- package/lib/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.js +152 -0
- package/lib/components/Catalog/CatalogFilter/CatalogFilterContent.d.ts +13 -0
- package/lib/components/Catalog/CatalogFilter/CatalogFilterContent.js +102 -0
- package/lib/components/Catalog/CatalogFilter/CatalogFilterDateRange.d.ts +6 -0
- package/lib/components/Catalog/CatalogFilter/CatalogFilterDateRange.js +121 -0
- package/lib/components/Catalog/CatalogFilter/CatalogFilterSelect.d.ts +6 -0
- package/lib/components/Catalog/CatalogFilter/CatalogFilterSelect.js +126 -0
- package/lib/components/Catalog/CatalogSelector.js +0 -1
- package/lib/components/Catalog/CatalogSortButton.js +17 -7
- package/lib/components/Catalog/CatalogTableView/CatalogTableHeaderCell.js +17 -7
- package/lib/components/Catalog/CatalogViewModeToggle.js +17 -7
- package/lib/components/Catalog/variables.js +0 -1
- package/lib/components/CatalogClassic/CatalogClassicActions.js +17 -7
- package/lib/components/CatalogClassic/CatalogClassicCard.js +17 -7
- package/lib/components/CatalogClassic/CatalogClassicHighlight.js +17 -7
- package/lib/components/CatalogClassic/CatalogClassicVirtualizedGroups.js +17 -7
- package/lib/components/CodeBlock/CodeBlock.js +17 -7
- package/lib/components/CodeBlock/CodeBlockContainer.js +17 -7
- package/lib/components/CodeBlock/CodeBlockTabs.js +17 -7
- package/lib/components/Dropdown/Dropdown.d.ts +16 -2
- package/lib/components/Dropdown/Dropdown.js +22 -12
- package/lib/components/Dropdown/DropdownMenuItem.js +17 -7
- package/lib/components/Feedback/Comment.js +17 -7
- package/lib/components/Feedback/Feedback.js +17 -7
- package/lib/components/Feedback/Mood.js +17 -7
- package/lib/components/Feedback/Rating.js +17 -7
- package/lib/components/Feedback/Reasons.js +17 -7
- package/lib/components/Feedback/Scale.js +17 -7
- package/lib/components/Feedback/Sentiment.js +17 -7
- package/lib/components/Feedback/Stars.js +17 -7
- package/lib/components/Filter/FilterContent.js +17 -7
- package/lib/components/Filter/FilterInput.d.ts +1 -0
- package/lib/components/Filter/FilterInput.js +19 -9
- package/lib/components/Filter/FilterOptions.js +2 -0
- package/lib/components/Filter/variables.js +7 -4
- package/lib/components/Image/Image.js +17 -7
- package/lib/components/JsonViewer/JsonViewer.js +17 -7
- package/lib/components/JsonViewer/helpers.js +17 -7
- package/lib/components/LastUpdated/LastUpdated.js +17 -7
- package/lib/components/Link/Link.js +17 -7
- package/lib/components/Markdown/Markdown.js +17 -7
- package/lib/components/Marker/Marker.js +17 -7
- package/lib/components/Menu/MenuContainer.js +17 -7
- package/lib/components/Menu/MenuItem.js +18 -8
- package/lib/components/Menu/MenuMobile.js +17 -7
- package/lib/components/PageActions/PageActions.js +17 -7
- package/lib/components/PageNavigation/NextButton.js +17 -7
- package/lib/components/Panel/Panel.js +17 -7
- package/lib/components/Panel/PanelBody.js +17 -7
- package/lib/components/Search/FilterFields/SearchFilterFieldSelect.js +17 -7
- package/lib/components/Search/SearchAiConversationInput.d.ts +2 -1
- package/lib/components/Search/SearchAiConversationInput.js +28 -10
- package/lib/components/Search/SearchAiDialog.js +19 -10
- package/lib/components/Search/SearchAiResponse.js +2 -3
- package/lib/components/Search/SearchDialog.d.ts +2 -1
- package/lib/components/Search/SearchDialog.js +25 -12
- package/lib/components/Search/SearchFilter.js +17 -7
- package/lib/components/Search/SearchGroups.js +17 -7
- package/lib/components/Search/SearchHighlight.js +17 -7
- package/lib/components/Search/SearchItem.js +17 -7
- package/lib/components/Search/SearchRecent.js +17 -7
- package/lib/components/Search/SearchShortcut.js +17 -7
- package/lib/components/Search/SearchSuggestedPages.js +17 -7
- package/lib/components/Search/SearchTrigger.js +17 -7
- package/lib/components/Search/variables.js +5 -1
- package/lib/components/Segmented/Segmented.js +17 -7
- package/lib/components/Select/Select.js +17 -7
- package/lib/components/Select/SelectInput.js +17 -7
- package/lib/components/Sidebar/Sidebar.js +17 -7
- package/lib/components/SidebarActions/styled.js +17 -7
- package/lib/components/SkipContent/SkipContent.js +17 -7
- package/lib/components/Switch/Switch.js +17 -7
- package/lib/components/TableOfContent/TableOfContent.js +17 -7
- package/lib/components/Tag/variables.dark.js +2 -2
- package/lib/components/Tooltip/Tooltip.js +17 -7
- package/lib/components/VersionPicker/VersionPicker.js +17 -7
- package/lib/core/constants/search.d.ts +5 -4
- package/lib/core/constants/search.js +4 -5
- package/lib/core/contexts/CodeSnippetContext.js +17 -7
- package/lib/core/hooks/use-tabs.d.ts +3 -2
- package/lib/core/hooks/use-tabs.js +115 -57
- package/lib/core/styles/dark.js +29 -26
- package/lib/core/styles/global.js +64 -59
- package/lib/core/templates/Markdown.js +17 -7
- package/lib/core/types/hooks.d.ts +6 -3
- package/lib/core/types/l10n.d.ts +1 -1
- package/lib/core/utils/download-code-walkthrough.js +17 -7
- package/lib/core/utils/get-file-icon.js +17 -7
- package/lib/icons/AiStarsIcon/AiStarsIcon.js +11 -2
- package/lib/icons/GenericIcon/GenericIcon.js +17 -7
- package/lib/icons/RedoclyIcon/RedoclyIcon.d.ts +9 -0
- package/lib/icons/RedoclyIcon/RedoclyIcon.js +24 -0
- package/lib/icons/Spinner/Spinner.js +17 -7
- package/lib/index.d.ts +2 -0
- package/lib/index.js +19 -7
- package/lib/layouts/OIDCForbidden.js +17 -7
- package/lib/layouts/RootLayout.js +6 -1
- package/lib/layouts/ThreePanelLayout.js +17 -7
- package/lib/markdoc/components/Cards/Cards.js +17 -7
- package/lib/markdoc/components/CodeGroup/CodeGroup.js +17 -7
- package/lib/markdoc/components/CodeWalkthrough/CodeContainer.js +17 -7
- package/lib/markdoc/components/CodeWalkthrough/CodePanel.js +17 -7
- package/lib/markdoc/components/CodeWalkthrough/CodePanelHeader.js +17 -7
- package/lib/markdoc/components/CodeWalkthrough/CodePanelPreview.js +17 -7
- package/lib/markdoc/components/CodeWalkthrough/CodePanelToolbar.js +17 -7
- package/lib/markdoc/components/CodeWalkthrough/CodeStep.js +17 -7
- package/lib/markdoc/components/CodeWalkthrough/CodeToggle.js +17 -7
- package/lib/markdoc/components/CodeWalkthrough/CodeWalkthrough.js +17 -7
- package/lib/markdoc/components/CodeWalkthrough/Input.js +17 -7
- package/lib/markdoc/components/Heading/Heading.js +17 -7
- package/lib/markdoc/components/HtmlBlock/HtmlBlock.js +17 -7
- package/lib/markdoc/components/InlineSvg/InlineSvg.js +17 -7
- package/lib/markdoc/components/MarkdocExample/MarkdocExample.js +17 -7
- package/lib/markdoc/components/Tabs/TabList.d.ts +3 -1
- package/lib/markdoc/components/Tabs/TabList.js +214 -54
- package/lib/markdoc/components/Tabs/Tabs.d.ts +2 -1
- package/lib/markdoc/components/Tabs/Tabs.js +74 -19
- package/lib/markdoc/default.d.ts +104 -1
- package/lib/markdoc/default.js +17 -7
- package/package.json +5 -5
- package/src/components/Accordion/Accordion.tsx +100 -0
- package/src/components/Accordion/AccordionBody.tsx +65 -0
- package/src/components/Accordion/AccordionHeader.tsx +68 -0
- package/src/components/Accordion/AccordionTitle.tsx +26 -0
- package/src/components/Accordion/variables.ts +56 -0
- package/src/components/Buttons/AIAssistantButton.tsx +141 -0
- package/src/components/Buttons/variables.dark.ts +7 -0
- package/src/components/Buttons/variables.ts +48 -0
- package/src/components/Catalog/Catalog.tsx +18 -6
- package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityApiDescriptionRelations.tsx +1 -1
- package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityTeamRelations.tsx +1 -1
- package/src/components/Catalog/CatalogFilter/CatalogFilter.tsx +56 -0
- package/src/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.tsx +169 -0
- package/src/components/Catalog/CatalogFilter/CatalogFilterContent.tsx +121 -0
- package/src/components/Catalog/CatalogFilter/CatalogFilterDateRange.tsx +147 -0
- package/src/components/Catalog/CatalogFilter/CatalogFilterSelect.tsx +136 -0
- package/src/components/Catalog/CatalogSelector.tsx +0 -1
- package/src/components/Catalog/variables.ts +0 -1
- package/src/components/Dropdown/Dropdown.tsx +84 -79
- package/src/components/Filter/FilterInput.tsx +3 -2
- package/src/components/Filter/FilterOptions.tsx +2 -0
- package/src/components/Filter/variables.ts +7 -4
- package/src/components/Menu/MenuItem.tsx +1 -0
- package/src/components/Search/SearchAiConversationInput.tsx +12 -2
- package/src/components/Search/SearchAiDialog.tsx +2 -2
- package/src/components/Search/SearchAiResponse.tsx +2 -2
- package/src/components/Search/SearchDialog.tsx +13 -5
- package/src/components/Search/variables.ts +5 -1
- package/src/components/Tag/variables.dark.ts +2 -2
- package/src/core/constants/search.ts +8 -4
- package/src/core/hooks/use-tabs.ts +168 -86
- package/src/core/styles/dark.ts +11 -8
- package/src/core/styles/global.ts +7 -2
- package/src/core/types/hooks.ts +6 -1
- package/src/core/types/l10n.ts +2 -0
- package/src/icons/AiStarsIcon/AiStarsIcon.tsx +11 -2
- package/src/icons/RedoclyIcon/RedoclyIcon.tsx +26 -0
- package/src/index.ts +2 -0
- package/src/layouts/RootLayout.tsx +6 -0
- package/src/markdoc/components/Tabs/TabList.tsx +312 -105
- package/src/markdoc/components/Tabs/Tabs.tsx +136 -11
|
@@ -13,39 +13,75 @@ type Tabs = {
|
|
|
13
13
|
overflow: number[];
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
+
type UseTabsReturn = {
|
|
17
|
+
setTabRef: (element: HTMLButtonElement | null, index: number) => void;
|
|
18
|
+
onTabClick: (labelOrIndex: string | number) => void;
|
|
19
|
+
handleKeyboard: (event: React.KeyboardEvent, index: number) => void;
|
|
20
|
+
visibleTabs: number[];
|
|
21
|
+
overflowTabs: number[];
|
|
22
|
+
isReady: boolean;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
type UseActiveTabProps = {
|
|
26
|
+
initialTab: string;
|
|
27
|
+
tabsId?: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
16
30
|
const MORE_BUTTON_WIDTH = 80;
|
|
17
31
|
const TABS_GAP = 8;
|
|
18
32
|
|
|
19
|
-
export function useTabs({
|
|
33
|
+
export function useTabs({
|
|
34
|
+
activeTab,
|
|
35
|
+
onTabChange,
|
|
36
|
+
totalTabs,
|
|
37
|
+
containerRef,
|
|
38
|
+
}: UseTabsProps): UseTabsReturn {
|
|
20
39
|
const [tabs, setTabs] = useState<Tabs>({
|
|
21
40
|
visible: Array.from({ length: totalTabs }, (_, i) => i),
|
|
22
41
|
overflow: [],
|
|
23
42
|
});
|
|
24
|
-
|
|
43
|
+
const [isReady, setIsReady] = useState(false);
|
|
44
|
+
const isFirstCalculation = useRef(true);
|
|
25
45
|
const tabRefs = useRef<(HTMLButtonElement | null)[]>([]);
|
|
26
46
|
const tabWidthsRef = useRef<number[]>([]);
|
|
27
47
|
const tabLabelsRef = useRef<string[]>([]);
|
|
48
|
+
const activeTabRef = useRef(activeTab);
|
|
49
|
+
const calculateVisibleTabsRef = useRef<(() => void) | null>(null);
|
|
28
50
|
|
|
29
|
-
|
|
51
|
+
// Synchronously update ref before any callbacks or effects run
|
|
52
|
+
activeTabRef.current = activeTab;
|
|
30
53
|
|
|
31
|
-
const setTabRef = useCallback(
|
|
32
|
-
|
|
54
|
+
const setTabRef = useCallback(
|
|
55
|
+
(element: HTMLButtonElement | null, index: number) => {
|
|
56
|
+
tabRefs.current[index] = element;
|
|
33
57
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
58
|
+
const width = element?.offsetWidth;
|
|
59
|
+
if (width) {
|
|
60
|
+
tabWidthsRef.current[index] = width;
|
|
61
|
+
}
|
|
38
62
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}, []);
|
|
63
|
+
const label = element?.getAttribute('data-label');
|
|
64
|
+
if (label) {
|
|
65
|
+
tabLabelsRef.current[index] = label;
|
|
66
|
+
}
|
|
44
67
|
|
|
45
|
-
|
|
68
|
+
// Trigger calculation once all tabs are registered
|
|
69
|
+
if (
|
|
70
|
+
isFirstCalculation.current &&
|
|
71
|
+
tabWidthsRef.current.length >= totalTabs &&
|
|
72
|
+
tabLabelsRef.current.length >= totalTabs &&
|
|
73
|
+
calculateVisibleTabsRef.current
|
|
74
|
+
) {
|
|
75
|
+
requestAnimationFrame(calculateVisibleTabsRef.current);
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
[totalTabs],
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const focusTab = useCallback((index: number) => {
|
|
46
82
|
const currentElement = tabRefs.current[index];
|
|
47
83
|
currentElement?.focus();
|
|
48
|
-
};
|
|
84
|
+
}, []);
|
|
49
85
|
|
|
50
86
|
const onTabSelect = useCallback(
|
|
51
87
|
(index: number) => {
|
|
@@ -53,7 +89,7 @@ export function useTabs({ activeTab, onTabChange, totalTabs, containerRef }: Use
|
|
|
53
89
|
const label = tabRefs.current[index]?.getAttribute('data-label');
|
|
54
90
|
if (label) onTabChange(label);
|
|
55
91
|
},
|
|
56
|
-
[onTabChange],
|
|
92
|
+
[onTabChange, focusTab],
|
|
57
93
|
);
|
|
58
94
|
|
|
59
95
|
const handleKeyboard = useCallback(
|
|
@@ -77,30 +113,19 @@ export function useTabs({ activeTab, onTabChange, totalTabs, containerRef }: Use
|
|
|
77
113
|
[totalTabs, onTabSelect],
|
|
78
114
|
);
|
|
79
115
|
|
|
80
|
-
const replaceLastVisibleTabWithClickedOverflowTab = useCallback(
|
|
81
|
-
(
|
|
82
|
-
const { visible: visibleTabs, overflow: overflowTabs } =
|
|
83
|
-
|
|
84
|
-
// Indexes of visible tabs should be sorted(asc), to replace the last visible tab with the clicked tab
|
|
85
|
-
const newVisibleTabs = [...visibleTabs].sort((a, b) => a - b);
|
|
86
|
-
const newOverflowTabs = [...overflowTabs];
|
|
87
|
-
|
|
88
|
-
const clickedIdxInOverflow = newOverflowTabs.indexOf(clickedIndex);
|
|
89
|
-
if (clickedIdxInOverflow !== -1) {
|
|
90
|
-
const lastVisible = newVisibleTabs[newVisibleTabs.length - 1];
|
|
91
|
-
newOverflowTabs.splice(clickedIdxInOverflow, 1);
|
|
92
|
-
newOverflowTabs.unshift(lastVisible);
|
|
93
|
-
newVisibleTabs.splice(newVisibleTabs.length - 1, 1);
|
|
94
|
-
newVisibleTabs.unshift(clickedIndex);
|
|
95
|
-
}
|
|
116
|
+
const replaceLastVisibleTabWithClickedOverflowTab = useCallback((clickedIndex: number) => {
|
|
117
|
+
setTabs((prevTabs) => {
|
|
118
|
+
const { visible: visibleTabs, overflow: overflowTabs } = prevTabs;
|
|
96
119
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
120
|
+
const sortedVisible = [...visibleTabs].sort((a, b) => a - b);
|
|
121
|
+
const lastVisible = sortedVisible[sortedVisible.length - 1];
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
visible: visibleTabs.map((idx) => (idx === lastVisible ? clickedIndex : idx)),
|
|
125
|
+
overflow: overflowTabs.map((idx) => (idx === clickedIndex ? lastVisible : idx)),
|
|
126
|
+
};
|
|
127
|
+
});
|
|
128
|
+
}, []);
|
|
104
129
|
|
|
105
130
|
const onTabClick = useCallback(
|
|
106
131
|
(labelOrIndex: string | number) => {
|
|
@@ -111,18 +136,18 @@ export function useTabs({ activeTab, onTabChange, totalTabs, containerRef }: Use
|
|
|
111
136
|
|
|
112
137
|
if (clickedIndex === -1) return;
|
|
113
138
|
|
|
114
|
-
const
|
|
115
|
-
if (
|
|
139
|
+
const label = tabLabelsRef.current[clickedIndex];
|
|
140
|
+
if (!label) return;
|
|
141
|
+
|
|
142
|
+
// If this is an overflow tab, replace it with a visible one
|
|
143
|
+
if (tabs.overflow.includes(clickedIndex)) {
|
|
116
144
|
replaceLastVisibleTabWithClickedOverflowTab(clickedIndex);
|
|
117
145
|
}
|
|
118
146
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
onTabChange(label);
|
|
122
|
-
focusTab(clickedIndex);
|
|
123
|
-
}
|
|
147
|
+
onTabChange(label);
|
|
148
|
+
focusTab(clickedIndex);
|
|
124
149
|
},
|
|
125
|
-
[
|
|
150
|
+
[tabs.overflow, onTabChange, replaceLastVisibleTabWithClickedOverflowTab, focusTab],
|
|
126
151
|
);
|
|
127
152
|
|
|
128
153
|
const calculateVisibleTabs = useCallback(() => {
|
|
@@ -131,87 +156,141 @@ export function useTabs({ activeTab, onTabChange, totalTabs, containerRef }: Use
|
|
|
131
156
|
|
|
132
157
|
const containerWidth = container.offsetWidth;
|
|
133
158
|
const tabWidths = tabWidthsRef.current;
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
159
|
+
const tabLabels = tabLabelsRef.current;
|
|
160
|
+
|
|
161
|
+
// Wait until all tabs are registered before calculating
|
|
162
|
+
if (tabWidths.length < totalTabs || tabLabels.length < totalTabs) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Check if container has proper width (not zero)
|
|
167
|
+
if (containerWidth === 0) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Find active tab index by label in tabLabelsRef, not by DOM element
|
|
172
|
+
// because tab might not be rendered if it's in overflow
|
|
173
|
+
const activeTabIndex = tabLabels.findIndex((label) => label === activeTabRef.current);
|
|
137
174
|
|
|
138
|
-
// Active tab should always be visible, so we include it at the beginning of the array
|
|
139
175
|
let tabsWidth = activeTabIndex !== -1 ? tabWidths[activeTabIndex] : 0;
|
|
140
|
-
const
|
|
141
|
-
const
|
|
176
|
+
const visibleTabs = activeTabIndex !== -1 ? [activeTabIndex] : [];
|
|
177
|
+
const overflowTabs = [];
|
|
142
178
|
|
|
143
179
|
for (let i = 0; i < tabWidths.length; i++) {
|
|
144
|
-
|
|
145
|
-
if (i === activeTabIndex) {
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
180
|
+
if (i === activeTabIndex) continue;
|
|
148
181
|
|
|
149
182
|
const tabWidthWithGap = tabWidths[i] + TABS_GAP;
|
|
150
183
|
const projectedWidth = tabsWidth + tabWidthWithGap;
|
|
151
184
|
|
|
152
185
|
if (projectedWidth <= containerWidth) {
|
|
153
|
-
|
|
186
|
+
visibleTabs.push(i);
|
|
154
187
|
tabsWidth += tabWidthWithGap;
|
|
155
188
|
} else {
|
|
156
|
-
|
|
189
|
+
overflowTabs.push(i);
|
|
157
190
|
}
|
|
158
191
|
}
|
|
159
192
|
|
|
160
|
-
if (
|
|
193
|
+
if (overflowTabs.length > 0) {
|
|
161
194
|
tabsWidth += MORE_BUTTON_WIDTH;
|
|
162
195
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
if (removed !== undefined) {
|
|
167
|
-
|
|
196
|
+
while (tabsWidth > containerWidth && visibleTabs.length > 1) {
|
|
197
|
+
const removed = visibleTabs.pop();
|
|
198
|
+
// Never remove the active tab - it should always stay visible or be the last one
|
|
199
|
+
if (removed !== undefined && removed !== activeTabIndex) {
|
|
200
|
+
overflowTabs.unshift(removed);
|
|
168
201
|
tabsWidth -= tabWidths[removed];
|
|
202
|
+
} else if (removed === activeTabIndex) {
|
|
203
|
+
// Put it back if we accidentally removed the active tab
|
|
204
|
+
visibleTabs.push(removed);
|
|
205
|
+
break;
|
|
169
206
|
}
|
|
170
207
|
}
|
|
208
|
+
|
|
209
|
+
// If even with only the active tab visible, it doesn't fit with More button,
|
|
210
|
+
// move all tabs to overflow (show only dropdown)
|
|
211
|
+
if (tabsWidth > containerWidth && visibleTabs.length === 1) {
|
|
212
|
+
overflowTabs.unshift(...visibleTabs);
|
|
213
|
+
visibleTabs.length = 0;
|
|
214
|
+
}
|
|
171
215
|
}
|
|
172
216
|
|
|
173
217
|
setTabs({
|
|
174
|
-
visible,
|
|
175
|
-
overflow,
|
|
218
|
+
visible: visibleTabs,
|
|
219
|
+
overflow: overflowTabs,
|
|
176
220
|
});
|
|
177
|
-
}, [containerRef, activeTab]);
|
|
178
221
|
|
|
222
|
+
// Set ready state on first calculation
|
|
223
|
+
if (isFirstCalculation.current) {
|
|
224
|
+
isFirstCalculation.current = false;
|
|
225
|
+
setIsReady(true);
|
|
226
|
+
}
|
|
227
|
+
}, [containerRef, totalTabs]);
|
|
228
|
+
|
|
229
|
+
// Store calculateVisibleTabs in ref for use in setTabRef
|
|
230
|
+
calculateVisibleTabsRef.current = calculateVisibleTabs;
|
|
231
|
+
|
|
232
|
+
// Reset isFirstCalculation when totalTabs changes (new page/tabs)
|
|
233
|
+
useEffect(() => {
|
|
234
|
+
isFirstCalculation.current = true;
|
|
235
|
+
setIsReady(false);
|
|
236
|
+
// Clear refs so we wait for new tabs to register
|
|
237
|
+
tabWidthsRef.current = [];
|
|
238
|
+
tabLabelsRef.current = [];
|
|
239
|
+
}, [totalTabs]);
|
|
240
|
+
|
|
241
|
+
// Call calculateVisibleTabs on first render and resize
|
|
179
242
|
useEffect(() => {
|
|
180
|
-
|
|
243
|
+
const container = containerRef?.current;
|
|
244
|
+
if (!container) return;
|
|
245
|
+
|
|
246
|
+
let resizeTimeout: number | null = null;
|
|
247
|
+
|
|
248
|
+
// Use ResizeObserver to wait until container has proper size
|
|
249
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
250
|
+
if (resizeTimeout) cancelAnimationFrame(resizeTimeout);
|
|
251
|
+
resizeTimeout = requestAnimationFrame(calculateVisibleTabs);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
resizeObserver.observe(container);
|
|
181
255
|
|
|
182
|
-
let resizeTimeout = requestAnimationFrame(calculateVisibleTabs);
|
|
183
256
|
const handleResize = () => {
|
|
184
|
-
if (resizeTimeout)
|
|
185
|
-
cancelAnimationFrame(resizeTimeout);
|
|
186
|
-
}
|
|
257
|
+
if (resizeTimeout) cancelAnimationFrame(resizeTimeout);
|
|
187
258
|
resizeTimeout = requestAnimationFrame(calculateVisibleTabs);
|
|
188
259
|
};
|
|
189
260
|
|
|
190
261
|
window.addEventListener('resize', handleResize);
|
|
262
|
+
|
|
191
263
|
return () => {
|
|
264
|
+
resizeObserver.disconnect();
|
|
192
265
|
window.removeEventListener('resize', handleResize);
|
|
193
|
-
cancelAnimationFrame(resizeTimeout);
|
|
266
|
+
if (resizeTimeout) cancelAnimationFrame(resizeTimeout);
|
|
194
267
|
};
|
|
195
268
|
}, [containerRef, totalTabs, calculateVisibleTabs]);
|
|
196
269
|
|
|
270
|
+
// Recalculate when activeTab changes to ensure it's visible
|
|
271
|
+
useEffect(() => {
|
|
272
|
+
if (!containerRef?.current || isFirstCalculation.current) return;
|
|
273
|
+
requestAnimationFrame(calculateVisibleTabs);
|
|
274
|
+
}, [activeTab, containerRef, calculateVisibleTabs]);
|
|
275
|
+
|
|
197
276
|
return {
|
|
198
277
|
setTabRef,
|
|
199
278
|
onTabClick,
|
|
200
279
|
handleKeyboard,
|
|
201
280
|
visibleTabs: tabs.visible,
|
|
202
281
|
overflowTabs: tabs.overflow,
|
|
203
|
-
|
|
282
|
+
isReady,
|
|
204
283
|
};
|
|
205
284
|
}
|
|
206
285
|
|
|
207
|
-
type UseActiveTabProps = {
|
|
208
|
-
initialTab: string;
|
|
209
|
-
tabsId?: string;
|
|
210
|
-
};
|
|
211
|
-
|
|
212
286
|
export const useActiveTab = ({ initialTab, tabsId }: UseActiveTabProps) => {
|
|
213
287
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
214
|
-
const
|
|
288
|
+
const initialTabValue = useMemo(
|
|
289
|
+
() => getInitialTab({ initialTab, searchParams, tabsId }),
|
|
290
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
291
|
+
[],
|
|
292
|
+
);
|
|
293
|
+
const [activeTab, setActiveTab] = useState(initialTabValue);
|
|
215
294
|
const prevActiveTabRef = useRef(activeTab);
|
|
216
295
|
|
|
217
296
|
useEffect(() => {
|
|
@@ -228,10 +307,13 @@ export const useActiveTab = ({ initialTab, tabsId }: UseActiveTabProps) => {
|
|
|
228
307
|
});
|
|
229
308
|
}, [activeTab, setSearchParams, tabsId]);
|
|
230
309
|
|
|
231
|
-
return
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
310
|
+
return useMemo(
|
|
311
|
+
() => ({
|
|
312
|
+
activeTab,
|
|
313
|
+
setActiveTab,
|
|
314
|
+
}),
|
|
315
|
+
[activeTab],
|
|
316
|
+
);
|
|
235
317
|
};
|
|
236
318
|
|
|
237
319
|
type GetInitialTabProps = {
|
package/src/core/styles/dark.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { scorecardDarkMode } from '@redocly/theme/components/Scorecard/variables
|
|
|
4
4
|
import { mermaidDarkMode } from '@redocly/theme/markdoc/components/Mermaid/variables.dark'
|
|
5
5
|
import { menuDarkMode } from '@redocly/theme/components/Menu/variables.dark';
|
|
6
6
|
import { buttonDarkMode } from '@redocly/theme/components/Button/variables.dark';
|
|
7
|
+
import { aiAssistantButtonDarkMode } from '@redocly/theme/components/Buttons/variables.dark';
|
|
7
8
|
import { segmentedButtonsDarkMode } from '@redocly/theme/components/Segmented/variables.dark';
|
|
8
9
|
import { checkboxDarkMode } from '@redocly/theme/icons/CheckboxIcon/variables.dark';
|
|
9
10
|
import { tagDarkMode } from '@redocly/theme/components/Tag/variables.dark';
|
|
@@ -137,14 +138,15 @@ export const darkMode = css`
|
|
|
137
138
|
|
|
138
139
|
--color-green-1: #1a4d40;
|
|
139
140
|
--color-green-2: #195848;
|
|
140
|
-
--color-green-3: #
|
|
141
|
-
--color-green-4: #
|
|
142
|
-
--color-green-5: #
|
|
143
|
-
--color-green-6: #
|
|
144
|
-
--color-green-7: #
|
|
145
|
-
--color-green-8: #
|
|
146
|
-
--color-green-9: #
|
|
147
|
-
--color-green-10: #
|
|
141
|
+
--color-green-3: #136a4d;
|
|
142
|
+
--color-green-4: #0e8450;
|
|
143
|
+
--color-green-5: #149e53;
|
|
144
|
+
--color-green-6: #1cb854;
|
|
145
|
+
--color-green-7: #4dd470;
|
|
146
|
+
--color-green-8: #72e985;
|
|
147
|
+
--color-green-9: #a3f7a9;
|
|
148
|
+
--color-green-10: #d2fbd0;
|
|
149
|
+
--color-green-11: #edfbec;
|
|
148
150
|
|
|
149
151
|
--color-grass-1: #1f4d2d;
|
|
150
152
|
--color-grass-2: #164f29;
|
|
@@ -309,6 +311,7 @@ export const darkMode = css`
|
|
|
309
311
|
|
|
310
312
|
${segmentedButtonsDarkMode}
|
|
311
313
|
${buttonDarkMode}
|
|
314
|
+
${aiAssistantButtonDarkMode}
|
|
312
315
|
${checkboxDarkMode}
|
|
313
316
|
${tagDarkMode}
|
|
314
317
|
${statusCodeDarkMode}
|
|
@@ -11,6 +11,7 @@ import { catalog } from '@redocly/theme/components/Catalog/variables';
|
|
|
11
11
|
import { filter } from '@redocly/theme/components/Filter/variables';
|
|
12
12
|
import { catalogClassic } from '@redocly/theme/components/CatalogClassic/variables';
|
|
13
13
|
import { apiReferencePanels, responsePanelColors } from '@redocly/theme/components/Panel/variables';
|
|
14
|
+
import { accordion } from '@redocly/theme/components/Accordion/variables';
|
|
14
15
|
import { select } from '@redocly/theme/components/Select/variables';
|
|
15
16
|
import { dropdown } from '@redocly/theme/components/Dropdown/variables';
|
|
16
17
|
import { tooltip } from '@redocly/theme/components/Tooltip/variables';
|
|
@@ -18,6 +19,7 @@ import { checkbox } from '@redocly/theme/icons/CheckboxIcon/variables';
|
|
|
18
19
|
import { admonition } from '@redocly/theme/components/Admonition/variables';
|
|
19
20
|
import { footer } from '@redocly/theme/components/Footer/variables';
|
|
20
21
|
import { button } from '@redocly/theme/components/Button/variables';
|
|
22
|
+
import { aiAssistantButton } from '@redocly/theme/components/Buttons/variables';
|
|
21
23
|
import { navbar } from '@redocly/theme/components/Navbar/variables';
|
|
22
24
|
import { search } from '@redocly/theme/components/Search/variables';
|
|
23
25
|
import { menu, mobileMenu } from '@redocly/theme/components/Menu/variables';
|
|
@@ -141,8 +143,9 @@ const themeColors = css`
|
|
|
141
143
|
--color-green-6: #1cb854;
|
|
142
144
|
--color-green-7: #149e53;
|
|
143
145
|
--color-green-8: #0e8450;
|
|
144
|
-
--color-green-9: #
|
|
145
|
-
--color-green-10: #
|
|
146
|
+
--color-green-9: #136a4d;
|
|
147
|
+
--color-green-10: #195848;
|
|
148
|
+
--color-green-11: #1a4d40;
|
|
146
149
|
|
|
147
150
|
--color-grass-1: #f0faeb;
|
|
148
151
|
--color-grass-2: #e3fad6;
|
|
@@ -1227,6 +1230,7 @@ const replay = css`
|
|
|
1227
1230
|
|
|
1228
1231
|
export const styles = css`
|
|
1229
1232
|
:root {
|
|
1233
|
+
${accordion}
|
|
1230
1234
|
${admonition}
|
|
1231
1235
|
${apiReferenceDocs}
|
|
1232
1236
|
${apiReferencePanels}
|
|
@@ -1234,6 +1238,7 @@ export const styles = css`
|
|
|
1234
1238
|
${borders}
|
|
1235
1239
|
${breadcrumbs}
|
|
1236
1240
|
${button}
|
|
1241
|
+
${aiAssistantButton}
|
|
1237
1242
|
${cards}
|
|
1238
1243
|
${catalog}
|
|
1239
1244
|
${catalogClassic}
|
package/src/core/types/hooks.ts
CHANGED
|
@@ -86,6 +86,7 @@ export type ThemeHooks = {
|
|
|
86
86
|
useSearch: (
|
|
87
87
|
product?: string,
|
|
88
88
|
autoSearchDisabled?: boolean,
|
|
89
|
+
searchSessionId?: string,
|
|
89
90
|
) => {
|
|
90
91
|
query: string;
|
|
91
92
|
setQuery: React.Dispatch<React.SetStateAction<string>>;
|
|
@@ -107,7 +108,10 @@ export type ThemeHooks = {
|
|
|
107
108
|
advancedSearch?: boolean;
|
|
108
109
|
askAi?: boolean;
|
|
109
110
|
};
|
|
110
|
-
useAiSearch: (
|
|
111
|
+
useAiSearch: (
|
|
112
|
+
options?: { filter?: SearchFilterItem[] },
|
|
113
|
+
searchSessionId?: string,
|
|
114
|
+
) => {
|
|
111
115
|
askQuestion: (question: string, history?: AiSearchConversationItem[]) => void;
|
|
112
116
|
isGeneratingResponse: boolean;
|
|
113
117
|
question: string;
|
|
@@ -142,6 +146,7 @@ export type ThemeHooks = {
|
|
|
142
146
|
| undefined;
|
|
143
147
|
useCatalog: (
|
|
144
148
|
config?: CatalogEntityConfig,
|
|
149
|
+
serverFilters?: Record<string, { value: string; count: number }[]>,
|
|
145
150
|
entitiesCounterInitial?: number,
|
|
146
151
|
initialViewMode?: CatalogViewMode,
|
|
147
152
|
) => UseCatalogResponse;
|
package/src/core/types/l10n.ts
CHANGED
|
@@ -103,6 +103,7 @@ export type TranslationKey =
|
|
|
103
103
|
| 'search.ai.error.header'
|
|
104
104
|
| 'search.ai.error.header.forbidden'
|
|
105
105
|
| 'search.ai.error.header.unauthorized'
|
|
106
|
+
| 'aiAssistant.trigger'
|
|
106
107
|
| 'toc.header'
|
|
107
108
|
| 'footer.copyrightText'
|
|
108
109
|
| 'page.homeButton'
|
|
@@ -145,6 +146,7 @@ export type TranslationKey =
|
|
|
145
146
|
| 'catalog.entity.metadata.title'
|
|
146
147
|
| 'catalog.entity.schema.title'
|
|
147
148
|
| 'catalog.entity.properties.apiDescription.title'
|
|
149
|
+
| 'catalog.backToAllLabel'
|
|
148
150
|
| 'sidebar.menu.backLabel'
|
|
149
151
|
| 'sidebar.menu.backToLabel'
|
|
150
152
|
| 'sidebar.actions.show'
|
|
@@ -35,19 +35,28 @@ export const AiStarsIcon = styled(Icon).attrs(() => ({
|
|
|
35
35
|
height: ${({ size }) => size || '16px'};
|
|
36
36
|
width: ${({ size }) => size || '16px'};
|
|
37
37
|
|
|
38
|
-
${({ background, borderRadius, margin }) =>
|
|
38
|
+
${({ background, borderRadius, margin, padding, size }) =>
|
|
39
39
|
background &&
|
|
40
40
|
`
|
|
41
41
|
display: flex;
|
|
42
42
|
align-items: center;
|
|
43
43
|
justify-content: center;
|
|
44
|
+
flex-shrink: 0;
|
|
44
45
|
|
|
45
46
|
background: ${getCssColorVariable(background)};
|
|
47
|
+
width: ${size || '16px'};
|
|
48
|
+
height: ${size || '16px'};
|
|
46
49
|
|
|
47
|
-
padding: var(--spacing-xs);
|
|
50
|
+
padding: ${padding || 'var(--spacing-xs)'};
|
|
48
51
|
margin: ${margin || '0'};
|
|
49
52
|
|
|
50
53
|
border-radius: ${background && borderRadius ? borderRadius : 'none'};
|
|
54
|
+
|
|
55
|
+
svg {
|
|
56
|
+
width: calc(${size || '16px'} - 2 * (${padding || 'var(--spacing-xs)'}));
|
|
57
|
+
height: calc(${size || '16px'} - 2 * (${padding || 'var(--spacing-xs)'}));
|
|
58
|
+
flex-shrink: 0;
|
|
59
|
+
}
|
|
51
60
|
`}
|
|
52
61
|
|
|
53
62
|
color: ${({ color }) => color && getCssColorVariable(color)};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
|
|
4
|
+
import type { IconProps } from '@redocly/theme/icons/types';
|
|
5
|
+
|
|
6
|
+
import { getCssColorVariable } from '@redocly/theme/core/utils';
|
|
7
|
+
|
|
8
|
+
const Icon = (props: IconProps) => (
|
|
9
|
+
<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg" {...props}>
|
|
10
|
+
<g>
|
|
11
|
+
<path d="M14.625 6.19973C14.625 8.69176 12.6173 10.712 10.1406 10.712H2.8125V10.2717C5.04753 10.2717 6.85938 8.44864 6.85938 6.19973C6.85938 3.95082 5.04753 2.12772 2.8125 2.12772V1.6875H10.1406C12.6173 1.6875 14.625 3.70769 14.625 6.19973Z" />
|
|
12
|
+
<path d="M14.625 16.875C14.625 14.383 12.6173 12.3628 10.1406 12.3628H2.8125V12.803C5.04753 12.803 6.85938 14.6261 6.85938 16.875H14.625Z" />
|
|
13
|
+
</g>
|
|
14
|
+
</svg>
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
export const RedoclyIcon = styled(Icon).attrs(() => ({
|
|
18
|
+
'data-component-name': 'icons/RedoclyIcon/RedoclyIcon',
|
|
19
|
+
}))<IconProps>`
|
|
20
|
+
path {
|
|
21
|
+
fill: ${({ color }) => getCssColorVariable(color)};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
height: ${({ size }) => size || '16px'};
|
|
25
|
+
width: ${({ size }) => size || '16px'};
|
|
26
|
+
`;
|
package/src/index.ts
CHANGED
|
@@ -32,6 +32,7 @@ export * from '@redocly/theme/components/Buttons/CopyButton';
|
|
|
32
32
|
export * from '@redocly/theme/components/Buttons/EditPageButton';
|
|
33
33
|
export * from '@redocly/theme/components/Buttons/EmailButton';
|
|
34
34
|
export * from '@redocly/theme/components/Buttons/NewTabButton';
|
|
35
|
+
export * from '@redocly/theme/components/Buttons/AIAssistantButton';
|
|
35
36
|
/* Markdown */
|
|
36
37
|
export * from '@redocly/theme/components/Markdown/Markdown';
|
|
37
38
|
export * from '@redocly/theme/components/Markdown/styles/baseTable';
|
|
@@ -282,6 +283,7 @@ export * from '@redocly/theme/icons/RabbitMQIcon/RabbitMQIcon';
|
|
|
282
283
|
export * from '@redocly/theme/icons/CurveAutoColonIcon/CurveAutoColonIcon';
|
|
283
284
|
export * from '@redocly/theme/icons/AiStarsIcon/AiStarsIcon';
|
|
284
285
|
export * from '@redocly/theme/icons/AiStarsGradientIcon/AiStarsGradientIcon';
|
|
286
|
+
export * from '@redocly/theme/icons/RedoclyIcon/RedoclyIcon';
|
|
285
287
|
export * from '@redocly/theme/icons/WorkflowHierarchyIcon/WorkflowHierarchyIcon';
|
|
286
288
|
export * from '@redocly/theme/icons/GenericIcon/GenericIcon';
|
|
287
289
|
export * from '@redocly/theme/icons/ShareIcon/ShareIcon';
|
|
@@ -5,18 +5,24 @@ import type { JSX } from 'react';
|
|
|
5
5
|
import { Navbar } from '@redocly/theme/components/Navbar/Navbar';
|
|
6
6
|
import { Footer } from '@redocly/theme/components/Footer/Footer';
|
|
7
7
|
import { SkipContent } from '@redocly/theme/components/SkipContent/SkipContent';
|
|
8
|
+
import { AIAssistantButton } from '@redocly/theme/components/Buttons/AIAssistantButton';
|
|
9
|
+
import { useThemeHooks } from '@redocly/theme/core/hooks';
|
|
8
10
|
|
|
9
11
|
export type LayoutConfig = {
|
|
10
12
|
children: React.ReactNode;
|
|
11
13
|
};
|
|
12
14
|
|
|
13
15
|
export function RootLayout({ children }: LayoutConfig): JSX.Element {
|
|
16
|
+
const { useSearch } = useThemeHooks();
|
|
17
|
+
const { askAi } = useSearch();
|
|
18
|
+
|
|
14
19
|
return (
|
|
15
20
|
<div data-component-name="layouts/RootLayout">
|
|
16
21
|
<SkipContent />
|
|
17
22
|
<Navbar />
|
|
18
23
|
{children}
|
|
19
24
|
<Footer />
|
|
25
|
+
{askAi && <AIAssistantButton />}
|
|
20
26
|
</div>
|
|
21
27
|
);
|
|
22
28
|
}
|