@sentropic/design-system-react 0.3.0 → 0.4.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 (44) hide show
  1. package/dist/AreaChart.d.ts +16 -2
  2. package/dist/AreaChart.d.ts.map +1 -1
  3. package/dist/AreaChart.js +96 -1
  4. package/dist/AreaChart.js.map +1 -1
  5. package/dist/BarChart.d.ts +16 -2
  6. package/dist/BarChart.d.ts.map +1 -1
  7. package/dist/BarChart.js +90 -1
  8. package/dist/BarChart.js.map +1 -1
  9. package/dist/DataTable.d.ts +49 -2
  10. package/dist/DataTable.d.ts.map +1 -1
  11. package/dist/DataTable.js +144 -1
  12. package/dist/DataTable.js.map +1 -1
  13. package/dist/DonutChart.d.ts +19 -2
  14. package/dist/DonutChart.d.ts.map +1 -1
  15. package/dist/DonutChart.js +61 -1
  16. package/dist/DonutChart.js.map +1 -1
  17. package/dist/LineChart.d.ts +17 -2
  18. package/dist/LineChart.d.ts.map +1 -1
  19. package/dist/LineChart.js +92 -1
  20. package/dist/LineChart.js.map +1 -1
  21. package/dist/ScatterPlot.d.ts +19 -2
  22. package/dist/ScatterPlot.d.ts.map +1 -1
  23. package/dist/ScatterPlot.js +55 -1
  24. package/dist/ScatterPlot.js.map +1 -1
  25. package/dist/Sparkline.d.ts +13 -2
  26. package/dist/Sparkline.d.ts.map +1 -1
  27. package/dist/Sparkline.js +32 -1
  28. package/dist/Sparkline.js.map +1 -1
  29. package/dist/StackedBarChart.d.ts +20 -2
  30. package/dist/StackedBarChart.d.ts.map +1 -1
  31. package/dist/StackedBarChart.js +78 -1
  32. package/dist/StackedBarChart.js.map +1 -1
  33. package/dist/catalog.d.ts +41 -95
  34. package/dist/catalog.d.ts.map +1 -1
  35. package/dist/catalog.js +61 -78
  36. package/dist/catalog.js.map +1 -1
  37. package/dist/chartScale.d.ts +25 -0
  38. package/dist/chartScale.d.ts.map +1 -0
  39. package/dist/chartScale.js +71 -0
  40. package/dist/chartScale.js.map +1 -0
  41. package/dist/index.d.ts +1 -1
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/styles.css +24 -0
  44. package/package.json +1 -1
package/dist/catalog.js CHANGED
@@ -1,6 +1,14 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import React from "react";
3
3
  import { classNames } from "./classNames.js";
4
+ // Normalises an action item so the rest of the component only deals with the
5
+ // React-native shape: `id` (from id|value) and `variant` (from variant|danger).
6
+ function actionId(item, index, prefix = "item") {
7
+ return item.id ?? item.value ?? (index === undefined ? undefined : `${prefix}-${index}`);
8
+ }
9
+ function isDangerAction(item) {
10
+ return item.variant === "danger" || item.danger === true;
11
+ }
4
12
  const DATA_TONES = [
5
13
  "category1",
6
14
  "category2",
@@ -137,21 +145,12 @@ function pct(value, min = 0, max = 100) {
137
145
  return 0;
138
146
  return clamp(((value - min) / (max - min)) * 100, 0, 100);
139
147
  }
140
- function pointsFrom(values, width, height) {
141
- const ys = values.map((entry) => (typeof entry === "number" ? entry : entry.y ?? entry.value ?? 0));
142
- const max = Math.max(...ys, 1);
143
- return ys
144
- .map((value, index) => {
145
- const x = ys.length === 1 ? width / 2 : (index / (ys.length - 1)) * width;
146
- const y = height - (value / max) * height;
147
- return `${x},${y}`;
148
- })
149
- .join(" ");
150
- }
151
- export function Accordion({ items, openIds, defaultOpenIds = [], allowMultiple = true, onChange, className, ...rest }) {
152
- const [open, setOpen] = useControlled(openIds, defaultOpenIds, onChange);
148
+ export function Accordion({ items, openIds, defaultOpenIds, open: openAlias, allowMultiple, multiple, onChange, className, ...rest }) {
149
+ const initialOpen = defaultOpenIds ?? openAlias ?? [];
150
+ const resolvedAllowMultiple = allowMultiple ?? multiple ?? true;
151
+ const [open, setOpen] = useControlled(openIds, initialOpen, onChange);
153
152
  const toggle = (id) => {
154
- const next = open.includes(id) ? open.filter((value) => value !== id) : allowMultiple ? [...open, id] : [id];
153
+ const next = open.includes(id) ? open.filter((value) => value !== id) : resolvedAllowMultiple ? [...open, id] : [id];
155
154
  setOpen(next);
156
155
  };
157
156
  return (_jsx("div", { ...rest, className: classNames("st-accordion", className), children: items.map((item, index) => {
@@ -165,46 +164,6 @@ export function Accordion({ items, openIds, defaultOpenIds = [], allowMultiple =
165
164
  export function Alert({ tone = "info", title, message, actions, children, className, ...rest }) {
166
165
  return (_jsxs("section", { ...rest, className: classNames("st-alert", `st-alert--${tone}`, className), role: tone === "warning" || tone === "error" ? "alert" : "status", children: [_jsxs("div", { className: "st-alert__content", children: [_jsx("h2", { className: "st-alert__title", children: title }), message ? _jsx("p", { className: "st-alert__message", children: message }) : null, children] }), actions ? _jsx("div", { className: "st-alert__actions", children: actions }) : null] }));
167
166
  }
168
- function LinearChart({ data, label, width = 320, height = 160, className, type }) {
169
- const classBase = type === "areaChart" ? "st-areaChart" : "st-lineChart";
170
- const points = pointsFrom(data, width, height);
171
- const accessibleLabel = label ?? (type === "areaChart" ? "Area chart" : "Line chart");
172
- return (_jsxs("figure", { className: classNames(classBase, className), "aria-label": accessibleLabel, children: [_jsx("span", { className: "st-visually-hidden", children: accessibleLabel }), _jsxs("svg", { viewBox: `0 0 `, "aria-hidden": "true", children: [_jsx("polyline", { className: `${classBase}__line`, points: points, fill: "none" }), type === "areaChart" ? _jsx("polygon", { className: "st-areaChart__area", points: `0,${height} ${points} ${width},${height}` }) : null, data.map((datum, index) => (_jsx("circle", { className: `${classBase}__dot`, cx: points.split(" ")[index]?.split(",")[0], cy: points.split(" ")[index]?.split(",")[1], r: "4" }, index)))] })] }));
173
- }
174
- export function AreaChart(props) {
175
- return _jsx(LinearChart, { ...props, type: "areaChart" });
176
- }
177
- export function LineChart(props) {
178
- return _jsx(LinearChart, { ...props, type: "lineChart" });
179
- }
180
- export function BarChart({ data, label = "Bar chart", width = 320, height = 160, className, ...rest }) {
181
- const max = Math.max(...data.map((datum) => datum.value ?? datum.y ?? 0), 1);
182
- return (_jsxs("figure", { ...rest, className: classNames("st-barChart", className), "aria-label": label, children: [_jsx("span", { className: "st-visually-hidden", children: label }), _jsx("svg", { viewBox: `0 0 `, "aria-hidden": "true", children: data.map((datum, index) => {
183
- const value = datum.value ?? datum.y ?? 0;
184
- const barWidth = width / Math.max(data.length, 1) - 8;
185
- const barHeight = (value / max) * height;
186
- return (_jsx("rect", { className: classNames("st-barChart__bar", `st-barChart__bar--${datum.tone ?? DATA_TONES[index % DATA_TONES.length]}`), x: index * (barWidth + 8) + 4, y: height - barHeight, width: barWidth, height: barHeight }, index));
187
- }) })] }));
188
- }
189
- export function DonutChart({ data, label = "Donut chart", className, ...rest }) {
190
- const total = data.reduce((sum, datum) => sum + (datum.value ?? datum.y ?? 0), 0);
191
- return (_jsxs("figure", { ...rest, className: classNames("st-donutChart", className), "aria-label": label, children: [_jsx("span", { className: "st-visually-hidden", children: label }), _jsxs("svg", { viewBox: "0 0 120 120", "aria-hidden": "true", children: [data.map((datum, index) => (_jsx("circle", { className: classNames("st-donutChart__slice", `st-donutChart__slice--${datum.tone ?? DATA_TONES[index % DATA_TONES.length]}`), cx: "60", cy: "60", r: 36 - index * 3, fill: "none", strokeWidth: "8" }, index))), _jsx("text", { className: "st-donutChart__center", x: "60", y: "64", textAnchor: "middle", children: total })] })] }));
192
- }
193
- export function ScatterPlot({ data, label = "Scatter chart", className, ...rest }) {
194
- return (_jsxs("figure", { ...rest, className: classNames("st-scatterPlot", className), "aria-label": label, children: [_jsx("span", { className: "st-visually-hidden", children: label }), _jsx("svg", { viewBox: "0 0 320 160", "aria-hidden": "true", children: data.map((datum, index) => (_jsx("circle", { className: classNames("st-scatterPlot__point", `st-scatterPlot__point--${datum.tone ?? DATA_TONES[index % DATA_TONES.length]}`), cx: clamp(datum.x * 24 + 24, 12, 308), cy: clamp(148 - datum.y * 24, 12, 148), r: "5", children: _jsx("title", { children: datum.label ?? `${datum.x}, ${datum.y}` }) }, index))) })] }));
195
- }
196
- export function StackedBarChart({ data, label = "Stacked bar chart", className, ...rest }) {
197
- return (_jsxs("figure", { ...rest, className: classNames("st-stackedBar", className), "aria-label": label, children: [_jsx("span", { className: "st-visually-hidden", children: label }), _jsx("svg", { viewBox: "0 0 320 160", "aria-hidden": "true", children: data.map((bar, barIndex) => {
198
- let x = 16;
199
- const total = Math.max(bar.segments.reduce((sum, segment) => sum + segment.value, 0), 1);
200
- return (_jsxs("g", { transform: `translate(0 ${barIndex * 32 + 16})`, children: [bar.segments.map((segment, index) => {
201
- const width = (segment.value / total) * 220;
202
- const rect = (_jsx("rect", { className: classNames("st-stackedBar__seg", `st-stackedBar__seg--${segment.tone ?? DATA_TONES[index % DATA_TONES.length]}`), x: x, y: "0", width: width, height: "20" }, segment.label));
203
- x += width;
204
- return rect;
205
- }), _jsx("text", { className: "st-stackedBar__categoryLabel", x: "250", y: "15", children: bar.label })] }, bar.label));
206
- }) })] }));
207
- }
208
167
  export function AspectRatio({ ratio = "16 / 9", className, style, children, ...rest }) {
209
168
  const aspectRatio = typeof ratio === "number" ? String(ratio) : ratio;
210
169
  return (_jsx("div", { ...rest, className: classNames("st-aspectRatio", className), style: { aspectRatio, ...style }, children: children }));
@@ -292,11 +251,12 @@ export function Combobox({ label, options, value, size = "md", placeholder = "Se
292
251
  selectOption(option);
293
252
  }, children: option.label }, option.value)))) : (_jsx("li", { className: "st-combobox__empty", role: "option", "aria-disabled": "true", "aria-selected": "false", children: noResultsLabel })) })) : null] }));
294
253
  }
295
- export function ContentSwitcher({ items, value, activeId, onChange, size = "md", className, ...rest }) {
254
+ export function ContentSwitcher({ items, value, activeId, onChange, onchange, size = "md", className, ...rest }) {
255
+ const handleChange = onChange ?? onchange;
296
256
  const current = value ?? activeId ?? idFrom(items[0] ?? {}, 0, "content");
297
257
  return (_jsx("div", { ...rest, className: classNames("st-contentSwitcher", `st-contentSwitcher--${size}`, className), role: "group", children: items.map((item, index) => {
298
258
  const itemId = idFrom(item, index, "content");
299
- return (_jsx("button", { type: "button", className: classNames("st-contentSwitcher__option st-contentSwitcher__button", itemId === current && "st-contentSwitcher__option--selected"), disabled: item.disabled, "aria-pressed": itemId === current, onClick: () => onChange?.(itemId), children: item.label }, itemId));
259
+ return (_jsx("button", { type: "button", className: classNames("st-contentSwitcher__option st-contentSwitcher__button", itemId === current && "st-contentSwitcher__option--selected"), disabled: item.disabled, "aria-pressed": itemId === current, onClick: () => handleChange?.(itemId), children: item.label }, itemId));
300
260
  }) }));
301
261
  }
302
262
  export function CopyButton({ text: copyText, value, label = "Copy", copiedLabel = "Copied", size = "md", className, onClick, ...rest }) {
@@ -307,11 +267,6 @@ export function CopyButton({ text: copyText, value, label = "Copy", copiedLabel
307
267
  onClick?.(event);
308
268
  }, children: _jsx("span", { className: "st-copyButton__label", children: copied ? copiedLabel : label }) }));
309
269
  }
310
- export function DataTable({ columns, rows, caption, size = "md", className, pageSize, page = 1, totalItems, ...rest }) {
311
- const visibleRows = pageSize ? rows.slice((page - 1) * pageSize, page * pageSize) : rows;
312
- const total = totalItems ?? rows.length;
313
- return (_jsxs("div", { className: "st-dataTable-wrap", children: [_jsxs("table", { ...rest, className: classNames("st-dataTable", `st-dataTable--${size}`, className), children: [caption ? _jsx("caption", { children: caption }) : null, _jsx("thead", { children: _jsx("tr", { children: columns.map((column) => _jsx("th", { children: column.label }, column.key)) }) }), _jsx("tbody", { children: visibleRows.map((row) => (_jsx("tr", { children: columns.map((column) => (_jsx("td", { className: classNames(column.align === "center" && "st-dataTable__cell--center", column.align === "end" && "st-dataTable__cell--end"), children: column.render?.(row, column) ?? text(row[column.key]) }, column.key))) }, row.id))) })] }), pageSize ? _jsx("div", { className: "st-dataTable__pagerStatus", children: `${(page - 1) * pageSize + 1}-${Math.min(page * pageSize, total)} of ${total}` }) : null] }));
314
- }
315
270
  export function DatePicker({ label, value, size = "md", className, ...rest }) {
316
271
  return (_jsx("div", { ...rest, className: classNames("st-datepicker", `st-datepicker--${size}`, className), children: _jsx(Field, { label: label, children: (inputId) => _jsx("input", { id: inputId, className: "st-control st-datepicker__control", type: "date", defaultValue: value }) }) }));
317
272
  }
@@ -338,7 +293,8 @@ export function Drawer({ open = false, title, description, footer, placement = "
338
293
  onClose?.();
339
294
  }, children: _jsxs("aside", { ...rest, ref: panelRef, className: classNames("st-drawer", `st-drawer--${placement}`, className), role: "dialog", "aria-modal": "true", "aria-label": text(title) || "Drawer", tabIndex: -1, onKeyDown: (event) => trapTabKey(event, panelRef.current), children: [_jsxs("div", { className: "st-drawer__header", children: [title ? _jsx("h2", { className: "st-drawer__title", children: title }) : null, _jsx("button", { ref: closeRef, type: "button", className: "st-drawer__close", onClick: onClose, "aria-label": "Close", children: "x" })] }), description ? _jsx("p", { className: "st-drawer__description", children: description }) : null, _jsx("div", { className: "st-drawer__body", children: children }), footer ? _jsx("div", { className: "st-drawer__footer", children: footer }) : null] }) }));
340
295
  }
341
- export function Dropdown({ label = "Select", options, value, open: controlledOpen, placeholder = "Select", onSelect, className, ...rest }) {
296
+ export function Dropdown({ label = "Select", options, value, open: controlledOpen, placeholder = "Select", onSelect, onselect, className, ...rest }) {
297
+ const handleSelect = onSelect ?? onselect;
342
298
  const hostRef = React.useRef(null);
343
299
  const itemRefs = React.useRef([]);
344
300
  const [open, setOpen] = useControlled(controlledOpen, false);
@@ -358,7 +314,7 @@ export function Dropdown({ label = "Select", options, value, open: controlledOpe
358
314
  setCurrent(option.value);
359
315
  setOpen(false);
360
316
  setActiveIndex(-1);
361
- onSelect?.(option.value);
317
+ handleSelect?.(option.value);
362
318
  };
363
319
  useOutsideMouseDown(open, hostRef, () => setOpen(false));
364
320
  useEscape(open, () => setOpen(false));
@@ -392,8 +348,30 @@ export function Dropdown({ label = "Select", options, value, open: controlledOpe
392
348
  export function EmptyState({ title, message, action, children, className, ...rest }) {
393
349
  return (_jsx("section", { ...rest, className: classNames("st-empty-state st-emptyState", className), children: _jsxs("div", { className: "st-empty-state__content", children: [_jsx("h2", { className: "st-empty-state__title st-emptyState__title", children: title }), message ? _jsx("p", { className: "st-empty-state__message st-emptyState__message", children: message }) : null, children, action ? _jsx("div", { className: "st-empty-state__action", children: action }) : null] }) }));
394
350
  }
351
+ function formatFileSize(bytes) {
352
+ if (typeof bytes !== "number" || !Number.isFinite(bytes) || bytes < 0)
353
+ return "";
354
+ if (bytes === 0)
355
+ return "0 B";
356
+ const units = ["B", "KB", "MB", "GB", "TB"];
357
+ const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
358
+ const value = bytes / Math.pow(1024, i);
359
+ const formatted = value >= 10 || i === 0 ? value.toFixed(0) : value.toFixed(1);
360
+ return `${formatted} ${units[i]}`;
361
+ }
362
+ function fileItemName(item) {
363
+ return item.file?.name ?? item.name;
364
+ }
365
+ function fileItemSize(item) {
366
+ return item.file?.size ?? item.size;
367
+ }
395
368
  export function FileUploader({ label = "Upload", items = [], disabled = false, className, ...rest }) {
396
- return (_jsxs("div", { ...rest, className: classNames("st-fileUploader-field", className), children: [_jsxs("div", { className: classNames("st-fileUploader__dropzone", disabled && "st-fileUploader__dropzone--disabled"), children: [_jsx("span", { className: "st-fileUploader__trigger", children: label }), _jsx("input", { className: "st-fileUploader__input", type: "file", disabled: disabled, "aria-label": text(label) })] }), _jsx("ul", { className: "st-fileUploader__list", children: items.map((item, index) => (_jsxs("li", { className: classNames("st-fileUploader__item", item.status && `st-fileUploader__item--${item.status}`), children: [_jsx("span", { className: "st-fileUploader__itemName st-fileUploader__name", children: item.name }), item.error ? _jsx("span", { className: "st-fileUploader__itemError", children: item.error }) : null] }, item.id ?? item.name ?? index))) })] }));
369
+ return (_jsxs("div", { ...rest, className: classNames("st-fileUploader-field", className), children: [_jsxs("div", { className: classNames("st-fileUploader__dropzone", disabled && "st-fileUploader__dropzone--disabled"), children: [_jsx("span", { className: "st-fileUploader__trigger", children: label }), _jsx("input", { className: "st-fileUploader__input", type: "file", disabled: disabled, "aria-label": text(label) })] }), _jsx("ul", { className: "st-fileUploader__list", children: items.map((item, index) => {
370
+ const name = fileItemName(item);
371
+ const size = fileItemSize(item);
372
+ const sizeLabel = formatFileSize(size);
373
+ return (_jsxs("li", { className: classNames("st-fileUploader__item", item.status && `st-fileUploader__item--${item.status}`), children: [_jsx("span", { className: "st-fileUploader__itemName st-fileUploader__name", children: name }), sizeLabel ? _jsx("span", { className: "st-fileUploader__itemSize", children: sizeLabel }) : null, item.error ? _jsx("span", { className: "st-fileUploader__itemError", children: item.error }) : null] }, item.id ?? name ?? index));
374
+ }) })] }));
397
375
  }
398
376
  export function Footer({ brand, columns, links, copyright, className, ...rest }) {
399
377
  const groups = columns ?? (links ? [{ links }] : []);
@@ -805,11 +783,15 @@ export function Link({ muted = false, standalone = false, disabled = false, clas
805
783
  export function LoadingState({ label, title, variant = "spinner", className, ...rest }) {
806
784
  return (_jsxs("section", { ...rest, className: classNames("st-loading", `st-loading--${variant}`, className), "aria-live": "polite", children: [_jsx("span", { className: "st-loading__spinner", "aria-hidden": "true" }), _jsx("span", { className: "st-loading__label", children: label ?? title ?? "Loading" })] }));
807
785
  }
786
+ function itemKind(item) {
787
+ const tagged = item;
788
+ return tagged.type ?? tagged.kind;
789
+ }
808
790
  function isDivider(item) {
809
- return "type" in item && item.type === "divider";
791
+ return itemKind(item) === "divider";
810
792
  }
811
793
  function isGroup(item) {
812
- return "type" in item && item.type === "group";
794
+ return itemKind(item) === "group";
813
795
  }
814
796
  export function Menu({ items, dense = false, onSelect, className, role, ...rest }) {
815
797
  const rootRef = React.useRef(null);
@@ -843,19 +825,21 @@ export function Menu({ items, dense = false, onSelect, className, role, ...rest
843
825
  if (isDivider(item))
844
826
  return _jsx("div", { className: "st-menu__divider", role: "separator" }, item.id ?? index);
845
827
  if (isGroup(item)) {
846
- return (_jsxs("section", { className: "st-menu__group", children: [_jsx("h3", { children: item.label }), item.items.map((child) => (_jsx("button", { type: "button", role: "menuitem", className: "st-menu__item", disabled: child.disabled, onClick: () => onSelect?.(child), onKeyDown: (event) => handleItemKeyDown(event, child), children: _jsx("span", { className: "st-menu__itemLabel", children: child.label }) }, child.id ?? text(child.label))))] }, item.id ?? index));
828
+ return (_jsxs("section", { className: "st-menu__group", children: [_jsx("h3", { children: item.label }), (item.items ?? []).map((child) => (_jsx("button", { type: "button", role: "menuitem", className: classNames("st-menu__item", isDangerAction(child) && "st-menu__item--danger"), disabled: child.disabled, onClick: () => onSelect?.(child), onKeyDown: (event) => handleItemKeyDown(event, child), children: _jsx("span", { className: "st-menu__itemLabel", children: child.label }) }, actionId(child) ?? text(child.label))))] }, item.id ?? index));
847
829
  }
848
- return (_jsx("button", { type: "button", role: "menuitem", disabled: item.disabled, className: classNames("st-menu__item", item.variant === "danger" && "st-menu__item--danger"), onClick: () => onSelect?.(item), onKeyDown: (event) => handleItemKeyDown(event, item), children: _jsx("span", { className: "st-menu__itemLabel", children: item.label }) }, item.id ?? text(item.label) ?? index));
830
+ const action = item;
831
+ return (_jsx("button", { type: "button", role: "menuitem", disabled: action.disabled, className: classNames("st-menu__item", isDangerAction(action) && "st-menu__item--danger"), onClick: () => onSelect?.(action), onKeyDown: (event) => handleItemKeyDown(event, action), children: _jsx("span", { className: "st-menu__itemLabel", children: action.label }) }, actionId(action) ?? text(action.label) ?? index));
849
832
  }) }));
850
833
  }
851
834
  export function MenuPopover({ trigger, items = [], open = true, placement = "bottom-start", children, className, ...rest }) {
852
835
  return (_jsxs("div", { ...rest, className: classNames("st-menuPopover", `st-menuPopover--${placement}`, className), children: [trigger, open ? _jsx("div", { className: "st-menuPopover__content", children: items.length ? _jsx(Menu, { items: items, role: "presentation" }) : children }) : null] }));
853
836
  }
854
- export function MenuTriggerButton({ open = false, type = "button", className, children, ...rest }) {
855
- return (_jsx("button", { ...rest, type: type, className: classNames("st-menuTriggerButton st-button st-button--secondary st-button--sm", className), "aria-expanded": open, children: children }));
837
+ export function MenuTriggerButton({ open, expanded, type = "button", className, children, ...rest }) {
838
+ const isOpen = open ?? expanded ?? false;
839
+ return (_jsx("button", { ...rest, type: type, className: classNames("st-menuTriggerButton st-button st-button--secondary st-button--sm", className), "aria-expanded": isOpen, children: children }));
856
840
  }
857
841
  export function MessageActions({ actions, visibility = "always", className, ...rest }) {
858
- return (_jsx("nav", { ...rest, className: classNames("st-messageActions", visibility === "hover" && "st-messageActions--hoverOnly", className), "aria-label": "Message actions", children: actions.map((action, index) => (_jsx("button", { type: "button", className: classNames("st-button st-button--ghost st-button--sm", action.variant === "danger" && "st-button--danger"), disabled: action.disabled, onClick: action.onClick, children: action.label }, action.id ?? index))) }));
842
+ return (_jsx("nav", { ...rest, className: classNames("st-messageActions", visibility === "hover" && "st-messageActions--hoverOnly", className), "aria-label": "Message actions", children: actions.map((action, index) => (_jsx("button", { type: "button", className: classNames("st-button st-button--ghost st-button--sm", action.variant === "danger" && "st-button--danger"), disabled: action.disabled, "aria-label": action.label == null && action.icon != null ? text(action.id) || undefined : undefined, onClick: action.onClick, children: action.label ?? action.icon }, action.id ?? index))) }));
859
843
  }
860
844
  export function MessageStatusBadge({ status, tone, labels, className, ...rest }) {
861
845
  const normalized = status === "sent" ? "completed" : status === "streaming" ? "processing" : status === "error" ? "failed" : status;
@@ -930,8 +914,9 @@ export function Pagination({ page, pageSize = 10, totalItems, totalPages, pageCo
930
914
  const visiblePages = Array.from({ length: pages }, (_, index) => index + 1);
931
915
  return (_jsxs("nav", { ...rest, className: classNames("st-pagination", className), "aria-label": "Pagination", children: [_jsx("button", { type: "button", disabled: page <= 1, onClick: () => onPageChange?.(page - 1), children: "Previous" }), _jsx("span", { className: "st-pagination__page--active", children: totalItems ? `${start}-${end} of ${totalItems}` : `Page ${page} of ${pages}` }), _jsx("span", { className: "st-pagination__pages", children: visiblePages.map((pageNumber) => (_jsx("button", { type: "button", className: classNames("st-pagination__page", pageNumber === page && "st-pagination__page--active"), "aria-label": `Page ${pageNumber}`, "aria-current": pageNumber === page ? "page" : undefined, onClick: () => onPageChange?.(pageNumber), children: pageNumber }, pageNumber))) }), _jsx("button", { type: "button", disabled: page >= pages, onClick: () => onPageChange?.(page + 1), children: "Next" })] }));
932
916
  }
933
- export function PaginationNav({ page = 1, totalPages = 1, previousHref, nextHref, className, ...rest }) {
934
- return (_jsxs("nav", { ...rest, className: classNames("st-paginationNav", className), "aria-label": "Pagination navigation", children: [previousHref ? _jsx("a", { href: previousHref, children: "Previous" }) : _jsx("button", { type: "button", disabled: page <= 1, children: "Previous" }), _jsx("ol", { className: "st-paginationNav__list", children: Array.from({ length: totalPages }, (_, index) => index + 1).map((item) => (_jsx("li", { children: _jsxs("button", { type: "button", className: classNames("st-paginationNav__page", item === page && "st-paginationNav__page--active"), children: ["Page ", item] }) }, item))) }), nextHref ? _jsx("a", { href: nextHref, children: "Next" }) : _jsx("button", { type: "button", disabled: page >= totalPages, children: "Next" })] }));
917
+ export function PaginationNav({ page = 1, totalPages, pageCount, previousHref, nextHref, className, ...rest }) {
918
+ const pages = totalPages ?? pageCount ?? 1;
919
+ return (_jsxs("nav", { ...rest, className: classNames("st-paginationNav", className), "aria-label": "Pagination navigation", children: [previousHref ? _jsx("a", { href: previousHref, children: "Previous" }) : _jsx("button", { type: "button", disabled: page <= 1, children: "Previous" }), _jsx("ol", { className: "st-paginationNav__list", children: Array.from({ length: pages }, (_, index) => index + 1).map((item) => (_jsx("li", { children: _jsxs("button", { type: "button", className: classNames("st-paginationNav__page", item === page && "st-paginationNav__page--active"), children: ["Page ", item] }) }, item))) }), nextHref ? _jsx("a", { href: nextHref, children: "Next" }) : _jsx("button", { type: "button", disabled: page >= pages, children: "Next" })] }));
935
920
  }
936
921
  export function PasswordInput({ label, helperText, errorText, size = "md", className, ...rest }) {
937
922
  const [shown, setShown] = React.useState(false);
@@ -978,9 +963,6 @@ export function Slider({ label, size = "md", value, defaultValue, min = 0, max =
978
963
  const numeric = Number(value ?? defaultValue ?? 0);
979
964
  return (_jsxs("div", { className: classNames("st-slider", `st-slider--${size}`, className), children: [_jsxs("div", { className: "st-slider__header", children: [label ? _jsx("label", { className: "st-field__label", children: label }) : null, _jsx("span", { className: "st-slider__value", children: numeric })] }), _jsx("input", { ...rest, className: "st-slider__input", type: "range", min: min, max: max, defaultValue: defaultValue, value: value })] }));
980
965
  }
981
- export function Sparkline({ data, label = "Sparkline", tone = "neutral", className, ...rest }) {
982
- return (_jsxs("figure", { ...rest, className: classNames("st-sparkline", `st-sparkline--${tone}`, className), "aria-label": label, children: [_jsx("span", { className: "st-visually-hidden", children: label }), _jsx("svg", { viewBox: "0 0 120 40", "aria-hidden": "true", children: _jsx("polyline", { className: "st-sparkline__line", points: pointsFrom(data, 120, 40), fill: "none" }) })] }));
983
- }
984
966
  export function StreamingMessage({ text: messageText, events = [], mode = "live", className, ...rest }) {
985
967
  return (_jsxs("section", { ...rest, className: classNames("st-streamingMessage", `st-streamingMessage--${mode}`, className), children: [_jsx("div", { className: "st-streamingMessage__text", children: messageText }), _jsx("ul", { className: "st-streamingMessage__trailList", children: events.map((event) => _jsx("li", { children: event.label }, event.id)) })] }));
986
968
  }
@@ -993,11 +975,12 @@ export function Switch({ label, helperText, className, ...rest }) {
993
975
  export function Table({ columns, rows, caption = "Table", className, ...rest }) {
994
976
  return (_jsx("div", { className: "st-table-wrap", children: _jsxs("table", { ...rest, className: classNames("st-table", className), children: [_jsx("caption", { children: caption }), _jsx("thead", { children: _jsx("tr", { children: columns.map((column) => _jsx("th", { children: column.label }, column.key)) }) }), _jsx("tbody", { children: rows.map((row, index) => (_jsx("tr", { children: columns.map((column) => _jsx("td", { className: classNames(column.align === "center" && "st-table__cell--center", (column.align === "right" || column.align === "end") && "st-table__cell--right"), children: text(row[column.key]) }, column.key)) }, text(row.id) || index))) })] }) }));
995
977
  }
996
- export function Tabs({ items, activeValue, activeId, label = "Tabs", onChange, className, ...rest }) {
978
+ export function Tabs({ items, activeValue, activeId, label = "Tabs", onChange, onchange, className, ...rest }) {
979
+ const handleChange = onChange ?? onchange;
997
980
  const reactId = React.useId();
998
981
  const tabRefs = React.useRef([]);
999
982
  const first = items.find((item) => !item.disabled) ?? items[0];
1000
- const [current, setCurrent] = useControlled(activeValue ?? activeId, idFrom(first ?? {}, 0, "tab"), onChange);
983
+ const [current, setCurrent] = useControlled(activeValue ?? activeId, idFrom(first ?? {}, 0, "tab"), handleChange);
1001
984
  const itemIds = items.map((item, index) => idFrom(item, index, "tab"));
1002
985
  const activeIndex = Math.max(0, itemIds.indexOf(current));
1003
986
  const active = items[activeIndex] ?? first;