@acronis-platform/ui-react 5.5.9 → 7.0.0
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/components/ui/chart/chart.js +14 -11
- package/dist/components/ui/chart/chart.js.map +1 -1
- package/dist/components/ui/chart-state/chart-state.js +47 -37
- package/dist/components/ui/chart-state/chart-state.js.map +1 -1
- package/dist/components/ui/chart-widget/chart-widget.js +21 -23
- package/dist/components/ui/chart-widget/chart-widget.js.map +1 -1
- package/dist/components/ui/data-table/data-table.js +174 -173
- package/dist/components/ui/data-table/data-table.js.map +1 -1
- package/dist/components/ui/dialog/dialog.js +52 -38
- package/dist/components/ui/dialog/dialog.js.map +1 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu.js +8 -2
- package/dist/components/ui/dropdown-menu/dropdown-menu.js.map +1 -1
- package/dist/components/ui/page-header/page-header.js +4 -4
- package/dist/components/ui/page-header/page-header.js.map +1 -1
- package/dist/components/ui/pie-chart/pie-chart.js +88 -84
- package/dist/components/ui/pie-chart/pie-chart.js.map +1 -1
- package/dist/index.js +45 -46
- package/dist/llms/chart-state.md +81 -164
- package/dist/llms/chart-widget.md +27 -25
- package/dist/llms/data-table.md +24 -14
- package/dist/llms/dialog-root.md +40 -13
- package/dist/llms/dialog.md +109 -14
- package/dist/llms/dropdown-menu.md +68 -3
- package/dist/llms/page-header.md +68 -11
- package/dist/llms/stepper-item.md +10 -7
- package/dist/llms.txt +4 -4
- package/dist/react.js +45 -46
- package/dist/src/components/ui/chart/chart.d.ts +3 -3
- package/dist/src/components/ui/chart-state/chart-state.d.ts +5 -14
- package/dist/src/components/ui/chart-state/index.d.ts +0 -1
- package/dist/src/components/ui/chart-widget/chart-widget.d.ts +10 -15
- package/dist/src/components/ui/data-table/data-table.d.ts +11 -5
- package/dist/src/components/ui/data-table/data-table.docs.d.ts +10 -4
- package/dist/src/components/ui/dialog/dialog.d.ts +10 -0
- package/dist/src/components/ui/dialog/dialog.docs.d.ts +7 -1
- package/dist/src/components/ui/dropdown-menu/dropdown-menu.d.ts +8 -1
- package/dist/src/components/ui/dropdown-menu/index.d.ts +1 -1
- package/dist/src/components/ui/page-header/page-header.d.ts +3 -1
- package/dist/src/components/ui/pie-chart/pie-chart.d.ts +1 -1
- package/dist/ui-react.css +1 -1
- package/package.json +13 -13
- package/dist/components/ui/chart-state/chart-state-illustrations.js +0 -412
- package/dist/components/ui/chart-state/chart-state-illustrations.js.map +0 -1
- package/dist/src/components/ui/chart-state/chart-state-illustrations.d.ts +0 -6
|
@@ -87,11 +87,11 @@ var m = {
|
|
|
87
87
|
lg: "max-h-[112px]",
|
|
88
88
|
xl: "max-h-[112px]"
|
|
89
89
|
}, g = {
|
|
90
|
-
xs: "max-h-[
|
|
91
|
-
sm: "max-h-[
|
|
92
|
-
base: "max-h-[
|
|
93
|
-
lg: "max-h-[
|
|
94
|
-
xl: "max-h-[
|
|
90
|
+
xs: "max-h-[184px]",
|
|
91
|
+
sm: "max-h-[216px]",
|
|
92
|
+
base: "max-h-[248px]",
|
|
93
|
+
lg: "max-h-[280px]",
|
|
94
|
+
xl: "max-h-[280px]"
|
|
95
95
|
}, _ = o.createContext(null);
|
|
96
96
|
function v() {
|
|
97
97
|
let e = o.useContext(_);
|
|
@@ -193,12 +193,15 @@ var w = u.Legend;
|
|
|
193
193
|
function T({ item: n, itemConfig: r, fontSize: i, valueKey: a, valueFormatter: o, valueClassName: s }) {
|
|
194
194
|
let u = a ? n.payload?.[a] : void 0, d = u == null ? void 0 : o ? o(u) : String(u), f = r?.label ?? n.value;
|
|
195
195
|
return /* @__PURE__ */ l("div", {
|
|
196
|
-
className: "flex w-full shrink-0 items-
|
|
196
|
+
className: "flex w-full shrink-0 items-center gap-2 overflow-hidden",
|
|
197
197
|
children: [/* @__PURE__ */ l("div", {
|
|
198
|
-
className: "flex min-w-0 flex-1 items-center gap-
|
|
199
|
-
children: [
|
|
200
|
-
className: "
|
|
201
|
-
|
|
198
|
+
className: "flex min-w-0 flex-1 items-center gap-1",
|
|
199
|
+
children: [/* @__PURE__ */ c("div", {
|
|
200
|
+
className: "grid size-4 shrink-0 place-items-center [&>svg]:h-4 [&>svg]:w-4",
|
|
201
|
+
children: r?.icon ? /* @__PURE__ */ c(r.icon, {}) : /* @__PURE__ */ c("div", {
|
|
202
|
+
className: "size-2 rounded-full",
|
|
203
|
+
style: { backgroundColor: n.color }
|
|
204
|
+
})
|
|
202
205
|
}), typeof f == "string" || typeof f == "number" ? /* @__PURE__ */ c(t, {
|
|
203
206
|
className: e("min-w-0 flex-1 text-foreground", m[i]),
|
|
204
207
|
children: String(f)
|
|
@@ -253,7 +256,7 @@ function D({ className: t, ariaLabel: n = "Chart legend", hideIcon: r = !1, payl
|
|
|
253
256
|
role: "group",
|
|
254
257
|
"aria-label": n,
|
|
255
258
|
tabIndex: S ? 0 : void 0,
|
|
256
|
-
className: e("flex flex-col overflow-y-auto focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--ui-focus-primary)]", g[d], t),
|
|
259
|
+
className: e("flex flex-col gap-2 overflow-y-auto focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--ui-focus-primary)]", g[d], t),
|
|
257
260
|
children: i.map((e) => {
|
|
258
261
|
let t = `${s || e.dataKey || "value"}`;
|
|
259
262
|
return /* @__PURE__ */ c(T, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chart.js","names":[],"sources":["../../../../src/components/ui/chart/chart.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport * as RechartsPrimitive from 'recharts';\nimport type { LegendPayload } from 'recharts/types/component/DefaultLegendContent';\nimport {\n NameType,\n Payload,\n ValueType,\n} from 'recharts/types/component/DefaultTooltipContent';\nimport type { Props as LegendProps } from 'recharts/types/component/Legend';\nimport { TooltipContentProps } from 'recharts/types/component/Tooltip';\n\nimport { cn } from '@/lib/utils';\nimport { TruncateText } from '../truncate-text';\nimport {\n CHART_DEFAULT_PALETTE,\n CHART_DIVERGING_TOKENS,\n CHART_STATUS_TOKENS,\n resolveSeriesColor,\n type ChartDivergingSide,\n type ChartPalette,\n type ChartSeriesTone,\n} from './chart-palette';\n\n/**\n * Normalize a config key / data name to a CSS-safe custom-property fragment.\n *\n * A `--color-<key>` custom property is invalid when `key` contains a space or\n * other ASCII special character — the browser discards it, and the element\n * falls back to SVG's default black. This function replaces every invalid ASCII\n * character with a hyphen, collapses runs, and trims leading/trailing hyphens.\n *\n * Non-ASCII code points (U+0080+) are **preserved**: CSS Syntax Level 3\n * §4.3.7 classifies them as valid ident-code-points, so `--color-日本` and\n * `--color-Москва` are legal custom properties. Replacing them would collapse\n * all-non-ASCII keys to the empty string and silently make distinct series\n * collide on `--color-`.\n *\n * For an already-CSS-safe key (ASCII letters, digits, `-`, `_`) the output is\n * identical (identity), so existing consumers are unaffected. If the entire\n * input collapses to an empty string (e.g. all ASCII special characters),\n * `--color-` is produced — an edge case with no worse outcome than before.\n *\n * The dev-mode warning fires **once per unique unsanitized key** per page load,\n * so render loops (hover, re-render) do not flood the console.\n */\n// Tracks keys that have already triggered a dev warning — one per page load.\nconst _toCssKeyWarned = new Set<string>();\n\nexport function toCssKey(key: string): string {\n // Only strip ASCII characters that are not valid CSS ident chars.\n // U+0080+ are preserved — they are valid per CSS Syntax L3 §4.3.7.\n const safe = key\n .replace(/[^-a-zA-Z0-9_-]/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '');\n\n if (process.env.NODE_ENV !== 'production' && safe !== key && !_toCssKeyWarned.has(key)) {\n _toCssKeyWarned.add(key);\n console.warn(\n `[ui-react] Chart config key \"${key}\" is not CSS-safe and was sanitized to \"${safe}\". ` +\n `Use CSS-safe keys (ASCII letters, digits, hyphens, underscores, or non-ASCII characters) to avoid this.`\n );\n }\n\n return safe;\n}\n\nexport type ChartConfig = {\n [k in string]: {\n label?: React.ReactNode;\n icon?: React.ComponentType;\n /**\n * Which stop of the container's `palette` this series paints with. Left\n * unset, it is assigned from the series' position. See `ChartSeriesTone`.\n *\n * There is deliberately no free-form `color` here: every series colour\n * comes from the chart's palette, so a chart cannot paint a hue the design\n * system does not define. Pick a different palette on `ChartContainer`, or\n * a different stop of it with this.\n */\n tone?: ChartSeriesTone;\n };\n};\n\n/**\n * A `ChartConfig` with every series' palette colour filled in. This is what the\n * container puts on the context, so the plot, the tooltip rows and the legend\n * markers all read the same resolved colour.\n */\nexport type ResolvedChartConfig = {\n [k in string]: ChartConfig[string] & { color: string };\n};\n\n/**\n * Resolve each series' colour out of `palette` — in the palette's defined\n * order, honouring any `tone` a series named.\n *\n * Exported for tests and for the widget editor, which needs the same resolved\n * map to detect duplicate colours (`findDuplicateTones`) without re-deriving it.\n */\nexport function resolveChartColors(\n config: ChartConfig,\n palette: ChartPalette\n): ResolvedChartConfig {\n const entries = Object.entries(config);\n\n // An aliased series (`tone: { sameAs }`) doesn't take a stop of its own — it\n // is the same metric drawn a second way, so it must not consume a colour or\n // shift the series after it along the palette.\n const isAlias = (item: ChartConfig[string]) => Boolean(item.tone?.sameAs);\n const resolved: Record<string, ChartConfig[string] & { color: string }> = {};\n\n // Warn about overrides that are meaningless under a diverging palette.\n // resolveSeriesColor issues the same warnings for every other palette type,\n // but the diverging branch below bypasses it, so we replicate the check here\n // to preserve the documented dev-warning contract (see ChartSeriesTone).\n const warnDivergingMismatch = (tone: ChartSeriesTone | undefined) => {\n if (process.env.NODE_ENV !== 'production' && tone) {\n if (tone.status) {\n console.warn(\n `[ui-react] A chart series names a status (\"${tone.status}\") under the \"diverging\" palette, which has no status tones. Ignoring.`\n );\n } else if (typeof tone.slot === 'number') {\n console.warn(\n `[ui-react] A chart series pins slot ${tone.slot} under the \"diverging\" palette, whose stops are a ramp and are not individually selectable. Ignoring.`\n );\n }\n }\n };\n\n if (palette.type === 'diverging') {\n const interleavedStops = CHART_DIVERGING_TOKENS[palette.pair];\n // Derive each side's three stops from the interleaved ramp (a at even\n // indices, b at odd indices): a3, a2, a1 and b3, b2, b1.\n const sideStops: Record<ChartDivergingSide, readonly string[]> = {\n a: [interleavedStops[0], interleavedStops[2], interleavedStops[4]],\n b: [interleavedStops[1], interleavedStops[3], interleavedStops[5]],\n };\n\n // First pass: assign sided series — group by side, each group walks its\n // side's three stops strongest-first, wrapping if there are more than three.\n const sideCounters: Record<ChartDivergingSide, number> = { a: 0, b: 0 };\n const takenStops = new Set<string>();\n for (const [key, item] of entries) {\n if (isAlias(item) || !item.tone?.side) continue;\n warnDivergingMismatch(item.tone);\n const side = item.tone.side;\n const stops = sideStops[side];\n const stop = stops[sideCounters[side]++ % stops.length];\n takenStops.add(stop);\n resolved[key] = { ...item, color: stop };\n }\n\n // Second pass: assign un-sided series from the remaining interleaved stops,\n // skipping any stop already claimed by a sided series. Fall back to the\n // full interleaved ramp if all stops were consumed.\n const available = interleavedStops.filter((s) => !takenStops.has(s));\n const pool = available.length > 0 ? available : interleavedStops;\n let unsidedIdx = 0;\n for (const [key, item] of entries) {\n if (isAlias(item) || item.tone?.side) continue;\n warnDivergingMismatch(item.tone);\n resolved[key] = { ...item, color: pool[unsidedIdx++ % pool.length] };\n }\n } else {\n let index = 0;\n for (const [key, item] of entries) {\n if (isAlias(item)) continue;\n resolved[key] = {\n ...item,\n color: resolveSeriesColor(palette, { index: index++, tone: item.tone }),\n };\n }\n }\n\n // An alias may point at another alias, in any order, so a single pass over\n // the aliases can miss a chain that is fully resolvable. Keep sweeping the\n // pending set until nothing more resolves; what is left is a cycle or a\n // target that isn't in this config at all.\n const pending = new Map<string, string>();\n for (const [key, item] of entries) {\n if (isAlias(item)) {\n pending.set(key, item.tone!.sameAs as string);\n }\n }\n\n let changed = true;\n while (changed && pending.size > 0) {\n changed = false;\n for (const [key, target] of pending) {\n const targetColor = resolved[target]?.color;\n if (targetColor !== undefined) {\n resolved[key] = { ...config[key], color: targetColor };\n pending.delete(key);\n changed = true;\n }\n }\n }\n\n for (const [key, target] of pending) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n `[ui-react] Chart series \"${key}\" aliases \"${target}\", which is not a palette-assigned series in this config. Falling back to neutral.`\n );\n }\n resolved[key] = { ...config[key], color: CHART_STATUS_TOKENS.neutral };\n }\n\n // Rebuilt in the config's own order: the passes above visit non-aliases\n // first, and callers (legend order, `Object.entries` consumers) rely on the\n // order they wrote.\n return Object.fromEntries(entries.map(([key]) => [key, resolved[key]]));\n}\n\ntype ChartContextProps = {\n config: ResolvedChartConfig;\n};\n\nexport type ChartTooltipContentProps = Partial<\n TooltipContentProps<ValueType, NameType>\n> & {\n className?: string;\n hideLabel?: boolean;\n hideIndicator?: boolean;\n nameKey?: string;\n labelKey?: string;\n labelFormatter?: (\n label: TooltipContentProps<number, string>['label'],\n payload: TooltipContentProps<number, string>['payload']\n ) => React.ReactNode;\n formatter?: (\n value: number | string,\n name: string,\n item: Payload<number | string, string>,\n index: number,\n payload: ReadonlyArray<Payload<number | string, string>>\n ) => React.ReactNode;\n labelClassName?: string;\n color?: string;\n};\n\n/**\n * The legend text-size scale. `xs` is the size the legend has always rendered\n * at; the rest of the steps follow Tailwind's `text-*` scale.\n */\nexport type ChartLegendFontSize = 'xs' | 'sm' | 'base' | 'lg' | 'xl';\n\n// A static lookup rather than a `text-${size}` template: Tailwind only emits a\n// utility it can read verbatim in the source.\nconst legendFontSizeClass: Record<ChartLegendFontSize, string> = {\n xs: 'text-xs',\n sm: 'text-sm',\n base: 'text-base',\n lg: 'text-lg',\n xl: 'text-xl',\n};\n\n// Fit whole rows at every supported font size: three wrapped rows plus the\n// normal legend's vertical padding/gaps, and eight rows in list mode.\nconst defaultLegendMaxHeightClass: Record<ChartLegendFontSize, string> = {\n xs: 'max-h-[76px]',\n sm: 'max-h-[88px]',\n base: 'max-h-[100px]',\n lg: 'max-h-[112px]',\n xl: 'max-h-[112px]',\n};\n\nconst listLegendMaxHeightClass: Record<ChartLegendFontSize, string> = {\n xs: 'max-h-[128px]',\n sm: 'max-h-[160px]',\n base: 'max-h-[192px]',\n lg: 'max-h-[224px]',\n xl: 'max-h-[224px]',\n};\n\nexport type ChartLegendContentProps = {\n className?: string;\n /** Accessible name for the legend group. */\n ariaLabel?: string;\n hideIcon?: boolean;\n verticalAlign?: LegendProps['verticalAlign'];\n payload?: LegendPayload[];\n nameKey?: string;\n /**\n * Font size of the legend text — labels, and the right-hand values of the\n * `list` variant. Defaults to `'xs'`, the size the legend has always\n * rendered at.\n */\n fontSize?: ChartLegendFontSize;\n /**\n * The series config the entries take their labels and icons from. Defaults to\n * the enclosing `ChartContainer`'s — pass it only to render the legend *outside*\n * the container, which a chart type has to do when the renderer can't lay a\n * legend out inside its plot (see `Treemap`).\n */\n config?: ChartConfig;\n /**\n * `default` — horizontal wrapped chips; scrolls after three rows (76px at\n * `xs`). `list` — vertical `[dot] [label flex-1] [value]` rows; scrolls after\n * eight rows (128px at `xs`). Both caps scale with `fontSize`; `className` can\n * override the cap.\n */\n variant?: 'default' | 'list';\n /**\n * When `variant=\"list\"`, the field on `payload[i].payload` to display as the\n * right-hand value. Typically the chart's `dataKey` (the numeric series field).\n */\n valueKey?: string;\n /**\n * Format the value shown in each list entry. When omitted the raw value is\n * stringified with `String()`.\n */\n valueFormatter?: (value: string | number) => string;\n /**\n * Extra Tailwind class(es) applied to the right-hand value in a list entry,\n * merged on top of the default colour (`text-[var(--ui-text-on-surface-link-idle)]`).\n * Pass a replacement colour class to override the default; other utilities\n * (e.g. `tabular-nums`) are additive and leave the default colour in place.\n */\n valueClassName?: string;\n};\n\n/** Accessible-name prop shared by chart components that render a legend. */\nexport interface ChartLegendAriaLabelProps {\n /**\n * Accessible name for this chart's legend. Defaults to `Chart legend`; give\n * each chart a distinct name when several legends appear on the same page.\n */\n legendAriaLabel?: string;\n}\n\nconst ChartContext = React.createContext<ChartContextProps | null>(null);\n\nfunction useChart() {\n const context = React.useContext(ChartContext);\n\n if (!context) {\n throw new Error('useChart must be used within a <ChartContainer />');\n }\n\n return context;\n}\n\nexport interface ChartContainerProps extends React.ComponentProps<'div'> {\n /**\n * The series this chart draws, keyed by data field.\n *\n * Declare exactly what the chart plots. A stop is assigned by an entry's\n * position here, not by which series end up rendered, so an entry for a\n * series the chart doesn't draw still consumes a colour and shifts the ones\n * that follow it. (That is deliberate: a series then keeps its colour when a\n * sibling is toggled off, instead of the whole chart recolouring.)\n */\n config: ChartConfig;\n /**\n * The dataviz palette this chart's series are painted from. Series take a\n * stop of it in the palette's defined order, or the one their `tone` names.\n * Defaults to `categorical` — there is no \"no palette\" state.\n */\n palette?: ChartPalette;\n children: React.ComponentProps<\n typeof RechartsPrimitive.ResponsiveContainer\n >['children'];\n}\n\nconst ChartContainer = React.forwardRef<HTMLDivElement, ChartContainerProps>(\n function ChartContainer(\n { id, className, children, config, palette = CHART_DEFAULT_PALETTE, ...props },\n ref\n ) {\n const uniqueId = React.useId();\n const chartId = `chart-${id || uniqueId.replace(/:/g, '')}`;\n // Resolved once and shared with the context, so the tooltip rows and legend\n // markers read the same colors the plot paints with.\n const resolvedConfig = React.useMemo(\n () => resolveChartColors(config, palette),\n [config, palette]\n );\n\n return (\n <ChartContext.Provider value={{ config: resolvedConfig }}>\n <div\n ref={ref}\n id={id}\n data-slot=\"chart\"\n data-chart={chartId}\n // The `[stroke='#fff']` rules undo recharts' hardcoded white outlines —\n // on pie/radial sectors, line dots and funnel trapezoids. They don't\n // follow the theme, so in dark mode they read as a light hairline around\n // every segment.\n className={cn(\n \"[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden [&_.recharts-trapezoid[stroke='#fff']]:stroke-transparent\",\n // A `<Brush>` traveller is the one `.recharts-layer` that is a real\n // control (`role=\"slider\"`, tabbable, arrow-key driven), so the blanket\n // `outline-hidden` above would leave it with no focus indicator. Restore\n // one — the `:focus-visible` pseudo-class outranks that rule, so this\n // wins regardless of the order Tailwind emits them in. An `outline`\n // rather than the usual `ring`: box-shadow doesn't paint on SVG children.\n '[&_.recharts-brush-traveller:focus-visible]:[outline:3px_solid_var(--ui-focus-primary)] [&_.recharts-brush-traveller:focus-visible]:[outline-offset:1px]',\n // recharts anchors its axis tick text with the *direction-relative*\n // SVG keywords (`text-anchor: start|end`, see `getTickTextAnchor` in\n // `CartesianAxis`), while placing every mark at a computed physical\n // coordinate. Inheriting `direction: rtl` therefore flips only the\n // text: an `end`-anchored Y tick mirrors about its anchor and lands\n // inside the plot (measured: +24px to +39px), rotated ticks shift\n // ~+15px, and a `ReferenceLine` label at the right edge overflows the\n // surface. Pinning the surface keeps the plot's own coordinate system\n // consistent with the geometry recharts computed for it. The chrome\n // that *should* mirror — tooltip and legend — is HTML outside\n // `.recharts-surface`, so it is unaffected.\n //\n // `Treemap` is the exception: its cell labels are HTML in a\n // `<foreignObject>` *inside* the surface, put there precisely so they\n // mirror with the page (see `treemap.tsx`). That is ordinary flow\n // layout, not a computed SVG coordinate, so the pin has to stop at the\n // `<foreignObject>` boundary and hand the page's direction back.\n '[&_.recharts-surface]:[direction:ltr] rtl:[&_.recharts-surface_foreignObject]:[direction:rtl]',\n className\n )}\n {...props}\n >\n <ChartStyle id={chartId} config={resolvedConfig} />\n <RechartsPrimitive.ResponsiveContainer>\n {children}\n </RechartsPrimitive.ResponsiveContainer>\n </div>\n </ChartContext.Provider>\n );\n }\n);\n\nChartContainer.displayName = 'ChartContainer';\n\nconst ChartStyle = ({\n id,\n config,\n}: {\n id: string;\n config: ResolvedChartConfig;\n}) => {\n const entries = Object.entries(config);\n\n if (!entries.length) {\n return null;\n }\n\n // One block, no light/dark split: every colour is now a `--ui-dataviz-*`\n // token, and those are `light-dark()` pairs that follow `color-scheme`\n // themselves. The old per-theme `[data-theme='dark']` duplication existed\n // only for hand-written `theme: { light, dark }` config entries, which the\n // palette replaced.\n //\n // Rendered as a text child (not dangerouslySetInnerHTML): React sets it via\n // textContent, which the browser does not HTML-parse, so a `</style>` in a\n // colour can't break out of the tag.\n if (process.env.NODE_ENV !== 'production') {\n const seen = new Map<string, string>();\n for (const [key] of entries) {\n const safe = toCssKey(key);\n const prior = seen.get(safe);\n if (prior !== undefined) {\n console.warn(\n `[ui-react] Chart config keys \"${prior}\" and \"${key}\" both sanitize to \"--color-${safe}\". ` +\n `One will overwrite the other — use unique CSS-safe keys.`\n );\n } else {\n seen.set(safe, key);\n }\n }\n }\n\n return (\n <style>\n {`[data-chart=${id}] {\\n${entries\n .map(([key, itemConfig]) => ` --color-${toCssKey(key)}: ${itemConfig.color};`)\n .join('\\n')}\\n}`}\n </style>\n );\n};\n\nconst ChartTooltip = RechartsPrimitive.Tooltip;\n\n/** The `content` a caller passes to override the default tooltip (recharts' `Tooltip['content']`). */\nexport type ChartTooltipContentType = React.ComponentProps<\n typeof ChartTooltip\n>['content'];\n\n/**\n * What `getPayloadConfigFromPayload` resolves a payload item to — the lookup can\n * miss, so every consumer has to cope with `undefined`. Derived from the helper\n * rather than restated, so the two can't drift.\n */\ntype ChartItemConfig = ReturnType<typeof getPayloadConfigFromPayload>;\n\n/** One row of the tooltip's payload, after the `active`/empty guard. */\ntype ChartTooltipItem = NonNullable<\n ChartTooltipContentProps['payload']\n>[number];\n\n/**\n * One tooltip row. Its own three-way branching (caller formatter, config icon,\n * default dot) is what drove the nesting in `ChartTooltipContent`; as a\n * component the map body reads as a single element.\n */\nfunction ChartTooltipRow({\n item,\n index,\n payload,\n itemConfig,\n indicatorColor,\n hideIndicator,\n formatter,\n}: {\n item: ChartTooltipItem;\n index: number;\n payload: NonNullable<ChartTooltipContentProps['payload']>;\n itemConfig: ChartItemConfig;\n indicatorColor?: string;\n hideIndicator: boolean;\n formatter: ChartTooltipContentProps['formatter'];\n}) {\n return (\n <div className=\"flex w-full flex-wrap items-center gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground\">\n {formatter && item?.value !== undefined && item.name ? (\n formatter(item.value, item.name, item, index, payload)\n ) : (\n <>\n {itemConfig?.icon ? (\n <itemConfig.icon />\n ) : (\n !hideIndicator && (\n // A row is always dotted, whatever marker the legend\n // gives that series.\n <div\n className=\"h-2.5 w-2.5 shrink-0 rounded-full\"\n style={{ backgroundColor: indicatorColor }}\n />\n )\n )}\n {/*\n * `gap-4` puts a floor under the name/value separation.\n * `justify-between` alone only separates them while the\n * container has free space to distribute — which the tooltip's\n * `min-w-[8rem]` guarantees for a short value and not for a\n * long one (a currency `tickFormatter`, a `labelFormatter`\n * with units, a value plus its share), where the two would\n * otherwise butt up against each other. A minimum, so a row\n * with slack renders as before.\n */}\n <div className=\"flex flex-1 items-center justify-between gap-4 leading-none\">\n <span className=\"text-muted-foreground\">\n {itemConfig?.label || item.name}\n </span>\n {item.value != null && (\n <span className=\"font-medium tabular-nums text-foreground\">\n {item.value.toLocaleString()}\n </span>\n )}\n </div>\n </>\n )}\n </div>\n );\n}\n\nfunction ChartTooltipContent({\n active,\n payload,\n label,\n className,\n hideLabel = false,\n hideIndicator = false,\n labelFormatter,\n formatter,\n labelClassName,\n color,\n nameKey,\n labelKey,\n}: ChartTooltipContentProps) {\n const { config } = useChart();\n\n const tooltipLabel = React.useMemo(() => {\n if (hideLabel || !payload?.length) {\n return null;\n }\n\n const [item] = payload;\n const key = `${labelKey || item?.dataKey || item?.name || 'value'}`;\n const itemConfig = getPayloadConfigFromPayload(config, item, key);\n const value = (() => {\n const v =\n !labelKey && typeof label === 'string'\n ? (config[label as keyof typeof config]?.label ?? label)\n : itemConfig?.label;\n\n return typeof v === 'string' || typeof v === 'number' ? v : undefined;\n })();\n\n if (labelFormatter) {\n return (\n <div className={cn('font-medium', labelClassName)}>\n {labelFormatter(value, payload)}\n </div>\n );\n }\n\n if (!value) {\n return null;\n }\n\n return <div className={cn('font-medium', labelClassName)}>{value}</div>;\n }, [\n label,\n labelFormatter,\n payload,\n hideLabel,\n labelClassName,\n config,\n labelKey,\n ]);\n\n if (!active || !payload?.length) {\n return null;\n }\n\n return (\n <div\n className={cn(\n 'grid min-w-[8rem] items-start gap-1.5 rounded-[var(--ui-tooltip-container-border-radius)] border border-border bg-background px-[var(--ui-tooltip-container-padding-x)] py-[var(--ui-tooltip-container-padding-y)] text-xs text-foreground shadow-md',\n className\n )}\n >\n {tooltipLabel}\n <div className=\"grid gap-1.5\">\n {payload.map((item, index) => {\n const key = `${nameKey || item.name || item.dataKey || 'value'}`;\n\n return (\n <ChartTooltipRow\n key={key}\n item={item}\n index={index}\n payload={payload}\n itemConfig={getPayloadConfigFromPayload(config, item, key)}\n indicatorColor={color || item.payload.fill || item.color}\n hideIndicator={hideIndicator}\n formatter={formatter}\n />\n );\n })}\n </div>\n </div>\n );\n}\n\nconst ChartLegend = RechartsPrimitive.Legend;\n\n/**\n * One entry in the `variant=\"list\"` legend: `[dot] [label flex-1] [value]`.\n *\n * The dot is always a circle, regardless of the series' recharts type — the\n * list layout is used by part-to-whole charts where each entry has a fill color.\n */\nfunction ChartLegendListEntry({\n item,\n itemConfig,\n fontSize,\n valueKey,\n valueFormatter,\n valueClassName,\n}: {\n item: LegendPayload;\n itemConfig: ChartItemConfig;\n fontSize: ChartLegendFontSize;\n valueKey?: string;\n valueFormatter?: (value: string | number) => string;\n valueClassName?: string;\n}) {\n const raw = valueKey\n ? (item.payload as Record<string, unknown> | undefined)?.[valueKey]\n : undefined;\n const displayValue =\n raw != null\n ? valueFormatter\n ? valueFormatter(raw as string | number)\n : String(raw)\n : undefined;\n const label = itemConfig?.label ?? item.value;\n\n return (\n <div className=\"flex w-full shrink-0 items-start gap-2 overflow-hidden\">\n <div className=\"flex min-w-0 flex-1 items-center gap-2\">\n {itemConfig?.icon ? (\n <itemConfig.icon />\n ) : (\n <div\n className=\"h-2.5 w-2.5 shrink-0 rounded-full\"\n style={{ backgroundColor: item.color }}\n />\n )}\n {typeof label === 'string' || typeof label === 'number' ? (\n <TruncateText\n className={cn(\n 'min-w-0 flex-1 text-foreground',\n legendFontSizeClass[fontSize]\n )}\n >\n {String(label)}\n </TruncateText>\n ) : (\n <span\n className={cn(\n 'min-w-0 flex-1 truncate text-foreground',\n legendFontSizeClass[fontSize]\n )}\n >\n {label}\n </span>\n )}\n </div>\n {displayValue != null && (\n <span\n className={cn(\n 'shrink-0 font-semibold text-[var(--ui-text-on-surface-link-idle)]',\n legendFontSizeClass[fontSize],\n valueClassName\n )}\n >\n {displayValue}\n </span>\n )}\n </div>\n );\n}\n\n/**\n * One legend entry. Split out so the marker it has to derive from the payload\n * sits next to the markup that uses it, instead of above a nested map body.\n */\nfunction ChartLegendEntry({\n item,\n itemConfig,\n hideIcon,\n}: {\n item: LegendPayload;\n itemConfig: ChartItemConfig;\n hideIcon: boolean;\n}) {\n return (\n <div\n className={cn(\n 'flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground'\n )}\n >\n {itemConfig?.icon && !hideIcon ? (\n <itemConfig.icon />\n ) : (\n <div\n className=\"h-2.5 w-2.5 shrink-0 rounded-full\"\n style={{ backgroundColor: item.color }}\n />\n )}\n {/*\n * Falls back to the series key, the way the tooltip row and the\n * treemap's on-cell label both do. `label` is optional on a\n * `ChartConfig` entry, and `getPayloadConfigFromPayload` can miss\n * entirely, so without this an entry renders as a marker with no\n * text — worst on the charts whose legend is the only place a series\n * is named.\n */}\n {itemConfig?.label ?? item.value}\n </div>\n );\n}\n\nfunction ChartLegendContent({\n className,\n ariaLabel = 'Chart legend',\n hideIcon = false,\n payload,\n verticalAlign = 'bottom',\n nameKey,\n config: configFromProps,\n variant = 'default',\n fontSize = 'xs',\n valueKey,\n valueFormatter,\n valueClassName,\n}: ChartLegendContentProps) {\n // The context read directly rather than through `useChart()`: a caller that\n // passes its own `config` is rendering the legend outside the container, where\n // the context is absent by design rather than by mistake. Every other caller\n // still gets `useChart()`'s error — the legend has no way to label itself\n // without a config, and failing loudly beats rendering an empty row.\n const contextConfig = React.useContext(ChartContext)?.config;\n const config = configFromProps ?? contextConfig;\n const legendRef = React.useRef<HTMLDivElement>(null);\n const [isScrollable, setIsScrollable] = React.useState(false);\n\n React.useEffect(() => {\n const element = legendRef.current;\n if (!element) return;\n\n const measureOverflow = () => {\n const next = element.scrollHeight > element.clientHeight;\n setIsScrollable((current) => (current === next ? current : next));\n };\n\n measureOverflow();\n\n const resizeObserver =\n typeof ResizeObserver === 'undefined'\n ? undefined\n : new ResizeObserver(measureOverflow);\n resizeObserver?.observe(element);\n\n const mutationObserver =\n typeof MutationObserver === 'undefined'\n ? undefined\n : new MutationObserver(measureOverflow);\n mutationObserver?.observe(element, {\n childList: true,\n characterData: true,\n subtree: true,\n });\n\n window.addEventListener('resize', measureOverflow);\n return () => {\n resizeObserver?.disconnect();\n mutationObserver?.disconnect();\n window.removeEventListener('resize', measureOverflow);\n };\n }, [className, fontSize, payload?.length, variant]);\n\n if (!config) {\n throw new Error(\n 'ChartLegendContent must be used within a <ChartContainer /> or given a `config` prop'\n );\n }\n\n if (!payload?.length) {\n return null;\n }\n\n if (variant === 'list') {\n return (\n <div\n data-slot=\"chart-legend\"\n ref={legendRef}\n role=\"group\"\n aria-label={ariaLabel}\n tabIndex={isScrollable ? 0 : undefined}\n className={cn(\n 'flex flex-col overflow-y-auto focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--ui-focus-primary)]',\n listLegendMaxHeightClass[fontSize],\n className\n )}\n >\n {payload.map((item) => {\n const key = `${nameKey || item.dataKey || 'value'}`;\n return (\n <ChartLegendListEntry\n key={item.value}\n item={item}\n itemConfig={getPayloadConfigFromPayload(config, item, key)}\n fontSize={fontSize}\n valueKey={valueKey}\n valueFormatter={valueFormatter}\n valueClassName={valueClassName}\n />\n );\n })}\n </div>\n );\n }\n\n return (\n <div\n data-slot=\"chart-legend\"\n ref={legendRef}\n role=\"group\"\n aria-label={ariaLabel}\n tabIndex={isScrollable ? 0 : undefined}\n className={cn(\n // Wraps rather than overflowing: a legend with many entries (a treemap's\n // one-per-tile, a pie's one-per-slice) is wider than the chart on a narrow\n // surface, and a row that can't wrap paints past the chart's edge. The\n // column gap is unchanged, so a legend that already fits on one row keeps\n // its exact layout.\n 'flex flex-wrap items-center justify-center gap-x-6 gap-y-2 overflow-y-auto focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--ui-focus-primary)]',\n verticalAlign === 'top' ? 'pb-3' : 'pt-3',\n defaultLegendMaxHeightClass[fontSize],\n // Stated on the wrapper even at the default ('xs'): default-variant\n // entries take their size by inheritance, and a legend rendered outside\n // a `ChartContainer` (see `Treemap`) has no container `text-xs` to fall\n // back on.\n legendFontSizeClass[fontSize],\n className\n )}\n >\n {payload.map((item) => {\n const key = `${nameKey || item.dataKey || 'value'}`;\n\n return (\n <ChartLegendEntry\n key={item.value}\n item={item}\n itemConfig={getPayloadConfigFromPayload(config, item, key)}\n hideIcon={hideIcon}\n />\n );\n })}\n </div>\n );\n}\n\n// Helper to extract item config from a payload.\nfunction getPayloadConfigFromPayload(\n config: ChartConfig,\n payload: unknown,\n key: string\n) {\n if (typeof payload !== 'object' || payload === null) {\n return undefined;\n }\n\n const payloadPayload =\n 'payload' in payload &&\n typeof payload.payload === 'object' &&\n payload.payload !== null\n ? payload.payload\n : undefined;\n\n let configLabelKey: string = key;\n\n if (\n key in payload &&\n typeof payload[key as keyof typeof payload] === 'string'\n ) {\n configLabelKey = payload[key as keyof typeof payload] as string;\n } else if (\n payloadPayload &&\n key in payloadPayload &&\n typeof payloadPayload[key as keyof typeof payloadPayload] === 'string'\n ) {\n configLabelKey = payloadPayload[\n key as keyof typeof payloadPayload\n ] as string;\n }\n\n return configLabelKey in config\n ? config[configLabelKey]\n : config[key as keyof typeof config];\n}\n\nexport {\n ChartContainer,\n ChartTooltip,\n ChartTooltipContent,\n ChartLegend,\n ChartLegendContent,\n ChartStyle,\n};\n"],"mappings":";;;;;;;;AAgDA,IAAM,oBAAkB,IAAI,IAAY;AAExC,SAAgB,EAAS,GAAqB;CAG5C,IAAM,IAAO,EACV,QAAQ,sBAAsB,GAAG,CAAC,CAClC,QAAQ,OAAO,GAAG,CAAC,CACnB,QAAQ,UAAU,EAAE;CAUvB,OARA,QAAA,IAAA,aAA6B,gBAAgB,MAAS,KAAO,CAAC,EAAgB,IAAI,CAAG,MACnF,EAAgB,IAAI,CAAG,GACvB,QAAQ,KACN,gCAAgC,EAAI,0CAA0C,EAAK,2GAErF,IAGK;AACT;AAmCA,SAAgB,EACd,GACA,GACqB;CACrB,IAAM,IAAU,OAAO,QAAQ,CAAM,GAK/B,KAAW,MAA8B,EAAQ,EAAK,MAAM,QAC5D,IAAoE,CAAC,GAMrE,KAAyB,MAAsC;EACnE,AAAA,QAAA,IAAA,aAA6B,gBAAgB,MACvC,EAAK,SACP,QAAQ,KACN,8CAA8C,EAAK,OAAO,uEAC5D,IACS,OAAO,EAAK,QAAS,YAC9B,QAAQ,KACN,uCAAuC,EAAK,KAAK,sGACnD;CAGN;CAEA,IAAI,EAAQ,SAAS,aAAa;EAChC,IAAM,IAAmB,EAAuB,EAAQ,OAGlD,IAA2D;GAC/D,GAAG;IAAC,EAAiB;IAAI,EAAiB;IAAI,EAAiB;GAAE;GACjE,GAAG;IAAC,EAAiB;IAAI,EAAiB;IAAI,EAAiB;GAAE;EACnE,GAIM,IAAmD;GAAE,GAAG;GAAG,GAAG;EAAE,GAChE,oBAAa,IAAI,IAAY;EACnC,KAAK,IAAM,CAAC,GAAK,MAAS,GAAS;GACjC,IAAI,EAAQ,CAAI,KAAK,CAAC,EAAK,MAAM,MAAM;GACvC,EAAsB,EAAK,IAAI;GAC/B,IAAM,IAAO,EAAK,KAAK,MACjB,IAAQ,EAAU,IAClB,IAAO,EAAM,EAAa,EAAK,KAAK,EAAM;GAEhD,AADA,EAAW,IAAI,CAAI,GACnB,EAAS,KAAO;IAAE,GAAG;IAAM,OAAO;GAAK;EACzC;EAKA,IAAM,IAAY,EAAiB,QAAQ,MAAM,CAAC,EAAW,IAAI,CAAC,CAAC,GAC7D,IAAO,EAAU,SAAS,IAAI,IAAY,GAC5C,IAAa;EACjB,KAAK,IAAM,CAAC,GAAK,MAAS,GACpB,EAAQ,CAAI,KAAK,EAAK,MAAM,SAChC,EAAsB,EAAK,IAAI,GAC/B,EAAS,KAAO;GAAE,GAAG;GAAM,OAAO,EAAK,MAAe,EAAK;EAAQ;CAEvE,OAAO;EACL,IAAI,IAAQ;EACZ,KAAK,IAAM,CAAC,GAAK,MAAS,GACpB,EAAQ,CAAI,MAChB,EAAS,KAAO;GACd,GAAG;GACH,OAAO,EAAmB,GAAS;IAAE,OAAO;IAAS,MAAM,EAAK;GAAK,CAAC;EACxE;CAEJ;CAMA,IAAM,oBAAU,IAAI,IAAoB;CACxC,KAAK,IAAM,CAAC,GAAK,MAAS,GACxB,AAAI,EAAQ,CAAI,KACd,EAAQ,IAAI,GAAK,EAAK,KAAM,MAAgB;CAIhD,IAAI,IAAU;CACd,OAAO,KAAW,EAAQ,OAAO,IAAG;EAClC,IAAU;EACV,KAAK,IAAM,CAAC,GAAK,MAAW,GAAS;GACnC,IAAM,IAAc,EAAS,EAAO,EAAE;GACtC,AAAI,MAAgB,KAAA,MAClB,EAAS,KAAO;IAAE,GAAG,EAAO;IAAM,OAAO;GAAY,GACrD,EAAQ,OAAO,CAAG,GAClB,IAAU;EAEd;CACF;CAEA,KAAK,IAAM,CAAC,GAAK,MAAW,GAM1B,AALA,QAAA,IAAA,aAA6B,gBAC3B,QAAQ,KACN,4BAA4B,EAAI,aAAa,EAAO,mFACtD,GAEF,EAAS,KAAO;EAAE,GAAG,EAAO;EAAM,OAAO,EAAoB;CAAQ;CAMvE,OAAO,OAAO,YAAY,EAAQ,KAAK,CAAC,OAAS,CAAC,GAAK,EAAS,EAAI,CAAC,CAAC;AACxE;AAqCA,IAAM,IAA2D;CAC/D,IAAI;CACJ,IAAI;CACJ,MAAM;CACN,IAAI;CACJ,IAAI;AACN,GAIM,IAAmE;CACvE,IAAI;CACJ,IAAI;CACJ,MAAM;CACN,IAAI;CACJ,IAAI;AACN,GAEM,IAAgE;CACpE,IAAI;CACJ,IAAI;CACJ,MAAM;CACN,IAAI;CACJ,IAAI;AACN,GA0DM,IAAe,EAAM,cAAwC,IAAI;AAEvE,SAAS,IAAW;CAClB,IAAM,IAAU,EAAM,WAAW,CAAY;CAE7C,IAAI,CAAC,GACH,MAAU,MAAM,mDAAmD;CAGrE,OAAO;AACT;AAwBA,IAAM,IAAiB,EAAM,WAC3B,SACE,EAAE,OAAI,cAAW,aAAU,WAAQ,aAAU,GAAuB,GAAG,KACvE,GACA;CACF,IAAM,IAAW,EAAM,MAAM,GACvB,IAAU,SAAS,KAAM,EAAS,QAAQ,MAAM,EAAE,KAGlD,IAAiB,EAAM,cACrB,EAAmB,GAAQ,CAAO,GACxC,CAAC,GAAQ,CAAO,CAClB;CAEA,OACE,kBAAC,EAAa,UAAd;EAAuB,OAAO,EAAE,QAAQ,EAAe;EACrD,UAAA,kBAAC,OAAD;GACO;GACD;GACJ,aAAU;GACV,cAAY;GAKZ,WAAW,EACT,ytBAOA,4JAkBA,iGACA,CACF;GACA,GAAI;GAtCN,UAAA,CAwCE,kBAAC,GAAD;IAAY,IAAI;IAAS,QAAQ;GAAiB,CAAA,GAClD,kBAAC,EAAkB,qBAAnB,EACG,YACoC,CAAA,CACpC;;CACgB,CAAA;AAEzB,CACF;AAEA,EAAe,cAAc;AAE7B,IAAM,KAAc,EAClB,OACA,gBAII;CACJ,IAAM,IAAU,OAAO,QAAQ,CAAM;CAErC,IAAI,CAAC,EAAQ,QACX,OAAO;CAYT,IAAA,QAAA,IAAA,aAA6B,cAAc;EACzC,IAAM,oBAAO,IAAI,IAAoB;EACrC,KAAK,IAAM,CAAC,MAAQ,GAAS;GAC3B,IAAM,IAAO,EAAS,CAAG,GACnB,IAAQ,EAAK,IAAI,CAAI;GAC3B,AAAI,MAAU,KAAA,IAMZ,EAAK,IAAI,GAAM,CAAG,IALlB,QAAQ,KACN,iCAAiC,EAAM,SAAS,EAAI,8BAA8B,EAAK,4DAEzF;EAIJ;CACF;CAEA,OACE,kBAAC,SAAD,EAAA,UACG,eAAe,EAAG,OAAO,EACvB,KAAK,CAAC,GAAK,OAAgB,aAAa,EAAS,CAAG,EAAE,IAAI,EAAW,MAAM,EAAE,CAAC,CAC9E,KAAK,IAAI,EAAE,KACT,CAAA;AAEX,GAEM,IAAe,EAAkB;AAwBvC,SAAS,EAAgB,EACvB,SACA,UACA,YACA,eACA,mBACA,kBACA,gBASC;CACD,OACE,kBAAC,OAAD;EAAK,WAAU;EACZ,UAAA,KAAa,GAAM,UAAU,KAAA,KAAa,EAAK,OAC9C,EAAU,EAAK,OAAO,EAAK,MAAM,GAAM,GAAO,CAAO,IAErD,kBAAA,GAAA,EAAA,UAAA,CACG,GAAY,OACX,kBAAC,EAAW,MAAZ,CAAkB,CAAA,IAElB,CAAC,KAGC,kBAAC,OAAD;GACE,WAAU;GACV,OAAO,EAAE,iBAAiB,EAAe;EAC1C,CAAA,GAaL,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACE,kBAAC,QAAD;IAAM,WAAU;IACb,UAAA,GAAY,SAAS,EAAK;GACvB,CAAA,GACL,EAAK,SAAS,QACb,kBAAC,QAAD;IAAM,WAAU;IACb,UAAA,EAAK,MAAM,eAAe;GACvB,CAAA,CAEL;EACL,CAAA,CAAA,EAAA,CAAA;CAED,CAAA;AAET;AAEA,SAAS,EAAoB,EAC3B,WACA,YACA,UACA,cACA,eAAY,IACZ,mBAAgB,IAChB,mBACA,cACA,mBACA,UACA,YACA,eAC2B;CAC3B,IAAM,EAAE,cAAW,EAAS,GAEtB,IAAe,EAAM,cAAc;EACvC,IAAI,KAAa,CAAC,GAAS,QACzB,OAAO;EAGT,IAAM,CAAC,KAAQ,GACT,IAAM,GAAG,KAAY,GAAM,WAAW,GAAM,QAAQ,WACpD,IAAa,EAA4B,GAAQ,GAAM,CAAG,GAC1D,WAAe;GACnB,IAAM,IACJ,CAAC,KAAY,OAAO,KAAU,WACzB,EAAO,EAA6B,EAAE,SAAS,IAChD,GAAY;GAElB,OAAO,OAAO,KAAM,YAAY,OAAO,KAAM,WAAW,IAAI,KAAA;EAC9D,EAAA,CAAG;EAcH,OAZI,IAEA,kBAAC,OAAD;GAAK,WAAW,EAAG,eAAe,CAAc;GAC7C,UAAA,EAAe,GAAO,CAAO;EAC3B,CAAA,IAIJ,IAIE,kBAAC,OAAD;GAAK,WAAW,EAAG,eAAe,CAAc;GAAI,UAAA;EAAW,CAAA,IAH7D;CAIX,GAAG;EACD;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CAMD,OAJI,CAAC,KAAU,CAAC,GAAS,SAChB,OAIP,kBAAC,OAAD;EACE,WAAW,EACT,wPACA,CACF;EAJF,UAAA,CAMG,GACD,kBAAC,OAAD;GAAK,WAAU;GACZ,UAAA,EAAQ,KAAK,GAAM,MAAU;IAC5B,IAAM,IAAM,GAAG,KAAW,EAAK,QAAQ,EAAK,WAAW;IAEvD,OACE,kBAAC,GAAD;KAEQ;KACC;KACE;KACT,YAAY,EAA4B,GAAQ,GAAM,CAAG;KACzD,gBAAgB,KAAS,EAAK,QAAQ,QAAQ,EAAK;KACpC;KACJ;IACZ,GARM,CAQN;GAEL,CAAC;EACE,CAAA,CACF;;AAET;AAEA,IAAM,IAAc,EAAkB;AAQtC,SAAS,EAAqB,EAC5B,SACA,eACA,aACA,aACA,mBACA,qBAQC;CACD,IAAM,IAAM,IACP,EAAK,UAAkD,KACxD,KAAA,GACE,IACJ,KAAO,OAIH,KAAA,IAHA,IACE,EAAe,CAAsB,IACrC,OAAO,CAAG,GAEZ,IAAQ,GAAY,SAAS,EAAK;CAExC,OACE,kBAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CACE,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACG,GAAY,OACX,kBAAC,EAAW,MAAZ,CAAkB,CAAA,IAElB,kBAAC,OAAD;IACE,WAAU;IACV,OAAO,EAAE,iBAAiB,EAAK,MAAM;GACtC,CAAA,GAEF,OAAO,KAAU,YAAY,OAAO,KAAU,WAC7C,kBAAC,GAAD;IACE,WAAW,EACT,kCACA,EAAoB,EACtB;IAEC,UAAA,OAAO,CAAK;GACD,CAAA,IAEd,kBAAC,QAAD;IACE,WAAW,EACT,2CACA,EAAoB,EACtB;IAEC,UAAA;GACG,CAAA,CAEL;EACJ,CAAA,GAAA,KAAgB,QACf,kBAAC,QAAD;GACE,WAAW,EACT,qEACA,EAAoB,IACpB,CACF;GAEC,UAAA;EACG,CAAA,CAEL;;AAET;AAMA,SAAS,EAAiB,EACxB,SACA,eACA,eAKC;CACD,OACE,kBAAC,OAAD;EACE,WAAW,EACT,iFACF;EAHF,UAAA,CAKG,GAAY,QAAQ,CAAC,IACpB,kBAAC,EAAW,MAAZ,CAAkB,CAAA,IAElB,kBAAC,OAAD;GACE,WAAU;GACV,OAAO,EAAE,iBAAiB,EAAK,MAAM;EACtC,CAAA,GAUF,GAAY,SAAS,EAAK,KACxB;;AAET;AAEA,SAAS,EAAmB,EAC1B,cACA,eAAY,gBACZ,cAAW,IACX,YACA,mBAAgB,UAChB,YACA,QAAQ,GACR,aAAU,WACV,cAAW,MACX,aACA,mBACA,qBAC0B;CAM1B,IAAM,IAAgB,EAAM,WAAW,CAAY,CAAC,EAAE,QAChD,IAAS,KAAmB,GAC5B,IAAY,EAAM,OAAuB,IAAI,GAC7C,CAAC,GAAc,KAAmB,EAAM,SAAS,EAAK;CAqC5D,IAnCA,EAAM,gBAAgB;EACpB,IAAM,IAAU,EAAU;EAC1B,IAAI,CAAC,GAAS;EAEd,IAAM,UAAwB;GAC5B,IAAM,IAAO,EAAQ,eAAe,EAAQ;GAC5C,GAAiB,MAAa,MAAY,IAAO,IAAU,CAAK;EAClE;EAEA,EAAgB;EAEhB,IAAM,IACJ,OAAO,iBAAmB,MACtB,KAAA,IACA,IAAI,eAAe,CAAe;EACxC,GAAgB,QAAQ,CAAO;EAE/B,IAAM,IACJ,OAAO,mBAAqB,MACxB,KAAA,IACA,IAAI,iBAAiB,CAAe;EAQ1C,OAPA,GAAkB,QAAQ,GAAS;GACjC,WAAW;GACX,eAAe;GACf,SAAS;EACX,CAAC,GAED,OAAO,iBAAiB,UAAU,CAAe,SACpC;GAGX,AAFA,GAAgB,WAAW,GAC3B,GAAkB,WAAW,GAC7B,OAAO,oBAAoB,UAAU,CAAe;EACtD;CACF,GAAG;EAAC;EAAW;EAAU,GAAS;EAAQ;CAAO,CAAC,GAE9C,CAAC,GACH,MAAU,MACR,sFACF;CAuCF,OApCK,GAAS,SAIV,MAAY,SAEZ,kBAAC,OAAD;EACE,aAAU;EACV,KAAK;EACL,MAAK;EACL,cAAY;EACZ,UAAU,IAAe,IAAI,KAAA;EAC7B,WAAW,EACT,kIACA,EAAyB,IACzB,CACF;EAEC,UAAA,EAAQ,KAAK,MAAS;GACrB,IAAM,IAAM,GAAG,KAAW,EAAK,WAAW;GAC1C,OACE,kBAAC,GAAD;IAEQ;IACN,YAAY,EAA4B,GAAQ,GAAM,CAAG;IAC/C;IACA;IACM;IACA;GACjB,GAPM,EAAK,KAOX;EAEL,CAAC;CACE,CAAA,IAKP,kBAAC,OAAD;EACE,aAAU;EACV,KAAK;EACL,MAAK;EACL,cAAY;EACZ,UAAU,IAAe,IAAI,KAAA;EAC7B,WAAW,EAMT,+KACA,MAAkB,QAAQ,SAAS,QACnC,EAA4B,IAK5B,EAAoB,IACpB,CACF;EAEC,UAAA,EAAQ,KAAK,MAAS;GACrB,IAAM,IAAM,GAAG,KAAW,EAAK,WAAW;GAE1C,OACE,kBAAC,GAAD;IAEQ;IACN,YAAY,EAA4B,GAAQ,GAAM,CAAG;IAC/C;GACX,GAJM,EAAK,KAIX;EAEL,CAAC;CACE,CAAA,IAvEE;AAyEX;AAGA,SAAS,EACP,GACA,GACA,GACA;CACA,IAAI,OAAO,KAAY,aAAY,GACjC;CAGF,IAAM,IACJ,aAAa,KACb,OAAO,EAAQ,WAAY,YAC3B,EAAQ,YAAY,OAChB,EAAQ,UACR,KAAA,GAEF,IAAyB;CAiB7B,OAdE,KAAO,KACP,OAAO,EAAQ,MAAiC,WAEhD,IAAiB,EAAQ,KAEzB,KACA,KAAO,KACP,OAAO,EAAe,MAAwC,aAE9D,IAAiB,EACf,KAIG,KAAkB,IACrB,EAAO,KACP,EAAO;AACb"}
|
|
1
|
+
{"version":3,"file":"chart.js","names":[],"sources":["../../../../src/components/ui/chart/chart.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport * as RechartsPrimitive from 'recharts';\nimport type { LegendPayload } from 'recharts/types/component/DefaultLegendContent';\nimport {\n NameType,\n Payload,\n ValueType,\n} from 'recharts/types/component/DefaultTooltipContent';\nimport type { Props as LegendProps } from 'recharts/types/component/Legend';\nimport { TooltipContentProps } from 'recharts/types/component/Tooltip';\n\nimport { cn } from '@/lib/utils';\nimport { TruncateText } from '../truncate-text';\nimport {\n CHART_DEFAULT_PALETTE,\n CHART_DIVERGING_TOKENS,\n CHART_STATUS_TOKENS,\n resolveSeriesColor,\n type ChartDivergingSide,\n type ChartPalette,\n type ChartSeriesTone,\n} from './chart-palette';\n\n/**\n * Normalize a config key / data name to a CSS-safe custom-property fragment.\n *\n * A `--color-<key>` custom property is invalid when `key` contains a space or\n * other ASCII special character — the browser discards it, and the element\n * falls back to SVG's default black. This function replaces every invalid ASCII\n * character with a hyphen, collapses runs, and trims leading/trailing hyphens.\n *\n * Non-ASCII code points (U+0080+) are **preserved**: CSS Syntax Level 3\n * §4.3.7 classifies them as valid ident-code-points, so `--color-日本` and\n * `--color-Москва` are legal custom properties. Replacing them would collapse\n * all-non-ASCII keys to the empty string and silently make distinct series\n * collide on `--color-`.\n *\n * For an already-CSS-safe key (ASCII letters, digits, `-`, `_`) the output is\n * identical (identity), so existing consumers are unaffected. If the entire\n * input collapses to an empty string (e.g. all ASCII special characters),\n * `--color-` is produced — an edge case with no worse outcome than before.\n *\n * The dev-mode warning fires **once per unique unsanitized key** per page load,\n * so render loops (hover, re-render) do not flood the console.\n */\n// Tracks keys that have already triggered a dev warning — one per page load.\nconst _toCssKeyWarned = new Set<string>();\n\nexport function toCssKey(key: string): string {\n // Only strip ASCII characters that are not valid CSS ident chars.\n // U+0080+ are preserved — they are valid per CSS Syntax L3 §4.3.7.\n const safe = key\n .replace(/[^-a-zA-Z0-9_-]/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '');\n\n if (process.env.NODE_ENV !== 'production' && safe !== key && !_toCssKeyWarned.has(key)) {\n _toCssKeyWarned.add(key);\n console.warn(\n `[ui-react] Chart config key \"${key}\" is not CSS-safe and was sanitized to \"${safe}\". ` +\n `Use CSS-safe keys (ASCII letters, digits, hyphens, underscores, or non-ASCII characters) to avoid this.`\n );\n }\n\n return safe;\n}\n\nexport type ChartConfig = {\n [k in string]: {\n label?: React.ReactNode;\n icon?: React.ComponentType;\n /**\n * Which stop of the container's `palette` this series paints with. Left\n * unset, it is assigned from the series' position. See `ChartSeriesTone`.\n *\n * There is deliberately no free-form `color` here: every series colour\n * comes from the chart's palette, so a chart cannot paint a hue the design\n * system does not define. Pick a different palette on `ChartContainer`, or\n * a different stop of it with this.\n */\n tone?: ChartSeriesTone;\n };\n};\n\n/**\n * A `ChartConfig` with every series' palette colour filled in. This is what the\n * container puts on the context, so the plot, the tooltip rows and the legend\n * markers all read the same resolved colour.\n */\nexport type ResolvedChartConfig = {\n [k in string]: ChartConfig[string] & { color: string };\n};\n\n/**\n * Resolve each series' colour out of `palette` — in the palette's defined\n * order, honouring any `tone` a series named.\n *\n * Exported for tests and for the widget editor, which needs the same resolved\n * map to detect duplicate colours (`findDuplicateTones`) without re-deriving it.\n */\nexport function resolveChartColors(\n config: ChartConfig,\n palette: ChartPalette\n): ResolvedChartConfig {\n const entries = Object.entries(config);\n\n // An aliased series (`tone: { sameAs }`) doesn't take a stop of its own — it\n // is the same metric drawn a second way, so it must not consume a colour or\n // shift the series after it along the palette.\n const isAlias = (item: ChartConfig[string]) => Boolean(item.tone?.sameAs);\n const resolved: Record<string, ChartConfig[string] & { color: string }> = {};\n\n // Warn about overrides that are meaningless under a diverging palette.\n // resolveSeriesColor issues the same warnings for every other palette type,\n // but the diverging branch below bypasses it, so we replicate the check here\n // to preserve the documented dev-warning contract (see ChartSeriesTone).\n const warnDivergingMismatch = (tone: ChartSeriesTone | undefined) => {\n if (process.env.NODE_ENV !== 'production' && tone) {\n if (tone.status) {\n console.warn(\n `[ui-react] A chart series names a status (\"${tone.status}\") under the \"diverging\" palette, which has no status tones. Ignoring.`\n );\n } else if (typeof tone.slot === 'number') {\n console.warn(\n `[ui-react] A chart series pins slot ${tone.slot} under the \"diverging\" palette, whose stops are a ramp and are not individually selectable. Ignoring.`\n );\n }\n }\n };\n\n if (palette.type === 'diverging') {\n const interleavedStops = CHART_DIVERGING_TOKENS[palette.pair];\n // Derive each side's three stops from the interleaved ramp (a at even\n // indices, b at odd indices): a3, a2, a1 and b3, b2, b1.\n const sideStops: Record<ChartDivergingSide, readonly string[]> = {\n a: [interleavedStops[0], interleavedStops[2], interleavedStops[4]],\n b: [interleavedStops[1], interleavedStops[3], interleavedStops[5]],\n };\n\n // First pass: assign sided series — group by side, each group walks its\n // side's three stops strongest-first, wrapping if there are more than three.\n const sideCounters: Record<ChartDivergingSide, number> = { a: 0, b: 0 };\n const takenStops = new Set<string>();\n for (const [key, item] of entries) {\n if (isAlias(item) || !item.tone?.side) continue;\n warnDivergingMismatch(item.tone);\n const side = item.tone.side;\n const stops = sideStops[side];\n const stop = stops[sideCounters[side]++ % stops.length];\n takenStops.add(stop);\n resolved[key] = { ...item, color: stop };\n }\n\n // Second pass: assign un-sided series from the remaining interleaved stops,\n // skipping any stop already claimed by a sided series. Fall back to the\n // full interleaved ramp if all stops were consumed.\n const available = interleavedStops.filter((s) => !takenStops.has(s));\n const pool = available.length > 0 ? available : interleavedStops;\n let unsidedIdx = 0;\n for (const [key, item] of entries) {\n if (isAlias(item) || item.tone?.side) continue;\n warnDivergingMismatch(item.tone);\n resolved[key] = { ...item, color: pool[unsidedIdx++ % pool.length] };\n }\n } else {\n let index = 0;\n for (const [key, item] of entries) {\n if (isAlias(item)) continue;\n resolved[key] = {\n ...item,\n color: resolveSeriesColor(palette, { index: index++, tone: item.tone }),\n };\n }\n }\n\n // An alias may point at another alias, in any order, so a single pass over\n // the aliases can miss a chain that is fully resolvable. Keep sweeping the\n // pending set until nothing more resolves; what is left is a cycle or a\n // target that isn't in this config at all.\n const pending = new Map<string, string>();\n for (const [key, item] of entries) {\n if (isAlias(item)) {\n pending.set(key, item.tone!.sameAs as string);\n }\n }\n\n let changed = true;\n while (changed && pending.size > 0) {\n changed = false;\n for (const [key, target] of pending) {\n const targetColor = resolved[target]?.color;\n if (targetColor !== undefined) {\n resolved[key] = { ...config[key], color: targetColor };\n pending.delete(key);\n changed = true;\n }\n }\n }\n\n for (const [key, target] of pending) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n `[ui-react] Chart series \"${key}\" aliases \"${target}\", which is not a palette-assigned series in this config. Falling back to neutral.`\n );\n }\n resolved[key] = { ...config[key], color: CHART_STATUS_TOKENS.neutral };\n }\n\n // Rebuilt in the config's own order: the passes above visit non-aliases\n // first, and callers (legend order, `Object.entries` consumers) rely on the\n // order they wrote.\n return Object.fromEntries(entries.map(([key]) => [key, resolved[key]]));\n}\n\ntype ChartContextProps = {\n config: ResolvedChartConfig;\n};\n\nexport type ChartTooltipContentProps = Partial<\n TooltipContentProps<ValueType, NameType>\n> & {\n className?: string;\n hideLabel?: boolean;\n hideIndicator?: boolean;\n nameKey?: string;\n labelKey?: string;\n labelFormatter?: (\n label: TooltipContentProps<number, string>['label'],\n payload: TooltipContentProps<number, string>['payload']\n ) => React.ReactNode;\n formatter?: (\n value: number | string,\n name: string,\n item: Payload<number | string, string>,\n index: number,\n payload: ReadonlyArray<Payload<number | string, string>>\n ) => React.ReactNode;\n labelClassName?: string;\n color?: string;\n};\n\n/**\n * The legend text-size scale. `xs` is the size the legend has always rendered\n * at; the rest of the steps follow Tailwind's `text-*` scale.\n */\nexport type ChartLegendFontSize = 'xs' | 'sm' | 'base' | 'lg' | 'xl';\n\n// A static lookup rather than a `text-${size}` template: Tailwind only emits a\n// utility it can read verbatim in the source.\nconst legendFontSizeClass: Record<ChartLegendFontSize, string> = {\n xs: 'text-xs',\n sm: 'text-sm',\n base: 'text-base',\n lg: 'text-lg',\n xl: 'text-xl',\n};\n\n// Fit whole rows at every supported font size: three wrapped rows plus the\n// normal legend's vertical padding/gaps, and eight rows in list mode.\nconst defaultLegendMaxHeightClass: Record<ChartLegendFontSize, string> = {\n xs: 'max-h-[76px]',\n sm: 'max-h-[88px]',\n base: 'max-h-[100px]',\n lg: 'max-h-[112px]',\n xl: 'max-h-[112px]',\n};\n\nconst listLegendMaxHeightClass: Record<ChartLegendFontSize, string> = {\n xs: 'max-h-[184px]',\n sm: 'max-h-[216px]',\n base: 'max-h-[248px]',\n lg: 'max-h-[280px]',\n xl: 'max-h-[280px]',\n};\n\nexport type ChartLegendContentProps = {\n className?: string;\n /** Accessible name for the legend group. */\n ariaLabel?: string;\n hideIcon?: boolean;\n verticalAlign?: LegendProps['verticalAlign'];\n payload?: LegendPayload[];\n nameKey?: string;\n /**\n * Font size of the legend text — labels, and the right-hand values of the\n * `list` variant. Defaults to `'xs'`, the size the legend has always\n * rendered at.\n */\n fontSize?: ChartLegendFontSize;\n /**\n * The series config the entries take their labels and icons from. Defaults to\n * the enclosing `ChartContainer`'s — pass it only to render the legend *outside*\n * the container, which a chart type has to do when the renderer can't lay a\n * legend out inside its plot (see `Treemap`).\n */\n config?: ChartConfig;\n /**\n * `default` — horizontal wrapped chips; scrolls after three rows (76px at\n * `xs`). `list` — vertical `[dot] [label flex-1] [value]` rows with an 8px\n * gap; scrolls after eight rows (184px at `xs`). Both caps scale with\n * `fontSize`; `className` can override the cap.\n */\n variant?: 'default' | 'list';\n /**\n * When `variant=\"list\"`, the field on `payload[i].payload` to display as the\n * right-hand value. Typically the chart's `dataKey` (the numeric series field).\n */\n valueKey?: string;\n /**\n * Format the value shown in each list entry. When omitted the raw value is\n * stringified with `String()`.\n */\n valueFormatter?: (value: string | number) => string;\n /**\n * Extra Tailwind class(es) applied to the right-hand value in a list entry,\n * merged on top of the default colour (`text-[var(--ui-text-on-surface-link-idle)]`).\n * Pass a replacement colour class to override the default; other utilities\n * (e.g. `tabular-nums`) are additive and leave the default colour in place.\n */\n valueClassName?: string;\n};\n\n/** Accessible-name prop shared by chart components that render a legend. */\nexport interface ChartLegendAriaLabelProps {\n /**\n * Accessible name for this chart's legend. Defaults to `Chart legend`; give\n * each chart a distinct name when several legends appear on the same page.\n */\n legendAriaLabel?: string;\n}\n\nconst ChartContext = React.createContext<ChartContextProps | null>(null);\n\nfunction useChart() {\n const context = React.useContext(ChartContext);\n\n if (!context) {\n throw new Error('useChart must be used within a <ChartContainer />');\n }\n\n return context;\n}\n\nexport interface ChartContainerProps extends React.ComponentProps<'div'> {\n /**\n * The series this chart draws, keyed by data field.\n *\n * Declare exactly what the chart plots. A stop is assigned by an entry's\n * position here, not by which series end up rendered, so an entry for a\n * series the chart doesn't draw still consumes a colour and shifts the ones\n * that follow it. (That is deliberate: a series then keeps its colour when a\n * sibling is toggled off, instead of the whole chart recolouring.)\n */\n config: ChartConfig;\n /**\n * The dataviz palette this chart's series are painted from. Series take a\n * stop of it in the palette's defined order, or the one their `tone` names.\n * Defaults to `categorical` — there is no \"no palette\" state.\n */\n palette?: ChartPalette;\n children: React.ComponentProps<\n typeof RechartsPrimitive.ResponsiveContainer\n >['children'];\n}\n\nconst ChartContainer = React.forwardRef<HTMLDivElement, ChartContainerProps>(\n function ChartContainer(\n { id, className, children, config, palette = CHART_DEFAULT_PALETTE, ...props },\n ref\n ) {\n const uniqueId = React.useId();\n const chartId = `chart-${id || uniqueId.replace(/:/g, '')}`;\n // Resolved once and shared with the context, so the tooltip rows and legend\n // markers read the same colors the plot paints with.\n const resolvedConfig = React.useMemo(\n () => resolveChartColors(config, palette),\n [config, palette]\n );\n\n return (\n <ChartContext.Provider value={{ config: resolvedConfig }}>\n <div\n ref={ref}\n id={id}\n data-slot=\"chart\"\n data-chart={chartId}\n // The `[stroke='#fff']` rules undo recharts' hardcoded white outlines —\n // on pie/radial sectors, line dots and funnel trapezoids. They don't\n // follow the theme, so in dark mode they read as a light hairline around\n // every segment.\n className={cn(\n \"[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden [&_.recharts-trapezoid[stroke='#fff']]:stroke-transparent\",\n // A `<Brush>` traveller is the one `.recharts-layer` that is a real\n // control (`role=\"slider\"`, tabbable, arrow-key driven), so the blanket\n // `outline-hidden` above would leave it with no focus indicator. Restore\n // one — the `:focus-visible` pseudo-class outranks that rule, so this\n // wins regardless of the order Tailwind emits them in. An `outline`\n // rather than the usual `ring`: box-shadow doesn't paint on SVG children.\n '[&_.recharts-brush-traveller:focus-visible]:[outline:3px_solid_var(--ui-focus-primary)] [&_.recharts-brush-traveller:focus-visible]:[outline-offset:1px]',\n // recharts anchors its axis tick text with the *direction-relative*\n // SVG keywords (`text-anchor: start|end`, see `getTickTextAnchor` in\n // `CartesianAxis`), while placing every mark at a computed physical\n // coordinate. Inheriting `direction: rtl` therefore flips only the\n // text: an `end`-anchored Y tick mirrors about its anchor and lands\n // inside the plot (measured: +24px to +39px), rotated ticks shift\n // ~+15px, and a `ReferenceLine` label at the right edge overflows the\n // surface. Pinning the surface keeps the plot's own coordinate system\n // consistent with the geometry recharts computed for it. The chrome\n // that *should* mirror — tooltip and legend — is HTML outside\n // `.recharts-surface`, so it is unaffected.\n //\n // `Treemap` is the exception: its cell labels are HTML in a\n // `<foreignObject>` *inside* the surface, put there precisely so they\n // mirror with the page (see `treemap.tsx`). That is ordinary flow\n // layout, not a computed SVG coordinate, so the pin has to stop at the\n // `<foreignObject>` boundary and hand the page's direction back.\n '[&_.recharts-surface]:[direction:ltr] rtl:[&_.recharts-surface_foreignObject]:[direction:rtl]',\n className\n )}\n {...props}\n >\n <ChartStyle id={chartId} config={resolvedConfig} />\n <RechartsPrimitive.ResponsiveContainer>\n {children}\n </RechartsPrimitive.ResponsiveContainer>\n </div>\n </ChartContext.Provider>\n );\n }\n);\n\nChartContainer.displayName = 'ChartContainer';\n\nconst ChartStyle = ({\n id,\n config,\n}: {\n id: string;\n config: ResolvedChartConfig;\n}) => {\n const entries = Object.entries(config);\n\n if (!entries.length) {\n return null;\n }\n\n // One block, no light/dark split: every colour is now a `--ui-dataviz-*`\n // token, and those are `light-dark()` pairs that follow `color-scheme`\n // themselves. The old per-theme `[data-theme='dark']` duplication existed\n // only for hand-written `theme: { light, dark }` config entries, which the\n // palette replaced.\n //\n // Rendered as a text child (not dangerouslySetInnerHTML): React sets it via\n // textContent, which the browser does not HTML-parse, so a `</style>` in a\n // colour can't break out of the tag.\n if (process.env.NODE_ENV !== 'production') {\n const seen = new Map<string, string>();\n for (const [key] of entries) {\n const safe = toCssKey(key);\n const prior = seen.get(safe);\n if (prior !== undefined) {\n console.warn(\n `[ui-react] Chart config keys \"${prior}\" and \"${key}\" both sanitize to \"--color-${safe}\". ` +\n `One will overwrite the other — use unique CSS-safe keys.`\n );\n } else {\n seen.set(safe, key);\n }\n }\n }\n\n return (\n <style>\n {`[data-chart=${id}] {\\n${entries\n .map(([key, itemConfig]) => ` --color-${toCssKey(key)}: ${itemConfig.color};`)\n .join('\\n')}\\n}`}\n </style>\n );\n};\n\nconst ChartTooltip = RechartsPrimitive.Tooltip;\n\n/** The `content` a caller passes to override the default tooltip (recharts' `Tooltip['content']`). */\nexport type ChartTooltipContentType = React.ComponentProps<\n typeof ChartTooltip\n>['content'];\n\n/**\n * What `getPayloadConfigFromPayload` resolves a payload item to — the lookup can\n * miss, so every consumer has to cope with `undefined`. Derived from the helper\n * rather than restated, so the two can't drift.\n */\ntype ChartItemConfig = ReturnType<typeof getPayloadConfigFromPayload>;\n\n/** One row of the tooltip's payload, after the `active`/empty guard. */\ntype ChartTooltipItem = NonNullable<\n ChartTooltipContentProps['payload']\n>[number];\n\n/**\n * One tooltip row. Its own three-way branching (caller formatter, config icon,\n * default dot) is what drove the nesting in `ChartTooltipContent`; as a\n * component the map body reads as a single element.\n */\nfunction ChartTooltipRow({\n item,\n index,\n payload,\n itemConfig,\n indicatorColor,\n hideIndicator,\n formatter,\n}: {\n item: ChartTooltipItem;\n index: number;\n payload: NonNullable<ChartTooltipContentProps['payload']>;\n itemConfig: ChartItemConfig;\n indicatorColor?: string;\n hideIndicator: boolean;\n formatter: ChartTooltipContentProps['formatter'];\n}) {\n return (\n <div className=\"flex w-full flex-wrap items-center gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground\">\n {formatter && item?.value !== undefined && item.name ? (\n formatter(item.value, item.name, item, index, payload)\n ) : (\n <>\n {itemConfig?.icon ? (\n <itemConfig.icon />\n ) : (\n !hideIndicator && (\n // A row is always dotted, whatever marker the legend\n // gives that series.\n <div\n className=\"h-2.5 w-2.5 shrink-0 rounded-full\"\n style={{ backgroundColor: indicatorColor }}\n />\n )\n )}\n {/*\n * `gap-4` puts a floor under the name/value separation.\n * `justify-between` alone only separates them while the\n * container has free space to distribute — which the tooltip's\n * `min-w-[8rem]` guarantees for a short value and not for a\n * long one (a currency `tickFormatter`, a `labelFormatter`\n * with units, a value plus its share), where the two would\n * otherwise butt up against each other. A minimum, so a row\n * with slack renders as before.\n */}\n <div className=\"flex flex-1 items-center justify-between gap-4 leading-none\">\n <span className=\"text-muted-foreground\">\n {itemConfig?.label || item.name}\n </span>\n {item.value != null && (\n <span className=\"font-medium tabular-nums text-foreground\">\n {item.value.toLocaleString()}\n </span>\n )}\n </div>\n </>\n )}\n </div>\n );\n}\n\nfunction ChartTooltipContent({\n active,\n payload,\n label,\n className,\n hideLabel = false,\n hideIndicator = false,\n labelFormatter,\n formatter,\n labelClassName,\n color,\n nameKey,\n labelKey,\n}: ChartTooltipContentProps) {\n const { config } = useChart();\n\n const tooltipLabel = React.useMemo(() => {\n if (hideLabel || !payload?.length) {\n return null;\n }\n\n const [item] = payload;\n const key = `${labelKey || item?.dataKey || item?.name || 'value'}`;\n const itemConfig = getPayloadConfigFromPayload(config, item, key);\n const value = (() => {\n const v =\n !labelKey && typeof label === 'string'\n ? (config[label as keyof typeof config]?.label ?? label)\n : itemConfig?.label;\n\n return typeof v === 'string' || typeof v === 'number' ? v : undefined;\n })();\n\n if (labelFormatter) {\n return (\n <div className={cn('font-medium', labelClassName)}>\n {labelFormatter(value, payload)}\n </div>\n );\n }\n\n if (!value) {\n return null;\n }\n\n return <div className={cn('font-medium', labelClassName)}>{value}</div>;\n }, [\n label,\n labelFormatter,\n payload,\n hideLabel,\n labelClassName,\n config,\n labelKey,\n ]);\n\n if (!active || !payload?.length) {\n return null;\n }\n\n return (\n <div\n className={cn(\n 'grid min-w-[8rem] items-start gap-1.5 rounded-[var(--ui-tooltip-container-border-radius)] border border-border bg-background px-[var(--ui-tooltip-container-padding-x)] py-[var(--ui-tooltip-container-padding-y)] text-xs text-foreground shadow-md',\n className\n )}\n >\n {tooltipLabel}\n <div className=\"grid gap-1.5\">\n {payload.map((item, index) => {\n const key = `${nameKey || item.name || item.dataKey || 'value'}`;\n\n return (\n <ChartTooltipRow\n key={key}\n item={item}\n index={index}\n payload={payload}\n itemConfig={getPayloadConfigFromPayload(config, item, key)}\n indicatorColor={color || item.payload.fill || item.color}\n hideIndicator={hideIndicator}\n formatter={formatter}\n />\n );\n })}\n </div>\n </div>\n );\n}\n\nconst ChartLegend = RechartsPrimitive.Legend;\n\n/**\n * One entry in the `variant=\"list\"` legend: `[dot] [label flex-1] [value]`.\n *\n * The dot is always a circle, regardless of the series' recharts type — the\n * list layout is used by part-to-whole charts where each entry has a fill color.\n */\nfunction ChartLegendListEntry({\n item,\n itemConfig,\n fontSize,\n valueKey,\n valueFormatter,\n valueClassName,\n}: {\n item: LegendPayload;\n itemConfig: ChartItemConfig;\n fontSize: ChartLegendFontSize;\n valueKey?: string;\n valueFormatter?: (value: string | number) => string;\n valueClassName?: string;\n}) {\n const raw = valueKey\n ? (item.payload as Record<string, unknown> | undefined)?.[valueKey]\n : undefined;\n const displayValue =\n raw != null\n ? valueFormatter\n ? valueFormatter(raw as string | number)\n : String(raw)\n : undefined;\n const label = itemConfig?.label ?? item.value;\n\n return (\n <div className=\"flex w-full shrink-0 items-center gap-2 overflow-hidden\">\n <div className=\"flex min-w-0 flex-1 items-center gap-1\">\n <div className=\"grid size-4 shrink-0 place-items-center [&>svg]:h-4 [&>svg]:w-4\">\n {itemConfig?.icon ? (\n <itemConfig.icon />\n ) : (\n <div\n className=\"size-2 rounded-full\"\n style={{ backgroundColor: item.color }}\n />\n )}\n </div>\n {typeof label === 'string' || typeof label === 'number' ? (\n <TruncateText\n className={cn(\n 'min-w-0 flex-1 text-foreground',\n legendFontSizeClass[fontSize]\n )}\n >\n {String(label)}\n </TruncateText>\n ) : (\n <span\n className={cn(\n 'min-w-0 flex-1 truncate text-foreground',\n legendFontSizeClass[fontSize]\n )}\n >\n {label}\n </span>\n )}\n </div>\n {displayValue != null && (\n <span\n className={cn(\n 'shrink-0 font-semibold text-[var(--ui-text-on-surface-link-idle)]',\n legendFontSizeClass[fontSize],\n valueClassName\n )}\n >\n {displayValue}\n </span>\n )}\n </div>\n );\n}\n\n/**\n * One legend entry. Split out so the marker it has to derive from the payload\n * sits next to the markup that uses it, instead of above a nested map body.\n */\nfunction ChartLegendEntry({\n item,\n itemConfig,\n hideIcon,\n}: {\n item: LegendPayload;\n itemConfig: ChartItemConfig;\n hideIcon: boolean;\n}) {\n return (\n <div\n className={cn(\n 'flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground'\n )}\n >\n {itemConfig?.icon && !hideIcon ? (\n <itemConfig.icon />\n ) : (\n <div\n className=\"h-2.5 w-2.5 shrink-0 rounded-full\"\n style={{ backgroundColor: item.color }}\n />\n )}\n {/*\n * Falls back to the series key, the way the tooltip row and the\n * treemap's on-cell label both do. `label` is optional on a\n * `ChartConfig` entry, and `getPayloadConfigFromPayload` can miss\n * entirely, so without this an entry renders as a marker with no\n * text — worst on the charts whose legend is the only place a series\n * is named.\n */}\n {itemConfig?.label ?? item.value}\n </div>\n );\n}\n\nfunction ChartLegendContent({\n className,\n ariaLabel = 'Chart legend',\n hideIcon = false,\n payload,\n verticalAlign = 'bottom',\n nameKey,\n config: configFromProps,\n variant = 'default',\n fontSize = 'xs',\n valueKey,\n valueFormatter,\n valueClassName,\n}: ChartLegendContentProps) {\n // The context read directly rather than through `useChart()`: a caller that\n // passes its own `config` is rendering the legend outside the container, where\n // the context is absent by design rather than by mistake. Every other caller\n // still gets `useChart()`'s error — the legend has no way to label itself\n // without a config, and failing loudly beats rendering an empty row.\n const contextConfig = React.useContext(ChartContext)?.config;\n const config = configFromProps ?? contextConfig;\n const legendRef = React.useRef<HTMLDivElement>(null);\n const [isScrollable, setIsScrollable] = React.useState(false);\n\n React.useEffect(() => {\n const element = legendRef.current;\n if (!element) return;\n\n const measureOverflow = () => {\n const next = element.scrollHeight > element.clientHeight;\n setIsScrollable((current) => (current === next ? current : next));\n };\n\n measureOverflow();\n\n const resizeObserver =\n typeof ResizeObserver === 'undefined'\n ? undefined\n : new ResizeObserver(measureOverflow);\n resizeObserver?.observe(element);\n\n const mutationObserver =\n typeof MutationObserver === 'undefined'\n ? undefined\n : new MutationObserver(measureOverflow);\n mutationObserver?.observe(element, {\n childList: true,\n characterData: true,\n subtree: true,\n });\n\n window.addEventListener('resize', measureOverflow);\n return () => {\n resizeObserver?.disconnect();\n mutationObserver?.disconnect();\n window.removeEventListener('resize', measureOverflow);\n };\n }, [className, fontSize, payload?.length, variant]);\n\n if (!config) {\n throw new Error(\n 'ChartLegendContent must be used within a <ChartContainer /> or given a `config` prop'\n );\n }\n\n if (!payload?.length) {\n return null;\n }\n\n if (variant === 'list') {\n return (\n <div\n data-slot=\"chart-legend\"\n ref={legendRef}\n role=\"group\"\n aria-label={ariaLabel}\n tabIndex={isScrollable ? 0 : undefined}\n className={cn(\n 'flex flex-col gap-2 overflow-y-auto focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--ui-focus-primary)]',\n listLegendMaxHeightClass[fontSize],\n className\n )}\n >\n {payload.map((item) => {\n const key = `${nameKey || item.dataKey || 'value'}`;\n return (\n <ChartLegendListEntry\n key={item.value}\n item={item}\n itemConfig={getPayloadConfigFromPayload(config, item, key)}\n fontSize={fontSize}\n valueKey={valueKey}\n valueFormatter={valueFormatter}\n valueClassName={valueClassName}\n />\n );\n })}\n </div>\n );\n }\n\n return (\n <div\n data-slot=\"chart-legend\"\n ref={legendRef}\n role=\"group\"\n aria-label={ariaLabel}\n tabIndex={isScrollable ? 0 : undefined}\n className={cn(\n // Wraps rather than overflowing: a legend with many entries (a treemap's\n // one-per-tile, a pie's one-per-slice) is wider than the chart on a narrow\n // surface, and a row that can't wrap paints past the chart's edge. The\n // column gap is unchanged, so a legend that already fits on one row keeps\n // its exact layout.\n 'flex flex-wrap items-center justify-center gap-x-6 gap-y-2 overflow-y-auto focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--ui-focus-primary)]',\n verticalAlign === 'top' ? 'pb-3' : 'pt-3',\n defaultLegendMaxHeightClass[fontSize],\n // Stated on the wrapper even at the default ('xs'): default-variant\n // entries take their size by inheritance, and a legend rendered outside\n // a `ChartContainer` (see `Treemap`) has no container `text-xs` to fall\n // back on.\n legendFontSizeClass[fontSize],\n className\n )}\n >\n {payload.map((item) => {\n const key = `${nameKey || item.dataKey || 'value'}`;\n\n return (\n <ChartLegendEntry\n key={item.value}\n item={item}\n itemConfig={getPayloadConfigFromPayload(config, item, key)}\n hideIcon={hideIcon}\n />\n );\n })}\n </div>\n );\n}\n\n// Helper to extract item config from a payload.\nfunction getPayloadConfigFromPayload(\n config: ChartConfig,\n payload: unknown,\n key: string\n) {\n if (typeof payload !== 'object' || payload === null) {\n return undefined;\n }\n\n const payloadPayload =\n 'payload' in payload &&\n typeof payload.payload === 'object' &&\n payload.payload !== null\n ? payload.payload\n : undefined;\n\n let configLabelKey: string = key;\n\n if (\n key in payload &&\n typeof payload[key as keyof typeof payload] === 'string'\n ) {\n configLabelKey = payload[key as keyof typeof payload] as string;\n } else if (\n payloadPayload &&\n key in payloadPayload &&\n typeof payloadPayload[key as keyof typeof payloadPayload] === 'string'\n ) {\n configLabelKey = payloadPayload[\n key as keyof typeof payloadPayload\n ] as string;\n }\n\n return configLabelKey in config\n ? config[configLabelKey]\n : config[key as keyof typeof config];\n}\n\nexport {\n ChartContainer,\n ChartTooltip,\n ChartTooltipContent,\n ChartLegend,\n ChartLegendContent,\n ChartStyle,\n};\n"],"mappings":"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"}
|
|
@@ -1,48 +1,58 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn as e } from "../../../lib/utils.js";
|
|
3
3
|
import { Spinner as t } from "../spinner/spinner.js";
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import { CircleWarningIcon as o } from "@acronis-platform/icons-react/stroke-mono";
|
|
4
|
+
import * as n from "react";
|
|
5
|
+
import { Fragment as r, jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
6
|
+
import { CircleMinusGrayIcon as o, DiamondWarningRedIcon as s, TriangleWarningYellowIcon as c } from "@acronis-platform/icons-react/stroke-multi";
|
|
8
7
|
//#region src/components/ui/chart-state/chart-state.tsx
|
|
9
|
-
var
|
|
8
|
+
var l = {
|
|
10
9
|
loading: "Data is loading…",
|
|
11
10
|
empty: "No data found",
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
11
|
+
warning: "Data may be incomplete",
|
|
12
|
+
error: "Something went wrong",
|
|
13
|
+
number: "0"
|
|
14
|
+
}, u = {
|
|
15
|
+
empty: {
|
|
16
|
+
Component: o,
|
|
17
|
+
background: "bg-[var(--ui-background-status-neutral-hover)]"
|
|
18
|
+
},
|
|
19
|
+
warning: {
|
|
20
|
+
Component: c,
|
|
21
|
+
background: "bg-[var(--ui-background-status-warning)]"
|
|
22
|
+
},
|
|
23
|
+
error: {
|
|
24
|
+
Component: s,
|
|
25
|
+
background: "bg-[var(--ui-background-status-critical)]"
|
|
26
|
+
}
|
|
27
|
+
}, d = n.forwardRef(({ className: n, state: o, description: s, ...c }, d) => {
|
|
28
|
+
let f = s ?? l[o], p = o === "loading", m = o === "number", h = !p && !m ? u[o] : null, g = h?.Component;
|
|
29
|
+
return /* @__PURE__ */ i("div", {
|
|
30
|
+
ref: d,
|
|
31
|
+
className: e("flex size-full min-h-0 min-w-0 flex-col items-center gap-3 text-center text-sm leading-6 text-foreground", o !== "error" && "justify-center", o === "error" && "overflow-y-auto overflow-x-hidden", n),
|
|
32
|
+
...c,
|
|
33
|
+
role: o === "error" ? "alert" : "status",
|
|
34
|
+
"aria-live": o === "error" ? "assertive" : "polite",
|
|
35
|
+
children: p ? /* @__PURE__ */ a(r, { children: [/* @__PURE__ */ i(t, {
|
|
36
|
+
size: "lg",
|
|
37
|
+
"aria-hidden": !0
|
|
38
|
+
}), /* @__PURE__ */ i("p", { children: f })] }) : m ? /* @__PURE__ */ i("p", {
|
|
39
|
+
className: "w-full text-center text-5xl font-bold leading-[64px] text-[var(--ui-text-on-surface-disabled)]",
|
|
40
|
+
children: f
|
|
41
|
+
}) : /* @__PURE__ */ a("div", {
|
|
42
|
+
className: e("flex w-full min-w-0 flex-col items-center gap-3", o === "error" && "my-auto shrink-0"),
|
|
43
|
+
children: [/* @__PURE__ */ i("span", {
|
|
44
|
+
"aria-hidden": "true",
|
|
45
|
+
className: e("flex size-16 shrink-0 items-center justify-center rounded-full p-2", h?.background),
|
|
46
|
+
children: g && /* @__PURE__ */ i(g, { size: 24 })
|
|
47
|
+
}), /* @__PURE__ */ i("p", {
|
|
48
|
+
className: "w-full min-w-0 [overflow-wrap:anywhere]",
|
|
49
|
+
children: f
|
|
50
|
+
})]
|
|
51
|
+
})
|
|
42
52
|
});
|
|
43
53
|
});
|
|
44
|
-
|
|
54
|
+
d.displayName = "ChartState";
|
|
45
55
|
//#endregion
|
|
46
|
-
export {
|
|
56
|
+
export { d as ChartState };
|
|
47
57
|
|
|
48
58
|
//# sourceMappingURL=chart-state.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chart-state.js","names":[],"sources":["../../../../src/components/ui/chart-state/chart-state.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport {
|
|
1
|
+
{"version":3,"file":"chart-state.js","names":[],"sources":["../../../../src/components/ui/chart-state/chart-state.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport {\n CircleMinusGrayIcon,\n DiamondWarningRedIcon,\n TriangleWarningYellowIcon,\n} from '@acronis-platform/icons-react/stroke-multi';\n\nimport { cn } from '@/lib/utils';\nimport { Spinner } from '../spinner';\n\n// The Breakpoints references are composed widget-card examples rather than a\n// standalone ChartState component set, so this inner component has no Code\n// Connect mapping.\nconst DEFAULT_MESSAGE = {\n loading: 'Data is loading…',\n empty: 'No data found',\n warning: 'Data may be incomplete',\n error: 'Something went wrong',\n number: '0',\n} as const;\n\nconst STATUS_ICON = {\n empty: {\n Component: CircleMinusGrayIcon,\n background: 'bg-[var(--ui-background-status-neutral-hover)]',\n },\n warning: {\n Component: TriangleWarningYellowIcon,\n background: 'bg-[var(--ui-background-status-warning)]',\n },\n error: {\n Component: DiamondWarningRedIcon,\n background: 'bg-[var(--ui-background-status-critical)]',\n },\n} as const;\n\nexport interface ChartStateProps extends Omit<\n React.ComponentPropsWithoutRef<'div'>,\n 'children'\n> {\n /** Which state to show. Empty, warning, and error share one icon system. */\n state: 'loading' | 'empty' | 'warning' | 'error' | 'number';\n /**\n * State text, or the numeric readout for `number`. Defaults to a short\n * message for the selected state (`0` for `number`). Pass a value to\n * provide localized or context-specific content.\n */\n description?: React.ReactNode;\n}\n\nconst ChartState = React.forwardRef<HTMLDivElement, ChartStateProps>(\n ({ className, state, description, ...props }, ref) => {\n const caption = description ?? DEFAULT_MESSAGE[state];\n const isLoading = state === 'loading';\n const isNumber = state === 'number';\n const status = !isLoading && !isNumber ? STATUS_ICON[state] : null;\n const StateIcon = status?.Component;\n\n return (\n <div\n ref={ref}\n className={cn(\n 'flex size-full min-h-0 min-w-0 flex-col items-center gap-3 text-center text-sm leading-6 text-foreground',\n state !== 'error' && 'justify-center',\n // Keep long error details scrollable while centering short messages.\n state === 'error' && 'overflow-y-auto overflow-x-hidden',\n className\n )}\n {...props}\n // Keep the live-region contract intrinsic to the state. The loading\n // placeholder is replaced rather than un-busied, so it must not set the\n // region itself to aria-busy.\n role={state === 'error' ? 'alert' : 'status'}\n aria-live={state === 'error' ? 'assertive' : 'polite'}\n >\n {isLoading ? (\n <>\n <Spinner size=\"lg\" aria-hidden />\n <p>{caption}</p>\n </>\n ) : isNumber ? (\n <p className=\"w-full text-center text-5xl font-bold leading-[64px] text-[var(--ui-text-on-surface-disabled)]\">\n {caption}\n </p>\n ) : (\n <div\n className={cn(\n 'flex w-full min-w-0 flex-col items-center gap-3',\n state === 'error' && 'my-auto shrink-0'\n )}\n >\n <span\n aria-hidden=\"true\"\n className={cn(\n 'flex size-16 shrink-0 items-center justify-center rounded-full p-2',\n status?.background\n )}\n >\n {StateIcon && <StateIcon size={24} />}\n </span>\n <p className=\"w-full min-w-0 [overflow-wrap:anywhere]\">{caption}</p>\n </div>\n )}\n </div>\n );\n }\n);\nChartState.displayName = 'ChartState';\n\nexport { ChartState };\n"],"mappings":";;;;;;;AAeA,IAAM,IAAkB;CACtB,SAAS;CACT,OAAO;CACP,SAAS;CACT,OAAO;CACP,QAAQ;AACV,GAEM,IAAc;CAClB,OAAO;EACL,WAAW;EACX,YAAY;CACd;CACA,SAAS;EACP,WAAW;EACX,YAAY;CACd;CACA,OAAO;EACL,WAAW;EACX,YAAY;CACd;AACF,GAgBM,IAAa,EAAM,YACtB,EAAE,cAAW,UAAO,gBAAa,GAAG,KAAS,MAAQ;CACpD,IAAM,IAAU,KAAe,EAAgB,IACzC,IAAY,MAAU,WACtB,IAAW,MAAU,UACrB,IAAS,CAAC,KAAa,CAAC,IAAW,EAAY,KAAS,MACxD,IAAY,GAAQ;CAE1B,OACE,kBAAC,OAAD;EACO;EACL,WAAW,EACT,4GACA,MAAU,WAAW,kBAErB,MAAU,WAAW,qCACrB,CACF;EACA,GAAI;EAIJ,MAAM,MAAU,UAAU,UAAU;EACpC,aAAW,MAAU,UAAU,cAAc;EAE5C,UAAA,IACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;GAAS,MAAK;GAAK,eAAA;EAAa,CAAA,GAChC,kBAAC,KAAD,EAAA,UAAI,EAAW,CAAA,CACf,EAAA,CAAA,IACA,IACF,kBAAC,KAAD;GAAG,WAAU;GACV,UAAA;EACA,CAAA,IAEH,kBAAC,OAAD;GACE,WAAW,EACT,mDACA,MAAU,WAAW,kBACvB;GAJF,UAAA,CAME,kBAAC,QAAD;IACE,eAAY;IACZ,WAAW,EACT,sEACA,GAAQ,UACV;IAEC,UAAA,KAAa,kBAAC,GAAD,EAAW,MAAM,GAAK,CAAA;GAChC,CAAA,GACN,kBAAC,KAAD;IAAG,WAAU;IAA2C,UAAA;GAAW,CAAA,CAChE;;CAEJ,CAAA;AAET,CACF;AACA,EAAW,cAAc"}
|
|
@@ -3,38 +3,36 @@ import { cn as e } from "../../../lib/utils.js";
|
|
|
3
3
|
import { Card as t, CardContent as n, CardHeader as r } from "../card/card.js";
|
|
4
4
|
import { ChartState as i } from "../chart-state/chart-state.js";
|
|
5
5
|
import * as a from "react";
|
|
6
|
-
import {
|
|
6
|
+
import { Fragment as o, jsx as s, jsxs as c } from "react/jsx-runtime";
|
|
7
7
|
//#region src/components/ui/chart-widget/chart-widget.tsx
|
|
8
|
-
var
|
|
9
|
-
let
|
|
10
|
-
return /* @__PURE__ */
|
|
11
|
-
ref:
|
|
12
|
-
hasError: f === "error",
|
|
8
|
+
var l = a.forwardRef(({ className: a, header: l, orientation: u = "vertical", metric: d, state: f, stateDescription: p, bodyClassName: m, children: h, ...g }, _) => {
|
|
9
|
+
let v = u === "horizontal";
|
|
10
|
+
return /* @__PURE__ */ c(t, {
|
|
11
|
+
ref: _,
|
|
13
12
|
"data-slot": "chart-widget",
|
|
14
13
|
"data-orientation": u,
|
|
15
|
-
className: e("flex h-full w-full flex-col", a),
|
|
16
|
-
...
|
|
17
|
-
children: [l ? /* @__PURE__ */
|
|
18
|
-
className: e("min-h-0 flex-1 gap-4 pt-4",
|
|
19
|
-
children:
|
|
20
|
-
className:
|
|
14
|
+
className: e("flex h-full w-full min-w-0 flex-col", a),
|
|
15
|
+
...g,
|
|
16
|
+
children: [l ? /* @__PURE__ */ s(r, { ...l }) : null, /* @__PURE__ */ s(n, {
|
|
17
|
+
className: e("min-h-0 min-w-0 flex-1 gap-4 pt-4", v && !f && "flex-row items-stretch", m),
|
|
18
|
+
children: f ? /* @__PURE__ */ s(i, {
|
|
19
|
+
className: "min-h-0 flex-1",
|
|
20
|
+
state: f,
|
|
21
|
+
description: p
|
|
22
|
+
}) : /* @__PURE__ */ c(o, { children: [d ? /* @__PURE__ */ s("div", {
|
|
23
|
+
className: e(v ? "min-w-0 flex-[1_0_0]" : "shrink-0"),
|
|
21
24
|
"data-slot": "chart-widget-metric",
|
|
22
25
|
children: d
|
|
23
|
-
}) : null, /* @__PURE__ */
|
|
24
|
-
className: e("min-h-0",
|
|
26
|
+
}) : null, /* @__PURE__ */ s("div", {
|
|
27
|
+
className: e("min-h-0 min-w-0", v ? "flex-[1_0_0]" : "flex-1"),
|
|
25
28
|
"data-slot": "chart-widget-body",
|
|
26
|
-
children:
|
|
27
|
-
|
|
28
|
-
variant: c,
|
|
29
|
-
description: p,
|
|
30
|
-
action: m
|
|
31
|
-
}) : g
|
|
32
|
-
})]
|
|
29
|
+
children: h
|
|
30
|
+
})] })
|
|
33
31
|
})]
|
|
34
32
|
});
|
|
35
33
|
});
|
|
36
|
-
|
|
34
|
+
l.displayName = "ChartWidget";
|
|
37
35
|
//#endregion
|
|
38
|
-
export {
|
|
36
|
+
export { l as ChartWidget };
|
|
39
37
|
|
|
40
38
|
//# sourceMappingURL=chart-widget.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chart-widget.js","names":[],"sources":["../../../../src/components/ui/chart-widget/chart-widget.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { useRender } from '@base-ui/react/use-render';\n\nimport { cn } from '@/lib/utils';\nimport { Card, CardContent, CardHeader, type CardHeaderProps } from '../card';\nimport {
|
|
1
|
+
{"version":3,"file":"chart-widget.js","names":[],"sources":["../../../../src/components/ui/chart-widget/chart-widget.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { useRender } from '@base-ui/react/use-render';\n\nimport { cn } from '@/lib/utils';\nimport { Card, CardContent, CardHeader, type CardHeaderProps } from '../card';\nimport { ChartState, type ChartStateProps } from '../chart-state';\n\n// A dashboard chart widget: the Card every chart mockup is wrapped in, plus the\n// one thing a Card doesn't know about — what the body shows while there is no\n// plot to show.\n//\n// Deliberately thin. `CardHeader` already owns the whole header surface (title,\n// description, extras, actions, drag handle, switch, avatar, rename, collapse\n// trigger) and draws the divider under itself, so this re-declares none of it:\n// `header` is typed as `CardHeaderProps` and spread, which means a header\n// feature added to Card arrives here for free instead of drifting.\n//\n// It owns no size either. The Figma `size` axis (sm/md/lg) only changes the\n// WIDTH (288/592/896), and every set is `HUG` vertically. So the height comes\n// from the dashboard grid, and the widget just passes it down: the header takes\n// what it needs, and the plot fills whatever is left (`flex-1 min-h-0` all the\n// way down, so a chart given `size-full` occupies the rest of the card).\n//\n// Dropped into a parent with no definite height, `h-full` resolves to `auto` and\n// the card hugs its content instead — so a standalone widget still works, with\n// the chart bringing its own height.\n//\n// The per-type chart components stay card-less on purpose. They are the plot;\n// the card is composition. That keeps `<AreaChart>` usable inside a table cell,\n// a popover, or a `Metric`'s sparkline slot, where a card would be wrong.\n//\n// Figma: every chart set wraps the `Card` set (`10012:195993`) — ChartArea\n// `8174:22232`, ChartBar `8700:55606`, ChartDonut `8811:172438`, and so on.\n\nexport interface ChartWidgetProps extends Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'title'\n> {\n /**\n * Replace the rendered `<div>` with another element or component (Base UI\n * composition), forwarded to `Card`. This is how a dashboard gives each\n * widget a landmark role — `render={<section aria-label=\"Sessions\" />}` — the\n * one thing the widget can't infer for itself.\n */\n render?: useRender.RenderProp;\n /**\n * The card header, forwarded verbatim to `CardHeader`. Everything that\n * component takes works here — `title`, `actions` (the ⋯ menu), `extras` (a\n * filter chip), `isDraggable`, `hasRename`, … Omit it for a header-less\n * widget.\n */\n header?: CardHeaderProps;\n /**\n * The readout beside or above the plot — typically a `Metric`.\n *\n * - `'vertical'` (default) — metric above the plot, full width. The metric\n * takes its natural height; the plot fills the rest of the card body.\n * - `'horizontal'` — metric and plot side by side, each `flex-1`. Use for\n * compact widgets (sm/288px) where vertical space is at a premium.\n *\n * Figma: vertical → `8174:22335`, horizontal → `8982:31681`.\n */\n orientation?: 'vertical' | 'horizontal';\n /**\n * The readout beside or above the plot — typically a `Metric`. Layout is\n * controlled by `orientation`; it is replaced along with the plot while a\n * state is active.\n */\n metric?: React.ReactNode;\n /**\n * Explicitly show a state in place of the metric and plot. The consumer\n * selects the state; this component does not infer it from chart data. The\n * `number` state displays a KPI-style numeric readout. The card chrome stays\n * the same for each state.\n */\n state?: ChartStateProps['state'];\n /**\n * Text shown in the selected state, or the numeric readout for `number`.\n * Defaults to `\"Data is loading…\"` / `\"No data found\"` / `\"0\"` / etc.\n */\n stateDescription?: ChartStateProps['description'];\n /**\n * Class for the card body. Rarely needed — the body already fills the card.\n * Use it where nothing above gives a height and the content can't either, e.g.\n * a placeholder-only widget outside a sized grid cell.\n */\n bodyClassName?: string;\n /** The chart. Replaced with `ChartState` together with `metric` while `state` is set. */\n children?: React.ReactNode;\n}\n\nconst ChartWidget = React.forwardRef<HTMLDivElement, ChartWidgetProps>(\n (\n {\n className,\n header,\n orientation = 'vertical',\n metric,\n state,\n stateDescription,\n bodyClassName,\n children,\n ...props\n },\n ref\n ) => {\n const isHorizontal = orientation === 'horizontal';\n return (\n <Card\n ref={ref}\n data-slot=\"chart-widget\"\n data-orientation={orientation}\n // `h-full` + a flex column is what lets the plot fill the card: the grid\n // sizes this, the header is `shrink-0`, and the body takes the rest.\n className={cn('flex h-full w-full min-w-0 flex-col', className)}\n {...props}\n >\n {header ? <CardHeader {...header} /> : null}\n {/* `CardContent` brings `px-4 pb-4`; the design's body is padded on all\n four sides with a 16px gap between the metric row and the plot. */}\n <CardContent\n className={cn(\n 'min-h-0 min-w-0 flex-1 gap-4 pt-4',\n // In horizontal mode override the default flex-col with flex-row so\n // the metric and the chart sit side by side, each taking half the body.\n isHorizontal && !state && 'flex-row items-stretch',\n bodyClassName\n )}\n >\n {state ? (\n <ChartState\n className=\"min-h-0 flex-1\"\n state={state}\n description={stateDescription}\n />\n ) : (\n <>\n {metric ? (\n <div\n className={cn(\n // Vertical: takes natural height, full width.\n // Horizontal: flex-[1_0_0] matches Figma — equal half-width,\n // no shrink (flex: 1 0 0), min-w-0 allows text to truncate.\n isHorizontal ? 'min-w-0 flex-[1_0_0]' : 'shrink-0'\n )}\n data-slot=\"chart-widget-metric\"\n >\n {metric}\n </div>\n ) : null}\n {/* `min-h-0` matters: a flex child refuses to shrink below its\n content's height without it, so a chart can push past the grid cell. */}\n <div\n className={cn(\n 'min-h-0 min-w-0',\n isHorizontal ? 'flex-[1_0_0]' : 'flex-1'\n )}\n data-slot=\"chart-widget-body\"\n >\n {children}\n </div>\n </>\n )}\n </CardContent>\n </Card>\n );\n }\n);\nChartWidget.displayName = 'ChartWidget';\n\nexport { ChartWidget };\n"],"mappings":";;;;;;;AA6FA,IAAM,IAAc,EAAM,YAEtB,EACE,cACA,WACA,iBAAc,YACd,WACA,UACA,qBACA,kBACA,aACA,GAAG,KAEL,MACG;CACH,IAAM,IAAe,MAAgB;CACrC,OACE,kBAAC,GAAD;EACO;EACL,aAAU;EACV,oBAAkB;EAGlB,WAAW,EAAG,uCAAuC,CAAS;EAC9D,GAAI;EAPN,UAAA,CASG,IAAS,kBAAC,GAAD,EAAY,GAAI,EAAS,CAAA,IAAI,MAGvC,kBAAC,GAAD;GACE,WAAW,EACT,qCAGA,KAAgB,CAAC,KAAS,0BAC1B,CACF;GAEC,UAAA,IACC,kBAAC,GAAD;IACE,WAAU;IACH;IACP,aAAa;GACd,CAAA,IAED,kBAAA,GAAA,EAAA,UAAA,CACG,IACC,kBAAC,OAAD;IACE,WAAW,EAIT,IAAe,yBAAyB,UAC1C;IACA,aAAU;IAET,UAAA;GACE,CAAA,IACH,MAGJ,kBAAC,OAAD;IACE,WAAW,EACT,mBACA,IAAe,iBAAiB,QAClC;IACA,aAAU;IAET;GACE,CAAA,CACL,EAAA,CAAA;EAEO,CAAA,CACT;;AAEV,CACF;AACA,EAAY,cAAc"}
|