@dynamic-field-kit/react 1.4.0 → 1.5.0

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.mjs CHANGED
@@ -113,6 +113,307 @@ layoutRegistry.register("responsive", ({ children, config }) => {
113
113
  // src/components/DynamicInput.tsx
114
114
  import React3, { useMemo } from "react";
115
115
 
116
+ // src/defaultRenderers.tsx
117
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
118
+ var DefaultTextRenderer = ({
119
+ value,
120
+ onValueChange,
121
+ onBlur,
122
+ disabled,
123
+ readOnly,
124
+ required,
125
+ placeholder,
126
+ id,
127
+ className,
128
+ ariaInvalid,
129
+ ariaDescribedBy,
130
+ ariaRequired,
131
+ inputType = "text"
132
+ }) => /* @__PURE__ */ jsx3(
133
+ "input",
134
+ {
135
+ type: inputType,
136
+ id,
137
+ className,
138
+ value: value ?? "",
139
+ onChange: (e) => onValueChange?.(e.target.value),
140
+ onBlur,
141
+ disabled,
142
+ readOnly,
143
+ required,
144
+ placeholder,
145
+ "aria-invalid": ariaInvalid,
146
+ "aria-describedby": ariaDescribedBy,
147
+ "aria-required": ariaRequired
148
+ }
149
+ );
150
+ var DefaultNumberRenderer = ({
151
+ value,
152
+ onValueChange,
153
+ onBlur,
154
+ disabled,
155
+ readOnly,
156
+ required,
157
+ placeholder,
158
+ id,
159
+ className,
160
+ ariaInvalid,
161
+ ariaDescribedBy,
162
+ ariaRequired
163
+ }) => /* @__PURE__ */ jsx3(
164
+ "input",
165
+ {
166
+ type: "number",
167
+ id,
168
+ className,
169
+ value: value ?? "",
170
+ onChange: (e) => onValueChange?.(
171
+ e.target.value === "" ? void 0 : Number(e.target.value)
172
+ ),
173
+ onBlur,
174
+ disabled,
175
+ readOnly,
176
+ required,
177
+ placeholder,
178
+ "aria-invalid": ariaInvalid,
179
+ "aria-describedby": ariaDescribedBy,
180
+ "aria-required": ariaRequired
181
+ }
182
+ );
183
+ var DefaultPasswordRenderer = (props) => /* @__PURE__ */ jsx3(DefaultTextRenderer, { ...props, inputType: "password" });
184
+ var DefaultEmailRenderer = (props) => /* @__PURE__ */ jsx3(DefaultTextRenderer, { ...props, inputType: "email" });
185
+ var DefaultTextareaRenderer = ({
186
+ value,
187
+ onValueChange,
188
+ onBlur,
189
+ disabled,
190
+ readOnly,
191
+ required,
192
+ placeholder,
193
+ id,
194
+ className,
195
+ ariaInvalid,
196
+ ariaDescribedBy,
197
+ ariaRequired
198
+ }) => /* @__PURE__ */ jsx3(
199
+ "textarea",
200
+ {
201
+ id,
202
+ className,
203
+ value: value ?? "",
204
+ onChange: (e) => onValueChange?.(e.target.value),
205
+ onBlur,
206
+ disabled,
207
+ readOnly,
208
+ required,
209
+ placeholder,
210
+ "aria-invalid": ariaInvalid,
211
+ "aria-describedby": ariaDescribedBy,
212
+ "aria-required": ariaRequired
213
+ }
214
+ );
215
+ var DefaultCheckboxRenderer = ({
216
+ value,
217
+ onValueChange,
218
+ onBlur,
219
+ disabled,
220
+ readOnly,
221
+ required,
222
+ id,
223
+ className,
224
+ ariaInvalid,
225
+ ariaDescribedBy,
226
+ ariaRequired
227
+ }) => /* @__PURE__ */ jsx3(
228
+ "input",
229
+ {
230
+ type: "checkbox",
231
+ id,
232
+ className,
233
+ checked: Boolean(value),
234
+ onChange: (e) => onValueChange?.(e.target.checked),
235
+ onBlur,
236
+ disabled: disabled || readOnly,
237
+ required,
238
+ "aria-invalid": ariaInvalid,
239
+ "aria-describedby": ariaDescribedBy,
240
+ "aria-required": ariaRequired
241
+ }
242
+ );
243
+ var DefaultSelectRenderer = ({
244
+ value,
245
+ onValueChange,
246
+ onBlur,
247
+ disabled,
248
+ readOnly,
249
+ required,
250
+ options = [],
251
+ id,
252
+ className,
253
+ ariaInvalid,
254
+ ariaDescribedBy,
255
+ ariaRequired
256
+ }) => /* @__PURE__ */ jsxs2(
257
+ "select",
258
+ {
259
+ id,
260
+ className,
261
+ value: value ?? "",
262
+ onChange: (e) => onValueChange?.(e.target.value),
263
+ onBlur,
264
+ disabled: disabled || readOnly,
265
+ required,
266
+ "aria-invalid": ariaInvalid,
267
+ "aria-describedby": ariaDescribedBy,
268
+ "aria-required": ariaRequired,
269
+ children: [
270
+ /* @__PURE__ */ jsx3("option", { value: "", disabled: true, children: "-- Select --" }),
271
+ options.map((opt, i) => {
272
+ const optVal = opt.value ?? opt.id ?? opt;
273
+ const optLabel = opt.label ?? opt.name ?? String(optVal);
274
+ return /* @__PURE__ */ jsx3("option", { value: String(optVal), children: String(optLabel) }, String(optVal) + i);
275
+ })
276
+ ]
277
+ }
278
+ );
279
+ var DefaultRadioRenderer = ({
280
+ value,
281
+ onValueChange,
282
+ onBlur,
283
+ disabled,
284
+ readOnly,
285
+ required,
286
+ options = [],
287
+ id,
288
+ className,
289
+ ariaInvalid,
290
+ ariaDescribedBy
291
+ }) => /* @__PURE__ */ jsx3("div", { className: `dfk-radio-group ${className || ""}`, id, onBlur, children: options.map((opt, i) => {
292
+ const optVal = opt.value ?? opt.id ?? opt;
293
+ const optLabel = opt.label ?? opt.name ?? String(optVal);
294
+ const isChecked = String(value) === String(optVal);
295
+ const radioId = `${id || "radio"}-${i}`;
296
+ return /* @__PURE__ */ jsxs2(
297
+ "label",
298
+ {
299
+ htmlFor: radioId,
300
+ style: {
301
+ marginRight: "12px",
302
+ display: "inline-flex",
303
+ alignItems: "center"
304
+ },
305
+ children: [
306
+ /* @__PURE__ */ jsx3(
307
+ "input",
308
+ {
309
+ type: "radio",
310
+ id: radioId,
311
+ name: id,
312
+ value: String(optVal),
313
+ checked: isChecked,
314
+ onChange: () => onValueChange?.(optVal),
315
+ disabled: disabled || readOnly,
316
+ required,
317
+ "aria-invalid": ariaInvalid,
318
+ "aria-describedby": ariaDescribedBy
319
+ }
320
+ ),
321
+ /* @__PURE__ */ jsx3("span", { style: { marginLeft: "4px" }, children: String(optLabel) })
322
+ ]
323
+ },
324
+ String(optVal) + i
325
+ );
326
+ }) });
327
+ var DefaultRangeRenderer = ({
328
+ value,
329
+ onValueChange,
330
+ onBlur,
331
+ disabled,
332
+ readOnly,
333
+ required,
334
+ min,
335
+ max,
336
+ step,
337
+ id,
338
+ className,
339
+ ariaInvalid,
340
+ ariaDescribedBy
341
+ }) => /* @__PURE__ */ jsx3(
342
+ "input",
343
+ {
344
+ type: "range",
345
+ id,
346
+ className,
347
+ value: value ?? min ?? 0,
348
+ min,
349
+ max,
350
+ step,
351
+ onChange: (e) => onValueChange?.(Number(e.target.value)),
352
+ onBlur,
353
+ disabled: disabled || readOnly,
354
+ required,
355
+ "aria-invalid": ariaInvalid,
356
+ "aria-describedby": ariaDescribedBy
357
+ }
358
+ );
359
+ var DefaultFileRenderer = ({
360
+ onValueChange,
361
+ onBlur,
362
+ disabled,
363
+ readOnly,
364
+ required,
365
+ accept,
366
+ multiple,
367
+ id,
368
+ className,
369
+ ariaInvalid,
370
+ ariaDescribedBy
371
+ }) => /* @__PURE__ */ jsx3(
372
+ "input",
373
+ {
374
+ type: "file",
375
+ id,
376
+ className,
377
+ accept,
378
+ multiple,
379
+ onChange: (e) => {
380
+ const files = e.target.files;
381
+ if (!files) {
382
+ return;
383
+ }
384
+ onValueChange?.(multiple ? Array.from(files) : files[0] || null);
385
+ },
386
+ onBlur,
387
+ disabled: disabled || readOnly,
388
+ required,
389
+ "aria-invalid": ariaInvalid,
390
+ "aria-describedby": ariaDescribedBy
391
+ }
392
+ );
393
+ var DefaultDateRenderer = (props) => /* @__PURE__ */ jsx3(DefaultTextRenderer, { ...props, inputType: "date" });
394
+ var DefaultTimeRenderer = (props) => /* @__PURE__ */ jsx3(DefaultTextRenderer, { ...props, inputType: "time" });
395
+ var DefaultDateTimeLocalRenderer = (props) => /* @__PURE__ */ jsx3(DefaultTextRenderer, { ...props, inputType: "datetime-local" });
396
+ var DefaultSwitchRenderer = (props) => /* @__PURE__ */ jsx3(DefaultCheckboxRenderer, { ...props });
397
+ var defaultRenderersMap = {
398
+ text: DefaultTextRenderer,
399
+ number: DefaultNumberRenderer,
400
+ password: DefaultPasswordRenderer,
401
+ email: DefaultEmailRenderer,
402
+ textarea: DefaultTextareaRenderer,
403
+ checkbox: DefaultCheckboxRenderer,
404
+ select: DefaultSelectRenderer,
405
+ radio: DefaultRadioRenderer,
406
+ range: DefaultRangeRenderer,
407
+ file: DefaultFileRenderer,
408
+ date: DefaultDateRenderer,
409
+ time: DefaultTimeRenderer,
410
+ "datetime-local": DefaultDateTimeLocalRenderer,
411
+ switch: DefaultSwitchRenderer
412
+ };
413
+ function getDefaultRenderer(type) {
414
+ return defaultRenderersMap[type];
415
+ }
416
+
116
417
  // src/FieldRegistryContext.tsx
117
418
  import { createContext, useContext } from "react";
118
419
 
@@ -123,18 +424,18 @@ import {
123
424
  var fieldRegistry = coreFieldRegistry;
124
425
 
125
426
  // src/FieldRegistryContext.tsx
126
- import { jsx as jsx3 } from "react/jsx-runtime";
427
+ import { jsx as jsx4 } from "react/jsx-runtime";
127
428
  var FieldRegistryContext = createContext(fieldRegistry);
128
429
  var FieldRegistryProvider = ({
129
430
  registry,
130
431
  children
131
- }) => /* @__PURE__ */ jsx3(FieldRegistryContext.Provider, { value: registry, children });
432
+ }) => /* @__PURE__ */ jsx4(FieldRegistryContext.Provider, { value: registry, children });
132
433
  function useFieldRegistry() {
133
434
  return useContext(FieldRegistryContext);
134
435
  }
135
436
 
136
437
  // src/components/DynamicInput.tsx
137
- import { jsxs as jsxs2 } from "react/jsx-runtime";
438
+ import { jsxs as jsxs3 } from "react/jsx-runtime";
138
439
  var DynamicInputInner = ({
139
440
  type,
140
441
  value,
@@ -158,11 +459,11 @@ var DynamicInputInner = ({
158
459
  }) => {
159
460
  const registry = useFieldRegistry();
160
461
  const Renderer = useMemo(
161
- () => registry.get(type),
462
+ () => registry.get(type) || getDefaultRenderer(type),
162
463
  [registry, type]
163
464
  );
164
465
  if (!Renderer) {
165
- return /* @__PURE__ */ jsxs2("div", { children: [
466
+ return /* @__PURE__ */ jsxs3("div", { children: [
166
467
  "Unknown field type: ",
167
468
  type
168
469
  ] });
@@ -188,7 +489,7 @@ var DynamicInputInner = ({
188
489
  ariaRequired
189
490
  });
190
491
  };
191
- var DynamicInput = React3.memo(
492
+ var DynamicInput = /* @__PURE__ */ React3.memo(
192
493
  DynamicInputInner
193
494
  );
194
495
  var DynamicInput_default = DynamicInput;
@@ -222,7 +523,7 @@ import {
222
523
  useRef,
223
524
  useState as useState2
224
525
  } from "react";
225
- import { jsx as jsx4 } from "react/jsx-runtime";
526
+ import { jsx as jsx5 } from "react/jsx-runtime";
226
527
  function resolveLayout(layout) {
227
528
  if (!layout) {
228
529
  return { type: "column", config: {} };
@@ -238,7 +539,8 @@ var MultiFieldInput = ({
238
539
  onChange,
239
540
  layout,
240
541
  rootData,
241
- onValidityChange
542
+ onValidityChange,
543
+ onBlurField
242
544
  }) => {
243
545
  const [data, setData] = useState2({});
244
546
  const [touchedFields, setTouchedFields] = useState2(
@@ -267,6 +569,8 @@ var MultiFieldInput = ({
267
569
  rootDataRef.current = rootData;
268
570
  const onValidityChangeRef = useRef(onValidityChange);
269
571
  onValidityChangeRef.current = onValidityChange;
572
+ const onBlurFieldRef = useRef(onBlurField);
573
+ onBlurFieldRef.current = onBlurField;
270
574
  useEffect2(() => {
271
575
  onValidityChangeRef.current?.(
272
576
  validateFields(fieldDescriptions, data, rootData)
@@ -285,13 +589,14 @@ var MultiFieldInput = ({
285
589
  }, []);
286
590
  const handleBlurField = useCallback((key) => {
287
591
  setTouchedFields((prev) => prev[key] ? prev : { ...prev, [key]: true });
592
+ onBlurFieldRef.current?.(key);
288
593
  }, []);
289
594
  const { type, config } = resolveLayout(layout);
290
595
  const Layout = layoutRegistry.get(type);
291
596
  if (!Layout) {
292
597
  throw new Error(`Unknown layout: ${type}`);
293
598
  }
294
- return /* @__PURE__ */ jsx4(Layout, { config, children: visibleFields.map((f) => /* @__PURE__ */ jsx4(
599
+ return /* @__PURE__ */ jsx5(Layout, { config, children: visibleFields.map((f) => /* @__PURE__ */ jsx5(
295
600
  FieldInput_default,
296
601
  {
297
602
  fieldDescription: f,
@@ -308,7 +613,7 @@ var MultiFieldInput = ({
308
613
  var MultiFieldInput_default = MultiFieldInput;
309
614
 
310
615
  // src/components/FieldGroupInput.tsx
311
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
616
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
312
617
  var FieldGroupInput = ({
313
618
  fieldDescription,
314
619
  items,
@@ -349,14 +654,14 @@ var FieldGroupInput = ({
349
654
  },
350
655
  [fieldDescription, items, onChange]
351
656
  );
352
- return /* @__PURE__ */ jsxs3("div", { className: fieldDescription.className, children: [
353
- label && /* @__PURE__ */ jsx5("div", { children: label }),
354
- items.map((item, index) => /* @__PURE__ */ jsxs3(
657
+ return /* @__PURE__ */ jsxs4("div", { className: fieldDescription.className, children: [
658
+ label && /* @__PURE__ */ jsx6("div", { children: label }),
659
+ items.map((item, index) => /* @__PURE__ */ jsxs4(
355
660
  "div",
356
661
  {
357
662
  style: { display: "flex", alignItems: "flex-start", gap: 8 },
358
663
  children: [
359
- /* @__PURE__ */ jsx5("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx5(
664
+ /* @__PURE__ */ jsx6("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx6(
360
665
  MultiFieldInput_default,
361
666
  {
362
667
  fieldDescriptions: fields,
@@ -365,7 +670,7 @@ var FieldGroupInput = ({
365
670
  onChange: (next) => handleItemChange(index, next)
366
671
  }
367
672
  ) }),
368
- /* @__PURE__ */ jsx5(
673
+ /* @__PURE__ */ jsx6(
369
674
  "button",
370
675
  {
371
676
  type: "button",
@@ -379,7 +684,7 @@ var FieldGroupInput = ({
379
684
  },
380
685
  itemKey(item, index)
381
686
  )),
382
- /* @__PURE__ */ jsx5(
687
+ /* @__PURE__ */ jsx6(
383
688
  "button",
384
689
  {
385
690
  type: "button",
@@ -394,7 +699,7 @@ var FieldGroupInput = ({
394
699
  var FieldGroupInput_default = FieldGroupInput;
395
700
 
396
701
  // src/components/FieldInput.tsx
397
- import { jsx as jsx6 } from "react/jsx-runtime";
702
+ import { jsx as jsx7 } from "react/jsx-runtime";
398
703
  var FieldInputInner = ({
399
704
  fieldDescription,
400
705
  renderInfos,
@@ -415,7 +720,7 @@ var FieldInputInner = ({
415
720
  );
416
721
  if (fields) {
417
722
  const items = Array.isArray(renderInfos[name]) ? renderInfos[name] : [];
418
- return /* @__PURE__ */ jsx6(
723
+ return /* @__PURE__ */ jsx7(
419
724
  FieldGroupInput_default,
420
725
  {
421
726
  fieldDescription,
@@ -439,7 +744,7 @@ var FieldInputInner = ({
439
744
  const errors = effectiveDisabled ? [] : validateField(fieldDescription, renderInfos[name], renderInfos, rootData);
440
745
  const error = errors.length > 0 ? errors : void 0;
441
746
  const fieldId = `dfk-field-${name}`;
442
- return /* @__PURE__ */ jsx6(
747
+ return /* @__PURE__ */ jsx7(
443
748
  DynamicInput_default,
444
749
  {
445
750
  id: fieldId,
@@ -463,18 +768,328 @@ var FieldInputInner = ({
463
768
  }
464
769
  );
465
770
  };
466
- var FieldInput = React6.memo(FieldInputInner, (prev, next) => {
771
+ var FieldInput = /* @__PURE__ */ React6.memo(FieldInputInner, (prev, next) => {
467
772
  const name = prev.fieldDescription.name;
468
773
  return prev.fieldDescription === next.fieldDescription && prev.onValueChangeField === next.onValueChangeField && prev.onBlurField === next.onBlurField && prev.rootData === next.rootData && prev.touched === next.touched && prev.dirty === next.dirty && prev.renderInfos[name] === next.renderInfos[name];
469
774
  });
470
775
  var FieldInput_default = FieldInput;
471
776
 
777
+ // src/components/DynamicFormDevTools.tsx
778
+ import { useState as useState3 } from "react";
779
+ import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
780
+ var DynamicFormDevTools = ({
781
+ data,
782
+ errors = {},
783
+ touched = {},
784
+ isDirty = false,
785
+ fields = [],
786
+ position = "bottom-right"
787
+ }) => {
788
+ const [isOpen, setIsOpen] = useState3(false);
789
+ const [activeTab, setActiveTab] = useState3("data");
790
+ const errorCount = Object.keys(errors).length;
791
+ const posStyle = position === "bottom-left" ? { left: "16px", bottom: "16px" } : { right: "16px", bottom: "16px" };
792
+ if (!isOpen) {
793
+ return /* @__PURE__ */ jsxs5(
794
+ "button",
795
+ {
796
+ type: "button",
797
+ onClick: () => setIsOpen(true),
798
+ style: {
799
+ position: "fixed",
800
+ ...posStyle,
801
+ zIndex: 99999,
802
+ background: "#1e293b",
803
+ color: "#f8fafc",
804
+ border: "1px solid #334155",
805
+ borderRadius: "20px",
806
+ padding: "8px 14px",
807
+ fontSize: "12px",
808
+ fontWeight: 600,
809
+ cursor: "pointer",
810
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
811
+ display: "flex",
812
+ alignItems: "center",
813
+ gap: "6px"
814
+ },
815
+ children: [
816
+ /* @__PURE__ */ jsx8("span", { children: "\u{1F50D} DevTools" }),
817
+ errorCount > 0 && /* @__PURE__ */ jsx8(
818
+ "span",
819
+ {
820
+ style: {
821
+ background: "#ef4444",
822
+ color: "#fff",
823
+ borderRadius: "10px",
824
+ padding: "2px 6px",
825
+ fontSize: "10px"
826
+ },
827
+ children: errorCount
828
+ }
829
+ )
830
+ ]
831
+ }
832
+ );
833
+ }
834
+ return /* @__PURE__ */ jsxs5(
835
+ "div",
836
+ {
837
+ style: {
838
+ position: "fixed",
839
+ ...posStyle,
840
+ zIndex: 99999,
841
+ width: "360px",
842
+ maxHeight: "420px",
843
+ background: "#0f172a",
844
+ color: "#f8fafc",
845
+ border: "1px solid #334155",
846
+ borderRadius: "12px",
847
+ boxShadow: "0 10px 25px rgba(0,0,0,0.3)",
848
+ display: "flex",
849
+ flexDirection: "column",
850
+ fontFamily: "monospace, sans-serif",
851
+ fontSize: "12px",
852
+ overflow: "hidden"
853
+ },
854
+ children: [
855
+ /* @__PURE__ */ jsxs5(
856
+ "div",
857
+ {
858
+ style: {
859
+ padding: "10px 14px",
860
+ background: "#1e293b",
861
+ display: "flex",
862
+ justifyContent: "space-between",
863
+ alignItems: "center",
864
+ borderBottom: "1px solid #334155"
865
+ },
866
+ children: [
867
+ /* @__PURE__ */ jsx8("span", { style: { fontWeight: "bold", color: "#38bdf8" }, children: "\u{1F6E0}\uFE0F Form DevTools" }),
868
+ /* @__PURE__ */ jsx8(
869
+ "button",
870
+ {
871
+ type: "button",
872
+ onClick: () => setIsOpen(false),
873
+ style: {
874
+ background: "transparent",
875
+ border: "none",
876
+ color: "#94a3b8",
877
+ fontSize: "14px",
878
+ cursor: "pointer"
879
+ },
880
+ children: "\u2715"
881
+ }
882
+ )
883
+ ]
884
+ }
885
+ ),
886
+ /* @__PURE__ */ jsx8(
887
+ "div",
888
+ {
889
+ style: {
890
+ display: "flex",
891
+ background: "#1e293b",
892
+ borderBottom: "1px solid #334155"
893
+ },
894
+ children: ["data", "errors", "meta", "fields"].map((tab) => /* @__PURE__ */ jsxs5(
895
+ "button",
896
+ {
897
+ type: "button",
898
+ onClick: () => setActiveTab(tab),
899
+ style: {
900
+ flex: 1,
901
+ padding: "6px 0",
902
+ background: activeTab === tab ? "#0f172a" : "transparent",
903
+ color: activeTab === tab ? "#38bdf8" : "#94a3b8",
904
+ border: "none",
905
+ cursor: "pointer",
906
+ textTransform: "capitalize",
907
+ fontSize: "11px",
908
+ fontWeight: activeTab === tab ? "bold" : "normal"
909
+ },
910
+ children: [
911
+ tab,
912
+ " ",
913
+ tab === "errors" && errorCount > 0 ? `(${errorCount})` : ""
914
+ ]
915
+ },
916
+ tab
917
+ ))
918
+ }
919
+ ),
920
+ /* @__PURE__ */ jsxs5("div", { style: { padding: "12px", overflowY: "auto", flex: 1 }, children: [
921
+ activeTab === "data" && /* @__PURE__ */ jsx8(
922
+ "pre",
923
+ {
924
+ style: {
925
+ margin: 0,
926
+ whiteSpace: "pre-wrap",
927
+ wordBreak: "break-all",
928
+ color: "#a7f3d0"
929
+ },
930
+ children: JSON.stringify(data, null, 2)
931
+ }
932
+ ),
933
+ activeTab === "errors" && /* @__PURE__ */ jsx8("div", { children: Object.keys(errors).length === 0 ? /* @__PURE__ */ jsx8("span", { style: { color: "#4ade80" }, children: "\u2713 No validation errors" }) : Object.entries(errors).map(([field, msgs]) => /* @__PURE__ */ jsxs5("div", { style: { marginBottom: "8px" }, children: [
934
+ /* @__PURE__ */ jsxs5("span", { style: { color: "#f87171", fontWeight: "bold" }, children: [
935
+ field,
936
+ ":"
937
+ ] }),
938
+ /* @__PURE__ */ jsx8("ul", { style: { margin: "4px 0 0 16px", padding: 0 }, children: msgs.map((m, i) => /* @__PURE__ */ jsx8("li", { style: { color: "#fca5a5" }, children: m }, i)) })
939
+ ] }, field)) }),
940
+ activeTab === "meta" && /* @__PURE__ */ jsxs5("div", { children: [
941
+ /* @__PURE__ */ jsxs5("div", { style: { marginBottom: "6px" }, children: [
942
+ /* @__PURE__ */ jsx8("span", { style: { color: "#94a3b8" }, children: "isDirty: " }),
943
+ /* @__PURE__ */ jsx8("span", { style: { color: isDirty ? "#facc15" : "#4ade80" }, children: String(isDirty) })
944
+ ] }),
945
+ /* @__PURE__ */ jsxs5("div", { children: [
946
+ /* @__PURE__ */ jsx8("span", { style: { color: "#94a3b8" }, children: "Touched Fields:" }),
947
+ /* @__PURE__ */ jsx8("pre", { style: { margin: "4px 0 0 0", color: "#cbd5e1" }, children: JSON.stringify(touched, null, 2) })
948
+ ] })
949
+ ] }),
950
+ activeTab === "fields" && /* @__PURE__ */ jsx8("div", { children: fields.length === 0 ? /* @__PURE__ */ jsx8("span", { style: { color: "#94a3b8" }, children: "No field descriptions passed" }) : fields.map((f) => /* @__PURE__ */ jsxs5(
951
+ "div",
952
+ {
953
+ style: {
954
+ padding: "6px",
955
+ marginBottom: "6px",
956
+ background: "#1e293b",
957
+ borderRadius: "4px"
958
+ },
959
+ children: [
960
+ /* @__PURE__ */ jsx8("div", { style: { color: "#38bdf8", fontWeight: "bold" }, children: f.name }),
961
+ /* @__PURE__ */ jsxs5("div", { style: { color: "#94a3b8", fontSize: "10px" }, children: [
962
+ "type: ",
963
+ f.type,
964
+ " | required: ",
965
+ String(Boolean(f.required))
966
+ ] })
967
+ ]
968
+ },
969
+ f.name
970
+ )) })
971
+ ] })
972
+ ]
973
+ }
974
+ );
975
+ };
976
+
977
+ // src/useDynamicForm.ts
978
+ import {
979
+ applyComputedValues as applyComputedValues2,
980
+ validateFields as validateFields2
981
+ } from "@dynamic-field-kit/core";
982
+ import { useCallback as useCallback4, useState as useState4 } from "react";
983
+ function useDynamicForm({
984
+ fields,
985
+ initialValues = {},
986
+ validateOnBlur = true,
987
+ validateOnChange = false
988
+ }) {
989
+ const [data, setData] = useState4(
990
+ () => applyComputedValues2(fields, initialValues)
991
+ );
992
+ const [errors, setErrors] = useState4({});
993
+ const [isDirty, setIsDirty] = useState4(false);
994
+ const [touched, setTouched] = useState4({});
995
+ const [isSubmitting, setIsSubmitting] = useState4(false);
996
+ const [isSubmitted, setIsSubmitted] = useState4(false);
997
+ const validate = useCallback4(() => {
998
+ const res = validateFields2(fields, data);
999
+ setErrors(res.errors);
1000
+ return res.valid;
1001
+ }, [fields, data]);
1002
+ const handleChange = useCallback4(
1003
+ (newData) => {
1004
+ const next = applyComputedValues2(fields, newData);
1005
+ setData(next);
1006
+ setIsDirty(true);
1007
+ if (validateOnChange) {
1008
+ const res = validateFields2(fields, next);
1009
+ setErrors(res.errors);
1010
+ }
1011
+ },
1012
+ [fields, validateOnChange]
1013
+ );
1014
+ const setFieldValue = useCallback4(
1015
+ (name, value) => {
1016
+ handleChange({ ...data, [name]: value });
1017
+ },
1018
+ [data, handleChange]
1019
+ );
1020
+ const setFieldTouched = useCallback4((name, isTouched = true) => {
1021
+ setTouched((prev) => ({ ...prev, [name]: isTouched }));
1022
+ }, []);
1023
+ const handleBlur = useCallback4(
1024
+ (fieldName) => {
1025
+ setFieldTouched(fieldName, true);
1026
+ if (validateOnBlur) {
1027
+ const res = validateFields2(fields, data);
1028
+ setErrors(res.errors);
1029
+ }
1030
+ },
1031
+ [fields, data, validateOnBlur, setFieldTouched]
1032
+ );
1033
+ const reset = useCallback4(
1034
+ (newValues) => {
1035
+ const seed = newValues ?? initialValues;
1036
+ const next = applyComputedValues2(fields, seed);
1037
+ setData(next);
1038
+ setErrors({});
1039
+ setIsDirty(false);
1040
+ setTouched({});
1041
+ setIsSubmitting(false);
1042
+ setIsSubmitted(false);
1043
+ },
1044
+ [fields, initialValues]
1045
+ );
1046
+ const handleSubmit = useCallback4(
1047
+ (onValid, onInvalid) => async (e) => {
1048
+ if (e && typeof e.preventDefault === "function") {
1049
+ e.preventDefault();
1050
+ }
1051
+ setIsSubmitting(true);
1052
+ try {
1053
+ const res = validateFields2(fields, data);
1054
+ setErrors(res.errors);
1055
+ setIsSubmitted(true);
1056
+ if (res.valid) {
1057
+ await onValid(data);
1058
+ } else if (onInvalid) {
1059
+ onInvalid(res.errors);
1060
+ }
1061
+ } finally {
1062
+ setIsSubmitting(false);
1063
+ }
1064
+ },
1065
+ [fields, data]
1066
+ );
1067
+ const isValid = Object.keys(errors).length === 0;
1068
+ return {
1069
+ data,
1070
+ errors,
1071
+ isValid,
1072
+ isDirty,
1073
+ isSubmitting,
1074
+ isSubmitted,
1075
+ touched,
1076
+ setData,
1077
+ setFieldValue,
1078
+ setFieldTouched,
1079
+ handleChange,
1080
+ handleBlur,
1081
+ reset,
1082
+ validate,
1083
+ handleSubmit
1084
+ };
1085
+ }
1086
+
472
1087
  // src/index.ts
473
1088
  import { FieldRegistry } from "@dynamic-field-kit/core";
474
1089
  import {
475
1090
  validateField as validateField2,
476
1091
  validateFieldAsync,
477
- validateFields as validateFields2,
1092
+ validateFields as validateFields3,
478
1093
  validateFieldsAsync,
479
1094
  resolveDisabled as resolveDisabled2,
480
1095
  resolveReadOnly as resolveReadOnly2,
@@ -482,21 +1097,24 @@ import {
482
1097
  validators
483
1098
  } from "@dynamic-field-kit/core";
484
1099
  export {
1100
+ DynamicFormDevTools,
485
1101
  DynamicInput_default as DynamicInput,
486
1102
  FieldInput_default as FieldInput,
487
1103
  FieldRegistry,
488
1104
  FieldRegistryProvider,
489
1105
  MultiFieldInput_default as MultiFieldInput,
1106
+ defaultRenderersMap,
490
1107
  fieldRegistry,
1108
+ getDefaultRenderer,
491
1109
  layoutRegistry,
492
1110
  resolveDisabled2 as resolveDisabled,
493
1111
  resolveOptions2 as resolveOptions,
494
1112
  resolveReadOnly2 as resolveReadOnly,
1113
+ useDynamicForm,
495
1114
  useFieldRegistry,
496
1115
  validateField2 as validateField,
497
1116
  validateFieldAsync,
498
- validateFields2 as validateFields,
1117
+ validateFields3 as validateFields,
499
1118
  validateFieldsAsync,
500
1119
  validators
501
1120
  };
502
- //# sourceMappingURL=index.mjs.map