@ukiahinsure/a2ui-react-adapter 0.1.15 → 0.1.17
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 +162 -65
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -19,48 +19,153 @@ import {
|
|
|
19
19
|
} from "@a2ui/web_core/v0_9";
|
|
20
20
|
|
|
21
21
|
// src/DateTimeInput.tsx
|
|
22
|
-
import { useId, useLayoutEffect, useRef } from "react";
|
|
22
|
+
import { useId, useLayoutEffect, useRef, useState } from "react";
|
|
23
23
|
import { createComponentImplementation } from "@a2ui/react/v0_9";
|
|
24
24
|
import { DateTimeInputApi } from "@a2ui/web_core/v0_9/basic_catalog";
|
|
25
25
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
26
|
-
var
|
|
27
|
-
|
|
28
|
-
(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
26
|
+
var inputStyle = {
|
|
27
|
+
backgroundColor: "var(--a2ui-datetimeinput-background, var(--a2ui-color-input, #fff))",
|
|
28
|
+
color: "var(--a2ui-datetimeinput-color, var(--a2ui-color-on-input, #333))",
|
|
29
|
+
border: "var(--a2ui-datetimeinput-border, var(--a2ui-border))",
|
|
30
|
+
borderRadius: "var(--a2ui-datetimeinput-border-radius, var(--a2ui-border-radius))",
|
|
31
|
+
padding: "var(--a2ui-datetimeinput-padding, var(--a2ui-spacing-s))",
|
|
32
|
+
boxSizing: "border-box"
|
|
33
|
+
};
|
|
34
|
+
var labelStyle = { fontSize: "var(--a2ui-label-font-size, var(--a2ui-font-size-s))", fontWeight: "bold" };
|
|
35
|
+
function parseDateDraft(raw) {
|
|
36
|
+
const value = raw.trim();
|
|
37
|
+
const iso = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
|
|
38
|
+
const us = /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/.exec(value);
|
|
39
|
+
if (!iso && !us) return null;
|
|
40
|
+
const year = Number(iso ? iso[1] : us[3]);
|
|
41
|
+
const month = Number(iso ? iso[2] : us[1]);
|
|
42
|
+
const day = Number(iso ? iso[3] : us[2]);
|
|
43
|
+
const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
44
|
+
const days = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
45
|
+
if (year < 1 || month < 1 || month > 12 || day < 1 || day > days[month - 1]) return null;
|
|
46
|
+
return `${String(year).padStart(4, "0")}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
|
47
|
+
}
|
|
48
|
+
function displayDate(value) {
|
|
49
|
+
const iso = parseDateDraft(value);
|
|
50
|
+
return iso ? `${iso.slice(5, 7)}/${iso.slice(8, 10)}/${iso.slice(0, 4)}` : value;
|
|
51
|
+
}
|
|
52
|
+
function DateOnlyInput(props) {
|
|
53
|
+
const id = useId();
|
|
54
|
+
const [draft, setDraft] = useState(() => displayDate(props.value || ""));
|
|
55
|
+
const [pickerOpen, setPickerOpen] = useState(false);
|
|
56
|
+
const published = useRef(props.value || "");
|
|
57
|
+
const inputRef = useRef(null);
|
|
58
|
+
const pickerRef = useRef(null);
|
|
59
|
+
const min = typeof props.min === "string" ? props.min : void 0;
|
|
60
|
+
const max = typeof props.max === "string" ? props.max : void 0;
|
|
61
|
+
const parsed = parseDateDraft(draft);
|
|
62
|
+
useLayoutEffect(() => {
|
|
63
|
+
const incoming = props.value || "";
|
|
64
|
+
if (incoming !== published.current) {
|
|
65
|
+
published.current = incoming;
|
|
66
|
+
setDraft(displayDate(incoming));
|
|
67
|
+
}
|
|
68
|
+
}, [props.value]);
|
|
69
|
+
useLayoutEffect(() => {
|
|
70
|
+
const message = draft.trim() && !parsed ? "Enter a valid date in MM/DD/YYYY format." : parsed && min && parsed < min ? `Enter a date on or after ${displayDate(min)}.` : parsed && max && parsed > max ? `Enter a date on or before ${displayDate(max)}.` : "";
|
|
71
|
+
inputRef.current?.setCustomValidity(message);
|
|
72
|
+
}, [draft, parsed, min, max]);
|
|
73
|
+
const update = (raw) => {
|
|
74
|
+
setDraft(raw);
|
|
75
|
+
const value = parseDateDraft(raw) || "";
|
|
76
|
+
published.current = value;
|
|
77
|
+
props.setValue(value);
|
|
78
|
+
};
|
|
79
|
+
return /* @__PURE__ */ jsxs("div", { "data-a2ui-date-editor": "true", style: { display: "flex", flexDirection: "column", gap: "var(--a2ui-spacing-xs, 0.25rem)" }, children: [
|
|
80
|
+
props.label && /* @__PURE__ */ jsx("label", { htmlFor: id, style: labelStyle, children: props.label }),
|
|
81
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "0.25rem", position: "relative" }, children: [
|
|
41
82
|
/* @__PURE__ */ jsx(
|
|
42
83
|
"input",
|
|
43
84
|
{
|
|
44
85
|
ref: inputRef,
|
|
45
86
|
id,
|
|
46
|
-
type,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
87
|
+
type: "text",
|
|
88
|
+
inputMode: "numeric",
|
|
89
|
+
autoComplete: "off",
|
|
90
|
+
"data-a2ui-date-text-draft": "true",
|
|
91
|
+
placeholder: "MM/DD/YYYY",
|
|
92
|
+
value: draft,
|
|
93
|
+
onChange: (event) => update(event.currentTarget.value),
|
|
94
|
+
onBlur: () => {
|
|
95
|
+
if (parsed) setDraft(displayDate(parsed));
|
|
96
|
+
},
|
|
97
|
+
style: { ...inputStyle, minWidth: 0, flex: 1 }
|
|
98
|
+
}
|
|
99
|
+
),
|
|
100
|
+
/* @__PURE__ */ jsx(
|
|
101
|
+
"button",
|
|
102
|
+
{
|
|
103
|
+
type: "button",
|
|
104
|
+
"aria-label": `Choose ${props.label || "date"} from calendar`,
|
|
105
|
+
style: inputStyle,
|
|
106
|
+
onClick: () => {
|
|
107
|
+
const picker = pickerRef.current;
|
|
108
|
+
if (picker && typeof picker.showPicker === "function") {
|
|
109
|
+
try {
|
|
110
|
+
picker.showPicker();
|
|
111
|
+
return;
|
|
112
|
+
} catch {
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
setPickerOpen(true);
|
|
116
|
+
},
|
|
117
|
+
children: "\u25A6"
|
|
118
|
+
}
|
|
119
|
+
),
|
|
120
|
+
/* @__PURE__ */ jsx(
|
|
121
|
+
"input",
|
|
122
|
+
{
|
|
123
|
+
ref: pickerRef,
|
|
124
|
+
type: "date",
|
|
125
|
+
"aria-label": `${props.label || "Date"} calendar`,
|
|
126
|
+
tabIndex: pickerOpen ? 0 : -1,
|
|
127
|
+
"aria-hidden": !pickerOpen,
|
|
128
|
+
"data-a2ui-calendar-input": "true",
|
|
129
|
+
value: parsed || "",
|
|
130
|
+
min,
|
|
131
|
+
max,
|
|
132
|
+
onChange: (event) => {
|
|
133
|
+
update(displayDate(event.currentTarget.value));
|
|
134
|
+
setPickerOpen(false);
|
|
135
|
+
},
|
|
136
|
+
style: pickerOpen ? inputStyle : { position: "absolute", opacity: 0, pointerEvents: "none", width: 1, height: 1, bottom: 0, right: 0 }
|
|
60
137
|
}
|
|
61
138
|
)
|
|
62
|
-
] })
|
|
63
|
-
}
|
|
139
|
+
] })
|
|
140
|
+
] });
|
|
141
|
+
}
|
|
142
|
+
function NativeTimeInput(props) {
|
|
143
|
+
const id = useId();
|
|
144
|
+
const inputRef = useRef(null);
|
|
145
|
+
const initialValue = useRef(props.value || "");
|
|
146
|
+
useLayoutEffect(() => {
|
|
147
|
+
if (inputRef.current && inputRef.current.value !== (props.value || "")) inputRef.current.value = props.value || "";
|
|
148
|
+
}, [props.value]);
|
|
149
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
150
|
+
props.label && /* @__PURE__ */ jsx("label", { htmlFor: id, style: labelStyle, children: props.label }),
|
|
151
|
+
/* @__PURE__ */ jsx(
|
|
152
|
+
"input",
|
|
153
|
+
{
|
|
154
|
+
ref: inputRef,
|
|
155
|
+
id,
|
|
156
|
+
type: !props.enableDate && props.enableTime ? "time" : "datetime-local",
|
|
157
|
+
defaultValue: initialValue.current,
|
|
158
|
+
onChange: (event) => props.setValue(event.currentTarget.value),
|
|
159
|
+
min: typeof props.min === "string" ? props.min : void 0,
|
|
160
|
+
max: typeof props.max === "string" ? props.max : void 0,
|
|
161
|
+
style: inputStyle
|
|
162
|
+
}
|
|
163
|
+
)
|
|
164
|
+
] });
|
|
165
|
+
}
|
|
166
|
+
var DateTimeInput = createComponentImplementation(
|
|
167
|
+
DateTimeInputApi,
|
|
168
|
+
({ props }) => props.enableDate && !props.enableTime ? /* @__PURE__ */ jsx(DateOnlyInput, { ...props }) : /* @__PURE__ */ jsx(NativeTimeInput, { ...props })
|
|
64
169
|
);
|
|
65
170
|
|
|
66
171
|
// src/adapter.ts
|
|
@@ -1068,7 +1173,7 @@ function mergePersistedListRowsIntoRoot(root, persistedRowsByPath, deletedItemId
|
|
|
1068
1173
|
optimisticEffectiveRows,
|
|
1069
1174
|
deletedItemIdsByPath.get(listPath)
|
|
1070
1175
|
);
|
|
1071
|
-
const normalizedRows = normalizeListRows(listPath, visibleRows
|
|
1176
|
+
const normalizedRows = normalizeListRows(listPath, visibleRows);
|
|
1072
1177
|
merged[listPath] = cloneJsonArray(normalizedRows);
|
|
1073
1178
|
setNestedValue(merged, listPath, cloneJsonArray(normalizedRows));
|
|
1074
1179
|
}
|
|
@@ -1122,21 +1227,17 @@ function rememberPersistedListRows(persistedRowsByPath, deletedItemIdsByPath, ro
|
|
|
1122
1227
|
);
|
|
1123
1228
|
persistedRowsByPath.set(
|
|
1124
1229
|
listPath,
|
|
1125
|
-
normalizeListRows(listPath, visibleRows
|
|
1230
|
+
normalizeListRows(listPath, visibleRows)
|
|
1126
1231
|
);
|
|
1127
1232
|
}
|
|
1128
1233
|
}
|
|
1129
|
-
function normalizeListRows(listPath, rows
|
|
1234
|
+
function normalizeListRows(listPath, rows) {
|
|
1130
1235
|
const clonedRows = cloneJsonArray(rows);
|
|
1131
1236
|
if (listPath !== "needs.providers") {
|
|
1132
1237
|
return clonedRows;
|
|
1133
1238
|
}
|
|
1134
|
-
const rootProvider = root === void 0 ? void 0 : providerRowFromAliases(root);
|
|
1135
|
-
if (clonedRows.length === 0 && rootProvider !== void 0) {
|
|
1136
|
-
return [normalizeProviderRow(rootProvider)];
|
|
1137
|
-
}
|
|
1138
1239
|
return clonedRows.map(
|
|
1139
|
-
(row) => isRecord(row) ? normalizeProviderRow(row
|
|
1240
|
+
(row) => isRecord(row) ? normalizeProviderRow(row) : row
|
|
1140
1241
|
);
|
|
1141
1242
|
}
|
|
1142
1243
|
function normalizeProviderRow(row, fallback) {
|
|
@@ -1159,24 +1260,12 @@ function normalizeProviderRow(row, fallback) {
|
|
|
1159
1260
|
}
|
|
1160
1261
|
return normalized;
|
|
1161
1262
|
}
|
|
1162
|
-
function providerRowFromAliases(source) {
|
|
1163
|
-
const name = firstProviderName(source);
|
|
1164
|
-
const location = firstProviderLocation(source);
|
|
1165
|
-
if (name === void 0 && location === void 0) {
|
|
1166
|
-
return void 0;
|
|
1167
|
-
}
|
|
1168
|
-
return {
|
|
1169
|
-
itemId: "voice-primary-care-provider",
|
|
1170
|
-
...name === void 0 ? {} : { primary_care_provider: name },
|
|
1171
|
-
...location === void 0 ? {} : { primary_care_provider_location: location }
|
|
1172
|
-
};
|
|
1173
|
-
}
|
|
1174
1263
|
function submittedProviderRowFromEvent(event) {
|
|
1175
1264
|
if (event.actionName !== "flow.submit") {
|
|
1176
1265
|
return void 0;
|
|
1177
1266
|
}
|
|
1178
1267
|
const listPath = event.context.listPath;
|
|
1179
|
-
if (listPath !== "needs.providers"
|
|
1268
|
+
if (listPath !== "needs.providers") {
|
|
1180
1269
|
return void 0;
|
|
1181
1270
|
}
|
|
1182
1271
|
const name = firstProviderName(event.context);
|
|
@@ -1703,7 +1792,7 @@ function A2UISurfaceHost({
|
|
|
1703
1792
|
resolvedSurfaceMetadata?.fieldInteractions ?? []
|
|
1704
1793
|
),
|
|
1705
1794
|
annotateActionControls(root),
|
|
1706
|
-
suppressSkipForMandatoryFields(root, structure.fields),
|
|
1795
|
+
suppressSkipForMandatoryFields(root, structure.fields, resolvedSurfaceMetadata?.fieldInteractions ?? []),
|
|
1707
1796
|
blockInvalidActionControls(root),
|
|
1708
1797
|
setPendingGroupsDisabled(
|
|
1709
1798
|
root,
|
|
@@ -1859,14 +1948,18 @@ function bindFieldInteractionGroups(root, groups, editing, controller) {
|
|
|
1859
1948
|
const field = control.dataset.a2uiField;
|
|
1860
1949
|
const group = field === void 0 ? void 0 : groupByField.get(field);
|
|
1861
1950
|
if (group !== void 0) {
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1951
|
+
const dateEditor = control.closest("[data-a2ui-date-editor]");
|
|
1952
|
+
const targets = dateEditor ? [control, ...dateEditor.querySelectorAll("button, input[data-a2ui-calendar-input]")] : [control];
|
|
1953
|
+
for (const target of targets) {
|
|
1954
|
+
originalGroupAttributes.set(target, {
|
|
1955
|
+
groupId: target.dataset.a2uiGroupId,
|
|
1956
|
+
groupMode: target.dataset.a2uiGroupMode,
|
|
1957
|
+
editState: target.dataset.a2uiEditState
|
|
1958
|
+
});
|
|
1959
|
+
target.dataset.a2uiGroupId = group.groupId;
|
|
1960
|
+
target.dataset.a2uiGroupMode = group.mode;
|
|
1961
|
+
target.dataset.a2uiEditState = groupEditState(group.groupId, editing);
|
|
1962
|
+
}
|
|
1870
1963
|
}
|
|
1871
1964
|
}
|
|
1872
1965
|
if (controller === void 0) {
|
|
@@ -1899,7 +1992,8 @@ function bindFieldInteractionGroups(root, groups, editing, controller) {
|
|
|
1899
1992
|
}
|
|
1900
1993
|
queueMicrotask(() => {
|
|
1901
1994
|
if (interactionGroupId(root, root.ownerDocument.activeElement) !== groupId) {
|
|
1902
|
-
|
|
1995
|
+
const invalidDate = [...root.querySelectorAll("input[data-a2ui-date-text-draft]")].some((input) => input.dataset.a2uiGroupId === groupId && !input.checkValidity());
|
|
1996
|
+
if (!invalidDate) controller.commitFieldGroup(groupId);
|
|
1903
1997
|
}
|
|
1904
1998
|
});
|
|
1905
1999
|
};
|
|
@@ -2039,8 +2133,11 @@ function annotateActionControls(root) {
|
|
|
2039
2133
|
}
|
|
2040
2134
|
};
|
|
2041
2135
|
}
|
|
2042
|
-
function suppressSkipForMandatoryFields(root, fields) {
|
|
2043
|
-
const
|
|
2136
|
+
function suppressSkipForMandatoryFields(root, fields, groups) {
|
|
2137
|
+
const currentFields = new Set(groups.filter((group) => group.mode === "current").flatMap((group) => group.fields.map((binding) => binding.field)));
|
|
2138
|
+
const requiredFields = fields.filter(
|
|
2139
|
+
(field) => field.required && (groups.length === 0 || currentFields.has(field.field)) && !field.componentId.includes(":list-new:field:") && !field.componentId.includes(":list-row:field:")
|
|
2140
|
+
);
|
|
2044
2141
|
if (requiredFields.length === 0 || requiredFields.every((field) => isOptionalCoverageField(field.field))) {
|
|
2045
2142
|
return () => void 0;
|
|
2046
2143
|
}
|