@krizaka/charts 0.1.0 → 0.2.1
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/README.md +17 -5
- package/dist/bar.js +5 -4
- package/dist/candlestick.js +3 -3
- package/dist/chart.js +3 -3
- package/dist/{chunk-EK3Z647J.js → chunk-4LBXEAGC.js} +3 -3
- package/dist/{chunk-AHQXJSAQ.js → chunk-AA5EWWYV.js} +2 -2
- package/dist/{chunk-VKO2OYYM.js → chunk-ARARFMNE.js} +2 -2
- package/dist/{chunk-WBH2QU77.js → chunk-BV4DBGN7.js} +2 -2
- package/dist/{chunk-BZ5CQFPH.js → chunk-C4MFZLJM.js} +2 -2
- package/dist/{chunk-XAJ3KCC4.js → chunk-CSSZLFLO.js} +3 -3
- package/dist/{chunk-UNK4MJVG.js → chunk-CVWSMUUB.js} +8 -8
- package/dist/chunk-E5DKBSWX.js +2 -0
- package/dist/chunk-E5DKBSWX.js.map +1 -0
- package/dist/chunk-EEP7PS2L.js +54 -0
- package/dist/chunk-EEP7PS2L.js.map +1 -0
- package/dist/{chunk-DZB6HEVO.js → chunk-EQLC2QVN.js} +2 -2
- package/dist/chunk-FINOPIDN.js +2 -0
- package/dist/chunk-FINOPIDN.js.map +1 -0
- package/dist/chunk-G2KF2WL4.js +2 -0
- package/dist/chunk-G2KF2WL4.js.map +1 -0
- package/dist/{chunk-PTSB3H5Q.js → chunk-I3D7KUGN.js} +2 -2
- package/dist/chunk-IKQV3NSL.js +76 -0
- package/dist/chunk-IKQV3NSL.js.map +1 -0
- package/dist/{chunk-DWJPPEMC.js → chunk-JTKR7ZWP.js} +73 -60
- package/dist/chunk-JTKR7ZWP.js.map +1 -0
- package/dist/chunk-KNDHO67X.js +78 -0
- package/dist/chunk-KNDHO67X.js.map +1 -0
- package/dist/chunk-L2EKYWVI.js +94 -0
- package/dist/chunk-L2EKYWVI.js.map +1 -0
- package/dist/{chunk-XC6BQ3RN.js → chunk-MXLITIGU.js} +3 -3
- package/dist/{chunk-OER6ETT4.js → chunk-NCIQMRV3.js} +3 -3
- package/dist/chunk-OIRHLJBC.js +83 -0
- package/dist/chunk-OIRHLJBC.js.map +1 -0
- package/dist/{chunk-WIRCIOLH.js → chunk-PBQV3B6O.js} +5 -5
- package/dist/chunk-POQIHFAZ.js +2 -0
- package/dist/chunk-POQIHFAZ.js.map +1 -0
- package/dist/{chunk-I5QUZVU2.js → chunk-PWMMXRMC.js} +2 -2
- package/dist/chunk-SLEJDQ3A.js +81 -0
- package/dist/chunk-SLEJDQ3A.js.map +1 -0
- package/dist/{chunk-7TIFCK5E.js → chunk-VKZZY2QD.js} +3 -3
- package/dist/{chunk-VKTZD7UT.js → chunk-W5ILLSPK.js} +3 -3
- package/dist/chunk-WBHFVBSK.js +25 -0
- package/dist/chunk-WBHFVBSK.js.map +1 -0
- package/dist/chunk-WNUFGMS7.js +113 -0
- package/dist/chunk-WNUFGMS7.js.map +1 -0
- package/dist/{chunk-XR4ZJDOV.js → chunk-WVTIKOII.js} +2 -2
- package/dist/{chunk-7CSAGFBW.js → chunk-XDDALY4C.js} +3 -3
- package/dist/credits.d.ts +42 -0
- package/dist/credits.js +21 -0
- package/dist/credits.js.map +1 -0
- package/dist/earnings.d.ts +29 -0
- package/dist/earnings.js +22 -0
- package/dist/earnings.js.map +1 -0
- package/dist/funnel.js +3 -3
- package/dist/gauge.js +3 -2
- package/dist/geo.js +2 -2
- package/dist/globe.js +2 -2
- package/dist/goal.d.ts +26 -0
- package/dist/goal.js +17 -0
- package/dist/goal.js.map +1 -0
- package/dist/heatmap.js +3 -3
- package/dist/index.d.ts +7 -0
- package/dist/index.js +80 -33
- package/dist/labels-DE_743fQ.d.ts +72 -0
- package/dist/latency.d.ts +33 -0
- package/dist/latency.js +24 -0
- package/dist/latency.js.map +1 -0
- package/dist/line.js +5 -4
- package/dist/native.d.ts +195 -1
- package/dist/native.js +383 -14
- package/dist/native.js.map +1 -1
- package/dist/pie.js +3 -3
- package/dist/radar.js +3 -3
- package/dist/sankey.js +3 -3
- package/dist/scatter.js +3 -3
- package/dist/sparkline.js +4 -3
- package/dist/throughput.d.ts +37 -0
- package/dist/throughput.js +24 -0
- package/dist/throughput.js.map +1 -0
- package/dist/ticker.d.ts +38 -0
- package/dist/ticker.js +22 -0
- package/dist/ticker.js.map +1 -0
- package/dist/treemap.js +3 -3
- package/package.json +102 -6
- package/registry/area-chart.json +1 -0
- package/registry/bar-chart.json +1 -0
- package/registry/calendar-heatmap.json +1 -0
- package/registry/candlestick.json +1 -0
- package/registry/chart.json +1 -0
- package/registry/charts-provider.json +1 -0
- package/registry/credit-usage.json +473 -0
- package/registry/earnings-chart.json +703 -0
- package/registry/examples/credit-usage/monthly-credits.d.ts +4 -0
- package/registry/examples/credit-usage/monthly-credits.js +24 -0
- package/registry/examples/credit-usage/monthly-credits.tsx +19 -0
- package/registry/examples/credit-usage/native/monthly-credits.d.ts +4 -0
- package/registry/examples/credit-usage/native/monthly-credits.dark.png +0 -0
- package/registry/examples/credit-usage/native/monthly-credits.js +23 -0
- package/registry/examples/credit-usage/native/monthly-credits.light.png +0 -0
- package/registry/examples/credit-usage/native/monthly-credits.tsx +18 -0
- package/registry/examples/credit-usage/storage-plan.d.ts +4 -0
- package/registry/examples/credit-usage/storage-plan.js +21 -0
- package/registry/examples/credit-usage/storage-plan.tsx +15 -0
- package/registry/examples/earnings-chart/creator-month.d.ts +4 -0
- package/registry/examples/earnings-chart/creator-month.js +29 -0
- package/registry/examples/earnings-chart/creator-month.tsx +24 -0
- package/registry/examples/earnings-chart/gross-and-net.d.ts +4 -0
- package/registry/examples/earnings-chart/gross-and-net.js +29 -0
- package/registry/examples/earnings-chart/gross-and-net.tsx +24 -0
- package/registry/examples/earnings-chart/native/creator-month.d.ts +4 -0
- package/registry/examples/earnings-chart/native/creator-month.dark.png +0 -0
- package/registry/examples/earnings-chart/native/creator-month.js +29 -0
- package/registry/examples/earnings-chart/native/creator-month.light.png +0 -0
- package/registry/examples/earnings-chart/native/creator-month.tsx +24 -0
- package/registry/examples/goal-progress/challenge-pot.d.ts +4 -0
- package/registry/examples/goal-progress/challenge-pot.js +22 -0
- package/registry/examples/goal-progress/challenge-pot.tsx +16 -0
- package/registry/examples/goal-progress/goal-reached.d.ts +4 -0
- package/registry/examples/goal-progress/goal-reached.js +11 -0
- package/registry/examples/goal-progress/goal-reached.tsx +6 -0
- package/registry/examples/goal-progress/native/challenge-pot.d.ts +4 -0
- package/registry/examples/goal-progress/native/challenge-pot.dark.png +0 -0
- package/registry/examples/goal-progress/native/challenge-pot.js +11 -0
- package/registry/examples/goal-progress/native/challenge-pot.light.png +0 -0
- package/registry/examples/goal-progress/native/challenge-pot.tsx +6 -0
- package/registry/examples/job-throughput/live-hours.d.ts +4 -0
- package/registry/examples/job-throughput/live-hours.js +33 -0
- package/registry/examples/job-throughput/live-hours.tsx +28 -0
- package/registry/examples/job-throughput/native/week-of-jobs.d.ts +4 -0
- package/registry/examples/job-throughput/native/week-of-jobs.dark.png +0 -0
- package/registry/examples/job-throughput/native/week-of-jobs.js +20 -0
- package/registry/examples/job-throughput/native/week-of-jobs.light.png +0 -0
- package/registry/examples/job-throughput/native/week-of-jobs.tsx +16 -0
- package/registry/examples/job-throughput/week-of-jobs.d.ts +4 -0
- package/registry/examples/job-throughput/week-of-jobs.js +20 -0
- package/registry/examples/job-throughput/week-of-jobs.tsx +16 -0
- package/registry/examples/latency-chart/by-capability.d.ts +4 -0
- package/registry/examples/latency-chart/by-capability.js +26 -0
- package/registry/examples/latency-chart/by-capability.tsx +21 -0
- package/registry/examples/latency-chart/native/by-capability.d.ts +4 -0
- package/registry/examples/latency-chart/native/by-capability.dark.png +0 -0
- package/registry/examples/latency-chart/native/by-capability.js +17 -0
- package/registry/examples/latency-chart/native/by-capability.light.png +0 -0
- package/registry/examples/latency-chart/native/by-capability.tsx +13 -0
- package/registry/examples/latency-chart/time-to-result.d.ts +4 -0
- package/registry/examples/latency-chart/time-to-result.js +27 -0
- package/registry/examples/latency-chart/time-to-result.tsx +22 -0
- package/registry/examples/price-ticker/auction-room.d.ts +4 -0
- package/registry/examples/price-ticker/auction-room.js +21 -0
- package/registry/examples/price-ticker/auction-room.tsx +17 -0
- package/registry/examples/price-ticker/native/auction-room.d.ts +4 -0
- package/registry/examples/price-ticker/native/auction-room.dark.png +0 -0
- package/registry/examples/price-ticker/native/auction-room.js +19 -0
- package/registry/examples/price-ticker/native/auction-room.light.png +0 -0
- package/registry/examples/price-ticker/native/auction-room.tsx +15 -0
- package/registry/examples/price-ticker/no-bid-yet.d.ts +4 -0
- package/registry/examples/price-ticker/no-bid-yet.js +11 -0
- package/registry/examples/price-ticker/no-bid-yet.tsx +6 -0
- package/registry/funnel.json +1 -0
- package/registry/gauge.json +1 -0
- package/registry/geo-map.json +1 -0
- package/registry/globe.json +1 -0
- package/registry/goal-progress.json +520 -0
- package/registry/heatmap.json +1 -0
- package/registry/index.json +163 -1
- package/registry/job-throughput.json +654 -0
- package/registry/latency-chart.json +671 -0
- package/registry/line-chart.json +1 -0
- package/registry/pie-chart.json +1 -0
- package/registry/price-ticker.json +684 -0
- package/registry/radar-chart.json +1 -0
- package/registry/sankey.json +1 -0
- package/registry/scatter-chart.json +1 -0
- package/registry/sparkline.json +1 -0
- package/registry/treemap.json +1 -0
- package/dist/chunk-DWJPPEMC.js.map +0 -1
- /package/dist/{chunk-EK3Z647J.js.map → chunk-4LBXEAGC.js.map} +0 -0
- /package/dist/{chunk-AHQXJSAQ.js.map → chunk-AA5EWWYV.js.map} +0 -0
- /package/dist/{chunk-VKO2OYYM.js.map → chunk-ARARFMNE.js.map} +0 -0
- /package/dist/{chunk-WBH2QU77.js.map → chunk-BV4DBGN7.js.map} +0 -0
- /package/dist/{chunk-BZ5CQFPH.js.map → chunk-C4MFZLJM.js.map} +0 -0
- /package/dist/{chunk-XAJ3KCC4.js.map → chunk-CSSZLFLO.js.map} +0 -0
- /package/dist/{chunk-UNK4MJVG.js.map → chunk-CVWSMUUB.js.map} +0 -0
- /package/dist/{chunk-DZB6HEVO.js.map → chunk-EQLC2QVN.js.map} +0 -0
- /package/dist/{chunk-PTSB3H5Q.js.map → chunk-I3D7KUGN.js.map} +0 -0
- /package/dist/{chunk-XC6BQ3RN.js.map → chunk-MXLITIGU.js.map} +0 -0
- /package/dist/{chunk-OER6ETT4.js.map → chunk-NCIQMRV3.js.map} +0 -0
- /package/dist/{chunk-WIRCIOLH.js.map → chunk-PBQV3B6O.js.map} +0 -0
- /package/dist/{chunk-I5QUZVU2.js.map → chunk-PWMMXRMC.js.map} +0 -0
- /package/dist/{chunk-7TIFCK5E.js.map → chunk-VKZZY2QD.js.map} +0 -0
- /package/dist/{chunk-VKTZD7UT.js.map → chunk-W5ILLSPK.js.map} +0 -0
- /package/dist/{chunk-XR4ZJDOV.js.map → chunk-WVTIKOII.js.map} +0 -0
- /package/dist/{chunk-7CSAGFBW.js.map → chunk-XDDALY4C.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/web/hooks.ts","../src/web/parts.tsx","../src/web/styles.tsx","../src/web/frame.tsx","../src/web/export.ts"],"sourcesContent":["\"use client\";\n// The browser facts a chart reacts to: its width (ResizeObserver), whether it is on screen (IntersectionObserver),\n// the reader's motion preference, the theme, and whether it runs in the browser at all (SSR renders a placeholder).\nimport { type RefObject, useEffect, useState, useSyncExternalStore } from \"react\";\n\n/** The element's content width, rounded; 0 until measured (on the server, before the first layout). */\nexport function useWidth(ref: RefObject<HTMLElement | null>): number {\n const [width, setWidth] = useState(0);\n useEffect(() => {\n const el = ref.current;\n if (!el) return undefined;\n const measure = () => setWidth(Math.round(el.getBoundingClientRect().width));\n measure();\n if (typeof ResizeObserver === \"undefined\") return undefined;\n // Coalesced to one frame: a drag of a window does not redraw the chart a hundred times.\n let frame = 0;\n const observer = new ResizeObserver(() => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(measure);\n });\n observer.observe(el);\n return () => {\n cancelAnimationFrame(frame);\n observer.disconnect();\n };\n }, [ref]);\n return width;\n}\n\n/**\n * Whether the element is on screen (with a margin): an entrance waits for it, a loop pauses outside it. `once` keeps\n * `true` after the first sight (entrances); otherwise it follows the element (loops). True where it cannot be known.\n */\nexport function useInView(ref: RefObject<HTMLElement | null>, { once = false, margin = \"120px\" } = {}): boolean {\n // Without IntersectionObserver (an old runtime, a test DOM), everything counts as on screen.\n const [inView, setInView] = useState(() => typeof window !== \"undefined\" && typeof IntersectionObserver === \"undefined\");\n useEffect(() => {\n const el = ref.current;\n if (!el || typeof IntersectionObserver === \"undefined\") return undefined;\n const observer = new IntersectionObserver(\n ([entry]) => {\n const visible = Boolean(entry?.isIntersecting);\n setInView(visible);\n if (visible && once) observer.disconnect();\n },\n { rootMargin: margin },\n );\n observer.observe(el);\n return () => observer.disconnect();\n }, [ref, once, margin]);\n return inView;\n}\n\nconst REDUCE = \"(prefers-reduced-motion: reduce)\";\nconst subscribeMotion = (notify: () => void) => {\n if (typeof matchMedia === \"undefined\") return () => {};\n const query = matchMedia(REDUCE);\n query.addEventListener(\"change\", notify);\n return () => query.removeEventListener(\"change\", notify);\n};\n\n/** Whether the reader asked for reduced motion (false on the server: the CSS guards the entrances anyway). */\nexport function useReducedMotion(): boolean {\n return useSyncExternalStore(\n subscribeMotion,\n () => typeof matchMedia !== \"undefined\" && matchMedia(REDUCE).matches,\n () => false,\n );\n}\n\n/** Increments on every theme change (`html` class, style or `data-theme`, and the OS scheme): canvas charts re-read their colours. */\nexport function useThemeVersion(): number {\n const [version, setVersion] = useState(0);\n useEffect(() => {\n const bump = () => setVersion((v) => v + 1);\n const observer = new MutationObserver(bump);\n observer.observe(document.documentElement, { attributes: true, attributeFilter: [\"class\", \"style\", \"data-theme\"] });\n observer.observe(document.body, { attributes: true, attributeFilter: [\"class\", \"style\", \"data-theme\"] });\n const query = typeof matchMedia === \"undefined\" ? null : matchMedia(\"(prefers-color-scheme: dark)\");\n query?.addEventListener(\"change\", bump);\n return () => {\n observer.disconnect();\n query?.removeEventListener(\"change\", bump);\n };\n }, []);\n return version;\n}\n\nconst noop = () => () => {};\n/** False during the server render and the hydration pass, true after: what only the browser can draw waits for it. */\nexport function useMounted(): boolean {\n return useSyncExternalStore(\n noop,\n () => true,\n () => false,\n );\n}\n","// The small pieces every chart shares: a series' colour, the legend and the tooltip (the surface of @krizaka/ui's\n// floating elements: raised surface, border, large shadow; text in the text roles, the colour on a key beside it).\nimport type { ReactNode } from \"react\";\n\nimport type { SeriesColor, Tone } from \"../core/types\";\n\n/** Joins class names, skipping the empty ones. */\nexport const cx = (...names: (string | false | null | undefined)[]) => names.filter(Boolean).join(\" \");\n\nconst TONES: Record<Tone, string> = {\n neutral: \"var(--kz-text-secondary)\",\n accent: \"var(--kz-accent)\",\n success: \"var(--kz-success)\",\n warning: \"var(--kz-warning)\",\n danger: \"var(--kz-danger)\",\n info: \"var(--kz-info)\",\n};\n\n/** The CSS colour of a series: its own (`color`), else its palette slot, `var(--kzc-<slot>)`. */\nexport function colorOf(color: SeriesColor | undefined, slot: number): string {\n if (color === undefined) return `var(--kzc-${((slot - 1) % 8) + 1})`;\n if (typeof color === \"number\") return `var(--kzc-${color})`;\n if (color in TONES) return TONES[color as Tone];\n return color;\n}\n\nexport interface LegendItem {\n readonly label: string;\n readonly color: string;\n readonly shape?: \"square\" | \"line\";\n readonly dashed?: boolean;\n}\n\n/** The legend: a key in the series' colour beside its name in the text colour. Shown from two series. */\nexport function Legend({ items }: { items: readonly LegendItem[] }) {\n if (items.length < 2) return null;\n return (\n <ul className=\"kzc-legend\" aria-hidden>\n {items.map((item) => (\n <li key={item.label}>\n <span\n className=\"kzc-key\"\n data-shape={item.shape}\n style={{ background: item.dashed ? `repeating-linear-gradient(90deg, ${item.color} 0 4px, transparent 4px 7px)` : item.color }}\n />\n {item.label}\n </li>\n ))}\n </ul>\n );\n}\n\nexport interface TooltipRow {\n readonly label: string;\n readonly value: string;\n readonly color?: string;\n readonly shape?: \"square\" | \"line\";\n}\n\nexport interface TooltipProps {\n /** Where the pointer or the active point is, in the plot's pixels. */\n readonly x: number;\n readonly y: number;\n /** The plot's width: the tooltip flips to the left of the point past the middle. */\n readonly width: number;\n readonly title?: string;\n readonly rows: readonly TooltipRow[];\n readonly note?: ReactNode;\n}\n\n/** The rich tooltip: a title (the category), one row per series (key, name, value), an optional note. */\nexport function Tooltip({ x, y, width, title, rows, note }: TooltipProps) {\n const flip = x > width / 2;\n return (\n <div className=\"kzc-tip\" aria-hidden style={{ left: flip ? x - 12 : x + 12, top: Math.max(0, y - 12), transform: flip ? \"translateX(-100%)\" : undefined }}>\n {title && <p className=\"kzc-tip-title\">{title}</p>}\n {rows.map((row, i) => (\n <p key={i} className=\"kzc-tip-row\">\n {row.color ? <span className=\"kzc-key\" data-shape={row.shape} style={{ background: row.color }} /> : <span />}\n <span>{row.label}</span>\n <b>{row.value}</b>\n </p>\n ))}\n {note && <p className=\"kzc-tip-note\">{note}</p>}\n </div>\n );\n}\n\n/**\n * The colour of step `i` of `n` of a sequential scale: the brand hue (slot 1) mixed into the surface in OKLab, from\n * 22 % (still distinct from the surface) to 100 % — the same steps as `sequential()` for the canvas engine.\n */\nexport const stepColor = (i: number, n: number) => `color-mix(in oklab, var(--kzc-1) ${Math.round(22 + (78 * i) / Math.max(1, n - 1))}%, var(--kz-surface-1))`;\n\n/** The legend of a sequential scale: its lowest value, the steps from near the surface to the full hue, its highest. */\nexport function RampLegend({ min, max, steps, write }: { min: number; max: number; steps: number; write: (v: number) => string }) {\n return (\n <div className=\"kzc-legend\" aria-hidden>\n <span>{write(min)}</span>\n <span style={{ display: \"inline-flex\", gap: 2 }}>\n {Array.from({ length: steps }, (_, i) => (\n <span key={i} className=\"kzc-key\" style={{ background: stepColor(i, steps), width: 14 }} />\n ))}\n </span>\n <span>{write(max)}</span>\n </div>\n );\n}\n","// The charts' own stylesheet: plain CSS with `kzc-` classes on the --kz-* roles, so a chart needs nothing but the\n// tokens an app already loads (no Tailwind source to declare, no CSS import). Rendered once per document through\n// React 19's stylesheet hoisting (`<style href precedence>`, deduplicated, server-rendered in <head>).\n//\n// The palette is `light-dark()`: it follows the `color-scheme` the tokens set (`:root`/`.theme-dark` dark,\n// `html.light`/`.theme-light` light) — a theme switch repaints every chart with no JavaScript. The brand's order comes\n// from a `.brand-<id>` ancestor or the chart's own `data-brand`.\nimport { HUES, ORDERS } from \"../core/palette\";\nimport type { BrandId } from \"../core/types\";\n\nconst slots = (brand: BrandId) =>\n ORDERS[brand].map((hue, i) => `--kzc-${i + 1}:light-dark(${HUES[hue].light},${HUES[hue].dark});`).join(\"\");\n\nconst PALETTE = [\n `[data-kzc]{${slots(\"krizaka\")}--kzc-low:light-dark(${HUES.blue.light},${HUES.blue.dark});--kzc-high:light-dark(${HUES.red.light},${HUES.red.dark});}`,\n ...([\"orazaka\", \"orochia\", \"krizaka\"] as const).map((brand) => `:where(.brand-${brand}) [data-kzc]{${slots(brand)}}`),\n ...([\"orazaka\", \"orochia\", \"krizaka\"] as const).map((brand) => `[data-kzc][data-brand=${brand}]{${slots(brand)}}`),\n].join(\"\");\n\nconst tone = (name: string, value: string) => `.kzc [data-tone=${name}]{--kzc-tone:${value}}`;\n\nexport const CSS = `${PALETTE}\n.kzc{--kzc-text:var(--kz-text-primary);--kzc-text-2:var(--kz-text-secondary);--kzc-text-3:var(--kz-text-muted);--kzc-grid:var(--kz-border-subtle);--kzc-axis:var(--kz-border-default);--kzc-surface:var(--kz-surface-1);--kzc-tone:var(--kz-text-secondary);margin:0;min-width:0;display:flex;flex-direction:column;gap:8px;color:var(--kzc-text);font-family:var(--kz-font-sans,system-ui,sans-serif);font-variant-numeric:tabular-nums}\n${tone(\"neutral\", \"var(--kz-text-secondary)\")}${tone(\"accent\", \"var(--kz-accent)\")}${tone(\"success\", \"var(--kz-success)\")}${tone(\"warning\", \"var(--kz-warning)\")}${tone(\"danger\", \"var(--kz-danger)\")}${tone(\"info\", \"var(--kz-info)\")}\n.kzc-head{display:flex;flex-wrap:wrap;align-items:flex-start;justify-content:space-between;gap:4px 12px}\n.kzc-heading{display:flex;flex-direction:column;gap:2px;min-width:0}\n.kzc-title{margin:0;font-family:var(--kz-font-display,inherit);font-size:14px;font-weight:600;line-height:20px;color:var(--kzc-text)}\n.kzc-desc{margin:0;font-size:13px;line-height:18px;color:var(--kzc-text-2)}\n.kzc-tools{display:flex;flex-wrap:wrap;gap:4px}\n.kzc-btn{appearance:none;display:inline-flex;align-items:center;gap:4px;height:28px;padding:0 10px;border:1px solid var(--kz-border-default);border-radius:var(--kz-radius-md,8px);background:transparent;color:var(--kzc-text-2);font:inherit;font-size:12px;font-weight:500;cursor:pointer;transition:background-color .15s,color .15s}\n.kzc-btn:hover{background:var(--kz-surface-2);color:var(--kzc-text)}\n.kzc-btn:focus-visible,.kzc-plot:focus-visible{outline:2px solid var(--kz-ring);outline-offset:2px}\n.kzc-btn[aria-pressed=true]{background:var(--kz-surface-3);color:var(--kzc-text)}\n.kzc-plot{position:relative;width:100%;border-radius:var(--kz-radius-md,8px);outline:none}\n.kzc-plot svg{display:block;overflow:visible}\n.kzc-svg text{fill:var(--kzc-text-2);font-size:11px;font-family:inherit}\n.kzc-svg .kzc-strong{fill:var(--kzc-text);font-weight:600}\n.kzc-grid{stroke:var(--kzc-grid);stroke-width:1;shape-rendering:crispEdges}\n.kzc-axis{stroke:var(--kzc-axis);stroke-width:1;shape-rendering:crispEdges}\n.kzc-threshold{stroke:var(--kzc-tone);stroke-width:1.5;stroke-dasharray:6 4}\n.kzc-threshold-label{fill:var(--kzc-text)!important;font-weight:600;paint-order:stroke;stroke:var(--kzc-surface);stroke-width:3px;stroke-linejoin:round}\n.kzc-cursor{stroke:var(--kzc-axis);stroke-width:1}\n.kzc-line{fill:none;stroke-width:2;stroke-linejoin:round;stroke-linecap:round}\n.kzc-area{stroke:none;opacity:.14}\n.kzc-dot{stroke:var(--kzc-surface);stroke-width:2}\n.kzc-mark{transition:opacity .15s}\n.kzc[data-active] .kzc-mark[data-dim]{opacity:.35}\n.kzc-legend{display:flex;flex-wrap:wrap;gap:4px 14px;margin:0;padding:0;list-style:none;font-size:12px;color:var(--kzc-text-2)}\n.kzc-legend li{display:inline-flex;align-items:center;gap:6px}\n.kzc-key{width:10px;height:10px;border-radius:3px;flex:none}\n.kzc-key[data-shape=line]{height:3px;border-radius:2px;width:14px}\n.kzc-tip{position:absolute;z-index:2;pointer-events:none;min-width:140px;max-width:260px;padding:8px 10px;border:1px solid var(--kz-border-default);border-radius:var(--kz-radius-lg,10px);background:var(--kz-surface-2);color:var(--kzc-text);box-shadow:var(--kz-shadow-lg);font-size:12px;line-height:18px}\n.kzc-tip-title{margin:0 0 4px;font-weight:600;color:var(--kzc-text)}\n.kzc-tip-row{display:flex;align-items:center;gap:8px;margin:0}\n.kzc-tip-row span:nth-child(2){flex:1;color:var(--kzc-text-2)}\n.kzc-tip-row b{font-weight:600}\n.kzc-tip-note{margin:4px 0 0;color:var(--kzc-text-3)}\n.kzc-summary{margin:0;font-size:12px;line-height:18px;color:var(--kzc-text-2)}\n.kzc-sr{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}\n.kzc-table-wrap{max-height:320px;overflow:auto;border:1px solid var(--kz-border-subtle);border-radius:var(--kz-radius-md,8px)}\n.kzc-table{width:100%;border-collapse:collapse;font-size:12px}\n.kzc-table caption{padding:8px 10px;text-align:left;font-weight:600;color:var(--kzc-text)}\n.kzc-table th,.kzc-table td{padding:6px 10px;border-top:1px solid var(--kz-border-subtle);text-align:right;white-space:nowrap}\n.kzc-table th[scope=row],.kzc-table thead th:first-child{text-align:left;font-weight:500;color:var(--kzc-text)}\n.kzc-table thead th{position:sticky;top:0;background:var(--kz-surface-1);color:var(--kzc-text-2);font-weight:500}\n.kzc-state{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:16px;text-align:center;font-size:13px;color:var(--kzc-text-2);border:1px dashed var(--kz-border-default);border-radius:var(--kz-radius-lg,10px)}\n.kzc-state[data-state=error]{color:var(--kz-danger-text,var(--kzc-text))}\n.kzc-skeleton{position:absolute;inset:0;display:flex;align-items:flex-end;gap:6%;padding:12px 4% 24px;border-radius:var(--kz-radius-lg,10px);overflow:hidden}\n.kzc-skeleton i{flex:1;border-radius:4px 4px 0 0;background:var(--kz-surface-2)}\n.kzc-live{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0)}\n.kzc-host{position:absolute;inset:0}\n.kzc-host>div{border-radius:inherit}\n.kzc-kpi{display:inline-flex;flex-wrap:wrap;align-items:baseline;gap:2px 10px;margin-top:4px}\n.kzc-kpi-value{font-family:var(--kz-font-display,inherit);font-size:24px;line-height:30px;font-weight:800;letter-spacing:-.01em;color:var(--kzc-text)}\n.kzc-kpi-label{font-size:13px;color:var(--kzc-text-2)}\n.kzc-kpi-delta{display:inline-flex;align-items:center;gap:4px;font-size:13px;font-weight:600;color:var(--kzc-text)}\n.kzc-kpi-delta svg{width:12px;height:12px;flex:none;color:var(--kzc-tone)}\n.kzc-meter{position:relative;height:10px;border-radius:999px;background:var(--kz-surface-3);overflow:hidden;--kzc-tone:var(--kz-accent)}\n.kzc-meter>i{position:absolute;inset:0 auto 0 0;border-radius:inherit;background:var(--kzc-tone);transition:width .6s var(--kz-ease,ease-out)}\n.kzc-meter[data-tone=warning]{--kzc-tone:var(--kz-warning)}.kzc-meter[data-tone=danger]{--kzc-tone:var(--kz-danger)}\n.kzc-row{display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;gap:4px 12px;margin:0;font-size:12px;line-height:18px;color:var(--kzc-text-2)}\n.kzc-row b{font-weight:600;color:var(--kzc-text)}\n@media (prefers-reduced-motion:no-preference){\n.kzc-skeleton i{animation:kzc-pulse 1.6s ease-in-out infinite}\n.kzc[data-inview] .kzc-enter-line{stroke-dasharray:1;stroke-dashoffset:1;animation:kzc-draw .9s var(--kz-ease,ease-out) forwards}\n.kzc[data-inview] .kzc-enter-fade{opacity:0;animation:kzc-fade .6s .3s var(--kz-ease,ease-out) forwards}\n.kzc[data-inview] .kzc-enter-grow{transform:scaleY(0);transform-box:fill-box;transform-origin:bottom;animation:kzc-grow .6s var(--kz-ease,ease-out) forwards}\n.kzc[data-inview] .kzc-enter-grow[data-negative]{transform-origin:top}\n.kzc[data-inview] .kzc-enter-growx{transform:scaleX(0);transform-box:fill-box;transform-origin:left;animation:kzc-growx .6s var(--kz-ease,ease-out) forwards}\n.kzc[data-inview] .kzc-enter-growx[data-negative]{transform-origin:right}\n.kzc[data-inview] .kzc-enter-pop{opacity:0;transform:scale(.92);transform-box:view-box;transform-origin:center;animation:kzc-pop .6s var(--kz-ease,ease-out) forwards}\n.kzc:not([data-inview]) .kzc-enter-line,.kzc:not([data-inview]) .kzc-enter-fade,.kzc:not([data-inview]) .kzc-enter-grow,.kzc:not([data-inview]) .kzc-enter-growx,.kzc:not([data-inview]) .kzc-enter-pop{visibility:hidden}\n.kzc[data-live] *{animation:none!important}\n.kzc[data-live] .kzc-enter-line{stroke-dasharray:none;stroke-dashoffset:0}\n.kzc[data-live] :is(.kzc-enter-fade,.kzc-enter-grow,.kzc-enter-growx,.kzc-enter-pop){opacity:1;transform:none;visibility:visible}\n}\n@keyframes kzc-draw{to{stroke-dashoffset:0}}\n@keyframes kzc-fade{to{opacity:1}}\n@keyframes kzc-grow{to{transform:scaleY(1)}}\n@keyframes kzc-growx{to{transform:scaleX(1)}}\n@keyframes kzc-pop{to{opacity:1;transform:none}}\n@keyframes kzc-pulse{50%{opacity:.5}}\n@media (forced-colors:active){.kzc-line{stroke:CanvasText}.kzc-key{forced-color-adjust:none}}\n`.replace(/\\n/g, \"\");\n\n/** The stylesheet, once per document (React 19 hoists and deduplicates it, on the server too). */\nexport function ChartStyles() {\n return (\n <style href=\"krizaka-charts\" precedence=\"default\">\n {CSS}\n </style>\n );\n}\n","\"use client\";\n// The frame every web chart is drawn in — where the \"better than the industry\" promises live, once for all of them:\n// - a title (the accessible name) and a description; a written summary (min, max, trend) read by screen readers, or\n// shown (`summary=\"visible\"`);\n// - the data table, always in the page for assistive technology (visually hidden), shown by \"Show data\";\n// - one tab stop on the plot (`role=\"group\"`, roledescription \"chart\"), the arrow keys walking its points, each one\n// announced in a polite live region;\n// - loading (a chart skeleton), empty and error states, at the chart's own height;\n// - responsive (ResizeObserver) and SSR without layout shift: the server renders the frame, the summary, the table and\n// a placeholder of the exact height; the browser draws once it knows the width;\n// - an entrance once on screen, none under reduced motion, none for live data;\n// - export to PNG, SVG and CSV.\nimport { type CSSProperties, type KeyboardEvent, type PointerEvent, type ReactNode, type RefObject, useCallback, useId, useMemo, useRef, useState } from \"react\";\n\nimport type { ChartKind, ChartLabels } from \"../core/labels\";\nimport { fill } from \"../core/labels\";\nimport type { BrandId, Locale } from \"../core/types\";\nimport { type ChartsSettings, useChartSettings } from \"./context\";\nimport { download, fileName, serializeSvg, svgToPng, toCsv } from \"./export\";\nimport { useInView, useMounted, useWidth } from \"./hooks\";\nimport { cx } from \"./parts\";\nimport { ChartStyles } from \"./styles\";\n\nexport type ExportFormat = \"png\" | \"svg\" | \"csv\";\n\n/** The props every web chart takes, on top of its data. */\nexport interface ChartFrameProps extends ChartsSettings {\n /** What the chart shows, in a few words: its accessible name, and its heading unless `hideTitle`. */\n title: string;\n /** A sentence under the title: the period, the unit, the source. */\n description?: ReactNode;\n /** Visually hide the title (it stays the chart's accessible name): a card that already has its heading. */\n hideTitle?: boolean;\n /** The height of the plot in pixels. Default 280 (a sparkline: 32). */\n height?: number;\n /** The data is on its way: a skeleton of the chart, announced once. */\n loading?: boolean;\n /** The data could not be loaded: `true` says the default words, a node says yours (a retry button…). */\n error?: boolean | ReactNode;\n /** What to show when there is nothing to draw (default: the `empty` label). An `EmptyState` of @krizaka/ui fits. */\n empty?: ReactNode;\n /** The written summary: read by screen readers only (`\"hidden\"`, default), or shown under the chart (`\"visible\"`). */\n summary?: \"hidden\" | \"visible\";\n /** Your own summary instead of the generated one (still read first by screen readers). */\n summaryText?: string;\n /** The \"Show data\" button (the table is in the page for screen readers either way). Default true. */\n dataTable?: boolean;\n /** Export buttons: `true` for PNG, SVG and CSV, or the formats you want. */\n exportable?: boolean | readonly ExportFormat[];\n /** Streaming data (a job queue, an auction): no entrance on updates, no announcement of every new point. */\n live?: boolean;\n /** The id of the figure. */\n id?: string;\n /** A class on the figure (layout: margins, grid placement). */\n className?: string;\n /** Inline style on the figure (layout only: colours come from the tokens). */\n style?: CSSProperties;\n}\n\nexport interface TableModel {\n /** The header of each column; the first one names the categories. */\n readonly columns: readonly string[];\n /** The rows, already written (formatted values). The first cell is the row's header. */\n readonly rows: readonly (readonly string[])[];\n}\n\nexport interface PlotHandlers {\n onKeyDown?: (event: KeyboardEvent<HTMLDivElement>) => void;\n onPointerMove?: (event: PointerEvent<HTMLDivElement>) => void;\n onPointerLeave?: () => void;\n onBlur?: () => void;\n}\n\ninterface FrameInternals {\n readonly kind: ChartKind;\n readonly frame: ChartFrameProps;\n readonly defaultHeight?: number;\n readonly table: TableModel;\n readonly summary: string;\n readonly isEmpty: boolean;\n /** Draws the plot at the measured width (browser only). */\n readonly children: (width: number, height: number) => ReactNode;\n readonly legend?: ReactNode;\n readonly tooltip?: ReactNode;\n readonly announcement?: string;\n readonly handlers?: PlotHandlers;\n /** Whether the plot has points to walk with the keyboard. */\n readonly navigable?: boolean;\n /** A canvas engine exports its own image; the SVG charts are serialised. */\n readonly exportImage?: (format: \"png\" | \"svg\") => Promise<string | null> | string | null;\n /** Draw on the server too (a sparkline of a known width). */\n readonly ssrWidth?: number;\n /** Whether something is drawn on top of the plot (an active point): dims the other marks. */\n readonly active?: boolean;\n readonly plotRef?: RefObject<HTMLDivElement | null>;\n readonly compact?: boolean;\n /** Something the chart needs is still loading (the world's countries): `data-pending` until it is there. */\n readonly pending?: boolean;\n}\n\n/** The settings of a chart (`useChartSettings` re-exported for the charts). */\nexport function useSettings(frame: ChartFrameProps): { locale: Locale; brand?: BrandId; labels: ChartLabels } {\n return useChartSettings(frame);\n}\n\nexport function ChartFrame({\n kind,\n frame,\n defaultHeight = 280,\n table,\n summary,\n isEmpty,\n children,\n legend,\n tooltip,\n announcement,\n handlers,\n navigable = true,\n exportImage,\n ssrWidth,\n active,\n plotRef,\n compact,\n pending,\n}: FrameInternals) {\n const { title, description, hideTitle, height = defaultHeight, loading, error, empty, summaryText, dataTable = true, exportable, live, id, className, style } = frame;\n const { brand, labels } = useChartSettings(frame);\n const uid = useId();\n const ids = { title: `${uid}-t`, summary: `${uid}-s`, hint: `${uid}-h`, table: `${uid}-d` };\n const ownRef = useRef<HTMLDivElement | null>(null);\n const ref = plotRef ?? ownRef;\n const figureRef = useRef<HTMLElement | null>(null);\n const mounted = useMounted();\n const measured = useWidth(ref);\n const width = mounted ? measured : 0;\n const inView = useInView(figureRef, { once: true });\n const [showTable, setShowTable] = useState(false);\n\n const formats = useMemo<readonly ExportFormat[]>(() => (exportable === true ? [\"png\", \"svg\", \"csv\"] : exportable || []), [exportable]);\n const state = error ? \"error\" : loading ? \"loading\" : isEmpty ? \"empty\" : \"ready\";\n\n const doExport = useCallback(\n async (format: ExportFormat) => {\n if (format === \"csv\") {\n download(fileName(title, \"csv\"), new Blob([`\\uFEFF${toCsv([table.columns, ...table.rows])}`], { type: \"text/csv;charset=utf-8\" }));\n return;\n }\n if (exportImage) {\n const url = await exportImage(format);\n if (url) download(fileName(title, format), url);\n return;\n }\n const svg = ref.current?.querySelector<SVGSVGElement>(\"svg.kzc-svg\");\n if (!svg) return;\n const background = getComputedStyle(figureRef.current ?? document.body).getPropertyValue(\"--kz-surface-1\").trim() || \"#fff\";\n const surface = resolveColor(background, figureRef.current);\n const markup = serializeSvg(svg, surface);\n if (format === \"svg\") download(fileName(title, \"svg\"), new Blob([markup], { type: \"image/svg+xml\" }));\n else {\n const box = svg.getBoundingClientRect();\n download(fileName(title, \"png\"), await svgToPng(markup, box.width, box.height, surface));\n }\n },\n [exportImage, ref, table, title],\n );\n\n const drawWidth = width || ssrWidth || 0;\n let plot: ReactNode;\n if (state === \"loading\") {\n plot = (\n <div className=\"kzc-skeleton\" role=\"status\">\n <span className=\"kzc-sr\">{labels.loading}</span>\n {!compact && [0.55, 0.8, 0.45, 0.95, 0.7, 0.6].map((h, i) => <i key={i} aria-hidden style={{ height: `${h * 100}%` }} />)}\n </div>\n );\n } else if (state === \"error\") {\n plot = (\n <div className=\"kzc-state\" data-state=\"error\" role=\"alert\">\n {error === true ? labels.error : error}\n </div>\n );\n } else if (state === \"empty\") {\n plot = empty ?? (\n <div className=\"kzc-state\" data-state=\"empty\" role=\"status\">\n {labels.empty}\n </div>\n );\n } else if (drawWidth > 0) {\n plot = children(drawWidth, height);\n } else {\n // SSR and the first browser pass: a placeholder of the exact height (no layout shift), the data already in the table.\n plot = !compact && (\n <div className=\"kzc-skeleton\" aria-hidden>\n {[0.55, 0.8, 0.45, 0.95, 0.7, 0.6].map((h, i) => (\n <i key={i} style={{ height: `${h * 100}%` }} />\n ))}\n </div>\n );\n }\n\n const ready = state === \"ready\" && drawWidth > 0;\n return (\n <figure\n ref={figureRef}\n id={id}\n className={cx(\"kzc\", className)}\n style={style}\n data-kzc=\"\"\n data-kind={kind}\n data-state={state}\n data-brand={brand}\n data-inview={inView || undefined}\n data-live={live || undefined}\n data-active={active || undefined}\n data-pending={pending || (state === \"ready\" && drawWidth === 0) || undefined}\n aria-labelledby={ids.title}\n >\n <ChartStyles />\n {(\n <div className={!hideTitle || description || (!compact && (dataTable || formats.length > 0)) ? \"kzc-head\" : \"kzc-sr\"}>\n <div className=\"kzc-heading\">\n <p id={ids.title} className={hideTitle ? \"kzc-sr\" : \"kzc-title\"}>\n {title}\n </p>\n {description && <p className=\"kzc-desc\">{description}</p>}\n </div>\n {(dataTable || formats.length > 0) && !compact && state !== \"loading\" && state !== \"error\" && (\n <div className=\"kzc-tools\">\n {dataTable && !isEmpty && (\n <button type=\"button\" className=\"kzc-btn\" aria-expanded={showTable} aria-controls={ids.table} onClick={() => setShowTable((v) => !v)}>\n {showTable ? labels.hideData : labels.showData}\n </button>\n )}\n {!isEmpty &&\n formats.map((format) => (\n <button key={format} type=\"button\" className=\"kzc-btn\" onClick={() => void doExport(format)} aria-label={`${title} — ${format.toUpperCase()}`}>\n {format === \"png\" ? labels.exportPng : format === \"svg\" ? labels.exportSvg : labels.exportCsv}\n </button>\n ))}\n </div>\n )}\n </div>\n )}\n {legend}\n <div\n ref={ref}\n className=\"kzc-plot\"\n style={{ height }}\n {...(ready\n ? {\n role: \"group\",\n \"aria-roledescription\": labels.roleDescription,\n \"aria-labelledby\": ids.title,\n \"aria-describedby\": navigable ? `${ids.summary} ${ids.hint}` : ids.summary,\n tabIndex: navigable ? 0 : undefined,\n ...handlers,\n }\n : {})}\n >\n {plot}\n {ready && tooltip}\n </div>\n <p id={ids.summary} className={frame.summary === \"visible\" ? \"kzc-summary\" : \"kzc-sr\"}>\n {summaryText ?? summary}\n </p>\n {ready && navigable && (\n <span id={ids.hint} className=\"kzc-sr\">\n {labels.keyboardHint}\n </span>\n )}\n <div className=\"kzc-live\" aria-live=\"polite\" aria-atomic=\"true\">\n {live ? \"\" : announcement}\n </div>\n {!isEmpty && (\n <div id={ids.table} className={showTable ? \"kzc-table-wrap\" : \"kzc-sr\"}>\n <table className=\"kzc-table\">\n <caption>{fill(labels.tableCaption, { title })}</caption>\n <thead>\n <tr>\n {table.columns.map((column, i) => (\n <th key={i} scope=\"col\">\n {column}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {table.rows.map((row, r) => (\n <tr key={r}>\n {row.map((cell, c) =>\n c === 0 ? (\n <th key={c} scope=\"row\">\n {cell}\n </th>\n ) : (\n <td key={c}>{cell}</td>\n ),\n )}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </figure>\n );\n}\n\n/** A CSS colour (a `var(--…)`, `light-dark()`, `hsl()`…) as the browser resolves it on `scope`: `rgb(r, g, b)`. */\nexport function resolveColor(color: string, scope: Element | null): string {\n if (typeof document === \"undefined\") return color;\n const probe = document.createElement(\"span\");\n probe.style.color = color;\n probe.style.display = \"none\";\n (scope ?? document.body).appendChild(probe);\n const resolved = getComputedStyle(probe).color;\n probe.remove();\n return resolved || color;\n}\n","// Export a chart as a file: CSV (the data table, as the reader sees it), SVG (the drawing, its colours resolved — a\n// file has no --kz-* variables) and PNG (the SVG rasterised at twice the size).\n\n/** One CSV line per row; cells quoted when they hold a separator, a quote or a line break. */\nexport function toCsv(rows: readonly (readonly string[])[]): string {\n const cell = (value: string) => (/[\",\\n;]/.test(value) ? `\"${value.replace(/\"/g, '\"\"')}\"` : value);\n return rows.map((row) => row.map(cell).join(\",\")).join(\"\\n\");\n}\n\n/** A file name from a title: `Earnings, last 6 months` → `earnings-last-6-months`. */\nexport function fileName(title: string | undefined, extension: string): string {\n const base =\n (title ?? \"chart\")\n .normalize(\"NFKD\")\n .replace(/[̀-ͯ]/g, \"\")\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\") || \"chart\";\n return `${base}.${extension}`;\n}\n\nexport function download(name: string, content: Blob | string): void {\n const url = typeof content === \"string\" ? content : URL.createObjectURL(content);\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = name;\n link.click();\n if (typeof content !== \"string\") setTimeout(() => URL.revokeObjectURL(url), 1000);\n}\n\nconst PROPS = [\"fill\", \"stroke\", \"stroke-width\", \"stroke-dasharray\", \"opacity\", \"fill-opacity\", \"font-size\", \"font-weight\", \"font-family\"] as const;\n\n/** A standalone copy of a drawn SVG: every colour and text style the CSS gave it, written on the element. */\nexport function serializeSvg(svg: SVGSVGElement, background?: string): string {\n const clone = svg.cloneNode(true) as SVGSVGElement;\n const source = [svg, ...svg.querySelectorAll(\"*\")];\n const target = [clone, ...clone.querySelectorAll(\"*\")];\n source.forEach((el, i) => {\n const style = getComputedStyle(el);\n const out = target[i] as SVGElement;\n for (const prop of PROPS) {\n const value = style.getPropertyValue(prop);\n if (value) out.style.setProperty(prop, value);\n }\n // The entrance animation's dash would export a line half drawn.\n out.style.removeProperty(\"stroke-dasharray\");\n if (el.getAttribute(\"stroke-dasharray\")) out.style.setProperty(\"stroke-dasharray\", el.getAttribute(\"stroke-dasharray\")!);\n });\n clone.setAttribute(\"xmlns\", \"http://www.w3.org/2000/svg\");\n if (background) clone.style.setProperty(\"background\", background);\n return new XMLSerializer().serializeToString(clone);\n}\n\n/** The SVG drawn on a canvas at `scale` × its size, as a PNG data URL. */\nexport function svgToPng(markup: string, width: number, height: number, background: string, scale = 2): Promise<string> {\n return new Promise((resolve, reject) => {\n const image = new Image();\n image.onload = () => {\n const canvas = document.createElement(\"canvas\");\n canvas.width = width * scale;\n canvas.height = height * scale;\n const context = canvas.getContext(\"2d\");\n if (!context) return reject(new Error(\"No 2D canvas\"));\n context.fillStyle = background;\n context.fillRect(0, 0, canvas.width, canvas.height);\n context.drawImage(image, 0, 0, canvas.width, canvas.height);\n resolve(canvas.toDataURL(\"image/png\"));\n return undefined;\n };\n image.onerror = () => reject(new Error(\"The SVG could not be rasterised\"));\n image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(markup)}`;\n });\n}\n"],"mappings":"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","names":["jsx","useState","jsx","jsxs","useState"]}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
LineChart
|
|
4
|
+
} from "./chunk-C4MFZLJM.js";
|
|
5
|
+
import {
|
|
6
|
+
Kpi,
|
|
7
|
+
toneOf
|
|
8
|
+
} from "./chunk-WBHFVBSK.js";
|
|
9
|
+
import {
|
|
10
|
+
TICKER_LABELS,
|
|
11
|
+
direction,
|
|
12
|
+
fillIn,
|
|
13
|
+
signedPercent
|
|
14
|
+
} from "./chunk-WNUFGMS7.js";
|
|
15
|
+
import {
|
|
16
|
+
valueFormatter
|
|
17
|
+
} from "./chunk-XLLYNNC6.js";
|
|
18
|
+
import {
|
|
19
|
+
useSettings
|
|
20
|
+
} from "./chunk-JTKR7ZWP.js";
|
|
21
|
+
|
|
22
|
+
// src/needs/ticker.ts
|
|
23
|
+
function tickerModel(bids, opening, openedAt) {
|
|
24
|
+
const sorted = [...bids].sort((a, b) => new Date(a.at).getTime() - new Date(b.at).getTime());
|
|
25
|
+
const points = [...openedAt ? [{ at: openedAt, amount: opening }] : [], ...sorted];
|
|
26
|
+
const current = sorted.length > 0 ? sorted[sorted.length - 1].amount : opening;
|
|
27
|
+
return { points, current, change: current - opening, ratio: opening > 0 ? current / opening - 1 : null, count: sorted.length };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// src/ticker/price-ticker.tsx
|
|
31
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
32
|
+
function PriceTicker({ bids, opening, openedAt, currency, reserve, timeFormat = "time", labels: given, description, live = true, ...props }) {
|
|
33
|
+
const words = { ...TICKER_LABELS, ...given };
|
|
34
|
+
const { locale } = useSettings(props);
|
|
35
|
+
const format = { style: "money", currency };
|
|
36
|
+
const write = valueFormatter(format, locale);
|
|
37
|
+
const model = tickerModel(bids, opening, openedAt);
|
|
38
|
+
const way = model.count > 0 ? direction(model.ratio) : null;
|
|
39
|
+
const signed = `${model.change > 0 ? "+" : ""}${write(model.change)}`;
|
|
40
|
+
const thresholds = reserve != null && model.current < reserve ? [{ value: reserve, label: words.reserve, tone: "warning" }] : [];
|
|
41
|
+
const kpi = /* @__PURE__ */ jsx(
|
|
42
|
+
Kpi,
|
|
43
|
+
{
|
|
44
|
+
value: write(model.current),
|
|
45
|
+
label: model.count > 0 ? words.current : words.opening,
|
|
46
|
+
delta: model.count > 0 ? fillIn(words.change, { change: model.ratio == null ? signed : `${signed} (${signedPercent(model.ratio, locale)})`, count: model.count }) : null,
|
|
47
|
+
direction: way,
|
|
48
|
+
tone: toneOf(way, "up")
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
return /* @__PURE__ */ jsx(
|
|
52
|
+
LineChart,
|
|
53
|
+
{
|
|
54
|
+
...props,
|
|
55
|
+
live,
|
|
56
|
+
data: model.points,
|
|
57
|
+
x: "at",
|
|
58
|
+
y: "amount",
|
|
59
|
+
yLabel: words.price,
|
|
60
|
+
xFormat: timeFormat,
|
|
61
|
+
format,
|
|
62
|
+
curve: "step",
|
|
63
|
+
dots: true,
|
|
64
|
+
zero: false,
|
|
65
|
+
thresholds,
|
|
66
|
+
description: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
67
|
+
description,
|
|
68
|
+
description ? /* @__PURE__ */ jsx("br", {}) : null,
|
|
69
|
+
kpi
|
|
70
|
+
] })
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export {
|
|
76
|
+
PriceTicker
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=chunk-KNDHO67X.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/needs/ticker.ts","../src/ticker/price-ticker.tsx"],"sourcesContent":["// The price of a live auction, shared by the web and React Native: the points to draw (the opening, then each bid)\n// and what the headline says (the current price, the change since the opening, the number of bids).\n\n/** A bid: when (an ISO date-time or a `Date`) and how much, in money minor units. */\nexport interface Bid {\n readonly at: string | Date;\n readonly amount: number;\n}\n\nexport interface TickerModel {\n readonly points: readonly { readonly at: string | Date; readonly amount: number }[];\n readonly current: number;\n readonly change: number;\n readonly ratio: number | null;\n readonly count: number;\n}\n\n/** The opening point (when `openedAt` is known), then the bids in time order. */\nexport function tickerModel(bids: readonly Bid[], opening: number, openedAt?: string | Date): TickerModel {\n const sorted = [...bids].sort((a, b) => new Date(a.at).getTime() - new Date(b.at).getTime());\n const points = [...(openedAt ? [{ at: openedAt, amount: opening }] : []), ...sorted];\n const current = sorted.length > 0 ? sorted[sorted.length - 1].amount : opening;\n return { points, current, change: current - opening, ratio: opening > 0 ? current / opening - 1 : null, count: sorted.length };\n}\n","\"use client\";\n// The price of a live auction (Orochia's auction room, web and mobile): the current bid in large, what it gained since\n// the opening and how many bids, the price as steps over time (a bid holds until the next), the reserve as a line.\n// `live` by default: new bids redraw without an entrance and without announcing every point.\nimport { valueFormatter } from \"../core/format\";\nimport type { CategoryFormat, Threshold } from \"../core/types\";\nimport { LineChart, type LineChartProps } from \"../line/line-chart\";\nimport { Kpi, toneOf } from \"../needs/kpi\";\nimport { TICKER_LABELS, type TickerLabels } from \"../needs/labels\";\nimport { direction, fillIn, signedPercent } from \"../needs/model\";\nimport { type Bid, tickerModel } from \"../needs/ticker\";\nimport { useSettings } from \"../web/frame\";\n\ntype Point = { at: string | Date; amount: number };\n\nexport interface PriceTickerProps\n extends Omit<LineChartProps<Point>, \"data\" | \"x\" | \"y\" | \"yLabel\" | \"series\" | \"format\" | \"xFormat\" | \"thresholds\" | \"curve\" | \"labels\"> {\n /** The bids, in any order: when and how much (minor units). */\n bids: readonly Bid[];\n /** The opening price, in minor units. */\n opening: number;\n /** When the auction opened: the first point of the line. */\n openedAt?: string | Date;\n /** The ISO 4217 currency. */\n currency: string;\n /** The reserve price, drawn as a line until it is met. */\n reserve?: number;\n /** How the times are written. Default `\"time\"` (`14:30`); `\"datetime\"` for auctions over several days. */\n timeFormat?: CategoryFormat;\n /** The words, passed translated; English by default. */\n labels?: Partial<TickerLabels>;\n}\n\n/** A live auction's price: the current bid, the change since the opening, the steps of the bids, the reserve. */\nexport function PriceTicker({ bids, opening, openedAt, currency, reserve, timeFormat = \"time\", labels: given, description, live = true, ...props }: PriceTickerProps) {\n const words = { ...TICKER_LABELS, ...given };\n const { locale } = useSettings(props);\n const format = { style: \"money\", currency } as const;\n const write = valueFormatter(format, locale);\n const model = tickerModel(bids, opening, openedAt);\n const way = model.count > 0 ? direction(model.ratio) : null;\n const signed = `${model.change > 0 ? \"+\" : \"\"}${write(model.change)}`;\n const thresholds: Threshold[] = reserve != null && model.current < reserve ? [{ value: reserve, label: words.reserve, tone: \"warning\" }] : [];\n const kpi = (\n <Kpi\n value={write(model.current)}\n label={model.count > 0 ? words.current : words.opening}\n delta={\n model.count > 0\n ? fillIn(words.change, { change: model.ratio == null ? signed : `${signed} (${signedPercent(model.ratio, locale)})`, count: model.count })\n : null\n }\n direction={way}\n tone={toneOf(way, \"up\")}\n />\n );\n return (\n <LineChart\n {...props}\n live={live}\n data={model.points as Point[]}\n x=\"at\"\n y=\"amount\"\n yLabel={words.price}\n xFormat={timeFormat}\n format={format}\n curve=\"step\"\n dots\n zero={false}\n thresholds={thresholds}\n description={\n <>\n {description}\n {description ? <br /> : null}\n {kpi}\n </>\n }\n />\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAkBO,SAAS,YAAY,MAAsB,SAAiB,UAAuC;AACxG,QAAM,SAAS,CAAC,GAAG,IAAI,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,KAAK,EAAE,EAAE,EAAE,QAAQ,IAAI,IAAI,KAAK,EAAE,EAAE,EAAE,QAAQ,CAAC;AAC3F,QAAM,SAAS,CAAC,GAAI,WAAW,CAAC,EAAE,IAAI,UAAU,QAAQ,QAAQ,CAAC,IAAI,CAAC,GAAI,GAAG,MAAM;AACnF,QAAM,UAAU,OAAO,SAAS,IAAI,OAAO,OAAO,SAAS,CAAC,EAAE,SAAS;AACvE,SAAO,EAAE,QAAQ,SAAS,QAAQ,UAAU,SAAS,OAAO,UAAU,IAAI,UAAU,UAAU,IAAI,MAAM,OAAO,OAAO,OAAO;AAC/H;;;ACqBI,SA2BI,UA3BJ,KA2BI,YA3BJ;AAVG,SAAS,YAAY,EAAE,MAAM,SAAS,UAAU,UAAU,SAAS,aAAa,QAAQ,QAAQ,OAAO,aAAa,OAAO,MAAM,GAAG,MAAM,GAAqB;AACpK,QAAM,QAAQ,EAAE,GAAG,eAAe,GAAG,MAAM;AAC3C,QAAM,EAAE,OAAO,IAAI,YAAY,KAAK;AACpC,QAAM,SAAS,EAAE,OAAO,SAAS,SAAS;AAC1C,QAAM,QAAQ,eAAe,QAAQ,MAAM;AAC3C,QAAM,QAAQ,YAAY,MAAM,SAAS,QAAQ;AACjD,QAAM,MAAM,MAAM,QAAQ,IAAI,UAAU,MAAM,KAAK,IAAI;AACvD,QAAM,SAAS,GAAG,MAAM,SAAS,IAAI,MAAM,EAAE,GAAG,MAAM,MAAM,MAAM,CAAC;AACnE,QAAM,aAA0B,WAAW,QAAQ,MAAM,UAAU,UAAU,CAAC,EAAE,OAAO,SAAS,OAAO,MAAM,SAAS,MAAM,UAAU,CAAC,IAAI,CAAC;AAC5I,QAAM,MACJ;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,MAAM,MAAM,OAAO;AAAA,MAC1B,OAAO,MAAM,QAAQ,IAAI,MAAM,UAAU,MAAM;AAAA,MAC/C,OACE,MAAM,QAAQ,IACV,OAAO,MAAM,QAAQ,EAAE,QAAQ,MAAM,SAAS,OAAO,SAAS,GAAG,MAAM,KAAK,cAAc,MAAM,OAAO,MAAM,CAAC,KAAK,OAAO,MAAM,MAAM,CAAC,IACvI;AAAA,MAEN,WAAW;AAAA,MACX,MAAM,OAAO,KAAK,IAAI;AAAA;AAAA,EACxB;AAEF,SACE;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ;AAAA,MACA,MAAM,MAAM;AAAA,MACZ,GAAE;AAAA,MACF,GAAE;AAAA,MACF,QAAQ,MAAM;AAAA,MACd,SAAS;AAAA,MACT;AAAA,MACA,OAAM;AAAA,MACN,MAAI;AAAA,MACJ,MAAM;AAAA,MACN;AAAA,MACA,aACE,iCACG;AAAA;AAAA,QACA,cAAc,oBAAC,QAAG,IAAK;AAAA,QACvB;AAAA,SACH;AAAA;AAAA,EAEJ;AAEJ;","names":[]}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
Sparkline
|
|
4
|
+
} from "./chunk-ARARFMNE.js";
|
|
5
|
+
import {
|
|
6
|
+
Kpi
|
|
7
|
+
} from "./chunk-WBHFVBSK.js";
|
|
8
|
+
import {
|
|
9
|
+
CREDIT_LABELS,
|
|
10
|
+
addDays,
|
|
11
|
+
daysLeft,
|
|
12
|
+
fillIn,
|
|
13
|
+
quotaLevel
|
|
14
|
+
} from "./chunk-WNUFGMS7.js";
|
|
15
|
+
import {
|
|
16
|
+
categoryFormatter,
|
|
17
|
+
valueFormatter
|
|
18
|
+
} from "./chunk-XLLYNNC6.js";
|
|
19
|
+
import {
|
|
20
|
+
ChartStyles,
|
|
21
|
+
cx,
|
|
22
|
+
useSettings
|
|
23
|
+
} from "./chunk-JTKR7ZWP.js";
|
|
24
|
+
|
|
25
|
+
// src/credits/credit-usage.tsx
|
|
26
|
+
import { useId } from "react";
|
|
27
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
28
|
+
function CreditUsage({
|
|
29
|
+
title,
|
|
30
|
+
description,
|
|
31
|
+
hideTitle,
|
|
32
|
+
used,
|
|
33
|
+
limit,
|
|
34
|
+
format = "integer",
|
|
35
|
+
daily,
|
|
36
|
+
days,
|
|
37
|
+
renewsAt,
|
|
38
|
+
now,
|
|
39
|
+
levels = [0.8, 0.95],
|
|
40
|
+
loading,
|
|
41
|
+
labels: given,
|
|
42
|
+
className,
|
|
43
|
+
style,
|
|
44
|
+
...settings
|
|
45
|
+
}) {
|
|
46
|
+
const words = { ...CREDIT_LABELS, ...given };
|
|
47
|
+
const { locale, brand } = useSettings({ title, ...settings });
|
|
48
|
+
const uid = useId();
|
|
49
|
+
const write = valueFormatter(format, locale);
|
|
50
|
+
const date = categoryFormatter("date", locale);
|
|
51
|
+
const ratio = limit > 0 ? Math.min(1, Math.max(0, used / limit)) : 0;
|
|
52
|
+
const level = quotaLevel(ratio, levels[0], levels[1]);
|
|
53
|
+
const remaining = Math.max(0, limit - used);
|
|
54
|
+
const percent = valueFormatter("percent", locale)(ratio);
|
|
55
|
+
const usedText = fillIn(words.used, { used: write(used), percent });
|
|
56
|
+
const left = daily ? daysLeft(remaining, daily) : null;
|
|
57
|
+
const today = now ?? /* @__PURE__ */ new Date();
|
|
58
|
+
const runOut = left == null ? null : addDays(today, left);
|
|
59
|
+
const renews = renewsAt ? new Date(renewsAt) : null;
|
|
60
|
+
const projection = daily ? runOut && (!renews || runOut < renews) ? fillIn(words.runsOut, { date: date(runOut) }) : left == null ? words.steady : null : null;
|
|
61
|
+
return /* @__PURE__ */ jsxs("figure", { className: cx("kzc", className), style, "data-kzc": "", "data-brand": brand, "data-kind": "credits", "aria-labelledby": `${uid}-t`, "aria-busy": loading || void 0, children: [
|
|
62
|
+
/* @__PURE__ */ jsx(ChartStyles, {}),
|
|
63
|
+
/* @__PURE__ */ jsxs("div", { className: "kzc-heading", children: [
|
|
64
|
+
/* @__PURE__ */ jsx("p", { id: `${uid}-t`, className: hideTitle ? "kzc-sr" : "kzc-title", children: title }),
|
|
65
|
+
description ? /* @__PURE__ */ jsx("p", { className: "kzc-desc", children: description }) : null
|
|
66
|
+
] }),
|
|
67
|
+
/* @__PURE__ */ jsx("p", { className: "kzc-desc", children: /* @__PURE__ */ jsx(Kpi, { value: loading ? "\u2014" : write(remaining), label: fillIn(words.remaining, { limit: write(limit) }) }) }),
|
|
68
|
+
/* @__PURE__ */ jsx(
|
|
69
|
+
"div",
|
|
70
|
+
{
|
|
71
|
+
role: "meter",
|
|
72
|
+
"aria-labelledby": `${uid}-t`,
|
|
73
|
+
"aria-valuemin": 0,
|
|
74
|
+
"aria-valuemax": limit,
|
|
75
|
+
"aria-valuenow": Math.min(limit, Math.max(0, used)),
|
|
76
|
+
"aria-valuetext": usedText,
|
|
77
|
+
className: "kzc-meter",
|
|
78
|
+
"data-tone": level === "ok" ? void 0 : level,
|
|
79
|
+
children: /* @__PURE__ */ jsx("i", { style: { width: loading ? 0 : `${ratio * 100}%` } })
|
|
80
|
+
}
|
|
81
|
+
),
|
|
82
|
+
/* @__PURE__ */ jsxs("p", { className: "kzc-row", children: [
|
|
83
|
+
/* @__PURE__ */ jsx("span", { children: usedText }),
|
|
84
|
+
renews ? /* @__PURE__ */ jsx("span", { children: fillIn(words.renews, { date: date(renews) }) }) : null
|
|
85
|
+
] }),
|
|
86
|
+
daily && daily.length > 1 ? /* @__PURE__ */ jsx(Sparkline, { title: words.daily, data: daily, categories: days, variant: "bar", format, height: 36, loading }) : null,
|
|
87
|
+
projection && !loading ? /* @__PURE__ */ jsx("p", { className: "kzc-row", children: /* @__PURE__ */ jsx("b", { children: projection }) }) : null
|
|
88
|
+
] });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export {
|
|
92
|
+
CreditUsage
|
|
93
|
+
};
|
|
94
|
+
//# sourceMappingURL=chunk-L2EKYWVI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/credits/credit-usage.tsx"],"sourcesContent":["\"use client\";\n// What is left of a credit allowance (Orazaka's billing meter, the console's quotas, Orochia's storage plan): the\n// remaining amount in large, a meter that turns to warning then danger, the use per day as a sparkline, and the day it\n// runs out at the current pace. A `meter` for assistive technology, its value said in words.\nimport { type CSSProperties, type ReactNode, useId } from \"react\";\n\nimport { categoryFormatter, valueFormatter } from \"../core/format\";\nimport type { ValueFormat } from \"../core/types\";\nimport { Kpi } from \"../needs/kpi\";\nimport { CREDIT_LABELS, type CreditLabels } from \"../needs/labels\";\nimport { addDays, daysLeft, fillIn, quotaLevel } from \"../needs/model\";\nimport { Sparkline } from \"../sparkline/sparkline\";\nimport type { ChartsSettings } from \"../web/context\";\nimport { useSettings } from \"../web/frame\";\nimport { cx } from \"../web/parts\";\nimport { ChartStyles } from \"../web/styles\";\n\nexport interface CreditUsageProps extends Omit<ChartsSettings, \"labels\"> {\n /** What the allowance is (“Credits this month”): the heading and the meter's name. */\n title: string;\n /** A line under the title: the plan, the period. */\n description?: ReactNode;\n /** Visually hide the title (it stays the accessible name). */\n hideTitle?: boolean;\n /** How much is used. */\n used: number;\n /** The allowance. */\n limit: number;\n /** How the amounts are written. Default `\"integer\"`; a `{ style: \"unit\", unit: \"gigabyte\" }` for storage. */\n format?: ValueFormat;\n /** The use of each recent day, oldest first: the sparkline and the projection. */\n daily?: readonly (number | null)[];\n /** The days of `daily` (ISO dates), for the sparkline's summary. */\n days?: readonly unknown[];\n /** When the allowance renews (an ISO date or a `Date`). */\n renewsAt?: string | Date;\n /** Today, for the projection (pass it from the server to render the same date on both sides). Default: now. */\n now?: Date;\n /** The share where the meter turns to warning, then danger. Default `[0.8, 0.95]`. */\n levels?: readonly [number, number];\n /** The data is on its way. */\n loading?: boolean;\n /** The words, passed translated; English by default. */\n labels?: Partial<CreditLabels>;\n /** A class on the figure (layout). */\n className?: string;\n /** Inline style on the figure (layout only). */\n style?: CSSProperties;\n}\n\n/** What is left of an allowance: the remaining amount, a meter in tones, the use per day and the run-out date. */\nexport function CreditUsage({\n title,\n description,\n hideTitle,\n used,\n limit,\n format = \"integer\",\n daily,\n days,\n renewsAt,\n now,\n levels = [0.8, 0.95],\n loading,\n labels: given,\n className,\n style,\n ...settings\n}: CreditUsageProps) {\n const words = { ...CREDIT_LABELS, ...given };\n const { locale, brand } = useSettings({ title, ...settings });\n const uid = useId();\n const write = valueFormatter(format, locale);\n const date = categoryFormatter(\"date\", locale);\n const ratio = limit > 0 ? Math.min(1, Math.max(0, used / limit)) : 0;\n const level = quotaLevel(ratio, levels[0], levels[1]);\n const remaining = Math.max(0, limit - used);\n const percent = valueFormatter(\"percent\", locale)(ratio);\n const usedText = fillIn(words.used, { used: write(used), percent });\n const left = daily ? daysLeft(remaining, daily) : null;\n const today = now ?? new Date();\n const runOut = left == null ? null : addDays(today, left);\n const renews = renewsAt ? new Date(renewsAt) : null;\n // The run-out date only matters when it comes before the renewal.\n const projection = daily ? (runOut && (!renews || runOut < renews) ? fillIn(words.runsOut, { date: date(runOut) }) : left == null ? words.steady : null) : null;\n return (\n <figure className={cx(\"kzc\", className)} style={style} data-kzc=\"\" data-brand={brand} data-kind=\"credits\" aria-labelledby={`${uid}-t`} aria-busy={loading || undefined}>\n <ChartStyles />\n <div className=\"kzc-heading\">\n <p id={`${uid}-t`} className={hideTitle ? \"kzc-sr\" : \"kzc-title\"}>\n {title}\n </p>\n {description ? <p className=\"kzc-desc\">{description}</p> : null}\n </div>\n <p className=\"kzc-desc\">\n <Kpi value={loading ? \"—\" : write(remaining)} label={fillIn(words.remaining, { limit: write(limit) })} />\n </p>\n <div\n role=\"meter\"\n aria-labelledby={`${uid}-t`}\n aria-valuemin={0}\n aria-valuemax={limit}\n aria-valuenow={Math.min(limit, Math.max(0, used))}\n aria-valuetext={usedText}\n className=\"kzc-meter\"\n data-tone={level === \"ok\" ? undefined : level}\n >\n <i style={{ width: loading ? 0 : `${ratio * 100}%` }} />\n </div>\n <p className=\"kzc-row\">\n <span>{usedText}</span>\n {renews ? <span>{fillIn(words.renews, { date: date(renews) })}</span> : null}\n </p>\n {daily && daily.length > 1 ? (\n <Sparkline title={words.daily} data={daily} categories={days} variant=\"bar\" format={format} height={36} loading={loading} />\n ) : null}\n {projection && !loading ? (\n <p className=\"kzc-row\">\n <b>{projection}</b>\n </p>\n ) : null}\n </figure>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
EChartHost,
|
|
4
4
|
baseOption,
|
|
5
5
|
tooltipHtml
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-PWMMXRMC.js";
|
|
7
7
|
import {
|
|
8
8
|
read
|
|
9
9
|
} from "./chunk-W5FX4TU3.js";
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from "./chunk-XLLYNNC6.js";
|
|
14
14
|
import {
|
|
15
15
|
useSettings
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-JTKR7ZWP.js";
|
|
17
17
|
import {
|
|
18
18
|
capitalize,
|
|
19
19
|
fill
|
|
@@ -89,4 +89,4 @@ function Sankey(props) {
|
|
|
89
89
|
export {
|
|
90
90
|
Sankey
|
|
91
91
|
};
|
|
92
|
-
//# sourceMappingURL=chunk-
|
|
92
|
+
//# sourceMappingURL=chunk-MXLITIGU.js.map
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
categoryAxis,
|
|
6
6
|
tooltipHtml,
|
|
7
7
|
valueAxis
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-PWMMXRMC.js";
|
|
9
9
|
import {
|
|
10
10
|
read
|
|
11
11
|
} from "./chunk-W5FX4TU3.js";
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from "./chunk-XLLYNNC6.js";
|
|
17
17
|
import {
|
|
18
18
|
useSettings
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-JTKR7ZWP.js";
|
|
20
20
|
import {
|
|
21
21
|
capitalize,
|
|
22
22
|
fill
|
|
@@ -152,4 +152,4 @@ function Candlestick(props) {
|
|
|
152
152
|
export {
|
|
153
153
|
Candlestick
|
|
154
154
|
};
|
|
155
|
-
//# sourceMappingURL=chunk-
|
|
155
|
+
//# sourceMappingURL=chunk-NCIQMRV3.js.map
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
BarChart
|
|
4
|
+
} from "./chunk-WVTIKOII.js";
|
|
5
|
+
import {
|
|
6
|
+
Kpi,
|
|
7
|
+
toneOf
|
|
8
|
+
} from "./chunk-WBHFVBSK.js";
|
|
9
|
+
import {
|
|
10
|
+
EARNINGS_LABELS,
|
|
11
|
+
direction,
|
|
12
|
+
fillIn,
|
|
13
|
+
lastChange,
|
|
14
|
+
numberOf,
|
|
15
|
+
signedPercent,
|
|
16
|
+
total
|
|
17
|
+
} from "./chunk-WNUFGMS7.js";
|
|
18
|
+
import {
|
|
19
|
+
valueFormatter
|
|
20
|
+
} from "./chunk-XLLYNNC6.js";
|
|
21
|
+
import {
|
|
22
|
+
useSettings
|
|
23
|
+
} from "./chunk-JTKR7ZWP.js";
|
|
24
|
+
|
|
25
|
+
// src/earnings/earnings-chart.tsx
|
|
26
|
+
import { useMemo } from "react";
|
|
27
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
28
|
+
function EarningsChart({
|
|
29
|
+
x = "month",
|
|
30
|
+
net,
|
|
31
|
+
gross,
|
|
32
|
+
currency,
|
|
33
|
+
period = "month",
|
|
34
|
+
payoutThreshold,
|
|
35
|
+
hideSummary,
|
|
36
|
+
labels: given,
|
|
37
|
+
description,
|
|
38
|
+
data,
|
|
39
|
+
...props
|
|
40
|
+
}) {
|
|
41
|
+
const words = { ...EARNINGS_LABELS, ...given };
|
|
42
|
+
const { locale } = useSettings(props);
|
|
43
|
+
const format = { style: "money", currency };
|
|
44
|
+
const series = useMemo(
|
|
45
|
+
() => gross ? [{ key: gross, label: words.gross, color: 2 }, { key: net, label: words.net, color: 1 }] : [{ key: net, label: words.net, color: 1 }],
|
|
46
|
+
[gross, net, words.gross, words.net]
|
|
47
|
+
);
|
|
48
|
+
const values = data.map((row, i) => numberOf(row, net, i));
|
|
49
|
+
const change = lastChange(values);
|
|
50
|
+
const way = direction(change);
|
|
51
|
+
const summary = hideSummary ? null : /* @__PURE__ */ jsx(
|
|
52
|
+
Kpi,
|
|
53
|
+
{
|
|
54
|
+
value: valueFormatter(format, locale)(total(data, net)),
|
|
55
|
+
label: fillIn(words.total, { count: data.length }),
|
|
56
|
+
delta: change == null ? null : fillIn(words.change, { change: signedPercent(change, locale) }),
|
|
57
|
+
direction: way,
|
|
58
|
+
tone: toneOf(way, "up")
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
return /* @__PURE__ */ jsx(
|
|
62
|
+
BarChart,
|
|
63
|
+
{
|
|
64
|
+
...props,
|
|
65
|
+
data,
|
|
66
|
+
x,
|
|
67
|
+
series,
|
|
68
|
+
format,
|
|
69
|
+
xFormat: period,
|
|
70
|
+
thresholds: payoutThreshold ? [{ tone: "success", ...payoutThreshold }] : void 0,
|
|
71
|
+
description: description || summary ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
72
|
+
description,
|
|
73
|
+
description && summary ? /* @__PURE__ */ jsx("br", {}) : null,
|
|
74
|
+
summary
|
|
75
|
+
] }) : void 0
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export {
|
|
81
|
+
EarningsChart
|
|
82
|
+
};
|
|
83
|
+
//# sourceMappingURL=chunk-OIRHLJBC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/earnings/earnings-chart.tsx"],"sourcesContent":["\"use client\";\n// A creator's earnings per period (Orochia /earnings, the console's treasury): bars of the net amount in money minor\n// units, the total and the last period's change written above, the payout threshold as a line. A BarChart preset:\n// the money format, the period format and the words are chosen for the need.\nimport { useMemo } from \"react\";\n\nimport { BarChart } from \"../bar/bar-chart\";\nimport type { CartesianProps } from \"../cartesian/cartesian\";\nimport { valueFormatter } from \"../core/format\";\nimport type { Accessor, CategoryFormat, Datum, SeriesSpec, Threshold } from \"../core/types\";\nimport { Kpi, toneOf } from \"../needs/kpi\";\nimport { EARNINGS_LABELS, type EarningsLabels } from \"../needs/labels\";\nimport { direction, fillIn, lastChange, numberOf, signedPercent, total } from \"../needs/model\";\nimport { useSettings } from \"../web/frame\";\n\nexport interface EarningsChartProps<T extends Datum>\n extends Omit<CartesianProps<T>, \"y\" | \"yLabel\" | \"series\" | \"format\" | \"x\" | \"xFormat\" | \"thresholds\" | \"labels\"> {\n /** The field (or function) of the period: an ISO month (`\"2026-04\"`), a day. Default `\"month\"`. */\n x?: Accessor<T, unknown>;\n /** The net amount of each period, in **minor units** (cents), as the ledger stores it. */\n net: Accessor<T, number | null | undefined>;\n /** The gross amount (before fees), drawn beside the net when given. */\n gross?: Accessor<T, number | null | undefined>;\n /** The ISO 4217 currency (`\"USD\"`, `\"EUR\"`). */\n currency: string;\n /** How the periods are written. Default `\"month\"` (`Apr 2026`). */\n period?: CategoryFormat;\n /** The minimum to withdraw, drawn as a line: `{ value: 5000, label: \"Payout threshold\" }`. */\n payoutThreshold?: Threshold;\n /** Hide the total and the change above the bars. */\n hideSummary?: boolean;\n /** The words, passed translated; English by default. */\n labels?: Partial<EarningsLabels>;\n}\n\n/** Earnings per period in money — the total, the change of the last period, the payout threshold. */\nexport function EarningsChart<T extends Datum>({\n x = \"month\" as Accessor<T, unknown>,\n net,\n gross,\n currency,\n period = \"month\",\n payoutThreshold,\n hideSummary,\n labels: given,\n description,\n data,\n ...props\n}: EarningsChartProps<T>) {\n const words = { ...EARNINGS_LABELS, ...given };\n const { locale } = useSettings(props as CartesianProps<T>);\n const format = { style: \"money\", currency } as const;\n const series = useMemo<SeriesSpec<T>[]>(\n () => (gross ? [{ key: gross, label: words.gross, color: 2 }, { key: net, label: words.net, color: 1 }] : [{ key: net, label: words.net, color: 1 }]),\n [gross, net, words.gross, words.net],\n );\n const values = data.map((row, i) => numberOf(row, net, i));\n const change = lastChange(values);\n const way = direction(change);\n const summary = hideSummary ? null : (\n <Kpi\n value={valueFormatter(format, locale)(total(data, net))}\n label={fillIn(words.total, { count: data.length })}\n delta={change == null ? null : fillIn(words.change, { change: signedPercent(change, locale) })}\n direction={way}\n tone={toneOf(way, \"up\")}\n />\n );\n return (\n <BarChart\n {...props}\n data={data}\n x={x}\n series={series}\n format={format}\n xFormat={period}\n thresholds={payoutThreshold ? [{ tone: \"success\", ...payoutThreshold }] : undefined}\n description={\n description || summary ? (\n <>\n {description}\n {description && summary ? <br /> : null}\n {summary}\n </>\n ) : undefined\n }\n />\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAIA,SAAS,eAAe;AAwDpB,SAmBM,UAnBN,KAmBM,YAnBN;AAxBG,SAAS,cAA+B;AAAA,EAC7C,IAAI;AAAA,EACJ;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAA0B;AACxB,QAAM,QAAQ,EAAE,GAAG,iBAAiB,GAAG,MAAM;AAC7C,QAAM,EAAE,OAAO,IAAI,YAAY,KAA0B;AACzD,QAAM,SAAS,EAAE,OAAO,SAAS,SAAS;AAC1C,QAAM,SAAS;AAAA,IACb,MAAO,QAAQ,CAAC,EAAE,KAAK,OAAO,OAAO,MAAM,OAAO,OAAO,EAAE,GAAG,EAAE,KAAK,KAAK,OAAO,MAAM,KAAK,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,KAAK,OAAO,MAAM,KAAK,OAAO,EAAE,CAAC;AAAA,IACnJ,CAAC,OAAO,KAAK,MAAM,OAAO,MAAM,GAAG;AAAA,EACrC;AACA,QAAM,SAAS,KAAK,IAAI,CAAC,KAAK,MAAM,SAAS,KAAK,KAAK,CAAC,CAAC;AACzD,QAAM,SAAS,WAAW,MAAM;AAChC,QAAM,MAAM,UAAU,MAAM;AAC5B,QAAM,UAAU,cAAc,OAC5B;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,eAAe,QAAQ,MAAM,EAAE,MAAM,MAAM,GAAG,CAAC;AAAA,MACtD,OAAO,OAAO,MAAM,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC;AAAA,MACjD,OAAO,UAAU,OAAO,OAAO,OAAO,MAAM,QAAQ,EAAE,QAAQ,cAAc,QAAQ,MAAM,EAAE,CAAC;AAAA,MAC7F,WAAW;AAAA,MACX,MAAM,OAAO,KAAK,IAAI;AAAA;AAAA,EACxB;AAEF,SACE;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT,YAAY,kBAAkB,CAAC,EAAE,MAAM,WAAW,GAAG,gBAAgB,CAAC,IAAI;AAAA,MAC1E,aACE,eAAe,UACb,iCACG;AAAA;AAAA,QACA,eAAe,UAAU,oBAAC,QAAG,IAAK;AAAA,QAClC;AAAA,SACH,IACE;AAAA;AAAA,EAER;AAEJ;","names":[]}
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
import {
|
|
3
3
|
describeParts
|
|
4
4
|
} from "./chunk-ANUCSRUN.js";
|
|
5
|
+
import {
|
|
6
|
+
read
|
|
7
|
+
} from "./chunk-W5FX4TU3.js";
|
|
5
8
|
import {
|
|
6
9
|
arcPath,
|
|
7
10
|
polar
|
|
8
11
|
} from "./chunk-OB5EFDDH.js";
|
|
9
|
-
import {
|
|
10
|
-
read
|
|
11
|
-
} from "./chunk-W5FX4TU3.js";
|
|
12
12
|
import {
|
|
13
13
|
shareFormatter,
|
|
14
14
|
valueFormatter
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
Tooltip,
|
|
19
19
|
colorOf,
|
|
20
20
|
useSettings
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-JTKR7ZWP.js";
|
|
22
22
|
|
|
23
23
|
// src/pie/pie-chart.tsx
|
|
24
24
|
import { useMemo, useState } from "react";
|
|
@@ -142,4 +142,4 @@ export {
|
|
|
142
142
|
PieChart,
|
|
143
143
|
DonutChart
|
|
144
144
|
};
|
|
145
|
-
//# sourceMappingURL=chunk-
|
|
145
|
+
//# sourceMappingURL=chunk-PBQV3B6O.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
useInView,
|
|
6
6
|
useReducedMotion,
|
|
7
7
|
useThemeVersion
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-JTKR7ZWP.js";
|
|
9
9
|
import {
|
|
10
10
|
sequential
|
|
11
11
|
} from "./chunk-3CJHBJXA.js";
|
|
@@ -231,4 +231,4 @@ export {
|
|
|
231
231
|
tooltipHtml,
|
|
232
232
|
EChartHost
|
|
233
233
|
};
|
|
234
|
-
//# sourceMappingURL=chunk-
|
|
234
|
+
//# sourceMappingURL=chunk-PWMMXRMC.js.map
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
BarChart
|
|
4
|
+
} from "./chunk-WVTIKOII.js";
|
|
5
|
+
import {
|
|
6
|
+
Kpi
|
|
7
|
+
} from "./chunk-WBHFVBSK.js";
|
|
8
|
+
import {
|
|
9
|
+
THROUGHPUT_LABELS,
|
|
10
|
+
fillIn,
|
|
11
|
+
successRate,
|
|
12
|
+
total
|
|
13
|
+
} from "./chunk-WNUFGMS7.js";
|
|
14
|
+
import {
|
|
15
|
+
valueFormatter
|
|
16
|
+
} from "./chunk-XLLYNNC6.js";
|
|
17
|
+
import {
|
|
18
|
+
useSettings
|
|
19
|
+
} from "./chunk-JTKR7ZWP.js";
|
|
20
|
+
|
|
21
|
+
// src/needs/statuses.ts
|
|
22
|
+
var DEFAULT_STATUSES = [
|
|
23
|
+
{ key: "completed", label: "Completed", tone: "success", outcome: "succeeded" },
|
|
24
|
+
{ key: "failed", label: "Failed", tone: "danger", outcome: "failed" },
|
|
25
|
+
{ key: "running", label: "Running", tone: "info" },
|
|
26
|
+
{ key: "queued", label: "Queued", tone: "neutral" }
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
// src/throughput/job-throughput.tsx
|
|
30
|
+
import { useMemo } from "react";
|
|
31
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
32
|
+
function JobThroughput({
|
|
33
|
+
x = "day",
|
|
34
|
+
period = "day",
|
|
35
|
+
statuses = DEFAULT_STATUSES,
|
|
36
|
+
hideSummary,
|
|
37
|
+
labels: given,
|
|
38
|
+
description,
|
|
39
|
+
data,
|
|
40
|
+
...props
|
|
41
|
+
}) {
|
|
42
|
+
const words = { ...THROUGHPUT_LABELS, ...given };
|
|
43
|
+
const { locale } = useSettings(props);
|
|
44
|
+
const series = useMemo(
|
|
45
|
+
() => statuses.map((s) => ({ key: s.key, label: s.label, color: s.tone })),
|
|
46
|
+
[statuses]
|
|
47
|
+
);
|
|
48
|
+
const sum = (outcome) => statuses.filter((s) => outcome === void 0 || s.outcome === outcome).reduce((n, s) => n + total(data, s.key), 0);
|
|
49
|
+
const rate = successRate(sum("succeeded"), sum("failed"));
|
|
50
|
+
const summary = hideSummary ? null : /* @__PURE__ */ jsx(
|
|
51
|
+
Kpi,
|
|
52
|
+
{
|
|
53
|
+
value: valueFormatter("integer", locale)(sum()),
|
|
54
|
+
label: fillIn(words.total, { count: data.length }),
|
|
55
|
+
delta: rate == null ? null : fillIn(words.success, { rate: valueFormatter("percent", locale)(rate) })
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
return /* @__PURE__ */ jsx(
|
|
59
|
+
BarChart,
|
|
60
|
+
{
|
|
61
|
+
...props,
|
|
62
|
+
data,
|
|
63
|
+
x,
|
|
64
|
+
xFormat: period,
|
|
65
|
+
series,
|
|
66
|
+
format: "integer",
|
|
67
|
+
stacked: true,
|
|
68
|
+
description: description || summary ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
69
|
+
description,
|
|
70
|
+
description && summary ? /* @__PURE__ */ jsx("br", {}) : null,
|
|
71
|
+
summary
|
|
72
|
+
] }) : void 0
|
|
73
|
+
}
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export {
|
|
78
|
+
DEFAULT_STATUSES,
|
|
79
|
+
JobThroughput
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=chunk-SLEJDQ3A.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/needs/statuses.ts","../src/throughput/job-throughput.tsx"],"sourcesContent":["import type { Tone } from \"../core/types\";\n\n/** A status of a job: the field holding its count per period, its name, its tone, and its part in the success rate. */\nexport interface JobStatus {\n /** The field of the row holding the count (`\"completed\"`). */\n readonly key: string;\n /** Its name, passed translated (`\"Completed\"`). */\n readonly label: string;\n /** Its tone: the status colours (`success`, `danger`, `info`, `neutral`). */\n readonly tone: Tone;\n /** `succeeded` or `failed`: counted in the success rate. Absent: still running or waiting. */\n readonly outcome?: \"succeeded\" | \"failed\";\n}\n\n/** The four statuses of an Orazaka job, in English: pass your own (translated) `statuses`. */\nexport const DEFAULT_STATUSES: readonly JobStatus[] = [\n { key: \"completed\", label: \"Completed\", tone: \"success\", outcome: \"succeeded\" },\n { key: \"failed\", label: \"Failed\", tone: \"danger\", outcome: \"failed\" },\n { key: \"running\", label: \"Running\", tone: \"info\" },\n { key: \"queued\", label: \"Queued\", tone: \"neutral\" },\n];\n","\"use client\";\n// Jobs per period, stacked by status (Orazaka insights, the console's supervision, the mobile Jobs screen): the total\n// and the success rate of the finished jobs written above, `live` for a stream. A stacked BarChart preset.\nimport { useMemo } from \"react\";\n\nimport { BarChart } from \"../bar/bar-chart\";\nimport type { CartesianProps } from \"../cartesian/cartesian\";\nimport { valueFormatter } from \"../core/format\";\nimport type { Accessor, CategoryFormat, Datum, SeriesSpec } from \"../core/types\";\nimport { Kpi } from \"../needs/kpi\";\nimport { THROUGHPUT_LABELS, type ThroughputLabels } from \"../needs/labels\";\nimport { fillIn, successRate, total } from \"../needs/model\";\nimport { DEFAULT_STATUSES, type JobStatus } from \"../needs/statuses\";\nimport { useSettings } from \"../web/frame\";\n\nexport interface JobThroughputProps<T extends Datum>\n extends Omit<CartesianProps<T>, \"y\" | \"yLabel\" | \"series\" | \"format\" | \"x\" | \"xFormat\" | \"labels\"> {\n /** The field (or function) of the period. Default `\"day\"`. */\n x?: Accessor<T, unknown>;\n /** How the periods are written. Default `\"day\"` (`Oct 11`); `\"time\"` for hours. */\n period?: CategoryFormat;\n /** The statuses, in stacking order, with their field, name and tone. Default: completed, failed, running, queued. */\n statuses?: readonly JobStatus[];\n /** Hide the total and the success rate above the bars. */\n hideSummary?: boolean;\n /** The words, passed translated; English by default. */\n labels?: Partial<ThroughputLabels>;\n}\n\n/** Jobs per period, stacked by status, with the total and the success rate. */\nexport function JobThroughput<T extends Datum>({\n x = \"day\" as Accessor<T, unknown>,\n period = \"day\",\n statuses = DEFAULT_STATUSES,\n hideSummary,\n labels: given,\n description,\n data,\n ...props\n}: JobThroughputProps<T>) {\n const words = { ...THROUGHPUT_LABELS, ...given };\n const { locale } = useSettings(props as CartesianProps<T>);\n const series = useMemo<SeriesSpec<T>[]>(\n () => statuses.map((s) => ({ key: s.key as Accessor<T, number | null | undefined>, label: s.label, color: s.tone })),\n [statuses],\n );\n const sum = (outcome?: \"succeeded\" | \"failed\") =>\n statuses.filter((s) => outcome === undefined || s.outcome === outcome).reduce((n, s) => n + total(data, s.key as Accessor<T, number>), 0);\n const rate = successRate(sum(\"succeeded\"), sum(\"failed\"));\n const summary = hideSummary ? null : (\n <Kpi\n value={valueFormatter(\"integer\", locale)(sum())}\n label={fillIn(words.total, { count: data.length })}\n delta={rate == null ? null : fillIn(words.success, { rate: valueFormatter(\"percent\", locale)(rate) })}\n />\n );\n return (\n <BarChart\n {...props}\n data={data}\n x={x}\n xFormat={period}\n series={series}\n format=\"integer\"\n stacked\n description={\n description || summary ? (\n <>\n {description}\n {description && summary ? <br /> : null}\n {summary}\n </>\n ) : undefined\n }\n />\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAeO,IAAM,mBAAyC;AAAA,EACpD,EAAE,KAAK,aAAa,OAAO,aAAa,MAAM,WAAW,SAAS,YAAY;AAAA,EAC9E,EAAE,KAAK,UAAU,OAAO,UAAU,MAAM,UAAU,SAAS,SAAS;AAAA,EACpE,EAAE,KAAK,WAAW,OAAO,WAAW,MAAM,OAAO;AAAA,EACjD,EAAE,KAAK,UAAU,OAAO,UAAU,MAAM,UAAU;AACpD;;;ACjBA,SAAS,eAAe;AA+CpB,SAiBM,UAjBN,KAiBM,YAjBN;AApBG,SAAS,cAA+B;AAAA,EAC7C,IAAI;AAAA,EACJ,SAAS;AAAA,EACT,WAAW;AAAA,EACX;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAA0B;AACxB,QAAM,QAAQ,EAAE,GAAG,mBAAmB,GAAG,MAAM;AAC/C,QAAM,EAAE,OAAO,IAAI,YAAY,KAA0B;AACzD,QAAM,SAAS;AAAA,IACb,MAAM,SAAS,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,KAA+C,OAAO,EAAE,OAAO,OAAO,EAAE,KAAK,EAAE;AAAA,IACnH,CAAC,QAAQ;AAAA,EACX;AACA,QAAM,MAAM,CAAC,YACX,SAAS,OAAO,CAAC,MAAM,YAAY,UAAa,EAAE,YAAY,OAAO,EAAE,OAAO,CAAC,GAAG,MAAM,IAAI,MAAM,MAAM,EAAE,GAA0B,GAAG,CAAC;AAC1I,QAAM,OAAO,YAAY,IAAI,WAAW,GAAG,IAAI,QAAQ,CAAC;AACxD,QAAM,UAAU,cAAc,OAC5B;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,eAAe,WAAW,MAAM,EAAE,IAAI,CAAC;AAAA,MAC9C,OAAO,OAAO,MAAM,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC;AAAA,MACjD,OAAO,QAAQ,OAAO,OAAO,OAAO,MAAM,SAAS,EAAE,MAAM,eAAe,WAAW,MAAM,EAAE,IAAI,EAAE,CAAC;AAAA;AAAA,EACtG;AAEF,SACE;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT;AAAA,MACA,QAAO;AAAA,MACP,SAAO;AAAA,MACP,aACE,eAAe,UACb,iCACG;AAAA;AAAA,QACA,eAAe,UAAU,oBAAC,QAAG,IAAK;AAAA,QAClC;AAAA,SACH,IACE;AAAA;AAAA,EAER;AAEJ;","names":[]}
|