@squadbase/vantage 0.6.2-dev1.d7c0911 → 0.6.2-dev1.e85faec

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 (221) hide show
  1. package/dist/chunk-2IAPIOPS.js +152 -0
  2. package/dist/chunk-2IAPIOPS.js.map +1 -0
  3. package/dist/chunk-57GHR5LN.js +163 -0
  4. package/dist/chunk-57GHR5LN.js.map +1 -0
  5. package/dist/chunk-6OTTGAPO.js +44 -0
  6. package/dist/chunk-6OTTGAPO.js.map +1 -0
  7. package/dist/{chunk-XPQSTZUU.js → chunk-7S52M2SL.js} +4 -3
  8. package/dist/chunk-7S52M2SL.js.map +1 -0
  9. package/dist/chunk-JXFCSF3K.js +673 -0
  10. package/dist/chunk-JXFCSF3K.js.map +1 -0
  11. package/dist/{chunk-WRSX2MDK.js → chunk-QOXXOFGL.js} +115 -930
  12. package/dist/chunk-QOXXOFGL.js.map +1 -0
  13. package/dist/components/index.d.ts +13 -249
  14. package/dist/components/index.js +5 -2
  15. package/dist/delta-text-CvwBGJ95.d.ts +15 -0
  16. package/dist/doc/index.d.ts +215 -2
  17. package/dist/doc/index.js +300 -3
  18. package/dist/doc/index.js.map +1 -1
  19. package/dist/format-U0cQl069.d.ts +243 -0
  20. package/dist/slide/index.d.ts +272 -1
  21. package/dist/slide/index.js +968 -3
  22. package/dist/slide/index.js.map +1 -1
  23. package/dist/table/index.js +6 -3
  24. package/dist/table/index.js.map +1 -1
  25. package/dist/ui/index.d.ts +4 -1
  26. package/dist/ui/index.js +3 -3
  27. package/dist/ui/index.js.map +1 -1
  28. package/docs/en/components.md +13 -2
  29. package/docs/en/doc/delta-text.md +40 -0
  30. package/docs/en/doc/doc-action-items.md +49 -0
  31. package/docs/en/doc/doc-callout.md +38 -0
  32. package/docs/en/doc/doc-data-table.md +60 -0
  33. package/docs/en/doc/doc-executive-summary.md +64 -0
  34. package/docs/en/doc/doc-figure.md +56 -0
  35. package/docs/en/doc/doc-key-findings.md +61 -0
  36. package/docs/en/doc/doc-kpi-scorecard.md +80 -0
  37. package/docs/en/doc/doc-notes-and-sources.md +43 -0
  38. package/docs/en/doc/doc-report-header.md +46 -0
  39. package/docs/en/parts/area-chart.md +99 -0
  40. package/docs/en/parts/bar-chart.md +91 -0
  41. package/docs/en/parts/bar-list.md +50 -0
  42. package/docs/en/parts/category-bar.md +37 -0
  43. package/docs/en/parts/chart-frame.md +39 -0
  44. package/docs/en/parts/delta.md +4 -3
  45. package/docs/en/parts/donut-chart.md +72 -0
  46. package/docs/en/parts/funnel-steps.md +2 -1
  47. package/docs/en/parts/heatmap.md +53 -0
  48. package/docs/en/parts/kpi-card.md +2 -1
  49. package/docs/en/parts/legend.md +35 -0
  50. package/docs/en/parts/line-chart.md +94 -0
  51. package/docs/en/parts/progress-circle.md +33 -0
  52. package/docs/en/parts/sample-data.md +39 -0
  53. package/docs/en/parts/scatter-chart.md +56 -0
  54. package/docs/en/parts/tracker.md +36 -0
  55. package/docs/en/parts/updated-at.md +30 -0
  56. package/docs/en/parts/waterfall-chart.md +56 -0
  57. package/docs/en/slide/slide-chart-full.md +48 -0
  58. package/docs/en/slide/slide-chart-insight.md +52 -0
  59. package/docs/en/slide/slide-columns.md +46 -0
  60. package/docs/en/slide/slide-comparison.md +68 -0
  61. package/docs/en/slide/slide-cover.md +39 -0
  62. package/docs/en/slide/slide-exec-summary.md +47 -0
  63. package/docs/en/slide/slide-heading.md +38 -0
  64. package/docs/en/slide/slide-kpi-grid.md +55 -0
  65. package/docs/en/slide/slide-kpi-hero.md +58 -0
  66. package/docs/en/slide/slide-next-actions.md +49 -0
  67. package/docs/en/slide/slide-scorecard-table.md +54 -0
  68. package/docs/en/slide/slide-section.md +44 -0
  69. package/docs/en/slide/slide-source-line.md +34 -0
  70. package/docs/en/ui/alert-dialog.md +42 -0
  71. package/docs/en/ui/avatar.md +31 -0
  72. package/docs/en/ui/combobox.md +74 -0
  73. package/docs/en/ui/drawer.md +41 -0
  74. package/docs/en/ui/empty.md +1 -1
  75. package/docs/en/ui/error-state.md +4 -3
  76. package/docs/en/ui/field.md +42 -0
  77. package/docs/en/ui/hover-card.md +21 -0
  78. package/docs/en/ui/kbd.md +17 -0
  79. package/docs/en/ui/loading.md +1 -1
  80. package/docs/en/ui/pagination.md +36 -0
  81. package/docs/en/ui/radio-group.md +21 -0
  82. package/docs/en/ui/toast.md +44 -0
  83. package/docs/en/ui/toggle-group.md +4 -3
  84. package/docs/index.json +857 -77
  85. package/docs/ja/components.md +12 -1
  86. package/docs/ja/doc/delta-text.md +40 -0
  87. package/docs/ja/doc/doc-action-items.md +49 -0
  88. package/docs/ja/doc/doc-callout.md +38 -0
  89. package/docs/ja/doc/doc-data-table.md +60 -0
  90. package/docs/ja/doc/doc-executive-summary.md +64 -0
  91. package/docs/ja/doc/doc-figure.md +56 -0
  92. package/docs/ja/doc/doc-key-findings.md +61 -0
  93. package/docs/ja/doc/doc-kpi-scorecard.md +80 -0
  94. package/docs/ja/doc/doc-notes-and-sources.md +43 -0
  95. package/docs/ja/doc/doc-report-header.md +46 -0
  96. package/docs/ja/parts/area-chart.md +49 -25
  97. package/docs/ja/parts/bar-chart.md +50 -24
  98. package/docs/ja/parts/bar-list.md +31 -8
  99. package/docs/ja/parts/chart-frame.md +36 -0
  100. package/docs/ja/parts/delta.md +5 -4
  101. package/docs/ja/parts/donut-chart.md +45 -26
  102. package/docs/ja/parts/funnel-steps.md +27 -8
  103. package/docs/ja/parts/heatmap.md +45 -0
  104. package/docs/ja/parts/kpi-card.md +2 -1
  105. package/docs/ja/parts/legend.md +36 -0
  106. package/docs/ja/parts/line-chart.md +48 -22
  107. package/docs/ja/parts/sample-data.md +61 -0
  108. package/docs/ja/parts/scatter-chart.md +69 -0
  109. package/docs/ja/parts/updated-at.md +27 -0
  110. package/docs/ja/parts/waterfall-chart.md +61 -0
  111. package/docs/ja/slide/slide-chart-full.md +48 -0
  112. package/docs/ja/slide/slide-chart-insight.md +52 -0
  113. package/docs/ja/slide/slide-columns.md +46 -0
  114. package/docs/ja/slide/slide-comparison.md +68 -0
  115. package/docs/ja/slide/slide-cover.md +39 -0
  116. package/docs/ja/slide/slide-exec-summary.md +47 -0
  117. package/docs/ja/slide/slide-heading.md +38 -0
  118. package/docs/ja/slide/slide-kpi-grid.md +55 -0
  119. package/docs/ja/slide/slide-kpi-hero.md +58 -0
  120. package/docs/ja/slide/slide-next-actions.md +49 -0
  121. package/docs/ja/slide/slide-scorecard-table.md +54 -0
  122. package/docs/ja/slide/slide-section.md +44 -0
  123. package/docs/ja/slide/slide-source-line.md +34 -0
  124. package/docs/ja/ui/empty.md +1 -1
  125. package/docs/ja/ui/error-state.md +4 -3
  126. package/docs/ja/ui/loading.md +1 -1
  127. package/docs/ja/ui/toggle-group.md +4 -3
  128. package/package.json +26 -25
  129. package/registry/ui/accordion.tsx +1 -1
  130. package/registry/ui/alert-dialog.tsx +1 -1
  131. package/registry/ui/alert.tsx +1 -1
  132. package/registry/ui/app-shell.tsx +1 -1
  133. package/registry/ui/area-chart.tsx +89 -27
  134. package/registry/ui/attention-list.tsx +303 -0
  135. package/registry/ui/avatar.tsx +1 -1
  136. package/registry/ui/badge.tsx +1 -1
  137. package/registry/ui/bar-chart.tsx +89 -27
  138. package/registry/ui/bar-list.tsx +88 -25
  139. package/registry/ui/box-plot-chart.tsx +3906 -0
  140. package/registry/ui/breadcrumb.tsx +1 -1
  141. package/registry/ui/button.tsx +1 -1
  142. package/registry/ui/calendar.tsx +1 -1
  143. package/registry/ui/card.tsx +1 -1
  144. package/registry/ui/category-bar.tsx +55 -20
  145. package/registry/ui/chart-frame.tsx +57 -22
  146. package/registry/ui/checkbox.tsx +1 -1
  147. package/registry/ui/collapsible.tsx +1 -1
  148. package/registry/ui/combobox.tsx +1 -1
  149. package/registry/ui/command.tsx +1 -1
  150. package/registry/ui/dashboard-card.tsx +5 -5
  151. package/registry/ui/data-table.tsx +1 -1
  152. package/registry/ui/date-range-picker.tsx +1 -1
  153. package/registry/ui/delta.tsx +12 -9
  154. package/registry/ui/dialog.tsx +1 -1
  155. package/registry/ui/donut-chart.tsx +90 -27
  156. package/registry/ui/drawer.tsx +1 -1
  157. package/registry/ui/dropdown-menu.tsx +1 -1
  158. package/registry/ui/echart.tsx +195 -45
  159. package/registry/ui/field.tsx +1 -1
  160. package/registry/ui/filter-bar.tsx +1 -1
  161. package/registry/ui/funnel-steps.tsx +34 -6
  162. package/registry/ui/heatmap.tsx +56 -22
  163. package/registry/ui/histogram-chart.tsx +3986 -0
  164. package/registry/ui/hover-card.tsx +1 -1
  165. package/registry/ui/input-group.tsx +1 -1
  166. package/registry/ui/input.tsx +1 -1
  167. package/registry/ui/kbd.tsx +1 -1
  168. package/registry/ui/kpi-card.tsx +42 -15
  169. package/registry/ui/label.tsx +1 -1
  170. package/registry/ui/legend.tsx +88 -25
  171. package/registry/ui/line-chart.tsx +89 -27
  172. package/registry/ui/metric-value.tsx +1 -1
  173. package/registry/ui/multi-select.tsx +1 -1
  174. package/registry/ui/page-shell.tsx +11 -11
  175. package/registry/ui/pagination.tsx +1 -1
  176. package/registry/ui/paired-chart.tsx +3827 -0
  177. package/registry/ui/placeholder.tsx +1 -1
  178. package/registry/ui/popover.tsx +1 -1
  179. package/registry/ui/progress-circle.tsx +1 -1
  180. package/registry/ui/progress.tsx +1 -1
  181. package/registry/ui/radio-group.tsx +1 -1
  182. package/registry/ui/sample-data.tsx +2 -2
  183. package/registry/ui/scatter-chart.tsx +89 -27
  184. package/registry/ui/scroll-area.tsx +1 -1
  185. package/registry/ui/searchable-select.tsx +1 -1
  186. package/registry/ui/section-header.tsx +3 -3
  187. package/registry/ui/segmented-control.tsx +1 -1
  188. package/registry/ui/select.tsx +1 -1
  189. package/registry/ui/separator.tsx +1 -1
  190. package/registry/ui/sheet.tsx +1 -1
  191. package/registry/ui/sidebar.tsx +1 -1
  192. package/registry/ui/skeleton.tsx +1 -1
  193. package/registry/ui/slider.tsx +2 -2
  194. package/registry/ui/sparkline.tsx +1 -1
  195. package/registry/ui/spinner.tsx +1 -1
  196. package/registry/ui/states.tsx +1 -1
  197. package/registry/ui/status-badge.tsx +9 -9
  198. package/registry/ui/switch.tsx +1 -1
  199. package/registry/ui/table.tsx +1 -1
  200. package/registry/ui/tabs.tsx +1 -1
  201. package/registry/ui/textarea.tsx +1 -1
  202. package/registry/ui/toast.tsx +1 -1
  203. package/registry/ui/toggle-group.tsx +1 -1
  204. package/registry/ui/toggle.tsx +1 -1
  205. package/registry/ui/tooltip.tsx +7 -3
  206. package/registry/ui/tracker.tsx +10 -4
  207. package/registry/ui/trend-indicator.tsx +3 -3
  208. package/registry/ui/updated-at.tsx +1 -1
  209. package/registry/ui/waterfall-chart.tsx +89 -27
  210. package/skills/vantage-design/SKILL.md +34 -24
  211. package/skills/vantage-design/references/extract.md +16 -14
  212. package/skills/vantage-design/references/format.md +6 -5
  213. package/skills/vantage-design/references/presets.md +60 -110
  214. package/skills/vantage-slide/SKILL.md +54 -36
  215. package/templates/AGENTS.md +2 -2
  216. package/templates/agents/doc.md +48 -0
  217. package/templates/agents/page.md +32 -1
  218. package/templates/agents/slide.md +70 -2
  219. package/theme.css +25 -20
  220. package/dist/chunk-WRSX2MDK.js.map +0 -1
  221. package/dist/chunk-XPQSTZUU.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  // Copied by `vantage add ui line-chart`. This file is yours to edit.
2
- // Generated from @squadbase/squadkit registry/charts/line-chart.tsx by `npm run registry` (in Vantage, edit packages/squadkit, not registry/).
2
+ // Generated from @squadbase/squadkit registry/charts/line-chart.tsx by `pnpm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
3
  "use client"
4
4
 
5
5
  /**
@@ -16,7 +16,7 @@ import { forwardRef, useMemo, type ForwardedRef, type ReactElement, type Ref } f
16
16
  import { ChartFrame } from "@squadbase/vantage/components"
17
17
  import { EChart } from "@squadbase/vantage/components"
18
18
  import type { ChartComparison, ChartForecast, NumericKeys } from "@squadbase/vantage/components"
19
- import { createContext, useCallback, useEffect, useRef, useState } from "react"
19
+ import { createContext, useCallback, useEffect, useRef, useState, type RefObject } from "react"
20
20
  import type { ChartTone } from "@squadbase/vantage/components"
21
21
  import { DEFAULT_LOCALE, EMPTY_VALUE, formatValue, toValueFormatter, type ValueFormat } from "@squadbase/vantage/components"
22
22
  import type { ChartColor, ChartColors, ChartSeriesColor, ValueFormatter } from "@squadbase/vantage/components"
@@ -49,6 +49,8 @@ const SERIES_TOKENS = ["--chart-1", "--chart-2", "--chart-3", "--chart-4", "--ch
49
49
  interface ChartTokens {
50
50
  /** Resolved `--chart-1..5`, in order. */
51
51
  series: string[]
52
+ /** Computed `font-family` of the container, so canvas text matches the DOM. Optional. */
53
+ fontFamily?: string
52
54
  /** Body text: titles and the default `textStyle`. */
53
55
  foreground: string | null
54
56
  /** Secondary text: axis labels, legend, subtitles. */
@@ -57,10 +59,23 @@ interface ChartTokens {
57
59
  border: string | null
58
60
  /** Tooltip surface. */
59
61
  popover: string | null
62
+ /** Semantic and surface dependencies used by typed chart options. Optional for hand-built tokens. */
63
+ colors?: Record<string, string>
64
+ /** Actual inherited painting surface (may change without a token changing). */
65
+ surface?: string | null
60
66
  /** Tooltip text. */
61
67
  popoverForeground: string | null
62
68
  }
63
69
 
70
+ // Keep scope out of the public token shape. A resolved snapshot remembers where arbitrary
71
+ // var(--token) expressions must be read, without retaining unmounted charts globally.
72
+ const tokenScopes = new WeakMap<ChartTokens, Element>()
73
+ const OPTION_COLOR_TOKENS = [
74
+ "--background", "--card", "--muted", "--primary", "--destructive",
75
+ ...["positive", "negative", "warning", "neutral", "info"].flatMap((tone) =>
76
+ ["", "-fill", "-subtle"].map((suffix) => `--${tone}${suffix}`)),
77
+ ]
78
+
64
79
  let normalizeContext: CanvasRenderingContext2D | null | undefined
65
80
 
66
81
  /** The forms `fillStyle` serializes legacy colours to, and zrender can parse. */
@@ -116,14 +131,19 @@ function readChartTokens(el?: Element | null): ChartTokens | null {
116
131
  const color = read(token)
117
132
  if (color) series.push(color)
118
133
  }
119
- return {
134
+ const tokens: ChartTokens = {
120
135
  series,
136
+ colors: Object.fromEntries(OPTION_COLOR_TOKENS.map((name) => [name, styles.getPropertyValue(name).trim()])),
137
+ surface: readChartSurface(target),
138
+ ...(styles.fontFamily ? { fontFamily: styles.fontFamily } : {}),
121
139
  foreground: read("--foreground"),
122
140
  mutedForeground: read("--muted-foreground"),
123
141
  border: read("--border"),
124
142
  popover: read("--popover"),
125
143
  popoverForeground: read("--popover-foreground"),
126
144
  }
145
+ tokenScopes.set(tokens, target)
146
+ return tokens
127
147
  }
128
148
 
129
149
  /** Structural equality, so a theme toggle that changes nothing doesn't re-init. */
@@ -131,8 +151,13 @@ function sameChartTokens(a: ChartTokens | null, b: ChartTokens | null): boolean
131
151
  if (a === b) return true
132
152
  if (!a || !b) return false
133
153
  return (
154
+ tokenScopes.get(a) === tokenScopes.get(b) &&
155
+ a.surface === b.surface &&
156
+ Object.keys(a.colors ?? {}).length === Object.keys(b.colors ?? {}).length &&
157
+ Object.entries(a.colors ?? {}).every(([name, value]) => b.colors?.[name] === value) &&
134
158
  a.series.length === b.series.length &&
135
159
  a.series.every((color, i) => color === b.series[i]) &&
160
+ a.fontFamily === b.fontFamily &&
136
161
  a.foreground === b.foreground &&
137
162
  a.mutedForeground === b.mutedForeground &&
138
163
  a.border === b.border &&
@@ -155,7 +180,7 @@ function sameChartTokens(a: ChartTokens | null, b: ChartTokens | null): boolean
155
180
  * attributes alone leaves the chart painted in the previous palette until a
156
181
  * reload — the tokens in the DOM are new, the canvas is not.
157
182
  */
158
- function watchChartTokens(onChange: () => void): () => void {
183
+ function watchChartTokens(onChange: () => void, scope?: Element | null): () => void {
159
184
  if (typeof document === "undefined") return () => {}
160
185
  // Style edits arrive as bursts of mutations; coalesce them into one read, and
161
186
  // take it after the next frame so a swapped stylesheet has been applied.
@@ -170,8 +195,11 @@ function watchChartTokens(onChange: () => void): () => void {
170
195
 
171
196
  const themeObserver = new MutationObserver(schedule)
172
197
  const attributeFilter = ["class", "style", "data-theme"]
173
- themeObserver.observe(document.documentElement, { attributes: true, attributeFilter })
174
- if (document.body) themeObserver.observe(document.body, { attributes: true, attributeFilter })
198
+ // Only the chart and its ancestors can change its inherited theme. Never observe
199
+ // the document subtree: canvas/tooltip/rendering mutations would trigger every chart.
200
+ for (let node: Element | null = scope ?? document.body ?? document.documentElement; node; node = node.parentElement) {
201
+ themeObserver.observe(node, { attributes: true, attributeFilter })
202
+ }
175
203
 
176
204
  const styleObserver = new MutationObserver(schedule)
177
205
  if (document.head) {
@@ -227,7 +255,7 @@ function buildChartTheme(tokens: ChartTokens): Record<string, unknown> {
227
255
  // Let the page background show through: charts sit on cards, not on a canvas.
228
256
  backgroundColor: "transparent",
229
257
  ...(tokens.series.length > 0 ? { color: tokens.series } : {}),
230
- textStyle: { color: text },
258
+ textStyle: { color: text, ...(tokens.fontFamily ? { fontFamily: tokens.fontFamily } : {}) },
231
259
  title: { textStyle: { color: text }, subtextStyle: { color: muted } },
232
260
  legend: { textStyle: { color: muted } },
233
261
  categoryAxis: axisTheme(tokens, false),
@@ -243,7 +271,11 @@ function buildChartTheme(tokens: ChartTokens): Record<string, unknown> {
243
271
  padding: [8, 12],
244
272
  // Rounded card with a soft shadow; in dark mode the shadow barely shows, so the border carries the edge.
245
273
  extraCssText: "border-radius:8px;box-shadow:var(--shadow-card, 0 4px 12px rgb(0 0 0 / 0.08));",
246
- textStyle: { color: tokens.popoverForeground ?? undefined, fontSize: 12 },
274
+ textStyle: {
275
+ color: tokens.popoverForeground ?? undefined,
276
+ fontSize: 12,
277
+ ...(tokens.fontFamily ? { fontFamily: tokens.fontFamily } : {}),
278
+ },
247
279
  axisPointer: { lineStyle: { color: line }, crossStyle: { color: line } },
248
280
  },
249
281
  }
@@ -255,22 +287,23 @@ function buildChartTheme(tokens: ChartTokens): Record<string, unknown> {
255
287
  * components that build an ECharts option from token colours themselves (the
256
288
  * typed charts), so their palette follows light/dark like `EChart`'s theme.
257
289
  */
258
- function useChartTokens(): ChartTokens | null {
290
+ function useChartTokens(scope?: Element | null | RefObject<Element | null>): ChartTokens | null {
259
291
  const [tokens, setTokens] = useState<ChartTokens | null>(() => readChartTokens())
260
292
  // Compared here, not inside a `setTokens` updater: an updater that reads the DOM is
261
293
  // replayed by React on every rebase and returns a fresh object each time, so the state
262
294
  // never settles ("Maximum update depth exceeded" once the tokens change after mount).
263
295
  const tokensRef = useRef(tokens)
264
296
  useEffect(() => {
297
+ const target = scope && "current" in scope ? scope.current : scope
265
298
  const sync = () => {
266
- const next = readChartTokens()
299
+ const next = readChartTokens(target)
267
300
  if (sameChartTokens(tokensRef.current, next)) return
268
301
  tokensRef.current = next
269
302
  setTokens(next)
270
303
  }
271
304
  sync()
272
- return watchChartTokens(sync)
273
- }, [])
305
+ return watchChartTokens(sync, target)
306
+ }, [scope])
274
307
  return tokens
275
308
  }
276
309
 
@@ -282,7 +315,7 @@ const EMBEDDED_VAR_RE = /var\(\s*(--[\w-]+)\s*\)/g
282
315
  /**
283
316
  * Turn chart colours into canvas-safe strings. `"chart-1"`〜`"chart-5"` come from
284
317
  * the resolved series tokens; `"positive"` / `"warning"` / `"negative"` / `"neutral"` (and the area
285
- * variants `"positive-fill"`, …) are read off the document root (`--positive`, …); `var(--token)` is read off the document root; any
318
+ * variants `"positive-fill"`, …) are read in the snapshot's inherited scope (`--positive`, …); `var(--token)` uses the same scope; any
286
319
  * other CSS colour is normalized like the tokens are. A colour that can't be
287
320
  * resolved comes back as `""`, which ECharts treats as "use the palette".
288
321
  */
@@ -293,12 +326,13 @@ function resolveChartColors(colors: readonly string[], tokens: ChartTokens | nul
293
326
  // Meaning colours are the theme tokens of the same name: same path as `var(--positive)`.
294
327
  if (TONE_NAME_RE.test(color.trim())) {
295
328
  if (typeof document === "undefined") return ""
296
- return toCanvasColor(getComputedStyle(document.documentElement).getPropertyValue(`--${color.trim()}`)) ?? ""
329
+ const target = (tokens && tokenScopes.get(tokens)) ?? document.documentElement
330
+ return toCanvasColor(getComputedStyle(target).getPropertyValue(`--${color.trim()}`)) ?? ""
297
331
  }
298
332
  if (typeof document === "undefined") return color
299
333
  let value = color.trim()
300
334
  const cssVar = CSS_VAR_RE.exec(value)
301
- const styles = getComputedStyle(document.documentElement)
335
+ const styles = getComputedStyle((tokens && tokenScopes.get(tokens)) ?? document.documentElement)
302
336
  if (cssVar) {
303
337
  value = styles.getPropertyValue(cssVar[1]!).trim() || (cssVar[2] ?? "").trim()
304
338
  } else if (value.includes("var(")) {
@@ -344,7 +378,7 @@ function readChartSurface(el: Element | null): string | null {
344
378
  if (color) return color
345
379
  }
346
380
  }
347
- return toCanvasColor(getComputedStyle(document.documentElement).getPropertyValue("--background"))
381
+ return toCanvasColor(getComputedStyle(el ?? document.documentElement).getPropertyValue("--background"))
348
382
  }
349
383
 
350
384
  /** Widths are bucketed so a resize only rebuilds the option when the label budget can change. */
@@ -358,9 +392,10 @@ const WIDTH_BUCKET = 40
358
392
  */
359
393
  function useChartSurface<E extends Element>(
360
394
  forwarded: ForwardedRef<E>,
361
- tokens: ChartTokens | null,
362
- ): { ref: (el: E | null) => void; surface: string | undefined; width: number | undefined } {
395
+ tokens?: ChartTokens | null,
396
+ ): { ref: (el: E | null) => void; surface: string | undefined; width: number | undefined; tokens: ChartTokens | null } {
363
397
  const [node, setNode] = useState<E | null>(null)
398
+ const scopedTokens = useChartTokens(node)
364
399
  const [surface, setSurface] = useState<string | undefined>(undefined)
365
400
  const [width, setWidth] = useState<number | undefined>(undefined)
366
401
  const ref = useCallback(
@@ -374,7 +409,7 @@ function useChartSurface<E extends Element>(
374
409
  useEffect(() => {
375
410
  if (!node) return
376
411
  setSurface(readChartSurface(node) ?? undefined)
377
- }, [node, tokens])
412
+ }, [node, tokens, scopedTokens])
378
413
  useEffect(() => {
379
414
  if (!node || typeof ResizeObserver === "undefined") return
380
415
  const update = (w: number) => setWidth(Math.floor(w / WIDTH_BUCKET) * WIDTH_BUCKET)
@@ -383,7 +418,7 @@ function useChartSurface<E extends Element>(
383
418
  observer.observe(node)
384
419
  return () => observer.disconnect()
385
420
  }, [node])
386
- return { ref, surface, width }
421
+ return { ref, surface, width, tokens: scopedTokens }
387
422
  }
388
423
 
389
424
  /* ---------- inlined from @squadbase/squadkit registry/lib/chart-utils.ts (internal to @squadbase/vantage) ---------- */
@@ -1537,6 +1572,10 @@ const toDate = (t: ParsedTime) => new Date(t.year, t.month - 1, t.day)
1537
1572
 
1538
1573
  /** How many x labels a time axis shows at most (the step is picked so the labels never crowd). */
1539
1574
  const TIME_AXIS_MAX_LABELS = 6
1575
+ /** Under this plot width (px) the axis, legend and labels switch to their compact form. */
1576
+ const NARROW_PLOT_WIDTH = 400
1577
+ /** The most x labels a narrow plot shows (a 262px plot reads 4 at most). */
1578
+ const NARROW_TIME_AXIS_MAX_LABELS = 4
1540
1579
 
1541
1580
  /** Calendar-friendly label steps per granularity (every 3 months, every week …). */
1542
1581
  const TIME_AXIS_STEPS: Record<TimeGranularity, readonly number[]> = {
@@ -1982,7 +2021,9 @@ const LEGEND_LINE_BUDGET = 320
1982
2021
  /** How many lines the plain legend takes, estimated from its labels (12px text + 8px dot + gaps). */
1983
2022
  function legendLines(items: readonly string[], plotWidth?: number, reserve = 0): number {
1984
2023
  const width = items.reduce((sum, name) => sum + [...name].length * 12 + 8 + 5 + 16, 0)
1985
- const budget = Math.max(80, (plotWidth ? Math.min(LEGEND_LINE_BUDGET, plotWidth) : LEGEND_LINE_BUDGET) - reserve)
2024
+ // A narrow plot has no room for the PNG buttons beside the legend: they move off its line, so no reserve.
2025
+ const narrow = plotWidth !== undefined && plotWidth < NARROW_PLOT_WIDTH
2026
+ const budget = Math.max(80, (plotWidth ? (narrow ? plotWidth : Math.min(LEGEND_LINE_BUDGET, plotWidth)) : LEGEND_LINE_BUDGET) - (narrow ? 0 : reserve))
1986
2027
  return Math.min(3, Math.max(1, Math.ceil(width / budget)))
1987
2028
  }
1988
2029
 
@@ -2237,7 +2278,15 @@ function buildCartesianOption(cfg: CartesianChartConfig): EChartsOption {
2237
2278
  ? null
2238
2279
  : timeAxisLabelStep(
2239
2280
  rawIndex,
2240
- plotWidth ? Math.max(2, Math.min(TIME_AXIS_MAX_LABELS, Math.floor(plotWidth / TIME_LABEL_WIDTH))) : undefined,
2281
+ plotWidth
2282
+ ? Math.max(
2283
+ 2,
2284
+ Math.min(
2285
+ plotWidth < NARROW_PLOT_WIDTH ? NARROW_TIME_AXIS_MAX_LABELS : TIME_AXIS_MAX_LABELS,
2286
+ Math.floor(plotWidth / TIME_LABEL_WIDTH),
2287
+ ),
2288
+ )
2289
+ : undefined,
2241
2290
  )
2242
2291
  // Plain (non-date) x labels of a line chart: thinned to every n-th so they never touch, never rotated.
2243
2292
  const plainStep = (() => {
@@ -2283,8 +2332,12 @@ function buildCartesianOption(cfg: CartesianChartConfig): EChartsOption {
2283
2332
  : []
2284
2333
  // On a narrow plot the max and the latest label would print over each other: keep the latest.
2285
2334
  const pointGap = plotWidth && labels.length > 1 ? plotWidth / (kind === "bar" ? labels.length : labels.length - 1) : null
2335
+ // By default only the latest value is labelled; an explicit `highlight` keeps its max + latest.
2336
+ const latestOnly = cfg.highlight === undefined && !shareHighlight && picked.length === 2
2286
2337
  const highlightPoints =
2287
- picked.length === 2 && pointGap !== null && Math.abs(picked[1]!.index - picked[0]!.index) * pointGap < HIGHLIGHT_LABEL_WIDTH
2338
+ latestOnly
2339
+ ? [picked[1]!]
2340
+ : picked.length === 2 && pointGap !== null && Math.abs(picked[1]!.index - picked[0]!.index) * pointGap < HIGHLIGHT_LABEL_WIDTH
2288
2341
  ? [picked[1]!]
2289
2342
  : picked
2290
2343
 
@@ -2495,6 +2548,8 @@ function buildCartesianOption(cfg: CartesianChartConfig): EChartsOption {
2495
2548
  ? { width: 0, color, opacity: 0 }
2496
2549
  : { width: 2, color, opacity: dimSeries ? DIMMED : 1 },
2497
2550
  itemStyle: { color, opacity: dimSeries ? DIMMED : 1 },
2551
+ // Hovering a series fades the others (not for a stack, where the bands read together).
2552
+ emphasis: stacked ? undefined : { focus: "series" as const },
2498
2553
  areaStyle: areaStyle && dimSeries ? { ...areaStyle, opacity: DIMMED * 0.3 } : areaStyle,
2499
2554
  markPoint: markPointFor(c, color),
2500
2555
  markLine: undefined,
@@ -2941,7 +2996,10 @@ function buildCartesianOption(cfg: CartesianChartConfig): EChartsOption {
2941
2996
  legend: {
2942
2997
  show: showLegend,
2943
2998
  top: 0,
2944
- right: reserveActions ? ACTIONS_RESERVE : 0,
2999
+ // Narrow: left-aligned and wrapping above the plot, so nothing is cut off at the right edge.
3000
+ ...(plotWidth !== undefined && plotWidth < NARROW_PLOT_WIDTH
3001
+ ? { left: 0, right: 0 }
3002
+ : { right: reserveActions ? ACTIONS_RESERVE : 0 }),
2945
3003
  // Plain wraps onto a second line at narrow widths instead of hiding items behind arrows.
2946
3004
  type: "plain",
2947
3005
  icon: "circle",
@@ -3002,7 +3060,12 @@ function buildCartesianOption(cfg: CartesianChartConfig): EChartsOption {
3002
3060
  }
3003
3061
  }
3004
3062
  const shares = percent ? cartesianSeriesValues(i <= lastActual ? [row] : [], categories, "percent") : null
3005
- for (const p of list) {
3063
+ // Rows follow the legend order (categories first), not ECharts' own series order.
3064
+ const legendRank = (p: AxisTooltipParam) => {
3065
+ const at = categories.indexOf(p.seriesName ?? "")
3066
+ return at === -1 ? categories.length : at
3067
+ }
3068
+ for (const p of [...list].sort((a, b) => legendRank(a) - legendRank(b))) {
3006
3069
  const color = typeof p.color === "string" ? p.color : "currentColor"
3007
3070
  if (p.seriesId === FORECAST_SERIES_ID) {
3008
3071
  // At and before the boundary the forecast repeats the actual value.
@@ -3518,9 +3581,8 @@ function LineChartImpl<T extends Record<string, unknown>>(
3518
3581
  }: LineChartProps<T>,
3519
3582
  ref: ForwardedRef<HTMLDivElement>,
3520
3583
  ) {
3521
- const tokens = useChartTokens()
3522
3584
  // Highlight labels are backed by the surface the chart sits on (a card, the page).
3523
- const { ref: frameRef, surface, width: plotWidth } = useChartSurface(ref, tokens)
3585
+ const { ref: frameRef, surface, width: plotWidth, tokens } = useChartSurface(ref)
3524
3586
  // Ticks and on-plot labels read short (¥1億); the tooltip reads the value as `format` says.
3525
3587
  const tooltipFormatter = useMemo(() => resolveValueFormatter(valueFormatter, format), [valueFormatter, format])
3526
3588
  const axisFormatter = useMemo(() => resolveAxisFormatter(valueFormatter, format), [valueFormatter, format])
@@ -1,5 +1,5 @@
1
1
  // Copied by `vantage add ui metric-value`. This file is yours to edit.
2
- // Generated from @squadbase/squadkit registry/components/metric-value.tsx by `npm run registry` (in Vantage, edit packages/squadkit, not registry/).
2
+ // Generated from @squadbase/squadkit registry/components/metric-value.tsx by `pnpm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
3
  "use client"
4
4
 
5
5
  /**
@@ -1,5 +1,5 @@
1
1
  // Copied by `vantage add ui multi-select`. This file is yours to edit.
2
- // Generated from @squadbase/squadkit registry/components/multi-select.tsx by `npm run registry` (in Vantage, edit packages/squadkit, not registry/).
2
+ // Generated from @squadbase/squadkit registry/components/multi-select.tsx by `pnpm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
3
  "use client"
4
4
 
5
5
  /**
@@ -1,5 +1,5 @@
1
1
  // Copied by `vantage add ui page-shell`. This file is yours to edit.
2
- // Generated from @squadbase/squadkit registry/components/page-shell.tsx by `npm run registry` (in Vantage, edit packages/squadkit, not registry/).
2
+ // Generated from @squadbase/squadkit registry/components/page-shell.tsx by `pnpm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
3
  "use client"
4
4
 
5
5
  /**
@@ -60,7 +60,7 @@ export const PageShellHeader = forwardRef<HTMLDivElement, PageShellHeaderProps>(
60
60
  <div
61
61
  ref={ref}
62
62
  data-slot="page-shell-header"
63
- className={cn("flex flex-wrap items-start gap-x-4 gap-y-6 px-6 py-12", className)}
63
+ className={cn("flex flex-wrap items-start gap-x-4 gap-y-4 px-6 pt-8", className)}
64
64
  {...props}
65
65
  >
66
66
  {children}
@@ -72,12 +72,12 @@ export const PageShellHeader = forwardRef<HTMLDivElement, PageShellHeaderProps>(
72
72
  {hasChips ? (
73
73
  <div data-slot="page-shell-chips" className="order-1 flex basis-full flex-wrap gap-1.5">
74
74
  {source ? (
75
- <Badge variant="outline" className="h-auto max-w-full whitespace-normal">
75
+ <Badge variant="outline" className="h-auto max-w-full whitespace-normal text-muted-foreground">
76
76
  出典:{source}
77
77
  </Badge>
78
78
  ) : null}
79
79
  {periodText && periodText !== EMPTY_VALUE ? (
80
- <Badge variant="outline" className="tabular-nums">
80
+ <Badge variant="outline" className="tabular-nums text-muted-foreground">
81
81
  {periodText}
82
82
  </Badge>
83
83
  ) : null}
@@ -115,7 +115,7 @@ export const PageShellTitle = forwardRef<HTMLHeadingElement, PageShellTitleProps
115
115
  <h1
116
116
  ref={ref}
117
117
  data-slot="page-shell-title"
118
- className={cn("text-2xl font-bold tracking-tight text-foreground", className)}
118
+ className={cn("text-2xl font-semibold tracking-tight text-foreground", className)}
119
119
  {...props}
120
120
  />
121
121
  ),
@@ -204,11 +204,11 @@ PageShellSummary.displayName = "PageShellSummary"
204
204
  * emits classes it can see literally in the source.
205
205
  */
206
206
  const summaryCardTones = {
207
- positive: "border-positive/30 bg-positive-subtle [&>svg]:text-positive",
208
- negative: "border-negative/30 bg-negative-subtle [&>svg]:text-negative",
209
- warning: "border-warning/30 bg-warning-subtle [&>svg]:text-warning",
210
- info: "border-info/30 bg-info-subtle [&>svg]:text-info",
211
- neutral: "border-neutral/30 bg-neutral-subtle [&>svg]:text-neutral",
207
+ positive: "border-positive/20 dark:border-positive/25 bg-positive-subtle [&>svg]:text-positive",
208
+ negative: "border-negative/20 dark:border-negative/25 bg-negative-subtle [&>svg]:text-negative",
209
+ warning: "border-warning/20 dark:border-warning/25 bg-warning-subtle [&>svg]:text-warning",
210
+ info: "border-info/20 dark:border-info/25 bg-info-subtle [&>svg]:text-info",
211
+ neutral: "border-neutral/20 dark:border-neutral/25 bg-neutral-subtle [&>svg]:text-neutral",
212
212
  violet: "border-chart-4/30 bg-chart-4/5 [&>svg]:text-chart-4",
213
213
  pink: "border-chart-5/30 bg-chart-5/5 [&>svg]:text-chart-5",
214
214
  } as const
@@ -274,7 +274,7 @@ export const PageShellContent = forwardRef<HTMLDivElement, PageShellContentProps
274
274
  <div
275
275
  ref={ref}
276
276
  data-slot="page-shell-content"
277
- className={cn("flex-1 p-6", className)}
277
+ className={cn("flex min-w-0 flex-1 flex-col gap-6 p-6", className)}
278
278
  {...props}
279
279
  />
280
280
  ),
@@ -1,5 +1,5 @@
1
1
  // Copied by `vantage add ui pagination`. This file is yours to edit.
2
- // Generated from @squadbase/squadkit registry/ui/pagination.tsx by `npm run registry` (in Vantage, edit packages/squadkit, not registry/).
2
+ // Generated from @squadbase/squadkit registry/ui/pagination.tsx by `pnpm run registry` (in Vantage, edit packages/squadkit, not registry/).
3
3
  "use client"
4
4
 
5
5
  import * as React from "react"