@redocly/theme 0.52.0-next.2 → 0.52.0-next.4

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 (82) hide show
  1. package/lib/components/Button/Button.d.ts +1 -0
  2. package/lib/components/Button/Button.js +2 -1
  3. package/lib/components/Catalog/Catalog.js +2 -26
  4. package/lib/components/Catalog/CatalogCard.js +1 -0
  5. package/lib/components/Catalog/CatalogVirtualizedGroups.d.ts +25 -0
  6. package/lib/components/Catalog/CatalogVirtualizedGroups.js +183 -0
  7. package/lib/components/Catalog/variables.js +3 -0
  8. package/lib/components/Dropdown/Dropdown.js +1 -1
  9. package/lib/components/Link/Link.js +2 -2
  10. package/lib/components/Menu/MenuItem.js +1 -1
  11. package/lib/components/Navbar/NavbarItem.js +4 -1
  12. package/lib/components/Search/FilterFields/SearchFilterFieldTags.js +12 -5
  13. package/lib/components/Search/SearchAiMessage.js +6 -5
  14. package/lib/components/Search/SearchDialog.js +79 -28
  15. package/lib/components/Search/SearchInput.d.ts +2 -2
  16. package/lib/components/Search/SearchInput.js +4 -4
  17. package/lib/components/Search/SearchItem.d.ts +3 -1
  18. package/lib/components/Search/SearchItem.js +53 -28
  19. package/lib/components/Search/variables.js +0 -1
  20. package/lib/components/Select/Select.js +6 -3
  21. package/lib/components/Select/SelectInput.d.ts +1 -0
  22. package/lib/components/Select/SelectInput.js +13 -2
  23. package/lib/components/Tag/Tag.d.ts +3 -1
  24. package/lib/components/Tag/Tag.js +2 -2
  25. package/lib/components/UserMenu/UserMenu.js +1 -1
  26. package/lib/core/hooks/__mocks__/index.d.ts +1 -0
  27. package/lib/core/hooks/__mocks__/index.js +1 -0
  28. package/lib/core/hooks/__mocks__/use-element-size.d.ts +4 -0
  29. package/lib/core/hooks/__mocks__/use-element-size.js +9 -0
  30. package/lib/core/hooks/index.d.ts +1 -0
  31. package/lib/core/hooks/index.js +1 -0
  32. package/lib/core/hooks/menu/use-collapse.js +0 -1
  33. package/lib/core/hooks/menu/use-nested-menu.js +5 -0
  34. package/lib/core/hooks/use-dialog-hotkeys.js +1 -2
  35. package/lib/core/hooks/use-element-size.d.ts +13 -0
  36. package/lib/core/hooks/use-element-size.js +75 -0
  37. package/lib/core/styles/global.js +0 -1
  38. package/lib/core/types/l10n.d.ts +1 -1
  39. package/lib/icons/CheckboxFilledIcon/CheckboxFilledIcon.d.ts +9 -0
  40. package/lib/icons/CheckboxFilledIcon/CheckboxFilledIcon.js +22 -0
  41. package/lib/icons/ReturnKeyIcon/ReturnKeyIcon.d.ts +3 -0
  42. package/lib/icons/ReturnKeyIcon/ReturnKeyIcon.js +18 -0
  43. package/lib/icons/WarningAltFilled/WarningAltFilled.d.ts +9 -0
  44. package/lib/icons/WarningAltFilled/WarningAltFilled.js +23 -0
  45. package/lib/index.d.ts +3 -1
  46. package/lib/index.js +3 -1
  47. package/package.json +2 -2
  48. package/src/components/Button/Button.tsx +4 -1
  49. package/src/components/Catalog/Catalog.tsx +3 -37
  50. package/src/components/Catalog/CatalogCard.tsx +1 -0
  51. package/src/components/Catalog/CatalogVirtualizedGroups.tsx +236 -0
  52. package/src/components/Catalog/variables.ts +3 -0
  53. package/src/components/Dropdown/Dropdown.tsx +0 -1
  54. package/src/components/Link/Link.tsx +2 -1
  55. package/src/components/Menu/MenuItem.tsx +1 -0
  56. package/src/components/Navbar/NavbarItem.tsx +5 -1
  57. package/src/components/Search/FilterFields/SearchFilterFieldTags.tsx +13 -4
  58. package/src/components/Search/SearchAiMessage.tsx +4 -4
  59. package/src/components/Search/SearchDialog.tsx +127 -53
  60. package/src/components/Search/SearchInput.tsx +10 -3
  61. package/src/components/Search/SearchItem.tsx +89 -55
  62. package/src/components/Search/variables.ts +0 -1
  63. package/src/components/Select/Select.tsx +7 -2
  64. package/src/components/Select/SelectInput.tsx +14 -2
  65. package/src/components/Tag/Tag.tsx +6 -0
  66. package/src/components/UserMenu/UserMenu.tsx +1 -1
  67. package/src/core/hooks/__mocks__/index.ts +1 -0
  68. package/src/core/hooks/__mocks__/use-element-size.ts +6 -0
  69. package/src/core/hooks/index.ts +1 -0
  70. package/src/core/hooks/menu/use-collapse.ts +0 -1
  71. package/src/core/hooks/menu/use-nested-menu.ts +4 -0
  72. package/src/core/hooks/use-dialog-hotkeys.ts +1 -1
  73. package/src/core/hooks/use-element-size.ts +98 -0
  74. package/src/core/styles/global.ts +0 -1
  75. package/src/core/types/l10n.ts +2 -0
  76. package/src/icons/CheckboxFilledIcon/CheckboxFilledIcon.tsx +23 -0
  77. package/src/icons/ReturnKeyIcon/ReturnKeyIcon.tsx +13 -0
  78. package/src/icons/WarningAltFilled/WarningAltFilled.tsx +24 -0
  79. package/src/index.ts +3 -1
  80. package/lib/components/Footer/FooterLogo.d.ts +0 -3
  81. package/lib/components/Footer/FooterLogo.js +0 -22
  82. package/src/components/Footer/FooterLogo.tsx +0 -20
@@ -15,6 +15,7 @@ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElemen
15
15
  icon?: JSX.Element;
16
16
  iconPosition?: 'left' | 'right';
17
17
  title?: string;
18
+ tabIndex?: number;
18
19
  onClick?: (e?: any) => void;
19
20
  type?: 'button' | 'submit' | 'reset';
20
21
  }
@@ -154,7 +154,8 @@ const StyledButton = styled_components_1.default.button.attrs((props) => ({
154
154
  `}
155
155
  `;
156
156
  const ButtonComponent = (props) => {
157
- const button = (react_1.default.createElement(StyledButton, Object.assign({ "data-component-name": "Button/Button" }, props, { iconOnly: !props.children && props.icon !== null, tabIndex: props.to ? -1 : undefined }),
157
+ const tabIndex = 'tabIndex' in props ? props.tabIndex : props.to ? -1 : undefined;
158
+ const button = (react_1.default.createElement(StyledButton, Object.assign({ "data-component-name": "Button/Button" }, props, { iconOnly: !props.children && props.icon !== null, tabIndex: tabIndex }),
158
159
  props.icon && props.iconPosition !== 'right' && props.icon,
159
160
  props.children,
160
161
  props.icon && props.iconPosition === 'right' && props.icon));
@@ -10,13 +10,12 @@ const styled_components_1 = __importDefault(require("styled-components"));
10
10
  const utils_1 = require("../../core/utils");
11
11
  const hooks_1 = require("../../core/hooks");
12
12
  const H2_1 = require("../../components/Typography/H2");
13
- const CatalogCard_1 = require("../../components/Catalog/CatalogCard");
14
13
  const FilterContent_1 = require("../../components/Filter/FilterContent");
15
14
  const FilterPopover_1 = require("../../components/Filter/FilterPopover");
16
15
  const CatalogHighlight_1 = require("../../components/Catalog/CatalogHighlight");
17
16
  const CatalogActions_1 = require("../../components/Catalog/CatalogActions");
18
17
  const Sidebar_1 = require("../../components/Sidebar/Sidebar");
19
- const CounterTag_1 = require("../../components/Tags/CounterTag");
18
+ const CatalogVirtualizedGroups_1 = require("../../components/Catalog/CatalogVirtualizedGroups");
20
19
  function Catalog(props) {
21
20
  const { catalogConfig } = props.pageProps;
22
21
  const { useTranslate, useCatalog } = (0, hooks_1.useThemeHooks)();
@@ -39,25 +38,8 @@ function Catalog(props) {
39
38
  ' ',
40
39
  translate(catalogConfig.descriptionTranslationKey, catalogConfig.description),
41
40
  ' ')) : null),
42
- groups.map((group) => (react_1.default.createElement(react_1.default.Fragment, { key: group.title },
43
- react_1.default.createElement(CatalogSeparator, { "data-testid": "catalog-separator" },
44
- react_1.default.createElement(CatalogSeparatorLabel, null, group.title),
45
- react_1.default.createElement(CounterTag_1.CounterTag, { borderless: true }, group.items.length)),
46
- react_1.default.createElement(CatalogCards, null, group.items.map((item) => (react_1.default.createElement(CatalogCard_1.CatalogCard, { item: item, key: item.link })))))))))));
41
+ react_1.default.createElement(CatalogVirtualizedGroups_1.CatalogVirtualizedGroups, { groups: groups, filters: filters, filterTerm: filterTerm })))));
47
42
  }
48
- const CatalogSeparator = styled_components_1.default.div `
49
- display: flex;
50
- align-items: center;
51
- color: var(--catalog-separator-color);
52
- font-size: var(--catalog-separator-font-size);
53
- font-weight: var(--catalog-separator-font-weight);
54
- border-top: 1px solid var(--catalog-separator-border-color);
55
- margin: var(--catalog-separator-margin);
56
- padding: var(--catalog-separator-padding);
57
- `;
58
- const CatalogSeparatorLabel = styled_components_1.default.div `
59
- margin: var(--catalog-separator-label-margin);
60
- `;
61
43
  exports.CatalogPageContent = styled_components_1.default.main `
62
44
  flex: 1;
63
45
  width: 90%;
@@ -68,12 +50,6 @@ exports.CatalogPageContent = styled_components_1.default.main `
68
50
  padding: var(--catalog-page-padding);
69
51
  }
70
52
  `;
71
- const CatalogCards = styled_components_1.default.div `
72
- display: grid;
73
- grid-template-columns: repeat(auto-fill, minmax(var(--api-catalog-card-min-width), 1fr));
74
- gap: 32px;
75
- margin: var(--catalog-cards-group-margin);
76
- `;
77
53
  exports.CatalogTitle = (0, styled_components_1.default)(H2_1.H2) `
78
54
  color: var(--catalog-title-text-color);
79
55
  font-weight: var(--catalog-title-font-weight);
@@ -110,6 +110,7 @@ const CardDescription = styled_components_1.default.div `
110
110
  font-size: var(--catalog-card-description-font-size);
111
111
  font-weight: var(--catalog-card-description-font-weight);
112
112
  line-height: var(--catalog-card-description-line-height);
113
+ height: var(--catalog-card-description-height);
113
114
  `;
114
115
  const CardContent = styled_components_1.default.div `
115
116
  flex: 1;
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ import { CatalogItem, ResolvedFilter } from '../../core/types';
3
+ export type Group = {
4
+ title: string;
5
+ items: CatalogItem[];
6
+ };
7
+ export type VirtualRowData = {
8
+ type: 'header';
9
+ groupTitle: string;
10
+ groupCount: number;
11
+ key: string;
12
+ } | {
13
+ type: 'cardRow';
14
+ groupTitle: string;
15
+ items: CatalogItem[];
16
+ key: string;
17
+ };
18
+ export type CatalogVirtualizedGroupsProps = {
19
+ groups: Group[];
20
+ filters: (ResolvedFilter & {
21
+ isFilterUsed?: boolean;
22
+ })[];
23
+ filterTerm: string;
24
+ };
25
+ export declare function CatalogVirtualizedGroups({ groups, filters, filterTerm, }: CatalogVirtualizedGroupsProps): React.JSX.Element;
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.CatalogVirtualizedGroups = CatalogVirtualizedGroups;
30
+ const react_1 = __importStar(require("react"));
31
+ const react_virtual_1 = require("@tanstack/react-virtual");
32
+ const styled_components_1 = __importDefault(require("styled-components"));
33
+ const hooks_1 = require("../../core/hooks");
34
+ const CatalogCard_1 = require("../../components/Catalog/CatalogCard");
35
+ const CounterTag_1 = require("../../components/Tags/CounterTag");
36
+ const SpinnerLoader_1 = require("../../components/Loaders/SpinnerLoader");
37
+ const GAP_SIZE = 32;
38
+ const ESTIMATED_HEADER_HEIGHT = 43;
39
+ const ESTIMATED_CARD_HEIGHT = 194 + GAP_SIZE;
40
+ const CARD_MIN_WIDTH_VAR = '--catalog-card-min-width';
41
+ function CatalogVirtualizedGroups({ groups, filters, filterTerm, }) {
42
+ const [isClient, setIsClient] = (0, react_1.useState)(false);
43
+ const [size, parentRef] = (0, hooks_1.useElementSize)({ delay: 50, detectSizes: 'width' });
44
+ (0, react_1.useEffect)(() => {
45
+ setIsClient(true);
46
+ }, []);
47
+ (0, react_1.useEffect)(() => {
48
+ if (!size.width) {
49
+ return;
50
+ }
51
+ virtualizer.measure();
52
+ // eslint-disable-next-line react-hooks/exhaustive-deps
53
+ }, [filters, filterTerm, size.width]);
54
+ const columnCount = (0, react_1.useMemo)(() => {
55
+ if (!size.width)
56
+ return 4;
57
+ const cardMinWidth = parseInt(getComputedStyle(document.documentElement).getPropertyValue(CARD_MIN_WIDTH_VAR), 10);
58
+ return Math.max(1, Math.floor((size.width + GAP_SIZE) / (cardMinWidth + GAP_SIZE)));
59
+ }, [size.width]);
60
+ const flatRows = (0, react_1.useMemo)(() => {
61
+ if (!isClient) {
62
+ return groups.flatMap((group) => [
63
+ {
64
+ type: 'header',
65
+ groupTitle: group.title,
66
+ groupCount: group.items.length,
67
+ key: `header-${group.title}`,
68
+ },
69
+ {
70
+ type: 'cardRow',
71
+ groupTitle: group.title,
72
+ items: group.items,
73
+ key: `${group.title}-cards`,
74
+ },
75
+ ]);
76
+ }
77
+ const rows = [];
78
+ groups.forEach((group) => {
79
+ rows.push({
80
+ type: 'header',
81
+ groupTitle: group.title,
82
+ groupCount: group.items.length,
83
+ key: `header-${group.title}`,
84
+ });
85
+ const numRows = Math.ceil(group.items.length / columnCount);
86
+ for (let rowIndex = 0; rowIndex < numRows; rowIndex++) {
87
+ const startIndex = rowIndex * columnCount;
88
+ const rowItems = group.items.slice(startIndex, startIndex + columnCount);
89
+ rows.push({
90
+ type: 'cardRow',
91
+ groupTitle: group.title,
92
+ items: rowItems,
93
+ key: `${group.title}-row-${rowIndex}`,
94
+ });
95
+ }
96
+ });
97
+ return rows;
98
+ }, [groups, columnCount, isClient]);
99
+ const virtualizer = (0, react_virtual_1.useWindowVirtualizer)({
100
+ count: flatRows.length,
101
+ estimateSize: (index) => {
102
+ const row = flatRows[index];
103
+ if (row.type === 'header')
104
+ return ESTIMATED_HEADER_HEIGHT;
105
+ return ESTIMATED_CARD_HEIGHT;
106
+ },
107
+ overscan: 5,
108
+ });
109
+ if (!isClient) {
110
+ return (react_1.default.createElement("div", { ref: parentRef, "data-component-name": "Catalog/CatalogVirtualizedGroups" },
111
+ flatRows.slice(0, 15).map((rowData) => {
112
+ if (rowData.type === 'header') {
113
+ return (react_1.default.createElement(SSRHeaderRow, { key: rowData.key },
114
+ react_1.default.createElement(CatalogSeparatorLabel, null, rowData.groupTitle),
115
+ react_1.default.createElement(CounterTag_1.CounterTag, { borderless: true }, rowData.groupCount)));
116
+ }
117
+ return (react_1.default.createElement(SSRRow, { key: rowData.key }, rowData.items.map((item) => (react_1.default.createElement(CardWrapper, { key: item.link },
118
+ react_1.default.createElement(CatalogCard_1.CatalogCard, { item: item }))))));
119
+ }),
120
+ react_1.default.createElement(LoadingWrapper, null,
121
+ react_1.default.createElement(SpinnerLoader_1.SpinnerLoader, { color: "var(--catalog-description-text-color)", size: "20px" }))));
122
+ }
123
+ return (react_1.default.createElement("div", { ref: parentRef, "data-component-name": "Catalog/CatalogVirtualizedGroups" },
124
+ react_1.default.createElement("div", { style: {
125
+ position: 'relative',
126
+ height: `${virtualizer.getTotalSize()}px`,
127
+ } }, virtualizer.getVirtualItems().map((virtualRow) => {
128
+ const rowData = flatRows[virtualRow.index];
129
+ if (rowData.type === 'header') {
130
+ return (react_1.default.createElement(HeaderRow, { key: rowData.key, ref: virtualizer.measureElement, "data-index": virtualRow.index, style: { transform: `translateY(${virtualRow.start}px)` } },
131
+ react_1.default.createElement(CatalogSeparatorLabel, null, rowData.groupTitle),
132
+ react_1.default.createElement(CounterTag_1.CounterTag, { borderless: true }, rowData.groupCount)));
133
+ }
134
+ return (react_1.default.createElement(GridRow, { key: rowData.key, ref: virtualizer.measureElement, "data-index": virtualRow.index, style: { transform: `translateY(${virtualRow.start}px)` } }, rowData.items.map((item) => (react_1.default.createElement(CatalogCard_1.CatalogCard, { key: item.link, item: item })))));
135
+ }))));
136
+ }
137
+ const SSRHeaderRow = styled_components_1.default.div `
138
+ width: 100%;
139
+ display: flex;
140
+ align-items: center;
141
+ padding: var(--catalog-separator-padding);
142
+ border-top: 1px solid var(--catalog-separator-border-color);
143
+ padding-bottom: calc(4px * 4);
144
+ color: var(--catalog-separator-color);
145
+ font-size: var(--catalog-separator-font-size);
146
+ font-weight: var(--catalog-separator-font-weight);
147
+ `;
148
+ const SSRRow = styled_components_1.default.div `
149
+ width: 100%;
150
+ display: flex;
151
+ flex-wrap: wrap;
152
+ gap: var(--catalog-cards-group-gap, 32px);
153
+ padding-bottom: var(--catalog-cards-group-gap, 32px);
154
+ `;
155
+ const HeaderRow = (0, styled_components_1.default)(SSRHeaderRow) `
156
+ position: absolute;
157
+ left: 0;
158
+ will-change: transform;
159
+ `;
160
+ const GridRow = styled_components_1.default.div `
161
+ position: absolute;
162
+ left: 0;
163
+ width: 100%;
164
+ display: grid;
165
+ grid-template-columns: repeat(auto-fill, minmax(var(--catalog-card-min-width), 1fr));
166
+ gap: var(--catalog-cards-group-gap, 32px);
167
+ padding-bottom: var(--catalog-cards-group-gap, 32px);
168
+ will-change: transform;
169
+ `;
170
+ const CardWrapper = styled_components_1.default.div `
171
+ flex: 1 0 var(--catalog-card-min-width);
172
+ max-width: 100%;
173
+ min-width: var(--catalog-card-min-width);
174
+ `;
175
+ const CatalogSeparatorLabel = styled_components_1.default.div `
176
+ margin: var(--catalog-separator-label-margin);
177
+ `;
178
+ const LoadingWrapper = styled_components_1.default.div `
179
+ display: flex;
180
+ justify-content: center;
181
+ align-items: center;
182
+ `;
183
+ //# sourceMappingURL=CatalogVirtualizedGroups.js.map
@@ -47,6 +47,7 @@ exports.catalog = (0, styled_components_1.css) `
47
47
  * @tokens Catalog card
48
48
  */
49
49
  --catalog-card-min-height: 194px;
50
+ --catalog-card-min-width: 260px;
50
51
  --catalog-card-padding-vertical: var(--spacing-base);
51
52
  --catalog-card-padding-horizontal: var(--spacing-md);
52
53
  --catalog-card-gap: var(--spacing-sm);
@@ -60,6 +61,7 @@ exports.catalog = (0, styled_components_1.css) `
60
61
  --catalog-card-border-color-hover: var(--border-color-primary);
61
62
  --catalog-card-border-radius: var(--border-radius-xxl);
62
63
  --catalog-cards-group-margin: 0 0 var(--spacing-base) 0;
64
+ --catalog-cards-group-gap: var(--spacing-xl);
63
65
 
64
66
  /**
65
67
  * @tokens Catalog card title
@@ -79,6 +81,7 @@ exports.catalog = (0, styled_components_1.css) `
79
81
  --catalog-card-description-font-size: var(--font-size-base);
80
82
  --catalog-card-description-line-height: var(--line-height-base);
81
83
  --catalog-card-description-font-weight: var(--font-weight-regular);
84
+ --catalog-card-description-height: 62px;
82
85
 
83
86
  --catalog-card-content-gap: var(--spacing-xxs);
84
87
 
@@ -56,7 +56,7 @@ function Dropdown({ children, className, active, trigger, triggerEvent = 'click'
56
56
  };
57
57
  (0, hooks_1.useOutsideClick)(dropdownRef, handleClose);
58
58
  const triggerChild = react_1.default.Children.only(trigger);
59
- const dropdownTrigger = (0, react_1.cloneElement)(triggerChild, Object.assign(Object.assign(Object.assign(Object.assign({ onClick: triggerEvent === 'click' ? handleToggle : undefined, icon: withArrow ? isOpen ? react_1.default.createElement(ChevronUpIcon_1.ChevronUpIcon, null) : react_1.default.createElement(ChevronDownIcon_1.ChevronDownIcon, null) : undefined }, (withArrow ? { iconPosition: 'right' } : {})), { tabIndex: 0 }), triggerChild.props), { onKeyDown: triggerEvent === 'click' ? handleKeyDown : undefined }));
59
+ const dropdownTrigger = (0, react_1.cloneElement)(triggerChild, Object.assign(Object.assign(Object.assign({ onClick: triggerEvent === 'click' ? handleToggle : undefined, icon: withArrow ? isOpen ? react_1.default.createElement(ChevronUpIcon_1.ChevronUpIcon, null) : react_1.default.createElement(ChevronDownIcon_1.ChevronDownIcon, null) : undefined }, (withArrow ? { iconPosition: 'right' } : {})), triggerChild.props), { onKeyDown: triggerEvent === 'click' ? handleKeyDown : undefined }));
60
60
  return (react_1.default.createElement(DropdownWrapper, Object.assign({ "data-component-name": "Dropdown/Dropdown", "data-testid": "dropdown" }, dataAttributes, { className: className, ref: dropdownRef, onPointerEnter: triggerEvent === 'hover' ? handleOpen : undefined, onPointerLeave: triggerEvent === 'hover' ? handleClose : undefined, onClick: onClick }),
61
61
  dropdownTrigger,
62
62
  react_1.default.createElement(ChildrenWrapper, { placement: placement, alignment: alignment, isOpen: isOpen, onClick: closeOnClick ? handleChildClick : undefined }, children)));
@@ -46,9 +46,9 @@ function Link(props) {
46
46
  return React.createElement(LinkComponent, Object.assign({}, props));
47
47
  }
48
48
  else {
49
- const { active: _, httpVerb: _1, hasActiveSubItem: _2, routeSlug: _3, external: _4 } = props, filteredProps = __rest(props, ["active", "httpVerb", "hasActiveSubItem", "routeSlug", "external"]);
49
+ const { active: _, httpVerb: _1, hasActiveSubItem: _2, routeSlug: _3, external: _4, innerRef } = props, filteredProps = __rest(props, ["active", "httpVerb", "hasActiveSubItem", "routeSlug", "external", "innerRef"]);
50
50
  // We omit "active" property to avoid "Warning: Received `false` for a non-boolean attribute `active`."
51
- return React.createElement(react_router_dom_1.Link, Object.assign({ ref: filteredProps.innerRef }, filteredProps));
51
+ return React.createElement(react_router_dom_1.Link, Object.assign({ ref: innerRef }, filteredProps));
52
52
  }
53
53
  }
54
54
  //# sourceMappingURL=Link.js.map
@@ -62,7 +62,7 @@ function MenuItem(props) {
62
62
  };
63
63
  const chevron = hasChevron ? (isExpanded ? (react_1.default.createElement(ChevronDownIcon_1.ChevronDownIcon, { size: "var(--menu-item-label-chevron-size)", color: "--tree-content-color-default" })) : (react_1.default.createElement(ChevronRightIcon_1.ChevronRightIcon, { size: "var(--menu-item-label-chevron-size)", color: "--tree-content-color-default" }))) : null;
64
64
  const httpColor = item.deprecated ? 'http-deprecated' : item.httpVerb;
65
- const label = item.label && (react_1.default.createElement(MenuItemLabelWrapper, { active: item.active, deprecated: item.deprecated, depth: depth, withChevron: hasChevron, onClick: handleOnClick, ref: labelRef, role: item.link ? 'none' : 'link' },
65
+ const label = item.label && (react_1.default.createElement(MenuItemLabelWrapper, { active: item.active, deprecated: item.deprecated, depth: depth, withChevron: hasChevron, onClick: handleOnClick, ref: labelRef, role: item.link ? 'none' : 'link', "data-testid": "menu-item-label" },
66
66
  chevron,
67
67
  item.icon ? react_1.default.createElement(MenuItemIcon, { src: item.icon }) : null,
68
68
  react_1.default.createElement(MenuItemLabelTextWrapper, null,
@@ -59,7 +59,10 @@ function NavbarItem({ navItem, className }) {
59
59
  const groupItemsComponents = groupItems.map((item, index) => {
60
60
  if (item.type !== 'link' && item.type !== 'separator')
61
61
  return null;
62
- return (react_1.default.createElement(DropdownMenuItem_1.DropdownMenuItem, { key: `${item.label}_${index}`, to: item.link, separator: item.type === 'separator', separatorLine: item.separatorLine, "data-translation-key": item.labelTranslationKey }, translate(item.labelTranslationKey, item.label)));
62
+ return (react_1.default.createElement(DropdownMenuItem_1.DropdownMenuItem, { key: `${item.label}_${index}`, to: item.link, separator: item.type === 'separator', separatorLine: item.separatorLine, "data-translation-key": item.labelTranslationKey },
63
+ react_1.default.createElement(NavbarIcon, { url: item.icon }),
64
+ react_1.default.createElement(NavbarLabel, { "data-translation-key": item.labelTranslationKey }, translate(item.labelTranslationKey, item.label)),
65
+ item.external ? react_1.default.createElement(ExternalLinkIcon, { size: "10px" }) : null));
63
66
  });
64
67
  return (react_1.default.createElement("div", { "data-component-name": "Navbar/NavbarItem" },
65
68
  react_1.default.createElement(NavbarMenuItemDropdown, { trigger: itemContent, triggerEvent: "hover" },
@@ -11,11 +11,18 @@ function SearchFilterFieldTags({ className, facet, selectedValues, onChange, })
11
11
  return (react_1.default.createElement(FilterTagsWrapper, { "data-component-name": "Search/FilterFields/SearchFilterFieldTags", className: className }, facet.values.map((facetCount, index) => {
12
12
  const { value, count, isCounterVisible } = facetCount;
13
13
  const active = selectedValues.includes(value);
14
- return (react_1.default.createElement(FilterTagWrapper, { key: `${count}-${index}`, color: value, onClick: () => {
15
- const values = active
16
- ? selectedValues.filter((item) => item !== value)
17
- : [...selectedValues, value];
18
- onChange(values);
14
+ const updateSelectedValues = () => {
15
+ const values = active
16
+ ? selectedValues.filter((item) => item !== value)
17
+ : [...selectedValues, value];
18
+ onChange(values);
19
+ };
20
+ return (react_1.default.createElement(FilterTagWrapper, { key: `${count}-${index}`, tabIndex: 0, color: value, onClick: () => {
21
+ updateSelectedValues();
22
+ }, onKeyDown: (e) => {
23
+ if (e.key === 'Enter') {
24
+ updateSelectedValues();
25
+ }
19
26
  }, active: active, borderless: true },
20
27
  value,
21
28
  " ",
@@ -24,9 +24,11 @@ function SearchAiMessage({ role, content, isThinking, resources, className, }) {
24
24
  react_1.default.createElement(ResponseText, { as: "div", children: markDownContent, "data-testid": "response-text" }),
25
25
  !isThinking && resources && resources.length > 0 && (react_1.default.createElement(ResourcesWrapper, { "data-testid": "resources-wrapper" },
26
26
  react_1.default.createElement(ResourcesTitle, { "data-translation-key": "search.ai.resourcesFound" },
27
- resources.length,
27
+ translate('search.ai.resourcesFound.basedOn', 'Based on'),
28
28
  " ",
29
- translate('search.ai.resourcesFound', 'resources found')),
29
+ resources.length,
30
+ ' ',
31
+ translate('search.ai.resourcesFound.resources', 'resources')),
30
32
  react_1.default.createElement(ResourceTagsWrapper, null, resources.map((resource, idx) => (react_1.default.createElement(Link_1.Link, { key: idx, to: resource.url, target: "_blank" },
31
33
  react_1.default.createElement(ResourceTag, { borderless: true, icon: react_1.default.createElement(DocumentIcon_1.DocumentIcon, { color: "--search-ai-resource-tag-icon-color" }) }, resource.title))))))))) : (content),
32
34
  isThinking && content.length === 0 && (react_1.default.createElement(ThinkingDotsWrapper, { "data-testid": "thinking-dots-wrapper" },
@@ -93,7 +95,6 @@ const ResourceTag = (0, styled_components_1.default)(Tag_1.Tag) `
93
95
  --tag-color: var(--search-ai-resource-tag-text-color);
94
96
  max-width: 100%;
95
97
  overflow: hidden;
96
- white-space: nowrap;
97
98
  display: inline-block;
98
99
  }
99
100
  svg {
@@ -103,8 +104,8 @@ const ResourceTag = (0, styled_components_1.default)(Tag_1.Tag) `
103
104
  }
104
105
  > div {
105
106
  overflow: hidden;
106
- text-overflow: ellipsis;
107
- white-space: nowrap;
107
+ word-break: break-word;
108
+ white-space: normal;
108
109
  max-width: 100%;
109
110
  }
110
111
  `;
@@ -46,6 +46,7 @@ const SpinnerLoader_1 = require("../../components/Loaders/SpinnerLoader");
46
46
  const SearchAiDialog_1 = require("../../components/Search/SearchAiDialog");
47
47
  const SettingsIcon_1 = require("../../icons/SettingsIcon/SettingsIcon");
48
48
  const AiStarsIcon_1 = require("../../icons/AiStarsIcon/AiStarsIcon");
49
+ const ReturnKeyIcon_1 = require("../../icons/ReturnKeyIcon/ReturnKeyIcon");
49
50
  const ChevronLeftIcon_1 = require("../../icons/ChevronLeftIcon/ChevronLeftIcon");
50
51
  const EditIcon_1 = require("../../icons/EditIcon/EditIcon");
51
52
  function SearchDialog({ onClose, className }) {
@@ -59,14 +60,25 @@ function SearchDialog({ onClose, className }) {
59
60
  const { query, setQuery, filter, setFilter, items, isSearchLoading, facets, setLoadMore, advancedSearch, askAi, groupField, } = useSearch(product === null || product === void 0 ? void 0 : product.name, autoSearchDisabled);
60
61
  const { isFilterOpen, onFilterToggle, onFilterChange, onFilterReset, onFacetReset, onQuickFilterReset, } = (0, hooks_1.useSearchFilter)(filter, setFilter);
61
62
  const aiSearch = useAiSearch({ filter });
63
+ const searchInputRef = (0, react_1.useRef)(null);
62
64
  const modalRef = (0, react_1.useRef)(null);
65
+ const aiQueryRef = (0, react_1.useRef)(null);
66
+ const firstSearchResultRef = (0, react_1.useRef)(null);
67
+ const searchKeysWithResults = items ? Object.keys(items).filter((key) => { var _a; return (_a = items[key]) === null || _a === void 0 ? void 0 : _a.length; }) : [];
63
68
  const { translate } = useTranslate();
64
69
  (0, hooks_1.useDialogHotKeys)(modalRef, onClose);
70
+ const focusSearchInput = () => {
71
+ requestAnimationFrame(() => {
72
+ var _a;
73
+ (_a = searchInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
74
+ });
75
+ };
65
76
  (0, react_1.useEffect)(() => {
66
77
  if (mode === 'ai-dialog' && aiSearch.isGeneratingResponse) {
67
78
  setQuery('');
68
79
  }
69
80
  }, [mode, aiSearch.isGeneratingResponse, setQuery]);
81
+ (0, react_1.useEffect)(focusSearchInput, []);
70
82
  const handleOverlayClick = (event) => {
71
83
  var _a;
72
84
  const target = event.target;
@@ -76,7 +88,7 @@ function SearchDialog({ onClose, className }) {
76
88
  onClose();
77
89
  }
78
90
  };
79
- const mapItem = (item, index, results) => {
91
+ const mapItem = (item, index, results, innerRef) => {
80
92
  var _a;
81
93
  let itemProduct;
82
94
  if (!product && item.document.product) {
@@ -86,7 +98,7 @@ function SearchDialog({ onClose, className }) {
86
98
  ? { name: resolvedProduct.name, icon: resolvedProduct.icon }
87
99
  : undefined;
88
100
  }
89
- return (react_1.default.createElement(SearchItem_1.SearchItem, { key: `${index}-${item.document.id}`, item: item, product: itemProduct, onClick: () => {
101
+ return (react_1.default.createElement(SearchItem_1.SearchItem, { key: `${index}-${item.document.id}`, item: item, product: itemProduct, innerRef: innerRef, onClick: () => {
90
102
  otelTelemetry.send('search.result.clicked', {
91
103
  query,
92
104
  url: item.document.url,
@@ -123,12 +135,17 @@ function SearchDialog({ onClose, className }) {
123
135
  product.name,
124
136
  react_1.default.createElement(CloseIcon_1.CloseIcon, { onClick: () => setProduct(undefined), color: "--icon-color-additional" }))),
125
137
  mode === 'search' ? (react_1.default.createElement(react_1.default.Fragment, null,
126
- react_1.default.createElement(SearchInput_1.SearchInput, { value: query, onChange: setQuery, placeholder: translate('search.label', 'Search docs...'), isLoading: isSearchLoading, onSubmit: showAiSearchButton
127
- ? () => {
128
- setMode('ai-dialog');
129
- aiSearch.askQuestion(query);
138
+ react_1.default.createElement(SearchInput_1.SearchInput, { value: query, onChange: setQuery, placeholder: translate('search.label', 'Search docs...'), isLoading: isSearchLoading, inputRef: searchInputRef, onSubmit: () => {
139
+ var _a;
140
+ if (isSearchLoading)
141
+ return;
142
+ if (showAiSearchButton && aiQueryRef.current) {
143
+ aiQueryRef.current.focus();
130
144
  }
131
- : undefined, "data-translation-key": "search.label" }),
145
+ else {
146
+ (_a = firstSearchResultRef.current) === null || _a === void 0 ? void 0 : _a.focus();
147
+ }
148
+ }, "data-translation-key": "search.label" }),
132
149
  showHeaderButtons && (react_1.default.createElement(SearchHeaderButtons, null,
133
150
  showAiSearchButton ? (react_1.default.createElement(SearchAiButton, { icon: react_1.default.createElement(AiStarsIcon_1.AiStarsIcon, { gradient: true }), onClick: () => {
134
151
  setMode('ai-dialog');
@@ -140,9 +157,12 @@ function SearchDialog({ onClose, className }) {
140
157
  react_1.default.createElement(Button_1.Button, { variant: "secondary", onClick: () => {
141
158
  setMode('search');
142
159
  aiSearch.clearConversation();
143
- }, icon: react_1.default.createElement(ChevronLeftIcon_1.ChevronLeftIcon, null) }, translate('search.ai.backToSearch', 'Back to search')),
144
- react_1.default.createElement(Button_1.Button, { variant: "secondary", disabled: !aiSearch.conversation.length, onClick: () => aiSearch.clearConversation(), icon: react_1.default.createElement(EditIcon_1.EditIcon, null) }, translate('search.ai.newConversation', 'New conversation'))))),
160
+ focusSearchInput();
161
+ }, tabIndex: 0, icon: react_1.default.createElement(ChevronLeftIcon_1.ChevronLeftIcon, null) }, translate('search.ai.backToSearch', 'Back to search')),
162
+ react_1.default.createElement(Button_1.Button, { variant: "secondary", disabled: !aiSearch.conversation.length, onClick: () => aiSearch.clearConversation(), tabIndex: 0, icon: react_1.default.createElement(EditIcon_1.EditIcon, null) }, translate('search.ai.newConversation', 'New conversation'))))),
145
163
  react_1.default.createElement(SearchDialogBody, null, mode === 'search' ? (react_1.default.createElement(react_1.default.Fragment, null,
164
+ advancedSearch && isFilterOpen && (react_1.default.createElement(SearchDialogBodyFilterView, null,
165
+ react_1.default.createElement(SearchFilter_1.SearchFilter, { facets: facets, filter: filter, query: query, quickFilterFields: [groupField], onFilterChange: onFilterChange, onFilterReset: onFilterReset, onFacetReset: onFacetReset }))),
146
166
  react_1.default.createElement(SearchDialogBodyMainView, null,
147
167
  react_1.default.createElement(SearchGroups_1.SearchGroups, { facets: facets, searchFilter: filter, onFilterChange: onFilterChange, onQuickFilterReset: onQuickFilterReset, groupField: groupField }),
148
168
  showAiSearchItem && (react_1.default.createElement(SearchWithAI, { onClick: () => {
@@ -150,27 +170,48 @@ function SearchDialog({ onClose, className }) {
150
170
  if (query.trim()) {
151
171
  aiSearch.askQuestion(query);
152
172
  }
153
- }, tabIndex: 0, role: "option", "aria-selected": "true" },
173
+ }, onKeyDown: (e) => {
174
+ if (e.key === 'Enter') {
175
+ setMode('ai-dialog');
176
+ if (query.trim()) {
177
+ aiSearch.askQuestion(query);
178
+ }
179
+ }
180
+ }, ref: aiQueryRef, tabIndex: 0, role: "option", "aria-selected": "true" },
154
181
  react_1.default.createElement(AiStarsIcon_1.AiStarsIcon, { color: "var(--search-ai-icon-color)", size: "36px", background: "var(--search-ai-icon-bg-color)", margin: "0 var(--spacing-md) 0 0", borderRadius: "var(--border-radius-lg)" }),
155
182
  react_1.default.createElement(Typography_1.Typography, { fontWeight: "var(--font-weight-semibold)" }, query),
156
183
  react_1.default.createElement(Typography_1.Typography, null,
157
184
  "- ",
158
185
  translate('search.ai.label', 'Ask AI assistant')),
159
- react_1.default.createElement(ReturnKey, null, "\u23CE"))),
160
- showResults ? (items && Object.keys(items).some((key) => { var _a; return (_a = items[key]) === null || _a === void 0 ? void 0 : _a.length; }) ? (Object.keys(items).map((key) => {
161
- var _a, _b, _c;
162
- return ((_a = items[key]) === null || _a === void 0 ? void 0 : _a.length) ? (react_1.default.createElement(react_1.Fragment, { key: key },
163
- react_1.default.createElement(SearchGroupTitle, { "data-testid": "search-group-title" }, key), (_b = items[key]) === null || _b === void 0 ? void 0 :
164
- _b.map(mapItem),
165
- showLoadMore(key, ((_c = items[key]) === null || _c === void 0 ? void 0 : _c.length) || 0) && (react_1.default.createElement(SearchGroupFooter, { "data-translation-key": "search.showMore", onClick: () => { var _a; return setLoadMore({ groupKey: key, offset: ((_a = items[key]) === null || _a === void 0 ? void 0 : _a.length) || 0 }); } }, translate('search.showMore', 'Show more'))))) : null;
166
- })) : isSearchLoading ? (react_1.default.createElement(SearchMessage, null,
186
+ react_1.default.createElement(ReturnKeyIcon_1.ReturnKeyIcon, { color: "var(--search-item-text-color)" }))),
187
+ showResults ? (searchKeysWithResults.length ? (searchKeysWithResults.map((key, searchGroupKeyIdx) => {
188
+ const searchResultItems = items[key];
189
+ if (searchResultItems === null || searchResultItems === void 0 ? void 0 : searchResultItems.length) {
190
+ return (react_1.default.createElement(react_1.Fragment, { key: key },
191
+ react_1.default.createElement(SearchGroupTitle, { "data-testid": "search-group-title" }, key),
192
+ searchResultItems.map((item, idx, resultsArr) => mapItem(item, idx, resultsArr, searchGroupKeyIdx === 0 ? firstSearchResultRef : undefined)),
193
+ showLoadMore(key, searchResultItems.length || 0) && (react_1.default.createElement(SearchGroupFooter, { tabIndex: 0, "data-translation-key": "search.showMore", onKeyDown: (e) => {
194
+ if (e.key === 'Enter') {
195
+ setLoadMore({
196
+ groupKey: key,
197
+ offset: searchResultItems.length || 0,
198
+ });
199
+ }
200
+ }, onClick: () => setLoadMore({
201
+ groupKey: key,
202
+ offset: searchResultItems.length || 0,
203
+ }) }, translate('search.showMore', 'Show more')))));
204
+ }
205
+ return null;
206
+ })) : isSearchLoading ? (react_1.default.createElement(SearchMessage, { "data-translation-key": "search.loading" },
167
207
  react_1.default.createElement(SpinnerLoader_1.SpinnerLoader, { size: "26px", color: "var(--search-input-icon-color)" }),
168
208
  translate('search.loading', 'Loading...'))) : (react_1.default.createElement(SearchMessage, { "data-translation-key": "search.noResults" },
169
209
  react_1.default.createElement("b", null, translate('search.noResults.title', 'No results'))))) : (react_1.default.createElement(react_1.default.Fragment, null,
170
- react_1.default.createElement(SearchRecent_1.SearchRecent, { onSelect: setQuery }),
171
- react_1.default.createElement(SearchSuggestedPages_1.SearchSuggestedPages, null)))),
172
- advancedSearch && mode === 'search' && isFilterOpen && (react_1.default.createElement(SearchDialogBodyFilterView, null,
173
- react_1.default.createElement(SearchFilter_1.SearchFilter, { facets: facets, filter: filter, query: query, quickFilterFields: [groupField], onFilterChange: onFilterChange, onFilterReset: onFilterReset, onFacetReset: onFacetReset }))))) : (react_1.default.createElement(SearchAiDialog_1.SearchAiDialog, { initialMessage: query, response: aiSearch.response, isGeneratingResponse: aiSearch.isGeneratingResponse, error: aiSearch.error, resources: aiSearch.resources, conversation: aiSearch.conversation, setConversation: aiSearch.setConversation, onMessageSent: aiSearch.askQuestion }))),
210
+ react_1.default.createElement(SearchRecent_1.SearchRecent, { onSelect: (query) => {
211
+ setQuery(query);
212
+ focusSearchInput();
213
+ } }),
214
+ react_1.default.createElement(SearchSuggestedPages_1.SearchSuggestedPages, null)))))) : (react_1.default.createElement(SearchAiDialog_1.SearchAiDialog, { initialMessage: query, response: aiSearch.response, isGeneratingResponse: aiSearch.isGeneratingResponse, error: aiSearch.error, resources: aiSearch.resources, conversation: aiSearch.conversation, setConversation: aiSearch.setConversation, onMessageSent: aiSearch.askQuestion }))),
174
215
  react_1.default.createElement(SearchDialogFooter, null, mode === 'ai-dialog' ? (react_1.default.createElement(AiDisclaimer, null, translate('search.ai.disclaimer', 'AI search might provide incomplete or incorrect results. Verify important information.'))) : (react_1.default.createElement(react_1.default.Fragment, null,
175
216
  react_1.default.createElement(SearchShortcuts, null,
176
217
  react_1.default.createElement(SearchShortcut_1.SearchShortcut, { "data-translation-key": "search.keys.navigate", combination: "Tab", text: translate('search.keys.navigate', 'to navigate') }),
@@ -243,7 +284,7 @@ const AiDialogHeaderWrapper = styled_components_1.default.div `
243
284
  `;
244
285
  const SearchDialogBody = styled_components_1.default.div `
245
286
  display: flex;
246
- flex-direction: row;
287
+ flex-direction: row-reverse;
247
288
  flex: 1;
248
289
  min-height: 0;
249
290
  overflow: hidden;
@@ -354,22 +395,32 @@ const SearchWithAI = styled_components_1.default.div `
354
395
  text-decoration: none;
355
396
  white-space: normal;
356
397
  outline: none;
357
- border: none;
398
+ border-top: 1px solid var(--search-item-border-color);
399
+ border-bottom: 1px solid var(--search-item-border-color);
358
400
 
359
401
  transition: all 0.3s ease;
360
402
 
403
+ ${ReturnKeyIcon_1.ReturnKeyIcon} {
404
+ opacity: 0;
405
+ }
406
+
361
407
  &:focus,
362
408
  &:hover {
363
409
  color: var(--search-item-text-color-hover);
364
410
  background-color: var(--search-item-bg-color-hover);
411
+
412
+ ${ReturnKeyIcon_1.ReturnKeyIcon} {
413
+ opacity: 1;
414
+ }
415
+ }
416
+
417
+ &:focus {
418
+ border-top: 1px solid var(--search-item-border-color-focused);
419
+ border-bottom: 1px solid var(--search-item-border-color-focused);
365
420
  }
366
421
 
367
422
  & > :first-child {
368
423
  margin-right: var(--spacing-xs);
369
424
  }
370
425
  `;
371
- const ReturnKey = (0, styled_components_1.default)(Typography_1.Typography) `
372
- color: var(--search-item-text-color);
373
- margin-left: auto;
374
- `;
375
426
  //# sourceMappingURL=SearchDialog.js.map