@k3-universe/react-kit 0.0.8 → 0.0.10

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 (334) hide show
  1. package/dist/index.js +18655 -16777
  2. package/dist/kit/builder/data-table/components/DataTable.d.ts +2 -2
  3. package/dist/kit/builder/data-table/components/DataTable.d.ts.map +1 -1
  4. package/dist/kit/builder/form/components/FormBuilder.d.ts +42 -14
  5. package/dist/kit/builder/form/components/FormBuilder.d.ts.map +1 -1
  6. package/dist/kit/builder/form/components/FormBuilderField.d.ts.map +1 -1
  7. package/dist/kit/builder/form/components/fields/AutocompleteField.d.ts.map +1 -1
  8. package/dist/kit/builder/form/components/fields/DatePickerField.d.ts +3 -0
  9. package/dist/kit/builder/form/components/fields/DatePickerField.d.ts.map +1 -0
  10. package/dist/kit/builder/form/components/fields/DateRangePickerField.d.ts +3 -0
  11. package/dist/kit/builder/form/components/fields/DateRangePickerField.d.ts.map +1 -0
  12. package/dist/kit/builder/form/components/fields/MonthPickerField.d.ts +3 -0
  13. package/dist/kit/builder/form/components/fields/MonthPickerField.d.ts.map +1 -0
  14. package/dist/kit/builder/form/components/fields/MonthRangePickerField.d.ts +3 -0
  15. package/dist/kit/builder/form/components/fields/MonthRangePickerField.d.ts.map +1 -0
  16. package/dist/kit/builder/form/components/fields/index.d.ts +4 -0
  17. package/dist/kit/builder/form/components/fields/index.d.ts.map +1 -1
  18. package/dist/kit/builder/section/SectionBuilder.d.ts.map +1 -1
  19. package/dist/kit/builder/section/types.d.ts +12 -1
  20. package/dist/kit/builder/section/types.d.ts.map +1 -1
  21. package/dist/kit/builder/stack-dialog/context.d.ts +3 -0
  22. package/dist/kit/builder/stack-dialog/context.d.ts.map +1 -0
  23. package/dist/kit/builder/stack-dialog/hooks.d.ts +6 -0
  24. package/dist/kit/builder/stack-dialog/hooks.d.ts.map +1 -0
  25. package/dist/kit/builder/stack-dialog/index.d.ts +5 -0
  26. package/dist/kit/builder/stack-dialog/index.d.ts.map +1 -0
  27. package/dist/kit/builder/stack-dialog/provider.d.ts +3 -0
  28. package/dist/kit/builder/stack-dialog/provider.d.ts.map +1 -0
  29. package/dist/kit/builder/stack-dialog/renderer.d.ts +6 -0
  30. package/dist/kit/builder/stack-dialog/renderer.d.ts.map +1 -0
  31. package/dist/kit/builder/stack-dialog/types.d.ts +20 -0
  32. package/dist/kit/builder/stack-dialog/types.d.ts.map +1 -0
  33. package/dist/kit/components/autocomplete/Autocomplete.d.ts +35 -3
  34. package/dist/kit/components/autocomplete/Autocomplete.d.ts.map +1 -1
  35. package/dist/kit/components/autocomplete/index.d.ts +1 -0
  36. package/dist/kit/components/autocomplete/index.d.ts.map +1 -1
  37. package/dist/kit/components/datepicker/DatePicker.d.ts +27 -0
  38. package/dist/kit/components/datepicker/DatePicker.d.ts.map +1 -0
  39. package/dist/kit/components/datepicker/DateRangePicker.d.ts +41 -0
  40. package/dist/kit/components/datepicker/DateRangePicker.d.ts.map +1 -0
  41. package/dist/kit/components/monthpicker/MonthInput.d.ts +26 -0
  42. package/dist/kit/components/monthpicker/MonthInput.d.ts.map +1 -0
  43. package/dist/kit/components/monthpicker/MonthPicker.d.ts +32 -0
  44. package/dist/kit/components/monthpicker/MonthPicker.d.ts.map +1 -0
  45. package/dist/kit/components/monthpicker/MonthRangeInput.d.ts +31 -0
  46. package/dist/kit/components/monthpicker/MonthRangeInput.d.ts.map +1 -0
  47. package/dist/kit/components/monthpicker/MonthRangePicker.d.ts +48 -0
  48. package/dist/kit/components/monthpicker/MonthRangePicker.d.ts.map +1 -0
  49. package/dist/kit/themes/clean-slate.css +165 -1
  50. package/dist/kit/themes/default.css +165 -1
  51. package/dist/kit/themes/minimal-modern.css +165 -1
  52. package/dist/kit/themes/spotify.css +165 -1
  53. package/package.json +1 -1
  54. package/src/kit/builder/data-table/components/DataTable.tsx +9 -9
  55. package/src/kit/builder/form/components/FormBuilder.tsx +363 -145
  56. package/src/kit/builder/form/components/FormBuilderField.tsx +52 -3
  57. package/src/kit/builder/form/components/fields/AutocompleteField.tsx +29 -1
  58. package/src/kit/builder/form/components/fields/DatePickerField.tsx +24 -0
  59. package/src/kit/builder/form/components/fields/DateRangePickerField.tsx +41 -0
  60. package/src/kit/builder/form/components/fields/MonthPickerField.tsx +26 -0
  61. package/src/kit/builder/form/components/fields/MonthRangePickerField.tsx +35 -0
  62. package/src/kit/builder/form/components/fields/index.ts +4 -0
  63. package/src/kit/builder/section/SectionBuilder.tsx +24 -2
  64. package/src/kit/builder/section/types.ts +15 -1
  65. package/src/kit/builder/stack-dialog/context.ts +9 -0
  66. package/src/kit/builder/stack-dialog/hooks.ts +11 -0
  67. package/src/kit/builder/stack-dialog/index.ts +13 -0
  68. package/src/kit/builder/stack-dialog/provider.tsx +55 -0
  69. package/src/kit/builder/stack-dialog/renderer.tsx +33 -0
  70. package/src/kit/builder/stack-dialog/types.ts +22 -0
  71. package/src/kit/components/autocomplete/Autocomplete.tsx +783 -233
  72. package/src/kit/components/autocomplete/index.ts +1 -0
  73. package/src/kit/components/datepicker/DatePicker.tsx +149 -0
  74. package/src/kit/components/datepicker/DateRangePicker.tsx +454 -0
  75. package/src/kit/components/monthpicker/MonthInput.tsx +122 -0
  76. package/src/kit/components/monthpicker/MonthPicker.tsx +223 -0
  77. package/src/kit/components/monthpicker/MonthRangeInput.tsx +132 -0
  78. package/src/kit/components/monthpicker/MonthRangePicker.tsx +407 -0
  79. package/src/stories/kit/builder/Form.Autocomplete.stories.tsx +210 -0
  80. package/src/stories/kit/builder/Form.Complex.stories.tsx +101 -0
  81. package/src/stories/kit/builder/Form.Dynamic.stories.tsx +149 -0
  82. package/src/stories/kit/builder/Form.Pickers.stories.tsx +71 -0
  83. package/src/stories/kit/builder/Section.stories.tsx +58 -2
  84. package/src/stories/kit/components/Autocomplete.stories.tsx +27 -0
  85. package/src/stories/kit/components/DatePicker.stories.tsx +128 -0
  86. package/src/stories/kit/components/DateRangePicker.stories.tsx +154 -0
  87. package/src/stories/kit/components/MonthPicker.stories.tsx +80 -0
  88. package/src/stories/kit/components/MonthRangePicker.stories.tsx +98 -0
  89. package/storybook-static/assets/{Accordion.stories-q6yg6wg1.js → Accordion.stories-KU4JBR8U.js} +1 -1
  90. package/storybook-static/assets/{AdminLayout-B9bV4J_6.js → AdminLayout-CPvVCwfY.js} +10 -10
  91. package/storybook-static/assets/AdminLayout.Basic.stories-DkP2UVXe.js +4 -0
  92. package/storybook-static/assets/AdminLayout.Collapsible.stories-BuuVjtpW.js +4 -0
  93. package/storybook-static/assets/AdminLayout.Complex.stories-D-k4H0hJ.js +29 -0
  94. package/storybook-static/assets/AdminLayout.CustomSidebarHeaderComponent.stories-B_0IEDd4.js +9 -0
  95. package/storybook-static/assets/AdminLayout.CustomSidebarTitleAndIcon.stories-CvAeXUyA.js +4 -0
  96. package/storybook-static/assets/AdminLayout.HeaderSlots.stories-RBFHoSZK.js +7 -0
  97. package/storybook-static/assets/{Alert.stories-DXwNfJ3w.js → Alert.stories-DKxKtIc0.js} +1 -1
  98. package/storybook-static/assets/{AlertDialog.stories-I324NsnP.js → AlertDialog.stories-BqTpZ_nG.js} +1 -1
  99. package/storybook-static/assets/{AspectRatio.stories-CghHiX3Z.js → AspectRatio.stories-DPO9QQ5F.js} +1 -1
  100. package/storybook-static/assets/Autocomplete-Cpg4CaJe.js +56 -0
  101. package/storybook-static/assets/Autocomplete.stories-CWj4G5fh.js +56 -0
  102. package/storybook-static/assets/{Avatar.stories-CEF5FVSR.js → Avatar.stories-DPhov_2g.js} +1 -1
  103. package/storybook-static/assets/Badge.stories-DFKrRdXq.js +12 -0
  104. package/storybook-static/assets/{Breadcrumb.stories-DORe9T4b.js → Breadcrumb.stories-CTE6CZUC.js} +1 -1
  105. package/storybook-static/assets/{Button.stories-3nQ6wsBX.js → Button.stories-cbt2InL-.js} +1 -1
  106. package/storybook-static/assets/{Calendar.stories-Dn__djE1.js → Calendar.stories-DRhTw_43.js} +1 -1
  107. package/storybook-static/assets/{Card.stories-BJcbdwCI.js → Card.stories-Isf6n_K3.js} +1 -1
  108. package/storybook-static/assets/{Carousel.stories-CfqbE7Va.js → Carousel.stories-Cmg0I3fR.js} +1 -1
  109. package/storybook-static/assets/{Chart.stories-cGgef3tv.js → Chart.stories-aQ-fNijT.js} +1 -1
  110. package/storybook-static/assets/{Checkbox.stories-BcaxWzCS.js → Checkbox.stories-B7YMXPDc.js} +1 -1
  111. package/storybook-static/assets/{Collapsible.stories-CQ95s7Cs.js → Collapsible.stories-BUzl17ZZ.js} +1 -1
  112. package/storybook-static/assets/{Combination-CeVus13L.js → Combination-BdQWAuko.js} +1 -1
  113. package/storybook-static/assets/{Command.stories-DYflJh8u.js → Command.stories-DzBlWQs0.js} +1 -1
  114. package/storybook-static/assets/{ContextMenu.stories-CU7ehYrE.js → ContextMenu.stories-CJlBQyXc.js} +1 -1
  115. package/storybook-static/assets/DataTable.Basic.stories-BWYKFDmK.js +6 -0
  116. package/storybook-static/assets/DataTable.Filters.stories-uZdtJk8t.js +21 -0
  117. package/storybook-static/assets/DataTable.Pagination.stories-C5N1khkp.js +24 -0
  118. package/storybook-static/assets/DataTable.SelectionAndActions.stories-FhCqZKvO.js +26 -0
  119. package/storybook-static/assets/DataTable.Sorting.stories-D-k7EtRj.js +6 -0
  120. package/storybook-static/assets/{Dialog.stories-DobNZ0Hp.js → Dialog.stories-C62AF-Gx.js} +1 -1
  121. package/storybook-static/assets/{Dialog.stories-Bu6ZJXNH.js → Dialog.stories-lrjRwOus.js} +1 -1
  122. package/storybook-static/assets/{Drawer.stories-BN0idp4u.js → Drawer.stories-CGjkdJeV.js} +1 -1
  123. package/storybook-static/assets/{DropdownMenu.stories-sfsVDTvm.js → DropdownMenu.stories-DkGClRAA.js} +1 -1
  124. package/storybook-static/assets/Form.ArrayLayouts.stories-C5d_062d.js +130 -0
  125. package/storybook-static/assets/Form.Autocomplete.stories-CPZPkk4o.js +142 -0
  126. package/storybook-static/assets/Form.Basic.stories-Bhcu3-3n.js +58 -0
  127. package/storybook-static/assets/Form.Complex.stories-QnXh5a7Q.js +361 -0
  128. package/storybook-static/assets/Form.Dynamic.stories-DFW6wIuT.js +502 -0
  129. package/storybook-static/assets/Form.Simple.stories-BhJcyhbE.js +53 -0
  130. package/storybook-static/assets/{Form.stories-27doU7JS.js → Form.stories-PFNsMYxO.js} +1 -1
  131. package/storybook-static/assets/FormBuilder-BQBBxo_k.js +5 -0
  132. package/storybook-static/assets/{HoverCard.stories-0lted9Zx.js → HoverCard.stories-CAlQEVn8.js} +1 -1
  133. package/storybook-static/assets/{Input.stories-CwsIObFs.js → Input.stories-CEhODt0V.js} +1 -1
  134. package/storybook-static/assets/{InputOtp.stories-DVc086h4.js → InputOtp.stories-DSvNP4dS.js} +1 -1
  135. package/storybook-static/assets/{Label.stories-bJa0rk1A.js → Label.stories-B3pa8ZLY.js} +1 -1
  136. package/storybook-static/assets/{Login.stories-C6X6Kj5B.js → Login.stories-C7KQkmR_.js} +5 -5
  137. package/storybook-static/assets/{Menubar.stories-CuediVp7.js → Menubar.stories-CHXhSHxc.js} +1 -1
  138. package/storybook-static/assets/MonthPicker.stories-BnrOc4fm.js +99 -0
  139. package/storybook-static/assets/MonthRangePicker.stories-55Gk1t-7.js +134 -0
  140. package/storybook-static/assets/{NavigationMenu.stories-CKGZYhKk.js → NavigationMenu.stories-CXoS080P.js} +1 -1
  141. package/storybook-static/assets/{Page.stories-Cf76wtRx.js → Page.stories-GdSJgZ6-.js} +1 -1
  142. package/storybook-static/assets/{Pagination.stories-D7IdL1_4.js → Pagination.stories-BEBwqH4N.js} +1 -1
  143. package/storybook-static/assets/{Popover.stories-1VP_zNY8.js → Popover.stories-BICy98Cw.js} +1 -1
  144. package/storybook-static/assets/{Progress.stories-C35R5YvA.js → Progress.stories-DECHNOME.js} +1 -1
  145. package/storybook-static/assets/{RadioGroup.stories-CjuD3CwD.js → RadioGroup.stories-DA7-uKfV.js} +1 -1
  146. package/storybook-static/assets/{Resizable.stories-Cs8dvdc5.js → Resizable.stories-B99kWkH7.js} +1 -1
  147. package/storybook-static/assets/{ScrollArea.stories-D6_z-5jm.js → ScrollArea.stories-BqvUAXqU.js} +1 -1
  148. package/storybook-static/assets/Section.stories-lFMlFBQn.js +277 -0
  149. package/storybook-static/assets/SectionBuilder-BQW705x0.js +1 -0
  150. package/storybook-static/assets/{Select.stories-w_kbza5k.js → Select.stories-BsKyZ6Io.js} +1 -1
  151. package/storybook-static/assets/{Separator.stories-B5QnaJXb.js → Separator.stories-BTDOaOM2.js} +1 -1
  152. package/storybook-static/assets/{Sheet.stories-CORnEGvV.js → Sheet.stories-Cam1gR6G.js} +1 -1
  153. package/storybook-static/assets/{Sidebar.stories-DVuWTi4j.js → Sidebar.stories-DnOa6G7y.js} +1 -1
  154. package/storybook-static/assets/{Slider.stories-YoS2vvT2.js → Slider.stories-Bslq7hjq.js} +1 -1
  155. package/storybook-static/assets/Sonner.stories-D34pBBtI.js +18 -0
  156. package/storybook-static/assets/Switch.stories-Puyb1-Bx.js +3 -0
  157. package/storybook-static/assets/{Table.stories-BuGU4MUM.js → Table.stories-ClZxAhut.js} +1 -1
  158. package/storybook-static/assets/Tabs.stories-CURNTETB.js +10 -0
  159. package/storybook-static/assets/{Textarea.stories-CHXVobz6.js → Textarea.stories-Cf1ZBrWw.js} +1 -1
  160. package/storybook-static/assets/{Toggle.stories-C4uB4Hkq.js → Toggle.stories-CdMHY_bi.js} +1 -1
  161. package/storybook-static/assets/{ToggleGroup.stories-Cr229wf_.js → ToggleGroup.stories-BM68m1dX.js} +1 -1
  162. package/storybook-static/assets/{Tooltip.stories-DmMYGR3T.js → Tooltip.stories-DiQv64dM.js} +1 -1
  163. package/storybook-static/assets/{accordion-BCfsz_gl.js → accordion-DVgwQcnw.js} +1 -1
  164. package/storybook-static/assets/{alert-dialog-BYt6Z7Kt.js → alert-dialog-DCUEwpqm.js} +1 -1
  165. package/storybook-static/assets/{avatar-CRn1qQsC.js → avatar-BzwOE-mi.js} +1 -1
  166. package/storybook-static/assets/{axe-JCJl60WC.js → axe-HmUsR1st.js} +1 -1
  167. package/storybook-static/assets/badge-BnQWua6u.js +1 -0
  168. package/storybook-static/assets/{button-BObfgcV1.js → button-0oMkiryo.js} +1 -1
  169. package/storybook-static/assets/{chart-column-CplFCmg8.js → chart-column-DZGb4ZZS.js} +1 -1
  170. package/storybook-static/assets/{check-DyecuwP4.js → check-B9hBGj6o.js} +1 -1
  171. package/storybook-static/assets/{checkbox-D5BmNJeL.js → checkbox-CyIeaWHX.js} +1 -1
  172. package/storybook-static/assets/{chevron-down-Bp7zbUB0.js → chevron-down-D_37S6il.js} +1 -1
  173. package/storybook-static/assets/{chevron-left-D1L4J3UW.js → chevron-left-BBoN0vbI.js} +1 -1
  174. package/storybook-static/assets/{chevron-right-oMYqDJ_f.js → chevron-right-B5vIMLxK.js} +1 -1
  175. package/storybook-static/assets/{circle-CyarsADt.js → circle-C5Lzx6Nx.js} +1 -1
  176. package/storybook-static/assets/clean-slate-D1HmMFJM.css +1 -0
  177. package/storybook-static/assets/{command-DCnXmNmT.js → command-Csa9p8_a.js} +1 -1
  178. package/storybook-static/assets/{createLucideIcon-CLBo0iA0.js → createLucideIcon-BrHXro7t.js} +1 -1
  179. package/storybook-static/assets/default-CN_Fo1GY.css +1 -0
  180. package/storybook-static/assets/{dialog-DL0QBIbC.js → dialog-CsnqITTn.js} +1 -1
  181. package/storybook-static/assets/{dropdown-menu-BwL9gQwG.js → dropdown-menu-BWxxwPHL.js} +1 -1
  182. package/storybook-static/assets/{ellipsis-DPg968Rc.js → ellipsis-BRS038RR.js} +1 -1
  183. package/storybook-static/assets/{grip-vertical-CM0GDwvq.js → grip-vertical-BxXG8KNA.js} +1 -1
  184. package/storybook-static/assets/{iframe-BWjPIle6.js → iframe-v7iAhKit.js} +219 -219
  185. package/storybook-static/assets/{index-DschQYGl.js → index-0-qMRXou.js} +1 -1
  186. package/storybook-static/assets/{index-CKwUGXmt.js → index-AvwFFKJc.js} +1 -1
  187. package/storybook-static/assets/{index-BfKpUbCE.js → index-BVDb4dFc.js} +1 -1
  188. package/storybook-static/assets/{index-Dr2xmx-F.js → index-B_qx7A5T.js} +1 -1
  189. package/storybook-static/assets/index-BfiCOk42.js +1 -0
  190. package/storybook-static/assets/{index-C9xvlw_B.js → index-Bv9yk470.js} +1 -1
  191. package/storybook-static/assets/{index-DMk2qc2_.js → index-Bw1A27Kp.js} +1 -1
  192. package/storybook-static/assets/{index-7XoYQV2z.js → index-ByqivBWx.js} +1 -1
  193. package/storybook-static/assets/{index-URSssr5a.js → index-C9Ta0ZTH.js} +1 -1
  194. package/storybook-static/assets/{index-Pk2lGsul.js → index-CDY5kTx5.js} +1 -1
  195. package/storybook-static/assets/{index-3zykFCgl.js → index-CGnyVRgB.js} +1 -1
  196. package/storybook-static/assets/{index-Cw7p-pms.js → index-CWjrGFAQ.js} +1 -1
  197. package/storybook-static/assets/{index-npvyVsxD.js → index-CwBdPBFz.js} +1 -1
  198. package/storybook-static/assets/index-D8RXF03I.js +1 -0
  199. package/storybook-static/assets/{index-DE2OlfDP.js → index-DLIxT4Z7.js} +1 -1
  200. package/storybook-static/assets/{index-BcAKBHrX.js → index-DbaA6-o1.js} +1 -1
  201. package/storybook-static/assets/index-Dph_5COR.js +1 -0
  202. package/storybook-static/assets/index-DrN5n71E.js +1 -0
  203. package/storybook-static/assets/{index-04C4iZwC.js → index-Tp9IdbR8.js} +1 -1
  204. package/storybook-static/assets/{index-Dsg7S8zu.js → index-WyF3-wTE.js} +1 -1
  205. package/storybook-static/assets/{index-A7UzX-Xw.js → index-XSmPROEP.js} +1 -1
  206. package/storybook-static/assets/{index-u70nzfOV.js → index-Z6wF44KX.js} +1 -1
  207. package/storybook-static/assets/{index-Dqw7miiX.js → index-_RPqOjlQ.js} +1 -1
  208. package/storybook-static/assets/{index-Cce91yJQ.js → index-jrimW4QO.js} +1 -1
  209. package/storybook-static/assets/{index-C6O7WofA.js → index-nqc17SX4.js} +1 -1
  210. package/storybook-static/assets/{label-CwntpYJ0.js → label-Do8ODIVk.js} +1 -1
  211. package/storybook-static/assets/lodash-DDwpuhPG.js +73 -0
  212. package/storybook-static/assets/minimal-modern-BlYVzfQU.css +1 -0
  213. package/storybook-static/assets/{popover-ClqrrvjL.js → popover-CcciSWAw.js} +1 -1
  214. package/storybook-static/assets/{radio-group-CvN0LQZp.js → radio-group-DiJ0Y_KQ.js} +1 -1
  215. package/storybook-static/assets/{react-18-Bj31y5Nr.js → react-18-Cr9fq_Ip.js} +1 -1
  216. package/storybook-static/assets/{react-icons.esm-DNr9VcvP.js → react-icons.esm-B_ULMmNU.js} +1 -1
  217. package/storybook-static/assets/{refresh-cw-OZakDsFY.js → refresh-cw-BmRDhIV_.js} +1 -1
  218. package/storybook-static/assets/{schemas-CaLvKjsI.js → schemas-CGNYCiJ6.js} +3 -3
  219. package/storybook-static/assets/{section-factories-DKfKL-5s.js → section-factories-DCCY9R35.js} +1 -1
  220. package/storybook-static/assets/{select-gtXRB92c.js → select-DDrkxaOg.js} +1 -1
  221. package/storybook-static/assets/{separator-CxCWfpZX.js → separator-o5SAUnaJ.js} +1 -1
  222. package/storybook-static/assets/{settings-2-DSSkfF6W.js → settings-2-xWGvvbG6.js} +1 -1
  223. package/storybook-static/assets/{sheet-r0VNiBiN.js → sheet-C7jhU3XE.js} +1 -1
  224. package/storybook-static/assets/{shopping-cart-CJBClF2m.js → shopping-cart-BFlrufvo.js} +1 -1
  225. package/storybook-static/assets/{sidebar-BsMnOE7Y.js → sidebar-C8hU1Mxy.js} +1 -1
  226. package/storybook-static/assets/spotify-CUDj7g8m.css +1 -0
  227. package/storybook-static/assets/switch-CKGRuk3u.js +1 -0
  228. package/storybook-static/assets/tabs-CopK2m3j.js +1 -0
  229. package/storybook-static/assets/{toggle-D08-8sQA.js → toggle-DmHbWetf.js} +1 -1
  230. package/storybook-static/assets/{tooltip-CZ1UAE_e.js → tooltip-_LqYEYFw.js} +1 -1
  231. package/storybook-static/assets/{trash-2-D55eseMQ.js → trash-2-xdbApPby.js} +1 -1
  232. package/storybook-static/assets/{x-n8sFtlI2.js → x-B1a4fyWM.js} +1 -1
  233. package/storybook-static/iframe.html +1 -1
  234. package/storybook-static/index.json +1 -1
  235. package/storybook-static/kit/builder/data-table/components/DataTable.d.ts +6 -3
  236. package/storybook-static/kit/builder/data-table/components/DataTable.d.ts.map +1 -1
  237. package/storybook-static/kit/builder/data-table/index.d.ts +6 -5
  238. package/storybook-static/kit/builder/data-table/index.d.ts.map +1 -1
  239. package/storybook-static/kit/builder/form/components/FormBuilder.d.ts +53 -13
  240. package/storybook-static/kit/builder/form/components/FormBuilder.d.ts.map +1 -1
  241. package/storybook-static/kit/builder/form/components/FormBuilderField.d.ts +5 -5
  242. package/storybook-static/kit/builder/form/components/FormBuilderField.d.ts.map +1 -1
  243. package/storybook-static/kit/builder/form/components/fields/ArrayField.d.ts +3 -0
  244. package/storybook-static/kit/builder/form/components/fields/ArrayField.d.ts.map +1 -0
  245. package/storybook-static/kit/builder/form/components/fields/AutocompleteField.d.ts +3 -0
  246. package/storybook-static/kit/builder/form/components/fields/AutocompleteField.d.ts.map +1 -0
  247. package/storybook-static/kit/builder/form/components/fields/CheckboxField.d.ts +3 -0
  248. package/storybook-static/kit/builder/form/components/fields/CheckboxField.d.ts.map +1 -0
  249. package/storybook-static/kit/builder/form/components/fields/DateField.d.ts +3 -0
  250. package/storybook-static/kit/builder/form/components/fields/DateField.d.ts.map +1 -0
  251. package/storybook-static/kit/builder/form/components/fields/FileField.d.ts +3 -0
  252. package/storybook-static/kit/builder/form/components/fields/FileField.d.ts.map +1 -0
  253. package/storybook-static/kit/builder/form/components/fields/NumberField.d.ts +3 -0
  254. package/storybook-static/kit/builder/form/components/fields/NumberField.d.ts.map +1 -0
  255. package/storybook-static/kit/builder/form/components/fields/ObjectField.d.ts +3 -0
  256. package/storybook-static/kit/builder/form/components/fields/ObjectField.d.ts.map +1 -0
  257. package/storybook-static/kit/builder/form/components/fields/RadioField.d.ts +3 -0
  258. package/storybook-static/kit/builder/form/components/fields/RadioField.d.ts.map +1 -0
  259. package/storybook-static/kit/builder/form/components/fields/SelectField.d.ts +3 -0
  260. package/storybook-static/kit/builder/form/components/fields/SelectField.d.ts.map +1 -0
  261. package/storybook-static/kit/builder/form/components/fields/SwitchField.d.ts +3 -0
  262. package/storybook-static/kit/builder/form/components/fields/SwitchField.d.ts.map +1 -0
  263. package/storybook-static/kit/builder/form/components/fields/TextField.d.ts +3 -0
  264. package/storybook-static/kit/builder/form/components/fields/TextField.d.ts.map +1 -0
  265. package/storybook-static/kit/builder/form/components/fields/TextareaField.d.ts +3 -0
  266. package/storybook-static/kit/builder/form/components/fields/TextareaField.d.ts.map +1 -0
  267. package/storybook-static/kit/builder/form/components/fields/index.d.ts +14 -0
  268. package/storybook-static/kit/builder/form/components/fields/index.d.ts.map +1 -0
  269. package/storybook-static/kit/builder/form/components/fields/types.d.ts +14 -0
  270. package/storybook-static/kit/builder/form/components/fields/types.d.ts.map +1 -0
  271. package/storybook-static/kit/builder/form/utils/field-factories.d.ts +1 -0
  272. package/storybook-static/kit/builder/form/utils/field-factories.d.ts.map +1 -1
  273. package/storybook-static/kit/builder/section/SectionBuilder.d.ts.map +1 -1
  274. package/storybook-static/kit/builder/section/types.d.ts +12 -1
  275. package/storybook-static/kit/builder/section/types.d.ts.map +1 -1
  276. package/storybook-static/kit/builder/stack-dialog/context.d.ts +3 -0
  277. package/storybook-static/kit/builder/stack-dialog/context.d.ts.map +1 -0
  278. package/storybook-static/kit/builder/stack-dialog/hooks.d.ts +6 -0
  279. package/storybook-static/kit/builder/stack-dialog/hooks.d.ts.map +1 -0
  280. package/storybook-static/kit/builder/stack-dialog/index.d.ts +5 -0
  281. package/storybook-static/kit/builder/stack-dialog/index.d.ts.map +1 -0
  282. package/storybook-static/kit/builder/stack-dialog/provider.d.ts +3 -0
  283. package/storybook-static/kit/builder/stack-dialog/provider.d.ts.map +1 -0
  284. package/storybook-static/kit/builder/stack-dialog/renderer.d.ts +6 -0
  285. package/storybook-static/kit/builder/stack-dialog/renderer.d.ts.map +1 -0
  286. package/storybook-static/kit/builder/stack-dialog/types.d.ts +20 -0
  287. package/storybook-static/kit/builder/stack-dialog/types.d.ts.map +1 -0
  288. package/storybook-static/kit/components/autocomplete/Autocomplete.d.ts +35 -3
  289. package/storybook-static/kit/components/autocomplete/Autocomplete.d.ts.map +1 -1
  290. package/storybook-static/kit/components/autocomplete/index.d.ts +1 -0
  291. package/storybook-static/kit/components/autocomplete/index.d.ts.map +1 -1
  292. package/storybook-static/kit/components/autocomplete/types.d.ts +1 -1
  293. package/storybook-static/kit/components/autocomplete/types.d.ts.map +1 -1
  294. package/storybook-static/kit/components/monthpicker/MonthPicker.d.ts +32 -0
  295. package/storybook-static/kit/components/monthpicker/MonthPicker.d.ts.map +1 -0
  296. package/storybook-static/kit/components/monthpicker/MonthRangePicker.d.ts +48 -0
  297. package/storybook-static/kit/components/monthpicker/MonthRangePicker.d.ts.map +1 -0
  298. package/storybook-static/kit/layouts/admin/components/AdminLayout.d.ts.map +1 -1
  299. package/storybook-static/kit/layouts/admin/hooks/menu.d.ts +1 -0
  300. package/storybook-static/kit/layouts/admin/hooks/menu.d.ts.map +1 -1
  301. package/storybook-static/kit/providers/ThemeProvider.d.ts.map +1 -1
  302. package/storybook-static/project.json +1 -1
  303. package/storybook-static/assets/AdminLayout.Basic.stories-C-ZxuH-O.js +0 -4
  304. package/storybook-static/assets/AdminLayout.Collapsible.stories-xcQzkxio.js +0 -4
  305. package/storybook-static/assets/AdminLayout.Complex.stories-DyjkVpvE.js +0 -29
  306. package/storybook-static/assets/AdminLayout.CustomSidebarHeaderComponent.stories-BqhzWCIA.js +0 -9
  307. package/storybook-static/assets/AdminLayout.CustomSidebarTitleAndIcon.stories-aWxyR67T.js +0 -4
  308. package/storybook-static/assets/AdminLayout.HeaderSlots.stories-dNzhUdDt.js +0 -7
  309. package/storybook-static/assets/Autocomplete-B4gV705L.js +0 -35
  310. package/storybook-static/assets/Autocomplete.stories-CyOvjTGN.js +0 -33
  311. package/storybook-static/assets/Badge.stories-_-G3GriP.js +0 -12
  312. package/storybook-static/assets/DataTable.Basic.stories-BQNWUKiw.js +0 -6
  313. package/storybook-static/assets/DataTable.Filters.stories-DPCoeYhK.js +0 -21
  314. package/storybook-static/assets/DataTable.Pagination.stories-8UNqTNgw.js +0 -24
  315. package/storybook-static/assets/DataTable.SelectionAndActions.stories-CDEjgQ6T.js +0 -26
  316. package/storybook-static/assets/DataTable.Sorting.stories-qG7-X_6r.js +0 -6
  317. package/storybook-static/assets/Form.Basic.stories-dE4nbgpr.js +0 -45
  318. package/storybook-static/assets/Form.Complex.stories-BEZufnjb.js +0 -230
  319. package/storybook-static/assets/Form.Dynamic.stories-cJTbd6cV.js +0 -247
  320. package/storybook-static/assets/Form.Simple.stories-DOPzZKhh.js +0 -50
  321. package/storybook-static/assets/FormBuilder-C0S7C69Q.js +0 -5
  322. package/storybook-static/assets/Section.stories-Ce5qYITI.js +0 -196
  323. package/storybook-static/assets/SectionBuilder-Df_lRZkj.js +0 -1
  324. package/storybook-static/assets/Sonner.stories-CXW5e_Qg.js +0 -18
  325. package/storybook-static/assets/Switch.stories-zr6i-aNi.js +0 -3
  326. package/storybook-static/assets/Tabs.stories-jxkJ-AUI.js +0 -10
  327. package/storybook-static/assets/clean-slate-V4nUw2Bm.css +0 -1
  328. package/storybook-static/assets/default-CuTBjDca.css +0 -1
  329. package/storybook-static/assets/index-BN5_W3Yi.js +0 -1
  330. package/storybook-static/assets/index-C5eJ31Z3.js +0 -1
  331. package/storybook-static/assets/index-DWC9SV-P.js +0 -1
  332. package/storybook-static/assets/lodash-BkmSIg_J.js +0 -73
  333. package/storybook-static/assets/minimal-modern-d2yFlFJM.css +0 -1
  334. package/storybook-static/assets/spotify-BuPUgQEa.css +0 -1
@@ -1,247 +1,797 @@
1
- import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
- import { cn } from '../../../shadcn/lib/utils';
3
- import { Popover, PopoverContent, PopoverTrigger } from '../../../shadcn/ui/popover';
1
+ import type React from "react";
4
2
  import {
5
- Command,
6
- CommandEmpty,
7
- CommandGroup,
8
- CommandInput,
9
- CommandItem,
10
- CommandList,
11
- CommandSeparator,
12
- } from '../../../shadcn/ui/command';
13
- import { ChevronsUpDown, Check, Loader2 } from 'lucide-react';
14
- import { Button } from '../../../shadcn/ui/button';
3
+ useCallback,
4
+ useEffect,
5
+ useMemo,
6
+ useRef,
7
+ useState,
8
+ useId,
9
+ } from "react";
10
+ import { cn } from "../../../shadcn/lib/utils";
11
+ import {
12
+ Popover,
13
+ PopoverContent,
14
+ PopoverTrigger,
15
+ } from "../../../shadcn/ui/popover";
16
+ import {
17
+ Command,
18
+ CommandEmpty,
19
+ CommandGroup,
20
+ CommandInput,
21
+ CommandItem,
22
+ CommandList,
23
+ CommandSeparator,
24
+ } from "../../../shadcn/ui/command";
25
+ import { ChevronsUpDown, Check, Loader2, X } from "lucide-react";
26
+ import { Badge } from "../../../shadcn/ui/badge";
15
27
  import type {
16
- AutocompleteFetcher,
17
- AutocompleteMode,
18
- AutocompleteOption,
19
- AutocompleteFetchResult,
20
- } from './types';
21
- import { useDebounce } from 'use-debounce';
28
+ AutocompleteFetcher,
29
+ AutocompleteMode,
30
+ AutocompleteOption,
31
+ AutocompleteFetchResult,
32
+ } from "./types";
33
+ import { useDebounce } from "use-debounce";
22
34
 
23
35
  export type AutocompleteProps = {
24
- mode: AutocompleteMode
25
- options?: AutocompleteOption[]
26
- fetcher?: AutocompleteFetcher
27
- pageSize?: number
28
- value?: string | number | null
29
- onChange?: (value: string | number | null, option: AutocompleteOption | null) => void
30
- placeholder?: string
31
- disabled?: boolean
32
- className?: string
33
- emptyText?: string
34
- renderOption?: (option: AutocompleteOption, selected: boolean) => React.ReactNode
35
- searchPlaceholder?: string
36
- /** Controls initial open state; component is uncontrolled otherwise */
37
- defaultOpen?: boolean
38
- }
36
+ mode: AutocompleteMode;
37
+ options?: AutocompleteOption[];
38
+ fetcher?: AutocompleteFetcher;
39
+ pageSize?: number;
40
+ /**
41
+ * Value can be a single primitive or an array when `multiple` is true
42
+ */
43
+ value?: string | number | null | Array<string | number>;
44
+ /**
45
+ * onChange returns a single value + option in single mode, or an array of values + options in multiple mode
46
+ */
47
+ onChange?: (
48
+ value: string | number | null | Array<string | number>,
49
+ option: AutocompleteOption | AutocompleteOption[] | null,
50
+ ) => void;
51
+ /** Enable selecting multiple values (shows chips) */
52
+ multiple?: boolean;
53
+ /** Placeholder shown when nothing is selected */
54
+ placeholder?: string;
55
+ disabled?: boolean;
56
+ className?: string;
57
+ emptyText?: string;
58
+ renderOption?: (
59
+ option: AutocompleteOption,
60
+ selected: boolean,
61
+ ) => React.ReactNode;
62
+ searchPlaceholder?: string;
63
+ /** Controls initial open state; component is uncontrolled otherwise */
64
+ defaultOpen?: boolean;
65
+ /** Initial value when component is uncontrolled */
66
+ defaultValue?: string | number | null | Array<string | number>;
67
+ /** Allow entering custom values not present in options (useful for tagging) */
68
+ allowCustomValue?: boolean;
69
+ /**
70
+ * Chip visual style for multiple selection. Uses shadcn Badge variants.
71
+ * default | secondary | destructive | outline
72
+ */
73
+ chipVariant?: "default" | "secondary" | "destructive" | "outline";
74
+ /** Additional className for each chip */
75
+ chipClassName?: string;
76
+ /** Show a clear button when a selection exists */
77
+ clearable?: boolean;
78
+ /**
79
+ * Optional: seed selected labels for edit pages. These options are only used to populate the
80
+ * internal label map so labels render correctly when values are prefilled.
81
+ */
82
+ initialSelectedOptions?: AutocompleteOption | AutocompleteOption[] | null;
83
+ /**
84
+ * Optional: load labels/options for a list of values whose labels are unknown.
85
+ * Useful for edit pages in server mode when only values are available.
86
+ */
87
+ loadSelected?: (values: Array<string | number>) => Promise<AutocompleteOption[]>;
88
+ };
39
89
 
40
90
  const DEFAULT_PAGE_SIZE = 20;
41
91
 
42
92
  const EMPTY_OPTIONS: AutocompleteOption[] = [];
43
93
 
44
94
  export function Autocomplete({
45
- mode,
46
- options = EMPTY_OPTIONS,
47
- fetcher,
48
- pageSize = DEFAULT_PAGE_SIZE,
49
- value: controlledValue,
50
- onChange,
51
- placeholder = 'Select...',
52
- disabled,
53
- className,
54
- emptyText = 'No results found',
55
- renderOption,
56
- searchPlaceholder = 'Search...',
57
- defaultOpen,
95
+ mode,
96
+ options = EMPTY_OPTIONS,
97
+ fetcher,
98
+ pageSize = DEFAULT_PAGE_SIZE,
99
+ value: controlledValue,
100
+ onChange,
101
+ multiple = false,
102
+ placeholder = "Select...",
103
+ disabled,
104
+ className,
105
+ emptyText = "No results found",
106
+ renderOption,
107
+ searchPlaceholder = "Search...",
108
+ defaultOpen,
109
+ defaultValue,
110
+ allowCustomValue = false,
111
+ chipVariant = "secondary",
112
+ chipClassName,
113
+ clearable = true,
114
+ initialSelectedOptions,
115
+ loadSelected,
58
116
  }: AutocompleteProps) {
59
- const [open, setOpen] = useState<boolean>(!!defaultOpen);
60
- const [search, setSearch] = useState('');
61
- const [debouncedSearch] = useDebounce(search, 250);
62
-
63
- // Selection
64
- const [value, setValue] = useState<string | number | null>(controlledValue ?? null);
65
- useEffect(() => {
66
- if (controlledValue !== undefined) setValue(controlledValue);
67
- }, [controlledValue]);
68
-
69
- const handleSelect = useCallback(
70
- (next: AutocompleteOption) => {
71
- const newValue = next.value;
72
- if (controlledValue === undefined) setValue(newValue);
73
- onChange?.(newValue, next);
74
- setOpen(false);
75
- },
76
- [controlledValue, onChange],
77
- );
78
-
79
- // Data state (shared for both modes)
80
- const [items, setItems] = useState<AutocompleteOption[]>([]);
81
- const [loading, setLoading] = useState(false);
82
- const [hasMore, setHasMore] = useState(false);
83
- const [nextCursor, setNextCursor] = useState<string | number | null | undefined>(undefined);
84
- const [page, setPage] = useState(1);
85
-
86
- const resetData = useCallback(() => {
87
- setItems([]);
88
- setHasMore(false);
89
- setNextCursor(undefined);
90
- setPage(1);
91
- }, []);
92
-
93
- // Load data
94
- const load = useCallback(async () => {
95
- if (mode === 'server') {
96
- if (!fetcher) return;
97
- setLoading(true);
98
- try {
99
- const res: AutocompleteFetchResult = await fetcher({
100
- search: debouncedSearch,
101
- cursor: nextCursor ?? null,
102
- page,
103
- pageSize,
104
- });
105
- setItems(prev => (page === 1 ? res.items : [...prev, ...res.items]));
106
- setHasMore(!!res.hasMore);
107
- setNextCursor(res.nextCursor);
108
- } finally {
109
- setLoading(false);
110
- }
111
- } else {
112
- // client mode: filter and paginate locally
113
- setLoading(true);
114
- try {
115
- const filtered = debouncedSearch
116
- ? options.filter(o =>
117
- o.label.toLowerCase().includes(debouncedSearch.toLowerCase()),
118
- )
119
- : options;
120
- const start = (page - 1) * pageSize;
121
- const slice = filtered.slice(start, start + pageSize);
122
- setItems(prev => (page === 1 ? slice : [...prev, ...slice]));
123
- setHasMore(start + pageSize < filtered.length);
124
- setNextCursor(undefined);
125
- } finally {
126
- setLoading(false);
127
- }
128
- }
129
- }, [mode, fetcher, debouncedSearch, nextCursor, page, pageSize, options]);
130
-
131
- // Reset and load on open/search change
132
- useEffect(() => {
133
- if (!open) return;
134
- resetData();
135
- // Load first page
136
- const t = window.setTimeout(() => {
137
- void load();
138
- }, 0);
139
- return () => window.clearTimeout(t);
140
- }, [open, debouncedSearch, mode]);
141
-
142
- // Infinite scroll
143
- const listRef = useRef<HTMLDivElement | null>(null);
144
- const onListScroll = useCallback(() => {
145
- const el = listRef.current;
146
- if (!el || loading) return;
147
- const nearBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 24;
148
- if (nearBottom && hasMore) {
149
- setPage(p => p + 1);
150
- }
151
- }, [loading, hasMore]);
152
-
153
- useEffect(() => {
154
- if (!open) return;
155
-
156
- load();
157
- }, [page]);
158
-
159
- // Selected label
160
- const selectedOption = useMemo(() => items.find(i => i.value === value), [items, value]);
161
-
162
- // Ensure selected label when item not in current page (server mode)
163
- const displayedLabel = useMemo(() => {
164
- if (selectedOption) return selectedOption.label;
165
- if (mode === 'client') {
166
- const found = options.find(i => i.value === value);
167
- return found?.label ?? placeholder;
168
- }
169
- return placeholder;
170
- }, [mode, options, selectedOption, value, placeholder]);
171
-
172
- return (
173
- <Popover open={open} onOpenChange={setOpen}>
174
- <PopoverTrigger asChild>
175
- <Button
176
- type="button"
177
- variant="outline"
178
- role="combobox"
179
- aria-expanded={open}
180
- className={cn('w-full justify-between', className)}
181
- disabled={disabled}
182
- >
183
- <span className={cn('truncate', !value && 'text-muted-foreground')}>{displayedLabel}</span>
184
- <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
185
- </Button>
186
- </PopoverTrigger>
187
- <PopoverContent className="w-[--radix-popover-trigger-width] p-0" align="start">
188
- <Command shouldFilter={false} className="w-full">
189
- <div className="p-2">
190
- <CommandInput
191
- value={search}
192
- onValueChange={setSearch}
193
- placeholder={searchPlaceholder}
194
- autoFocus
195
- />
196
- </div>
197
- <CommandList className="max-h-56 overflow-auto" ref={listRef} onScroll={onListScroll}>
198
- {loading && items.length === 0 ? (
199
- <div className="flex items-center justify-center py-6 text-sm text-muted-foreground">
200
- <Loader2 className="mr-2 h-4 w-4 animate-spin" /> Loading
201
- </div>
202
- ) : null}
203
- <CommandEmpty>{emptyText}</CommandEmpty>
204
- {items.length > 0 ? (
205
- <CommandGroup>
206
- {items.map((item) => {
207
- const selected = item.value === value;
208
- return (
209
- <CommandItem
210
- key={`${item.value}`}
211
- value={`${item.label}`}
212
- onSelect={() => handleSelect(item)}
213
- className="flex items-center justify-between"
214
- >
215
- <div className="min-w-0 truncate">
216
- {renderOption ? renderOption(item, selected) : item.label}
217
- </div>
218
- {selected ? (
219
- <Check className="h-4 w-4" />
220
- ) : null}
221
- </CommandItem>
222
- );
223
- })}
224
- </CommandGroup>
225
- ) : null}
226
- {hasMore ? (
227
- <>
228
- <CommandSeparator />
229
- <div className="flex items-center justify-center py-2 text-xs text-muted-foreground">
230
- {loading ? (
231
- <>
232
- <Loader2 className="mr-1 h-3 w-3 animate-spin" /> Loading more
233
- </>
234
- ) : (
235
- 'Scroll to load more'
236
- )}
237
- </div>
238
- </>
239
- ) : null}
240
- </CommandList>
241
- </Command>
242
- </PopoverContent>
243
- </Popover>
244
- );
117
+ const [open, setOpen] = useState<boolean>(!!defaultOpen);
118
+ const [search, setSearch] = useState("");
119
+ const [debouncedSearch] = useDebounce(search, 250);
120
+ const listId = useId();
121
+
122
+ // Selection
123
+ const isMultiple = !!multiple;
124
+ const [value, setValue] = useState<
125
+ string | number | null | Array<string | number>
126
+ >(() => {
127
+ if (controlledValue !== undefined) return controlledValue;
128
+ if (defaultValue !== undefined) return defaultValue;
129
+ return isMultiple ? [] : null;
130
+ });
131
+ useEffect(() => {
132
+ if (controlledValue !== undefined) setValue(controlledValue);
133
+ }, [controlledValue]);
134
+
135
+ // Keep a map of value -> label to ensure we can render chips/labels even if the option
136
+ // is not present in the current page (especially in server mode or for custom values)
137
+ const labelMapRef = useRef<Map<string | number, string>>(new Map());
138
+ const addToLabelMap = useCallback((opt: AutocompleteOption) => {
139
+ labelMapRef.current.set(opt.value, opt.label);
140
+ }, []);
141
+ // Tick to force re-render when labels hydrate via async
142
+ const [labelTick, setLabelTick] = useState(0);
143
+ const getLabel = useCallback(
144
+ (v: string | number) =>
145
+ labelMapRef.current.get(v) ??
146
+ options.find((o) => o.value === v)?.label ??
147
+ (loadSelected ? "Loading..." : String(v)),
148
+ [options, loadSelected],
149
+ );
150
+
151
+ const handleSelect = useCallback(
152
+ (next: AutocompleteOption) => {
153
+ addToLabelMap(next);
154
+ if (isMultiple) {
155
+ const prevValues = Array.isArray(value) ? value : [];
156
+ const exists = prevValues.some((v) => v === next.value);
157
+ const newValues = exists
158
+ ? prevValues.filter((v) => v !== next.value)
159
+ : [...prevValues, next.value];
160
+
161
+ if (controlledValue === undefined) setValue(newValues);
162
+ const selectedOptions: AutocompleteOption[] = newValues.map((v) => ({
163
+ value: v,
164
+ label: getLabel(v),
165
+ }));
166
+ onChange?.(newValues, selectedOptions);
167
+ // Keep open for multi-select
168
+ } else {
169
+ const newValue = next.value;
170
+ if (controlledValue === undefined) setValue(newValue);
171
+ onChange?.(newValue, next);
172
+ setOpen(false);
173
+ }
174
+ },
175
+ [addToLabelMap, controlledValue, getLabel, isMultiple, onChange, value],
176
+ );
177
+
178
+ // Data state (shared for both modes)
179
+ const [items, setItems] = useState<AutocompleteOption[]>([]);
180
+ const [loading, setLoading] = useState(false);
181
+ const [hasMore, setHasMore] = useState(false);
182
+ const [nextCursor, setNextCursor] = useState<
183
+ string | number | null | undefined
184
+ >(undefined);
185
+ const [page, setPage] = useState(1);
186
+
187
+ const resetData = useCallback(() => {
188
+ setItems([]);
189
+ setHasMore(false);
190
+ setNextCursor(undefined);
191
+ setPage(1);
192
+ }, []);
193
+
194
+ // Load data
195
+ const load = useCallback(async () => {
196
+ if (mode === "server") {
197
+ if (!fetcher) return;
198
+ setLoading(true);
199
+ try {
200
+ const res: AutocompleteFetchResult = await fetcher({
201
+ search: debouncedSearch,
202
+ cursor: nextCursor ?? null,
203
+ page,
204
+ pageSize,
205
+ });
206
+ setItems((prev) => (page === 1 ? res.items : [...prev, ...res.items]));
207
+ setHasMore(!!res.hasMore);
208
+ setNextCursor(res.nextCursor);
209
+ } finally {
210
+ setLoading(false);
211
+ }
212
+ } else {
213
+ // client mode: filter and paginate locally
214
+ setLoading(true);
215
+ try {
216
+ const filtered = debouncedSearch
217
+ ? options.filter((o) =>
218
+ o.label.toLowerCase().includes(debouncedSearch.toLowerCase()),
219
+ )
220
+ : options;
221
+ const start = (page - 1) * pageSize;
222
+ const slice = filtered.slice(start, start + pageSize);
223
+ setItems((prev) => (page === 1 ? slice : [...prev, ...slice]));
224
+ setHasMore(start + pageSize < filtered.length);
225
+ setNextCursor(undefined);
226
+ } finally {
227
+ setLoading(false);
228
+ }
229
+ }
230
+ }, [mode, fetcher, debouncedSearch, nextCursor, page, pageSize, options]);
231
+
232
+ // Keep a ref to latest load() to avoid stale closures
233
+ const loadRef = useRef(load);
234
+ useEffect(() => {
235
+ loadRef.current = load;
236
+ }, [load]);
237
+
238
+ // Reset and load on open/search change (do NOT depend on load to avoid resets during pagination)
239
+ useEffect(() => {
240
+ if (!open) return;
241
+ // Reference debouncedSearch to intentionally re-run on search changes
242
+ void debouncedSearch;
243
+ resetData();
244
+ const t = window.setTimeout(() => {
245
+ void loadRef.current();
246
+ }, 0);
247
+ return () => window.clearTimeout(t);
248
+ }, [open, debouncedSearch, resetData]);
249
+
250
+ // Load subsequent pages when page changes (>1)
251
+ useEffect(() => {
252
+ if (!open) return;
253
+ if (page <= 1) return;
254
+ void loadRef.current();
255
+ }, [open, page]);
256
+
257
+ // Infinite scroll
258
+ const listRef = useRef<HTMLDivElement | null>(null);
259
+ const onListScroll = useCallback(() => {
260
+ const el = listRef.current;
261
+ if (!el || loading) return;
262
+ const nearBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 24;
263
+ if (nearBottom && hasMore) {
264
+ setPage((p) => p + 1);
265
+ }
266
+ }, [loading, hasMore]);
267
+
268
+ // Prime label map from loaded items and static options
269
+ useEffect(() => {
270
+ items.forEach(addToLabelMap);
271
+ }, [items, addToLabelMap]);
272
+ useEffect(() => {
273
+ options.forEach(addToLabelMap);
274
+ }, [options, addToLabelMap]);
275
+ // Seed label map from explicitly provided initialSelectedOptions
276
+ useEffect(() => {
277
+ if (!initialSelectedOptions) return;
278
+ const arr = Array.isArray(initialSelectedOptions)
279
+ ? initialSelectedOptions
280
+ : [initialSelectedOptions];
281
+ arr.forEach(addToLabelMap);
282
+ setLabelTick((t) => t + 1);
283
+ }, [initialSelectedOptions, addToLabelMap]);
284
+
285
+ // Resolve labels for current values if missing and a resolver is provided
286
+ useEffect(() => {
287
+ if (!loadSelected) return;
288
+ const curValues: Array<string | number> = Array.isArray(value)
289
+ ? (isMultiple ? (value as Array<string | number>) : [])
290
+ : value !== null && value !== undefined
291
+ ? [value as string | number]
292
+ : [];
293
+ if (curValues.length === 0) return;
294
+ const missing = curValues.filter(
295
+ (v) => !labelMapRef.current.has(v) && !options.some((o) => o.value === v),
296
+ );
297
+ if (missing.length === 0) return;
298
+ let cancelled = false;
299
+ loadSelected(missing)
300
+ .then((opts) => {
301
+ if (cancelled) return;
302
+ opts.forEach(addToLabelMap);
303
+ setLabelTick((t) => t + 1);
304
+ })
305
+ .catch(() => {
306
+ /* swallow resolver errors */
307
+ });
308
+ return () => {
309
+ cancelled = true;
310
+ };
311
+ // include options so we don't resolve when already present
312
+ }, [value, isMultiple, loadSelected, options, addToLabelMap]);
313
+
314
+ // Selected label
315
+ const selectedOption = useMemo(() => {
316
+ if (isMultiple || Array.isArray(value)) return undefined;
317
+ return items.find((i) => i.value === value);
318
+ }, [isMultiple, items, value]);
319
+
320
+ // Ensure selected label when item not in current page (server mode)
321
+ // biome-ignore lint/correctness/useExhaustiveDependencies: labelTick intentionally triggers recompute when labelMap hydrates
322
+ const displayedLabel = useMemo(() => {
323
+ if (isMultiple || Array.isArray(value)) return placeholder;
324
+ if (selectedOption) return selectedOption.label;
325
+ const v = value;
326
+ if (v !== null && v !== undefined && !Array.isArray(v)) {
327
+ const mapped = labelMapRef.current.get(v);
328
+ if (mapped) return mapped;
329
+ if (mode === "client") {
330
+ const found = options.find((i) => i.value === v);
331
+ return found?.label ?? placeholder;
332
+ }
333
+ }
334
+ return placeholder;
335
+ }, [isMultiple, mode, options, selectedOption, value, placeholder, labelTick]);
336
+
337
+
338
+ const selectedValues: Array<string | number> = useMemo(
339
+ () => (isMultiple && Array.isArray(value) ? value : []),
340
+ [isMultiple, value],
341
+ );
342
+ // biome-ignore lint/correctness/useExhaustiveDependencies: labelTick intentionally triggers recompute when labelMap hydrates
343
+ const selectedOptionsMulti: AutocompleteOption[] = useMemo(
344
+ () => selectedValues.map((v) => ({ value: v, label: getLabel(v) })),
345
+ [getLabel, selectedValues, labelTick],
346
+ );
347
+
348
+ const handleClear = useCallback(() => {
349
+ if (isMultiple) {
350
+ if (controlledValue === undefined) setValue([]);
351
+ onChange?.([], []);
352
+ } else {
353
+ if (controlledValue === undefined) setValue(null);
354
+ onChange?.(null, null);
355
+ }
356
+ }, [controlledValue, isMultiple, onChange]);
357
+
358
+ // Helper: add custom value from current search
359
+ const addCustomValue = useCallback(
360
+ (text: string) => {
361
+ const t = text.trim();
362
+ if (!t) return;
363
+ const created: AutocompleteOption = { value: t, label: t };
364
+ addToLabelMap(created);
365
+ if (isMultiple) {
366
+ const prevValues = Array.isArray(value) ? value : [];
367
+ const exists = prevValues.some((v) => v === created.value);
368
+ const newValues = exists ? prevValues : [...prevValues, created.value];
369
+ if (controlledValue === undefined) setValue(newValues);
370
+ const newOptions = newValues.map((v) => ({
371
+ value: v,
372
+ label: getLabel(v),
373
+ }));
374
+ onChange?.(newValues, newOptions);
375
+ setSearch("");
376
+ } else {
377
+ if (controlledValue === undefined) setValue(created.value);
378
+ onChange?.(created.value, created);
379
+ setSearch("");
380
+ setOpen(false);
381
+ }
382
+ },
383
+ [addToLabelMap, controlledValue, getLabel, isMultiple, onChange, value],
384
+ );
385
+
386
+ const inlineInputRef = useRef<HTMLInputElement | null>(null);
387
+ const canOpen = useMemo(() => {
388
+ // When purely tagging (no static options and no fetcher) in client mode with multi+allowCustomValue,
389
+ // we do not show the dropdown; user types inline.
390
+ const pureTagging =
391
+ isMultiple &&
392
+ allowCustomValue &&
393
+ mode === "client" &&
394
+ options.length === 0 &&
395
+ !fetcher;
396
+ return !pureTagging;
397
+ }, [allowCustomValue, fetcher, isMultiple, mode, options.length]);
398
+ useEffect(() => {
399
+ if (!canOpen && open) setOpen(false);
400
+ }, [canOpen, open]);
401
+
402
+ return (
403
+ <Popover open={open} onOpenChange={setOpen}>
404
+ {canOpen ? (
405
+ <PopoverTrigger asChild>
406
+ <div
407
+ // biome-ignore lint/a11y/useSemanticElements: <explanation>
408
+ role="combobox"
409
+ aria-expanded={open}
410
+ aria-controls={listId}
411
+ tabIndex={disabled ? -1 : 0}
412
+ aria-disabled={disabled || undefined}
413
+ className={cn(
414
+ "w-full inline-flex items-center justify-between rounded-md border bg-background px-3 py-2 text-sm shadow-sm transition-colors",
415
+ "hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
416
+ disabled && "opacity-50 pointer-events-none",
417
+ className,
418
+ )}
419
+ onKeyDown={(e) => {
420
+ if (disabled || !canOpen) return;
421
+ if (e.target instanceof HTMLInputElement) return; // let input handle keys
422
+ if (e.key === "Enter" || e.key === " ") {
423
+ e.preventDefault();
424
+ setOpen((v) => !v);
425
+ }
426
+ }}
427
+ >
428
+ {isMultiple ? (
429
+ <div
430
+ className={cn(
431
+ "flex min-w-0 flex-1 flex-wrap items-center gap-1 text-left",
432
+ )}
433
+ >
434
+ {selectedOptionsMulti.length > 0
435
+ ? selectedOptionsMulti.map((opt) => (
436
+ <Badge
437
+ key={`${opt.value}`}
438
+ variant={chipVariant}
439
+ className={cn("pr-1", chipClassName)}
440
+ >
441
+ <span className="truncate max-w-[10rem]">
442
+ {opt.label}
443
+ </span>
444
+ <button
445
+ type="button"
446
+ aria-label={`Remove ${opt.label}`}
447
+ className="ml-1 inline-flex items-center rounded-sm hover:bg-black/5 dark:hover:bg-white/10"
448
+ onMouseDown={(e) => {
449
+ e.preventDefault();
450
+ e.stopPropagation();
451
+ }}
452
+ onClick={(e) => {
453
+ e.preventDefault();
454
+ e.stopPropagation();
455
+ const prevValues = Array.isArray(value)
456
+ ? value
457
+ : [];
458
+ const newValues = prevValues.filter(
459
+ (v) => v !== opt.value,
460
+ );
461
+ if (controlledValue === undefined)
462
+ setValue(newValues);
463
+ const newOptions = newValues.map((v) => ({
464
+ value: v,
465
+ label: getLabel(v),
466
+ }));
467
+ onChange?.(newValues, newOptions);
468
+ }}
469
+ >
470
+ <X className="h-3 w-3" />
471
+ </button>
472
+ </Badge>
473
+ ))
474
+ : null}
475
+ {allowCustomValue ? (
476
+ <input
477
+ ref={inlineInputRef}
478
+ value={search}
479
+ onChange={(e) => setSearch(e.target.value)}
480
+ placeholder={
481
+ selectedOptionsMulti.length === 0
482
+ ? placeholder
483
+ : undefined
484
+ }
485
+ className="flex-1 min-w-[8ch] bg-transparent outline-none text-sm placeholder:text-muted-foreground"
486
+ onKeyDown={(e) => {
487
+ if (e.key === "Enter" || e.key === ",") {
488
+ e.preventDefault();
489
+ addCustomValue(search);
490
+ } else if (
491
+ e.key === "Backspace" &&
492
+ search === "" &&
493
+ selectedOptionsMulti.length > 0
494
+ ) {
495
+ // Remove last chip when input is empty
496
+ const prevValues = Array.isArray(value) ? value : [];
497
+ const newValues = prevValues.slice(0, -1);
498
+ if (controlledValue === undefined) setValue(newValues);
499
+ const newOptions = newValues.map((v) => ({
500
+ value: v,
501
+ label: getLabel(v),
502
+ }));
503
+ onChange?.(newValues, newOptions);
504
+ }
505
+ }}
506
+ />
507
+ ) : null}
508
+ {selectedOptionsMulti.length === 0 && !allowCustomValue ? (
509
+ <span className="truncate text-muted-foreground">
510
+ {placeholder}
511
+ </span>
512
+ ) : null}
513
+ </div>
514
+ ) : (
515
+ <span
516
+ className={cn(
517
+ "truncate",
518
+ (!value || Array.isArray(value)) && "text-muted-foreground",
519
+ )}
520
+ >
521
+ {displayedLabel}
522
+ </span>
523
+ )}
524
+ {clearable &&
525
+ ((isMultiple && selectedOptionsMulti.length > 0) ||
526
+ (!isMultiple &&
527
+ value !== null &&
528
+ value !== undefined &&
529
+ !Array.isArray(value))) ? (
530
+ <button
531
+ type="button"
532
+ aria-label="Clear selection"
533
+ className="ml-2 inline-flex items-center rounded-sm p-1 hover:bg-black/5 dark:hover:bg-white/10"
534
+ onMouseDown={(e) => {
535
+ e.preventDefault();
536
+ e.stopPropagation();
537
+ }}
538
+ onClick={(e) => {
539
+ e.preventDefault();
540
+ e.stopPropagation();
541
+ handleClear();
542
+ }}
543
+ onKeyDown={(e) => {
544
+ if (e.key === "Enter" || e.key === " ") {
545
+ e.preventDefault();
546
+ e.stopPropagation();
547
+ handleClear();
548
+ }
549
+ }}
550
+ >
551
+ <X className="h-4 w-4 opacity-60" />
552
+ </button>
553
+ ) : null}
554
+ <ChevronsUpDown
555
+ className={cn(
556
+ "ml-2 h-4 w-4 shrink-0 opacity-50",
557
+ !canOpen && "hidden",
558
+ )}
559
+ />
560
+ </div>
561
+ </PopoverTrigger>
562
+ ) : (
563
+ <div
564
+ // biome-ignore lint/a11y/useSemanticElements: <explanation>
565
+ role="combobox"
566
+ aria-expanded={open}
567
+ aria-controls={listId}
568
+ tabIndex={disabled ? -1 : 0}
569
+ aria-disabled={disabled || undefined}
570
+ className={cn(
571
+ "w-full inline-flex items-center justify-between rounded-md border bg-background px-3 py-2 text-sm shadow-sm transition-colors",
572
+ "hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
573
+ disabled && "opacity-50 pointer-events-none",
574
+ className,
575
+ )}
576
+ onKeyDown={(_e) => {
577
+ if (disabled || canOpen) return;
578
+ // Do not toggle popover; handle inline input only
579
+ }}
580
+ >
581
+ {isMultiple ? (
582
+ <div
583
+ className={cn(
584
+ "flex min-w-0 flex-1 flex-wrap items-center gap-1 text-left",
585
+ )}
586
+ >
587
+ {selectedOptionsMulti.length > 0
588
+ ? selectedOptionsMulti.map((opt) => (
589
+ <Badge
590
+ key={`${opt.value}`}
591
+ variant={chipVariant}
592
+ className={cn("pr-1", chipClassName)}
593
+ >
594
+ <span className="truncate max-w-[10rem]">
595
+ {opt.label}
596
+ </span>
597
+ <button
598
+ type="button"
599
+ aria-label={`Remove ${opt.label}`}
600
+ className="ml-1 inline-flex items-center rounded-sm hover:bg-black/5 dark:hover:bg-white/10"
601
+ onMouseDown={(e) => {
602
+ e.preventDefault();
603
+ e.stopPropagation();
604
+ }}
605
+ onClick={(e) => {
606
+ e.preventDefault();
607
+ e.stopPropagation();
608
+ const prevValues = Array.isArray(value) ? value : [];
609
+ const newValues = prevValues.filter(
610
+ (v) => v !== opt.value,
611
+ );
612
+ if (controlledValue === undefined)
613
+ setValue(newValues);
614
+ const newOptions = newValues.map((v) => ({
615
+ value: v,
616
+ label: getLabel(v),
617
+ }));
618
+ onChange?.(newValues, newOptions);
619
+ }}
620
+ >
621
+ <X className="h-3 w-3" />
622
+ </button>
623
+ </Badge>
624
+ ))
625
+ : null}
626
+ {allowCustomValue ? (
627
+ <input
628
+ ref={inlineInputRef}
629
+ value={search}
630
+ onChange={(e) => setSearch(e.target.value)}
631
+ placeholder={
632
+ selectedOptionsMulti.length === 0 ? placeholder : undefined
633
+ }
634
+ className="flex-1 min-w-[8ch] bg-transparent outline-none text-sm placeholder:text-muted-foreground"
635
+ onKeyDown={(e) => {
636
+ if (e.key === "Enter" || e.key === ",") {
637
+ e.preventDefault();
638
+ addCustomValue(search);
639
+ } else if (
640
+ e.key === "Backspace" &&
641
+ search === "" &&
642
+ selectedOptionsMulti.length > 0
643
+ ) {
644
+ const prevValues = Array.isArray(value) ? value : [];
645
+ const newValues = prevValues.slice(0, -1);
646
+ if (controlledValue === undefined) setValue(newValues);
647
+ const newOptions = newValues.map((v) => ({
648
+ value: v,
649
+ label: getLabel(v),
650
+ }));
651
+ onChange?.(newValues, newOptions);
652
+ }
653
+ }}
654
+ />
655
+ ) : null}
656
+ {selectedOptionsMulti.length === 0 && !allowCustomValue ? (
657
+ <span className="truncate text-muted-foreground">
658
+ {placeholder}
659
+ </span>
660
+ ) : null}
661
+ </div>
662
+ ) : (
663
+ <span
664
+ className={cn(
665
+ "truncate",
666
+ (!value || Array.isArray(value)) && "text-muted-foreground",
667
+ )}
668
+ >
669
+ {displayedLabel}
670
+ </span>
671
+ )}
672
+ {clearable &&
673
+ ((isMultiple && selectedOptionsMulti.length > 0) ||
674
+ (!isMultiple &&
675
+ value !== null &&
676
+ value !== undefined &&
677
+ !Array.isArray(value))) ? (
678
+ <button
679
+ type="button"
680
+ aria-label="Clear selection"
681
+ className="ml-2 inline-flex items-center rounded-sm p-1 hover:bg-black/5 dark:hover:bg-white/10"
682
+ onMouseDown={(e) => {
683
+ e.preventDefault();
684
+ e.stopPropagation();
685
+ }}
686
+ onClick={(e) => {
687
+ e.preventDefault();
688
+ e.stopPropagation();
689
+ handleClear();
690
+ }}
691
+ onKeyDown={(e) => {
692
+ if (e.key === "Enter" || e.key === " ") {
693
+ e.preventDefault();
694
+ e.stopPropagation();
695
+ handleClear();
696
+ }
697
+ }}
698
+ >
699
+ <X className="h-4 w-4 opacity-60" />
700
+ </button>
701
+ ) : null}
702
+ <ChevronsUpDown
703
+ className={cn(
704
+ "ml-2 h-4 w-4 shrink-0 opacity-50",
705
+ !canOpen && "hidden",
706
+ )}
707
+ />
708
+ </div>
709
+ )}
710
+ {canOpen ? (
711
+ <PopoverContent
712
+ className="w-[--radix-popover-trigger-width] p-0"
713
+ align="start"
714
+ >
715
+ <Command shouldFilter={false} className="w-full">
716
+ <div className="p-2">
717
+ <CommandInput
718
+ value={search}
719
+ onValueChange={setSearch}
720
+ placeholder={searchPlaceholder}
721
+ autoFocus
722
+ onKeyDown={(e) => {
723
+ if (e.key === "Enter" && allowCustomValue && search.trim()) {
724
+ e.preventDefault();
725
+ addCustomValue(search);
726
+ }
727
+ }}
728
+ />
729
+ </div>
730
+ <CommandList
731
+ id={listId}
732
+ className="max-h-56 overflow-auto"
733
+ ref={listRef}
734
+ onScroll={onListScroll}
735
+ >
736
+ {loading && items.length === 0 ? (
737
+ <div className="flex items-center justify-center py-6 text-sm text-muted-foreground">
738
+ <Loader2 className="mr-2 h-4 w-4 animate-spin" /> Loading
739
+ </div>
740
+ ) : null}
741
+ <CommandEmpty>
742
+ {allowCustomValue && search.trim() ? (
743
+ <span>Press Enter to add "{search.trim()}"</span>
744
+ ) : (
745
+ emptyText
746
+ )}
747
+ </CommandEmpty>
748
+ {items.length > 0 ? (
749
+ <CommandGroup>
750
+ {items.map((item) => {
751
+ const selected = isMultiple
752
+ ? Array.isArray(value)
753
+ ? value.includes(item.value)
754
+ : false
755
+ : item.value === value;
756
+ return (
757
+ <CommandItem
758
+ key={`${item.value}`}
759
+ value={`${item.label}`}
760
+ onSelect={() => handleSelect(item)}
761
+ className="flex items-center justify-between"
762
+ >
763
+ <div className="min-w-0 truncate">
764
+ {renderOption
765
+ ? renderOption(item, selected)
766
+ : item.label}
767
+ </div>
768
+ {selected ? <Check className="h-4 w-4" /> : null}
769
+ </CommandItem>
770
+ );
771
+ })}
772
+ </CommandGroup>
773
+ ) : null}
774
+ {hasMore ? (
775
+ <>
776
+ <CommandSeparator />
777
+ <div className="flex items-center justify-center py-2 text-xs text-muted-foreground">
778
+ {loading ? (
779
+ <>
780
+ <Loader2 className="mr-1 h-3 w-3 animate-spin" />{" "}
781
+ Loading more
782
+ </>
783
+ ) : (
784
+ "Scroll to load more"
785
+ )}
786
+ </div>
787
+ </>
788
+ ) : null}
789
+ </CommandList>
790
+ </Command>
791
+ </PopoverContent>
792
+ ) : null}
793
+ </Popover>
794
+ );
245
795
  }
246
796
 
247
797
  export default Autocomplete;