@squadbase/vantage 0.6.2-stg.3a2ba98 → 0.7.0-dev1.1197de7

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 (357) hide show
  1. package/dist/add-4GHF2TX2.js +6 -0
  2. package/dist/add-4GHF2TX2.js.map +1 -0
  3. package/dist/{build-O5TVNH4E.js → build-U7GO26HR.js} +9 -9
  4. package/dist/{build-O5TVNH4E.js.map → build-U7GO26HR.js.map} +1 -1
  5. package/dist/{check-WFBEDHDR.js → check-MRVAIHG5.js} +7 -7
  6. package/dist/{check-WFBEDHDR.js.map → check-MRVAIHG5.js.map} +1 -1
  7. package/dist/{chunk-QDZA5JSL.js → chunk-AABMKHEU.js} +4 -4
  8. package/dist/{chunk-QDZA5JSL.js.map → chunk-AABMKHEU.js.map} +1 -1
  9. package/dist/{chunk-PVTHHV7X.js → chunk-ANZLVSEP.js} +28 -7
  10. package/dist/chunk-ANZLVSEP.js.map +1 -0
  11. package/dist/chunk-CKWPK4RO.js +50 -0
  12. package/dist/chunk-CKWPK4RO.js.map +1 -0
  13. package/dist/chunk-EG4PX726.js +678 -0
  14. package/dist/chunk-EG4PX726.js.map +1 -0
  15. package/dist/{chunk-3G43C6OL.js → chunk-GNXCX4WE.js} +46 -3
  16. package/dist/chunk-GNXCX4WE.js.map +1 -0
  17. package/dist/chunk-LYUGDHSF.js +61 -0
  18. package/dist/chunk-LYUGDHSF.js.map +1 -0
  19. package/dist/{chunk-RPZAALCX.js → chunk-MQUIAP66.js} +321 -165
  20. package/dist/chunk-MQUIAP66.js.map +1 -0
  21. package/dist/chunk-P2KIHF65.js +152 -0
  22. package/dist/chunk-P2KIHF65.js.map +1 -0
  23. package/dist/{chunk-BCE22JSM.js → chunk-PJ4SPXHS.js} +52 -16
  24. package/dist/chunk-PJ4SPXHS.js.map +1 -0
  25. package/dist/{chunk-ZVANIULZ.js → chunk-RNTDBX6S.js} +4 -4
  26. package/dist/{chunk-ZVANIULZ.js.map → chunk-RNTDBX6S.js.map} +1 -1
  27. package/dist/{chunk-PXRX5PEC.js → chunk-ROHNXP7J.js} +73 -4
  28. package/dist/chunk-ROHNXP7J.js.map +1 -0
  29. package/dist/chunk-S5MDTHH7.js +466 -0
  30. package/dist/chunk-S5MDTHH7.js.map +1 -0
  31. package/dist/chunk-SDJTTMLK.js +1877 -0
  32. package/dist/chunk-SDJTTMLK.js.map +1 -0
  33. package/dist/{chunk-EP6JHXSR.js → chunk-SHZM3EXB.js} +68 -12
  34. package/dist/chunk-SHZM3EXB.js.map +1 -0
  35. package/dist/{chunk-A2UUGASH.js → chunk-U736PVHY.js} +2 -2
  36. package/dist/chunk-U736PVHY.js.map +1 -0
  37. package/dist/{chunk-JGRMRXXZ.js → chunk-UWSQUHX6.js} +4 -99
  38. package/dist/chunk-UWSQUHX6.js.map +1 -0
  39. package/dist/chunk-XRKSIC5Z.js +176 -0
  40. package/dist/chunk-XRKSIC5Z.js.map +1 -0
  41. package/dist/chunk-YJQCB3UP.js +10764 -0
  42. package/dist/chunk-YJQCB3UP.js.map +1 -0
  43. package/dist/cli.js +15 -11
  44. package/dist/cli.js.map +1 -1
  45. package/dist/client/index.d.ts +34 -3
  46. package/dist/client/index.js +211 -12
  47. package/dist/client/index.js.map +1 -1
  48. package/dist/components/index.d.ts +2389 -31
  49. package/dist/components/index.js +7 -4
  50. package/dist/delta-text-tav2Lh5d.d.ts +17 -0
  51. package/dist/{dev-NZRLOQCL.js → dev-O42JFQ64.js} +24 -13
  52. package/dist/dev-O42JFQ64.js.map +1 -0
  53. package/dist/doc/index.d.ts +215 -2
  54. package/dist/doc/index.js +423 -5
  55. package/dist/doc/index.js.map +1 -1
  56. package/dist/{docs-ZGDJUHJI.js → docs-JVP4RPKT.js} +3 -3
  57. package/dist/{docs-ZGDJUHJI.js.map → docs-JVP4RPKT.js.map} +1 -1
  58. package/dist/{doctor-VFKEMR54.js → doctor-YD3XAAOT.js} +8 -8
  59. package/dist/doctor-YD3XAAOT.js.map +1 -0
  60. package/dist/format-D34piCei.d.ts +250 -0
  61. package/dist/geo-japan-7BMSOJRX.js +8 -0
  62. package/dist/geo-japan-7BMSOJRX.js.map +1 -0
  63. package/dist/geo-us-GVOG7VXH.js +8 -0
  64. package/dist/geo-us-GVOG7VXH.js.map +1 -0
  65. package/dist/geo-world-35HL56UU.js +8 -0
  66. package/dist/geo-world-35HL56UU.js.map +1 -0
  67. package/dist/markdown/index.js +1 -1
  68. package/dist/markdown/index.js.map +1 -1
  69. package/dist/{preview-QDCIVVSR.js → preview-T6CLSQVX.js} +12 -4
  70. package/dist/preview-T6CLSQVX.js.map +1 -0
  71. package/dist/query/index.d.ts +0 -11
  72. package/dist/query/index.js +2 -2
  73. package/dist/{routes-RHP5FVGD.js → routes-PVKNT777.js} +6 -6
  74. package/dist/{routes-RHP5FVGD.js.map → routes-PVKNT777.js.map} +1 -1
  75. package/dist/{search-RFBELGY7.js → search-5LC4XPB5.js} +3 -3
  76. package/dist/{search-RFBELGY7.js.map → search-5LC4XPB5.js.map} +1 -1
  77. package/dist/slide/index.d.ts +272 -1
  78. package/dist/slide/index.js +1086 -19
  79. package/dist/slide/index.js.map +1 -1
  80. package/dist/table/index.js +13 -10
  81. package/dist/table/index.js.map +1 -1
  82. package/dist/ui/index.d.ts +148 -12
  83. package/dist/ui/index.js +1339 -88
  84. package/dist/ui/index.js.map +1 -1
  85. package/dist/{upgrade-GF2P67M6.js → upgrade-6DT7ADRP.js} +7 -7
  86. package/dist/{upgrade-GF2P67M6.js.map → upgrade-6DT7ADRP.js.map} +1 -1
  87. package/dist/vite/index.d.ts +10 -0
  88. package/dist/vite/index.js +4 -4
  89. package/docs/en/agent-skills.md +1 -1
  90. package/docs/en/changelog.md +82 -0
  91. package/docs/en/cli-reference.md +30 -0
  92. package/docs/en/components.md +42 -17
  93. package/docs/en/doc/delta-text.md +40 -0
  94. package/docs/en/doc/doc-action-items.md +49 -0
  95. package/docs/en/doc/doc-callout.md +38 -0
  96. package/docs/en/doc/doc-data-table.md +60 -0
  97. package/docs/en/doc/doc-executive-summary.md +64 -0
  98. package/docs/en/doc/doc-figure.md +56 -0
  99. package/docs/en/doc/doc-key-findings.md +61 -0
  100. package/docs/en/doc/doc-kpi-scorecard.md +80 -0
  101. package/docs/en/doc/doc-notes-and-sources.md +43 -0
  102. package/docs/en/doc/doc-report-header.md +46 -0
  103. package/docs/en/pages-and-metadata.md +15 -0
  104. package/docs/en/parts/area-chart.md +99 -0
  105. package/docs/en/parts/bar-chart.md +91 -0
  106. package/docs/en/parts/bar-list.md +50 -0
  107. package/docs/en/parts/category-bar.md +37 -0
  108. package/docs/en/parts/chart-frame.md +39 -0
  109. package/docs/en/parts/data-table.md +22 -0
  110. package/docs/en/parts/delta.md +51 -0
  111. package/docs/en/parts/donut-chart.md +72 -0
  112. package/docs/en/parts/draft-chart.md +42 -0
  113. package/docs/en/parts/format.md +43 -0
  114. package/docs/en/parts/funnel-steps.md +2 -1
  115. package/docs/en/parts/heatmap.md +53 -0
  116. package/docs/en/parts/kpi-card.md +58 -0
  117. package/docs/en/parts/legend.md +35 -0
  118. package/docs/en/parts/line-chart.md +94 -0
  119. package/docs/en/parts/metric-value.md +3 -2
  120. package/docs/en/parts/page-shell.md +2 -2
  121. package/docs/en/parts/progress-circle.md +33 -0
  122. package/docs/en/parts/sample-data.md +39 -0
  123. package/docs/en/parts/scatter-chart.md +56 -0
  124. package/docs/en/parts/sparkline.md +1 -1
  125. package/docs/en/parts/tracker.md +36 -0
  126. package/docs/en/parts/trend-indicator.md +17 -1
  127. package/docs/en/parts/updated-at.md +30 -0
  128. package/docs/en/parts/waterfall-chart.md +56 -0
  129. package/docs/en/slide/slide-chart-full.md +48 -0
  130. package/docs/en/slide/slide-chart-insight.md +52 -0
  131. package/docs/en/slide/slide-columns.md +46 -0
  132. package/docs/en/slide/slide-comparison.md +68 -0
  133. package/docs/en/slide/slide-cover.md +39 -0
  134. package/docs/en/slide/slide-exec-summary.md +47 -0
  135. package/docs/en/slide/slide-heading.md +38 -0
  136. package/docs/en/slide/slide-kpi-grid.md +55 -0
  137. package/docs/en/slide/slide-kpi-hero.md +58 -0
  138. package/docs/en/slide/slide-next-actions.md +49 -0
  139. package/docs/en/slide/slide-scorecard-table.md +54 -0
  140. package/docs/en/slide/slide-section.md +44 -0
  141. package/docs/en/slide/slide-source-line.md +34 -0
  142. package/docs/en/ui/alert-dialog.md +42 -0
  143. package/docs/en/ui/avatar.md +31 -0
  144. package/docs/en/ui/combobox.md +74 -0
  145. package/docs/en/ui/drawer.md +41 -0
  146. package/docs/en/ui/empty.md +1 -1
  147. package/docs/en/ui/error-state.md +4 -3
  148. package/docs/en/ui/field.md +42 -0
  149. package/docs/en/ui/hover-card.md +21 -0
  150. package/docs/en/ui/kbd.md +17 -0
  151. package/docs/en/ui/loading.md +1 -1
  152. package/docs/en/ui/pagination.md +36 -0
  153. package/docs/en/ui/radio-group.md +21 -0
  154. package/docs/en/ui/toast.md +44 -0
  155. package/docs/en/ui/toggle-group.md +4 -3
  156. package/docs/en/ui-and-theming.md +1 -1
  157. package/docs/index.json +1222 -132
  158. package/docs/ja/agent-skills.md +1 -1
  159. package/docs/ja/changelog.md +84 -0
  160. package/docs/ja/cli-reference.md +29 -0
  161. package/docs/ja/components.md +68 -101
  162. package/docs/ja/doc/delta-text.md +40 -0
  163. package/docs/ja/doc/doc-action-items.md +49 -0
  164. package/docs/ja/doc/doc-callout.md +38 -0
  165. package/docs/ja/doc/doc-data-table.md +60 -0
  166. package/docs/ja/doc/doc-executive-summary.md +64 -0
  167. package/docs/ja/doc/doc-figure.md +56 -0
  168. package/docs/ja/doc/doc-key-findings.md +61 -0
  169. package/docs/ja/doc/doc-kpi-scorecard.md +80 -0
  170. package/docs/ja/doc/doc-notes-and-sources.md +43 -0
  171. package/docs/ja/doc/doc-report-header.md +46 -0
  172. package/docs/ja/page-types/csv.md +83 -0
  173. package/docs/ja/page-types/doc.md +83 -0
  174. package/docs/ja/page-types/excel.md +82 -0
  175. package/docs/ja/page-types/slide.md +133 -0
  176. package/docs/ja/page-types.md +47 -0
  177. package/docs/ja/pages-and-metadata.md +15 -0
  178. package/docs/ja/parts/area-chart.md +85 -0
  179. package/docs/ja/parts/bar-chart.md +95 -0
  180. package/docs/ja/parts/bar-list.md +58 -0
  181. package/docs/ja/parts/category-bar.md +36 -0
  182. package/docs/ja/parts/chart-frame.md +36 -0
  183. package/docs/ja/parts/data-table.md +22 -0
  184. package/docs/ja/parts/delta.md +51 -0
  185. package/docs/ja/parts/donut-chart.md +66 -0
  186. package/docs/ja/parts/draft-chart.md +40 -0
  187. package/docs/ja/parts/echart.md +3 -0
  188. package/docs/ja/parts/format.md +44 -0
  189. package/docs/ja/parts/funnel-steps.md +27 -8
  190. package/docs/ja/parts/heatmap.md +45 -0
  191. package/docs/ja/parts/kpi-card.md +56 -0
  192. package/docs/ja/parts/legend.md +36 -0
  193. package/docs/ja/parts/line-chart.md +84 -0
  194. package/docs/ja/parts/metric-value.md +3 -2
  195. package/docs/ja/parts/page-shell.md +2 -2
  196. package/docs/ja/parts/progress-circle.md +34 -0
  197. package/docs/ja/parts/sample-data.md +61 -0
  198. package/docs/ja/parts/scatter-chart.md +69 -0
  199. package/docs/ja/parts/sparkline.md +2 -2
  200. package/docs/ja/parts/tracker.md +32 -0
  201. package/docs/ja/parts/trend-indicator.md +17 -1
  202. package/docs/ja/parts/updated-at.md +27 -0
  203. package/docs/ja/parts/waterfall-chart.md +61 -0
  204. package/docs/ja/routing.md +8 -86
  205. package/docs/ja/slide/slide-chart-full.md +48 -0
  206. package/docs/ja/slide/slide-chart-insight.md +52 -0
  207. package/docs/ja/slide/slide-columns.md +46 -0
  208. package/docs/ja/slide/slide-comparison.md +68 -0
  209. package/docs/ja/slide/slide-cover.md +39 -0
  210. package/docs/ja/slide/slide-exec-summary.md +47 -0
  211. package/docs/ja/slide/slide-heading.md +38 -0
  212. package/docs/ja/slide/slide-kpi-grid.md +55 -0
  213. package/docs/ja/slide/slide-kpi-hero.md +58 -0
  214. package/docs/ja/slide/slide-next-actions.md +49 -0
  215. package/docs/ja/slide/slide-scorecard-table.md +54 -0
  216. package/docs/ja/slide/slide-section.md +44 -0
  217. package/docs/ja/slide/slide-source-line.md +34 -0
  218. package/docs/ja/ui/alert-dialog.md +41 -0
  219. package/docs/ja/ui/avatar.md +31 -0
  220. package/docs/ja/ui/combobox.md +74 -0
  221. package/docs/ja/ui/drawer.md +41 -0
  222. package/docs/ja/ui/empty.md +1 -1
  223. package/docs/ja/ui/error-state.md +4 -3
  224. package/docs/ja/ui/field.md +42 -0
  225. package/docs/ja/ui/hover-card.md +21 -0
  226. package/docs/ja/ui/kbd.md +16 -0
  227. package/docs/ja/ui/loading.md +1 -1
  228. package/docs/ja/ui/pagination.md +36 -0
  229. package/docs/ja/ui/radio-group.md +21 -0
  230. package/docs/ja/ui/toast.md +46 -0
  231. package/docs/ja/ui/toggle-group.md +4 -3
  232. package/docs/ja/ui-and-theming.md +14 -7
  233. package/package.json +43 -29
  234. package/registry/parts.json +5221 -0
  235. package/registry/ui/accordion.tsx +76 -0
  236. package/registry/ui/alert-dialog.tsx +189 -0
  237. package/registry/ui/alert.tsx +80 -0
  238. package/registry/ui/app-shell.tsx +424 -0
  239. package/registry/ui/area-chart.tsx +3960 -0
  240. package/registry/ui/attention-list.tsx +465 -0
  241. package/registry/ui/avatar.tsx +110 -0
  242. package/registry/ui/badge.tsx +56 -0
  243. package/registry/ui/bar-chart.tsx +3954 -0
  244. package/registry/ui/bar-list.tsx +3998 -0
  245. package/registry/ui/box-plot-chart.tsx +4214 -0
  246. package/registry/ui/breadcrumb.tsx +128 -0
  247. package/registry/ui/button.tsx +62 -0
  248. package/registry/ui/calendar.tsx +223 -0
  249. package/registry/ui/card.tsx +107 -0
  250. package/registry/ui/category-bar.tsx +1498 -0
  251. package/registry/ui/chart-frame.tsx +1612 -0
  252. package/registry/ui/checkbox.tsx +30 -0
  253. package/registry/ui/collapsible.tsx +23 -0
  254. package/registry/ui/combo-chart.tsx +1852 -0
  255. package/registry/ui/combobox.tsx +310 -0
  256. package/registry/ui/command.tsx +198 -0
  257. package/registry/ui/dashboard-card.tsx +219 -0
  258. package/registry/ui/data-table.tsx +899 -158
  259. package/registry/ui/date-range-picker.tsx +544 -0
  260. package/registry/ui/delta.tsx +352 -0
  261. package/registry/ui/dialog.tsx +161 -0
  262. package/registry/ui/donut-chart.tsx +4006 -0
  263. package/registry/ui/drawer.tsx +229 -0
  264. package/registry/ui/dropdown-menu.tsx +274 -0
  265. package/registry/ui/echart.tsx +1178 -0
  266. package/registry/ui/field.tsx +240 -0
  267. package/registry/ui/filter-bar.tsx +553 -0
  268. package/registry/ui/forecast-chart.tsx +4734 -0
  269. package/registry/ui/funnel-steps.tsx +3843 -0
  270. package/registry/ui/heatmap.tsx +2027 -0
  271. package/registry/ui/histogram-chart.tsx +4284 -0
  272. package/registry/ui/hover-card.tsx +52 -0
  273. package/registry/ui/input-group.tsx +160 -0
  274. package/registry/ui/input.tsx +24 -0
  275. package/registry/ui/kbd.tsx +30 -0
  276. package/registry/ui/kpi-card.tsx +907 -0
  277. package/registry/ui/label.tsx +22 -0
  278. package/registry/ui/legend.tsx +3817 -0
  279. package/registry/ui/line-chart.tsx +3958 -0
  280. package/registry/ui/metric-value.tsx +62 -0
  281. package/registry/ui/multi-select.tsx +382 -0
  282. package/registry/ui/page-shell.tsx +441 -0
  283. package/registry/ui/pagination.tsx +140 -0
  284. package/registry/ui/paired-chart.tsx +4086 -0
  285. package/registry/ui/pivot-table.tsx +1260 -0
  286. package/registry/ui/placeholder.tsx +36 -0
  287. package/registry/ui/popover.tsx +92 -0
  288. package/registry/ui/progress-circle.tsx +1033 -0
  289. package/registry/ui/progress.tsx +85 -0
  290. package/registry/ui/radio-group.tsx +39 -0
  291. package/registry/ui/sample-data.tsx +230 -0
  292. package/registry/ui/scatter-chart.tsx +4503 -0
  293. package/registry/ui/scroll-area.tsx +56 -0
  294. package/registry/ui/searchable-select.tsx +352 -0
  295. package/registry/ui/section-header.tsx +43 -0
  296. package/registry/ui/segmented-control.tsx +76 -0
  297. package/registry/ui/select.tsx +255 -0
  298. package/registry/ui/separator.tsx +27 -0
  299. package/registry/ui/sheet.tsx +139 -0
  300. package/registry/ui/sidebar.tsx +725 -0
  301. package/registry/ui/skeleton.tsx +17 -0
  302. package/registry/ui/slider.tsx +56 -0
  303. package/registry/ui/sparkline.tsx +230 -0
  304. package/registry/ui/spinner.tsx +14 -0
  305. package/registry/ui/states.tsx +247 -0
  306. package/registry/ui/status-badge.tsx +286 -0
  307. package/registry/ui/switch.tsx +34 -0
  308. package/registry/ui/table.tsx +118 -0
  309. package/registry/ui/tabs.tsx +84 -0
  310. package/registry/ui/textarea.tsx +22 -0
  311. package/registry/ui/toast.tsx +250 -0
  312. package/registry/ui/toggle-group.tsx +91 -0
  313. package/registry/ui/toggle.tsx +47 -0
  314. package/registry/ui/tooltip.tsx +72 -0
  315. package/registry/ui/tracker.tsx +1015 -0
  316. package/registry/ui/treemap.tsx +1917 -0
  317. package/registry/ui/trend-indicator.tsx +217 -0
  318. package/registry/ui/updated-at.tsx +276 -0
  319. package/registry/ui/waterfall-chart.tsx +4561 -0
  320. package/skills/vantage-add-feature/SKILL.md +14 -7
  321. package/skills/vantage-design/SKILL.md +62 -52
  322. package/skills/vantage-design/references/extract.md +26 -27
  323. package/skills/vantage-design/references/format.md +12 -172
  324. package/skills/vantage-design/references/presets.md +50 -110
  325. package/skills/vantage-pitfalls/SKILL.md +10 -1
  326. package/skills/vantage-slide/SKILL.md +71 -36
  327. package/templates/AGENTS.md +54 -16
  328. package/templates/agents/api.md +36 -0
  329. package/templates/agents/csv.md +2 -1
  330. package/templates/agents/doc.md +52 -3
  331. package/templates/agents/page.md +91 -10
  332. package/templates/agents/slide.md +90 -4
  333. package/templates/agents/xlsx.md +2 -1
  334. package/theme.css +247 -132
  335. package/dist/add-EISGSZAH.js +0 -6
  336. package/dist/add-EISGSZAH.js.map +0 -1
  337. package/dist/chunk-3G43C6OL.js.map +0 -1
  338. package/dist/chunk-5ZOP4SQR.js +0 -47
  339. package/dist/chunk-5ZOP4SQR.js.map +0 -1
  340. package/dist/chunk-A2UUGASH.js.map +0 -1
  341. package/dist/chunk-BCE22JSM.js.map +0 -1
  342. package/dist/chunk-DTDVSFRY.js +0 -29
  343. package/dist/chunk-DTDVSFRY.js.map +0 -1
  344. package/dist/chunk-EP6JHXSR.js.map +0 -1
  345. package/dist/chunk-JGRMRXXZ.js.map +0 -1
  346. package/dist/chunk-KLEIVDUV.js +0 -165
  347. package/dist/chunk-KLEIVDUV.js.map +0 -1
  348. package/dist/chunk-LSRMRIXA.js +0 -2227
  349. package/dist/chunk-LSRMRIXA.js.map +0 -1
  350. package/dist/chunk-PVTHHV7X.js.map +0 -1
  351. package/dist/chunk-PXRX5PEC.js.map +0 -1
  352. package/dist/chunk-RPZAALCX.js.map +0 -1
  353. package/dist/chunk-T3FLAGI6.js +0 -316
  354. package/dist/chunk-T3FLAGI6.js.map +0 -1
  355. package/dist/dev-NZRLOQCL.js.map +0 -1
  356. package/dist/doctor-VFKEMR54.js.map +0 -1
  357. package/dist/preview-QDCIVVSR.js.map +0 -1
@@ -0,0 +1,553 @@
1
+ // Copied by `vantage add ui filter-bar`. This file is yours to edit.
2
+ // Generated from @squadbase/squadkit registry/components/filter-bar.tsx by `pnpm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
+ "use client"
4
+
5
+ /**
6
+ * FilterBarPreset — a whole filter row built from a `FilterConfig[]`, placed at the top of a page or section.
7
+ *
8
+ * `FilterBar` is the compound form: `<FilterBar>` holds the (always
9
+ * controlled) filter values on context; the select, multi-select, active-chip
10
+ * and clear parts read and update them. `FilterBarPreset` builds the whole row
11
+ * from a `FilterConfig[]`.
12
+ */
13
+ import {
14
+ createContext,
15
+ forwardRef,
16
+ useContext,
17
+ type HTMLAttributes,
18
+ type ReactNode,
19
+ } from "react"
20
+ import { ChevronDown, X } from "lucide-react"
21
+
22
+ import { cn } from "@squadbase/vantage/ui"
23
+ import { Badge } from "@squadbase/vantage/ui"
24
+ import { Button } from "@squadbase/vantage/ui"
25
+ import { Checkbox } from "@squadbase/vantage/ui"
26
+ import { Popover, PopoverContent, PopoverTrigger } from "@squadbase/vantage/ui"
27
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@squadbase/vantage/ui"
28
+ import type { FilterConfig, FilterOption, FilterValues } from "@squadbase/vantage/components"
29
+ import { useSyncExternalStore } from "react"
30
+
31
+ /* ---------- inlined from @squadbase/squadkit registry/lib/locale.ts (internal to @squadbase/vantage) ---------- */
32
+
33
+ /**
34
+ * The display language of the parts and the defaults that follow it.
35
+ *
36
+ * The language comes from `<html lang>`: a tag starting with `ja` reads Japanese, anything else English.
37
+ * Without a usable `lang` (missing, invalid, or no `document` — SSR, Node) the locale is `FALLBACK_LOCALE`
38
+ * (`en-US`). A `locale` passed explicitly (a prop, an argument) always wins.
39
+ *
40
+ * Parts read the locale through `useLocale` (`hooks/use-locale`) and pass it down; the pure helpers here
41
+ * and in `format` / `comparison` / `chart-*` take it as an argument and fall back to `documentLocale()`.
42
+ */
43
+
44
+ /** The language of the built-in wording: Japanese or English. */
45
+ type UiLanguage = "ja" | "en"
46
+
47
+ /** The locale used when `<html lang>` gives none (and on the server). */
48
+ const FALLBACK_LOCALE = "en-US"
49
+
50
+ /** `"ja"` when `locale` starts with `ja` (`ja`, `ja-JP`), else `"en"`. A missing locale is `"en"`. */
51
+ function uiLanguage(locale: string | null | undefined): UiLanguage {
52
+ return typeof locale === "string" && locale.trim().toLowerCase().startsWith("ja") ? "ja" : "en"
53
+ }
54
+
55
+ /** A BCP 47 tag in its canonical form (`en-gb` → `en-GB`), or `FALLBACK_LOCALE` when it is empty or invalid. */
56
+ function normalizeLocale(tag: string | null | undefined): string {
57
+ const value = tag?.trim()
58
+ if (!value) return FALLBACK_LOCALE
59
+ try {
60
+ return Intl.getCanonicalLocales(value)[0] ?? FALLBACK_LOCALE
61
+ } catch {
62
+ return FALLBACK_LOCALE
63
+ }
64
+ }
65
+
66
+ /**
67
+ * The locale of the page: `document.documentElement.lang`, canonicalized. `FALLBACK_LOCALE` when it is empty or
68
+ * invalid, or there is no `document`. Parts read it through `useLocale`, never while rendering.
69
+ */
70
+ function documentLocale(): string {
71
+ if (typeof document === "undefined") return FALLBACK_LOCALE
72
+ return normalizeLocale(document.documentElement?.getAttribute("lang"))
73
+ }
74
+
75
+ /** `locale` when given, else the page's locale (`documentLocale()`). */
76
+ function resolveLocale(locale?: string | null): string {
77
+ return locale || documentLocale()
78
+ }
79
+
80
+ /** Picks the wording table of the locale's language. */
81
+ function pickLocale<T>(table: { ja: T; en: T }, locale?: string | null): T {
82
+ return table[uiLanguage(resolveLocale(locale))]
83
+ }
84
+
85
+ /** The currency `format="currency"` uses without a `currency`: JPY for Japanese, USD otherwise. */
86
+ function defaultCurrency(locale?: string | null): string {
87
+ return uiLanguage(resolveLocale(locale)) === "ja" ? "JPY" : "USD"
88
+ }
89
+
90
+ /** The month a fiscal year starts in (1–12) when none is given: April for Japanese, January otherwise. */
91
+ function defaultFiscalYearStartMonth(locale?: string | null): number {
92
+ return uiLanguage(resolveLocale(locale)) === "ja" ? 4 : 1
93
+ }
94
+
95
+ /** A day of the week, 0 = Sunday … 6 = Saturday (the `Date#getDay` / date-fns numbering). */
96
+ type WeekDay = 0 | 1 | 2 | 3 | 4 | 5 | 6
97
+
98
+ /** Locales whose week starts on Sunday when the runtime has no week data. */
99
+ const SUNDAY_FIRST = new Set(["en", "en-US", "en-CA"])
100
+
101
+ /**
102
+ * The first day of the week: Monday for Japanese; otherwise the locale's convention from `Intl.Locale`
103
+ * (`en-US` Sunday, `en-GB` Monday). Without week data in the runtime: Sunday for `en` / `en-US` / `en-CA`,
104
+ * Monday for the rest.
105
+ */
106
+ function weekStartsOn(locale?: string | null): WeekDay {
107
+ const tag = normalizeLocale(resolveLocale(locale))
108
+ if (uiLanguage(tag) === "ja") return 1
109
+ try {
110
+ const info = new Intl.Locale(tag) as Intl.Locale & {
111
+ getWeekInfo?: () => { firstDay: number }
112
+ weekInfo?: { firstDay: number }
113
+ }
114
+ const firstDay = info.getWeekInfo?.().firstDay ?? info.weekInfo?.firstDay
115
+ if (typeof firstDay === "number" && firstDay >= 1 && firstDay <= 7) return (firstDay % 7) as WeekDay
116
+ } catch {
117
+ // Fall through to the table below.
118
+ }
119
+ return SUNDAY_FIRST.has(tag) ? 0 : 1
120
+ }
121
+
122
+ /* ---------- inlined from @squadbase/squadkit registry/hooks/use-locale.ts (internal to @squadbase/vantage) ---------- */
123
+
124
+ const listeners = new Set<() => void>()
125
+ let observer: MutationObserver | null = null
126
+
127
+ /** One observer on `<html lang>` for every subscribed part. */
128
+ function subscribe(onChange: () => void): () => void {
129
+ if (typeof document === "undefined" || typeof MutationObserver === "undefined") return () => {}
130
+ listeners.add(onChange)
131
+ if (!observer) {
132
+ observer = new MutationObserver(() => {
133
+ for (const listener of [...listeners]) listener()
134
+ })
135
+ observer.observe(document.documentElement, { attributes: true, attributeFilter: ["lang"] })
136
+ }
137
+ return () => {
138
+ listeners.delete(onChange)
139
+ if (listeners.size === 0 && observer) {
140
+ observer.disconnect()
141
+ observer = null
142
+ }
143
+ }
144
+ }
145
+
146
+ const serverLocale = () => FALLBACK_LOCALE
147
+
148
+ /**
149
+ * The page's locale (`<html lang>`), following changes made while the page runs. On the server and during
150
+ * hydration it is `FALLBACK_LOCALE` (`en-US`), so the markup matches; it switches right after.
151
+ */
152
+ function useDocumentLocale(): string {
153
+ return useSyncExternalStore(subscribe, documentLocale, serverLocale)
154
+ }
155
+
156
+ /** The locale a part renders in: its `locale` prop when given, else the page's (`useDocumentLocale`). */
157
+ function useLocale(locale?: string | null): string {
158
+ const page = useDocumentLocale()
159
+ return locale || page
160
+ }
161
+
162
+ /* ---------- end of inlined code ---------- */
163
+
164
+ /* --------------------------------------------------------------- context */
165
+
166
+ interface FilterBarContextValue {
167
+ /** Current filter values (controlled from outside) */
168
+ value: FilterValues
169
+ /** Notify the parent of a value change */
170
+ onChange: (next: FilterValues) => void
171
+ /** Optional clear callback — undefined means no clear button */
172
+ onClear?: () => void
173
+ }
174
+
175
+ const FilterBarContext = createContext<FilterBarContextValue | null>(null)
176
+
177
+ const TEXT = {
178
+ ja: {
179
+ filters: "フィルター",
180
+ selected: (count: number) => ` (${count}件選択中)`,
181
+ remove: (filter: string, option: string) => `${filter}: ${option} を削除`,
182
+ clear: "クリア",
183
+ clearAll: "すべてのフィルターをクリア",
184
+ },
185
+ en: {
186
+ filters: "Filters",
187
+ selected: (count: number) => ` (${count} selected)`,
188
+ remove: (filter: string, option: string) => `Remove ${filter}: ${option}`,
189
+ clear: "Clear",
190
+ clearAll: "Clear all filters",
191
+ },
192
+ }
193
+
194
+ const useText = () => TEXT[uiLanguage(useLocale())]
195
+
196
+ function useFilterBar(): FilterBarContextValue {
197
+ const ctx = useContext(FilterBarContext)
198
+ if (!ctx) throw new Error("useFilterBar must be used within <FilterBar>")
199
+ return ctx
200
+ }
201
+
202
+ /* ------------------------------------------------------------------ root */
203
+
204
+ export interface FilterBarProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange"> {
205
+ value: FilterValues
206
+ onChange: (values: FilterValues) => void
207
+ onClear?: () => void
208
+ className?: string
209
+ children?: ReactNode
210
+ }
211
+
212
+ export const FilterBar = forwardRef<HTMLDivElement, FilterBarProps>(
213
+ ({ value, onChange, onClear, className, children, ...props }, ref) => {
214
+ const t = useText()
215
+ return (
216
+ <FilterBarContext.Provider value={{ value, onChange, onClear }}>
217
+ <div
218
+ ref={ref}
219
+ role="search"
220
+ aria-label={t.filters}
221
+ data-slot="filter-bar"
222
+ className={cn("flex flex-wrap items-center gap-2", className)}
223
+ {...props}
224
+ >
225
+ {children}
226
+ </div>
227
+ </FilterBarContext.Provider>
228
+ )
229
+ },
230
+ )
231
+ FilterBar.displayName = "FilterBar"
232
+
233
+ /* ---------------------------------------------------------------- select */
234
+
235
+ export interface FilterBarSelectProps {
236
+ filterKey: string
237
+ label: string
238
+ options: FilterOption[]
239
+ placeholder?: string
240
+ /**
241
+ * 渡すと、選択肢の先頭に「絞り込まない」項目をこの文言で足す(例: `"すべて"`)。選ぶとこのキーを値から外す(`undefined`)ので、
242
+ * アクティブチップにも出ない。絞り込んでいないときのトリガーは `placeholder`(既定は `label`)のまま。既定は足さない。
243
+ * @example allLabel="すべて"
244
+ */
245
+ allLabel?: string
246
+ className?: string
247
+ }
248
+
249
+ /** 「すべて」項目の値。実際の選択肢の値と衝突しない内部用の印で、`onChange` には渡らない。 */
250
+ const ALL_VALUE = "__squadkit_all__"
251
+
252
+ export function FilterBarSelect({
253
+ filterKey,
254
+ label,
255
+ options,
256
+ placeholder,
257
+ allLabel,
258
+ className,
259
+ }: FilterBarSelectProps) {
260
+ const { value, onChange } = useFilterBar()
261
+ const hasAll = allLabel !== undefined
262
+ const items = hasAll ? [{ value: ALL_VALUE, label: allLabel }, ...options] : options
263
+ const current = value[filterKey] as string | undefined
264
+
265
+ // Base UI reports `null` when the selection is cleared — drop the key so
266
+ // "no filter" reads as absent rather than as an empty string. "すべて" does the same.
267
+ const handleChange = (selected: string | null) =>
268
+ onChange({ ...value, [filterKey]: selected === null || selected === ALL_VALUE ? undefined : selected })
269
+
270
+ return (
271
+ <Select
272
+ // FilterOption is already `{ value, label }` — Base UI uses it to show the
273
+ // label (not the raw value) in the trigger.
274
+ items={items}
275
+ value={current ?? ""}
276
+ onValueChange={handleChange}
277
+ >
278
+ <SelectTrigger size="sm" aria-label={label} data-slot="filter-bar-select" className={className}>
279
+ <SelectValue placeholder={placeholder ?? label} />
280
+ </SelectTrigger>
281
+ <SelectContent>
282
+ {hasAll && <SelectItem value={ALL_VALUE}>{allLabel}</SelectItem>}
283
+ {options.map((option) => (
284
+ <SelectItem key={option.value} value={option.value} disabled={option.disabled}>
285
+ {option.label}
286
+ </SelectItem>
287
+ ))}
288
+ </SelectContent>
289
+ </Select>
290
+ )
291
+ }
292
+ FilterBarSelect.displayName = "FilterBarSelect"
293
+
294
+ /* ---------------------------------------------------------- multi-select */
295
+
296
+ export interface FilterBarMultiSelectProps {
297
+ filterKey: string
298
+ label: string
299
+ options: FilterOption[]
300
+ placeholder?: string
301
+ className?: string
302
+ }
303
+
304
+ export function FilterBarMultiSelect({
305
+ filterKey,
306
+ label,
307
+ options,
308
+ placeholder,
309
+ className,
310
+ }: FilterBarMultiSelectProps) {
311
+ const { value, onChange } = useFilterBar()
312
+ const t = useText()
313
+ const selected = (value[filterKey] as string[] | undefined) ?? []
314
+
315
+ const handleToggle = (option: string) => {
316
+ const next = selected.includes(option)
317
+ ? selected.filter((v) => v !== option)
318
+ : [...selected, option]
319
+ // Drop the key entirely when nothing is left, so "no filter" reads as absent.
320
+ onChange({ ...value, [filterKey]: next.length > 0 ? next : undefined })
321
+ }
322
+
323
+ return (
324
+ <Popover>
325
+ <PopoverTrigger
326
+ render={<Button variant="outline" size="sm" className={cn("h-8 gap-1", className)} />}
327
+ aria-label={`${placeholder ?? label}${selected.length > 0 ? t.selected(selected.length) : ""}`}
328
+ data-slot="filter-bar-multi-select"
329
+ >
330
+ {placeholder ?? label}
331
+ {selected.length > 0 && (
332
+ <Badge variant="secondary" className="ml-1 px-1.5 py-0 text-xs">
333
+ {selected.length}
334
+ </Badge>
335
+ )}
336
+ <ChevronDown className="size-3.5 opacity-50" aria-hidden="true" />
337
+ </PopoverTrigger>
338
+ <PopoverContent align="start" className="w-56 p-2">
339
+ <div className="flex flex-col gap-1" role="group" aria-label={label}>
340
+ {options.map((option) => (
341
+ <label
342
+ key={option.value}
343
+ className="flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm hover:bg-accent"
344
+ >
345
+ <Checkbox
346
+ checked={selected.includes(option.value)}
347
+ disabled={option.disabled}
348
+ onCheckedChange={() => handleToggle(option.value)}
349
+ />
350
+ {option.label}
351
+ </label>
352
+ ))}
353
+ </div>
354
+ </PopoverContent>
355
+ </Popover>
356
+ )
357
+ }
358
+ FilterBarMultiSelect.displayName = "FilterBarMultiSelect"
359
+
360
+ /* ---------------------------------------------------------- active chips */
361
+
362
+ export interface FilterBarActiveChipsProps {
363
+ /** Filter configs used to resolve option labels. Without them, raw values show. */
364
+ filters?: FilterConfig[]
365
+ className?: string
366
+ }
367
+
368
+ interface ActiveChip {
369
+ key: string
370
+ filterLabel: string
371
+ optionLabel: string
372
+ optionValue?: string
373
+ isMulti: boolean
374
+ }
375
+
376
+ export function FilterBarActiveChips({ filters, className }: FilterBarActiveChipsProps) {
377
+ const { value, onChange } = useFilterBar()
378
+ const t = useText()
379
+
380
+ const getActiveChips = (): ActiveChip[] => {
381
+ const chips: ActiveChip[] = []
382
+
383
+ const pushRaw = (key: string, v: string | string[]) => {
384
+ if (typeof v === "string") {
385
+ chips.push({ key, filterLabel: key, optionLabel: v, isMulti: false })
386
+ } else {
387
+ for (const val of v) {
388
+ chips.push({ key, filterLabel: key, optionLabel: val, optionValue: val, isMulti: true })
389
+ }
390
+ }
391
+ }
392
+
393
+ for (const [key, v] of Object.entries(value)) {
394
+ if (v === undefined || v === "") continue
395
+
396
+ const filter = filters?.find((f) => f.key === key)
397
+ if (!filter) {
398
+ pushRaw(key, v)
399
+ continue
400
+ }
401
+
402
+ if (filter.type === "select" && typeof v === "string") {
403
+ const option = filter.options?.find((o) => o.value === v)
404
+ chips.push({
405
+ key,
406
+ filterLabel: filter.label,
407
+ optionLabel: option?.label ?? v,
408
+ isMulti: false,
409
+ })
410
+ } else if (filter.type === "multi-select" && Array.isArray(v)) {
411
+ for (const val of v) {
412
+ const option = filter.options?.find((o) => o.value === val)
413
+ chips.push({
414
+ key,
415
+ filterLabel: filter.label,
416
+ optionLabel: option?.label ?? val,
417
+ optionValue: val,
418
+ isMulti: true,
419
+ })
420
+ }
421
+ }
422
+ }
423
+
424
+ return chips
425
+ }
426
+
427
+ const handleRemoveFilter = (key: string, optionValue?: string) => {
428
+ if (optionValue !== undefined) {
429
+ const current = (value[key] as string[] | undefined) ?? []
430
+ const next = current.filter((v) => v !== optionValue)
431
+ onChange({ ...value, [key]: next.length > 0 ? next : undefined })
432
+ } else {
433
+ onChange({ ...value, [key]: undefined })
434
+ }
435
+ }
436
+
437
+ const activeChips = getActiveChips()
438
+
439
+ if (activeChips.length === 0) return null
440
+
441
+ return (
442
+ <>
443
+ {activeChips.map((chip) => (
444
+ <Badge
445
+ key={`${chip.key}-${chip.optionValue ?? "single"}`}
446
+ variant="secondary"
447
+ className={cn("gap-1 pr-1", className)}
448
+ data-slot="filter-bar-chip"
449
+ >
450
+ <span className="text-xs text-muted-foreground">{chip.filterLabel}:</span>
451
+ {chip.optionLabel}
452
+ <button
453
+ type="button"
454
+ className="ml-0.5 rounded-full p-0.5 hover:bg-muted-foreground/20"
455
+ onClick={() => handleRemoveFilter(chip.key, chip.optionValue)}
456
+ aria-label={t.remove(chip.filterLabel, chip.optionLabel)}
457
+ >
458
+ <X className="size-3" aria-hidden="true" />
459
+ </button>
460
+ </Badge>
461
+ ))}
462
+ </>
463
+ )
464
+ }
465
+ FilterBarActiveChips.displayName = "FilterBarActiveChips"
466
+
467
+ /* ----------------------------------------------------------- clear button */
468
+
469
+ export interface FilterBarClearButtonProps {
470
+ /** Override the button label. Defaults to "Clear" (ja: "クリア"). */
471
+ label?: string
472
+ className?: string
473
+ }
474
+
475
+ export function FilterBarClearButton({ label, className }: FilterBarClearButtonProps) {
476
+ const { value, onClear } = useFilterBar()
477
+ const t = useText()
478
+ const hasActiveFilters = Object.values(value).some((v) =>
479
+ Array.isArray(v) ? v.length > 0 : v !== undefined && v !== "",
480
+ )
481
+ if (!hasActiveFilters || !onClear) return null
482
+
483
+ return (
484
+ <Button
485
+ variant="ghost"
486
+ size="sm"
487
+ className={cn("h-7 px-2 text-xs", className)}
488
+ onClick={onClear}
489
+ aria-label={t.clearAll}
490
+ data-slot="filter-bar-clear-button"
491
+ >
492
+ {label ?? t.clear}
493
+ </Button>
494
+ )
495
+ }
496
+ FilterBarClearButton.displayName = "FilterBarClearButton"
497
+
498
+ /* ------------------------------------------------------------------ preset */
499
+
500
+ export interface FilterBarPresetProps {
501
+ filters: FilterConfig[]
502
+ value: FilterValues
503
+ onChange: (values: FilterValues) => void
504
+ onClear?: () => void
505
+ className?: string
506
+ }
507
+
508
+ /** Not given yet (a draft fills props in steps): render empty, keep the same value between renders. */
509
+ const NO_FILTERS: never[] = []
510
+ const NO_FILTER_VALUES: FilterValues = {}
511
+
512
+ /** The whole row built from a `FilterConfig[]`. */
513
+ export function FilterBarPreset({
514
+ filters = NO_FILTERS,
515
+ value = NO_FILTER_VALUES,
516
+ onChange,
517
+ onClear,
518
+ className,
519
+ }: FilterBarPresetProps) {
520
+ return (
521
+ <FilterBar value={value} onChange={onChange} onClear={onClear} className={className}>
522
+ {filters.map((filter) => {
523
+ if (filter.type === "select") {
524
+ return (
525
+ <FilterBarSelect
526
+ key={filter.key}
527
+ filterKey={filter.key}
528
+ label={filter.label}
529
+ placeholder={filter.placeholder}
530
+ allLabel={filter.allLabel}
531
+ options={filter.options ?? []}
532
+ />
533
+ )
534
+ }
535
+ if (filter.type === "multi-select") {
536
+ return (
537
+ <FilterBarMultiSelect
538
+ key={filter.key}
539
+ filterKey={filter.key}
540
+ label={filter.label}
541
+ placeholder={filter.placeholder}
542
+ options={filter.options ?? []}
543
+ />
544
+ )
545
+ }
546
+ return null
547
+ })}
548
+ <FilterBarActiveChips filters={filters} />
549
+ {onClear && <FilterBarClearButton />}
550
+ </FilterBar>
551
+ )
552
+ }
553
+ FilterBarPreset.displayName = "FilterBarPreset"