@squadbase/vantage 0.6.2-dev1.5aae04e → 0.6.2-dev1.72360fb
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/chunk-2IAPIOPS.js +152 -0
- package/dist/chunk-2IAPIOPS.js.map +1 -0
- package/dist/chunk-57GHR5LN.js +163 -0
- package/dist/chunk-57GHR5LN.js.map +1 -0
- package/dist/chunk-6OTTGAPO.js +44 -0
- package/dist/chunk-6OTTGAPO.js.map +1 -0
- package/dist/{chunk-XPQSTZUU.js → chunk-7S52M2SL.js} +4 -3
- package/dist/chunk-7S52M2SL.js.map +1 -0
- package/dist/chunk-JXFCSF3K.js +673 -0
- package/dist/chunk-JXFCSF3K.js.map +1 -0
- package/dist/{chunk-HKSVYMYJ.js → chunk-QOXXOFGL.js} +61 -929
- package/dist/chunk-QOXXOFGL.js.map +1 -0
- package/dist/components/index.d.ts +11 -247
- package/dist/components/index.js +5 -2
- package/dist/delta-text-CvwBGJ95.d.ts +15 -0
- package/dist/doc/index.d.ts +215 -2
- package/dist/doc/index.js +300 -3
- package/dist/doc/index.js.map +1 -1
- package/dist/format-U0cQl069.d.ts +243 -0
- package/dist/slide/index.d.ts +272 -1
- package/dist/slide/index.js +966 -1
- package/dist/slide/index.js.map +1 -1
- package/dist/table/index.js +5 -2
- package/dist/table/index.js.map +1 -1
- package/dist/ui/index.d.ts +4 -1
- package/dist/ui/index.js +3 -3
- package/dist/ui/index.js.map +1 -1
- 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/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/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/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/area-chart.tsx +45 -19
- package/registry/ui/bar-chart.tsx +45 -19
- package/registry/ui/bar-list.tsx +44 -17
- package/registry/ui/box-plot-chart.tsx +45 -19
- package/registry/ui/category-bar.tsx +44 -17
- package/registry/ui/chart-frame.tsx +44 -17
- package/registry/ui/delta.tsx +11 -8
- package/registry/ui/donut-chart.tsx +46 -19
- package/registry/ui/echart.tsx +171 -54
- package/registry/ui/heatmap.tsx +45 -19
- package/registry/ui/histogram-chart.tsx +45 -19
- package/registry/ui/kpi-card.tsx +9 -4
- package/registry/ui/legend.tsx +44 -17
- package/registry/ui/line-chart.tsx +45 -19
- package/registry/ui/page-shell.tsx +7 -7
- package/registry/ui/paired-chart.tsx +45 -19
- package/registry/ui/sample-data.tsx +1 -1
- package/registry/ui/scatter-chart.tsx +45 -19
- package/registry/ui/slider.tsx +1 -1
- package/registry/ui/status-badge.tsx +8 -8
- package/registry/ui/tooltip.tsx +6 -2
- package/registry/ui/tracker.tsx +9 -3
- package/registry/ui/trend-indicator.tsx +2 -2
- package/registry/ui/waterfall-chart.tsx +45 -19
- package/skills/vantage-design/SKILL.md +27 -25
- package/skills/vantage-design/references/extract.md +16 -14
- package/skills/vantage-design/references/format.md +6 -5
- package/skills/vantage-design/references/presets.md +60 -110
- package/skills/vantage-slide/SKILL.md +54 -36
- package/templates/AGENTS.md +2 -2
- package/templates/agents/doc.md +48 -0
- package/templates/agents/page.md +22 -1
- package/templates/agents/slide.md +70 -2
- package/theme.css +16 -15
- package/dist/chunk-HKSVYMYJ.js.map +0 -1
- package/dist/chunk-XPQSTZUU.js.map +0 -1
|
@@ -15,7 +15,7 @@ import { forwardRef, useMemo, type ForwardedRef, type ReactElement, type Ref } f
|
|
|
15
15
|
import { ChartFrame } from "@squadbase/vantage/components"
|
|
16
16
|
import { EChart } from "@squadbase/vantage/components"
|
|
17
17
|
import type { ChartComparison, ChartForecast, NumericKeys } from "@squadbase/vantage/components"
|
|
18
|
-
import { createContext, useCallback, useEffect, useRef, useState } from "react"
|
|
18
|
+
import { createContext, useCallback, useEffect, useRef, useState, type RefObject } from "react"
|
|
19
19
|
import type { ChartTone } from "@squadbase/vantage/components"
|
|
20
20
|
import { DEFAULT_LOCALE, EMPTY_VALUE, formatValue, toValueFormatter, type ValueFormat } from "@squadbase/vantage/components"
|
|
21
21
|
import type { ChartColor, ChartColors, ChartSeriesColor, ValueFormatter } from "@squadbase/vantage/components"
|
|
@@ -58,10 +58,23 @@ interface ChartTokens {
|
|
|
58
58
|
border: string | null
|
|
59
59
|
/** Tooltip surface. */
|
|
60
60
|
popover: string | null
|
|
61
|
+
/** Semantic and surface dependencies used by typed chart options. Optional for hand-built tokens. */
|
|
62
|
+
colors?: Record<string, string>
|
|
63
|
+
/** Actual inherited painting surface (may change without a token changing). */
|
|
64
|
+
surface?: string | null
|
|
61
65
|
/** Tooltip text. */
|
|
62
66
|
popoverForeground: string | null
|
|
63
67
|
}
|
|
64
68
|
|
|
69
|
+
// Keep scope out of the public token shape. A resolved snapshot remembers where arbitrary
|
|
70
|
+
// var(--token) expressions must be read, without retaining unmounted charts globally.
|
|
71
|
+
const tokenScopes = new WeakMap<ChartTokens, Element>()
|
|
72
|
+
const OPTION_COLOR_TOKENS = [
|
|
73
|
+
"--background", "--card", "--muted", "--primary", "--destructive",
|
|
74
|
+
...["positive", "negative", "warning", "neutral", "info"].flatMap((tone) =>
|
|
75
|
+
["", "-fill", "-subtle"].map((suffix) => `--${tone}${suffix}`)),
|
|
76
|
+
]
|
|
77
|
+
|
|
65
78
|
let normalizeContext: CanvasRenderingContext2D | null | undefined
|
|
66
79
|
|
|
67
80
|
/** The forms `fillStyle` serializes legacy colours to, and zrender can parse. */
|
|
@@ -117,8 +130,10 @@ function readChartTokens(el?: Element | null): ChartTokens | null {
|
|
|
117
130
|
const color = read(token)
|
|
118
131
|
if (color) series.push(color)
|
|
119
132
|
}
|
|
120
|
-
|
|
133
|
+
const tokens: ChartTokens = {
|
|
121
134
|
series,
|
|
135
|
+
colors: Object.fromEntries(OPTION_COLOR_TOKENS.map((name) => [name, styles.getPropertyValue(name).trim()])),
|
|
136
|
+
surface: readChartSurface(target),
|
|
122
137
|
...(styles.fontFamily ? { fontFamily: styles.fontFamily } : {}),
|
|
123
138
|
foreground: read("--foreground"),
|
|
124
139
|
mutedForeground: read("--muted-foreground"),
|
|
@@ -126,6 +141,8 @@ function readChartTokens(el?: Element | null): ChartTokens | null {
|
|
|
126
141
|
popover: read("--popover"),
|
|
127
142
|
popoverForeground: read("--popover-foreground"),
|
|
128
143
|
}
|
|
144
|
+
tokenScopes.set(tokens, target)
|
|
145
|
+
return tokens
|
|
129
146
|
}
|
|
130
147
|
|
|
131
148
|
/** Structural equality, so a theme toggle that changes nothing doesn't re-init. */
|
|
@@ -133,6 +150,10 @@ function sameChartTokens(a: ChartTokens | null, b: ChartTokens | null): boolean
|
|
|
133
150
|
if (a === b) return true
|
|
134
151
|
if (!a || !b) return false
|
|
135
152
|
return (
|
|
153
|
+
tokenScopes.get(a) === tokenScopes.get(b) &&
|
|
154
|
+
a.surface === b.surface &&
|
|
155
|
+
Object.keys(a.colors ?? {}).length === Object.keys(b.colors ?? {}).length &&
|
|
156
|
+
Object.entries(a.colors ?? {}).every(([name, value]) => b.colors?.[name] === value) &&
|
|
136
157
|
a.series.length === b.series.length &&
|
|
137
158
|
a.series.every((color, i) => color === b.series[i]) &&
|
|
138
159
|
a.fontFamily === b.fontFamily &&
|
|
@@ -158,7 +179,7 @@ function sameChartTokens(a: ChartTokens | null, b: ChartTokens | null): boolean
|
|
|
158
179
|
* attributes alone leaves the chart painted in the previous palette until a
|
|
159
180
|
* reload — the tokens in the DOM are new, the canvas is not.
|
|
160
181
|
*/
|
|
161
|
-
function watchChartTokens(onChange: () => void): () => void {
|
|
182
|
+
function watchChartTokens(onChange: () => void, scope?: Element | null): () => void {
|
|
162
183
|
if (typeof document === "undefined") return () => {}
|
|
163
184
|
// Style edits arrive as bursts of mutations; coalesce them into one read, and
|
|
164
185
|
// take it after the next frame so a swapped stylesheet has been applied.
|
|
@@ -173,8 +194,11 @@ function watchChartTokens(onChange: () => void): () => void {
|
|
|
173
194
|
|
|
174
195
|
const themeObserver = new MutationObserver(schedule)
|
|
175
196
|
const attributeFilter = ["class", "style", "data-theme"]
|
|
176
|
-
|
|
177
|
-
|
|
197
|
+
// Only the chart and its ancestors can change its inherited theme. Never observe
|
|
198
|
+
// the document subtree: canvas/tooltip/rendering mutations would trigger every chart.
|
|
199
|
+
for (let node: Element | null = scope ?? document.body ?? document.documentElement; node; node = node.parentElement) {
|
|
200
|
+
themeObserver.observe(node, { attributes: true, attributeFilter })
|
|
201
|
+
}
|
|
178
202
|
|
|
179
203
|
const styleObserver = new MutationObserver(schedule)
|
|
180
204
|
if (document.head) {
|
|
@@ -262,22 +286,23 @@ function buildChartTheme(tokens: ChartTokens): Record<string, unknown> {
|
|
|
262
286
|
* components that build an ECharts option from token colours themselves (the
|
|
263
287
|
* typed charts), so their palette follows light/dark like `EChart`'s theme.
|
|
264
288
|
*/
|
|
265
|
-
function useChartTokens(): ChartTokens | null {
|
|
289
|
+
function useChartTokens(scope?: Element | null | RefObject<Element | null>): ChartTokens | null {
|
|
266
290
|
const [tokens, setTokens] = useState<ChartTokens | null>(() => readChartTokens())
|
|
267
291
|
// Compared here, not inside a `setTokens` updater: an updater that reads the DOM is
|
|
268
292
|
// replayed by React on every rebase and returns a fresh object each time, so the state
|
|
269
293
|
// never settles ("Maximum update depth exceeded" once the tokens change after mount).
|
|
270
294
|
const tokensRef = useRef(tokens)
|
|
271
295
|
useEffect(() => {
|
|
296
|
+
const target = scope && "current" in scope ? scope.current : scope
|
|
272
297
|
const sync = () => {
|
|
273
|
-
const next = readChartTokens()
|
|
298
|
+
const next = readChartTokens(target)
|
|
274
299
|
if (sameChartTokens(tokensRef.current, next)) return
|
|
275
300
|
tokensRef.current = next
|
|
276
301
|
setTokens(next)
|
|
277
302
|
}
|
|
278
303
|
sync()
|
|
279
|
-
return watchChartTokens(sync)
|
|
280
|
-
}, [])
|
|
304
|
+
return watchChartTokens(sync, target)
|
|
305
|
+
}, [scope])
|
|
281
306
|
return tokens
|
|
282
307
|
}
|
|
283
308
|
|
|
@@ -289,7 +314,7 @@ const EMBEDDED_VAR_RE = /var\(\s*(--[\w-]+)\s*\)/g
|
|
|
289
314
|
/**
|
|
290
315
|
* Turn chart colours into canvas-safe strings. `"chart-1"`〜`"chart-5"` come from
|
|
291
316
|
* the resolved series tokens; `"positive"` / `"warning"` / `"negative"` / `"neutral"` (and the area
|
|
292
|
-
* variants `"positive-fill"`, …) are read
|
|
317
|
+
* variants `"positive-fill"`, …) are read in the snapshot's inherited scope (`--positive`, …); `var(--token)` uses the same scope; any
|
|
293
318
|
* other CSS colour is normalized like the tokens are. A colour that can't be
|
|
294
319
|
* resolved comes back as `""`, which ECharts treats as "use the palette".
|
|
295
320
|
*/
|
|
@@ -300,12 +325,13 @@ function resolveChartColors(colors: readonly string[], tokens: ChartTokens | nul
|
|
|
300
325
|
// Meaning colours are the theme tokens of the same name: same path as `var(--positive)`.
|
|
301
326
|
if (TONE_NAME_RE.test(color.trim())) {
|
|
302
327
|
if (typeof document === "undefined") return ""
|
|
303
|
-
|
|
328
|
+
const target = (tokens && tokenScopes.get(tokens)) ?? document.documentElement
|
|
329
|
+
return toCanvasColor(getComputedStyle(target).getPropertyValue(`--${color.trim()}`)) ?? ""
|
|
304
330
|
}
|
|
305
331
|
if (typeof document === "undefined") return color
|
|
306
332
|
let value = color.trim()
|
|
307
333
|
const cssVar = CSS_VAR_RE.exec(value)
|
|
308
|
-
const styles = getComputedStyle(document.documentElement)
|
|
334
|
+
const styles = getComputedStyle((tokens && tokenScopes.get(tokens)) ?? document.documentElement)
|
|
309
335
|
if (cssVar) {
|
|
310
336
|
value = styles.getPropertyValue(cssVar[1]!).trim() || (cssVar[2] ?? "").trim()
|
|
311
337
|
} else if (value.includes("var(")) {
|
|
@@ -351,7 +377,7 @@ function readChartSurface(el: Element | null): string | null {
|
|
|
351
377
|
if (color) return color
|
|
352
378
|
}
|
|
353
379
|
}
|
|
354
|
-
return toCanvasColor(getComputedStyle(document.documentElement).getPropertyValue("--background"))
|
|
380
|
+
return toCanvasColor(getComputedStyle(el ?? document.documentElement).getPropertyValue("--background"))
|
|
355
381
|
}
|
|
356
382
|
|
|
357
383
|
/** Widths are bucketed so a resize only rebuilds the option when the label budget can change. */
|
|
@@ -365,9 +391,10 @@ const WIDTH_BUCKET = 40
|
|
|
365
391
|
*/
|
|
366
392
|
function useChartSurface<E extends Element>(
|
|
367
393
|
forwarded: ForwardedRef<E>,
|
|
368
|
-
tokens
|
|
369
|
-
): { ref: (el: E | null) => void; surface: string | undefined; width: number | undefined } {
|
|
394
|
+
tokens?: ChartTokens | null,
|
|
395
|
+
): { ref: (el: E | null) => void; surface: string | undefined; width: number | undefined; tokens: ChartTokens | null } {
|
|
370
396
|
const [node, setNode] = useState<E | null>(null)
|
|
397
|
+
const scopedTokens = useChartTokens(node)
|
|
371
398
|
const [surface, setSurface] = useState<string | undefined>(undefined)
|
|
372
399
|
const [width, setWidth] = useState<number | undefined>(undefined)
|
|
373
400
|
const ref = useCallback(
|
|
@@ -381,7 +408,7 @@ function useChartSurface<E extends Element>(
|
|
|
381
408
|
useEffect(() => {
|
|
382
409
|
if (!node) return
|
|
383
410
|
setSurface(readChartSurface(node) ?? undefined)
|
|
384
|
-
}, [node, tokens])
|
|
411
|
+
}, [node, tokens, scopedTokens])
|
|
385
412
|
useEffect(() => {
|
|
386
413
|
if (!node || typeof ResizeObserver === "undefined") return
|
|
387
414
|
const update = (w: number) => setWidth(Math.floor(w / WIDTH_BUCKET) * WIDTH_BUCKET)
|
|
@@ -390,7 +417,7 @@ function useChartSurface<E extends Element>(
|
|
|
390
417
|
observer.observe(node)
|
|
391
418
|
return () => observer.disconnect()
|
|
392
419
|
}, [node])
|
|
393
|
-
return { ref, surface, width }
|
|
420
|
+
return { ref, surface, width, tokens: scopedTokens }
|
|
394
421
|
}
|
|
395
422
|
|
|
396
423
|
/* ---------- inlined from @squadbase/squadkit registry/lib/chart-utils.ts (internal to @squadbase/vantage) ---------- */
|
|
@@ -3557,9 +3584,8 @@ function AreaChartImpl<T extends Record<string, unknown>>(
|
|
|
3557
3584
|
}: AreaChartProps<T>,
|
|
3558
3585
|
ref: ForwardedRef<HTMLDivElement>,
|
|
3559
3586
|
) {
|
|
3560
|
-
const tokens = useChartTokens()
|
|
3561
3587
|
// Highlight labels are backed by the surface the chart sits on (a card, the page).
|
|
3562
|
-
const { ref: frameRef, surface, width: plotWidth } = useChartSurface(ref
|
|
3588
|
+
const { ref: frameRef, surface, width: plotWidth, tokens } = useChartSurface(ref)
|
|
3563
3589
|
// Ticks and on-plot labels read short (¥1億); the tooltip reads the value as `format` says.
|
|
3564
3590
|
const tooltipFormatter = useMemo(() => resolveValueFormatter(valueFormatter, format), [valueFormatter, format])
|
|
3565
3591
|
const axisFormatter = useMemo(() => resolveAxisFormatter(valueFormatter, format), [valueFormatter, format])
|
|
@@ -18,7 +18,7 @@ import { forwardRef, useMemo, type ForwardedRef, type ReactElement, type Ref } f
|
|
|
18
18
|
import { ChartFrame } from "@squadbase/vantage/components"
|
|
19
19
|
import { EChart } from "@squadbase/vantage/components"
|
|
20
20
|
import type { ChartThreshold } from "@squadbase/vantage/components"
|
|
21
|
-
import { createContext, useCallback, useEffect, useRef, useState } from "react"
|
|
21
|
+
import { createContext, useCallback, useEffect, useRef, useState, type RefObject } from "react"
|
|
22
22
|
import type { ChartTone } from "@squadbase/vantage/components"
|
|
23
23
|
import { DEFAULT_LOCALE, EMPTY_VALUE, formatValue, toValueFormatter, type ValueFormat } from "@squadbase/vantage/components"
|
|
24
24
|
import type { ChartColor, ChartColors, ChartSeriesColor, ValueFormatter } from "@squadbase/vantage/components"
|
|
@@ -61,10 +61,23 @@ interface ChartTokens {
|
|
|
61
61
|
border: string | null
|
|
62
62
|
/** Tooltip surface. */
|
|
63
63
|
popover: string | null
|
|
64
|
+
/** Semantic and surface dependencies used by typed chart options. Optional for hand-built tokens. */
|
|
65
|
+
colors?: Record<string, string>
|
|
66
|
+
/** Actual inherited painting surface (may change without a token changing). */
|
|
67
|
+
surface?: string | null
|
|
64
68
|
/** Tooltip text. */
|
|
65
69
|
popoverForeground: string | null
|
|
66
70
|
}
|
|
67
71
|
|
|
72
|
+
// Keep scope out of the public token shape. A resolved snapshot remembers where arbitrary
|
|
73
|
+
// var(--token) expressions must be read, without retaining unmounted charts globally.
|
|
74
|
+
const tokenScopes = new WeakMap<ChartTokens, Element>()
|
|
75
|
+
const OPTION_COLOR_TOKENS = [
|
|
76
|
+
"--background", "--card", "--muted", "--primary", "--destructive",
|
|
77
|
+
...["positive", "negative", "warning", "neutral", "info"].flatMap((tone) =>
|
|
78
|
+
["", "-fill", "-subtle"].map((suffix) => `--${tone}${suffix}`)),
|
|
79
|
+
]
|
|
80
|
+
|
|
68
81
|
let normalizeContext: CanvasRenderingContext2D | null | undefined
|
|
69
82
|
|
|
70
83
|
/** The forms `fillStyle` serializes legacy colours to, and zrender can parse. */
|
|
@@ -120,8 +133,10 @@ function readChartTokens(el?: Element | null): ChartTokens | null {
|
|
|
120
133
|
const color = read(token)
|
|
121
134
|
if (color) series.push(color)
|
|
122
135
|
}
|
|
123
|
-
|
|
136
|
+
const tokens: ChartTokens = {
|
|
124
137
|
series,
|
|
138
|
+
colors: Object.fromEntries(OPTION_COLOR_TOKENS.map((name) => [name, styles.getPropertyValue(name).trim()])),
|
|
139
|
+
surface: readChartSurface(target),
|
|
125
140
|
...(styles.fontFamily ? { fontFamily: styles.fontFamily } : {}),
|
|
126
141
|
foreground: read("--foreground"),
|
|
127
142
|
mutedForeground: read("--muted-foreground"),
|
|
@@ -129,6 +144,8 @@ function readChartTokens(el?: Element | null): ChartTokens | null {
|
|
|
129
144
|
popover: read("--popover"),
|
|
130
145
|
popoverForeground: read("--popover-foreground"),
|
|
131
146
|
}
|
|
147
|
+
tokenScopes.set(tokens, target)
|
|
148
|
+
return tokens
|
|
132
149
|
}
|
|
133
150
|
|
|
134
151
|
/** Structural equality, so a theme toggle that changes nothing doesn't re-init. */
|
|
@@ -136,6 +153,10 @@ function sameChartTokens(a: ChartTokens | null, b: ChartTokens | null): boolean
|
|
|
136
153
|
if (a === b) return true
|
|
137
154
|
if (!a || !b) return false
|
|
138
155
|
return (
|
|
156
|
+
tokenScopes.get(a) === tokenScopes.get(b) &&
|
|
157
|
+
a.surface === b.surface &&
|
|
158
|
+
Object.keys(a.colors ?? {}).length === Object.keys(b.colors ?? {}).length &&
|
|
159
|
+
Object.entries(a.colors ?? {}).every(([name, value]) => b.colors?.[name] === value) &&
|
|
139
160
|
a.series.length === b.series.length &&
|
|
140
161
|
a.series.every((color, i) => color === b.series[i]) &&
|
|
141
162
|
a.fontFamily === b.fontFamily &&
|
|
@@ -161,7 +182,7 @@ function sameChartTokens(a: ChartTokens | null, b: ChartTokens | null): boolean
|
|
|
161
182
|
* attributes alone leaves the chart painted in the previous palette until a
|
|
162
183
|
* reload — the tokens in the DOM are new, the canvas is not.
|
|
163
184
|
*/
|
|
164
|
-
function watchChartTokens(onChange: () => void): () => void {
|
|
185
|
+
function watchChartTokens(onChange: () => void, scope?: Element | null): () => void {
|
|
165
186
|
if (typeof document === "undefined") return () => {}
|
|
166
187
|
// Style edits arrive as bursts of mutations; coalesce them into one read, and
|
|
167
188
|
// take it after the next frame so a swapped stylesheet has been applied.
|
|
@@ -176,8 +197,11 @@ function watchChartTokens(onChange: () => void): () => void {
|
|
|
176
197
|
|
|
177
198
|
const themeObserver = new MutationObserver(schedule)
|
|
178
199
|
const attributeFilter = ["class", "style", "data-theme"]
|
|
179
|
-
|
|
180
|
-
|
|
200
|
+
// Only the chart and its ancestors can change its inherited theme. Never observe
|
|
201
|
+
// the document subtree: canvas/tooltip/rendering mutations would trigger every chart.
|
|
202
|
+
for (let node: Element | null = scope ?? document.body ?? document.documentElement; node; node = node.parentElement) {
|
|
203
|
+
themeObserver.observe(node, { attributes: true, attributeFilter })
|
|
204
|
+
}
|
|
181
205
|
|
|
182
206
|
const styleObserver = new MutationObserver(schedule)
|
|
183
207
|
if (document.head) {
|
|
@@ -265,22 +289,23 @@ function buildChartTheme(tokens: ChartTokens): Record<string, unknown> {
|
|
|
265
289
|
* components that build an ECharts option from token colours themselves (the
|
|
266
290
|
* typed charts), so their palette follows light/dark like `EChart`'s theme.
|
|
267
291
|
*/
|
|
268
|
-
function useChartTokens(): ChartTokens | null {
|
|
292
|
+
function useChartTokens(scope?: Element | null | RefObject<Element | null>): ChartTokens | null {
|
|
269
293
|
const [tokens, setTokens] = useState<ChartTokens | null>(() => readChartTokens())
|
|
270
294
|
// Compared here, not inside a `setTokens` updater: an updater that reads the DOM is
|
|
271
295
|
// replayed by React on every rebase and returns a fresh object each time, so the state
|
|
272
296
|
// never settles ("Maximum update depth exceeded" once the tokens change after mount).
|
|
273
297
|
const tokensRef = useRef(tokens)
|
|
274
298
|
useEffect(() => {
|
|
299
|
+
const target = scope && "current" in scope ? scope.current : scope
|
|
275
300
|
const sync = () => {
|
|
276
|
-
const next = readChartTokens()
|
|
301
|
+
const next = readChartTokens(target)
|
|
277
302
|
if (sameChartTokens(tokensRef.current, next)) return
|
|
278
303
|
tokensRef.current = next
|
|
279
304
|
setTokens(next)
|
|
280
305
|
}
|
|
281
306
|
sync()
|
|
282
|
-
return watchChartTokens(sync)
|
|
283
|
-
}, [])
|
|
307
|
+
return watchChartTokens(sync, target)
|
|
308
|
+
}, [scope])
|
|
284
309
|
return tokens
|
|
285
310
|
}
|
|
286
311
|
|
|
@@ -292,7 +317,7 @@ const EMBEDDED_VAR_RE = /var\(\s*(--[\w-]+)\s*\)/g
|
|
|
292
317
|
/**
|
|
293
318
|
* Turn chart colours into canvas-safe strings. `"chart-1"`〜`"chart-5"` come from
|
|
294
319
|
* the resolved series tokens; `"positive"` / `"warning"` / `"negative"` / `"neutral"` (and the area
|
|
295
|
-
* variants `"positive-fill"`, …) are read
|
|
320
|
+
* variants `"positive-fill"`, …) are read in the snapshot's inherited scope (`--positive`, …); `var(--token)` uses the same scope; any
|
|
296
321
|
* other CSS colour is normalized like the tokens are. A colour that can't be
|
|
297
322
|
* resolved comes back as `""`, which ECharts treats as "use the palette".
|
|
298
323
|
*/
|
|
@@ -303,12 +328,13 @@ function resolveChartColors(colors: readonly string[], tokens: ChartTokens | nul
|
|
|
303
328
|
// Meaning colours are the theme tokens of the same name: same path as `var(--positive)`.
|
|
304
329
|
if (TONE_NAME_RE.test(color.trim())) {
|
|
305
330
|
if (typeof document === "undefined") return ""
|
|
306
|
-
|
|
331
|
+
const target = (tokens && tokenScopes.get(tokens)) ?? document.documentElement
|
|
332
|
+
return toCanvasColor(getComputedStyle(target).getPropertyValue(`--${color.trim()}`)) ?? ""
|
|
307
333
|
}
|
|
308
334
|
if (typeof document === "undefined") return color
|
|
309
335
|
let value = color.trim()
|
|
310
336
|
const cssVar = CSS_VAR_RE.exec(value)
|
|
311
|
-
const styles = getComputedStyle(document.documentElement)
|
|
337
|
+
const styles = getComputedStyle((tokens && tokenScopes.get(tokens)) ?? document.documentElement)
|
|
312
338
|
if (cssVar) {
|
|
313
339
|
value = styles.getPropertyValue(cssVar[1]!).trim() || (cssVar[2] ?? "").trim()
|
|
314
340
|
} else if (value.includes("var(")) {
|
|
@@ -354,7 +380,7 @@ function readChartSurface(el: Element | null): string | null {
|
|
|
354
380
|
if (color) return color
|
|
355
381
|
}
|
|
356
382
|
}
|
|
357
|
-
return toCanvasColor(getComputedStyle(document.documentElement).getPropertyValue("--background"))
|
|
383
|
+
return toCanvasColor(getComputedStyle(el ?? document.documentElement).getPropertyValue("--background"))
|
|
358
384
|
}
|
|
359
385
|
|
|
360
386
|
/** Widths are bucketed so a resize only rebuilds the option when the label budget can change. */
|
|
@@ -368,9 +394,10 @@ const WIDTH_BUCKET = 40
|
|
|
368
394
|
*/
|
|
369
395
|
function useChartSurface<E extends Element>(
|
|
370
396
|
forwarded: ForwardedRef<E>,
|
|
371
|
-
tokens
|
|
372
|
-
): { ref: (el: E | null) => void; surface: string | undefined; width: number | undefined } {
|
|
397
|
+
tokens?: ChartTokens | null,
|
|
398
|
+
): { ref: (el: E | null) => void; surface: string | undefined; width: number | undefined; tokens: ChartTokens | null } {
|
|
373
399
|
const [node, setNode] = useState<E | null>(null)
|
|
400
|
+
const scopedTokens = useChartTokens(node)
|
|
374
401
|
const [surface, setSurface] = useState<string | undefined>(undefined)
|
|
375
402
|
const [width, setWidth] = useState<number | undefined>(undefined)
|
|
376
403
|
const ref = useCallback(
|
|
@@ -384,7 +411,7 @@ function useChartSurface<E extends Element>(
|
|
|
384
411
|
useEffect(() => {
|
|
385
412
|
if (!node) return
|
|
386
413
|
setSurface(readChartSurface(node) ?? undefined)
|
|
387
|
-
}, [node, tokens])
|
|
414
|
+
}, [node, tokens, scopedTokens])
|
|
388
415
|
useEffect(() => {
|
|
389
416
|
if (!node || typeof ResizeObserver === "undefined") return
|
|
390
417
|
const update = (w: number) => setWidth(Math.floor(w / WIDTH_BUCKET) * WIDTH_BUCKET)
|
|
@@ -393,7 +420,7 @@ function useChartSurface<E extends Element>(
|
|
|
393
420
|
observer.observe(node)
|
|
394
421
|
return () => observer.disconnect()
|
|
395
422
|
}, [node])
|
|
396
|
-
return { ref, surface, width }
|
|
423
|
+
return { ref, surface, width, tokens: scopedTokens }
|
|
397
424
|
}
|
|
398
425
|
|
|
399
426
|
/* ---------- inlined from @squadbase/squadkit registry/lib/chart-utils.ts (internal to @squadbase/vantage) ---------- */
|
|
@@ -3554,9 +3581,8 @@ function BarChartImpl<T extends Record<string, unknown>>(
|
|
|
3554
3581
|
}: BarChartProps<T>,
|
|
3555
3582
|
ref: ForwardedRef<HTMLDivElement>,
|
|
3556
3583
|
) {
|
|
3557
|
-
const tokens = useChartTokens()
|
|
3558
3584
|
// Highlight labels are backed by the surface the chart sits on (a card, the page).
|
|
3559
|
-
const { ref: frameRef, surface, width: plotWidth } = useChartSurface(ref
|
|
3585
|
+
const { ref: frameRef, surface, width: plotWidth, tokens } = useChartSurface(ref)
|
|
3560
3586
|
// Ticks and on-plot labels read short (¥1億); the tooltip reads the value as `format` says.
|
|
3561
3587
|
const tooltipFormatter = useMemo(() => resolveValueFormatter(valueFormatter, format), [valueFormatter, format])
|
|
3562
3588
|
const axisFormatter = useMemo(() => resolveAxisFormatter(valueFormatter, format), [valueFormatter, format])
|
package/registry/ui/bar-list.tsx
CHANGED
|
@@ -31,7 +31,7 @@ import { cn } from "@squadbase/vantage/ui"
|
|
|
31
31
|
import { ChartFrame } from "@squadbase/vantage/components"
|
|
32
32
|
import { toneForValue } from "@squadbase/vantage/components"
|
|
33
33
|
import type { ChartColor, ChartCommonProps, ChartSeriesColor, ChartThreshold } from "@squadbase/vantage/components"
|
|
34
|
-
import { createContext, useCallback, useRef } from "react"
|
|
34
|
+
import { createContext, useCallback, useRef, type RefObject } from "react"
|
|
35
35
|
import type { ChartTone } from "@squadbase/vantage/components"
|
|
36
36
|
import { DEFAULT_LOCALE, EMPTY_VALUE, formatValue, toValueFormatter, type ValueFormat } from "@squadbase/vantage/components"
|
|
37
37
|
import type { ChartColors, ValueFormatter } from "@squadbase/vantage/components"
|
|
@@ -74,10 +74,23 @@ interface ChartTokens {
|
|
|
74
74
|
border: string | null
|
|
75
75
|
/** Tooltip surface. */
|
|
76
76
|
popover: string | null
|
|
77
|
+
/** Semantic and surface dependencies used by typed chart options. Optional for hand-built tokens. */
|
|
78
|
+
colors?: Record<string, string>
|
|
79
|
+
/** Actual inherited painting surface (may change without a token changing). */
|
|
80
|
+
surface?: string | null
|
|
77
81
|
/** Tooltip text. */
|
|
78
82
|
popoverForeground: string | null
|
|
79
83
|
}
|
|
80
84
|
|
|
85
|
+
// Keep scope out of the public token shape. A resolved snapshot remembers where arbitrary
|
|
86
|
+
// var(--token) expressions must be read, without retaining unmounted charts globally.
|
|
87
|
+
const tokenScopes = new WeakMap<ChartTokens, Element>()
|
|
88
|
+
const OPTION_COLOR_TOKENS = [
|
|
89
|
+
"--background", "--card", "--muted", "--primary", "--destructive",
|
|
90
|
+
...["positive", "negative", "warning", "neutral", "info"].flatMap((tone) =>
|
|
91
|
+
["", "-fill", "-subtle"].map((suffix) => `--${tone}${suffix}`)),
|
|
92
|
+
]
|
|
93
|
+
|
|
81
94
|
let normalizeContext: CanvasRenderingContext2D | null | undefined
|
|
82
95
|
|
|
83
96
|
/** The forms `fillStyle` serializes legacy colours to, and zrender can parse. */
|
|
@@ -133,8 +146,10 @@ function readChartTokens(el?: Element | null): ChartTokens | null {
|
|
|
133
146
|
const color = read(token)
|
|
134
147
|
if (color) series.push(color)
|
|
135
148
|
}
|
|
136
|
-
|
|
149
|
+
const tokens: ChartTokens = {
|
|
137
150
|
series,
|
|
151
|
+
colors: Object.fromEntries(OPTION_COLOR_TOKENS.map((name) => [name, styles.getPropertyValue(name).trim()])),
|
|
152
|
+
surface: readChartSurface(target),
|
|
138
153
|
...(styles.fontFamily ? { fontFamily: styles.fontFamily } : {}),
|
|
139
154
|
foreground: read("--foreground"),
|
|
140
155
|
mutedForeground: read("--muted-foreground"),
|
|
@@ -142,6 +157,8 @@ function readChartTokens(el?: Element | null): ChartTokens | null {
|
|
|
142
157
|
popover: read("--popover"),
|
|
143
158
|
popoverForeground: read("--popover-foreground"),
|
|
144
159
|
}
|
|
160
|
+
tokenScopes.set(tokens, target)
|
|
161
|
+
return tokens
|
|
145
162
|
}
|
|
146
163
|
|
|
147
164
|
/** Structural equality, so a theme toggle that changes nothing doesn't re-init. */
|
|
@@ -149,6 +166,10 @@ function sameChartTokens(a: ChartTokens | null, b: ChartTokens | null): boolean
|
|
|
149
166
|
if (a === b) return true
|
|
150
167
|
if (!a || !b) return false
|
|
151
168
|
return (
|
|
169
|
+
tokenScopes.get(a) === tokenScopes.get(b) &&
|
|
170
|
+
a.surface === b.surface &&
|
|
171
|
+
Object.keys(a.colors ?? {}).length === Object.keys(b.colors ?? {}).length &&
|
|
172
|
+
Object.entries(a.colors ?? {}).every(([name, value]) => b.colors?.[name] === value) &&
|
|
152
173
|
a.series.length === b.series.length &&
|
|
153
174
|
a.series.every((color, i) => color === b.series[i]) &&
|
|
154
175
|
a.fontFamily === b.fontFamily &&
|
|
@@ -174,7 +195,7 @@ function sameChartTokens(a: ChartTokens | null, b: ChartTokens | null): boolean
|
|
|
174
195
|
* attributes alone leaves the chart painted in the previous palette until a
|
|
175
196
|
* reload — the tokens in the DOM are new, the canvas is not.
|
|
176
197
|
*/
|
|
177
|
-
function watchChartTokens(onChange: () => void): () => void {
|
|
198
|
+
function watchChartTokens(onChange: () => void, scope?: Element | null): () => void {
|
|
178
199
|
if (typeof document === "undefined") return () => {}
|
|
179
200
|
// Style edits arrive as bursts of mutations; coalesce them into one read, and
|
|
180
201
|
// take it after the next frame so a swapped stylesheet has been applied.
|
|
@@ -189,8 +210,11 @@ function watchChartTokens(onChange: () => void): () => void {
|
|
|
189
210
|
|
|
190
211
|
const themeObserver = new MutationObserver(schedule)
|
|
191
212
|
const attributeFilter = ["class", "style", "data-theme"]
|
|
192
|
-
|
|
193
|
-
|
|
213
|
+
// Only the chart and its ancestors can change its inherited theme. Never observe
|
|
214
|
+
// the document subtree: canvas/tooltip/rendering mutations would trigger every chart.
|
|
215
|
+
for (let node: Element | null = scope ?? document.body ?? document.documentElement; node; node = node.parentElement) {
|
|
216
|
+
themeObserver.observe(node, { attributes: true, attributeFilter })
|
|
217
|
+
}
|
|
194
218
|
|
|
195
219
|
const styleObserver = new MutationObserver(schedule)
|
|
196
220
|
if (document.head) {
|
|
@@ -278,22 +302,23 @@ function buildChartTheme(tokens: ChartTokens): Record<string, unknown> {
|
|
|
278
302
|
* components that build an ECharts option from token colours themselves (the
|
|
279
303
|
* typed charts), so their palette follows light/dark like `EChart`'s theme.
|
|
280
304
|
*/
|
|
281
|
-
function useChartTokens(): ChartTokens | null {
|
|
305
|
+
function useChartTokens(scope?: Element | null | RefObject<Element | null>): ChartTokens | null {
|
|
282
306
|
const [tokens, setTokens] = useState<ChartTokens | null>(() => readChartTokens())
|
|
283
307
|
// Compared here, not inside a `setTokens` updater: an updater that reads the DOM is
|
|
284
308
|
// replayed by React on every rebase and returns a fresh object each time, so the state
|
|
285
309
|
// never settles ("Maximum update depth exceeded" once the tokens change after mount).
|
|
286
310
|
const tokensRef = useRef(tokens)
|
|
287
311
|
useEffect(() => {
|
|
312
|
+
const target = scope && "current" in scope ? scope.current : scope
|
|
288
313
|
const sync = () => {
|
|
289
|
-
const next = readChartTokens()
|
|
314
|
+
const next = readChartTokens(target)
|
|
290
315
|
if (sameChartTokens(tokensRef.current, next)) return
|
|
291
316
|
tokensRef.current = next
|
|
292
317
|
setTokens(next)
|
|
293
318
|
}
|
|
294
319
|
sync()
|
|
295
|
-
return watchChartTokens(sync)
|
|
296
|
-
}, [])
|
|
320
|
+
return watchChartTokens(sync, target)
|
|
321
|
+
}, [scope])
|
|
297
322
|
return tokens
|
|
298
323
|
}
|
|
299
324
|
|
|
@@ -305,7 +330,7 @@ const EMBEDDED_VAR_RE = /var\(\s*(--[\w-]+)\s*\)/g
|
|
|
305
330
|
/**
|
|
306
331
|
* Turn chart colours into canvas-safe strings. `"chart-1"`〜`"chart-5"` come from
|
|
307
332
|
* the resolved series tokens; `"positive"` / `"warning"` / `"negative"` / `"neutral"` (and the area
|
|
308
|
-
* variants `"positive-fill"`, …) are read
|
|
333
|
+
* variants `"positive-fill"`, …) are read in the snapshot's inherited scope (`--positive`, …); `var(--token)` uses the same scope; any
|
|
309
334
|
* other CSS colour is normalized like the tokens are. A colour that can't be
|
|
310
335
|
* resolved comes back as `""`, which ECharts treats as "use the palette".
|
|
311
336
|
*/
|
|
@@ -316,12 +341,13 @@ function resolveChartColors(colors: readonly string[], tokens: ChartTokens | nul
|
|
|
316
341
|
// Meaning colours are the theme tokens of the same name: same path as `var(--positive)`.
|
|
317
342
|
if (TONE_NAME_RE.test(color.trim())) {
|
|
318
343
|
if (typeof document === "undefined") return ""
|
|
319
|
-
|
|
344
|
+
const target = (tokens && tokenScopes.get(tokens)) ?? document.documentElement
|
|
345
|
+
return toCanvasColor(getComputedStyle(target).getPropertyValue(`--${color.trim()}`)) ?? ""
|
|
320
346
|
}
|
|
321
347
|
if (typeof document === "undefined") return color
|
|
322
348
|
let value = color.trim()
|
|
323
349
|
const cssVar = CSS_VAR_RE.exec(value)
|
|
324
|
-
const styles = getComputedStyle(document.documentElement)
|
|
350
|
+
const styles = getComputedStyle((tokens && tokenScopes.get(tokens)) ?? document.documentElement)
|
|
325
351
|
if (cssVar) {
|
|
326
352
|
value = styles.getPropertyValue(cssVar[1]!).trim() || (cssVar[2] ?? "").trim()
|
|
327
353
|
} else if (value.includes("var(")) {
|
|
@@ -367,7 +393,7 @@ function readChartSurface(el: Element | null): string | null {
|
|
|
367
393
|
if (color) return color
|
|
368
394
|
}
|
|
369
395
|
}
|
|
370
|
-
return toCanvasColor(getComputedStyle(document.documentElement).getPropertyValue("--background"))
|
|
396
|
+
return toCanvasColor(getComputedStyle(el ?? document.documentElement).getPropertyValue("--background"))
|
|
371
397
|
}
|
|
372
398
|
|
|
373
399
|
/** Widths are bucketed so a resize only rebuilds the option when the label budget can change. */
|
|
@@ -381,9 +407,10 @@ const WIDTH_BUCKET = 40
|
|
|
381
407
|
*/
|
|
382
408
|
function useChartSurface<E extends Element>(
|
|
383
409
|
forwarded: ForwardedRef<E>,
|
|
384
|
-
tokens
|
|
385
|
-
): { ref: (el: E | null) => void; surface: string | undefined; width: number | undefined } {
|
|
410
|
+
tokens?: ChartTokens | null,
|
|
411
|
+
): { ref: (el: E | null) => void; surface: string | undefined; width: number | undefined; tokens: ChartTokens | null } {
|
|
386
412
|
const [node, setNode] = useState<E | null>(null)
|
|
413
|
+
const scopedTokens = useChartTokens(node)
|
|
387
414
|
const [surface, setSurface] = useState<string | undefined>(undefined)
|
|
388
415
|
const [width, setWidth] = useState<number | undefined>(undefined)
|
|
389
416
|
const ref = useCallback(
|
|
@@ -397,7 +424,7 @@ function useChartSurface<E extends Element>(
|
|
|
397
424
|
useEffect(() => {
|
|
398
425
|
if (!node) return
|
|
399
426
|
setSurface(readChartSurface(node) ?? undefined)
|
|
400
|
-
}, [node, tokens])
|
|
427
|
+
}, [node, tokens, scopedTokens])
|
|
401
428
|
useEffect(() => {
|
|
402
429
|
if (!node || typeof ResizeObserver === "undefined") return
|
|
403
430
|
const update = (w: number) => setWidth(Math.floor(w / WIDTH_BUCKET) * WIDTH_BUCKET)
|
|
@@ -406,7 +433,7 @@ function useChartSurface<E extends Element>(
|
|
|
406
433
|
observer.observe(node)
|
|
407
434
|
return () => observer.disconnect()
|
|
408
435
|
}, [node])
|
|
409
|
-
return { ref, surface, width }
|
|
436
|
+
return { ref, surface, width, tokens: scopedTokens }
|
|
410
437
|
}
|
|
411
438
|
|
|
412
439
|
/* ---------- inlined from @squadbase/squadkit registry/lib/chart-utils.ts (internal to @squadbase/vantage) ---------- */
|