@datum-cloud/datum-ui 0.5.0 → 0.6.0-alpha.a37bf9a

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.
Files changed (175) hide show
  1. package/README.md +79 -40
  2. package/dist/adapter-context-rWveHhDd.mjs +25 -0
  3. package/dist/autocomplete/index.mjs +1 -1
  4. package/dist/{autocomplete-V5-qslzS.mjs → autocomplete-CkYJueBL.mjs} +2 -2
  5. package/dist/autosearch/index.mjs +199 -0
  6. package/dist/{calendar-date-picker-DWK94_DC.mjs → calendar-date-picker-CDT-8Ha8.mjs} +2 -1
  7. package/dist/combobox/index.mjs +2 -0
  8. package/dist/combobox-B-C9lJeD.mjs +97 -0
  9. package/dist/components/features/autocomplete/autocomplete.d.ts +1 -1
  10. package/dist/components/features/autocomplete/autocomplete.d.ts.map +1 -1
  11. package/dist/components/features/autocomplete/autocomplete.types.d.ts +2 -0
  12. package/dist/components/features/autocomplete/autocomplete.types.d.ts.map +1 -1
  13. package/dist/components/features/autosearch/autosearch.d.ts +35 -0
  14. package/dist/components/features/autosearch/autosearch.d.ts.map +1 -0
  15. package/dist/components/features/autosearch/autosearch.types.d.ts +51 -0
  16. package/dist/components/features/autosearch/autosearch.types.d.ts.map +1 -0
  17. package/dist/components/features/autosearch/index.d.ts +3 -0
  18. package/dist/components/features/autosearch/index.d.ts.map +1 -0
  19. package/dist/components/features/calendar-date-picker/calendar-date-picker.d.ts +2 -1
  20. package/dist/components/features/calendar-date-picker/calendar-date-picker.d.ts.map +1 -1
  21. package/dist/components/features/combobox/combobox.d.ts +27 -0
  22. package/dist/components/features/combobox/combobox.d.ts.map +1 -0
  23. package/dist/components/features/combobox/index.d.ts +3 -0
  24. package/dist/components/features/combobox/index.d.ts.map +1 -0
  25. package/dist/components/features/combobox/types.d.ts +84 -0
  26. package/dist/components/features/combobox/types.d.ts.map +1 -0
  27. package/dist/components/features/date-time-picker/date-time-picker.d.ts +9 -0
  28. package/dist/components/features/date-time-picker/date-time-picker.d.ts.map +1 -0
  29. package/dist/components/features/date-time-picker/index.d.ts +3 -0
  30. package/dist/components/features/date-time-picker/index.d.ts.map +1 -0
  31. package/dist/components/features/date-time-picker/types.d.ts +59 -0
  32. package/dist/components/features/date-time-picker/types.d.ts.map +1 -0
  33. package/dist/components/features/date-time-picker/utils/format.d.ts +13 -0
  34. package/dist/components/features/date-time-picker/utils/format.d.ts.map +1 -0
  35. package/dist/components/features/date-time-picker/utils/index.d.ts +3 -0
  36. package/dist/components/features/date-time-picker/utils/index.d.ts.map +1 -0
  37. package/dist/components/features/date-time-picker/utils/timezone.d.ts +23 -0
  38. package/dist/components/features/date-time-picker/utils/timezone.d.ts.map +1 -0
  39. package/dist/components/features/form/adapter-context.d.ts +17 -0
  40. package/dist/components/features/form/adapter-context.d.ts.map +1 -0
  41. package/dist/components/features/form/adapter-types.d.ts +126 -0
  42. package/dist/components/features/form/adapter-types.d.ts.map +1 -0
  43. package/dist/components/features/form/adapters/conform/conform-adapter.d.ts +9 -0
  44. package/dist/components/features/form/adapters/conform/conform-adapter.d.ts.map +1 -0
  45. package/dist/components/features/form/adapters/conform/conform-provider.d.ts +22 -0
  46. package/dist/components/features/form/adapters/conform/conform-provider.d.ts.map +1 -0
  47. package/dist/components/features/form/adapters/conform/index.d.ts +3 -0
  48. package/dist/components/features/form/adapters/conform/index.d.ts.map +1 -0
  49. package/dist/components/features/form/adapters/rhf/index.d.ts +3 -0
  50. package/dist/components/features/form/adapters/rhf/index.d.ts.map +1 -0
  51. package/dist/components/features/form/adapters/rhf/rhf-adapter.d.ts +10 -0
  52. package/dist/components/features/form/adapters/rhf/rhf-adapter.d.ts.map +1 -0
  53. package/dist/components/features/form/adapters/rhf/rhf-provider.d.ts +22 -0
  54. package/dist/components/features/form/adapters/rhf/rhf-provider.d.ts.map +1 -0
  55. package/dist/components/features/form/components/form-autocomplete.d.ts.map +1 -1
  56. package/dist/components/features/form/components/form-autosearch.d.ts +37 -0
  57. package/dist/components/features/form/components/form-autosearch.d.ts.map +1 -0
  58. package/dist/components/features/form/components/form-checkbox.d.ts.map +1 -1
  59. package/dist/components/features/form/components/form-combobox.d.ts +80 -0
  60. package/dist/components/features/form/components/form-combobox.d.ts.map +1 -0
  61. package/dist/components/features/form/components/form-copy-box.d.ts +3 -0
  62. package/dist/components/features/form/components/form-copy-box.d.ts.map +1 -1
  63. package/dist/components/features/form/components/form-custom.d.ts.map +1 -1
  64. package/dist/components/features/form/components/form-date-picker.d.ts +40 -0
  65. package/dist/components/features/form/components/form-date-picker.d.ts.map +1 -0
  66. package/dist/components/features/form/components/form-date-time-picker.d.ts +39 -0
  67. package/dist/components/features/form/components/form-date-time-picker.d.ts.map +1 -0
  68. package/dist/components/features/form/components/form-dialog.d.ts.map +1 -1
  69. package/dist/components/features/form/components/form-field-array.d.ts +5 -17
  70. package/dist/components/features/form/components/form-field-array.d.ts.map +1 -1
  71. package/dist/components/features/form/components/form-field.d.ts +7 -21
  72. package/dist/components/features/form/components/form-field.d.ts.map +1 -1
  73. package/dist/components/features/form/components/form-input-group.d.ts +4 -4
  74. package/dist/components/features/form/components/form-input-group.d.ts.map +1 -1
  75. package/dist/components/features/form/components/form-input.d.ts.map +1 -1
  76. package/dist/components/features/form/components/form-radio-group.d.ts.map +1 -1
  77. package/dist/components/features/form/components/form-root.d.ts +5 -25
  78. package/dist/components/features/form/components/form-root.d.ts.map +1 -1
  79. package/dist/components/features/form/components/form-select.d.ts.map +1 -1
  80. package/dist/components/features/form/components/form-switch.d.ts.map +1 -1
  81. package/dist/components/features/form/components/form-textarea.d.ts.map +1 -1
  82. package/dist/components/features/form/components/form-time-picker.d.ts +21 -0
  83. package/dist/components/features/form/components/form-time-picker.d.ts.map +1 -0
  84. package/dist/components/features/form/components/form-transfer.d.ts +37 -0
  85. package/dist/components/features/form/components/form-transfer.d.ts.map +1 -0
  86. package/dist/components/features/form/components/index.d.ts +7 -1
  87. package/dist/components/features/form/components/index.d.ts.map +1 -1
  88. package/dist/components/features/form/components/stepper/form-stepper.d.ts.map +1 -1
  89. package/dist/components/features/form/context/form-context.d.ts +2 -2
  90. package/dist/components/features/form/context/form-context.d.ts.map +1 -1
  91. package/dist/components/features/form/hooks/index.d.ts +1 -1
  92. package/dist/components/features/form/hooks/index.d.ts.map +1 -1
  93. package/dist/components/features/form/hooks/use-field.d.ts +12 -18
  94. package/dist/components/features/form/hooks/use-field.d.ts.map +1 -1
  95. package/dist/components/features/form/hooks/use-form-state.d.ts +36 -0
  96. package/dist/components/features/form/hooks/use-form-state.d.ts.map +1 -0
  97. package/dist/components/features/form/hooks/use-watch.d.ts +9 -20
  98. package/dist/components/features/form/hooks/use-watch.d.ts.map +1 -1
  99. package/dist/components/features/form/index.d.ts +69 -45
  100. package/dist/components/features/form/index.d.ts.map +1 -1
  101. package/dist/components/features/form/stepper/index.d.ts +17 -0
  102. package/dist/components/features/form/stepper/index.d.ts.map +1 -0
  103. package/dist/components/features/form/types/index.d.ts +78 -32
  104. package/dist/components/features/form/types/index.d.ts.map +1 -1
  105. package/dist/components/features/form/utils/get-field-constraints.d.ts +33 -0
  106. package/dist/components/features/form/utils/get-field-constraints.d.ts.map +1 -0
  107. package/dist/components/features/form/utils/get-schema-defaults.d.ts +24 -0
  108. package/dist/components/features/form/utils/get-schema-defaults.d.ts.map +1 -0
  109. package/dist/components/features/form/utils/zod-helpers.d.ts +12 -0
  110. package/dist/components/features/form/utils/zod-helpers.d.ts.map +1 -0
  111. package/dist/components/features/time-picker/index.d.ts +3 -0
  112. package/dist/components/features/time-picker/index.d.ts.map +1 -0
  113. package/dist/components/features/time-picker/time-picker.d.ts +22 -0
  114. package/dist/components/features/time-picker/time-picker.d.ts.map +1 -0
  115. package/dist/components/features/time-picker/types.d.ts +31 -0
  116. package/dist/components/features/time-picker/types.d.ts.map +1 -0
  117. package/dist/components/features/transfer/components/index.d.ts +9 -0
  118. package/dist/components/features/transfer/components/index.d.ts.map +1 -0
  119. package/dist/components/features/transfer/components/transfer-group.d.ts +7 -0
  120. package/dist/components/features/transfer/components/transfer-group.d.ts.map +1 -0
  121. package/dist/components/features/transfer/components/transfer-item.d.ts +10 -0
  122. package/dist/components/features/transfer/components/transfer-item.d.ts.map +1 -0
  123. package/dist/components/features/transfer/components/transfer-panel.d.ts +18 -0
  124. package/dist/components/features/transfer/components/transfer-panel.d.ts.map +1 -0
  125. package/dist/components/features/transfer/components/transfer-search.d.ts +9 -0
  126. package/dist/components/features/transfer/components/transfer-search.d.ts.map +1 -0
  127. package/dist/components/features/transfer/hooks/use-transfer-dnd.d.ts +26 -0
  128. package/dist/components/features/transfer/hooks/use-transfer-dnd.d.ts.map +1 -0
  129. package/dist/components/features/transfer/hooks/use-transfer-state.d.ts +20 -0
  130. package/dist/components/features/transfer/hooks/use-transfer-state.d.ts.map +1 -0
  131. package/dist/components/features/transfer/index.d.ts +3 -0
  132. package/dist/components/features/transfer/index.d.ts.map +1 -0
  133. package/dist/components/features/transfer/transfer.d.ts +6 -0
  134. package/dist/components/features/transfer/transfer.d.ts.map +1 -0
  135. package/dist/components/features/transfer/types.d.ts +69 -0
  136. package/dist/components/features/transfer/types.d.ts.map +1 -0
  137. package/dist/data-table/index.mjs +1 -1
  138. package/dist/date-picker/index.mjs +2 -2
  139. package/dist/date-time-picker/index.mjs +2 -0
  140. package/dist/date-time-picker-BomrW07W.mjs +178 -0
  141. package/dist/form/adapters/conform/index.mjs +346 -0
  142. package/dist/form/adapters/rhf/index.mjs +282 -0
  143. package/dist/form/index.mjs +3 -2
  144. package/dist/form/stepper/index.mjs +545 -0
  145. package/dist/form-CxrQ92WO.mjs +1685 -0
  146. package/dist/form-context-Ccxm-wqL.mjs +17 -0
  147. package/dist/get-field-constraints-BicgDkfH.mjs +51 -0
  148. package/dist/grid/index.mjs +1 -1
  149. package/dist/hooks/index.mjs +2 -2
  150. package/dist/index.mjs +16 -15
  151. package/dist/input-number/index.mjs +1 -1
  152. package/dist/map/index.mjs +1 -1
  153. package/dist/{map-ClxB41Hg.mjs → map-CWIQ-eql.mjs} +1 -1
  154. package/dist/more-actions/index.mjs +1 -1
  155. package/dist/page-title/index.mjs +1 -1
  156. package/dist/stepper/index.mjs +1 -320
  157. package/dist/stepper-DvIOp0hh.mjs +321 -0
  158. package/dist/tag-input/index.mjs +1 -1
  159. package/dist/task-queue/index.mjs +1 -1
  160. package/dist/time-picker/index.mjs +2 -0
  161. package/dist/time-picker-BoF7pZZ2.mjs +43 -0
  162. package/dist/transfer/index.mjs +2 -0
  163. package/dist/transfer-B2n8pgEQ.mjs +260 -0
  164. package/package.json +63 -2
  165. package/dist/form-Co3fM4B7.mjs +0 -2114
  166. /package/dist/{col-q-J99UHe.mjs → col-1T0Q3SlH.mjs} +0 -0
  167. /package/dist/{hooks-Cb7YlxN4.mjs → hooks-D8r2M2U6.mjs} +0 -0
  168. /package/dist/{input-number-mDB-5M5C.mjs → input-number-a7uydAsw.mjs} +0 -0
  169. /package/dist/{map-leaflet-imports-CaMm_rdF.mjs → map-leaflet-imports-CRSKA79m.mjs} +0 -0
  170. /package/dist/{more-actions-CGagbIDT.mjs → more-actions-ILnEZq_E.mjs} +0 -0
  171. /package/dist/{page-title-R7QbfbWp.mjs → page-title-ChsnpBiH.mjs} +0 -0
  172. /package/dist/{tag-input-BVSwNcRd.mjs → tag-input-T9cUX9-G.mjs} +0 -0
  173. /package/dist/{task-queue-dropdown-DyM5R8KF.mjs → task-queue-dropdown-Wcbj-f0V.mjs} +0 -0
  174. /package/dist/{to-api-format-BnbRFYQI.mjs → to-api-format-Bh3c01gr.mjs} +0 -0
  175. /package/dist/{use-copy-to-clipboard-BGdTmkFV.mjs → use-copy-to-clipboard-uNeeVHC4.mjs} +0 -0
@@ -0,0 +1,346 @@
1
+ import { t as FormAdapterProvider } from "../../../adapter-context-rWveHhDd.mjs";
2
+ import { t as getFieldConstraints } from "../../../get-field-constraints-BicgDkfH.mjs";
3
+ import * as React$1 from "react";
4
+ import { jsx } from "react/jsx-runtime";
5
+ import { FormProvider, getFormProps, getInputProps, useForm, useFormMetadata, useInputControl } from "@conform-to/react";
6
+ import { isDirty, useFormData } from "@conform-to/react/future";
7
+ import { getZodConstraint, parseWithZod } from "@conform-to/zod/v4";
8
+ //#region src/components/features/form/adapters/conform/conform-adapter.tsx
9
+ /**
10
+ * Shared context for the touched fields set.
11
+ * Created in useConformCreateForm and consumed by useConformField.
12
+ */
13
+ const TouchedFieldsContext = React$1.createContext({ current: /* @__PURE__ */ new Set() });
14
+ /**
15
+ * Resolve a Conform field metadata object by dot-notation path.
16
+ *
17
+ * Handles:
18
+ * - Top-level fields: `"email"` -> `fields.email`
19
+ * - Nested objects: `"address.city"` -> `fields.address.getFieldset().city`
20
+ * - Array items: `"items.0.name"` -> `fields.items.getFieldList()[0].getFieldset().name`
21
+ */
22
+ function resolveConformField(fields, name) {
23
+ const parts = name.split(".");
24
+ let current = fields;
25
+ for (let i = 0; i < parts.length; i++) {
26
+ const part = parts[i];
27
+ if (!current) break;
28
+ if (/^\d+$/.test(part)) {
29
+ const fieldList = current.getFieldList?.();
30
+ if (fieldList) {
31
+ const item = fieldList[Number.parseInt(part, 10)];
32
+ if (i < parts.length - 1 && item?.getFieldset) current = item.getFieldset();
33
+ else current = item;
34
+ } else current = current[part];
35
+ } else if (current[part] !== void 0) current = current[part];
36
+ else if (typeof current.getFieldset === "function") current = current.getFieldset()[part];
37
+ else current = void 0;
38
+ }
39
+ return current;
40
+ }
41
+ function convertFromString(value) {
42
+ if (value === void 0) return void 0;
43
+ if (Array.isArray(value)) return value[0];
44
+ if (value === "on") return true;
45
+ if (value.startsWith("[") && value.endsWith("]")) try {
46
+ return JSON.parse(value);
47
+ } catch {
48
+ return value;
49
+ }
50
+ return value;
51
+ }
52
+ function convertToString(value) {
53
+ if (typeof value === "boolean") return value ? "on" : "";
54
+ if (value === null || value === void 0) return "";
55
+ if (Array.isArray(value)) return JSON.stringify(value);
56
+ return String(value);
57
+ }
58
+ /** Create a Conform form instance and normalize it to the adapter interface. */
59
+ function useConformCreateForm(props) {
60
+ const { schema, defaultValues, mode, id, onSubmit, formRef } = props;
61
+ const [form, fields] = useForm({
62
+ id,
63
+ defaultValue: defaultValues,
64
+ constraint: getZodConstraint(schema),
65
+ shouldValidate: {
66
+ onBlur: "onBlur",
67
+ onChange: "onInput",
68
+ onSubmit: "onSubmit"
69
+ }[mode],
70
+ shouldRevalidate: mode !== "onSubmit" ? "onInput" : void 0,
71
+ onValidate: ({ formData }) => parseWithZod(formData, { schema }),
72
+ onSubmit: onSubmit ? (event, context) => {
73
+ event.preventDefault();
74
+ const submission = context.submission;
75
+ if (submission?.status === "success") onSubmit(submission.value);
76
+ } : void 0
77
+ });
78
+ const constraints = React$1.useMemo(() => getFieldConstraints(schema), [schema]);
79
+ const formIsDirty = useFormData(formRef, (formData) => isDirty(formData, { defaultValue: defaultValues }) ?? false) ?? false;
80
+ const dirtyFields = useFormData(formRef, (formData) => {
81
+ const result = {};
82
+ const defaults = defaultValues ?? {};
83
+ for (const key of Object.keys(defaults)) {
84
+ const current = formData.get(key);
85
+ const defaultVal = defaults[key];
86
+ result[key] = current !== (defaultVal === true ? "on" : defaultVal === false || defaultVal === null || defaultVal === void 0 ? "" : String(defaultVal));
87
+ }
88
+ for (const key of formData.keys()) if (!(key in result) && !key.startsWith("$")) {
89
+ const current = formData.get(key);
90
+ result[key] = current !== "" && current !== null;
91
+ }
92
+ const dirty = {};
93
+ for (const [key, value] of Object.entries(result)) if (value) dirty[key] = true;
94
+ return dirty;
95
+ }) ?? {};
96
+ const isValid = useFormData(formRef, (formData) => {
97
+ return parseWithZod(formData, { schema })?.status === "success";
98
+ }) ?? false;
99
+ const touchedFieldsRef = React$1.useRef(/* @__PURE__ */ new Set());
100
+ const [touchedFields, setTouchedFields] = React$1.useState({});
101
+ React$1.useEffect(() => {
102
+ const formEl = formRef?.current;
103
+ if (!formEl) return;
104
+ const handleFocusOut = (event) => {
105
+ const target = event.target;
106
+ if (target instanceof HTMLInputElement || target instanceof HTMLSelectElement || target instanceof HTMLTextAreaElement) {
107
+ const name = target.name;
108
+ if (name && !touchedFieldsRef.current.has(name)) {
109
+ touchedFieldsRef.current.add(name);
110
+ setTouchedFields((prev) => ({
111
+ ...prev,
112
+ [name]: true
113
+ }));
114
+ }
115
+ }
116
+ };
117
+ formEl.addEventListener("focusout", handleFocusOut);
118
+ return () => formEl.removeEventListener("focusout", handleFocusOut);
119
+ }, [formRef]);
120
+ const formState = React$1.useMemo(() => ({
121
+ isDirty: formIsDirty,
122
+ isValid,
123
+ isSubmitted: false,
124
+ submitCount: 0,
125
+ dirtyFields,
126
+ touchedFields
127
+ }), [
128
+ formIsDirty,
129
+ isValid,
130
+ dirtyFields,
131
+ touchedFields
132
+ ]);
133
+ const normalizedFields = React$1.useMemo(() => {
134
+ const result = {};
135
+ for (const [key, fieldMeta] of Object.entries(fields)) result[key] = {
136
+ id: fieldMeta.id ?? `${id ?? form.id}-${key}`,
137
+ errors: fieldMeta.errors ?? [],
138
+ required: constraints[key]?.required ?? false,
139
+ isDirty: dirtyFields[key] ?? false,
140
+ isTouched: touchedFields[key] ?? false
141
+ };
142
+ return result;
143
+ }, [
144
+ fields,
145
+ id,
146
+ form.id,
147
+ constraints,
148
+ dirtyFields,
149
+ touchedFields
150
+ ]);
151
+ const conformFormProps = React$1.useMemo(() => getFormProps(form), [form]);
152
+ const [displayTouchedFields, setDisplayTouchedFields] = React$1.useState(/* @__PURE__ */ new Set());
153
+ const markFieldTouched = React$1.useCallback((fieldName) => {
154
+ setDisplayTouchedFields((prev) => {
155
+ if (prev.has(fieldName)) return prev;
156
+ const next = new Set(prev);
157
+ next.add(fieldName);
158
+ return next;
159
+ });
160
+ }, []);
161
+ const markAllFieldsTouched = React$1.useCallback(() => {
162
+ const allFieldNames = Object.keys(getFieldConstraints(schema));
163
+ setDisplayTouchedFields(new Set(allFieldNames));
164
+ }, [schema]);
165
+ return React$1.useMemo(() => ({
166
+ id: form.id,
167
+ fields: normalizedFields,
168
+ formProps: conformFormProps,
169
+ formState,
170
+ submit: () => formRef?.current?.requestSubmit(),
171
+ reset: () => form.reset(),
172
+ getValues: () => {
173
+ if (!formRef?.current) return {};
174
+ const formData = new FormData(formRef.current);
175
+ const values = {};
176
+ for (const [key, value] of formData.entries()) values[key] = value;
177
+ return values;
178
+ },
179
+ touchedFields: Array.from(displayTouchedFields),
180
+ markFieldTouched,
181
+ markAllFieldsTouched,
182
+ raw: {
183
+ form,
184
+ fields,
185
+ touchedFieldsRef
186
+ }
187
+ }), [
188
+ form,
189
+ fields,
190
+ normalizedFields,
191
+ conformFormProps,
192
+ formState,
193
+ formRef,
194
+ displayTouchedFields,
195
+ markFieldTouched,
196
+ markAllFieldsTouched
197
+ ]);
198
+ }
199
+ /** Resolve a field by dot-notation path and return its normalized state. */
200
+ function useConformField(name) {
201
+ const fieldMeta = resolveConformField(useFormMetadata().getFieldset(), name);
202
+ const touchedFieldsRef = React$1.use(TouchedFieldsContext);
203
+ const control = useInputControl(fieldMeta ?? {
204
+ name,
205
+ key: void 0,
206
+ id: name
207
+ });
208
+ if (!fieldMeta) throw new Error(`[Conform Adapter] Field "${name}" not found. Make sure the field name matches your schema.`);
209
+ const currentValue = convertFromString(control.value);
210
+ const defaultValue = convertFromString(fieldMeta.initialValue);
211
+ const fieldIsDirty = currentValue !== (defaultValue === void 0 ? "" : defaultValue);
212
+ const fieldIsTouched = touchedFieldsRef.current.has(name);
213
+ return React$1.useMemo(() => ({
214
+ name: fieldMeta.name,
215
+ id: fieldMeta.id,
216
+ errors: fieldMeta.errors ?? [],
217
+ required: fieldMeta.required ?? false,
218
+ isDirty: fieldIsDirty,
219
+ isTouched: fieldIsTouched,
220
+ value: currentValue,
221
+ change: (value) => control.change(convertToString(value)),
222
+ blur: () => control.blur(),
223
+ focus: () => control.focus(),
224
+ inputProps: getInputProps(fieldMeta, { type: "text" })
225
+ }), [
226
+ fieldMeta,
227
+ control,
228
+ currentValue,
229
+ fieldIsDirty,
230
+ fieldIsTouched
231
+ ]);
232
+ }
233
+ /** Watch a single field's value reactively. */
234
+ function useConformWatch(name) {
235
+ const fieldMeta = resolveConformField(useFormMetadata().getFieldset(), name);
236
+ if (!fieldMeta) return void 0;
237
+ return convertFromString(fieldMeta.value);
238
+ }
239
+ /** Watch multiple fields' values reactively. */
240
+ function useConformWatchAll(names) {
241
+ const allFields = useFormMetadata().getFieldset();
242
+ const namesKey = names.join(",");
243
+ return React$1.useMemo(() => {
244
+ const result = {};
245
+ for (const name of names) {
246
+ const fieldMeta = resolveConformField(allFields, name);
247
+ if (fieldMeta) result[name] = convertFromString(fieldMeta.value);
248
+ }
249
+ return result;
250
+ }, [allFields, namesKey]);
251
+ }
252
+ /** Get field array helpers for a given array field name. */
253
+ function useConformFieldArray(name) {
254
+ const formMeta = useFormMetadata();
255
+ const arrayField = resolveConformField(formMeta.getFieldset(), name);
256
+ const items = React$1.useMemo(() => {
257
+ if (!arrayField?.getFieldList) return [];
258
+ return arrayField.getFieldList().map((item, index) => ({
259
+ id: item.id ?? `${name}-${index}`,
260
+ key: item.key ?? `${name}-${index}`,
261
+ name: `${name}[${index}]`
262
+ }));
263
+ }, [arrayField, name]);
264
+ const arrayFieldName = arrayField?.name;
265
+ return {
266
+ items,
267
+ append: React$1.useCallback((defaultValue) => {
268
+ if (!arrayFieldName) return;
269
+ formMeta.insert({
270
+ name: arrayFieldName,
271
+ defaultValue
272
+ });
273
+ }, [formMeta, arrayFieldName]),
274
+ remove: React$1.useCallback((index) => {
275
+ if (!arrayFieldName) return;
276
+ formMeta.remove({
277
+ name: arrayFieldName,
278
+ index
279
+ });
280
+ }, [formMeta, arrayFieldName]),
281
+ move: React$1.useCallback((from, to) => {
282
+ if (!arrayFieldName) return;
283
+ formMeta.reorder({
284
+ name: arrayFieldName,
285
+ from,
286
+ to
287
+ });
288
+ }, [formMeta, arrayFieldName])
289
+ };
290
+ }
291
+ /**
292
+ * Wraps children in Conform's FormProvider so that useFormMetadata() and
293
+ * useInputControl() work inside field components.
294
+ * Also provides the touched fields ref via context for useConformField.
295
+ */
296
+ function ConformFormProviderWrapper({ instance, children }) {
297
+ const { form, touchedFieldsRef } = instance.raw;
298
+ return /* @__PURE__ */ jsx(FormProvider, {
299
+ context: form.context,
300
+ children: /* @__PURE__ */ jsx(TouchedFieldsContext, {
301
+ value: touchedFieldsRef,
302
+ children
303
+ })
304
+ });
305
+ }
306
+ /**
307
+ * Conform.js adapter implementing the `FormAdapter` interface.
308
+ *
309
+ * Maps Conform's `useForm` / `useInputControl` / `useFormMetadata` APIs
310
+ * to the normalized form adapter contract.
311
+ */
312
+ const conformAdapter = {
313
+ name: "Conform",
314
+ useCreateForm: useConformCreateForm,
315
+ useField: useConformField,
316
+ useWatch: useConformWatch,
317
+ useWatchAll: useConformWatchAll,
318
+ useFieldArray: useConformFieldArray,
319
+ FormProvider: ConformFormProviderWrapper
320
+ };
321
+ //#endregion
322
+ //#region src/components/features/form/adapters/conform/conform-provider.tsx
323
+ /**
324
+ * Wrap your application with this provider to use Conform.js as the form backend.
325
+ *
326
+ * @example
327
+ * ```tsx
328
+ * import { ConformAdapter } from '@datum-cloud/datum-ui/form/adapters/conform'
329
+ *
330
+ * function App() {
331
+ * return (
332
+ * <ConformAdapter>
333
+ * <MyApp />
334
+ * </ConformAdapter>
335
+ * )
336
+ * }
337
+ * ```
338
+ */
339
+ function ConformAdapter({ children }) {
340
+ return /* @__PURE__ */ jsx(FormAdapterProvider, {
341
+ adapter: conformAdapter,
342
+ children
343
+ });
344
+ }
345
+ //#endregion
346
+ export { ConformAdapter, conformAdapter };
@@ -0,0 +1,282 @@
1
+ import { t as FormAdapterProvider } from "../../../adapter-context-rWveHhDd.mjs";
2
+ import { n as getObjectShape, t as getFieldConstraints } from "../../../get-field-constraints-BicgDkfH.mjs";
3
+ import * as React$1 from "react";
4
+ import { jsx } from "react/jsx-runtime";
5
+ import { zodResolver } from "@hookform/resolvers/zod";
6
+ import { FormProvider, useController, useFieldArray, useForm, useFormContext, useWatch } from "react-hook-form";
7
+ //#region src/components/features/form/utils/get-schema-defaults.ts
8
+ /**
9
+ * Derive default values from a Zod schema that match what React Hook Form's
10
+ * `useController` will produce when fields register.
11
+ *
12
+ * This is critical for RHF's `isDirty` tracking: RHF compares current values
13
+ * against `_defaultValues`. Without explicit defaults, RHF uses `{}` as the
14
+ * baseline, so when `useController` registers fields (e.g. `username: ''`),
15
+ * the form is immediately considered dirty.
16
+ *
17
+ * By passing these schema-derived defaults to `useForm({ defaultValues })`,
18
+ * the baseline matches the registered values and `isDirty` starts as `false`.
19
+ *
20
+ * Maps Zod field types to their `useController` registration values:
21
+ * - string -> `''`
22
+ * - number -> `undefined`
23
+ * - boolean -> `false`
24
+ * - array -> `[]`
25
+ * - object -> `{}` (recursive)
26
+ * - optional/nullable wrappers -> unwrap and derive inner default
27
+ * - `.default()` -> use the provided default value
28
+ */
29
+ function getSchemaDefaults(schema) {
30
+ const shape = getObjectShape(schema);
31
+ if (!shape) return {};
32
+ const defaults = {};
33
+ for (const [key, fieldSchema] of Object.entries(shape)) defaults[key] = getFieldDefault(fieldSchema);
34
+ return defaults;
35
+ }
36
+ /**
37
+ * Get the default value for a Zod field type that matches what RHF's
38
+ * `useController` will produce when registering the field.
39
+ *
40
+ * Unwraps optional/nullable/default wrappers to find the inner type,
41
+ * then returns the natural "empty" value for that type.
42
+ */
43
+ function getFieldDefault(schema) {
44
+ const { type } = schema.def;
45
+ if (type === "default") return schema.def.defaultValue;
46
+ if (type === "optional" || type === "nullable") {
47
+ const innerDef = schema.def;
48
+ return getFieldDefault(innerDef.innerType);
49
+ }
50
+ if (type === "pipe") {
51
+ const pipeDef = schema.def;
52
+ return getFieldDefault(pipeDef.in);
53
+ }
54
+ if (type === "string") return "";
55
+ if (type === "number" || type === "bigint" || type === "nan") return void 0;
56
+ if (type === "boolean") return false;
57
+ if (type === "array") return [];
58
+ if (type === "object") return getSchemaDefaults(schema);
59
+ }
60
+ //#endregion
61
+ //#region src/components/features/form/adapters/rhf/rhf-adapter.tsx
62
+ const RHFFormIdContext = React$1.createContext("form");
63
+ /** Create a React Hook Form instance and normalize it to the adapter interface. */
64
+ function useRHFCreateForm(props) {
65
+ const { schema, defaultValues, mode, id, onSubmit, formRef } = props;
66
+ const schemaDefaults = React$1.useMemo(() => getSchemaDefaults(schema), [schema]);
67
+ const mergedDefaults = React$1.useMemo(() => ({
68
+ ...schemaDefaults,
69
+ ...defaultValues
70
+ }), [schemaDefaults, defaultValues]);
71
+ const form = useForm({
72
+ resolver: zodResolver(schema),
73
+ defaultValues: mergedDefaults,
74
+ mode: "onChange",
75
+ reValidateMode: "onChange"
76
+ });
77
+ const { errors, isDirty, isValid, dirtyFields, touchedFields } = form.formState;
78
+ const constraints = React$1.useMemo(() => getFieldConstraints(schema), [schema]);
79
+ const onSubmitRef = React$1.useRef(onSubmit);
80
+ onSubmitRef.current = onSubmit;
81
+ const normalizedFields = React$1.useMemo(() => {
82
+ const result = {};
83
+ for (const [key, constraint] of Object.entries(constraints)) {
84
+ const fieldError = errors[key];
85
+ result[key] = {
86
+ id: `${id ?? "form"}-${key}`,
87
+ errors: fieldError?.message ? [String(fieldError.message)] : [],
88
+ required: constraint.required,
89
+ isDirty: Boolean(dirtyFields[key]),
90
+ isTouched: Boolean(touchedFields[key])
91
+ };
92
+ }
93
+ return result;
94
+ }, [
95
+ constraints,
96
+ errors,
97
+ id,
98
+ dirtyFields,
99
+ touchedFields
100
+ ]);
101
+ const formState = React$1.useMemo(() => ({
102
+ isDirty,
103
+ isValid,
104
+ isSubmitted: false,
105
+ submitCount: 0,
106
+ dirtyFields: Object.fromEntries(Object.entries(dirtyFields).map(([k, v]) => [k, Boolean(v)])),
107
+ touchedFields: Object.fromEntries(Object.entries(touchedFields).map(([k, v]) => [k, Boolean(v)]))
108
+ }), [
109
+ isDirty,
110
+ isValid,
111
+ dirtyFields,
112
+ touchedFields
113
+ ]);
114
+ const handleSubmit = React$1.useMemo(() => form.handleSubmit((data) => {
115
+ onSubmitRef.current?.(data);
116
+ }), [form]);
117
+ const formProps = React$1.useMemo(() => ({
118
+ id,
119
+ onSubmit: handleSubmit,
120
+ noValidate: true
121
+ }), [id, handleSubmit]);
122
+ const [displayTouchedFields, setDisplayTouchedFields] = React$1.useState(/* @__PURE__ */ new Set());
123
+ const markFieldTouched = React$1.useCallback((fieldName) => {
124
+ setDisplayTouchedFields((prev) => {
125
+ if (prev.has(fieldName)) return prev;
126
+ const next = new Set(prev);
127
+ next.add(fieldName);
128
+ return next;
129
+ });
130
+ }, []);
131
+ const markAllFieldsTouched = React$1.useCallback(() => {
132
+ const allFieldNames = Object.keys(getFieldConstraints(schema));
133
+ setDisplayTouchedFields(new Set(allFieldNames));
134
+ }, [schema]);
135
+ return React$1.useMemo(() => ({
136
+ id: id ?? "form",
137
+ fields: normalizedFields,
138
+ formProps,
139
+ formState,
140
+ submit: () => formRef?.current?.requestSubmit(),
141
+ reset: () => form.reset(),
142
+ getValues: () => form.getValues(),
143
+ touchedFields: Array.from(displayTouchedFields),
144
+ markFieldTouched,
145
+ markAllFieldsTouched,
146
+ raw: form
147
+ }), [
148
+ id,
149
+ normalizedFields,
150
+ formProps,
151
+ formState,
152
+ form,
153
+ formRef,
154
+ displayTouchedFields,
155
+ markFieldTouched,
156
+ markAllFieldsTouched
157
+ ]);
158
+ }
159
+ /** Resolve a field by name and return its normalized state. */
160
+ function useRHFField(name) {
161
+ const form = useFormContext();
162
+ const formId = React$1.use(RHFFormIdContext);
163
+ const { field, fieldState } = useController({
164
+ name,
165
+ control: form.control
166
+ });
167
+ return React$1.useMemo(() => ({
168
+ name: field.name,
169
+ id: `${formId}-${name}`,
170
+ errors: fieldState.error?.message ? [String(fieldState.error.message)] : [],
171
+ required: false,
172
+ isDirty: fieldState.isDirty,
173
+ isTouched: fieldState.isTouched,
174
+ value: field.value,
175
+ change: (value) => field.onChange(value),
176
+ blur: () => field.onBlur(),
177
+ focus: () => form.setFocus(name)
178
+ }), [
179
+ field,
180
+ fieldState,
181
+ name,
182
+ formId,
183
+ form
184
+ ]);
185
+ }
186
+ /** Watch a single field's value reactively. */
187
+ function useRHFWatchHook(name) {
188
+ return useWatch({
189
+ name,
190
+ control: useFormContext().control
191
+ });
192
+ }
193
+ /** Watch multiple fields' values reactively. */
194
+ function useRHFWatchAllHook(names) {
195
+ const values = useWatch({
196
+ name: names,
197
+ control: useFormContext().control
198
+ });
199
+ const namesKey = names.join(",");
200
+ return React$1.useMemo(() => {
201
+ const result = {};
202
+ names.forEach((n, index) => {
203
+ result[n] = values[index];
204
+ });
205
+ return result;
206
+ }, [namesKey, values]);
207
+ }
208
+ /** Get field array helpers for a given array field name. */
209
+ function useRHFFieldArrayHook(name) {
210
+ const { fields, append, remove, move } = useFieldArray({
211
+ name,
212
+ control: useFormContext().control
213
+ });
214
+ return {
215
+ items: React$1.useMemo(() => fields.map((field, index) => ({
216
+ id: field.id,
217
+ key: field.id,
218
+ name: `${name}.${index}`
219
+ })), [fields, name]),
220
+ append: React$1.useCallback((defaultValue) => {
221
+ append(defaultValue ?? {});
222
+ }, [append]),
223
+ remove,
224
+ move
225
+ };
226
+ }
227
+ /**
228
+ * Wraps children in React Hook Form's FormProvider so that useFormContext()
229
+ * and useController() work inside field components.
230
+ */
231
+ function RHFFormProviderWrapper({ instance, children }) {
232
+ const form = instance.raw;
233
+ return /* @__PURE__ */ jsx(RHFFormIdContext, {
234
+ value: instance.id,
235
+ children: /* @__PURE__ */ jsx(FormProvider, {
236
+ ...form,
237
+ children
238
+ })
239
+ });
240
+ }
241
+ /**
242
+ * React Hook Form adapter implementing the `FormAdapter` interface.
243
+ *
244
+ * Maps react-hook-form's `useForm` / `useController` / `useWatch` APIs
245
+ * to the normalized form adapter contract. Uses `@hookform/resolvers/zod`
246
+ * for Zod schema validation.
247
+ */
248
+ const rhfAdapter = {
249
+ name: "React Hook Form",
250
+ useCreateForm: useRHFCreateForm,
251
+ useField: useRHFField,
252
+ useWatch: useRHFWatchHook,
253
+ useWatchAll: useRHFWatchAllHook,
254
+ useFieldArray: useRHFFieldArrayHook,
255
+ FormProvider: RHFFormProviderWrapper
256
+ };
257
+ //#endregion
258
+ //#region src/components/features/form/adapters/rhf/rhf-provider.tsx
259
+ /**
260
+ * Wrap your application with this provider to use React Hook Form as the form backend.
261
+ *
262
+ * @example
263
+ * ```tsx
264
+ * import { RHFAdapter } from '@datum-cloud/datum-ui/form/adapters/rhf'
265
+ *
266
+ * function App() {
267
+ * return (
268
+ * <RHFAdapter>
269
+ * <MyApp />
270
+ * </RHFAdapter>
271
+ * )
272
+ * }
273
+ * ```
274
+ */
275
+ function RHFAdapter({ children }) {
276
+ return /* @__PURE__ */ jsx(FormAdapterProvider, {
277
+ adapter: rhfAdapter,
278
+ children
279
+ });
280
+ }
281
+ //#endregion
282
+ export { RHFAdapter, rhfAdapter };
@@ -1,2 +1,3 @@
1
- import { A as FormButton, C as FormField, D as FormCustom, E as FormDescription, O as FormCopyBox, S as FormFieldArray, T as FormDialog, _ as FormSelectItem, a as useField, b as FormRadioItem, c as FormStep, d as useWatch, f as useWatchAll, g as FormSelect, h as FormSubmit, i as useFieldContext, j as FormAutocomplete, k as FormCheckbox, l as FormStepper, m as FormSwitch, n as useStepper, o as StepperNavigation, p as FormTextarea, r as useFormContext, s as StepperControls, t as Form, u as FormWhen, v as FormRoot, w as FormError, x as FormInput, y as FormRadioGroup } from "../form-Co3fM4B7.mjs";
2
- export { Form, FormAutocomplete, FormButton, FormCheckbox, FormCopyBox, FormCustom, FormDescription, FormDialog, FormError, FormField, FormFieldArray, FormInput, FormRadioGroup, FormRadioItem, FormRoot, FormSelect, FormSelectItem, FormStep, FormStepper, FormSubmit, FormSwitch, FormTextarea, FormWhen, StepperControls, StepperNavigation, useField, useFieldContext, useFormContext, useStepper, useWatch, useWatchAll };
1
+ import { A as FormCheckbox, C as FormDialog, D as FormCustom, E as FormDatePicker, M as FormAutosearch, N as FormAutocomplete, O as FormCopyBox, S as FormError, T as FormDateTimePicker, _ as FormRadioGroup, a as useField, b as FormFieldArray, c as useWatchAll, d as FormTextarea, f as FormSwitch, g as FormRoot, h as FormSelectItem, i as useFieldContext, j as FormButton, k as FormCombobox, l as FormTransfer, m as FormSelect, n as useFormState, o as FormWhen, p as FormSubmit, r as useFormContext, s as useWatch, t as Form, u as FormTimePicker, v as FormRadioItem, w as FormDescription, x as FormField, y as FormInput } from "../form-CxrQ92WO.mjs";
2
+ import { n as useAdapter, t as FormAdapterProvider } from "../adapter-context-rWveHhDd.mjs";
3
+ export { Form, FormAdapterProvider, FormAutocomplete, FormAutosearch, FormButton, FormCheckbox, FormCombobox, FormCopyBox, FormCustom, FormDatePicker, FormDateTimePicker, FormDescription, FormDialog, FormError, FormField, FormFieldArray, FormInput, FormRadioGroup, FormRadioItem, FormRoot, FormSelect, FormSelectItem, FormSubmit, FormSwitch, FormTextarea, FormTimePicker, FormTransfer, FormWhen, useAdapter, useField, useFieldContext, useFormContext, useFormState, useWatch, useWatchAll };