@ukiahinsure/a2ui-react-adapter 0.1.16 → 0.1.18

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 CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  } from "./chunk-AOBRTDAU.js";
11
11
 
12
12
  // src/adapter.ts
13
- import { basicCatalog } from "@a2ui/react/v0_9";
13
+ import { basicCatalog as basicCatalog2 } from "@a2ui/react/v0_9";
14
14
  import {
15
15
  A2uiError,
16
16
  Catalog,
@@ -18,11 +18,44 @@ import {
18
18
  MessageProcessor
19
19
  } from "@a2ui/web_core/v0_9";
20
20
 
21
+ // src/HospitalChoice.tsx
22
+ import { useId } from "react";
23
+ import { basicCatalog, createComponentImplementation } from "@a2ui/react/v0_9";
24
+ import { ChoicePickerApi } from "@a2ui/web_core/v0_9/basic_catalog";
25
+ import { jsx, jsxs } from "react/jsx-runtime";
26
+ var OriginalChoice = basicCatalog.components.get("ChoicePicker").render;
27
+ var HospitalChoice = createComponentImplementation(ChoicePickerApi, ({ props, context, buildChild }) => {
28
+ const id = useId();
29
+ if (props.label !== "hospital_preference") {
30
+ return /* @__PURE__ */ jsx(OriginalChoice, { context, buildChild });
31
+ }
32
+ const selected = Array.isArray(props.value) ? props.value[0] ?? "" : "";
33
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.25rem" }, children: [
34
+ /* @__PURE__ */ jsx("label", { htmlFor: id, children: "Hospital preference" }),
35
+ /* @__PURE__ */ jsxs(
36
+ "select",
37
+ {
38
+ id,
39
+ "data-a2ui-field": "hospital_preference",
40
+ value: selected,
41
+ required: true,
42
+ style: { padding: "0.5rem", border: "var(--a2ui-border)", borderRadius: "var(--a2ui-border-radius)" },
43
+ onChange: (event) => props.setValue(event.currentTarget.value ? [event.currentTarget.value] : []),
44
+ children: [
45
+ /* @__PURE__ */ jsx("option", { value: "", children: "Choose Prefer or Avoid" }),
46
+ /* @__PURE__ */ jsx("option", { value: "Prefer", children: "Prefer" }),
47
+ /* @__PURE__ */ jsx("option", { value: "Avoid", children: "Avoid" })
48
+ ]
49
+ }
50
+ )
51
+ ] });
52
+ });
53
+
21
54
  // src/DateTimeInput.tsx
22
- import { useId, useLayoutEffect, useRef, useState } from "react";
23
- import { createComponentImplementation } from "@a2ui/react/v0_9";
55
+ import { useId as useId2, useLayoutEffect, useRef, useState } from "react";
56
+ import { createComponentImplementation as createComponentImplementation2 } from "@a2ui/react/v0_9";
24
57
  import { DateTimeInputApi } from "@a2ui/web_core/v0_9/basic_catalog";
25
- import { jsx, jsxs } from "react/jsx-runtime";
58
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
26
59
  var inputStyle = {
27
60
  backgroundColor: "var(--a2ui-datetimeinput-background, var(--a2ui-color-input, #fff))",
28
61
  color: "var(--a2ui-datetimeinput-color, var(--a2ui-color-on-input, #333))",
@@ -50,7 +83,7 @@ function displayDate(value) {
50
83
  return iso ? `${iso.slice(5, 7)}/${iso.slice(8, 10)}/${iso.slice(0, 4)}` : value;
51
84
  }
52
85
  function DateOnlyInput(props) {
53
- const id = useId();
86
+ const id = useId2();
54
87
  const [draft, setDraft] = useState(() => displayDate(props.value || ""));
55
88
  const [pickerOpen, setPickerOpen] = useState(false);
56
89
  const published = useRef(props.value || "");
@@ -76,10 +109,10 @@ function DateOnlyInput(props) {
76
109
  published.current = value;
77
110
  props.setValue(value);
78
111
  };
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: [
82
- /* @__PURE__ */ jsx(
112
+ return /* @__PURE__ */ jsxs2("div", { "data-a2ui-date-editor": "true", style: { display: "flex", flexDirection: "column", gap: "var(--a2ui-spacing-xs, 0.25rem)" }, children: [
113
+ props.label && /* @__PURE__ */ jsx2("label", { htmlFor: id, style: labelStyle, children: props.label }),
114
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: "0.25rem", position: "relative" }, children: [
115
+ /* @__PURE__ */ jsx2(
83
116
  "input",
84
117
  {
85
118
  ref: inputRef,
@@ -97,7 +130,7 @@ function DateOnlyInput(props) {
97
130
  style: { ...inputStyle, minWidth: 0, flex: 1 }
98
131
  }
99
132
  ),
100
- /* @__PURE__ */ jsx(
133
+ /* @__PURE__ */ jsx2(
101
134
  "button",
102
135
  {
103
136
  type: "button",
@@ -117,7 +150,7 @@ function DateOnlyInput(props) {
117
150
  children: "\u25A6"
118
151
  }
119
152
  ),
120
- /* @__PURE__ */ jsx(
153
+ /* @__PURE__ */ jsx2(
121
154
  "input",
122
155
  {
123
156
  ref: pickerRef,
@@ -140,15 +173,15 @@ function DateOnlyInput(props) {
140
173
  ] });
141
174
  }
142
175
  function NativeTimeInput(props) {
143
- const id = useId();
176
+ const id = useId2();
144
177
  const inputRef = useRef(null);
145
178
  const initialValue = useRef(props.value || "");
146
179
  useLayoutEffect(() => {
147
180
  if (inputRef.current && inputRef.current.value !== (props.value || "")) inputRef.current.value = props.value || "";
148
181
  }, [props.value]);
149
- return /* @__PURE__ */ jsxs("div", { children: [
150
- props.label && /* @__PURE__ */ jsx("label", { htmlFor: id, style: labelStyle, children: props.label }),
151
- /* @__PURE__ */ jsx(
182
+ return /* @__PURE__ */ jsxs2("div", { children: [
183
+ props.label && /* @__PURE__ */ jsx2("label", { htmlFor: id, style: labelStyle, children: props.label }),
184
+ /* @__PURE__ */ jsx2(
152
185
  "input",
153
186
  {
154
187
  ref: inputRef,
@@ -163,19 +196,19 @@ function NativeTimeInput(props) {
163
196
  )
164
197
  ] });
165
198
  }
166
- var DateTimeInput = createComponentImplementation(
199
+ var DateTimeInput = createComponentImplementation2(
167
200
  DateTimeInputApi,
168
- ({ props }) => props.enableDate && !props.enableTime ? /* @__PURE__ */ jsx(DateOnlyInput, { ...props }) : /* @__PURE__ */ jsx(NativeTimeInput, { ...props })
201
+ ({ props }) => props.enableDate && !props.enableTime ? /* @__PURE__ */ jsx2(DateOnlyInput, { ...props }) : /* @__PURE__ */ jsx2(NativeTimeInput, { ...props })
169
202
  );
170
203
 
171
204
  // src/adapter.ts
172
205
  var editableDateCatalog = new Catalog(
173
- basicCatalog.id,
174
- [...basicCatalog.components.values()].map(
175
- (component) => component.name === DateTimeInput.name ? DateTimeInput : component
206
+ basicCatalog2.id,
207
+ [...basicCatalog2.components.values()].map(
208
+ (component) => component.name === DateTimeInput.name ? DateTimeInput : component.name === HospitalChoice.name ? HospitalChoice : component
176
209
  ),
177
- [...basicCatalog.functions.values()],
178
- basicCatalog.themeSchema
210
+ [...basicCatalog2.functions.values()],
211
+ basicCatalog2.themeSchema
179
212
  );
180
213
  var RENDERER_NAME = "a2ui_react_adapter";
181
214
  var RENDERER_VERSION = "0.1.0";
@@ -203,7 +236,7 @@ var A2UIClientAdapter = class {
203
236
  }
204
237
  /** Catalog ids this adapter will announce in capability envelopes. */
205
238
  get supportedCatalogIds() {
206
- return [basicCatalog.id];
239
+ return [basicCatalog2.id];
207
240
  }
208
241
  get lastSeq() {
209
242
  return this.#lastSeq;
@@ -1173,7 +1206,7 @@ function mergePersistedListRowsIntoRoot(root, persistedRowsByPath, deletedItemId
1173
1206
  optimisticEffectiveRows,
1174
1207
  deletedItemIdsByPath.get(listPath)
1175
1208
  );
1176
- const normalizedRows = normalizeListRows(listPath, visibleRows, merged);
1209
+ const normalizedRows = normalizeListRows(listPath, visibleRows);
1177
1210
  merged[listPath] = cloneJsonArray(normalizedRows);
1178
1211
  setNestedValue(merged, listPath, cloneJsonArray(normalizedRows));
1179
1212
  }
@@ -1227,21 +1260,17 @@ function rememberPersistedListRows(persistedRowsByPath, deletedItemIdsByPath, ro
1227
1260
  );
1228
1261
  persistedRowsByPath.set(
1229
1262
  listPath,
1230
- normalizeListRows(listPath, visibleRows, root)
1263
+ normalizeListRows(listPath, visibleRows)
1231
1264
  );
1232
1265
  }
1233
1266
  }
1234
- function normalizeListRows(listPath, rows, root) {
1267
+ function normalizeListRows(listPath, rows) {
1235
1268
  const clonedRows = cloneJsonArray(rows);
1236
1269
  if (listPath !== "needs.providers") {
1237
1270
  return clonedRows;
1238
1271
  }
1239
- const rootProvider = root === void 0 ? void 0 : providerRowFromAliases(root);
1240
- if (clonedRows.length === 0 && rootProvider !== void 0) {
1241
- return [normalizeProviderRow(rootProvider)];
1242
- }
1243
1272
  return clonedRows.map(
1244
- (row) => isRecord(row) ? normalizeProviderRow(row, rootProvider) : row
1273
+ (row) => isRecord(row) ? normalizeProviderRow(row) : row
1245
1274
  );
1246
1275
  }
1247
1276
  function normalizeProviderRow(row, fallback) {
@@ -1264,24 +1293,12 @@ function normalizeProviderRow(row, fallback) {
1264
1293
  }
1265
1294
  return normalized;
1266
1295
  }
1267
- function providerRowFromAliases(source) {
1268
- const name = firstProviderName(source);
1269
- const location = firstProviderLocation(source);
1270
- if (name === void 0 && location === void 0) {
1271
- return void 0;
1272
- }
1273
- return {
1274
- itemId: "voice-primary-care-provider",
1275
- ...name === void 0 ? {} : { primary_care_provider: name },
1276
- ...location === void 0 ? {} : { primary_care_provider_location: location }
1277
- };
1278
- }
1279
1296
  function submittedProviderRowFromEvent(event) {
1280
1297
  if (event.actionName !== "flow.submit") {
1281
1298
  return void 0;
1282
1299
  }
1283
1300
  const listPath = event.context.listPath;
1284
- if (listPath !== "needs.providers" && !hasProviderSubmitFields(event.context)) {
1301
+ if (listPath !== "needs.providers") {
1285
1302
  return void 0;
1286
1303
  }
1287
1304
  const name = firstProviderName(event.context);
@@ -1730,13 +1747,13 @@ function classifyProcessingError(error) {
1730
1747
  // src/SurfaceHost.tsx
1731
1748
  import {
1732
1749
  Component,
1733
- useId as useId2,
1750
+ useId as useId3,
1734
1751
  useLayoutEffect as useLayoutEffect2,
1735
1752
  useRef as useRef2,
1736
1753
  useSyncExternalStore
1737
1754
  } from "react";
1738
1755
  import { A2uiSurface } from "@a2ui/react/v0_9";
1739
- import { jsx as jsx2 } from "react/jsx-runtime";
1756
+ import { jsx as jsx3 } from "react/jsx-runtime";
1740
1757
  function A2UISurfaceHost({
1741
1758
  adapter,
1742
1759
  surfaceId,
@@ -1763,7 +1780,7 @@ function A2UISurfaceHost({
1763
1780
  readSessionSnapshot,
1764
1781
  readSessionSnapshot
1765
1782
  );
1766
- const generatedId = useId2().replaceAll(":", "");
1783
+ const generatedId = useId3().replaceAll(":", "");
1767
1784
  const rootRef = useRef2(null);
1768
1785
  const lastFocusedState = useRef2(void 0);
1769
1786
  const lastViewedSection = useRef2(void 0);
@@ -1808,7 +1825,7 @@ function A2UISurfaceHost({
1808
1825
  resolvedSurfaceMetadata?.fieldInteractions ?? []
1809
1826
  ),
1810
1827
  annotateActionControls(root),
1811
- suppressSkipForMandatoryFields(root, structure.fields),
1828
+ suppressSkipForMandatoryFields(root, structure.fields, resolvedSurfaceMetadata?.fieldInteractions ?? []),
1812
1829
  blockInvalidActionControls(root),
1813
1830
  setPendingGroupsDisabled(
1814
1831
  root,
@@ -1876,9 +1893,9 @@ function A2UISurfaceHost({
1876
1893
  version
1877
1894
  ]);
1878
1895
  if (surface === void 0) {
1879
- return noSurfaceFallback ?? /* @__PURE__ */ jsx2("div", { "data-a2ui-host": "no-surface", role: "status", "aria-live": "polite", children: "Your conversation continues by voice." });
1896
+ return noSurfaceFallback ?? /* @__PURE__ */ jsx3("div", { "data-a2ui-host": "no-surface", role: "status", "aria-live": "polite", children: "Your conversation continues by voice." });
1880
1897
  }
1881
- return /* @__PURE__ */ jsx2(
1898
+ return /* @__PURE__ */ jsx3(
1882
1899
  "div",
1883
1900
  {
1884
1901
  ref: rootRef,
@@ -1891,13 +1908,13 @@ function A2UISurfaceHost({
1891
1908
  "aria-busy": busy || void 0,
1892
1909
  "aria-disabled": disabled || busy || void 0,
1893
1910
  tabIndex: -1,
1894
- children: /* @__PURE__ */ jsx2(
1911
+ children: /* @__PURE__ */ jsx3(
1895
1912
  SurfaceErrorBoundary,
1896
1913
  {
1897
1914
  adapter,
1898
1915
  surface,
1899
- fallback: errorFallback ?? /* @__PURE__ */ jsx2("div", { "data-a2ui-host": "render-error", role: "alert", children: "This step can't be shown right now. Please continue by voice." }),
1900
- children: /* @__PURE__ */ jsx2(A2uiSurface, { surface })
1916
+ fallback: errorFallback ?? /* @__PURE__ */ jsx3("div", { "data-a2ui-host": "render-error", role: "alert", children: "This step can't be shown right now. Please continue by voice." }),
1917
+ children: /* @__PURE__ */ jsx3(A2uiSurface, { surface })
1901
1918
  },
1902
1919
  surface.id
1903
1920
  )
@@ -2149,8 +2166,11 @@ function annotateActionControls(root) {
2149
2166
  }
2150
2167
  };
2151
2168
  }
2152
- function suppressSkipForMandatoryFields(root, fields) {
2153
- const requiredFields = fields.filter((field) => field.required);
2169
+ function suppressSkipForMandatoryFields(root, fields, groups) {
2170
+ const currentFields = new Set(groups.filter((group) => group.mode === "current").flatMap((group) => group.fields.map((binding) => binding.field)));
2171
+ const requiredFields = fields.filter(
2172
+ (field) => field.required && (groups.length === 0 || currentFields.has(field.field)) && !field.componentId.includes(":list-new:field:") && !field.componentId.includes(":list-row:field:")
2173
+ );
2154
2174
  if (requiredFields.length === 0 || requiredFields.every((field) => isOptionalCoverageField(field.field))) {
2155
2175
  return () => void 0;
2156
2176
  }
@@ -2188,7 +2208,7 @@ var SurfaceErrorBoundary = class extends Component {
2188
2208
  }
2189
2209
  render() {
2190
2210
  if (this.state.failedSurfaceId !== void 0) {
2191
- return /* @__PURE__ */ jsx2(
2211
+ return /* @__PURE__ */ jsx3(
2192
2212
  "div",
2193
2213
  {
2194
2214
  "data-a2ui-error-boundary": "true",
@@ -2406,7 +2426,7 @@ function blockInvalidActionControls(root) {
2406
2426
  };
2407
2427
  }
2408
2428
  function firstInvalidInput(root) {
2409
- return [...root.querySelectorAll("input[data-a2ui-field]")].filter((input) => input.type !== "radio" && !isControlDisabled(input)).find((input) => !input.checkValidity());
2429
+ return [...root.querySelectorAll("input[data-a2ui-field], select[data-a2ui-field]")].filter((input) => input.type !== "radio" && !isControlDisabled(input)).find((input) => !input.checkValidity());
2410
2430
  }
2411
2431
  function findFieldControls(root, field) {
2412
2432
  const labels = [...root.querySelectorAll("label")];
@@ -2880,11 +2900,23 @@ function renderSavedLoopEditInputs(editButton, row, orderedFields, summaryFields
2880
2900
  continue;
2881
2901
  }
2882
2902
  let input = value.querySelector(
2883
- "input[data-a2ui-loop-edit-input='true']"
2903
+ "[data-a2ui-loop-edit-input='true']"
2884
2904
  );
2885
2905
  if (input === null) {
2886
- input = row.ownerDocument.createElement("input");
2887
- input.type = "text";
2906
+ if (fieldName === "hospital_preference") {
2907
+ const select = row.ownerDocument.createElement("select");
2908
+ for (const choice of ["", "Prefer", "Avoid"]) {
2909
+ const option = row.ownerDocument.createElement("option");
2910
+ option.value = choice;
2911
+ option.textContent = choice || "Choose Prefer or Avoid";
2912
+ select.appendChild(option);
2913
+ }
2914
+ select.required = true;
2915
+ input = select;
2916
+ } else {
2917
+ input = row.ownerDocument.createElement("input");
2918
+ input.type = "text";
2919
+ }
2888
2920
  input.name = fieldName;
2889
2921
  input.setAttribute("aria-label", displayFieldLabel(fieldName));
2890
2922
  input.dataset.a2uiLoopEditInput = "true";
@@ -2910,7 +2942,7 @@ function renderSavedLoopEditInputs(editButton, row, orderedFields, summaryFields
2910
2942
  setText(editButton, "Save");
2911
2943
  if (focusFirstInput) {
2912
2944
  row.querySelector(
2913
- "input[data-a2ui-loop-edit-input='true']"
2945
+ "[data-a2ui-loop-edit-input='true']"
2914
2946
  )?.focus();
2915
2947
  }
2916
2948
  }
@@ -2932,7 +2964,7 @@ function savedLoopRowInputValues(orderedFields, summaryFields) {
2932
2964
  orderedFields.forEach((field, index) => {
2933
2965
  const fieldName = summaryFields[index];
2934
2966
  const input = field.querySelector(
2935
- "input[data-a2ui-loop-edit-input='true']"
2967
+ "[data-a2ui-loop-edit-input='true']"
2936
2968
  );
2937
2969
  const value = field.querySelector(
2938
2970
  "[data-a2ui-loop-cell='value']"
@@ -3014,7 +3046,7 @@ function normalizedLoopSummaryFields(fields) {
3014
3046
  if (hasPharmacyFields) {
3015
3047
  return pharmacyFields.filter((field) => withoutInternal.includes(field));
3016
3048
  }
3017
- const hospitalFields = ["hospital_name", "hospital_city_state"];
3049
+ const hospitalFields = ["hospital_name", "hospital_city_state", "hospital_preference"];
3018
3050
  const hasHospitalFields = hospitalFields.some(
3019
3051
  (field) => withoutInternal.includes(field)
3020
3052
  );
@@ -3130,7 +3162,8 @@ var LOOP_FIELD_LABELS = /* @__PURE__ */ new Map([
3130
3162
  ["pharmacy type", "pharmacy_type"],
3131
3163
  ["pharmacy address", "pharmacy_address"],
3132
3164
  ["hospital name", "hospital_name"],
3133
- ["hospital city state", "hospital_city_state"]
3165
+ ["hospital city state", "hospital_city_state"],
3166
+ ["hospital preference", "hospital_preference"]
3134
3167
  ]);
3135
3168
  function isSavedLoopSummary(element) {
3136
3169
  if (element.children.length > 0) {