@oods/components-react 0.3.3 → 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.
package/README.md CHANGED
@@ -32,4 +32,4 @@ Use matching versions of the OODS packages. These packages are separate from the
32
32
 
33
33
  ## License
34
34
 
35
- Apache License 2.0. See [LICENSE](https://cdn.jsdelivr.net/npm/@oods/components-react@0.3.3/LICENSE) and [NOTICE](https://cdn.jsdelivr.net/npm/@oods/components-react@0.3.3/NOTICE). Generated output belongs to its user; installed package dependencies retain their own licenses.
35
+ Apache License 2.0. See [LICENSE](https://cdn.jsdelivr.net/npm/@oods/components-react@0.4.0/LICENSE) and [NOTICE](https://cdn.jsdelivr.net/npm/@oods/components-react@0.4.0/NOTICE). Generated output belongs to its user; installed package dependencies retain their own licenses.
@@ -0,0 +1 @@
1
+ export { STATUS_DOMAINS, getBannerToneTokenSet, getStatusPresentation, getToneTokenSet, listStatuses } from '@oods/component-contracts';
@@ -1,5 +1,5 @@
1
- import { getStatusPresentation, getToneTokenSet, resolveStatusGlyph, getBannerToneTokenSet } from './chunk-G74PKSW3.js';
2
- import { dateTimeInputValue, currencyMinorUnits, formatDateTime, formatReadOnlyValue } from '@oods/component-contracts';
1
+ import { getStatusPresentation, getToneTokenSet, getBannerToneTokenSet } from './chunk-2VEQWKGK.js';
2
+ import { dateTimeInputValue, STATUS_ICON_SVG_ATTRS, resolveStatusIcon, currencyMinorUnits, formatDateTime, formatReadOnlyValue, STATUS_ICON_SHAPES } from '@oods/component-contracts';
3
3
  import * as React3 from 'react';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
5
  import { Slot } from '@radix-ui/react-slot';
@@ -230,6 +230,84 @@ var Checkbox = React3.forwardRef(
230
230
  }
231
231
  );
232
232
  Checkbox.displayName = "OODS.Checkbox";
233
+ var Switch = React3.forwardRef(
234
+ ({
235
+ id,
236
+ label,
237
+ checked,
238
+ defaultChecked = false,
239
+ required,
240
+ help,
241
+ validation,
242
+ size = "md",
243
+ className,
244
+ style,
245
+ onClick,
246
+ onCheckedChange,
247
+ onUpdate,
248
+ "aria-describedby": nativeDescription,
249
+ ...rest
250
+ }, ref) => {
251
+ const [internalChecked, setInternalChecked] = React3.useState(defaultChecked);
252
+ const on = checked ?? internalChecked;
253
+ const validationId = validation?.id ?? `${id}-validation`;
254
+ const describedBy = [nativeDescription, help ? `${id}-help` : void 0, validation?.message ? validationId : void 0].filter(Boolean).join(" ") || void 0;
255
+ const tone = validation ? validation.state === "error" ? "critical" : validation.state : void 0;
256
+ const handleClick = (event) => {
257
+ onClick?.(event);
258
+ if (event.defaultPrevented) return;
259
+ const next = !on;
260
+ if (checked === void 0) setInternalChecked(next);
261
+ onCheckedChange?.(next);
262
+ onUpdate?.(next);
263
+ };
264
+ return /* @__PURE__ */ jsxs(
265
+ "div",
266
+ {
267
+ className: classes("oods-field", "oods-switch", className),
268
+ style: {
269
+ ...tone ? {
270
+ "--cmp-input-message-border": `var(--sys-status-${tone}-border)`,
271
+ "--cmp-input-message-text": `var(--sys-status-${tone}-text)`
272
+ } : {},
273
+ ...style
274
+ },
275
+ "data-oods-component": "Switch",
276
+ "data-size": size,
277
+ "data-validation-state": validation?.state,
278
+ children: [
279
+ /* @__PURE__ */ jsxs("div", { className: "oods-switch__row", children: [
280
+ /* @__PURE__ */ jsx(
281
+ "button",
282
+ {
283
+ ref,
284
+ id,
285
+ type: "button",
286
+ role: "switch",
287
+ className: "oods-switch__control",
288
+ "aria-checked": on,
289
+ "aria-labelledby": `${id}-label`,
290
+ "aria-describedby": describedBy,
291
+ "aria-required": required || void 0,
292
+ "aria-invalid": validation?.state === "error" || void 0,
293
+ onClick: handleClick,
294
+ ...rest,
295
+ children: /* @__PURE__ */ jsx("span", { className: "oods-switch__thumb", "aria-hidden": "true" })
296
+ }
297
+ ),
298
+ /* @__PURE__ */ jsxs("label", { id: `${id}-label`, className: "oods-field-label oods-switch__label", htmlFor: id, children: [
299
+ label,
300
+ required ? /* @__PURE__ */ jsx("span", { className: "oods-field-required", "aria-hidden": "true", children: "*" }) : null
301
+ ] })
302
+ ] }),
303
+ help ? /* @__PURE__ */ jsx("p", { id: `${id}-help`, className: "oods-field-help", children: help }) : null,
304
+ validation?.message ? /* @__PURE__ */ jsx("p", { id: validationId, className: "oods-field-error", role: validation.state === "error" ? "alert" : void 0, children: validation.message }) : null
305
+ ]
306
+ }
307
+ );
308
+ }
309
+ );
310
+ Switch.displayName = "OODS.Switch";
233
311
  var Select = React3.forwardRef(
234
312
  ({
235
313
  id,
@@ -368,6 +446,20 @@ var Textarea = React3.forwardRef(
368
446
  }
369
447
  );
370
448
  Textarea.displayName = "OODS.Textarea";
449
+ var REACT_NAMES = {
450
+ viewBox: "viewBox",
451
+ fill: "fill",
452
+ stroke: "stroke",
453
+ "stroke-width": "strokeWidth",
454
+ "stroke-linecap": "strokeLinecap",
455
+ "stroke-linejoin": "strokeLinejoin",
456
+ "aria-hidden": "aria-hidden",
457
+ focusable: "focusable"
458
+ };
459
+ var svgProps = Object.fromEntries(Object.entries(STATUS_ICON_SVG_ATTRS).map(([name, value]) => [REACT_NAMES[name] ?? name, value]));
460
+ function StatusIcon({ name }) {
461
+ return /* @__PURE__ */ jsx("svg", { ...svgProps, children: STATUS_ICON_SHAPES[name].map((shape, index) => React3.createElement(shape.tag, { key: index, ...shape.attrs })) });
462
+ }
371
463
  var classes2 = (...values) => values.filter(Boolean).join(" ");
372
464
  var Badge = React3.forwardRef(
373
465
  ({
@@ -390,7 +482,8 @@ var Badge = React3.forwardRef(
390
482
  const tone = toneOverride ?? presentation?.tone ?? "neutral";
391
483
  const tokenSet = toneOverride !== void 0 ? getToneTokenSet(tone) : emphasis === "solid" ? presentation?.badge.solid ?? getToneTokenSet(tone) : presentation?.badge.subtle ?? getToneTokenSet(tone);
392
484
  const resolvedContent = children ?? content ?? presentation?.label ?? status;
393
- const resolvedIcon = icon ?? resolveStatusGlyph(presentation?.iconName);
485
+ const statusIcon = resolveStatusIcon(presentation?.iconName);
486
+ const resolvedIcon = icon ?? (statusIcon ? /* @__PURE__ */ jsx(StatusIcon, { name: statusIcon }) : void 0);
394
487
  const resolvedShowIcon = showIcon ?? resolvedIcon !== void 0;
395
488
  const iconNode = resolvedShowIcon && resolvedIcon ? /* @__PURE__ */ jsx("span", { className: "oods-badge__icon", "aria-hidden": "true", children: resolvedIcon }) : null;
396
489
  return /* @__PURE__ */ jsxs(
@@ -448,7 +541,8 @@ var Banner = React3.forwardRef(
448
541
  const heading = title ?? presentation?.label ?? status;
449
542
  const body = detail ?? description ?? children ?? content ?? presentation?.description;
450
543
  const role = tone === "critical" || tone === "danger" ? "alert" : "status";
451
- const resolvedIcon = icon ?? resolveStatusGlyph(presentation?.iconName);
544
+ const statusIcon = resolveStatusIcon(presentation?.iconName);
545
+ const resolvedIcon = icon ?? (statusIcon ? /* @__PURE__ */ jsx(StatusIcon, { name: statusIcon }) : void 0);
452
546
  const resolvedShowIcon = showIcon ?? resolvedIcon !== void 0;
453
547
  return /* @__PURE__ */ jsxs(
454
548
  "div",
@@ -483,7 +577,7 @@ var Banner = React3.forwardRef(
483
577
  className: "oods-banner-dismiss oods-banner__dismiss",
484
578
  "aria-label": dismissLabel,
485
579
  onClick: () => onDismiss(),
486
- children: /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xD7" })
580
+ children: /* @__PURE__ */ jsx(StatusIcon, { name: "x" })
487
581
  }
488
582
  ) : null
489
583
  ]
@@ -538,6 +632,84 @@ var Button = React3.forwardRef(
538
632
  }
539
633
  );
540
634
  Button.displayName = "OODS.Button";
635
+ var noSubscription = () => () => void 0;
636
+ var useIsomorphicLayoutEffect = typeof window === "undefined" ? React3.useEffect : React3.useLayoutEffect;
637
+ var canShowModal = () => typeof HTMLDialogElement !== "undefined" && typeof HTMLDialogElement.prototype.showModal === "function";
638
+ var hasContent = (node) => node !== void 0 && node !== null && node !== false && node !== "";
639
+ var Dialog = React3.forwardRef(
640
+ ({
641
+ id,
642
+ open = false,
643
+ modal = true,
644
+ title,
645
+ description,
646
+ dismissLabel = "Close",
647
+ size = "md",
648
+ actions,
649
+ children,
650
+ className,
651
+ onClose,
652
+ onKeyDown,
653
+ ...rest
654
+ }, ref) => {
655
+ const generatedId = React3.useId().replace(/[^\w-]/g, "");
656
+ const dialogId = id ?? `oods-dialog-${generatedId}`;
657
+ const element = React3.useRef(null);
658
+ const openRef = React3.useRef(open);
659
+ const serverMarkup = React3.useSyncExternalStore(noSubscription, () => false, () => true);
660
+ const managed = modal && !serverMarkup && canShowModal();
661
+ const setRef = React3.useCallback((node) => {
662
+ element.current = node;
663
+ if (typeof ref === "function") ref(node);
664
+ else if (ref) ref.current = node;
665
+ }, [ref]);
666
+ useIsomorphicLayoutEffect(() => {
667
+ openRef.current = open;
668
+ const dialog = element.current;
669
+ if (!dialog || !managed) return;
670
+ if (open && !dialog.open) dialog.showModal();
671
+ else if (!open && dialog.open) dialog.close();
672
+ }, [open, managed]);
673
+ const requestClose = (event) => {
674
+ event.preventDefault();
675
+ onClose?.();
676
+ };
677
+ return /* @__PURE__ */ jsxs(
678
+ "dialog",
679
+ {
680
+ ref: setRef,
681
+ id: dialogId,
682
+ className: classes2("oods-dialog", className),
683
+ "data-oods-component": "Dialog",
684
+ "data-size": size,
685
+ "aria-labelledby": `${dialogId}-title`,
686
+ "aria-describedby": hasContent(description) ? `${dialogId}-description` : void 0,
687
+ open: managed ? void 0 : open,
688
+ onKeyDown: (event) => {
689
+ onKeyDown?.(event);
690
+ if (event.key === "Escape" && !event.defaultPrevented) requestClose(event);
691
+ },
692
+ onCancel: requestClose,
693
+ onClose: () => {
694
+ if (openRef.current && !element.current?.open) onClose?.();
695
+ },
696
+ ...rest,
697
+ children: [
698
+ /* @__PURE__ */ jsxs("div", { className: "oods-dialog__header", children: [
699
+ /* @__PURE__ */ jsxs("div", { className: "oods-dialog__heading", children: [
700
+ /* @__PURE__ */ jsx("h2", { id: `${dialogId}-title`, className: "oods-dialog__title", children: title }),
701
+ hasContent(description) ? /* @__PURE__ */ jsx("p", { id: `${dialogId}-description`, className: "oods-dialog__description", children: description }) : null
702
+ ] }),
703
+ /* @__PURE__ */ jsx("button", { type: "button", className: "oods-dialog__close", "aria-label": dismissLabel, onClick: () => onClose?.(), children: /* @__PURE__ */ jsx(StatusIcon, { name: "x" }) })
704
+ ] }),
705
+ hasContent(children) ? /* @__PURE__ */ jsx("div", { className: "oods-dialog__body", children }) : null,
706
+ hasContent(actions) ? /* @__PURE__ */ jsx("div", { className: "oods-dialog__footer", children: actions }) : null
707
+ ]
708
+ }
709
+ );
710
+ }
711
+ );
712
+ Dialog.displayName = "OODS.Dialog";
541
713
  var Card = React3.forwardRef(
542
714
  ({ as = "div", elevated = false, className, ...rest }, ref) => {
543
715
  const Element = as;
@@ -973,7 +1145,7 @@ var SearchInput = React3.forwardRef(
973
1145
  "aria-label": "Clear search",
974
1146
  disabled,
975
1147
  onClick: clear,
976
- children: "\xD7"
1148
+ children: /* @__PURE__ */ jsx(StatusIcon, { name: "x" })
977
1149
  }
978
1150
  ) : null
979
1151
  ]
@@ -1161,7 +1333,7 @@ var RelativeTimestamp = React3.forwardRef(
1161
1333
  const date = asDate(raw);
1162
1334
  if (hideWhenEmpty && !date) return null;
1163
1335
  const nowDate = asDate(now) ?? /* @__PURE__ */ new Date();
1164
- const iso = date?.toISOString() ?? (raw === void 0 ? void 0 : String(raw));
1336
+ const iso = date?.toISOString() ?? (raw === void 0 || raw === "" ? void 0 : String(raw));
1165
1337
  const absolute = formatDateTime(date, { timeZone: timezone });
1166
1338
  const content = relative ?? label ?? text ?? (now !== void 0 && date ? relativeLabel(date, nowDate) : absolute || children || "Unknown time");
1167
1339
  return /* @__PURE__ */ jsx(
@@ -1181,4 +1353,4 @@ var RelativeTimestamp = React3.forwardRef(
1181
1353
  );
1182
1354
  RelativeTimestamp.displayName = "OODS.RelativeTimestamp";
1183
1355
 
1184
- export { AuditTimeline, Badge, Banner, Button, CancellationSummary, Card, Checkbox, DatePicker, Input, PaginationBar, PriceBadge, RelativeTimestamp, SearchInput, Select, StatusBadge, StatusTimeline, Text, Textarea };
1356
+ export { AuditTimeline, Badge, Banner, Button, CancellationSummary, Card, Checkbox, DatePicker, Dialog, Input, PaginationBar, PriceBadge, RelativeTimestamp, SearchInput, Select, StatusBadge, StatusTimeline, Switch, Text, Textarea };
@@ -1,4 +1,4 @@
1
- import { getStatusPresentation, getToneTokenSet } from './chunk-G74PKSW3.js';
1
+ import { getStatusPresentation, getToneTokenSet } from './chunk-2VEQWKGK.js';
2
2
  import * as React from 'react';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
 
@@ -120,6 +120,7 @@ TableCell.displayName = "OODS.Table.Cell";
120
120
  var TableRoot = React.forwardRef(
121
121
  ({
122
122
  caption,
123
+ showCaption = true,
123
124
  columns,
124
125
  rows,
125
126
  density = "comfortable",
@@ -145,7 +146,7 @@ var TableRoot = React.forwardRef(
145
146
  "data-density": density,
146
147
  ...rest,
147
148
  children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
148
- caption ? /* @__PURE__ */ jsx(TableCaption, { children: caption }) : null,
149
+ caption ? /* @__PURE__ */ jsx(TableCaption, { "data-visually-hidden": showCaption ? void 0 : "true", children: caption }) : null,
149
150
  /* @__PURE__ */ jsx(TableHead, { children: /* @__PURE__ */ jsx("tr", { className: "oods-table__row", children: columns?.map((column) => /* @__PURE__ */ jsx(TableHeaderCell, { numeric: column.numeric, children: column.label }, column.key)) }) }),
150
151
  /* @__PURE__ */ jsx(TableBody, { children: (rows?.length ?? 0) === 0 ? /* @__PURE__ */ jsx("tr", { className: "oods-table__row", children: /* @__PURE__ */ jsx(TableCell, { colSpan: Math.max(columns?.length ?? 0, 1), children: "No rows available." }) }) : rows?.map((row) => /* @__PURE__ */ jsx(
151
152
  "tr",