@mapples/form 1.1.2 → 1.2.2

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.
@@ -13,6 +13,7 @@ import { FormProps } from '../types';
13
13
  * @param props.ref - Optional ref to access the FormState instance directly
14
14
  * @param props.historyItems - Number of history states to keep for undo/redo (0 to disable)
15
15
  * @param props.historyItemThrottle - Save a history entry every Nth change
16
+ * @param props.historyCoalesceMs - Coalesce write bursts into one history entry
16
17
  * @param props.filterHistoryKeys - Field paths to exclude from history tracking
17
18
  * @param props.onChange - Callback invoked whenever any form value changes
18
19
  * @returns JSX element that provides form context to children
@@ -41,6 +42,6 @@ import { FormProps } from '../types';
41
42
  *
42
43
  * @public
43
44
  */
44
- declare const Form: <T extends object>({ children, initialValues, validationSchema, onSubmit, ref, historyItems, filterHistoryKeys, historyItemThrottle, onChange, }: PropsWithChildren<FormProps<T>>) => import("react").JSX.Element;
45
+ declare const Form: <T extends object>({ children, initialValues, validationSchema, onSubmit, ref, historyItems, filterHistoryKeys, historyItemThrottle, historyCoalesceMs, onChange, }: PropsWithChildren<FormProps<T>>) => import("react").JSX.Element;
45
46
  export default Form;
46
47
  //# sourceMappingURL=Form.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Form.d.ts","sourceRoot":"","sources":["../../src/components/Form.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EAKlB,MAAM,OAAO,CAAC;AAOf,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,QAAA,MAAM,IAAI,GAAI,CAAC,SAAS,MAAM,EAAE,+HAU7B,iBAAiB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,gCAoGjC,CAAC;AAEF,eAAe,IAAI,CAAC"}
1
+ {"version":3,"file":"Form.d.ts","sourceRoot":"","sources":["../../src/components/Form.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EAKlB,MAAM,OAAO,CAAC;AAOf,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,QAAA,MAAM,IAAI,GAAI,CAAC,SAAS,MAAM,EAAE,kJAW7B,iBAAiB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,gCAuGjC,CAAC;AAEF,eAAe,IAAI,CAAC"}
@@ -15,6 +15,7 @@ import FormState from '../state';
15
15
  * @param props.ref - Optional ref to access the FormState instance directly
16
16
  * @param props.historyItems - Number of history states to keep for undo/redo (0 to disable)
17
17
  * @param props.historyItemThrottle - Save a history entry every Nth change
18
+ * @param props.historyCoalesceMs - Coalesce write bursts into one history entry
18
19
  * @param props.filterHistoryKeys - Field paths to exclude from history tracking
19
20
  * @param props.onChange - Callback invoked whenever any form value changes
20
21
  * @returns JSX element that provides form context to children
@@ -43,7 +44,7 @@ import FormState from '../state';
43
44
  *
44
45
  * @public
45
46
  */
46
- const Form = ({ children, initialValues, validationSchema, onSubmit, ref, historyItems, filterHistoryKeys, historyItemThrottle, onChange, }) => {
47
+ const Form = ({ children, initialValues, validationSchema, onSubmit, ref, historyItems, filterHistoryKeys, historyItemThrottle, historyCoalesceMs, onChange, }) => {
47
48
  const [touched, setTouched] = useState({});
48
49
  const [submitting, setSubmitting] = useState(false);
49
50
  const [validationErrors, setValidationErrors] = useState(null);
@@ -52,7 +53,7 @@ const Form = ({ children, initialValues, validationSchema, onSubmit, ref, histor
52
53
  // when initialValues/schema change identity (e.g. inline object literals on
53
54
  // every parent render), which would otherwise wipe edited values back to the
54
55
  // initial state. We never call the setter, so this never triggers a re-render.
55
- const [form] = useState(() => new FormState(initialValues, validationSchema, historyItems, historyItemThrottle, filterHistoryKeys));
56
+ const [form] = useState(() => new FormState(initialValues, validationSchema, historyItems, historyItemThrottle, filterHistoryKeys, historyCoalesceMs));
56
57
  useEffect(() => {
57
58
  if (!onChange)
58
59
  return;
@@ -81,12 +82,13 @@ const Form = ({ children, initialValues, validationSchema, onSubmit, ref, histor
81
82
  setSubmitting(false);
82
83
  return;
83
84
  }
85
+ // `onSubmit` belongs to the consumer: it is responsible for reporting its
86
+ // own failures. Anything that escapes it is a real fault and must reach the
87
+ // caller rather than being swallowed here — `finally` only clears the
88
+ // submitting flag.
84
89
  try {
85
90
  await onSubmit?.(form.getValues());
86
91
  }
87
- catch (e) {
88
- console.error(e);
89
- }
90
92
  finally {
91
93
  setSubmitting(false);
92
94
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Form.js","sourceRoot":"","sources":["../../src/components/Form.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Form.js","sourceRoot":"","sources":["../../src/components/Form.tsx"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useBindFormField.d.ts","sourceRoot":"","sources":["../../src/hooks/useBindFormField.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,QAAA,MAAM,gBAAgB,GAAI,CAAC,EAAE,WAAW,MAAM,EAAE,eAAe,MAAM;;;;;;;yBAoBtD,CAAC;CAef,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"useBindFormField.d.ts","sourceRoot":"","sources":["../../src/hooks/useBindFormField.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,QAAA,MAAM,gBAAgB,GAAI,CAAC,EAAE,WAAW,MAAM,EAAE,eAAe,MAAM;;;;;;;yBAyBtD,CAAC;CAef,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -34,7 +34,12 @@ const useBindFormField = (fieldPath, fallBackPath) => {
34
34
  const [value, setValue] = useState();
35
35
  useEffect(() => {
36
36
  const unsubscribe = form?.subscribeOnChange((v) => {
37
- if (fallBackPath) {
37
+ // Every document change notifies this subscription, including changes
38
+ // that moved or removed the node `fieldPath` points at — the path is
39
+ // fixed at subscribe time and goes stale until the effect re-runs. A
40
+ // source that no longer resolves has nothing to mirror; propagating
41
+ // its `undefined` would only write at the stale location.
42
+ if (fallBackPath && v !== undefined) {
38
43
  form?.setValue(fallBackPath, v);
39
44
  }
40
45
  setValue(v);
@@ -1 +1 @@
1
- {"version":3,"file":"useBindFormField.js","sourceRoot":"","sources":["../../src/hooks/useBindFormField.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEzD,OAAO,aAAa,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,gBAAgB,GAAG,CAAI,SAAiB,EAAE,YAAqB,EAAE,EAAE;IACvE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GACvD,aAAa,CAAC,SAAS,CAAC,CAAC;IAE3B,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,EAAiB,CAAC;IAEpD,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,WAAW,GAAG,IAAI,EAAE,iBAAiB,CAAI,CAAC,CAAC,EAAE,EAAE;YACnD,IAAI,YAAY,EAAE,CAAC;gBACjB,IAAI,EAAE,QAAQ,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;YAClC,CAAC;YACD,QAAQ,CAAC,CAAC,CAAC,CAAC;QACd,CAAC,EAAE,SAAS,CAAC,CAAC;QAEd,OAAO,GAAG,EAAE;YACV,WAAW,EAAE,EAAE,CAAC;QAClB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,YAAY,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC;IAEpC,MAAM,cAAc,GAAG,WAAW,CAChC,CAAC,QAAW,EAAE,EAAE;QACd,IAAI,EAAE,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACtC,CAAC,EACD,CAAC,SAAS,EAAE,IAAI,CAAC,CAClB,CAAC;IAEF,OAAO;QACL,KAAK;QACL,KAAK;QACL,MAAM;QACN,SAAS;QACT,QAAQ;QACR,KAAK;QACL,QAAQ,EAAE,cAAc;KACzB,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"useBindFormField.js","sourceRoot":"","sources":["../../src/hooks/useBindFormField.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEzD,OAAO,aAAa,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,gBAAgB,GAAG,CAAI,SAAiB,EAAE,YAAqB,EAAE,EAAE;IACvE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GACvD,aAAa,CAAC,SAAS,CAAC,CAAC;IAE3B,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,EAAiB,CAAC;IAEpD,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,WAAW,GAAG,IAAI,EAAE,iBAAiB,CAAI,CAAC,CAAC,EAAE,EAAE;YACnD,sEAAsE;YACtE,qEAAqE;YACrE,qEAAqE;YACrE,oEAAoE;YACpE,0DAA0D;YAC1D,IAAI,YAAY,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;gBACpC,IAAI,EAAE,QAAQ,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;YAClC,CAAC;YACD,QAAQ,CAAC,CAAC,CAAC,CAAC;QACd,CAAC,EAAE,SAAS,CAAC,CAAC;QAEd,OAAO,GAAG,EAAE;YACV,WAAW,EAAE,EAAE,CAAC;QAClB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,YAAY,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC;IAEpC,MAAM,cAAc,GAAG,WAAW,CAChC,CAAC,QAAW,EAAE,EAAE;QACd,IAAI,EAAE,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACtC,CAAC,EACD,CAAC,SAAS,EAAE,IAAI,CAAC,CAClB,CAAC;IAEF,OAAO;QACL,KAAK;QACL,KAAK;QACL,MAAM;QACN,SAAS;QACT,QAAQ;QACR,KAAK;QACL,QAAQ,EAAE,cAAc;KACzB,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -1,6 +1,8 @@
1
1
  /**
2
2
  * Hook to manage boolean toggle fields.
3
3
  * This hook provides a simple way to handle boolean fields with toggle functionality.
4
+ * The value follows the stored field (an existing `true` renders as on), not
5
+ * only the toggles made through this hook.
4
6
  *
5
7
  * @param fieldPath - Dot notation path to the boolean field
6
8
  * @returns Object containing current value and toggle function
@@ -1 +1 @@
1
- {"version":3,"file":"useFormFieldToggle.d.ts","sourceRoot":"","sources":["../../src/hooks/useFormFieldToggle.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,QAAA,MAAM,kBAAkB,GAAI,WAAW,MAAM;;;CAiB5C,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"useFormFieldToggle.d.ts","sourceRoot":"","sources":["../../src/hooks/useFormFieldToggle.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,QAAA,MAAM,kBAAkB,GAAI,WAAW,MAAM;;;CA4B5C,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -1,8 +1,10 @@
1
- import { useState } from 'react';
1
+ import { useEffect, useState } from 'react';
2
2
  import useForm from './useForm';
3
3
  /**
4
4
  * Hook to manage boolean toggle fields.
5
5
  * This hook provides a simple way to handle boolean fields with toggle functionality.
6
+ * The value follows the stored field (an existing `true` renders as on), not
7
+ * only the toggles made through this hook.
6
8
  *
7
9
  * @param fieldPath - Dot notation path to the boolean field
8
10
  * @returns Object containing current value and toggle function
@@ -29,13 +31,17 @@ import useForm from './useForm';
29
31
  */
30
32
  const useFormFieldToggle = (fieldPath) => {
31
33
  const { form } = useForm();
32
- const [value, setValue] = useState(false);
34
+ const [value, setValue] = useState(() => !!form?.getValue(fieldPath));
35
+ useEffect(() => {
36
+ const unsubscribe = form?.subscribeOnChange((next) => setValue(!!next), fieldPath);
37
+ return () => {
38
+ unsubscribe?.();
39
+ };
40
+ }, [fieldPath, form]);
33
41
  const handleToggleValue = () => {
34
- setValue((prev) => {
35
- const nextValue = !prev;
36
- form?.setValue(fieldPath, nextValue);
37
- return nextValue;
38
- });
42
+ const nextValue = !value;
43
+ setValue(nextValue);
44
+ form?.setValue(fieldPath, nextValue);
39
45
  };
40
46
  return {
41
47
  value,
@@ -1 +1 @@
1
- {"version":3,"file":"useFormFieldToggle.js","sourceRoot":"","sources":["../../src/hooks/useFormFieldToggle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEjC,OAAO,OAAO,MAAM,WAAW,CAAC;AAEhC;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,kBAAkB,GAAG,CAAC,SAAiB,EAAE,EAAE;IAC/C,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,EAAE,CAAC;IAE3B,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAE1C,MAAM,iBAAiB,GAAG,GAAG,EAAE;QAC7B,QAAQ,CAAC,CAAC,IAAI,EAAE,EAAE;YAChB,MAAM,SAAS,GAAG,CAAC,IAAI,CAAC;YACxB,IAAI,EAAE,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;YACrC,OAAO,SAAS,CAAC;QACnB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,OAAO;QACL,KAAK;QACL,WAAW,EAAE,iBAAiB;KAC/B,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"useFormFieldToggle.js","sourceRoot":"","sources":["../../src/hooks/useFormFieldToggle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE5C,OAAO,OAAO,MAAM,WAAW,CAAC;AAEhC;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,kBAAkB,GAAG,CAAC,SAAiB,EAAE,EAAE;IAC/C,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,EAAE,CAAC;IAE3B,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAChC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAsB,SAAS,CAAC,CACvD,CAAC;IAEF,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,WAAW,GAAG,IAAI,EAAE,iBAAiB,CACzC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,EAC1B,SAAS,CACV,CAAC;QAEF,OAAO,GAAG,EAAE;YACV,WAAW,EAAE,EAAE,CAAC;QAClB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC;IAEtB,MAAM,iBAAiB,GAAG,GAAG,EAAE;QAC7B,MAAM,SAAS,GAAG,CAAC,KAAK,CAAC;QACzB,QAAQ,CAAC,SAAS,CAAC,CAAC;QACpB,IAAI,EAAE,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IACvC,CAAC,CAAC;IAEF,OAAO;QACL,KAAK;QACL,WAAW,EAAE,iBAAiB;KAC/B,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -20,6 +20,10 @@ declare class FormState<T extends object> {
20
20
  private readonly historyItemThrottle;
21
21
  private saveCounter;
22
22
  private readonly filterHistoryKeys;
23
+ private readonly historyCoalesceMs;
24
+ private lastHistoryWriteAt;
25
+ private historyDirty;
26
+ private listenerSeq;
23
27
  /**
24
28
  * Creates a new FormState instance.
25
29
  *
@@ -28,8 +32,11 @@ declare class FormState<T extends object> {
28
32
  * @param historyItems - Number of history items to keep for undo/redo (0 to disable)
29
33
  * @param historyItemThrottle - Throttle for saving history items (every Nth change)
30
34
  * @param filterHistoryKeys - Array of field paths to exclude from history tracking
35
+ * @param historyCoalesceMs - Coalesce writes closer together than this into
36
+ * one history entry (a typing or drag burst becomes a single undo step).
37
+ * 0 keeps the counter-throttle behavior.
31
38
  */
32
- constructor(initialValues: T, validationSchema?: ObjectSchema<T>, historyItems?: number, historyItemThrottle?: number, filterHistoryKeys?: string[]);
39
+ constructor(initialValues: T, validationSchema?: ObjectSchema<T>, historyItems?: number, historyItemThrottle?: number, filterHistoryKeys?: string[], historyCoalesceMs?: number);
33
40
  /**
34
41
  * Gets a deep copy of all form values.
35
42
  *
@@ -63,6 +70,14 @@ declare class FormState<T extends object> {
63
70
  * @param value - The value to set at the specified path
64
71
  */
65
72
  setValue<V>(path: string, value?: V): void;
73
+ /**
74
+ * Whether a write to `writePath` affects a subscriber at `subscriptionPath`.
75
+ * Segment-aware in both directions: a write below the subscription changes
76
+ * the subscribed value's contents; a write at or above it replaces the
77
+ * subscribed value outright. Root subscribers (no path) see every write.
78
+ * @private
79
+ */
80
+ private pathsIntersect;
66
81
  /**
67
82
  * Subscribes to form value changes.
68
83
  *
@@ -93,7 +108,16 @@ declare class FormState<T extends object> {
93
108
  * @throws Throws validation errors if validation fails
94
109
  */
95
110
  validate(): Promise<void>;
111
+ /**
112
+ * Commits the end state of a coalescing burst that has gone quiet. MUST run
113
+ * before the next mutation lands in `this.values` — afterwards the burst's
114
+ * true end state is gone. Called at the top of `setValue`/`setValues`, and
115
+ * from `prev()` so an undo taken mid-burst leaves the burst redoable.
116
+ * @private
117
+ */
118
+ private finalizeStaleHistoryBurst;
96
119
  private saveToHistory;
120
+ private pushHistoryEntry;
97
121
  /**
98
122
  * Moves to the previous state in the form history (undo operation).
99
123
  *
@@ -118,6 +142,18 @@ declare class FormState<T extends object> {
118
142
  * @returns True if redo operation is possible
119
143
  */
120
144
  canNext(): boolean;
145
+ /**
146
+ * Delivers the current values to subscribers — all of them, or only those
147
+ * whose path intersects `changedPath` when one is given.
148
+ *
149
+ * Cost discipline: the editor holds hundreds of subscriptions on one large
150
+ * document, and this runs on every write. Pathed subscribers get a clone of
151
+ * just their subtree; root subscribers share ONE lazily-created full clone
152
+ * per pass. Subscribers must therefore treat delivered values as read-only.
153
+ * Fresh clones (not `this.values` references) are still required so React
154
+ * state setters see a new identity per delivery.
155
+ * @private
156
+ */
121
157
  private notifySubscriptions;
122
158
  private notifyHistorySubscriptions;
123
159
  private remapValidationError;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/state/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,KAAK,CAAC;AAEpD,OAAO,KAAK,EAEV,oBAAoB,EAErB,MAAM,UAAU,CAAC;AAElB;;;;;;;GAOG;AACH,cAAM,SAAS,CAAC,CAAC,SAAS,MAAM;IAC9B,OAAO,CAAC,MAAM,CAAI;IAElB,OAAO,CAAC,gBAAgB,CAAC,CAAkB;IAC3C,OAAO,CAAC,aAAa,CAA6C;IAClE,OAAO,CAAC,wBAAwB,CAAsC;IACtE,OAAO,CAAC,MAAM,CAAkD;IAEhE,OAAO,CAAC,OAAO,CAAW;IAC1B,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAC7C,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAW;IAE7C;;;;;;;;OAQG;gBAED,aAAa,EAAE,CAAC,EAChB,gBAAgB,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,EAClC,YAAY,GAAE,MAAU,EACxB,mBAAmB,GAAE,MAAU,EAC/B,iBAAiB,GAAE,MAAM,EAAO;IAclC;;;;OAIG;IACH,SAAS;IAIT;;;;OAIG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,EAAE,CAAC;IAInD;;;;OAIG;IACH,SAAS;IAIT;;;;;OAKG;IACH,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI;IAcpD;;;;;;OAMG;IACH,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC;IAwBnC;;;;;;;OAOG;IACH,iBAAiB,CAAC,CAAC,EAAE,EAAE,EAAE,oBAAoB,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM;IAa/D;;;;;OAKG;IACH,wBAAwB,CAAC,EAAE,EAAE,MAAM,IAAI;IAQvC;;;;;;OAMG;IACH,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,CAAC;IAI7B;;;;OAIG;IACG,QAAQ;IAYd,OAAO,CAAC,aAAa;IA4BrB;;;;OAIG;IACH,IAAI;IASJ;;;;OAIG;IACH,IAAI;IASJ;;;;OAIG;IACH,OAAO;IAIP;;;;OAIG;IACH,OAAO;IAIP,OAAO,CAAC,mBAAmB;IAU3B,OAAO,CAAC,0BAA0B;IAMlC,OAAO,CAAC,oBAAoB;IAI5B,OAAO,CAAC,mBAAmB;IAI3B,OAAO,CAAC,0BAA0B;IAIlC,OAAO,CAAC,kBAAkB;CAM3B;AAED,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/state/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,KAAK,CAAC;AAEpD,OAAO,KAAK,EAEV,oBAAoB,EAErB,MAAM,UAAU,CAAC;AAElB;;;;;;;GAOG;AACH,cAAM,SAAS,CAAC,CAAC,SAAS,MAAM;IAC9B,OAAO,CAAC,MAAM,CAAI;IAElB,OAAO,CAAC,gBAAgB,CAAC,CAAkB;IAC3C,OAAO,CAAC,aAAa,CAA6C;IAClE,OAAO,CAAC,wBAAwB,CAAsC;IACtE,OAAO,CAAC,MAAM,CAAkD;IAEhE,OAAO,CAAC,OAAO,CAAW;IAC1B,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAC7C,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAW;IAC7C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,kBAAkB,CAAa;IACvC,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,WAAW,CAAa;IAEhC;;;;;;;;;;;OAWG;gBAED,aAAa,EAAE,CAAC,EAChB,gBAAgB,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,EAClC,YAAY,GAAE,MAAU,EACxB,mBAAmB,GAAE,MAAU,EAC/B,iBAAiB,GAAE,MAAM,EAAO,EAChC,iBAAiB,GAAE,MAAU;IAe/B;;;;OAIG;IACH,SAAS;IAIT;;;;OAIG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,EAAE,CAAC;IAInD;;;;OAIG;IACH,SAAS;IAIT;;;;;OAKG;IACH,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI;IAQpD;;;;;;OAMG;IACH,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC;IA2BnC;;;;;;OAMG;IACH,OAAO,CAAC,cAAc;IAQtB;;;;;;;OAOG;IACH,iBAAiB,CAAC,CAAC,EAAE,EAAE,EAAE,oBAAoB,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM;IAa/D;;;;;OAKG;IACH,wBAAwB,CAAC,EAAE,EAAE,MAAM,IAAI;IAQvC;;;;;;OAMG;IACH,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,CAAC;IAI7B;;;;OAIG;IACG,QAAQ;IAYd;;;;;;OAMG;IACH,OAAO,CAAC,yBAAyB;IAQjC,OAAO,CAAC,aAAa;IAuCrB,OAAO,CAAC,gBAAgB;IAaxB;;;;OAIG;IACH,IAAI;IAgBJ;;;;OAIG;IACH,IAAI;IASJ;;;;OAIG;IACH,OAAO;IAIP;;;;OAIG;IACH,OAAO;IAIP;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,mBAAmB;IAyB3B,OAAO,CAAC,0BAA0B;IAMlC,OAAO,CAAC,oBAAoB;IAI5B,OAAO,CAAC,mBAAmB;IAI3B,OAAO,CAAC,0BAA0B;IAIlC,OAAO,CAAC,kBAAkB;CAK3B;AAED,eAAe,SAAS,CAAC"}
@@ -1,4 +1,4 @@
1
- import { cloneDeep, get, set, groupBy, isEqual, some } from 'lodash';
1
+ import { cloneDeep, get, has, set, groupBy, isEqual, some } from 'lodash';
2
2
  /**
3
3
  * FormState manages the state of a form including values, validation, history, and subscriptions.
4
4
  * It provides methods to get/set values, validate data, manage history for undo/redo functionality,
@@ -19,6 +19,10 @@ class FormState {
19
19
  historyItemThrottle;
20
20
  saveCounter = 0;
21
21
  filterHistoryKeys;
22
+ historyCoalesceMs;
23
+ lastHistoryWriteAt = 0;
24
+ historyDirty = false;
25
+ listenerSeq = 0;
22
26
  /**
23
27
  * Creates a new FormState instance.
24
28
  *
@@ -27,13 +31,17 @@ class FormState {
27
31
  * @param historyItems - Number of history items to keep for undo/redo (0 to disable)
28
32
  * @param historyItemThrottle - Throttle for saving history items (every Nth change)
29
33
  * @param filterHistoryKeys - Array of field paths to exclude from history tracking
34
+ * @param historyCoalesceMs - Coalesce writes closer together than this into
35
+ * one history entry (a typing or drag burst becomes a single undo step).
36
+ * 0 keeps the counter-throttle behavior.
30
37
  */
31
- constructor(initialValues, validationSchema, historyItems = 0, historyItemThrottle = 5, filterHistoryKeys = []) {
38
+ constructor(initialValues, validationSchema, historyItems = 0, historyItemThrottle = 5, filterHistoryKeys = [], historyCoalesceMs = 0) {
32
39
  this.values = cloneDeep(initialValues);
33
40
  this.validationSchema = validationSchema;
34
41
  this.historyItems = historyItems;
35
42
  this.historyItemThrottle = historyItemThrottle;
36
43
  this.filterHistoryKeys = filterHistoryKeys;
44
+ this.historyCoalesceMs = historyCoalesceMs;
37
45
  if (this.historyItems > 0) {
38
46
  this.history = [cloneDeep(initialValues)];
39
47
  this.historyIndex = 0;
@@ -70,13 +78,10 @@ class FormState {
70
78
  * @param cb - Optional callback invoked with `true` after the values and subscriptions have been updated
71
79
  */
72
80
  setValues(values, cb) {
81
+ this.finalizeStaleHistoryBurst();
73
82
  this.values = cloneDeep(values);
74
83
  this.saveToHistory();
75
- this.subscriptions.forEach((subscription) => {
76
- subscription.cb(subscription.path
77
- ? get(this.getValues(), subscription.path)
78
- : this.getValues());
79
- });
84
+ this.notifySubscriptions();
80
85
  cb?.(true);
81
86
  }
82
87
  /**
@@ -87,20 +92,42 @@ class FormState {
87
92
  * @param value - The value to set at the specified path
88
93
  */
89
94
  setValue(path, value) {
95
+ // `set` builds every missing segment of the path. That is what makes
96
+ // writing a nested field work, but it also means a write to a path that
97
+ // no longer exists FABRICATES the structure it names. Field paths do go
98
+ // stale — a subscriber keeps the path it was created with, and the node
99
+ // it points at can move or be removed before the subscription is torn
100
+ // down. Writing `undefined` there has nothing to clear, so the only
101
+ // effect would be that fabricated structure: refuse it.
102
+ if (value === undefined && !has(this.values, path)) {
103
+ return;
104
+ }
105
+ this.finalizeStaleHistoryBurst();
90
106
  set(this.values, path, value);
91
107
  if (!some(this.filterHistoryKeys, (value) => {
92
108
  return path.includes(value);
93
109
  })) {
94
110
  this.saveToHistory();
95
111
  }
96
- this.subscriptions.forEach((subscription) => {
97
- if (!path.includes(subscription.path || '')) {
98
- return;
99
- }
100
- subscription.cb(subscription.path
101
- ? get(this.getValues(), subscription.path)
102
- : this.getValues());
103
- });
112
+ this.notifySubscriptions(path);
113
+ }
114
+ /**
115
+ * Whether a write to `writePath` affects a subscriber at `subscriptionPath`.
116
+ * Segment-aware in both directions: a write below the subscription changes
117
+ * the subscribed value's contents; a write at or above it replaces the
118
+ * subscribed value outright. Root subscribers (no path) see every write.
119
+ * @private
120
+ */
121
+ pathsIntersect(writePath, subscriptionPath) {
122
+ if (!subscriptionPath)
123
+ return true;
124
+ if (writePath === subscriptionPath)
125
+ return true;
126
+ if (writePath.startsWith(subscriptionPath + '.'))
127
+ return true;
128
+ if (subscriptionPath.startsWith(writePath + '.'))
129
+ return true;
130
+ return false;
104
131
  }
105
132
  /**
106
133
  * Subscribes to form value changes.
@@ -138,7 +165,7 @@ class FormState {
138
165
  * @returns The value at the specified path or all form values
139
166
  */
140
167
  getValue(path) {
141
- return cloneDeep(path ? get(this.getValues(), path) : this.getValues());
168
+ return cloneDeep(path ? get(this.values, path) : this.values);
142
169
  }
143
170
  /**
144
171
  * Validates the form using the provided validation schema.
@@ -157,9 +184,40 @@ class FormState {
157
184
  throw errors;
158
185
  }
159
186
  }
187
+ /**
188
+ * Commits the end state of a coalescing burst that has gone quiet. MUST run
189
+ * before the next mutation lands in `this.values` — afterwards the burst's
190
+ * true end state is gone. Called at the top of `setValue`/`setValues`, and
191
+ * from `prev()` so an undo taken mid-burst leaves the burst redoable.
192
+ * @private
193
+ */
194
+ finalizeStaleHistoryBurst() {
195
+ if (!this.historyDirty)
196
+ return;
197
+ if (Date.now() - this.lastHistoryWriteAt <= this.historyCoalesceMs)
198
+ return;
199
+ this.history[this.historyIndex] = cloneDeep(this.values);
200
+ this.historyDirty = false;
201
+ }
160
202
  saveToHistory() {
161
203
  if (this.historyItems === 0)
162
204
  return;
205
+ if (this.historyCoalesceMs > 0) {
206
+ this.lastHistoryWriteAt = Date.now();
207
+ // `finalizeStaleHistoryBurst` ran before this write landed, so a
208
+ // still-dirty entry means this write continues the current burst: its
209
+ // end state is captured when the burst finalizes. No clone, no compare.
210
+ if (this.historyDirty)
211
+ return;
212
+ const currentValues = cloneDeep(this.values);
213
+ if (this.history.length > 0 &&
214
+ isEqual(this.history[this.historyIndex], currentValues)) {
215
+ return;
216
+ }
217
+ this.pushHistoryEntry(currentValues);
218
+ this.historyDirty = true;
219
+ return;
220
+ }
163
221
  this.saveCounter++;
164
222
  if (this.saveCounter % this.historyItemThrottle !== 0)
165
223
  return;
@@ -168,17 +226,17 @@ class FormState {
168
226
  if (this.history.length > 0 && diff) {
169
227
  return;
170
228
  }
229
+ this.pushHistoryEntry(currentValues);
230
+ }
231
+ pushHistoryEntry(currentValues) {
171
232
  if (this.historyIndex < this.history.length - 1) {
172
233
  this.history = this.history.slice(0, this.historyIndex + 1);
173
234
  }
174
235
  this.history.push(currentValues);
175
236
  if (this.history.length > this.historyItems) {
176
237
  this.history = this.history.slice(-this.historyItems);
177
- this.historyIndex = this.history.length - 1;
178
- }
179
- else {
180
- this.historyIndex = this.history.length - 1;
181
238
  }
239
+ this.historyIndex = this.history.length - 1;
182
240
  }
183
241
  /**
184
242
  * Moves to the previous state in the form history (undo operation).
@@ -186,6 +244,12 @@ class FormState {
186
244
  * @returns True if the operation was successful, false if no previous state exists
187
245
  */
188
246
  prev() {
247
+ // An undo mid-burst first commits the burst's end state, so a redo can
248
+ // return to what the user actually last saw.
249
+ if (this.historyDirty) {
250
+ this.history[this.historyIndex] = cloneDeep(this.values);
251
+ this.historyDirty = false;
252
+ }
189
253
  if (!this.canPrev())
190
254
  return false;
191
255
  this.historyIndex--;
@@ -224,12 +288,37 @@ class FormState {
224
288
  canNext() {
225
289
  return this.historyItems > 0 && this.historyIndex < this.history.length - 1;
226
290
  }
227
- notifySubscriptions() {
228
- this.subscriptions.forEach((subscription) => {
229
- subscription.cb(subscription.path
230
- ? get(this.getValues(), subscription.path)
231
- : this.getValues());
232
- });
291
+ /**
292
+ * Delivers the current values to subscribers — all of them, or only those
293
+ * whose path intersects `changedPath` when one is given.
294
+ *
295
+ * Cost discipline: the editor holds hundreds of subscriptions on one large
296
+ * document, and this runs on every write. Pathed subscribers get a clone of
297
+ * just their subtree; root subscribers share ONE lazily-created full clone
298
+ * per pass. Subscribers must therefore treat delivered values as read-only.
299
+ * Fresh clones (not `this.values` references) are still required so React
300
+ * state setters see a new identity per delivery.
301
+ * @private
302
+ */
303
+ notifySubscriptions(changedPath) {
304
+ // Snapshot: callbacks may (un)subscribe or write back into the form
305
+ // mid-pass; iterating the live map would tangle delivery with those
306
+ // mutations.
307
+ const subscribers = Array.from(this.subscriptions.values());
308
+ let fullClone;
309
+ for (const subscription of subscribers) {
310
+ if (changedPath !== undefined &&
311
+ !this.pathsIntersect(changedPath, subscription.path)) {
312
+ continue;
313
+ }
314
+ if (subscription.path) {
315
+ subscription.cb(cloneDeep(get(this.values, subscription.path)));
316
+ }
317
+ else {
318
+ fullClone ??= cloneDeep(this.values);
319
+ subscription.cb(fullClone);
320
+ }
321
+ }
233
322
  }
234
323
  notifyHistorySubscriptions() {
235
324
  this.formHistorySubscriptions.forEach((callback) => {
@@ -246,9 +335,9 @@ class FormState {
246
335
  return () => this.formHistorySubscriptions.delete(id);
247
336
  }
248
337
  generateListenerId() {
249
- const date = new Date().getTime().toString(36);
250
- const random = Math.random().toString(36).substring(2, 15);
251
- return `${date}-${random}`;
338
+ // Monotonic — timestamp+random ids could collide, silently replacing a
339
+ // live subscription in the Map and orphaning its callback.
340
+ return `listener-${++this.listenerSeq}`;
252
341
  }
253
342
  }
254
343
  export default FormState;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/state/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/state/index.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { RefObject } from 'react';
1
+ import { Ref } from 'react';
2
2
  import { ObjectSchema, ValidationError } from 'yup';
3
3
  import FormState from '../state';
4
4
  /**
@@ -57,12 +57,22 @@ export interface FormProps<T extends object> {
57
57
  validationSchema?: ObjectSchema<T>;
58
58
  /** Callback function called when the form is successfully submitted */
59
59
  onSubmit?: (value: T) => void | Promise<void>;
60
- /** Optional ref to access the FormState instance directly */
61
- ref?: RefObject<FormState<T> | null>;
60
+ /**
61
+ * Optional ref to access the FormState instance directly. Accepts either a
62
+ * ref object or a callback ref — a callback is the only way for a consumer
63
+ * to observe REMOUNTS of the form (a ref object mutates silently).
64
+ */
65
+ ref?: Ref<FormState<T> | null>;
62
66
  /** Number of history items to keep for undo/redo functionality (0 to disable) */
63
67
  historyItems?: number;
64
68
  /** Throttle for saving history items (every Nth change) */
65
69
  historyItemThrottle?: number;
70
+ /**
71
+ * Coalesce writes closer together than this into one history entry, so a
72
+ * typing or drag burst becomes a single undo step. 0 (default) keeps the
73
+ * counter-throttle behavior.
74
+ */
75
+ historyCoalesceMs?: number;
66
76
  /** Array of field paths to exclude from history tracking */
67
77
  filterHistoryKeys?: string[];
68
78
  /** Callback function called whenever form values change */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAElC,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,KAAK,CAAC;AAEpD,OAAO,SAAS,MAAM,UAAU,CAAC;AAEjC;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,MAAM,CAAC;AAE1E;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;AAEzD;;;;GAIG;AACH,MAAM,WAAW,iBAAiB,CAAC,CAAC,GAAG,QAAQ;IAC7C,wFAAwF;IACxF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,EAAE,EAAE,oBAAoB,CAAC,CAAC,CAAC,CAAC;CAC7B;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe,CAAC,CAAC,SAAS,MAAM;IAC/C,mEAAmE;IACnE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IACnB,iEAAiE;IACjE,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;IAClC,+EAA+E;IAC/E,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,kEAAkE;IAClE,UAAU,EAAE,OAAO,CAAC;IACpB,gDAAgD;IAChD,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,EAAE,CAAC,GAAG,IAAI,CAAC;IAC3D,uDAAuD;IACvD,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,mDAAmD;IACnD,KAAK,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAED;;;;GAIG;AACH,MAAM,WAAW,SAAS,CAAC,CAAC,SAAS,MAAM;IACzC,yCAAyC;IACzC,aAAa,EAAE,CAAC,CAAC;IACjB,yDAAyD;IACzD,gBAAgB,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;IACnC,uEAAuE;IACvE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,6DAA6D;IAC7D,GAAG,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACrC,iFAAiF;IACjF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2DAA2D;IAC3D,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,4DAA4D;IAC5D,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;CAC/B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAE5B,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,KAAK,CAAC;AAEpD,OAAO,SAAS,MAAM,UAAU,CAAC;AAEjC;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,MAAM,CAAC;AAE1E;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;AAEzD;;;;GAIG;AACH,MAAM,WAAW,iBAAiB,CAAC,CAAC,GAAG,QAAQ;IAC7C,wFAAwF;IACxF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,EAAE,EAAE,oBAAoB,CAAC,CAAC,CAAC,CAAC;CAC7B;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe,CAAC,CAAC,SAAS,MAAM;IAC/C,mEAAmE;IACnE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IACnB,iEAAiE;IACjE,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;IAClC,+EAA+E;IAC/E,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,kEAAkE;IAClE,UAAU,EAAE,OAAO,CAAC;IACpB,gDAAgD;IAChD,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,EAAE,CAAC,GAAG,IAAI,CAAC;IAC3D,uDAAuD;IACvD,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,mDAAmD;IACnD,KAAK,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAED;;;;GAIG;AACH,MAAM,WAAW,SAAS,CAAC,CAAC,SAAS,MAAM;IACzC,yCAAyC;IACzC,aAAa,EAAE,CAAC,CAAC;IACjB,yDAAyD;IACzD,gBAAgB,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;IACnC,uEAAuE;IACvE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C;;;;OAIG;IACH,GAAG,CAAC,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC/B,iFAAiF;IACjF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2DAA2D;IAC3D,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,4DAA4D;IAC5D,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;CAC/B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mapples/form",
3
- "version": "1.1.2",
3
+ "version": "1.2.2",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "scripts": {