@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/dist/ported.cjs CHANGED
@@ -26,337 +26,6 @@ function _interopNamespace(e) {
26
26
  var React3__namespace = /*#__PURE__*/_interopNamespace(React3);
27
27
 
28
28
  // src/ported.tsx
29
-
30
- // src/status-map.json
31
- var status_map_default = {
32
- domains: {
33
- subscription: {
34
- future: {
35
- description: "Subscription is scheduled but not yet active.",
36
- chip: {
37
- foreground: "color.text.info",
38
- background: "color.background.info.subtle",
39
- border: "color.border.info",
40
- icon: "icon.status.scheduled"
41
- },
42
- banner: {
43
- foreground: "color.text.info",
44
- background: "color.background.surface.default",
45
- border: "color.border.info",
46
- icon: "icon.status.scheduled"
47
- }
48
- },
49
- trialing: {
50
- description: "Subscription is in a trial period before billing begins.",
51
- chip: {
52
- foreground: "color.text.accent",
53
- background: "color.background.accent.subtle",
54
- border: "color.border.accent",
55
- icon: "icon.status.trial"
56
- },
57
- banner: {
58
- foreground: "color.text.accent",
59
- background: "color.background.surface.default",
60
- border: "color.border.accent",
61
- icon: "icon.status.trial"
62
- }
63
- },
64
- active: {
65
- description: "Subscription is paid and service is provisioned.",
66
- chip: {
67
- foreground: "color.text.positive",
68
- background: "color.background.positive.subtle",
69
- border: "color.border.positive",
70
- icon: "icon.status.success"
71
- },
72
- banner: {
73
- foreground: "color.text.positive",
74
- background: "color.background.surface.default",
75
- border: "color.border.positive",
76
- icon: "icon.status.success"
77
- }
78
- },
79
- paused: {
80
- description: "Billing paused temporarily by finance or customer request.",
81
- chip: {
82
- foreground: "color.text.neutral",
83
- background: "color.background.neutral.subtle",
84
- border: "color.border.neutral",
85
- icon: "icon.status.paused"
86
- },
87
- banner: {
88
- foreground: "color.text.neutral",
89
- background: "color.background.surface.muted",
90
- border: "color.border.neutral",
91
- icon: "icon.status.paused"
92
- }
93
- },
94
- pending_cancellation: {
95
- description: "Subscription will cancel at period end but remains active now.",
96
- chip: {
97
- foreground: "color.text.info",
98
- background: "color.background.info.subtle",
99
- border: "color.border.info",
100
- icon: "icon.status.pending"
101
- },
102
- banner: {
103
- foreground: "color.text.info",
104
- background: "color.background.surface.default",
105
- border: "color.border.info",
106
- icon: "icon.status.pending"
107
- }
108
- },
109
- past_due: {
110
- description: "Renewal payment failed; smart retries ongoing and access retained during the grace window.",
111
- chip: {
112
- foreground: "color.text.danger",
113
- background: "color.background.danger.subtle",
114
- border: "color.border.danger",
115
- icon: "icon.status.warning"
116
- },
117
- banner: {
118
- foreground: "color.text.onbold",
119
- background: "color.background.danger.bold",
120
- border: "color.border.danger",
121
- icon: "icon.status.warning"
122
- }
123
- },
124
- unpaid: {
125
- description: "Payment retries exhausted; service access revoked.",
126
- chip: {
127
- foreground: "color.text.danger",
128
- background: "color.background.danger.subtle",
129
- border: "color.border.danger",
130
- icon: "icon.status.error"
131
- },
132
- banner: {
133
- foreground: "color.text.onbold",
134
- background: "color.background.danger.bold",
135
- border: "color.border.danger",
136
- icon: "icon.status.error"
137
- }
138
- },
139
- terminated: {
140
- description: "Subscription ended; service access revoked and billing stopped.",
141
- chip: {
142
- foreground: "color.text.disabled",
143
- background: "color.background.disabled",
144
- border: "color.border.disabled",
145
- icon: "icon.status.canceled"
146
- },
147
- banner: {
148
- foreground: "color.text.neutral",
149
- background: "color.background.neutral.subtle",
150
- border: "color.border.neutral",
151
- icon: "icon.status.canceled"
152
- }
153
- }
154
- },
155
- invoice: {
156
- draft: {
157
- description: "Invoice is editable and not yet finalized.",
158
- chip: {
159
- foreground: "color.text.neutral",
160
- background: "color.background.neutral.subtle",
161
- border: "color.border.neutral",
162
- icon: "icon.status.draft"
163
- },
164
- banner: {
165
- foreground: "color.text.neutral",
166
- background: "color.background.surface.muted",
167
- border: "color.border.neutral",
168
- icon: "icon.status.draft"
169
- }
170
- },
171
- posted: {
172
- description: "Invoice finalized with terms before collection.",
173
- chip: {
174
- foreground: "color.text.info",
175
- background: "color.background.info.subtle",
176
- border: "color.border.info",
177
- icon: "icon.status.pending"
178
- },
179
- banner: {
180
- foreground: "color.text.info",
181
- background: "color.background.surface.default",
182
- border: "color.border.info",
183
- icon: "icon.status.pending"
184
- }
185
- },
186
- paid: {
187
- description: "Invoice collected successfully in full.",
188
- chip: {
189
- foreground: "color.text.positive",
190
- background: "color.background.positive.subtle",
191
- border: "color.border.positive",
192
- icon: "icon.status.success"
193
- },
194
- banner: {
195
- foreground: "color.text.positive",
196
- background: "color.background.surface.default",
197
- border: "color.border.positive",
198
- icon: "icon.status.success"
199
- }
200
- },
201
- past_due: {
202
- description: "Invoice is unpaid past its due date.",
203
- chip: {
204
- foreground: "color.text.danger",
205
- background: "color.background.danger.subtle",
206
- border: "color.border.danger",
207
- icon: "icon.status.error"
208
- },
209
- banner: {
210
- foreground: "color.text.onbold",
211
- background: "color.background.danger.bold",
212
- border: "color.border.danger",
213
- icon: "icon.status.error"
214
- }
215
- },
216
- void: {
217
- description: "Invoice canceled and no longer payable.",
218
- chip: {
219
- foreground: "color.text.disabled",
220
- background: "color.background.disabled",
221
- border: "color.border.disabled",
222
- icon: "icon.status.void"
223
- },
224
- banner: {
225
- foreground: "color.text.neutral",
226
- background: "color.background.neutral.subtle",
227
- border: "color.border.neutral",
228
- icon: "icon.status.void"
229
- }
230
- }
231
- }
232
- }
233
- };
234
-
235
- // src/status.ts
236
- var TONE_ALIASES = {
237
- info: "info",
238
- informational: "info",
239
- accent: "accent",
240
- highlight: "accent",
241
- positive: "success",
242
- success: "success",
243
- ok: "success",
244
- warning: "warning",
245
- caution: "warning",
246
- danger: "critical",
247
- critical: "critical",
248
- error: "critical",
249
- negative: "critical",
250
- neutral: "neutral",
251
- pending: "info"
252
- };
253
- var toLabel = (status) => status.split(/[_-]/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)).join(" ");
254
- var inferTone = (entry) => {
255
- if (!entry) return "neutral";
256
- for (const reference of [entry.foreground, entry.background, entry.border]) {
257
- const segments = reference.replace(/^\{|\}$/g, "").split(/[\s./_-]+/).reverse();
258
- for (const segment of segments) {
259
- const tone = TONE_ALIASES[segment.toLowerCase()];
260
- if (tone) return tone;
261
- }
262
- }
263
- return "neutral";
264
- };
265
- var componentTokenSet = (tone) => {
266
- const normalized = tone === "positive" ? "success" : tone === "danger" ? "critical" : tone;
267
- return {
268
- background: `var(--sys-status-${normalized}-surface)`,
269
- border: `var(--sys-status-${normalized}-border)`,
270
- foreground: `var(--sys-status-${normalized}-text)`
271
- };
272
- };
273
- var rawTokenSet = (entry) => {
274
- const tone = inferTone(entry);
275
- return componentTokenSet(tone);
276
- };
277
- var bannerFallback = (tone) => tone === "neutral" ? {
278
- background: "var(--cmp-banner-background)",
279
- border: "var(--cmp-banner-border)",
280
- foreground: "var(--cmp-banner-text)"
281
- } : componentTokenSet(tone);
282
- var fallback = (domain, status, tone = "neutral") => ({
283
- domain,
284
- status,
285
- label: toLabel(status || "Unknown"),
286
- description: "Status not found in registry; falling back to the requested token set.",
287
- tone,
288
- badge: { subtle: componentTokenSet(tone), solid: componentTokenSet(tone) },
289
- banner: { subtle: bannerFallback(tone), solid: componentTokenSet(tone) }
290
- });
291
- var registry = /* @__PURE__ */ new Map();
292
- for (const [domain, entries] of Object.entries(status_map_default.domains)) {
293
- const domainEntries = /* @__PURE__ */ new Map();
294
- for (const [status, entry] of Object.entries(entries)) {
295
- const tone = inferTone(entry.chip);
296
- domainEntries.set(status.toLowerCase(), {
297
- domain,
298
- status,
299
- label: toLabel(status),
300
- description: entry.description,
301
- tone,
302
- iconName: entry.chip.icon ?? entry.banner?.icon,
303
- badge: { subtle: rawTokenSet(entry.chip), solid: componentTokenSet(tone) },
304
- banner: {
305
- subtle: entry.banner ? rawTokenSet(entry.banner) : bannerFallback(tone),
306
- solid: componentTokenSet(tone)
307
- }
308
- });
309
- }
310
- registry.set(domain, domainEntries);
311
- }
312
- var LEGACY_STATUS_ALIASES = {
313
- subscription: { delinquent: "unpaid" }
314
- };
315
- Object.freeze([...registry.keys()]);
316
- function getStatusPresentation(domain, status) {
317
- const entries = registry.get(domain);
318
- if (!entries) return fallback(domain, status);
319
- const normalized = status.toLowerCase();
320
- const direct = entries.get(normalized);
321
- if (direct) return direct;
322
- const alias = LEGACY_STATUS_ALIASES[domain]?.[normalized];
323
- const aliased = alias ? entries.get(alias) : void 0;
324
- return aliased ? { ...aliased, status, label: toLabel(status) } : fallback(domain, status);
325
- }
326
- function getToneTokenSet(tone) {
327
- return componentTokenSet(tone);
328
- }
329
- function getBannerToneTokenSet(tone) {
330
- return bannerFallback(tone);
331
- }
332
- var ICON_GLYPHS = {
333
- success: "\u2714\uFE0E",
334
- warning: "\u26A0\uFE0E",
335
- critical: "\u2A2F",
336
- danger: "\u2A2F",
337
- error: "\u2A2F",
338
- negative: "\u2A2F",
339
- pending: "\u2026",
340
- processing: "\u27F3",
341
- paused: "\u23F8",
342
- canceled: "\u2205",
343
- cancelled: "\u2205",
344
- draft: "\u270E",
345
- void: "\u2205",
346
- paid: "\u2714\uFE0E",
347
- info: "\u2139\uFE0E",
348
- trial: "\u2605",
349
- future: "\u23F2",
350
- scheduled: "\u23F2",
351
- refunded: "\u21BA",
352
- locked: "\u{1F512}",
353
- unlocked: "\u{1F513}",
354
- default: "\u2022"
355
- };
356
- function resolveStatusGlyph(iconName) {
357
- const segment = iconName?.split(".").pop()?.toLowerCase();
358
- return segment ? ICON_GLYPHS[segment] ?? ICON_GLYPHS.default : void 0;
359
- }
360
29
  var classes = (...values) => values.filter(Boolean).join(" ");
361
30
  var describedByIds = (id, help, validation, nativeDescription) => [
362
31
  nativeDescription,
@@ -383,7 +52,7 @@ var FieldFrame = ({
383
52
  }) => {
384
53
  const resolvedHelp = help ?? description;
385
54
  const validationTone = validation?.state === "error" ? "critical" : validation?.state === "warning" ? "warning" : validation?.state;
386
- const validationTokens = validationTone ? getToneTokenSet(validationTone) : void 0;
55
+ const validationTokens = validationTone ? componentContracts.getToneTokenSet(validationTone) : void 0;
387
56
  const validationStyle = validationTokens ? {
388
57
  "--cmp-input-message-border": validationTokens.border,
389
58
  "--cmp-input-message-text": validationTokens.foreground,
@@ -583,6 +252,84 @@ var Checkbox = React3__namespace.forwardRef(
583
252
  }
584
253
  );
585
254
  Checkbox.displayName = "OODS.Checkbox";
255
+ var Switch = React3__namespace.forwardRef(
256
+ ({
257
+ id,
258
+ label,
259
+ checked,
260
+ defaultChecked = false,
261
+ required,
262
+ help,
263
+ validation,
264
+ size = "md",
265
+ className,
266
+ style,
267
+ onClick,
268
+ onCheckedChange,
269
+ onUpdate,
270
+ "aria-describedby": nativeDescription,
271
+ ...rest
272
+ }, ref) => {
273
+ const [internalChecked, setInternalChecked] = React3__namespace.useState(defaultChecked);
274
+ const on = checked ?? internalChecked;
275
+ const validationId = validation?.id ?? `${id}-validation`;
276
+ const describedBy = [nativeDescription, help ? `${id}-help` : void 0, validation?.message ? validationId : void 0].filter(Boolean).join(" ") || void 0;
277
+ const tone = validation ? validation.state === "error" ? "critical" : validation.state : void 0;
278
+ const handleClick = (event) => {
279
+ onClick?.(event);
280
+ if (event.defaultPrevented) return;
281
+ const next = !on;
282
+ if (checked === void 0) setInternalChecked(next);
283
+ onCheckedChange?.(next);
284
+ onUpdate?.(next);
285
+ };
286
+ return /* @__PURE__ */ jsxRuntime.jsxs(
287
+ "div",
288
+ {
289
+ className: classes("oods-field", "oods-switch", className),
290
+ style: {
291
+ ...tone ? {
292
+ "--cmp-input-message-border": `var(--sys-status-${tone}-border)`,
293
+ "--cmp-input-message-text": `var(--sys-status-${tone}-text)`
294
+ } : {},
295
+ ...style
296
+ },
297
+ "data-oods-component": "Switch",
298
+ "data-size": size,
299
+ "data-validation-state": validation?.state,
300
+ children: [
301
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "oods-switch__row", children: [
302
+ /* @__PURE__ */ jsxRuntime.jsx(
303
+ "button",
304
+ {
305
+ ref,
306
+ id,
307
+ type: "button",
308
+ role: "switch",
309
+ className: "oods-switch__control",
310
+ "aria-checked": on,
311
+ "aria-labelledby": `${id}-label`,
312
+ "aria-describedby": describedBy,
313
+ "aria-required": required || void 0,
314
+ "aria-invalid": validation?.state === "error" || void 0,
315
+ onClick: handleClick,
316
+ ...rest,
317
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "oods-switch__thumb", "aria-hidden": "true" })
318
+ }
319
+ ),
320
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { id: `${id}-label`, className: "oods-field-label oods-switch__label", htmlFor: id, children: [
321
+ label,
322
+ required ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "oods-field-required", "aria-hidden": "true", children: "*" }) : null
323
+ ] })
324
+ ] }),
325
+ help ? /* @__PURE__ */ jsxRuntime.jsx("p", { id: `${id}-help`, className: "oods-field-help", children: help }) : null,
326
+ validation?.message ? /* @__PURE__ */ jsxRuntime.jsx("p", { id: validationId, className: "oods-field-error", role: validation.state === "error" ? "alert" : void 0, children: validation.message }) : null
327
+ ]
328
+ }
329
+ );
330
+ }
331
+ );
332
+ Switch.displayName = "OODS.Switch";
586
333
  var Select = React3__namespace.forwardRef(
587
334
  ({
588
335
  id,
@@ -721,6 +468,20 @@ var Textarea = React3__namespace.forwardRef(
721
468
  }
722
469
  );
723
470
  Textarea.displayName = "OODS.Textarea";
471
+ var REACT_NAMES = {
472
+ viewBox: "viewBox",
473
+ fill: "fill",
474
+ stroke: "stroke",
475
+ "stroke-width": "strokeWidth",
476
+ "stroke-linecap": "strokeLinecap",
477
+ "stroke-linejoin": "strokeLinejoin",
478
+ "aria-hidden": "aria-hidden",
479
+ focusable: "focusable"
480
+ };
481
+ var svgProps = Object.fromEntries(Object.entries(componentContracts.STATUS_ICON_SVG_ATTRS).map(([name, value]) => [REACT_NAMES[name] ?? name, value]));
482
+ function StatusIcon({ name }) {
483
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { ...svgProps, children: componentContracts.STATUS_ICON_SHAPES[name].map((shape, index) => React3__namespace.createElement(shape.tag, { key: index, ...shape.attrs })) });
484
+ }
724
485
  var classes2 = (...values) => values.filter(Boolean).join(" ");
725
486
  var Badge = React3__namespace.forwardRef(
726
487
  ({
@@ -739,11 +500,12 @@ var Badge = React3__namespace.forwardRef(
739
500
  "aria-label": ariaLabel,
740
501
  ...rest
741
502
  }, ref) => {
742
- const presentation = status ? getStatusPresentation(domain, status) : void 0;
503
+ const presentation = status ? componentContracts.getStatusPresentation(domain, status) : void 0;
743
504
  const tone = toneOverride ?? presentation?.tone ?? "neutral";
744
- const tokenSet = toneOverride !== void 0 ? getToneTokenSet(tone) : emphasis === "solid" ? presentation?.badge.solid ?? getToneTokenSet(tone) : presentation?.badge.subtle ?? getToneTokenSet(tone);
505
+ const tokenSet = toneOverride !== void 0 ? componentContracts.getToneTokenSet(tone) : emphasis === "solid" ? presentation?.badge.solid ?? componentContracts.getToneTokenSet(tone) : presentation?.badge.subtle ?? componentContracts.getToneTokenSet(tone);
745
506
  const resolvedContent = children ?? content ?? presentation?.label ?? status;
746
- const resolvedIcon = icon ?? resolveStatusGlyph(presentation?.iconName);
507
+ const statusIcon = componentContracts.resolveStatusIcon(presentation?.iconName);
508
+ const resolvedIcon = icon ?? (statusIcon ? /* @__PURE__ */ jsxRuntime.jsx(StatusIcon, { name: statusIcon }) : void 0);
747
509
  const resolvedShowIcon = showIcon ?? resolvedIcon !== void 0;
748
510
  const iconNode = resolvedShowIcon && resolvedIcon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "oods-badge__icon", "aria-hidden": "true", children: resolvedIcon }) : null;
749
511
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -795,13 +557,14 @@ var Banner = React3__namespace.forwardRef(
795
557
  style,
796
558
  ...rest
797
559
  }, ref) => {
798
- const presentation = status ? getStatusPresentation(domain, status) : void 0;
560
+ const presentation = status ? componentContracts.getStatusPresentation(domain, status) : void 0;
799
561
  const tone = toneOverride ?? presentation?.tone ?? "neutral";
800
- const tokenSet = emphasis === "solid" ? presentation?.banner.solid ?? getToneTokenSet(tone) : presentation?.banner.subtle ?? getBannerToneTokenSet(tone);
562
+ const tokenSet = emphasis === "solid" ? presentation?.banner.solid ?? componentContracts.getToneTokenSet(tone) : presentation?.banner.subtle ?? componentContracts.getBannerToneTokenSet(tone);
801
563
  const heading = title ?? presentation?.label ?? status;
802
564
  const body = detail ?? description ?? children ?? content ?? presentation?.description;
803
565
  const role = tone === "critical" || tone === "danger" ? "alert" : "status";
804
- const resolvedIcon = icon ?? resolveStatusGlyph(presentation?.iconName);
566
+ const statusIcon = componentContracts.resolveStatusIcon(presentation?.iconName);
567
+ const resolvedIcon = icon ?? (statusIcon ? /* @__PURE__ */ jsxRuntime.jsx(StatusIcon, { name: statusIcon }) : void 0);
805
568
  const resolvedShowIcon = showIcon ?? resolvedIcon !== void 0;
806
569
  return /* @__PURE__ */ jsxRuntime.jsxs(
807
570
  "div",
@@ -836,7 +599,7 @@ var Banner = React3__namespace.forwardRef(
836
599
  className: "oods-banner-dismiss oods-banner__dismiss",
837
600
  "aria-label": dismissLabel,
838
601
  onClick: () => onDismiss(),
839
- children: /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\xD7" })
602
+ children: /* @__PURE__ */ jsxRuntime.jsx(StatusIcon, { name: "x" })
840
603
  }
841
604
  ) : null
842
605
  ]
@@ -891,6 +654,84 @@ var Button = React3__namespace.forwardRef(
891
654
  }
892
655
  );
893
656
  Button.displayName = "OODS.Button";
657
+ var noSubscription = () => () => void 0;
658
+ var useIsomorphicLayoutEffect = typeof window === "undefined" ? React3__namespace.useEffect : React3__namespace.useLayoutEffect;
659
+ var canShowModal = () => typeof HTMLDialogElement !== "undefined" && typeof HTMLDialogElement.prototype.showModal === "function";
660
+ var hasContent = (node) => node !== void 0 && node !== null && node !== false && node !== "";
661
+ var Dialog = React3__namespace.forwardRef(
662
+ ({
663
+ id,
664
+ open = false,
665
+ modal = true,
666
+ title,
667
+ description,
668
+ dismissLabel = "Close",
669
+ size = "md",
670
+ actions,
671
+ children,
672
+ className,
673
+ onClose,
674
+ onKeyDown,
675
+ ...rest
676
+ }, ref) => {
677
+ const generatedId = React3__namespace.useId().replace(/[^\w-]/g, "");
678
+ const dialogId = id ?? `oods-dialog-${generatedId}`;
679
+ const element = React3__namespace.useRef(null);
680
+ const openRef = React3__namespace.useRef(open);
681
+ const serverMarkup = React3__namespace.useSyncExternalStore(noSubscription, () => false, () => true);
682
+ const managed = modal && !serverMarkup && canShowModal();
683
+ const setRef = React3__namespace.useCallback((node) => {
684
+ element.current = node;
685
+ if (typeof ref === "function") ref(node);
686
+ else if (ref) ref.current = node;
687
+ }, [ref]);
688
+ useIsomorphicLayoutEffect(() => {
689
+ openRef.current = open;
690
+ const dialog = element.current;
691
+ if (!dialog || !managed) return;
692
+ if (open && !dialog.open) dialog.showModal();
693
+ else if (!open && dialog.open) dialog.close();
694
+ }, [open, managed]);
695
+ const requestClose = (event) => {
696
+ event.preventDefault();
697
+ onClose?.();
698
+ };
699
+ return /* @__PURE__ */ jsxRuntime.jsxs(
700
+ "dialog",
701
+ {
702
+ ref: setRef,
703
+ id: dialogId,
704
+ className: classes2("oods-dialog", className),
705
+ "data-oods-component": "Dialog",
706
+ "data-size": size,
707
+ "aria-labelledby": `${dialogId}-title`,
708
+ "aria-describedby": hasContent(description) ? `${dialogId}-description` : void 0,
709
+ open: managed ? void 0 : open,
710
+ onKeyDown: (event) => {
711
+ onKeyDown?.(event);
712
+ if (event.key === "Escape" && !event.defaultPrevented) requestClose(event);
713
+ },
714
+ onCancel: requestClose,
715
+ onClose: () => {
716
+ if (openRef.current && !element.current?.open) onClose?.();
717
+ },
718
+ ...rest,
719
+ children: [
720
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "oods-dialog__header", children: [
721
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "oods-dialog__heading", children: [
722
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { id: `${dialogId}-title`, className: "oods-dialog__title", children: title }),
723
+ hasContent(description) ? /* @__PURE__ */ jsxRuntime.jsx("p", { id: `${dialogId}-description`, className: "oods-dialog__description", children: description }) : null
724
+ ] }),
725
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "oods-dialog__close", "aria-label": dismissLabel, onClick: () => onClose?.(), children: /* @__PURE__ */ jsxRuntime.jsx(StatusIcon, { name: "x" }) })
726
+ ] }),
727
+ hasContent(children) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "oods-dialog__body", children }) : null,
728
+ hasContent(actions) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "oods-dialog__footer", children: actions }) : null
729
+ ]
730
+ }
731
+ );
732
+ }
733
+ );
734
+ Dialog.displayName = "OODS.Dialog";
894
735
  var Card = React3__namespace.forwardRef(
895
736
  ({ as = "div", elevated = false, className, ...rest }, ref) => {
896
737
  const Element = as;
@@ -961,7 +802,7 @@ var StatusBadge = React3__namespace.forwardRef(
961
802
  ...rest
962
803
  }, ref) => {
963
804
  const resolvedStatus = status ?? value ?? "unknown";
964
- const presentation = getStatusPresentation(domain, resolvedStatus);
805
+ const presentation = componentContracts.getStatusPresentation(domain, resolvedStatus);
965
806
  return /* @__PURE__ */ jsxRuntime.jsx(
966
807
  Badge,
967
808
  {
@@ -982,8 +823,8 @@ var StatusBadge = React3__namespace.forwardRef(
982
823
  }
983
824
  );
984
825
  StatusBadge.displayName = "OODS.StatusBadge";
985
- function formatPrice(amount, currency, fallback2) {
986
- if (amount === void 0 || !Number.isFinite(amount)) return fallback2;
826
+ function formatPrice(amount, currency, fallback) {
827
+ if (amount === void 0 || !Number.isFinite(amount)) return fallback;
987
828
  if (!currency) return new Intl.NumberFormat(void 0, { maximumFractionDigits: 2 }).format(amount);
988
829
  try {
989
830
  return new Intl.NumberFormat(void 0, {
@@ -1326,7 +1167,7 @@ var SearchInput = React3__namespace.forwardRef(
1326
1167
  "aria-label": "Clear search",
1327
1168
  disabled,
1328
1169
  onClick: clear,
1329
- children: "\xD7"
1170
+ children: /* @__PURE__ */ jsxRuntime.jsx(StatusIcon, { name: "x" })
1330
1171
  }
1331
1172
  ) : null
1332
1173
  ]
@@ -1514,7 +1355,7 @@ var RelativeTimestamp = React3__namespace.forwardRef(
1514
1355
  const date = asDate(raw);
1515
1356
  if (hideWhenEmpty && !date) return null;
1516
1357
  const nowDate = asDate(now) ?? /* @__PURE__ */ new Date();
1517
- const iso = date?.toISOString() ?? (raw === void 0 ? void 0 : String(raw));
1358
+ const iso = date?.toISOString() ?? (raw === void 0 || raw === "" ? void 0 : String(raw));
1518
1359
  const absolute = componentContracts.formatDateTime(date, { timeZone: timezone });
1519
1360
  const content = relative ?? label ?? text ?? (now !== void 0 && date ? relativeLabel(date, nowDate) : absolute || children || "Unknown time");
1520
1361
  return /* @__PURE__ */ jsxRuntime.jsx(
package/dist/ported.js CHANGED
@@ -1,2 +1,2 @@
1
- export { AuditTimeline, CancellationSummary, PaginationBar, PriceBadge, RelativeTimestamp, SearchInput, StatusBadge, StatusTimeline } from './chunk-BPEHI36W.js';
2
- import './chunk-G74PKSW3.js';
1
+ export { AuditTimeline, CancellationSummary, PaginationBar, PriceBadge, RelativeTimestamp, SearchInput, StatusBadge, StatusTimeline } from './chunk-OFTMKX3W.js';
2
+ import './chunk-2VEQWKGK.js';