@central-ui/react 0.0.0-stage → 0.1.1

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 (292) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +33 -2
  3. package/dist/components/accordion/index.d.ts +73 -0
  4. package/dist/components/accordion/index.d.ts.map +1 -0
  5. package/dist/components/accordion/index.js +92 -0
  6. package/dist/components/accordion/index.js.map +1 -0
  7. package/dist/components/autocomplete/index.d.ts +365 -0
  8. package/dist/components/autocomplete/index.d.ts.map +1 -0
  9. package/dist/components/autocomplete/index.js +369 -0
  10. package/dist/components/autocomplete/index.js.map +1 -0
  11. package/dist/components/avatar/index.d.ts +48 -0
  12. package/dist/components/avatar/index.d.ts.map +1 -0
  13. package/dist/components/avatar/index.js +60 -0
  14. package/dist/components/avatar/index.js.map +1 -0
  15. package/dist/components/badge/index.d.ts +29 -0
  16. package/dist/components/badge/index.d.ts.map +1 -0
  17. package/dist/components/badge/index.js +36 -0
  18. package/dist/components/badge/index.js.map +1 -0
  19. package/dist/components/banner/index.d.ts +71 -0
  20. package/dist/components/banner/index.d.ts.map +1 -0
  21. package/dist/components/banner/index.js +119 -0
  22. package/dist/components/banner/index.js.map +1 -0
  23. package/dist/components/breadcrumb/index.d.ts +31 -0
  24. package/dist/components/breadcrumb/index.d.ts.map +1 -0
  25. package/dist/components/breadcrumb/index.js +42 -0
  26. package/dist/components/breadcrumb/index.js.map +1 -0
  27. package/dist/components/button/index.d.ts +48 -0
  28. package/dist/components/button/index.d.ts.map +1 -0
  29. package/dist/components/button/index.js +60 -0
  30. package/dist/components/button/index.js.map +1 -0
  31. package/dist/components/card/index.d.ts +34 -0
  32. package/dist/components/card/index.d.ts.map +1 -0
  33. package/dist/components/card/index.js +45 -0
  34. package/dist/components/card/index.js.map +1 -0
  35. package/dist/components/checkbox/index.d.ts +66 -0
  36. package/dist/components/checkbox/index.d.ts.map +1 -0
  37. package/dist/components/checkbox/index.js +146 -0
  38. package/dist/components/checkbox/index.js.map +1 -0
  39. package/dist/components/chip/index.d.ts +26 -0
  40. package/dist/components/chip/index.d.ts.map +1 -0
  41. package/dist/components/chip/index.js +40 -0
  42. package/dist/components/chip/index.js.map +1 -0
  43. package/dist/components/combobox/index.d.ts +427 -0
  44. package/dist/components/combobox/index.d.ts.map +1 -0
  45. package/dist/components/combobox/index.js +416 -0
  46. package/dist/components/combobox/index.js.map +1 -0
  47. package/dist/components/datepicker/index.d.ts +297 -0
  48. package/dist/components/datepicker/index.d.ts.map +1 -0
  49. package/dist/components/datepicker/index.js +507 -0
  50. package/dist/components/datepicker/index.js.map +1 -0
  51. package/dist/components/dialog/index.d.ts +88 -0
  52. package/dist/components/dialog/index.d.ts.map +1 -0
  53. package/dist/components/dialog/index.js +102 -0
  54. package/dist/components/dialog/index.js.map +1 -0
  55. package/dist/components/divider/index.d.ts +21 -0
  56. package/dist/components/divider/index.d.ts.map +1 -0
  57. package/dist/components/divider/index.js +24 -0
  58. package/dist/components/divider/index.js.map +1 -0
  59. package/dist/components/drawer/index.d.ts +99 -0
  60. package/dist/components/drawer/index.d.ts.map +1 -0
  61. package/dist/components/drawer/index.js +137 -0
  62. package/dist/components/drawer/index.js.map +1 -0
  63. package/dist/components/empty/index.d.ts +40 -0
  64. package/dist/components/empty/index.d.ts.map +1 -0
  65. package/dist/components/empty/index.js +61 -0
  66. package/dist/components/empty/index.js.map +1 -0
  67. package/dist/components/fileupload/index.d.ts +107 -0
  68. package/dist/components/fileupload/index.d.ts.map +1 -0
  69. package/dist/components/fileupload/index.js +185 -0
  70. package/dist/components/fileupload/index.js.map +1 -0
  71. package/dist/components/form/index.d.ts +34 -0
  72. package/dist/components/form/index.d.ts.map +1 -0
  73. package/dist/components/form/index.js +35 -0
  74. package/dist/components/form/index.js.map +1 -0
  75. package/dist/components/input/index.d.ts +85 -0
  76. package/dist/components/input/index.d.ts.map +1 -0
  77. package/dist/components/input/index.js +249 -0
  78. package/dist/components/input/index.js.map +1 -0
  79. package/dist/components/menu/index.d.ts +115 -0
  80. package/dist/components/menu/index.d.ts.map +1 -0
  81. package/dist/components/menu/index.js +188 -0
  82. package/dist/components/menu/index.js.map +1 -0
  83. package/dist/components/metric/index.d.ts +24 -0
  84. package/dist/components/metric/index.d.ts.map +1 -0
  85. package/dist/components/metric/index.js +26 -0
  86. package/dist/components/metric/index.js.map +1 -0
  87. package/dist/components/numberinput/index.d.ts +105 -0
  88. package/dist/components/numberinput/index.d.ts.map +1 -0
  89. package/dist/components/numberinput/index.js +240 -0
  90. package/dist/components/numberinput/index.js.map +1 -0
  91. package/dist/components/otpinput/index.d.ts +63 -0
  92. package/dist/components/otpinput/index.d.ts.map +1 -0
  93. package/dist/components/otpinput/index.js +133 -0
  94. package/dist/components/otpinput/index.js.map +1 -0
  95. package/dist/components/page/index.d.ts +54 -0
  96. package/dist/components/page/index.d.ts.map +1 -0
  97. package/dist/components/page/index.js +100 -0
  98. package/dist/components/page/index.js.map +1 -0
  99. package/dist/components/passwordinput/index.d.ts +88 -0
  100. package/dist/components/passwordinput/index.d.ts.map +1 -0
  101. package/dist/components/passwordinput/index.js +251 -0
  102. package/dist/components/passwordinput/index.js.map +1 -0
  103. package/dist/components/popover/index.d.ts +67 -0
  104. package/dist/components/popover/index.d.ts.map +1 -0
  105. package/dist/components/popover/index.js +76 -0
  106. package/dist/components/popover/index.js.map +1 -0
  107. package/dist/components/radio/index.d.ts +90 -0
  108. package/dist/components/radio/index.d.ts.map +1 -0
  109. package/dist/components/radio/index.js +232 -0
  110. package/dist/components/radio/index.js.map +1 -0
  111. package/dist/components/ratinginput/index.d.ts +59 -0
  112. package/dist/components/ratinginput/index.d.ts.map +1 -0
  113. package/dist/components/ratinginput/index.js +88 -0
  114. package/dist/components/ratinginput/index.js.map +1 -0
  115. package/dist/components/select/index.d.ts +205 -0
  116. package/dist/components/select/index.d.ts.map +1 -0
  117. package/dist/components/select/index.js +234 -0
  118. package/dist/components/select/index.js.map +1 -0
  119. package/dist/components/skeleton/index.d.ts +19 -0
  120. package/dist/components/skeleton/index.d.ts.map +1 -0
  121. package/dist/components/skeleton/index.js +20 -0
  122. package/dist/components/skeleton/index.js.map +1 -0
  123. package/dist/components/slider/index.d.ts +72 -0
  124. package/dist/components/slider/index.d.ts.map +1 -0
  125. package/dist/components/slider/index.js +159 -0
  126. package/dist/components/slider/index.js.map +1 -0
  127. package/dist/components/spinner/index.d.ts +14 -0
  128. package/dist/components/spinner/index.d.ts.map +1 -0
  129. package/dist/components/spinner/index.js +15 -0
  130. package/dist/components/spinner/index.js.map +1 -0
  131. package/dist/components/splitter/index.d.ts +49 -0
  132. package/dist/components/splitter/index.d.ts.map +1 -0
  133. package/dist/components/splitter/index.js +67 -0
  134. package/dist/components/splitter/index.js.map +1 -0
  135. package/dist/components/stepper/index.d.ts +82 -0
  136. package/dist/components/stepper/index.d.ts.map +1 -0
  137. package/dist/components/stepper/index.js +100 -0
  138. package/dist/components/stepper/index.js.map +1 -0
  139. package/dist/components/switch/index.d.ts +57 -0
  140. package/dist/components/switch/index.d.ts.map +1 -0
  141. package/dist/components/switch/index.js +101 -0
  142. package/dist/components/switch/index.js.map +1 -0
  143. package/dist/components/table/index.d.ts +110 -0
  144. package/dist/components/table/index.d.ts.map +1 -0
  145. package/dist/components/table/index.js +211 -0
  146. package/dist/components/table/index.js.map +1 -0
  147. package/dist/components/tabs/index.d.ts +67 -0
  148. package/dist/components/tabs/index.d.ts.map +1 -0
  149. package/dist/components/tabs/index.js +78 -0
  150. package/dist/components/tabs/index.js.map +1 -0
  151. package/dist/components/textarea/index.d.ts +72 -0
  152. package/dist/components/textarea/index.d.ts.map +1 -0
  153. package/dist/components/textarea/index.js +205 -0
  154. package/dist/components/textarea/index.js.map +1 -0
  155. package/dist/components/textlink/index.d.ts +39 -0
  156. package/dist/components/textlink/index.d.ts.map +1 -0
  157. package/dist/components/textlink/index.js +42 -0
  158. package/dist/components/textlink/index.js.map +1 -0
  159. package/dist/components/toast/index.d.ts +111 -0
  160. package/dist/components/toast/index.d.ts.map +1 -0
  161. package/dist/components/toast/index.js +205 -0
  162. package/dist/components/toast/index.js.map +1 -0
  163. package/dist/components/toggle/index.d.ts +41 -0
  164. package/dist/components/toggle/index.d.ts.map +1 -0
  165. package/dist/components/toggle/index.js +78 -0
  166. package/dist/components/toggle/index.js.map +1 -0
  167. package/dist/components/tooltip/index.d.ts +44 -0
  168. package/dist/components/tooltip/index.d.ts.map +1 -0
  169. package/dist/components/tooltip/index.js +51 -0
  170. package/dist/components/tooltip/index.js.map +1 -0
  171. package/dist/index.d.ts +50 -0
  172. package/dist/index.d.ts.map +1 -0
  173. package/dist/index.js +46 -0
  174. package/dist/index.js.map +1 -0
  175. package/dist/types/Style.d.ts +14 -0
  176. package/dist/types/Style.d.ts.map +1 -0
  177. package/dist/types/Style.js +2 -0
  178. package/dist/types/Style.js.map +1 -0
  179. package/dist/types/Tone.d.ts +4 -0
  180. package/dist/types/Tone.d.ts.map +1 -0
  181. package/dist/types/Tone.js +2 -0
  182. package/dist/types/Tone.js.map +1 -0
  183. package/dist/utils/ArkStateToDataAttributes.d.ts +4 -0
  184. package/dist/utils/ArkStateToDataAttributes.d.ts.map +1 -0
  185. package/dist/utils/ArkStateToDataAttributes.js +9 -0
  186. package/dist/utils/ArkStateToDataAttributes.js.map +1 -0
  187. package/dist/utils/Tone.d.ts +3 -0
  188. package/dist/utils/Tone.d.ts.map +1 -0
  189. package/dist/utils/Tone.js +7 -0
  190. package/dist/utils/Tone.js.map +1 -0
  191. package/dist/utils/useForwardClickToInput.d.ts +4 -0
  192. package/dist/utils/useForwardClickToInput.d.ts.map +1 -0
  193. package/dist/utils/useForwardClickToInput.js +27 -0
  194. package/dist/utils/useForwardClickToInput.js.map +1 -0
  195. package/dist/utils/virtualization/useVirtualizedAutocomplete.d.ts +198 -0
  196. package/dist/utils/virtualization/useVirtualizedAutocomplete.d.ts.map +1 -0
  197. package/dist/utils/virtualization/useVirtualizedAutocomplete.js +180 -0
  198. package/dist/utils/virtualization/useVirtualizedAutocomplete.js.map +1 -0
  199. package/dist/utils/virtualization/useVirtualizedCombobox.d.ts +200 -0
  200. package/dist/utils/virtualization/useVirtualizedCombobox.d.ts.map +1 -0
  201. package/dist/utils/virtualization/useVirtualizedCombobox.js +174 -0
  202. package/dist/utils/virtualization/useVirtualizedCombobox.js.map +1 -0
  203. package/dist/utils/virtualization/useVirtualizedTable.d.ts +169 -0
  204. package/dist/utils/virtualization/useVirtualizedTable.d.ts.map +1 -0
  205. package/dist/utils/virtualization/useVirtualizedTable.js +127 -0
  206. package/dist/utils/virtualization/useVirtualizedTable.js.map +1 -0
  207. package/package.json +64 -4
  208. package/src/presets/shopify-polaris/shopify-polaris-accordion.css +28 -0
  209. package/src/presets/shopify-polaris/shopify-polaris-autocomplete.css +155 -0
  210. package/src/presets/shopify-polaris/shopify-polaris-avatar.css +47 -0
  211. package/src/presets/shopify-polaris/shopify-polaris-badge.css +27 -0
  212. package/src/presets/shopify-polaris/shopify-polaris-banner.css +111 -0
  213. package/src/presets/shopify-polaris/shopify-polaris-breadcrumb.css +16 -0
  214. package/src/presets/shopify-polaris/shopify-polaris-button.css +79 -0
  215. package/src/presets/shopify-polaris/shopify-polaris-card.css +28 -0
  216. package/src/presets/shopify-polaris/shopify-polaris-checkbox.css +52 -0
  217. package/src/presets/shopify-polaris/shopify-polaris-chip.css +16 -0
  218. package/src/presets/shopify-polaris/shopify-polaris-combobox.css +179 -0
  219. package/src/presets/shopify-polaris/shopify-polaris-datepicker.css +183 -0
  220. package/src/presets/shopify-polaris/shopify-polaris-dialog.css +40 -0
  221. package/src/presets/shopify-polaris/shopify-polaris-divider.css +12 -0
  222. package/src/presets/shopify-polaris/shopify-polaris-drawer.css +40 -0
  223. package/src/presets/shopify-polaris/shopify-polaris-empty.css +32 -0
  224. package/src/presets/shopify-polaris/shopify-polaris-fileupload.css +78 -0
  225. package/src/presets/shopify-polaris/shopify-polaris-form.css +8 -0
  226. package/src/presets/shopify-polaris/shopify-polaris-input.css +72 -0
  227. package/src/presets/shopify-polaris/shopify-polaris-menu.css +112 -0
  228. package/src/presets/shopify-polaris/shopify-polaris-metric.css +16 -0
  229. package/src/presets/shopify-polaris/shopify-polaris-numberinput.css +88 -0
  230. package/src/presets/shopify-polaris/shopify-polaris-otpinput.css +42 -0
  231. package/src/presets/shopify-polaris/shopify-polaris-page.css +69 -0
  232. package/src/presets/shopify-polaris/shopify-polaris-passwordinput.css +76 -0
  233. package/src/presets/shopify-polaris/shopify-polaris-popover.css +47 -0
  234. package/src/presets/shopify-polaris/shopify-polaris-radio.css +54 -0
  235. package/src/presets/shopify-polaris/shopify-polaris-ratinginput.css +42 -0
  236. package/src/presets/shopify-polaris/shopify-polaris-select.css +119 -0
  237. package/src/presets/shopify-polaris/shopify-polaris-skeleton.css +8 -0
  238. package/src/presets/shopify-polaris/shopify-polaris-slider.css +86 -0
  239. package/src/presets/shopify-polaris/shopify-polaris-spinner.css +16 -0
  240. package/src/presets/shopify-polaris/shopify-polaris-splitter.css +34 -0
  241. package/src/presets/shopify-polaris/shopify-polaris-stepper.css +55 -0
  242. package/src/presets/shopify-polaris/shopify-polaris-switch.css +38 -0
  243. package/src/presets/shopify-polaris/shopify-polaris-table.css +72 -0
  244. package/src/presets/shopify-polaris/shopify-polaris-tabs.css +33 -0
  245. package/src/presets/shopify-polaris/shopify-polaris-textarea.css +61 -0
  246. package/src/presets/shopify-polaris/shopify-polaris-textlink.css +8 -0
  247. package/src/presets/shopify-polaris/shopify-polaris-toggle.css +52 -0
  248. package/src/presets/shopify-polaris/shopify-polaris-tooltip.css +31 -0
  249. package/src/presets/shopify-polaris/shopify-polaris.css +380 -0
  250. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-accordion.css +28 -0
  251. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-autocomplete.css +155 -0
  252. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-avatar.css +47 -0
  253. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-badge.css +27 -0
  254. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-banner.css +96 -0
  255. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-breadcrumb.css +16 -0
  256. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-button.css +68 -0
  257. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-card.css +28 -0
  258. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-checkbox.css +52 -0
  259. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-chip.css +16 -0
  260. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-combobox.css +184 -0
  261. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-datepicker.css +187 -0
  262. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-dialog.css +40 -0
  263. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-divider.css +12 -0
  264. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-drawer.css +40 -0
  265. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-empty.css +32 -0
  266. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-fileupload.css +72 -0
  267. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-form.css +8 -0
  268. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-input.css +72 -0
  269. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-menu.css +112 -0
  270. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-metric.css +16 -0
  271. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-numberinput.css +88 -0
  272. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-otpinput.css +42 -0
  273. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-page.css +69 -0
  274. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-passwordinput.css +76 -0
  275. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-popover.css +47 -0
  276. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-radio.css +54 -0
  277. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-ratinginput.css +42 -0
  278. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-select.css +118 -0
  279. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-skeleton.css +8 -0
  280. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-slider.css +86 -0
  281. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-spinner.css +16 -0
  282. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-splitter.css +34 -0
  283. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-stepper.css +55 -0
  284. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-switch.css +38 -0
  285. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-table.css +72 -0
  286. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-tabs.css +33 -0
  287. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-textarea.css +61 -0
  288. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-textlink.css +8 -0
  289. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-toast.css +43 -0
  290. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-toggle.css +52 -0
  291. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-tooltip.css +31 -0
  292. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy.css +381 -0
@@ -0,0 +1,174 @@
1
+ import { Combobox as BaseCombobox } from '@base-ui/react/combobox';
2
+ import { useCallback, useLayoutEffect, useRef, useState } from 'react';
3
+ import { useVirtualizer } from '@tanstack/react-virtual';
4
+ /**
5
+ * Virtualize a large Combobox while keeping native search and keyboard behavior.
6
+ * Call this hook in your component, outside Root. Spread each returned props object
7
+ * on its matching element, and keep ComboboxFilteredItemsSync mounted inside Popup
8
+ * so native filtering supplies the complete results. Keep rootProps.onItemHighlighted
9
+ * connected; it scrolls keyboard highlights into view. Merge it if adding your own callback.
10
+ *
11
+ * Filtered groups are flattened into one list: this hook does not virtualize separate
12
+ * group headings. Use one shared heading, as below, or omit GroupLabel for a flat list
13
+ * and adjust paddingStart. Preserve the returned refs and layout styles when customizing.
14
+ * Render only rows, but give Group all filteredItems and each Item its returned index.
15
+ * The mapped rows already preserve your custom item fields; no manual annotation is needed.
16
+ *
17
+ * @example Complete virtualized Combobox
18
+ * import {
19
+ * Combobox,
20
+ * ComboboxFilteredItemsSync,
21
+ * useVirtualizedCombobox,
22
+ * } from '@central-ui/react'
23
+ *
24
+ * // Replace this sample with your larger dataset; keep the source stable between renders.
25
+ * const cityGroups = [{
26
+ * label: 'Cities',
27
+ * items: [
28
+ * { value: 'ma-rabat', label: 'Rabat', country: 'Morocco', population: 580000 },
29
+ * { value: 'ma-casablanca', label: 'Casablanca', country: 'Morocco', population: 3359818 },
30
+ * { value: 'fr-paris', label: 'Paris', country: 'France', population: 2102650 },
31
+ * ],
32
+ * }]
33
+ *
34
+ * function CityCombobox() {
35
+ * const virtual = useVirtualizedCombobox({
36
+ * items: cityGroups, // rootProps supplies these complete groups to Root.
37
+ * estimateSize: 34, // Match the row height plus spacing in your layout.
38
+ * paddingStart: 32, // Match the heading and leading space; adjust if styled differently.
39
+ * paddingEnd: 4, // Match the trailing group padding.
40
+ * })
41
+ *
42
+ * return (
43
+ * <Combobox.Root {...virtual.rootProps}>
44
+ * <Combobox.Container>
45
+ * <Combobox.Label>City</Combobox.Label>
46
+ * <Combobox.InputGroup>
47
+ * <Combobox.Input placeholder="Search cities" />
48
+ * <Combobox.Addon>
49
+ * <Combobox.AddonOpenButton aria-label="Show cities">
50
+ * <Combobox.AddonOpenButtonIcon />
51
+ * </Combobox.AddonOpenButton>
52
+ * <Combobox.AddonClearButton aria-label="Clear city">
53
+ * <Combobox.AddonClearButtonIcon />
54
+ * </Combobox.AddonClearButton>
55
+ * </Combobox.Addon>
56
+ * </Combobox.InputGroup>
57
+ * <Combobox.Popup>
58
+ * <ComboboxFilteredItemsSync {...virtual.filteredItemsSyncProps} />
59
+ * <Combobox.Empty // Keep mounted; native filtering controls visibility.
60
+ * >
61
+ * <Combobox.EmptyText>No cities found.</Combobox.EmptyText>
62
+ * </Combobox.Empty>
63
+ * <Combobox.ScrollArea {...virtual.scrollAreaProps}>
64
+ * <Combobox.GroupsList>
65
+ * {virtual.filteredItems.length > 0 && (
66
+ * <Combobox.Group items={virtual.filteredItems}>
67
+ * <Combobox.GroupLabel>Cities</Combobox.GroupLabel>
68
+ * <Combobox.ItemsList {...virtual.itemsListProps}>
69
+ * {virtual.rows.map((row) => (
70
+ * <Combobox.Item
71
+ * key={row.key}
72
+ * value={row.item}
73
+ * index={row.index} // Preserve the index in the full filtered list.
74
+ * {...row.itemProps}
75
+ * >
76
+ * <Combobox.ItemText>{row.item.label}</Combobox.ItemText>
77
+ * <Combobox.ItemIndicator />
78
+ * </Combobox.Item>
79
+ * ))}
80
+ * </Combobox.ItemsList>
81
+ * </Combobox.Group>
82
+ * )}
83
+ * </Combobox.GroupsList>
84
+ * </Combobox.ScrollArea>
85
+ * </Combobox.Popup>
86
+ * </Combobox.Container>
87
+ * </Combobox.Root>
88
+ * )
89
+ * }
90
+ */
91
+ export function useVirtualizedCombobox({ items: source, estimateSize = 34, overscan = 5, paddingStart = 0, paddingEnd = 0, maxHeight = 320, availableHeight = 'var(--available-height)', }) {
92
+ const [filteredItems, setFilteredItems] = useState([]);
93
+ const scrollRef = useRef(null);
94
+ const virtualizer = useVirtualizer({
95
+ count: filteredItems.length,
96
+ getScrollElement: () => scrollRef.current,
97
+ estimateSize: (index) => typeof estimateSize === 'function'
98
+ ? estimateSize(filteredItems[index], index)
99
+ : estimateSize,
100
+ getItemKey: (index) => filteredItems[index].value,
101
+ overscan,
102
+ paddingStart,
103
+ paddingEnd,
104
+ });
105
+ // Filtering or sorting supplies a new array; scrolling alone must not reset it.
106
+ useLayoutEffect(() => {
107
+ virtualizer.scrollToOffset(0);
108
+ }, [filteredItems, virtualizer]);
109
+ const totalSize = virtualizer.getTotalSize();
110
+ const virtualRows = virtualizer.getVirtualItems();
111
+ const scrollAreaProps = {
112
+ ref: scrollRef,
113
+ style: {
114
+ height: Math.min(totalSize, maxHeight),
115
+ maxHeight: availableHeight,
116
+ },
117
+ };
118
+ // Exclude the space already occupied by the caller's heading and padding.
119
+ const itemsListProps = {
120
+ style: {
121
+ position: 'relative',
122
+ height: totalSize - paddingStart - paddingEnd,
123
+ flexShrink: 0,
124
+ },
125
+ };
126
+ const rows = virtualRows.map((row) => ({
127
+ key: row.key,
128
+ index: row.index,
129
+ item: filteredItems[row.index],
130
+ start: row.start,
131
+ size: row.size,
132
+ itemProps: {
133
+ style: {
134
+ position: 'absolute',
135
+ top: 0,
136
+ left: 0,
137
+ width: '100%',
138
+ transform: `translateY(${row.start - paddingStart}px)`,
139
+ },
140
+ 'aria-setsize': filteredItems.length,
141
+ 'aria-posinset': row.index + 1,
142
+ },
143
+ }));
144
+ const scrollToIndex = useCallback((index, options) => virtualizer.scrollToIndex(index, { align: 'auto', ...options }), [virtualizer]);
145
+ const onItemHighlighted = useCallback((item, details) => {
146
+ if (!item || details.reason === 'pointer' || details.index < 0)
147
+ return;
148
+ queueMicrotask(() => scrollToIndex(details.index));
149
+ }, [scrollToIndex]);
150
+ const onFilteredItemsChange = useCallback((next) => setFilteredItems(next), []);
151
+ return {
152
+ rootProps: { items: source, virtualized: true, onItemHighlighted },
153
+ filteredItemsSyncProps: { onChange: onFilteredItemsChange },
154
+ filteredItems,
155
+ scrollAreaProps,
156
+ itemsListProps,
157
+ rows,
158
+ scrollToIndex,
159
+ virtualizer,
160
+ };
161
+ }
162
+ /**
163
+ * Renders nothing. Mount inside Root (normally inside Popup) to mirror the
164
+ * complete native search results into the external virtualization hook.
165
+ * The callback infers the item type, including extra fields, without a cast.
166
+ */
167
+ export function FilteredItemsSync({ onChange, }) {
168
+ const groups = BaseCombobox.useFilteredItems();
169
+ useLayoutEffect(() => {
170
+ onChange(groups.flatMap((group) => group.items));
171
+ }, [groups, onChange]);
172
+ return null;
173
+ }
174
+ //# sourceMappingURL=useVirtualizedCombobox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useVirtualizedCombobox.js","sourceRoot":"","sources":["../../../src/utils/virtualization/useVirtualizedCombobox.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,169 @@
1
+ import type { CSSProperties } from 'react';
2
+ export interface UseVirtualizedTableOptions<TItem> {
3
+ /**
4
+ * Pass the rows in their current display order after filtering or sorting.
5
+ * Keep this array stable between those changes: a new array scrolls back to the top.
6
+ * @example items: orders
7
+ */
8
+ items: readonly TItem[];
9
+ /**
10
+ * Return a unique database ID or another stable key so rows keep their identity
11
+ * when sorted or filtered. Avoid the array index when row order can change.
12
+ * @example getItemKey: (order) => order.id
13
+ */
14
+ getItemKey: (item: TItem, index: number) => string | number;
15
+ /**
16
+ * Match the rendered row height in pixels, including any spacing between rows.
17
+ * Use a callback when rows have known different heights. This hook does not
18
+ * automatically measure rendered rows; inaccurate sizes can leave gaps or overlaps.
19
+ * @example estimateSize: 34
20
+ * @example estimateSize: (order) => order.hasDetails ? 56 : 34
21
+ * @default 34
22
+ */
23
+ estimateSize?: number | ((item: TItem, index: number) => number);
24
+ /**
25
+ * Mount extra rows above and below the visible area to reduce blanks during fast
26
+ * scrolling. Increase this if needed, at the cost of rendering more rows.
27
+ * @example overscan: 8
28
+ * @default 5
29
+ */
30
+ overscan?: number;
31
+ /**
32
+ * Reserve leading scroll space before the first data row. The returned before
33
+ * spacer includes it; do not add that space a second time in your markup.
34
+ * The table header is outside Body, so do not include its height here.
35
+ * @example paddingStart: 16
36
+ * @default 0
37
+ */
38
+ paddingStart?: number;
39
+ /**
40
+ * Reserve trailing scroll space after the last data row. The returned after
41
+ * spacer includes it; do not add that space a second time in your markup.
42
+ * @example paddingEnd: 16
43
+ * @default 0
44
+ */
45
+ paddingEnd?: number;
46
+ /**
47
+ * Cap the scrolling viewport height while allowing short lists to shrink.
48
+ * The header stays outside this viewport and does not count toward this cap.
49
+ * @example maxHeight: 480
50
+ * @default 320
51
+ */
52
+ maxHeight?: number;
53
+ /**
54
+ * Apply an additional CSS max-height to the scrolling viewport.
55
+ * Use this when the surrounding layout leaves less space than maxHeight.
56
+ * It constrains Body only; allow separately for the header height.
57
+ * @example availableHeight: 'calc(100dvh - 160px)'
58
+ * @default maxHeight
59
+ */
60
+ availableHeight?: CSSProperties['maxHeight'];
61
+ }
62
+ /**
63
+ * Virtualize data rows while keeping table markup and columns under your control.
64
+ * Call the hook in your component. Spread bodyProps on the scrolling Table.Body and
65
+ * rowProps on each visible Table.Row. Render both spacer rows to preserve the scroll
66
+ * height of unmounted rows. The header is outside Body; keep its column widths aligned
67
+ * with the body. Do not make Table.ScrollArea a second vertical scrolling viewport.
68
+ *
69
+ * This example follows the demo's block table/grid row layout, with explicit table
70
+ * roles and row indices so assistive technology knows each row's position in the full
71
+ * collection. Keep the same grid columns for the header and body, preserve bodyProps.ref
72
+ * and styles, and account for the header with aria-rowcount and aria-rowindex.
73
+ *
74
+ * @example Complete virtualized table
75
+ * import { Table, useVirtualizedTable } from '@central-ui/react'
76
+ *
77
+ * // Stable sample records; sorting/filtering should intentionally replace this array.
78
+ * const orders = Array.from({ length: 5000 }, (_, index) => ({
79
+ * id: 'order-' + (10000 + index),
80
+ * number: '#' + (10000 + index),
81
+ * customer: index % 2 === 0 ? 'Alex Morgan' : 'Sam Lee',
82
+ * }))
83
+ *
84
+ * function OrdersTable() {
85
+ * const virtual = useVirtualizedTable({
86
+ * items: orders,
87
+ * getItemKey: (order) => order.id, // Preserve identity after sorting/filtering.
88
+ * estimateSize: 40, // Sets the rendered data row height through rowProps.
89
+ * maxHeight: 480, // Body scrolls; the header stays outside this height.
90
+ * })
91
+ *
92
+ * return (
93
+ * <Table.Root>
94
+ * <Table.Surface>
95
+ * <Table.Table className="block w-full" role="table"
96
+ * aria-label="Orders" aria-rowcount={orders.length + 1}>
97
+ * <Table.Head role="rowgroup"
98
+ * className="static block overflow-hidden [scrollbar-gutter:stable]">
99
+ * <Table.Row role="row" aria-rowindex={1}
100
+ * className="grid grid-cols-2" style={{ height: 40 }}>
101
+ * <Table.HeadCell role="columnheader" className="flex items-center">
102
+ * Order
103
+ * </Table.HeadCell>
104
+ * <Table.HeadCell role="columnheader" className="flex items-center">
105
+ * Customer
106
+ * </Table.HeadCell>
107
+ * </Table.Row>
108
+ * </Table.Head>
109
+ * <Table.Body {...virtual.bodyProps} role="rowgroup"
110
+ * className="block overflow-y-auto [scrollbar-gutter:stable]">
111
+ * {virtual.spacers.before > 0 && (
112
+ * <tr aria-hidden="true" className="block">
113
+ * <td colSpan={2} className="block"
114
+ * style={{ height: virtual.spacers.before, padding: 0, border: 0 }} />
115
+ * </tr>
116
+ * )}
117
+ * {virtual.rows.map((row) => (
118
+ * <Table.Row key={row.key} {...row.rowProps} role="row"
119
+ * className="grid grid-cols-2" aria-rowindex={row.index + 2}>
120
+ * <Table.Cell role="cell" className="flex h-full items-center">
121
+ * {row.item.number}
122
+ * </Table.Cell>
123
+ * <Table.Cell role="cell" className="flex h-full items-center">
124
+ * {row.item.customer}
125
+ * </Table.Cell>
126
+ * </Table.Row>
127
+ * ))}
128
+ * {virtual.spacers.after > 0 && (
129
+ * <tr aria-hidden="true" className="block">
130
+ * <td colSpan={2} className="block"
131
+ * style={{ height: virtual.spacers.after, padding: 0, border: 0 }} />
132
+ * </tr>
133
+ * )}
134
+ * </Table.Body>
135
+ * </Table.Table>
136
+ * </Table.Surface>
137
+ * </Table.Root>
138
+ * )
139
+ * }
140
+ */
141
+ export declare function useVirtualizedTable<TItem>({ items, getItemKey, estimateSize, overscan, paddingStart, paddingEnd, maxHeight, availableHeight, }: UseVirtualizedTableOptions<TItem>): {
142
+ bodyProps: {
143
+ ref: import("react").RefObject<HTMLTableSectionElement | null>;
144
+ style: {
145
+ height: number;
146
+ maxHeight: import("csstype").Property.MaxHeight<string | number>;
147
+ overflowY: "auto";
148
+ };
149
+ };
150
+ rows: {
151
+ key: string | number | bigint;
152
+ index: number;
153
+ item: TItem;
154
+ start: number;
155
+ size: number;
156
+ rowProps: {
157
+ style: {
158
+ height: number;
159
+ };
160
+ };
161
+ }[];
162
+ spacers: {
163
+ before: number;
164
+ after: number;
165
+ };
166
+ scrollToIndex: (index: number, options?: Parameters<(index: number, { align: initialAlign, behavior, }?: import("@tanstack/virtual-core").ScrollToOptions) => void>[1]) => void;
167
+ virtualizer: import("@tanstack/react-virtual").ReactVirtualizer<HTMLTableSectionElement, HTMLTableRowElement>;
168
+ };
169
+ //# sourceMappingURL=useVirtualizedTable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useVirtualizedTable.d.ts","sourceRoot":"","sources":["../../../src/utils/virtualization/useVirtualizedTable.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAG1C,MAAM,WAAW,0BAA0B,CAAC,KAAK;IAC/C;;;;OAIG;IACH,KAAK,EAAE,SAAS,KAAK,EAAE,CAAA;IACvB;;;;OAIG;IACH,UAAU,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,CAAA;IAC3D;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,CAAA;IAChE;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,aAAa,CAAC,WAAW,CAAC,CAAA;CAC7C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8EG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,EACzC,KAAK,EACL,UAAU,EACV,YAAiB,EACjB,QAAY,EACZ,YAAgB,EAChB,UAAc,EACd,SAAe,EACf,eAA2B,GAC5B,EAAE,0BAA0B,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;2BAmDvB,MAAM,YACH,UAAU,gHAAkC,CAAC,CAAC,CAAC;;EAM9D"}
@@ -0,0 +1,127 @@
1
+ import { useCallback, useLayoutEffect, useRef } from 'react';
2
+ import { useVirtualizer } from '@tanstack/react-virtual';
3
+ /**
4
+ * Virtualize data rows while keeping table markup and columns under your control.
5
+ * Call the hook in your component. Spread bodyProps on the scrolling Table.Body and
6
+ * rowProps on each visible Table.Row. Render both spacer rows to preserve the scroll
7
+ * height of unmounted rows. The header is outside Body; keep its column widths aligned
8
+ * with the body. Do not make Table.ScrollArea a second vertical scrolling viewport.
9
+ *
10
+ * This example follows the demo's block table/grid row layout, with explicit table
11
+ * roles and row indices so assistive technology knows each row's position in the full
12
+ * collection. Keep the same grid columns for the header and body, preserve bodyProps.ref
13
+ * and styles, and account for the header with aria-rowcount and aria-rowindex.
14
+ *
15
+ * @example Complete virtualized table
16
+ * import { Table, useVirtualizedTable } from '@central-ui/react'
17
+ *
18
+ * // Stable sample records; sorting/filtering should intentionally replace this array.
19
+ * const orders = Array.from({ length: 5000 }, (_, index) => ({
20
+ * id: 'order-' + (10000 + index),
21
+ * number: '#' + (10000 + index),
22
+ * customer: index % 2 === 0 ? 'Alex Morgan' : 'Sam Lee',
23
+ * }))
24
+ *
25
+ * function OrdersTable() {
26
+ * const virtual = useVirtualizedTable({
27
+ * items: orders,
28
+ * getItemKey: (order) => order.id, // Preserve identity after sorting/filtering.
29
+ * estimateSize: 40, // Sets the rendered data row height through rowProps.
30
+ * maxHeight: 480, // Body scrolls; the header stays outside this height.
31
+ * })
32
+ *
33
+ * return (
34
+ * <Table.Root>
35
+ * <Table.Surface>
36
+ * <Table.Table className="block w-full" role="table"
37
+ * aria-label="Orders" aria-rowcount={orders.length + 1}>
38
+ * <Table.Head role="rowgroup"
39
+ * className="static block overflow-hidden [scrollbar-gutter:stable]">
40
+ * <Table.Row role="row" aria-rowindex={1}
41
+ * className="grid grid-cols-2" style={{ height: 40 }}>
42
+ * <Table.HeadCell role="columnheader" className="flex items-center">
43
+ * Order
44
+ * </Table.HeadCell>
45
+ * <Table.HeadCell role="columnheader" className="flex items-center">
46
+ * Customer
47
+ * </Table.HeadCell>
48
+ * </Table.Row>
49
+ * </Table.Head>
50
+ * <Table.Body {...virtual.bodyProps} role="rowgroup"
51
+ * className="block overflow-y-auto [scrollbar-gutter:stable]">
52
+ * {virtual.spacers.before > 0 && (
53
+ * <tr aria-hidden="true" className="block">
54
+ * <td colSpan={2} className="block"
55
+ * style={{ height: virtual.spacers.before, padding: 0, border: 0 }} />
56
+ * </tr>
57
+ * )}
58
+ * {virtual.rows.map((row) => (
59
+ * <Table.Row key={row.key} {...row.rowProps} role="row"
60
+ * className="grid grid-cols-2" aria-rowindex={row.index + 2}>
61
+ * <Table.Cell role="cell" className="flex h-full items-center">
62
+ * {row.item.number}
63
+ * </Table.Cell>
64
+ * <Table.Cell role="cell" className="flex h-full items-center">
65
+ * {row.item.customer}
66
+ * </Table.Cell>
67
+ * </Table.Row>
68
+ * ))}
69
+ * {virtual.spacers.after > 0 && (
70
+ * <tr aria-hidden="true" className="block">
71
+ * <td colSpan={2} className="block"
72
+ * style={{ height: virtual.spacers.after, padding: 0, border: 0 }} />
73
+ * </tr>
74
+ * )}
75
+ * </Table.Body>
76
+ * </Table.Table>
77
+ * </Table.Surface>
78
+ * </Table.Root>
79
+ * )
80
+ * }
81
+ */
82
+ export function useVirtualizedTable({ items, getItemKey, estimateSize = 34, overscan = 5, paddingStart = 0, paddingEnd = 0, maxHeight = 320, availableHeight = maxHeight, }) {
83
+ const bodyRef = useRef(null);
84
+ const virtualizer = useVirtualizer({
85
+ count: items.length,
86
+ getScrollElement: () => bodyRef.current,
87
+ estimateSize: (index) => typeof estimateSize === 'function'
88
+ ? estimateSize(items[index], index)
89
+ : estimateSize,
90
+ getItemKey: (index) => getItemKey(items[index], index),
91
+ overscan,
92
+ paddingStart,
93
+ paddingEnd,
94
+ });
95
+ // Filtering or sorting supplies a new array; scrolling alone must not reset it.
96
+ useLayoutEffect(() => {
97
+ virtualizer.scrollToOffset(0);
98
+ }, [items, virtualizer]);
99
+ const totalSize = virtualizer.getTotalSize();
100
+ const virtualRows = virtualizer.getVirtualItems();
101
+ const bodyProps = {
102
+ ref: bodyRef,
103
+ style: {
104
+ height: Math.min(totalSize, maxHeight),
105
+ maxHeight: availableHeight,
106
+ overflowY: 'auto',
107
+ },
108
+ };
109
+ const rows = virtualRows.map((row) => ({
110
+ key: row.key,
111
+ index: row.index,
112
+ item: items[row.index],
113
+ start: row.start,
114
+ size: row.size,
115
+ rowProps: { style: { height: row.size } },
116
+ }));
117
+ // Table rows stay in normal flow; the caller renders these as spacer rows.
118
+ const spacers = {
119
+ before: virtualRows[0]?.start ?? 0,
120
+ after: virtualRows.length
121
+ ? totalSize - virtualRows[virtualRows.length - 1].end
122
+ : 0,
123
+ };
124
+ const scrollToIndex = useCallback((index, options) => virtualizer.scrollToIndex(index, { align: 'auto', ...options }), [virtualizer]);
125
+ return { bodyProps, rows, spacers, scrollToIndex, virtualizer };
126
+ }
127
+ //# sourceMappingURL=useVirtualizedTable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useVirtualizedTable.js","sourceRoot":"","sources":["../../../src/utils/virtualization/useVirtualizedTable.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,66 @@
1
1
  {
2
2
  "name": "@central-ui/react",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "description": "Accessible React components with optional Tailwind CSS style presets",
5
+ "license": "MIT",
6
+ "keywords": [
7
+ "react",
8
+ "ui",
9
+ "components",
10
+ "ark-ui",
11
+ "unstyled"
12
+ ],
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "type": "module",
17
+ "sideEffects": [
18
+ "./src/presets/**/*.css"
19
+ ],
20
+ "files": [
21
+ "dist",
22
+ "src/presets",
23
+ "README.md",
24
+ "LICENSE"
25
+ ],
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "import": "./dist/index.js"
30
+ },
31
+ "./presets/shopify-polaris-legacy.css": "./src/presets/shopify-polaris-legacy/shopify-polaris-legacy.css",
32
+ "./presets/shopify-polaris.css": "./src/presets/shopify-polaris/shopify-polaris.css"
33
+ },
34
+ "scripts": {
35
+ "build": "tsc --project tsconfig.json",
36
+ "prepack": "npm run build",
37
+ "typecheck": "tsc --project tsconfig.json --noEmit"
38
+ },
39
+ "peerDependencies": {
40
+ "react": "^18.0.0 || ^19.0.0",
41
+ "react-dom": "^18.0.0 || ^19.0.0",
42
+ "tailwindcss": "^4.2.1"
43
+ },
44
+ "peerDependenciesMeta": {
45
+ "tailwindcss": {
46
+ "optional": true
47
+ }
48
+ },
49
+ "devDependencies": {
50
+ "@types/react": "19.3.0",
51
+ "@types/react-dom": "19.3.0",
52
+ "typescript": "6.0.3"
53
+ },
54
+ "dependencies": {
55
+ "@ark-ui/react": "5.39.3",
56
+ "@base-ui/react": "1.9.0",
57
+ "@internationalized/date": "3.12.4",
58
+ "@radix-ui/react-password-toggle-field": "0.2.1",
59
+ "@tanstack/react-virtual": "3.14.20",
60
+ "lucide-react": "1.57.0",
61
+ "react-aria": "3.53.1",
62
+ "react-aria-components": "1.22.1",
63
+ "react-resizable-panels": "4.14.5",
64
+ "tailwind-merge": "3.7.0"
65
+ }
66
+ }
@@ -0,0 +1,28 @@
1
+ /* accordion */
2
+ @layer components {
3
+ :where(:not([data-style-disabled])) {
4
+ &:where([data-component='accordion-list']) {
5
+ @apply flex w-full flex-col overflow-hidden rounded-xl border border-neutral-200 bg-white;
6
+ }
7
+
8
+ &:where([data-component='accordion-item']) {
9
+ @apply w-full border-b border-neutral-200 last:border-b-0;
10
+ }
11
+
12
+ &:where([data-component='accordion-openbutton']) {
13
+ @apply flex min-h-10 w-full cursor-pointer items-center justify-between gap-4 bg-white px-3 py-2 text-left text-sm font-medium text-neutral-950 outline-none hover:bg-neutral-50 focus-visible:relative focus-visible:z-1 focus-visible:outline-2 focus-visible:outline-blue-500 disabled:pointer-events-none disabled:bg-neutral-100 disabled:text-neutral-500 disabled:opacity-50;
14
+ }
15
+
16
+ &:where([data-component='accordion-openertitle']) {
17
+ @apply min-w-0 flex-1;
18
+ }
19
+
20
+ &:where([data-component='accordion-openerindicator']) {
21
+ @apply h-4 w-fit shrink-0 text-inherit transition-transform data-panel-open:rotate-180;
22
+ }
23
+
24
+ &:where([data-component='accordion-content']) {
25
+ @apply h-(--accordion-panel-height) overflow-hidden text-sm text-neutral-700 transition-[height] duration-200 data-starting-style:h-0 data-ending-style:h-0;
26
+ }
27
+ }
28
+ }