@oods/components-react 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,338 @@
1
+ // src/status-map.json
2
+ var status_map_default = {
3
+ domains: {
4
+ subscription: {
5
+ future: {
6
+ description: "Subscription is scheduled but not yet active.",
7
+ chip: {
8
+ foreground: "color.text.info",
9
+ background: "color.background.info.subtle",
10
+ border: "color.border.info",
11
+ icon: "icon.status.scheduled"
12
+ },
13
+ banner: {
14
+ foreground: "color.text.info",
15
+ background: "color.background.surface.default",
16
+ border: "color.border.info",
17
+ icon: "icon.status.scheduled"
18
+ }
19
+ },
20
+ trialing: {
21
+ description: "Subscription is in a trial period before billing begins.",
22
+ chip: {
23
+ foreground: "color.text.accent",
24
+ background: "color.background.accent.subtle",
25
+ border: "color.border.accent",
26
+ icon: "icon.status.trial"
27
+ },
28
+ banner: {
29
+ foreground: "color.text.accent",
30
+ background: "color.background.surface.default",
31
+ border: "color.border.accent",
32
+ icon: "icon.status.trial"
33
+ }
34
+ },
35
+ active: {
36
+ description: "Subscription is paid and service is provisioned.",
37
+ chip: {
38
+ foreground: "color.text.positive",
39
+ background: "color.background.positive.subtle",
40
+ border: "color.border.positive",
41
+ icon: "icon.status.success"
42
+ },
43
+ banner: {
44
+ foreground: "color.text.positive",
45
+ background: "color.background.surface.default",
46
+ border: "color.border.positive",
47
+ icon: "icon.status.success"
48
+ }
49
+ },
50
+ paused: {
51
+ description: "Billing paused temporarily by finance or customer request.",
52
+ chip: {
53
+ foreground: "color.text.neutral",
54
+ background: "color.background.neutral.subtle",
55
+ border: "color.border.neutral",
56
+ icon: "icon.status.paused"
57
+ },
58
+ banner: {
59
+ foreground: "color.text.neutral",
60
+ background: "color.background.surface.muted",
61
+ border: "color.border.neutral",
62
+ icon: "icon.status.paused"
63
+ }
64
+ },
65
+ pending_cancellation: {
66
+ description: "Subscription will cancel at period end but remains active now.",
67
+ chip: {
68
+ foreground: "color.text.info",
69
+ background: "color.background.info.subtle",
70
+ border: "color.border.info",
71
+ icon: "icon.status.pending"
72
+ },
73
+ banner: {
74
+ foreground: "color.text.info",
75
+ background: "color.background.surface.default",
76
+ border: "color.border.info",
77
+ icon: "icon.status.pending"
78
+ }
79
+ },
80
+ past_due: {
81
+ description: "Renewal payment failed; smart retries ongoing and access retained during the grace window.",
82
+ chip: {
83
+ foreground: "color.text.danger",
84
+ background: "color.background.danger.subtle",
85
+ border: "color.border.danger",
86
+ icon: "icon.status.warning"
87
+ },
88
+ banner: {
89
+ foreground: "color.text.onbold",
90
+ background: "color.background.danger.bold",
91
+ border: "color.border.danger",
92
+ icon: "icon.status.warning"
93
+ }
94
+ },
95
+ unpaid: {
96
+ description: "Payment retries exhausted; service access revoked.",
97
+ chip: {
98
+ foreground: "color.text.danger",
99
+ background: "color.background.danger.subtle",
100
+ border: "color.border.danger",
101
+ icon: "icon.status.error"
102
+ },
103
+ banner: {
104
+ foreground: "color.text.onbold",
105
+ background: "color.background.danger.bold",
106
+ border: "color.border.danger",
107
+ icon: "icon.status.error"
108
+ }
109
+ },
110
+ terminated: {
111
+ description: "Subscription ended; service access revoked and billing stopped.",
112
+ chip: {
113
+ foreground: "color.text.disabled",
114
+ background: "color.background.disabled",
115
+ border: "color.border.disabled",
116
+ icon: "icon.status.canceled"
117
+ },
118
+ banner: {
119
+ foreground: "color.text.neutral",
120
+ background: "color.background.neutral.subtle",
121
+ border: "color.border.neutral",
122
+ icon: "icon.status.canceled"
123
+ }
124
+ }
125
+ },
126
+ invoice: {
127
+ draft: {
128
+ description: "Invoice is editable and not yet finalized.",
129
+ chip: {
130
+ foreground: "color.text.neutral",
131
+ background: "color.background.neutral.subtle",
132
+ border: "color.border.neutral",
133
+ icon: "icon.status.draft"
134
+ },
135
+ banner: {
136
+ foreground: "color.text.neutral",
137
+ background: "color.background.surface.muted",
138
+ border: "color.border.neutral",
139
+ icon: "icon.status.draft"
140
+ }
141
+ },
142
+ posted: {
143
+ description: "Invoice finalized with terms before collection.",
144
+ chip: {
145
+ foreground: "color.text.info",
146
+ background: "color.background.info.subtle",
147
+ border: "color.border.info",
148
+ icon: "icon.status.pending"
149
+ },
150
+ banner: {
151
+ foreground: "color.text.info",
152
+ background: "color.background.surface.default",
153
+ border: "color.border.info",
154
+ icon: "icon.status.pending"
155
+ }
156
+ },
157
+ paid: {
158
+ description: "Invoice collected successfully in full.",
159
+ chip: {
160
+ foreground: "color.text.positive",
161
+ background: "color.background.positive.subtle",
162
+ border: "color.border.positive",
163
+ icon: "icon.status.success"
164
+ },
165
+ banner: {
166
+ foreground: "color.text.positive",
167
+ background: "color.background.surface.default",
168
+ border: "color.border.positive",
169
+ icon: "icon.status.success"
170
+ }
171
+ },
172
+ past_due: {
173
+ description: "Invoice is unpaid past its due date.",
174
+ chip: {
175
+ foreground: "color.text.danger",
176
+ background: "color.background.danger.subtle",
177
+ border: "color.border.danger",
178
+ icon: "icon.status.error"
179
+ },
180
+ banner: {
181
+ foreground: "color.text.onbold",
182
+ background: "color.background.danger.bold",
183
+ border: "color.border.danger",
184
+ icon: "icon.status.error"
185
+ }
186
+ },
187
+ void: {
188
+ description: "Invoice canceled and no longer payable.",
189
+ chip: {
190
+ foreground: "color.text.disabled",
191
+ background: "color.background.disabled",
192
+ border: "color.border.disabled",
193
+ icon: "icon.status.void"
194
+ },
195
+ banner: {
196
+ foreground: "color.text.neutral",
197
+ background: "color.background.neutral.subtle",
198
+ border: "color.border.neutral",
199
+ icon: "icon.status.void"
200
+ }
201
+ }
202
+ }
203
+ }
204
+ };
205
+
206
+ // src/status.ts
207
+ var TONE_ALIASES = {
208
+ info: "info",
209
+ informational: "info",
210
+ accent: "accent",
211
+ highlight: "accent",
212
+ positive: "success",
213
+ success: "success",
214
+ ok: "success",
215
+ warning: "warning",
216
+ caution: "warning",
217
+ danger: "critical",
218
+ critical: "critical",
219
+ error: "critical",
220
+ negative: "critical",
221
+ neutral: "neutral",
222
+ pending: "info"
223
+ };
224
+ var toLabel = (status) => status.split(/[_-]/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)).join(" ");
225
+ var inferTone = (entry) => {
226
+ if (!entry) return "neutral";
227
+ for (const reference of [entry.foreground, entry.background, entry.border]) {
228
+ const segments = reference.replace(/^\{|\}$/g, "").split(/[\s./_-]+/).reverse();
229
+ for (const segment of segments) {
230
+ const tone = TONE_ALIASES[segment.toLowerCase()];
231
+ if (tone) return tone;
232
+ }
233
+ }
234
+ return "neutral";
235
+ };
236
+ var componentTokenSet = (tone) => {
237
+ const normalized = tone === "positive" ? "success" : tone === "danger" ? "critical" : tone;
238
+ return {
239
+ background: `var(--sys-status-${normalized}-surface)`,
240
+ border: `var(--sys-status-${normalized}-border)`,
241
+ foreground: `var(--sys-status-${normalized}-text)`
242
+ };
243
+ };
244
+ var rawTokenSet = (entry) => {
245
+ const tone = inferTone(entry);
246
+ return componentTokenSet(tone);
247
+ };
248
+ var bannerFallback = (tone) => tone === "neutral" ? {
249
+ background: "var(--cmp-banner-background)",
250
+ border: "var(--cmp-banner-border)",
251
+ foreground: "var(--cmp-banner-text)"
252
+ } : componentTokenSet(tone);
253
+ var fallback = (domain, status, tone = "neutral") => ({
254
+ domain,
255
+ status,
256
+ label: toLabel(status || "Unknown"),
257
+ description: "Status not found in registry; falling back to the requested token set.",
258
+ tone,
259
+ badge: { subtle: componentTokenSet(tone), solid: componentTokenSet(tone) },
260
+ banner: { subtle: bannerFallback(tone), solid: componentTokenSet(tone) }
261
+ });
262
+ var registry = /* @__PURE__ */ new Map();
263
+ for (const [domain, entries] of Object.entries(status_map_default.domains)) {
264
+ const domainEntries = /* @__PURE__ */ new Map();
265
+ for (const [status, entry] of Object.entries(entries)) {
266
+ const tone = inferTone(entry.chip);
267
+ domainEntries.set(status.toLowerCase(), {
268
+ domain,
269
+ status,
270
+ label: toLabel(status),
271
+ description: entry.description,
272
+ tone,
273
+ iconName: entry.chip.icon ?? entry.banner?.icon,
274
+ badge: { subtle: rawTokenSet(entry.chip), solid: componentTokenSet(tone) },
275
+ banner: {
276
+ subtle: entry.banner ? rawTokenSet(entry.banner) : bannerFallback(tone),
277
+ solid: componentTokenSet(tone)
278
+ }
279
+ });
280
+ }
281
+ registry.set(domain, domainEntries);
282
+ }
283
+ var LEGACY_STATUS_ALIASES = {
284
+ subscription: { delinquent: "unpaid" }
285
+ };
286
+ var STATUS_DOMAINS = Object.freeze([...registry.keys()]);
287
+ function getStatusPresentation(domain, status) {
288
+ const entries = registry.get(domain);
289
+ if (!entries) return fallback(domain, status);
290
+ const normalized = status.toLowerCase();
291
+ const direct = entries.get(normalized);
292
+ if (direct) return direct;
293
+ const alias = LEGACY_STATUS_ALIASES[domain]?.[normalized];
294
+ const aliased = alias ? entries.get(alias) : void 0;
295
+ return aliased ? { ...aliased, status, label: toLabel(status) } : fallback(domain, status);
296
+ }
297
+ function listStatuses(domain) {
298
+ return [...registry.get(domain)?.values() ?? []];
299
+ }
300
+ function getToneTokenSet(tone) {
301
+ return componentTokenSet(tone);
302
+ }
303
+ function getBannerToneTokenSet(tone) {
304
+ return bannerFallback(tone);
305
+ }
306
+ var ICON_GLYPHS = {
307
+ success: "\u2714\uFE0E",
308
+ warning: "\u26A0\uFE0E",
309
+ critical: "\u2A2F",
310
+ danger: "\u2A2F",
311
+ error: "\u2A2F",
312
+ negative: "\u2A2F",
313
+ pending: "\u2026",
314
+ processing: "\u27F3",
315
+ paused: "\u23F8",
316
+ canceled: "\u2205",
317
+ cancelled: "\u2205",
318
+ draft: "\u270E",
319
+ void: "\u2205",
320
+ paid: "\u2714\uFE0E",
321
+ info: "\u2139\uFE0E",
322
+ trial: "\u2605",
323
+ future: "\u23F2",
324
+ scheduled: "\u23F2",
325
+ refunded: "\u21BA",
326
+ locked: "\u{1F512}",
327
+ unlocked: "\u{1F513}",
328
+ default: "\u2022"
329
+ };
330
+ function resolveStatusGlyph(iconName) {
331
+ const segment = iconName?.split(".").pop()?.toLowerCase();
332
+ return segment ? ICON_GLYPHS[segment] ?? ICON_GLYPHS.default : void 0;
333
+ }
334
+ function defaultStatusGlyph() {
335
+ return ICON_GLYPHS.default;
336
+ }
337
+
338
+ export { STATUS_DOMAINS, defaultStatusGlyph, getBannerToneTokenSet, getStatusPresentation, getToneTokenSet, listStatuses, resolveStatusGlyph };
@@ -0,0 +1,187 @@
1
+ import { getStatusPresentation, getToneTokenSet } from './chunk-G74PKSW3.js';
2
+ import * as React from 'react';
3
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
+
5
+ var classes = (...values) => values.filter(Boolean).join(" ");
6
+ var TableHead = React.forwardRef(
7
+ ({ className, ...rest }, ref) => /* @__PURE__ */ jsx("thead", { ref, className: classes("oods-table__header", className), ...rest })
8
+ );
9
+ TableHead.displayName = "OODS.Table.Head";
10
+ var TableBody = React.forwardRef(
11
+ ({ className, ...rest }, ref) => /* @__PURE__ */ jsx("tbody", { ref, className: classes("oods-table__body", className), ...rest })
12
+ );
13
+ TableBody.displayName = "OODS.Table.Body";
14
+ var TableCaption = React.forwardRef(
15
+ ({ className, ...rest }, ref) => /* @__PURE__ */ jsx("caption", { ref, className: classes("oods-table__caption", className), ...rest })
16
+ );
17
+ TableCaption.displayName = "OODS.Table.Caption";
18
+ var TableRow = React.forwardRef(
19
+ ({
20
+ selectable = false,
21
+ selected = false,
22
+ onActivate,
23
+ className,
24
+ tabIndex,
25
+ onClick,
26
+ onKeyDown,
27
+ ...rest
28
+ }, ref) => {
29
+ const handleClick = (event) => {
30
+ onClick?.(event);
31
+ if (!event.defaultPrevented) onActivate?.(event);
32
+ };
33
+ const handleKeyDown = (event) => {
34
+ onKeyDown?.(event);
35
+ if (!event.defaultPrevented && selectable && (event.key === "Enter" || event.key === " ")) {
36
+ event.preventDefault();
37
+ onActivate?.(event);
38
+ }
39
+ };
40
+ return /* @__PURE__ */ jsx(
41
+ "tr",
42
+ {
43
+ ref,
44
+ className: classes("oods-table__row", className),
45
+ "data-selectable": selectable ? "true" : void 0,
46
+ "data-selected": selected ? "true" : void 0,
47
+ "aria-selected": selectable ? selected : void 0,
48
+ tabIndex: selectable ? tabIndex ?? 0 : tabIndex,
49
+ onClick: handleClick,
50
+ onKeyDown: handleKeyDown,
51
+ ...rest
52
+ }
53
+ );
54
+ }
55
+ );
56
+ TableRow.displayName = "OODS.Table.Row";
57
+ var TableHeaderCell = React.forwardRef(
58
+ ({ numeric = false, className, scope = "col", ...rest }, ref) => /* @__PURE__ */ jsx(
59
+ "th",
60
+ {
61
+ ref,
62
+ className: classes(
63
+ "oods-table__header-cell",
64
+ numeric && "oods-table__header-cell--numeric",
65
+ className
66
+ ),
67
+ "data-numeric": numeric ? "true" : void 0,
68
+ scope,
69
+ ...rest
70
+ }
71
+ )
72
+ );
73
+ TableHeaderCell.displayName = "OODS.Table.HeaderCell";
74
+ var TableCell = React.forwardRef(
75
+ ({
76
+ numeric = false,
77
+ status,
78
+ statusDomain = "subscription",
79
+ tone: toneOverride,
80
+ statusEmphasis = "surface",
81
+ className,
82
+ children,
83
+ style,
84
+ title,
85
+ ...rest
86
+ }, ref) => {
87
+ const presentation = status ? getStatusPresentation(statusDomain, status) : void 0;
88
+ const tone = toneOverride ?? presentation?.tone;
89
+ const tokens = tone ? getToneTokenSet(tone) : void 0;
90
+ const statusStyle = tokens ? statusEmphasis === "text" ? { "--table-cell-status-foreground": tokens.foreground } : {
91
+ "--table-cell-status-background": tokens.background,
92
+ "--table-cell-status-border": tokens.border,
93
+ "--table-cell-status-foreground": tokens.foreground
94
+ } : {};
95
+ return /* @__PURE__ */ jsx(
96
+ "td",
97
+ {
98
+ ref,
99
+ className: classes(
100
+ "oods-table__cell",
101
+ numeric && "oods-table__cell--numeric",
102
+ status && "oods-table__cell--status",
103
+ className
104
+ ),
105
+ "data-numeric": numeric ? "true" : void 0,
106
+ "data-status": status,
107
+ "data-status-domain": status ? statusDomain : void 0,
108
+ "data-status-tone": status ? tone : void 0,
109
+ "data-status-emphasis": status ? statusEmphasis : void 0,
110
+ "data-has-status": status ? "true" : void 0,
111
+ title: title ?? presentation?.description,
112
+ style: { ...statusStyle, ...style },
113
+ ...rest,
114
+ children: children ?? presentation?.label ?? status
115
+ }
116
+ );
117
+ }
118
+ );
119
+ TableCell.displayName = "OODS.Table.Cell";
120
+ var TableRoot = React.forwardRef(
121
+ ({
122
+ caption,
123
+ columns,
124
+ rows,
125
+ density = "comfortable",
126
+ selectable = false,
127
+ onRowActivate,
128
+ children,
129
+ containerClassName,
130
+ containerStyle,
131
+ className,
132
+ ...rest
133
+ }, ref) => /* @__PURE__ */ jsx(
134
+ "div",
135
+ {
136
+ className: classes("oods-table__container", containerClassName),
137
+ style: containerStyle,
138
+ "data-density": density,
139
+ children: /* @__PURE__ */ jsx(
140
+ "table",
141
+ {
142
+ ref,
143
+ className: classes("oods-table", className),
144
+ "data-oods-component": "Table",
145
+ "data-density": density,
146
+ ...rest,
147
+ children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
148
+ caption ? /* @__PURE__ */ jsx(TableCaption, { children: caption }) : null,
149
+ /* @__PURE__ */ jsx(TableHead, { children: /* @__PURE__ */ jsx("tr", { children: columns?.map((column) => /* @__PURE__ */ jsx(TableHeaderCell, { numeric: column.numeric, children: column.label }, column.key)) }) }),
150
+ /* @__PURE__ */ jsx(TableBody, { children: (rows?.length ?? 0) === 0 ? /* @__PURE__ */ jsx("tr", { className: "oods-table__row", children: /* @__PURE__ */ jsx(TableCell, { colSpan: Math.max(columns?.length ?? 0, 1), children: "No rows available." }) }) : rows?.map((row) => /* @__PURE__ */ jsx(
151
+ "tr",
152
+ {
153
+ className: "oods-table__row",
154
+ "data-selectable": selectable ? "true" : void 0,
155
+ children: columns?.map((column, columnIndex) => {
156
+ const value = row[column.key];
157
+ const rendered = column.render ? column.render(value, row) : String(value ?? "");
158
+ return /* @__PURE__ */ jsx(TableCell, { numeric: column.numeric, children: selectable && columnIndex === 0 ? /* @__PURE__ */ jsx(
159
+ "button",
160
+ {
161
+ type: "button",
162
+ className: "oods-table-row-action",
163
+ onClick: (event) => onRowActivate?.(row.id, row, event),
164
+ children: rendered
165
+ }
166
+ ) : rendered }, column.key);
167
+ })
168
+ },
169
+ row.id
170
+ )) })
171
+ ] })
172
+ }
173
+ )
174
+ }
175
+ )
176
+ );
177
+ TableRoot.displayName = "OODS.Table";
178
+ var Table = Object.assign(TableRoot, {
179
+ Head: TableHead,
180
+ Body: TableBody,
181
+ Caption: TableCaption,
182
+ Row: TableRow,
183
+ HeaderCell: TableHeaderCell,
184
+ Cell: TableCell
185
+ });
186
+
187
+ export { Table, TableBody, TableCaption, TableCell, TableHead, TableHeaderCell, TableRow };