@flanksource/clicky-ui 0.1.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (378) hide show
  1. package/README.md +29 -2
  2. package/dist/api-explorer.d.ts +5 -0
  3. package/dist/api-explorer.d.ts.map +1 -0
  4. package/dist/api-explorer.js +25 -0
  5. package/dist/api-explorer.js.map +1 -0
  6. package/dist/clicky.d.ts +2 -0
  7. package/dist/clicky.d.ts.map +1 -0
  8. package/dist/clicky.js +5 -0
  9. package/dist/clicky.js.map +1 -0
  10. package/dist/components/DatePicker.d.ts +18 -0
  11. package/dist/components/DatePicker.d.ts.map +1 -0
  12. package/dist/components/DatePicker.js +63 -0
  13. package/dist/components/DatePicker.js.map +1 -0
  14. package/dist/components/DateTimePicker.d.ts +18 -0
  15. package/dist/components/DateTimePicker.d.ts.map +1 -0
  16. package/dist/components/DateTimePicker.js +84 -0
  17. package/dist/components/DateTimePicker.js.map +1 -0
  18. package/dist/components/FilterBar.d.ts +180 -0
  19. package/dist/components/FilterBar.d.ts.map +1 -0
  20. package/dist/components/FilterBar.js +1685 -0
  21. package/dist/components/FilterBar.js.map +1 -0
  22. package/dist/components/MultiSelect.d.ts +19 -0
  23. package/dist/components/MultiSelect.d.ts.map +1 -0
  24. package/dist/components/MultiSelect.js +143 -0
  25. package/dist/components/MultiSelect.js.map +1 -0
  26. package/dist/components/RangeSlider.d.ts +16 -0
  27. package/dist/components/RangeSlider.d.ts.map +1 -0
  28. package/dist/components/RangeSlider.js +92 -0
  29. package/dist/components/RangeSlider.js.map +1 -0
  30. package/dist/components/button.d.ts +21 -0
  31. package/dist/components/button.d.ts.map +1 -0
  32. package/dist/components/button.js +126 -0
  33. package/dist/components/button.js.map +1 -0
  34. package/dist/components/density-switcher.d.ts +7 -0
  35. package/dist/components/density-switcher.d.ts.map +1 -0
  36. package/dist/components/density-switcher.js +32 -0
  37. package/dist/components/density-switcher.js.map +1 -0
  38. package/dist/components/icon-menu-picker.d.ts +23 -0
  39. package/dist/components/icon-menu-picker.d.ts.map +1 -0
  40. package/dist/components/icon-menu-picker.js +192 -0
  41. package/dist/components/icon-menu-picker.js.map +1 -0
  42. package/dist/components/select.d.ts +15 -0
  43. package/dist/components/select.d.ts.map +1 -0
  44. package/dist/components/select.js +39 -0
  45. package/dist/components/select.js.map +1 -0
  46. package/dist/components/theme-switcher.d.ts +7 -0
  47. package/dist/components/theme-switcher.d.ts.map +1 -0
  48. package/dist/components/theme-switcher.js +42 -0
  49. package/dist/components/theme-switcher.js.map +1 -0
  50. package/dist/components.d.ts +15 -0
  51. package/dist/components.d.ts.map +1 -0
  52. package/dist/components.js +33 -0
  53. package/dist/components.js.map +1 -0
  54. package/dist/data/AnsiHtml.d.ts +7 -0
  55. package/dist/data/AnsiHtml.d.ts.map +1 -0
  56. package/dist/data/AnsiHtml.js +71 -0
  57. package/dist/data/AnsiHtml.js.map +1 -0
  58. package/dist/data/Avatar.d.ts +32 -0
  59. package/dist/data/Avatar.d.ts.map +1 -0
  60. package/dist/data/Avatar.js +201 -0
  61. package/dist/data/Avatar.js.map +1 -0
  62. package/dist/data/AvatarBadge.d.ts +26 -0
  63. package/dist/data/AvatarBadge.d.ts.map +1 -0
  64. package/dist/data/AvatarBadge.js +104 -0
  65. package/dist/data/AvatarBadge.js.map +1 -0
  66. package/dist/data/Badge.d.ts +44 -0
  67. package/dist/data/Badge.d.ts.map +1 -0
  68. package/dist/data/Badge.js +667 -0
  69. package/dist/data/Badge.js.map +1 -0
  70. package/dist/data/Clicky.d.ts +147 -0
  71. package/dist/data/Clicky.d.ts.map +1 -0
  72. package/dist/data/Clicky.fixtures.d.ts +3 -0
  73. package/dist/data/Clicky.fixtures.d.ts.map +1 -0
  74. package/dist/data/Clicky.js +1960 -0
  75. package/dist/data/Clicky.js.map +1 -0
  76. package/dist/data/DataTable.d.ts +95 -0
  77. package/dist/data/DataTable.d.ts.map +1 -0
  78. package/dist/data/DataTable.js +1464 -0
  79. package/dist/data/DataTable.js.map +1 -0
  80. package/dist/data/FilterPill.d.ts +29 -0
  81. package/dist/data/FilterPill.d.ts.map +1 -0
  82. package/dist/data/FilterPill.js +153 -0
  83. package/dist/data/FilterPill.js.map +1 -0
  84. package/dist/data/Gauge.d.ts +15 -0
  85. package/dist/data/Gauge.d.ts.map +1 -0
  86. package/dist/data/Gauge.js +63 -0
  87. package/dist/data/Gauge.js.map +1 -0
  88. package/dist/data/Icon.d.ts +53 -0
  89. package/dist/data/Icon.d.ts.map +1 -0
  90. package/dist/data/Icon.js +115 -0
  91. package/dist/data/Icon.js.map +1 -0
  92. package/dist/data/JsonView.d.ts +8 -0
  93. package/dist/data/JsonView.d.ts.map +1 -0
  94. package/dist/data/JsonView.js +74 -0
  95. package/dist/data/JsonView.js.map +1 -0
  96. package/dist/data/KeyValueList.d.ts +17 -0
  97. package/dist/data/KeyValueList.d.ts.map +1 -0
  98. package/dist/data/KeyValueList.js +33 -0
  99. package/dist/data/KeyValueList.js.map +1 -0
  100. package/dist/data/LogViewer.d.ts +10 -0
  101. package/dist/data/LogViewer.d.ts.map +1 -0
  102. package/dist/data/LogViewer.js +46 -0
  103. package/dist/data/LogViewer.js.map +1 -0
  104. package/dist/data/LogsTable.d.ts +27 -0
  105. package/dist/data/LogsTable.d.ts.map +1 -0
  106. package/dist/data/LogsTable.js +580 -0
  107. package/dist/data/LogsTable.js.map +1 -0
  108. package/dist/data/Markdown.d.ts +6 -0
  109. package/dist/data/Markdown.d.ts.map +1 -0
  110. package/dist/data/Markdown.js +59 -0
  111. package/dist/data/Markdown.js.map +1 -0
  112. package/dist/data/MatrixTable.d.ts +22 -0
  113. package/dist/data/MatrixTable.d.ts.map +1 -0
  114. package/dist/data/MatrixTable.js +164 -0
  115. package/dist/data/MatrixTable.js.map +1 -0
  116. package/dist/data/MethodBadge.d.ts +6 -0
  117. package/dist/data/MethodBadge.d.ts.map +1 -0
  118. package/dist/data/MethodBadge.js +18 -0
  119. package/dist/data/MethodBadge.js.map +1 -0
  120. package/dist/data/ProgressBar.d.ts +13 -0
  121. package/dist/data/ProgressBar.d.ts.map +1 -0
  122. package/dist/data/ProgressBar.js +34 -0
  123. package/dist/data/ProgressBar.js.map +1 -0
  124. package/dist/data/SortableHeader.d.ts +12 -0
  125. package/dist/data/SortableHeader.d.ts.map +1 -0
  126. package/dist/data/SortableHeader.js +33 -0
  127. package/dist/data/SortableHeader.js.map +1 -0
  128. package/dist/data/TabButton.d.ts +12 -0
  129. package/dist/data/TabButton.d.ts.map +1 -0
  130. package/dist/data/TabButton.js +45 -0
  131. package/dist/data/TabButton.js.map +1 -0
  132. package/dist/data/Tree.d.ts +35 -0
  133. package/dist/data/Tree.d.ts.map +1 -0
  134. package/dist/data/Tree.js +236 -0
  135. package/dist/data/Tree.js.map +1 -0
  136. package/dist/data/TreeGroupHeader.d.ts +11 -0
  137. package/dist/data/TreeGroupHeader.d.ts.map +1 -0
  138. package/dist/data/TreeGroupHeader.js +41 -0
  139. package/dist/data/TreeGroupHeader.js.map +1 -0
  140. package/dist/data/TreeNode.d.ts +43 -0
  141. package/dist/data/TreeNode.d.ts.map +1 -0
  142. package/dist/data/TreeNode.js +100 -0
  143. package/dist/data/TreeNode.js.map +1 -0
  144. package/dist/data/cells/StatusDot.d.ts +11 -0
  145. package/dist/data/cells/StatusDot.d.ts.map +1 -0
  146. package/dist/data/cells/StatusDot.js +38 -0
  147. package/dist/data/cells/StatusDot.js.map +1 -0
  148. package/dist/data/cells/TagList.d.ts +59 -0
  149. package/dist/data/cells/TagList.d.ts.map +1 -0
  150. package/dist/data/cells/TagList.js +257 -0
  151. package/dist/data/cells/TagList.js.map +1 -0
  152. package/dist/data/cells/Timestamp.d.ts +46 -0
  153. package/dist/data/cells/Timestamp.d.ts.map +1 -0
  154. package/dist/data/cells/Timestamp.js +146 -0
  155. package/dist/data/cells/Timestamp.js.map +1 -0
  156. package/dist/data/cells/status-mapping.d.ts +8 -0
  157. package/dist/data/cells/status-mapping.d.ts.map +1 -0
  158. package/dist/data/cells/status-mapping.js +44 -0
  159. package/dist/data/cells/status-mapping.js.map +1 -0
  160. package/dist/data/code-highlight.d.ts +12 -0
  161. package/dist/data/code-highlight.d.ts.map +1 -0
  162. package/dist/data/code-highlight.js +28 -0
  163. package/dist/data/code-highlight.js.map +1 -0
  164. package/dist/data/diagnostics/DiagnosticsDetailPanel.d.ts +9 -0
  165. package/dist/data/diagnostics/DiagnosticsDetailPanel.d.ts.map +1 -0
  166. package/dist/data/diagnostics/DiagnosticsDetailPanel.js +445 -0
  167. package/dist/data/diagnostics/DiagnosticsDetailPanel.js.map +1 -0
  168. package/dist/data/diagnostics/DiagnosticsTree.d.ts +9 -0
  169. package/dist/data/diagnostics/DiagnosticsTree.d.ts.map +1 -0
  170. package/dist/data/diagnostics/DiagnosticsTree.js +78 -0
  171. package/dist/data/diagnostics/DiagnosticsTree.js.map +1 -0
  172. package/dist/data/diagnostics/ErrorDetails.d.ts +20 -0
  173. package/dist/data/diagnostics/ErrorDetails.d.ts.map +1 -0
  174. package/dist/data/diagnostics/ErrorDetails.js +157 -0
  175. package/dist/data/diagnostics/ErrorDetails.js.map +1 -0
  176. package/dist/data/diagnostics/GoroutineCard.d.ts +10 -0
  177. package/dist/data/diagnostics/GoroutineCard.d.ts.map +1 -0
  178. package/dist/data/diagnostics/GoroutineCard.js +75 -0
  179. package/dist/data/diagnostics/GoroutineCard.js.map +1 -0
  180. package/dist/data/diagnostics/JvmStackTrace.d.ts +11 -0
  181. package/dist/data/diagnostics/JvmStackTrace.d.ts.map +1 -0
  182. package/dist/data/diagnostics/JvmStackTrace.js +26 -0
  183. package/dist/data/diagnostics/JvmStackTrace.js.map +1 -0
  184. package/dist/data/diagnostics/RenderedStackTrace.d.ts +18 -0
  185. package/dist/data/diagnostics/RenderedStackTrace.d.ts.map +1 -0
  186. package/dist/data/diagnostics/RenderedStackTrace.js +235 -0
  187. package/dist/data/diagnostics/RenderedStackTrace.js.map +1 -0
  188. package/dist/data/diagnostics/ThreadCard.d.ts +10 -0
  189. package/dist/data/diagnostics/ThreadCard.d.ts.map +1 -0
  190. package/dist/data/diagnostics/ThreadCard.js +78 -0
  191. package/dist/data/diagnostics/ThreadCard.js.map +1 -0
  192. package/dist/data/diagnostics/error-diagnostics.d.ts +26 -0
  193. package/dist/data/diagnostics/error-diagnostics.d.ts.map +1 -0
  194. package/dist/data/diagnostics/error-diagnostics.js +108 -0
  195. package/dist/data/diagnostics/error-diagnostics.js.map +1 -0
  196. package/dist/data/diagnostics/fixtures.d.ts +3 -0
  197. package/dist/data/diagnostics/fixtures.d.ts.map +1 -0
  198. package/dist/data/diagnostics/jvm-stacktrace.d.ts +34 -0
  199. package/dist/data/diagnostics/jvm-stacktrace.d.ts.map +1 -0
  200. package/dist/data/diagnostics/jvm-stacktrace.js +190 -0
  201. package/dist/data/diagnostics/jvm-stacktrace.js.map +1 -0
  202. package/dist/data/diagnostics/stacktrace-parse.d.ts +11 -0
  203. package/dist/data/diagnostics/stacktrace-parse.d.ts.map +1 -0
  204. package/dist/data/diagnostics/stacktrace-parse.js +87 -0
  205. package/dist/data/diagnostics/stacktrace-parse.js.map +1 -0
  206. package/dist/data/diagnostics/stacktrace.d.ts +38 -0
  207. package/dist/data/diagnostics/stacktrace.d.ts.map +1 -0
  208. package/dist/data/diagnostics/stacktrace.js +119 -0
  209. package/dist/data/diagnostics/stacktrace.js.map +1 -0
  210. package/dist/data/diagnostics/types.d.ts +30 -0
  211. package/dist/data/diagnostics/types.d.ts.map +1 -0
  212. package/dist/data/diagnostics/utils.d.ts +8 -0
  213. package/dist/data/diagnostics/utils.d.ts.map +1 -0
  214. package/dist/data/diagnostics/utils.js +59 -0
  215. package/dist/data/diagnostics/utils.js.map +1 -0
  216. package/dist/data/har/HarPanel.d.ts +9 -0
  217. package/dist/data/har/HarPanel.d.ts.map +1 -0
  218. package/dist/data/har/HarPanel.js +175 -0
  219. package/dist/data/har/HarPanel.js.map +1 -0
  220. package/dist/data/har/fixtures.d.ts +3 -0
  221. package/dist/data/har/fixtures.d.ts.map +1 -0
  222. package/dist/data/har/types.d.ts +47 -0
  223. package/dist/data/har/types.d.ts.map +1 -0
  224. package/dist/data.d.ts +39 -0
  225. package/dist/data.d.ts.map +1 -0
  226. package/dist/data.js +109 -0
  227. package/dist/data.js.map +1 -0
  228. package/dist/hooks/use-density.d.ts +22 -0
  229. package/dist/hooks/use-density.d.ts.map +1 -0
  230. package/dist/hooks/use-density.js +54 -0
  231. package/dist/hooks/use-density.js.map +1 -0
  232. package/dist/hooks/use-history-route.d.ts +6 -0
  233. package/dist/hooks/use-history-route.d.ts.map +1 -0
  234. package/dist/hooks/use-history-route.js +31 -0
  235. package/dist/hooks/use-history-route.js.map +1 -0
  236. package/dist/hooks/use-sort.d.ts +17 -0
  237. package/dist/hooks/use-sort.d.ts.map +1 -0
  238. package/dist/hooks/use-sort.js +42 -0
  239. package/dist/hooks/use-sort.js.map +1 -0
  240. package/dist/hooks/use-theme.d.ts +17 -0
  241. package/dist/hooks/use-theme.d.ts.map +1 -0
  242. package/dist/hooks/use-theme.js +68 -0
  243. package/dist/hooks/use-theme.js.map +1 -0
  244. package/dist/hooks.d.ts +5 -0
  245. package/dist/hooks.d.ts.map +1 -0
  246. package/dist/hooks.js +14 -0
  247. package/dist/hooks.js.map +1 -0
  248. package/dist/index.d.ts +7 -0
  249. package/dist/index.d.ts.map +1 -0
  250. package/dist/index.js +195 -0
  251. package/dist/index.js.map +1 -0
  252. package/dist/layout/Section.d.ts +23 -0
  253. package/dist/layout/Section.d.ts.map +1 -0
  254. package/dist/layout/Section.js +73 -0
  255. package/dist/layout/Section.js.map +1 -0
  256. package/dist/layout/SplitPane.d.ts +13 -0
  257. package/dist/layout/SplitPane.d.ts.map +1 -0
  258. package/dist/layout/SplitPane.js +72 -0
  259. package/dist/layout/SplitPane.js.map +1 -0
  260. package/dist/lib/palette.d.ts +4 -0
  261. package/dist/lib/palette.d.ts.map +1 -0
  262. package/dist/lib/palette.js +35 -0
  263. package/dist/lib/palette.js.map +1 -0
  264. package/dist/lib/size.d.ts +6 -0
  265. package/dist/lib/size.d.ts.map +1 -0
  266. package/dist/lib/size.js +14 -0
  267. package/dist/lib/size.js.map +1 -0
  268. package/dist/lib/utils.d.ts +3 -0
  269. package/dist/lib/utils.d.ts.map +1 -0
  270. package/dist/lib/utils.js +9 -0
  271. package/dist/lib/utils.js.map +1 -0
  272. package/dist/overlay/HoverCard.d.ts +13 -0
  273. package/dist/overlay/HoverCard.d.ts.map +1 -0
  274. package/dist/overlay/HoverCard.js +170 -0
  275. package/dist/overlay/HoverCard.js.map +1 -0
  276. package/dist/overlay/Modal.d.ts +17 -0
  277. package/dist/overlay/Modal.d.ts.map +1 -0
  278. package/dist/overlay/Modal.js +91 -0
  279. package/dist/overlay/Modal.js.map +1 -0
  280. package/dist/rpc/AcceptPicker.d.ts +52 -0
  281. package/dist/rpc/AcceptPicker.d.ts.map +1 -0
  282. package/dist/rpc/AcceptPicker.js +126 -0
  283. package/dist/rpc/AcceptPicker.js.map +1 -0
  284. package/dist/rpc/ApiExplorer.d.ts +11 -0
  285. package/dist/rpc/ApiExplorer.d.ts.map +1 -0
  286. package/dist/rpc/ApiExplorer.js +37 -0
  287. package/dist/rpc/ApiExplorer.js.map +1 -0
  288. package/dist/rpc/CommandForm.d.ts +15 -0
  289. package/dist/rpc/CommandForm.d.ts.map +1 -0
  290. package/dist/rpc/CommandForm.js +356 -0
  291. package/dist/rpc/CommandForm.js.map +1 -0
  292. package/dist/rpc/CommandOutput.d.ts +11 -0
  293. package/dist/rpc/CommandOutput.d.ts.map +1 -0
  294. package/dist/rpc/CommandOutput.js +195 -0
  295. package/dist/rpc/CommandOutput.js.map +1 -0
  296. package/dist/rpc/EndpointList.d.ts +18 -0
  297. package/dist/rpc/EndpointList.d.ts.map +1 -0
  298. package/dist/rpc/EndpointList.js +56 -0
  299. package/dist/rpc/EndpointList.js.map +1 -0
  300. package/dist/rpc/EntityExplorerApp.d.ts +15 -0
  301. package/dist/rpc/EntityExplorerApp.d.ts.map +1 -0
  302. package/dist/rpc/EntityExplorerApp.js +282 -0
  303. package/dist/rpc/EntityExplorerApp.js.map +1 -0
  304. package/dist/rpc/ExecutionResult.d.ts +14 -0
  305. package/dist/rpc/ExecutionResult.d.ts.map +1 -0
  306. package/dist/rpc/ExecutionResult.js +51 -0
  307. package/dist/rpc/ExecutionResult.js.map +1 -0
  308. package/dist/rpc/FilterForm.d.ts +22 -0
  309. package/dist/rpc/FilterForm.d.ts.map +1 -0
  310. package/dist/rpc/FilterForm.js +392 -0
  311. package/dist/rpc/FilterForm.js.map +1 -0
  312. package/dist/rpc/InlineError.d.ts +7 -0
  313. package/dist/rpc/InlineError.d.ts.map +1 -0
  314. package/dist/rpc/InlineError.js +54 -0
  315. package/dist/rpc/InlineError.js.map +1 -0
  316. package/dist/rpc/OperationActionDialog.d.ts +12 -0
  317. package/dist/rpc/OperationActionDialog.d.ts.map +1 -0
  318. package/dist/rpc/OperationActionDialog.js +105 -0
  319. package/dist/rpc/OperationActionDialog.js.map +1 -0
  320. package/dist/rpc/OperationCatalog.d.ts +21 -0
  321. package/dist/rpc/OperationCatalog.d.ts.map +1 -0
  322. package/dist/rpc/OperationCatalog.js +339 -0
  323. package/dist/rpc/OperationCatalog.js.map +1 -0
  324. package/dist/rpc/OperationCommandPage.d.ts +22 -0
  325. package/dist/rpc/OperationCommandPage.d.ts.map +1 -0
  326. package/dist/rpc/OperationCommandPage.js +484 -0
  327. package/dist/rpc/OperationCommandPage.js.map +1 -0
  328. package/dist/rpc/OperationEntityPage.d.ts +23 -0
  329. package/dist/rpc/OperationEntityPage.d.ts.map +1 -0
  330. package/dist/rpc/OperationEntityPage.js +218 -0
  331. package/dist/rpc/OperationEntityPage.js.map +1 -0
  332. package/dist/rpc/classify.d.ts +8 -0
  333. package/dist/rpc/classify.d.ts.map +1 -0
  334. package/dist/rpc/classify.js +76 -0
  335. package/dist/rpc/classify.js.map +1 -0
  336. package/dist/rpc/clickyMetadata.d.ts +12 -0
  337. package/dist/rpc/clickyMetadata.d.ts.map +1 -0
  338. package/dist/rpc/clickyMetadata.js +71 -0
  339. package/dist/rpc/clickyMetadata.js.map +1 -0
  340. package/dist/rpc/formMetadata.d.ts +27 -0
  341. package/dist/rpc/formMetadata.d.ts.map +1 -0
  342. package/dist/rpc/formMetadata.js +250 -0
  343. package/dist/rpc/formMetadata.js.map +1 -0
  344. package/dist/rpc/types.d.ts +109 -0
  345. package/dist/rpc/types.d.ts.map +1 -0
  346. package/dist/rpc/types.js +8 -0
  347. package/dist/rpc/types.js.map +1 -0
  348. package/dist/rpc/useOperations.d.ts +346 -0
  349. package/dist/rpc/useOperations.d.ts.map +1 -0
  350. package/dist/rpc/useOperations.js +31 -0
  351. package/dist/rpc/useOperations.js.map +1 -0
  352. package/dist/rpc.d.ts +14 -0
  353. package/dist/rpc.d.ts.map +1 -0
  354. package/dist/rpc.js +37 -0
  355. package/dist/rpc.js.map +1 -0
  356. package/dist/styles.css +2 -0
  357. package/dist/styles.d.ts +1 -0
  358. package/dist/styles.d.ts.map +1 -0
  359. package/dist/styles.js +2 -0
  360. package/dist/styles.js.map +1 -0
  361. package/dist/tailwind-preset.d.ts +4 -0
  362. package/dist/tailwind-preset.d.ts.map +1 -0
  363. package/dist/tailwind-preset.js +69 -0
  364. package/dist/tailwind-preset.js.map +1 -0
  365. package/dist/utils.d.ts +4 -0
  366. package/dist/utils.d.ts.map +1 -0
  367. package/dist/utils.js +12 -0
  368. package/dist/utils.js.map +1 -0
  369. package/package.json +83 -40
  370. package/dist/index.d.mts +0 -834
  371. package/dist/index.d.mts.map +0 -1
  372. package/dist/index.mjs +0 -2918
  373. package/dist/index.mjs.map +0 -1
  374. package/dist/tailwind-preset.d.mts +0 -7
  375. package/dist/tailwind-preset.d.mts.map +0 -1
  376. package/dist/tailwind-preset.mjs +0 -64
  377. package/dist/tailwind-preset.mjs.map +0 -1
  378. package/src/styles/tokens.css +0 -78
@@ -0,0 +1,1685 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useMemo, createContext, useRef, useState, useEffect, useContext } from "react";
3
+ import { FilterPill } from "../data/FilterPill.js";
4
+ import { Icon } from "../data/Icon.js";
5
+ import { cn } from "../lib/utils.js";
6
+ import { Button } from "./button.js";
7
+ import { DatePicker } from "./DatePicker.js";
8
+ import { DateTimePicker } from "./DateTimePicker.js";
9
+ import { MultiSelect } from "./MultiSelect.js";
10
+ import { RangeSlider } from "./RangeSlider.js";
11
+ import { Select } from "./select.js";
12
+ const FILTER_INPUT_DEBOUNCE_MS = 500;
13
+ const FilterBarContext = createContext({
14
+ autoSubmit: true
15
+ });
16
+ function FilterBar({
17
+ search,
18
+ filters,
19
+ timeRange,
20
+ dateRange,
21
+ children,
22
+ leading,
23
+ trailing,
24
+ className,
25
+ autoSubmit = true,
26
+ onApply,
27
+ applyLabel = "Apply",
28
+ isPending = false
29
+ }) {
30
+ const hasRangeControls = Boolean(timeRange || dateRange);
31
+ const showApply = !autoSubmit && !!onApply;
32
+ const contextValue = useMemo(() => ({ autoSubmit }), [autoSubmit]);
33
+ return /* @__PURE__ */ jsx(FilterBarContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs(
34
+ "div",
35
+ {
36
+ className: cn(
37
+ "flex flex-wrap items-center gap-2 rounded-lg border border-input bg-background px-2 py-1.5 shadow-sm",
38
+ className
39
+ ),
40
+ children: [
41
+ leading && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: leading }),
42
+ search && /* @__PURE__ */ jsx(SearchField, { search }),
43
+ children,
44
+ filters == null ? void 0 : filters.map((filter, index) => {
45
+ const grow = !search && index === 0;
46
+ if (filter.kind === "lookup") {
47
+ return /* @__PURE__ */ jsx(LookupFilterField, { filter, grow }, filter.key);
48
+ }
49
+ if (filter.kind === "lookup-multi") {
50
+ return /* @__PURE__ */ jsx(LookupMultiFilterField, { filter, grow }, filter.key);
51
+ }
52
+ if (filter.kind === "multi") {
53
+ return /* @__PURE__ */ jsx(MultiFilterField, { filter, grow }, filter.key);
54
+ }
55
+ if (filter.kind === "nested-multi") {
56
+ return /* @__PURE__ */ jsx(NestedMultiFilterField, { filter, grow }, filter.key);
57
+ }
58
+ if (filter.kind === "select-multi") {
59
+ return /* @__PURE__ */ jsx(SelectMultiFilterField, { filter, grow }, filter.key);
60
+ }
61
+ if (filter.kind === "number") {
62
+ return /* @__PURE__ */ jsx(NumberFilterField, { filter, grow }, filter.key);
63
+ }
64
+ if (filter.kind === "enum") {
65
+ return /* @__PURE__ */ jsx(EnumFilterField, { filter, grow }, filter.key);
66
+ }
67
+ if (filter.kind === "boolean") {
68
+ return /* @__PURE__ */ jsx(BooleanFilterField, { filter }, filter.key);
69
+ }
70
+ return /* @__PURE__ */ jsx(TextFilterField, { filter, grow }, filter.key);
71
+ }),
72
+ (hasRangeControls || trailing || showApply) && /* @__PURE__ */ jsxs("div", { className: "ml-auto flex flex-wrap items-center gap-2", children: [
73
+ dateRange && /* @__PURE__ */ jsx(RangeControlButton, { kind: "date", label: "Date range", ...dateRange }),
74
+ timeRange && /* @__PURE__ */ jsx(RangeControlButton, { kind: "time", label: "Time range", ...timeRange }),
75
+ trailing,
76
+ showApply && /* @__PURE__ */ jsx(
77
+ Button,
78
+ {
79
+ type: "button",
80
+ variant: "default",
81
+ size: "sm",
82
+ disabled: isPending,
83
+ onClick: onApply,
84
+ children: isPending ? "Loading…" : applyLabel
85
+ }
86
+ )
87
+ ] })
88
+ ]
89
+ }
90
+ ) });
91
+ }
92
+ function FilterBarFilterPanel({
93
+ filter,
94
+ chrome = "full"
95
+ }) {
96
+ const contextValue = useMemo(() => ({ autoSubmit: true }), []);
97
+ return /* @__PURE__ */ jsxs(FilterBarContext.Provider, { value: contextValue, children: [
98
+ filter.kind === "lookup" && /* @__PURE__ */ jsx(LookupFilterField, { filter, grow: true }),
99
+ filter.kind === "lookup-multi" && /* @__PURE__ */ jsx(LookupMultiFilterField, { filter, grow: true }),
100
+ filter.kind === "multi" && /* @__PURE__ */ jsx(MultiFilterPanel, { filter, chrome }),
101
+ filter.kind === "nested-multi" && /* @__PURE__ */ jsx(NestedMultiFilterPanel, { filter, chrome }),
102
+ filter.kind === "select-multi" && /* @__PURE__ */ jsx(SelectMultiFilterField, { filter, grow: true }),
103
+ filter.kind === "number" && /* @__PURE__ */ jsx(NumberFilterPanel, { filter, chrome }),
104
+ filter.kind === "enum" && /* @__PURE__ */ jsx(EnumFilterField, { filter, grow: true }),
105
+ filter.kind === "boolean" && /* @__PURE__ */ jsx(BooleanFilterField, { filter }),
106
+ filter.kind === "text" && /* @__PURE__ */ jsx(TextFilterField, { filter, grow: true })
107
+ ] });
108
+ }
109
+ function FilterBarRangePanel({
110
+ kind,
111
+ label,
112
+ from = "",
113
+ to = "",
114
+ onApply,
115
+ presets: _presets,
116
+ fromPlaceholder,
117
+ toPlaceholder,
118
+ emptyLabel: _emptyLabel
119
+ }) {
120
+ const fromInputRef = useRef(null);
121
+ const toInputRef = useRef(null);
122
+ const [draftFrom, setDraftFrom] = useState(from);
123
+ const [draftTo, setDraftTo] = useState(to);
124
+ useEffect(() => {
125
+ setDraftFrom(from);
126
+ setDraftTo(to);
127
+ }, [from, to]);
128
+ function applyRange(nextFrom, nextTo) {
129
+ onApply(
130
+ kind === "time" ? normalizeDateMath(nextFrom) : nextFrom.trim(),
131
+ kind === "time" ? normalizeDateMath(nextTo) : nextTo.trim()
132
+ );
133
+ }
134
+ return /* @__PURE__ */ jsx("div", { className: "w-72 text-popover-foreground", children: /* @__PURE__ */ jsxs("div", { className: "p-3", children: [
135
+ /* @__PURE__ */ jsx("div", { className: "mb-2 text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: label }),
136
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
137
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
138
+ /* @__PURE__ */ jsx("label", { className: "text-[10px] text-muted-foreground", children: "From" }),
139
+ /* @__PURE__ */ jsx(
140
+ RangeInput,
141
+ {
142
+ inputRef: fromInputRef,
143
+ kind,
144
+ ariaLabel: `${label} from`,
145
+ placeholder: fromPlaceholder ?? (kind === "date" ? "" : "now-24h"),
146
+ value: draftFrom,
147
+ onChange: setDraftFrom
148
+ }
149
+ )
150
+ ] }),
151
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
152
+ /* @__PURE__ */ jsx("label", { className: "text-[10px] text-muted-foreground", children: "To" }),
153
+ /* @__PURE__ */ jsx(
154
+ RangeInput,
155
+ {
156
+ inputRef: toInputRef,
157
+ kind,
158
+ ariaLabel: `${label} to`,
159
+ placeholder: toPlaceholder ?? (kind === "date" ? "" : "now"),
160
+ value: draftTo,
161
+ onChange: setDraftTo
162
+ }
163
+ )
164
+ ] })
165
+ ] }),
166
+ /* @__PURE__ */ jsx("div", { className: "mt-3 flex justify-end", children: /* @__PURE__ */ jsx(
167
+ Button,
168
+ {
169
+ type: "button",
170
+ variant: "default",
171
+ size: "sm",
172
+ className: "h-8 px-3 text-xs",
173
+ onClick: () => applyRange(draftFrom, draftTo),
174
+ children: "Apply"
175
+ }
176
+ ) })
177
+ ] }) });
178
+ }
179
+ function EnumFilterField({ filter, grow }) {
180
+ return /* @__PURE__ */ jsxs(
181
+ "label",
182
+ {
183
+ title: filter.description,
184
+ className: cn(
185
+ "flex h-8 items-center gap-2 rounded-md border border-input bg-muted/30 pl-2 pr-1 text-xs",
186
+ grow ? "min-w-[12rem] max-w-[18rem] flex-1" : "min-w-[11rem] max-w-[15rem] shrink-0",
187
+ filter.disabled && "opacity-60",
188
+ filter.className
189
+ ),
190
+ children: [
191
+ /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap font-medium uppercase tracking-wide text-muted-foreground", children: filter.label }),
192
+ /* @__PURE__ */ jsx(
193
+ Select,
194
+ {
195
+ "aria-label": filter.label,
196
+ className: "h-6 border-0 bg-transparent px-1 text-xs shadow-none focus-visible:ring-0",
197
+ value: filter.value,
198
+ placeholder: filter.placeholder ?? `Any ${filter.label.toLowerCase()}`,
199
+ disabled: filter.disabled,
200
+ onChange: (event) => filter.onChange(event.target.value),
201
+ options: filter.options.map((option) => ({
202
+ value: option.value,
203
+ label: option.label ?? option.value
204
+ }))
205
+ }
206
+ )
207
+ ]
208
+ }
209
+ );
210
+ }
211
+ function BooleanFilterField({ filter }) {
212
+ return /* @__PURE__ */ jsxs(
213
+ "label",
214
+ {
215
+ title: filter.description,
216
+ className: cn(
217
+ "flex h-8 shrink-0 items-center gap-2 rounded-md border border-input bg-muted/30 px-2 text-xs",
218
+ filter.disabled && "opacity-60",
219
+ filter.className
220
+ ),
221
+ children: [
222
+ /* @__PURE__ */ jsx(
223
+ "input",
224
+ {
225
+ type: "checkbox",
226
+ "aria-label": filter.label,
227
+ className: "h-3.5 w-3.5 accent-primary",
228
+ checked: filter.value,
229
+ disabled: filter.disabled,
230
+ onChange: (event) => filter.onChange(event.target.checked)
231
+ }
232
+ ),
233
+ /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap font-medium uppercase tracking-wide text-muted-foreground", children: filter.label })
234
+ ]
235
+ }
236
+ );
237
+ }
238
+ function SearchField({ search }) {
239
+ const [draft, setDraft] = useDebouncedTextDraft(search.value, search.onChange);
240
+ return /* @__PURE__ */ jsx("div", { className: "flex min-w-[14rem] max-w-[24rem] flex-1 items-center gap-2", children: /* @__PURE__ */ jsxs(
241
+ "label",
242
+ {
243
+ className: cn(
244
+ "flex h-8 min-w-0 flex-1 items-center rounded-md border border-input bg-background px-3 text-sm",
245
+ search.className
246
+ ),
247
+ children: [
248
+ draft.trim() ? /* @__PURE__ */ jsx("span", { className: "mr-2 whitespace-nowrap text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: search.ariaLabel ?? "Search" }) : /* @__PURE__ */ jsx(Icon, { name: "codicon:search", className: "mr-2 shrink-0 text-muted-foreground" }),
249
+ /* @__PURE__ */ jsx(
250
+ "input",
251
+ {
252
+ type: "search",
253
+ "aria-label": search.ariaLabel ?? search.placeholder ?? "Search",
254
+ className: "w-full bg-transparent outline-none placeholder:text-muted-foreground",
255
+ placeholder: search.placeholder ?? "Search…",
256
+ value: draft,
257
+ onChange: (event) => setDraft(event.target.value)
258
+ }
259
+ )
260
+ ]
261
+ }
262
+ ) });
263
+ }
264
+ function TextFilterField({ filter, grow }) {
265
+ const [draft, setDraft] = useDebouncedTextDraft(filter.value, filter.onChange);
266
+ return /* @__PURE__ */ jsxs(
267
+ "label",
268
+ {
269
+ title: filter.description,
270
+ className: cn(
271
+ "flex h-8 items-center gap-2 rounded-md border border-input bg-muted/30 pl-2 pr-2 text-xs",
272
+ grow ? "min-w-[12rem] max-w-[18rem] flex-1" : "min-w-[11rem] max-w-[15rem] shrink-0",
273
+ filter.disabled && "opacity-60",
274
+ filter.className
275
+ ),
276
+ children: [
277
+ /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap font-medium uppercase tracking-wide text-muted-foreground", children: filter.label }),
278
+ /* @__PURE__ */ jsx(
279
+ "input",
280
+ {
281
+ type: "text",
282
+ "aria-label": filter.label,
283
+ className: "w-full min-w-0 bg-transparent text-sm text-foreground outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed",
284
+ placeholder: filter.placeholder ?? "Filter…",
285
+ value: draft,
286
+ disabled: filter.disabled,
287
+ onChange: (event) => setDraft(event.target.value)
288
+ }
289
+ )
290
+ ]
291
+ }
292
+ );
293
+ }
294
+ function LookupFilterField({ filter, grow }) {
295
+ const [draft, setDraft] = useDebouncedTextDraft(filter.value, filter.onChange);
296
+ const listId = `${filter.key}-lookup-options`;
297
+ return /* @__PURE__ */ jsxs(
298
+ "label",
299
+ {
300
+ title: filter.description,
301
+ className: cn(
302
+ "flex h-8 items-center gap-2 rounded-md border border-input bg-muted/30 pl-2 pr-2 text-xs",
303
+ grow ? "min-w-[12rem] max-w-[18rem] flex-1" : "min-w-[11rem] max-w-[15rem] shrink-0",
304
+ filter.disabled && "opacity-60",
305
+ filter.className
306
+ ),
307
+ children: [
308
+ /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap font-medium uppercase tracking-wide text-muted-foreground", children: filter.label }),
309
+ filter.inputType === "date" ? /* @__PURE__ */ jsx(
310
+ DateTimePicker,
311
+ {
312
+ "aria-label": filter.label,
313
+ className: "w-full",
314
+ inputClassName: "w-full min-w-0 border-0 bg-transparent px-0 pr-6 text-sm text-foreground shadow-none focus-visible:ring-0",
315
+ buttonClassName: "right-0",
316
+ placeholder: filter.placeholder ?? "Filter…",
317
+ value: draft,
318
+ list: listId,
319
+ disabled: filter.disabled,
320
+ onChange: setDraft
321
+ }
322
+ ) : /* @__PURE__ */ jsx(
323
+ "input",
324
+ {
325
+ type: filter.inputType === "number" ? "number" : "text",
326
+ "aria-label": filter.label,
327
+ className: "w-full min-w-0 bg-transparent text-sm text-foreground outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed",
328
+ placeholder: filter.placeholder ?? "Filter…",
329
+ value: draft,
330
+ list: listId,
331
+ disabled: filter.disabled,
332
+ onChange: (event) => setDraft(event.target.value)
333
+ }
334
+ ),
335
+ /* @__PURE__ */ jsx("datalist", { id: listId, children: filter.options.map((option) => /* @__PURE__ */ jsx(
336
+ "option",
337
+ {
338
+ value: option.value,
339
+ label: option.label ?? option.value,
340
+ disabled: option.disabled
341
+ },
342
+ option.value
343
+ )) })
344
+ ]
345
+ }
346
+ );
347
+ }
348
+ function LookupMultiFilterField({
349
+ filter,
350
+ grow
351
+ }) {
352
+ const [draft, setDraft] = useDebouncedTextDraft(
353
+ filter.value.join(", "),
354
+ (next) => filter.onChange(parseLookupMultiValue(next))
355
+ );
356
+ const listId = `${filter.key}-lookup-options`;
357
+ return /* @__PURE__ */ jsxs(
358
+ "label",
359
+ {
360
+ title: filter.description,
361
+ className: cn(
362
+ "flex h-8 items-center gap-2 rounded-md border border-input bg-muted/30 pl-2 pr-2 text-xs",
363
+ grow ? "min-w-[12rem] max-w-[18rem] flex-1" : "min-w-[11rem] max-w-[15rem] shrink-0",
364
+ filter.disabled && "opacity-60",
365
+ filter.className
366
+ ),
367
+ children: [
368
+ /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap font-medium uppercase tracking-wide text-muted-foreground", children: filter.label }),
369
+ /* @__PURE__ */ jsx(
370
+ "input",
371
+ {
372
+ type: "text",
373
+ "aria-label": filter.label,
374
+ className: "w-full min-w-0 bg-transparent text-sm text-foreground outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed",
375
+ placeholder: filter.placeholder ?? "value-1, value-2",
376
+ value: draft,
377
+ list: listId,
378
+ disabled: filter.disabled,
379
+ onChange: (event) => setDraft(event.target.value)
380
+ }
381
+ ),
382
+ /* @__PURE__ */ jsx("datalist", { id: listId, children: filter.options.map((option) => /* @__PURE__ */ jsx(
383
+ "option",
384
+ {
385
+ value: option.value,
386
+ label: option.label ?? option.value,
387
+ disabled: option.disabled
388
+ },
389
+ option.value
390
+ )) })
391
+ ]
392
+ }
393
+ );
394
+ }
395
+ function NumberFilterField({ filter, grow }) {
396
+ const rootRef = useRef(null);
397
+ const triggerRef = useRef(null);
398
+ const [open, setOpen] = useState(false);
399
+ useDismissablePopup(open, rootRef, triggerRef, () => setOpen(false));
400
+ const bounds = resolveNumberFilterBounds(filter);
401
+ const [draft, setDraft] = useDebouncedNumberDraft(filter.value, filter.onChange);
402
+ const sliderMin = clampNumber(parseFilterNumber(draft.min) ?? bounds.min, bounds.min, bounds.max);
403
+ const sliderMax = clampNumber(parseFilterNumber(draft.max) ?? bounds.max, bounds.min, bounds.max);
404
+ const activeMin = Math.min(sliderMin, sliderMax);
405
+ const activeMax = Math.max(sliderMin, sliderMax);
406
+ const summary = summarizeNumberFilter(filter, bounds, draft);
407
+ return /* @__PURE__ */ jsxs(
408
+ "div",
409
+ {
410
+ ref: rootRef,
411
+ title: filter.description,
412
+ className: cn(
413
+ "relative min-w-0",
414
+ grow ? "min-w-[8rem] max-w-[12rem] flex-1" : "shrink-0",
415
+ filter.disabled && "opacity-60",
416
+ filter.className
417
+ ),
418
+ children: [
419
+ /* @__PURE__ */ jsxs(
420
+ Button,
421
+ {
422
+ ref: triggerRef,
423
+ type: "button",
424
+ variant: "outline",
425
+ size: "sm",
426
+ "aria-label": `${filter.label} filter`,
427
+ "aria-haspopup": "dialog",
428
+ "aria-expanded": open,
429
+ disabled: filter.disabled,
430
+ onClick: () => setOpen((current) => !current),
431
+ className: cn(
432
+ "min-w-0 gap-2 font-normal",
433
+ grow ? "w-full max-w-[12rem] justify-between" : "w-auto max-w-[9.5rem] px-2.5",
434
+ summary === filter.label && "text-muted-foreground"
435
+ ),
436
+ children: [
437
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: summary }),
438
+ /* @__PURE__ */ jsx(
439
+ Icon,
440
+ {
441
+ name: open ? "codicon:chevron-up" : "codicon:chevron-down",
442
+ className: "text-muted-foreground"
443
+ }
444
+ )
445
+ ]
446
+ }
447
+ ),
448
+ open && /* @__PURE__ */ jsxs("div", { className: "absolute left-0 top-[calc(100%+0.375rem)] z-50 min-w-[18rem] max-w-[22rem] rounded-md border border-border bg-popover p-3 text-popover-foreground shadow-lg shadow-black/5", children: [
449
+ /* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-center justify-between gap-2", children: [
450
+ /* @__PURE__ */ jsx("div", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: filter.label }),
451
+ /* @__PURE__ */ jsx(
452
+ "button",
453
+ {
454
+ type: "button",
455
+ className: "text-[10px] text-primary disabled:text-muted-foreground",
456
+ onClick: () => {
457
+ setDraft({});
458
+ filter.onChange({});
459
+ },
460
+ disabled: !String(draft.min ?? "").trim() && !String(draft.max ?? "").trim(),
461
+ children: "Clear all"
462
+ }
463
+ )
464
+ ] }),
465
+ /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
466
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
467
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-[11px] text-muted-foreground", children: [
468
+ /* @__PURE__ */ jsx("span", { children: formatNumberValue(bounds.min, filter) }),
469
+ /* @__PURE__ */ jsx("span", { children: formatNumberValue(bounds.max, filter) })
470
+ ] }),
471
+ /* @__PURE__ */ jsx("div", { className: "relative h-6", children: /* @__PURE__ */ jsx(
472
+ RangeSlider,
473
+ {
474
+ min: bounds.min,
475
+ max: bounds.max,
476
+ step: bounds.step,
477
+ value: [activeMin, activeMax],
478
+ ariaLabelMin: `${filter.label} minimum slider`,
479
+ ariaLabelMax: `${filter.label} maximum slider`,
480
+ onChange: ([nextMin, nextMax]) => setDraft(numberFilterValueFromSlider([nextMin, nextMax], bounds))
481
+ }
482
+ ) })
483
+ ] }),
484
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
485
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
486
+ /* @__PURE__ */ jsx("label", { className: "text-[10px] text-muted-foreground", children: "Min" }),
487
+ /* @__PURE__ */ jsx(
488
+ "input",
489
+ {
490
+ type: "number",
491
+ inputMode: "decimal",
492
+ step: bounds.step,
493
+ "aria-label": `${filter.label} minimum`,
494
+ className: "h-8 w-full rounded-md border border-input bg-background px-2 text-xs outline-none focus-visible:ring-2 focus-visible:ring-ring",
495
+ placeholder: filter.minPlaceholder ?? formatNumberValue(bounds.min, filter),
496
+ value: draft.min ?? "",
497
+ onChange: (event) => setDraft(
498
+ normalizeNumberFilterValue(
499
+ { min: event.target.value, max: draft.max ?? "" },
500
+ "min-input"
501
+ )
502
+ )
503
+ }
504
+ )
505
+ ] }),
506
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
507
+ /* @__PURE__ */ jsx("label", { className: "text-[10px] text-muted-foreground", children: "Max" }),
508
+ /* @__PURE__ */ jsx(
509
+ "input",
510
+ {
511
+ type: "number",
512
+ inputMode: "decimal",
513
+ step: bounds.step,
514
+ "aria-label": `${filter.label} maximum`,
515
+ className: "h-8 w-full rounded-md border border-input bg-background px-2 text-xs outline-none focus-visible:ring-2 focus-visible:ring-ring",
516
+ placeholder: filter.maxPlaceholder ?? formatNumberValue(bounds.max, filter),
517
+ value: draft.max ?? "",
518
+ onChange: (event) => setDraft(
519
+ normalizeNumberFilterValue(
520
+ { min: draft.min ?? "", max: event.target.value },
521
+ "max-input"
522
+ )
523
+ )
524
+ }
525
+ )
526
+ ] })
527
+ ] })
528
+ ] })
529
+ ] })
530
+ ]
531
+ }
532
+ );
533
+ }
534
+ function NumberFilterPanel({
535
+ filter,
536
+ chrome = "full"
537
+ }) {
538
+ const bounds = resolveNumberFilterBounds(filter);
539
+ const [draft, setDraft] = useDebouncedNumberDraft(filter.value, filter.onChange);
540
+ const sliderMin = clampNumber(parseFilterNumber(draft.min) ?? bounds.min, bounds.min, bounds.max);
541
+ const sliderMax = clampNumber(parseFilterNumber(draft.max) ?? bounds.max, bounds.min, bounds.max);
542
+ const activeMin = Math.min(sliderMin, sliderMax);
543
+ const activeMax = Math.max(sliderMin, sliderMax);
544
+ const embedded = chrome === "embedded";
545
+ return /* @__PURE__ */ jsxs(
546
+ "div",
547
+ {
548
+ "data-filter-panel-chrome": chrome,
549
+ className: cn(
550
+ "min-w-[18rem] max-w-[22rem] text-popover-foreground",
551
+ embedded ? "p-0" : "rounded-md border border-border bg-popover p-3 shadow-sm shadow-black/5"
552
+ ),
553
+ children: [
554
+ !embedded && /* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-center justify-between gap-2", children: [
555
+ /* @__PURE__ */ jsx("div", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: filter.label }),
556
+ /* @__PURE__ */ jsx(
557
+ "button",
558
+ {
559
+ type: "button",
560
+ className: "text-[10px] text-primary disabled:text-muted-foreground",
561
+ onClick: () => {
562
+ setDraft({});
563
+ filter.onChange({});
564
+ },
565
+ disabled: !String(draft.min ?? "").trim() && !String(draft.max ?? "").trim(),
566
+ children: "Clear all"
567
+ }
568
+ )
569
+ ] }),
570
+ /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
571
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
572
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-[11px] text-muted-foreground", children: [
573
+ /* @__PURE__ */ jsx("span", { children: formatNumberValue(bounds.min, filter) }),
574
+ /* @__PURE__ */ jsx("span", { children: formatNumberValue(bounds.max, filter) })
575
+ ] }),
576
+ /* @__PURE__ */ jsx("div", { className: "relative h-6", children: /* @__PURE__ */ jsx(
577
+ RangeSlider,
578
+ {
579
+ min: bounds.min,
580
+ max: bounds.max,
581
+ step: bounds.step,
582
+ value: [activeMin, activeMax],
583
+ ariaLabelMin: `${filter.label} minimum slider`,
584
+ ariaLabelMax: `${filter.label} maximum slider`,
585
+ onChange: ([nextMin, nextMax]) => setDraft(numberFilterValueFromSlider([nextMin, nextMax], bounds))
586
+ }
587
+ ) })
588
+ ] }),
589
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
590
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
591
+ /* @__PURE__ */ jsx("label", { className: "text-[10px] text-muted-foreground", children: "Min" }),
592
+ /* @__PURE__ */ jsx(
593
+ "input",
594
+ {
595
+ type: "number",
596
+ inputMode: "decimal",
597
+ step: bounds.step,
598
+ "aria-label": `${filter.label} minimum`,
599
+ className: "h-8 w-full rounded-md border border-input bg-background px-2 text-xs outline-none focus-visible:ring-2 focus-visible:ring-ring",
600
+ placeholder: filter.minPlaceholder ?? formatNumberValue(bounds.min, filter),
601
+ value: draft.min ?? "",
602
+ onChange: (event) => setDraft(
603
+ normalizeNumberFilterValue(
604
+ { min: event.target.value, max: draft.max ?? "" },
605
+ "min-input"
606
+ )
607
+ )
608
+ }
609
+ )
610
+ ] }),
611
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
612
+ /* @__PURE__ */ jsx("label", { className: "text-[10px] text-muted-foreground", children: "Max" }),
613
+ /* @__PURE__ */ jsx(
614
+ "input",
615
+ {
616
+ type: "number",
617
+ inputMode: "decimal",
618
+ step: bounds.step,
619
+ "aria-label": `${filter.label} maximum`,
620
+ className: "h-8 w-full rounded-md border border-input bg-background px-2 text-xs outline-none focus-visible:ring-2 focus-visible:ring-ring",
621
+ placeholder: filter.maxPlaceholder ?? formatNumberValue(bounds.max, filter),
622
+ value: draft.max ?? "",
623
+ onChange: (event) => setDraft(
624
+ normalizeNumberFilterValue(
625
+ { min: draft.min ?? "", max: event.target.value },
626
+ "max-input"
627
+ )
628
+ )
629
+ }
630
+ )
631
+ ] })
632
+ ] })
633
+ ] })
634
+ ]
635
+ }
636
+ );
637
+ }
638
+ function MultiFilterField({ filter, grow }) {
639
+ const rootRef = useRef(null);
640
+ const triggerRef = useRef(null);
641
+ const [open, setOpen] = useState(false);
642
+ const [optionQuery, setOptionQuery] = useState("");
643
+ const [draft, setDraft] = useDebouncedMultiDraft(filter.value, filter.onChange);
644
+ useDismissablePopup(open, rootRef, triggerRef, () => setOpen(false));
645
+ const summary = summarizeMultiFilter(filter.label, draft);
646
+ const showOptionFilter = filter.options.length > 7;
647
+ const visibleOptions = useMemo(() => {
648
+ const query = optionQuery.trim().toLowerCase();
649
+ if (!query) return filter.options;
650
+ return filter.options.filter(
651
+ (option) => multiSelectOptionText(option).toLowerCase().includes(query)
652
+ );
653
+ }, [filter.options, optionQuery]);
654
+ return /* @__PURE__ */ jsxs(
655
+ "div",
656
+ {
657
+ ref: rootRef,
658
+ title: filter.description,
659
+ className: cn(
660
+ "relative min-w-0",
661
+ grow ? "min-w-[8rem] max-w-[12rem] flex-1" : "shrink-0",
662
+ filter.disabled && "opacity-60",
663
+ filter.className
664
+ ),
665
+ children: [
666
+ /* @__PURE__ */ jsxs(
667
+ Button,
668
+ {
669
+ ref: triggerRef,
670
+ type: "button",
671
+ variant: "outline",
672
+ size: "sm",
673
+ "aria-label": `${filter.label} filter`,
674
+ "aria-haspopup": "dialog",
675
+ "aria-expanded": open,
676
+ disabled: filter.disabled,
677
+ onClick: () => setOpen((current) => !current),
678
+ className: cn(
679
+ "min-w-0 gap-2 font-normal",
680
+ grow ? "w-full max-w-[12rem] justify-between" : "w-auto max-w-[8.5rem] px-2.5",
681
+ summary === filter.label && "text-muted-foreground"
682
+ ),
683
+ children: [
684
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: summary }),
685
+ /* @__PURE__ */ jsx(
686
+ Icon,
687
+ {
688
+ name: open ? "codicon:chevron-up" : "codicon:chevron-down",
689
+ className: "text-muted-foreground"
690
+ }
691
+ )
692
+ ]
693
+ }
694
+ ),
695
+ open && /* @__PURE__ */ jsxs("div", { className: "absolute left-0 top-[calc(100%+0.375rem)] z-50 min-w-[18rem] max-w-[22rem] rounded-md border border-border bg-popover p-2 text-popover-foreground shadow-lg shadow-black/5", children: [
696
+ /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [
697
+ /* @__PURE__ */ jsx("div", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: filter.label }),
698
+ /* @__PURE__ */ jsx(
699
+ "button",
700
+ {
701
+ type: "button",
702
+ className: "text-[10px] text-primary disabled:text-muted-foreground",
703
+ onClick: () => setDraft({}),
704
+ disabled: Object.keys(draft).length === 0,
705
+ children: "Clear all"
706
+ }
707
+ )
708
+ ] }),
709
+ showOptionFilter && /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center gap-2 rounded-md border border-input bg-background px-2", children: [
710
+ /* @__PURE__ */ jsx(Icon, { name: "codicon:search", className: "shrink-0 text-muted-foreground" }),
711
+ /* @__PURE__ */ jsx(
712
+ "input",
713
+ {
714
+ type: "search",
715
+ "aria-label": `Filter ${filter.label} options`,
716
+ className: "h-8 min-w-0 flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground",
717
+ placeholder: `Filter ${filter.label.toLowerCase()}`,
718
+ value: optionQuery,
719
+ onChange: (event) => setOptionQuery(event.target.value)
720
+ }
721
+ )
722
+ ] }),
723
+ /* @__PURE__ */ jsxs("div", { className: "max-h-72 space-y-0.5 overflow-auto", children: [
724
+ visibleOptions.map((option) => {
725
+ const mode = draft[option.value] ?? "neutral";
726
+ const title = option.title ?? multiSelectOptionText(option);
727
+ return /* @__PURE__ */ jsx(
728
+ "div",
729
+ {
730
+ role: "button",
731
+ tabIndex: 0,
732
+ "data-filter-option": option.value,
733
+ className: "rounded-md px-1.5 py-0.5 hover:bg-accent/50 focus-visible:bg-accent/50 focus-visible:outline-none",
734
+ onClick: () => setDraft(updateMultiFilterValue(draft, option.value, nextFilterMode(mode))),
735
+ onKeyDown: (event) => {
736
+ if (event.key === "Enter" || event.key === " ") {
737
+ event.preventDefault();
738
+ setDraft(updateMultiFilterValue(draft, option.value, nextFilterMode(mode)));
739
+ }
740
+ },
741
+ children: /* @__PURE__ */ jsx(
742
+ FilterPill,
743
+ {
744
+ className: "w-full justify-between",
745
+ label: option.label,
746
+ mode,
747
+ title,
748
+ togglePosition: "right",
749
+ onModeChange: (next) => setDraft(updateMultiFilterValue(draft, option.value, next))
750
+ }
751
+ )
752
+ },
753
+ option.value
754
+ );
755
+ }),
756
+ visibleOptions.length === 0 && /* @__PURE__ */ jsx("div", { className: "px-2 py-3 text-sm text-muted-foreground", children: "No options found" })
757
+ ] })
758
+ ] })
759
+ ]
760
+ }
761
+ );
762
+ }
763
+ function MultiFilterPanel({
764
+ filter,
765
+ chrome = "full"
766
+ }) {
767
+ const [optionQuery, setOptionQuery] = useState("");
768
+ const [draft, setDraft] = useDebouncedMultiDraft(filter.value, filter.onChange);
769
+ const showOptionFilter = filter.options.length > 7;
770
+ const embedded = chrome === "embedded";
771
+ const visibleOptions = useMemo(() => {
772
+ const query = optionQuery.trim().toLowerCase();
773
+ if (!query) return filter.options;
774
+ return filter.options.filter(
775
+ (option) => multiSelectOptionText(option).toLowerCase().includes(query)
776
+ );
777
+ }, [filter.options, optionQuery]);
778
+ return /* @__PURE__ */ jsxs(
779
+ "div",
780
+ {
781
+ "data-filter-panel-chrome": chrome,
782
+ className: cn(
783
+ "min-w-[18rem] max-w-[22rem] text-popover-foreground",
784
+ embedded ? "p-0" : "rounded-md border border-border bg-popover p-2 shadow-sm shadow-black/5"
785
+ ),
786
+ children: [
787
+ !embedded && /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [
788
+ /* @__PURE__ */ jsx("div", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: filter.label }),
789
+ /* @__PURE__ */ jsx(
790
+ "button",
791
+ {
792
+ type: "button",
793
+ className: "text-[10px] text-primary disabled:text-muted-foreground",
794
+ onClick: () => setDraft({}),
795
+ disabled: Object.keys(draft).length === 0,
796
+ children: "Clear all"
797
+ }
798
+ )
799
+ ] }),
800
+ showOptionFilter && /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center gap-2 rounded-md border border-input bg-background px-2", children: [
801
+ /* @__PURE__ */ jsx(Icon, { name: "codicon:search", className: "shrink-0 text-muted-foreground" }),
802
+ /* @__PURE__ */ jsx(
803
+ "input",
804
+ {
805
+ type: "search",
806
+ "aria-label": `Filter ${filter.label} options`,
807
+ className: "h-8 min-w-0 flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground",
808
+ placeholder: `Filter ${filter.label.toLowerCase()}`,
809
+ value: optionQuery,
810
+ onChange: (event) => setOptionQuery(event.target.value)
811
+ }
812
+ )
813
+ ] }),
814
+ /* @__PURE__ */ jsxs("div", { className: "max-h-72 space-y-0.5 overflow-auto", children: [
815
+ visibleOptions.map((option) => {
816
+ const mode = draft[option.value] ?? "neutral";
817
+ const title = option.title ?? multiSelectOptionText(option);
818
+ return /* @__PURE__ */ jsx(
819
+ "div",
820
+ {
821
+ role: "button",
822
+ tabIndex: 0,
823
+ "data-filter-option": option.value,
824
+ className: "rounded-md px-1.5 py-0.5 hover:bg-accent/50 focus-visible:bg-accent/50 focus-visible:outline-none",
825
+ onClick: () => setDraft(updateMultiFilterValue(draft, option.value, nextFilterMode(mode))),
826
+ onKeyDown: (event) => {
827
+ if (event.key === "Enter" || event.key === " ") {
828
+ event.preventDefault();
829
+ setDraft(updateMultiFilterValue(draft, option.value, nextFilterMode(mode)));
830
+ }
831
+ },
832
+ children: /* @__PURE__ */ jsx(
833
+ FilterPill,
834
+ {
835
+ className: "w-full justify-between",
836
+ label: option.label,
837
+ mode,
838
+ title,
839
+ togglePosition: "right",
840
+ onModeChange: (next) => setDraft(updateMultiFilterValue(draft, option.value, next))
841
+ }
842
+ )
843
+ },
844
+ option.value
845
+ );
846
+ }),
847
+ visibleOptions.length === 0 && /* @__PURE__ */ jsx("div", { className: "px-2 py-3 text-sm text-muted-foreground", children: "No options found" })
848
+ ] })
849
+ ]
850
+ }
851
+ );
852
+ }
853
+ function NestedMultiFilterPanel({
854
+ filter,
855
+ chrome = "full"
856
+ }) {
857
+ var _a;
858
+ const [activeGroup, setActiveGroup] = useState(((_a = filter.groups[0]) == null ? void 0 : _a.groupKey) ?? null);
859
+ const [draft, setDraft] = useDebouncedMultiDraft(filter.value, filter.onChange);
860
+ const groups = filter.groups;
861
+ const embedded = chrome === "embedded";
862
+ const activeGroupData = useMemo(
863
+ () => groups.find((group) => group.groupKey === activeGroup) ?? groups[0] ?? null,
864
+ [groups, activeGroup]
865
+ );
866
+ const selectedByGroup = useMemo(() => {
867
+ const counts = {};
868
+ for (const group of groups) {
869
+ let n = 0;
870
+ for (const option of group.options) {
871
+ if (draft[option.value]) n += 1;
872
+ }
873
+ counts[group.groupKey] = n;
874
+ }
875
+ return counts;
876
+ }, [groups, draft]);
877
+ const sortedGroups = useMemo(() => {
878
+ const selected = [];
879
+ const rest = [];
880
+ for (const group of groups) {
881
+ if ((selectedByGroup[group.groupKey] ?? 0) > 0) selected.push(group);
882
+ else rest.push(group);
883
+ }
884
+ return [...selected, ...rest];
885
+ }, [groups, selectedByGroup]);
886
+ const clearGroup = (groupKey) => {
887
+ const group = groups.find((g) => g.groupKey === groupKey);
888
+ if (!group) return;
889
+ const next = { ...draft };
890
+ for (const option of group.options) {
891
+ delete next[option.value];
892
+ }
893
+ setDraft(next);
894
+ };
895
+ return /* @__PURE__ */ jsxs("div", { role: "dialog", className: "flex", children: [
896
+ /* @__PURE__ */ jsxs(
897
+ "div",
898
+ {
899
+ "data-filter-panel-chrome": chrome,
900
+ className: cn(
901
+ "min-w-[14rem] max-w-[16rem] text-popover-foreground",
902
+ embedded ? "p-0" : "rounded-md border border-border bg-popover p-2 shadow-sm shadow-black/5"
903
+ ),
904
+ children: [
905
+ !embedded && /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [
906
+ /* @__PURE__ */ jsx("div", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: filter.label }),
907
+ /* @__PURE__ */ jsx(
908
+ "button",
909
+ {
910
+ type: "button",
911
+ className: "text-[10px] text-primary disabled:text-muted-foreground",
912
+ onClick: () => setDraft({}),
913
+ disabled: Object.keys(draft).length === 0,
914
+ children: "Clear all"
915
+ }
916
+ )
917
+ ] }),
918
+ /* @__PURE__ */ jsxs("div", { className: "max-h-72 space-y-0.5 overflow-auto", children: [
919
+ sortedGroups.map((group) => {
920
+ const selected = selectedByGroup[group.groupKey] ?? 0;
921
+ const isActive = group.groupKey === activeGroup;
922
+ return /* @__PURE__ */ jsxs(
923
+ "div",
924
+ {
925
+ role: "button",
926
+ tabIndex: 0,
927
+ onMouseEnter: () => setActiveGroup(group.groupKey),
928
+ onFocus: () => setActiveGroup(group.groupKey),
929
+ onClick: () => setActiveGroup(group.groupKey),
930
+ onKeyDown: (event) => {
931
+ if (event.key === "Enter" || event.key === " " || event.key === "ArrowRight") {
932
+ event.preventDefault();
933
+ setActiveGroup(group.groupKey);
934
+ }
935
+ },
936
+ className: cn(
937
+ "flex cursor-pointer items-center justify-between gap-2 rounded-md px-1.5 py-0.5 text-sm",
938
+ "hover:bg-accent/50 focus-visible:bg-accent/50 focus-visible:outline-none",
939
+ isActive && "bg-accent/60"
940
+ ),
941
+ children: [
942
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: group.label ?? group.groupKey }),
943
+ selected > 0 && /* @__PURE__ */ jsxs("span", { className: "rounded-full bg-primary/15 px-1.5 text-[10px] font-medium text-primary", children: [
944
+ selected,
945
+ "/",
946
+ group.options.length
947
+ ] }),
948
+ /* @__PURE__ */ jsx(Icon, { name: "codicon:chevron-right", className: "shrink-0 text-muted-foreground" })
949
+ ]
950
+ },
951
+ group.groupKey
952
+ );
953
+ }),
954
+ groups.length === 0 && /* @__PURE__ */ jsx("div", { className: "px-2 py-3 text-sm text-muted-foreground", children: "No groups" })
955
+ ] })
956
+ ]
957
+ }
958
+ ),
959
+ activeGroupData && /* @__PURE__ */ jsxs(
960
+ "div",
961
+ {
962
+ onMouseEnter: () => setActiveGroup(activeGroupData.groupKey),
963
+ className: "ml-1.5 min-w-[16rem] max-w-[20rem] rounded-md border border-border bg-popover p-2 text-popover-foreground shadow-sm shadow-black/5",
964
+ children: [
965
+ /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [
966
+ /* @__PURE__ */ jsx("div", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: activeGroupData.label ?? activeGroupData.groupKey }),
967
+ /* @__PURE__ */ jsx(
968
+ "button",
969
+ {
970
+ type: "button",
971
+ className: "text-[10px] text-primary disabled:text-muted-foreground",
972
+ onClick: () => clearGroup(activeGroupData.groupKey),
973
+ disabled: (selectedByGroup[activeGroupData.groupKey] ?? 0) === 0,
974
+ children: "Clear"
975
+ }
976
+ )
977
+ ] }),
978
+ /* @__PURE__ */ jsxs("div", { className: "max-h-72 space-y-0.5 overflow-auto", children: [
979
+ activeGroupData.options.map((option) => {
980
+ const mode = draft[option.value] ?? "neutral";
981
+ const title = option.title ?? multiSelectOptionText(option);
982
+ return /* @__PURE__ */ jsx(
983
+ "div",
984
+ {
985
+ role: "button",
986
+ tabIndex: 0,
987
+ "data-filter-option": option.value,
988
+ className: "rounded-md px-1.5 py-0.5 hover:bg-accent/50 focus-visible:bg-accent/50 focus-visible:outline-none",
989
+ onClick: () => setDraft(updateMultiFilterValue(draft, option.value, nextFilterMode(mode))),
990
+ onKeyDown: (event) => {
991
+ if (event.key === "Enter" || event.key === " ") {
992
+ event.preventDefault();
993
+ setDraft(updateMultiFilterValue(draft, option.value, nextFilterMode(mode)));
994
+ } else if (event.key === "ArrowLeft") {
995
+ event.preventDefault();
996
+ setActiveGroup(null);
997
+ }
998
+ },
999
+ children: /* @__PURE__ */ jsx(
1000
+ FilterPill,
1001
+ {
1002
+ className: "w-full justify-between",
1003
+ label: renderNestedOptionLabel(option, activeGroupData.groupKey),
1004
+ mode,
1005
+ title,
1006
+ togglePosition: "right",
1007
+ onModeChange: (next) => setDraft(updateMultiFilterValue(draft, option.value, next))
1008
+ }
1009
+ )
1010
+ },
1011
+ option.value
1012
+ );
1013
+ }),
1014
+ activeGroupData.options.length === 0 && /* @__PURE__ */ jsx("div", { className: "px-2 py-3 text-sm text-muted-foreground", children: "No values" })
1015
+ ] })
1016
+ ]
1017
+ }
1018
+ )
1019
+ ] });
1020
+ }
1021
+ function multiSelectOptionText(option) {
1022
+ const label = typeof option.label === "string" ? option.label : "";
1023
+ return [option.value, label, option.title ?? ""].filter(Boolean).join(" ");
1024
+ }
1025
+ function NestedMultiFilterField({
1026
+ filter,
1027
+ grow
1028
+ }) {
1029
+ const rootRef = useRef(null);
1030
+ const triggerRef = useRef(null);
1031
+ const [open, setOpen] = useState(false);
1032
+ const [activeGroup, setActiveGroup] = useState(null);
1033
+ const [draft, setDraft] = useDebouncedMultiDraft(filter.value, filter.onChange);
1034
+ useDismissablePopup(open, rootRef, triggerRef, () => {
1035
+ setOpen(false);
1036
+ setActiveGroup(null);
1037
+ });
1038
+ const summary = summarizeMultiFilter(filter.label, draft);
1039
+ const groups = filter.groups;
1040
+ const activeGroupData = useMemo(
1041
+ () => groups.find((group) => group.groupKey === activeGroup) ?? null,
1042
+ [groups, activeGroup]
1043
+ );
1044
+ const selectedByGroup = useMemo(() => {
1045
+ const counts = {};
1046
+ for (const group of groups) {
1047
+ let n = 0;
1048
+ for (const option of group.options) {
1049
+ if (draft[option.value]) n += 1;
1050
+ }
1051
+ counts[group.groupKey] = n;
1052
+ }
1053
+ return counts;
1054
+ }, [groups, draft]);
1055
+ const sortedGroups = useMemo(() => {
1056
+ const selected = [];
1057
+ const rest = [];
1058
+ for (const group of groups) {
1059
+ if ((selectedByGroup[group.groupKey] ?? 0) > 0) selected.push(group);
1060
+ else rest.push(group);
1061
+ }
1062
+ return [...selected, ...rest];
1063
+ }, [groups, selectedByGroup]);
1064
+ const clearGroup = (groupKey) => {
1065
+ const group = groups.find((g) => g.groupKey === groupKey);
1066
+ if (!group) return;
1067
+ const next = { ...draft };
1068
+ for (const option of group.options) {
1069
+ delete next[option.value];
1070
+ }
1071
+ setDraft(next);
1072
+ };
1073
+ return /* @__PURE__ */ jsxs(
1074
+ "div",
1075
+ {
1076
+ ref: rootRef,
1077
+ title: filter.description,
1078
+ className: cn(
1079
+ "relative min-w-0",
1080
+ grow ? "min-w-[8rem] max-w-[12rem] flex-1" : "shrink-0",
1081
+ filter.disabled && "opacity-60",
1082
+ filter.className
1083
+ ),
1084
+ children: [
1085
+ /* @__PURE__ */ jsxs(
1086
+ Button,
1087
+ {
1088
+ ref: triggerRef,
1089
+ type: "button",
1090
+ variant: "outline",
1091
+ size: "sm",
1092
+ "aria-label": `${filter.label} filter`,
1093
+ "aria-haspopup": "dialog",
1094
+ "aria-expanded": open,
1095
+ disabled: filter.disabled,
1096
+ onClick: () => {
1097
+ setOpen((current) => !current);
1098
+ if (open) setActiveGroup(null);
1099
+ },
1100
+ className: cn(
1101
+ "min-w-0 gap-2 font-normal",
1102
+ grow ? "w-full max-w-[12rem] justify-between" : "w-auto max-w-[8.5rem] px-2.5",
1103
+ summary === filter.label && "text-muted-foreground"
1104
+ ),
1105
+ children: [
1106
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: summary }),
1107
+ /* @__PURE__ */ jsx(
1108
+ Icon,
1109
+ {
1110
+ name: open ? "codicon:chevron-up" : "codicon:chevron-down",
1111
+ className: "text-muted-foreground"
1112
+ }
1113
+ )
1114
+ ]
1115
+ }
1116
+ ),
1117
+ open && /* @__PURE__ */ jsxs(
1118
+ "div",
1119
+ {
1120
+ role: "dialog",
1121
+ onKeyDown: (event) => {
1122
+ if (event.key === "Escape") {
1123
+ event.preventDefault();
1124
+ setOpen(false);
1125
+ setActiveGroup(null);
1126
+ }
1127
+ },
1128
+ className: "absolute left-0 top-[calc(100%+0.375rem)] z-50 flex",
1129
+ children: [
1130
+ /* @__PURE__ */ jsxs("div", { className: "min-w-[14rem] max-w-[16rem] rounded-md border border-border bg-popover p-2 text-popover-foreground shadow-lg shadow-black/5", children: [
1131
+ /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [
1132
+ /* @__PURE__ */ jsx("div", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: filter.label }),
1133
+ /* @__PURE__ */ jsx(
1134
+ "button",
1135
+ {
1136
+ type: "button",
1137
+ className: "text-[10px] text-primary disabled:text-muted-foreground",
1138
+ onClick: () => setDraft({}),
1139
+ disabled: Object.keys(draft).length === 0,
1140
+ children: "Clear all"
1141
+ }
1142
+ )
1143
+ ] }),
1144
+ /* @__PURE__ */ jsxs("div", { className: "max-h-72 space-y-0.5 overflow-auto", children: [
1145
+ sortedGroups.map((group) => {
1146
+ const selected = selectedByGroup[group.groupKey] ?? 0;
1147
+ const isActive = group.groupKey === activeGroup;
1148
+ return /* @__PURE__ */ jsxs(
1149
+ "div",
1150
+ {
1151
+ role: "button",
1152
+ tabIndex: 0,
1153
+ onMouseEnter: () => setActiveGroup(group.groupKey),
1154
+ onFocus: () => setActiveGroup(group.groupKey),
1155
+ onClick: () => setActiveGroup(group.groupKey),
1156
+ onKeyDown: (event) => {
1157
+ if (event.key === "Enter" || event.key === " " || event.key === "ArrowRight") {
1158
+ event.preventDefault();
1159
+ setActiveGroup(group.groupKey);
1160
+ }
1161
+ },
1162
+ className: cn(
1163
+ "flex cursor-pointer items-center justify-between gap-2 rounded-md px-1.5 py-0.5 text-sm",
1164
+ "hover:bg-accent/50 focus-visible:bg-accent/50 focus-visible:outline-none",
1165
+ isActive && "bg-accent/60"
1166
+ ),
1167
+ children: [
1168
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: group.label ?? group.groupKey }),
1169
+ selected > 0 && /* @__PURE__ */ jsxs("span", { className: "rounded-full bg-primary/15 px-1.5 text-[10px] font-medium text-primary", children: [
1170
+ selected,
1171
+ "/",
1172
+ group.options.length
1173
+ ] }),
1174
+ /* @__PURE__ */ jsx(Icon, { name: "codicon:chevron-right", className: "shrink-0 text-muted-foreground" })
1175
+ ]
1176
+ },
1177
+ group.groupKey
1178
+ );
1179
+ }),
1180
+ groups.length === 0 && /* @__PURE__ */ jsx("div", { className: "px-2 py-3 text-sm text-muted-foreground", children: "No groups" })
1181
+ ] })
1182
+ ] }),
1183
+ activeGroupData && /* @__PURE__ */ jsxs(
1184
+ "div",
1185
+ {
1186
+ onMouseEnter: () => setActiveGroup(activeGroupData.groupKey),
1187
+ className: "ml-1.5 min-w-[16rem] max-w-[20rem] rounded-md border border-border bg-popover p-2 text-popover-foreground shadow-lg shadow-black/5",
1188
+ children: [
1189
+ /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [
1190
+ /* @__PURE__ */ jsx("div", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: activeGroupData.label ?? activeGroupData.groupKey }),
1191
+ /* @__PURE__ */ jsx(
1192
+ "button",
1193
+ {
1194
+ type: "button",
1195
+ className: "text-[10px] text-primary disabled:text-muted-foreground",
1196
+ onClick: () => clearGroup(activeGroupData.groupKey),
1197
+ disabled: (selectedByGroup[activeGroupData.groupKey] ?? 0) === 0,
1198
+ children: "Clear"
1199
+ }
1200
+ )
1201
+ ] }),
1202
+ /* @__PURE__ */ jsxs("div", { className: "max-h-72 space-y-0.5 overflow-auto", children: [
1203
+ activeGroupData.options.map((option) => {
1204
+ const mode = draft[option.value] ?? "neutral";
1205
+ const title = option.title ?? multiSelectOptionText(option);
1206
+ return /* @__PURE__ */ jsx(
1207
+ "div",
1208
+ {
1209
+ role: "button",
1210
+ tabIndex: 0,
1211
+ "data-filter-option": option.value,
1212
+ className: "rounded-md px-1.5 py-0.5 hover:bg-accent/50 focus-visible:bg-accent/50 focus-visible:outline-none",
1213
+ onClick: () => setDraft(updateMultiFilterValue(draft, option.value, nextFilterMode(mode))),
1214
+ onKeyDown: (event) => {
1215
+ if (event.key === "Enter" || event.key === " ") {
1216
+ event.preventDefault();
1217
+ setDraft(
1218
+ updateMultiFilterValue(draft, option.value, nextFilterMode(mode))
1219
+ );
1220
+ } else if (event.key === "ArrowLeft") {
1221
+ event.preventDefault();
1222
+ setActiveGroup(null);
1223
+ }
1224
+ },
1225
+ children: /* @__PURE__ */ jsx(
1226
+ FilterPill,
1227
+ {
1228
+ className: "w-full justify-between",
1229
+ label: renderNestedOptionLabel(option, activeGroupData.groupKey),
1230
+ mode,
1231
+ title,
1232
+ togglePosition: "right",
1233
+ onModeChange: (next) => setDraft(updateMultiFilterValue(draft, option.value, next))
1234
+ }
1235
+ )
1236
+ },
1237
+ option.value
1238
+ );
1239
+ }),
1240
+ activeGroupData.options.length === 0 && /* @__PURE__ */ jsx("div", { className: "px-2 py-3 text-sm text-muted-foreground", children: "No values" })
1241
+ ] })
1242
+ ]
1243
+ }
1244
+ )
1245
+ ]
1246
+ }
1247
+ )
1248
+ ]
1249
+ }
1250
+ );
1251
+ }
1252
+ function renderNestedOptionLabel(option, groupKey) {
1253
+ if (typeof option.label === "string") {
1254
+ const prefix = `${groupKey}=`;
1255
+ if (option.label.startsWith(prefix)) return option.label.slice(prefix.length);
1256
+ return option.label;
1257
+ }
1258
+ return option.label ?? option.value;
1259
+ }
1260
+ function SelectMultiFilterField({
1261
+ filter,
1262
+ grow
1263
+ }) {
1264
+ return /* @__PURE__ */ jsxs(
1265
+ "label",
1266
+ {
1267
+ title: filter.description,
1268
+ className: cn(
1269
+ "flex h-8 items-center gap-2 rounded-md border border-input bg-muted/30 pl-2 pr-1 text-xs",
1270
+ grow ? "min-w-[12rem] max-w-[18rem] flex-1" : "min-w-[11rem] max-w-[15rem] shrink-0",
1271
+ filter.disabled && "opacity-60",
1272
+ filter.className
1273
+ ),
1274
+ children: [
1275
+ /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap font-medium uppercase tracking-wide text-muted-foreground", children: filter.label }),
1276
+ /* @__PURE__ */ jsx(
1277
+ MultiSelect,
1278
+ {
1279
+ options: filter.options,
1280
+ value: filter.value,
1281
+ onChange: filter.onChange,
1282
+ placeholder: filter.placeholder ?? `Any ${filter.label.toLowerCase()}`,
1283
+ ...filter.disabled !== void 0 ? { disabled: filter.disabled } : {},
1284
+ triggerClassName: "h-6 min-w-0 border-0 bg-transparent px-1 text-xs shadow-none focus-visible:ring-0",
1285
+ menuClassName: "left-auto right-0"
1286
+ }
1287
+ )
1288
+ ]
1289
+ }
1290
+ );
1291
+ }
1292
+ function RangeControlButton({
1293
+ kind,
1294
+ label,
1295
+ from = "",
1296
+ to = "",
1297
+ onApply,
1298
+ presets: _presets,
1299
+ fromPlaceholder,
1300
+ toPlaceholder,
1301
+ emptyLabel,
1302
+ className
1303
+ }) {
1304
+ const rootRef = useRef(null);
1305
+ const triggerRef = useRef(null);
1306
+ const fromInputRef = useRef(null);
1307
+ const toInputRef = useRef(null);
1308
+ const [open, setOpen] = useState(false);
1309
+ const [draftFrom, setDraftFrom] = useState(from);
1310
+ const [draftTo, setDraftTo] = useState(to);
1311
+ useDismissablePopup(open, rootRef, triggerRef, () => setOpen(false));
1312
+ useEffect(() => {
1313
+ if (open) {
1314
+ setDraftFrom(from);
1315
+ setDraftTo(to);
1316
+ }
1317
+ }, [from, open, to]);
1318
+ const buttonLabel = formatRangeLabel(kind, from, to, emptyLabel);
1319
+ function applyRange(nextFrom, nextTo) {
1320
+ onApply(
1321
+ kind === "time" ? normalizeDateMath(nextFrom) : nextFrom.trim(),
1322
+ kind === "time" ? normalizeDateMath(nextTo) : nextTo.trim()
1323
+ );
1324
+ setOpen(false);
1325
+ }
1326
+ function clearRange() {
1327
+ setDraftFrom("");
1328
+ setDraftTo("");
1329
+ onApply("", "");
1330
+ setOpen(false);
1331
+ }
1332
+ return /* @__PURE__ */ jsxs("div", { ref: rootRef, className: cn("relative", className), children: [
1333
+ /* @__PURE__ */ jsxs(
1334
+ Button,
1335
+ {
1336
+ ref: triggerRef,
1337
+ type: "button",
1338
+ variant: "outline",
1339
+ size: "sm",
1340
+ "aria-label": `${label} filter`,
1341
+ "aria-expanded": open,
1342
+ "aria-haspopup": "dialog",
1343
+ onClick: () => setOpen((current) => !current),
1344
+ className: "h-7 w-fit max-w-[11rem] min-w-0 gap-2 px-2 text-xs font-normal",
1345
+ children: [
1346
+ /* @__PURE__ */ jsx(Icon, { name: "codicon:calendar", className: "text-muted-foreground text-[14px]" }),
1347
+ /* @__PURE__ */ jsx("span", { className: "truncate font-normal tabular-nums", children: buttonLabel })
1348
+ ]
1349
+ }
1350
+ ),
1351
+ open && /* @__PURE__ */ jsx("div", { className: "absolute right-0 top-[calc(100%+0.375rem)] z-50 w-72 rounded-md border border-border bg-popover text-popover-foreground shadow-md shadow-black/5 outline-none", children: /* @__PURE__ */ jsxs("div", { className: "p-3", children: [
1352
+ /* @__PURE__ */ jsx("div", { className: "mb-2 text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: label }),
1353
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
1354
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
1355
+ /* @__PURE__ */ jsx("label", { className: "text-[10px] text-muted-foreground", children: "From" }),
1356
+ /* @__PURE__ */ jsx(
1357
+ RangeInput,
1358
+ {
1359
+ inputRef: fromInputRef,
1360
+ kind,
1361
+ ariaLabel: `${label} from`,
1362
+ placeholder: fromPlaceholder ?? (kind === "date" ? "" : "now-24h"),
1363
+ value: draftFrom,
1364
+ onChange: setDraftFrom
1365
+ }
1366
+ )
1367
+ ] }),
1368
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
1369
+ /* @__PURE__ */ jsx("label", { className: "text-[10px] text-muted-foreground", children: "To" }),
1370
+ /* @__PURE__ */ jsx(
1371
+ RangeInput,
1372
+ {
1373
+ inputRef: toInputRef,
1374
+ kind,
1375
+ ariaLabel: `${label} to`,
1376
+ placeholder: toPlaceholder ?? (kind === "date" ? "" : "now"),
1377
+ value: draftTo,
1378
+ onChange: setDraftTo
1379
+ }
1380
+ )
1381
+ ] })
1382
+ ] }),
1383
+ /* @__PURE__ */ jsxs("div", { className: "mt-3 flex justify-between gap-2", children: [
1384
+ /* @__PURE__ */ jsx(
1385
+ Button,
1386
+ {
1387
+ type: "button",
1388
+ variant: "ghost",
1389
+ size: "sm",
1390
+ className: "h-8 px-3 text-xs",
1391
+ disabled: !from && !to,
1392
+ onClick: clearRange,
1393
+ children: "Clear"
1394
+ }
1395
+ ),
1396
+ /* @__PURE__ */ jsx(
1397
+ Button,
1398
+ {
1399
+ type: "button",
1400
+ variant: "default",
1401
+ size: "sm",
1402
+ className: "h-8 px-3 text-xs",
1403
+ onClick: () => applyRange(draftFrom, draftTo),
1404
+ children: "Apply"
1405
+ }
1406
+ )
1407
+ ] })
1408
+ ] }) })
1409
+ ] });
1410
+ }
1411
+ function useDismissablePopup(open, rootRef, triggerRef, onClose) {
1412
+ useEffect(() => {
1413
+ if (!open) return;
1414
+ const onPointerDown = (event) => {
1415
+ var _a;
1416
+ if (!((_a = rootRef.current) == null ? void 0 : _a.contains(event.target))) {
1417
+ onClose();
1418
+ }
1419
+ };
1420
+ const onKeyDown = (event) => {
1421
+ var _a;
1422
+ if (event.key === "Escape") {
1423
+ onClose();
1424
+ (_a = triggerRef.current) == null ? void 0 : _a.focus();
1425
+ }
1426
+ };
1427
+ document.addEventListener("mousedown", onPointerDown);
1428
+ document.addEventListener("keydown", onKeyDown);
1429
+ return () => {
1430
+ document.removeEventListener("mousedown", onPointerDown);
1431
+ document.removeEventListener("keydown", onKeyDown);
1432
+ };
1433
+ }, [onClose, open, rootRef, triggerRef]);
1434
+ }
1435
+ function summarizeMultiFilter(label, value) {
1436
+ const includeCount = Object.values(value).filter((mode) => mode === "include").length;
1437
+ const excludeCount = Object.values(value).filter((mode) => mode === "exclude").length;
1438
+ if (includeCount === 0 && excludeCount === 0) {
1439
+ return label;
1440
+ }
1441
+ const counts = [
1442
+ includeCount > 0 ? `+${includeCount}` : null,
1443
+ excludeCount > 0 ? `-${excludeCount}` : null
1444
+ ].filter(Boolean);
1445
+ return `${label} ${counts.join(" ")}`;
1446
+ }
1447
+ function parseLookupMultiValue(value) {
1448
+ return value.split(",").map((item) => item.trim()).filter(Boolean);
1449
+ }
1450
+ function useDebouncedMultiDraft(value, onChange) {
1451
+ const { autoSubmit } = useContext(FilterBarContext);
1452
+ const [draft, setDraft] = useState(value);
1453
+ const latestOnChange = useRef(onChange);
1454
+ useEffect(() => {
1455
+ latestOnChange.current = onChange;
1456
+ }, [onChange]);
1457
+ useEffect(() => {
1458
+ setDraft(value);
1459
+ }, [value]);
1460
+ useEffect(() => {
1461
+ if (sameMultiFilterValue(draft, value)) return;
1462
+ if (!autoSubmit) {
1463
+ latestOnChange.current(draft);
1464
+ return;
1465
+ }
1466
+ const timeoutId = window.setTimeout(() => {
1467
+ latestOnChange.current(draft);
1468
+ }, FILTER_INPUT_DEBOUNCE_MS);
1469
+ return () => window.clearTimeout(timeoutId);
1470
+ }, [autoSubmit, draft, value]);
1471
+ return [draft, setDraft];
1472
+ }
1473
+ function sameMultiFilterValue(a, b) {
1474
+ const aKeys = Object.keys(a);
1475
+ const bKeys = Object.keys(b);
1476
+ if (aKeys.length !== bKeys.length) return false;
1477
+ for (const key of aKeys) {
1478
+ if (a[key] !== b[key]) return false;
1479
+ }
1480
+ return true;
1481
+ }
1482
+ function useDebouncedTextDraft(value, onChange) {
1483
+ const { autoSubmit } = useContext(FilterBarContext);
1484
+ const [draft, setDraft] = useState(value);
1485
+ const latestOnChange = useRef(onChange);
1486
+ useEffect(() => {
1487
+ latestOnChange.current = onChange;
1488
+ }, [onChange]);
1489
+ useEffect(() => {
1490
+ setDraft(value);
1491
+ }, [value]);
1492
+ useEffect(() => {
1493
+ if (draft === value) return;
1494
+ if (!autoSubmit) {
1495
+ latestOnChange.current(draft);
1496
+ return;
1497
+ }
1498
+ const timeoutId = window.setTimeout(() => {
1499
+ latestOnChange.current(draft);
1500
+ }, FILTER_INPUT_DEBOUNCE_MS);
1501
+ return () => window.clearTimeout(timeoutId);
1502
+ }, [autoSubmit, draft, value]);
1503
+ return [draft, setDraft];
1504
+ }
1505
+ function useDebouncedNumberDraft(value, onChange) {
1506
+ const { autoSubmit } = useContext(FilterBarContext);
1507
+ const [draft, setDraft] = useState(value);
1508
+ const latestOnChange = useRef(onChange);
1509
+ useEffect(() => {
1510
+ latestOnChange.current = onChange;
1511
+ }, [onChange]);
1512
+ useEffect(() => {
1513
+ setDraft(value);
1514
+ }, [value.max, value.min]);
1515
+ useEffect(() => {
1516
+ if (sameNumberFilterValue(draft, value)) return;
1517
+ if (!autoSubmit) {
1518
+ latestOnChange.current(draft);
1519
+ return;
1520
+ }
1521
+ const timeoutId = window.setTimeout(() => {
1522
+ latestOnChange.current(draft);
1523
+ }, FILTER_INPUT_DEBOUNCE_MS);
1524
+ return () => window.clearTimeout(timeoutId);
1525
+ }, [autoSubmit, draft, value]);
1526
+ return [draft, setDraft];
1527
+ }
1528
+ function sameNumberFilterValue(left, right) {
1529
+ return (left.min ?? "") === (right.min ?? "") && (left.max ?? "") === (right.max ?? "");
1530
+ }
1531
+ function resolveNumberFilterBounds(filter) {
1532
+ const parsedMin = parseFilterNumber(filter.value.min);
1533
+ const parsedMax = parseFilterNumber(filter.value.max);
1534
+ const fallbackMin = parsedMin ?? 0;
1535
+ const fallbackMax = parsedMax ?? Math.max(fallbackMin + 100, 100);
1536
+ const min = filter.domainMin ?? Math.min(fallbackMin, fallbackMax);
1537
+ const max = filter.domainMax ?? Math.max(fallbackMax, min + 100);
1538
+ const step = filter.step && filter.step > 0 ? filter.step : 1;
1539
+ return {
1540
+ min,
1541
+ max: max <= min ? min + step : max,
1542
+ step
1543
+ };
1544
+ }
1545
+ function summarizeNumberFilter(filter, bounds, value) {
1546
+ const parsedMin = parseFilterNumber(value.min);
1547
+ const parsedMax = parseFilterNumber(value.max);
1548
+ const hasMin = String(value.min ?? "").trim() !== "";
1549
+ const hasMax = String(value.max ?? "").trim() !== "";
1550
+ if (!hasMin && !hasMax) {
1551
+ return filter.label;
1552
+ }
1553
+ const minLabel = parsedMin == null ? formatNumberValue(bounds.min, filter) : formatNumberValue(parsedMin, filter);
1554
+ const maxLabel = parsedMax == null ? formatNumberValue(bounds.max, filter) : formatNumberValue(parsedMax, filter);
1555
+ if (hasMin && hasMax) {
1556
+ if (minLabel === maxLabel) {
1557
+ return `${filter.label} ${minLabel}`;
1558
+ }
1559
+ return `${filter.label} ${minLabel}-${maxLabel}`;
1560
+ }
1561
+ if (hasMin) {
1562
+ return `${filter.label} >=${minLabel}`;
1563
+ }
1564
+ return `${filter.label} <=${maxLabel}`;
1565
+ }
1566
+ function normalizeNumberFilterValue(nextValue, source) {
1567
+ let min = nextValue.min ?? "";
1568
+ let max = nextValue.max ?? "";
1569
+ const parsedMin = parseFilterNumber(min);
1570
+ const parsedMax = parseFilterNumber(max);
1571
+ if (parsedMin != null && parsedMax != null && parsedMin > parsedMax) {
1572
+ if (source.startsWith("min")) {
1573
+ max = min;
1574
+ } else {
1575
+ min = max;
1576
+ }
1577
+ }
1578
+ return { min, max };
1579
+ }
1580
+ function numberFilterValueFromSlider(value, bounds) {
1581
+ const [min, max] = value;
1582
+ return {
1583
+ min: min <= bounds.min ? "" : formatRawNumber(clampNumber(min, bounds.min, bounds.max)),
1584
+ max: max >= bounds.max ? "" : formatRawNumber(clampNumber(max, bounds.min, bounds.max))
1585
+ };
1586
+ }
1587
+ function parseFilterNumber(value) {
1588
+ if (!(value == null ? void 0 : value.trim())) return null;
1589
+ const parsed = Number(value);
1590
+ return Number.isFinite(parsed) ? parsed : null;
1591
+ }
1592
+ function clampNumber(value, min, max) {
1593
+ return Math.min(max, Math.max(min, value));
1594
+ }
1595
+ function formatNumberValue(value, filter) {
1596
+ if (filter.formatValue) {
1597
+ return filter.formatValue(value);
1598
+ }
1599
+ return formatRawNumber(value);
1600
+ }
1601
+ function formatRawNumber(value) {
1602
+ return Number.isInteger(value) ? String(value) : String(Number(value.toFixed(6)));
1603
+ }
1604
+ function updateMultiFilterValue(current, optionValue, nextMode) {
1605
+ const next = { ...current };
1606
+ if (nextMode === "neutral") {
1607
+ delete next[optionValue];
1608
+ return next;
1609
+ }
1610
+ if (nextMode === "include" || nextMode === "exclude") {
1611
+ next[optionValue] = nextMode;
1612
+ }
1613
+ return next;
1614
+ }
1615
+ function nextFilterMode(mode) {
1616
+ if (mode === "include") return "exclude";
1617
+ if (mode === "exclude") return "neutral";
1618
+ return "include";
1619
+ }
1620
+ function normalizeDateMath(value) {
1621
+ const trimmed = value.trim();
1622
+ if (!trimmed) return "";
1623
+ if (trimmed === "now" || trimmed.startsWith("now")) return trimmed;
1624
+ if (/^[+-]\d/.test(trimmed)) return `now${trimmed}`;
1625
+ return trimmed;
1626
+ }
1627
+ function formatRangeLabel(kind, from, to, emptyLabel) {
1628
+ const trimmedFrom = from.trim();
1629
+ const trimmedTo = to.trim();
1630
+ if (!trimmedFrom && !trimmedTo) {
1631
+ return emptyLabel ?? (kind === "date" ? "Any date" : "now-24h");
1632
+ }
1633
+ if (kind === "time") {
1634
+ const normalizedFrom = normalizeDateMath(trimmedFrom);
1635
+ const normalizedTo = normalizeDateMath(trimmedTo || "now");
1636
+ if (!normalizedFrom) return normalizedTo;
1637
+ if (normalizedTo === "now") return normalizedFrom;
1638
+ return `${normalizedFrom} → ${normalizedTo}`;
1639
+ }
1640
+ if (trimmedFrom && trimmedTo && trimmedFrom === trimmedTo) {
1641
+ return trimmedFrom;
1642
+ }
1643
+ if (!trimmedFrom) return trimmedTo;
1644
+ if (!trimmedTo) return trimmedFrom;
1645
+ return `${trimmedFrom} → ${trimmedTo}`;
1646
+ }
1647
+ function RangeInput({
1648
+ inputRef,
1649
+ kind,
1650
+ ariaLabel,
1651
+ value,
1652
+ onChange,
1653
+ placeholder
1654
+ }) {
1655
+ if (kind === "date") {
1656
+ return /* @__PURE__ */ jsx(
1657
+ DatePicker,
1658
+ {
1659
+ ref: inputRef,
1660
+ "aria-label": ariaLabel,
1661
+ value,
1662
+ onChange,
1663
+ placeholder,
1664
+ inputClassName: "pr-8"
1665
+ }
1666
+ );
1667
+ }
1668
+ return /* @__PURE__ */ jsx(
1669
+ DateTimePicker,
1670
+ {
1671
+ ref: inputRef,
1672
+ "aria-label": ariaLabel,
1673
+ inputClassName: "pr-8",
1674
+ placeholder,
1675
+ value,
1676
+ onChange
1677
+ }
1678
+ );
1679
+ }
1680
+ export {
1681
+ FilterBar,
1682
+ FilterBarFilterPanel,
1683
+ FilterBarRangePanel
1684
+ };
1685
+ //# sourceMappingURL=FilterBar.js.map