@oods/components-vue 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.
package/dist/index.js ADDED
@@ -0,0 +1,2995 @@
1
+ import { defineComponent, h, ref, computed, useId, watch, nextTick, onMounted, onBeforeUnmount, isVNode, Text as Text$1, Comment, Fragment } from 'vue';
2
+ import { svgCarriesTitle, assertStaticSvg, formatPriceAmount, formatPriceCode, formatReferenceLabel, summaryValue, formatDateTime, dateTimeInputValue, formatReadOnlyValue, BILLING_MINOR_UNITS, billingSummary, formatBillingAmount, billingAmountText, billingAmountMessage, parseBillingAmount, BILLING_INTERVALS, billingIntervalMessage, billingCycle, auditSummary, initialSort, ariaSort, nextSort, recipeItemLabels, DEFAULT_COLOR_STATES, geoFieldMapping, currencyMinorUnits, billingPaymentSummary, billingPaymentRows, traitEventRows, vizControlFields, VIZ_CONTROL_TITLES, vizFieldValue, vizSummaryRows, editVizIntent } from '@oods/component-contracts';
3
+
4
+ // src/primitives.ts
5
+ var toneProp = String;
6
+ var emphasisProp = String;
7
+ var sizeProp = String;
8
+ var STATUS_TONES = {
9
+ active: "success",
10
+ paid: "success",
11
+ success: "success",
12
+ successful: "success",
13
+ future: "info",
14
+ pending: "info",
15
+ pending_cancellation: "info",
16
+ posted: "info",
17
+ processing: "info",
18
+ trialing: "accent",
19
+ caution: "warning",
20
+ warning: "warning",
21
+ delinquent: "critical",
22
+ error: "critical",
23
+ failed: "critical",
24
+ past_due: "critical",
25
+ unpaid: "critical"
26
+ };
27
+ var DOMAIN_STATUS_TONES = {
28
+ subscription: {
29
+ future: "info",
30
+ trialing: "accent",
31
+ active: "success",
32
+ paused: "neutral",
33
+ pending_cancellation: "info",
34
+ past_due: "critical",
35
+ unpaid: "critical",
36
+ terminated: "neutral"
37
+ },
38
+ invoice: {
39
+ draft: "neutral",
40
+ posted: "info",
41
+ paid: "success",
42
+ past_due: "critical",
43
+ void: "neutral"
44
+ }
45
+ };
46
+ function normalizeStatus(status) {
47
+ return status?.trim().toLowerCase().replace(/[\s-]+/g, "_") ?? "";
48
+ }
49
+ function resolveTone(tone, status, domain) {
50
+ const normalizedStatus = normalizeStatus(status);
51
+ const normalizedDomain = domain?.trim().toLowerCase() ?? "";
52
+ return tone ?? DOMAIN_STATUS_TONES[normalizedDomain]?.[normalizedStatus] ?? STATUS_TONES[normalizedStatus] ?? "neutral";
53
+ }
54
+ function tokenTone(tone) {
55
+ if (tone === "positive") return "success";
56
+ if (tone === "danger") return "critical";
57
+ return tone;
58
+ }
59
+ function statusLabel(status) {
60
+ return normalizeStatus(status).split("_").filter(Boolean).map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`).join(" ");
61
+ }
62
+ function slotOrValue(slot, value) {
63
+ return slot ? slot() : value;
64
+ }
65
+ var Badge = defineComponent({
66
+ name: "OodsBadge",
67
+ props: {
68
+ content: { type: [String, Number], default: void 0 },
69
+ status: String,
70
+ domain: { type: String, default: "subscription" },
71
+ tone: toneProp,
72
+ emphasis: { type: emphasisProp, default: "subtle" },
73
+ icon: { type: [String, Number, Object], default: void 0 }
74
+ },
75
+ setup(props, { slots }) {
76
+ return () => {
77
+ const tone = resolveTone(props.tone, props.status, props.domain);
78
+ const palette = tokenTone(tone);
79
+ return h("span", {
80
+ class: "oods-badge",
81
+ "data-oods-component": "Badge",
82
+ "data-status": props.status,
83
+ "data-domain": props.domain,
84
+ "data-tone": tone,
85
+ "data-emphasis": props.emphasis,
86
+ style: {
87
+ "--cmp-badge-background": `var(--sys-status-${palette}-surface)`,
88
+ "--cmp-badge-border": `var(--sys-status-${palette}-border)`,
89
+ "--cmp-badge-text": `var(--sys-status-${palette}-text)`
90
+ }
91
+ }, [
92
+ props.icon || slots.icon ? h("span", { class: "oods-badge-icon", "aria-hidden": "true" }, [slotOrValue(slots.icon, props.icon)]) : null,
93
+ slotOrValue(slots.default, props.content ?? statusLabel(props.status))
94
+ ]);
95
+ };
96
+ }
97
+ });
98
+ var Banner = defineComponent({
99
+ name: "OodsBanner",
100
+ props: {
101
+ title: { type: String, default: "" },
102
+ detail: { type: String, default: "" },
103
+ content: { type: [String, Number], default: "" },
104
+ status: String,
105
+ domain: { type: String, default: "subscription" },
106
+ tone: toneProp,
107
+ emphasis: { type: emphasisProp, default: "subtle" },
108
+ dismissLabel: String
109
+ },
110
+ emits: {
111
+ dismiss: () => true
112
+ },
113
+ setup(props, { emit, slots }) {
114
+ return () => {
115
+ const tone = resolveTone(props.tone, props.status, props.domain);
116
+ const palette = tokenTone(tone);
117
+ return h("section", {
118
+ class: "oods-banner",
119
+ "data-oods-component": "Banner",
120
+ "data-status": props.status,
121
+ "data-domain": props.domain,
122
+ "data-tone": tone,
123
+ "data-emphasis": props.emphasis,
124
+ role: tone === "critical" || tone === "danger" ? "alert" : "status",
125
+ "aria-live": tone === "critical" || tone === "danger" ? "assertive" : "polite",
126
+ style: {
127
+ "--cmp-banner-background": `var(--sys-status-${palette}-surface)`,
128
+ "--cmp-banner-border": `var(--sys-status-${palette}-border)`,
129
+ "--cmp-banner-text": `var(--sys-status-${palette}-text)`
130
+ }
131
+ }, [
132
+ h("div", { class: "oods-banner-content" }, [
133
+ props.title || props.status || slots.title ? h("strong", { class: "oods-banner-title" }, [
134
+ slotOrValue(slots.title, props.title || statusLabel(props.status))
135
+ ]) : null,
136
+ props.detail ? h("p", { class: "oods-banner-detail" }, props.detail) : null,
137
+ slots.default || props.content ? h(slots.default ? "div" : "p", { class: "oods-banner-body" }, [slotOrValue(slots.default, props.content)]) : null,
138
+ slots.actions ? h("div", { class: "oods-banner-actions" }, slots.actions()) : null
139
+ ]),
140
+ props.dismissLabel ? h("button", {
141
+ type: "button",
142
+ class: "oods-banner-dismiss",
143
+ "aria-label": props.dismissLabel,
144
+ onClick: () => emit("dismiss")
145
+ }, [h("span", { "aria-hidden": "true" }, "\xD7")]) : null
146
+ ]);
147
+ };
148
+ }
149
+ });
150
+ var Button = defineComponent({
151
+ name: "OodsButton",
152
+ props: {
153
+ content: { type: [String, Number], default: "" },
154
+ intent: { type: String, default: "neutral" },
155
+ size: { type: sizeProp, default: "md" },
156
+ disabled: Boolean,
157
+ type: { type: String, default: "button" }
158
+ },
159
+ emits: {
160
+ activate: (_event) => true
161
+ },
162
+ setup(props, { emit, slots }) {
163
+ return () => h("button", {
164
+ type: props.type,
165
+ disabled: props.disabled,
166
+ class: "oods-button",
167
+ "data-oods-component": "Button",
168
+ "data-intent": props.intent,
169
+ "data-size": props.size,
170
+ style: {
171
+ "--cmp-button-background": "var(--sys-surface-interactive-primary-default)",
172
+ "--cmp-button-background-hover": "var(--sys-surface-interactive-primary-hover)",
173
+ "--cmp-button-background-disabled": "var(--sys-surface-disabled)",
174
+ "--cmp-button-border": "var(--sys-border-subtle)",
175
+ "--cmp-button-text": "var(--sys-text-on-interactive)",
176
+ "--cmp-button-text-disabled": "var(--sys-text-disabled)"
177
+ },
178
+ onClick: (event) => emit("activate", event)
179
+ }, [slotOrValue(slots.default, props.content)]);
180
+ }
181
+ });
182
+ var SAFE_CARD_ELEMENTS = /* @__PURE__ */ new Set(["div", "article", "section", "aside"]);
183
+ var Card = defineComponent({
184
+ name: "OodsCard",
185
+ props: {
186
+ elevated: Boolean,
187
+ as: { type: String, default: "div" }
188
+ },
189
+ setup(props, { slots }) {
190
+ return () => h(SAFE_CARD_ELEMENTS.has(props.as) ? props.as : "div", {
191
+ class: "oods-card",
192
+ "data-oods-component": "Card",
193
+ "data-elevated": String(props.elevated)
194
+ }, slots.default?.());
195
+ }
196
+ });
197
+ var GAP_VALUES = {
198
+ xs: "var(--cmp-spacing-inline-xs, 0.5rem)",
199
+ sm: "var(--cmp-spacing-inline-sm, 0.75rem)",
200
+ md: "var(--cmp-spacing-stack-default, 1rem)",
201
+ lg: "var(--cmp-spacing-stack-lg, 1.5rem)",
202
+ xl: "var(--cmp-spacing-stack-xl, 2rem)"
203
+ };
204
+ function resolveGap(gap) {
205
+ return GAP_VALUES[gap] ?? gap;
206
+ }
207
+ function maxColumns(columns) {
208
+ const count = Number(columns);
209
+ return Number.isFinite(count) && count > 0 ? count : void 0;
210
+ }
211
+ var Grid = defineComponent({
212
+ name: "OodsGrid",
213
+ props: {
214
+ columns: { type: [Number, String], default: "auto-fit" },
215
+ minColumnWidth: { type: String, default: "16rem" },
216
+ gap: { type: String, default: "md" },
217
+ align: { type: String, default: "stretch" },
218
+ justify: { type: String, default: "normal" }
219
+ },
220
+ setup(props, { slots }) {
221
+ return () => h("div", {
222
+ class: "oods-grid",
223
+ "data-oods-component": "Grid",
224
+ "data-max-columns": maxColumns(props.columns),
225
+ style: {
226
+ "--oods-grid-columns": String(props.columns),
227
+ "--oods-grid-min-column": props.minColumnWidth,
228
+ "--oods-layout-gap": resolveGap(props.gap),
229
+ "--oods-layout-align": props.align,
230
+ "--oods-layout-justify": props.justify
231
+ }
232
+ }, slots.default?.());
233
+ }
234
+ });
235
+ var Stack = defineComponent({
236
+ name: "OodsStack",
237
+ props: {
238
+ direction: { type: String, default: "column" },
239
+ gap: { type: String, default: "md" },
240
+ align: { type: String, default: "stretch" },
241
+ justify: { type: String, default: "flex-start" },
242
+ wrap: Boolean
243
+ },
244
+ setup(props, { slots }) {
245
+ return () => h("div", {
246
+ class: "oods-stack",
247
+ "data-oods-component": "Stack",
248
+ style: {
249
+ "--oods-stack-direction": props.direction,
250
+ "--oods-stack-wrap": props.wrap ? "wrap" : "nowrap",
251
+ "--oods-layout-gap": resolveGap(props.gap),
252
+ "--oods-layout-align": props.align,
253
+ "--oods-layout-justify": props.justify
254
+ }
255
+ }, slots.default?.());
256
+ }
257
+ });
258
+ var SAFE_TEXT_ELEMENTS = /* @__PURE__ */ new Set([
259
+ "span",
260
+ "p",
261
+ "strong",
262
+ "em",
263
+ "small",
264
+ "div",
265
+ "label",
266
+ "h1",
267
+ "h2",
268
+ "h3",
269
+ "h4",
270
+ "h5",
271
+ "h6"
272
+ ]);
273
+ var Text = defineComponent({
274
+ name: "OodsText",
275
+ props: {
276
+ content: { type: [String, Number], default: "" },
277
+ label: String,
278
+ as: { type: String, default: "span" },
279
+ size: { type: sizeProp, default: "md" },
280
+ weight: { type: String, default: "normal" }
281
+ },
282
+ setup(props, { slots }) {
283
+ return () => h(SAFE_TEXT_ELEMENTS.has(props.as) ? props.as : "span", {
284
+ class: "oods-text",
285
+ "data-oods-component": "Text",
286
+ "data-size": props.size,
287
+ "data-weight": props.weight,
288
+ "aria-description": props.label
289
+ }, [slotOrValue(slots.default, props.content)]);
290
+ }
291
+ });
292
+ function firstText(...values) {
293
+ return values.find((value) => typeof value === "string" && value.trim().length > 0);
294
+ }
295
+ function headingElement(as, level, fallback) {
296
+ if (as && /^h[1-6]$/.test(as)) return as;
297
+ const resolvedLevel = level !== void 0 && Number.isFinite(level) ? Math.min(6, Math.max(1, Math.trunc(level))) : fallback;
298
+ return `h${resolvedLevel}`;
299
+ }
300
+ function authoredContent(value) {
301
+ function flatten(child) {
302
+ if (Array.isArray(child)) return child.flatMap(flatten);
303
+ if (child == null || typeof child === "boolean" || isVNode(child) && child.type === Comment) return [];
304
+ if (isVNode(child) && child.type === Fragment) return flatten(child.children);
305
+ return [child];
306
+ }
307
+ const content = flatten(value);
308
+ const meaningful = content.some((child) => {
309
+ if (typeof child === "string") return child.trim().length > 0;
310
+ if (isVNode(child) && child.type === Text$1) return String(child.children ?? "").trim().length > 0;
311
+ return true;
312
+ });
313
+ return meaningful ? content : [];
314
+ }
315
+ function scalarContent(content) {
316
+ if (!content.length) return void 0;
317
+ if (!content.every((child) => typeof child === "string" || typeof child === "number" || isVNode(child) && child.type === Text$1)) return void 0;
318
+ return content.map((child) => String(isVNode(child) ? child.children : child)).join("");
319
+ }
320
+ var DetailHeader = defineComponent({
321
+ name: "OodsDetailHeader",
322
+ props: {
323
+ title: String,
324
+ label: String,
325
+ text: String,
326
+ subtitle: String,
327
+ sublabel: String,
328
+ description: String,
329
+ metadata: String,
330
+ meta: String,
331
+ as: String,
332
+ level: Number
333
+ },
334
+ setup(props, { slots }) {
335
+ return () => {
336
+ const content = authoredContent(slots.default?.());
337
+ const scalar = scalarContent(content);
338
+ const title = scalar ?? firstText(props.title, props.label, props.text) ?? "Details";
339
+ const subtitle = notEcho(firstText(props.subtitle, props.sublabel, props.description), title);
340
+ const metadata = firstText(props.metadata, props.meta);
341
+ return h(
342
+ "header",
343
+ { class: "oods-detail-header", "data-oods-component": "DetailHeader" },
344
+ content.length && scalar === void 0 ? content : [
345
+ h(headingElement(props.as, props.level, 2), title),
346
+ subtitle ? h("span", { "data-oods-subtitle": "true" }, subtitle) : null,
347
+ metadata ? h("span", { "data-oods-metadata": "true" }, metadata) : null
348
+ ]
349
+ );
350
+ };
351
+ }
352
+ });
353
+ function notEcho(supporting, heading) {
354
+ if (!supporting || !heading) return supporting;
355
+ return supporting.trim() === heading.trim() ? void 0 : supporting;
356
+ }
357
+ var CardHeader = defineComponent({
358
+ name: "OodsCardHeader",
359
+ props: {
360
+ title: String,
361
+ label: String,
362
+ text: String,
363
+ supporting: String,
364
+ supportingText: String,
365
+ subtitle: String,
366
+ description: String,
367
+ as: String,
368
+ level: Number
369
+ },
370
+ setup(props, { slots }) {
371
+ return () => {
372
+ const content = authoredContent(slots.default?.());
373
+ const scalar = scalarContent(content);
374
+ const title = scalar ?? firstText(props.title, props.label, props.text) ?? "Card";
375
+ const supporting = notEcho(firstText(props.supporting, props.supportingText, props.subtitle, props.description), title);
376
+ return h(
377
+ "header",
378
+ { class: "oods-card-header", "data-oods-component": "CardHeader" },
379
+ content.length && scalar === void 0 ? content : [
380
+ h(headingElement(props.as, props.level, 2), title),
381
+ supporting ? h("span", { "data-oods-supporting": "true" }, supporting) : null
382
+ ]
383
+ );
384
+ };
385
+ }
386
+ });
387
+ var ColorSwatch = defineComponent({
388
+ name: "OodsColorSwatch",
389
+ props: { color: String, value: String, state: String, label: String },
390
+ setup(props, { slots }) {
391
+ return () => {
392
+ const color = firstText(props.color, props.value, props.state) ?? "default";
393
+ const content = authoredContent(slots.default?.());
394
+ const scalar = scalarContent(content);
395
+ const label = firstText(scalar, props.label) ?? color;
396
+ return h("span", {
397
+ class: "oods-color-swatch",
398
+ "data-oods-component": "ColorSwatch",
399
+ "data-summary-type": "color-swatch",
400
+ "data-swatch-color": color,
401
+ style: { "--oods-swatch-color": color === "default" ? void 0 : color }
402
+ }, [
403
+ h("span", { "data-oods-swatch-chip": "true", "aria-hidden": "true" }),
404
+ h("span", { "data-oods-swatch-label": "true" }, label),
405
+ ...scalar === void 0 ? content : []
406
+ ]);
407
+ };
408
+ }
409
+ });
410
+ var ColorizedBadge = defineComponent({
411
+ name: "OodsColorizedBadge",
412
+ props: {
413
+ label: String,
414
+ text: String,
415
+ state: String,
416
+ value: String,
417
+ status: String,
418
+ color: String,
419
+ hue: String,
420
+ swatch: String,
421
+ variant: String,
422
+ tone: String,
423
+ emphasis: String
424
+ },
425
+ setup(props, { slots }) {
426
+ return () => {
427
+ const content = authoredContent(slots.default?.());
428
+ const scalar = scalarContent(content);
429
+ const label = firstText(scalar, props.label, props.text, props.state, props.value) ?? "Color";
430
+ const status = firstText(props.status, props.state, props.value);
431
+ const color = firstText(props.color, props.hue, props.swatch, props.state);
432
+ const variant = firstText(props.variant) ?? "colorized";
433
+ return h(Badge, {
434
+ status,
435
+ tone: props.tone,
436
+ emphasis: props.emphasis,
437
+ "data-oods-component": "ColorizedBadge",
438
+ "data-badge-status": status,
439
+ "data-badge-color": color,
440
+ "data-badge-variant": variant,
441
+ style: { "--oods-badge-color": color }
442
+ }, {
443
+ icon: () => h("span", { "data-oods-badge-marker": "true", "aria-hidden": "true" }),
444
+ default: () => [
445
+ h("span", { "data-oods-badge-label": "true" }, label),
446
+ ...scalar === void 0 ? content : []
447
+ ]
448
+ });
449
+ };
450
+ }
451
+ });
452
+ var VizAreaPreview = defineComponent({
453
+ name: "OodsVizAreaPreview",
454
+ props: {
455
+ svg: String,
456
+ /** The same chart rendered at the narrow size; the figure shows it instead of svg when its own inline size is at most 600px. */
457
+ svgNarrow: String,
458
+ /** The same chart rendered at the wide size; the figure shows it instead of svg when its own inline size is at least 900px. */
459
+ svgWide: String,
460
+ title: String,
461
+ description: String,
462
+ width: { type: Number, default: 640 },
463
+ height: { type: Number, default: 360 }
464
+ },
465
+ setup(props, { slots }) {
466
+ return () => {
467
+ const content = authoredContent(slots.default?.());
468
+ if (props.svg !== void 0) return h("figure", {
469
+ class: "oods-viz-area-preview",
470
+ "data-oods-component": "VizAreaPreview",
471
+ "data-viz-preview-type": "area",
472
+ "data-viz-rendered": "true",
473
+ "data-viz-width": props.width,
474
+ "data-viz-height": props.height,
475
+ ...props.svgNarrow !== void 0 ? { "data-viz-narrow": "true" } : {},
476
+ ...props.svgWide !== void 0 ? { "data-viz-wide": "true" } : {},
477
+ role: "img",
478
+ "aria-label": props.title ?? props.description ?? "Payment amounts",
479
+ style: { "--oods-viz-width": `${props.width}px` }
480
+ }, [
481
+ ...props.title && !svgCarriesTitle(props.svg, props.title) ? [h("figcaption", props.title)] : [],
482
+ h("div", { "data-viz-svg": "true", innerHTML: assertStaticSvg(props.svg) }),
483
+ ...props.svgNarrow !== void 0 ? [h("div", { "data-viz-svg-narrow": "true", innerHTML: assertStaticSvg(props.svgNarrow) })] : [],
484
+ ...props.svgWide !== void 0 ? [h("div", { "data-viz-svg-wide": "true", innerHTML: assertStaticSvg(props.svgWide) })] : [],
485
+ ...props.description ? [h("p", { "data-viz-description": "true" }, props.description)] : []
486
+ ]);
487
+ return h("div", {
488
+ class: "oods-viz-area-preview",
489
+ "data-oods-component": "VizAreaPreview",
490
+ "data-viz-preview-type": "area",
491
+ "data-viz-width": props.width,
492
+ "data-viz-height": props.height,
493
+ style: { "--oods-viz-width": `${props.width}px`, "--oods-viz-height": `${props.height}px` }
494
+ }, content.length ? content : [
495
+ h("div", { "data-viz-preview-placeholder": "true" }, `Area preview (${props.width} x ${props.height})`)
496
+ ]);
497
+ };
498
+ }
499
+ });
500
+ function firstScalar(...values) {
501
+ for (const value of values) {
502
+ if (typeof value === "string") {
503
+ if (value.trim().length > 0) return value;
504
+ continue;
505
+ }
506
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
507
+ }
508
+ return void 0;
509
+ }
510
+ function isRecord(value) {
511
+ return value !== null && typeof value === "object" && !Array.isArray(value);
512
+ }
513
+ function descriptorLabel(descriptor) {
514
+ return firstText(descriptor.label, descriptor.field) ?? "Filter";
515
+ }
516
+ var PANEL_SECTION_PROPS = {
517
+ title: String,
518
+ label: String,
519
+ heading: String,
520
+ name: String,
521
+ subtitle: String,
522
+ description: String,
523
+ metadata: String,
524
+ summary: String,
525
+ text: String,
526
+ body: String,
527
+ emptyMessage: String
528
+ };
529
+ function createPanelSection(component, className, panelType, defaultTitle) {
530
+ return defineComponent({
531
+ name: `Oods${component}`,
532
+ props: PANEL_SECTION_PROPS,
533
+ setup(props, { slots }) {
534
+ return () => {
535
+ const content = authoredContent(slots.default?.());
536
+ const title = firstText(props.title, props.label, props.heading, props.name) ?? defaultTitle;
537
+ const subtitle = firstText(props.subtitle, props.description, props.metadata);
538
+ const fallback = firstText(props.summary, props.text, props.body, props.emptyMessage);
539
+ return h("section", {
540
+ class: className,
541
+ "data-oods-component": component,
542
+ "data-panel-type": panelType
543
+ }, [
544
+ h("header", { "data-panel-header": "true" }, [
545
+ h("h2", title),
546
+ subtitle ? h("span", { "data-panel-subtitle": "true" }, subtitle) : null
547
+ ]),
548
+ h("div", { "data-panel-content": "true" }, content.length ? content : fallback ? [h("span", { "data-panel-summary": "true" }, fallback)] : [])
549
+ ]);
550
+ };
551
+ }
552
+ });
553
+ }
554
+ var ClassificationPanel = createPanelSection("ClassificationPanel", "oods-classification-panel", "classification", "Classification");
555
+ var AddressCollectionPanel = createPanelSection("AddressCollectionPanel", "oods-address-collection-panel", "address", "Addresses");
556
+ var MembershipPanel = createPanelSection("MembershipPanel", "oods-membership-panel", "membership", "Membership");
557
+ var PreferencePanel = createPanelSection("PreferencePanel", "oods-preference-panel", "preference", "Preferences");
558
+ var FilterPanel = defineComponent({
559
+ name: "OodsFilterPanel",
560
+ props: {
561
+ filters: { type: Array, default: () => [] },
562
+ activeFilters: { type: Array, default: () => [] },
563
+ mode: String,
564
+ collapsible: { type: Boolean, default: true }
565
+ },
566
+ setup(props, { slots }) {
567
+ return () => {
568
+ const content = authoredContent(slots.default?.());
569
+ const mode = firstText(props.mode) ?? "immediate";
570
+ const descriptors = (Array.isArray(props.filters) ? props.filters : []).filter(isRecord);
571
+ const activeCount = Array.isArray(props.activeFilters) ? props.activeFilters.length : 0;
572
+ return h("aside", {
573
+ class: "oods-filter-panel",
574
+ "data-oods-component": "FilterPanel",
575
+ "data-behavioral": "filter",
576
+ "data-filter-mode": mode,
577
+ role: "region",
578
+ "aria-label": "Filters"
579
+ }, content.length ? content : [
580
+ activeCount > 0 ? h("div", { "data-active-filters": "true", "aria-live": "polite" }, [
581
+ h("span", { "data-filter-count": "true" }, `${activeCount} active`),
582
+ h("button", { type: "button", "data-filter-clear-all": "true" }, "Clear all")
583
+ ]) : null,
584
+ ...descriptors.map((descriptor, index) => h("fieldset", {
585
+ key: index,
586
+ "data-filter-section": "true",
587
+ "data-collapsible": props.collapsible ? "true" : void 0
588
+ }, [h("legend", descriptorLabel(descriptor))])),
589
+ mode === "batch" ? h("button", { type: "button", "data-filter-apply": "true" }, "Apply") : null
590
+ ]);
591
+ };
592
+ }
593
+ });
594
+ var PriceSummary = defineComponent({
595
+ name: "OodsPriceSummary",
596
+ props: {
597
+ title: String,
598
+ label: String,
599
+ heading: String,
600
+ name: String,
601
+ amount: [String, Number],
602
+ amountCents: [String, Number],
603
+ unitAmountCents: [String, Number],
604
+ minorUnits: Number,
605
+ currency: String,
606
+ currencyCode: String,
607
+ model: String,
608
+ pricingModel: String,
609
+ interval: String,
610
+ billingInterval: String,
611
+ summary: String,
612
+ text: String,
613
+ description: String
614
+ },
615
+ setup(props, { slots }) {
616
+ return () => {
617
+ const content = authoredContent(slots.default?.());
618
+ const title = firstText(props.title, props.label, props.heading, props.name) ?? "Price Summary";
619
+ const terms = [
620
+ ["Amount", [props.amount, props.amountCents, props.unitAmountCents, props.currency, props.currencyCode, props.model, props.pricingModel, props.interval, props.billingInterval].some((value) => value !== void 0) ? formatPriceAmount(props.amount ?? props.amountCents ?? props.unitAmountCents, firstText(props.currency, props.currencyCode), props.minorUnits) : void 0],
621
+ ["Currency", firstScalar(props.currency, props.currencyCode)],
622
+ ["Model", formatPriceCode(props.model ?? props.pricingModel)],
623
+ ["Interval", formatPriceCode(props.interval ?? props.billingInterval)]
624
+ ].filter((entry) => entry[1] !== void 0);
625
+ const fallback = firstText(props.summary, props.text, props.description);
626
+ return h("section", {
627
+ class: "oods-price-summary",
628
+ "data-oods-component": "PriceSummary",
629
+ "data-summary-type": "price"
630
+ }, [
631
+ h("h2", { "data-summary-title": "true" }, title),
632
+ ...content.length ? content : terms.length ? [h("dl", terms.map(([term, value]) => h("div", { key: term, "data-summary-item": "true" }, [h("dt", term), h("dd", value)])))] : fallback ? [h("p", { "data-summary-fallback": "true" }, fallback)] : [h("dl")]
633
+ ]);
634
+ };
635
+ }
636
+ });
637
+ function normalizeTagItems(value) {
638
+ if (!Array.isArray(value)) return [];
639
+ const items = [];
640
+ for (const entry of value) {
641
+ if (entry === void 0 || entry === null) continue;
642
+ if (isRecord(entry)) {
643
+ const text = firstScalar(entry.label, entry.name, entry.role, entry.value, entry.id);
644
+ if (text) items.push(text);
645
+ continue;
646
+ }
647
+ items.push(typeof entry === "object" ? JSON.stringify(entry) : String(entry));
648
+ }
649
+ return items.filter((item) => item.length > 0);
650
+ }
651
+ var TagManager = defineComponent({
652
+ name: "OodsTagManager",
653
+ props: {
654
+ title: String,
655
+ label: String,
656
+ heading: String,
657
+ name: String,
658
+ description: String,
659
+ subtitle: String,
660
+ hint: String,
661
+ tags: Array,
662
+ value: Array
663
+ },
664
+ setup(props, { slots }) {
665
+ return () => {
666
+ const content = authoredContent(slots.default?.());
667
+ const title = firstText(props.title, props.label, props.heading, props.name) ?? "Tag Manager";
668
+ const subtitle = firstText(props.description, props.subtitle, props.hint);
669
+ const items = normalizeTagItems(props.tags ?? props.value);
670
+ return h("form", {
671
+ class: "oods-tag-manager",
672
+ "data-oods-component": "TagManager",
673
+ "data-form-type": "tag-manager",
674
+ // The add control is unwired: submitting never navigates.
675
+ onSubmit: (event) => event.preventDefault()
676
+ }, [
677
+ h("header", { "data-form-header": "true" }, [
678
+ h("h2", title),
679
+ subtitle ? h("span", { "data-form-subtitle": "true" }, subtitle) : null
680
+ ]),
681
+ h("div", { "data-form-content": "true" }, content.length ? content : [
682
+ h("div", { "data-tag-list": "true" }, items.map((tag, index) => h("span", { key: index, "data-tag-item": "true" }, tag))),
683
+ h("label", { "data-form-control": "input" }, [
684
+ h("span", "Add Tag"),
685
+ h("input", { type: "text", name: "newTag", placeholder: "Type a tag" })
686
+ ])
687
+ ])
688
+ ]);
689
+ };
690
+ }
691
+ });
692
+ function asCount(value) {
693
+ if (typeof value === "number" && Number.isFinite(value)) return value;
694
+ if (typeof value === "string" && value.trim().length > 0) {
695
+ const parsed = Number.parseInt(value, 10);
696
+ if (Number.isFinite(parsed)) return parsed;
697
+ }
698
+ return void 0;
699
+ }
700
+ function createBadgeFamily(options) {
701
+ const props = {
702
+ label: String,
703
+ text: String,
704
+ value: String,
705
+ status: String,
706
+ state: String,
707
+ variant: String,
708
+ tone: String,
709
+ emphasis: String
710
+ };
711
+ for (const key of options.extraProps) props[key] = String;
712
+ for (const key of options.booleanProps ?? []) props[key] = { type: [String, Boolean], default: void 0 };
713
+ return defineComponent({
714
+ name: `Oods${options.component}`,
715
+ props,
716
+ setup(componentProps, { slots }) {
717
+ return () => {
718
+ const record = componentProps;
719
+ const content = authoredContent(slots.default?.());
720
+ const scalar = scalarContent(content);
721
+ const read = (key) => typeof record[key] === "boolean" && options.booleanProps?.includes(key) ? String(record[key]) : record[key];
722
+ const label = scalar ?? firstText(...options.labelKeys.map((key) => options.component === "CancellationBadge" && typeof record[key] === "boolean" && options.booleanProps?.includes(key) ? record[key] ? "Cancellation scheduled" : "No cancellation scheduled" : read(key))) ?? options.defaultLabel;
723
+ const status = firstText(...options.statusKeys.map(read));
724
+ const variant = firstText(record.variant) ?? options.defaultVariant;
725
+ return h(Badge, {
726
+ status,
727
+ tone: record.tone,
728
+ emphasis: record.emphasis,
729
+ class: options.className,
730
+ "data-oods-component": options.component,
731
+ "data-badge-status": status,
732
+ "data-badge-variant": variant
733
+ }, {
734
+ default: () => content.length && scalar === void 0 ? content : [h("span", { "data-oods-badge-label": "true" }, label)]
735
+ });
736
+ };
737
+ }
738
+ });
739
+ }
740
+ var AddressSummaryBadge = createBadgeFamily({
741
+ component: "AddressSummaryBadge",
742
+ className: "oods-address-summary-badge",
743
+ defaultLabel: "Address",
744
+ defaultVariant: "address",
745
+ labelKeys: ["label", "text", "role", "value"],
746
+ statusKeys: ["status", "state", "role"],
747
+ extraProps: ["role"]
748
+ });
749
+ var MessageStatusBadge = createBadgeFamily({
750
+ component: "MessageStatusBadge",
751
+ className: "oods-message-status-badge",
752
+ defaultLabel: "Message",
753
+ defaultVariant: "message",
754
+ labelKeys: ["label", "text", "status", "delivery", "value"],
755
+ statusKeys: ["status", "state", "delivery", "value"],
756
+ extraProps: ["delivery"]
757
+ });
758
+ var PreferenceSummaryBadge = createBadgeFamily({
759
+ component: "PreferenceSummaryBadge",
760
+ className: "oods-preference-summary-badge",
761
+ defaultLabel: "Preferences",
762
+ defaultVariant: "preference",
763
+ labelKeys: ["label", "text", "namespace", "value"],
764
+ statusKeys: ["status", "state", "version"],
765
+ extraProps: ["namespace", "version"]
766
+ });
767
+ var RoleBadgeList = defineComponent({
768
+ name: "OodsRoleBadgeList",
769
+ props: {
770
+ roles: Array,
771
+ badges: Array,
772
+ roleLabels: Array,
773
+ value: Array,
774
+ variant: String,
775
+ tone: String,
776
+ label: String,
777
+ text: String
778
+ },
779
+ setup(props, { slots }) {
780
+ return () => {
781
+ const content = authoredContent(slots.default?.());
782
+ const items = normalizeTagItems(props.roles ?? props.badges ?? props.roleLabels ?? props.value);
783
+ const variant = firstText(props.variant, props.tone) ?? "roles";
784
+ return h("span", {
785
+ class: "oods-role-badge-list",
786
+ "data-oods-component": "RoleBadgeList",
787
+ "data-badge-variant": variant
788
+ }, content.length ? content : items.length ? items.map((role, index) => h("span", { key: index, "data-role-badge": "true" }, role)) : [firstText(props.label, props.text) ?? "Roles"]);
789
+ };
790
+ }
791
+ });
792
+ var TagPills = defineComponent({
793
+ name: "OodsTagPills",
794
+ props: {
795
+ tags: Array,
796
+ value: Array,
797
+ maxVisible: [Number, String],
798
+ overflowLabel: String
799
+ },
800
+ setup(props, { slots }) {
801
+ return () => {
802
+ const content = authoredContent(slots.default?.());
803
+ const items = normalizeTagItems(props.tags ?? props.value);
804
+ const limit = asCount(props.maxVisible) ?? items.length;
805
+ const visible = items.slice(0, Math.max(0, limit));
806
+ const overflow = items.length - visible.length;
807
+ const template = firstText(props.overflowLabel);
808
+ const overflowText = overflow > 0 ? template ? template.replace("{{ tag_count }}", String(items.length)) : `+${overflow}` : "";
809
+ return h("div", {
810
+ class: "oods-tag-pills",
811
+ "data-oods-component": "TagPills",
812
+ "data-summary-type": "tag-pills"
813
+ }, content.length ? content : [
814
+ ...visible.map((tag, index) => h("span", { key: index, "data-tag-pill": "true" }, tag)),
815
+ overflowText ? h("span", { "data-tag-overflow": "true" }, overflowText) : null
816
+ ]);
817
+ };
818
+ }
819
+ });
820
+ function normalizeTimelineItems(raw) {
821
+ if (!Array.isArray(raw)) return [];
822
+ const items = [];
823
+ for (const entry of raw) {
824
+ if (entry === void 0 || entry === null) continue;
825
+ if (isRecord(entry)) {
826
+ items.push({
827
+ label: firstScalar(entry.label, entry.title, entry.event, entry.status, entry.state, entry.text, entry.name) ?? "Event",
828
+ timestamp: firstScalar(entry.timestamp, entry.datetime, entry.time, entry.at, entry.createdAt, entry.updatedAt),
829
+ detail: firstScalar(entry.detail, entry.description, entry.reason, entry.message, entry.from, entry.to)
830
+ });
831
+ continue;
832
+ }
833
+ items.push({ label: typeof entry === "object" ? JSON.stringify(entry) : String(entry) });
834
+ }
835
+ return items;
836
+ }
837
+ function createTimelineFamily(options) {
838
+ const props = { title: String, label: String, heading: String, name: String };
839
+ for (const key of options.eventKeys) props[key] = Array;
840
+ return defineComponent({
841
+ name: `Oods${options.component}`,
842
+ props,
843
+ setup(componentProps, { slots }) {
844
+ return () => {
845
+ const record = componentProps;
846
+ const content = authoredContent(slots.default?.());
847
+ const title = firstText(record.title, record.label, record.heading, record.name) ?? options.defaultTitle;
848
+ const events = normalizeTimelineItems(options.eventKeys.map((key) => record[key]).find((value) => Array.isArray(value)));
849
+ return h("div", {
850
+ class: options.className,
851
+ "data-oods-component": options.component,
852
+ "data-timeline-type": options.timelineType,
853
+ role: "log",
854
+ "aria-label": title
855
+ }, [
856
+ h("h2", { "data-timeline-title": "true" }, title),
857
+ h("ol", { "data-timeline-events": "true" }, content.length ? content : events.length ? events.map((item, index) => h("li", { key: index }, [h("article", { "data-timeline-event": "true" }, [
858
+ h("p", { "data-timeline-label": "true" }, item.label),
859
+ item.timestamp ? h("time", { "data-timeline-time": "true", datetime: item.timestamp }, item.timestamp) : null,
860
+ item.detail ? h("p", { "data-timeline-detail": "true" }, item.detail) : null
861
+ ])])) : [h("li", { "data-timeline-empty": "true" }, "No events")])
862
+ ]);
863
+ };
864
+ }
865
+ });
866
+ }
867
+ var AddressValidationTimeline = createTimelineFamily({
868
+ component: "AddressValidationTimeline",
869
+ className: "oods-address-validation-timeline",
870
+ defaultTitle: "Address Validation Timeline",
871
+ timelineType: "address-validation",
872
+ eventKeys: ["events", "validations", "history"]
873
+ });
874
+ var MembershipAuditTimeline = createTimelineFamily({
875
+ component: "MembershipAuditTimeline",
876
+ className: "oods-membership-audit-timeline",
877
+ defaultTitle: "Membership Timeline",
878
+ timelineType: "membership",
879
+ eventKeys: ["events", "memberships", "history"]
880
+ });
881
+ var MessageEventTimeline = createTimelineFamily({
882
+ component: "MessageEventTimeline",
883
+ className: "oods-message-event-timeline",
884
+ defaultTitle: "Message Timeline",
885
+ timelineType: "message",
886
+ eventKeys: ["events", "messages", "statuses"]
887
+ });
888
+ var PreferenceTimeline = createTimelineFamily({
889
+ component: "PreferenceTimeline",
890
+ className: "oods-preference-timeline",
891
+ defaultTitle: "Preference Timeline",
892
+ timelineType: "preference",
893
+ eventKeys: ["events", "changes", "history"]
894
+ });
895
+ var AuditEvent = defineComponent({
896
+ name: "OodsAuditEvent",
897
+ props: {
898
+ label: String,
899
+ title: String,
900
+ event: String,
901
+ status: String,
902
+ state: String,
903
+ reason: String,
904
+ text: String,
905
+ timestamp: String,
906
+ datetime: String,
907
+ time: String,
908
+ at: String,
909
+ createdAt: String,
910
+ updatedAt: String,
911
+ detail: String,
912
+ description: String,
913
+ message: String,
914
+ from: String,
915
+ to: String,
916
+ code: String
917
+ },
918
+ setup(props, { slots }) {
919
+ return () => {
920
+ const content = authoredContent(slots.default?.());
921
+ const label = firstText(props.label, props.title, props.event, props.status, props.state, props.reason, props.text) ?? "Audit Event";
922
+ const timestamp = firstText(props.timestamp, props.datetime, props.time, props.at, props.createdAt, props.updatedAt);
923
+ const detail = firstText(props.detail, props.description, props.reason, props.message, props.from, props.to, props.code);
924
+ return h("article", {
925
+ class: "oods-audit-event",
926
+ "data-oods-component": "AuditEvent",
927
+ "data-event-type": "audit"
928
+ }, content.length ? content : [
929
+ timestamp ? h("time", { "data-event-time": "true", datetime: timestamp }, timestamp) : null,
930
+ h("p", { "data-event-label": "true" }, label),
931
+ detail ? h("p", { "data-event-detail": "true" }, detail) : null
932
+ ]);
933
+ };
934
+ }
935
+ });
936
+ function normalizeSelectOptions(raw) {
937
+ if (!Array.isArray(raw)) return [];
938
+ const choices = [];
939
+ for (const entry of raw) {
940
+ if (isRecord(entry)) {
941
+ const text = (key) => typeof entry[key] === "string" && entry[key].length > 0 ? entry[key] : void 0;
942
+ const value2 = text("value") ?? text("id") ?? text("label") ?? "";
943
+ const label = text("label") ?? text("name") ?? formatReadOnlyValue(value2, "string", true);
944
+ if (!value2 && !label) continue;
945
+ choices.push({ value: value2, label });
946
+ continue;
947
+ }
948
+ if (entry === void 0 || entry === null) continue;
949
+ const value = typeof entry === "object" ? JSON.stringify(entry) : String(entry);
950
+ if (!value) continue;
951
+ choices.push({ value, label: formatReadOnlyValue(value, "string", true) });
952
+ }
953
+ return choices;
954
+ }
955
+ function selectOptionNodes(choices, selected) {
956
+ return choices.length ? choices.map((choice, index) => h("option", { key: index, value: choice.value, selected: choice.value === selected }, choice.label)) : [h("option", { value: "" }, "Select...")];
957
+ }
958
+ var preventSubmit = (event) => {
959
+ event.preventDefault();
960
+ };
961
+ var FORM_SHELL_PROPS = {
962
+ title: String,
963
+ label: String,
964
+ heading: String,
965
+ name: String,
966
+ description: String,
967
+ subtitle: String,
968
+ hint: String
969
+ };
970
+ function formHeader(title, subtitle) {
971
+ return h("header", { "data-form-header": "true" }, [
972
+ h("h2", title),
973
+ subtitle ? h("span", { "data-form-subtitle": "true" }, subtitle) : null
974
+ ]);
975
+ }
976
+ function fieldsetHeader(title, subtitle) {
977
+ return [h("legend", title), subtitle ? h("span", { "data-form-subtitle": "true" }, subtitle) : null];
978
+ }
979
+ function addressRecord(form) {
980
+ const read = (name) => {
981
+ const control = form?.elements.namedItem(name);
982
+ return control instanceof HTMLInputElement ? control.value : "";
983
+ };
984
+ return { street: read("street"), city: read("city"), region: read("region"), postalCode: read("postalCode") };
985
+ }
986
+ var AddressEditor = defineComponent({
987
+ name: "OodsAddressEditor",
988
+ props: {
989
+ ...FORM_SHELL_PROPS,
990
+ street: String,
991
+ line1: String,
992
+ addressLine1: String,
993
+ city: String,
994
+ region: String,
995
+ state: String,
996
+ postalCode: String,
997
+ zip: String
998
+ },
999
+ emits: { change: (_address) => true },
1000
+ setup(props, { emit, slots }) {
1001
+ return () => {
1002
+ const content = authoredContent(slots.default?.());
1003
+ const title = firstText(props.title, props.label, props.heading, props.name) ?? "Address Editor";
1004
+ const subtitle = firstText(props.description, props.subtitle, props.hint);
1005
+ const publish = (event) => emit("change", addressRecord(event.target.form));
1006
+ const control = (fieldLabel, fieldName, initial) => h("label", { "data-form-control": "input" }, [
1007
+ h("span", fieldLabel),
1008
+ h("input", { type: "text", name: fieldName, value: initial, onInput: publish })
1009
+ ]);
1010
+ return h("form", {
1011
+ class: "oods-address-editor",
1012
+ "data-oods-component": "AddressEditor",
1013
+ "data-form-type": "address-editor",
1014
+ onSubmit: preventSubmit
1015
+ }, [
1016
+ formHeader(title, subtitle),
1017
+ h("div", { "data-form-content": "true" }, content.length ? content : [
1018
+ control("Street", "street", firstText(props.street, props.line1, props.addressLine1)),
1019
+ control("City", "city", firstText(props.city)),
1020
+ control("Region", "region", firstText(props.region, props.state)),
1021
+ control("Postal Code", "postalCode", firstText(props.postalCode, props.zip))
1022
+ ])
1023
+ ]);
1024
+ };
1025
+ }
1026
+ });
1027
+ var PreferenceEditor = defineComponent({
1028
+ name: "OodsPreferenceEditor",
1029
+ props: {
1030
+ ...FORM_SHELL_PROPS,
1031
+ namespaces: Array,
1032
+ namespace: String,
1033
+ document: String,
1034
+ json: String,
1035
+ value: String
1036
+ },
1037
+ setup(props, { slots }) {
1038
+ return () => {
1039
+ const content = authoredContent(slots.default?.());
1040
+ const title = firstText(props.title, props.label, props.heading, props.name) ?? "Preference Editor";
1041
+ const subtitle = firstText(props.description, props.subtitle, props.hint);
1042
+ const choices = normalizeSelectOptions(Array.isArray(props.namespaces) ? props.namespaces : ["default"]);
1043
+ const selected = firstText(props.namespace) ?? choices[0]?.value;
1044
+ return h("form", {
1045
+ class: "oods-preference-editor",
1046
+ "data-oods-component": "PreferenceEditor",
1047
+ "data-form-type": "preference-editor",
1048
+ onSubmit: preventSubmit
1049
+ }, [
1050
+ formHeader(title, subtitle),
1051
+ h("div", { "data-form-content": "true" }, content.length ? content : [
1052
+ h("label", { "data-form-control": "select" }, [h("span", "Namespace"), h("select", { name: "namespace", value: selected }, selectOptionNodes(choices, selected))]),
1053
+ h("label", { "data-form-control": "textarea" }, [h("span", "Preference Document"), h("textarea", { name: "preferenceDocument", value: firstScalar(props.document, props.json, props.value) ?? "" })])
1054
+ ])
1055
+ ]);
1056
+ };
1057
+ }
1058
+ });
1059
+ var RoleAssignmentForm = defineComponent({
1060
+ name: "OodsRoleAssignmentForm",
1061
+ props: {
1062
+ ...FORM_SHELL_PROPS,
1063
+ roles: Array,
1064
+ availableRoles: Array,
1065
+ role: String,
1066
+ defaultRoleId: String,
1067
+ assignee: String,
1068
+ member: String
1069
+ },
1070
+ setup(props, { slots }) {
1071
+ return () => {
1072
+ const content = authoredContent(slots.default?.());
1073
+ const title = firstText(props.title, props.label, props.heading, props.name) ?? "Role Assignment";
1074
+ const subtitle = firstText(props.description, props.subtitle, props.hint);
1075
+ const choices = normalizeSelectOptions(props.roles ?? props.availableRoles ?? []);
1076
+ const selected = firstText(props.role, props.defaultRoleId) ?? choices[0]?.value;
1077
+ return h("form", {
1078
+ class: "oods-role-assignment-form",
1079
+ "data-oods-component": "RoleAssignmentForm",
1080
+ "data-form-type": "role-assignment",
1081
+ onSubmit: preventSubmit
1082
+ }, [
1083
+ formHeader(title, subtitle),
1084
+ h("div", { "data-form-content": "true" }, content.length ? content : [
1085
+ h("label", { "data-form-control": "select" }, [h("span", "Role"), h("select", { name: "role", value: selected }, selectOptionNodes(choices, selected))]),
1086
+ h("label", { "data-form-control": "input" }, [h("span", "Assignee"), h("input", { type: "text", name: "assignee", value: firstText(props.assignee, props.member) })])
1087
+ ])
1088
+ ]);
1089
+ };
1090
+ }
1091
+ });
1092
+ var StatusSelector = defineComponent({
1093
+ name: "OodsStatusSelector",
1094
+ props: {
1095
+ label: String,
1096
+ title: String,
1097
+ help: String,
1098
+ options: Array,
1099
+ states: Array,
1100
+ value: String,
1101
+ status: String,
1102
+ modelValue: String
1103
+ },
1104
+ emits: { "update:modelValue": (_value) => true, change: (_value) => true },
1105
+ setup(props, { emit, slots }) {
1106
+ return () => {
1107
+ const content = authoredContent(slots.default?.());
1108
+ const label = firstText(props.label, props.title) ?? "Status";
1109
+ const choices = normalizeSelectOptions(props.options ?? props.states ?? ["draft", "active", "inactive"]);
1110
+ const selected = props.modelValue ?? props.value ?? firstText(props.status);
1111
+ const onChange = (event) => {
1112
+ const next = event.target.value;
1113
+ emit("update:modelValue", next);
1114
+ emit("change", next);
1115
+ };
1116
+ return h("div", {
1117
+ class: "oods-status-selector",
1118
+ "data-oods-component": "StatusSelector",
1119
+ "data-summary-type": "status-selector"
1120
+ }, content.length ? content : [
1121
+ h("label", { "data-form-control": "select" }, [
1122
+ h("span", label),
1123
+ h("select", { name: "status", value: selected, onChange }, selectOptionNodes(choices, selected))
1124
+ ]),
1125
+ props.help ? h("p", { class: "oods-field-help" }, props.help) : null
1126
+ ]);
1127
+ };
1128
+ }
1129
+ });
1130
+ var TagInput = defineComponent({
1131
+ name: "OodsTagInput",
1132
+ props: {
1133
+ ...FORM_SHELL_PROPS,
1134
+ tags: Array,
1135
+ value: String,
1136
+ placeholder: String,
1137
+ modelValue: String
1138
+ },
1139
+ emits: { "update:modelValue": (_value) => true, input: (_value) => true, change: (_value) => true },
1140
+ setup(props, { emit, slots }) {
1141
+ return () => {
1142
+ const content = authoredContent(slots.default?.());
1143
+ const title = firstText(props.title, props.label, props.heading, props.name) ?? "Tag Input";
1144
+ const subtitle = firstText(props.description, props.subtitle, props.hint);
1145
+ const items = normalizeTagItems(props.tags);
1146
+ const current = props.modelValue ?? props.value;
1147
+ const read = (event) => event.target.value;
1148
+ return h("fieldset", {
1149
+ class: "oods-tag-input",
1150
+ "data-oods-component": "TagInput",
1151
+ "data-form-type": "tag-input"
1152
+ }, [
1153
+ ...fieldsetHeader(title, subtitle),
1154
+ h("div", { "data-form-content": "true" }, content.length ? content : [
1155
+ h("label", { "data-form-control": "input" }, [
1156
+ h("span", "Tag"),
1157
+ h("input", {
1158
+ type: "text",
1159
+ name: "tag",
1160
+ placeholder: firstText(props.placeholder),
1161
+ value: current,
1162
+ onInput: (event) => {
1163
+ emit("update:modelValue", read(event));
1164
+ emit("input", read(event));
1165
+ },
1166
+ onChange: (event) => emit("change", read(event))
1167
+ })
1168
+ ]),
1169
+ items.length ? h("div", { "data-tag-list": "true" }, items.map((tag, index) => h("span", { key: index, "data-tag-item": "true" }, tag))) : null
1170
+ ])
1171
+ ]);
1172
+ };
1173
+ }
1174
+ });
1175
+ var TemplatePicker = defineComponent({
1176
+ name: "OodsTemplatePicker",
1177
+ props: {
1178
+ ...FORM_SHELL_PROPS,
1179
+ templates: Array,
1180
+ options: Array,
1181
+ templateId: String,
1182
+ value: String,
1183
+ channels: Array,
1184
+ channel: String
1185
+ },
1186
+ setup(props, { slots }) {
1187
+ return () => {
1188
+ const content = authoredContent(slots.default?.());
1189
+ const title = firstText(props.title, props.label, props.heading, props.name) ?? "Template Picker";
1190
+ const subtitle = firstText(props.description, props.subtitle, props.hint);
1191
+ const templateChoices = normalizeSelectOptions(props.templates ?? props.options ?? []);
1192
+ const channelChoices = normalizeSelectOptions(props.channels ?? ["email", "sms", "in_app"]);
1193
+ const selectedTemplate = firstText(props.templateId, props.value) ?? templateChoices[0]?.value;
1194
+ const selectedChannel = firstText(props.channel) ?? channelChoices[0]?.value;
1195
+ return h("fieldset", {
1196
+ class: "oods-template-picker",
1197
+ "data-oods-component": "TemplatePicker",
1198
+ "data-form-type": "template-picker"
1199
+ }, [
1200
+ ...fieldsetHeader(title, subtitle),
1201
+ h("div", { "data-form-content": "true" }, content.length ? content : [
1202
+ h("label", { "data-form-control": "select" }, [h("span", "Template"), h("select", { name: "template", value: selectedTemplate }, selectOptionNodes(templateChoices, selectedTemplate))]),
1203
+ h("label", { "data-form-control": "select" }, [h("span", "Channel"), h("select", { name: "channel", value: selectedChannel }, selectOptionNodes(channelChoices, selectedChannel))])
1204
+ ])
1205
+ ]);
1206
+ };
1207
+ }
1208
+ });
1209
+ var truncateLabel = (value, maxLength) => {
1210
+ const limit = typeof maxLength === "number" ? maxLength : Number(maxLength);
1211
+ return !Number.isFinite(limit) || limit <= 0 || value.length <= limit ? value : `${value.slice(0, Math.max(0, limit - 1)).trimEnd()}...`;
1212
+ };
1213
+ var InlineLabel = defineComponent({
1214
+ name: "OodsInlineLabel",
1215
+ props: { label: String, text: String, value: String, maxLength: [Number, String] },
1216
+ setup(props, { slots }) {
1217
+ return () => {
1218
+ const content = authoredContent(slots.default?.());
1219
+ return h(
1220
+ "span",
1221
+ { class: "oods-inline-label", "data-oods-component": "InlineLabel" },
1222
+ content.length ? content : truncateLabel(firstText(props.label, props.text, props.value) ?? "", props.maxLength)
1223
+ );
1224
+ };
1225
+ }
1226
+ });
1227
+ var LabelCell = defineComponent({
1228
+ name: "OodsLabelCell",
1229
+ props: { label: String, text: String, value: String, description: String, subtitle: String, sublabel: String, supporting: String, truncate: Boolean, maxLength: [Number, String] },
1230
+ setup(props, { slots }) {
1231
+ return () => {
1232
+ const content = authoredContent(slots.default?.());
1233
+ const limit = props.truncate ? props.maxLength ?? 40 : props.maxLength;
1234
+ const description = notEcho(firstText(props.description, props.subtitle, props.sublabel, props.supporting), firstText(props.label, props.text, props.value));
1235
+ return h(
1236
+ "span",
1237
+ { class: "oods-label-cell", "data-oods-component": "LabelCell" },
1238
+ content.length ? content : [
1239
+ h("span", { "data-oods-label-cell-primary": "true" }, truncateLabel(firstText(props.label, props.text, props.value) ?? "", limit)),
1240
+ description ? h("span", { "data-oods-label-cell-description": "true" }, truncateLabel(description, limit)) : null
1241
+ ]
1242
+ );
1243
+ };
1244
+ }
1245
+ });
1246
+ var FormLabelGroup = defineComponent({
1247
+ name: "OodsFormLabelGroup",
1248
+ props: { label: String, text: String, title: String, placeholder: String, hint: String, description: String, htmlFor: String, for: String, inputId: String },
1249
+ setup(props, { slots }) {
1250
+ return () => {
1251
+ const hint = firstText(props.placeholder, props.hint, props.description);
1252
+ return h("label", { class: "oods-form-label-group", "data-oods-component": "FormLabelGroup", for: props.htmlFor ?? props.for ?? props.inputId }, [
1253
+ h("span", { "data-oods-form-label": "true" }, firstText(props.label, props.text, props.title) ?? "Label"),
1254
+ ...authoredContent(slots.default?.()),
1255
+ hint ? h("span", { "data-oods-form-hint": "true" }, hint) : null
1256
+ ]);
1257
+ };
1258
+ }
1259
+ });
1260
+ var ClassificationBadge = createBadgeFamily({
1261
+ component: "ClassificationBadge",
1262
+ className: "oods-classification-badge",
1263
+ defaultLabel: "Classification",
1264
+ defaultVariant: "classification",
1265
+ labelKeys: ["label", "text", "category", "value"],
1266
+ statusKeys: ["status", "state", "mode"],
1267
+ extraProps: ["category", "mode"]
1268
+ });
1269
+ var ClassificationEditor = defineComponent({
1270
+ name: "OodsClassificationEditor",
1271
+ props: {
1272
+ title: String,
1273
+ label: String,
1274
+ heading: String,
1275
+ name: String,
1276
+ description: String,
1277
+ subtitle: String,
1278
+ hint: String,
1279
+ category: String,
1280
+ primaryCategory: String,
1281
+ tags: [String, Array],
1282
+ modes: Array,
1283
+ mode: String,
1284
+ classificationMode: String
1285
+ },
1286
+ setup(props, { slots }) {
1287
+ return () => {
1288
+ const content = authoredContent(slots.default?.());
1289
+ const tagText = typeof props.tags === "string" ? props.tags : props.tags === void 0 ? "" : JSON.stringify(props.tags);
1290
+ const choices = normalizeSelectOptions(Array.isArray(props.modes) ? props.modes : ["strict", "flexible"]);
1291
+ const selected = firstText(props.mode, props.classificationMode) ?? choices[0]?.value;
1292
+ return h("form", { class: "oods-classification-editor", "data-oods-component": "ClassificationEditor", "data-form-type": "classification-editor", onSubmit: (event) => event.preventDefault() }, [
1293
+ formHeader(firstText(props.title, props.label, props.heading, props.name) ?? "Classification Editor", firstText(props.description, props.subtitle, props.hint)),
1294
+ h("div", { "data-form-content": "true" }, content.length ? content : [
1295
+ h("label", { "data-form-control": "input" }, [h("span", "Category"), h("input", { type: "text", name: "category", value: firstText(props.category, props.primaryCategory) ?? "" })]),
1296
+ h("label", { "data-form-control": "input" }, [h("span", "Tags"), h("input", { type: "text", name: "tags", placeholder: "tag-1, tag-2", value: tagText })]),
1297
+ h("label", { "data-form-control": "select" }, [h("span", "Mode"), h("select", { name: "mode", value: selected }, selectOptionNodes(choices, selected))])
1298
+ ])
1299
+ ]);
1300
+ };
1301
+ }
1302
+ });
1303
+ var OwnerBadge = createBadgeFamily({
1304
+ component: "OwnerBadge",
1305
+ className: "oods-owner-badge",
1306
+ defaultLabel: "Owner",
1307
+ defaultVariant: "owner",
1308
+ labelKeys: ["label", "text", "owner", "ownerType", "value"],
1309
+ statusKeys: ["status", "state"],
1310
+ extraProps: ["owner", "ownerType"]
1311
+ });
1312
+ var OwnershipSummary = defineComponent({
1313
+ name: "OodsOwnershipSummary",
1314
+ props: { title: String, label: String, heading: String, name: String, ownerLabel: String, ownerId: String, owner_id: String, ownerType: String, owner_type: String, role: String, ownershipRole: String, summary: String, text: String, description: String },
1315
+ setup(props, { slots }) {
1316
+ return () => {
1317
+ const content = authoredContent(slots.default?.());
1318
+ const terms = [["Owner", props.ownerId === void 0 && props.owner_id === void 0 && props.ownerLabel === void 0 ? void 0 : formatReferenceLabel(props.ownerId ?? props.owner_id, props.ownerLabel, "Owner")], ["Owner Type", firstScalar(props.ownerType, props.owner_type)], ["Role", firstScalar(props.role, props.ownershipRole)]].filter((entry) => entry[1] !== void 0);
1319
+ const fallback = firstText(props.summary, props.text, props.description);
1320
+ return h("section", { class: "oods-ownership-summary", "data-oods-component": "OwnershipSummary", "data-summary-type": "ownership" }, [
1321
+ h("h2", { "data-summary-title": "true" }, firstText(props.title, props.label, props.heading, props.name) ?? "Ownership Summary"),
1322
+ ...content.length ? content : terms.length ? [h("dl", terms.map(([term, value]) => h("div", { key: term, "data-summary-item": "true" }, [h("dt", term), h("dd", value)])))] : fallback ? [h("p", { "data-summary-fallback": "true" }, fallback)] : [h("dl")]
1323
+ ]);
1324
+ };
1325
+ }
1326
+ });
1327
+ var TagSummary = defineComponent({
1328
+ name: "OodsTagSummary",
1329
+ props: { title: String, label: String, heading: String, name: String, tagCount: [Number, String], count: [Number, String], tags: [String, Array], summary: String, text: String, description: String },
1330
+ setup(props, { slots }) {
1331
+ return () => {
1332
+ const content = authoredContent(slots.default?.());
1333
+ const tagText = Array.isArray(props.tags) ? normalizeTagItems(props.tags).join(", ") || void 0 : firstText(props.tags);
1334
+ const terms = [["Tag Count", firstScalar(props.tagCount, props.count)], ["Tags", tagText]].filter((entry) => entry[1] !== void 0);
1335
+ const fallback = firstText(props.summary, props.text, props.description);
1336
+ return h("section", { class: "oods-tags-summary", "data-oods-component": "TagSummary", "data-summary-type": "tags" }, [
1337
+ h("h2", { "data-summary-title": "true" }, firstText(props.title, props.label, props.heading, props.name) ?? "Tag Summary"),
1338
+ ...content.length ? content : terms.length ? [h("dl", terms.map(([term, value]) => h("div", { key: term, "data-summary-item": "true" }, [h("dt", term), h("dd", value)])))] : fallback ? [h("p", { "data-summary-fallback": "true" }, fallback)] : [h("dl")]
1339
+ ]);
1340
+ };
1341
+ }
1342
+ });
1343
+ var OwnershipMeta = defineComponent({
1344
+ name: "OodsOwnershipMeta",
1345
+ props: { title: String, label: String, heading: String, name: String, ownerType: String, owner_type: String, role: String, ownershipRole: String },
1346
+ setup(props, { slots }) {
1347
+ return () => {
1348
+ const content = authoredContent(slots.default?.());
1349
+ const terms = [["Owner Type", firstScalar(props.ownerType, props.owner_type)], ["Role", firstScalar(props.role, props.ownershipRole)]].filter((entry) => entry[1] !== void 0);
1350
+ return h("div", { class: "oods-ownership-meta", "data-oods-component": "OwnershipMeta", "data-meta-type": "ownership" }, content.length ? content : [
1351
+ h("span", { "data-meta-title": "true" }, firstText(props.title, props.label, props.heading, props.name) ?? "Ownership"),
1352
+ ...terms.map(([term, value]) => h("span", { key: term, "data-meta-item": "true" }, [h("strong", `${term}:`), ` ${value}`]))
1353
+ ]);
1354
+ };
1355
+ }
1356
+ });
1357
+ var ArchivePill = createBadgeFamily({
1358
+ component: "ArchivePill",
1359
+ className: "oods-archive-pill",
1360
+ defaultLabel: "Archive",
1361
+ defaultVariant: "archive",
1362
+ labelKeys: ["label", "text", "status", "state", "isArchived", "value"],
1363
+ statusKeys: ["status", "state", "isArchived", "value"],
1364
+ extraProps: ["isArchived"],
1365
+ booleanProps: ["isArchived", "value"]
1366
+ });
1367
+ var CancellationBadge = createBadgeFamily({
1368
+ component: "CancellationBadge",
1369
+ className: "oods-cancellation-badge",
1370
+ defaultLabel: "Cancellation",
1371
+ defaultVariant: "cancellation",
1372
+ labelKeys: ["label", "text", "status", "state", "cancelAtPeriodEnd", "value"],
1373
+ statusKeys: ["status", "state", "cancelAtPeriodEnd", "isCancelled", "value"],
1374
+ extraProps: ["cancelAtPeriodEnd", "isCancelled"],
1375
+ booleanProps: ["cancelAtPeriodEnd", "isCancelled", "value"]
1376
+ });
1377
+ var ArchiveSummary = defineComponent({
1378
+ name: "OodsArchiveSummary",
1379
+ props: { title: String, label: String, heading: String, name: String, isArchived: { type: [String, Boolean], default: void 0 }, archived: { type: [String, Boolean], default: void 0 }, status: { type: [String, Boolean], default: void 0 }, archivedAt: String, reason: String, archiveReason: String, summary: String, text: String, description: String },
1380
+ setup(props, { slots }) {
1381
+ return () => {
1382
+ const content = authoredContent(slots.default?.());
1383
+ const terms = [["Archived", summaryValue(props.isArchived ?? props.archived ?? props.status)], ["Archived At", props.archivedAt ? formatDateTime(props.archivedAt) : void 0], ["Reason", firstText(props.reason, props.archiveReason)]].filter((entry) => entry[1] !== void 0);
1384
+ const fallback = firstText(props.summary, props.text, props.description);
1385
+ return h("section", { class: "oods-archive-summary", "data-oods-component": "ArchiveSummary", "data-summary-type": "archive" }, [
1386
+ h("h2", { "data-summary-title": "true" }, firstText(props.title, props.label, props.heading, props.name) ?? "Archive Summary"),
1387
+ ...content.length ? content : terms.length ? [h("dl", terms.map(([term, value]) => h("div", { key: term, "data-summary-item": "true" }, [h("dt", term), h("dd", value)])))] : fallback ? [h("p", { "data-summary-fallback": "true" }, fallback)] : [h("dl")]
1388
+ ]);
1389
+ };
1390
+ }
1391
+ });
1392
+ var PriceCardMeta = defineComponent({
1393
+ name: "OodsPriceCardMeta",
1394
+ props: { title: String, label: String, heading: String, name: String, model: String, pricingModel: String, interval: String, billingInterval: String },
1395
+ setup(props, { slots }) {
1396
+ return () => {
1397
+ const content = authoredContent(slots.default?.());
1398
+ const terms = [["Model", firstText(props.model, props.pricingModel)], ["Interval", firstText(props.interval, props.billingInterval)]].filter((entry) => entry[1] !== void 0);
1399
+ return h("div", { class: "oods-price-card-meta", "data-oods-component": "PriceCardMeta", "data-meta-type": "price" }, content.length ? content : [
1400
+ h("span", { "data-meta-title": "true" }, firstText(props.title, props.label, props.heading, props.name) ?? "Price"),
1401
+ ...terms.map(([term, value]) => h("span", { key: term, "data-meta-item": "true" }, [h("strong", `${term}:`), ` ${value}`]))
1402
+ ]);
1403
+ };
1404
+ }
1405
+ });
1406
+ var CancellationForm = defineComponent({
1407
+ name: "OodsCancellationForm",
1408
+ props: {
1409
+ title: String,
1410
+ label: String,
1411
+ heading: String,
1412
+ name: String,
1413
+ description: String,
1414
+ subtitle: String,
1415
+ hint: String,
1416
+ allowedReasons: Array,
1417
+ reasonCode: String,
1418
+ reason: String,
1419
+ cancellationReason: String,
1420
+ embedded: Boolean,
1421
+ reasonHelp: String,
1422
+ codeHelp: String
1423
+ },
1424
+ setup(props, { slots }) {
1425
+ return () => {
1426
+ const content = authoredContent(slots.default?.());
1427
+ const choices = normalizeSelectOptions(props.allowedReasons ?? ["no_longer_needed", "budget", "duplicate"]);
1428
+ if (props.reasonCode && !choices.some((choice) => choice.value === props.reasonCode)) choices.unshift({ value: props.reasonCode, label: props.reasonCode });
1429
+ return h(props.embedded ? "fieldset" : "form", { class: "oods-cancellation-form", "data-oods-component": "CancellationForm", "data-form-type": "cancellation", onSubmit: props.embedded ? void 0 : (event) => event.preventDefault() }, [
1430
+ formHeader(firstText(props.title, props.label, props.heading, props.name) ?? "Cancellation Form", firstText(props.description, props.subtitle, props.hint)),
1431
+ h("div", { "data-form-content": "true" }, content.length ? content : [
1432
+ h("label", { "data-form-control": "select" }, [h("span", "Reason Code"), props.allowedReasons?.length === 0 ? h("input", { name: "reasonCode", value: props.reasonCode ?? "" }) : h("select", { name: "reasonCode", value: props.reasonCode }, selectOptionNodes(choices, props.reasonCode))]),
1433
+ props.codeHelp ? h("p", { class: "oods-field-help" }, props.codeHelp) : null,
1434
+ h("label", { "data-form-control": "textarea" }, [h("span", "Reason"), h("textarea", { name: "reason", value: firstText(props.reason, props.cancellationReason) ?? "" })]),
1435
+ props.reasonHelp ? h("p", { class: "oods-field-help" }, props.reasonHelp) : null
1436
+ ])
1437
+ ]);
1438
+ };
1439
+ }
1440
+ });
1441
+ function validationStyle(validation) {
1442
+ if (!validation) return void 0;
1443
+ const tone = validation.state === "error" ? "critical" : validation.state;
1444
+ return {
1445
+ "--cmp-input-message-border": `var(--sys-status-${tone}-border)`,
1446
+ "--cmp-input-message-text": `var(--sys-status-${tone}-text)`
1447
+ };
1448
+ }
1449
+ function useFieldMetadata(componentName, props, slots) {
1450
+ const generatedId = useId();
1451
+ const controlId = computed(() => props.id || `oods-${componentName.toLowerCase()}-${generatedId}`);
1452
+ const helpId = computed(() => `${controlId.value}-help`);
1453
+ const validationId = computed(() => `${controlId.value}-validation`);
1454
+ const describedBy = computed(() => {
1455
+ const ids = [];
1456
+ if (props.help || slots.help) ids.push(helpId.value);
1457
+ if (props.validation?.message || slots.validation) ids.push(validationId.value);
1458
+ return ids.length > 0 ? ids.join(" ") : void 0;
1459
+ });
1460
+ return { controlId, helpId, validationId, describedBy };
1461
+ }
1462
+ function renderField(componentName, props, slots, metadata, control) {
1463
+ const label = slots.label?.() ?? props.label;
1464
+ const help = slots.help?.() ?? props.help;
1465
+ const validation = slots.validation?.() ?? props.validation?.message;
1466
+ return h("div", {
1467
+ class: "oods-field",
1468
+ "data-oods-component": componentName,
1469
+ style: validationStyle(props.validation)
1470
+ }, [
1471
+ label ? h("label", { class: "oods-field-label", for: metadata.controlId.value }, [label, props.required ? h("span", { class: "oods-field-required", "aria-hidden": "true" }, "*") : null]) : null,
1472
+ control,
1473
+ help ? h("p", { id: metadata.helpId.value, class: "oods-field-help" }, help) : null,
1474
+ validation ? h("p", {
1475
+ id: metadata.validationId.value,
1476
+ class: "oods-field-error",
1477
+ role: props.validation?.state === "error" ? "alert" : void 0
1478
+ }, validation) : null
1479
+ ]);
1480
+ }
1481
+ var textFieldProps = {
1482
+ id: String,
1483
+ label: String,
1484
+ modelValue: String,
1485
+ value: String,
1486
+ defaultValue: { type: String, default: "" },
1487
+ placeholder: String,
1488
+ required: Boolean,
1489
+ disabled: Boolean,
1490
+ readOnly: Boolean,
1491
+ help: String,
1492
+ validation: Object,
1493
+ name: String,
1494
+ min: String,
1495
+ max: String,
1496
+ step: [String, Number]
1497
+ };
1498
+ function eventValue(event) {
1499
+ return event.target.value;
1500
+ }
1501
+ var Input = defineComponent({
1502
+ name: "OodsInput",
1503
+ props: {
1504
+ ...textFieldProps,
1505
+ type: { type: String, default: "text" }
1506
+ },
1507
+ emits: {
1508
+ "update:modelValue": (_value) => true,
1509
+ input: (_value) => true,
1510
+ change: (_value) => true
1511
+ },
1512
+ setup(props, { emit, slots }) {
1513
+ const internalValue = ref(props.defaultValue);
1514
+ const currentValue = computed(() => props.modelValue ?? props.value ?? internalValue.value);
1515
+ const metadata = useFieldMetadata("Input", props, slots);
1516
+ const updateValue = (nextValue) => {
1517
+ if (props.modelValue === void 0 && props.value === void 0) internalValue.value = nextValue;
1518
+ emit("update:modelValue", nextValue);
1519
+ emit("input", nextValue);
1520
+ };
1521
+ return () => renderField("Input", props, slots, metadata, h("input", {
1522
+ id: metadata.controlId.value,
1523
+ class: "oods-field-control",
1524
+ type: props.type,
1525
+ name: props.name,
1526
+ value: props.type === "datetime-local" ? dateTimeInputValue(currentValue.value) : currentValue.value,
1527
+ placeholder: props.placeholder,
1528
+ required: props.required,
1529
+ disabled: props.disabled,
1530
+ readonly: props.readOnly,
1531
+ min: props.min,
1532
+ max: props.max,
1533
+ step: props.step,
1534
+ "aria-invalid": props.validation?.state === "error" ? "true" : void 0,
1535
+ "aria-describedby": metadata.describedBy.value,
1536
+ "aria-errormessage": props.validation?.state === "error" ? metadata.validationId.value : void 0,
1537
+ onInput: (event) => updateValue(eventValue(event)),
1538
+ onChange: (event) => emit("change", eventValue(event))
1539
+ }));
1540
+ }
1541
+ });
1542
+ var DatePicker = defineComponent({
1543
+ name: "OodsDatePicker",
1544
+ inheritAttrs: false,
1545
+ props: textFieldProps,
1546
+ emits: {
1547
+ "update:modelValue": (_value) => true,
1548
+ input: (_value) => true,
1549
+ change: (_value) => true
1550
+ },
1551
+ setup(props, { attrs, emit, slots }) {
1552
+ return () => h("div", {
1553
+ ...attrs,
1554
+ class: ["oods-date-picker", attrs.class],
1555
+ "data-oods-component": "DatePicker"
1556
+ }, [h(Input, {
1557
+ ...props,
1558
+ type: "date",
1559
+ "onUpdate:modelValue": (value) => emit("update:modelValue", value),
1560
+ onInput: (value) => emit("input", value),
1561
+ onChange: (value) => emit("change", value)
1562
+ }, slots)]);
1563
+ }
1564
+ });
1565
+ var Textarea = defineComponent({
1566
+ name: "OodsTextarea",
1567
+ props: {
1568
+ ...textFieldProps,
1569
+ rows: { type: Number, default: 3 }
1570
+ },
1571
+ emits: {
1572
+ "update:modelValue": (_value) => true,
1573
+ input: (_value) => true,
1574
+ change: (_value) => true
1575
+ },
1576
+ setup(props, { emit, slots }) {
1577
+ const internalValue = ref(props.defaultValue);
1578
+ const currentValue = computed(() => props.modelValue ?? props.value ?? internalValue.value);
1579
+ const metadata = useFieldMetadata("Textarea", props, slots);
1580
+ const updateValue = (nextValue) => {
1581
+ if (props.modelValue === void 0 && props.value === void 0) internalValue.value = nextValue;
1582
+ emit("update:modelValue", nextValue);
1583
+ emit("input", nextValue);
1584
+ };
1585
+ return () => renderField("Textarea", props, slots, metadata, h("textarea", {
1586
+ id: metadata.controlId.value,
1587
+ class: "oods-field-control",
1588
+ name: props.name,
1589
+ value: currentValue.value,
1590
+ rows: props.rows,
1591
+ placeholder: props.placeholder,
1592
+ required: props.required,
1593
+ disabled: props.disabled,
1594
+ readonly: props.readOnly,
1595
+ "aria-invalid": props.validation?.state === "error" ? "true" : void 0,
1596
+ "aria-describedby": metadata.describedBy.value,
1597
+ "aria-errormessage": props.validation?.state === "error" ? metadata.validationId.value : void 0,
1598
+ onInput: (event) => updateValue(eventValue(event)),
1599
+ onChange: (event) => emit("change", eventValue(event))
1600
+ }));
1601
+ }
1602
+ });
1603
+ var Select = defineComponent({
1604
+ name: "OodsSelect",
1605
+ props: {
1606
+ id: String,
1607
+ label: String,
1608
+ modelValue: String,
1609
+ value: String,
1610
+ defaultValue: { type: String, default: "" },
1611
+ placeholder: String,
1612
+ required: Boolean,
1613
+ disabled: Boolean,
1614
+ help: String,
1615
+ validation: Object,
1616
+ name: String,
1617
+ options: { type: Array, default: () => [] }
1618
+ },
1619
+ emits: {
1620
+ "update:modelValue": (_value) => true,
1621
+ change: (_value) => true
1622
+ },
1623
+ setup(props, { emit, slots }) {
1624
+ const internalValue = ref(props.defaultValue);
1625
+ const currentValue = computed(() => props.modelValue ?? props.value ?? internalValue.value);
1626
+ const metadata = useFieldMetadata("Select", props, slots);
1627
+ const selectValue = (nextValue) => {
1628
+ if (props.modelValue === void 0 && props.value === void 0) internalValue.value = nextValue;
1629
+ emit("update:modelValue", nextValue);
1630
+ emit("change", nextValue);
1631
+ };
1632
+ return () => renderField("Select", props, slots, metadata, h("select", {
1633
+ id: metadata.controlId.value,
1634
+ class: "oods-field-control",
1635
+ name: props.name,
1636
+ value: currentValue.value,
1637
+ required: props.required,
1638
+ disabled: props.disabled,
1639
+ "aria-invalid": props.validation?.state === "error" ? "true" : void 0,
1640
+ "aria-describedby": metadata.describedBy.value,
1641
+ "aria-errormessage": props.validation?.state === "error" ? metadata.validationId.value : void 0,
1642
+ onChange: (event) => selectValue(eventValue(event))
1643
+ }, [
1644
+ props.placeholder ? h("option", {
1645
+ value: "",
1646
+ selected: currentValue.value === "",
1647
+ disabled: true
1648
+ }, props.placeholder) : null,
1649
+ ...props.options.map((option) => h("option", {
1650
+ key: option.value,
1651
+ value: option.value,
1652
+ selected: option.value === currentValue.value,
1653
+ disabled: option.disabled
1654
+ }, slots.option?.({ option }) ?? option.label))
1655
+ ]));
1656
+ }
1657
+ });
1658
+ var Checkbox = defineComponent({
1659
+ name: "OodsCheckbox",
1660
+ props: {
1661
+ id: String,
1662
+ label: String,
1663
+ modelValue: { type: Boolean, default: void 0 },
1664
+ checked: { type: Boolean, default: void 0 },
1665
+ defaultChecked: Boolean,
1666
+ required: Boolean,
1667
+ disabled: Boolean,
1668
+ help: String,
1669
+ validation: Object,
1670
+ name: String
1671
+ },
1672
+ emits: {
1673
+ "update:modelValue": (_value) => true,
1674
+ change: (_value) => true
1675
+ },
1676
+ setup(props, { emit, slots }) {
1677
+ const internalChecked = ref(props.defaultChecked);
1678
+ const currentChecked = computed(() => props.modelValue ?? props.checked ?? internalChecked.value);
1679
+ const metadata = useFieldMetadata("Checkbox", props, slots);
1680
+ const change = (event) => {
1681
+ const checked = event.target.checked;
1682
+ if (props.modelValue === void 0 && props.checked === void 0) internalChecked.value = checked;
1683
+ emit("update:modelValue", checked);
1684
+ emit("change", checked);
1685
+ };
1686
+ return () => {
1687
+ const label = slots.label?.() ?? props.label;
1688
+ const help = slots.help?.() ?? props.help;
1689
+ const validation = slots.validation?.() ?? props.validation?.message;
1690
+ return h("div", {
1691
+ class: "oods-field",
1692
+ "data-oods-component": "Checkbox",
1693
+ style: validationStyle(props.validation)
1694
+ }, [
1695
+ h("label", { class: "oods-checkbox", for: metadata.controlId.value }, [
1696
+ h("input", {
1697
+ id: metadata.controlId.value,
1698
+ type: "checkbox",
1699
+ name: props.name,
1700
+ checked: currentChecked.value,
1701
+ required: props.required,
1702
+ disabled: props.disabled,
1703
+ "aria-invalid": props.validation?.state === "error" ? "true" : void 0,
1704
+ "aria-describedby": metadata.describedBy.value,
1705
+ "aria-errormessage": props.validation?.state === "error" ? metadata.validationId.value : void 0,
1706
+ onChange: change
1707
+ }),
1708
+ h("span", { class: "oods-field-label" }, [
1709
+ label,
1710
+ props.required ? h("span", { class: "oods-field-required", "aria-hidden": "true" }, "*") : null
1711
+ ])
1712
+ ]),
1713
+ help ? h("p", { id: metadata.helpId.value, class: "oods-field-help" }, help) : null,
1714
+ validation ? h("p", {
1715
+ id: metadata.validationId.value,
1716
+ class: "oods-field-error",
1717
+ role: props.validation?.state === "error" ? "alert" : void 0
1718
+ }, validation) : null
1719
+ ]);
1720
+ };
1721
+ }
1722
+ });
1723
+ var TableCaption = defineComponent({
1724
+ name: "OodsTableCaption",
1725
+ setup(_props, { slots }) {
1726
+ return () => h("caption", {}, slots.default?.());
1727
+ }
1728
+ });
1729
+ var TableHead = defineComponent({
1730
+ name: "OodsTableHead",
1731
+ setup(_props, { slots }) {
1732
+ return () => h("thead", {}, slots.default?.());
1733
+ }
1734
+ });
1735
+ var TableBody = defineComponent({
1736
+ name: "OodsTableBody",
1737
+ setup(_props, { slots }) {
1738
+ return () => h("tbody", {}, slots.default?.());
1739
+ }
1740
+ });
1741
+ var TableRow = defineComponent({
1742
+ name: "OodsTableRow",
1743
+ setup(_props, { slots }) {
1744
+ return () => h("tr", {}, slots.default?.());
1745
+ }
1746
+ });
1747
+ var TableHeaderCell = defineComponent({
1748
+ name: "OodsTableHeaderCell",
1749
+ props: {
1750
+ scope: { type: String, default: "col" }
1751
+ },
1752
+ setup(props, { slots }) {
1753
+ return () => h("th", { scope: props.scope }, slots.default?.());
1754
+ }
1755
+ });
1756
+ var TableCell = defineComponent({
1757
+ name: "OodsTableCell",
1758
+ setup(_props, { slots }) {
1759
+ return () => h("td", {}, slots.default?.());
1760
+ }
1761
+ });
1762
+ var Table = defineComponent({
1763
+ name: "OodsTable",
1764
+ props: {
1765
+ caption: { type: String, default: "" },
1766
+ columns: { type: Array, default: () => [] },
1767
+ rows: { type: Array, default: () => [] },
1768
+ density: { type: String, default: "default" },
1769
+ selectable: Boolean
1770
+ },
1771
+ emits: {
1772
+ rowActivate: (_rowId) => true
1773
+ },
1774
+ setup(props, { emit, slots }) {
1775
+ const activate = (row) => emit("rowActivate", row.id);
1776
+ return () => h("table", {
1777
+ class: "oods-table",
1778
+ "data-oods-component": "Table",
1779
+ "data-density": props.density
1780
+ }, [
1781
+ h(TableCaption, {}, {
1782
+ default: () => slots.caption?.() ?? props.caption
1783
+ }),
1784
+ h(TableHead, {}, {
1785
+ default: () => slots.head?.() ?? h(TableRow, {}, {
1786
+ default: () => props.columns.map((column) => h(TableHeaderCell, { key: column.key }, {
1787
+ default: () => slots.headerCell?.({ column }) ?? column.label
1788
+ }))
1789
+ })
1790
+ }),
1791
+ h(TableBody, {}, {
1792
+ default: () => slots.body?.() ?? (props.rows.length === 0 ? h(TableRow, {}, {
1793
+ default: () => h(TableCell, {
1794
+ colspan: Math.max(props.columns.length, 1)
1795
+ }, {
1796
+ default: () => "No rows available."
1797
+ })
1798
+ }) : props.rows.map((row, rowIndex) => slots.row?.({ row, rowIndex, activate: () => activate(row) }) ?? h(TableRow, { key: row.id }, {
1799
+ default: () => props.columns.map((column, columnIndex) => {
1800
+ const content = slots.cell?.({ row, column, value: row[column.key] }) ?? String(row[column.key] ?? "");
1801
+ return h(TableCell, { key: column.key }, {
1802
+ default: () => props.selectable && columnIndex === 0 ? h("button", {
1803
+ type: "button",
1804
+ class: "oods-table-row-action",
1805
+ onClick: () => activate(row)
1806
+ }, content) : content
1807
+ });
1808
+ })
1809
+ })))
1810
+ })
1811
+ ]);
1812
+ }
1813
+ });
1814
+ function itemDisabled(item) {
1815
+ return item.disabled === true || item.isDisabled === true;
1816
+ }
1817
+ function enabledItems(items) {
1818
+ return items.filter((item) => !itemDisabled(item));
1819
+ }
1820
+ function initialTabId(items, preferred) {
1821
+ if (preferred && items.some((item) => item.id === preferred && !itemDisabled(item))) return preferred;
1822
+ return enabledItems(items)[0]?.id ?? "";
1823
+ }
1824
+ function domId(value) {
1825
+ return value.replace(/[^A-Za-z0-9_-]/g, "-");
1826
+ }
1827
+ var Tabs = defineComponent({
1828
+ name: "OodsTabs",
1829
+ props: {
1830
+ items: { type: Array, default: () => [] },
1831
+ selectedId: String,
1832
+ defaultSelectedId: String,
1833
+ size: { type: String, default: "md" },
1834
+ overflowLabel: { type: String, default: "More" },
1835
+ ariaLabel: { type: String, default: "Tabs" }
1836
+ },
1837
+ emits: {
1838
+ "update:selectedId": (_id) => true,
1839
+ change: (_id) => true
1840
+ },
1841
+ setup(props, { emit, slots }) {
1842
+ const baseId = domId(useId());
1843
+ const internalSelectedId = ref(initialTabId(props.items, props.defaultSelectedId));
1844
+ const activeId = computed(() => props.selectedId !== void 0 ? initialTabId(props.items, props.selectedId) : initialTabId(props.items, internalSelectedId.value));
1845
+ const tabRefs = /* @__PURE__ */ new Map();
1846
+ const tabWidths = /* @__PURE__ */ new Map();
1847
+ const listRef = ref();
1848
+ const visibleLimit = ref(Number.POSITIVE_INFINITY);
1849
+ const overflowOpen = ref(false);
1850
+ let resizeObserver;
1851
+ const visibleItems = computed(() => {
1852
+ if (visibleLimit.value >= props.items.length) return [...props.items];
1853
+ const count = Math.max(1, visibleLimit.value);
1854
+ const visible = props.items.slice(0, count);
1855
+ const active = props.items.find((item) => item.id === activeId.value);
1856
+ if (active && !visible.some((item) => item.id === active.id)) {
1857
+ visible.splice(Math.max(visible.length - 1, 0), 1, active);
1858
+ visible.sort((left, right) => props.items.findIndex((item) => item.id === left.id) - props.items.findIndex((item) => item.id === right.id));
1859
+ }
1860
+ return visible;
1861
+ });
1862
+ const overflowItems = computed(() => props.items.filter((item) => !visibleItems.value.some((visible) => visible.id === item.id)));
1863
+ const tabId = (id) => `oods-${baseId}-tab-${domId(id)}`;
1864
+ const panelId = (id) => `oods-${baseId}-panel-${domId(id)}`;
1865
+ const focusTab = (id) => nextTick(() => tabRefs.get(id)?.focus());
1866
+ const selectTab = (id) => {
1867
+ const item = props.items.find((candidate) => candidate.id === id);
1868
+ if (!item || itemDisabled(item)) return;
1869
+ if (props.selectedId === void 0) internalSelectedId.value = id;
1870
+ emit("update:selectedId", id);
1871
+ emit("change", id);
1872
+ void focusTab(id);
1873
+ };
1874
+ const onTabKeydown = (event, currentId) => {
1875
+ const available = enabledItems(props.items);
1876
+ if (available.length === 0) return;
1877
+ const currentIndex = Math.max(available.findIndex((item) => item.id === currentId), 0);
1878
+ let nextIndex;
1879
+ if (event.key === "ArrowRight") nextIndex = (currentIndex + 1) % available.length;
1880
+ if (event.key === "ArrowLeft") nextIndex = (currentIndex - 1 + available.length) % available.length;
1881
+ if (event.key === "Home") nextIndex = 0;
1882
+ if (event.key === "End") nextIndex = available.length - 1;
1883
+ if (nextIndex === void 0) return;
1884
+ event.preventDefault();
1885
+ selectTab(available[nextIndex].id);
1886
+ };
1887
+ const recalculateOverflow = () => {
1888
+ const list = listRef.value;
1889
+ if (!list || list.clientWidth <= 0 || props.items.length <= 1) {
1890
+ visibleLimit.value = Number.POSITIVE_INFINITY;
1891
+ return;
1892
+ }
1893
+ for (const [id, element] of tabRefs) {
1894
+ if (element.offsetWidth > 0) tabWidths.set(id, element.offsetWidth);
1895
+ }
1896
+ const widths = props.items.map((item) => tabWidths.get(item.id) ?? 120);
1897
+ const total = widths.reduce((sum, width) => sum + width, 0);
1898
+ if (total <= list.clientWidth) {
1899
+ visibleLimit.value = Number.POSITIVE_INFINITY;
1900
+ return;
1901
+ }
1902
+ const availableWidth = Math.max(list.clientWidth - 96, 0);
1903
+ let usedWidth = 0;
1904
+ let count = 0;
1905
+ for (const width of widths) {
1906
+ if (count > 0 && usedWidth + width > availableWidth) break;
1907
+ usedWidth += width;
1908
+ count += 1;
1909
+ }
1910
+ visibleLimit.value = Math.max(1, count);
1911
+ };
1912
+ watch(
1913
+ () => props.items,
1914
+ (items) => {
1915
+ if (!items.some((item) => item.id === internalSelectedId.value && !itemDisabled(item))) {
1916
+ internalSelectedId.value = initialTabId(items, props.defaultSelectedId);
1917
+ }
1918
+ void nextTick(recalculateOverflow);
1919
+ }
1920
+ );
1921
+ onMounted(() => {
1922
+ void nextTick(recalculateOverflow);
1923
+ if (typeof ResizeObserver === "function" && listRef.value) {
1924
+ resizeObserver = new ResizeObserver(recalculateOverflow);
1925
+ resizeObserver.observe(listRef.value);
1926
+ } else if (typeof window !== "undefined") {
1927
+ window.addEventListener("resize", recalculateOverflow);
1928
+ }
1929
+ });
1930
+ onBeforeUnmount(() => {
1931
+ resizeObserver?.disconnect();
1932
+ if (typeof window !== "undefined") window.removeEventListener("resize", recalculateOverflow);
1933
+ });
1934
+ return () => h("div", {
1935
+ class: "oods-tabs",
1936
+ "data-oods-component": "Tabs",
1937
+ "data-size": props.size
1938
+ }, [
1939
+ h("div", {
1940
+ ref: listRef,
1941
+ class: "oods-tab-list",
1942
+ role: "tablist",
1943
+ "aria-label": props.ariaLabel
1944
+ }, [
1945
+ ...visibleItems.value.map((item) => h("button", {
1946
+ key: item.id,
1947
+ ref: (element) => {
1948
+ if (element instanceof HTMLButtonElement) tabRefs.set(item.id, element);
1949
+ else tabRefs.delete(item.id);
1950
+ },
1951
+ id: tabId(item.id),
1952
+ class: "oods-tab",
1953
+ type: "button",
1954
+ role: "tab",
1955
+ disabled: itemDisabled(item),
1956
+ tabindex: item.id === activeId.value ? 0 : -1,
1957
+ "aria-selected": item.id === activeId.value,
1958
+ "aria-controls": panelId(item.id),
1959
+ "data-tab-id": item.id,
1960
+ onClick: () => selectTab(item.id),
1961
+ onKeydown: (event) => onTabKeydown(event, item.id)
1962
+ }, slots.itemLabel?.({ item, selected: item.id === activeId.value }) ?? [item.label])),
1963
+ overflowItems.value.length > 0 ? h("div", { class: "oods-tabs-overflow" }, [
1964
+ h("button", {
1965
+ type: "button",
1966
+ class: "oods-tabs-overflow-trigger",
1967
+ "aria-label": `${props.overflowLabel} tabs`,
1968
+ "aria-haspopup": "menu",
1969
+ "aria-expanded": overflowOpen.value,
1970
+ onClick: () => {
1971
+ overflowOpen.value = !overflowOpen.value;
1972
+ }
1973
+ }, props.overflowLabel),
1974
+ overflowOpen.value ? h("div", { class: "oods-tabs-overflow-menu", role: "menu" }, overflowItems.value.map((item) => h("button", {
1975
+ key: item.id,
1976
+ type: "button",
1977
+ role: "menuitem",
1978
+ disabled: itemDisabled(item),
1979
+ "aria-current": item.id === activeId.value ? "page" : void 0,
1980
+ onClick: () => {
1981
+ selectTab(item.id);
1982
+ overflowOpen.value = false;
1983
+ }
1984
+ }, [item.label]))) : null
1985
+ ]) : null
1986
+ ]),
1987
+ ...props.items.map((item) => h("div", {
1988
+ key: item.id,
1989
+ id: panelId(item.id),
1990
+ class: "oods-tab-panel",
1991
+ role: "tabpanel",
1992
+ tabindex: 0,
1993
+ hidden: item.id !== activeId.value,
1994
+ "aria-labelledby": tabId(item.id)
1995
+ }, slots.panel?.({ item, selected: item.id === activeId.value }) ?? [item.panel]))
1996
+ ]);
1997
+ }
1998
+ });
1999
+
2000
+ // src/ported-core.ts
2001
+ var STATUS_METADATA = {
2002
+ subscription: {
2003
+ future: {
2004
+ description: "Subscription is scheduled but not yet active.",
2005
+ tone: "info",
2006
+ icon: "\u23F2"
2007
+ },
2008
+ trialing: {
2009
+ description: "Subscription is in a trial period before billing begins.",
2010
+ tone: "accent",
2011
+ icon: "\u2605"
2012
+ },
2013
+ active: {
2014
+ description: "Subscription is paid and service is provisioned.",
2015
+ tone: "success",
2016
+ icon: "\u2714\uFE0E"
2017
+ },
2018
+ paused: {
2019
+ description: "Billing paused temporarily by finance or customer request.",
2020
+ tone: "neutral",
2021
+ icon: "\u23F8"
2022
+ },
2023
+ pending_cancellation: {
2024
+ description: "Subscription will cancel at period end but remains active now.",
2025
+ tone: "info",
2026
+ icon: "\u2026"
2027
+ },
2028
+ past_due: {
2029
+ description: "Renewal payment failed; smart retries ongoing and access retained during the grace window.",
2030
+ tone: "critical",
2031
+ icon: "\u26A0\uFE0E"
2032
+ },
2033
+ unpaid: {
2034
+ description: "Payment retries exhausted; service access revoked.",
2035
+ tone: "critical",
2036
+ icon: "\u2A2F"
2037
+ },
2038
+ terminated: {
2039
+ description: "Subscription ended; service access revoked and billing stopped.",
2040
+ tone: "neutral",
2041
+ icon: "\u2205"
2042
+ }
2043
+ },
2044
+ invoice: {
2045
+ draft: {
2046
+ description: "Invoice is editable and not yet finalized.",
2047
+ tone: "neutral",
2048
+ icon: "\u270E"
2049
+ },
2050
+ posted: {
2051
+ description: "Invoice finalized with terms before collection.",
2052
+ tone: "info",
2053
+ icon: "\u2026"
2054
+ },
2055
+ paid: {
2056
+ description: "Invoice collected successfully in full.",
2057
+ tone: "success",
2058
+ icon: "\u2714\uFE0E"
2059
+ },
2060
+ past_due: {
2061
+ description: "Invoice is unpaid past its due date.",
2062
+ tone: "critical",
2063
+ icon: "\u2A2F"
2064
+ },
2065
+ void: {
2066
+ description: "Invoice canceled and no longer payable.",
2067
+ tone: "neutral",
2068
+ icon: "\u2205"
2069
+ }
2070
+ }
2071
+ };
2072
+ var STATUS_ALIASES = {
2073
+ subscription: { delinquent: "unpaid" }
2074
+ };
2075
+ function isRecord2(value) {
2076
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2077
+ }
2078
+ function firstText2(record, keys) {
2079
+ for (const key of keys) {
2080
+ const value = record[key];
2081
+ if (value === void 0 || value === null || value === "") continue;
2082
+ if (typeof value === "string") return value;
2083
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
2084
+ }
2085
+ return void 0;
2086
+ }
2087
+ function statusLabel2(status) {
2088
+ return status.split(/[_-]/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)).join(" ");
2089
+ }
2090
+ function getPortedStatusPresentation(domain, status) {
2091
+ const normalizedDomain = domain.toLowerCase();
2092
+ const normalizedStatus = status.toLowerCase();
2093
+ const alias = STATUS_ALIASES[normalizedDomain]?.[normalizedStatus];
2094
+ const metadata = STATUS_METADATA[normalizedDomain]?.[alias ?? normalizedStatus];
2095
+ if (!metadata) {
2096
+ return {
2097
+ label: statusLabel2(status || "Unknown"),
2098
+ description: "Status not found in registry; falling back to the requested token set.",
2099
+ tone: "neutral"
2100
+ };
2101
+ }
2102
+ return { ...metadata, label: statusLabel2(status) };
2103
+ }
2104
+ function formatPrice(amountCents, amount, currency) {
2105
+ const normalizedAmount = amount;
2106
+ if (normalizedAmount === void 0 || !Number.isFinite(normalizedAmount)) return void 0;
2107
+ const formatted = Number.isInteger(normalizedAmount) ? String(normalizedAmount) : normalizedAmount.toFixed(2);
2108
+ return currency ? `${currency.toUpperCase()} ${formatted}` : formatted;
2109
+ }
2110
+ function normalizeTimelineEvents(raw) {
2111
+ if (!Array.isArray(raw)) return [];
2112
+ const events = [];
2113
+ for (const entry of raw) {
2114
+ if (entry === void 0 || entry === null) continue;
2115
+ if (!isRecord2(entry)) {
2116
+ events.push({ label: String(entry) });
2117
+ continue;
2118
+ }
2119
+ const from = firstText2(entry, ["from"]);
2120
+ const to = firstText2(entry, ["to"]);
2121
+ events.push({
2122
+ label: firstText2(entry, ["label", "title"]) ?? (firstText2(entry, ["event", "status", "state"]) ? statusLabel2(firstText2(entry, ["event", "status", "state"])) : void 0) ?? firstText2(entry, ["text", "name"]) ?? (to ? from ? `${statusLabel2(from)} \u2192 ${statusLabel2(to)}` : statusLabel2(to) : void 0) ?? "Event",
2123
+ timestamp: firstText2(entry, ["timestamp", "datetime", "time", "at", "createdAt", "updatedAt"]),
2124
+ detail: firstText2(entry, ["detail", "description", "message"]) ?? (to ? from ? `${statusLabel2(from)} \u2192 ${statusLabel2(to)}` : statusLabel2(to) : void 0),
2125
+ actorId: firstText2(entry, ["actorId", "actor_id", "actor"]),
2126
+ reason: firstText2(entry, ["reason"])
2127
+ });
2128
+ }
2129
+ return events;
2130
+ }
2131
+ function integerAtLeast(value, minimum, fallback) {
2132
+ return Number.isFinite(value) ? Math.max(minimum, Math.floor(value)) : fallback;
2133
+ }
2134
+ function buildPaginationItems(options) {
2135
+ const count = integerAtLeast(options.count, 0, 0);
2136
+ const rawPage = integerAtLeast(options.page, 1, 1);
2137
+ const page = count > 0 ? Math.min(rawPage, count) : 1;
2138
+ const items = [{
2139
+ type: "previous",
2140
+ page: page > 1 ? page - 1 : page,
2141
+ selected: false,
2142
+ disabled: count === 0 || page <= 1
2143
+ }];
2144
+ if (count === 0) {
2145
+ items.push({ type: "next", page: 1, selected: false, disabled: true });
2146
+ return { page, count, items };
2147
+ }
2148
+ const siblingCount = integerAtLeast(options.siblingCount ?? 1, 0, 1);
2149
+ const boundaryCount = integerAtLeast(options.boundaryCount ?? 1, 1, 1);
2150
+ const candidates = count <= boundaryCount * 2 + siblingCount * 2 + 3 ? Array.from({ length: count }, (_, index) => index + 1) : [.../* @__PURE__ */ new Set([
2151
+ ...Array.from({ length: boundaryCount }, (_, index) => index + 1),
2152
+ ...Array.from({ length: siblingCount * 2 + 1 }, (_, index) => page - siblingCount + index),
2153
+ ...Array.from({ length: boundaryCount }, (_, index) => count - boundaryCount + index + 1)
2154
+ ])].filter((candidate) => candidate >= 1 && candidate <= count).sort((left, right) => left - right);
2155
+ let priorPage;
2156
+ let ellipsisIndex = 0;
2157
+ for (const pageNumber of candidates) {
2158
+ if (priorPage !== void 0 && pageNumber - priorPage > 1) {
2159
+ items.push({
2160
+ type: "ellipsis",
2161
+ selected: false,
2162
+ disabled: true,
2163
+ index: ellipsisIndex++
2164
+ });
2165
+ }
2166
+ items.push({
2167
+ type: "page",
2168
+ page: pageNumber,
2169
+ selected: pageNumber === page,
2170
+ disabled: false
2171
+ });
2172
+ priorPage = pageNumber;
2173
+ }
2174
+ items.push({
2175
+ type: "next",
2176
+ page: page < count ? page + 1 : page,
2177
+ selected: false,
2178
+ disabled: page >= count
2179
+ });
2180
+ return { page, count, items };
2181
+ }
2182
+ function relativeTimestampLabel(datetime, now) {
2183
+ if (now === void 0) return datetime;
2184
+ const timestamp = Date.parse(datetime);
2185
+ const reference = now instanceof Date ? now.getTime() : typeof now === "number" ? now : Date.parse(now);
2186
+ if (!Number.isFinite(timestamp) || !Number.isFinite(reference)) return datetime;
2187
+ const seconds = Math.round((timestamp - reference) / 1e3);
2188
+ const absoluteSeconds = Math.abs(seconds);
2189
+ const formatter = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
2190
+ if (absoluteSeconds < 60) return formatter.format(seconds, "second");
2191
+ const minutes = Math.round(seconds / 60);
2192
+ if (Math.abs(minutes) < 60) return formatter.format(minutes, "minute");
2193
+ const hours = Math.round(minutes / 60);
2194
+ if (Math.abs(hours) < 24) return formatter.format(hours, "hour");
2195
+ return formatter.format(Math.round(hours / 24), "day");
2196
+ }
2197
+
2198
+ // src/ported.ts
2199
+ function stringValue(value) {
2200
+ if (typeof value === "string") return value;
2201
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
2202
+ return void 0;
2203
+ }
2204
+ function firstDefined(...values) {
2205
+ return values.find((value) => value !== void 0);
2206
+ }
2207
+ function firstArray(...values) {
2208
+ return values.find((value) => Array.isArray(value)) ?? [];
2209
+ }
2210
+ function mergedClass(componentClass, value) {
2211
+ return value ? [componentClass, value] : [componentClass];
2212
+ }
2213
+ var StatusBadge = defineComponent({
2214
+ name: "OodsStatusBadge",
2215
+ inheritAttrs: false,
2216
+ props: {
2217
+ content: { type: [String, Number], default: void 0 },
2218
+ status: String,
2219
+ value: String,
2220
+ label: String,
2221
+ domain: { type: String, default: "subscription" },
2222
+ tone: String,
2223
+ emphasis: { type: String, default: "subtle" },
2224
+ showIcon: { type: Boolean, default: true },
2225
+ field: String,
2226
+ statusField: String,
2227
+ domainField: String,
2228
+ readOnly: Boolean,
2229
+ compact: Boolean,
2230
+ variant: String
2231
+ },
2232
+ setup(props, { attrs, slots }) {
2233
+ return () => {
2234
+ const status = props.status ?? props.value ?? "unknown";
2235
+ const presentation = getPortedStatusPresentation(props.domain, status);
2236
+ const tone = props.tone === "lifecycle" ? presentation.tone : props.tone ?? presentation.tone;
2237
+ const ariaLabel = stringValue(attrs["aria-label"]) ?? props.label ?? `Status: ${presentation.label}`;
2238
+ return h(Badge, {
2239
+ ...attrs,
2240
+ class: mergedClass("oods-status-badge", attrs.class),
2241
+ content: slots.default ? void 0 : props.content ?? presentation.label,
2242
+ status,
2243
+ domain: props.domain,
2244
+ tone,
2245
+ emphasis: props.variant === "solid" ? "solid" : props.emphasis,
2246
+ icon: props.showIcon ? presentation.icon : void 0,
2247
+ title: stringValue(attrs.title) ?? presentation.description,
2248
+ "aria-label": ariaLabel,
2249
+ "data-oods-component": "StatusBadge"
2250
+ }, slots);
2251
+ };
2252
+ }
2253
+ });
2254
+ function currencyLabel(amountCents, amount, currency, minorUnits) {
2255
+ const normalizedAmount = amountCents !== void 0 ? amountCents / currencyMinorUnits(currency, minorUnits) : amount;
2256
+ if (normalizedAmount === void 0 || !Number.isFinite(normalizedAmount)) return void 0;
2257
+ if (!currency) return formatPrice(void 0, normalizedAmount, void 0);
2258
+ try {
2259
+ return new Intl.NumberFormat("en-US", {
2260
+ style: "currency",
2261
+ currency: currency.toUpperCase()
2262
+ }).format(normalizedAmount);
2263
+ } catch {
2264
+ return formatPrice(void 0, normalizedAmount, currency);
2265
+ }
2266
+ }
2267
+ var PriceBadge = defineComponent({
2268
+ name: "OodsPriceBadge",
2269
+ inheritAttrs: false,
2270
+ props: {
2271
+ amountCents: Number,
2272
+ unitAmountCents: Number,
2273
+ minorUnits: Number,
2274
+ amount: Number,
2275
+ unitAmount: Number,
2276
+ currency: String,
2277
+ currencyCode: String,
2278
+ label: String,
2279
+ value: [String, Number],
2280
+ emphasis: { type: String, default: "subtle" },
2281
+ field: String,
2282
+ amountField: String,
2283
+ currencyField: String,
2284
+ intervalField: String,
2285
+ minorUnitsParameter: String
2286
+ },
2287
+ setup(props, { attrs, slots }) {
2288
+ return () => {
2289
+ const currency = props.currency ?? props.currencyCode;
2290
+ const cents = props.amountCents ?? props.unitAmountCents;
2291
+ const amount = props.amount ?? props.unitAmount;
2292
+ const content = props.label ?? props.value ?? currencyLabel(cents, amount, currency, props.minorUnits) ?? "Price";
2293
+ return h(Badge, {
2294
+ ...attrs,
2295
+ class: mergedClass("oods-price-badge", attrs.class),
2296
+ content,
2297
+ emphasis: props.emphasis,
2298
+ title: stringValue(attrs.title) ?? props.label,
2299
+ "data-oods-component": "PriceBadge",
2300
+ "data-price": "true",
2301
+ "data-badge-variant": "price",
2302
+ "data-currency": currency?.toUpperCase()
2303
+ }, slots);
2304
+ };
2305
+ }
2306
+ });
2307
+ var timelineProps = {
2308
+ title: String,
2309
+ source: Array,
2310
+ events: Array,
2311
+ history: Array,
2312
+ entries: Array,
2313
+ stateHistory: Array,
2314
+ auditLog: Array,
2315
+ status: String,
2316
+ allowedTransitions: Array,
2317
+ showActorId: { type: Boolean, default: true },
2318
+ showReason: { type: Boolean, default: true },
2319
+ maxVisible: Number,
2320
+ field: String,
2321
+ historyField: String,
2322
+ statesParameter: String,
2323
+ auditLogField: String,
2324
+ createdField: String,
2325
+ updatedField: String,
2326
+ eventField: String,
2327
+ eventTimestampField: String,
2328
+ eventOptionsParameter: String,
2329
+ showFromState: Boolean
2330
+ };
2331
+ function renderTimelineEvent(event, index, showActorId, showReason) {
2332
+ return h("li", { key: `${event.timestamp ?? "event"}-${index}` }, [
2333
+ h("article", { "data-timeline-event": "true" }, [
2334
+ h("p", { "data-timeline-label": "true" }, event.label),
2335
+ event.timestamp ? h("time", { "data-timeline-time": "true", datetime: event.timestamp }, formatDateTime(event.timestamp)) : null,
2336
+ event.detail ? h("p", { "data-timeline-detail": "true" }, event.detail) : null,
2337
+ showActorId && event.actorId ? h("p", { "data-timeline-actor": "true" }, `Actor: ${event.actorId}`) : null,
2338
+ showReason && event.reason ? h("p", { "data-timeline-reason": "true" }, `Reason: ${event.reason}`) : null
2339
+ ])
2340
+ ]);
2341
+ }
2342
+ function timelineComponent(configuration) {
2343
+ return defineComponent({
2344
+ name: `Oods${configuration.componentId}`,
2345
+ inheritAttrs: false,
2346
+ props: timelineProps,
2347
+ setup(props, { attrs, slots }) {
2348
+ return () => {
2349
+ const rawEvents = configuration.componentId === "StatusTimeline" ? firstArray(props.source, props.events, props.history, props.entries, props.stateHistory) : firstArray(props.source, props.events, props.history, props.entries, props.auditLog);
2350
+ const normalized = normalizeTimelineEvents(rawEvents);
2351
+ const events = props.maxVisible === void 0 ? normalized : normalized.slice(0, Math.max(0, Math.floor(props.maxVisible)));
2352
+ const title = props.title ?? configuration.defaultTitle;
2353
+ const slottedEvents = slots.default?.();
2354
+ return h("div", {
2355
+ ...attrs,
2356
+ class: mergedClass("oods-timeline", attrs.class),
2357
+ role: "log",
2358
+ "aria-label": stringValue(attrs["aria-label"]) ?? title,
2359
+ "data-oods-component": configuration.componentId,
2360
+ "data-timeline-type": configuration.timelineType
2361
+ }, [
2362
+ h("h2", { "data-timeline-title": "true" }, title),
2363
+ props.status ? h("p", { "data-timeline-current": "true" }, `Current status: ${statusLabel2(props.status)}`) : null,
2364
+ props.allowedTransitions?.length ? h("p", { "data-timeline-transitions": "true" }, `Allowed transitions: ${props.allowedTransitions.join(", ")}`) : null,
2365
+ h("ol", { "data-timeline-events": "true" }, slottedEvents?.length ? slottedEvents : events.length ? events.map((event, index) => renderTimelineEvent(
2366
+ event,
2367
+ index,
2368
+ props.showActorId,
2369
+ props.showReason
2370
+ )) : [h("li", { "data-timeline-empty": "true" }, "No events")])
2371
+ ]);
2372
+ };
2373
+ }
2374
+ });
2375
+ }
2376
+ var StatusTimeline = timelineComponent({
2377
+ componentId: "StatusTimeline",
2378
+ defaultTitle: "Status Timeline",
2379
+ timelineType: "status"
2380
+ });
2381
+ var AuditTimeline = timelineComponent({
2382
+ componentId: "AuditTimeline",
2383
+ defaultTitle: "Audit Timeline",
2384
+ timelineType: "audit"
2385
+ });
2386
+ function slotSummaryValue(slots) {
2387
+ const nodes = slots.default?.() ?? [];
2388
+ for (const node of nodes) {
2389
+ if (typeof node.children === "string") {
2390
+ const text = node.children.trim().toLowerCase();
2391
+ if (text === "true") return true;
2392
+ if (text === "false") return false;
2393
+ if (node.children.length > 0) return node.children;
2394
+ }
2395
+ }
2396
+ return void 0;
2397
+ }
2398
+ function summaryEntry(label, value, key) {
2399
+ return h("div", { key, "data-summary-item": "true" }, [
2400
+ h("dt", label),
2401
+ h("dd", [value])
2402
+ ]);
2403
+ }
2404
+ var CancellationSummary = defineComponent({
2405
+ name: "OodsCancellationSummary",
2406
+ inheritAttrs: false,
2407
+ props: {
2408
+ title: String,
2409
+ label: String,
2410
+ cancelAtPeriodEnd: { type: Boolean, default: void 0 },
2411
+ requestedAt: String,
2412
+ reason: String,
2413
+ code: String,
2414
+ field: String,
2415
+ cancelAtPeriodEndField: String,
2416
+ requestedAtField: String,
2417
+ reasonField: String,
2418
+ codeField: String
2419
+ },
2420
+ setup(props, { attrs, slots }) {
2421
+ return () => {
2422
+ const slotValue = slotSummaryValue(slots);
2423
+ const cancellationValue = props.cancelAtPeriodEnd ?? slotValue;
2424
+ const rows = [];
2425
+ if (cancellationValue !== void 0) {
2426
+ rows.push(summaryEntry(
2427
+ "Cancel at period end",
2428
+ typeof cancellationValue === "boolean" ? cancellationValue ? "Yes" : "No" : cancellationValue,
2429
+ "cancel-at-period-end"
2430
+ ));
2431
+ }
2432
+ if (props.requestedAt) {
2433
+ rows.push(summaryEntry(
2434
+ "Requested at",
2435
+ h("time", { datetime: props.requestedAt }, formatDateTime(props.requestedAt)),
2436
+ "requested-at"
2437
+ ));
2438
+ }
2439
+ if (props.reason) rows.push(summaryEntry("Reason", props.reason, "reason"));
2440
+ if (props.code) rows.push(summaryEntry("Code", formatReadOnlyValue(props.code, "string", true), "code"));
2441
+ return h("section", {
2442
+ ...attrs,
2443
+ class: mergedClass("oods-cancellation-summary", attrs.class),
2444
+ "data-oods-component": "CancellationSummary",
2445
+ "data-summary-type": "cancellation",
2446
+ "data-cancellation-state": props.cancelAtPeriodEnd === void 0 ? void 0 : String(props.cancelAtPeriodEnd)
2447
+ }, [
2448
+ h("h2", { "data-summary-title": "true" }, props.title ?? props.label ?? "Cancellation Summary"),
2449
+ h("dl", rows)
2450
+ ]);
2451
+ };
2452
+ }
2453
+ });
2454
+ function inputValue(event) {
2455
+ return event.target.value;
2456
+ }
2457
+ var SearchInput = defineComponent({
2458
+ name: "OodsSearchInput",
2459
+ inheritAttrs: false,
2460
+ props: {
2461
+ id: String,
2462
+ label: String,
2463
+ modelValue: String,
2464
+ value: String,
2465
+ defaultValue: { type: String, default: "" },
2466
+ placeholder: { type: String, default: "Search\u2026" },
2467
+ clearable: { type: Boolean, default: true },
2468
+ debounceMs: { type: Number, default: 0 },
2469
+ debounce: Number,
2470
+ minQueryLength: { type: Number, default: 0 },
2471
+ disabled: Boolean,
2472
+ field: String,
2473
+ placeholderParameter: String,
2474
+ debounceParameter: String,
2475
+ minQueryLengthParameter: String,
2476
+ clearableParameter: String
2477
+ },
2478
+ emits: {
2479
+ "update:modelValue": (_value) => true,
2480
+ valueChange: (_value) => true,
2481
+ update: (_value) => true,
2482
+ input: (_value) => true,
2483
+ search: (_value) => true,
2484
+ clear: () => true
2485
+ },
2486
+ setup(props, { attrs, emit }) {
2487
+ const generatedId = useId();
2488
+ const internalValue = ref(props.defaultValue);
2489
+ const currentValue = computed(() => props.modelValue ?? props.value ?? internalValue.value);
2490
+ let timer;
2491
+ onBeforeUnmount(() => {
2492
+ if (timer !== void 0) clearTimeout(timer);
2493
+ });
2494
+ const emitSearch = (value) => {
2495
+ if (value.length < Math.max(0, props.minQueryLength) && value.length !== 0) return;
2496
+ emit("valueChange", value);
2497
+ emit("update", value);
2498
+ emit("search", value);
2499
+ };
2500
+ const scheduleSearch = (value) => {
2501
+ if (timer !== void 0) clearTimeout(timer);
2502
+ const debounceMs = Math.max(0, props.debounceMs || props.debounce || 0);
2503
+ if (debounceMs > 0) {
2504
+ timer = setTimeout(() => emitSearch(value), debounceMs);
2505
+ } else {
2506
+ emitSearch(value);
2507
+ }
2508
+ };
2509
+ const updateValue = (value) => {
2510
+ if (props.modelValue === void 0 && props.value === void 0) internalValue.value = value;
2511
+ emit("update:modelValue", value);
2512
+ emit("input", value);
2513
+ scheduleSearch(value);
2514
+ };
2515
+ const clear = () => {
2516
+ if (props.disabled || currentValue.value.length === 0) return;
2517
+ if (timer !== void 0) clearTimeout(timer);
2518
+ updateValue("");
2519
+ emit("clear");
2520
+ };
2521
+ const submit = (event) => {
2522
+ event.preventDefault();
2523
+ if (!props.disabled) {
2524
+ if (timer !== void 0) clearTimeout(timer);
2525
+ emitSearch(currentValue.value);
2526
+ }
2527
+ };
2528
+ return () => {
2529
+ const id = props.id ?? `oods-search-${generatedId.replace(/:/g, "")}`;
2530
+ const ariaLabel = stringValue(attrs["aria-label"]) ?? props.label ?? props.placeholder;
2531
+ return h("form", {
2532
+ ...attrs,
2533
+ class: mergedClass("oods-search-input", attrs.class),
2534
+ role: "search",
2535
+ "aria-label": ariaLabel,
2536
+ "data-oods-component": "SearchInput",
2537
+ "data-behavioral": "search",
2538
+ onSubmit: submit
2539
+ }, [
2540
+ h(Input, {
2541
+ id,
2542
+ label: props.label,
2543
+ type: "search",
2544
+ value: currentValue.value,
2545
+ placeholder: props.placeholder,
2546
+ disabled: props.disabled,
2547
+ "aria-label": props.label ? void 0 : ariaLabel,
2548
+ onInput: (value) => updateValue(value),
2549
+ onKeydown: (event) => {
2550
+ if (event.key === "Escape") clear();
2551
+ }
2552
+ }),
2553
+ props.clearable && currentValue.value ? h("button", {
2554
+ type: "button",
2555
+ "data-search-clear": "true",
2556
+ "aria-label": "Clear search",
2557
+ disabled: props.disabled,
2558
+ onClick: clear
2559
+ }, "\xD7") : null
2560
+ ]);
2561
+ };
2562
+ }
2563
+ });
2564
+ var PaginationBar = defineComponent({
2565
+ name: "OodsPaginationBar",
2566
+ inheritAttrs: false,
2567
+ props: {
2568
+ page: { type: Number, default: 1 },
2569
+ pageSize: { type: Number, default: 25 },
2570
+ totalItems: { type: Number, default: 0 },
2571
+ totalPages: Number,
2572
+ pageSizeOptions: { type: Array, default: () => [10, 25, 50, 100] },
2573
+ showPageSizeSelector: Boolean,
2574
+ showGotoPage: Boolean,
2575
+ showItemRange: { type: Boolean, default: true },
2576
+ pageField: String,
2577
+ pageSizeField: String,
2578
+ totalItemsField: String,
2579
+ totalPagesField: String,
2580
+ pageSizeOptionsParameter: String,
2581
+ showPageSizeSelectorParameter: String,
2582
+ showGotoPageParameter: String,
2583
+ showItemRangeParameter: String
2584
+ },
2585
+ emits: {
2586
+ "update:page": (_page) => true,
2587
+ pageChange: (_page) => true,
2588
+ change: (_page) => true,
2589
+ update: (_page) => true,
2590
+ pageSizeChange: (_pageSize) => true
2591
+ },
2592
+ setup(props, { attrs, emit }) {
2593
+ const generatedId = useId().replace(/:/g, "");
2594
+ const gotoPage = ref("");
2595
+ const count = computed(() => props.totalPages ?? (props.pageSize > 0 ? Math.ceil(props.totalItems / props.pageSize) : 0));
2596
+ const pagination = computed(() => buildPaginationItems({
2597
+ page: props.page,
2598
+ count: count.value
2599
+ }));
2600
+ const setPage = (page) => {
2601
+ if (pagination.value.count === 0) return;
2602
+ const nextPage = Math.max(1, Math.min(Math.floor(page), pagination.value.count));
2603
+ if (nextPage === pagination.value.page) return;
2604
+ emit("update:page", nextPage);
2605
+ emit("pageChange", nextPage);
2606
+ emit("change", nextPage);
2607
+ emit("update", nextPage);
2608
+ };
2609
+ const submitGoto = (event) => {
2610
+ event.preventDefault();
2611
+ const nextPage = Number(gotoPage.value);
2612
+ if (Number.isFinite(nextPage)) setPage(nextPage);
2613
+ };
2614
+ return () => {
2615
+ const { page, count: totalPages, items } = pagination.value;
2616
+ return h("nav", {
2617
+ ...attrs,
2618
+ class: mergedClass("oods-pagination-bar", attrs.class),
2619
+ "aria-label": stringValue(attrs["aria-label"]) ?? "Pagination",
2620
+ "data-oods-component": "PaginationBar",
2621
+ "data-behavioral": "pagination"
2622
+ }, [
2623
+ props.showItemRange ? h("span", { "data-pagination-count": "true" }, `${props.totalItems} ${props.totalItems === 1 ? "record" : "records"}`) : null,
2624
+ // s211-m02: on one page the range only repeats the count ("15 records Showing 1–15 of 15").
2625
+ props.showItemRange && props.totalItems > 0 && totalPages > 1 ? h("span", { "data-pagination-range": "true" }, `Showing ${(page - 1) * Math.max(1, props.pageSize) + 1}\u2013${Math.min(page * Math.max(1, props.pageSize), props.totalItems)} of ${props.totalItems}`) : null,
2626
+ // s210-m01: one page needs no page controls; the count and range above already say what is shown.
2627
+ totalPages > 1 ? h("ul", { class: "oods-pagination-bar__pages" }, items.map((item, index) => {
2628
+ if (item.type === "ellipsis") {
2629
+ return h("li", { key: `ellipsis-${item.index ?? index}`, "aria-hidden": "true" }, "\u2026");
2630
+ }
2631
+ const label = item.type === "page" ? `Page ${item.page}` : item.type === "previous" ? "Previous page" : "Next page";
2632
+ const content = item.type === "page" ? String(item.page) : item.type === "previous" ? "\u2039" : "\u203A";
2633
+ return h("li", { key: `${item.type}-${item.page}` }, [
2634
+ h("button", {
2635
+ type: "button",
2636
+ "data-pagination-page": item.type === "page" ? item.page : void 0,
2637
+ "data-pagination-prev": item.type === "previous" ? "true" : void 0,
2638
+ "data-pagination-next": item.type === "next" ? "true" : void 0,
2639
+ "aria-label": label,
2640
+ "aria-current": item.selected ? "page" : void 0,
2641
+ disabled: item.disabled,
2642
+ onClick: () => item.page !== void 0 && setPage(item.page)
2643
+ }, content)
2644
+ ]);
2645
+ })) : null,
2646
+ totalPages > 1 ? h("span", { "data-pagination-current": "true" }, `Page ${page} of ${Math.max(1, totalPages)}`) : null,
2647
+ props.showPageSizeSelector ? h("label", { class: "oods-pagination-size" }, [
2648
+ h("span", "Items per page"),
2649
+ h("select", {
2650
+ value: props.pageSize,
2651
+ onChange: (event) => {
2652
+ const nextSize = Number(event.target.value);
2653
+ if (Number.isFinite(nextSize) && nextSize > 0 && nextSize !== props.pageSize) {
2654
+ emit("pageSizeChange", nextSize);
2655
+ }
2656
+ }
2657
+ }, props.pageSizeOptions.map((option) => h("option", { value: option }, String(option))))
2658
+ ]) : null,
2659
+ props.showGotoPage ? h("form", { class: "oods-pagination-goto", onSubmit: submitGoto }, [
2660
+ h("label", { for: `oods-pagination-goto-${generatedId}` }, "Go to page"),
2661
+ h("input", {
2662
+ id: `oods-pagination-goto-${generatedId}`,
2663
+ type: "number",
2664
+ min: 1,
2665
+ max: totalPages,
2666
+ value: gotoPage.value,
2667
+ onInput: (event) => {
2668
+ gotoPage.value = inputValue(event);
2669
+ }
2670
+ }),
2671
+ h("button", { type: "submit" }, "Go")
2672
+ ]) : null
2673
+ ]);
2674
+ };
2675
+ }
2676
+ });
2677
+ var RelativeTimestamp = defineComponent({
2678
+ name: "OodsRelativeTimestamp",
2679
+ inheritAttrs: false,
2680
+ props: {
2681
+ datetime: String,
2682
+ timestamp: String,
2683
+ value: String,
2684
+ updatedAt: String,
2685
+ createdAt: String,
2686
+ relative: String,
2687
+ label: String,
2688
+ text: String,
2689
+ timezone: String,
2690
+ hideWhenEmpty: Boolean,
2691
+ now: [String, Number, Date],
2692
+ field: String,
2693
+ fallbackField: String,
2694
+ timezoneParameter: String
2695
+ },
2696
+ setup(props, { attrs }) {
2697
+ return () => {
2698
+ const datetime = firstDefined(
2699
+ props.datetime,
2700
+ props.timestamp,
2701
+ props.value,
2702
+ props.updatedAt,
2703
+ props.createdAt
2704
+ ) ?? "";
2705
+ if (props.hideWhenEmpty && (!datetime || Number.isNaN(Date.parse(datetime)))) return null;
2706
+ const content = firstDefined(
2707
+ props.relative,
2708
+ props.label,
2709
+ props.text,
2710
+ props.now === void 0 ? formatDateTime(datetime, { timeZone: props.timezone }) : relativeTimestampLabel(datetime, props.now)
2711
+ ) ?? "";
2712
+ return h("time", {
2713
+ ...attrs,
2714
+ class: mergedClass("oods-relative-timestamp", attrs.class),
2715
+ datetime: datetime || void 0,
2716
+ title: stringValue(attrs.title) ?? formatDateTime(datetime, { timeZone: props.timezone }),
2717
+ "data-oods-component": "RelativeTimestamp"
2718
+ }, content);
2719
+ };
2720
+ }
2721
+ });
2722
+ var amountProps = { id: String, amount: Number, currency: { type: String, default: "usd" }, minorUnits: { type: Number, default: BILLING_MINOR_UNITS } };
2723
+ var BillingSummaryBadge = defineComponent({
2724
+ name: "BillingSummaryBadge",
2725
+ props: { ...amountProps, interval: String, showInterval: { type: Boolean, default: true } },
2726
+ setup: (props) => () => h("span", { id: props.id, class: "oods-billing-summary", "data-oods-component": "BillingSummaryBadge" }, props.showInterval ? billingSummary(props.amount, props.currency, props.minorUnits, props.interval) : formatBillingAmount(props.amount, props.currency, props.minorUnits))
2727
+ });
2728
+ var BillingAmountInput = defineComponent({
2729
+ name: "BillingAmountInput",
2730
+ props: { ...amountProps, label: { type: String, default: "Billing amount" }, name: String, disabled: Boolean, help: String },
2731
+ emits: { change: (value) => value === void 0 || Number.isSafeInteger(value) },
2732
+ setup(props, { emit }) {
2733
+ const generatedId = useId();
2734
+ const text = ref(billingAmountText(props.amount, props.minorUnits));
2735
+ const error = ref(billingAmountMessage(props.amount, props.minorUnits));
2736
+ watch(() => [props.amount, props.minorUnits], () => {
2737
+ text.value = billingAmountText(props.amount, props.minorUnits);
2738
+ error.value = billingAmountMessage(props.amount, props.minorUnits);
2739
+ });
2740
+ return () => {
2741
+ const id = props.id ?? `billing-amount-${generatedId}`;
2742
+ return h("div", { class: "oods-billing-field", "data-oods-component": "BillingAmountInput", "data-state": error.value ? "invalid" : "editing" }, [
2743
+ h("label", { for: id }, props.label),
2744
+ h("span", { id: `${id}-currency` }, props.currency.toUpperCase()),
2745
+ h("input", {
2746
+ id,
2747
+ name: props.name,
2748
+ type: "text",
2749
+ inputmode: "decimal",
2750
+ value: text.value,
2751
+ disabled: props.disabled,
2752
+ "data-billing-minor-units": props.minorUnits,
2753
+ "aria-invalid": error.value ? true : void 0,
2754
+ "aria-describedby": [`${id}-currency`, error.value && `${id}-error`].filter(Boolean).join(" "),
2755
+ onInput(event) {
2756
+ const target = event.currentTarget;
2757
+ const result = parseBillingAmount(target.value, props.minorUnits);
2758
+ target.setCustomValidity(result.valid ? "" : result.message);
2759
+ text.value = target.value;
2760
+ error.value = result.valid ? void 0 : result.message;
2761
+ if (result.valid) emit("change", result.value);
2762
+ }
2763
+ }),
2764
+ props.help ? h("p", { class: "oods-field-help" }, props.help) : null,
2765
+ error.value ? h("p", { id: `${id}-error`, role: "alert" }, error.value) : null
2766
+ ]);
2767
+ };
2768
+ }
2769
+ });
2770
+ var BillingIntervalSelector = defineComponent({
2771
+ name: "BillingIntervalSelector",
2772
+ props: { id: String, interval: String, intervals: { type: Array, default: () => [...BILLING_INTERVALS] }, label: { type: String, default: "Billing interval" }, name: String, disabled: Boolean, help: String },
2773
+ emits: { change: (value) => typeof value === "string" },
2774
+ setup(props, { emit }) {
2775
+ const generatedId = useId();
2776
+ const value = ref(props.interval ?? "");
2777
+ watch(() => props.interval, () => {
2778
+ value.value = props.interval ?? "";
2779
+ });
2780
+ return () => {
2781
+ const id = props.id ?? `billing-interval-${generatedId}`;
2782
+ const error = billingIntervalMessage(value.value || void 0, props.intervals);
2783
+ return h("div", { class: "oods-billing-field", "data-oods-component": "BillingIntervalSelector", "data-state": error ? "invalid" : "editing" }, [
2784
+ h("label", { for: id }, props.label),
2785
+ h("select", {
2786
+ id,
2787
+ name: props.name,
2788
+ value: value.value,
2789
+ disabled: props.disabled,
2790
+ "aria-invalid": error ? true : void 0,
2791
+ "aria-describedby": error ? `${id}-error` : void 0,
2792
+ onChange(event) {
2793
+ const next = event.currentTarget.value;
2794
+ if (props.intervals.includes(next)) {
2795
+ value.value = next;
2796
+ emit("change", next);
2797
+ }
2798
+ }
2799
+ }, [
2800
+ !props.intervals.includes(value.value) ? h("option", { value: value.value, disabled: true, selected: true }, value.value || "Choose interval") : null,
2801
+ ...props.intervals.map((option) => h("option", { value: option, selected: option === value.value }, formatReadOnlyValue(option, "string", true)))
2802
+ ]),
2803
+ props.help ? h("p", { class: "oods-field-help" }, props.help) : null,
2804
+ error ? h("p", { id: `${id}-error`, role: "alert" }, error) : null
2805
+ ]);
2806
+ };
2807
+ }
2808
+ });
2809
+ var CycleProgressCard = defineComponent({
2810
+ name: "CycleProgressCard",
2811
+ props: { id: String, title: { type: String, default: "Billing cycle" }, progress: Number, periodStart: String, periodEnd: String, interval: String, now: String },
2812
+ setup: (props) => () => {
2813
+ const cycle = billingCycle(props);
2814
+ return h("section", { id: props.id, class: "oods-billing-cycle", "data-oods-component": "CycleProgressCard", "aria-label": props.title }, [
2815
+ h("h2", props.title),
2816
+ h("p", cycle.announcement),
2817
+ cycle.percent !== void 0 ? h("progress", { max: 100, value: cycle.percent, "aria-label": cycle.announcement }) : null,
2818
+ props.interval ? h("p", { class: "oods-billing-muted" }, props.interval) : null
2819
+ ]);
2820
+ }
2821
+ });
2822
+ var paymentProps = { id: String, title: String, lastPayment: String, nextPayment: String, paymentStatus: String, amount: Number, currency: String, minorUnits: Number };
2823
+ function renderBillingTimeline(props, component, includeMethod) {
2824
+ const heading = props.title ?? (includeMethod ? "Payments" : "Payment events");
2825
+ return h("section", { id: props.id, class: "oods-payment-timeline", "data-oods-component": component, role: "log", "aria-label": heading }, [
2826
+ h("h2", heading),
2827
+ h("p", billingPaymentSummary(props)),
2828
+ includeMethod ? h("p", { class: "oods-billing-muted" }, `Payment method: ${props.paymentMethod ?? "Not provided"}`) : null,
2829
+ h("ol", billingPaymentRows(props).map((row) => h("li", { key: row.kind, "data-payment-kind": row.kind }, [h("strong", row.label), row.at ? h("time", { datetime: row.at }, row.text) : h("span", row.text)])))
2830
+ ]);
2831
+ }
2832
+ var PaymentTimeline = defineComponent({ name: "PaymentTimeline", props: { ...paymentProps, paymentMethod: String }, setup: (props) => () => renderBillingTimeline(props, "PaymentTimeline", true) });
2833
+ var PaymentEventTimeline = defineComponent({ name: "PaymentEventTimeline", props: { ...paymentProps, event: Object }, setup: (props) => () => props.event ? h("section", { id: props.id, "data-oods-component": "PaymentEventTimeline", "aria-label": props.event.title ?? "Payment event" }, [h("strong", props.event.title), h("time", { datetime: props.event.at }, formatDateTime(props.event.at)), h("p", props.event.description)]) : renderBillingTimeline(props, "PaymentEventTimeline", false) });
2834
+ var BillingCardMeta = defineComponent({
2835
+ name: "BillingCardMeta",
2836
+ props: { id: String, amount: Number, currency: String, minorUnits: Number, interval: String },
2837
+ setup: (props) => () => h("span", { id: props.id, class: "oods-billing-card-meta", "data-oods-component": "BillingCardMeta" }, billingSummary(props.amount, props.currency, props.minorUnits, props.interval))
2838
+ });
2839
+ var ArchivedRowOverlay = defineComponent({
2840
+ name: "ArchivedRowOverlay",
2841
+ props: { id: String, isArchived: Boolean, showBadge: { type: Boolean, default: true }, separateTab: { type: Boolean, default: true }, tabLabel: { type: String, default: "Archived" }, label: String },
2842
+ setup: (props, { slots }) => () => h("span", {
2843
+ id: props.id,
2844
+ class: "oods-archived-row",
2845
+ "data-oods-component": "ArchivedRowOverlay",
2846
+ "data-archived": props.isArchived ? "true" : void 0,
2847
+ "data-archive-tab": props.separateTab ? props.tabLabel : void 0,
2848
+ role: props.isArchived ? "group" : void 0,
2849
+ "aria-hidden": props.isArchived ? false : void 0,
2850
+ "aria-label": props.isArchived ? `${props.tabLabel}${props.label ? `: ${props.label}` : ""}` : void 0
2851
+ }, [slots.default?.(), props.isArchived && props.showBadge ? h("span", { class: "oods-archive-badge" }, props.tabLabel) : null])
2852
+ });
2853
+ var AuditSummaryCard = defineComponent({
2854
+ name: "AuditSummaryCard",
2855
+ props: { id: String, title: { type: String, default: "Audit summary" }, auditLog: Array, lastN: { type: Number, default: 5 }, showTransitionCount: { type: Boolean, default: true }, showLastTransitionTime: { type: Boolean, default: true }, showLastActor: { type: Boolean, default: true } },
2856
+ setup: (props) => () => {
2857
+ const summary2 = auditSummary(props);
2858
+ return h("section", { id: props.id, class: "oods-audit-summary", "data-oods-component": "AuditSummaryCard", "aria-label": props.title }, [h("h2", props.title), h("dl", [
2859
+ ...props.showTransitionCount ? [h("dt", "Transitions"), h("dd", summary2.count)] : [],
2860
+ ...props.showLastActor ? [h("dt", "Last actor"), h("dd", summary2.actor)] : [],
2861
+ ...props.showLastTransitionTime ? [h("dt", "Last transition"), h("dd", summary2.at ? h("time", { datetime: summary2.at }, summary2.timestamp) : summary2.timestamp)] : []
2862
+ ]), summary2.recent.length ? h("ol", { "aria-label": "Recent transitions" }, summary2.recent.map((entry, index) => h("li", { key: index }, String(entry.to_state ?? "Transition")))) : null]);
2863
+ }
2864
+ });
2865
+ var SortIndicator = defineComponent({
2866
+ name: "SortIndicator",
2867
+ inheritAttrs: false,
2868
+ props: { id: String, label: { type: String, default: "Sort" }, sortField: String, sortDirection: String, sortActive: { type: Boolean, default: false }, triStateSort: { type: Boolean, default: true }, sortableFields: Array, defaultSortField: String, defaultSortDirection: String },
2869
+ emits: { change: (_state) => true },
2870
+ setup(props, { emit, attrs }) {
2871
+ const state = ref(initialSort(props));
2872
+ watch(() => [props.sortField, props.sortDirection, props.sortActive, props.defaultSortField, props.defaultSortDirection], () => {
2873
+ state.value = initialSort(props);
2874
+ });
2875
+ return () => h("table", { ...attrs, "data-oods-action": void 0, id: props.id, class: "oods-sort-indicator", "data-oods-component": "SortIndicator", "aria-label": props.label }, [h("thead", [h("tr", [h("th", { scope: "col", "aria-sort": ariaSort(state.value) }, [
2876
+ h("button", { type: "button", "data-oods-action": attrs["data-oods-action"], "aria-label": `${props.label} ${state.value.field}`, onClick: () => {
2877
+ const next = nextSort(state.value, props.triStateSort);
2878
+ state.value = next;
2879
+ emit("change", next);
2880
+ } }, `${state.value.field}: ${ariaSort(state.value)}`)
2881
+ ])])])]);
2882
+ }
2883
+ });
2884
+ var TimelineEntryLabel = defineComponent({
2885
+ name: "TimelineEntryLabel",
2886
+ props: { label: String, text: String, value: String, maxLength: [Number, String], compact: { type: Boolean, default: true } },
2887
+ setup: (props, { slots }) => () => h(InlineLabel, { label: props.label, text: props.text, value: props.value, maxLength: props.maxLength ?? (props.compact ? 40 : void 0), "data-oods-component": "TimelineEntryLabel", "data-timeline-label": "true", "data-compact": props.compact }, slots)
2888
+ });
2889
+ var array = Array;
2890
+ function eventRecipe(id, kind, title) {
2891
+ return defineComponent({ name: id, props: { id: String, title: { type: String, default: title }, archivedAt: String, restoredAt: String, archivedBy: String, reason: String, timestamp: String, code: String, history: array, status: String, showActor: { type: Boolean, default: true }, showReason: { type: Boolean, default: true } }, setup: (props) => () => {
2892
+ const rows = traitEventRows(kind, props);
2893
+ return h("article", { id: props.id, class: "oods-trait-recipe", "data-oods-component": id, "aria-label": props.title }, [h("h2", props.title), rows.length ? h("ol", rows.map((row, index) => h("li", { key: index }, [h("strong", row.title), h("p", row.at ? h("time", { datetime: row.at }, row.time) : row.time), row.actor && h("p", `Actor: ${row.actor}`), row.reason && h("p", `Reason: ${row.reason}`), row.code && h("p", `Code: ${row.code}`)]))) : h("p", "No events recorded.")]);
2894
+ } });
2895
+ }
2896
+ var ArchiveEvent = eventRecipe("ArchiveEvent", "archive", "Archive events");
2897
+ var CancellationEvent = eventRecipe("CancellationEvent", "cancellation", "Cancellation event");
2898
+ var StateTransitionEvent = eventRecipe("StateTransitionEvent", "transition", "State transitions");
2899
+ var CommunicationDetailPanel = defineComponent({ name: "CommunicationDetailPanel", props: { id: String, title: { type: String, default: "Communication" }, channels: array, templates: array, policies: array, conversations: array }, setup: (props) => () => h("section", { id: props.id, class: "oods-trait-recipe", "data-oods-component": "CommunicationDetailPanel", "aria-label": props.title }, [h("h2", props.title), h("div", { role: "status", "aria-live": "polite" }, `${props.conversations?.length ?? 0} conversations`), h("dl", [["Channels", props.channels], ["Templates", props.templates], ["Delivery policies", props.policies], ["Conversations", props.conversations]].map(([label, rows]) => h("div", { key: label }, [h("dt", label), h("dd", rows?.length ? h("ul", recipeItemLabels(rows).map((text, index) => h("li", { key: index }, text))) : "None recorded")])))]) });
2900
+ var ColorStatePicker = defineComponent({ name: "ColorStatePicker", props: { id: String, title: { type: String, default: "Color state" }, colorStates: { type: Array, default: () => [...DEFAULT_COLOR_STATES] }, value: String, disabled: Boolean }, emits: { change: (value) => typeof value === "string" }, setup(props, { emit }) {
2901
+ const generatedId = useId();
2902
+ const selected = ref(props.value ?? props.colorStates[0] ?? "");
2903
+ watch(() => [props.value, props.colorStates], () => {
2904
+ selected.value = props.value ?? props.colorStates[0] ?? "";
2905
+ });
2906
+ return () => h("fieldset", { id: props.id, class: "oods-trait-recipe", "data-oods-component": "ColorStatePicker" }, [h("legend", props.title), h(Select, { id: `${props.id ?? generatedId}-state`, label: "Color state", name: "color_state", modelValue: selected.value, disabled: props.disabled, options: [...!props.colorStates.includes(selected.value) ? [{ value: selected.value, label: selected.value || "Choose state", disabled: true }] : [], ...props.colorStates.map((value) => ({ value, label: formatReadOnlyValue(value, "string", true) }))], "onUpdate:modelValue": (next) => {
2907
+ selected.value = next;
2908
+ emit("change", next);
2909
+ } })]);
2910
+ } });
2911
+ var StatusColorLegend = defineComponent({ name: "StatusColorLegend", props: { id: String, title: { type: String, default: "Status colors" }, colorStates: { type: Array, default: () => [...DEFAULT_COLOR_STATES] }, value: String, showTokenReferences: Boolean }, setup: (props) => () => h("section", { id: props.id, class: "oods-trait-recipe", "data-oods-component": "StatusColorLegend", "aria-label": props.title }, [h("h2", props.title), h("dl", props.colorStates.map((state) => h("div", { key: state }, [h("dt", [h(ColorSwatch, { label: state, color: `var(--sys-status-${state}-surface)` }), state === props.value ? " (current)" : ""]), h("dd", props.showTokenReferences ? `--sys-status-${state}-surface` : state)])))]) });
2912
+ var GeoFieldMappingForm = defineComponent({ name: "GeoFieldMappingForm", props: { id: String, title: { type: String, default: "Geographic fields" }, embedded: Boolean, disabled: Boolean, latitude: String, longitude: String, identifier: String, autoDetect: { type: Boolean, default: true } }, emits: { change: (_value) => true }, setup(props, { emit }) {
2913
+ const generatedId = useId();
2914
+ const value = ref(geoFieldMapping(props));
2915
+ watch(() => [props.latitude, props.longitude, props.identifier, props.autoDetect], () => {
2916
+ value.value = geoFieldMapping(props);
2917
+ });
2918
+ const update = (patch) => {
2919
+ value.value = { ...value.value, ...patch };
2920
+ emit("change", value.value);
2921
+ };
2922
+ return () => h(props.embedded ? "fieldset" : "form", { id: props.id, class: "oods-trait-recipe", "data-oods-component": "GeoFieldMappingForm", "aria-label": props.title, onSubmit: (event) => event.preventDefault() }, [h(props.embedded ? "legend" : "h2", props.title), ...["latitude", "longitude", "identifier"].map((field) => h(Input, { key: field, id: `${props.id ?? generatedId}-${field}`, name: `geo_${field}_field`, label: `${field[0].toUpperCase()}${field.slice(1)} field`, modelValue: value.value[field], disabled: props.disabled, "onUpdate:modelValue": (next) => update({ [field]: next }) })), h(Checkbox, { id: `${props.id ?? generatedId}-auto`, name: "geo_auto_detect_enabled", label: "Auto detect fields", modelValue: value.value.autoDetect, disabled: props.disabled, "onUpdate:modelValue": (autoDetect) => update({ autoDetect }) })]);
2923
+ } });
2924
+ var GeoResolutionBadge = defineComponent({ name: "GeoResolutionBadge", props: { id: String, resolution: String }, setup: (props) => () => h("span", { id: props.id, class: "oods-geo-resolution", "data-oods-component": "GeoResolutionBadge" }, `Resolution: ${props.resolution || "Not determined"}`) });
2925
+ var GeocodablePreview = defineComponent({ name: "GeocodablePreview", props: { id: String, title: { type: String, default: "Geographic data" }, resolution: String, requiresLookup: { type: Boolean, default: void 0 }, detectedFields: array }, setup: (props) => () => h("section", { id: props.id, class: "oods-trait-recipe", "data-oods-component": "GeocodablePreview", "aria-label": props.title }, [h("h2", props.title), h("dl", [h("dt", "Resolution"), h("dd", props.resolution || "Not determined"), h("dt", "Requires lookup"), h("dd", props.requiresLookup === void 0 ? "Not determined" : props.requiresLookup ? "Yes" : "No"), h("dt", "Detected fields"), h("dd", recipeItemLabels(props.detectedFields).join(", ") || "None detected")])]) });
2926
+ function controls(id) {
2927
+ return defineComponent({ name: id, props: { id: String, title: String, value: Object, channel: { type: String, default: "x" }, disabled: Boolean }, emits: { change: (_value) => true }, setup(props, { emit }) {
2928
+ const generatedId = useId();
2929
+ const current = ref(props.value ?? {});
2930
+ const drafts = ref({});
2931
+ const errors = ref({});
2932
+ watch(() => props.value, (value) => {
2933
+ current.value = value ?? {};
2934
+ drafts.value = {};
2935
+ errors.value = {};
2936
+ }, { deep: true });
2937
+ const update = (field, text) => {
2938
+ const result = editVizIntent(current.value, field, text);
2939
+ drafts.value = { ...drafts.value, [field.key]: text };
2940
+ errors.value = { ...errors.value, [field.key]: result.error };
2941
+ if (result.value) {
2942
+ current.value = result.value;
2943
+ emit("change", result.value);
2944
+ }
2945
+ };
2946
+ return () => h("fieldset", { id: props.id, class: "oods-viz-controls oods-trait-recipe", "data-oods-component": id, disabled: props.disabled }, [h("legend", props.title ?? VIZ_CONTROL_TITLES[id]), ...vizControlFields(id, props.channel).map((field) => {
2947
+ const text = drafts.value[field.key] ?? vizFieldValue(current.value, field.key);
2948
+ const common = { key: field.key, id: `${props.id ?? generatedId}-${field.key.replace(".", "-")}`, name: field.key, label: field.label, modelValue: text, validation: errors.value[field.key] ? { state: "error", message: errors.value[field.key] } : void 0, "onUpdate:modelValue": (value) => update(field, value) };
2949
+ return field.type === "select" ? h(Select, { ...common, options: [...!field.options?.includes(text) ? [{ value: text, label: text || "Choose option", disabled: true }] : [], ...field.options.map((value) => ({ value, label: value }))] }) : h(Input, { ...common, type: field.type === "number" ? "number" : "text", min: field.min === void 0 ? void 0 : String(field.min), max: field.max === void 0 ? void 0 : String(field.max), step: field.step === void 0 ? void 0 : String(field.step) });
2950
+ }), ...id === "VizColorControls" || id === "VizColorLegendConfig" ? [h("div", { class: "oods-viz-swatches" }, current.value.encodings?.color?.range?.map((color, index) => h(ColorSwatch, { key: index, color, label: `Category color ${index + 1}: ${color}` })))] : [], ...id === "VizShapeLegend" ? [h(Text, { content: `Shape field: ${current.value.encodings?.shape?.field || "Not bound"}` })] : []]);
2951
+ } });
2952
+ }
2953
+ function summary(id) {
2954
+ return defineComponent({ name: id, props: { id: String, title: String, value: Object, channel: { type: String, default: "x" } }, setup(props) {
2955
+ return () => {
2956
+ const rows = vizSummaryRows(id, props.value ?? {}, props.channel);
2957
+ if (id === "VizEncodingBadge" || id === "VizRoleBadge") return h("span", { id: props.id, "data-oods-component": id }, [h(Badge, { content: rows.map(([label, text]) => `${label}: ${text}`).join(" \xB7 ") })]);
2958
+ const title = props.title ?? id.replace(/^Viz/, "").replace(/Summary$/, "") + " summary";
2959
+ return h("section", { id: props.id, class: "oods-trait-recipe", "data-oods-component": id, "aria-label": title }, [h("h2", title), h("dl", rows.map(([label, text]) => h("div", { key: label }, [h("dt", label), h("dd", [h(Text, { content: text })])])))]);
2960
+ };
2961
+ } });
2962
+ }
2963
+ function preview(id, type) {
2964
+ return defineComponent({ name: id, props: { svg: String, title: String, description: String, width: Number, height: Number }, setup(props, { slots }) {
2965
+ return () => h(VizAreaPreview, { ...props, title: props.title ?? `${type[0].toUpperCase()}${type.slice(1)} chart`, "data-oods-component": id, "data-viz-preview-type": type }, { default: slots.default ?? (() => h(Text, { content: "No rendered chart supplied" })) });
2966
+ } });
2967
+ }
2968
+ var VizAreaControls = controls("VizAreaControls");
2969
+ var VizAxisControls = controls("VizAxisControls");
2970
+ var VizColorControls = controls("VizColorControls");
2971
+ var VizHeatmapControls = controls("VizHeatmapControls");
2972
+ var VizLineControls = controls("VizLineControls");
2973
+ var VizMarkControls = controls("VizMarkControls");
2974
+ var VizOpacityControls = controls("VizOpacityControls");
2975
+ var VizPointControls = controls("VizPointControls");
2976
+ var VizScaleControls = controls("VizScaleControls");
2977
+ var VizScatterControls = controls("VizScatterControls");
2978
+ var VizShapeControls = controls("VizShapeControls");
2979
+ var VizSizeControls = controls("VizSizeControls");
2980
+ var VizColorLegendConfig = controls("VizColorLegendConfig");
2981
+ var VizShapeLegend = controls("VizShapeLegend");
2982
+ var VizAxisSummary = summary("VizAxisSummary");
2983
+ var VizOpacitySummary = summary("VizOpacitySummary");
2984
+ var VizScaleSummary = summary("VizScaleSummary");
2985
+ var VizSizeSummary = summary("VizSizeSummary");
2986
+ var VizEncodingBadge = summary("VizEncodingBadge");
2987
+ var VizRoleBadge = summary("VizRoleBadge");
2988
+ var VizHeatmapPreview = preview("VizHeatmapPreview", "heatmap");
2989
+ var VizGraphPreview = preview("VizGraphPreview", "force_graph");
2990
+ var VizLinePreview = preview("VizLinePreview", "line");
2991
+ var VizMarkPreview = preview("VizMarkPreview", "bar");
2992
+ var VizPointPreview = preview("VizPointPreview", "scatter");
2993
+ var VizScatterPreview = preview("VizScatterPreview", "scatter");
2994
+
2995
+ export { AddressCollectionPanel, AddressEditor, AddressSummaryBadge, AddressValidationTimeline, ArchiveEvent, ArchivePill, ArchiveSummary, ArchivedRowOverlay, AuditEvent, AuditSummaryCard, AuditTimeline, Badge, Banner, BillingAmountInput, BillingCardMeta, BillingIntervalSelector, BillingSummaryBadge, Button, CancellationBadge, CancellationEvent, CancellationForm, CancellationSummary, Card, CardHeader, Checkbox, ClassificationBadge, ClassificationEditor, ClassificationPanel, ColorStatePicker, ColorSwatch, ColorizedBadge, CommunicationDetailPanel, CycleProgressCard, DatePicker, DetailHeader, FilterPanel, FormLabelGroup, GeoFieldMappingForm, GeoResolutionBadge, GeocodablePreview, Grid, InlineLabel, Input, LabelCell, MembershipAuditTimeline, MembershipPanel, MessageEventTimeline, MessageStatusBadge, OwnerBadge, OwnershipMeta, OwnershipSummary, PaginationBar, PaymentEventTimeline, PaymentTimeline, PreferenceEditor, PreferencePanel, PreferenceSummaryBadge, PreferenceTimeline, PriceBadge, PriceCardMeta, PriceSummary, RelativeTimestamp, RoleAssignmentForm, RoleBadgeList, SearchInput, Select, SortIndicator, Stack, StateTransitionEvent, StatusBadge, StatusColorLegend, StatusSelector, StatusTimeline, Table, TableBody, TableCaption, TableCell, TableHead, TableHeaderCell, TableRow, Tabs, TagInput, TagManager, TagPills, TagSummary, TemplatePicker, Text, Textarea, TimelineEntryLabel, VizAreaControls, VizAreaPreview, VizAxisControls, VizAxisSummary, VizColorControls, VizColorLegendConfig, VizEncodingBadge, VizGraphPreview, VizHeatmapControls, VizHeatmapPreview, VizLineControls, VizLinePreview, VizMarkControls, VizMarkPreview, VizOpacityControls, VizOpacitySummary, VizPointControls, VizPointPreview, VizRoleBadge, VizScaleControls, VizScaleSummary, VizScatterControls, VizScatterPreview, VizShapeControls, VizShapeLegend, VizSizeControls, VizSizeSummary };