@ukiahinsure/a2ui-react-adapter 0.1.14 → 0.1.16

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
@@ -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 DateTimeInput = createComponentImplementation(
27
- DateTimeInputApi,
28
- ({ props }) => {
29
- const id = useId();
30
- const inputRef = useRef(null);
31
- const initialValue = useRef(props.value || "");
32
- const type = props.enableDate && !props.enableTime ? "date" : !props.enableDate && props.enableTime ? "time" : "datetime-local";
33
- useLayoutEffect(() => {
34
- const input = inputRef.current;
35
- if (input && input.ownerDocument.activeElement !== input && input.value !== (props.value || "")) {
36
- input.value = props.value || "";
37
- }
38
- }, [props.value]);
39
- return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "var(--a2ui-spacing-xs, 0.25rem)" }, children: [
40
- props.label && /* @__PURE__ */ jsx("label", { htmlFor: id, style: { fontSize: "var(--a2ui-label-font-size, var(--a2ui-font-size-s))", fontWeight: "bold" }, children: props.label }),
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
- "data-a2ui-native-date-draft": "true",
48
- defaultValue: initialValue.current,
49
- onChange: (event) => props.setValue(event.currentTarget.value),
50
- min: typeof props.min === "string" ? props.min : void 0,
51
- max: typeof props.max === "string" ? props.max : void 0,
52
- style: {
53
- backgroundColor: "var(--a2ui-datetimeinput-background, var(--a2ui-color-input, #fff))",
54
- color: "var(--a2ui-datetimeinput-color, var(--a2ui-color-on-input, #333))",
55
- border: "var(--a2ui-datetimeinput-border, var(--a2ui-border))",
56
- borderRadius: "var(--a2ui-datetimeinput-border-radius, var(--a2ui-border-radius))",
57
- padding: "var(--a2ui-datetimeinput-padding, var(--a2ui-spacing-s))",
58
- boxSizing: "border-box"
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
@@ -1859,14 +1964,18 @@ function bindFieldInteractionGroups(root, groups, editing, controller) {
1859
1964
  const field = control.dataset.a2uiField;
1860
1965
  const group = field === void 0 ? void 0 : groupByField.get(field);
1861
1966
  if (group !== void 0) {
1862
- originalGroupAttributes.set(control, {
1863
- groupId: control.dataset.a2uiGroupId,
1864
- groupMode: control.dataset.a2uiGroupMode,
1865
- editState: control.dataset.a2uiEditState
1866
- });
1867
- control.dataset.a2uiGroupId = group.groupId;
1868
- control.dataset.a2uiGroupMode = group.mode;
1869
- control.dataset.a2uiEditState = groupEditState(group.groupId, editing);
1967
+ const dateEditor = control.closest("[data-a2ui-date-editor]");
1968
+ const targets = dateEditor ? [control, ...dateEditor.querySelectorAll("button, input[data-a2ui-calendar-input]")] : [control];
1969
+ for (const target of targets) {
1970
+ originalGroupAttributes.set(target, {
1971
+ groupId: target.dataset.a2uiGroupId,
1972
+ groupMode: target.dataset.a2uiGroupMode,
1973
+ editState: target.dataset.a2uiEditState
1974
+ });
1975
+ target.dataset.a2uiGroupId = group.groupId;
1976
+ target.dataset.a2uiGroupMode = group.mode;
1977
+ target.dataset.a2uiEditState = groupEditState(group.groupId, editing);
1978
+ }
1870
1979
  }
1871
1980
  }
1872
1981
  if (controller === void 0) {
@@ -1899,7 +2008,8 @@ function bindFieldInteractionGroups(root, groups, editing, controller) {
1899
2008
  }
1900
2009
  queueMicrotask(() => {
1901
2010
  if (interactionGroupId(root, root.ownerDocument.activeElement) !== groupId) {
1902
- controller.commitFieldGroup(groupId);
2011
+ const invalidDate = [...root.querySelectorAll("input[data-a2ui-date-text-draft]")].some((input) => input.dataset.a2uiGroupId === groupId && !input.checkValidity());
2012
+ if (!invalidDate) controller.commitFieldGroup(groupId);
1903
2013
  }
1904
2014
  });
1905
2015
  };