@axenstudio/axen-form 0.1.17 → 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.
Files changed (128) hide show
  1. package/README.md +34 -3
  2. package/dist/adapters/yupAdapter.cjs +29 -10
  3. package/dist/adapters/yupAdapter.d.ts +1 -2
  4. package/dist/adapters/yupAdapter.js +29 -10
  5. package/dist/adapters/zodAdapter.cjs +29 -10
  6. package/dist/adapters/zodAdapter.d.ts +1 -2
  7. package/dist/adapters/zodAdapter.js +29 -10
  8. package/dist/core/dateUtils.d.ts +0 -1
  9. package/dist/core/formStore.cjs +110 -39
  10. package/dist/core/formStore.d.ts +1 -1
  11. package/dist/core/formStore.js +110 -39
  12. package/dist/core/numberUtils.d.ts +0 -1
  13. package/dist/core/pathUtils.d.ts +0 -1
  14. package/dist/core/payloadUtils.d.ts +0 -1
  15. package/dist/core/semanticValidation.cjs +69 -0
  16. package/dist/core/semanticValidation.d.ts +5 -0
  17. package/dist/core/semanticValidation.js +69 -0
  18. package/dist/core/validation.cjs +4 -3
  19. package/dist/core/validation.d.ts +0 -1
  20. package/dist/core/validation.js +4 -3
  21. package/dist/fields/AutocompleteField/AutocompleteField.d.ts +1 -2
  22. package/dist/fields/AutocompleteMultiField/AutocompleteMultiField.d.ts +1 -2
  23. package/dist/fields/AutocompletePredictField/AutocompletePredictField.d.ts +1 -2
  24. package/dist/fields/CheckboxField/CheckboxField.d.ts +1 -2
  25. package/dist/fields/ColorField/ColorField.d.ts +1 -2
  26. package/dist/fields/CurrencyField/CurrencyField.d.ts +1 -2
  27. package/dist/fields/DateField/DateField.d.ts +1 -2
  28. package/dist/fields/DateField/DatePicker.d.ts +1 -2
  29. package/dist/fields/DateTimeField/DateTimeField.d.ts +1 -2
  30. package/dist/fields/EmailField/EmailField.d.ts +1 -2
  31. package/dist/fields/NumberField/NumberField.d.ts +1 -2
  32. package/dist/fields/PasswordField/PasswordField.d.ts +1 -2
  33. package/dist/fields/PhoneField/PhoneField.d.ts +1 -2
  34. package/dist/fields/RadioField/RadioField.d.ts +1 -2
  35. package/dist/fields/SelectField/SelectField.d.ts +1 -2
  36. package/dist/fields/SliderField/SliderField.d.ts +1 -2
  37. package/dist/fields/SwitchField/SwitchField.d.ts +1 -2
  38. package/dist/fields/TextField/TextField.d.ts +1 -2
  39. package/dist/fields/TextareaField/TextareaField.d.ts +1 -2
  40. package/dist/fields/TimeField/TimeField.d.ts +1 -2
  41. package/dist/fields/TimeField/TimePicker.d.ts +1 -2
  42. package/dist/fields/defaultComponentMap.cjs +3 -0
  43. package/dist/fields/defaultComponentMap.d.ts +0 -1
  44. package/dist/fields/defaultComponentMap.js +3 -0
  45. package/dist/fields/defaultPlaceholders.d.ts +0 -1
  46. package/dist/fields/fieldBase.module.css.cjs +92 -70
  47. package/dist/fields/fieldBase.module.css.js +92 -70
  48. package/dist/fields/fieldUtils.d.ts +0 -1
  49. package/dist/fields/iconUtils.d.ts +0 -1
  50. package/dist/fields/icons.d.ts +3 -4
  51. package/dist/index.cjs +31 -22
  52. package/dist/index.d.ts +3 -2
  53. package/dist/index.js +32 -23
  54. package/dist/layout/Box/Box.d.ts +1 -2
  55. package/dist/layout/Divider/Divider.d.ts +1 -2
  56. package/dist/layout/Grid/Grid.d.ts +1 -2
  57. package/dist/layout/Spacer/Spacer.d.ts +1 -2
  58. package/dist/layout/Stack/Stack.d.ts +1 -2
  59. package/dist/layout/index.d.ts +0 -1
  60. package/dist/react/AxenArrayField.d.ts +1 -2
  61. package/dist/react/AxenField.d.ts +1 -2
  62. package/dist/react/AxenForm.cjs +29 -23
  63. package/dist/react/AxenForm.d.ts +0 -1
  64. package/dist/react/AxenForm.js +30 -24
  65. package/dist/react/FormContext.d.ts +0 -1
  66. package/dist/react/useAxenForm.cjs +11 -4
  67. package/dist/react/useAxenForm.d.ts +1 -2
  68. package/dist/react/useAxenForm.js +11 -4
  69. package/dist/react/useField.cjs +3 -0
  70. package/dist/react/useField.d.ts +0 -1
  71. package/dist/react/useField.js +3 -0
  72. package/dist/react/useFieldArray.cjs +19 -13
  73. package/dist/react/useFieldArray.d.ts +0 -1
  74. package/dist/react/useFieldArray.js +19 -13
  75. package/dist/react/useFormState.cjs +18 -0
  76. package/dist/react/useFormState.d.ts +5 -0
  77. package/dist/react/useFormState.js +18 -0
  78. package/dist/types.d.ts +18 -2
  79. package/package.json +5 -2
  80. package/dist/adapters/yupAdapter.d.ts.map +0 -1
  81. package/dist/adapters/zodAdapter.d.ts.map +0 -1
  82. package/dist/core/dateUtils.d.ts.map +0 -1
  83. package/dist/core/formStore.d.ts.map +0 -1
  84. package/dist/core/numberUtils.d.ts.map +0 -1
  85. package/dist/core/pathUtils.d.ts.map +0 -1
  86. package/dist/core/payloadUtils.d.ts.map +0 -1
  87. package/dist/core/validation.d.ts.map +0 -1
  88. package/dist/fields/AutocompleteField/AutocompleteField.d.ts.map +0 -1
  89. package/dist/fields/AutocompleteMultiField/AutocompleteMultiField.d.ts.map +0 -1
  90. package/dist/fields/AutocompletePredictField/AutocompletePredictField.d.ts.map +0 -1
  91. package/dist/fields/CheckboxField/CheckboxField.d.ts.map +0 -1
  92. package/dist/fields/ColorField/ColorField.d.ts.map +0 -1
  93. package/dist/fields/CurrencyField/CurrencyField.d.ts.map +0 -1
  94. package/dist/fields/DateField/DateField.d.ts.map +0 -1
  95. package/dist/fields/DateField/DatePicker.d.ts.map +0 -1
  96. package/dist/fields/DateTimeField/DateTimeField.d.ts.map +0 -1
  97. package/dist/fields/EmailField/EmailField.d.ts.map +0 -1
  98. package/dist/fields/NumberField/NumberField.d.ts.map +0 -1
  99. package/dist/fields/PasswordField/PasswordField.d.ts.map +0 -1
  100. package/dist/fields/PhoneField/PhoneField.d.ts.map +0 -1
  101. package/dist/fields/RadioField/RadioField.d.ts.map +0 -1
  102. package/dist/fields/SelectField/SelectField.d.ts.map +0 -1
  103. package/dist/fields/SliderField/SliderField.d.ts.map +0 -1
  104. package/dist/fields/SwitchField/SwitchField.d.ts.map +0 -1
  105. package/dist/fields/TextField/TextField.d.ts.map +0 -1
  106. package/dist/fields/TextareaField/TextareaField.d.ts.map +0 -1
  107. package/dist/fields/TimeField/TimeField.d.ts.map +0 -1
  108. package/dist/fields/TimeField/TimePicker.d.ts.map +0 -1
  109. package/dist/fields/defaultComponentMap.d.ts.map +0 -1
  110. package/dist/fields/defaultPlaceholders.d.ts.map +0 -1
  111. package/dist/fields/fieldUtils.d.ts.map +0 -1
  112. package/dist/fields/iconUtils.d.ts.map +0 -1
  113. package/dist/fields/icons.d.ts.map +0 -1
  114. package/dist/index.d.ts.map +0 -1
  115. package/dist/layout/Box/Box.d.ts.map +0 -1
  116. package/dist/layout/Divider/Divider.d.ts.map +0 -1
  117. package/dist/layout/Grid/Grid.d.ts.map +0 -1
  118. package/dist/layout/Spacer/Spacer.d.ts.map +0 -1
  119. package/dist/layout/Stack/Stack.d.ts.map +0 -1
  120. package/dist/layout/index.d.ts.map +0 -1
  121. package/dist/react/AxenArrayField.d.ts.map +0 -1
  122. package/dist/react/AxenField.d.ts.map +0 -1
  123. package/dist/react/AxenForm.d.ts.map +0 -1
  124. package/dist/react/FormContext.d.ts.map +0 -1
  125. package/dist/react/useAxenForm.d.ts.map +0 -1
  126. package/dist/react/useField.d.ts.map +0 -1
  127. package/dist/react/useFieldArray.d.ts.map +0 -1
  128. package/dist/types.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -5,20 +5,21 @@
5
5
  var elementStyle = document.createElement("style");
6
6
  elementStyle.appendChild(document.createTextNode(`/* ═══════════════════════════════════════════════
7
7
  axen-form CSS Reset
8
- Minimal reset for form elements
8
+ Reset only inside an Axen form or an explicit data-axen-theme wrapper
9
9
  ═══════════════════════════════════════════════ */
10
10
 
11
- *,
12
- *::before,
13
- *::after {
11
+ :where([data-axen-theme]),
12
+ :where([data-axen-theme]) *,
13
+ :where([data-axen-theme]) *::before,
14
+ :where([data-axen-theme]) *::after {
14
15
  box-sizing: border-box;
15
16
  }
16
17
 
17
18
  /* Reset form element defaults */
18
- input,
19
- textarea,
20
- select,
21
- button {
19
+ :where([data-axen-theme]) input,
20
+ :where([data-axen-theme]) textarea,
21
+ :where([data-axen-theme]) select,
22
+ :where([data-axen-theme]) button {
22
23
  font-family: inherit;
23
24
  font-size: inherit;
24
25
  line-height: inherit;
@@ -27,46 +28,46 @@ button {
27
28
  }
28
29
 
29
30
  /* Remove default styling from number inputs */
30
- input[type='number']::-webkit-inner-spin-button,
31
- input[type='number']::-webkit-outer-spin-button {
31
+ :where([data-axen-theme]) input[type='number']::-webkit-inner-spin-button,
32
+ :where([data-axen-theme]) input[type='number']::-webkit-outer-spin-button {
32
33
  -webkit-appearance: none;
33
34
  margin: 0;
34
35
  }
35
36
 
36
- input[type='number'] {
37
+ :where([data-axen-theme]) input[type='number'] {
37
38
  -moz-appearance: textfield;
38
39
  appearance: textfield;
39
40
  }
40
41
 
41
42
  /* Remove default date/time input icons if using custom components */
42
- input[type='date']::-webkit-calendar-picker-indicator,
43
- input[type='time']::-webkit-calendar-picker-indicator,
44
- input[type='datetime-local']::-webkit-calendar-picker-indicator {
43
+ :where([data-axen-theme]) input[type='date']::-webkit-calendar-picker-indicator,
44
+ :where([data-axen-theme]) input[type='time']::-webkit-calendar-picker-indicator,
45
+ :where([data-axen-theme]) input[type='datetime-local']::-webkit-calendar-picker-indicator {
45
46
  cursor: pointer;
46
47
  }
47
48
 
48
49
  /* Ensure fieldset/legend reset */
49
- fieldset {
50
+ :where([data-axen-theme]) fieldset {
50
51
  border: none;
51
52
  padding: 0;
52
53
  margin: 0;
53
54
  }
54
55
 
55
- legend {
56
+ :where([data-axen-theme]) legend {
56
57
  padding: 0;
57
58
  }
58
59
 
59
60
  /* Remove textarea resize handle (can be overridden) */
60
- textarea {
61
+ :where([data-axen-theme]) textarea {
61
62
  resize: vertical;
62
63
  }
63
64
 
64
65
  /* Focus visible only — keyboard navigation (A11y) */
65
- :focus:not(:focus-visible) {
66
+ :where([data-axen-theme]) :focus:not(:focus-visible) {
66
67
  outline: none;
67
68
  }
68
69
 
69
- :focus-visible {
70
+ :where([data-axen-theme]) :focus-visible {
70
71
  outline: var(--axen-focus-ring-width, 2px) solid
71
72
  var(--axen-focus-ring-color, rgba(25, 118, 210, 0.5));
72
73
  outline-offset: var(--axen-focus-ring-offset, 2px);
@@ -108,9 +109,6 @@ textarea {
108
109
  ═══════════════════════════════════════════════ */
109
110
 
110
111
  :root {
111
- /* Force light rendering for all native form controls (select, date, etc.) */
112
- color-scheme: light;
113
-
114
112
  /* ── Color Palette ─────────────────────────── */
115
113
  --axen-color-primary: #1976d2;
116
114
  --axen-color-primary-hover: #1565c0;
@@ -189,6 +187,11 @@ textarea {
189
187
  --axen-z-tooltip: 200;
190
188
  }
191
189
 
190
+ :where([data-axen-theme]) {
191
+ /* Keep native Axen controls light without changing the host document. */
192
+ color-scheme: light;
193
+ }
194
+
192
195
  /* ── Dark Mode ───────────────────────────────
193
196
  Removed: axen-form ships as light-only by default.
194
197
  Form elements are locked to color-scheme: light above.
@@ -231,10 +234,12 @@ import { calculateCursorPosition, formatNumber, getDecimalSeparator, getGroupSep
231
234
  import { get, pathToString, set, toPathArray } from "./core/pathUtils.js";
232
235
  import { getStrippedFields, mapPayload, pickFields } from "./core/payloadUtils.js";
233
236
  import { simpleValidator } from "./core/validation.js";
237
+ import { validateSemanticValue, withValidationPolicy } from "./core/semanticValidation.js";
234
238
  import { FormContext, useFormContext } from "./react/FormContext.js";
235
239
  import { useAxenForm } from "./react/useAxenForm.js";
236
240
  import { useField } from "./react/useField.js";
237
241
  import { useFieldArray } from "./react/useFieldArray.js";
242
+ import { useFormState, useWatch } from "./react/useFormState.js";
238
243
  import { AxenArrayField } from "./react/AxenArrayField.js";
239
244
  import { AxenField } from "./react/AxenField.js";
240
245
  import { AxenForm } from "./react/AxenForm.js";
@@ -333,5 +338,9 @@ export {
333
338
  useAxenForm,
334
339
  useField,
335
340
  useFieldArray,
336
- useFormContext
341
+ useFormContext,
342
+ useFormState,
343
+ useWatch,
344
+ validateSemanticValue,
345
+ withValidationPolicy
337
346
  };
@@ -2,5 +2,4 @@ import { BoxProps } from '../../types';
2
2
  /**
3
3
  * Utility box component for flex/block layout + spacing.
4
4
  */
5
- export declare function Box({ children, component: Component, display, flexDirection, justifyContent, alignItems, flexWrap, gap, p, px, py, m, mx, my, className, style: userStyle, }: Readonly<BoxProps>): import("react/jsx-runtime").JSX.Element;
6
- //# sourceMappingURL=Box.d.ts.map
5
+ export declare function Box({ children, component: Component, display, flexDirection, justifyContent, alignItems, flexWrap, gap, p, px, py, m, mx, my, className, style: userStyle, }: Readonly<BoxProps>): import("react").JSX.Element;
@@ -2,5 +2,4 @@ import { DividerProps } from '../../types';
2
2
  /**
3
3
  * Visual divider line — horizontal or vertical.
4
4
  */
5
- export declare function Divider({ orientation, className, style }: Readonly<DividerProps>): import("react/jsx-runtime").JSX.Element;
6
- //# sourceMappingURL=Divider.d.ts.map
5
+ export declare function Divider({ orientation, className, style }: Readonly<DividerProps>): import("react").JSX.Element;
@@ -3,5 +3,4 @@ import { GridProps } from '../../types';
3
3
  * CSS Grid-based layout component (MUI v7 Grid API).
4
4
  * 12-column responsive grid system.
5
5
  */
6
- export declare function Grid({ children, container, columns, spacing, rowSpacing, columnSpacing, size: sizeProp, xs: xsProp, sm: smProp, md: mdProp, lg: lgProp, xl: xlProp, offset, className, style: userStyle, }: Readonly<GridProps>): import("react/jsx-runtime").JSX.Element;
7
- //# sourceMappingURL=Grid.d.ts.map
6
+ export declare function Grid({ children, container, columns, spacing, rowSpacing, columnSpacing, size: sizeProp, xs: xsProp, sm: smProp, md: mdProp, lg: lgProp, xl: xlProp, offset, className, style: userStyle, }: Readonly<GridProps>): import("react").JSX.Element;
@@ -17,5 +17,4 @@ import { SpacerProps } from '../../types';
17
17
  * // Flex basis (fixed space in flex container)
18
18
  * <Spacer basis={4} /> // 32px flex-basis
19
19
  */
20
- export declare function Spacer({ x, y, basis, grow, className, style: userStyle, }: Readonly<SpacerProps>): import("react/jsx-runtime").JSX.Element;
21
- //# sourceMappingURL=Spacer.d.ts.map
20
+ export declare function Spacer({ x, y, basis, grow, className, style: userStyle, }: Readonly<SpacerProps>): import("react").JSX.Element;
@@ -3,5 +3,4 @@ import { StackProps } from '../../types';
3
3
  * Stack component — arranges children in a row or column with spacing.
4
4
  * Optionally inserts a divider between items.
5
5
  */
6
- export declare function Stack({ children, direction, spacing, alignItems, justifyContent, divider, className, style: userStyle, }: Readonly<StackProps>): import("react/jsx-runtime").JSX.Element;
7
- //# sourceMappingURL=Stack.d.ts.map
6
+ export declare function Stack({ children, direction, spacing, alignItems, justifyContent, divider, className, style: userStyle, }: Readonly<StackProps>): import("react").JSX.Element;
@@ -3,4 +3,3 @@ export { Divider } from './Divider/Divider';
3
3
  export { Grid } from './Grid/Grid';
4
4
  export { Spacer } from './Spacer/Spacer';
5
5
  export { Stack } from './Stack/Stack';
6
- //# sourceMappingURL=index.d.ts.map
@@ -11,5 +11,4 @@ export interface AxenArrayFieldProps {
11
11
  /**
12
12
  * Array field component for repeating field groups.
13
13
  */
14
- export declare function AxenArrayField({ name, config, children }: Readonly<AxenArrayFieldProps>): import("react/jsx-runtime").JSX.Element;
15
- //# sourceMappingURL=AxenArrayField.d.ts.map
14
+ export declare function AxenArrayField({ name, config, children }: Readonly<AxenArrayFieldProps>): import("react").JSX.Element;
@@ -9,5 +9,4 @@ export interface AxenFieldProps {
9
9
  * Standalone field component — can be used outside AxenForm grid
10
10
  * when you need fine-grained placement.
11
11
  */
12
- export declare function AxenField({ name, config, component, children }: Readonly<AxenFieldProps>): import("react/jsx-runtime").JSX.Element | null;
13
- //# sourceMappingURL=AxenField.d.ts.map
12
+ export declare function AxenField({ name, config, component, children }: Readonly<AxenFieldProps>): import("react").JSX.Element | null;
@@ -5,20 +5,21 @@
5
5
  var elementStyle = document.createElement("style");
6
6
  elementStyle.appendChild(document.createTextNode(`/* ═══════════════════════════════════════════════
7
7
  axen-form CSS Reset
8
- Minimal reset for form elements
8
+ Reset only inside an Axen form or an explicit data-axen-theme wrapper
9
9
  ═══════════════════════════════════════════════ */
10
10
 
11
- *,
12
- *::before,
13
- *::after {
11
+ :where([data-axen-theme]),
12
+ :where([data-axen-theme]) *,
13
+ :where([data-axen-theme]) *::before,
14
+ :where([data-axen-theme]) *::after {
14
15
  box-sizing: border-box;
15
16
  }
16
17
 
17
18
  /* Reset form element defaults */
18
- input,
19
- textarea,
20
- select,
21
- button {
19
+ :where([data-axen-theme]) input,
20
+ :where([data-axen-theme]) textarea,
21
+ :where([data-axen-theme]) select,
22
+ :where([data-axen-theme]) button {
22
23
  font-family: inherit;
23
24
  font-size: inherit;
24
25
  line-height: inherit;
@@ -27,46 +28,46 @@ button {
27
28
  }
28
29
 
29
30
  /* Remove default styling from number inputs */
30
- input[type='number']::-webkit-inner-spin-button,
31
- input[type='number']::-webkit-outer-spin-button {
31
+ :where([data-axen-theme]) input[type='number']::-webkit-inner-spin-button,
32
+ :where([data-axen-theme]) input[type='number']::-webkit-outer-spin-button {
32
33
  -webkit-appearance: none;
33
34
  margin: 0;
34
35
  }
35
36
 
36
- input[type='number'] {
37
+ :where([data-axen-theme]) input[type='number'] {
37
38
  -moz-appearance: textfield;
38
39
  appearance: textfield;
39
40
  }
40
41
 
41
42
  /* Remove default date/time input icons if using custom components */
42
- input[type='date']::-webkit-calendar-picker-indicator,
43
- input[type='time']::-webkit-calendar-picker-indicator,
44
- input[type='datetime-local']::-webkit-calendar-picker-indicator {
43
+ :where([data-axen-theme]) input[type='date']::-webkit-calendar-picker-indicator,
44
+ :where([data-axen-theme]) input[type='time']::-webkit-calendar-picker-indicator,
45
+ :where([data-axen-theme]) input[type='datetime-local']::-webkit-calendar-picker-indicator {
45
46
  cursor: pointer;
46
47
  }
47
48
 
48
49
  /* Ensure fieldset/legend reset */
49
- fieldset {
50
+ :where([data-axen-theme]) fieldset {
50
51
  border: none;
51
52
  padding: 0;
52
53
  margin: 0;
53
54
  }
54
55
 
55
- legend {
56
+ :where([data-axen-theme]) legend {
56
57
  padding: 0;
57
58
  }
58
59
 
59
60
  /* Remove textarea resize handle (can be overridden) */
60
- textarea {
61
+ :where([data-axen-theme]) textarea {
61
62
  resize: vertical;
62
63
  }
63
64
 
64
65
  /* Focus visible only — keyboard navigation (A11y) */
65
- :focus:not(:focus-visible) {
66
+ :where([data-axen-theme]) :focus:not(:focus-visible) {
66
67
  outline: none;
67
68
  }
68
69
 
69
- :focus-visible {
70
+ :where([data-axen-theme]) :focus-visible {
70
71
  outline: var(--axen-focus-ring-width, 2px) solid
71
72
  var(--axen-focus-ring-color, rgba(25, 118, 210, 0.5));
72
73
  outline-offset: var(--axen-focus-ring-offset, 2px);
@@ -108,9 +109,6 @@ textarea {
108
109
  ═══════════════════════════════════════════════ */
109
110
 
110
111
  :root {
111
- /* Force light rendering for all native form controls (select, date, etc.) */
112
- color-scheme: light;
113
-
114
112
  /* ── Color Palette ─────────────────────────── */
115
113
  --axen-color-primary: #1976d2;
116
114
  --axen-color-primary-hover: #1565c0;
@@ -189,6 +187,11 @@ textarea {
189
187
  --axen-z-tooltip: 200;
190
188
  }
191
189
 
190
+ :where([data-axen-theme]) {
191
+ /* Keep native Axen controls light without changing the host document. */
192
+ color-scheme: light;
193
+ }
194
+
192
195
  /* ── Dark Mode ───────────────────────────────
193
196
  Removed: axen-form ships as light-only by default.
194
197
  Form elements are locked to color-scheme: light above.
@@ -243,6 +246,7 @@ function AxenFormInner(props, ref) {
243
246
  onSubmit,
244
247
  components,
245
248
  validationAdapter,
249
+ validationPolicy,
246
250
  payloadFields,
247
251
  payloadDiscriminator,
248
252
  columns = 12,
@@ -270,6 +274,7 @@ function AxenFormInner(props, ref) {
270
274
  config,
271
275
  onSubmit: wrappedOnSubmit,
272
276
  validationAdapter,
277
+ validationPolicy,
273
278
  validateOnChange,
274
279
  validateOnBlur
275
280
  });
@@ -366,7 +371,8 @@ function AxenFieldRenderer({
366
371
  }) {
367
372
  const { store, components } = FormContext.useFormContext();
368
373
  const { value, error, touched, onChange, onBlur } = useField.useField(config.name);
369
- const hidden = typeof config.hidden === "function" ? config.hidden(store.getState().values) : config.hidden;
374
+ const getHidden = () => typeof config.hidden === "function" ? Boolean(config.hidden(store.getState().values)) : Boolean(config.hidden);
375
+ const hidden = react.useSyncExternalStore(store.subscribe, getHidden, getHidden);
370
376
  if (hidden) return null;
371
377
  const Component = config.component ?? components[config.type];
372
378
  if (!Component) {
@@ -4,4 +4,3 @@ export declare const AxenForm: <TValues extends Record<string, unknown>>(props:
4
4
  children?: ReactNode;
5
5
  ref?: React.Ref<AxenFormRef>;
6
6
  }) => ReactNode;
7
- //# sourceMappingURL=AxenForm.d.ts.map
@@ -5,20 +5,21 @@
5
5
  var elementStyle = document.createElement("style");
6
6
  elementStyle.appendChild(document.createTextNode(`/* ═══════════════════════════════════════════════
7
7
  axen-form CSS Reset
8
- Minimal reset for form elements
8
+ Reset only inside an Axen form or an explicit data-axen-theme wrapper
9
9
  ═══════════════════════════════════════════════ */
10
10
 
11
- *,
12
- *::before,
13
- *::after {
11
+ :where([data-axen-theme]),
12
+ :where([data-axen-theme]) *,
13
+ :where([data-axen-theme]) *::before,
14
+ :where([data-axen-theme]) *::after {
14
15
  box-sizing: border-box;
15
16
  }
16
17
 
17
18
  /* Reset form element defaults */
18
- input,
19
- textarea,
20
- select,
21
- button {
19
+ :where([data-axen-theme]) input,
20
+ :where([data-axen-theme]) textarea,
21
+ :where([data-axen-theme]) select,
22
+ :where([data-axen-theme]) button {
22
23
  font-family: inherit;
23
24
  font-size: inherit;
24
25
  line-height: inherit;
@@ -27,46 +28,46 @@ button {
27
28
  }
28
29
 
29
30
  /* Remove default styling from number inputs */
30
- input[type='number']::-webkit-inner-spin-button,
31
- input[type='number']::-webkit-outer-spin-button {
31
+ :where([data-axen-theme]) input[type='number']::-webkit-inner-spin-button,
32
+ :where([data-axen-theme]) input[type='number']::-webkit-outer-spin-button {
32
33
  -webkit-appearance: none;
33
34
  margin: 0;
34
35
  }
35
36
 
36
- input[type='number'] {
37
+ :where([data-axen-theme]) input[type='number'] {
37
38
  -moz-appearance: textfield;
38
39
  appearance: textfield;
39
40
  }
40
41
 
41
42
  /* Remove default date/time input icons if using custom components */
42
- input[type='date']::-webkit-calendar-picker-indicator,
43
- input[type='time']::-webkit-calendar-picker-indicator,
44
- input[type='datetime-local']::-webkit-calendar-picker-indicator {
43
+ :where([data-axen-theme]) input[type='date']::-webkit-calendar-picker-indicator,
44
+ :where([data-axen-theme]) input[type='time']::-webkit-calendar-picker-indicator,
45
+ :where([data-axen-theme]) input[type='datetime-local']::-webkit-calendar-picker-indicator {
45
46
  cursor: pointer;
46
47
  }
47
48
 
48
49
  /* Ensure fieldset/legend reset */
49
- fieldset {
50
+ :where([data-axen-theme]) fieldset {
50
51
  border: none;
51
52
  padding: 0;
52
53
  margin: 0;
53
54
  }
54
55
 
55
- legend {
56
+ :where([data-axen-theme]) legend {
56
57
  padding: 0;
57
58
  }
58
59
 
59
60
  /* Remove textarea resize handle (can be overridden) */
60
- textarea {
61
+ :where([data-axen-theme]) textarea {
61
62
  resize: vertical;
62
63
  }
63
64
 
64
65
  /* Focus visible only — keyboard navigation (A11y) */
65
- :focus:not(:focus-visible) {
66
+ :where([data-axen-theme]) :focus:not(:focus-visible) {
66
67
  outline: none;
67
68
  }
68
69
 
69
- :focus-visible {
70
+ :where([data-axen-theme]) :focus-visible {
70
71
  outline: var(--axen-focus-ring-width, 2px) solid
71
72
  var(--axen-focus-ring-color, rgba(25, 118, 210, 0.5));
72
73
  outline-offset: var(--axen-focus-ring-offset, 2px);
@@ -108,9 +109,6 @@ textarea {
108
109
  ═══════════════════════════════════════════════ */
109
110
 
110
111
  :root {
111
- /* Force light rendering for all native form controls (select, date, etc.) */
112
- color-scheme: light;
113
-
114
112
  /* ── Color Palette ─────────────────────────── */
115
113
  --axen-color-primary: #1976d2;
116
114
  --axen-color-primary-hover: #1565c0;
@@ -189,6 +187,11 @@ textarea {
189
187
  --axen-z-tooltip: 200;
190
188
  }
191
189
 
190
+ :where([data-axen-theme]) {
191
+ /* Keep native Axen controls light without changing the host document. */
192
+ color-scheme: light;
193
+ }
194
+
192
195
  /* ── Dark Mode ───────────────────────────────
193
196
  Removed: axen-form ships as light-only by default.
194
197
  Form elements are locked to color-scheme: light above.
@@ -222,7 +225,7 @@ textarea {
222
225
  }
223
226
  })();
224
227
  import { jsx, jsxs } from "react/jsx-runtime";
225
- import { forwardRef, useMemo, useImperativeHandle } from "react";
228
+ import { forwardRef, useMemo, useImperativeHandle, useSyncExternalStore } from "react";
226
229
  import { mapPayload } from "../core/payloadUtils.js";
227
230
  import { defaultComponentMap } from "../fields/defaultComponentMap.js";
228
231
  import { defaultPlaceholders } from "../fields/defaultPlaceholders.js";
@@ -241,6 +244,7 @@ function AxenFormInner(props, ref) {
241
244
  onSubmit,
242
245
  components,
243
246
  validationAdapter,
247
+ validationPolicy,
244
248
  payloadFields,
245
249
  payloadDiscriminator,
246
250
  columns = 12,
@@ -268,6 +272,7 @@ function AxenFormInner(props, ref) {
268
272
  config,
269
273
  onSubmit: wrappedOnSubmit,
270
274
  validationAdapter,
275
+ validationPolicy,
271
276
  validateOnChange,
272
277
  validateOnBlur
273
278
  });
@@ -364,7 +369,8 @@ function AxenFieldRenderer({
364
369
  }) {
365
370
  const { store, components } = useFormContext();
366
371
  const { value, error, touched, onChange, onBlur } = useField(config.name);
367
- const hidden = typeof config.hidden === "function" ? config.hidden(store.getState().values) : config.hidden;
372
+ const getHidden = () => typeof config.hidden === "function" ? Boolean(config.hidden(store.getState().values)) : Boolean(config.hidden);
373
+ const hidden = useSyncExternalStore(store.subscribe, getHidden, getHidden);
368
374
  if (hidden) return null;
369
375
  const Component = config.component ?? components[config.type];
370
376
  if (!Component) {
@@ -8,4 +8,3 @@ export interface FormContextValue {
8
8
  }
9
9
  export declare const FormContext: import('react').Context<FormContextValue | null>;
10
10
  export declare function useFormContext(): FormContextValue;
11
- //# sourceMappingURL=FormContext.d.ts.map
@@ -3,25 +3,32 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const react = require("react");
4
4
  const formStore = require("../core/formStore.cjs");
5
5
  const validation = require("../core/validation.cjs");
6
+ const semanticValidation = require("../core/semanticValidation.cjs");
7
+ const types = require("../types.cjs");
6
8
  function useAxenForm(props) {
7
9
  const {
8
10
  config,
9
11
  onSubmit,
10
12
  validationAdapter,
13
+ validationPolicy,
11
14
  validateOnChange = true,
12
15
  validateOnBlur = true
13
16
  } = props;
14
- const adapter = validationAdapter ?? validation.simpleValidator;
17
+ const adapter = semanticValidation.withValidationPolicy(validationAdapter ?? validation.simpleValidator, validationPolicy ?? config.validationPolicy);
18
+ const onSubmitRef = react.useRef(onSubmit);
19
+ onSubmitRef.current = onSubmit;
20
+ const isUpdateRef = react.useRef(config.isUpdate ?? false);
21
+ isUpdateRef.current = config.isUpdate ?? false;
15
22
  const storeRef = react.useRef(null);
16
23
  storeRef.current ?? (storeRef.current = formStore.createFormStore({
17
24
  initialValues: config.initialValues,
18
25
  validationAdapter: adapter,
19
- fields: config.fields,
26
+ fields: config.fields.flatMap((item) => types.isFieldGroup(item) ? item.fields : [item]),
20
27
  validateOnChange,
21
28
  validateOnBlur,
22
29
  onSubmit: async (values) => {
23
- await onSubmit(values, {
24
- isUpdate: config.isUpdate ?? false,
30
+ await onSubmitRef.current(values, {
31
+ isUpdate: isUpdateRef.current,
25
32
  resetForm: () => {
26
33
  var _a;
27
34
  return (_a = storeRef.current) == null ? void 0 : _a.resetForm();
@@ -4,5 +4,4 @@ export interface UseAxenFormReturn<TValues extends Record<string, unknown>> {
4
4
  store: FormStore<TValues>;
5
5
  formApi: FormApi<TValues>;
6
6
  }
7
- export declare function useAxenForm<TValues extends Record<string, unknown>>(props: Pick<AxenFormProps<TValues>, 'config' | 'onSubmit' | 'validationAdapter' | 'validateOnChange' | 'validateOnBlur' | 'payloadFields' | 'payloadDiscriminator'>): UseAxenFormReturn<TValues>;
8
- //# sourceMappingURL=useAxenForm.d.ts.map
7
+ export declare function useAxenForm<TValues extends Record<string, unknown>>(props: Pick<AxenFormProps<TValues>, 'config' | 'onSubmit' | 'validationAdapter' | 'validationPolicy' | 'validateOnChange' | 'validateOnBlur' | 'payloadFields' | 'payloadDiscriminator'>): UseAxenFormReturn<TValues>;
@@ -1,25 +1,32 @@
1
1
  import { useRef, useMemo } from "react";
2
2
  import { createFormStore } from "../core/formStore.js";
3
3
  import { simpleValidator } from "../core/validation.js";
4
+ import { withValidationPolicy } from "../core/semanticValidation.js";
5
+ import { isFieldGroup } from "../types.js";
4
6
  function useAxenForm(props) {
5
7
  const {
6
8
  config,
7
9
  onSubmit,
8
10
  validationAdapter,
11
+ validationPolicy,
9
12
  validateOnChange = true,
10
13
  validateOnBlur = true
11
14
  } = props;
12
- const adapter = validationAdapter ?? simpleValidator;
15
+ const adapter = withValidationPolicy(validationAdapter ?? simpleValidator, validationPolicy ?? config.validationPolicy);
16
+ const onSubmitRef = useRef(onSubmit);
17
+ onSubmitRef.current = onSubmit;
18
+ const isUpdateRef = useRef(config.isUpdate ?? false);
19
+ isUpdateRef.current = config.isUpdate ?? false;
13
20
  const storeRef = useRef(null);
14
21
  storeRef.current ?? (storeRef.current = createFormStore({
15
22
  initialValues: config.initialValues,
16
23
  validationAdapter: adapter,
17
- fields: config.fields,
24
+ fields: config.fields.flatMap((item) => isFieldGroup(item) ? item.fields : [item]),
18
25
  validateOnChange,
19
26
  validateOnBlur,
20
27
  onSubmit: async (values) => {
21
- await onSubmit(values, {
22
- isUpdate: config.isUpdate ?? false,
28
+ await onSubmitRef.current(values, {
29
+ isUpdate: isUpdateRef.current,
23
30
  resetForm: () => {
24
31
  var _a;
25
32
  return (_a = storeRef.current) == null ? void 0 : _a.resetForm();
@@ -6,14 +6,17 @@ function useField(name) {
6
6
  const { store } = FormContext.useFormContext();
7
7
  const value = react.useSyncExternalStore(
8
8
  (cb) => store.subscribeField(name, cb),
9
+ () => store.getFieldValue(name),
9
10
  () => store.getFieldValue(name)
10
11
  );
11
12
  const error = react.useSyncExternalStore(
12
13
  (cb) => store.subscribeField(name, cb),
14
+ () => store.getFieldError(name),
13
15
  () => store.getFieldError(name)
14
16
  );
15
17
  const touched = react.useSyncExternalStore(
16
18
  (cb) => store.subscribeField(name, cb),
19
+ () => store.getFieldTouched(name),
17
20
  () => store.getFieldTouched(name)
18
21
  );
19
22
  const onChange = react.useCallback(
@@ -10,4 +10,3 @@ export interface UseFieldReturn {
10
10
  setError: (error: string | undefined) => void;
11
11
  }
12
12
  export declare function useField(name: string): UseFieldReturn;
13
- //# sourceMappingURL=useField.d.ts.map
@@ -4,14 +4,17 @@ function useField(name) {
4
4
  const { store } = useFormContext();
5
5
  const value = useSyncExternalStore(
6
6
  (cb) => store.subscribeField(name, cb),
7
+ () => store.getFieldValue(name),
7
8
  () => store.getFieldValue(name)
8
9
  );
9
10
  const error = useSyncExternalStore(
10
11
  (cb) => store.subscribeField(name, cb),
12
+ () => store.getFieldError(name),
11
13
  () => store.getFieldError(name)
12
14
  );
13
15
  const touched = useSyncExternalStore(
14
16
  (cb) => store.subscribeField(name, cb),
17
+ () => store.getFieldTouched(name),
15
18
  () => store.getFieldTouched(name)
16
19
  );
17
20
  const onChange = useCallback(