@redocly/theme 0.70.0-next.3 → 0.70.0-next.5
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/Catalog/Catalog.js +1 -1
- package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsGraph.lazy.d.ts +1 -1
- package/lib/components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityHistorySidebar.js +2 -2
- package/lib/components/Catalog/CatalogEntity/CatalogEntitySchema.js +1 -1
- package/lib/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.js +30 -11
- package/lib/components/Catalog/CatalogMobileFiltersPanel/CatalogMobileFiltersPanel.js +1 -1
- package/lib/components/Catalog/CatalogMobileFiltersPanel/CatalogMobileFiltersPanelOverlay.js +2 -2
- package/lib/components/Dropdown/Dropdown.js +3 -1
- package/lib/components/Dropdown/DropdownMenu.d.ts +1 -0
- package/lib/components/Dropdown/DropdownMenu.js +1 -1
- package/lib/components/Feedback/Reasons.js +3 -2
- package/lib/components/Filter/FilterCheckboxes.js +30 -11
- package/lib/components/Image/Image.js +24 -4
- package/lib/components/Markdown/Markdown.js +3 -2
- package/lib/components/Menu/Menu.js +4 -0
- package/lib/components/Menu/MenuSelect.d.ts +7 -0
- package/lib/components/Menu/MenuSelect.js +129 -0
- package/lib/components/Menu/variables.js +43 -4
- package/lib/components/Navbar/Navbar.js +44 -5
- package/lib/components/Navbar/variables.js +3 -0
- package/lib/components/NavigationTabs/NavigationTab.d.ts +7 -0
- package/lib/components/NavigationTabs/NavigationTab.js +93 -0
- package/lib/components/NavigationTabs/NavigationTabContent.d.ts +7 -0
- package/lib/components/NavigationTabs/NavigationTabContent.js +50 -0
- package/lib/components/NavigationTabs/NavigationTabMenu.d.ts +7 -0
- package/lib/components/NavigationTabs/NavigationTabMenu.js +18 -0
- package/lib/components/NavigationTabs/NavigationTabs.d.ts +7 -0
- package/lib/components/NavigationTabs/NavigationTabs.js +97 -0
- package/lib/components/NavigationTabs/variables.d.ts +1 -0
- package/lib/components/NavigationTabs/variables.js +37 -0
- package/lib/components/Search/SearchTrigger.js +8 -2
- package/lib/components/Segmented/Segmented.js +23 -3
- package/lib/components/Select/Select.js +10 -3
- package/lib/components/Select/SelectInput.d.ts +1 -0
- package/lib/components/Select/SelectInput.js +3 -3
- package/lib/components/Sidebar/Sidebar.js +2 -2
- package/lib/components/TableOfContent/TableOfContent.js +2 -2
- package/lib/core/constants/common.d.ts +1 -0
- package/lib/core/constants/common.js +1 -0
- package/lib/core/hooks/index.d.ts +2 -0
- package/lib/core/hooks/index.js +2 -0
- package/lib/core/hooks/menu/use-mobile-menu-items.js +75 -51
- package/lib/core/hooks/menu/use-mobile-menu-levels.js +12 -7
- package/lib/core/hooks/use-scroll-item-into-view.d.ts +2 -0
- package/lib/core/hooks/use-scroll-item-into-view.js +16 -0
- package/lib/core/hooks/use-tab-list.d.ts +31 -0
- package/lib/core/hooks/use-tab-list.js +49 -0
- package/lib/core/hooks/use-theme-hooks.js +2 -0
- package/lib/core/styles/global.js +12 -2
- package/lib/core/types/hooks.d.ts +5 -0
- package/lib/core/types/index.d.ts +1 -0
- package/lib/core/types/l10n.d.ts +1 -1
- package/lib/core/types/navigation-tabs.d.ts +49 -0
- package/lib/core/types/navigation-tabs.js +3 -0
- package/lib/core/utils/Dynamic.d.ts +8 -1
- package/lib/core/utils/Dynamic.js +34 -3
- package/lib/core/utils/index.d.ts +1 -0
- package/lib/core/utils/index.js +1 -0
- package/lib/core/utils/menu.d.ts +4 -0
- package/lib/core/utils/menu.js +42 -3
- package/lib/core/utils/navigation-tabs.d.ts +8 -0
- package/lib/core/utils/navigation-tabs.js +71 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.js +7 -1
- package/lib/layouts/PageLayout.js +1 -1
- package/lib/layouts/RootLayout.js +2 -2
- package/lib/markdoc/components/CodeWalkthrough/CodeFilters.js +1 -1
- package/lib/markdoc/components/CodeWalkthrough/CodePanel.js +3 -3
- package/lib/markdoc/components/CodeWalkthrough/CodeWalkthrough.js +1 -1
- package/package.json +3 -3
- package/src/components/Catalog/Catalog.tsx +1 -1
- package/src/components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityHistorySidebar.tsx +2 -2
- package/src/components/Catalog/CatalogEntity/CatalogEntitySchema.tsx +1 -1
- package/src/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.tsx +37 -22
- package/src/components/Catalog/CatalogMobileFiltersPanel/CatalogMobileFiltersPanel.tsx +1 -1
- package/src/components/Catalog/CatalogMobileFiltersPanel/CatalogMobileFiltersPanelOverlay.tsx +2 -2
- package/src/components/Dropdown/Dropdown.tsx +4 -1
- package/src/components/Dropdown/DropdownMenu.tsx +7 -1
- package/src/components/Feedback/Reasons.tsx +3 -2
- package/src/components/Filter/FilterCheckboxes.tsx +33 -18
- package/src/components/Image/Image.tsx +26 -5
- package/src/components/Markdown/Markdown.tsx +3 -2
- package/src/components/Menu/Menu.tsx +5 -0
- package/src/components/Menu/MenuSelect.tsx +198 -0
- package/src/components/Menu/variables.ts +43 -4
- package/src/components/Navbar/Navbar.tsx +22 -5
- package/src/components/Navbar/variables.ts +3 -0
- package/src/components/NavigationTabs/NavigationTab.tsx +148 -0
- package/src/components/NavigationTabs/NavigationTabContent.tsx +66 -0
- package/src/components/NavigationTabs/NavigationTabMenu.tsx +40 -0
- package/src/components/NavigationTabs/NavigationTabs.tsx +83 -0
- package/src/components/NavigationTabs/variables.ts +34 -0
- package/src/components/Search/SearchTrigger.tsx +11 -2
- package/src/components/Segmented/Segmented.tsx +25 -16
- package/src/components/Select/Select.tsx +12 -6
- package/src/components/Select/SelectInput.tsx +8 -10
- package/src/components/Sidebar/Sidebar.tsx +2 -2
- package/src/components/TableOfContent/TableOfContent.tsx +2 -2
- package/src/core/constants/common.ts +1 -0
- package/src/core/hooks/__mocks__/use-theme-hooks.ts +3 -0
- package/src/core/hooks/index.ts +2 -0
- package/src/core/hooks/menu/use-mobile-menu-items.ts +91 -56
- package/src/core/hooks/menu/use-mobile-menu-levels.ts +14 -6
- package/src/core/hooks/use-scroll-item-into-view.ts +18 -0
- package/src/core/hooks/use-tab-list.ts +63 -0
- package/src/core/hooks/use-theme-hooks.ts +2 -0
- package/src/core/styles/global.ts +12 -2
- package/src/core/types/hooks.ts +5 -0
- package/src/core/types/index.ts +1 -0
- package/src/core/types/l10n.ts +3 -0
- package/src/core/types/navigation-tabs.ts +54 -0
- package/src/core/utils/Dynamic.tsx +67 -35
- package/src/core/utils/index.ts +1 -0
- package/src/core/utils/menu.ts +49 -3
- package/src/core/utils/navigation-tabs.ts +99 -0
- package/src/icons/CheckboxIcon/CheckboxIcon.tsx +1 -0
- package/src/icons/ChevronSortIcon/ChevronSortIcon.tsx +1 -0
- package/src/index.ts +6 -0
- package/src/layouts/PageLayout.tsx +1 -1
- package/src/layouts/RootLayout.tsx +2 -2
- package/src/markdoc/components/CodeWalkthrough/CodeFilters.tsx +1 -1
- package/src/markdoc/components/CodeWalkthrough/CodePanel.tsx +3 -3
- package/src/markdoc/components/CodeWalkthrough/CodeWalkthrough.tsx +1 -1
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
|
|
4
|
+
import type { JSX } from 'react';
|
|
5
|
+
import type { NavigationTabRowItem } from '@redocly/theme/core/types';
|
|
6
|
+
|
|
7
|
+
import { useThemeHooks } from '@redocly/theme/core/hooks';
|
|
8
|
+
import { GenericIcon } from '@redocly/theme/icons/GenericIcon/GenericIcon';
|
|
9
|
+
import { LaunchIcon } from '@redocly/theme/icons/LaunchIcon/LaunchIcon';
|
|
10
|
+
|
|
11
|
+
export type NavigationTabContentProps = {
|
|
12
|
+
item: NavigationTabRowItem;
|
|
13
|
+
className?: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function NavigationTabContent({ item, className }: NavigationTabContentProps): JSX.Element {
|
|
17
|
+
const { useTranslate } = useThemeHooks();
|
|
18
|
+
const { translate } = useTranslate();
|
|
19
|
+
const label = translate(item.labelTranslationKey, item.label);
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<NavigationTabContentWrapper
|
|
23
|
+
data-component-name="NavigationTabs/NavigationTabContent"
|
|
24
|
+
className={className}
|
|
25
|
+
>
|
|
26
|
+
{(item.icon || item.srcSet) && <NavigationTabIcon icon={item.icon} srcSet={item.srcSet} />}
|
|
27
|
+
<NavigationTabLabel data-label={label} data-translation-key={item.labelTranslationKey}>
|
|
28
|
+
{label}
|
|
29
|
+
</NavigationTabLabel>
|
|
30
|
+
{'external' in item && item.external ? <NavigationTabExternalIcon size="10px" /> : null}
|
|
31
|
+
</NavigationTabContentWrapper>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const NavigationTabContentWrapper = styled.span`
|
|
36
|
+
display: inline-flex;
|
|
37
|
+
align-items: center;
|
|
38
|
+
gap: var(--navigation-tabs-item-content-gap);
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
const NavigationTabIcon = styled(GenericIcon)`
|
|
42
|
+
--icon-width: var(--navigation-tabs-item-icon-size);
|
|
43
|
+
--icon-height: var(--navigation-tabs-item-icon-size);
|
|
44
|
+
flex-shrink: 0;
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
const NavigationTabLabel = styled.span`
|
|
48
|
+
display: inline-flex;
|
|
49
|
+
flex-direction: column;
|
|
50
|
+
cursor: pointer;
|
|
51
|
+
vertical-align: middle;
|
|
52
|
+
|
|
53
|
+
&::after {
|
|
54
|
+
content: attr(data-label);
|
|
55
|
+
height: 0;
|
|
56
|
+
overflow: hidden;
|
|
57
|
+
visibility: hidden;
|
|
58
|
+
pointer-events: none;
|
|
59
|
+
font-weight: var(--navigation-tabs-item-label-font-weight-active);
|
|
60
|
+
}
|
|
61
|
+
`;
|
|
62
|
+
|
|
63
|
+
const NavigationTabExternalIcon = styled(LaunchIcon)`
|
|
64
|
+
margin-left: var(--navigation-tabs-item-external-icon-margin-left);
|
|
65
|
+
flex-shrink: 0;
|
|
66
|
+
`;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
|
|
4
|
+
import type { JSX } from 'react';
|
|
5
|
+
import type { NavigationTabItem } from '@redocly/theme/core/types';
|
|
6
|
+
|
|
7
|
+
import { DropdownMenu } from '@redocly/theme/components/Dropdown/DropdownMenu';
|
|
8
|
+
import { DropdownMenuItem } from '@redocly/theme/components/Dropdown/DropdownMenuItem';
|
|
9
|
+
import { NavigationTabContent } from '@redocly/theme/components/NavigationTabs/NavigationTabContent';
|
|
10
|
+
|
|
11
|
+
export type NavigationTabMenuProps = {
|
|
12
|
+
items: NavigationTabItem[];
|
|
13
|
+
className?: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function NavigationTabMenu({ items, className }: NavigationTabMenuProps): JSX.Element {
|
|
17
|
+
return (
|
|
18
|
+
<NavigationTabMenuWrapper
|
|
19
|
+
data-component-name="NavigationTabs/NavigationTabMenu"
|
|
20
|
+
className={className}
|
|
21
|
+
>
|
|
22
|
+
<DropdownMenu>
|
|
23
|
+
{items.map((item, index) => (
|
|
24
|
+
<DropdownMenuItem
|
|
25
|
+
key={`${item.label}_${index}`}
|
|
26
|
+
to={item.link}
|
|
27
|
+
external={item.external}
|
|
28
|
+
data-translation-key={item.labelTranslationKey}
|
|
29
|
+
active={item.active}
|
|
30
|
+
aria-current={item.active ? 'page' : undefined}
|
|
31
|
+
>
|
|
32
|
+
<NavigationTabContent item={item} />
|
|
33
|
+
</DropdownMenuItem>
|
|
34
|
+
))}
|
|
35
|
+
</DropdownMenu>
|
|
36
|
+
</NavigationTabMenuWrapper>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const NavigationTabMenuWrapper = styled.div``;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import React, { useRef } from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
|
|
4
|
+
import type { JSX } from 'react';
|
|
5
|
+
import type { NavigationTabsConfig } from '@redocly/theme/core/types';
|
|
6
|
+
|
|
7
|
+
import { breakpoints } from '@redocly/theme/core/utils';
|
|
8
|
+
import { useScrollItemIntoView, useThemeHooks } from '@redocly/theme/core/hooks';
|
|
9
|
+
import { NavigationTab } from '@redocly/theme/components/NavigationTabs/NavigationTab';
|
|
10
|
+
|
|
11
|
+
export type NavigationTabsProps = {
|
|
12
|
+
config?: NavigationTabsConfig;
|
|
13
|
+
className?: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function NavigationTabs({ config, className }: NavigationTabsProps): JSX.Element | null {
|
|
17
|
+
const { useTranslate } = useThemeHooks();
|
|
18
|
+
const { translate } = useTranslate();
|
|
19
|
+
const listRef = useRef<HTMLUListElement | null>(null);
|
|
20
|
+
|
|
21
|
+
const tabs = config?.items ?? [];
|
|
22
|
+
const activeIndex = tabs.findIndex((tab) => tab.active);
|
|
23
|
+
|
|
24
|
+
useScrollItemIntoView(listRef, activeIndex);
|
|
25
|
+
|
|
26
|
+
if (!tabs.length) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<NavigationTabsWrapper
|
|
32
|
+
data-component-name="NavigationTabs/NavigationTabs"
|
|
33
|
+
className={className}
|
|
34
|
+
aria-label={translate('navigationTabs.label', 'Navigation tabs')}
|
|
35
|
+
>
|
|
36
|
+
<NavigationTabsList ref={listRef}>
|
|
37
|
+
{tabs.map((tab, index) => (
|
|
38
|
+
<NavigationTab key={`${tab.label}_${index}`} tab={tab} />
|
|
39
|
+
))}
|
|
40
|
+
</NavigationTabsList>
|
|
41
|
+
</NavigationTabsWrapper>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const NavigationTabsWrapper = styled.nav`
|
|
46
|
+
display: none;
|
|
47
|
+
box-sizing: border-box;
|
|
48
|
+
width: 100%;
|
|
49
|
+
height: var(--navigation-tabs-height);
|
|
50
|
+
max-width: var(--navbar-container-max-width);
|
|
51
|
+
padding: var(--navigation-tabs-padding);
|
|
52
|
+
background: var(--navigation-tabs-bg-color);
|
|
53
|
+
border-bottom: var(--navigation-tabs-bottom-border);
|
|
54
|
+
flex-shrink: 0;
|
|
55
|
+
|
|
56
|
+
@media screen and (min-width: ${breakpoints.medium}) {
|
|
57
|
+
display: flex;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@media screen and (min-width: ${breakpoints.max}) {
|
|
61
|
+
max-width: var(--container-max-width);
|
|
62
|
+
margin-left: auto;
|
|
63
|
+
margin-right: auto;
|
|
64
|
+
}
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
const NavigationTabsList = styled.ul`
|
|
68
|
+
list-style: none;
|
|
69
|
+
margin: 0 0 calc(-1 * var(--navigation-tabs-border-width));
|
|
70
|
+
padding: 0;
|
|
71
|
+
display: flex;
|
|
72
|
+
flex-wrap: nowrap;
|
|
73
|
+
align-items: stretch;
|
|
74
|
+
gap: var(--navigation-tabs-gap);
|
|
75
|
+
min-width: 0;
|
|
76
|
+
overflow-x: auto;
|
|
77
|
+
scrollbar-width: none;
|
|
78
|
+
-ms-overflow-style: none;
|
|
79
|
+
|
|
80
|
+
&::-webkit-scrollbar {
|
|
81
|
+
display: none;
|
|
82
|
+
}
|
|
83
|
+
`;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { css } from 'styled-components';
|
|
2
|
+
|
|
3
|
+
export const navigationTabs = css`
|
|
4
|
+
--navigation-tabs-height: 48px; // @presenter Spacing
|
|
5
|
+
--navigation-tabs-bg-color: transparent; // @presenter Color
|
|
6
|
+
--navigation-tabs-border-color: var(--border-color-secondary);
|
|
7
|
+
--navigation-tabs-border-width: 1px;
|
|
8
|
+
--navigation-tabs-bottom-border: var(--navigation-tabs-border-width) solid var(--navigation-tabs-border-color);
|
|
9
|
+
--navigation-tabs-padding-vertical: 0;
|
|
10
|
+
--navigation-tabs-padding-horizontal: var(--spacing-xl);
|
|
11
|
+
--navigation-tabs-padding: var(--navigation-tabs-padding-vertical) var(--navigation-tabs-padding-horizontal); // @presenter Spacing
|
|
12
|
+
--navigation-tabs-gap: var(--spacing-base); // @presenter Spacing
|
|
13
|
+
|
|
14
|
+
--navigation-tabs-item-padding-vertical: var(--spacing-xxs);
|
|
15
|
+
--navigation-tabs-item-padding-horizontal: 0;
|
|
16
|
+
--navigation-tabs-item-padding: var(--navigation-tabs-item-padding-vertical) var(--navigation-tabs-item-padding-horizontal);
|
|
17
|
+
--navigation-tabs-item-content-gap: var(--spacing-xs);
|
|
18
|
+
--navigation-tabs-item-font-size: var(--font-size-base);
|
|
19
|
+
--navigation-tabs-item-line-height: var(--line-height-base);
|
|
20
|
+
--navigation-tabs-item-font-weight: var(--font-weight-regular);
|
|
21
|
+
--navigation-tabs-item-font-weight-active: var(--font-weight-medium);
|
|
22
|
+
--navigation-tabs-item-text-color: var(--navbar-text-color); // @presenter Color
|
|
23
|
+
--navigation-tabs-item-text-color-hover: var(--text-color-primary); // @presenter Color
|
|
24
|
+
--navigation-tabs-item-text-color-active: var(--text-color-primary); // @presenter Color
|
|
25
|
+
--navigation-tabs-item-bottom-border: var(--navigation-tabs-border-width) solid transparent;
|
|
26
|
+
--navigation-tabs-item-bottom-border-hover: var(--navigation-tabs-border-width) solid var(--border-color-primary);
|
|
27
|
+
--navigation-tabs-item-bottom-border-active: var(--navigation-tabs-border-width) solid var(--border-color-inverse);
|
|
28
|
+
--navigation-tabs-item-border-color-focused: var(--button-border-color-focused);
|
|
29
|
+
--navigation-tabs-item-border-radius: var(--navbar-item-border-radius);
|
|
30
|
+
--navigation-tabs-item-icon-size: 1.25em; // @presenter Spacing
|
|
31
|
+
--navigation-tabs-item-external-icon-margin-left: 0;
|
|
32
|
+
--navigation-tabs-item-label-font-weight-active: var(--navigation-tabs-item-font-weight-active);
|
|
33
|
+
--navigation-tabs-item-transition: 150ms ease-in-out;
|
|
34
|
+
`;
|
|
@@ -31,7 +31,10 @@ export function SearchTrigger({ onClick, className }: SearchTriggerProps): JSX.E
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
mainShortcutKey = mainShortcutKey?.toUpperCase();
|
|
34
|
-
|
|
34
|
+
// A page can be cached and served to every platform, so the server renders a hidden
|
|
35
|
+
// placeholder and the browser picks the label after hydration.
|
|
36
|
+
const [isMac, setIsMac] = React.useState<boolean | undefined>(undefined);
|
|
37
|
+
React.useEffect(() => setIsMac(getUserAgent().includes('Mac')), []);
|
|
35
38
|
|
|
36
39
|
return (
|
|
37
40
|
<SearchTriggerWrapper
|
|
@@ -53,7 +56,9 @@ export function SearchTrigger({ onClick, className }: SearchTriggerProps): JSX.E
|
|
|
53
56
|
<SearchIcon />
|
|
54
57
|
{translate('search.navbar.label', 'Search')}
|
|
55
58
|
{mainShortcutKey === DEFAULT_KEYBOARD_SHORTCUTS ? (
|
|
56
|
-
<
|
|
59
|
+
<SearchTriggerShortcut $isPending={isMac === undefined}>
|
|
60
|
+
{isMac ? '⌘K' : 'Ctrl+K'}
|
|
61
|
+
</SearchTriggerShortcut>
|
|
57
62
|
) : (
|
|
58
63
|
<span>{mainShortcutKey}</span>
|
|
59
64
|
)}
|
|
@@ -80,6 +85,10 @@ const SearchTriggerButton = styled(Button)`
|
|
|
80
85
|
}
|
|
81
86
|
`;
|
|
82
87
|
|
|
88
|
+
const SearchTriggerShortcut = styled.span<{ $isPending: boolean }>`
|
|
89
|
+
visibility: ${({ $isPending }) => ($isPending ? 'hidden' : 'visible')};
|
|
90
|
+
`;
|
|
91
|
+
|
|
83
92
|
const SearchTriggerInput = styled.div`
|
|
84
93
|
display: none;
|
|
85
94
|
align-items: center;
|
|
@@ -5,34 +5,43 @@ import type { ForwardedRef, ReactElement } from 'react';
|
|
|
5
5
|
import type { SegmentedProps } from '@redocly/theme/core/types';
|
|
6
6
|
|
|
7
7
|
import { typedMemo } from '@redocly/theme/core/hoc/typed-memo';
|
|
8
|
+
import { useTabList } from '@redocly/theme/core/hooks';
|
|
8
9
|
|
|
9
10
|
function SegmentedComponent<T>(
|
|
10
11
|
{ options, onChange, value, className = '', size = 'regular' }: SegmentedProps<T>,
|
|
11
12
|
ref?: ForwardedRef<HTMLDivElement>,
|
|
12
13
|
): ReactElement {
|
|
14
|
+
const { tabListProps, getTabProps } = useTabList({
|
|
15
|
+
count: options.length,
|
|
16
|
+
activeIndex: options.findIndex((opt) => value == opt.value),
|
|
17
|
+
onChange: (index) => onChange(options[index]),
|
|
18
|
+
});
|
|
19
|
+
|
|
13
20
|
return (
|
|
14
21
|
<SegmentedGroup
|
|
15
22
|
ref={ref}
|
|
16
23
|
data-component-name="Segmented/Segmented"
|
|
17
24
|
className={`tag-grey ${size} ${className}`}
|
|
18
|
-
|
|
25
|
+
{...tabListProps}
|
|
19
26
|
$size={size}
|
|
20
27
|
>
|
|
21
|
-
{options.map((opt, index) =>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
{options.map((opt, index) => {
|
|
29
|
+
const { 'aria-controls': _noPanel, ...tabProps } = getTabProps(index);
|
|
30
|
+
return (
|
|
31
|
+
<React.Fragment key={index}>
|
|
32
|
+
{opt.divider}
|
|
33
|
+
<SegmentedItem
|
|
34
|
+
key={opt.label}
|
|
35
|
+
{...tabProps}
|
|
36
|
+
title={opt.label}
|
|
37
|
+
$isActive={value == opt.value}
|
|
38
|
+
$size={size}
|
|
39
|
+
>
|
|
40
|
+
{opt.element || opt.label}
|
|
41
|
+
</SegmentedItem>
|
|
42
|
+
</React.Fragment>
|
|
43
|
+
);
|
|
44
|
+
})}
|
|
36
45
|
</SegmentedGroup>
|
|
37
46
|
);
|
|
38
47
|
}
|
|
@@ -63,6 +63,7 @@ export function Select<T>(props: SelectProps<T>): JSX.Element {
|
|
|
63
63
|
const [filteredOptions, setFilteredOptions] = useState<SelectOption<T>[]>(options);
|
|
64
64
|
const [stickyInputValue, setStickyInputValue] = useState<string>(placeholder || '');
|
|
65
65
|
const inputId = useId();
|
|
66
|
+
const listboxId = `${inputId}-listbox`;
|
|
66
67
|
|
|
67
68
|
useOutsideClick(selectRef, () => {
|
|
68
69
|
setDropdownActive(false);
|
|
@@ -189,7 +190,8 @@ export function Select<T>(props: SelectProps<T>): JSX.Element {
|
|
|
189
190
|
return (
|
|
190
191
|
<SelectInput
|
|
191
192
|
id={inputId}
|
|
192
|
-
listboxId={
|
|
193
|
+
listboxId={listboxId}
|
|
194
|
+
expanded={dropdownActive}
|
|
193
195
|
selectedOptions={selectedOptions}
|
|
194
196
|
searchValue={searchValue}
|
|
195
197
|
placeholder={placeholder}
|
|
@@ -223,10 +225,14 @@ export function Select<T>(props: SelectProps<T>): JSX.Element {
|
|
|
223
225
|
setDropdownActive(!dropdownActive);
|
|
224
226
|
}
|
|
225
227
|
}}
|
|
226
|
-
tabIndex={0}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
228
|
+
tabIndex={searchable ? undefined : 0}
|
|
229
|
+
{...(!searchable && {
|
|
230
|
+
role: 'combobox',
|
|
231
|
+
'aria-haspopup': 'listbox' as const,
|
|
232
|
+
'aria-expanded': dropdownActive,
|
|
233
|
+
'aria-controls': listboxId,
|
|
234
|
+
'aria-label': 'Select option',
|
|
235
|
+
})}
|
|
230
236
|
onFocus={(e) => {
|
|
231
237
|
if (e.target === selectRef.current) {
|
|
232
238
|
selectInputRef.current?.focus();
|
|
@@ -245,7 +251,7 @@ export function Select<T>(props: SelectProps<T>): JSX.Element {
|
|
|
245
251
|
alignment={alignment}
|
|
246
252
|
active={!renderInput ? dropdownActive : undefined}
|
|
247
253
|
>
|
|
248
|
-
<SelectDropdownMenu role="listbox" footer={footer}>
|
|
254
|
+
<SelectDropdownMenu id={listboxId} role="listbox" footer={footer}>
|
|
249
255
|
{filteredOptions.length === 0 ? (
|
|
250
256
|
<DropdownMenuItem disabled>
|
|
251
257
|
{translate('select.noResults', 'No results')}
|
|
@@ -22,6 +22,7 @@ type SelectInputProps<T> = {
|
|
|
22
22
|
placeholder?: string;
|
|
23
23
|
multiple?: boolean;
|
|
24
24
|
searchable?: boolean;
|
|
25
|
+
expanded?: boolean;
|
|
25
26
|
clearable?: boolean;
|
|
26
27
|
inputRef?: React.ForwardedRef<HTMLInputElement>;
|
|
27
28
|
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
|
@@ -48,6 +49,7 @@ export function SelectInput<T>(props: SelectInputProps<T>): React.ReactNode {
|
|
|
48
49
|
stickyValue,
|
|
49
50
|
multiple,
|
|
50
51
|
searchable,
|
|
52
|
+
expanded,
|
|
51
53
|
clearable,
|
|
52
54
|
clearHandler,
|
|
53
55
|
searchHandler,
|
|
@@ -134,6 +136,11 @@ export function SelectInput<T>(props: SelectInputProps<T>): React.ReactNode {
|
|
|
134
136
|
}}
|
|
135
137
|
$width={multiple ? (!searchValue && selectedOptions.length ? '10px' : 'auto') : '100%'}
|
|
136
138
|
tabIndex={0}
|
|
139
|
+
role="combobox"
|
|
140
|
+
aria-expanded={!!expanded}
|
|
141
|
+
aria-controls={listboxId}
|
|
142
|
+
aria-autocomplete="list"
|
|
143
|
+
aria-label="Select option"
|
|
137
144
|
onFocus={(e) => {
|
|
138
145
|
e.stopPropagation();
|
|
139
146
|
}}
|
|
@@ -156,16 +163,7 @@ export function SelectInput<T>(props: SelectInputProps<T>): React.ReactNode {
|
|
|
156
163
|
);
|
|
157
164
|
|
|
158
165
|
return (
|
|
159
|
-
<SelectInputWrapper
|
|
160
|
-
id={id}
|
|
161
|
-
onFocus={onFocusHandler}
|
|
162
|
-
onClick={onClickHandler}
|
|
163
|
-
role="button"
|
|
164
|
-
aria-haspopup="listbox"
|
|
165
|
-
aria-controls={listboxId}
|
|
166
|
-
aria-owns={listboxId}
|
|
167
|
-
aria-label="Select option"
|
|
168
|
-
>
|
|
166
|
+
<SelectInputWrapper id={id} onFocus={onFocusHandler} onClick={onClickHandler}>
|
|
169
167
|
{!onlyIcon && (
|
|
170
168
|
<>
|
|
171
169
|
<SelectInputValue>
|
|
@@ -84,8 +84,8 @@ const SidebarContent = styled.aside<{ $opened: boolean; $collapsed?: boolean }>`
|
|
|
84
84
|
flex-direction: column;
|
|
85
85
|
width: 100%;
|
|
86
86
|
-webkit-font-smoothing: antialiased;
|
|
87
|
-
top:
|
|
88
|
-
height: calc(100vh - var(--navbar-
|
|
87
|
+
top: var(--navbar-offset);
|
|
88
|
+
height: calc(100vh - var(--navbar-offset));
|
|
89
89
|
overflow-x: hidden;
|
|
90
90
|
${({ $opened }) => `
|
|
91
91
|
display: ${$opened ? 'flex' : 'none'}
|
|
@@ -101,8 +101,8 @@ const TableOfContentHeader = styled.div`
|
|
|
101
101
|
|
|
102
102
|
const TableOfContentItems = styled.div`
|
|
103
103
|
position: sticky;
|
|
104
|
-
top: calc(var(--navbar-height) + var(--toc-offset-top));
|
|
105
|
-
max-height: calc(100vh - var(--navbar-height) - var(--toc-offset-top));
|
|
104
|
+
top: calc(var(--navbar-stack-height) + var(--toc-offset-top));
|
|
105
|
+
max-height: calc(100vh - var(--navbar-stack-height) - var(--toc-offset-top));
|
|
106
106
|
overflow-y: auto;
|
|
107
107
|
width: var(--toc-width);
|
|
108
108
|
border-left: solid 1px var(--toc-border-color);
|
|
@@ -20,6 +20,7 @@ export const useThemeHooks = vi.fn(() => ({
|
|
|
20
20
|
sendPageActionsButtonClickedMessage: vi.fn(),
|
|
21
21
|
sendColorModeSwitchedMessage: vi.fn(),
|
|
22
22
|
sendConsentChangedMessage: vi.fn(),
|
|
23
|
+
sendFilterCheckboxToggledMessage: vi.fn(),
|
|
23
24
|
})),
|
|
24
25
|
useBreadcrumbs: vi.fn().mockReturnValue({ breadcrumbs: [], siblings: undefined }),
|
|
25
26
|
useBanner: vi.fn(() => ({
|
|
@@ -70,6 +71,7 @@ export const useThemeHooks = vi.fn(() => ({
|
|
|
70
71
|
items: [],
|
|
71
72
|
isLoading: false,
|
|
72
73
|
})),
|
|
74
|
+
useSearchFeatures: vi.fn(() => ({ askAi: false })),
|
|
73
75
|
useAiSearch: vi.fn(() => ({
|
|
74
76
|
askQuestion: vi.fn(),
|
|
75
77
|
isGeneratingResponse: false,
|
|
@@ -117,6 +119,7 @@ export const useThemeHooks = vi.fn(() => ({
|
|
|
117
119
|
pushDrilldownState: vi.fn(),
|
|
118
120
|
}),
|
|
119
121
|
useSidebarSiblingsData: vi.fn(),
|
|
122
|
+
useNavigationTabs: vi.fn(),
|
|
120
123
|
useSubmitFeedback: vi.fn(() => ({
|
|
121
124
|
submitFeedback: vi.fn(),
|
|
122
125
|
})),
|
package/src/core/hooks/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ export * from './menu/use-mobile-menu-levels';
|
|
|
21
21
|
export * from './menu/use-collapse';
|
|
22
22
|
export * from './feedback/use-report-dialog';
|
|
23
23
|
export * from './use-tabs';
|
|
24
|
+
export * from './use-tab-list';
|
|
24
25
|
export * from './use-focus-trap';
|
|
25
26
|
export * from './use-consent-dialog';
|
|
26
27
|
export * from './use-consent-telemetry';
|
|
@@ -58,3 +59,4 @@ export * from './use-toast-logic';
|
|
|
58
59
|
export * from './use-banner-telemetry';
|
|
59
60
|
export * from './use-tooltip-fallback-placement';
|
|
60
61
|
export * from './use-anchor-positioning';
|
|
62
|
+
export * from './use-scroll-item-into-view';
|