@mapples/form 1.0.1

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 (74) hide show
  1. package/README.md +411 -0
  2. package/dist/components/Form.d.ts +37 -0
  3. package/dist/components/Form.d.ts.map +1 -0
  4. package/dist/components/Form.js +111 -0
  5. package/dist/components/Form.js.map +1 -0
  6. package/dist/components/index.d.ts +3 -0
  7. package/dist/components/index.d.ts.map +1 -0
  8. package/dist/components/index.js +3 -0
  9. package/dist/components/index.js.map +1 -0
  10. package/dist/context/FormContext.d.ts +19 -0
  11. package/dist/context/FormContext.d.ts.map +1 -0
  12. package/dist/context/FormContext.js +28 -0
  13. package/dist/context/FormContext.js.map +1 -0
  14. package/dist/context/index.d.ts +4 -0
  15. package/dist/context/index.d.ts.map +1 -0
  16. package/dist/context/index.js +4 -0
  17. package/dist/context/index.js.map +1 -0
  18. package/dist/hooks/index.d.ts +11 -0
  19. package/dist/hooks/index.d.ts.map +1 -0
  20. package/dist/hooks/index.js +11 -0
  21. package/dist/hooks/index.js.map +1 -0
  22. package/dist/hooks/useBindFormField.d.ts +43 -0
  23. package/dist/hooks/useBindFormField.d.ts.map +1 -0
  24. package/dist/hooks/useBindFormField.js +93 -0
  25. package/dist/hooks/useBindFormField.js.map +1 -0
  26. package/dist/hooks/useFormArrayField.d.ts +43 -0
  27. package/dist/hooks/useFormArrayField.d.ts.map +1 -0
  28. package/dist/hooks/useFormArrayField.js +88 -0
  29. package/dist/hooks/useFormArrayField.js.map +1 -0
  30. package/dist/hooks/useFormContext.d.ts +28 -0
  31. package/dist/hooks/useFormContext.d.ts.map +1 -0
  32. package/dist/hooks/useFormContext.js +32 -0
  33. package/dist/hooks/useFormContext.js.map +1 -0
  34. package/dist/hooks/useFormField.d.ts +42 -0
  35. package/dist/hooks/useFormField.d.ts.map +1 -0
  36. package/dist/hooks/useFormField.js +79 -0
  37. package/dist/hooks/useFormField.js.map +1 -0
  38. package/dist/hooks/useFormFieldToggle.d.ts +33 -0
  39. package/dist/hooks/useFormFieldToggle.d.ts.map +1 -0
  40. package/dist/hooks/useFormFieldToggle.js +49 -0
  41. package/dist/hooks/useFormFieldToggle.js.map +1 -0
  42. package/dist/hooks/useFormHistory.d.ts +30 -0
  43. package/dist/hooks/useFormHistory.d.ts.map +1 -0
  44. package/dist/hooks/useFormHistory.js +57 -0
  45. package/dist/hooks/useFormHistory.js.map +1 -0
  46. package/dist/hooks/useFormState.d.ts +34 -0
  47. package/dist/hooks/useFormState.d.ts.map +1 -0
  48. package/dist/hooks/useFormState.js +54 -0
  49. package/dist/hooks/useFormState.js.map +1 -0
  50. package/dist/hooks/useFormSubmit.d.ts +27 -0
  51. package/dist/hooks/useFormSubmit.d.ts.map +1 -0
  52. package/dist/hooks/useFormSubmit.js +31 -0
  53. package/dist/hooks/useFormSubmit.js.map +1 -0
  54. package/dist/hooks/useFormValue.d.ts +28 -0
  55. package/dist/hooks/useFormValue.d.ts.map +1 -0
  56. package/dist/hooks/useFormValue.js +43 -0
  57. package/dist/hooks/useFormValue.js.map +1 -0
  58. package/dist/index.d.ts +45 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +49 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/state/index.d.ts +128 -0
  63. package/dist/state/index.d.ts.map +1 -0
  64. package/dist/state/index.js +249 -0
  65. package/dist/state/index.js.map +1 -0
  66. package/dist/types/index.d.ts +71 -0
  67. package/dist/types/index.d.ts.map +1 -0
  68. package/dist/types/index.js +1 -0
  69. package/dist/types/index.js.map +1 -0
  70. package/dist/utils/index.d.ts +40 -0
  71. package/dist/utils/index.d.ts.map +1 -0
  72. package/dist/utils/index.js +95 -0
  73. package/dist/utils/index.js.map +1 -0
  74. package/package.json +40 -0
@@ -0,0 +1,249 @@
1
+ import { cloneDeep, get, set, groupBy, isEqual, some } from 'lodash';
2
+ /**
3
+ * FormState manages the state of a form including values, validation, history, and subscriptions.
4
+ * It provides methods to get/set values, validate data, manage history for undo/redo functionality,
5
+ * and subscribe to value changes.
6
+ *
7
+ * @template T - The type of the form data object
8
+ * @public
9
+ */
10
+ class FormState {
11
+ values;
12
+ validationSchema;
13
+ subscriptions = new Map();
14
+ formHistorySubscriptions = new Map();
15
+ errors = null;
16
+ history = [];
17
+ historyIndex = -1;
18
+ historyItems;
19
+ historyItemThrottle;
20
+ saveCounter = 0;
21
+ filterHistoryKeys;
22
+ /**
23
+ * Creates a new FormState instance.
24
+ *
25
+ * @param initialValues - The initial values for the form
26
+ * @param validationSchema - Optional Yup validation schema
27
+ * @param historyItems - Number of history items to keep for undo/redo (0 to disable)
28
+ * @param historyItemThrottle - Throttle for saving history items (every Nth change)
29
+ * @param filterHistoryKeys - Array of field paths to exclude from history tracking
30
+ */
31
+ constructor(initialValues, validationSchema, historyItems = 0, historyItemThrottle = 5, filterHistoryKeys = []) {
32
+ this.values = cloneDeep(initialValues);
33
+ this.validationSchema = validationSchema;
34
+ this.historyItems = historyItems;
35
+ this.historyItemThrottle = historyItemThrottle;
36
+ this.filterHistoryKeys = filterHistoryKeys;
37
+ if (this.historyItems > 0) {
38
+ this.history = [cloneDeep(initialValues)];
39
+ this.historyIndex = 0;
40
+ }
41
+ }
42
+ /**
43
+ * Gets a deep copy of all form values.
44
+ *
45
+ * @returns A deep copy of the current form values
46
+ */
47
+ getValues() {
48
+ return cloneDeep(this.values);
49
+ }
50
+ /**
51
+ * Sets validation errors for the form.
52
+ *
53
+ * @param errors - Validation errors organized by field path
54
+ */
55
+ setErrors(errors) {
56
+ this.errors = errors;
57
+ }
58
+ /**
59
+ * Gets the current validation errors.
60
+ *
61
+ * @returns Current validation errors or null if no errors
62
+ */
63
+ getErrors() {
64
+ return this.errors;
65
+ }
66
+ /**
67
+ * Sets all form values and triggers subscriptions.
68
+ *
69
+ * @param values - New values to set for the form
70
+ */
71
+ setValues(values) {
72
+ this.values = cloneDeep(values);
73
+ this.saveToHistory();
74
+ this.subscriptions.forEach((subscription) => {
75
+ subscription.cb(subscription.path
76
+ ? get(this.getValues(), subscription.path)
77
+ : this.getValues());
78
+ });
79
+ }
80
+ /**
81
+ * Sets a value at a specific path and triggers subscriptions.
82
+ *
83
+ * @template V - The type of value being set
84
+ * @param path - Dot notation path to the field (e.g., 'user.name')
85
+ * @param value - The value to set at the specified path
86
+ */
87
+ setValue(path, value) {
88
+ set(this.values, path, value);
89
+ if (!some(this.filterHistoryKeys, (value) => {
90
+ return path.includes(value);
91
+ })) {
92
+ this.saveToHistory();
93
+ }
94
+ this.subscriptions.forEach((subscription) => {
95
+ subscription.cb(subscription.path
96
+ ? get(this.getValues(), subscription.path)
97
+ : this.getValues());
98
+ });
99
+ }
100
+ /**
101
+ * Subscribes to form value changes.
102
+ *
103
+ * @template V - The type of value being subscribed to
104
+ * @param cb - Callback function to execute when values change
105
+ * @param path - Optional path to subscribe to a specific field
106
+ * @returns Unsubscribe function to remove the subscription
107
+ */
108
+ subscribeOnChange(cb, path) {
109
+ const id = this.generateListenerId();
110
+ this.subscriptions.set(id, {
111
+ cb: cb,
112
+ path,
113
+ });
114
+ return this.unsubscribeOnChange(id);
115
+ }
116
+ /**
117
+ * Subscribes to form history changes (undo/redo operations).
118
+ *
119
+ * @param cb - Callback function to execute when history changes
120
+ * @returns Unsubscribe function to remove the subscription
121
+ */
122
+ subscribeOnHistoryChange(cb) {
123
+ const id = this.generateListenerId();
124
+ this.formHistorySubscriptions.set(id, cb);
125
+ return this.unsubscribeOnHistoryChange(id);
126
+ }
127
+ /**
128
+ * Gets a value at a specific path or all values if no path is provided.
129
+ *
130
+ * @template V - The type of value to return
131
+ * @param path - Optional dot notation path to the field
132
+ * @returns The value at the specified path or all form values
133
+ */
134
+ getValue(path) {
135
+ return cloneDeep(path ? get(this.getValues(), path) : this.getValues());
136
+ }
137
+ /**
138
+ * Validates the form using the provided validation schema.
139
+ *
140
+ * @throws {Record<string, ValidationError[]>} Throws validation errors if validation fails
141
+ */
142
+ async validate() {
143
+ try {
144
+ await this.validationSchema?.validate(this.getValues(), {
145
+ abortEarly: false,
146
+ });
147
+ }
148
+ catch (error) {
149
+ const errors = this.remapValidationError(error);
150
+ this.setErrors(errors);
151
+ throw errors;
152
+ }
153
+ }
154
+ saveToHistory() {
155
+ if (this.historyItems === 0)
156
+ return;
157
+ this.saveCounter++;
158
+ if (this.saveCounter % this.historyItemThrottle !== 0)
159
+ return;
160
+ const currentValues = cloneDeep(this.values);
161
+ const diff = isEqual(this.history[this.history.length - 1], currentValues);
162
+ if (this.history.length > 0 && diff) {
163
+ return;
164
+ }
165
+ if (this.historyIndex < this.history.length - 1) {
166
+ this.history = this.history.slice(0, this.historyIndex + 1);
167
+ }
168
+ this.history.push(currentValues);
169
+ if (this.history.length > this.historyItems) {
170
+ this.history = this.history.slice(-this.historyItems);
171
+ this.historyIndex = this.history.length - 1;
172
+ }
173
+ else {
174
+ this.historyIndex = this.history.length - 1;
175
+ }
176
+ }
177
+ /**
178
+ * Moves to the previous state in the form history (undo operation).
179
+ *
180
+ * @returns True if the operation was successful, false if no previous state exists
181
+ */
182
+ prev() {
183
+ if (!this.canPrev())
184
+ return false;
185
+ this.historyIndex--;
186
+ this.values = cloneDeep(this.history[this.historyIndex]);
187
+ this.notifySubscriptions();
188
+ this.notifyHistorySubscriptions();
189
+ return true;
190
+ }
191
+ /**
192
+ * Moves to the next state in the form history (redo operation).
193
+ *
194
+ * @returns True if the operation was successful, false if no next state exists
195
+ */
196
+ next() {
197
+ if (!this.canNext())
198
+ return false;
199
+ this.historyIndex++;
200
+ this.values = cloneDeep(this.history[this.historyIndex]);
201
+ this.notifySubscriptions();
202
+ this.notifyHistorySubscriptions();
203
+ return true;
204
+ }
205
+ /**
206
+ * Checks if there is a previous state available for undo operation.
207
+ *
208
+ * @returns True if undo operation is possible
209
+ */
210
+ canPrev() {
211
+ return this.historyItems > 0 && this.historyIndex > 0;
212
+ }
213
+ /**
214
+ * Checks if there is a next state available for redo operation.
215
+ *
216
+ * @returns True if redo operation is possible
217
+ */
218
+ canNext() {
219
+ return this.historyItems > 0 && this.historyIndex < this.history.length - 1;
220
+ }
221
+ notifySubscriptions() {
222
+ this.subscriptions.forEach((subscription) => {
223
+ subscription.cb(subscription.path
224
+ ? get(this.getValues(), subscription.path)
225
+ : this.getValues());
226
+ });
227
+ }
228
+ notifyHistorySubscriptions() {
229
+ this.formHistorySubscriptions.forEach((callback) => {
230
+ callback();
231
+ });
232
+ }
233
+ remapValidationError(error) {
234
+ return groupBy(error.inner, 'path');
235
+ }
236
+ unsubscribeOnChange(id) {
237
+ return () => this.subscriptions.delete(id);
238
+ }
239
+ unsubscribeOnHistoryChange(id) {
240
+ return () => this.formHistorySubscriptions.delete(id);
241
+ }
242
+ generateListenerId() {
243
+ const date = new Date().getTime().toString(36);
244
+ const random = Math.random().toString(36).substring(2, 15);
245
+ return `${date}-${random}`;
246
+ }
247
+ }
248
+ export default FormState;
249
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/state/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ import { RefObject } from 'react';
2
+ import { ObjectSchema, ValidationError } from 'yup';
3
+ import FormState from '../state';
4
+ /**
5
+ * Represents any possible value type that can be stored in a form field.
6
+ * @public
7
+ */
8
+ export type AnyValue = string | number | object | boolean | null | symbol;
9
+ /**
10
+ * Callback function type for form value subscriptions.
11
+ * @template T - The type of value being subscribed to
12
+ * @param value - The current value
13
+ * @public
14
+ */
15
+ export type SubscriptionCallBack<T> = (value: T) => void;
16
+ /**
17
+ * Configuration for subscribing to form value changes.
18
+ * @template T - The type of value being subscribed to
19
+ * @public
20
+ */
21
+ export interface ValueSubscription<T = AnyValue> {
22
+ /** Optional path to a specific field. If not provided, subscribes to the entire form */
23
+ path?: string;
24
+ /** Callback function to execute when the value changes */
25
+ cb: SubscriptionCallBack<T>;
26
+ }
27
+ /**
28
+ * Context type that provides form state and methods to child components.
29
+ * @template T - The type of the form data object
30
+ * @public
31
+ */
32
+ export interface FormContextType<T extends object> {
33
+ /** The form state instance containing all form data and methods */
34
+ form: FormState<T> | null;
35
+ /** Object tracking which fields have been touched by the user */
36
+ touched: Record<keyof T, boolean>;
37
+ /** Function to submit the form, triggering validation and onSubmit callback */
38
+ submitForm: () => void;
39
+ /** Boolean indicating if the form is currently being submitted */
40
+ submitting: boolean;
41
+ /** Validation errors organized by field path */
42
+ validationErrors: Record<string, ValidationError[]> | null;
43
+ /** Function to validate the form without submitting */
44
+ validate: () => void;
45
+ /** Function to mark a specific field as touched */
46
+ touch: (fieldName: string) => void;
47
+ }
48
+ /**
49
+ * Props for the Form component.
50
+ * @template T - The type of the form data object
51
+ * @public
52
+ */
53
+ export interface FormProps<T extends object> {
54
+ /** Initial values for the form fields */
55
+ initialValues: T;
56
+ /** Optional Yup validation schema for form validation */
57
+ validationSchema?: ObjectSchema<T>;
58
+ /** Callback function called when the form is successfully submitted */
59
+ onSubmit?: (value: T) => void | Promise<void>;
60
+ /** Optional ref to access the FormState instance directly */
61
+ ref?: RefObject<FormState<T> | null>;
62
+ /** Number of history items to keep for undo/redo functionality (0 to disable) */
63
+ historyItems?: number;
64
+ /** Throttle for saving history items (every Nth change) */
65
+ historyItemThrottle?: number;
66
+ /** Array of field paths to exclude from history tracking */
67
+ filterHistoryKeys?: string[];
68
+ /** Callback function called whenever form values change */
69
+ onChange?: (value: T) => void;
70
+ }
71
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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,GAAG,IAAI,CAAC;IAC1B,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"}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Compares two objects and returns the differences between them.
3
+ * This utility function performs a deep comparison and returns an object
4
+ * containing the differences between the two input objects.
5
+ *
6
+ * @template T - The type of objects being compared
7
+ * @param obj1 - First object to compare
8
+ * @param obj2 - Second object to compare
9
+ * @param excludeKeys - Array of keys to exclude from comparison (optional)
10
+ * @returns Object containing diff keys and values, empty object if no differences
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * const obj1 = { name: 'John', age: 30, city: 'NYC' };
15
+ * const obj2 = { name: 'Jane', age: 30, city: 'LA' };
16
+ * const diff = objectDiff(obj1, obj2, ['age']); // Excludes age from comparison
17
+ * // Result: { name: { _value: { from: 'John', to: 'Jane' } }, city: { _value: { from: 'NYC', to: 'LA' } } }
18
+ * ```
19
+ *
20
+ * @public
21
+ */
22
+ export declare function objectDiff<T>(obj1: T, obj2: T, excludeKeys?: string[]): Record<string, any>;
23
+ /**
24
+ * Checks if an object is empty (has no enumerable properties).
25
+ * This utility function determines whether an object has any own enumerable properties.
26
+ *
27
+ * @param obj - Object to check
28
+ * @returns True if object is empty (no enumerable properties), false otherwise
29
+ *
30
+ * @example
31
+ * ```tsx
32
+ * isEmpty({}); // true
33
+ * isEmpty({ name: 'John' }); // false
34
+ * isEmpty({ length: 0 }); // false (has enumerable property)
35
+ * ```
36
+ *
37
+ * @public
38
+ */
39
+ export declare function isEmpty(obj: Record<string, any>): boolean;
40
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAC1B,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,CAAC,EACP,WAAW,GAAE,MAAM,EAAO,GACzB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAuErB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAEzD"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Compares two objects and returns the differences between them.
3
+ * This utility function performs a deep comparison and returns an object
4
+ * containing the differences between the two input objects.
5
+ *
6
+ * @template T - The type of objects being compared
7
+ * @param obj1 - First object to compare
8
+ * @param obj2 - Second object to compare
9
+ * @param excludeKeys - Array of keys to exclude from comparison (optional)
10
+ * @returns Object containing diff keys and values, empty object if no differences
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * const obj1 = { name: 'John', age: 30, city: 'NYC' };
15
+ * const obj2 = { name: 'Jane', age: 30, city: 'LA' };
16
+ * const diff = objectDiff(obj1, obj2, ['age']); // Excludes age from comparison
17
+ * // Result: { name: { _value: { from: 'John', to: 'Jane' } }, city: { _value: { from: 'NYC', to: 'LA' } } }
18
+ * ```
19
+ *
20
+ * @public
21
+ */
22
+ export function objectDiff(obj1, obj2, excludeKeys = []) {
23
+ const diff = {};
24
+ // Handle primitive types and null/undefined
25
+ if (obj1 === obj2) {
26
+ return {}; // No difference
27
+ }
28
+ // Handle null/undefined cases
29
+ if (obj1 == null || obj2 == null) {
30
+ return { _value: { from: obj1, to: obj2 } }; // Different
31
+ }
32
+ // Handle different types
33
+ if (typeof obj1 !== typeof obj2) {
34
+ return { _value: { from: obj1, to: obj2 } }; // Different
35
+ }
36
+ // Handle arrays
37
+ if (Array.isArray(obj1) && Array.isArray(obj2)) {
38
+ if (obj1.length !== obj2.length) {
39
+ return { _value: { from: obj1, to: obj2 } }; // Different lengths
40
+ }
41
+ for (let i = 0; i < obj1.length; i++) {
42
+ const elementDiff = objectDiff(obj1[i], obj2[i]);
43
+ if (Object.keys(elementDiff).length > 0) {
44
+ diff[i.toString()] = elementDiff;
45
+ }
46
+ }
47
+ return diff;
48
+ }
49
+ // Handle objects
50
+ if (typeof obj1 === 'object' && typeof obj2 === 'object') {
51
+ const keys1 = Object.keys(obj1).filter((key) => !excludeKeys.includes(key));
52
+ const keys2 = Object.keys(obj2).filter((key) => !excludeKeys.includes(key));
53
+ // Check for keys in obj1 but not in obj2
54
+ for (const key of keys1) {
55
+ if (!keys2.includes(key)) {
56
+ diff[key] = { removed: obj1[key] };
57
+ }
58
+ else {
59
+ const valueDiff = objectDiff(obj1[key], obj2[key], excludeKeys);
60
+ if (Object.keys(valueDiff).length > 0) {
61
+ diff[key] = valueDiff;
62
+ }
63
+ }
64
+ }
65
+ // Check for keys in obj2 but not in obj1
66
+ for (const key of keys2) {
67
+ if (!keys1.includes(key)) {
68
+ diff[key] = { added: obj2[key] };
69
+ }
70
+ }
71
+ return diff;
72
+ }
73
+ // For all other cases (primitives that are not equal)
74
+ return { _value: { from: obj1, to: obj2 } };
75
+ }
76
+ /**
77
+ * Checks if an object is empty (has no enumerable properties).
78
+ * This utility function determines whether an object has any own enumerable properties.
79
+ *
80
+ * @param obj - Object to check
81
+ * @returns True if object is empty (no enumerable properties), false otherwise
82
+ *
83
+ * @example
84
+ * ```tsx
85
+ * isEmpty({}); // true
86
+ * isEmpty({ name: 'John' }); // false
87
+ * isEmpty({ length: 0 }); // false (has enumerable property)
88
+ * ```
89
+ *
90
+ * @public
91
+ */
92
+ export function isEmpty(obj) {
93
+ return Object.keys(obj).length === 0;
94
+ }
95
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@mapples/form",
3
+ "version": "1.0.1",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "scripts": {
7
+ "build": "tsc --p tsconfig.build.json",
8
+ "lint": "eslint .",
9
+ "type-check": "tsc --noEmit",
10
+ "clean": "rm -rf dist"
11
+ },
12
+ "author": {
13
+ "name": "Oleksandr Soloviov",
14
+ "url": "https://mapples.org/people/olek_soloviov"
15
+ },
16
+ "license": "MIT",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/mapplesorg/mapples-open-kit.git",
20
+ "directory": "packages/@mapples/form"
21
+ },
22
+ "dependencies": {
23
+ "lodash": "^4.17.21"
24
+ },
25
+ "peerDependencies": {
26
+ "react": ">=18.0.0 <20.0.0",
27
+ "react-native": ">=0.71.0",
28
+ "yup": "^1.6.0"
29
+ },
30
+ "devDependencies": {
31
+ "@types/lodash": "^4.17.0",
32
+ "@types/react": ">=18.0.0 <20.0.0",
33
+ "typescript": "~5.9.2"
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "README.md",
38
+ "package.json"
39
+ ]
40
+ }