@squadbase/vantage 0.6.2-dev1.9365dcd → 0.6.2-dev1.9d7130b
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.
- package/dist/{build-X5NVCQKT.js → build-U7GO26HR.js} +7 -7
- package/dist/{build-X5NVCQKT.js.map → build-U7GO26HR.js.map} +1 -1
- package/dist/{check-ZBIH422T.js → check-MRVAIHG5.js} +5 -5
- package/dist/{check-ZBIH422T.js.map → check-MRVAIHG5.js.map} +1 -1
- package/dist/chunk-2IAPIOPS.js +152 -0
- package/dist/chunk-2IAPIOPS.js.map +1 -0
- package/dist/chunk-3RJEOP3J.js +174 -0
- package/dist/chunk-3RJEOP3J.js.map +1 -0
- package/dist/{chunk-HKSVYMYJ.js → chunk-77CISBMG.js} +1049 -1387
- package/dist/chunk-77CISBMG.js.map +1 -0
- package/dist/{chunk-V3LF7BE7.js → chunk-ANZLVSEP.js} +4 -5
- package/dist/chunk-ANZLVSEP.js.map +1 -0
- package/dist/{chunk-XQUSGIDG.js → chunk-CKWPK4RO.js} +10 -3
- package/dist/chunk-CKWPK4RO.js.map +1 -0
- package/dist/chunk-EG4PX726.js +678 -0
- package/dist/chunk-EG4PX726.js.map +1 -0
- package/dist/{chunk-2N72FKLB.js → chunk-NZ5QIIG4.js} +33 -11
- package/dist/chunk-NZ5QIIG4.js.map +1 -0
- package/dist/{chunk-7DZGJQ3P.js → chunk-PJ4SPXHS.js} +23 -6
- package/dist/chunk-PJ4SPXHS.js.map +1 -0
- package/dist/{chunk-XPQSTZUU.js → chunk-QNOX7WLZ.js} +5 -4
- package/dist/chunk-QNOX7WLZ.js.map +1 -0
- package/dist/{chunk-PDOBHMUQ.js → chunk-ROHNXP7J.js} +72 -3
- package/dist/chunk-ROHNXP7J.js.map +1 -0
- package/dist/{chunk-CEXAQQ5W.js → chunk-S5MDTHH7.js} +180 -18
- package/dist/chunk-S5MDTHH7.js.map +1 -0
- package/dist/{chunk-2ICNHUG6.js → chunk-SDJTTMLK.js} +64 -7
- package/dist/chunk-SDJTTMLK.js.map +1 -0
- package/dist/{chunk-WAMZWZJB.js → chunk-SHZM3EXB.js} +19 -8
- package/dist/chunk-SHZM3EXB.js.map +1 -0
- package/dist/chunk-TRMHNMMZ.js +61 -0
- package/dist/chunk-TRMHNMMZ.js.map +1 -0
- package/dist/{chunk-JKLLFVEU.js → chunk-UWSQUHX6.js} +4 -99
- package/dist/chunk-UWSQUHX6.js.map +1 -0
- package/dist/cli.js +6 -6
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +8 -2
- package/dist/client/index.js.map +1 -1
- package/dist/components/index.d.ts +57 -273
- package/dist/components/index.js +6 -3
- package/dist/delta-text-CbrOaW83.d.ts +17 -0
- package/dist/{dev-4XEDTELV.js → dev-O42JFQ64.js} +7 -7
- package/dist/{dev-4XEDTELV.js.map → dev-O42JFQ64.js.map} +1 -1
- package/dist/doc/index.d.ts +215 -2
- package/dist/doc/index.js +422 -4
- package/dist/doc/index.js.map +1 -1
- package/dist/{doctor-5E2VEZPK.js → doctor-YD3XAAOT.js} +5 -5
- package/dist/{doctor-5E2VEZPK.js.map → doctor-YD3XAAOT.js.map} +1 -1
- package/dist/format-DihQkL1N.d.ts +249 -0
- package/dist/query/index.js +2 -2
- package/dist/{routes-3SGAFDMC.js → routes-PVKNT777.js} +4 -4
- package/dist/{routes-3SGAFDMC.js.map → routes-PVKNT777.js.map} +1 -1
- package/dist/slide/index.d.ts +272 -1
- package/dist/slide/index.js +1023 -2
- package/dist/slide/index.js.map +1 -1
- package/dist/table/index.js +9 -6
- package/dist/table/index.js.map +1 -1
- package/dist/ui/index.d.ts +5 -2
- package/dist/ui/index.js +6 -6
- package/dist/ui/index.js.map +1 -1
- package/dist/{upgrade-JHXZBGQS.js → upgrade-6DT7ADRP.js} +4 -4
- package/dist/{upgrade-JHXZBGQS.js.map → upgrade-6DT7ADRP.js.map} +1 -1
- package/dist/vite/index.js +3 -3
- package/docs/en/agent-skills.md +1 -1
- package/docs/en/changelog.md +2 -0
- package/docs/en/components.md +13 -2
- package/docs/en/doc/delta-text.md +40 -0
- package/docs/en/doc/doc-action-items.md +49 -0
- package/docs/en/doc/doc-callout.md +38 -0
- package/docs/en/doc/doc-data-table.md +60 -0
- package/docs/en/doc/doc-executive-summary.md +64 -0
- package/docs/en/doc/doc-figure.md +56 -0
- package/docs/en/doc/doc-key-findings.md +61 -0
- package/docs/en/doc/doc-kpi-scorecard.md +80 -0
- package/docs/en/doc/doc-notes-and-sources.md +43 -0
- package/docs/en/doc/doc-report-header.md +46 -0
- package/docs/en/pages-and-metadata.md +15 -0
- package/docs/en/parts/area-chart.md +99 -0
- package/docs/en/parts/bar-chart.md +91 -0
- package/docs/en/parts/bar-list.md +50 -0
- package/docs/en/parts/category-bar.md +37 -0
- package/docs/en/parts/chart-frame.md +39 -0
- package/docs/en/parts/delta.md +4 -3
- package/docs/en/parts/donut-chart.md +72 -0
- package/docs/en/parts/funnel-steps.md +2 -1
- package/docs/en/parts/heatmap.md +53 -0
- package/docs/en/parts/kpi-card.md +2 -1
- package/docs/en/parts/legend.md +35 -0
- package/docs/en/parts/line-chart.md +94 -0
- package/docs/en/parts/progress-circle.md +33 -0
- package/docs/en/parts/sample-data.md +39 -0
- package/docs/en/parts/scatter-chart.md +56 -0
- package/docs/en/parts/tracker.md +36 -0
- package/docs/en/parts/updated-at.md +30 -0
- package/docs/en/parts/waterfall-chart.md +56 -0
- package/docs/en/slide/slide-chart-full.md +48 -0
- package/docs/en/slide/slide-chart-insight.md +52 -0
- package/docs/en/slide/slide-columns.md +46 -0
- package/docs/en/slide/slide-comparison.md +68 -0
- package/docs/en/slide/slide-cover.md +39 -0
- package/docs/en/slide/slide-exec-summary.md +47 -0
- package/docs/en/slide/slide-heading.md +38 -0
- package/docs/en/slide/slide-kpi-grid.md +55 -0
- package/docs/en/slide/slide-kpi-hero.md +58 -0
- package/docs/en/slide/slide-next-actions.md +49 -0
- package/docs/en/slide/slide-scorecard-table.md +54 -0
- package/docs/en/slide/slide-section.md +44 -0
- package/docs/en/slide/slide-source-line.md +34 -0
- package/docs/en/ui/alert-dialog.md +42 -0
- package/docs/en/ui/avatar.md +31 -0
- package/docs/en/ui/combobox.md +74 -0
- package/docs/en/ui/drawer.md +41 -0
- package/docs/en/ui/empty.md +1 -1
- package/docs/en/ui/error-state.md +4 -3
- package/docs/en/ui/field.md +42 -0
- package/docs/en/ui/hover-card.md +21 -0
- package/docs/en/ui/kbd.md +17 -0
- package/docs/en/ui/loading.md +1 -1
- package/docs/en/ui/pagination.md +36 -0
- package/docs/en/ui/radio-group.md +21 -0
- package/docs/en/ui/toast.md +44 -0
- package/docs/en/ui/toggle-group.md +4 -3
- package/docs/index.json +857 -77
- package/docs/ja/agent-skills.md +1 -1
- package/docs/ja/changelog.md +2 -0
- package/docs/ja/components.md +12 -1
- package/docs/ja/doc/delta-text.md +40 -0
- package/docs/ja/doc/doc-action-items.md +49 -0
- package/docs/ja/doc/doc-callout.md +38 -0
- package/docs/ja/doc/doc-data-table.md +60 -0
- package/docs/ja/doc/doc-executive-summary.md +64 -0
- package/docs/ja/doc/doc-figure.md +56 -0
- package/docs/ja/doc/doc-key-findings.md +61 -0
- package/docs/ja/doc/doc-kpi-scorecard.md +80 -0
- package/docs/ja/doc/doc-notes-and-sources.md +43 -0
- package/docs/ja/doc/doc-report-header.md +46 -0
- package/docs/ja/page-types/slide.md +1 -0
- package/docs/ja/pages-and-metadata.md +15 -0
- package/docs/ja/parts/area-chart.md +49 -25
- package/docs/ja/parts/bar-chart.md +50 -24
- package/docs/ja/parts/bar-list.md +31 -8
- package/docs/ja/parts/chart-frame.md +36 -0
- package/docs/ja/parts/delta.md +5 -4
- package/docs/ja/parts/donut-chart.md +45 -26
- package/docs/ja/parts/funnel-steps.md +27 -8
- package/docs/ja/parts/heatmap.md +45 -0
- package/docs/ja/parts/kpi-card.md +2 -1
- package/docs/ja/parts/legend.md +36 -0
- package/docs/ja/parts/line-chart.md +48 -22
- package/docs/ja/parts/sample-data.md +61 -0
- package/docs/ja/parts/scatter-chart.md +69 -0
- package/docs/ja/parts/updated-at.md +27 -0
- package/docs/ja/parts/waterfall-chart.md +61 -0
- package/docs/ja/slide/slide-chart-full.md +48 -0
- package/docs/ja/slide/slide-chart-insight.md +52 -0
- package/docs/ja/slide/slide-columns.md +46 -0
- package/docs/ja/slide/slide-comparison.md +68 -0
- package/docs/ja/slide/slide-cover.md +39 -0
- package/docs/ja/slide/slide-exec-summary.md +47 -0
- package/docs/ja/slide/slide-heading.md +38 -0
- package/docs/ja/slide/slide-kpi-grid.md +55 -0
- package/docs/ja/slide/slide-kpi-hero.md +58 -0
- package/docs/ja/slide/slide-next-actions.md +49 -0
- package/docs/ja/slide/slide-scorecard-table.md +54 -0
- package/docs/ja/slide/slide-section.md +44 -0
- package/docs/ja/slide/slide-source-line.md +34 -0
- package/docs/ja/ui/empty.md +1 -1
- package/docs/ja/ui/error-state.md +4 -3
- package/docs/ja/ui/loading.md +1 -1
- package/docs/ja/ui/toggle-group.md +4 -3
- package/package.json +2 -2
- package/registry/ui/app-shell.tsx +138 -1
- package/registry/ui/area-chart.tsx +389 -104
- package/registry/ui/attention-list.tsx +177 -15
- package/registry/ui/bar-chart.tsx +386 -104
- package/registry/ui/bar-list.tsx +376 -94
- package/registry/ui/box-plot-chart.tsx +450 -116
- package/registry/ui/category-bar.tsx +292 -57
- package/registry/ui/chart-frame.tsx +301 -63
- package/registry/ui/data-table.tsx +229 -37
- package/registry/ui/date-range-picker.tsx +190 -36
- package/registry/ui/delta.tsx +181 -19
- package/registry/ui/donut-chart.tsx +385 -103
- package/registry/ui/echart.tsx +313 -58
- package/registry/ui/filter-bar.tsx +179 -20
- package/registry/ui/funnel-steps.tsx +354 -80
- package/registry/ui/heatmap.tsx +338 -71
- package/registry/ui/histogram-chart.tsx +440 -116
- package/registry/ui/kpi-card.tsx +254 -48
- package/registry/ui/legend.tsx +374 -93
- package/registry/ui/line-chart.tsx +389 -104
- package/registry/ui/multi-select.tsx +159 -9
- package/registry/ui/page-shell.tsx +148 -10
- package/registry/ui/paired-chart.tsx +394 -109
- package/registry/ui/progress-circle.tsx +205 -39
- package/registry/ui/sample-data.tsx +154 -10
- package/registry/ui/scatter-chart.tsx +436 -119
- package/registry/ui/searchable-select.tsx +145 -7
- package/registry/ui/slider.tsx +1 -1
- package/registry/ui/states.tsx +170 -13
- package/registry/ui/status-badge.tsx +201 -36
- package/registry/ui/tooltip.tsx +6 -2
- package/registry/ui/tracker.tsx +214 -42
- package/registry/ui/trend-indicator.tsx +151 -8
- package/registry/ui/updated-at.tsx +170 -16
- package/registry/ui/waterfall-chart.tsx +460 -121
- package/skills/vantage-add-feature/SKILL.md +5 -0
- package/skills/vantage-design/SKILL.md +54 -54
- package/skills/vantage-design/references/extract.md +26 -27
- package/skills/vantage-design/references/format.md +12 -172
- package/skills/vantage-design/references/presets.md +50 -110
- package/skills/vantage-pitfalls/SKILL.md +5 -0
- package/skills/vantage-slide/SKILL.md +54 -36
- package/templates/AGENTS.md +35 -18
- package/templates/agents/api.md +7 -0
- package/templates/agents/doc.md +48 -0
- package/templates/agents/page.md +59 -2
- package/templates/agents/slide.md +77 -3
- package/theme.css +23 -19
- package/dist/chunk-2ICNHUG6.js.map +0 -1
- package/dist/chunk-2N72FKLB.js.map +0 -1
- package/dist/chunk-7DZGJQ3P.js.map +0 -1
- package/dist/chunk-CEXAQQ5W.js.map +0 -1
- package/dist/chunk-HKSVYMYJ.js.map +0 -1
- package/dist/chunk-JKLLFVEU.js.map +0 -1
- package/dist/chunk-PDOBHMUQ.js.map +0 -1
- package/dist/chunk-V3LF7BE7.js.map +0 -1
- package/dist/chunk-WAMZWZJB.js.map +0 -1
- package/dist/chunk-XPQSTZUU.js.map +0 -1
- package/dist/chunk-XQUSGIDG.js.map +0 -1
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* MultiSelect — a multi-value combobox that shows the selection as removable
|
|
7
|
-
* badges. Past `maxCount` badges the rest collapse into a "+N件" counter.
|
|
7
|
+
* badges. Past `maxCount` badges the rest collapse into a "+N more" (ja: "+N件") counter.
|
|
8
8
|
* Pass `onSearch` to take filtering over yourself (server-side search).
|
|
9
9
|
*/
|
|
10
10
|
import { forwardRef, useId, useMemo, useState } from "react"
|
|
@@ -14,11 +14,143 @@ import { cn } from "@squadbase/vantage/ui"
|
|
|
14
14
|
import { Badge } from "@squadbase/vantage/ui"
|
|
15
15
|
import { Popover, PopoverContent, PopoverTrigger } from "@squadbase/vantage/ui"
|
|
16
16
|
import { flattenOptions, type SelectOptions } from "@squadbase/vantage/components"
|
|
17
|
+
import { useSyncExternalStore } from "react"
|
|
17
18
|
import type { KeyboardEvent, MouseEvent } from "react"
|
|
18
19
|
import { CheckIcon, LoaderIcon, XIcon } from "lucide-react"
|
|
19
20
|
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@squadbase/vantage/ui"
|
|
20
21
|
import { isGroupedOptions, type SelectOption } from "@squadbase/vantage/components"
|
|
21
22
|
|
|
23
|
+
/* ---------- inlined from @squadbase/squadkit registry/lib/locale.ts (internal to @squadbase/vantage) ---------- */
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The display language of the parts and the defaults that follow it.
|
|
27
|
+
*
|
|
28
|
+
* The language comes from `<html lang>`: a tag starting with `ja` reads Japanese, anything else English.
|
|
29
|
+
* Without a usable `lang` (missing, invalid, or no `document` — SSR, Node) the locale is `FALLBACK_LOCALE`
|
|
30
|
+
* (`en-US`). A `locale` passed explicitly (a prop, an argument) always wins.
|
|
31
|
+
*
|
|
32
|
+
* Parts read the locale through `useLocale` (`hooks/use-locale`) and pass it down; the pure helpers here
|
|
33
|
+
* and in `format` / `comparison` / `chart-*` take it as an argument and fall back to `documentLocale()`.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/** The language of the built-in wording: Japanese or English. */
|
|
37
|
+
type UiLanguage = "ja" | "en"
|
|
38
|
+
|
|
39
|
+
/** The locale used when `<html lang>` gives none (and on the server). */
|
|
40
|
+
const FALLBACK_LOCALE = "en-US"
|
|
41
|
+
|
|
42
|
+
/** `"ja"` when `locale` starts with `ja` (`ja`, `ja-JP`), else `"en"`. A missing locale is `"en"`. */
|
|
43
|
+
function uiLanguage(locale: string | null | undefined): UiLanguage {
|
|
44
|
+
return typeof locale === "string" && locale.trim().toLowerCase().startsWith("ja") ? "ja" : "en"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** A BCP 47 tag in its canonical form (`en-gb` → `en-GB`), or `FALLBACK_LOCALE` when it is empty or invalid. */
|
|
48
|
+
function normalizeLocale(tag: string | null | undefined): string {
|
|
49
|
+
const value = tag?.trim()
|
|
50
|
+
if (!value) return FALLBACK_LOCALE
|
|
51
|
+
try {
|
|
52
|
+
return Intl.getCanonicalLocales(value)[0] ?? FALLBACK_LOCALE
|
|
53
|
+
} catch {
|
|
54
|
+
return FALLBACK_LOCALE
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The locale of the page: `document.documentElement.lang`, canonicalized. `FALLBACK_LOCALE` when it is empty or
|
|
60
|
+
* invalid, or there is no `document`. Parts read it through `useLocale`, never while rendering.
|
|
61
|
+
*/
|
|
62
|
+
function documentLocale(): string {
|
|
63
|
+
if (typeof document === "undefined") return FALLBACK_LOCALE
|
|
64
|
+
return normalizeLocale(document.documentElement?.getAttribute("lang"))
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** `locale` when given, else the page's locale (`documentLocale()`). */
|
|
68
|
+
function resolveLocale(locale?: string | null): string {
|
|
69
|
+
return locale || documentLocale()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Picks the wording table of the locale's language. */
|
|
73
|
+
function pickLocale<T>(table: { ja: T; en: T }, locale?: string | null): T {
|
|
74
|
+
return table[uiLanguage(resolveLocale(locale))]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** The currency `format="currency"` uses without a `currency`: JPY for Japanese, USD otherwise. */
|
|
78
|
+
function defaultCurrency(locale?: string | null): string {
|
|
79
|
+
return uiLanguage(resolveLocale(locale)) === "ja" ? "JPY" : "USD"
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** The month a fiscal year starts in (1–12) when none is given: April for Japanese, January otherwise. */
|
|
83
|
+
function defaultFiscalYearStartMonth(locale?: string | null): number {
|
|
84
|
+
return uiLanguage(resolveLocale(locale)) === "ja" ? 4 : 1
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** A day of the week, 0 = Sunday … 6 = Saturday (the `Date#getDay` / date-fns numbering). */
|
|
88
|
+
type WeekDay = 0 | 1 | 2 | 3 | 4 | 5 | 6
|
|
89
|
+
|
|
90
|
+
/** Locales whose week starts on Sunday when the runtime has no week data. */
|
|
91
|
+
const SUNDAY_FIRST = new Set(["en", "en-US", "en-CA"])
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The first day of the week: Monday for Japanese; otherwise the locale's convention from `Intl.Locale`
|
|
95
|
+
* (`en-US` Sunday, `en-GB` Monday). Without week data in the runtime: Sunday for `en` / `en-US` / `en-CA`,
|
|
96
|
+
* Monday for the rest.
|
|
97
|
+
*/
|
|
98
|
+
function weekStartsOn(locale?: string | null): WeekDay {
|
|
99
|
+
const tag = normalizeLocale(resolveLocale(locale))
|
|
100
|
+
if (uiLanguage(tag) === "ja") return 1
|
|
101
|
+
try {
|
|
102
|
+
const info = new Intl.Locale(tag) as Intl.Locale & {
|
|
103
|
+
getWeekInfo?: () => { firstDay: number }
|
|
104
|
+
weekInfo?: { firstDay: number }
|
|
105
|
+
}
|
|
106
|
+
const firstDay = info.getWeekInfo?.().firstDay ?? info.weekInfo?.firstDay
|
|
107
|
+
if (typeof firstDay === "number" && firstDay >= 1 && firstDay <= 7) return (firstDay % 7) as WeekDay
|
|
108
|
+
} catch {
|
|
109
|
+
// Fall through to the table below.
|
|
110
|
+
}
|
|
111
|
+
return SUNDAY_FIRST.has(tag) ? 0 : 1
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* ---------- inlined from @squadbase/squadkit registry/hooks/use-locale.ts (internal to @squadbase/vantage) ---------- */
|
|
115
|
+
|
|
116
|
+
const listeners = new Set<() => void>()
|
|
117
|
+
let observer: MutationObserver | null = null
|
|
118
|
+
|
|
119
|
+
/** One observer on `<html lang>` for every subscribed part. */
|
|
120
|
+
function subscribe(onChange: () => void): () => void {
|
|
121
|
+
if (typeof document === "undefined" || typeof MutationObserver === "undefined") return () => {}
|
|
122
|
+
listeners.add(onChange)
|
|
123
|
+
if (!observer) {
|
|
124
|
+
observer = new MutationObserver(() => {
|
|
125
|
+
for (const listener of [...listeners]) listener()
|
|
126
|
+
})
|
|
127
|
+
observer.observe(document.documentElement, { attributes: true, attributeFilter: ["lang"] })
|
|
128
|
+
}
|
|
129
|
+
return () => {
|
|
130
|
+
listeners.delete(onChange)
|
|
131
|
+
if (listeners.size === 0 && observer) {
|
|
132
|
+
observer.disconnect()
|
|
133
|
+
observer = null
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const serverLocale = () => FALLBACK_LOCALE
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* The page's locale (`<html lang>`), following changes made while the page runs. On the server and during
|
|
142
|
+
* hydration it is `FALLBACK_LOCALE` (`en-US`), so the markup matches; it switches right after.
|
|
143
|
+
*/
|
|
144
|
+
function useDocumentLocale(): string {
|
|
145
|
+
return useSyncExternalStore(subscribe, documentLocale, serverLocale)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** The locale a part renders in: its `locale` prop when given, else the page's (`useDocumentLocale`). */
|
|
149
|
+
function useLocale(locale?: string | null): string {
|
|
150
|
+
const page = useDocumentLocale()
|
|
151
|
+
return locale || page
|
|
152
|
+
}
|
|
153
|
+
|
|
22
154
|
/* ---------- inlined from @squadbase/squadkit registry/components/option-command.tsx (internal to @squadbase/vantage) ---------- */
|
|
23
155
|
|
|
24
156
|
interface OptionCommandListProps {
|
|
@@ -136,6 +268,23 @@ export interface MultiSelectProps {
|
|
|
136
268
|
className?: string
|
|
137
269
|
}
|
|
138
270
|
|
|
271
|
+
const TEXT = {
|
|
272
|
+
ja: {
|
|
273
|
+
placeholder: "選択してください",
|
|
274
|
+
search: "検索...",
|
|
275
|
+
empty: "見つかりませんでした",
|
|
276
|
+
remove: (label: string) => `${label}を削除`,
|
|
277
|
+
more: (count: number) => `+${count}件`,
|
|
278
|
+
},
|
|
279
|
+
en: {
|
|
280
|
+
placeholder: "Select options",
|
|
281
|
+
search: "Search...",
|
|
282
|
+
empty: "No results found.",
|
|
283
|
+
remove: (label: string) => `Remove ${label}`,
|
|
284
|
+
more: (count: number) => `+${count} more`,
|
|
285
|
+
},
|
|
286
|
+
}
|
|
287
|
+
|
|
139
288
|
/** Not given yet (a draft fills props in steps): render empty, keep the same value between renders. */
|
|
140
289
|
const NO_OPTIONS: never[] = []
|
|
141
290
|
const NO_VALUES: string[] = []
|
|
@@ -147,9 +296,9 @@ export const MultiSelect = forwardRef<HTMLDivElement, MultiSelectProps>(function
|
|
|
147
296
|
onChange,
|
|
148
297
|
onSearch,
|
|
149
298
|
maxCount = 3,
|
|
150
|
-
placeholder
|
|
151
|
-
searchPlaceholder
|
|
152
|
-
emptyText
|
|
299
|
+
placeholder,
|
|
300
|
+
searchPlaceholder,
|
|
301
|
+
emptyText,
|
|
153
302
|
isLoading = false,
|
|
154
303
|
disabled = false,
|
|
155
304
|
className,
|
|
@@ -158,6 +307,7 @@ export const MultiSelect = forwardRef<HTMLDivElement, MultiSelectProps>(function
|
|
|
158
307
|
) {
|
|
159
308
|
const [open, setOpen] = useState(false)
|
|
160
309
|
const listId = useId()
|
|
310
|
+
const t = TEXT[uiLanguage(useLocale())]
|
|
161
311
|
|
|
162
312
|
const allOptions = useMemo(() => flattenOptions(options), [options])
|
|
163
313
|
const selectedOptions = useMemo(
|
|
@@ -191,14 +341,14 @@ export const MultiSelect = forwardRef<HTMLDivElement, MultiSelectProps>(function
|
|
|
191
341
|
>
|
|
192
342
|
<span className="flex min-w-0 flex-1 flex-wrap items-center gap-1">
|
|
193
343
|
{selectedOptions.length === 0 ? (
|
|
194
|
-
<span className="text-muted-foreground">{placeholder}</span>
|
|
344
|
+
<span className="text-muted-foreground">{placeholder ?? t.placeholder}</span>
|
|
195
345
|
) : (
|
|
196
346
|
<>
|
|
197
347
|
{visibleBadges.map((option) => (
|
|
198
348
|
<Badge key={option.value} variant="secondary" className="shrink-0 gap-1 pr-1">
|
|
199
349
|
<span className="max-w-[120px] truncate">{option.label}</span>
|
|
200
350
|
<RemoveButton
|
|
201
|
-
label={
|
|
351
|
+
label={t.remove(option.label)}
|
|
202
352
|
onRemove={() => onChange?.(value.filter((v) => v !== option.value))}
|
|
203
353
|
iconClassName="size-3"
|
|
204
354
|
/>
|
|
@@ -206,7 +356,7 @@ export const MultiSelect = forwardRef<HTMLDivElement, MultiSelectProps>(function
|
|
|
206
356
|
))}
|
|
207
357
|
{overflowCount > 0 && (
|
|
208
358
|
<Badge variant="secondary" className="shrink-0">
|
|
209
|
-
|
|
359
|
+
{t.more(overflowCount)}
|
|
210
360
|
</Badge>
|
|
211
361
|
)}
|
|
212
362
|
</>
|
|
@@ -221,8 +371,8 @@ export const MultiSelect = forwardRef<HTMLDivElement, MultiSelectProps>(function
|
|
|
221
371
|
onSelect={handleSelect}
|
|
222
372
|
onSearch={onSearch}
|
|
223
373
|
isLoading={isLoading}
|
|
224
|
-
searchPlaceholder={searchPlaceholder}
|
|
225
|
-
emptyText={emptyText}
|
|
374
|
+
searchPlaceholder={searchPlaceholder ?? t.search}
|
|
375
|
+
emptyText={emptyText ?? t.empty}
|
|
226
376
|
/>
|
|
227
377
|
</PopoverContent>
|
|
228
378
|
</Popover>
|
|
@@ -14,6 +14,140 @@ import { formatDateRange, EMPTY_VALUE } from "@squadbase/vantage/components"
|
|
|
14
14
|
import { cn } from "@squadbase/vantage/ui"
|
|
15
15
|
import { Badge } from "@squadbase/vantage/ui"
|
|
16
16
|
import { UpdatedAt, type UpdatedAtProps } from "@squadbase/vantage/components"
|
|
17
|
+
import { useSyncExternalStore } from "react"
|
|
18
|
+
|
|
19
|
+
/* ---------- inlined from @squadbase/squadkit registry/lib/locale.ts (internal to @squadbase/vantage) ---------- */
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The display language of the parts and the defaults that follow it.
|
|
23
|
+
*
|
|
24
|
+
* The language comes from `<html lang>`: a tag starting with `ja` reads Japanese, anything else English.
|
|
25
|
+
* Without a usable `lang` (missing, invalid, or no `document` — SSR, Node) the locale is `FALLBACK_LOCALE`
|
|
26
|
+
* (`en-US`). A `locale` passed explicitly (a prop, an argument) always wins.
|
|
27
|
+
*
|
|
28
|
+
* Parts read the locale through `useLocale` (`hooks/use-locale`) and pass it down; the pure helpers here
|
|
29
|
+
* and in `format` / `comparison` / `chart-*` take it as an argument and fall back to `documentLocale()`.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/** The language of the built-in wording: Japanese or English. */
|
|
33
|
+
type UiLanguage = "ja" | "en"
|
|
34
|
+
|
|
35
|
+
/** The locale used when `<html lang>` gives none (and on the server). */
|
|
36
|
+
const FALLBACK_LOCALE = "en-US"
|
|
37
|
+
|
|
38
|
+
/** `"ja"` when `locale` starts with `ja` (`ja`, `ja-JP`), else `"en"`. A missing locale is `"en"`. */
|
|
39
|
+
function uiLanguage(locale: string | null | undefined): UiLanguage {
|
|
40
|
+
return typeof locale === "string" && locale.trim().toLowerCase().startsWith("ja") ? "ja" : "en"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** A BCP 47 tag in its canonical form (`en-gb` → `en-GB`), or `FALLBACK_LOCALE` when it is empty or invalid. */
|
|
44
|
+
function normalizeLocale(tag: string | null | undefined): string {
|
|
45
|
+
const value = tag?.trim()
|
|
46
|
+
if (!value) return FALLBACK_LOCALE
|
|
47
|
+
try {
|
|
48
|
+
return Intl.getCanonicalLocales(value)[0] ?? FALLBACK_LOCALE
|
|
49
|
+
} catch {
|
|
50
|
+
return FALLBACK_LOCALE
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The locale of the page: `document.documentElement.lang`, canonicalized. `FALLBACK_LOCALE` when it is empty or
|
|
56
|
+
* invalid, or there is no `document`. Parts read it through `useLocale`, never while rendering.
|
|
57
|
+
*/
|
|
58
|
+
function documentLocale(): string {
|
|
59
|
+
if (typeof document === "undefined") return FALLBACK_LOCALE
|
|
60
|
+
return normalizeLocale(document.documentElement?.getAttribute("lang"))
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** `locale` when given, else the page's locale (`documentLocale()`). */
|
|
64
|
+
function resolveLocale(locale?: string | null): string {
|
|
65
|
+
return locale || documentLocale()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Picks the wording table of the locale's language. */
|
|
69
|
+
function pickLocale<T>(table: { ja: T; en: T }, locale?: string | null): T {
|
|
70
|
+
return table[uiLanguage(resolveLocale(locale))]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** The currency `format="currency"` uses without a `currency`: JPY for Japanese, USD otherwise. */
|
|
74
|
+
function defaultCurrency(locale?: string | null): string {
|
|
75
|
+
return uiLanguage(resolveLocale(locale)) === "ja" ? "JPY" : "USD"
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** The month a fiscal year starts in (1–12) when none is given: April for Japanese, January otherwise. */
|
|
79
|
+
function defaultFiscalYearStartMonth(locale?: string | null): number {
|
|
80
|
+
return uiLanguage(resolveLocale(locale)) === "ja" ? 4 : 1
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** A day of the week, 0 = Sunday … 6 = Saturday (the `Date#getDay` / date-fns numbering). */
|
|
84
|
+
type WeekDay = 0 | 1 | 2 | 3 | 4 | 5 | 6
|
|
85
|
+
|
|
86
|
+
/** Locales whose week starts on Sunday when the runtime has no week data. */
|
|
87
|
+
const SUNDAY_FIRST = new Set(["en", "en-US", "en-CA"])
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The first day of the week: Monday for Japanese; otherwise the locale's convention from `Intl.Locale`
|
|
91
|
+
* (`en-US` Sunday, `en-GB` Monday). Without week data in the runtime: Sunday for `en` / `en-US` / `en-CA`,
|
|
92
|
+
* Monday for the rest.
|
|
93
|
+
*/
|
|
94
|
+
function weekStartsOn(locale?: string | null): WeekDay {
|
|
95
|
+
const tag = normalizeLocale(resolveLocale(locale))
|
|
96
|
+
if (uiLanguage(tag) === "ja") return 1
|
|
97
|
+
try {
|
|
98
|
+
const info = new Intl.Locale(tag) as Intl.Locale & {
|
|
99
|
+
getWeekInfo?: () => { firstDay: number }
|
|
100
|
+
weekInfo?: { firstDay: number }
|
|
101
|
+
}
|
|
102
|
+
const firstDay = info.getWeekInfo?.().firstDay ?? info.weekInfo?.firstDay
|
|
103
|
+
if (typeof firstDay === "number" && firstDay >= 1 && firstDay <= 7) return (firstDay % 7) as WeekDay
|
|
104
|
+
} catch {
|
|
105
|
+
// Fall through to the table below.
|
|
106
|
+
}
|
|
107
|
+
return SUNDAY_FIRST.has(tag) ? 0 : 1
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/* ---------- inlined from @squadbase/squadkit registry/hooks/use-locale.ts (internal to @squadbase/vantage) ---------- */
|
|
111
|
+
|
|
112
|
+
const listeners = new Set<() => void>()
|
|
113
|
+
let observer: MutationObserver | null = null
|
|
114
|
+
|
|
115
|
+
/** One observer on `<html lang>` for every subscribed part. */
|
|
116
|
+
function subscribe(onChange: () => void): () => void {
|
|
117
|
+
if (typeof document === "undefined" || typeof MutationObserver === "undefined") return () => {}
|
|
118
|
+
listeners.add(onChange)
|
|
119
|
+
if (!observer) {
|
|
120
|
+
observer = new MutationObserver(() => {
|
|
121
|
+
for (const listener of [...listeners]) listener()
|
|
122
|
+
})
|
|
123
|
+
observer.observe(document.documentElement, { attributes: true, attributeFilter: ["lang"] })
|
|
124
|
+
}
|
|
125
|
+
return () => {
|
|
126
|
+
listeners.delete(onChange)
|
|
127
|
+
if (listeners.size === 0 && observer) {
|
|
128
|
+
observer.disconnect()
|
|
129
|
+
observer = null
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const serverLocale = () => FALLBACK_LOCALE
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* The page's locale (`<html lang>`), following changes made while the page runs. On the server and during
|
|
138
|
+
* hydration it is `FALLBACK_LOCALE` (`en-US`), so the markup matches; it switches right after.
|
|
139
|
+
*/
|
|
140
|
+
function useDocumentLocale(): string {
|
|
141
|
+
return useSyncExternalStore(subscribe, documentLocale, serverLocale)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** The locale a part renders in: its `locale` prop when given, else the page's (`useDocumentLocale`). */
|
|
145
|
+
function useLocale(locale?: string | null): string {
|
|
146
|
+
const page = useDocumentLocale()
|
|
147
|
+
return locale || page
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* ---------- end of inlined code ---------- */
|
|
17
151
|
|
|
18
152
|
export interface PageShellProps extends HTMLAttributes<HTMLDivElement> {}
|
|
19
153
|
|
|
@@ -24,6 +158,8 @@ export const PageShell = forwardRef<HTMLDivElement, PageShellProps>(
|
|
|
24
158
|
)
|
|
25
159
|
PageShell.displayName = "PageShell"
|
|
26
160
|
|
|
161
|
+
const SOURCE_PREFIX = { ja: "出典:", en: "Source: " }
|
|
162
|
+
|
|
27
163
|
export interface PageShellHeaderProps extends HTMLAttributes<HTMLDivElement> {
|
|
28
164
|
/** When the data was last updated. Drawn by `UpdatedAt` at the right end of the title row. */
|
|
29
165
|
updatedAt?: UpdatedAtProps["at"]
|
|
@@ -31,7 +167,7 @@ export interface PageShellHeaderProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
31
167
|
updatedAtVariant?: UpdatedAtProps["variant"]
|
|
32
168
|
/** IANA time zone for the absolute time of `updatedAt`. Defaults to the runtime's zone. */
|
|
33
169
|
timeZone?: string
|
|
34
|
-
/** Where the data comes from. Shown as a chip "出典:○○" on the second row. */
|
|
170
|
+
/** Where the data comes from. Shown as a chip "Source: ○○" (ja: "出典:○○") on the second row. */
|
|
35
171
|
source?: string
|
|
36
172
|
/** The period the data covers. Shown as a chip "2026/9/23〜10/20" on the second row. */
|
|
37
173
|
period?: { start: Date | string | number; end: Date | string | number }
|
|
@@ -53,7 +189,8 @@ export interface PageShellHeaderProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
53
189
|
*/
|
|
54
190
|
export const PageShellHeader = forwardRef<HTMLDivElement, PageShellHeaderProps>(
|
|
55
191
|
({ className, updatedAt, updatedAtVariant, timeZone, source, period, status, children, ...props }, ref) => {
|
|
56
|
-
const
|
|
192
|
+
const locale = useLocale()
|
|
193
|
+
const periodText = period ? formatDateRange(period.start, period.end, { timeZone, locale }) : undefined
|
|
57
194
|
const hasChips = Boolean(source) || (periodText !== undefined && periodText !== EMPTY_VALUE)
|
|
58
195
|
const hasStatus = status !== undefined && status !== null && status !== false && status !== ""
|
|
59
196
|
return (
|
|
@@ -72,12 +209,13 @@ export const PageShellHeader = forwardRef<HTMLDivElement, PageShellHeaderProps>(
|
|
|
72
209
|
{hasChips ? (
|
|
73
210
|
<div data-slot="page-shell-chips" className="order-1 flex basis-full flex-wrap gap-1.5">
|
|
74
211
|
{source ? (
|
|
75
|
-
<Badge variant="outline" className="h-auto max-w-full whitespace-normal">
|
|
76
|
-
|
|
212
|
+
<Badge variant="outline" className="h-auto max-w-full whitespace-normal text-muted-foreground">
|
|
213
|
+
{SOURCE_PREFIX[uiLanguage(locale)]}
|
|
214
|
+
{source}
|
|
77
215
|
</Badge>
|
|
78
216
|
) : null}
|
|
79
217
|
{periodText && periodText !== EMPTY_VALUE ? (
|
|
80
|
-
<Badge variant="outline" className="tabular-nums">
|
|
218
|
+
<Badge variant="outline" className="tabular-nums text-muted-foreground">
|
|
81
219
|
{periodText}
|
|
82
220
|
</Badge>
|
|
83
221
|
) : null}
|
|
@@ -204,11 +342,11 @@ PageShellSummary.displayName = "PageShellSummary"
|
|
|
204
342
|
* emits classes it can see literally in the source.
|
|
205
343
|
*/
|
|
206
344
|
const summaryCardTones = {
|
|
207
|
-
positive: "border-positive/
|
|
208
|
-
negative: "border-negative/
|
|
209
|
-
warning: "border-warning/
|
|
210
|
-
info: "border-info/
|
|
211
|
-
neutral: "border-neutral/
|
|
345
|
+
positive: "border-positive/20 dark:border-positive/25 bg-positive-subtle [&>svg]:text-positive",
|
|
346
|
+
negative: "border-negative/20 dark:border-negative/25 bg-negative-subtle [&>svg]:text-negative",
|
|
347
|
+
warning: "border-warning/20 dark:border-warning/25 bg-warning-subtle [&>svg]:text-warning",
|
|
348
|
+
info: "border-info/20 dark:border-info/25 bg-info-subtle [&>svg]:text-info",
|
|
349
|
+
neutral: "border-neutral/20 dark:border-neutral/25 bg-neutral-subtle [&>svg]:text-neutral",
|
|
212
350
|
violet: "border-chart-4/30 bg-chart-4/5 [&>svg]:text-chart-4",
|
|
213
351
|
pink: "border-chart-5/30 bg-chart-5/5 [&>svg]:text-chart-5",
|
|
214
352
|
} as const
|