@oods/components-react 0.3.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.
@@ -0,0 +1,1551 @@
1
+ 'use strict';
2
+
3
+ var componentContracts = require('@oods/component-contracts');
4
+ var React3 = require('react');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+ var reactSlot = require('@radix-ui/react-slot');
7
+
8
+ function _interopNamespace(e) {
9
+ if (e && e.__esModule) return e;
10
+ var n = Object.create(null);
11
+ if (e) {
12
+ Object.keys(e).forEach(function (k) {
13
+ if (k !== 'default') {
14
+ var d = Object.getOwnPropertyDescriptor(e, k);
15
+ Object.defineProperty(n, k, d.get ? d : {
16
+ enumerable: true,
17
+ get: function () { return e[k]; }
18
+ });
19
+ }
20
+ });
21
+ }
22
+ n.default = e;
23
+ return Object.freeze(n);
24
+ }
25
+
26
+ var React3__namespace = /*#__PURE__*/_interopNamespace(React3);
27
+
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
+ var classes = (...values) => values.filter(Boolean).join(" ");
361
+ var describedByIds = (id, help, validation, nativeDescription) => [
362
+ nativeDescription,
363
+ help ? `${id}-description` : void 0,
364
+ validation?.message ? validation.id ?? `${id}-validation` : void 0
365
+ ].filter(Boolean).join(" ") || void 0;
366
+ var RequiredIndicator = ({
367
+ required,
368
+ children
369
+ }) => required && children !== null ? children ?? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "oods-field-required form-field__required-indicator", "aria-hidden": "true", children: "*" }) : null;
370
+ var FieldFrame = ({
371
+ id,
372
+ label,
373
+ help,
374
+ description,
375
+ validation,
376
+ density = "comfortable",
377
+ required,
378
+ requiredIndicator,
379
+ checkboxControl,
380
+ className,
381
+ style,
382
+ children
383
+ }) => {
384
+ const resolvedHelp = help ?? description;
385
+ const validationTone = validation?.state === "error" ? "critical" : validation?.state === "warning" ? "warning" : validation?.state;
386
+ const validationTokens = validationTone ? getToneTokenSet(validationTone) : void 0;
387
+ const validationStyle = validationTokens ? {
388
+ "--cmp-input-message-border": validationTokens.border,
389
+ "--cmp-input-message-text": validationTokens.foreground,
390
+ "--form-field-border": validationTokens.border,
391
+ "--form-field-border-active": validationTokens.border,
392
+ "--form-field-background-active": validationTokens.background,
393
+ "--form-field-validation-color": validationTokens.foreground,
394
+ "--form-field-focus-inner": validationTokens.border,
395
+ "--form-field-focus-outer": validationTokens.background,
396
+ "--form-field-accent": validationTokens.foreground
397
+ } : {};
398
+ return /* @__PURE__ */ jsxRuntime.jsxs(
399
+ "div",
400
+ {
401
+ className: classes(
402
+ "oods-field",
403
+ "form-field",
404
+ checkboxControl !== void 0 && "oods-checkbox",
405
+ className
406
+ ),
407
+ style: { ...validationStyle, ...style },
408
+ "data-density": density,
409
+ "data-validation-state": validation?.state,
410
+ children: [
411
+ /* @__PURE__ */ jsxRuntime.jsxs(
412
+ "label",
413
+ {
414
+ className: "form-field__label",
415
+ htmlFor: id,
416
+ children: [
417
+ checkboxControl,
418
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "oods-field-label", children: label }),
419
+ /* @__PURE__ */ jsxRuntime.jsx(RequiredIndicator, { required, children: requiredIndicator })
420
+ ]
421
+ }
422
+ ),
423
+ checkboxControl === void 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "form-field__control", children }) : null,
424
+ resolvedHelp ? /* @__PURE__ */ jsxRuntime.jsx("p", { id: `${id}-description`, className: "oods-field-help form-field__description", children: resolvedHelp }) : null,
425
+ validation?.message ? /* @__PURE__ */ jsxRuntime.jsx(
426
+ "p",
427
+ {
428
+ id: validation.id ?? `${id}-validation`,
429
+ className: "oods-field-error form-field__validation",
430
+ role: validation.state === "error" ? "alert" : "status",
431
+ "aria-live": validation.state === "error" ? "assertive" : "polite",
432
+ "data-state": validation.state,
433
+ children: validation.message
434
+ }
435
+ ) : null
436
+ ]
437
+ }
438
+ );
439
+ };
440
+ var InputPrimitive = React3__namespace.forwardRef(
441
+ ({
442
+ id,
443
+ label,
444
+ help,
445
+ description,
446
+ validation,
447
+ density,
448
+ requiredIndicator,
449
+ className,
450
+ style,
451
+ inputClassName,
452
+ inputStyle,
453
+ onChange,
454
+ onInput,
455
+ onValueChange,
456
+ onUpdate,
457
+ required,
458
+ "aria-describedby": nativeDescription,
459
+ type = "text",
460
+ ...rest
461
+ }, ref) => {
462
+ if (type === "datetime-local") {
463
+ if (rest.value !== void 0) rest.value = componentContracts.dateTimeInputValue(rest.value);
464
+ if (rest.defaultValue !== void 0) rest.defaultValue = componentContracts.dateTimeInputValue(rest.defaultValue);
465
+ }
466
+ const resolvedHelp = help ?? description;
467
+ const handleChange = (event) => {
468
+ onChange?.(event);
469
+ if (!event.defaultPrevented) {
470
+ onValueChange?.(event.currentTarget.value);
471
+ onUpdate?.(event.currentTarget.value);
472
+ }
473
+ };
474
+ return /* @__PURE__ */ jsxRuntime.jsx(
475
+ FieldFrame,
476
+ {
477
+ id,
478
+ label,
479
+ help: resolvedHelp,
480
+ validation,
481
+ density,
482
+ required,
483
+ requiredIndicator,
484
+ className,
485
+ style,
486
+ children: /* @__PURE__ */ jsxRuntime.jsx(
487
+ "input",
488
+ {
489
+ ref,
490
+ id,
491
+ type,
492
+ className: classes("oods-field-control", "form-field__input", inputClassName),
493
+ style: inputStyle,
494
+ "data-oods-component": "Input",
495
+ "data-validation-state": validation?.state,
496
+ "aria-describedby": describedByIds(id, resolvedHelp, validation, nativeDescription),
497
+ "aria-invalid": validation?.state === "error" || void 0,
498
+ required,
499
+ onInput,
500
+ onChange: handleChange,
501
+ ...rest
502
+ }
503
+ )
504
+ }
505
+ );
506
+ }
507
+ );
508
+ InputPrimitive.displayName = "OODS.Input";
509
+ var Input = InputPrimitive;
510
+ var DatePicker = React3__namespace.forwardRef(
511
+ ({ pickerClassName, pickerStyle, inputClassName, inputStyle, ...rest }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
512
+ Input,
513
+ {
514
+ ref,
515
+ type: "date",
516
+ "data-oods-component": "DatePicker",
517
+ inputClassName: classes("form-field__date-picker", inputClassName, pickerClassName),
518
+ inputStyle: { ...inputStyle, ...pickerStyle },
519
+ ...rest
520
+ }
521
+ )
522
+ );
523
+ DatePicker.displayName = "OODS.DatePicker";
524
+ var Checkbox = React3__namespace.forwardRef(
525
+ ({
526
+ id,
527
+ label,
528
+ help,
529
+ description,
530
+ validation,
531
+ density,
532
+ requiredIndicator,
533
+ className,
534
+ style,
535
+ checkboxClassName,
536
+ checkboxStyle,
537
+ onChange,
538
+ onCheckedChange,
539
+ onUpdate,
540
+ required,
541
+ "aria-describedby": nativeDescription,
542
+ ...rest
543
+ }, ref) => {
544
+ const resolvedHelp = help ?? description;
545
+ const handleChange = (event) => {
546
+ onChange?.(event);
547
+ if (!event.defaultPrevented) {
548
+ onCheckedChange?.(event.currentTarget.checked);
549
+ onUpdate?.(event.currentTarget.checked);
550
+ }
551
+ };
552
+ const control = /* @__PURE__ */ jsxRuntime.jsx(
553
+ "input",
554
+ {
555
+ ref,
556
+ id,
557
+ type: "checkbox",
558
+ className: classes("form-field__checkbox", checkboxClassName),
559
+ style: checkboxStyle,
560
+ "data-oods-component": "Checkbox",
561
+ "data-validation-state": validation?.state,
562
+ "aria-describedby": describedByIds(id, resolvedHelp, validation, nativeDescription),
563
+ "aria-invalid": validation?.state === "error" || void 0,
564
+ required,
565
+ onChange: handleChange,
566
+ ...rest
567
+ }
568
+ );
569
+ return /* @__PURE__ */ jsxRuntime.jsx(
570
+ FieldFrame,
571
+ {
572
+ id,
573
+ label,
574
+ help: resolvedHelp,
575
+ validation,
576
+ density,
577
+ required,
578
+ requiredIndicator,
579
+ checkboxControl: control,
580
+ className,
581
+ style
582
+ }
583
+ );
584
+ }
585
+ );
586
+ Checkbox.displayName = "OODS.Checkbox";
587
+ var Select = React3__namespace.forwardRef(
588
+ ({
589
+ id,
590
+ label,
591
+ help,
592
+ description,
593
+ validation,
594
+ density,
595
+ requiredIndicator,
596
+ className,
597
+ style,
598
+ options,
599
+ placeholder,
600
+ selectClassName,
601
+ selectStyle,
602
+ onChange,
603
+ onValueChange,
604
+ onUpdate,
605
+ children,
606
+ required,
607
+ "aria-describedby": nativeDescription,
608
+ ...rest
609
+ }, ref) => {
610
+ const resolvedHelp = help ?? description;
611
+ const handleChange = (event) => {
612
+ onChange?.(event);
613
+ if (!event.defaultPrevented) {
614
+ onValueChange?.(event.currentTarget.value);
615
+ onUpdate?.(event.currentTarget.value);
616
+ }
617
+ };
618
+ return /* @__PURE__ */ jsxRuntime.jsx(
619
+ FieldFrame,
620
+ {
621
+ id,
622
+ label,
623
+ help: resolvedHelp,
624
+ validation,
625
+ density,
626
+ required,
627
+ requiredIndicator,
628
+ className,
629
+ style,
630
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
631
+ "select",
632
+ {
633
+ ref,
634
+ id,
635
+ className: classes("oods-field-control", "form-field__select", selectClassName),
636
+ style: selectStyle,
637
+ "data-oods-component": "Select",
638
+ "data-validation-state": validation?.state,
639
+ "aria-describedby": describedByIds(id, resolvedHelp, validation, nativeDescription),
640
+ "aria-invalid": validation?.state === "error" || void 0,
641
+ required,
642
+ onChange: handleChange,
643
+ ...rest,
644
+ children: [
645
+ placeholder ? /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: placeholder }) : null,
646
+ children ?? options?.map((option) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, option.value))
647
+ ]
648
+ }
649
+ )
650
+ }
651
+ );
652
+ }
653
+ );
654
+ Select.displayName = "OODS.Select";
655
+ var Textarea = React3__namespace.forwardRef(
656
+ ({
657
+ id,
658
+ label,
659
+ help,
660
+ description,
661
+ validation,
662
+ density,
663
+ requiredIndicator,
664
+ className,
665
+ style,
666
+ textareaClassName,
667
+ textareaStyle,
668
+ onChange,
669
+ onInput,
670
+ onValueChange,
671
+ onUpdate,
672
+ rows = 4,
673
+ required,
674
+ "aria-describedby": nativeDescription,
675
+ ...rest
676
+ }, ref) => {
677
+ const resolvedHelp = help ?? description;
678
+ const handleChange = (event) => {
679
+ onChange?.(event);
680
+ if (!event.defaultPrevented) {
681
+ onValueChange?.(event.currentTarget.value);
682
+ onUpdate?.(event.currentTarget.value);
683
+ }
684
+ };
685
+ return /* @__PURE__ */ jsxRuntime.jsx(
686
+ FieldFrame,
687
+ {
688
+ id,
689
+ label,
690
+ help: resolvedHelp,
691
+ validation,
692
+ density,
693
+ required,
694
+ requiredIndicator,
695
+ className,
696
+ style,
697
+ children: /* @__PURE__ */ jsxRuntime.jsx(
698
+ "textarea",
699
+ {
700
+ ref,
701
+ id,
702
+ className: classes(
703
+ "oods-field-control",
704
+ "form-field__input",
705
+ "form-field__textarea",
706
+ textareaClassName
707
+ ),
708
+ style: textareaStyle,
709
+ "data-oods-component": "Textarea",
710
+ "data-validation-state": validation?.state,
711
+ "aria-describedby": describedByIds(id, resolvedHelp, validation, nativeDescription),
712
+ "aria-invalid": validation?.state === "error" || void 0,
713
+ rows,
714
+ required,
715
+ onInput,
716
+ onChange: handleChange,
717
+ ...rest
718
+ }
719
+ )
720
+ }
721
+ );
722
+ }
723
+ );
724
+ Textarea.displayName = "OODS.Textarea";
725
+ var classes2 = (...values) => values.filter(Boolean).join(" ");
726
+ var Badge = React3__namespace.forwardRef(
727
+ ({
728
+ content,
729
+ status,
730
+ domain = "subscription",
731
+ tone: toneOverride,
732
+ emphasis = "subtle",
733
+ icon,
734
+ iconPosition = "start",
735
+ showIcon,
736
+ children,
737
+ className,
738
+ style,
739
+ title,
740
+ "aria-label": ariaLabel,
741
+ ...rest
742
+ }, ref) => {
743
+ const presentation = status ? getStatusPresentation(domain, status) : void 0;
744
+ const tone = toneOverride ?? presentation?.tone ?? "neutral";
745
+ const tokenSet = toneOverride !== void 0 ? getToneTokenSet(tone) : emphasis === "solid" ? presentation?.badge.solid ?? getToneTokenSet(tone) : presentation?.badge.subtle ?? getToneTokenSet(tone);
746
+ const resolvedContent = children ?? content ?? presentation?.label ?? status;
747
+ const resolvedIcon = icon ?? resolveStatusGlyph(presentation?.iconName);
748
+ const resolvedShowIcon = showIcon ?? resolvedIcon !== void 0;
749
+ const iconNode = resolvedShowIcon && resolvedIcon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "oods-badge__icon statusable-badge__icon", "aria-hidden": "true", children: resolvedIcon }) : null;
750
+ return /* @__PURE__ */ jsxRuntime.jsxs(
751
+ "span",
752
+ {
753
+ ref,
754
+ className: classes2("oods-badge", "statusable-badge", className),
755
+ "data-oods-component": "Badge",
756
+ "data-status": status,
757
+ "data-domain": domain,
758
+ "data-tone": tone,
759
+ "data-emphasis": emphasis,
760
+ title: title ?? presentation?.description,
761
+ "aria-label": ariaLabel,
762
+ style: {
763
+ "--cmp-badge-background": tokenSet.background,
764
+ "--cmp-badge-border": tokenSet.border,
765
+ "--cmp-badge-text": tokenSet.foreground,
766
+ "--statusable-badge-background": tokenSet.background,
767
+ "--statusable-badge-border": tokenSet.border,
768
+ "--statusable-badge-foreground": tokenSet.foreground,
769
+ "--statusable-badge-icon-color": tokenSet.foreground,
770
+ ...style
771
+ },
772
+ ...rest,
773
+ children: [
774
+ iconPosition === "start" ? iconNode : null,
775
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "oods-badge__label statusable-badge__label", children: resolvedContent }),
776
+ iconPosition === "end" ? iconNode : null
777
+ ]
778
+ }
779
+ );
780
+ }
781
+ );
782
+ Badge.displayName = "OODS.Badge";
783
+ var Banner = React3__namespace.forwardRef(
784
+ ({
785
+ title,
786
+ detail,
787
+ description,
788
+ content,
789
+ status,
790
+ domain = "subscription",
791
+ tone: toneOverride,
792
+ emphasis = "subtle",
793
+ icon,
794
+ actions,
795
+ onDismiss,
796
+ dismissLabel = "Dismiss notification",
797
+ showIcon,
798
+ children,
799
+ className,
800
+ style,
801
+ ...rest
802
+ }, ref) => {
803
+ const presentation = status ? getStatusPresentation(domain, status) : void 0;
804
+ const tone = toneOverride ?? presentation?.tone ?? "neutral";
805
+ const tokenSet = emphasis === "solid" ? presentation?.banner.solid ?? getToneTokenSet(tone) : presentation?.banner.subtle ?? getBannerToneTokenSet(tone);
806
+ const heading = title ?? presentation?.label ?? status;
807
+ const body = detail ?? description ?? children ?? content ?? presentation?.description;
808
+ const role = tone === "critical" || tone === "danger" ? "alert" : "status";
809
+ const resolvedIcon = icon ?? resolveStatusGlyph(presentation?.iconName);
810
+ const resolvedShowIcon = showIcon ?? resolvedIcon !== void 0;
811
+ return /* @__PURE__ */ jsxRuntime.jsxs(
812
+ "div",
813
+ {
814
+ ref,
815
+ role,
816
+ "aria-live": role === "alert" ? "assertive" : "polite",
817
+ className: classes2("oods-banner", "statusable-banner", className),
818
+ "data-oods-component": "Banner",
819
+ "data-status": status,
820
+ "data-domain": domain,
821
+ "data-tone": tone,
822
+ "data-emphasis": emphasis,
823
+ style: {
824
+ "--cmp-banner-background": tokenSet.background,
825
+ "--cmp-banner-border": tokenSet.border,
826
+ "--cmp-banner-text": tokenSet.foreground,
827
+ "--statusable-banner-background": tokenSet.background,
828
+ "--statusable-banner-border": tokenSet.border,
829
+ "--statusable-banner-foreground": tokenSet.foreground,
830
+ "--statusable-banner-icon-color": tokenSet.foreground,
831
+ ...style
832
+ },
833
+ ...rest,
834
+ children: [
835
+ resolvedShowIcon && resolvedIcon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "oods-banner__icon statusable-banner__icon", "aria-hidden": "true", children: resolvedIcon }) : null,
836
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "oods-banner__content statusable-banner__content", children: [
837
+ heading ? /* @__PURE__ */ jsxRuntime.jsx("strong", { className: "oods-banner__title statusable-banner__title", children: heading }) : null,
838
+ body ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "oods-banner__detail statusable-banner__description", children: body }) : null,
839
+ actions ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "oods-banner__actions statusable-banner__actions", children: actions }) : null
840
+ ] }),
841
+ onDismiss ? /* @__PURE__ */ jsxRuntime.jsx(
842
+ "button",
843
+ {
844
+ type: "button",
845
+ className: "oods-banner-dismiss oods-banner__dismiss statusable-banner__dismiss",
846
+ "aria-label": dismissLabel,
847
+ onClick: () => onDismiss(),
848
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\xD7" })
849
+ }
850
+ ) : null
851
+ ]
852
+ }
853
+ );
854
+ }
855
+ );
856
+ Banner.displayName = "OODS.Banner";
857
+ var Button = React3__namespace.forwardRef(
858
+ ({
859
+ asChild = false,
860
+ content,
861
+ children,
862
+ className,
863
+ intent = "neutral",
864
+ size = "md",
865
+ style,
866
+ type,
867
+ onClick,
868
+ onActivate,
869
+ ...rest
870
+ }, ref) => {
871
+ const Component = asChild ? reactSlot.Slot : "button";
872
+ const handleClick = (event) => {
873
+ onClick?.(event);
874
+ if (!event.defaultPrevented) onActivate?.(event);
875
+ };
876
+ const nativeProps = asChild ? {} : { type: type ?? "button" };
877
+ return /* @__PURE__ */ jsxRuntime.jsx(
878
+ Component,
879
+ {
880
+ ref,
881
+ className: classes2("oods-button", className),
882
+ "data-oods-component": "Button",
883
+ "data-intent": intent,
884
+ "data-size": size,
885
+ style: {
886
+ "--cmp-button-background": "var(--sys-surface-interactive-primary-default)",
887
+ "--cmp-button-background-hover": "var(--sys-surface-interactive-primary-hover)",
888
+ "--cmp-button-background-disabled": "var(--sys-surface-disabled)",
889
+ "--cmp-button-border": "var(--sys-border-subtle)",
890
+ "--cmp-button-text": "var(--sys-text-on-interactive)",
891
+ "--cmp-button-text-disabled": "var(--sys-text-disabled)",
892
+ ...style
893
+ },
894
+ onClick: handleClick,
895
+ ...nativeProps,
896
+ ...rest,
897
+ children: children ?? content
898
+ }
899
+ );
900
+ }
901
+ );
902
+ Button.displayName = "OODS.Button";
903
+ var Card = React3__namespace.forwardRef(
904
+ ({ as = "div", elevated = false, className, ...rest }, ref) => {
905
+ const Element = as;
906
+ return /* @__PURE__ */ jsxRuntime.jsx(
907
+ Element,
908
+ {
909
+ ref,
910
+ className: classes2("oods-card", className),
911
+ "data-oods-component": "Card",
912
+ "data-elevated": elevated ? "true" : "false",
913
+ ...rest
914
+ }
915
+ );
916
+ }
917
+ );
918
+ Card.displayName = "OODS.Card";
919
+ var Text = React3__namespace.forwardRef(
920
+ ({
921
+ as = "span",
922
+ content,
923
+ children,
924
+ label,
925
+ size = "md",
926
+ weight = "regular",
927
+ className,
928
+ "aria-description": ariaDescription,
929
+ ...rest
930
+ }, ref) => {
931
+ const Element = as;
932
+ return /* @__PURE__ */ jsxRuntime.jsx(
933
+ Element,
934
+ {
935
+ ref,
936
+ className: classes2("oods-text", className),
937
+ "data-oods-component": "Text",
938
+ "data-size": size,
939
+ "data-weight": weight,
940
+ "aria-description": ariaDescription ?? label,
941
+ ...rest,
942
+ children: children ?? content
943
+ }
944
+ );
945
+ }
946
+ );
947
+ Text.displayName = "OODS.Text";
948
+ var classes3 = (...values) => values.filter(Boolean).join(" ");
949
+ var humanize = (value) => value.split(/[_-]/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)).join(" ");
950
+ var StatusBadge = React3__namespace.forwardRef(
951
+ ({
952
+ status,
953
+ value,
954
+ label,
955
+ children,
956
+ content,
957
+ domain = "subscription",
958
+ tone,
959
+ emphasis = "subtle",
960
+ showIcon = true,
961
+ field,
962
+ statusField,
963
+ domainField,
964
+ readOnly,
965
+ compact,
966
+ variant,
967
+ title,
968
+ "aria-label": ariaLabel,
969
+ ...rest
970
+ }, ref) => {
971
+ const resolvedStatus = status ?? value ?? "unknown";
972
+ const presentation = getStatusPresentation(domain, resolvedStatus);
973
+ return /* @__PURE__ */ jsxRuntime.jsx(
974
+ Badge,
975
+ {
976
+ ref,
977
+ status: resolvedStatus,
978
+ domain,
979
+ tone: tone === "lifecycle" ? void 0 : tone,
980
+ emphasis: variant === "solid" ? "solid" : emphasis,
981
+ showIcon,
982
+ content: children ?? content,
983
+ title: title ?? presentation.description,
984
+ "aria-label": ariaLabel ?? label ?? `Status: ${presentation.label}`,
985
+ "data-oods-component": "StatusBadge",
986
+ ...rest
987
+ }
988
+ );
989
+ }
990
+ );
991
+ StatusBadge.displayName = "OODS.StatusBadge";
992
+ function formatPrice(amount, currency, fallback2) {
993
+ if (amount === void 0 || !Number.isFinite(amount)) return fallback2;
994
+ if (!currency) return new Intl.NumberFormat(void 0, { maximumFractionDigits: 2 }).format(amount);
995
+ try {
996
+ return new Intl.NumberFormat(void 0, {
997
+ style: "currency",
998
+ currency: currency.toUpperCase()
999
+ }).format(amount);
1000
+ } catch {
1001
+ return `${currency.toUpperCase()} ${amount.toFixed(2)}`;
1002
+ }
1003
+ }
1004
+ var PriceBadge = React3__namespace.forwardRef(
1005
+ ({
1006
+ amountCents,
1007
+ unitAmountCents,
1008
+ minorUnits,
1009
+ amount,
1010
+ unitAmount,
1011
+ currency,
1012
+ currencyCode,
1013
+ label,
1014
+ value,
1015
+ children,
1016
+ emphasis = "subtle",
1017
+ field,
1018
+ amountField,
1019
+ currencyField,
1020
+ intervalField,
1021
+ minorUnitsParameter,
1022
+ className,
1023
+ ...rest
1024
+ }, ref) => {
1025
+ const cents = amountCents ?? unitAmountCents;
1026
+ const resolvedCurrency = currency ?? currencyCode;
1027
+ const resolvedAmount = cents === void 0 ? amount ?? unitAmount : cents / componentContracts.currencyMinorUnits(resolvedCurrency, minorUnits);
1028
+ const content = label ?? children ?? formatPrice(resolvedAmount, resolvedCurrency, value ?? "Price");
1029
+ return /* @__PURE__ */ jsxRuntime.jsx(
1030
+ Badge,
1031
+ {
1032
+ ref,
1033
+ content,
1034
+ emphasis,
1035
+ className: classes3("oods-price-badge", className),
1036
+ "data-oods-component": "PriceBadge",
1037
+ "data-badge-variant": "price",
1038
+ "data-price": "true",
1039
+ "data-currency": resolvedCurrency?.toUpperCase(),
1040
+ ...rest
1041
+ }
1042
+ );
1043
+ }
1044
+ );
1045
+ PriceBadge.displayName = "OODS.PriceBadge";
1046
+ function isRecord(value) {
1047
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1048
+ }
1049
+ function eventText(record, keys) {
1050
+ for (const key of keys) {
1051
+ const value = record[key];
1052
+ if (typeof value === "string") return value;
1053
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
1054
+ }
1055
+ return void 0;
1056
+ }
1057
+ function normalizeTimelineEvents(values) {
1058
+ return values.flatMap((value, index) => {
1059
+ if (!isRecord(value)) {
1060
+ return value === void 0 || value === null ? [] : [{ label: String(value) }];
1061
+ }
1062
+ const from = eventText(value, ["from"]);
1063
+ const to = eventText(value, ["to"]);
1064
+ return [{
1065
+ id: eventText(value, ["id"]) ?? `event-${index}`,
1066
+ label: eventText(value, ["label", "title"]) ?? (eventText(value, ["event", "status", "state"]) ? humanize(eventText(value, ["event", "status", "state"])) : void 0) ?? eventText(value, ["text", "name"]) ?? (to ? from ? `${humanize(from)} \u2192 ${humanize(to)}` : humanize(to) : void 0) ?? "Event",
1067
+ from,
1068
+ to,
1069
+ status: eventText(value, ["status"]),
1070
+ state: eventText(value, ["state"]),
1071
+ timestamp: eventText(value, ["timestamp", "datetime", "time", "at", "createdAt", "updatedAt"]),
1072
+ actor: eventText(value, ["actorId", "actor_id", "actor"]),
1073
+ actorId: eventText(value, ["actorId", "actor_id"]),
1074
+ reason: eventText(value, ["reason"]),
1075
+ detail: eventText(value, ["detail", "description", "message"]) ?? (to ? from ? `${humanize(from)} \u2192 ${humanize(to)}` : humanize(to) : void 0)
1076
+ }];
1077
+ });
1078
+ }
1079
+ var TimelinePrimitive = React3__namespace.forwardRef(
1080
+ ({
1081
+ componentId,
1082
+ defaultTitle,
1083
+ kind,
1084
+ source,
1085
+ title = defaultTitle,
1086
+ events,
1087
+ history,
1088
+ entries,
1089
+ stateHistory,
1090
+ status,
1091
+ allowedTransitions,
1092
+ showActorId = true,
1093
+ showReason = true,
1094
+ maxVisible,
1095
+ children,
1096
+ field,
1097
+ historyField,
1098
+ statesParameter,
1099
+ auditLogField,
1100
+ createdField,
1101
+ updatedField,
1102
+ eventField,
1103
+ eventTimestampField,
1104
+ eventOptionsParameter,
1105
+ showFromState,
1106
+ className,
1107
+ ...rest
1108
+ }, ref) => {
1109
+ const allEvents = normalizeTimelineEvents(source ?? events ?? history ?? entries ?? stateHistory ?? []);
1110
+ const visibleEvents = Number.isFinite(maxVisible) ? allEvents.slice(0, Math.max(0, Math.floor(maxVisible ?? 0))) : allEvents;
1111
+ const transitionCount = allowedTransitions?.length ?? 0;
1112
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1113
+ "div",
1114
+ {
1115
+ ref,
1116
+ role: "log",
1117
+ "aria-label": typeof title === "string" ? title : defaultTitle,
1118
+ className: classes3("oods-timeline", `oods-${kind}-timeline`, className),
1119
+ "data-oods-component": componentId,
1120
+ "data-timeline-type": kind,
1121
+ ...rest,
1122
+ children: [
1123
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "oods-timeline__title", children: title }),
1124
+ kind === "status" && status ? /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "oods-timeline__current", "data-timeline-current": "true", children: [
1125
+ "Current status: ",
1126
+ humanize(status)
1127
+ ] }) : null,
1128
+ transitionCount > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
1129
+ "Allowed transitions: ",
1130
+ allowedTransitions?.join(", ")
1131
+ ] }) : null,
1132
+ children ?? /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "oods-timeline__events", children: visibleEvents.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("li", { "data-timeline-empty": "true", children: "No events" }) : visibleEvents.map((event, index) => {
1133
+ const timestamp = event.timestamp ?? event.datetime;
1134
+ const actor = event.actor ?? event.actorId ?? event.actor_id;
1135
+ const eventLabel = event.label ?? event.title ?? (event.from && event.to ? `${humanize(event.from)} \u2192 ${humanize(event.to)}` : void 0) ?? (event.status ? humanize(event.status) : void 0) ?? (event.state ? humanize(event.state) : void 0) ?? "Event";
1136
+ const detail = event.detail ?? event.description;
1137
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs("article", { className: "oods-timeline__event", "data-timeline-event": "true", children: [
1138
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "oods-timeline__label", children: eventLabel }),
1139
+ timestamp ? /* @__PURE__ */ jsxRuntime.jsx("time", { dateTime: timestamp, children: componentContracts.formatDateTime(timestamp) }) : null,
1140
+ detail ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "oods-timeline__detail", children: detail }) : null,
1141
+ showActorId && actor ? /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "oods-timeline__actor", children: [
1142
+ "Actor: ",
1143
+ actor
1144
+ ] }) : null,
1145
+ showReason && event.reason ? /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "oods-timeline__reason", children: [
1146
+ "Reason: ",
1147
+ event.reason
1148
+ ] }) : null
1149
+ ] }) }, event.id ?? `${timestamp ?? "event"}-${index}`);
1150
+ }) })
1151
+ ]
1152
+ }
1153
+ );
1154
+ }
1155
+ );
1156
+ TimelinePrimitive.displayName = "OODS.TimelinePrimitive";
1157
+ var StatusTimeline = React3__namespace.forwardRef(
1158
+ (props, ref) => /* @__PURE__ */ jsxRuntime.jsx(
1159
+ TimelinePrimitive,
1160
+ {
1161
+ ref,
1162
+ componentId: "StatusTimeline",
1163
+ defaultTitle: "Status Timeline",
1164
+ kind: "status",
1165
+ ...props
1166
+ }
1167
+ )
1168
+ );
1169
+ StatusTimeline.displayName = "OODS.StatusTimeline";
1170
+ var AuditTimeline = React3__namespace.forwardRef(
1171
+ ({ auditLog, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
1172
+ TimelinePrimitive,
1173
+ {
1174
+ ref,
1175
+ componentId: "AuditTimeline",
1176
+ defaultTitle: "Audit Timeline",
1177
+ kind: "audit",
1178
+ source: auditLog,
1179
+ ...props
1180
+ }
1181
+ )
1182
+ );
1183
+ AuditTimeline.displayName = "OODS.AuditTimeline";
1184
+ var CancellationSummary = React3__namespace.forwardRef(
1185
+ ({
1186
+ label,
1187
+ title = label ?? "Cancellation Summary",
1188
+ cancelAtPeriodEnd,
1189
+ requestedAt,
1190
+ reason,
1191
+ code,
1192
+ children,
1193
+ field,
1194
+ cancelAtPeriodEndField,
1195
+ requestedAtField,
1196
+ reasonField,
1197
+ codeField,
1198
+ className,
1199
+ ...rest
1200
+ }, ref) => {
1201
+ const childValue = typeof children === "boolean" ? children ? "Yes" : "No" : children;
1202
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1203
+ "section",
1204
+ {
1205
+ ref,
1206
+ className: classes3("oods-cancellation-summary", className),
1207
+ "data-oods-component": "CancellationSummary",
1208
+ "data-summary-type": "cancellation",
1209
+ "data-cancellation-state": cancelAtPeriodEnd === void 0 ? void 0 : String(cancelAtPeriodEnd),
1210
+ ...rest,
1211
+ children: [
1212
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { children: title }),
1213
+ /* @__PURE__ */ jsxRuntime.jsxs("dl", { children: [
1214
+ cancelAtPeriodEnd !== void 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-summary-item": "true", children: [
1215
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { children: "Cancel at period end" }),
1216
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { children: cancelAtPeriodEnd ? "Yes" : "No" })
1217
+ ] }) : null,
1218
+ requestedAt ? /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-summary-item": "true", children: [
1219
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { children: "Requested at" }),
1220
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { children: componentContracts.formatDateTime(requestedAt) })
1221
+ ] }) : null,
1222
+ reason ? /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-summary-item": "true", children: [
1223
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { children: "Reason" }),
1224
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { children: reason })
1225
+ ] }) : null,
1226
+ code ? /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-summary-item": "true", children: [
1227
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { children: "Code" }),
1228
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { children: componentContracts.formatReadOnlyValue(code, "string", true) })
1229
+ ] }) : null,
1230
+ childValue !== void 0 && childValue !== null ? /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-summary-item": "true", children: [
1231
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { children: label ?? "Cancellation" }),
1232
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { children: childValue })
1233
+ ] }) : null
1234
+ ] })
1235
+ ]
1236
+ }
1237
+ );
1238
+ }
1239
+ );
1240
+ CancellationSummary.displayName = "OODS.CancellationSummary";
1241
+ var SearchInput = React3__namespace.forwardRef(
1242
+ ({
1243
+ id,
1244
+ label = "Search",
1245
+ value,
1246
+ defaultValue = "",
1247
+ placeholder = "Search\u2026",
1248
+ clearable = true,
1249
+ debounceMs,
1250
+ debounce,
1251
+ minQueryLength = 0,
1252
+ field,
1253
+ placeholderParameter,
1254
+ debounceParameter,
1255
+ minQueryLengthParameter,
1256
+ clearableParameter,
1257
+ disabled,
1258
+ className,
1259
+ onChange,
1260
+ onValueChange,
1261
+ onUpdate,
1262
+ onSearch,
1263
+ onClear,
1264
+ onKeyDown,
1265
+ ...rest
1266
+ }, ref) => {
1267
+ const generatedId = React3__namespace.useId();
1268
+ const resolvedId = id ?? `oods-search-${generatedId.replace(/:/g, "")}`;
1269
+ const [uncontrolledValue, setUncontrolledValue] = React3__namespace.useState(defaultValue);
1270
+ const currentValue = value ?? uncontrolledValue;
1271
+ const timerRef = React3__namespace.useRef(void 0);
1272
+ const delay = Math.max(0, debounceMs ?? debounce ?? 0);
1273
+ React3__namespace.useEffect(() => () => {
1274
+ if (timerRef.current !== void 0) clearTimeout(timerRef.current);
1275
+ }, []);
1276
+ const publish = (nextValue) => {
1277
+ if (timerRef.current !== void 0) clearTimeout(timerRef.current);
1278
+ const emit = () => {
1279
+ if (nextValue.length >= minQueryLength || nextValue.length === 0) {
1280
+ onValueChange?.(nextValue);
1281
+ onUpdate?.(nextValue);
1282
+ onSearch?.(nextValue);
1283
+ }
1284
+ };
1285
+ if (delay === 0) emit();
1286
+ else timerRef.current = setTimeout(emit, delay);
1287
+ };
1288
+ const updateValue = (nextValue) => {
1289
+ if (value === void 0) setUncontrolledValue(nextValue);
1290
+ publish(nextValue);
1291
+ };
1292
+ const handleChange = (event) => {
1293
+ onChange?.(event);
1294
+ if (!event.defaultPrevented) updateValue(event.currentTarget.value);
1295
+ };
1296
+ const clear = () => {
1297
+ onClear?.();
1298
+ updateValue("");
1299
+ };
1300
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1301
+ "div",
1302
+ {
1303
+ role: "search",
1304
+ "aria-label": label,
1305
+ className: classes3("oods-search-input", className),
1306
+ "data-oods-component": "SearchInput",
1307
+ "data-behavioral": "search",
1308
+ children: [
1309
+ /* @__PURE__ */ jsxRuntime.jsx(
1310
+ Input,
1311
+ {
1312
+ ...rest,
1313
+ ref,
1314
+ id: resolvedId,
1315
+ type: "search",
1316
+ label,
1317
+ placeholder,
1318
+ value: currentValue,
1319
+ disabled,
1320
+ inputClassName: "oods-search-input__control",
1321
+ onChange: handleChange,
1322
+ onKeyDown: (event) => {
1323
+ onKeyDown?.(event);
1324
+ if (!event.defaultPrevented && event.key === "Escape" && currentValue) clear();
1325
+ }
1326
+ }
1327
+ ),
1328
+ clearable && currentValue ? /* @__PURE__ */ jsxRuntime.jsx(
1329
+ "button",
1330
+ {
1331
+ type: "button",
1332
+ className: "oods-search-input__clear",
1333
+ "aria-label": "Clear search",
1334
+ disabled,
1335
+ onClick: clear,
1336
+ children: "\xD7"
1337
+ }
1338
+ ) : null
1339
+ ]
1340
+ }
1341
+ );
1342
+ }
1343
+ );
1344
+ SearchInput.displayName = "OODS.SearchInput";
1345
+ function visiblePages(page, totalPages) {
1346
+ if (totalPages <= 7) return Array.from({ length: totalPages }, (_, index) => index + 1);
1347
+ const candidates = /* @__PURE__ */ new Set([1, totalPages, page - 1, page, page + 1]);
1348
+ const pages = [...candidates].filter((candidate) => candidate >= 1 && candidate <= totalPages).sort((a, b) => a - b);
1349
+ const result = [];
1350
+ for (const candidate of pages) {
1351
+ const previous = result[result.length - 1];
1352
+ if (typeof previous === "number" && candidate - previous > 1) result.push("ellipsis");
1353
+ result.push(candidate);
1354
+ }
1355
+ return result;
1356
+ }
1357
+ var PaginationBar = React3__namespace.forwardRef(
1358
+ ({
1359
+ page = 1,
1360
+ pageSize = 25,
1361
+ totalItems = 0,
1362
+ totalPages: suppliedTotalPages,
1363
+ pageSizeOptions = [10, 25, 50, 100],
1364
+ showPageSizeSelector = false,
1365
+ showGotoPage = false,
1366
+ showItemRange = true,
1367
+ pageField,
1368
+ pageSizeField,
1369
+ totalItemsField,
1370
+ totalPagesField,
1371
+ pageSizeOptionsParameter,
1372
+ showPageSizeSelectorParameter,
1373
+ showGotoPageParameter,
1374
+ showItemRangeParameter,
1375
+ onPageChange,
1376
+ onPageSizeChange,
1377
+ onChange,
1378
+ onUpdate,
1379
+ className,
1380
+ "aria-label": ariaLabel = "Pagination",
1381
+ ...rest
1382
+ }, ref) => {
1383
+ const safePageSize = Number.isFinite(pageSize) && pageSize > 0 ? Math.floor(pageSize) : 25;
1384
+ const derivedTotalPages = safePageSize > 0 ? Math.ceil(Math.max(0, totalItems) / safePageSize) : 0;
1385
+ const totalPages = Math.max(0, Math.floor(suppliedTotalPages ?? derivedTotalPages));
1386
+ const currentPage = totalPages > 0 ? Math.max(1, Math.min(Math.floor(page), totalPages)) : 1;
1387
+ const goToPage = (nextPage) => {
1388
+ if (totalPages === 0) return;
1389
+ const target = Math.max(1, Math.min(Math.floor(nextPage), totalPages));
1390
+ if (target === currentPage) return;
1391
+ onPageChange?.(target);
1392
+ onChange?.(target);
1393
+ onUpdate?.(target);
1394
+ };
1395
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1396
+ "nav",
1397
+ {
1398
+ ref,
1399
+ "aria-label": ariaLabel,
1400
+ className: classes3("oods-pagination-bar", className),
1401
+ "data-oods-component": "PaginationBar",
1402
+ "data-behavioral": "pagination",
1403
+ ...rest,
1404
+ children: [
1405
+ showItemRange ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1406
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-pagination-count": "true", children: `${totalItems} ${totalItems === 1 ? "record" : "records"}` }),
1407
+ totalItems > 0 && totalPages > 1 && /* @__PURE__ */ jsxRuntime.jsx("span", { "data-pagination-range": "true", children: `Showing ${(currentPage - 1) * safePageSize + 1}\u2013${Math.min(currentPage * safePageSize, totalItems)} of ${totalItems}` })
1408
+ ] }) : null,
1409
+ totalPages > 1 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1410
+ /* @__PURE__ */ jsxRuntime.jsx(
1411
+ "button",
1412
+ {
1413
+ type: "button",
1414
+ "aria-label": "Previous page",
1415
+ "data-pagination-prev": "true",
1416
+ disabled: totalPages === 0 || currentPage <= 1,
1417
+ onClick: () => goToPage(currentPage - 1),
1418
+ children: "\u2039"
1419
+ }
1420
+ ),
1421
+ /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "oods-pagination-bar__pages", children: visiblePages(currentPage, totalPages).map((item, index) => item === "ellipsis" ? /* @__PURE__ */ jsxRuntime.jsx("li", { "aria-hidden": "true", children: "\u2026" }, `ellipsis-${index}`) : /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(
1422
+ "button",
1423
+ {
1424
+ type: "button",
1425
+ "aria-label": `Page ${item}`,
1426
+ "aria-current": item === currentPage ? "page" : void 0,
1427
+ "data-pagination-page": item,
1428
+ onClick: () => goToPage(item),
1429
+ children: item
1430
+ }
1431
+ ) }, item)) }),
1432
+ /* @__PURE__ */ jsxRuntime.jsx(
1433
+ "button",
1434
+ {
1435
+ type: "button",
1436
+ "aria-label": "Next page",
1437
+ "data-pagination-next": "true",
1438
+ disabled: totalPages === 0 || currentPage >= totalPages,
1439
+ onClick: () => goToPage(currentPage + 1),
1440
+ children: "\u203A"
1441
+ }
1442
+ ),
1443
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-pagination-current": "true", children: `Page ${currentPage} of ${Math.max(1, totalPages)}` })
1444
+ ] }) : null,
1445
+ showPageSizeSelector ? /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
1446
+ "Items per page",
1447
+ /* @__PURE__ */ jsxRuntime.jsx(
1448
+ "select",
1449
+ {
1450
+ "aria-label": "Items per page",
1451
+ value: safePageSize,
1452
+ onChange: (event) => onPageSizeChange?.(Number(event.currentTarget.value)),
1453
+ children: pageSizeOptions.map((option) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: option, children: option }, option))
1454
+ }
1455
+ )
1456
+ ] }) : null,
1457
+ showGotoPage ? /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
1458
+ "Go to page",
1459
+ /* @__PURE__ */ jsxRuntime.jsx(
1460
+ "input",
1461
+ {
1462
+ type: "number",
1463
+ min: 1,
1464
+ max: Math.max(1, totalPages),
1465
+ defaultValue: currentPage,
1466
+ onKeyDown: (event) => {
1467
+ if (event.key === "Enter") goToPage(Number(event.currentTarget.value));
1468
+ }
1469
+ }
1470
+ )
1471
+ ] }) : null
1472
+ ]
1473
+ }
1474
+ );
1475
+ }
1476
+ );
1477
+ PaginationBar.displayName = "OODS.PaginationBar";
1478
+ function asDate(value) {
1479
+ if (value === void 0) return void 0;
1480
+ const date = value instanceof Date ? value : new Date(value);
1481
+ return Number.isNaN(date.getTime()) ? void 0 : date;
1482
+ }
1483
+ function relativeLabel(date, now) {
1484
+ const seconds = Math.round((date.getTime() - now.getTime()) / 1e3);
1485
+ const units = [
1486
+ ["year", 31536e3],
1487
+ ["month", 2592e3],
1488
+ ["day", 86400],
1489
+ ["hour", 3600],
1490
+ ["minute", 60]
1491
+ ];
1492
+ const [unit, divisor] = units.find(([, candidate]) => Math.abs(seconds) >= candidate) ?? ["second", 1];
1493
+ return new Intl.RelativeTimeFormat(void 0, { numeric: "auto" }).format(
1494
+ Math.round(seconds / divisor),
1495
+ unit
1496
+ );
1497
+ }
1498
+ var RelativeTimestamp = React3__namespace.forwardRef(
1499
+ ({
1500
+ datetime,
1501
+ timestamp,
1502
+ value,
1503
+ updatedAt,
1504
+ createdAt,
1505
+ relative,
1506
+ label,
1507
+ text,
1508
+ timezone = "UTC",
1509
+ hideWhenEmpty,
1510
+ now,
1511
+ children,
1512
+ field,
1513
+ fallbackField,
1514
+ timezoneParameter,
1515
+ title,
1516
+ className,
1517
+ ...rest
1518
+ }, ref) => {
1519
+ const childRaw = typeof children === "string" || typeof children === "number" ? children : void 0;
1520
+ const raw = datetime ?? timestamp ?? value ?? updatedAt ?? createdAt ?? childRaw;
1521
+ const date = asDate(raw);
1522
+ if (hideWhenEmpty && !date) return null;
1523
+ const nowDate = asDate(now) ?? /* @__PURE__ */ new Date();
1524
+ const iso = date?.toISOString() ?? (raw === void 0 ? void 0 : String(raw));
1525
+ const absolute = componentContracts.formatDateTime(date, { timeZone: timezone });
1526
+ const content = relative ?? label ?? text ?? (now !== void 0 && date ? relativeLabel(date, nowDate) : absolute || children || "Unknown time");
1527
+ return /* @__PURE__ */ jsxRuntime.jsx(
1528
+ "time",
1529
+ {
1530
+ ref,
1531
+ dateTime: iso,
1532
+ title: title ?? absolute,
1533
+ className: classes3("oods-relative-timestamp", className),
1534
+ "data-oods-component": "RelativeTimestamp",
1535
+ suppressHydrationWarning: true,
1536
+ ...rest,
1537
+ children: content
1538
+ }
1539
+ );
1540
+ }
1541
+ );
1542
+ RelativeTimestamp.displayName = "OODS.RelativeTimestamp";
1543
+
1544
+ exports.AuditTimeline = AuditTimeline;
1545
+ exports.CancellationSummary = CancellationSummary;
1546
+ exports.PaginationBar = PaginationBar;
1547
+ exports.PriceBadge = PriceBadge;
1548
+ exports.RelativeTimestamp = RelativeTimestamp;
1549
+ exports.SearchInput = SearchInput;
1550
+ exports.StatusBadge = StatusBadge;
1551
+ exports.StatusTimeline = StatusTimeline;