@squadbase/vantage 0.6.2-dev1.42c4e48 → 0.6.2-dev1.51e401a

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 (295) hide show
  1. package/dist/{build-MFWRPYKW.js → build-U7GO26HR.js} +7 -7
  2. package/dist/{build-MFWRPYKW.js.map → build-U7GO26HR.js.map} +1 -1
  3. package/dist/{check-ZBIH422T.js → check-MRVAIHG5.js} +5 -5
  4. package/dist/{check-ZBIH422T.js.map → check-MRVAIHG5.js.map} +1 -1
  5. package/dist/chunk-2IAPIOPS.js +152 -0
  6. package/dist/chunk-2IAPIOPS.js.map +1 -0
  7. package/dist/chunk-3RJEOP3J.js +174 -0
  8. package/dist/chunk-3RJEOP3J.js.map +1 -0
  9. package/dist/chunk-77CISBMG.js +7581 -0
  10. package/dist/chunk-77CISBMG.js.map +1 -0
  11. package/dist/{chunk-V3LF7BE7.js → chunk-ANZLVSEP.js} +4 -5
  12. package/dist/chunk-ANZLVSEP.js.map +1 -0
  13. package/dist/{chunk-XQUSGIDG.js → chunk-CKWPK4RO.js} +10 -3
  14. package/dist/chunk-CKWPK4RO.js.map +1 -0
  15. package/dist/chunk-EG4PX726.js +678 -0
  16. package/dist/chunk-EG4PX726.js.map +1 -0
  17. package/dist/chunk-NZ5QIIG4.js +74 -0
  18. package/dist/chunk-NZ5QIIG4.js.map +1 -0
  19. package/dist/{chunk-7DZGJQ3P.js → chunk-PJ4SPXHS.js} +23 -6
  20. package/dist/chunk-PJ4SPXHS.js.map +1 -0
  21. package/dist/{chunk-OB6UBJXF.js → chunk-QNOX7WLZ.js} +127 -36
  22. package/dist/chunk-QNOX7WLZ.js.map +1 -0
  23. package/dist/{chunk-PXRX5PEC.js → chunk-ROHNXP7J.js} +73 -4
  24. package/dist/chunk-ROHNXP7J.js.map +1 -0
  25. package/dist/{chunk-CEXAQQ5W.js → chunk-S5MDTHH7.js} +180 -18
  26. package/dist/chunk-S5MDTHH7.js.map +1 -0
  27. package/dist/{chunk-IBGOCWJB.js → chunk-SDJTTMLK.js} +247 -86
  28. package/dist/chunk-SDJTTMLK.js.map +1 -0
  29. package/dist/{chunk-QEWCYW3I.js → chunk-SHZM3EXB.js} +29 -8
  30. package/dist/chunk-SHZM3EXB.js.map +1 -0
  31. package/dist/chunk-TRMHNMMZ.js +61 -0
  32. package/dist/chunk-TRMHNMMZ.js.map +1 -0
  33. package/dist/{chunk-A2UUGASH.js → chunk-U736PVHY.js} +2 -2
  34. package/dist/chunk-U736PVHY.js.map +1 -0
  35. package/dist/{chunk-JKLLFVEU.js → chunk-UWSQUHX6.js} +4 -99
  36. package/dist/chunk-UWSQUHX6.js.map +1 -0
  37. package/dist/cli.js +6 -6
  38. package/dist/client/index.d.ts +2 -0
  39. package/dist/client/index.js +8 -2
  40. package/dist/client/index.js.map +1 -1
  41. package/dist/components/index.d.ts +1617 -101
  42. package/dist/components/index.js +7 -4
  43. package/dist/delta-text-CbrOaW83.d.ts +17 -0
  44. package/dist/{dev-DCTOHQ6W.js → dev-O42JFQ64.js} +7 -7
  45. package/dist/{dev-DCTOHQ6W.js.map → dev-O42JFQ64.js.map} +1 -1
  46. package/dist/doc/index.d.ts +215 -2
  47. package/dist/doc/index.js +423 -5
  48. package/dist/doc/index.js.map +1 -1
  49. package/dist/{doctor-5E2VEZPK.js → doctor-YD3XAAOT.js} +5 -5
  50. package/dist/{doctor-5E2VEZPK.js.map → doctor-YD3XAAOT.js.map} +1 -1
  51. package/dist/format-DihQkL1N.d.ts +249 -0
  52. package/dist/markdown/index.js +1 -1
  53. package/dist/markdown/index.js.map +1 -1
  54. package/dist/query/index.js +2 -2
  55. package/dist/{routes-3SGAFDMC.js → routes-PVKNT777.js} +4 -4
  56. package/dist/{routes-3SGAFDMC.js.map → routes-PVKNT777.js.map} +1 -1
  57. package/dist/slide/index.d.ts +272 -1
  58. package/dist/slide/index.js +1025 -4
  59. package/dist/slide/index.js.map +1 -1
  60. package/dist/table/index.js +11 -8
  61. package/dist/table/index.js.map +1 -1
  62. package/dist/ui/index.d.ts +8 -2
  63. package/dist/ui/index.js +22 -112
  64. package/dist/ui/index.js.map +1 -1
  65. package/dist/{upgrade-DBBE6GIR.js → upgrade-6DT7ADRP.js} +4 -4
  66. package/dist/{upgrade-DBBE6GIR.js.map → upgrade-6DT7ADRP.js.map} +1 -1
  67. package/dist/vite/index.js +3 -3
  68. package/docs/en/agent-skills.md +1 -1
  69. package/docs/en/changelog.md +2 -0
  70. package/docs/en/components.md +42 -17
  71. package/docs/en/doc/delta-text.md +40 -0
  72. package/docs/en/doc/doc-action-items.md +49 -0
  73. package/docs/en/doc/doc-callout.md +38 -0
  74. package/docs/en/doc/doc-data-table.md +60 -0
  75. package/docs/en/doc/doc-executive-summary.md +64 -0
  76. package/docs/en/doc/doc-figure.md +56 -0
  77. package/docs/en/doc/doc-key-findings.md +61 -0
  78. package/docs/en/doc/doc-kpi-scorecard.md +80 -0
  79. package/docs/en/doc/doc-notes-and-sources.md +43 -0
  80. package/docs/en/doc/doc-report-header.md +46 -0
  81. package/docs/en/pages-and-metadata.md +15 -0
  82. package/docs/en/parts/area-chart.md +99 -0
  83. package/docs/en/parts/bar-chart.md +91 -0
  84. package/docs/en/parts/bar-list.md +50 -0
  85. package/docs/en/parts/category-bar.md +37 -0
  86. package/docs/en/parts/chart-frame.md +39 -0
  87. package/docs/en/parts/data-table.md +22 -0
  88. package/docs/en/parts/delta.md +51 -0
  89. package/docs/en/parts/donut-chart.md +72 -0
  90. package/docs/en/parts/format.md +43 -0
  91. package/docs/en/parts/funnel-steps.md +2 -1
  92. package/docs/en/parts/heatmap.md +53 -0
  93. package/docs/en/parts/kpi-card.md +58 -0
  94. package/docs/en/parts/legend.md +35 -0
  95. package/docs/en/parts/line-chart.md +94 -0
  96. package/docs/en/parts/metric-value.md +3 -2
  97. package/docs/en/parts/progress-circle.md +33 -0
  98. package/docs/en/parts/sample-data.md +39 -0
  99. package/docs/en/parts/scatter-chart.md +56 -0
  100. package/docs/en/parts/sparkline.md +1 -1
  101. package/docs/en/parts/tracker.md +36 -0
  102. package/docs/en/parts/trend-indicator.md +17 -1
  103. package/docs/en/parts/updated-at.md +30 -0
  104. package/docs/en/parts/waterfall-chart.md +56 -0
  105. package/docs/en/slide/slide-chart-full.md +48 -0
  106. package/docs/en/slide/slide-chart-insight.md +52 -0
  107. package/docs/en/slide/slide-columns.md +46 -0
  108. package/docs/en/slide/slide-comparison.md +68 -0
  109. package/docs/en/slide/slide-cover.md +39 -0
  110. package/docs/en/slide/slide-exec-summary.md +47 -0
  111. package/docs/en/slide/slide-heading.md +38 -0
  112. package/docs/en/slide/slide-kpi-grid.md +55 -0
  113. package/docs/en/slide/slide-kpi-hero.md +58 -0
  114. package/docs/en/slide/slide-next-actions.md +49 -0
  115. package/docs/en/slide/slide-scorecard-table.md +54 -0
  116. package/docs/en/slide/slide-section.md +44 -0
  117. package/docs/en/slide/slide-source-line.md +34 -0
  118. package/docs/en/ui/alert-dialog.md +42 -0
  119. package/docs/en/ui/avatar.md +31 -0
  120. package/docs/en/ui/combobox.md +74 -0
  121. package/docs/en/ui/drawer.md +41 -0
  122. package/docs/en/ui/empty.md +1 -1
  123. package/docs/en/ui/error-state.md +4 -3
  124. package/docs/en/ui/field.md +42 -0
  125. package/docs/en/ui/hover-card.md +21 -0
  126. package/docs/en/ui/kbd.md +17 -0
  127. package/docs/en/ui/loading.md +1 -1
  128. package/docs/en/ui/pagination.md +36 -0
  129. package/docs/en/ui/radio-group.md +21 -0
  130. package/docs/en/ui/toast.md +44 -0
  131. package/docs/en/ui/toggle-group.md +4 -3
  132. package/docs/en/ui-and-theming.md +1 -1
  133. package/docs/index.json +917 -77
  134. package/docs/ja/agent-skills.md +1 -1
  135. package/docs/ja/changelog.md +2 -0
  136. package/docs/ja/components.md +39 -16
  137. package/docs/ja/doc/delta-text.md +40 -0
  138. package/docs/ja/doc/doc-action-items.md +49 -0
  139. package/docs/ja/doc/doc-callout.md +38 -0
  140. package/docs/ja/doc/doc-data-table.md +60 -0
  141. package/docs/ja/doc/doc-executive-summary.md +64 -0
  142. package/docs/ja/doc/doc-figure.md +56 -0
  143. package/docs/ja/doc/doc-key-findings.md +61 -0
  144. package/docs/ja/doc/doc-kpi-scorecard.md +80 -0
  145. package/docs/ja/doc/doc-notes-and-sources.md +43 -0
  146. package/docs/ja/doc/doc-report-header.md +46 -0
  147. package/docs/ja/page-types/slide.md +1 -0
  148. package/docs/ja/pages-and-metadata.md +15 -0
  149. package/docs/ja/parts/area-chart.md +49 -25
  150. package/docs/ja/parts/bar-chart.md +50 -24
  151. package/docs/ja/parts/bar-list.md +31 -8
  152. package/docs/ja/parts/chart-frame.md +36 -0
  153. package/docs/ja/parts/data-table.md +22 -0
  154. package/docs/ja/parts/delta.md +51 -0
  155. package/docs/ja/parts/donut-chart.md +45 -26
  156. package/docs/ja/parts/format.md +44 -0
  157. package/docs/ja/parts/funnel-steps.md +27 -8
  158. package/docs/ja/parts/heatmap.md +45 -0
  159. package/docs/ja/parts/kpi-card.md +56 -0
  160. package/docs/ja/parts/legend.md +36 -0
  161. package/docs/ja/parts/line-chart.md +48 -22
  162. package/docs/ja/parts/metric-value.md +3 -2
  163. package/docs/ja/parts/sample-data.md +61 -0
  164. package/docs/ja/parts/scatter-chart.md +69 -0
  165. package/docs/ja/parts/sparkline.md +1 -1
  166. package/docs/ja/parts/trend-indicator.md +17 -1
  167. package/docs/ja/parts/updated-at.md +27 -0
  168. package/docs/ja/parts/waterfall-chart.md +61 -0
  169. package/docs/ja/slide/slide-chart-full.md +48 -0
  170. package/docs/ja/slide/slide-chart-insight.md +52 -0
  171. package/docs/ja/slide/slide-columns.md +46 -0
  172. package/docs/ja/slide/slide-comparison.md +68 -0
  173. package/docs/ja/slide/slide-cover.md +39 -0
  174. package/docs/ja/slide/slide-exec-summary.md +47 -0
  175. package/docs/ja/slide/slide-heading.md +38 -0
  176. package/docs/ja/slide/slide-kpi-grid.md +55 -0
  177. package/docs/ja/slide/slide-kpi-hero.md +58 -0
  178. package/docs/ja/slide/slide-next-actions.md +49 -0
  179. package/docs/ja/slide/slide-scorecard-table.md +54 -0
  180. package/docs/ja/slide/slide-section.md +44 -0
  181. package/docs/ja/slide/slide-source-line.md +34 -0
  182. package/docs/ja/ui/empty.md +1 -1
  183. package/docs/ja/ui/error-state.md +4 -3
  184. package/docs/ja/ui/loading.md +1 -1
  185. package/docs/ja/ui/toggle-group.md +4 -3
  186. package/docs/ja/ui-and-theming.md +2 -2
  187. package/package.json +26 -24
  188. package/registry/ui/accordion.tsx +4 -2
  189. package/registry/ui/alert-dialog.tsx +3 -3
  190. package/registry/ui/alert.tsx +3 -1
  191. package/registry/ui/app-shell.tsx +145 -2
  192. package/registry/ui/area-chart.tsx +3111 -166
  193. package/registry/ui/attention-list.tsx +465 -0
  194. package/registry/ui/avatar.tsx +1 -1
  195. package/registry/ui/badge.tsx +3 -1
  196. package/registry/ui/bar-chart.tsx +3105 -164
  197. package/registry/ui/bar-list.tsx +3877 -181
  198. package/registry/ui/box-plot-chart.tsx +4214 -0
  199. package/registry/ui/breadcrumb.tsx +3 -1
  200. package/registry/ui/button.tsx +3 -1
  201. package/registry/ui/calendar.tsx +3 -1
  202. package/registry/ui/card.tsx +4 -2
  203. package/registry/ui/category-bar.tsx +1233 -16
  204. package/registry/ui/chart-frame.tsx +1604 -0
  205. package/registry/ui/checkbox.tsx +3 -1
  206. package/registry/ui/collapsible.tsx +3 -1
  207. package/registry/ui/combobox.tsx +2 -2
  208. package/registry/ui/command.tsx +3 -1
  209. package/registry/ui/dashboard-card.tsx +71 -28
  210. package/registry/ui/data-table.tsx +882 -68
  211. package/registry/ui/date-range-picker.tsx +400 -65
  212. package/registry/ui/delta.tsx +350 -0
  213. package/registry/ui/dialog.tsx +5 -3
  214. package/registry/ui/donut-chart.tsx +3184 -182
  215. package/registry/ui/drawer.tsx +1 -1
  216. package/registry/ui/dropdown-menu.tsx +5 -3
  217. package/registry/ui/echart.tsx +761 -69
  218. package/registry/ui/field.tsx +1 -1
  219. package/registry/ui/filter-bar.tsx +212 -26
  220. package/registry/ui/funnel-steps.tsx +3622 -53
  221. package/registry/ui/heatmap.tsx +2027 -0
  222. package/registry/ui/histogram-chart.tsx +4284 -0
  223. package/registry/ui/hover-card.tsx +2 -2
  224. package/registry/ui/input-group.tsx +3 -1
  225. package/registry/ui/input.tsx +3 -1
  226. package/registry/ui/kbd.tsx +3 -1
  227. package/registry/ui/kpi-card.tsx +905 -0
  228. package/registry/ui/label.tsx +3 -1
  229. package/registry/ui/legend.tsx +3817 -0
  230. package/registry/ui/line-chart.tsx +3109 -158
  231. package/registry/ui/metric-value.tsx +9 -1
  232. package/registry/ui/multi-select.tsx +170 -14
  233. package/registry/ui/page-shell.tsx +244 -51
  234. package/registry/ui/pagination.tsx +3 -1
  235. package/registry/ui/paired-chart.tsx +4086 -0
  236. package/registry/ui/placeholder.tsx +8 -2
  237. package/registry/ui/popover.tsx +4 -2
  238. package/registry/ui/progress-circle.tsx +773 -12
  239. package/registry/ui/progress.tsx +3 -1
  240. package/registry/ui/radio-group.tsx +1 -1
  241. package/registry/ui/sample-data.tsx +230 -0
  242. package/registry/ui/scatter-chart.tsx +4503 -0
  243. package/registry/ui/scroll-area.tsx +3 -2
  244. package/registry/ui/searchable-select.tsx +153 -10
  245. package/registry/ui/section-header.tsx +5 -3
  246. package/registry/ui/segmented-control.tsx +7 -2
  247. package/registry/ui/select.tsx +4 -2
  248. package/registry/ui/separator.tsx +3 -1
  249. package/registry/ui/sheet.tsx +4 -2
  250. package/registry/ui/sidebar.tsx +4 -2
  251. package/registry/ui/skeleton.tsx +4 -2
  252. package/registry/ui/slider.tsx +4 -2
  253. package/registry/ui/sparkline.tsx +84 -9
  254. package/registry/ui/spinner.tsx +3 -1
  255. package/registry/ui/states.tsx +194 -8
  256. package/registry/ui/status-badge.tsx +246 -39
  257. package/registry/ui/switch.tsx +3 -1
  258. package/registry/ui/table.tsx +4 -2
  259. package/registry/ui/tabs.tsx +5 -3
  260. package/registry/ui/textarea.tsx +3 -1
  261. package/registry/ui/toast.tsx +2 -2
  262. package/registry/ui/toggle-group.tsx +3 -1
  263. package/registry/ui/toggle.tsx +3 -1
  264. package/registry/ui/tooltip.tsx +10 -4
  265. package/registry/ui/tracker.tsx +798 -19
  266. package/registry/ui/trend-indicator.tsx +164 -9
  267. package/registry/ui/updated-at.tsx +276 -0
  268. package/registry/ui/waterfall-chart.tsx +4561 -0
  269. package/skills/vantage-add-feature/SKILL.md +5 -0
  270. package/skills/vantage-design/SKILL.md +60 -52
  271. package/skills/vantage-design/references/extract.md +26 -27
  272. package/skills/vantage-design/references/format.md +12 -172
  273. package/skills/vantage-design/references/presets.md +50 -110
  274. package/skills/vantage-pitfalls/SKILL.md +5 -0
  275. package/skills/vantage-slide/SKILL.md +54 -36
  276. package/templates/AGENTS.md +45 -16
  277. package/templates/agents/api.md +7 -0
  278. package/templates/agents/doc.md +48 -0
  279. package/templates/agents/page.md +69 -2
  280. package/templates/agents/slide.md +77 -3
  281. package/theme.css +247 -149
  282. package/dist/chunk-5ZOP4SQR.js +0 -47
  283. package/dist/chunk-5ZOP4SQR.js.map +0 -1
  284. package/dist/chunk-7DZGJQ3P.js.map +0 -1
  285. package/dist/chunk-A2UUGASH.js.map +0 -1
  286. package/dist/chunk-AG6RZ2RR.js +0 -3208
  287. package/dist/chunk-AG6RZ2RR.js.map +0 -1
  288. package/dist/chunk-CEXAQQ5W.js.map +0 -1
  289. package/dist/chunk-IBGOCWJB.js.map +0 -1
  290. package/dist/chunk-JKLLFVEU.js.map +0 -1
  291. package/dist/chunk-OB6UBJXF.js.map +0 -1
  292. package/dist/chunk-PXRX5PEC.js.map +0 -1
  293. package/dist/chunk-QEWCYW3I.js.map +0 -1
  294. package/dist/chunk-V3LF7BE7.js.map +0 -1
  295. package/dist/chunk-XQUSGIDG.js.map +0 -1
@@ -1,98 +1,403 @@
1
1
  // Copied by `vantage add ui date-range-picker`. This file is yours to edit.
2
- // Generated from @squadbase/vantage src/components/date-range-picker.tsx by `npm run registry` (in Vantage, edit src/, not registry/).
2
+ // Generated from @squadbase/squadkit registry/components/date-range-picker.tsx by `pnpm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
+ "use client"
4
+
3
5
  /**
4
6
  * DateRangePicker — a range calendar in a popover with quick presets down the
5
7
  * side. On mobile the presets stack above a single month; on desktop they sit
6
- * beside a two-month view.
8
+ * beside a two-month view. The labels, the date format, the fiscal year start and the
9
+ * first day of the week follow the page (`<html lang>`) unless `locale` is given.
7
10
  */
8
- import { forwardRef, useCallback, useState } from "react"
11
+ import { forwardRef, useCallback, useMemo, useState } from "react"
9
12
  import { CalendarIcon } from "lucide-react"
10
- import { endOfDay, endOfMonth, format, startOfDay, startOfMonth, subDays, subMonths } from "date-fns"
13
+ import {
14
+ addMonths,
15
+ endOfDay,
16
+ endOfMonth,
17
+ endOfWeek,
18
+ isSameDay,
19
+ startOfDay,
20
+ startOfMonth,
21
+ startOfWeek,
22
+ subDays,
23
+ subMonths,
24
+ subWeeks,
25
+ } from "date-fns"
11
26
  import { ja } from "date-fns/locale"
12
27
  import type { DateRange as RdpDateRange } from "react-day-picker"
13
28
 
14
29
  import { cn } from "@squadbase/vantage/ui"
15
30
  import { useIsMobile } from "@squadbase/vantage/ui"
31
+ import { formatDate } from "@squadbase/vantage/components"
16
32
  import { Button } from "@squadbase/vantage/ui"
17
33
  import { Calendar } from "@squadbase/vantage/ui"
18
34
  import { Popover, PopoverContent, PopoverTrigger } from "@squadbase/vantage/ui"
19
35
  import type { DateRange, PresetItem } from "@squadbase/vantage/components"
36
+ import { useSyncExternalStore } from "react"
20
37
 
21
- export const defaultPresets: PresetItem[] = [
22
- {
23
- label: "Today",
24
- getValue: () => {
25
- const today = new Date()
26
- return { from: startOfDay(today), to: endOfDay(today) }
27
- },
28
- },
38
+ /* ---------- inlined from @squadbase/squadkit registry/lib/locale.ts (internal to @squadbase/vantage) ---------- */
39
+
40
+ /**
41
+ * The display language of the parts and the defaults that follow it.
42
+ *
43
+ * The language comes from `<html lang>`: a tag starting with `ja` reads Japanese, anything else English.
44
+ * Without a usable `lang` (missing, invalid, or no `document` — SSR, Node) the locale is `FALLBACK_LOCALE`
45
+ * (`en-US`). A `locale` passed explicitly (a prop, an argument) always wins.
46
+ *
47
+ * Parts read the locale through `useLocale` (`hooks/use-locale`) and pass it down; the pure helpers here
48
+ * and in `format` / `comparison` / `chart-*` take it as an argument and fall back to `documentLocale()`.
49
+ */
50
+
51
+ /** The language of the built-in wording: Japanese or English. */
52
+ type UiLanguage = "ja" | "en"
53
+
54
+ /** The locale used when `<html lang>` gives none (and on the server). */
55
+ const FALLBACK_LOCALE = "en-US"
56
+
57
+ /** `"ja"` when `locale` starts with `ja` (`ja`, `ja-JP`), else `"en"`. A missing locale is `"en"`. */
58
+ function uiLanguage(locale: string | null | undefined): UiLanguage {
59
+ return typeof locale === "string" && locale.trim().toLowerCase().startsWith("ja") ? "ja" : "en"
60
+ }
61
+
62
+ /** A BCP 47 tag in its canonical form (`en-gb` → `en-GB`), or `FALLBACK_LOCALE` when it is empty or invalid. */
63
+ function normalizeLocale(tag: string | null | undefined): string {
64
+ const value = tag?.trim()
65
+ if (!value) return FALLBACK_LOCALE
66
+ try {
67
+ return Intl.getCanonicalLocales(value)[0] ?? FALLBACK_LOCALE
68
+ } catch {
69
+ return FALLBACK_LOCALE
70
+ }
71
+ }
72
+
73
+ /**
74
+ * The locale of the page: `document.documentElement.lang`, canonicalized. `FALLBACK_LOCALE` when it is empty or
75
+ * invalid, or there is no `document`. Parts read it through `useLocale`, never while rendering.
76
+ */
77
+ function documentLocale(): string {
78
+ if (typeof document === "undefined") return FALLBACK_LOCALE
79
+ return normalizeLocale(document.documentElement?.getAttribute("lang"))
80
+ }
81
+
82
+ /** `locale` when given, else the page's locale (`documentLocale()`). */
83
+ function resolveLocale(locale?: string | null): string {
84
+ return locale || documentLocale()
85
+ }
86
+
87
+ /** Picks the wording table of the locale's language. */
88
+ function pickLocale<T>(table: { ja: T; en: T }, locale?: string | null): T {
89
+ return table[uiLanguage(resolveLocale(locale))]
90
+ }
91
+
92
+ /** The currency `format="currency"` uses without a `currency`: JPY for Japanese, USD otherwise. */
93
+ function defaultCurrency(locale?: string | null): string {
94
+ return uiLanguage(resolveLocale(locale)) === "ja" ? "JPY" : "USD"
95
+ }
96
+
97
+ /** The month a fiscal year starts in (1–12) when none is given: April for Japanese, January otherwise. */
98
+ function defaultFiscalYearStartMonth(locale?: string | null): number {
99
+ return uiLanguage(resolveLocale(locale)) === "ja" ? 4 : 1
100
+ }
101
+
102
+ /** A day of the week, 0 = Sunday … 6 = Saturday (the `Date#getDay` / date-fns numbering). */
103
+ type WeekDay = 0 | 1 | 2 | 3 | 4 | 5 | 6
104
+
105
+ /** Locales whose week starts on Sunday when the runtime has no week data. */
106
+ const SUNDAY_FIRST = new Set(["en", "en-US", "en-CA"])
107
+
108
+ /**
109
+ * The first day of the week: Monday for Japanese; otherwise the locale's convention from `Intl.Locale`
110
+ * (`en-US` Sunday, `en-GB` Monday). Without week data in the runtime: Sunday for `en` / `en-US` / `en-CA`,
111
+ * Monday for the rest.
112
+ */
113
+ function weekStartsOn(locale?: string | null): WeekDay {
114
+ const tag = normalizeLocale(resolveLocale(locale))
115
+ if (uiLanguage(tag) === "ja") return 1
116
+ try {
117
+ const info = new Intl.Locale(tag) as Intl.Locale & {
118
+ getWeekInfo?: () => { firstDay: number }
119
+ weekInfo?: { firstDay: number }
120
+ }
121
+ const firstDay = info.getWeekInfo?.().firstDay ?? info.weekInfo?.firstDay
122
+ if (typeof firstDay === "number" && firstDay >= 1 && firstDay <= 7) return (firstDay % 7) as WeekDay
123
+ } catch {
124
+ // Fall through to the table below.
125
+ }
126
+ return SUNDAY_FIRST.has(tag) ? 0 : 1
127
+ }
128
+
129
+ /* ---------- inlined from @squadbase/squadkit registry/hooks/use-locale.ts (internal to @squadbase/vantage) ---------- */
130
+
131
+ const listeners = new Set<() => void>()
132
+ let observer: MutationObserver | null = null
133
+
134
+ /** One observer on `<html lang>` for every subscribed part. */
135
+ function subscribe(onChange: () => void): () => void {
136
+ if (typeof document === "undefined" || typeof MutationObserver === "undefined") return () => {}
137
+ listeners.add(onChange)
138
+ if (!observer) {
139
+ observer = new MutationObserver(() => {
140
+ for (const listener of [...listeners]) listener()
141
+ })
142
+ observer.observe(document.documentElement, { attributes: true, attributeFilter: ["lang"] })
143
+ }
144
+ return () => {
145
+ listeners.delete(onChange)
146
+ if (listeners.size === 0 && observer) {
147
+ observer.disconnect()
148
+ observer = null
149
+ }
150
+ }
151
+ }
152
+
153
+ const serverLocale = () => FALLBACK_LOCALE
154
+
155
+ /**
156
+ * The page's locale (`<html lang>`), following changes made while the page runs. On the server and during
157
+ * hydration it is `FALLBACK_LOCALE` (`en-US`), so the markup matches; it switches right after.
158
+ */
159
+ function useDocumentLocale(): string {
160
+ return useSyncExternalStore(subscribe, documentLocale, serverLocale)
161
+ }
162
+
163
+ /** The locale a part renders in: its `locale` prop when given, else the page's (`useDocumentLocale`). */
164
+ function useLocale(locale?: string | null): string {
165
+ const page = useDocumentLocale()
166
+ return locale || page
167
+ }
168
+
169
+ /* ---------- end of inlined code ---------- */
170
+
171
+ /**
172
+ * 会計年度の開始月のフォールバック(1 月始まり)。ロケールから決まる既定は `defaultFiscalYearStartMonth`
173
+ * (`<html lang>` が ja なら 4 月、それ以外は 1 月)。
174
+ */
175
+ export const DEFAULT_FISCAL_YEAR_START_MONTH = 1
176
+
177
+ /** ラベルの言語のフォールバック(英語)。`locale` を渡さなければ、ほかの部品と同じくページの `<html lang>` に従う。 */
178
+ export const DEFAULT_DATE_PICKER_LOCALE = FALLBACK_LOCALE
179
+
180
+ /**
181
+ * 1〜12 の整数だけを開始月として受け、それ以外は `locale` の既定(ja は 4 月、それ以外は 1 月)に戻す。
182
+ * `locale` の既定はページのロケール(`<html lang>`)。
183
+ */
184
+ export function normalizeFiscalYearStartMonth(month: number | undefined, locale?: string): number {
185
+ return typeof month === "number" && Number.isInteger(month) && month >= 1 && month <= 12
186
+ ? month
187
+ : defaultFiscalYearStartMonth(locale)
188
+ }
189
+
190
+ /**
191
+ * `date` を含む年度(または年度基準の四半期)の全期間。`offset` で前後にずらす(`-1` が前の年度/四半期)。
192
+ * 年度は `fiscalYearStartMonth`(1-12)の 1 日に始まり 12 か月続く。四半期は年度の初日から 3 か月ごと。
193
+ * `fiscalYearStartMonth` を省くと `locale`(既定はページのロケール)の既定(ja は 4 月、それ以外は 1 月)。
194
+ *
195
+ * @example
196
+ * // 4 月始まりで 2026-02-10 は 2025 年度(2025-04-01〜2026-03-31)
197
+ * fiscalPeriodRange(new Date(2026, 1, 10), { unit: "year", fiscalYearStartMonth: 4 })
198
+ */
199
+ export function fiscalPeriodRange(
200
+ date: Date,
29
201
  {
30
- label: "Yesterday",
31
- getValue: () => {
32
- const yesterday = subDays(new Date(), 1)
33
- return { from: startOfDay(yesterday), to: endOfDay(yesterday) }
34
- },
202
+ unit,
203
+ fiscalYearStartMonth,
204
+ offset = 0,
205
+ locale,
206
+ }: { unit: "year" | "quarter"; fiscalYearStartMonth?: number; offset?: number; locale?: string },
207
+ ): { from: Date; to: Date } {
208
+ const startMonth = normalizeFiscalYearStartMonth(fiscalYearStartMonth, locale)
209
+ const length = unit === "year" ? 12 : 3
210
+ const sinceYearStart = (date.getMonth() - (startMonth - 1) + 12) % 12
211
+ const into = unit === "year" ? sinceYearStart : sinceYearStart % 3
212
+ const periodStart = addMonths(startOfMonth(subMonths(date, into)), offset * length)
213
+ return { from: periodStart, to: endOfMonth(addMonths(periodStart, length - 1)) }
214
+ }
215
+
216
+ const PRESET_LABELS = {
217
+ ja: {
218
+ today: "今日",
219
+ yesterday: "昨日",
220
+ last7: "過去 7 日間",
221
+ last14: "過去 14 日間",
222
+ last28: "過去 28 日間",
223
+ last30: "過去 30 日間",
224
+ last90: "過去 90 日間",
225
+ thisWeek: "今週",
226
+ lastWeek: "先週",
227
+ thisMonth: "今月",
228
+ lastMonth: "先月",
229
+ thisQuarter: "今四半期",
230
+ lastQuarter: "前四半期",
231
+ thisFiscalYear: "今年度",
232
+ lastFiscalYear: "前年度",
35
233
  },
36
- {
37
- label: "Last 7 days",
38
- getValue: () => {
39
- const today = new Date()
40
- return { from: startOfDay(subDays(today, 6)), to: endOfDay(today) }
41
- },
234
+ en: {
235
+ today: "Today",
236
+ yesterday: "Yesterday",
237
+ last7: "Last 7 days",
238
+ last14: "Last 14 days",
239
+ last28: "Last 28 days",
240
+ last30: "Last 30 days",
241
+ last90: "Last 90 days",
242
+ thisWeek: "This week",
243
+ lastWeek: "Last week",
244
+ thisMonth: "This month",
245
+ lastMonth: "Last month",
246
+ thisQuarter: "This quarter",
247
+ lastQuarter: "Last quarter",
248
+ thisFiscalYear: "This fiscal year",
249
+ lastFiscalYear: "Last fiscal year",
42
250
  },
43
- {
44
- label: "Last 14 days",
45
- getValue: () => {
46
- const today = new Date()
47
- return { from: startOfDay(subDays(today, 13)), to: endOfDay(today) }
251
+ } as const
252
+
253
+ const TEXT = {
254
+ ja: { placeholder: "期間を選択", rangeSeparator: " - " },
255
+ en: { placeholder: "Pick a date range", rangeSeparator: " – " },
256
+ }
257
+
258
+ /** カレンダーに渡す date-fns の locale。`ja*` なら日本語、それ以外は react-day-picker の既定(英語)。 */
259
+ const calendarLocale = (locale: string) => (uiLanguage(locale) === "ja" ? ja : undefined)
260
+
261
+ /**
262
+ * 既定のプリセット一覧を作る。ラベルは `locale`(`ja*` なら日本語、それ以外は英語)、
263
+ * 「今四半期」「前四半期」「今年度」「前年度」は `fiscalYearStartMonth`(省くと ja は 4 月、それ以外は 1 月)を基準にする。
264
+ * 「今〜」は期の初日から今日まで、「前〜」「先月」「先週」は前の期の全期間。週の始まりは `locale` の慣習
265
+ * (ja は月曜、`en-US` は日曜、`en-GB` は月曜。`weekStartsOn`)。`getValue` は押した時点の日付で計算する。
266
+ * `locale` の既定はページのロケール(`<html lang>`、無ければ `"en-US"`)。
267
+ *
268
+ * @example
269
+ * createDatePresets({ locale: "ja-JP", fiscalYearStartMonth: 4 })
270
+ */
271
+ export function createDatePresets({
272
+ locale: localeArg,
273
+ fiscalYearStartMonth,
274
+ now = () => new Date(),
275
+ }: { locale?: string; fiscalYearStartMonth?: number; now?: () => Date } = {}): PresetItem[] {
276
+ const locale = resolveLocale(localeArg)
277
+ const t = PRESET_LABELS[uiLanguage(locale)]
278
+ const startMonth = normalizeFiscalYearStartMonth(fiscalYearStartMonth, locale)
279
+ const WEEK_OPTIONS = { weekStartsOn: weekStartsOn(locale) }
280
+ const lastDays = (days: number) => () => {
281
+ const today = now()
282
+ return { from: startOfDay(subDays(today, days - 1)), to: endOfDay(today) }
283
+ }
284
+ const toDate = (unit: "year" | "quarter") => () => {
285
+ const today = now()
286
+ return {
287
+ from: fiscalPeriodRange(today, { unit, fiscalYearStartMonth: startMonth }).from,
288
+ to: endOfDay(today),
289
+ }
290
+ }
291
+ const previous = (unit: "year" | "quarter") => () =>
292
+ fiscalPeriodRange(now(), { unit, fiscalYearStartMonth: startMonth, offset: -1 })
293
+ return [
294
+ { label: t.today, getValue: lastDays(1) },
295
+ {
296
+ label: t.yesterday,
297
+ getValue: () => {
298
+ const yesterday = subDays(now(), 1)
299
+ return { from: startOfDay(yesterday), to: endOfDay(yesterday) }
300
+ },
48
301
  },
49
- },
50
- {
51
- label: "Last 30 days",
52
- getValue: () => {
53
- const today = new Date()
54
- return { from: startOfDay(subDays(today, 29)), to: endOfDay(today) }
302
+ { label: t.last7, getValue: lastDays(7) },
303
+ { label: t.last14, getValue: lastDays(14) },
304
+ { label: t.last28, getValue: lastDays(28) },
305
+ { label: t.last30, getValue: lastDays(30) },
306
+ { label: t.last90, getValue: lastDays(90) },
307
+ {
308
+ label: t.thisWeek,
309
+ getValue: () => {
310
+ const today = now()
311
+ return { from: startOfWeek(today, WEEK_OPTIONS), to: endOfDay(today) }
312
+ },
55
313
  },
56
- },
57
- {
58
- label: "Last 90 days",
59
- getValue: () => {
60
- const today = new Date()
61
- return { from: startOfDay(subDays(today, 89)), to: endOfDay(today) }
314
+ {
315
+ label: t.lastWeek,
316
+ getValue: () => {
317
+ const lastWeek = subWeeks(now(), 1)
318
+ return { from: startOfWeek(lastWeek, WEEK_OPTIONS), to: endOfWeek(lastWeek, WEEK_OPTIONS) }
319
+ },
62
320
  },
63
- },
64
- {
65
- label: "This month",
66
- getValue: () => {
67
- const today = new Date()
68
- return { from: startOfMonth(today), to: endOfDay(today) }
321
+ {
322
+ label: t.thisMonth,
323
+ getValue: () => {
324
+ const today = now()
325
+ return { from: startOfMonth(today), to: endOfDay(today) }
326
+ },
69
327
  },
70
- },
71
- {
72
- label: "Last month",
73
- getValue: () => {
74
- const lastMonth = subMonths(new Date(), 1)
75
- return { from: startOfMonth(lastMonth), to: endOfMonth(lastMonth) }
328
+ {
329
+ label: t.lastMonth,
330
+ getValue: () => {
331
+ const lastMonth = subMonths(now(), 1)
332
+ return { from: startOfMonth(lastMonth), to: endOfMonth(lastMonth) }
333
+ },
76
334
  },
77
- },
78
- ]
335
+ { label: t.thisQuarter, getValue: toDate("quarter") },
336
+ { label: t.lastQuarter, getValue: previous("quarter") },
337
+ { label: t.thisFiscalYear, getValue: toDate("year") },
338
+ { label: t.lastFiscalYear, getValue: previous("year") },
339
+ ]
340
+ }
341
+
342
+ /**
343
+ * `value` と日単位で同じ範囲を返す最初のプリセットを探す(無ければ `undefined`)。`getValue` は呼んだ時点の日付で計算される。
344
+ * 「今日」と「今週」が月曜に重なるときなど、複数が一致したら並びの早いほうを返す。
345
+ */
346
+ export function findMatchingPreset(presets: PresetItem[], value: DateRange | undefined): PresetItem | undefined {
347
+ const { from, to } = value ?? {}
348
+ if (!from || !to) return undefined
349
+ return presets.find((preset) => {
350
+ const range = preset.getValue()
351
+ return range.from != null && range.to != null && isSameDay(range.from, from) && isSameDay(range.to, to)
352
+ })
353
+ }
354
+
355
+ /**
356
+ * フォールバック(`en-US`:英語・1 月始まり・日曜始まり)のプリセット。DateRangePicker は `presets` を渡さなければ
357
+ * ページのロケールからプリセットを作るので、これを渡す必要はない。`locale` / `fiscalYearStartMonth` を変えるなら
358
+ * `createDatePresets` か DateRangePicker の同名 props。
359
+ */
360
+ export const defaultPresets: PresetItem[] = createDatePresets({ locale: FALLBACK_LOCALE })
79
361
 
80
362
  export interface DateRangePickerProps {
81
363
  /**
82
- * 選択中の日付範囲。型は Vantage 独自の `DateRange`。
364
+ * 選択中の日付範囲。型はこの部品独自の `DateRange`。
83
365
  * react-day-picker の `DateRange` とは別物なので注意 — そちらが必要なら
84
366
  * このファイルの末尾で re-export している `RdpDateRange` を使う。
85
367
  *
86
368
  * @example
87
- * import type { DateRange } from "@squadbase/vantage/components"
369
+ * import { DateRangePicker, type DateRange } from "@/components/date-range-picker"
370
+ * // Vantage: import { DateRangePicker, type DateRange } from "@squadbase/vantage/components"
88
371
  *
89
372
  * const [range, setRange] = useState<DateRange | undefined>()
90
373
  * <DateRangePicker value={range} onChange={setRange} />
91
374
  */
92
375
  value?: DateRange
93
376
  onChange?: (range: DateRange) => void
377
+ /**
378
+ * ショートカットの一覧。指定すると `locale` / `fiscalYearStartMonth` による既定の一覧の代わりに使う。
379
+ *
380
+ * @example
381
+ * presets={[{ label: "直近 10 日", getValue: () => ({ from: subDays(new Date(), 9), to: new Date() }) }]}
382
+ */
94
383
  presets?: PresetItem[]
95
384
  showPresets?: boolean
385
+ /**
386
+ * プリセットのラベル・日付の書式・カレンダーの月名と曜日・週の始まりのロケール。`ja*` なら日本語、それ以外は英語。
387
+ * 既定はページのロケール(`<html lang>`、無ければ `"en-US"`)。
388
+ *
389
+ * @example
390
+ * <DateRangePicker locale="ja-JP" />
391
+ */
392
+ locale?: string
393
+ /**
394
+ * 年度の開始月(1-12)。「今年度」「前年度」と、年度基準の「今四半期」「前四半期」の区切りになる。
395
+ * 既定はロケールで決まる(ja は `4`、それ以外は `1`)。範囲外・整数でない値はその既定として扱う。
396
+ *
397
+ * @example
398
+ * <DateRangePicker fiscalYearStartMonth={1} />
399
+ */
400
+ fiscalYearStartMonth?: number
96
401
  maxDate?: Date
97
402
  minDate?: Date
98
403
  placeholder?: string
@@ -106,11 +411,13 @@ export const DateRangePicker = forwardRef<HTMLDivElement, DateRangePickerProps>(
106
411
  {
107
412
  value,
108
413
  onChange,
109
- presets = defaultPresets,
414
+ presets: presetsProp,
110
415
  showPresets = true,
416
+ locale: localeProp,
417
+ fiscalYearStartMonth,
111
418
  maxDate,
112
419
  minDate,
113
- placeholder = "Select date range",
420
+ placeholder,
114
421
  disabled = false,
115
422
  className,
116
423
  align = "start",
@@ -119,6 +426,12 @@ export const DateRangePicker = forwardRef<HTMLDivElement, DateRangePickerProps>(
119
426
  ) {
120
427
  const [open, setOpen] = useState(false)
121
428
  const isMobile = useIsMobile()
429
+ const locale = useLocale(localeProp)
430
+ const t = TEXT[uiLanguage(locale)]
431
+ const presets = useMemo(
432
+ () => presetsProp ?? createDatePresets({ locale, fiscalYearStartMonth }),
433
+ [presetsProp, locale, fiscalYearStartMonth],
434
+ )
122
435
 
123
436
  const handleCalendarSelect = useCallback(
124
437
  (range: RdpDateRange | undefined) => {
@@ -136,10 +449,17 @@ export const DateRangePicker = forwardRef<HTMLDivElement, DateRangePickerProps>(
136
449
  )
137
450
 
138
451
  const formatDateRange = () => {
139
- if (!value?.from) return placeholder
140
- if (!value.to) return format(value.from, "yyyy/MM/dd", { locale: ja })
141
- return `${format(value.from, "yyyy/MM/dd", { locale: ja })} - ${format(value.to, "yyyy/MM/dd", { locale: ja })}`
452
+ if (!value?.from) return placeholder ?? t.placeholder
453
+ if (!value.to) return formatDate(value.from, "date", locale)
454
+ return `${formatDate(value.from, "date", locale)}${t.rangeSeparator}${formatDate(value.to, "date", locale)}`
142
455
  }
456
+ // 選んだ期間がプリセットの結果と一致するときは、日付の前にそのプリセット名を出す。
457
+ const selectedPreset = findMatchingPreset(presets, value)
458
+ const presetLabel = selectedPreset?.label
459
+
460
+ // 開いたとき、右の月が終了月・左がその前月になるよう、表示月(左の月)を決める。1 か月表示(モバイル)は終了月。
461
+ const endMonth = value?.to ?? value?.from
462
+ const defaultMonth = endMonth ? (isMobile ? endMonth : subMonths(endMonth, 1)) : undefined
143
463
 
144
464
  const calendarSelected: RdpDateRange | undefined = value?.from
145
465
  ? { from: value.from, to: value.to ?? undefined }
@@ -160,15 +480,22 @@ export const DateRangePicker = forwardRef<HTMLDivElement, DateRangePickerProps>(
160
480
  )}
161
481
  />
162
482
  }
163
- aria-label={formatDateRange()}
483
+ aria-label={presetLabel ? `${presetLabel} ${formatDateRange()}` : formatDateRange()}
164
484
  aria-expanded={open}
165
485
  >
166
486
  <CalendarIcon className="size-4" aria-hidden="true" />
167
- <span>{formatDateRange()}</span>
487
+ {presetLabel ? (
488
+ <>
489
+ <span>{presetLabel}</span>
490
+ <span className="text-muted-foreground">{formatDateRange()}</span>
491
+ </>
492
+ ) : (
493
+ <span>{formatDateRange()}</span>
494
+ )}
168
495
  </PopoverTrigger>
169
496
  <PopoverContent
170
497
  align={align}
171
- className={cn("w-auto p-0", isMobile ? "flex flex-col" : "flex")}
498
+ className={cn("w-auto gap-0 p-0", isMobile ? "flex-col" : "flex-row")}
172
499
  >
173
500
  {showPresets && (
174
501
  <div className={cn("flex flex-col gap-1 p-3", isMobile ? "border-b" : "border-r")}>
@@ -177,7 +504,11 @@ export const DateRangePicker = forwardRef<HTMLDivElement, DateRangePickerProps>(
177
504
  key={preset.label}
178
505
  variant="ghost"
179
506
  size="sm"
180
- className="justify-start text-xs"
507
+ aria-pressed={selectedPreset === preset}
508
+ className={cn(
509
+ "justify-start text-xs",
510
+ selectedPreset === preset && "bg-primary/10 text-primary hover:bg-primary/15 hover:text-primary",
511
+ )}
181
512
  onClick={() => handlePresetClick(preset)}
182
513
  >
183
514
  {preset.label}
@@ -187,7 +518,11 @@ export const DateRangePicker = forwardRef<HTMLDivElement, DateRangePickerProps>(
187
518
  )}
188
519
  <div className="p-3">
189
520
  <Calendar
521
+ key={`${open}-${isMobile}`}
190
522
  mode="range"
523
+ defaultMonth={defaultMonth}
524
+ locale={calendarLocale(locale)}
525
+ weekStartsOn={weekStartsOn(locale)}
191
526
  numberOfMonths={isMobile ? 1 : 2}
192
527
  selected={calendarSelected}
193
528
  onSelect={handleCalendarSelect}