@robr0/design-system 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.md +3 -3
  2. package/components/Accordion/Accordion.js +1 -0
  3. package/components/AgentPlan/AgentPlan.css +2 -2
  4. package/components/AgentPlan/AgentPlan.js +1 -0
  5. package/components/AgentStatus/AgentStatus.js +1 -0
  6. package/components/AiButton/AiButton.js +1 -0
  7. package/components/AlertDialog/AlertDialog.js +1 -0
  8. package/components/AnchorNav/AnchorNav.css +63 -0
  9. package/components/AnchorNav/AnchorNav.d.ts +33 -0
  10. package/components/AnchorNav/AnchorNav.js +82 -0
  11. package/components/AppLayout/AppLayout.js +1 -0
  12. package/components/AppSidebar/AppSidebar.js +1 -0
  13. package/components/Avatar/Avatar.js +1 -0
  14. package/components/Button/Button.js +1 -0
  15. package/components/Card/Card.js +1 -0
  16. package/components/Carousel/Carousel.js +1 -0
  17. package/components/ChatThread/ChatThread.js +1 -0
  18. package/components/Checkbox/Checkbox.js +1 -0
  19. package/components/CodeBlock/CodeBlock.js +1 -0
  20. package/components/CodeDiff/CodeDiff.css +175 -0
  21. package/components/CodeDiff/CodeDiff.d.ts +58 -0
  22. package/components/CodeDiff/CodeDiff.js +114 -0
  23. package/components/ColorPicker/ColorPicker.js +1 -0
  24. package/components/Combobox/Combobox.js +1 -0
  25. package/components/CommandPalette/CommandPalette.js +1 -0
  26. package/components/Composer/Composer.js +1 -0
  27. package/components/ContactCard/ContactCard.js +1 -0
  28. package/components/ContextMenu/ContextMenu.js +1 -0
  29. package/components/DataTable/DataTable.js +1 -0
  30. package/components/DateInput/DateInput.js +1 -0
  31. package/components/DatePicker/DatePicker.js +1 -0
  32. package/components/Dialog/Dialog.js +1 -0
  33. package/components/Drawer/Drawer.js +1 -0
  34. package/components/Dropdown/Dropdown.js +1 -0
  35. package/components/DropdownMenu/DropdownMenu.css +1 -1
  36. package/components/DropdownMenu/DropdownMenu.js +1 -0
  37. package/components/EventCalendar/EventCalendar.js +1 -0
  38. package/components/Field/Field.js +1 -0
  39. package/components/Field/FieldContext.js +1 -0
  40. package/components/FileInput/FileInput.js +1 -0
  41. package/components/Input/Input.js +1 -0
  42. package/components/InterruptCard/InterruptCard.css +11 -21
  43. package/components/InterruptCard/InterruptCard.d.ts +5 -6
  44. package/components/MessageCard/MessageCard.css +1 -2
  45. package/components/ModelPicker/ModelPicker.css +3 -1
  46. package/components/ModelPicker/ModelPicker.js +1 -0
  47. package/components/NavList/NavList.js +1 -0
  48. package/components/NotificationCenter/NotificationCenter.js +1 -0
  49. package/components/NumberInput/NumberInput.css +154 -0
  50. package/components/NumberInput/NumberInput.d.ts +44 -0
  51. package/components/NumberInput/NumberInput.js +147 -0
  52. package/components/Pagination/Pagination.js +1 -0
  53. package/components/PinInput/PinInput.css +92 -0
  54. package/components/PinInput/PinInput.d.ts +46 -0
  55. package/components/PinInput/PinInput.js +161 -0
  56. package/components/Popover/Popover.css +5 -6
  57. package/components/Popover/Popover.js +1 -0
  58. package/components/RadioButton/RadioButton.js +1 -0
  59. package/components/Reasoning/Reasoning.js +1 -0
  60. package/components/SegmentedControl/SegmentedControl.js +1 -0
  61. package/components/SelectionCard/SelectionCard.js +1 -0
  62. package/components/ShaderField/ShaderField.js +1 -0
  63. package/components/ShaderField/useShaderField.js +1 -0
  64. package/components/Slider/Slider.js +1 -0
  65. package/components/Sparkline/Sparkline.css +52 -0
  66. package/components/Sparkline/Sparkline.d.ts +49 -0
  67. package/components/Sparkline/Sparkline.js +86 -0
  68. package/components/Stepper/Stepper.css +207 -0
  69. package/components/Stepper/Stepper.d.ts +41 -0
  70. package/components/Stepper/Stepper.js +61 -0
  71. package/components/Tabs/Tabs.js +1 -0
  72. package/components/TagInput/TagInput.css +160 -0
  73. package/components/TagInput/TagInput.d.ts +43 -0
  74. package/components/TagInput/TagInput.js +137 -0
  75. package/components/Textarea/Textarea.js +1 -0
  76. package/components/TimePicker/TimePicker.css +230 -0
  77. package/components/TimePicker/TimePicker.d.ts +46 -0
  78. package/components/TimePicker/TimePicker.js +246 -0
  79. package/components/Toast/Toast.js +1 -0
  80. package/components/ToggleGroup/ToggleGroup.js +1 -0
  81. package/components/ToggleSwitch/ToggleSwitch.js +1 -0
  82. package/components/ToolCall/ToolCall.css +7 -11
  83. package/components/ToolCall/ToolCall.js +1 -0
  84. package/components/Tooltip/Tooltip.js +1 -0
  85. package/components/TreeView/TreeView.css +119 -0
  86. package/components/TreeView/TreeView.d.ts +45 -0
  87. package/components/TreeView/TreeView.js +232 -0
  88. package/components/registry.d.ts +22 -6
  89. package/components/registry.js +3 -1
  90. package/components/registry.json +206 -20
  91. package/components/registry.json.d.ts +206 -20
  92. package/components/registry.json.js +2 -2
  93. package/index.d.ts +11 -2
  94. package/index.js +21 -1
  95. package/package.json +1 -1
@@ -0,0 +1,92 @@
1
+ /* ============================================
2
+ PIN INPUT COMPONENT
3
+ Segmented one-time-code input
4
+ ============================================ */
5
+
6
+ /* Base — the Field wrapper carries the root class */
7
+ .ds-pininput {
8
+ display: flex;
9
+ flex-direction: column;
10
+ gap: var(--gap-sm); /* 8px */
11
+ }
12
+
13
+ /* ============================================
14
+ CELLS ROW
15
+ ============================================ */
16
+
17
+ .ds-pininput__cells {
18
+ display: flex;
19
+ gap: var(--gap-sm); /* 8px */
20
+ }
21
+
22
+ /* ============================================
23
+ CELL
24
+ ============================================ */
25
+
26
+ .ds-pininput__cell {
27
+ /* Fixed control geometry, like the ToggleSwitch track */
28
+ width: 44px;
29
+ height: 52px;
30
+ padding: 0;
31
+ text-align: center;
32
+
33
+ border: var(--border-xs) solid var(--color-input-border-primary);
34
+ border-radius: var(--radius-md); /* 12px */
35
+ background-color: var(--color-input-bg-primary);
36
+
37
+ /* Typography */
38
+ font-family: var(--font-paragraph-em-family);
39
+ font-size: var(--font-paragraph-em-size);
40
+ font-weight: var(--font-paragraph-em-weight);
41
+ line-height: var(--font-paragraph-em-line-height);
42
+ letter-spacing: var(--font-paragraph-em-letter-spacing);
43
+ color: var(--color-input-text-primary);
44
+
45
+ /* Transitions */
46
+ transition: border-color var(--motion-duration-base) var(--motion-ease-standard), background-color var(--motion-duration-base) var(--motion-ease-standard);
47
+ outline: none;
48
+ }
49
+
50
+ /* ============================================
51
+ STATES
52
+ ============================================ */
53
+
54
+ .ds-pininput__cell:hover:not(:focus):not(:disabled) {
55
+ border-color: var(--color-input-border-hover);
56
+ }
57
+
58
+ .ds-pininput__cell:focus {
59
+ border-color: var(--color-input-border-selected);
60
+ }
61
+
62
+ .ds-pininput__cell:focus-visible {
63
+ outline: 2px solid var(--color-action-primary-bg);
64
+ outline-offset: 2px;
65
+ }
66
+
67
+ /* ============================================
68
+ ERROR STATE
69
+ ============================================ */
70
+
71
+ .ds-pininput--error .ds-pininput__cell {
72
+ border-color: var(--color-status-error-border);
73
+ }
74
+
75
+ .ds-pininput--error .ds-pininput__cell:focus {
76
+ border-color: var(--color-status-error-border);
77
+ }
78
+
79
+ /* ============================================
80
+ DISABLED STATE
81
+ ============================================ */
82
+
83
+ .ds-pininput--disabled .ds-pininput__cells {
84
+ opacity: 0.4;
85
+ }
86
+
87
+ .ds-pininput--disabled .ds-pininput__cell {
88
+ background-color: var(--color-input-bg-disabled);
89
+ border-color: var(--color-input-border-disabled);
90
+ color: var(--color-input-text-disabled);
91
+ cursor: not-allowed;
92
+ }
@@ -0,0 +1,46 @@
1
+ import { default as React } from 'react';
2
+ /** Props owned by PinInput itself — everything else falls through to the group container. */
3
+ type PinInputOwnProps = {
4
+ /** Number of cells, one character each */
5
+ length?: number;
6
+ /** Current code (controlled) — character at index n renders in cell n */
7
+ value?: string;
8
+ /** Initial code (uncontrolled) */
9
+ defaultValue?: string;
10
+ /**
11
+ * Convenience callback receiving the joined code whenever any cell changes.
12
+ * There is no single native `onChange` here: the control is a group of
13
+ * one-character inputs, so the value callback is the primary API.
14
+ */
15
+ onValueChange?: (value: string) => void;
16
+ /** Fires once when every cell is filled, with the complete code */
17
+ onComplete?: (value: string) => void;
18
+ /** Render cells as password fields, hiding the entered characters */
19
+ mask?: boolean;
20
+ /** Accepted characters — `numeric` rejects non-digits and sets a numeric keyboard */
21
+ format?: 'numeric' | 'alphanumeric';
22
+ /** Label text rendered above the cells; also names the group for screen readers */
23
+ label?: string;
24
+ /** Helper or error message displayed below the cells */
25
+ helperText?: string;
26
+ /** Error state — recolours cell borders and the helper text */
27
+ error?: boolean;
28
+ /** Whether the whole control is disabled */
29
+ disabled?: boolean;
30
+ /** Additional CSS classes — applied to the wrapper, not the cells */
31
+ className?: string;
32
+ };
33
+ export interface PinInputProps extends PinInputOwnProps, Omit<React.ComponentPropsWithoutRef<'div'>, keyof PinInputOwnProps> {
34
+ }
35
+ /**
36
+ * Segmented one-time-code input: one cell per character, with auto-advance,
37
+ * Backspace retreat, arrow-key movement and paste distribution.
38
+ *
39
+ * The ref forwards to the FIRST cell's `<input>`, so `ref.current.focus()`
40
+ * focuses the start of the code. Unrecognised props spread onto the
41
+ * `role="group"` container `<div>` that wraps the cells — there is no single
42
+ * underlying input for them to land on — so `data-*` and `aria-*` attributes
43
+ * describe the group.
44
+ */
45
+ export declare const PinInput: React.ForwardRefExoticComponent<PinInputProps & React.RefAttributes<HTMLInputElement>>;
46
+ export {};
@@ -0,0 +1,161 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import React from "react";
4
+ import { Field } from "../Field/Field.js";
5
+ import "./PinInput.css";
6
+ const toChars = (value, length) => Array.from({ length }, (_, i) => value[i] ?? "");
7
+ const PinInput = React.forwardRef(
8
+ ({
9
+ length = 6,
10
+ value,
11
+ defaultValue,
12
+ onValueChange,
13
+ onComplete,
14
+ mask = false,
15
+ format = "numeric",
16
+ label,
17
+ helperText,
18
+ error = false,
19
+ disabled = false,
20
+ className = "",
21
+ id,
22
+ ...rest
23
+ }, ref) => {
24
+ const baseClass = "ds-pininput";
25
+ const generatedId = React.useId();
26
+ const controlId = id || generatedId;
27
+ const isControlled = value !== void 0;
28
+ const [internalChars, setInternalChars] = React.useState(
29
+ () => toChars(defaultValue ?? "", length)
30
+ );
31
+ const chars = isControlled ? toChars(value, length) : internalChars;
32
+ const cellRefs = React.useRef([]);
33
+ const setCellRef = (index) => (node) => {
34
+ cellRefs.current[index] = node;
35
+ if (index === 0) {
36
+ if (typeof ref === "function") ref(node);
37
+ else if (ref) ref.current = node;
38
+ }
39
+ };
40
+ const classes = [
41
+ baseClass,
42
+ error ? `${baseClass}--error` : "",
43
+ disabled ? `${baseClass}--disabled` : "",
44
+ className
45
+ ].filter(Boolean).join(" ");
46
+ const sanitize = (raw) => raw.replace(format === "numeric" ? /[^0-9]/g : /[^a-zA-Z0-9]/g, "");
47
+ const focusCell = (index) => {
48
+ const clamped = Math.max(0, Math.min(index, length - 1));
49
+ const cell = cellRefs.current[clamped];
50
+ cell?.focus();
51
+ cell?.select();
52
+ };
53
+ const commit = (next) => {
54
+ if (!isControlled) setInternalChars(next);
55
+ onValueChange?.(next.join(""));
56
+ };
57
+ const joined = chars.join("");
58
+ const complete = joined.length === length;
59
+ const wasComplete = React.useRef(complete);
60
+ React.useEffect(() => {
61
+ if (complete && !wasComplete.current) onComplete?.(joined);
62
+ wasComplete.current = complete;
63
+ }, [complete, joined, onComplete]);
64
+ const handleCellChange = (index) => (e) => {
65
+ const clean = sanitize(e.target.value);
66
+ if (e.target.value && !clean) return;
67
+ const next = [...chars];
68
+ if (clean.length <= 1) {
69
+ next[index] = clean;
70
+ commit(next);
71
+ if (clean) focusCell(index + 1);
72
+ } else {
73
+ let cursor = index;
74
+ for (const ch of clean) {
75
+ if (cursor >= length) break;
76
+ next[cursor] = ch;
77
+ cursor += 1;
78
+ }
79
+ commit(next);
80
+ focusCell(cursor);
81
+ }
82
+ };
83
+ const handleCellKeyDown = (index) => (e) => {
84
+ if (e.key === "Backspace") {
85
+ e.preventDefault();
86
+ const next = [...chars];
87
+ if (next[index]) {
88
+ next[index] = "";
89
+ commit(next);
90
+ focusCell(index - 1);
91
+ } else if (index > 0) {
92
+ next[index - 1] = "";
93
+ commit(next);
94
+ focusCell(index - 1);
95
+ }
96
+ } else if (e.key === "ArrowLeft") {
97
+ e.preventDefault();
98
+ focusCell(index - 1);
99
+ } else if (e.key === "ArrowRight") {
100
+ e.preventDefault();
101
+ focusCell(index + 1);
102
+ }
103
+ };
104
+ const handlePaste = (e) => {
105
+ e.preventDefault();
106
+ const clean = sanitize(e.clipboardData.getData("text")).slice(0, length);
107
+ if (!clean) return;
108
+ commit(toChars(clean, length));
109
+ focusCell(clean.length);
110
+ };
111
+ const describedBy = helperText ? `${controlId}-helper` : rest["aria-describedby"];
112
+ const characterNoun = format === "numeric" ? "Digit" : "Character";
113
+ return /* @__PURE__ */ jsx(
114
+ Field,
115
+ {
116
+ className: classes,
117
+ label,
118
+ helperText,
119
+ error,
120
+ disabled,
121
+ id: controlId,
122
+ children: /* @__PURE__ */ jsx(
123
+ "div",
124
+ {
125
+ ...rest,
126
+ role: "group",
127
+ "aria-labelledby": label ? `${controlId}-label` : void 0,
128
+ "aria-label": !label ? rest["aria-label"] : void 0,
129
+ "aria-describedby": describedBy,
130
+ className: `${baseClass}__cells`,
131
+ children: chars.map((char, index) => /* @__PURE__ */ jsx(
132
+ "input",
133
+ {
134
+ ref: setCellRef(index),
135
+ className: `${baseClass}__cell`,
136
+ type: mask ? "password" : "text",
137
+ inputMode: format === "numeric" ? "numeric" : "text",
138
+ autoComplete: index === 0 ? "one-time-code" : "off",
139
+ maxLength: 1,
140
+ value: char,
141
+ disabled,
142
+ id: index === 0 ? controlId : void 0,
143
+ "aria-label": `${characterNoun} ${index + 1} of ${length}`,
144
+ "aria-invalid": error || void 0,
145
+ onChange: handleCellChange(index),
146
+ onKeyDown: handleCellKeyDown(index),
147
+ onPaste: handlePaste,
148
+ onFocus: (e) => e.target.select()
149
+ },
150
+ index
151
+ ))
152
+ }
153
+ )
154
+ }
155
+ );
156
+ }
157
+ );
158
+ PinInput.displayName = "PinInput";
159
+ export {
160
+ PinInput
161
+ };
@@ -28,7 +28,9 @@
28
28
  padding: var(--padding-md); /* 16px */
29
29
  border: var(--border-xs) solid var(--color-bg-container-border);
30
30
  border-radius: var(--radius-sm); /* 8px */
31
- background-color: var(--color-bg-container-primary);
31
+ /* Page background, not container: the container fill is semi-transparent
32
+ in dark mode, and a floating panel must not show text through itself */
33
+ background-color: var(--color-bg-page-primary);
32
34
  box-shadow: var(--shadow-floating);
33
35
 
34
36
  /* Hidden by default */
@@ -88,12 +90,9 @@
88
90
  SIZE — COMPACT
89
91
  ============================================ */
90
92
 
93
+ /* The size class rides on the panel element itself, so this is a plain
94
+ compound selector — a descendant form would never match */
91
95
  .ds-popover--compact {
92
96
  padding: var(--padding-sm); /* 8px */
93
97
  min-width: 160px;
94
98
  }
95
-
96
- .ds-popover--compact .ds-popover__panel {
97
- padding: var(--padding-sm);
98
- min-width: 160px;
99
- }
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsxs, jsx } from "react/jsx-runtime";
2
3
  import React, { useState, useRef, useCallback, useEffect } from "react";
3
4
  import "./Popover.css";
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsxs, jsx } from "react/jsx-runtime";
2
3
  import React from "react";
3
4
  import "./RadioButton.css";
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx, jsxs } from "react/jsx-runtime";
2
3
  import React, { useId, useState, useRef, useEffect, useCallback } from "react";
3
4
  import "./Reasoning.css";
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx, jsxs } from "react/jsx-runtime";
2
3
  import { useRef } from "react";
3
4
  import "./SegmentedControl.css";
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx, jsxs } from "react/jsx-runtime";
2
3
  import React from "react";
3
4
  import "./SelectionCard.css";
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx } from "react/jsx-runtime";
2
3
  import React, { useRef, useMemo, useState, useEffect } from "react";
3
4
  import { DEFAULT_SHADER_BLOBS, DEFAULT_SHADER_PARAMS, useShaderField } from "./useShaderField.js";
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { useState, useRef, useEffect } from "react";
2
3
  import { BLOB_COUNT, vertexSource, fragmentSource } from "./field.glsl.js";
3
4
  const DEFAULT_SHADER_PARAMS = {
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx } from "react/jsx-runtime";
2
3
  import React from "react";
3
4
  import "./Slider.css";
@@ -0,0 +1,52 @@
1
+ /* ============================================
2
+ SPARKLINE COMPONENT
3
+ Inline trend line for stats and table cells.
4
+ Colour flows through `color` — the line, fill
5
+ and dot all draw with currentColor, so a tone
6
+ is one custom property swap.
7
+ ============================================ */
8
+
9
+ /* Base */
10
+
11
+ .ds-sparkline {
12
+ display: inline-block;
13
+ vertical-align: middle;
14
+ flex-shrink: 0;
15
+ color: var(--color-action-primary-bg);
16
+ }
17
+
18
+ /* Parts */
19
+
20
+ .ds-sparkline__line {
21
+ fill: none;
22
+ stroke: currentColor;
23
+ stroke-linecap: round;
24
+ stroke-linejoin: round;
25
+ }
26
+
27
+ .ds-sparkline__fill {
28
+ fill: currentColor;
29
+ opacity: 0.15;
30
+ }
31
+
32
+ .ds-sparkline__dot {
33
+ fill: currentColor;
34
+ }
35
+
36
+ /* Tones */
37
+
38
+ .ds-sparkline--accent {
39
+ color: var(--color-action-primary-bg);
40
+ }
41
+
42
+ .ds-sparkline--positive {
43
+ color: var(--color-status-positive-text);
44
+ }
45
+
46
+ .ds-sparkline--negative {
47
+ color: var(--color-status-error-text);
48
+ }
49
+
50
+ .ds-sparkline--neutral {
51
+ color: var(--color-text-secondary);
52
+ }
@@ -0,0 +1,49 @@
1
+ import { default as React } from 'react';
2
+ /** Props owned by Sparkline itself — everything else falls through to the SVG node. */
3
+ type SparklineOwnProps = {
4
+ /** The series to plot, in order. Values are normalised into the viewBox. */
5
+ data: number[];
6
+ /** Rendering treatment — a bare line, or a line with a soft fill underneath. */
7
+ variant?: 'line' | 'area';
8
+ /**
9
+ * Colour role for the line and end dot. `accent` follows the chart palette's
10
+ * lead colour; `positive`/`negative` map to the status text tokens; `neutral`
11
+ * recedes to secondary text.
12
+ */
13
+ tone?: 'accent' | 'positive' | 'negative' | 'neutral';
14
+ /** Marks the final point with a small dot. */
15
+ showDot?: boolean;
16
+ /** Line thickness in viewBox units — an SVG geometry attribute, not a token. */
17
+ strokeWidth?: number;
18
+ /**
19
+ * ViewBox width. Also the default rendered width; the SVG scales to its
20
+ * container when sized externally via CSS. Intentionally shadows the native
21
+ * SVG `width` attribute, which it sets.
22
+ */
23
+ width?: number;
24
+ /**
25
+ * ViewBox height. Also the default rendered height; the SVG scales to its
26
+ * container when sized externally via CSS. Intentionally shadows the native
27
+ * SVG `height` attribute, which it sets.
28
+ */
29
+ height?: number;
30
+ /**
31
+ * Accessible description of the trend, e.g. "Revenue, trending up". When
32
+ * omitted the SVG is decorative and hidden from assistive technology.
33
+ */
34
+ label?: string;
35
+ /** Additional CSS classes */
36
+ className?: string;
37
+ };
38
+ export interface SparklineProps extends SparklineOwnProps, Omit<React.ComponentPropsWithoutRef<'svg'>, keyof SparklineOwnProps | 'children'> {
39
+ }
40
+ /**
41
+ * Sparkline — an inline trend line for stats and table cells, drawn without
42
+ * axes or chrome. Pure SVG computed from props: no charting library, no hooks,
43
+ * no dependencies, so it renders from a Server Component and costs nothing in
44
+ * a dense table. Degenerate data never breaks the path: an all-equal series
45
+ * renders a horizontal midline, and fewer than two points render just the
46
+ * end dot, or nothing.
47
+ */
48
+ export declare const Sparkline: React.ForwardRefExoticComponent<SparklineProps & React.RefAttributes<SVGSVGElement>>;
49
+ export {};
@@ -0,0 +1,86 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import "./Sparkline.css";
4
+ const round = (n) => Math.round(n * 100) / 100;
5
+ const Sparkline = React.forwardRef(
6
+ ({
7
+ data,
8
+ variant = "line",
9
+ tone = "accent",
10
+ showDot = true,
11
+ strokeWidth = 2,
12
+ width = 120,
13
+ height = 32,
14
+ label,
15
+ className = "",
16
+ ...rest
17
+ }, ref) => {
18
+ const baseClass = "ds-sparkline";
19
+ const classes = [
20
+ baseClass,
21
+ `${baseClass}--${variant}`,
22
+ `${baseClass}--${tone}`,
23
+ className
24
+ ].filter(Boolean).join(" ");
25
+ const dotRadius = strokeWidth * 1.5;
26
+ const pad = dotRadius + strokeWidth / 2;
27
+ const innerWidth = Math.max(width - pad * 2, 0);
28
+ const innerHeight = Math.max(height - pad * 2, 0);
29
+ const min = Math.min(...data);
30
+ const max = Math.max(...data);
31
+ const range = max - min;
32
+ const points = data.map((value, i) => ({
33
+ x: round(
34
+ data.length < 2 ? pad + innerWidth : pad + i / (data.length - 1) * innerWidth
35
+ ),
36
+ // A flat series (range 0) draws a horizontal midline instead of dividing by zero.
37
+ y: round(
38
+ range === 0 ? pad + innerHeight / 2 : pad + (1 - (value - min) / range) * innerHeight
39
+ )
40
+ }));
41
+ const lastPoint = points.length > 0 ? points[points.length - 1] : void 0;
42
+ const hasLine = points.length >= 2;
43
+ const linePath = hasLine ? `M ${points.map((p) => `${p.x} ${p.y}`).join(" L ")}` : void 0;
44
+ const baseline = round(height - pad);
45
+ const areaPath = hasLine && variant === "area" ? `${linePath} L ${points[points.length - 1].x} ${baseline} L ${points[0].x} ${baseline} Z` : void 0;
46
+ return /* @__PURE__ */ jsxs(
47
+ "svg",
48
+ {
49
+ ...rest,
50
+ ref,
51
+ className: classes,
52
+ viewBox: `0 0 ${width} ${height}`,
53
+ width,
54
+ height,
55
+ xmlns: "http://www.w3.org/2000/svg",
56
+ role: label ? "img" : void 0,
57
+ "aria-label": label,
58
+ "aria-hidden": label ? void 0 : true,
59
+ children: [
60
+ areaPath && /* @__PURE__ */ jsx("path", { className: `${baseClass}__fill`, d: areaPath }),
61
+ linePath && /* @__PURE__ */ jsx(
62
+ "path",
63
+ {
64
+ className: `${baseClass}__line`,
65
+ d: linePath,
66
+ strokeWidth
67
+ }
68
+ ),
69
+ showDot && lastPoint && /* @__PURE__ */ jsx(
70
+ "circle",
71
+ {
72
+ className: `${baseClass}__dot`,
73
+ cx: lastPoint.x,
74
+ cy: lastPoint.y,
75
+ r: dotRadius
76
+ }
77
+ )
78
+ ]
79
+ }
80
+ );
81
+ }
82
+ );
83
+ Sparkline.displayName = "Sparkline";
84
+ export {
85
+ Sparkline
86
+ };