@seekalfred/primitives 0.7.3 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/README.md +60 -1
  2. package/dist/alert/alert.d.ts +2 -2
  3. package/dist/alert-dialog/alert-dialog.d.ts +14 -0
  4. package/dist/alert-dialog/alert-dialog.js +51 -26
  5. package/dist/alert-dialog/alert-dialog.js.map +1 -1
  6. package/dist/announcement-bar/announcement-bar.d.ts +15 -4
  7. package/dist/announcement-bar/announcement-bar.js +31 -6
  8. package/dist/announcement-bar/announcement-bar.js.map +1 -1
  9. package/dist/avatar/avatar.d.ts +7 -0
  10. package/dist/avatar/avatar.js +33 -12
  11. package/dist/avatar/avatar.js.map +1 -1
  12. package/dist/badge/badge.d.ts +1 -1
  13. package/dist/bar-chart/bar-chart-series.d.ts +75 -0
  14. package/dist/bar-chart/bar-chart-series.js +401 -0
  15. package/dist/bar-chart/bar-chart-series.js.map +1 -0
  16. package/dist/bar-chart/bar-chart.d.ts +25 -1
  17. package/dist/bar-chart/bar-chart.js +26 -1
  18. package/dist/bar-chart/bar-chart.js.map +1 -1
  19. package/dist/bar-chart/index.d.ts +2 -1
  20. package/dist/button/button.d.ts +2 -2
  21. package/dist/chip-group/chip-group.d.ts +11 -0
  22. package/dist/chip-group/chip-group.js +50 -34
  23. package/dist/chip-group/chip-group.js.map +1 -1
  24. package/dist/chip-group/index.d.ts +2 -2
  25. package/dist/copy-button/copy-button.d.ts +19 -3
  26. package/dist/copy-button/copy-button.js +7 -4
  27. package/dist/copy-button/copy-button.js.map +1 -1
  28. package/dist/file-drop-zone/file-drop-zone.d.ts +29 -3
  29. package/dist/file-drop-zone/file-drop-zone.js +26 -8
  30. package/dist/file-drop-zone/file-drop-zone.js.map +1 -1
  31. package/dist/gauge/gauge.d.ts +18 -1
  32. package/dist/gauge/gauge.js +35 -21
  33. package/dist/gauge/gauge.js.map +1 -1
  34. package/dist/gauge/index.d.ts +2 -2
  35. package/dist/index.d.ts +8 -7
  36. package/dist/index.js +4 -4
  37. package/dist/input/input.d.ts +7 -0
  38. package/dist/input/input.js +3 -1
  39. package/dist/input/input.js.map +1 -1
  40. package/dist/lib/field-shell.js +2 -6
  41. package/dist/lib/field-shell.js.map +1 -1
  42. package/dist/lib/required-marker.js +20 -0
  43. package/dist/lib/required-marker.js.map +1 -0
  44. package/dist/line-chart/index.d.ts +2 -2
  45. package/dist/line-chart/line-chart.d.ts +105 -3
  46. package/dist/line-chart/line-chart.js +183 -44
  47. package/dist/line-chart/line-chart.js.map +1 -1
  48. package/dist/money-input/index.d.ts +2 -2
  49. package/dist/money-input/money-input.d.ts +14 -0
  50. package/dist/money-input/money-input.js +17 -7
  51. package/dist/money-input/money-input.js.map +1 -1
  52. package/dist/morph-select/morph-select.d.ts +8 -1
  53. package/dist/morph-select/morph-select.js +8 -5
  54. package/dist/morph-select/morph-select.js.map +1 -1
  55. package/dist/multi-select/multi-select.d.ts +31 -0
  56. package/dist/multi-select/multi-select.js +122 -5
  57. package/dist/multi-select/multi-select.js.map +1 -1
  58. package/dist/progress/index.d.ts +2 -2
  59. package/dist/progress/index.js +2 -2
  60. package/dist/progress/progress.d.ts +53 -2
  61. package/dist/progress/progress.js +146 -36
  62. package/dist/progress/progress.js.map +1 -1
  63. package/dist/radio-group/radio-group.d.ts +1 -1
  64. package/dist/scrub-input/scrub-input.d.ts +11 -1
  65. package/dist/scrub-input/scrub-input.js +22 -10
  66. package/dist/scrub-input/scrub-input.js.map +1 -1
  67. package/dist/select/select.d.ts +9 -0
  68. package/dist/select/select.js +3 -2
  69. package/dist/select/select.js.map +1 -1
  70. package/dist/sparkline/sparkline.d.ts +27 -2
  71. package/dist/sparkline/sparkline.js +115 -18
  72. package/dist/sparkline/sparkline.js.map +1 -1
  73. package/dist/stat-card/index.d.ts +2 -2
  74. package/dist/stat-card/index.js +2 -2
  75. package/dist/stat-card/stat-card.d.ts +32 -1
  76. package/dist/stat-card/stat-card.js +52 -9
  77. package/dist/stat-card/stat-card.js.map +1 -1
  78. package/dist/styles.css +1 -1
  79. package/dist/table/index.d.ts +2 -2
  80. package/dist/table/index.js +2 -2
  81. package/dist/table/table.d.ts +114 -4
  82. package/dist/table/table.js +374 -94
  83. package/dist/table/table.js.map +1 -1
  84. package/dist/text-shimmer/text-shimmer.d.ts +18 -2
  85. package/dist/text-shimmer/text-shimmer.js +5 -2
  86. package/dist/text-shimmer/text-shimmer.js.map +1 -1
  87. package/dist/textarea/textarea.d.ts +7 -0
  88. package/dist/textarea/textarea.js +3 -1
  89. package/dist/textarea/textarea.js.map +1 -1
  90. package/dist/toast/toast.d.ts +2 -2
  91. package/package.json +3 -3
@@ -4,11 +4,13 @@ import { mergeRefs } from "../lib/merge-refs.js";
4
4
  import { prefersReducedMotion, useIsomorphicLayoutEffect } from "../lib/motion.js";
5
5
  import { SMOOTH, SNAPPY, tween } from "../lib/spring-tween.js";
6
6
  import { forwardRef, useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
7
- import { jsx, jsxs } from "react/jsx-runtime";
7
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
8
  //#region src/line-chart/line-chart.tsx
9
9
  /** Room above the top gridline, and the gap between the crosshair and its tooltip. */
10
10
  const TOP = 12;
11
11
  const GAP = 14;
12
+ /** Between a point and its label. */
13
+ const LABEL_GAP = 6;
12
14
  /** The first draw: 480ms x 1.75, slow at both ends. */
13
15
  const LINE_CHART_DRAW_MS = 840;
14
16
  const PALETTE = [
@@ -25,9 +27,9 @@ const GRID = Array.from({ length: 241 }, (_, index) => index / 240);
25
27
  const clamp = (value, low, high) => Math.min(high, Math.max(low, value));
26
28
  const colorOf = (line, index) => line.color ?? PALETTE[Math.min(index, PALETTE.length - 1)];
27
29
  const clean = (value) => Number(value.toPrecision(12));
28
- /** Clean gridlines: the smallest step of 1, 2, 2.5 or 5 that covers the data in four rows or fewer, from zero unless the data dips below it. */
29
- function niceScale(low, high) {
30
- const lo = Math.min(0, low);
30
+ /** Clean gridlines: the smallest step of 1, 2, 2.5 or 5 that covers the data in four rows or fewer, from zero unless the data dips below it (or `fromZero` is off). */
31
+ function niceScale(low, high, fromZero = true) {
32
+ const lo = fromZero ? Math.min(0, low) : low;
31
33
  const hi = Math.max(high, lo + 1);
32
34
  const magnitude = 10 ** Math.floor(Math.log10((hi - lo) / 4));
33
35
  for (const factor of [
@@ -56,6 +58,23 @@ function niceScale(low, high) {
56
58
  ticks: [lo, hi]
57
59
  };
58
60
  }
61
+ /** The nice scale with either end pinned: a pinned end is a tick, and the nice ticks within half a step of it give way. */
62
+ function pinScale(scale, min, max) {
63
+ if (min === void 0 && max === void 0) return scale;
64
+ const lo = min ?? scale.min;
65
+ const hi = max ?? scale.max;
66
+ if (!(hi > lo)) return scale;
67
+ const step = scale.ticks.length > 1 ? scale.ticks[1] - scale.ticks[0] : hi - lo;
68
+ return {
69
+ min: lo,
70
+ max: hi,
71
+ ticks: [
72
+ lo,
73
+ ...scale.ticks.filter((tick) => tick > lo + step / 2 && tick < hi - step / 2),
74
+ hi
75
+ ]
76
+ };
77
+ }
59
78
  /** The line through every value, sampled densely on x from 0 to 1 in data units. Monotone tangents keep a smooth curve from swinging past the data. */
60
79
  function curveFor(values, smooth) {
61
80
  if (values.length === 0) return [];
@@ -101,18 +120,22 @@ function fillGaps(values) {
101
120
  return values[before] + (values[after] - values[before]) * (index - before) / (after - before);
102
121
  });
103
122
  }
104
- /** Where a line with missing values is not drawn: before its first value, after its last, and between two values with missing ones between them. */
105
- function breaksFor(values) {
123
+ /** Where a line with missing values is not drawn: before its first value, after its last, and (unless it connects across them) between two values with missing ones between them. */
124
+ function breaksFor(values, across = false) {
106
125
  const present = values.flatMap((value, index) => value === null ? [] : [index]);
107
126
  if (!present.length) return values.length ? [[-Infinity, Infinity]] : [];
108
127
  const last = values.length - 1;
109
128
  if (last === 0) return [];
110
129
  const breaks = [];
111
130
  if (present[0] > 0) breaks.push([-Infinity, present[0] / last]);
112
- for (let at = 1; at < present.length; at++) if (present[at] - present[at - 1] > 1) breaks.push([present[at - 1] / last, present[at] / last]);
131
+ for (let at = 1; at < present.length; at++) if (!across && present[at] - present[at - 1] > 1) breaks.push([present[at - 1] / last, present[at] / last]);
113
132
  if (present[present.length - 1] < last) breaks.push([present[present.length - 1] / last, Infinity]);
114
133
  return breaks;
115
134
  }
135
+ function holesFor(values) {
136
+ const last = values.length - 1;
137
+ return values.flatMap((value, index) => value === null && last > 0 ? [index / last] : []);
138
+ }
116
139
  const EDGE = 1e-9;
117
140
  /** Radius of a lone point's dot, inside its 2px stroke. */
118
141
  const DOT = 1.5;
@@ -167,7 +190,7 @@ function axisPicks(data, width) {
167
190
  const stride = Math.ceil(labeled.length / room);
168
191
  return labeled.reverse().filter((_, rank) => rank % stride === 0).reverse();
169
192
  }
170
- const LineChart = forwardRef(function LineChart({ data, series, label, unit = "", height = 220, formatValue, formatTick = (value) => COMPACT.format(value), hiddenSeries, defaultHiddenSeries, onHiddenSeriesChange, onActiveChange, loading = false, emptyLabel = "No data for this range", legend, categoryLabel = "Date", curve = "smooth", missing = "zero", missingLabel = "No data", references, className }, ref) {
193
+ const LineChart = forwardRef(function LineChart({ data, series, label, unit = "", height = 220, formatValue, formatTick = (value) => COMPACT.format(value), hiddenSeries, defaultHiddenSeries, onHiddenSeriesChange, onActiveChange, loading = false, emptyLabel = "No data for this range", legend, categoryLabel = "Date", curve = "smooth", missing = "zero", missingLabel = "No data", references, markers, reversed = false, valueDomain, bands, renderTooltip, className }, ref) {
171
194
  const figure = useRef(null);
172
195
  const refs = useMemo(() => mergeRefs(ref, figure), [ref]);
173
196
  const plot = useRef(null);
@@ -186,7 +209,7 @@ const LineChart = forwardRef(function LineChart({ data, series, label, unit = ""
186
209
  };
187
210
  const visible = series.filter((line) => !hidden.includes(line.key));
188
211
  const showLegend = legend ?? series.length > 1;
189
- const gaps = missing === "gap";
212
+ const gaps = missing !== "zero";
190
213
  const read = (item, key) => {
191
214
  const value = item.values[key];
192
215
  if (!gaps) return value ?? 0;
@@ -197,7 +220,8 @@ const LineChart = forwardRef(function LineChart({ data, series, label, unit = ""
197
220
  const values = data.map((item) => read(item, line.key));
198
221
  return [line.key, {
199
222
  shape: curveFor(fillGaps(values), curve === "smooth"),
200
- breaks: breaksFor(values)
223
+ breaks: breaksFor(values, missing === "connect"),
224
+ holes: holesFor(values)
201
225
  }];
202
226
  })), [signature]);
203
227
  let low = Infinity;
@@ -213,9 +237,12 @@ const LineChart = forwardRef(function LineChart({ data, series, label, unit = ""
213
237
  low = Math.min(low, reference.value);
214
238
  high = Math.max(high, reference.value);
215
239
  }
216
- const range = Number.isFinite(low) ? niceScale(low, high) : null;
217
- const [steady, setSteady] = useState(() => range ?? niceScale(0, 100));
218
- if (range && (range.min !== steady.min || range.max !== steady.max)) setSteady(range);
240
+ const pinMin = Number.isFinite(valueDomain?.min) ? valueDomain.min : void 0;
241
+ const pinMax = Number.isFinite(valueDomain?.max) ? valueDomain.max : void 0;
242
+ const fit = (from, to) => pinScale(niceScale(pinMin ?? from, pinMax ?? to, pinMin === void 0), pinMin, pinMax);
243
+ const range = Number.isFinite(low) ? fit(low, high) : pinMin !== void 0 && pinMax !== void 0 ? fit(pinMin, pinMax) : null;
244
+ const [steady, setSteady] = useState(() => range ?? fit(0, 100));
245
+ if (range && (range.min !== steady.min || range.max !== steady.max || range.ticks.join() !== steady.ticks.join())) setSteady(range);
219
246
  const [active, setActive] = useState(null);
220
247
  const [seen, setSeen] = useState(signature);
221
248
  if (seen !== signature) {
@@ -239,6 +266,9 @@ const LineChart = forwardRef(function LineChart({ data, series, label, unit = ""
239
266
  return at < 0 ? void 0 : last > 0 ? at / last : .5;
240
267
  };
241
268
  const drawnReferences = (references ?? []).filter((reference) => referenceAt(reference) !== void 0);
269
+ const markerIndex = (marker) => data.findIndex((item) => item.key === marker.at);
270
+ const drawnMarkers = (markers ?? []).filter((marker) => markerIndex(marker) >= 0);
271
+ const drawnBands = (bands ?? []).filter((band) => Number.isFinite(band.from) && Number.isFinite(band.to));
242
272
  const live = useRef({
243
273
  series,
244
274
  hidden
@@ -256,14 +286,15 @@ const LineChart = forwardRef(function LineChart({ data, series, label, unit = ""
256
286
  if (!w || !root) return;
257
287
  const lo = scale.current.min;
258
288
  const span = scale.current.max - lo || 1;
259
- const toY = (value) => TOP + (1 - (value - lo) / span) * (height - TOP);
289
+ const toY = (value) => TOP + (reversed ? (value - lo) / span : 1 - (value - lo) / span) * (height - TOP);
290
+ const base = reversed ? scale.current.max : lo;
260
291
  const at = cursor.current;
261
292
  const dotYs = [];
262
293
  for (const line of live.current.series) {
263
294
  const track = tracks.current.get(line.key);
264
295
  if (!track) continue;
265
296
  const p = track.presence;
266
- const y = (value) => toY(lo + (value - lo) * p);
297
+ const y = (value) => toY(base + (value - base) * p);
267
298
  const group = root.querySelector(`[data-series="${CSS.escape(line.key)}"]`);
268
299
  if (track.breaks.length) {
269
300
  const paths = pathsFor(track.shape, track.breaks, (x) => x * w, y, height);
@@ -277,7 +308,7 @@ const LineChart = forwardRef(function LineChart({ data, series, label, unit = ""
277
308
  if (group) group.style.opacity = String(p);
278
309
  const dot = root.querySelector(`[data-dot="${CSS.escape(line.key)}"]`);
279
310
  if (dot && track.shape.length) {
280
- const gap = inBreak(track.breaks, at);
311
+ const gap = inBreak(track.breaks, at) || track.holes.some((x) => Math.abs(x - at) < 1e-6);
281
312
  const cy = y(valueAt(track.shape, at));
282
313
  dot.setAttribute("cx", (at * w).toFixed(1));
283
314
  dot.setAttribute("cy", cy.toFixed(1));
@@ -292,6 +323,18 @@ const LineChart = forwardRef(function LineChart({ data, series, label, unit = ""
292
323
  node.setAttribute("y2", yy);
293
324
  }
294
325
  for (const node of gutter.current?.querySelectorAll("[data-tick]") ?? []) node.style.translate = `0 ${Math.round(toY(Number(node.dataset.tick)))}px`;
326
+ for (const node of root.querySelectorAll("[data-band]")) {
327
+ const ys = [toY(Number(node.dataset.from)), toY(Number(node.dataset.to))];
328
+ const top = clamp(Math.min(...ys), TOP, height);
329
+ const bottom = clamp(Math.max(...ys), TOP, height);
330
+ node.setAttribute("y", String(Math.round(top)));
331
+ node.setAttribute("height", String(Math.max(0, Math.round(bottom) - Math.round(top))));
332
+ const text = plot.current?.querySelector(`[data-band-label="${CSS.escape(node.dataset.band)}"]`);
333
+ if (text) {
334
+ text.style.translate = `0 ${Math.round(top)}px`;
335
+ text.style.visibility = bottom - top >= text.offsetHeight ? "" : "hidden";
336
+ }
337
+ }
295
338
  for (const node of root.querySelectorAll("[data-reference-value]")) {
296
339
  const yy = String(Math.round(toY(Number(node.dataset.referenceValue))) + .5);
297
340
  node.setAttribute("y1", yy);
@@ -306,6 +349,17 @@ const LineChart = forwardRef(function LineChart({ data, series, label, unit = ""
306
349
  const { referenceValue, referenceAt } = node.dataset;
307
350
  node.style.translate = referenceValue !== void 0 ? `0 ${Math.round(toY(Number(referenceValue)))}px` : `${Math.round(Number(referenceAt) * w)}px 0`;
308
351
  }
352
+ for (const node of plot.current?.querySelectorAll("[data-point-label]") ?? []) {
353
+ const track = tracks.current.get(node.dataset.pointLabel);
354
+ if (!track?.shape.length) continue;
355
+ const x = Number(node.dataset.x);
356
+ const cy = toY(base + (valueAt(track.shape, x) - base) * track.presence);
357
+ const lw = node.offsetWidth;
358
+ const lh = node.offsetHeight;
359
+ const top = cy - lh - LABEL_GAP < 0 ? cy + LABEL_GAP : cy - lh - LABEL_GAP;
360
+ node.style.translate = `${Math.round(clamp(x * w - lw / 2, 0, Math.max(0, w - lw)))}px ${Math.round(clamp(top, 0, Math.max(0, height - lh)))}px`;
361
+ node.style.opacity = String(track.presence);
362
+ }
309
363
  const cx = Math.round(at * w) + .5;
310
364
  const cross = root.querySelector("[data-part=crosshair]");
311
365
  cross?.setAttribute("x1", String(cx));
@@ -319,7 +373,7 @@ const LineChart = forwardRef(function LineChart({ data, series, label, unit = ""
319
373
  const middle = dotYs.length ? dotYs.reduce((sum, value) => sum + value, 0) / dotYs.length : height / 2;
320
374
  bubble.style.translate = `${clamp(x, 0, Math.max(0, w - tw))}px ${clamp(middle - th / 2, 0, Math.max(0, height - th))}px`;
321
375
  }
322
- }, [height]);
376
+ }, [height, reversed]);
323
377
  useIsomorphicLayoutEffect(() => {
324
378
  const node = plot.current;
325
379
  if (!node) return;
@@ -337,18 +391,20 @@ const LineChart = forwardRef(function LineChart({ data, series, label, unit = ""
337
391
  useIsomorphicLayoutEffect(paint);
338
392
  useEffect(() => {
339
393
  const moves = [];
340
- for (const [key, { shape: target, breaks }] of targets) {
394
+ for (const [key, { shape: target, breaks, holes }] of targets) {
341
395
  const track = tracks.current.get(key);
342
396
  if (!track) {
343
397
  tracks.current.set(key, {
344
398
  shape: target,
345
399
  breaks,
400
+ holes,
346
401
  presence: live.current.hidden.includes(key) ? 0 : 1,
347
402
  stop: null
348
403
  });
349
404
  continue;
350
405
  }
351
406
  track.breaks = breaks;
407
+ track.holes = holes;
352
408
  if (!track.shape.length || !target.length) {
353
409
  track.shape = target;
354
410
  continue;
@@ -479,13 +535,66 @@ const LineChart = forwardRef(function LineChart({ data, series, label, unit = ""
479
535
  const onPointerMove = (event) => {
480
536
  if (!empty && (event.pointerType === "mouse" || event.buttons)) setActive(pointAt(event.clientX));
481
537
  };
482
- const formatNumber = (value, line) => `${formatValue ? formatValue(value, line) : GROUPED.format(value)}${unit ? ` ${unit}` : ""}`;
538
+ const formatShort = (value, line) => formatValue ? formatValue(value, line) : GROUPED.format(value);
539
+ const formatNumber = (value, line) => `${formatShort(value, line)}${unit ? ` ${unit}` : ""}`;
483
540
  const format = (value, line) => value === null ? missingLabel : formatNumber(value, line);
484
541
  const formatReference = (value) => series[0] ? formatNumber(value, series[0]) : `${GROUPED.format(value)}${unit ? ` ${unit}` : ""}`;
485
542
  const reading = index === null ? null : data[index];
486
543
  const shown = reading ?? data[last];
487
- const valueText = reading ? `${reading.label}: ${visible.map((line) => `${line.label} ${format(read(reading, line.key), line)}`).join(", ")}` : empty ? emptyLabel : `${visible.length} of ${series.length} series shown`;
488
- const summary = empty ? `${label}. ${emptyLabel}.` : `${label}, ${data[0].label} to ${data[last].label}. ${visible.map((line) => `${line.label}, latest ${format(read(data[last], line.key), line)}`).join(". ")}.${drawnReferences.map((reference) => typeof reference.value === "number" ? ` ${reference.label}: ${formatReference(reference.value)}.` : ` ${reference.label}: ${data.find((item) => item.key === reference.at).label}.`).join("")}`;
544
+ const markedHere = index === null ? [] : drawnMarkers.filter((marker) => markerIndex(marker) === index);
545
+ const valueText = reading ? `${reading.label}: ${visible.map((line) => `${line.label} ${format(read(reading, line.key), line)}`).join(", ")}${markedHere.map((marker) => `. ${marker.description ?? marker.label}`).join("")}` : empty ? emptyLabel : `${visible.length} of ${series.length} series shown`;
546
+ const summary = empty ? `${label}. ${emptyLabel}.` : `${label}, ${data[0].label} to ${data[last].label}. ${visible.map((line) => `${line.label}, latest ${format(read(data[last], line.key), line)}${typeof line.endLabel === "string" ? `, ${line.endLabel}` : ""}`).join(". ")}.${drawnReferences.map((reference) => typeof reference.value === "number" ? ` ${reference.label}: ${formatReference(reference.value)}.` : ` ${reference.label}: ${data.find((item) => item.key === reference.at).label}.`).join("")}${drawnMarkers.map((marker) => ` ${marker.description ?? marker.label}: ${data[markerIndex(marker)].label}.`).join("")}${drawnBands.map((band) => ` ${band.description ?? `${band.label}: ${formatReference(Math.min(band.from, band.to))} to ${formatReference(Math.max(band.from, band.to))}`}.`).join("")}`;
547
+ const pointLabels = empty ? [] : series.flatMap((line) => {
548
+ if (!line.pointLabels && !line.endLabel) return [];
549
+ const present = data.flatMap((item, at) => {
550
+ const value = read(item, line.key);
551
+ return value === null ? [] : [{
552
+ at,
553
+ value
554
+ }];
555
+ });
556
+ const end = line.endLabel ? present.at(-1) : void 0;
557
+ return [...(line.pointLabels ? present.filter((point) => point !== end) : []).map((point) => ({
558
+ line,
559
+ ...point,
560
+ end: false,
561
+ text: formatShort(point.value, line)
562
+ })), ...end ? [{
563
+ line,
564
+ ...end,
565
+ end: true,
566
+ text: typeof line.endLabel === "string" ? line.endLabel : formatShort(end.value, line)
567
+ }] : []];
568
+ });
569
+ const readout = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("p", {
570
+ className: "m-0 mb-0.5 whitespace-nowrap text-xs tabular-nums text-text-secondary",
571
+ children: shown?.label ?? ""
572
+ }), visible.map((line) => /* @__PURE__ */ jsxs("p", {
573
+ className: "m-0 flex min-w-0 items-center gap-2 text-sm",
574
+ style: { "--series": colorOf(line, series.indexOf(line)) },
575
+ children: [
576
+ /* @__PURE__ */ jsx("span", {
577
+ "data-dashed": line.dashed || void 0,
578
+ className: "chart-tip-swatch"
579
+ }),
580
+ /* @__PURE__ */ jsx("span", {
581
+ className: "min-w-0 flex-1 truncate text-text-secondary",
582
+ children: line.label
583
+ }),
584
+ /* @__PURE__ */ jsx("span", {
585
+ className: "ms-2 flex-none whitespace-nowrap font-medium tabular-nums text-foreground",
586
+ children: shown ? format(read(shown, line.key), line) : format(0, line)
587
+ })
588
+ ]
589
+ }, line.key))] });
590
+ const tipContent = renderTooltip && shown ? renderTooltip({
591
+ datum: shown,
592
+ index: index ?? last,
593
+ series: visible,
594
+ values: Object.fromEntries(visible.map((line) => [line.key, read(shown, line.key)])),
595
+ format,
596
+ readout
597
+ }) : readout;
489
598
  const picks = axisPicks(data, plotWidth);
490
599
  const allHidden = !empty && visible.length === 0;
491
600
  return /* @__PURE__ */ jsxs("figure", {
@@ -570,7 +679,16 @@ const LineChart = forwardRef(function LineChart({ data, series, label, unit = ""
570
679
  style: { width: drawn ? plotWidth + 16 : 0 }
571
680
  })
572
681
  }) }),
573
- steady.ticks.slice(1).map((value) => /* @__PURE__ */ jsx("line", {
682
+ drawnBands.map((band) => /* @__PURE__ */ jsx("rect", {
683
+ "data-band": band.key,
684
+ "data-from": band.from,
685
+ "data-to": band.to,
686
+ "data-tone": band.tone,
687
+ className: "line-chart-band",
688
+ x: 0,
689
+ width: "100%"
690
+ }, band.key)),
691
+ (reversed ? steady.ticks.slice(0, -1) : steady.ticks.slice(1)).map((value) => /* @__PURE__ */ jsx("line", {
574
692
  "data-grid": value,
575
693
  className: "line-chart-grid",
576
694
  x1: 0,
@@ -586,12 +704,14 @@ const LineChart = forwardRef(function LineChart({ data, series, label, unit = ""
586
704
  drawnReferences.map((reference) => typeof reference.value === "number" ? /* @__PURE__ */ jsx("line", {
587
705
  "data-reference": reference.key,
588
706
  "data-reference-value": reference.value,
707
+ "data-tone": reference.tone,
589
708
  className: "line-chart-reference",
590
709
  x1: 0,
591
710
  x2: "100%"
592
711
  }, reference.key) : /* @__PURE__ */ jsx("line", {
593
712
  "data-reference": reference.key,
594
713
  "data-reference-at": referenceAt(reference),
714
+ "data-tone": reference.tone,
595
715
  className: "line-chart-reference",
596
716
  y1: 8,
597
717
  y2: height
@@ -627,6 +747,14 @@ const LineChart = forwardRef(function LineChart({ data, series, label, unit = ""
627
747
  }, line.key))
628
748
  ]
629
749
  }),
750
+ drawnBands.map((band) => /* @__PURE__ */ jsx("span", {
751
+ "aria-hidden": "true",
752
+ "data-slot": "line-chart-band-label",
753
+ "data-band-label": band.key,
754
+ "data-tone": band.tone,
755
+ className: "line-chart-band-label",
756
+ children: band.label
757
+ }, band.key)),
630
758
  drawnReferences.map((reference) => /* @__PURE__ */ jsx("span", {
631
759
  "aria-hidden": "true",
632
760
  "data-slot": "line-chart-reference-label",
@@ -634,35 +762,46 @@ const LineChart = forwardRef(function LineChart({ data, series, label, unit = ""
634
762
  "data-reference-value": typeof reference.value === "number" ? reference.value : void 0,
635
763
  "data-reference-at": typeof reference.value === "number" ? void 0 : referenceAt(reference),
636
764
  "data-axis": typeof reference.value === "number" ? "value" : "category",
765
+ "data-tone": reference.tone,
637
766
  className: "line-chart-reference-label",
638
767
  children: reference.label
639
768
  }, reference.key)),
640
- /* @__PURE__ */ jsxs("div", {
769
+ drawnMarkers.map((marker) => {
770
+ const share = last > 0 ? markerIndex(marker) / last : .5;
771
+ return /* @__PURE__ */ jsx("span", {
772
+ "aria-hidden": "true",
773
+ "data-slot": "line-chart-marker",
774
+ "data-marker": marker.key,
775
+ "data-tone": marker.tone,
776
+ className: "line-chart-marker",
777
+ style: { left: `${share * 100}%` },
778
+ children: /* @__PURE__ */ jsx("span", {
779
+ className: "line-chart-marker-label",
780
+ style: { translate: `${-share * 100}% 0` },
781
+ children: marker.label
782
+ })
783
+ }, marker.key);
784
+ }),
785
+ pointLabels.map(({ line, at, end, text }) => /* @__PURE__ */ jsxs("span", {
786
+ "aria-hidden": "true",
787
+ "data-slot": end ? "line-chart-end-label" : "line-chart-point-label",
788
+ "data-point-label": line.key,
789
+ "data-x": last > 0 ? at / last : .5,
790
+ className: "line-chart-point-label",
791
+ "data-end": end || void 0,
792
+ style: { "--series": colorOf(line, series.indexOf(line)) },
793
+ children: [end ? /* @__PURE__ */ jsx("span", {
794
+ "data-dashed": line.dashed || void 0,
795
+ className: "chart-tip-swatch"
796
+ }) : null, text]
797
+ }, `${line.key}:${data[at].key}`)),
798
+ /* @__PURE__ */ jsx("div", {
641
799
  ref: tip,
642
800
  "aria-hidden": "true",
643
801
  "data-slot": "line-chart-tooltip",
802
+ "data-empty": !tipContent || void 0,
644
803
  className: "line-chart-tooltip",
645
- children: [/* @__PURE__ */ jsx("p", {
646
- className: "m-0 mb-0.5 whitespace-nowrap text-xs tabular-nums text-text-secondary",
647
- children: shown?.label ?? ""
648
- }), visible.map((line) => /* @__PURE__ */ jsxs("p", {
649
- className: "m-0 flex min-w-0 items-center gap-2 text-sm",
650
- style: { "--series": colorOf(line, series.indexOf(line)) },
651
- children: [
652
- /* @__PURE__ */ jsx("span", {
653
- "data-dashed": line.dashed || void 0,
654
- className: "chart-tip-swatch"
655
- }),
656
- /* @__PURE__ */ jsx("span", {
657
- className: "min-w-0 flex-1 truncate text-text-secondary",
658
- children: line.label
659
- }),
660
- /* @__PURE__ */ jsx("span", {
661
- className: "ms-2 flex-none whitespace-nowrap font-medium tabular-nums text-foreground",
662
- children: shown ? format(read(shown, line.key), line) : format(0, line)
663
- })
664
- ]
665
- }, line.key))]
804
+ children: tipContent
666
805
  }),
667
806
  (empty || allHidden) && !loading ? /* @__PURE__ */ jsx("p", {
668
807
  className: "absolute inset-0 m-0 grid place-items-center text-sm text-muted-foreground pointer-events-none motion-safe:animate-fade-in",