@gunjo/ui 0.0.1-alpha.0 → 0.0.1-alpha.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 (224) hide show
  1. package/LICENSE +21 -0
  2. package/README.ja.md +90 -0
  3. package/README.md +52 -91
  4. package/package.json +47 -6
  5. package/src/components/display/Accordion.tsx +185 -0
  6. package/src/components/display/AccordionGroup.tsx +155 -0
  7. package/src/components/display/ActionDataTable.tsx +413 -0
  8. package/src/components/display/ActivityTimelineCard.tsx +483 -0
  9. package/src/components/display/AnalyticsCard.tsx +167 -0
  10. package/src/components/display/AssetCard.tsx +242 -0
  11. package/src/components/display/AssetGrid.tsx +164 -0
  12. package/src/components/display/Avatar.tsx +127 -0
  13. package/src/components/display/AvatarGroup.tsx +131 -0
  14. package/src/components/{atoms → display}/Badge.tsx +3 -3
  15. package/src/components/display/BarChart.tsx +247 -0
  16. package/src/components/{molecules → display}/Card.tsx +1 -1
  17. package/src/components/display/Carousel.tsx +593 -0
  18. package/src/components/display/ChartLegend.tsx +124 -0
  19. package/src/components/display/ChatMessage.tsx +382 -0
  20. package/src/components/display/ChoroplethMap.tsx +613 -0
  21. package/src/components/display/Code.tsx +42 -0
  22. package/src/components/display/CodeBlock.tsx +338 -0
  23. package/src/components/display/ColorSwatch.tsx +71 -0
  24. package/src/components/display/ConcentricProgressCard.tsx +545 -0
  25. package/src/components/display/DataTable.tsx +522 -0
  26. package/src/components/display/DistributionBar.tsx +102 -0
  27. package/src/components/display/DocNote.tsx +36 -0
  28. package/src/components/display/DonutChart.tsx +257 -0
  29. package/src/components/display/EmptyState.tsx +44 -0
  30. package/src/components/display/FileTree.tsx +180 -0
  31. package/src/components/display/GaugeChart.tsx +219 -0
  32. package/src/components/display/HeatmapChart.tsx +266 -0
  33. package/src/components/display/Icon.tsx +66 -0
  34. package/src/components/display/ImagePreview.tsx +140 -0
  35. package/src/components/{atoms → display}/Img.tsx +46 -12
  36. package/src/components/display/LabeledDonutCard.tsx +475 -0
  37. package/src/components/display/LineChart.tsx +464 -0
  38. package/src/components/{molecules → display}/List.tsx +20 -13
  39. package/src/components/display/MarkdownRenderer.tsx +157 -0
  40. package/src/components/display/MetadataList.tsx +81 -0
  41. package/src/components/display/MiniDistributionBarCard.tsx +314 -0
  42. package/src/components/display/PieChart.tsx +234 -0
  43. package/src/components/display/QuadrantMatrix.tsx +330 -0
  44. package/src/components/display/RadarChart.tsx +335 -0
  45. package/src/components/display/RadialBarChart.tsx +264 -0
  46. package/src/components/display/RetentionCohortCard.tsx +350 -0
  47. package/src/components/display/RibbonChart.tsx +618 -0
  48. package/src/components/display/SearchableAccordion.tsx +270 -0
  49. package/src/components/display/SegmentTimelineCard.tsx +452 -0
  50. package/src/components/display/SegmentedGaugeCard.tsx +607 -0
  51. package/src/components/display/Spacer.tsx +51 -0
  52. package/src/components/display/SparklineChart.tsx +394 -0
  53. package/src/components/display/StackedBarChart.tsx +393 -0
  54. package/src/components/display/Statistic.tsx +70 -0
  55. package/src/components/{molecules → display}/Table.tsx +22 -7
  56. package/src/components/display/Tag.tsx +80 -0
  57. package/src/components/display/TagEditor.tsx +141 -0
  58. package/src/components/display/Timeline.tsx +121 -0
  59. package/src/components/{atoms → display}/ToolPill.tsx +42 -18
  60. package/src/components/display/TreeView.tsx +226 -0
  61. package/src/components/display/chart-tooltip.tsx +423 -0
  62. package/src/components/display/chart-utils.ts +71 -0
  63. package/src/components/display/circular-chart-utils.ts +147 -0
  64. package/src/components/display/generated/default-variant-keys.ts +90 -0
  65. package/src/components/display/generated/variant-keys.ts +169 -0
  66. package/src/components/{atoms → feedback}/Alert.tsx +12 -5
  67. package/src/components/feedback/Banner.tsx +90 -0
  68. package/src/components/{molecules → feedback}/NotificationCenter.tsx +64 -31
  69. package/src/components/feedback/ProgressWidget.tsx +44 -0
  70. package/src/components/{atoms → feedback}/Spinner.tsx +2 -2
  71. package/src/components/{molecules → feedback}/StatusBar.tsx +4 -4
  72. package/src/components/feedback/StatusScreen.tsx +148 -0
  73. package/src/components/{molecules → feedback}/Stepper.tsx +10 -5
  74. package/src/components/feedback/Toast.tsx +108 -0
  75. package/src/components/feedback/ToastProvider.tsx +78 -0
  76. package/src/components/feedback/generated/default-variant-keys.ts +16 -0
  77. package/src/components/feedback/generated/variant-keys.ts +21 -0
  78. package/src/components/generated/component-manifest.ts +1568 -454
  79. package/src/components/generated/component-style-hints.ts +1958 -718
  80. package/src/components/{atoms → inputs}/ButtonVariants.ts +13 -3
  81. package/src/components/inputs/Calendar.tsx +212 -0
  82. package/src/components/inputs/ChatComposer.tsx +75 -0
  83. package/src/components/inputs/ChatInput.tsx +528 -0
  84. package/src/components/{atoms → inputs}/Checkbox.tsx +2 -2
  85. package/src/components/inputs/Combobox.tsx +175 -0
  86. package/src/components/inputs/CopyButton.tsx +187 -0
  87. package/src/components/inputs/DatePicker.tsx +519 -0
  88. package/src/components/inputs/DateRangePicker.tsx +878 -0
  89. package/src/components/inputs/EditableField.tsx +182 -0
  90. package/src/components/{organisms → inputs}/FileUploader.tsx +24 -9
  91. package/src/components/inputs/FilterButton.tsx +163 -0
  92. package/src/components/{molecules → inputs}/Form.tsx +20 -3
  93. package/src/components/{atoms → inputs}/Input.tsx +2 -0
  94. package/src/components/inputs/InputOTP.tsx +75 -0
  95. package/src/components/inputs/Mention.tsx +279 -0
  96. package/src/components/inputs/NumberInput.tsx +109 -0
  97. package/src/components/inputs/PasswordGroup.tsx +138 -0
  98. package/src/components/inputs/PasswordInput.tsx +74 -0
  99. package/src/components/inputs/PasswordRequirementList.tsx +96 -0
  100. package/src/components/inputs/PasswordStrengthMeter.tsx +93 -0
  101. package/src/components/inputs/PhoneInput.tsx +99 -0
  102. package/src/components/inputs/PostalCodeInput.tsx +98 -0
  103. package/src/components/inputs/RangeSlider.tsx +129 -0
  104. package/src/components/inputs/SearchInput.tsx +76 -0
  105. package/src/components/inputs/Select.tsx +39 -0
  106. package/src/components/{atoms → inputs}/Slider.tsx +18 -5
  107. package/src/components/{molecules → inputs}/SortButton.tsx +5 -2
  108. package/src/components/{atoms → inputs}/Switch.tsx +15 -4
  109. package/src/components/inputs/TagInput.tsx +114 -0
  110. package/src/components/{atoms → inputs}/Textarea.tsx +1 -0
  111. package/src/components/inputs/TimePicker.tsx +150 -0
  112. package/src/components/inputs/Toggle.tsx +48 -0
  113. package/src/components/{atoms → inputs}/ToggleGroup.tsx +2 -2
  114. package/src/components/inputs/TooltipButton.tsx +148 -0
  115. package/src/components/inputs/VoiceInputButton.tsx +317 -0
  116. package/src/components/inputs/calendar-holidays.ts +56 -0
  117. package/src/components/inputs/generated/default-variant-keys.ts +32 -0
  118. package/src/components/{atoms → inputs}/generated/variant-keys.ts +19 -27
  119. package/src/components/layout/AspectRatio.tsx +12 -0
  120. package/src/components/layout/AssetInspectorPanel.tsx +416 -0
  121. package/src/components/layout/Cluster.tsx +56 -0
  122. package/src/components/layout/CollapsiblePanelToggle.tsx +94 -0
  123. package/src/components/layout/Container.tsx +43 -0
  124. package/src/components/layout/DeviceFrame.tsx +227 -0
  125. package/src/components/layout/Grid.tsx +65 -0
  126. package/src/components/layout/HStack.tsx +73 -0
  127. package/src/components/{organisms → layout}/InspectorPanel.tsx +6 -5
  128. package/src/components/layout/MarqueeFrame.tsx +158 -0
  129. package/src/components/layout/Resizable.tsx +94 -0
  130. package/src/components/layout/ScrollArea.tsx +71 -0
  131. package/src/components/{organisms → layout}/SpatialCanvas.tsx +12 -7
  132. package/src/components/layout/VStack.tsx +69 -0
  133. package/src/components/layout/generated/default-variant-keys.ts +16 -0
  134. package/src/components/layout/generated/variant-keys.ts +21 -0
  135. package/src/components/{molecules → navigation}/Breadcrumb.tsx +5 -4
  136. package/src/components/navigation/Command.tsx +266 -0
  137. package/src/components/navigation/CommandPalette.tsx +83 -0
  138. package/src/components/navigation/DocumentPager.tsx +171 -0
  139. package/src/components/navigation/Footer.tsx +88 -0
  140. package/src/components/navigation/Header.tsx +80 -0
  141. package/src/components/{molecules → navigation}/Menubar.tsx +45 -12
  142. package/src/components/navigation/NavigationMenu.tsx +128 -0
  143. package/src/components/navigation/PageAside.tsx +84 -0
  144. package/src/components/{molecules → navigation}/Pagination.tsx +60 -7
  145. package/src/components/{organisms → navigation}/RightRail.tsx +1 -1
  146. package/src/components/navigation/Sidebar.tsx +223 -0
  147. package/src/components/navigation/SidebarItem.tsx +160 -0
  148. package/src/components/{molecules → navigation}/Tabs.tsx +2 -2
  149. package/src/components/navigation/TextLink.tsx +71 -0
  150. package/src/components/navigation/generated/default-variant-keys.ts +12 -0
  151. package/src/components/navigation/generated/variant-keys.ts +13 -0
  152. package/src/components/overlay/AIChatInput.tsx +5 -0
  153. package/src/components/overlay/AIChatMessage.tsx +6 -0
  154. package/src/components/overlay/AlertDialog.tsx +145 -0
  155. package/src/components/overlay/ChatPanel.tsx +180 -0
  156. package/src/components/{molecules → overlay}/ContextMenu.tsx +65 -29
  157. package/src/components/{molecules → overlay}/Dialog.tsx +21 -13
  158. package/src/components/overlay/Drawer.tsx +131 -0
  159. package/src/components/{molecules → overlay}/DropdownMenu.tsx +52 -17
  160. package/src/components/overlay/FloatingPanel.tsx +90 -0
  161. package/src/components/overlay/HoverCard.tsx +36 -0
  162. package/src/components/overlay/MediaLightbox.tsx +403 -0
  163. package/src/components/overlay/MediaPickerDialog.tsx +198 -0
  164. package/src/components/overlay/Modal.tsx +103 -0
  165. package/src/components/overlay/OnboardingFlow.tsx +172 -0
  166. package/src/components/overlay/Popover.tsx +36 -0
  167. package/src/components/overlay/ShareModal.tsx +324 -0
  168. package/src/components/{molecules → overlay}/Sheet.tsx +76 -19
  169. package/src/components/overlay/Tooltip.tsx +130 -0
  170. package/src/components/overlay/generated/default-variant-keys.ts +14 -0
  171. package/src/components/overlay/generated/variant-keys.ts +17 -0
  172. package/src/components/patterns/BlogTemplate.tsx +46 -0
  173. package/src/components/{templates → patterns}/DashboardTemplate.tsx +2 -2
  174. package/src/components/patterns/DocsTemplate.tsx +41 -0
  175. package/src/components/{templates → patterns}/MediaLibraryTemplate.tsx +1 -1
  176. package/src/components/patterns/OnboardingTemplate.tsx +32 -0
  177. package/src/components/patterns/PricingTemplate.tsx +106 -0
  178. package/src/globals.css +173 -22
  179. package/src/index.ts +177 -76
  180. package/tailwind-theme-extend.cjs +48 -3
  181. package/design/atoms-metadata.json +0 -82
  182. package/design/molecules-metadata.json +0 -130
  183. package/design/organisms-metadata.json +0 -38
  184. package/design/templates-metadata.json +0 -38
  185. package/src/components/atoms/Avatar.tsx +0 -57
  186. package/src/components/atoms/Select.tsx +0 -28
  187. package/src/components/atoms/generated/default-variant-keys.ts +0 -36
  188. package/src/components/molecules/AIChatInput.tsx +0 -140
  189. package/src/components/molecules/AIChatMessage.tsx +0 -109
  190. package/src/components/molecules/Accordion.tsx +0 -99
  191. package/src/components/molecules/Calendar.tsx +0 -60
  192. package/src/components/molecules/Carousel.tsx +0 -261
  193. package/src/components/molecules/Command.tsx +0 -152
  194. package/src/components/molecules/FilterButton.tsx +0 -133
  195. package/src/components/molecules/HoverCard.tsx +0 -29
  196. package/src/components/molecules/Modal.tsx +0 -66
  197. package/src/components/molecules/Popover.tsx +0 -31
  198. package/src/components/molecules/ProgressWidget.tsx +0 -40
  199. package/src/components/molecules/Resizable.tsx +0 -47
  200. package/src/components/molecules/ScrollArea.tsx +0 -48
  201. package/src/components/molecules/SidebarItem.tsx +0 -134
  202. package/src/components/molecules/Toast.tsx +0 -57
  203. package/src/components/molecules/Tooltip.tsx +0 -30
  204. package/src/components/molecules/generated/default-variant-keys.ts +0 -22
  205. package/src/components/molecules/generated/variant-keys.ts +0 -33
  206. package/src/components/organisms/CommandPalette.tsx +0 -58
  207. package/src/components/organisms/FloatingPanel.tsx +0 -46
  208. package/src/components/organisms/ShareModal.tsx +0 -182
  209. package/src/components/organisms/ToastProvider.tsx +0 -49
  210. /package/src/components/{atoms → display}/Kbd.tsx +0 -0
  211. /package/src/components/{atoms → display}/Separator.tsx +0 -0
  212. /package/src/components/{atoms → display}/Skeleton.tsx +0 -0
  213. /package/src/components/{atoms → feedback}/Progress.tsx +0 -0
  214. /package/src/components/{atoms → inputs}/Button.tsx +0 -0
  215. /package/src/components/{atoms → inputs}/Label.tsx +0 -0
  216. /package/src/components/{atoms → inputs}/RadioGroup.tsx +0 -0
  217. /package/src/components/{organisms → navigation}/AppRail.tsx +0 -0
  218. /package/src/components/{templates → patterns}/AuthTemplate.tsx +0 -0
  219. /package/src/components/{templates → patterns}/BannalyzeTemplate.tsx +0 -0
  220. /package/src/components/{templates → patterns}/ChatTemplate.tsx +0 -0
  221. /package/src/components/{templates → patterns}/EditorTemplate.tsx +0 -0
  222. /package/src/components/{templates → patterns}/KanbanTemplate.tsx +0 -0
  223. /package/src/components/{templates → patterns}/LandingTemplate.tsx +0 -0
  224. /package/src/components/{templates → patterns}/SettingsTemplate.tsx +0 -0
@@ -0,0 +1,270 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { IconSearch as Search } from "@tabler/icons-react"
5
+
6
+ import { cn } from "../../lib/utils"
7
+ import { Button } from "../inputs/Button"
8
+ import { SearchInput } from "../inputs/SearchInput"
9
+ import { Tabs, TabsList, TabsTrigger } from "../navigation/Tabs"
10
+ import { AccordionContent, AccordionItem, AccordionTrigger } from "./Accordion"
11
+ import { AccordionGroup } from "./AccordionGroup"
12
+ import { Badge } from "./Badge"
13
+ import { EmptyState } from "./EmptyState"
14
+ import { Icon } from "./Icon"
15
+ import { searchableAccordionDefaultVariantKey } from "./generated/default-variant-keys"
16
+ import type { SearchableAccordionVariantKey } from "./generated/variant-keys"
17
+
18
+ export type SearchableAccordionItem = {
19
+ id: string
20
+ title: React.ReactNode
21
+ body: React.ReactNode
22
+ category?: string
23
+ keywords?: string[]
24
+ searchText?: string
25
+ }
26
+
27
+ export type SearchableAccordionCategory = {
28
+ id: string
29
+ label: React.ReactNode
30
+ }
31
+
32
+ export type SearchableAccordionLabels = {
33
+ searchPlaceholder?: string
34
+ clearSearchLabel?: string
35
+ allCategoryLabel?: React.ReactNode
36
+ resultCountLabel?: (visibleCount: number, totalCount: number) => React.ReactNode
37
+ clearFiltersLabel?: React.ReactNode
38
+ emptyTitle?: React.ReactNode
39
+ emptyDescription?: React.ReactNode
40
+ expandLabel?: string
41
+ collapseLabel?: string
42
+ controlsLabel?: string
43
+ }
44
+
45
+ export interface SearchableAccordionProps
46
+ extends Omit<React.HTMLAttributes<HTMLDivElement>, "defaultValue" | "onChange"> {
47
+ items: SearchableAccordionItem[]
48
+ variant?: SearchableAccordionVariantKey
49
+ searchValue?: string
50
+ defaultSearchValue?: string
51
+ onSearchValueChange?: (value: string) => void
52
+ categoryValue?: string
53
+ defaultCategoryValue?: string
54
+ onCategoryValueChange?: (value: string) => void
55
+ openValue?: string[]
56
+ defaultOpenValue?: string[]
57
+ onOpenValueChange?: (value: string[]) => void
58
+ categories?: SearchableAccordionCategory[]
59
+ showCategoryTabs?: boolean
60
+ label?: React.ReactNode
61
+ description?: React.ReactNode
62
+ labels?: SearchableAccordionLabels
63
+ emptyState?: React.ReactNode
64
+ }
65
+
66
+ const searchableAccordionVariantClasses: Record<SearchableAccordionVariantKey, string> = {
67
+ default: "space-y-4 p-0",
68
+ withCategories: "space-y-4 p-0",
69
+ empty: "space-y-4 p-0",
70
+ }
71
+
72
+ function normalizeNode(value: React.ReactNode) {
73
+ return typeof value === "string" || typeof value === "number" ? String(value) : ""
74
+ }
75
+
76
+ function includesQuery(item: SearchableAccordionItem, query: string) {
77
+ if (!query) return true
78
+ const haystack = [
79
+ normalizeNode(item.title),
80
+ normalizeNode(item.body),
81
+ item.category,
82
+ item.searchText,
83
+ ...(item.keywords ?? []),
84
+ ]
85
+ .filter(Boolean)
86
+ .join(" ")
87
+ .toLowerCase()
88
+
89
+ return haystack.includes(query.toLowerCase())
90
+ }
91
+
92
+ function deriveCategories(items: SearchableAccordionItem[]) {
93
+ const ids = Array.from(new Set(items.map((item) => item.category).filter(Boolean))) as string[]
94
+ return ids.map((id) => ({ id, label: id }))
95
+ }
96
+
97
+ const SearchableAccordion = React.forwardRef<HTMLDivElement, SearchableAccordionProps>(
98
+ (
99
+ {
100
+ className,
101
+ items,
102
+ variant = searchableAccordionDefaultVariantKey,
103
+ searchValue,
104
+ defaultSearchValue = "",
105
+ onSearchValueChange,
106
+ categoryValue,
107
+ defaultCategoryValue = "all",
108
+ onCategoryValueChange,
109
+ openValue,
110
+ defaultOpenValue = [],
111
+ onOpenValueChange,
112
+ categories: categoriesProp,
113
+ showCategoryTabs = true,
114
+ label,
115
+ description,
116
+ labels,
117
+ emptyState,
118
+ ...props
119
+ },
120
+ ref
121
+ ) => {
122
+ const [internalSearchValue, setInternalSearchValue] = React.useState(defaultSearchValue)
123
+ const [internalCategoryValue, setInternalCategoryValue] = React.useState(defaultCategoryValue)
124
+ const resolvedSearchValue = searchValue ?? internalSearchValue
125
+ const resolvedCategoryValue = categoryValue ?? internalCategoryValue
126
+ const allCategoryId = "all"
127
+ const categories = React.useMemo(
128
+ () => categoriesProp ?? deriveCategories(items),
129
+ [categoriesProp, items]
130
+ )
131
+
132
+ const setSearchValue = React.useCallback(
133
+ (nextValue: string) => {
134
+ if (searchValue === undefined) {
135
+ setInternalSearchValue(nextValue)
136
+ }
137
+ onSearchValueChange?.(nextValue)
138
+ },
139
+ [onSearchValueChange, searchValue]
140
+ )
141
+
142
+ const setCategoryValue = React.useCallback(
143
+ (nextValue: string) => {
144
+ if (categoryValue === undefined) {
145
+ setInternalCategoryValue(nextValue)
146
+ }
147
+ onCategoryValueChange?.(nextValue)
148
+ },
149
+ [categoryValue, onCategoryValueChange]
150
+ )
151
+
152
+ const filteredItems = React.useMemo(
153
+ () =>
154
+ items.filter((item) => {
155
+ const categoryMatches =
156
+ resolvedCategoryValue === allCategoryId ||
157
+ item.category === resolvedCategoryValue
158
+ return categoryMatches && includesQuery(item, resolvedSearchValue.trim())
159
+ }),
160
+ [items, resolvedCategoryValue, resolvedSearchValue]
161
+ )
162
+
163
+ const visibleIds = React.useMemo(
164
+ () => filteredItems.map((item) => item.id),
165
+ [filteredItems]
166
+ )
167
+
168
+ const showTabs = showCategoryTabs && categories.length > 0
169
+ const hasActiveFilter =
170
+ resolvedSearchValue.trim().length > 0 || resolvedCategoryValue !== allCategoryId
171
+
172
+ const clearFilters = React.useCallback(() => {
173
+ setSearchValue("")
174
+ setCategoryValue(allCategoryId)
175
+ }, [setCategoryValue, setSearchValue])
176
+
177
+ return (
178
+ <div
179
+ ref={ref}
180
+ className={cn(searchableAccordionVariantClasses[variant], className)}
181
+ data-slot="searchable-accordion"
182
+ {...props}
183
+ >
184
+ {label || description ? (
185
+ <div className="space-y-1">
186
+ {label ? <div className="text-sm font-medium text-foreground">{label}</div> : null}
187
+ {description ? <p className="text-sm text-muted-foreground">{description}</p> : null}
188
+ </div>
189
+ ) : null}
190
+
191
+ <div className="space-y-3">
192
+ <SearchInput
193
+ value={resolvedSearchValue}
194
+ onValueChange={setSearchValue}
195
+ placeholder={labels?.searchPlaceholder ?? "Search items..."}
196
+ clearLabel={labels?.clearSearchLabel ?? "Clear search"}
197
+ />
198
+ {showTabs ? (
199
+ <Tabs
200
+ value={resolvedCategoryValue}
201
+ onValueChange={setCategoryValue}
202
+ className="w-full border-0"
203
+ >
204
+ <TabsList className="min-h-0 w-full justify-start overflow-x-auto overflow-y-hidden p-1">
205
+ <TabsTrigger value={allCategoryId} className="h-8 px-3">
206
+ {labels?.allCategoryLabel ?? "All"}
207
+ </TabsTrigger>
208
+ {categories.map((category) => (
209
+ <TabsTrigger key={category.id} value={category.id} className="h-8 px-3">
210
+ {category.label}
211
+ </TabsTrigger>
212
+ ))}
213
+ </TabsList>
214
+ </Tabs>
215
+ ) : null}
216
+ </div>
217
+
218
+ <div className="flex flex-wrap items-center justify-between gap-2 text-sm text-muted-foreground">
219
+ <span>
220
+ {labels?.resultCountLabel
221
+ ? labels.resultCountLabel(filteredItems.length, items.length)
222
+ : `${filteredItems.length} / ${items.length}`}
223
+ </span>
224
+ <Badge variant={filteredItems.length > 0 ? "secondary" : "outline"}>
225
+ {resolvedCategoryValue === allCategoryId
226
+ ? labels?.allCategoryLabel ?? "All"
227
+ : categories.find((category) => category.id === resolvedCategoryValue)?.label ?? resolvedCategoryValue}
228
+ </Badge>
229
+ </div>
230
+
231
+ {filteredItems.length > 0 ? (
232
+ <AccordionGroup
233
+ values={visibleIds}
234
+ value={openValue}
235
+ defaultValue={defaultOpenValue}
236
+ onValueChange={onOpenValueChange}
237
+ expandLabel={labels?.expandLabel ?? "Open visible items"}
238
+ collapseLabel={labels?.collapseLabel ?? "Close visible items"}
239
+ controlsLabel={labels?.controlsLabel ?? "Searchable accordion controls"}
240
+ >
241
+ {filteredItems.map((item) => (
242
+ <AccordionItem key={item.id} value={item.id}>
243
+ <AccordionTrigger>{item.title}</AccordionTrigger>
244
+ <AccordionContent>{item.body}</AccordionContent>
245
+ </AccordionItem>
246
+ ))}
247
+ </AccordionGroup>
248
+ ) : (
249
+ emptyState ?? (
250
+ <EmptyState
251
+ icon={<Icon icon={Search} size="md" decorative />}
252
+ title={labels?.emptyTitle ?? "No matching items"}
253
+ description={labels?.emptyDescription ?? "Change the search term or category."}
254
+ action={
255
+ hasActiveFilter ? (
256
+ <Button type="button" variant="outline" size="sm" onClick={clearFilters}>
257
+ {labels?.clearFiltersLabel ?? "Clear filters"}
258
+ </Button>
259
+ ) : null
260
+ }
261
+ />
262
+ )
263
+ )}
264
+ </div>
265
+ )
266
+ }
267
+ )
268
+ SearchableAccordion.displayName = "SearchableAccordion"
269
+
270
+ export { SearchableAccordion }