@redocly/theme 0.59.0-next.1 → 0.59.0-next.3

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 (153) hide show
  1. package/LICENSE +7 -1
  2. package/lib/components/Accordion/Accordion.js +17 -7
  3. package/lib/components/Accordion/AccordionBody.js +17 -7
  4. package/lib/components/Admonition/Admonition.js +17 -7
  5. package/lib/components/Badge/Badge.js +17 -7
  6. package/lib/components/Breadcrumbs/Breadcrumb.js +17 -7
  7. package/lib/components/Breadcrumbs/BreadcrumbDropdown.js +17 -7
  8. package/lib/components/Button/Button.js +17 -7
  9. package/lib/components/Buttons/AIAssistantButton.js +17 -7
  10. package/lib/components/Buttons/CopyButton.js +17 -7
  11. package/lib/components/Catalog/Catalog.d.ts +6 -0
  12. package/lib/components/Catalog/Catalog.js +7 -6
  13. package/lib/components/Catalog/CatalogEntities.js +17 -7
  14. package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsGraph.js +17 -7
  15. package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsGraph.lazy.js +17 -7
  16. package/lib/components/Catalog/CatalogEntity/CatalogEntityMetadata.js +17 -7
  17. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityApiDescriptionRelations.js +1 -1
  18. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityRelations.js +17 -7
  19. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityTeamRelations.js +1 -1
  20. package/lib/components/Catalog/CatalogEntity/CatalogEntitySchema.js +17 -7
  21. package/lib/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.js +17 -7
  22. package/lib/components/Catalog/CatalogFilter/CatalogFilterContent.js +17 -7
  23. package/lib/components/Catalog/CatalogFilter/CatalogFilterDateRange.js +17 -7
  24. package/lib/components/Catalog/CatalogFilter/CatalogFilterSelect.js +17 -7
  25. package/lib/components/Catalog/CatalogSortButton.js +17 -7
  26. package/lib/components/Catalog/CatalogTableView/CatalogTableHeaderCell.js +17 -7
  27. package/lib/components/Catalog/CatalogViewModeToggle.js +17 -7
  28. package/lib/components/CatalogClassic/CatalogClassicActions.js +17 -7
  29. package/lib/components/CatalogClassic/CatalogClassicCard.js +17 -7
  30. package/lib/components/CatalogClassic/CatalogClassicHighlight.js +17 -7
  31. package/lib/components/CatalogClassic/CatalogClassicVirtualizedGroups.js +17 -7
  32. package/lib/components/CodeBlock/CodeBlock.js +17 -7
  33. package/lib/components/CodeBlock/CodeBlockContainer.js +17 -7
  34. package/lib/components/CodeBlock/CodeBlockTabs.js +17 -7
  35. package/lib/components/Dropdown/Dropdown.d.ts +16 -2
  36. package/lib/components/Dropdown/Dropdown.js +22 -12
  37. package/lib/components/Dropdown/DropdownMenuItem.js +17 -7
  38. package/lib/components/Feedback/Comment.js +17 -7
  39. package/lib/components/Feedback/Feedback.js +17 -7
  40. package/lib/components/Feedback/Mood.js +17 -7
  41. package/lib/components/Feedback/Rating.js +17 -7
  42. package/lib/components/Feedback/Reasons.js +17 -7
  43. package/lib/components/Feedback/Scale.js +17 -7
  44. package/lib/components/Feedback/Sentiment.js +17 -7
  45. package/lib/components/Feedback/Stars.js +17 -7
  46. package/lib/components/Filter/FilterContent.js +17 -7
  47. package/lib/components/Filter/FilterInput.js +17 -7
  48. package/lib/components/Image/Image.js +17 -7
  49. package/lib/components/JsonViewer/JsonViewer.js +17 -7
  50. package/lib/components/JsonViewer/helpers.js +17 -7
  51. package/lib/components/LastUpdated/LastUpdated.js +17 -7
  52. package/lib/components/Link/Link.js +17 -7
  53. package/lib/components/Markdown/Markdown.js +17 -7
  54. package/lib/components/Marker/Marker.js +17 -7
  55. package/lib/components/Menu/MenuContainer.js +17 -7
  56. package/lib/components/Menu/MenuItem.js +18 -8
  57. package/lib/components/Menu/MenuMobile.js +17 -7
  58. package/lib/components/Navbar/NavbarItem.js +3 -3
  59. package/lib/components/PageActions/PageActions.js +17 -7
  60. package/lib/components/PageNavigation/NextButton.js +17 -7
  61. package/lib/components/Panel/Panel.js +17 -7
  62. package/lib/components/Panel/PanelBody.js +17 -7
  63. package/lib/components/Search/FilterFields/SearchFilterFieldSelect.js +17 -7
  64. package/lib/components/Search/FilterFields/SearchFilterFieldTags.js +1 -2
  65. package/lib/components/Search/SearchAiConversationInput.d.ts +2 -1
  66. package/lib/components/Search/SearchAiConversationInput.js +28 -10
  67. package/lib/components/Search/SearchAiDialog.js +17 -7
  68. package/lib/components/Search/SearchDialog.js +23 -10
  69. package/lib/components/Search/SearchFilter.js +17 -7
  70. package/lib/components/Search/SearchGroups.js +19 -9
  71. package/lib/components/Search/SearchHighlight.js +17 -7
  72. package/lib/components/Search/SearchItem.js +17 -7
  73. package/lib/components/Search/SearchRecent.js +17 -7
  74. package/lib/components/Search/SearchShortcut.js +17 -7
  75. package/lib/components/Search/SearchSuggestedPages.js +17 -7
  76. package/lib/components/Search/SearchTrigger.js +17 -7
  77. package/lib/components/Search/variables.js +5 -1
  78. package/lib/components/Segmented/Segmented.js +17 -7
  79. package/lib/components/Select/Select.js +17 -7
  80. package/lib/components/Select/SelectInput.js +18 -8
  81. package/lib/components/Sidebar/Sidebar.js +17 -7
  82. package/lib/components/SidebarActions/styled.js +17 -7
  83. package/lib/components/SkipContent/SkipContent.js +17 -7
  84. package/lib/components/Switch/Switch.js +17 -7
  85. package/lib/components/TableOfContent/TableOfContent.js +17 -7
  86. package/lib/components/Tag/Tag.d.ts +2 -1
  87. package/lib/components/Tag/Tag.js +67 -18
  88. package/lib/components/Tag/variables.dark.js +135 -36
  89. package/lib/components/Tag/variables.js +78 -61
  90. package/lib/components/Tooltip/Tooltip.js +17 -7
  91. package/lib/components/VersionPicker/VersionPicker.js +17 -7
  92. package/lib/core/constants/search.d.ts +5 -4
  93. package/lib/core/constants/search.js +4 -5
  94. package/lib/core/contexts/CodeSnippetContext.js +17 -7
  95. package/lib/core/hooks/use-tabs.d.ts +3 -2
  96. package/lib/core/hooks/use-tabs.js +115 -57
  97. package/lib/core/templates/Markdown.js +17 -7
  98. package/lib/core/types/hooks.d.ts +6 -3
  99. package/lib/core/types/l10n.d.ts +1 -1
  100. package/lib/core/utils/download-code-walkthrough.js +17 -7
  101. package/lib/core/utils/get-file-icon.js +17 -7
  102. package/lib/icons/AiStarsIcon/AiStarsIcon.js +11 -2
  103. package/lib/icons/GenericIcon/GenericIcon.js +17 -7
  104. package/lib/icons/RedoclyIcon/RedoclyIcon.js +4 -7
  105. package/lib/icons/Spinner/Spinner.js +17 -7
  106. package/lib/index.js +17 -7
  107. package/lib/layouts/OIDCForbidden.js +17 -7
  108. package/lib/layouts/ThreePanelLayout.js +17 -7
  109. package/lib/markdoc/components/Cards/Cards.js +17 -7
  110. package/lib/markdoc/components/CodeGroup/CodeGroup.js +17 -7
  111. package/lib/markdoc/components/CodeWalkthrough/CodeContainer.js +17 -7
  112. package/lib/markdoc/components/CodeWalkthrough/CodePanel.js +17 -7
  113. package/lib/markdoc/components/CodeWalkthrough/CodePanelHeader.js +17 -7
  114. package/lib/markdoc/components/CodeWalkthrough/CodePanelPreview.js +17 -7
  115. package/lib/markdoc/components/CodeWalkthrough/CodePanelToolbar.js +17 -7
  116. package/lib/markdoc/components/CodeWalkthrough/CodeStep.js +17 -7
  117. package/lib/markdoc/components/CodeWalkthrough/CodeToggle.js +17 -7
  118. package/lib/markdoc/components/CodeWalkthrough/CodeWalkthrough.js +17 -7
  119. package/lib/markdoc/components/CodeWalkthrough/Input.js +17 -7
  120. package/lib/markdoc/components/Heading/Heading.js +17 -7
  121. package/lib/markdoc/components/HtmlBlock/HtmlBlock.js +17 -7
  122. package/lib/markdoc/components/InlineSvg/InlineSvg.js +17 -7
  123. package/lib/markdoc/components/MarkdocExample/MarkdocExample.js +17 -7
  124. package/lib/markdoc/components/Tabs/TabList.d.ts +3 -1
  125. package/lib/markdoc/components/Tabs/TabList.js +214 -54
  126. package/lib/markdoc/components/Tabs/Tabs.d.ts +2 -1
  127. package/lib/markdoc/components/Tabs/Tabs.js +74 -19
  128. package/lib/markdoc/default.d.ts +104 -1
  129. package/lib/markdoc/default.js +17 -7
  130. package/package.json +6 -6
  131. package/src/components/Catalog/Catalog.tsx +15 -4
  132. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityApiDescriptionRelations.tsx +1 -1
  133. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityTeamRelations.tsx +1 -1
  134. package/src/components/Dropdown/Dropdown.tsx +84 -79
  135. package/src/components/Menu/MenuItem.tsx +1 -0
  136. package/src/components/Navbar/NavbarItem.tsx +6 -5
  137. package/src/components/Search/FilterFields/SearchFilterFieldTags.tsx +3 -3
  138. package/src/components/Search/SearchAiConversationInput.tsx +12 -2
  139. package/src/components/Search/SearchDialog.tsx +6 -3
  140. package/src/components/Search/SearchGroups.tsx +2 -0
  141. package/src/components/Search/variables.ts +5 -1
  142. package/src/components/Select/SelectInput.tsx +1 -0
  143. package/src/components/Tag/Tag.tsx +36 -20
  144. package/src/components/Tag/variables.dark.ts +135 -36
  145. package/src/components/Tag/variables.ts +78 -61
  146. package/src/core/constants/search.ts +8 -4
  147. package/src/core/hooks/use-tabs.ts +168 -86
  148. package/src/core/types/hooks.ts +6 -1
  149. package/src/core/types/l10n.ts +1 -0
  150. package/src/icons/AiStarsIcon/AiStarsIcon.tsx +11 -2
  151. package/src/icons/RedoclyIcon/RedoclyIcon.tsx +4 -22
  152. package/src/markdoc/components/Tabs/TabList.tsx +312 -105
  153. package/src/markdoc/components/Tabs/Tabs.tsx +136 -11
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@redocly/theme",
3
- "version": "0.59.0-next.1",
3
+ "version": "0.59.0-next.3",
4
4
  "description": "Shared UI components lib",
5
5
  "keywords": [
6
6
  "theme",
7
7
  "redocly"
8
8
  ],
9
9
  "author": "team@redocly.com",
10
- "license": "SEE LICENSE IN LICENSE",
10
+ "license": "MIT",
11
11
  "main": "lib/index.js",
12
12
  "types": "lib/index.d.ts",
13
13
  "exports": {
@@ -63,10 +63,10 @@
63
63
  "styled-system": "5.1.5",
64
64
  "ts-node": "10.9.2",
65
65
  "ts-node-dev": "2.0.0",
66
- "tsc-alias": "1.8.10",
66
+ "tsc-alias": "1.8.16",
67
67
  "tsconfig-paths": "4.2.0",
68
68
  "tsconfig-paths-webpack-plugin": "3.5.2",
69
- "typescript": "5.6.2",
69
+ "typescript": "5.9.3",
70
70
  "vitest": "3.2.4",
71
71
  "vitest-when": "0.6.2",
72
72
  "webpack": "5.94.0"
@@ -84,10 +84,10 @@
84
84
  "lodash.debounce": "^4.0.8",
85
85
  "lodash.throttle": "4.1.1",
86
86
  "nprogress": "0.2.0",
87
- "openapi-sampler": "1.6.1",
87
+ "openapi-sampler": "1.6.2",
88
88
  "react-calendar": "5.1.0",
89
89
  "react-date-picker": "11.0.0",
90
- "@redocly/config": "0.35.1",
90
+ "@redocly/config": "0.36.0",
91
91
  "@redocly/realm-asyncapi-sdk": "0.5.0-next.0"
92
92
  },
93
93
  "scripts": {
@@ -20,8 +20,11 @@ import { CatalogViewModeToggle } from '@redocly/theme/components/Catalog/Catalog
20
20
  import { CatalogSortButton } from '@redocly/theme/components/Catalog/CatalogSortButton';
21
21
  import { CatalogEntities } from '@redocly/theme/components/Catalog/CatalogEntities';
22
22
 
23
+ type CatalogFiltersWithCounts = Record<string, { value: string; count: number }[]>;
24
+
23
25
  export type CatalogProps = {
24
26
  catalogConfig: CatalogEntityConfig;
27
+ filters?: CatalogFiltersWithCounts;
25
28
  entitiesTypes: string[];
26
29
  initialEntitiesList?: BffCatalogEntityList;
27
30
  catalogSwitcherItems: CatalogSwitcherItem[];
@@ -29,9 +32,11 @@ export type CatalogProps = {
29
32
  };
30
33
 
31
34
  const customCatalogOptionsCasing = (str: string): string => {
32
- if (!str) return str;
35
+ const trimmedStr = str.trim();
36
+ if (!trimmedStr) return trimmedStr;
37
+
38
+ const words = trimmedStr.split(/[\s-_]+/);
33
39
 
34
- const words = str.split(/[\s-_]+/);
35
40
  return words
36
41
  .map((word, index) => {
37
42
  if (index === 0 && word.toLowerCase() === 'api') {
@@ -45,6 +50,7 @@ const customCatalogOptionsCasing = (str: string): string => {
45
50
  export function Catalog(props: CatalogProps): JSX.Element {
46
51
  const {
47
52
  catalogConfig,
53
+ filters: serverFilters,
48
54
  entitiesTypes,
49
55
  initialEntitiesList,
50
56
  catalogSwitcherItems,
@@ -72,7 +78,12 @@ export function Catalog(props: CatalogProps): JSX.Element {
72
78
  onChangeCollapseSidebarClick,
73
79
  layout,
74
80
  collapsedSidebar,
75
- } = useCatalog(catalogConfig, initialEntitiesList?.page.total || 0, initialViewMode);
81
+ } = useCatalog(
82
+ catalogConfig,
83
+ serverFilters,
84
+ initialEntitiesList?.page.total || 0,
85
+ initialViewMode,
86
+ );
76
87
 
77
88
  return (
78
89
  <>
@@ -94,7 +105,7 @@ export function Catalog(props: CatalogProps): JSX.Element {
94
105
  filters={filters}
95
106
  filterTerm={searchQuery}
96
107
  hideSearch={true}
97
- showCounter={false}
108
+ showCounter={true}
98
109
  filterValuesCasing={customCatalogOptionsCasing}
99
110
  />
100
111
  }
@@ -46,7 +46,7 @@ export function CatalogEntityApiDescriptionRelations({
46
46
  }: CatalogEntityApiDescriptionRelationsProps): JSX.Element {
47
47
  return (
48
48
  <div data-component-name="Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityApiDescriptionRelations">
49
- <Tabs key={entity.id} size={TabsSize.MEDIUM}>
49
+ <Tabs key={entity.id} forceReady={relations.length > 0} size={TabsSize.MEDIUM}>
50
50
  <TabItem
51
51
  label="Operations"
52
52
  icon={<MoleculesIcon />}
@@ -74,7 +74,7 @@ export function CatalogEntityTeamRelations({
74
74
  }: CatalogEntityTeamRelationsProps): JSX.Element {
75
75
  return (
76
76
  <div data-component-name="Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityTeamRelations">
77
- <Tabs size={TabsSize.MEDIUM}>
77
+ <Tabs forceReady={relations.length > 0} size={TabsSize.MEDIUM}>
78
78
  <TabItem label="Members" icon={<PeopleIcon />} onClick={() => setFilter('type:user')}>
79
79
  <CatalogEntityRelationsTable
80
80
  key="members-table"
@@ -1,7 +1,7 @@
1
- import React, { cloneElement, useRef } from 'react';
1
+ import React, { cloneElement, useRef, forwardRef } from 'react';
2
2
  import styled from 'styled-components';
3
3
 
4
- import type { PropsWithChildren, ReactElement, JSX } from 'react';
4
+ import type { PropsWithChildren, ReactElement } from 'react';
5
5
 
6
6
  import { useOutsideClick, useControlledState } from '@redocly/theme/core/hooks';
7
7
  import { ChevronDownIcon } from '@redocly/theme/icons/ChevronDownIcon/ChevronDownIcon';
@@ -32,84 +32,89 @@ export type DropdownProps = PropsWithChildren<{
32
32
  onClose?: () => void;
33
33
  }>;
34
34
 
35
- export function Dropdown({
36
- children,
37
- className,
38
- active,
39
- trigger,
40
- triggerEvent = 'click',
41
- closeOnClick = true,
42
- withArrow,
43
- dataAttributes,
44
- placement,
45
- alignment,
46
- onClick,
47
- onClose,
48
- }: DropdownProps): JSX.Element {
49
- const dropdownRef = useRef<HTMLDivElement | null>(null);
50
- const [isOpen, setIsOpen] = useControlledState<boolean>(false, active);
51
-
52
- const handleOpen = () => {
53
- setIsOpen(true);
54
- };
55
-
56
- const handleClose = () => {
57
- setIsOpen(false);
58
- onClose?.();
59
- };
60
-
61
- const handleChildClick = () => {
62
- handleClose();
63
- };
64
-
65
- const handleToggle = (event: React.UIEvent) => {
66
- event.stopPropagation();
67
- event.preventDefault();
68
- setIsOpen(!isOpen);
69
- };
70
-
71
- const handleKeyDown = (event: React.KeyboardEvent) => {
72
- if (event.key === 'Enter' || event.key === ' ') {
73
- handleToggle(event);
74
- }
75
- };
76
-
77
- useOutsideClick(dropdownRef, handleClose);
78
-
79
- const triggerChild = React.Children.only(trigger) as ReactElement<TriggerProps>;
80
-
81
- const dropdownTrigger = cloneElement(triggerChild, {
82
- onClick: triggerEvent === 'click' ? handleToggle : undefined,
83
- icon: withArrow ? isOpen ? <ChevronUpIcon /> : <ChevronDownIcon /> : undefined,
84
- ...(withArrow ? { iconPosition: 'right' } : {}),
85
- ...triggerChild.props,
86
- onKeyDown: triggerEvent === 'click' ? handleKeyDown : undefined,
87
- });
88
-
89
- return (
90
- <DropdownWrapper
91
- data-component-name="Dropdown/Dropdown"
92
- data-testid="dropdown"
93
- {...dataAttributes}
94
- className={className}
95
- ref={dropdownRef}
96
- onPointerEnter={triggerEvent === 'hover' ? handleOpen : undefined}
97
- onPointerLeave={triggerEvent === 'hover' ? handleClose : undefined}
98
- onClick={onClick}
99
- >
100
- {dropdownTrigger}
101
-
102
- <ChildrenWrapper
103
- placement={placement}
104
- alignment={alignment}
105
- isOpen={isOpen}
106
- onClick={closeOnClick ? handleChildClick : undefined}
35
+ export const Dropdown = forwardRef<HTMLDivElement, DropdownProps>(
36
+ (
37
+ {
38
+ children,
39
+ className,
40
+ active,
41
+ trigger,
42
+ triggerEvent = 'click',
43
+ closeOnClick = true,
44
+ withArrow,
45
+ dataAttributes,
46
+ placement,
47
+ alignment,
48
+ onClick,
49
+ onClose,
50
+ },
51
+ ref,
52
+ ) => {
53
+ const dropdownRef = useRef<HTMLDivElement | null>(null);
54
+ const [isOpen, setIsOpen] = useControlledState<boolean>(false, active);
55
+
56
+ const handleOpen = () => {
57
+ setIsOpen(true);
58
+ };
59
+
60
+ const handleClose = () => {
61
+ setIsOpen(false);
62
+ onClose?.();
63
+ };
64
+
65
+ const handleChildClick = () => {
66
+ handleClose();
67
+ };
68
+
69
+ const handleToggle = (event: React.UIEvent) => {
70
+ event.stopPropagation();
71
+ event.preventDefault();
72
+ setIsOpen(!isOpen);
73
+ };
74
+
75
+ const handleKeyDown = (event: React.KeyboardEvent) => {
76
+ if (event.key === 'Enter' || event.key === ' ') {
77
+ handleToggle(event);
78
+ }
79
+ };
80
+
81
+ useOutsideClick((ref as React.RefObject<HTMLElement | null>) || dropdownRef, handleClose);
82
+
83
+ const triggerChild = React.Children.only(trigger) as ReactElement<TriggerProps>;
84
+
85
+ const dropdownTrigger = cloneElement(triggerChild, {
86
+ onClick: triggerEvent === 'click' ? handleToggle : undefined,
87
+ icon: withArrow ? isOpen ? <ChevronUpIcon /> : <ChevronDownIcon /> : undefined,
88
+ ...(withArrow ? { iconPosition: 'right' } : {}),
89
+ ...triggerChild.props,
90
+ onKeyDown: triggerEvent === 'click' ? handleKeyDown : undefined,
91
+ });
92
+
93
+ return (
94
+ <DropdownWrapper
95
+ data-component-name="Dropdown/Dropdown"
96
+ data-testid="dropdown"
97
+ {...dataAttributes}
98
+ className={className}
99
+ ref={ref || dropdownRef}
100
+ onPointerEnter={triggerEvent === 'hover' ? handleOpen : undefined}
101
+ onPointerLeave={triggerEvent === 'hover' ? handleClose : undefined}
102
+ onClick={onClick}
107
103
  >
108
- {children}
109
- </ChildrenWrapper>
110
- </DropdownWrapper>
111
- );
112
- }
104
+ {dropdownTrigger}
105
+
106
+ <ChildrenWrapper
107
+ placement={placement}
108
+ alignment={alignment}
109
+ isOpen={isOpen}
110
+ onClick={closeOnClick ? handleChildClick : undefined}
111
+ >
112
+ {children}
113
+ </ChildrenWrapper>
114
+ </DropdownWrapper>
115
+ );
116
+ },
117
+ );
113
118
 
114
119
  const DropdownWrapper = styled.div`
115
120
  --button-gap: var(--spacing-xxs);
@@ -82,6 +82,7 @@ export function MenuItem(props: React.PropsWithChildren<MenuItemProps>): JSX.Ele
82
82
  role={item.link ? 'none' : 'link'}
83
83
  tabIndex={!item.link ? 0 : undefined}
84
84
  data-testid="menu-item-label"
85
+ data-active={item.active}
85
86
  >
86
87
  {hasChevron ? <ChevronWrapper>{chevron}</ChevronWrapper> : null}
87
88
  <MenuItemIcon icon={item.icon} srcSet={item.srcSet} />
@@ -34,12 +34,13 @@ export function NavbarItem({ navItem, className }: NavbarItemProps): JSX.Element
34
34
  if (navItem.type !== 'link' && !navItem.items) return null;
35
35
 
36
36
  const item = navItem as ResolvedNavLinkItem;
37
- const normalizedPath =
38
- (item.link && item.link !== '/' ? removeTrailingSlash(item.link) : item.link) || '';
37
+ const normalizedPath = (item.link ? removeTrailingSlash(item.link) : item.link) || '';
39
38
 
40
- const isActive =
41
- pathname ===
42
- withPathPrefix(getPathnameForLocale(normalizedPath, defaultLocale, currentLocale, locales));
39
+ const pathWithPathPrefix = withPathPrefix(
40
+ getPathnameForLocale(normalizedPath, defaultLocale, currentLocale, locales),
41
+ );
42
+
43
+ const isActive = removeTrailingSlash(pathname) === removeTrailingSlash(pathWithPathPrefix);
43
44
 
44
45
  const itemContent = (
45
46
  <NavbarMenuItem
@@ -3,7 +3,7 @@ import styled from 'styled-components';
3
3
 
4
4
  import type { SearchFacet, SearchFacetCount } from '@redocly/theme/core/types';
5
5
 
6
- import { Tag, type TagProps } from '@redocly/theme/components/Tag/Tag';
6
+ import { Tag } from '@redocly/theme/components/Tag/Tag';
7
7
 
8
8
  type SearchFilterFieldTagsProps = {
9
9
  className?: string;
@@ -47,6 +47,7 @@ export function SearchFilterFieldTags({
47
47
  }}
48
48
  active={active}
49
49
  borderless
50
+ selectable
50
51
  >
51
52
  {value} {isCounterVisible && <span>{count}</span>}
52
53
  </FilterTagWrapper>
@@ -62,9 +63,8 @@ const FilterTagsWrapper = styled.div`
62
63
  gap: var(--search-filter-field-tags-gap);
63
64
  `;
64
65
 
65
- const FilterTagWrapper = styled(Tag)<{ color: TagProps['color'] }>`
66
+ const FilterTagWrapper = styled(Tag)`
66
67
  text-transform: uppercase;
67
68
  cursor: pointer;
68
- ${({ color }) => color && `background-color: var(--tag-operation-bg-color-${color});`}
69
69
  margin: var(--search-filter-field-tags-tag-margin);
70
70
  `;
@@ -13,6 +13,7 @@ type SearchAiConversationInputProps = {
13
13
  isGeneratingResponse: boolean;
14
14
  placeholder?: string;
15
15
  className?: string;
16
+ disabled?: boolean;
16
17
  };
17
18
 
18
19
  export function SearchAiConversationInput({
@@ -20,6 +21,7 @@ export function SearchAiConversationInput({
20
21
  onMessageSent,
21
22
  className,
22
23
  placeholder,
24
+ disabled,
23
25
  }: SearchAiConversationInputProps): JSX.Element {
24
26
  const { useTranslate } = useThemeHooks();
25
27
  const { translate } = useTranslate();
@@ -36,6 +38,8 @@ export function SearchAiConversationInput({
36
38
  }, [isGeneratingResponse]);
37
39
 
38
40
  const handleSendMessage = () => {
41
+ if (disabled) return;
42
+
39
43
  setQuery('');
40
44
  onMessageSent(query);
41
45
  };
@@ -46,7 +50,7 @@ export function SearchAiConversationInput({
46
50
  }
47
51
  };
48
52
 
49
- const isDisabled = isGeneratingResponse || query.trim().length === 0;
53
+ const isDisabled = disabled || isGeneratingResponse || query.trim().length === 0;
50
54
 
51
55
  return (
52
56
  <SearchAiConversationInputWrapper
@@ -62,7 +66,7 @@ export function SearchAiConversationInput({
62
66
  onChange={(e) => setQuery(e.target.value)}
63
67
  onKeyUp={handleOnKeyUp}
64
68
  value={query}
65
- disabled={isGeneratingResponse}
69
+ disabled={disabled || isGeneratingResponse}
66
70
  maxLength={AI_SEARCH_MAX_MESSAGE_LENGTH}
67
71
  />
68
72
 
@@ -111,6 +115,10 @@ const ConversationInput = styled.input`
111
115
  border-color: var(--search-ai-conversation-input-border-color-focus);
112
116
  }
113
117
 
118
+ &:disabled {
119
+ background-color: var(--search-ai-conversation-input-bg-color-disabled);
120
+ }
121
+
114
122
  &:focus:disabled {
115
123
  border-color: var(--search-ai-conversation-input-border-color-disabled);
116
124
  }
@@ -126,6 +134,8 @@ const SendButton = styled(Button)`
126
134
  display: flex;
127
135
  align-items: center;
128
136
  justify-content: center;
137
+ border-radius: var(--search-ai-conversation-input-send-button-border-radius);
138
+ padding: var(--search-ai-conversation-input-send-button-padding);
129
139
 
130
140
  &:hover {
131
141
  background-color: var(--search-ai-conversation-input-send-button-bg-color-hover);
@@ -48,6 +48,7 @@ export function SearchDialog({
48
48
  const products = useProducts();
49
49
  const currentProduct = useCurrentProduct();
50
50
  const [product, setProduct] = useState(currentProduct);
51
+ const searchSessionId = `search-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
51
52
  const [mode, setMode] = useState<'search' | 'ai-dialog'>(initialMode);
52
53
  const autoSearchDisabled = mode !== 'search';
53
54
  const {
@@ -62,7 +63,7 @@ export function SearchDialog({
62
63
  advancedSearch,
63
64
  askAi,
64
65
  groupField,
65
- } = useSearch(product?.name, autoSearchDisabled);
66
+ } = useSearch(product?.name, autoSearchDisabled, searchSessionId);
66
67
  const {
67
68
  isFilterOpen,
68
69
  onFilterToggle,
@@ -72,7 +73,7 @@ export function SearchDialog({
72
73
  onQuickFilterReset,
73
74
  } = useSearchFilter(filter, setFilter);
74
75
  const { addSearchHistoryItem } = useRecentSearches();
75
- const aiSearch = useAiSearch({ filter });
76
+ const aiSearch = useAiSearch({ filter }, searchSessionId);
76
77
 
77
78
  const searchInputRef = useRef<HTMLInputElement>(null);
78
79
  const modalRef = useRef<HTMLDivElement>(null);
@@ -150,13 +151,14 @@ export function SearchDialog({
150
151
  totalResults: results.length.toString(),
151
152
  index: index.toString(),
152
153
  searchEngine: mode,
154
+ searchSessionId,
153
155
  });
154
156
  onClose();
155
157
  }}
156
158
  />
157
159
  );
158
160
  },
159
- [onClose, product, products, addSearchHistoryItem, query, telemetry, mode],
161
+ [onClose, product, products, addSearchHistoryItem, query, telemetry, mode, searchSessionId],
160
162
  );
161
163
 
162
164
  const showLoadMore = useCallback(
@@ -389,6 +391,7 @@ export function SearchDialog({
389
391
  telemetry.sendSearchRecentClickedMessage({
390
392
  query,
391
393
  index: index.toString(),
394
+ searchSessionId,
392
395
  });
393
396
  setQuery(query);
394
397
  focusSearchInput();
@@ -45,6 +45,7 @@ export function SearchGroups({
45
45
  borderless
46
46
  active={!searchFilter.some((item) => item.isQuickFilter)}
47
47
  onClick={() => searchFilter.some((item) => item.isQuickFilter) && onQuickFilterReset()}
48
+ selectable
48
49
  >
49
50
  All
50
51
  </GroupTag>
@@ -61,6 +62,7 @@ export function SearchGroups({
61
62
  onClick={() => handleGroupTagClick(value, facet.field, active, currentValues)}
62
63
  active={active}
63
64
  borderless
65
+ selectable
64
66
  >
65
67
  {value} {isCounterVisible && <span>{count}</span>}
66
68
  </GroupTag>
@@ -171,6 +171,7 @@ export const search = css`
171
171
  --search-ai-assistant-bg-color: var(--layer-color);
172
172
  --search-ai-assistant-text-color: var(--text-color-primary);
173
173
  --search-ai-assistant-border: 1px solid var(--border-color-primary);
174
+ --search-ai-assistant-message-max-width: 80%;
174
175
 
175
176
  --search-ai-resources-gap: var(--spacing-base);
176
177
  --search-ai-resources-title-font-weight: var(--font-weight-medium);
@@ -232,6 +233,7 @@ export const search = css`
232
233
  * @tokens AI Search Conversation Input
233
234
  */
234
235
  --search-ai-conversation-input-bg-color: var(--bg-color);
236
+ --search-ai-conversation-input-bg-color-disabled: var(--color-warm-grey-1);
235
237
  --search-ai-conversation-input-padding: var(--spacing-sm) var(--spacing-md);
236
238
  --search-ai-conversation-input-border: 1px solid var(--border-color-secondary);
237
239
  --search-ai-conversation-input-border-radius: var(--border-radius-lg);
@@ -241,10 +243,12 @@ export const search = css`
241
243
  --search-ai-conversation-input-border-color-disabled: var(--border-color-secondary);
242
244
 
243
245
  --search-ai-conversation-input-send-button-right: 12px;
246
+ --search-ai-conversation-input-send-button-padding: 5px;
244
247
  --search-ai-conversation-input-send-button-bg-color: var(--button-bg-color-primary);
245
248
  --search-ai-conversation-input-send-button-bg-color-hover: var(--button-bg-color-primary-hover);
246
249
  --search-ai-conversation-input-send-button-bg-color-disabled: var(--button-bg-color-disabled);
247
250
  --search-ai-conversation-input-send-button-border-disabled: 1px solid var(--button-border-color-disabled);
251
+ --search-ai-conversation-input-send-button-border-radius: var(--border-radius);
248
252
 
249
253
  /**
250
254
  * @tokens AI Search Response
@@ -277,7 +281,7 @@ export const search = css`
277
281
 
278
282
  --search-ai-suggestions-gap: var(--spacing-sm);
279
283
  --search-ai-suggestions-margin-left: var(--spacing-xs);
280
- --search-ai-suggestion-item-gap: var(--spacing-xs);
284
+ --search-ai-suggestion-item-gap: var(--spacing-xxs);
281
285
 
282
286
  --search-ai-suggestions-title-text-color: var(--text-color-description);
283
287
  --search-ai-suggestions-title-font-size: var(--font-size-base);
@@ -83,6 +83,7 @@ export function SelectInput<T>(props: SelectInputProps<T>): React.ReactNode {
83
83
  const selectTags = selectedOptions.map((option, index) => (
84
84
  <SelectInputTag
85
85
  closable
86
+ tabIndex={0}
86
87
  key={index}
87
88
  onClose={() => {
88
89
  clearHandler?.(option);
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import styled from 'styled-components';
2
+ import styled, { css } from 'styled-components';
3
3
 
4
4
  import type { JSX } from 'react';
5
5
 
@@ -56,6 +56,7 @@ export type TagProps = {
56
56
  maxLength?: number;
57
57
  textTransform?: 'uppercase' | 'lowercase' | 'capitalize' | 'none';
58
58
  variant?: 'outline' | 'filled';
59
+ selectable?: boolean;
59
60
  };
60
61
 
61
62
  export function Tag({
@@ -75,6 +76,7 @@ export function Tag({
75
76
  maxLength,
76
77
  textTransform,
77
78
  variant = 'filled',
79
+ selectable,
78
80
  ...otherProps
79
81
  }: TagProps): JSX.Element {
80
82
  const truncateText = (text: string, maxLen: number): string => {
@@ -152,6 +154,7 @@ export function Tag({
152
154
  hasCloseButton={closable}
153
155
  textTransform={textTransform}
154
156
  variant={variant}
157
+ selectable={selectable}
155
158
  {...otherProps}
156
159
  >
157
160
  {withStatusDot ? <StatusDot color={statusDotColor} /> : icon ? icon : null}
@@ -186,16 +189,28 @@ const CloseButton = styled.div`
186
189
  justify-content: center;
187
190
  align-self: stretch;
188
191
  border-radius: 0 var(--tag-border-radius) var(--tag-border-radius) 0;
192
+ margin: calc(-1 * var(--tag-border-width));
193
+ padding: var(--tag-border-width);
189
194
 
190
195
  &:hover {
191
196
  background: var(--tag-close-button-bg-color-hover);
192
197
  }
198
+
199
+ &:focus-visible {
200
+ background: var(--tag-close-button-bg-color-focus);
201
+ }
193
202
  `;
194
203
 
195
- const TagWrapper = styled.div.attrs(({ className, color, size }: TagProps) => ({
196
- className:
197
- (className || '') +
198
- ` tag-default ${color ? `tag-${color}` : ''} ${size ? `tag-size-${size}` : ''}`,
204
+ const TagWrapper = styled.div.attrs(({ className, color, size, variant }: TagProps) => ({
205
+ className: [
206
+ className,
207
+ 'tag-default',
208
+ color && `tag-${color}`,
209
+ size && `tag-size-${size}`,
210
+ `tag-variant-${variant || 'filled'}`,
211
+ ]
212
+ .filter(Boolean)
213
+ .join(' '),
199
214
  }))<
200
215
  TagProps & {
201
216
  hasCloseButton?: boolean;
@@ -227,12 +242,11 @@ const TagWrapper = styled.div.attrs(({ className, color, size }: TagProps) => ({
227
242
  `text-transform: ${textTransform ? `${textTransform}` : 'var(--tag-text-transform)'};`}
228
243
 
229
244
  color: var(--tag-color);
230
- background-color: ${({ variant }) =>
231
- variant === 'filled' ? 'var(--tag-bg-color)' : 'transparent'};
232
- ${({ borderless, variant }) =>
245
+ background-color: var(--tag-bg-color);
246
+ ${({ borderless }) =>
233
247
  borderless
234
248
  ? ''
235
- : `border: var(--tag-border-width) var(--tag-border-style) ${variant === 'filled' ? 'transparent' : 'var(--tag-border-color)'};`}
249
+ : `border: var(--tag-border-width) var(--tag-border-style) var(--tag-border-color);`}
236
250
  border-radius: var(--tag-border-radius);
237
251
 
238
252
  svg {
@@ -240,18 +254,20 @@ const TagWrapper = styled.div.attrs(({ className, color, size }: TagProps) => ({
240
254
  height: var(--tag-icon-height);
241
255
  }
242
256
 
243
- &:hover {
244
- background-color: ${({ variant }) =>
245
- variant === 'filled' ? 'var(--tag-bg-color-hover)' : 'transparent'};
246
- border-color: ${({ variant }) =>
247
- variant === 'outline' ? 'var(--tag-border-color-hover)' : 'transparent'};
248
- }
257
+ ${({ selectable }) =>
258
+ selectable &&
259
+ css`
260
+ &:hover {
261
+ background-color: var(--tag-bg-color-hover);
262
+ border-color: var(--tag-border-color-hover);
263
+ }
249
264
 
250
- &:focus-visible {
251
- outline: 1px solid var(--tag-border-color-focused);
252
- outline-offset: 2px;
253
- border-radius: var(--tag-border-radius-focused);
254
- }
265
+ &:focus-visible {
266
+ outline: 1px solid var(--tag-border-color-focused);
267
+ outline-offset: 2px;
268
+ border-radius: var(--tag-border-radius-focused);
269
+ }
270
+ `};
255
271
  `;
256
272
 
257
273
  const StatusDot = styled.div<{ color: string }>`