@eifi1/ui-kit 0.15.0 → 0.15.2

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.
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- import { jsx, jsxs } from "react/jsx-runtime";
3
- import { useLayoutEffect, useMemo, useRef, useState } from "react";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
4
4
  import {
5
5
  Area,
6
6
  Bar,
@@ -25,6 +25,7 @@ import {
25
25
  import { STEP_DASH, strokeDash } from "./toggle-legend.js";
26
26
  import { DEFAULT_SERIES_CHART_LABELS } from "./series-chart-labels.js";
27
27
  import { categoryTicks, integerTicks, niceTicks, timeTicksWithUnit } from "./series-chart-ticks.js";
28
+ import { autoAxisBudget, axisUnit, budgetedAxes } from "./series-chart-budget.js";
28
29
  import { paletteFor } from "../theme/chart-palette.js";
29
30
  import { useKitLabels, useKitLocale } from "../i18n/kit-labels.js";
30
31
  import { cn } from "../lib/cn.js";
@@ -104,14 +105,31 @@ function anchoredBand(extent) {
104
105
  function visibleSeries(series, hidden) {
105
106
  return series.map((entry, index) => ({ ...entry, color: entry.color ?? paletteFor(index) })).filter((entry) => !hidden.has(entry.key));
106
107
  }
107
- function seriesLegendEntries(series) {
108
+ function withUnit(label, unit) {
109
+ if (!unit) return label;
110
+ if (typeof label === "string" || typeof label === "number") {
111
+ const text = String(label);
112
+ return text.trimEnd().endsWith(`(${unit})`) ? label : `${text} (${unit})`;
113
+ }
114
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
115
+ label,
116
+ " (",
117
+ unit,
118
+ ")"
119
+ ] });
120
+ }
121
+ function seriesLegendEntries(series, { axes, budgeted } = {}) {
122
+ const undrawn = /* @__PURE__ */ new Map();
123
+ for (const axis of axes ?? []) {
124
+ if (axis.hide || budgeted?.includes(axis.id)) undrawn.set(axis.id, axisUnit(axis));
125
+ }
108
126
  return series.map((entry, index) => {
109
127
  const own = entry.dash ?? (entry.dashed ? 1 : 0);
110
128
  const dash = entry.step ? STEP_DASH : own;
111
129
  const line = (entry.type ?? "line") === "line";
112
130
  return {
113
131
  key: entry.key,
114
- label: entry.label,
132
+ label: withUnit(entry.label, undrawn.get(entry.axis ?? DEFAULT_Y_AXIS)),
115
133
  color: entry.color ?? paletteFor(index),
116
134
  ...line && dash !== 0 ? { marker: "stroke", dash } : {}
117
135
  };
@@ -323,13 +341,44 @@ function SeriesPlot({
323
341
  labels: labelsProp,
324
342
  locale: localeProp,
325
343
  className,
326
- minBarLength
344
+ minBarLength,
345
+ maxVisibleAxes,
346
+ axisBudget = "auto",
347
+ onAxisBudget
327
348
  }) {
328
349
  const labels = useKitLabels("seriesChart", DEFAULT_SERIES_CHART_LABELS, labelsProp);
329
350
  const locale = useKitLocale(localeProp);
330
351
  const number = useDefaultFormat(localeProp);
331
352
  const [keyStop, setKeyStop] = useState(0);
332
353
  const [keyFocus, setKeyFocus] = useState(void 0);
354
+ const [box, setBox] = useState(null);
355
+ const [width, setWidth] = useState(void 0);
356
+ useLayoutEffect(() => {
357
+ if (!box || axisBudget === "off" || typeof ResizeObserver === "undefined") return;
358
+ const measure = () => setWidth(box.getBoundingClientRect().width || void 0);
359
+ measure();
360
+ const observer = new ResizeObserver(measure);
361
+ observer.observe(box);
362
+ return () => observer.disconnect();
363
+ }, [box, axisBudget]);
364
+ const capped = budgetedAxes(axes, maxVisibleAxes);
365
+ const afterCap = capped.length ? axes.map((axis) => capped.includes(axis.id) ? { ...axis, hide: true } : axis) : axes;
366
+ const auto = axisBudget === "off" ? [] : budgetedAxes(
367
+ afterCap,
368
+ autoAxisBudget(
369
+ afterCap,
370
+ width,
371
+ (axis) => axisBandWidth(axis.width, Boolean(axis.title))
372
+ )
373
+ );
374
+ const budgeted = [...capped, ...auto];
375
+ const budgetKey = budgeted.join("\n");
376
+ const reported = useRef("");
377
+ useEffect(() => {
378
+ if (budgetKey === reported.current) return;
379
+ reported.current = budgetKey;
380
+ onAxisBudget?.(budgetKey ? budgetKey.split("\n") : []);
381
+ }, [budgetKey, onAxisBudget]);
333
382
  const heightClass = typeof height === "string" ? height : void 0;
334
383
  const heightStyle = typeof height === "number" ? { height } : void 0;
335
384
  if (!model.source.length || !series.length) {
@@ -346,14 +395,21 @@ function SeriesPlot({
346
395
  const xKey = model.plotKey;
347
396
  const plotted = model.rows;
348
397
  const rows = model.source;
398
+ const drawnAxes = budgeted.length ? axes.map((axis) => budgeted.includes(axis.id) ? { ...axis, hide: true } : axis) : axes;
399
+ const lostUnit = new Map(
400
+ axes.filter((axis) => budgeted.includes(axis.id)).map((axis) => [axis.id, axisUnit(axis)])
401
+ );
349
402
  const config = Object.fromEntries(
350
403
  series.map((entry, index) => [
351
404
  entry.key,
352
- { label: entry.label, color: entry.color ?? paletteFor(index) }
405
+ {
406
+ label: withUnit(entry.label, lostUnit.get(entry.axis ?? DEFAULT_Y_AXIS)),
407
+ color: entry.color ?? paletteFor(index)
408
+ }
353
409
  ])
354
410
  );
355
- const visible = axes.filter((axis) => !axis.hide);
356
- const gridAxis = (visible[0] ?? axes[0])?.id ?? DEFAULT_Y_AXIS;
411
+ const visible = drawnAxes.filter((axis) => !axis.hide);
412
+ const gridAxis = (visible[0] ?? drawnAxes[0])?.id ?? DEFAULT_Y_AXIS;
357
413
  const onLeft = visible.some((axis) => (axis.orientation ?? "left") === "left");
358
414
  const onRight = visible.some((axis) => axis.orientation === "right");
359
415
  const margin = {
@@ -511,6 +567,8 @@ function SeriesPlot({
511
567
  return /* @__PURE__ */ jsx(
512
568
  ChartContainer,
513
569
  {
570
+ ref: setBox,
571
+ "data-axis-budget": budgeted.length ? budgetKey.replace(/\n/g, " ") : void 0,
514
572
  config,
515
573
  className: cn("w-full", heightClass, className, onPointClick && "cursor-pointer"),
516
574
  style: heightStyle,
@@ -555,7 +613,7 @@ function SeriesPlot({
555
613
  )
556
614
  }
557
615
  ),
558
- axes.map((axis) => {
616
+ drawnAxes.map((axis) => {
559
617
  const domain = fittedY(axis);
560
618
  const zoomed = anchored(axis) ? zoom?.xDomain !== void 0 : zoom?.yDomains[axis.id] !== void 0;
561
619
  return /* @__PURE__ */ jsx(
@@ -567,7 +625,7 @@ function SeriesPlot({
567
625
  ticks: resolveTicks(axis.tickValues, domain, finite) ?? (wholeOnly(axis) ? integerTicks(domain) ?? niceTicks(domain) : niceTicks(domain)),
568
626
  allowDataOverflow: zoomed,
569
627
  orientation: axis.orientation ?? "left",
570
- width: axisBandWidth(axis.width, Boolean(axis.title) && !axis.hide),
628
+ width: axis.hide ? 0 : axisBandWidth(axis.width, Boolean(axis.title)),
571
629
  tickLine: false,
572
630
  axisLine: false,
573
631
  tickFormatter: axis.format ?? number,
@@ -610,7 +668,7 @@ function SeriesPlot({
610
668
  const own = entry.dash ?? (entry.dashed ? 1 : 0);
611
669
  const dash = entry.step ? strokeDash(STEP_DASH) : typeof own === "string" ? own : strokeDash(own);
612
670
  const curve = entry.step ? "stepAfter" : entry.curve ?? "monotone";
613
- const width = entry.strokeWidth ?? (entry.step ? STEP_WIDTH : LINE_WIDTH);
671
+ const width2 = entry.strokeWidth ?? (entry.step ? STEP_WIDTH : LINE_WIDTH);
614
672
  if (type === "bar") {
615
673
  const stub = entry.minBarLength ?? minBarLength;
616
674
  return /* @__PURE__ */ jsx(
@@ -643,7 +701,7 @@ function SeriesPlot({
643
701
  dataKey: entry.key,
644
702
  stackId: entry.stack,
645
703
  stroke: color,
646
- strokeWidth: width,
704
+ strokeWidth: width2,
647
705
  strokeDasharray: dash,
648
706
  fill: color,
649
707
  fillOpacity: entry.fillOpacity ?? (entry.stack !== void 0 ? 0.55 : 0.2),
@@ -662,7 +720,7 @@ function SeriesPlot({
662
720
  type: curve,
663
721
  dataKey: entry.key,
664
722
  stroke: color,
665
- strokeWidth: width,
723
+ strokeWidth: width2,
666
724
  strokeDasharray: dash,
667
725
  dot: dotFor(entry),
668
726
  ...entry.activeDot !== void 0 ? { activeDot: entry.activeDot } : {},