@redocly/theme 0.59.0-next.0 → 0.59.0-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/lib/components/Accordion/Accordion.d.ts +12 -0
  2. package/lib/components/Accordion/Accordion.js +85 -0
  3. package/lib/components/Accordion/AccordionBody.d.ts +8 -0
  4. package/lib/components/Accordion/AccordionBody.js +73 -0
  5. package/lib/components/Accordion/AccordionHeader.d.ts +10 -0
  6. package/lib/components/Accordion/AccordionHeader.js +37 -0
  7. package/lib/components/Accordion/AccordionTitle.d.ts +6 -0
  8. package/lib/components/Accordion/AccordionTitle.js +20 -0
  9. package/lib/components/Accordion/variables.d.ts +1 -0
  10. package/lib/components/Accordion/variables.js +59 -0
  11. package/lib/components/Admonition/Admonition.js +17 -7
  12. package/lib/components/Badge/Badge.js +17 -7
  13. package/lib/components/Breadcrumbs/Breadcrumb.js +17 -7
  14. package/lib/components/Breadcrumbs/BreadcrumbDropdown.js +17 -7
  15. package/lib/components/Button/Button.js +17 -7
  16. package/lib/components/Buttons/AIAssistantButton.d.ts +2 -0
  17. package/lib/components/Buttons/AIAssistantButton.js +135 -0
  18. package/lib/components/Buttons/CopyButton.js +17 -7
  19. package/lib/components/Buttons/variables.d.ts +1 -0
  20. package/lib/components/Buttons/variables.dark.d.ts +1 -0
  21. package/lib/components/Buttons/variables.dark.js +10 -0
  22. package/lib/components/Buttons/variables.js +51 -0
  23. package/lib/components/Catalog/Catalog.d.ts +6 -0
  24. package/lib/components/Catalog/Catalog.js +9 -8
  25. package/lib/components/Catalog/CatalogEntities.js +17 -7
  26. package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsGraph.js +17 -7
  27. package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsGraph.lazy.js +17 -7
  28. package/lib/components/Catalog/CatalogEntity/CatalogEntityMetadata.js +17 -7
  29. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityApiDescriptionRelations.js +1 -1
  30. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityRelations.js +17 -7
  31. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityTeamRelations.js +1 -1
  32. package/lib/components/Catalog/CatalogEntity/CatalogEntitySchema.js +17 -7
  33. package/lib/components/Catalog/CatalogFilter/CatalogFilter.d.ts +6 -0
  34. package/lib/components/Catalog/CatalogFilter/CatalogFilter.js +35 -0
  35. package/lib/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.d.ts +6 -0
  36. package/lib/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.js +152 -0
  37. package/lib/components/Catalog/CatalogFilter/CatalogFilterContent.d.ts +13 -0
  38. package/lib/components/Catalog/CatalogFilter/CatalogFilterContent.js +102 -0
  39. package/lib/components/Catalog/CatalogFilter/CatalogFilterDateRange.d.ts +6 -0
  40. package/lib/components/Catalog/CatalogFilter/CatalogFilterDateRange.js +121 -0
  41. package/lib/components/Catalog/CatalogFilter/CatalogFilterSelect.d.ts +6 -0
  42. package/lib/components/Catalog/CatalogFilter/CatalogFilterSelect.js +126 -0
  43. package/lib/components/Catalog/CatalogSelector.js +0 -1
  44. package/lib/components/Catalog/CatalogSortButton.js +17 -7
  45. package/lib/components/Catalog/CatalogTableView/CatalogTableHeaderCell.js +17 -7
  46. package/lib/components/Catalog/CatalogViewModeToggle.js +17 -7
  47. package/lib/components/Catalog/variables.js +0 -1
  48. package/lib/components/CatalogClassic/CatalogClassicActions.js +17 -7
  49. package/lib/components/CatalogClassic/CatalogClassicCard.js +17 -7
  50. package/lib/components/CatalogClassic/CatalogClassicHighlight.js +17 -7
  51. package/lib/components/CatalogClassic/CatalogClassicVirtualizedGroups.js +17 -7
  52. package/lib/components/CodeBlock/CodeBlock.js +17 -7
  53. package/lib/components/CodeBlock/CodeBlockContainer.js +17 -7
  54. package/lib/components/CodeBlock/CodeBlockTabs.js +17 -7
  55. package/lib/components/Dropdown/Dropdown.d.ts +16 -2
  56. package/lib/components/Dropdown/Dropdown.js +22 -12
  57. package/lib/components/Dropdown/DropdownMenuItem.js +17 -7
  58. package/lib/components/Feedback/Comment.js +17 -7
  59. package/lib/components/Feedback/Feedback.js +17 -7
  60. package/lib/components/Feedback/Mood.js +17 -7
  61. package/lib/components/Feedback/Rating.js +17 -7
  62. package/lib/components/Feedback/Reasons.js +17 -7
  63. package/lib/components/Feedback/Scale.js +17 -7
  64. package/lib/components/Feedback/Sentiment.js +17 -7
  65. package/lib/components/Feedback/Stars.js +17 -7
  66. package/lib/components/Filter/FilterContent.js +17 -7
  67. package/lib/components/Filter/FilterInput.d.ts +1 -0
  68. package/lib/components/Filter/FilterInput.js +19 -9
  69. package/lib/components/Filter/FilterOptions.js +2 -0
  70. package/lib/components/Filter/variables.js +7 -4
  71. package/lib/components/Image/Image.js +17 -7
  72. package/lib/components/JsonViewer/JsonViewer.js +17 -7
  73. package/lib/components/JsonViewer/helpers.js +17 -7
  74. package/lib/components/LastUpdated/LastUpdated.js +17 -7
  75. package/lib/components/Link/Link.js +17 -7
  76. package/lib/components/Markdown/Markdown.js +17 -7
  77. package/lib/components/Marker/Marker.js +17 -7
  78. package/lib/components/Menu/MenuContainer.js +17 -7
  79. package/lib/components/Menu/MenuItem.js +18 -8
  80. package/lib/components/Menu/MenuMobile.js +17 -7
  81. package/lib/components/PageActions/PageActions.js +17 -7
  82. package/lib/components/PageNavigation/NextButton.js +17 -7
  83. package/lib/components/Panel/Panel.js +17 -7
  84. package/lib/components/Panel/PanelBody.js +17 -7
  85. package/lib/components/Search/FilterFields/SearchFilterFieldSelect.js +17 -7
  86. package/lib/components/Search/SearchAiConversationInput.d.ts +2 -1
  87. package/lib/components/Search/SearchAiConversationInput.js +28 -10
  88. package/lib/components/Search/SearchAiDialog.js +19 -10
  89. package/lib/components/Search/SearchAiResponse.js +2 -3
  90. package/lib/components/Search/SearchDialog.d.ts +2 -1
  91. package/lib/components/Search/SearchDialog.js +25 -12
  92. package/lib/components/Search/SearchFilter.js +17 -7
  93. package/lib/components/Search/SearchGroups.js +17 -7
  94. package/lib/components/Search/SearchHighlight.js +17 -7
  95. package/lib/components/Search/SearchItem.js +17 -7
  96. package/lib/components/Search/SearchRecent.js +17 -7
  97. package/lib/components/Search/SearchShortcut.js +17 -7
  98. package/lib/components/Search/SearchSuggestedPages.js +17 -7
  99. package/lib/components/Search/SearchTrigger.js +17 -7
  100. package/lib/components/Search/variables.js +5 -1
  101. package/lib/components/Segmented/Segmented.js +17 -7
  102. package/lib/components/Select/Select.js +17 -7
  103. package/lib/components/Select/SelectInput.js +17 -7
  104. package/lib/components/Sidebar/Sidebar.js +17 -7
  105. package/lib/components/SidebarActions/styled.js +17 -7
  106. package/lib/components/SkipContent/SkipContent.js +17 -7
  107. package/lib/components/Switch/Switch.js +17 -7
  108. package/lib/components/TableOfContent/TableOfContent.js +17 -7
  109. package/lib/components/Tag/variables.dark.js +2 -2
  110. package/lib/components/Tooltip/Tooltip.js +17 -7
  111. package/lib/components/VersionPicker/VersionPicker.js +17 -7
  112. package/lib/core/constants/search.d.ts +5 -4
  113. package/lib/core/constants/search.js +4 -5
  114. package/lib/core/contexts/CodeSnippetContext.js +17 -7
  115. package/lib/core/hooks/use-tabs.d.ts +3 -2
  116. package/lib/core/hooks/use-tabs.js +115 -57
  117. package/lib/core/styles/dark.js +29 -26
  118. package/lib/core/styles/global.js +64 -59
  119. package/lib/core/templates/Markdown.js +17 -7
  120. package/lib/core/types/hooks.d.ts +6 -3
  121. package/lib/core/types/l10n.d.ts +1 -1
  122. package/lib/core/utils/download-code-walkthrough.js +17 -7
  123. package/lib/core/utils/get-file-icon.js +17 -7
  124. package/lib/icons/AiStarsIcon/AiStarsIcon.js +11 -2
  125. package/lib/icons/GenericIcon/GenericIcon.js +17 -7
  126. package/lib/icons/RedoclyIcon/RedoclyIcon.d.ts +9 -0
  127. package/lib/icons/RedoclyIcon/RedoclyIcon.js +24 -0
  128. package/lib/icons/Spinner/Spinner.js +17 -7
  129. package/lib/index.d.ts +2 -0
  130. package/lib/index.js +19 -7
  131. package/lib/layouts/OIDCForbidden.js +17 -7
  132. package/lib/layouts/RootLayout.js +6 -1
  133. package/lib/layouts/ThreePanelLayout.js +17 -7
  134. package/lib/markdoc/components/Cards/Cards.js +17 -7
  135. package/lib/markdoc/components/CodeGroup/CodeGroup.js +17 -7
  136. package/lib/markdoc/components/CodeWalkthrough/CodeContainer.js +17 -7
  137. package/lib/markdoc/components/CodeWalkthrough/CodePanel.js +17 -7
  138. package/lib/markdoc/components/CodeWalkthrough/CodePanelHeader.js +17 -7
  139. package/lib/markdoc/components/CodeWalkthrough/CodePanelPreview.js +17 -7
  140. package/lib/markdoc/components/CodeWalkthrough/CodePanelToolbar.js +17 -7
  141. package/lib/markdoc/components/CodeWalkthrough/CodeStep.js +17 -7
  142. package/lib/markdoc/components/CodeWalkthrough/CodeToggle.js +17 -7
  143. package/lib/markdoc/components/CodeWalkthrough/CodeWalkthrough.js +17 -7
  144. package/lib/markdoc/components/CodeWalkthrough/Input.js +17 -7
  145. package/lib/markdoc/components/Heading/Heading.js +17 -7
  146. package/lib/markdoc/components/HtmlBlock/HtmlBlock.js +17 -7
  147. package/lib/markdoc/components/InlineSvg/InlineSvg.js +17 -7
  148. package/lib/markdoc/components/MarkdocExample/MarkdocExample.js +17 -7
  149. package/lib/markdoc/components/Tabs/TabList.d.ts +3 -1
  150. package/lib/markdoc/components/Tabs/TabList.js +214 -54
  151. package/lib/markdoc/components/Tabs/Tabs.d.ts +2 -1
  152. package/lib/markdoc/components/Tabs/Tabs.js +74 -19
  153. package/lib/markdoc/default.d.ts +104 -1
  154. package/lib/markdoc/default.js +17 -7
  155. package/package.json +5 -5
  156. package/src/components/Accordion/Accordion.tsx +100 -0
  157. package/src/components/Accordion/AccordionBody.tsx +65 -0
  158. package/src/components/Accordion/AccordionHeader.tsx +68 -0
  159. package/src/components/Accordion/AccordionTitle.tsx +26 -0
  160. package/src/components/Accordion/variables.ts +56 -0
  161. package/src/components/Buttons/AIAssistantButton.tsx +141 -0
  162. package/src/components/Buttons/variables.dark.ts +7 -0
  163. package/src/components/Buttons/variables.ts +48 -0
  164. package/src/components/Catalog/Catalog.tsx +18 -6
  165. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityApiDescriptionRelations.tsx +1 -1
  166. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityTeamRelations.tsx +1 -1
  167. package/src/components/Catalog/CatalogFilter/CatalogFilter.tsx +56 -0
  168. package/src/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.tsx +169 -0
  169. package/src/components/Catalog/CatalogFilter/CatalogFilterContent.tsx +121 -0
  170. package/src/components/Catalog/CatalogFilter/CatalogFilterDateRange.tsx +147 -0
  171. package/src/components/Catalog/CatalogFilter/CatalogFilterSelect.tsx +136 -0
  172. package/src/components/Catalog/CatalogSelector.tsx +0 -1
  173. package/src/components/Catalog/variables.ts +0 -1
  174. package/src/components/Dropdown/Dropdown.tsx +84 -79
  175. package/src/components/Filter/FilterInput.tsx +3 -2
  176. package/src/components/Filter/FilterOptions.tsx +2 -0
  177. package/src/components/Filter/variables.ts +7 -4
  178. package/src/components/Menu/MenuItem.tsx +1 -0
  179. package/src/components/Search/SearchAiConversationInput.tsx +12 -2
  180. package/src/components/Search/SearchAiDialog.tsx +2 -2
  181. package/src/components/Search/SearchAiResponse.tsx +2 -2
  182. package/src/components/Search/SearchDialog.tsx +13 -5
  183. package/src/components/Search/variables.ts +5 -1
  184. package/src/components/Tag/variables.dark.ts +2 -2
  185. package/src/core/constants/search.ts +8 -4
  186. package/src/core/hooks/use-tabs.ts +168 -86
  187. package/src/core/styles/dark.ts +11 -8
  188. package/src/core/styles/global.ts +7 -2
  189. package/src/core/types/hooks.ts +6 -1
  190. package/src/core/types/l10n.ts +2 -0
  191. package/src/icons/AiStarsIcon/AiStarsIcon.tsx +11 -2
  192. package/src/icons/RedoclyIcon/RedoclyIcon.tsx +26 -0
  193. package/src/index.ts +2 -0
  194. package/src/layouts/RootLayout.tsx +6 -0
  195. package/src/markdoc/components/Tabs/TabList.tsx +312 -105
  196. package/src/markdoc/components/Tabs/Tabs.tsx +136 -11
@@ -0,0 +1,56 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import type { JSX } from 'react';
5
+ import type { FilterProps, FilterTypes } from '@redocly/theme/core/types';
6
+
7
+ import { CatalogFilterSelect } from '@redocly/theme/components/Catalog/CatalogFilter/CatalogFilterSelect';
8
+ import { CatalogFilterCheckboxes } from '@redocly/theme/components/Catalog/CatalogFilter/CatalogFilterCheckboxes';
9
+ import { CatalogFilterDateRange } from '@redocly/theme/components/Catalog/CatalogFilter/CatalogFilterDateRange';
10
+
11
+ const filterComponents = {
12
+ select: CatalogFilterSelect,
13
+ 'date-range': CatalogFilterDateRange,
14
+ checkboxes: CatalogFilterCheckboxes,
15
+ } as const;
16
+
17
+ export type CatalogFilterProps = FilterProps & {
18
+ className?: string;
19
+ };
20
+
21
+ export function CatalogFilter({
22
+ filter,
23
+ filterValuesCasing,
24
+ showCounter = true,
25
+ className,
26
+ }: CatalogFilterProps): JSX.Element | null {
27
+ if (!filter.parentUsed) return null;
28
+
29
+ const FilterComponent = filterComponents[(filter.type || 'checkboxes') as FilterTypes];
30
+
31
+ return (
32
+ <CatalogFilterGroup
33
+ className={className}
34
+ data-component-name="Catalog/CatalogFilter"
35
+ key={filter.property + filter.title}
36
+ >
37
+ <FilterComponent
38
+ filter={filter}
39
+ filterValuesCasing={filterValuesCasing}
40
+ showCounter={showCounter}
41
+ />
42
+ </CatalogFilterGroup>
43
+ );
44
+ }
45
+
46
+ const CatalogFilterGroup = styled.div`
47
+ padding: var(--filter-group-padding);
48
+ display: flex;
49
+ flex-direction: column;
50
+ gap: var(--filter-group-gap);
51
+ border-bottom: 1px solid var(--catalog-table-border-color);
52
+
53
+ &:first-child {
54
+ border-top: 1px solid var(--catalog-table-border-color);
55
+ }
56
+ `;
@@ -0,0 +1,169 @@
1
+ import React, { useState, useMemo } from 'react';
2
+ import styled from 'styled-components';
3
+ import { findAll } from 'highlight-words-core';
4
+
5
+ import type { JSX } from 'react';
6
+ import type { FilterProps } from '@redocly/theme/core/types';
7
+
8
+ import { FilterOptions } from '@redocly/theme/components/Filter/FilterOptions';
9
+ import { FilterOption } from '@redocly/theme/components/Filter/FilterOption';
10
+ import { FilterOptionLabel } from '@redocly/theme/components/Filter/FilterOptionLabel';
11
+ import { FilterInput } from '@redocly/theme/components/Filter/FilterInput';
12
+ import { useThemeHooks } from '@redocly/theme/core/hooks';
13
+ import { CheckboxIcon } from '@redocly/theme/icons/CheckboxIcon/CheckboxIcon';
14
+ import { CounterTag } from '@redocly/theme/components/Tags/CounterTag';
15
+ import { changeTextCasing } from '@redocly/theme/core/utils';
16
+ import { Accordion } from '@redocly/theme/components/Accordion/Accordion';
17
+
18
+ export type CatalogFilterCheckboxesProps = FilterProps & {
19
+ className?: string;
20
+ };
21
+
22
+ export function CatalogFilterCheckboxes({
23
+ filter,
24
+ filterValuesCasing,
25
+ showCounter = true,
26
+ className,
27
+ }: CatalogFilterCheckboxesProps): JSX.Element {
28
+ const { useTranslate, useTelemetry } = useThemeHooks();
29
+ const { translate } = useTranslate();
30
+ const telemetry = useTelemetry();
31
+
32
+ const [searchTerm, setSearchTerm] = useState('');
33
+ const [isExpanded, setIsExpanded] = useState(false);
34
+
35
+ const filteredOptions = useMemo(() => {
36
+ const options = filter.filteredOptions || filter.options;
37
+ if (!searchTerm.trim()) return options;
38
+
39
+ const lowerSearchTerm = searchTerm.toLowerCase();
40
+ return options.filter(({ value }) => {
41
+ const translatedValue = changeTextCasing(translate(value), filterValuesCasing);
42
+ if (!translatedValue) return false;
43
+ return translatedValue.toLowerCase().includes(lowerSearchTerm);
44
+ });
45
+ }, [searchTerm, filter.filteredOptions, filter.options, translate, filterValuesCasing]);
46
+
47
+ const selectedCount = useMemo(() => {
48
+ if (filter.selectedOptions instanceof Set) {
49
+ return filter.selectedOptions.size;
50
+ }
51
+ return 0;
52
+ }, [filter.selectedOptions]);
53
+
54
+ const highlightText = (text: string): JSX.Element => {
55
+ if (!searchTerm.trim()) {
56
+ return <>{text}</>;
57
+ }
58
+
59
+ const chunks = findAll({
60
+ searchWords: [searchTerm],
61
+ textToHighlight: text,
62
+ });
63
+
64
+ return (
65
+ <>
66
+ {chunks.map((chunk, idx) => {
67
+ const { end, highlight, start } = chunk;
68
+ const chunkText = text.substr(start, end - start);
69
+ if (highlight) {
70
+ return <HighlightedText key={idx}>{chunkText}</HighlightedText>;
71
+ } else {
72
+ return <span key={idx}>{chunkText}</span>;
73
+ }
74
+ })}
75
+ </>
76
+ );
77
+ };
78
+
79
+ const headerContent = (
80
+ <AccordionHeaderContent>
81
+ <span>{translate(filter.titleTranslationKey, filter.title)}</span>
82
+ {selectedCount > 0 && <CounterTag borderless>{selectedCount}</CounterTag>}
83
+ </AccordionHeaderContent>
84
+ );
85
+
86
+ return (
87
+ <AccordionWrapper
88
+ className={className}
89
+ $hasSelection={selectedCount > 0 && !isExpanded}
90
+ data-component-name="Catalog/CatalogFilterCheckboxes"
91
+ >
92
+ <Accordion expanded={isExpanded} header={headerContent} onToggle={setIsExpanded}>
93
+ <FilterSearchWrapper>
94
+ <FilterInput value={searchTerm} onChange={setSearchTerm} />
95
+ </FilterSearchWrapper>
96
+ <FilterOptions>
97
+ {filteredOptions.map(({ value, count }) => {
98
+ const id = 'filter--' + filter.property + '--' + value;
99
+ return (
100
+ <FilterCheckboxOption
101
+ key={id}
102
+ role="link"
103
+ onClick={() => {
104
+ filter.toggleOption(value);
105
+ telemetry.sendFilterCheckboxToggledMessage({ id });
106
+ }}
107
+ >
108
+ <CheckboxIcon
109
+ checked={
110
+ filter.selectedOptions instanceof Set
111
+ ? filter.selectedOptions.has(value) ||
112
+ filter.selectedOptions.has(value?.toLowerCase())
113
+ : false
114
+ }
115
+ />
116
+ <FilterOptionLabel data-translation-key={value}>
117
+ {highlightText(changeTextCasing(translate(value), filterValuesCasing) || '')}
118
+ </FilterOptionLabel>
119
+ {showCounter && <CounterTag borderless>{count}</CounterTag>}
120
+ </FilterCheckboxOption>
121
+ );
122
+ })}
123
+ </FilterOptions>
124
+ </Accordion>
125
+ </AccordionWrapper>
126
+ );
127
+ }
128
+
129
+ const AccordionWrapper = styled.div<{ $hasSelection: boolean }>`
130
+ position: relative;
131
+ border-right: 4px solid transparent;
132
+
133
+ ${({ $hasSelection }) =>
134
+ $hasSelection &&
135
+ `
136
+ border-right-color: var(--color-blueberry-6);
137
+
138
+ &::after {
139
+ content: '';
140
+ position: absolute;
141
+ bottom: -1px;
142
+ right: -4px;
143
+ width: 4px;
144
+ height: 1px;
145
+ background-color: var(--color-blueberry-6);
146
+ z-index: 1;
147
+ }
148
+ `}
149
+ `;
150
+
151
+ const AccordionHeaderContent = styled.div`
152
+ display: flex;
153
+ align-items: center;
154
+ gap: var(--spacing-xs);
155
+ width: 100%;
156
+ `;
157
+
158
+ const HighlightedText = styled.span`
159
+ background-color: var(--catalog-highlight-bg-color);
160
+ color: var(--catalog-highlight-text-color);
161
+ `;
162
+
163
+ const FilterCheckboxOption = styled(FilterOption)`
164
+ padding-left: var(--filter-option-checkbox-padding-left);
165
+ `;
166
+
167
+ const FilterSearchWrapper = styled.div`
168
+ padding: var(--filter-content-search-padding);
169
+ `;
@@ -0,0 +1,121 @@
1
+ import React from 'react';
2
+ import styled, { css } from 'styled-components';
3
+
4
+ import type { JSX } from 'react';
5
+ import type { ResolvedFilter } from '@redocly/theme/core/types';
6
+ import type { RedoclyConfig } from '@redocly/theme/config';
7
+
8
+ import { FilterInput } from '@redocly/theme/components/Filter/FilterInput';
9
+ import { useThemeHooks } from '@redocly/theme/core/hooks';
10
+ import { Button } from '@redocly/theme/components/Button/Button';
11
+ import { CatalogFilter } from '@redocly/theme/components/Catalog/CatalogFilter/CatalogFilter';
12
+ import { isFromToSelectedOptions } from '@redocly/theme/core/utils';
13
+
14
+ export type CatalogFilterContentProps = {
15
+ setFilterTerm: (value: string) => void;
16
+ filters: ResolvedFilter[];
17
+ filterTerm: string;
18
+ filterValuesCasing?:
19
+ | NonNullable<RedoclyConfig['catalog']>[string]['filterValuesCasing']
20
+ | ((str: string) => string);
21
+ hideSearch?: boolean;
22
+ showCounter?: boolean;
23
+ className?: string;
24
+ };
25
+
26
+ export function CatalogFilterContent({
27
+ setFilterTerm,
28
+ filters,
29
+ filterTerm,
30
+ filterValuesCasing,
31
+ hideSearch,
32
+ showCounter = true,
33
+ className,
34
+ }: CatalogFilterContentProps): JSX.Element | null {
35
+ const { useTranslate } = useThemeHooks();
36
+ const { translate } = useTranslate();
37
+
38
+ const hasActiveFilters = filters.some((filter) => {
39
+ if (filterTerm) return true;
40
+ if (filter.selectedOptions && filter.selectedOptions instanceof Set) {
41
+ return filter.selectedOptions.size;
42
+ } else if (
43
+ isFromToSelectedOptions(filter.selectedOptions) &&
44
+ filter.selectedOptions.from &&
45
+ filter.selectedOptions.to
46
+ ) {
47
+ return true;
48
+ }
49
+ });
50
+
51
+ const handleClearAll = (): void => {
52
+ filters.forEach((filter) => filter.selectOption(''));
53
+ setFilterTerm('');
54
+ };
55
+
56
+ if (!filters.length) {
57
+ return null;
58
+ }
59
+
60
+ return (
61
+ <CatalogFilterContentWrapper
62
+ className={className}
63
+ data-component-name="Catalog/CatalogFilterContent"
64
+ >
65
+ <FiltersHeader>
66
+ <FiltersTitle data-translation-key="catalog.filters.title">
67
+ {translate('catalog.filters.title', 'Filters')}
68
+ </FiltersTitle>
69
+ {hasActiveFilters ? (
70
+ <Button size="medium" tone="danger" variant="ghost" onClick={handleClearAll}>
71
+ {translate('catalog.filters.clearAll', 'Clear filters')}
72
+ </Button>
73
+ ) : null}
74
+ </FiltersHeader>
75
+ {!hideSearch && (
76
+ <FilterInput value={filterTerm} onChange={(updatedTerm) => setFilterTerm(updatedTerm)} />
77
+ )}
78
+ <FilterItems hideSearch={hideSearch}>
79
+ {filters.map((filter, idx) => (
80
+ <CatalogFilter
81
+ filter={filter}
82
+ key={filter.property + '-' + idx}
83
+ filterValuesCasing={filterValuesCasing}
84
+ showCounter={showCounter}
85
+ />
86
+ ))}
87
+ </FilterItems>
88
+ </CatalogFilterContentWrapper>
89
+ );
90
+ }
91
+
92
+ const CatalogFilterContentWrapper = styled.div`
93
+ padding: 0 0 var(--filter-content-padding-vertical) 0;
94
+ display: flex;
95
+ flex-direction: column;
96
+ `;
97
+
98
+ const FiltersHeader = styled.div`
99
+ display: flex;
100
+ padding: var(--filter-content-header-padding-vertical)
101
+ var(--filter-content-header-padding-horizontal);
102
+ `;
103
+
104
+ const FiltersTitle = styled.div`
105
+ margin-right: auto;
106
+ font-size: var(--filter-content-title-font-size);
107
+ font-weight: var(--filter-content-title-font-weight);
108
+ line-height: var(--filter-content-title-line-height);
109
+ `;
110
+
111
+ const FilterItems = styled.div<{ hideSearch?: boolean }>`
112
+ height: 100%;
113
+ display: flex;
114
+ flex-direction: column;
115
+ border-top: 1px solid var(--border-color-secondary);
116
+ ${({ hideSearch }) =>
117
+ hideSearch &&
118
+ css`
119
+ border-top: none;
120
+ `}
121
+ `;
@@ -0,0 +1,147 @@
1
+ import React, { useMemo } from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import type { JSX } from 'react';
5
+ import type { FilterProps } from '@redocly/theme/core/types';
6
+
7
+ import { FilterOptions } from '@redocly/theme/components/Filter/FilterOptions';
8
+ import { DatePicker } from '@redocly/theme/components/DatePicker/DatePicker';
9
+ import { CounterTag } from '@redocly/theme/components/Tags/CounterTag';
10
+ import { useThemeHooks } from '@redocly/theme/core/hooks';
11
+ import { formatDateWithoutTimeZone } from '@redocly/theme/core/utils';
12
+ import { Accordion } from '@redocly/theme/components/Accordion/Accordion';
13
+
14
+ export type CatalogFilterDateRangeProps = FilterProps & {
15
+ className?: string;
16
+ };
17
+
18
+ export function CatalogFilterDateRange({
19
+ filter,
20
+ className,
21
+ }: CatalogFilterDateRangeProps): JSX.Element {
22
+ const { useTranslate } = useThemeHooks();
23
+ const { translate } = useTranslate();
24
+
25
+ const from = useMemo(
26
+ () => (filter.selectedOptions.from ? new Date(filter.selectedOptions.from) : undefined),
27
+ [filter.selectedOptions.from],
28
+ );
29
+ const to = useMemo(
30
+ () => (filter.selectedOptions.to ? new Date(filter.selectedOptions.to) : undefined),
31
+ [filter.selectedOptions.to],
32
+ );
33
+
34
+ const selectedCount = useMemo(() => {
35
+ return from && to ? 1 : 0;
36
+ }, [from, to]);
37
+
38
+ const headerContent = (
39
+ <AccordionHeaderContent>
40
+ <span>{translate(filter.titleTranslationKey, filter.title)}</span>
41
+ {selectedCount > 0 && <CounterTag borderless>{selectedCount}</CounterTag>}
42
+ </AccordionHeaderContent>
43
+ );
44
+
45
+ return (
46
+ <AccordionWrapper
47
+ className={className}
48
+ $hasSelection={selectedCount > 0}
49
+ data-component-name="Catalog/CatalogFilterDateRange"
50
+ >
51
+ <Accordion expanded={false} header={headerContent}>
52
+ <FilterOptions>
53
+ <DateRangeWrapper>
54
+ <DateRangeRow>
55
+ <span>From:</span>
56
+ <DatePicker
57
+ closeCalendar={true}
58
+ format="y-MM-dd"
59
+ dayPlaceholder="DD"
60
+ monthPlaceholder="MM"
61
+ yearPlaceholder="YYYY"
62
+ value={from}
63
+ maxDate={new Date()}
64
+ onChange={(from) => {
65
+ if (Array.isArray(from)) return;
66
+ filter.selectOption({
67
+ ...filter.selectedOptions,
68
+ from: formatDateWithoutTimeZone(from),
69
+ to: formatDateWithoutTimeZone(to),
70
+ });
71
+ }}
72
+ />
73
+ </DateRangeRow>
74
+ <DateRangeRow>
75
+ <span>To:</span>
76
+ <DatePicker
77
+ closeCalendar={true}
78
+ dayPlaceholder="DD"
79
+ monthPlaceholder="MM"
80
+ yearPlaceholder="YYYY"
81
+ format="y-MM-dd"
82
+ minDate={from}
83
+ value={to}
84
+ onChange={(to) => {
85
+ if (Array.isArray(to)) return;
86
+ filter.selectOption({
87
+ ...filter.selectedOptions,
88
+ from: formatDateWithoutTimeZone(from),
89
+ to: formatDateWithoutTimeZone(to),
90
+ });
91
+ }}
92
+ />
93
+ </DateRangeRow>
94
+ </DateRangeWrapper>
95
+ </FilterOptions>
96
+ </Accordion>
97
+ </AccordionWrapper>
98
+ );
99
+ }
100
+
101
+ const AccordionWrapper = styled.div<{ $hasSelection: boolean }>`
102
+ position: relative;
103
+ border-right: 4px solid transparent;
104
+
105
+ ${({ $hasSelection }) =>
106
+ $hasSelection &&
107
+ `border-right-color: var(--color-blueberry-6);
108
+
109
+ &::after {
110
+ content: '';
111
+ position: absolute;
112
+ bottom: -1px;
113
+ right: -4px;
114
+ width: 4px;
115
+ height: 1px;
116
+ background-color: var(--color-blueberry-6);
117
+ z-index: 1;
118
+ }
119
+ `}
120
+ `;
121
+
122
+ const AccordionHeaderContent = styled.div`
123
+ display: flex;
124
+ align-items: center;
125
+ gap: var(--spacing-xs);
126
+ width: 100%;
127
+ `;
128
+
129
+ const DateRangeWrapper = styled.div`
130
+ display: flex;
131
+ flex-direction: column;
132
+ gap: var(--filter-options-gap);
133
+ `;
134
+
135
+ const DateRangeRow = styled.div`
136
+ color: var(--filter-date-picker-color);
137
+ display: flex;
138
+ flex-direction: row;
139
+
140
+ align-items: center;
141
+ gap: var(--filter-date-picker-gap);
142
+
143
+ > span {
144
+ width: var(--filter-date-picker-width);
145
+ color: var(--filter-option-label-color);
146
+ }
147
+ `;
@@ -0,0 +1,136 @@
1
+ import React, { useMemo, useState } from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import type { JSX } from 'react';
5
+ import type { FilterProps, SelectProps, SelectOption } from '@redocly/theme/core/types';
6
+
7
+ import { FilterOption } from '@redocly/theme/components/Filter/FilterOption';
8
+ import { FilterOptionLabel } from '@redocly/theme/components/Filter/FilterOptionLabel';
9
+ import { Select } from '@redocly/theme/components/Select/Select';
10
+ import { CounterTag } from '@redocly/theme/components/Tags/CounterTag';
11
+ import { changeTextCasing } from '@redocly/theme/core/utils';
12
+ import { useThemeHooks } from '@redocly/theme/core/hooks';
13
+ import { Accordion } from '@redocly/theme/components/Accordion/Accordion';
14
+
15
+ export type CatalogFilterSelectProps = FilterProps & {
16
+ className?: string;
17
+ };
18
+
19
+ export function CatalogFilterSelect({
20
+ filter,
21
+ filterValuesCasing,
22
+ showCounter = true,
23
+ className,
24
+ }: CatalogFilterSelectProps): JSX.Element {
25
+ const { useTranslate } = useThemeHooks();
26
+ const { translate } = useTranslate();
27
+ const [isExpanded, setIsExpanded] = useState(false);
28
+
29
+ const selectedCount = useMemo(() => {
30
+ if (filter.selectedOptions instanceof Set) {
31
+ const firstValue = filter.selectedOptions.values().next()?.value;
32
+
33
+ return firstValue && firstValue !== '' ? 1 : 0;
34
+ }
35
+ return 0;
36
+ }, [filter.selectedOptions]);
37
+
38
+ let selectOptions: SelectOption<string>[] = [];
39
+
40
+ const defaultOptionCount = filter.options.reduce((acc, option) => acc + option.count, 0);
41
+
42
+ selectOptions = [
43
+ {
44
+ value: '',
45
+ element: (
46
+ <FilterOption key="all" onClick={() => filter.selectOption('')}>
47
+ <FilterOptionLabel data-translation-key="catalog.filters.select.all">
48
+ {translate('catalog.filters.select.all', 'All')}
49
+ </FilterOptionLabel>
50
+ <CounterTag borderless>{defaultOptionCount}</CounterTag>
51
+ </FilterOption>
52
+ ),
53
+ },
54
+ ...filter.options.map((option) => ({
55
+ value: option.value,
56
+ element: (
57
+ <FilterOption key={option.value}>
58
+ <FilterOptionLabel data-translation-key={option.value}>
59
+ {changeTextCasing(translate(option.value), filterValuesCasing)}
60
+ </FilterOptionLabel>
61
+ {showCounter && <CounterTag borderless>{option.count}</CounterTag>}
62
+ </FilterOption>
63
+ ),
64
+ })),
65
+ ];
66
+
67
+ const headerContent = (
68
+ <AccordionHeaderContent>
69
+ <span>{translate(filter.titleTranslationKey, filter.title)}</span>
70
+ {selectedCount > 0 && <CounterTag borderless>{selectedCount}</CounterTag>}
71
+ </AccordionHeaderContent>
72
+ );
73
+
74
+ return (
75
+ <AccordionWrapper
76
+ className={className}
77
+ $hasSelection={selectedCount > 0 && !isExpanded}
78
+ data-component-name="Catalog/CatalogFilterSelect"
79
+ >
80
+ <Accordion expanded={isExpanded} header={headerContent} onToggle={setIsExpanded}>
81
+ <StyledSelect
82
+ value={
83
+ (filter.selectedOptions instanceof Set ? filter.selectedOptions : new Set())
84
+ .values()
85
+ .next()?.value || ''
86
+ }
87
+ onChange={(value) => filter.selectOption(value)}
88
+ options={selectOptions}
89
+ />
90
+ </Accordion>
91
+ </AccordionWrapper>
92
+ );
93
+ }
94
+
95
+ const AccordionWrapper = styled.div<{ $hasSelection: boolean }>`
96
+ position: relative;
97
+ border-right: 4px solid transparent;
98
+
99
+ ${({ $hasSelection }) =>
100
+ $hasSelection &&
101
+ `border-right-color: var(--color-blueberry-6);
102
+
103
+ &::after {
104
+ content: '';
105
+ position: absolute;
106
+ bottom: -1px;
107
+ right: -4px;
108
+ width: 4px;
109
+ height: 1px;
110
+ background-color: var(--color-blueberry-6);
111
+ z-index: 1;
112
+ }
113
+ `}
114
+ `;
115
+
116
+ const AccordionHeaderContent = styled.div`
117
+ display: flex;
118
+ align-items: center;
119
+ gap: var(--spacing-xs);
120
+ width: 100%;
121
+ `;
122
+
123
+ const StyledSelect = styled(Select)<SelectProps>`
124
+ --select-list-max-width: var(--filter-select-max-width);
125
+
126
+ min-height: var(--filter-select-min-height);
127
+ --select-text-color: var(--filter-select-color);
128
+ border: 1px solid var(--filter-select-border-color);
129
+ --select-border-radius: var(--filter-select-border-radius);
130
+ padding: var(--filter-select-padding);
131
+
132
+ :hover {
133
+ border-color: var(--filter-select-border-color-hover);
134
+ }
135
+ --filter-option-margin: var(--filter-select-option-margin);
136
+ `;
@@ -52,7 +52,6 @@ export function CatalogSelector({
52
52
 
53
53
  export const CatalogSelectWrapper = styled.div`
54
54
  margin-top: var(--catalog-select-wrapper-margin-top);
55
- margin-bottom: var(--catalog-select-wrapper-margin-bottom);
56
55
  `;
57
56
 
58
57
  export const CatalogSelectLabel = styled.label`
@@ -80,7 +80,6 @@ export const catalog = css`
80
80
  * @tokens Catalog selector
81
81
  */
82
82
  --catalog-select-wrapper-margin-top: var(--spacing-xxs);
83
- --catalog-select-wrapper-margin-bottom: var(--spacing-base);
84
83
  --catalog-select-label-margin-bottom: var(--spacing-xxs);
85
84
  --catalog-select-label-font-weight: var(--filter-title-font-weight);
86
85
  --catalog-select-label-font-size: var(--filter-title-font-size);