@viax.io/uxm 4.39.0 → 4.40.1
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/CHANGELOG.md +20 -0
- package/LICENSE.md +21 -0
- package/README.md +33 -44
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js.map +1 -1
- package/dist/previews/index.cjs.map +1 -1
- package/dist/previews/index.d.ts +2 -2
- package/dist/previews/index.js.map +1 -1
- package/dist/studio/index.cjs.map +1 -1
- package/dist/studio/index.d.ts +2 -2
- package/dist/studio/index.js.map +1 -1
- package/dist/studio/lib/registry/buttons.cjs +2 -2
- package/dist/studio/lib/registry/buttons.cjs.map +1 -1
- package/dist/studio/lib/registry/buttons.js +2 -2
- package/dist/studio/lib/registry/buttons.js.map +1 -1
- package/dist/studio/lib/registry/composite/sidebar-nav-trigger.cjs +1 -1
- package/dist/studio/lib/registry/composite/sidebar-nav-trigger.cjs.map +1 -1
- package/dist/studio/lib/registry/composite/sidebar-nav-trigger.js +1 -1
- package/dist/studio/lib/registry/composite/sidebar-nav-trigger.js.map +1 -1
- package/dist/studio/lib/registry/diagram.cjs +1 -1
- package/dist/studio/lib/registry/diagram.cjs.map +1 -1
- package/dist/studio/lib/registry/diagram.js +1 -1
- package/dist/studio/lib/registry/diagram.js.map +1 -1
- package/dist/studio/lib/registry/display.cjs +2 -2
- package/dist/studio/lib/registry/display.cjs.map +1 -1
- package/dist/studio/lib/registry/display.js +2 -2
- package/dist/studio/lib/registry/display.js.map +1 -1
- package/dist/studio/lib/registry/feedback.cjs +3 -3
- package/dist/studio/lib/registry/feedback.cjs.map +1 -1
- package/dist/studio/lib/registry/feedback.js +3 -3
- package/dist/studio/lib/registry/feedback.js.map +1 -1
- package/dist/studio/lib/registry/inputs/color-input.cjs +1 -1
- package/dist/studio/lib/registry/inputs/color-input.cjs.map +1 -1
- package/dist/studio/lib/registry/inputs/color-input.js +1 -1
- package/dist/studio/lib/registry/inputs/color-input.js.map +1 -1
- package/dist/studio/lib/registry/inputs/currency-input.cjs +1 -1
- package/dist/studio/lib/registry/inputs/currency-input.cjs.map +1 -1
- package/dist/studio/lib/registry/inputs/currency-input.js +1 -1
- package/dist/studio/lib/registry/inputs/currency-input.js.map +1 -1
- package/dist/studio/lib/registry/inputs/editable-cell.cjs +1 -1
- package/dist/studio/lib/registry/inputs/editable-cell.cjs.map +1 -1
- package/dist/studio/lib/registry/inputs/editable-cell.js +1 -1
- package/dist/studio/lib/registry/inputs/editable-cell.js.map +1 -1
- package/dist/studio/lib/registry/inputs/file-upload.cjs +1 -1
- package/dist/studio/lib/registry/inputs/file-upload.cjs.map +1 -1
- package/dist/studio/lib/registry/inputs/file-upload.js +1 -1
- package/dist/studio/lib/registry/inputs/file-upload.js.map +1 -1
- package/dist/studio/lib/registry/inputs/input-text.cjs +1 -1
- package/dist/studio/lib/registry/inputs/input-text.cjs.map +1 -1
- package/dist/studio/lib/registry/inputs/input-text.js +1 -1
- package/dist/studio/lib/registry/inputs/input-text.js.map +1 -1
- package/dist/studio/lib/registry/inputs/listbox.cjs +1 -1
- package/dist/studio/lib/registry/inputs/listbox.cjs.map +1 -1
- package/dist/studio/lib/registry/inputs/listbox.js +1 -1
- package/dist/studio/lib/registry/inputs/listbox.js.map +1 -1
- package/dist/studio/lib/registry/inputs/menu.cjs +1 -1
- package/dist/studio/lib/registry/inputs/menu.cjs.map +1 -1
- package/dist/studio/lib/registry/inputs/menu.js +1 -1
- package/dist/studio/lib/registry/inputs/menu.js.map +1 -1
- package/dist/studio/lib/registry/inputs/number-input.cjs +1 -1
- package/dist/studio/lib/registry/inputs/number-input.cjs.map +1 -1
- package/dist/studio/lib/registry/inputs/number-input.js +1 -1
- package/dist/studio/lib/registry/inputs/number-input.js.map +1 -1
- package/dist/studio/lib/registry/inputs/password-input.cjs +1 -1
- package/dist/studio/lib/registry/inputs/password-input.cjs.map +1 -1
- package/dist/studio/lib/registry/inputs/password-input.js +1 -1
- package/dist/studio/lib/registry/inputs/password-input.js.map +1 -1
- package/dist/studio/lib/registry/inputs/phone-input.cjs +1 -1
- package/dist/studio/lib/registry/inputs/phone-input.cjs.map +1 -1
- package/dist/studio/lib/registry/inputs/phone-input.js +1 -1
- package/dist/studio/lib/registry/inputs/phone-input.js.map +1 -1
- package/dist/studio/lib/registry/inputs/slider.cjs +1 -1
- package/dist/studio/lib/registry/inputs/slider.cjs.map +1 -1
- package/dist/studio/lib/registry/inputs/slider.js +1 -1
- package/dist/studio/lib/registry/inputs/slider.js.map +1 -1
- package/dist/studio/lib/registry/inputs/time-input.cjs +1 -1
- package/dist/studio/lib/registry/inputs/time-input.cjs.map +1 -1
- package/dist/studio/lib/registry/inputs/time-input.js +1 -1
- package/dist/studio/lib/registry/inputs/time-input.js.map +1 -1
- package/dist/studio/lib/types.cjs.map +1 -1
- package/dist/studio/persistence/generate-css.cjs.map +1 -1
- package/dist/studio/persistence/generate-css.js.map +1 -1
- package/dist/ui/editable-cell/editable-cell-date.cjs +226 -0
- package/dist/ui/editable-cell/editable-cell-date.cjs.map +1 -0
- package/dist/ui/editable-cell/editable-cell-date.d.ts +4 -0
- package/dist/ui/editable-cell/editable-cell-date.d.ts.map +1 -0
- package/dist/ui/editable-cell/editable-cell-date.js +202 -0
- package/dist/ui/editable-cell/editable-cell-date.js.map +1 -0
- package/dist/ui/editable-cell/editable-cell-select.cjs +297 -0
- package/dist/ui/editable-cell/editable-cell-select.cjs.map +1 -0
- package/dist/ui/editable-cell/editable-cell-select.d.ts +4 -0
- package/dist/ui/editable-cell/editable-cell-select.d.ts.map +1 -0
- package/dist/ui/editable-cell/editable-cell-select.js +273 -0
- package/dist/ui/editable-cell/editable-cell-select.js.map +1 -0
- package/dist/ui/editable-cell/editable-cell-text.cjs +175 -0
- package/dist/ui/editable-cell/editable-cell-text.cjs.map +1 -0
- package/dist/ui/editable-cell/editable-cell-text.d.ts +4 -0
- package/dist/ui/editable-cell/editable-cell-text.d.ts.map +1 -0
- package/dist/ui/editable-cell/editable-cell-text.js +151 -0
- package/dist/ui/editable-cell/editable-cell-text.js.map +1 -0
- package/dist/ui/editable-cell/editable-cell.cjs +27 -705
- package/dist/ui/editable-cell/editable-cell.cjs.map +1 -1
- package/dist/ui/editable-cell/editable-cell.d.ts +1 -1
- package/dist/ui/editable-cell/editable-cell.d.ts.map +1 -1
- package/dist/ui/editable-cell/editable-cell.js +28 -706
- package/dist/ui/editable-cell/editable-cell.js.map +1 -1
- package/dist/ui/editable-cell/use-editable-cell.cjs +284 -0
- package/dist/ui/editable-cell/use-editable-cell.cjs.map +1 -0
- package/dist/ui/editable-cell/use-editable-cell.d.ts +51 -0
- package/dist/ui/editable-cell/use-editable-cell.d.ts.map +1 -0
- package/dist/ui/editable-cell/use-editable-cell.js +260 -0
- package/dist/ui/editable-cell/use-editable-cell.js.map +1 -0
- package/dist/ui/page-shell/page-shell.cjs.map +1 -1
- package/dist/ui/page-shell/page-shell.d.ts +1 -1
- package/dist/ui/page-shell/page-shell.d.ts.map +1 -1
- package/dist/ui/page-shell/page-shell.js.map +1 -1
- package/dist/ui/styles.css +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { FORMAT_SPEC, formatDate as formatDateAs, invalidDateMessage, parseDate as parseFormattedDate } from "../date-input/index.js";
|
|
3
|
+
import { isEmptyValue, isoToFormatted, parseISODate, toISODate } from "./editable-cell-utils.js";
|
|
4
|
+
function useEditableCell(props) {
|
|
5
|
+
const {
|
|
6
|
+
value,
|
|
7
|
+
onCommit,
|
|
8
|
+
options,
|
|
9
|
+
format,
|
|
10
|
+
validate,
|
|
11
|
+
requiredMessage,
|
|
12
|
+
placeholder,
|
|
13
|
+
forceMode,
|
|
14
|
+
invalidDateMessage: invalidDateMessageProp,
|
|
15
|
+
type,
|
|
16
|
+
dateFormat,
|
|
17
|
+
required,
|
|
18
|
+
saveErrorMessage,
|
|
19
|
+
invalidNumberMessage,
|
|
20
|
+
invalidValueMessage
|
|
21
|
+
} = props;
|
|
22
|
+
const [isEditing, setIsEditing] = useState(false);
|
|
23
|
+
const [draft, setDraft] = useState(() => {
|
|
24
|
+
if (type === "date") return isoToFormatted(value, dateFormat);
|
|
25
|
+
if (Array.isArray(value)) return "";
|
|
26
|
+
return String(value ?? "");
|
|
27
|
+
});
|
|
28
|
+
const [submitting, setSubmitting] = useState(false);
|
|
29
|
+
const [error, setError] = useState(null);
|
|
30
|
+
const [open, setOpen] = useState(false);
|
|
31
|
+
const inputRef = useRef(null);
|
|
32
|
+
const wrapRef = useRef(null);
|
|
33
|
+
const parsedValue = useMemo(
|
|
34
|
+
() => type === "date" && typeof value === "string" && value ? parseISODate(value) ?? parseFormattedDate(value, dateFormat) : null,
|
|
35
|
+
[type, value, dateFormat]
|
|
36
|
+
);
|
|
37
|
+
const displayFormatted = useMemo(
|
|
38
|
+
() => type === "date" ? isoToFormatted(value, dateFormat) : "",
|
|
39
|
+
[type, value, dateFormat]
|
|
40
|
+
);
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (!isEditing) {
|
|
43
|
+
const next = Array.isArray(value) ? "" : type === "date" ? displayFormatted : String(value ?? "");
|
|
44
|
+
setDraft(next);
|
|
45
|
+
}
|
|
46
|
+
}, [value, isEditing, type, displayFormatted]);
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
if (isEditing && inputRef.current) {
|
|
49
|
+
inputRef.current.focus();
|
|
50
|
+
inputRef.current.select();
|
|
51
|
+
}
|
|
52
|
+
}, [isEditing]);
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (error && type === "date") {
|
|
55
|
+
setOpen(false);
|
|
56
|
+
}
|
|
57
|
+
}, [error, type]);
|
|
58
|
+
const parseDraft = useCallback(() => {
|
|
59
|
+
if (type === "number") return draft.trim() === "" ? Number.NaN : Number(draft);
|
|
60
|
+
if (type === "date") return draft;
|
|
61
|
+
return draft;
|
|
62
|
+
}, [type, draft]);
|
|
63
|
+
const requiredMsg = requiredMessage ?? (type === "select" ? "Select an option" : "Required");
|
|
64
|
+
const commitValue = useCallback(
|
|
65
|
+
async (next) => {
|
|
66
|
+
if (submitting) return false;
|
|
67
|
+
if (required && isEmptyValue(next)) {
|
|
68
|
+
setError({ message: requiredMsg, severity: "warning" });
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
const validationError = validate?.(next);
|
|
72
|
+
if (validationError) {
|
|
73
|
+
setError({ message: validationError, severity: "warning" });
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
setError(null);
|
|
77
|
+
setSubmitting(true);
|
|
78
|
+
try {
|
|
79
|
+
await onCommit(next);
|
|
80
|
+
setIsEditing(false);
|
|
81
|
+
setOpen(false);
|
|
82
|
+
return true;
|
|
83
|
+
} catch (err) {
|
|
84
|
+
setError({
|
|
85
|
+
message: err instanceof Error ? err.message : saveErrorMessage,
|
|
86
|
+
severity: "error"
|
|
87
|
+
});
|
|
88
|
+
requestAnimationFrame(() => inputRef.current?.focus());
|
|
89
|
+
return false;
|
|
90
|
+
} finally {
|
|
91
|
+
setSubmitting(false);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
[submitting, required, requiredMsg, saveErrorMessage, validate, onCommit]
|
|
95
|
+
);
|
|
96
|
+
const commitDate = useCallback(
|
|
97
|
+
async (iso) => {
|
|
98
|
+
if (submitting) return;
|
|
99
|
+
const currentIso = parsedValue ? toISODate(parsedValue) : "";
|
|
100
|
+
if (iso === currentIso) {
|
|
101
|
+
const validationError = validate?.(iso);
|
|
102
|
+
if (validationError) {
|
|
103
|
+
setError({ message: validationError, severity: "warning" });
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
setIsEditing(false);
|
|
107
|
+
setOpen(false);
|
|
108
|
+
setError(null);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
await commitValue(iso);
|
|
112
|
+
},
|
|
113
|
+
[submitting, parsedValue, validate, commitValue]
|
|
114
|
+
);
|
|
115
|
+
const handleCommit = useCallback(async () => {
|
|
116
|
+
const next = parseDraft();
|
|
117
|
+
if (type === "number" && Number.isNaN(next)) {
|
|
118
|
+
if (draft.trim() === "") {
|
|
119
|
+
if (String(value ?? "") === "") {
|
|
120
|
+
setIsEditing(false);
|
|
121
|
+
setError(null);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
await commitValue("");
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
setError({ message: invalidNumberMessage, severity: "warning" });
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (type === "date") {
|
|
131
|
+
const raw = String(next).trim();
|
|
132
|
+
if (raw === "") {
|
|
133
|
+
await commitDate("");
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const parsed = parseFormattedDate(raw, dateFormat) ?? parseISODate(raw);
|
|
137
|
+
if (!parsed) {
|
|
138
|
+
setError({
|
|
139
|
+
message: invalidDateMessageProp ?? invalidDateMessage(dateFormat),
|
|
140
|
+
severity: "warning"
|
|
141
|
+
});
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
await commitDate(toISODate(parsed));
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const valueStr = Array.isArray(value) ? "" : String(value ?? "");
|
|
148
|
+
if (String(next) === valueStr) {
|
|
149
|
+
setIsEditing(false);
|
|
150
|
+
setError(null);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
await commitValue(next);
|
|
154
|
+
}, [
|
|
155
|
+
parseDraft,
|
|
156
|
+
type,
|
|
157
|
+
draft,
|
|
158
|
+
dateFormat,
|
|
159
|
+
value,
|
|
160
|
+
commitValue,
|
|
161
|
+
commitDate,
|
|
162
|
+
invalidNumberMessage,
|
|
163
|
+
invalidDateMessageProp
|
|
164
|
+
]);
|
|
165
|
+
const handlePick = useCallback(
|
|
166
|
+
async (calValue) => {
|
|
167
|
+
if (!calValue.start || submitting) return;
|
|
168
|
+
setDraft(formatDateAs(calValue.start, dateFormat));
|
|
169
|
+
await commitDate(toISODate(calValue.start));
|
|
170
|
+
},
|
|
171
|
+
[dateFormat, submitting, commitDate]
|
|
172
|
+
);
|
|
173
|
+
const handleClearDraft = useCallback(() => {
|
|
174
|
+
setDraft("");
|
|
175
|
+
setError(null);
|
|
176
|
+
}, []);
|
|
177
|
+
const handleCancel = useCallback(() => {
|
|
178
|
+
if (Array.isArray(value)) {
|
|
179
|
+
setDraft("");
|
|
180
|
+
} else if (type === "date") {
|
|
181
|
+
setDraft(displayFormatted);
|
|
182
|
+
} else {
|
|
183
|
+
setDraft(String(value ?? ""));
|
|
184
|
+
}
|
|
185
|
+
setIsEditing(false);
|
|
186
|
+
setOpen(false);
|
|
187
|
+
setError(null);
|
|
188
|
+
}, [value, type, displayFormatted]);
|
|
189
|
+
const handleKeyDown = useCallback(
|
|
190
|
+
(e) => {
|
|
191
|
+
if (e.key === "Enter") {
|
|
192
|
+
e.preventDefault();
|
|
193
|
+
handleCommit();
|
|
194
|
+
} else if (e.key === "Escape") {
|
|
195
|
+
e.preventDefault();
|
|
196
|
+
handleCancel();
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
[handleCommit, handleCancel]
|
|
200
|
+
);
|
|
201
|
+
const editing = forceMode != null || isEditing;
|
|
202
|
+
const shownError = forceMode === "error" ? error ?? { message: saveErrorMessage, severity: "error" } : forceMode === "warning" ? error ?? { message: invalidValueMessage, severity: "warning" } : error;
|
|
203
|
+
const dateHint = FORMAT_SPEC[dateFormat].placeholder;
|
|
204
|
+
const isEmpty = Array.isArray(value) ? value.length === 0 : value === "" || value === null || value === void 0;
|
|
205
|
+
const display = (() => {
|
|
206
|
+
if (isEmpty) return placeholder ?? (type === "date" ? dateHint : "");
|
|
207
|
+
if (format) return format(value);
|
|
208
|
+
if (Array.isArray(value)) {
|
|
209
|
+
if (options) {
|
|
210
|
+
return value.map((v) => options.find((o) => o.value === v)?.label ?? v).join(", ");
|
|
211
|
+
}
|
|
212
|
+
return value.join(", ");
|
|
213
|
+
}
|
|
214
|
+
if (type === "select" && options) {
|
|
215
|
+
return options.find((o) => o.value === String(value))?.label ?? String(value);
|
|
216
|
+
}
|
|
217
|
+
return value;
|
|
218
|
+
})();
|
|
219
|
+
const isMulti = type === "multiselect";
|
|
220
|
+
const isSelect = type === "select";
|
|
221
|
+
const isDate = type === "date";
|
|
222
|
+
const titleText = isEmpty ? placeholder ?? "" : isMulti ? Array.isArray(value) ? (options ?? []).filter((o) => value.includes(o.value)).map((o) => o.label).join(", ") : "" : isSelect ? options?.find((o) => o.value === String(value))?.label ?? String(value) : isDate ? displayFormatted || String(value) : String(value);
|
|
223
|
+
return {
|
|
224
|
+
isEditing,
|
|
225
|
+
setIsEditing,
|
|
226
|
+
draft,
|
|
227
|
+
setDraft,
|
|
228
|
+
submitting,
|
|
229
|
+
setSubmitting,
|
|
230
|
+
error,
|
|
231
|
+
setError,
|
|
232
|
+
open,
|
|
233
|
+
setOpen,
|
|
234
|
+
inputRef,
|
|
235
|
+
wrapRef,
|
|
236
|
+
parsedValue,
|
|
237
|
+
displayFormatted,
|
|
238
|
+
parseDraft,
|
|
239
|
+
commitValue,
|
|
240
|
+
commitDate,
|
|
241
|
+
handleCommit,
|
|
242
|
+
handlePick,
|
|
243
|
+
handleClearDraft,
|
|
244
|
+
handleCancel,
|
|
245
|
+
handleKeyDown,
|
|
246
|
+
editing,
|
|
247
|
+
dateHint,
|
|
248
|
+
isEmpty,
|
|
249
|
+
display,
|
|
250
|
+
isMulti,
|
|
251
|
+
isSelect,
|
|
252
|
+
isDate,
|
|
253
|
+
titleText,
|
|
254
|
+
shownError
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
export {
|
|
258
|
+
useEditableCell
|
|
259
|
+
};
|
|
260
|
+
//# sourceMappingURL=use-editable-cell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/ui/editable-cell/use-editable-cell.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { FORMAT_SPEC, formatDate as formatDateAs, invalidDateMessage, parseDate as parseFormattedDate } from '../date-input';\n\nimport { isEmptyValue, isoToFormatted, parseISODate, toISODate } from './editable-cell-utils';\n\nimport type { CalendarValue } from '../calendar';\nimport type { CellError, EditableCellProps, EditableCellValue } from './editable-cell.types';\n\n/** Keys `EditableCell` defaults before handing the props to the hook and the views. */\nexport type EditableCellDefaultedKey = 'type' | 'dateFormat' | 'searchable' | 'clearable' | 'align' | 'size' | 'required' | 'saveErrorMessage' | 'invalidNumberMessage' | 'invalidValueMessage' | 'clearSelectionLabel' | 'clearDateLabel' | 'clearValueLabel' | 'openCalendarLabel' | 'calendarDialogLabel' | 'addDateLabel';\n\n/** `EditableCellProps` with every default applied — what the hook and the views see. */\nexport type EditableCellResolvedProps = Omit<EditableCellProps, EditableCellDefaultedKey> &\n Required<Pick<EditableCellProps, EditableCellDefaultedKey>>;\n\nexport interface EditableCellViewProps {\n props: EditableCellResolvedProps;\n cell: EditableCellState;\n}\n\nexport type EditableCellState = ReturnType<typeof useEditableCell>;\n\n/**\n * All of EditableCell's state, effects, commit paths and derived display\n * values — everything that is NOT per-type JSX. The three views\n * (`editable-cell-{text,date,select}.tsx`) are hook-free and read this\n * object, so the hook order is fixed here regardless of which view renders.\n */\nexport function useEditableCell(props: EditableCellResolvedProps) {\n const {\n value,\n onCommit,\n options,\n format,\n validate,\n requiredMessage,\n placeholder,\n forceMode,\n invalidDateMessage: invalidDateMessageProp,\n type,\n dateFormat,\n required,\n saveErrorMessage,\n invalidNumberMessage,\n invalidValueMessage,\n } = props;\n const [isEditing, setIsEditing] = useState(false);\n const [draft, setDraft] = useState<string>(() => {\n if (type === 'date') return isoToFormatted(value, dateFormat);\n if (Array.isArray(value)) return '';\n return String(value ?? '');\n });\n const [submitting, setSubmitting] = useState(false);\n const [error, setError] = useState<CellError | null>(null);\n const [open, setOpen] = useState(false);\n const inputRef = useRef<HTMLInputElement | null>(null);\n // Anchor for the error popover — the editing wrapper, so the banner hangs\n // under the cell without affecting the table row's layout.\n const wrapRef = useRef<HTMLSpanElement | null>(null);\n\n // Committed date as a Date and in the cell's mask, memoized so the display,\n // tooltip, aria-label, calendar highlight, draft sync, and cancel-reset all\n // share one parse/format instead of each re-running it. Cheap `null`/`''`\n // no-ops for non-date types. Declared before the effects so they can reuse it.\n const parsedValue = useMemo(\n () =>\n type === 'date' && typeof value === 'string' && value\n ? parseISODate(value) ?? parseFormattedDate(value, dateFormat)\n : null,\n [type, value, dateFormat],\n );\n const displayFormatted = useMemo(\n () => (type === 'date' ? isoToFormatted(value, dateFormat) : ''),\n [type, value, dateFormat],\n );\n\n // When the externally-committed value changes (parent state updated, or\n // someone else edited the row), sync the draft — but only when we're\n // NOT mid-edit. Mid-edit syncs would clobber what the user is typing.\n useEffect(() => {\n if (!isEditing) {\n const next = Array.isArray(value)\n ? ''\n : type === 'date'\n ? displayFormatted\n : String(value ?? '');\n // eslint-disable-next-line react-hooks/set-state-in-effect -- intentional external-value → draft sync, gated to non-editing mode\n setDraft(next);\n }\n }, [value, isEditing, type, displayFormatted]);\n\n // Focus + select-all when entering edit mode. Select-all means typing\n // immediately replaces the value — the most common edit intent — while\n // keyboard navigation (Home/End/arrows) still works to refine.\n useEffect(() => {\n if (isEditing && inputRef.current) {\n inputRef.current.focus();\n inputRef.current.select();\n }\n }, [isEditing]);\n\n // A surfaced problem and the date calendar can't share the anchor, so the\n // calendar is hidden while an error shows. Close it outright (not just via\n // the render gate) so clearing the error later — e.g. the next keystroke's\n // `setError(null)` — doesn't silently reopen a calendar the user dismissed.\n useEffect(() => {\n if (error && type === 'date') {\n // eslint-disable-next-line react-hooks/set-state-in-effect -- close the calendar exactly when a date error appears, so it can't auto-reopen on clear\n setOpen(false);\n }\n }, [error, type]);\n\n const parseDraft = useCallback((): EditableCellValue => {\n // `Number('')` is 0, not NaN — map an empty/whitespace draft to NaN so\n // blur-with-empty hits the \"Enter a number\" boundary instead of silently\n // committing 0.\n if (type === 'number') return draft.trim() === '' ? Number.NaN : Number(draft);\n if (type === 'date') return draft;\n return draft;\n }, [type, draft]);\n\n // Per-type default required message; overridable via `requiredMessage`.\n // (multiselect's own default lives in MultiListbox, which owns that rule.)\n const requiredMsg =\n requiredMessage ?? (type === 'select' ? 'Select an option' : 'Required');\n\n const commitValue = useCallback(\n async (next: EditableCellValue): Promise<boolean> => {\n // Re-entrancy guard lives HERE, not only in each caller's render\n // condition: every commit path funnels through this function, so a new\n // call site can't forget to gate itself (`commitDate` keeps its own\n // early return for the same reason).\n if (submitting) return false;\n // Required is checked before `validate` so consumers get the empty rule\n // for free. Multiselect empties are already blocked upstream by\n // MultiListbox (they never reach here), so this covers the scalar types.\n if (required && isEmptyValue(next)) {\n setError({ message: requiredMsg, severity: 'warning' });\n return false;\n }\n const validationError = validate?.(next);\n if (validationError) {\n setError({ message: validationError, severity: 'warning' });\n return false;\n }\n\n setError(null);\n setSubmitting(true);\n try {\n await onCommit(next);\n setIsEditing(false);\n setOpen(false);\n return true;\n } catch (err) {\n // Error severity: the value was acceptable but saving failed.\n setError({\n message: err instanceof Error ? err.message : saveErrorMessage,\n severity: 'error',\n });\n // Refocus the input so the user can retry without re-entering edit mode.\n requestAnimationFrame(() => inputRef.current?.focus());\n return false;\n } finally {\n setSubmitting(false);\n }\n },\n [submitting, required, requiredMsg, saveErrorMessage, validate, onCommit],\n );\n\n // Shared commit path for both the typed input (handleCommit) and the calendar\n // (handlePick). Guards a re-entrant commit while one is in flight, and skips\n // the consumer's onCommit when nothing changed — comparing normalized ISO on\n // BOTH sides so a formatted seed (or a re-picked day) isn't mistaken for a\n // change — while still running `validate` on the no-op so a required /\n // row-dependent rule still surfaces.\n const commitDate = useCallback(\n async (iso: string) => {\n if (submitting) return;\n const currentIso = parsedValue ? toISODate(parsedValue) : '';\n if (iso === currentIso) {\n const validationError = validate?.(iso);\n if (validationError) {\n setError({ message: validationError, severity: 'warning' });\n return;\n }\n setIsEditing(false);\n setOpen(false);\n setError(null);\n return;\n }\n await commitValue(iso);\n },\n [submitting, parsedValue, validate, commitValue],\n );\n\n const handleCommit = useCallback(async () => {\n const next = parseDraft();\n\n // Number type with a NaN draft is rejected at the parse boundary —\n // EXCEPT when the draft was emptied outright (the ✕, or select-all +\n // Delete): that's an intentional clear, not a typo, so it routes\n // through `commitValue('')` like every other type — `required` blocks\n // it with its own message, an optional cell commits '' and shows its\n // placeholder. Half-typed non-numbers (\"-\", \".\") stay input errors.\n // Warning severity: the input is the problem and the user can fix it.\n if (type === 'number' && Number.isNaN(next)) {\n if (draft.trim() === '') {\n if (String(value ?? '') === '') {\n // Already empty — pure exit, no spurious onCommit (mirrors the\n // unchanged-value no-op below).\n setIsEditing(false);\n setError(null);\n return;\n }\n await commitValue('');\n return;\n }\n setError({ message: invalidNumberMessage, severity: 'warning' });\n return;\n }\n\n // Typed dates parse at the boundary: per the cell's `dateFormat` mask\n // first, canonical ISO as fallback. An emptied draft clears the date\n // (commits \"\"). Unchanged-value / submitting / validation handling all\n // live in the shared `commitDate` so typing and calendar picking behave\n // identically.\n if (type === 'date') {\n const raw = String(next).trim();\n if (raw === '') {\n await commitDate('');\n return;\n }\n const parsed = parseFormattedDate(raw, dateFormat) ?? parseISODate(raw);\n if (!parsed) {\n setError({\n message: invalidDateMessageProp ?? invalidDateMessage(dateFormat),\n severity: 'warning',\n });\n return;\n }\n await commitDate(toISODate(parsed));\n return;\n }\n\n // No-op when value didn't change — exit edit mode without firing\n // the consumer's handler (avoids spurious \"save\" calls on blur).\n // NOTE: unlike the date path in `commitDate` (which re-runs `validate`\n // on an unchanged value so a required / row-dependent rule still\n // surfaces on blur), text/number intentionally skip `validate` here.\n // These cells have no cross-row validity story today, so an unchanged\n // blur is treated as a pure exit; keep the two paths in sync if that\n // assumption ever changes.\n const valueStr = Array.isArray(value) ? '' : String(value ?? '');\n if (String(next) === valueStr) {\n setIsEditing(false);\n setError(null);\n return;\n }\n\n await commitValue(next);\n }, [\n parseDraft,\n type,\n draft,\n dateFormat,\n value,\n commitValue,\n commitDate,\n invalidNumberMessage,\n invalidDateMessageProp,\n ]);\n\n const handlePick = useCallback(\n async (calValue: CalendarValue) => {\n if (!calValue.start || submitting) return;\n setDraft(formatDateAs(calValue.start, dateFormat));\n // Routes through the shared guard: re-picking the already-selected day is\n // a no-op (no spurious onCommit), and a pick can't fire mid-commit.\n await commitDate(toISODate(calValue.start));\n },\n [dateFormat, submitting, commitDate],\n );\n\n // ✕ inside the editing input (text/number/date, gated by `clearable`).\n // Empties the DRAFT only — focus stays in the input (the button swallows\n // its mousedown so the field never blurs) and nothing commits until\n // Enter/blur, so required/validate still guard and Esc still restores.\n const handleClearDraft = useCallback(() => {\n setDraft('');\n setError(null);\n }, []);\n\n const handleCancel = useCallback(() => {\n if (Array.isArray(value)) {\n setDraft('');\n } else if (type === 'date') {\n setDraft(displayFormatted);\n } else {\n setDraft(String(value ?? ''));\n }\n setIsEditing(false);\n setOpen(false);\n setError(null);\n }, [value, type, displayFormatted]);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n handleCommit();\n } else if (e.key === 'Escape') {\n e.preventDefault();\n handleCancel();\n }\n },\n [handleCommit, handleCancel],\n );\n\n // `forceMode` (preview-only) renders the input branch without a real\n // click. `warning` / `error` also surface a sample message at that\n // severity so the matching knobs paint. Derived in the render body —\n // deliberately NOT fed into the focus effect above, so a forced preview\n // never steals focus from the rest of the page. Production leaves\n // forceMode unset, so `editing` collapses to the internal `isEditing`.\n const editing = forceMode != null || isEditing;\n const shownError: CellError | null =\n forceMode === 'error'\n ? error ?? { message: saveErrorMessage, severity: 'error' }\n : forceMode === 'warning'\n ? error ?? { message: invalidValueMessage, severity: 'warning' }\n : error;\n\n // Hint shown in the date input — the canonical format pattern for the\n // chosen `dateFormat`. Used as the placeholder when the cell is empty\n // and as the input placeholder in edit mode.\n const dateHint = FORMAT_SPEC[dateFormat].placeholder;\n\n // Compute display value\n const isEmpty = Array.isArray(value)\n ? value.length === 0\n : value === '' || value === null || value === undefined;\n\n const display = (() => {\n // Empty wins over `format`: a cleared cell must read as its placeholder,\n // not as the formatter's rendering of \"\" (e.g. a Tag formatter would\n // paint an empty pill). `format` only ever sees real values.\n if (isEmpty) return placeholder ?? (type === 'date' ? dateHint : '');\n if (format) return format(value);\n if (Array.isArray(value)) {\n // For multiselect: show labels from options if available\n if (options) {\n return value\n .map((v) => options.find((o) => o.value === v)?.label ?? v)\n .join(', ');\n }\n return value.join(', ');\n }\n if (type === 'select' && options) {\n return options.find((o) => o.value === String(value))?.label ?? String(value);\n }\n return value;\n })();\n\n // Plain-text form of the value, fed to the HoverTooltip so a value that's\n // clipped at the cell's max-width is still readable on hover. Best-effort\n // for custom `format` cells — those render arbitrary nodes, so the tooltip\n // falls back to the underlying value rather than extracting text from JSX.\n const isMulti = type === 'multiselect';\n const isSelect = type === 'select';\n const isDate = type === 'date';\n const titleText = isEmpty\n ? placeholder ?? ''\n : isMulti\n ? Array.isArray(value)\n ? (options ?? [])\n .filter((o) => (value as string[]).includes(o.value))\n .map((o) => o.label)\n .join(', ')\n : ''\n : isSelect\n ? (options?.find((o) => o.value === String(value))?.label ?? String(value))\n : isDate\n ? displayFormatted || String(value)\n : String(value);\n\n return {\n isEditing,\n setIsEditing,\n draft,\n setDraft,\n submitting,\n setSubmitting,\n error,\n setError,\n open,\n setOpen,\n inputRef,\n wrapRef,\n parsedValue,\n displayFormatted,\n parseDraft,\n commitValue,\n commitDate,\n handleCommit,\n handlePick,\n handleClearDraft,\n handleCancel,\n handleKeyDown,\n editing,\n dateHint,\n isEmpty,\n display,\n isMulti,\n isSelect,\n isDate,\n titleText,\n shownError,\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/ui/page-shell/page-shell.tsx"],"sourcesContent":["import { cn } from '@/helpers';\n\nimport type { HTMLAttributes, ReactNode } from 'react';\n\nexport type PageShellVariant = 'standard' | 'canvas';\n\nexport interface PageShellProps extends HTMLAttributes<HTMLDivElement> {\n /**\n * Layout variant — controls the content area only. The sidebar and top bar\n * always render whatever you pass.\n *\n * - `standard`: padded, scrollable content area for normal product pages.\n * - `canvas`: full-bleed, non-scrolling content area for diagram surfaces\n * and lifecycle modelers. Pass a lighter context-specific top bar\n * (Back + title + diagram actions) instead of the full AppTopBar.\n */\n variant?: PageShellVariant;\n /** Left navigation. Typically <AppSidebar /> from @viax/uxm/ui. */\n sidebar?: ReactNode;\n /** Top header — anything you want, or omit/null to render no header. */\n topBar?: ReactNode;\n /** Page content. */\n children: ReactNode;\n}\n\nexport function PageShell({\n variant = 'standard',\n sidebar,\n topBar,\n children,\n className,\n ...rest\n}: PageShellProps) {\n return (\n <div className={cn('uxm-page-shell', `uxm-page-shell--${variant}`, className)} {...rest}>\n {sidebar && <div className=\"uxm-page-shell__sidebar\">{sidebar}</div>}\n <div className=\"uxm-page-shell__body\">\n {topBar && <div className=\"uxm-page-shell__top-bar\">{topBar}</div>}\n <main className=\"uxm-page-shell__content\">{children}</main>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAmCkB;AAnClB,qBAAmB;AAyBZ,SAAS,UAAU;AAAA,EACxB,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAmB;AACjB,SACE,6CAAC,SAAI,eAAW,mBAAG,kBAAkB,mBAAmB,OAAO,IAAI,SAAS,GAAI,GAAG,MAChF;AAAA,eAAW,4CAAC,SAAI,WAAU,2BAA2B,mBAAQ;AAAA,IAC9D,6CAAC,SAAI,WAAU,wBACZ;AAAA,gBAAU,4CAAC,SAAI,WAAU,2BAA2B,kBAAO;AAAA,MAC5D,4CAAC,UAAK,WAAU,2BAA2B,UAAS;AAAA,OACtD;AAAA,KACF;AAEJ;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/ui/page-shell/page-shell.tsx"],"sourcesContent":["import { cn } from '@/helpers';\n\nimport type { HTMLAttributes, ReactNode } from 'react';\n\nexport type PageShellVariant = 'standard' | 'canvas';\n\nexport interface PageShellProps extends HTMLAttributes<HTMLDivElement> {\n /**\n * Layout variant — controls the content area only. The sidebar and top bar\n * always render whatever you pass.\n *\n * - `standard`: padded, scrollable content area for normal product pages.\n * - `canvas`: full-bleed, non-scrolling content area for diagram surfaces\n * and lifecycle modelers. Pass a lighter context-specific top bar\n * (Back + title + diagram actions) instead of the full AppTopBar.\n */\n variant?: PageShellVariant;\n /** Left navigation. Typically <AppSidebar /> from @viax.io/uxm/ui. */\n sidebar?: ReactNode;\n /** Top header — anything you want, or omit/null to render no header. */\n topBar?: ReactNode;\n /** Page content. */\n children: ReactNode;\n}\n\nexport function PageShell({\n variant = 'standard',\n sidebar,\n topBar,\n children,\n className,\n ...rest\n}: PageShellProps) {\n return (\n <div className={cn('uxm-page-shell', `uxm-page-shell--${variant}`, className)} {...rest}>\n {sidebar && <div className=\"uxm-page-shell__sidebar\">{sidebar}</div>}\n <div className=\"uxm-page-shell__body\">\n {topBar && <div className=\"uxm-page-shell__top-bar\">{topBar}</div>}\n <main className=\"uxm-page-shell__content\">{children}</main>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAmCkB;AAnClB,qBAAmB;AAyBZ,SAAS,UAAU;AAAA,EACxB,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAmB;AACjB,SACE,6CAAC,SAAI,eAAW,mBAAG,kBAAkB,mBAAmB,OAAO,IAAI,SAAS,GAAI,GAAG,MAChF;AAAA,eAAW,4CAAC,SAAI,WAAU,2BAA2B,mBAAQ;AAAA,IAC9D,6CAAC,SAAI,WAAU,wBACZ;AAAA,gBAAU,4CAAC,SAAI,WAAU,2BAA2B,kBAAO;AAAA,MAC5D,4CAAC,UAAK,WAAU,2BAA2B,UAAS;AAAA,OACtD;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -11,7 +11,7 @@ export interface PageShellProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
11
11
|
* (Back + title + diagram actions) instead of the full AppTopBar.
|
|
12
12
|
*/
|
|
13
13
|
variant?: PageShellVariant;
|
|
14
|
-
/** Left navigation. Typically <AppSidebar /> from @viax/uxm/ui. */
|
|
14
|
+
/** Left navigation. Typically <AppSidebar /> from @viax.io/uxm/ui. */
|
|
15
15
|
sidebar?: ReactNode;
|
|
16
16
|
/** Top header — anything you want, or omit/null to render no header. */
|
|
17
17
|
topBar?: ReactNode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page-shell.d.ts","sourceRoot":"","sources":["../../../src/ui/page-shell/page-shell.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvD,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,QAAQ,CAAC;AAErD,MAAM,WAAW,cAAe,SAAQ,cAAc,CAAC,cAAc,CAAC;IACpE;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,
|
|
1
|
+
{"version":3,"file":"page-shell.d.ts","sourceRoot":"","sources":["../../../src/ui/page-shell/page-shell.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvD,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,QAAQ,CAAC;AAErD,MAAM,WAAW,cAAe,SAAQ,cAAc,CAAC,cAAc,CAAC;IACpE;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,sEAAsE;IACtE,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,wEAAwE;IACxE,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,oBAAoB;IACpB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,SAAS,CAAC,EACxB,OAAoB,EACpB,OAAO,EACP,MAAM,EACN,QAAQ,EACR,SAAS,EACT,GAAG,IAAI,EACR,EAAE,cAAc,+BAUhB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/ui/page-shell/page-shell.tsx"],"sourcesContent":["import { cn } from '@/helpers';\n\nimport type { HTMLAttributes, ReactNode } from 'react';\n\nexport type PageShellVariant = 'standard' | 'canvas';\n\nexport interface PageShellProps extends HTMLAttributes<HTMLDivElement> {\n /**\n * Layout variant — controls the content area only. The sidebar and top bar\n * always render whatever you pass.\n *\n * - `standard`: padded, scrollable content area for normal product pages.\n * - `canvas`: full-bleed, non-scrolling content area for diagram surfaces\n * and lifecycle modelers. Pass a lighter context-specific top bar\n * (Back + title + diagram actions) instead of the full AppTopBar.\n */\n variant?: PageShellVariant;\n /** Left navigation. Typically <AppSidebar /> from @viax/uxm/ui. */\n sidebar?: ReactNode;\n /** Top header — anything you want, or omit/null to render no header. */\n topBar?: ReactNode;\n /** Page content. */\n children: ReactNode;\n}\n\nexport function PageShell({\n variant = 'standard',\n sidebar,\n topBar,\n children,\n className,\n ...rest\n}: PageShellProps) {\n return (\n <div className={cn('uxm-page-shell', `uxm-page-shell--${variant}`, className)} {...rest}>\n {sidebar && <div className=\"uxm-page-shell__sidebar\">{sidebar}</div>}\n <div className=\"uxm-page-shell__body\">\n {topBar && <div className=\"uxm-page-shell__top-bar\">{topBar}</div>}\n <main className=\"uxm-page-shell__content\">{children}</main>\n </div>\n </div>\n );\n}\n"],"mappings":"AAmCkB,cACZ,YADY;AAnClB,SAAS,UAAU;AAyBZ,SAAS,UAAU;AAAA,EACxB,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAmB;AACjB,SACE,qBAAC,SAAI,WAAW,GAAG,kBAAkB,mBAAmB,OAAO,IAAI,SAAS,GAAI,GAAG,MAChF;AAAA,eAAW,oBAAC,SAAI,WAAU,2BAA2B,mBAAQ;AAAA,IAC9D,qBAAC,SAAI,WAAU,wBACZ;AAAA,gBAAU,oBAAC,SAAI,WAAU,2BAA2B,kBAAO;AAAA,MAC5D,oBAAC,UAAK,WAAU,2BAA2B,UAAS;AAAA,OACtD;AAAA,KACF;AAEJ;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/ui/page-shell/page-shell.tsx"],"sourcesContent":["import { cn } from '@/helpers';\n\nimport type { HTMLAttributes, ReactNode } from 'react';\n\nexport type PageShellVariant = 'standard' | 'canvas';\n\nexport interface PageShellProps extends HTMLAttributes<HTMLDivElement> {\n /**\n * Layout variant — controls the content area only. The sidebar and top bar\n * always render whatever you pass.\n *\n * - `standard`: padded, scrollable content area for normal product pages.\n * - `canvas`: full-bleed, non-scrolling content area for diagram surfaces\n * and lifecycle modelers. Pass a lighter context-specific top bar\n * (Back + title + diagram actions) instead of the full AppTopBar.\n */\n variant?: PageShellVariant;\n /** Left navigation. Typically <AppSidebar /> from @viax.io/uxm/ui. */\n sidebar?: ReactNode;\n /** Top header — anything you want, or omit/null to render no header. */\n topBar?: ReactNode;\n /** Page content. */\n children: ReactNode;\n}\n\nexport function PageShell({\n variant = 'standard',\n sidebar,\n topBar,\n children,\n className,\n ...rest\n}: PageShellProps) {\n return (\n <div className={cn('uxm-page-shell', `uxm-page-shell--${variant}`, className)} {...rest}>\n {sidebar && <div className=\"uxm-page-shell__sidebar\">{sidebar}</div>}\n <div className=\"uxm-page-shell__body\">\n {topBar && <div className=\"uxm-page-shell__top-bar\">{topBar}</div>}\n <main className=\"uxm-page-shell__content\">{children}</main>\n </div>\n </div>\n );\n}\n"],"mappings":"AAmCkB,cACZ,YADY;AAnClB,SAAS,UAAU;AAyBZ,SAAS,UAAU;AAAA,EACxB,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAmB;AACjB,SACE,qBAAC,SAAI,WAAW,GAAG,kBAAkB,mBAAmB,OAAO,IAAI,SAAS,GAAI,GAAG,MAChF;AAAA,eAAW,oBAAC,SAAI,WAAU,2BAA2B,mBAAQ;AAAA,IAC9D,qBAAC,SAAI,WAAU,wBACZ;AAAA,gBAAU,oBAAC,SAAI,WAAU,2BAA2B,kBAAO;AAAA,MAC5D,oBAAC,UAAK,WAAU,2BAA2B,UAAS;AAAA,OACtD;AAAA,KACF;AAEJ;","names":[]}
|
package/dist/ui/styles.css
CHANGED
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@viax.io/uxm",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.40.1",
|
|
4
4
|
"description": "Viax UXM — React 19 UI primitives and design tokens.",
|
|
5
|
-
"license": "
|
|
5
|
+
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
7
7
|
"type": "module",
|
|
8
8
|
"sideEffects": [
|
|
@@ -72,8 +72,8 @@
|
|
|
72
72
|
"test:ci": "vitest run --reporter=default --reporter=junit --outputFile=junit-report.xml",
|
|
73
73
|
"audit:report": "npm audit --registry=https://registry.npmjs.org",
|
|
74
74
|
"audit:ci": "npm audit --registry=https://registry.npmjs.org --audit-level=critical",
|
|
75
|
-
"check:lock": "node scripts/check-lock-nexus.mjs",
|
|
76
75
|
"commit": "cz",
|
|
76
|
+
"prepare": "husky",
|
|
77
77
|
"prepublishOnly": "npm run build",
|
|
78
78
|
"check:type-scale": "node scripts/sweep-type-scale.mjs --check",
|
|
79
79
|
"check:tokens": "node scripts/check-token-parity.mjs",
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
102
102
|
"@semantic-release/exec": "^7.1.0",
|
|
103
103
|
"@semantic-release/git": "^10.0.1",
|
|
104
|
-
"@semantic-release/
|
|
104
|
+
"@semantic-release/github": "^12.0.9",
|
|
105
105
|
"@semantic-release/npm": "^13.1.5",
|
|
106
106
|
"@semantic-release/release-notes-generator": "^14.1.1",
|
|
107
107
|
"@tailwindcss/cli": "^4.0.0",
|
|
@@ -141,7 +141,7 @@
|
|
|
141
141
|
},
|
|
142
142
|
"repository": {
|
|
143
143
|
"type": "git",
|
|
144
|
-
"url": "https://
|
|
144
|
+
"url": "https://github.com/viax-io/uxm.git"
|
|
145
145
|
},
|
|
146
146
|
"engines": {
|
|
147
147
|
"node": ">=20"
|