@reactables/forms 1.0.0-beta.5 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,6 +7,13 @@ Reactive forms with [Reactables](https://github.com/reactables/reactables/tree/m
7
7
  ## Table of Contents
8
8
 
9
9
  1. [Installation](#installation)
10
+ 1. [Examples](#examples)
11
+ 1. [Basic Form Group](#basic-form-group)
12
+ 1. [Validation](#validation-example)
13
+ 1. [Async Validation](#async-validation-example)
14
+ 1. [Normalizing Values](#normalizing-values)
15
+ 1. [Custom Reducer](#custom-reducer-example)
16
+
10
17
  1. [API](#api)
11
18
  1. [RxActions](#api-actions)
12
19
  1. [updateValues](#api-actions-update-values)
@@ -27,18 +34,254 @@ Reactive forms with [Reactables](https://github.com/reactables/reactables/tree/m
27
34
  1. [FormControl](#api-form-control)
28
35
  1. [ControlRef](#api-control-ref)
29
36
  1. [FormErrors](#api-form-errors)
37
+ 1. [ValidatorFn](#api-validator-fn)
38
+ 1. [ValidatorAsyncFn](#api-validator-fn-async)
30
39
  1. [FormReducers](#api-form-reducers)
40
+ 1. [CustomReducers](#api-custom-reducers)
41
+ 1. [BaseFormState](#api-base-form-state)
42
+ 1. [BaseControl](#api-base-control)
31
43
 
32
44
 
33
45
  ## Installation <a name="installation"></a>
34
46
 
35
47
  Installation requires [RxJS](https://rxjs.dev/) to be present.
36
48
 
37
- `npm i rxjs @reactables/forms`
49
+ `npm i rxjs` (if not already installed)
50
+
51
+ `npm i @reactables/forms`
52
+
53
+ ## Examples <a name="examples"></a>
54
+
55
+ ### Basic Form Group <a name="basic-form-group"></a>
56
+
57
+ [See full example on StackBlitz](https://stackblitz.com/edit/github-qtpo1k-vm45ed?file=src%2Findex.js)
58
+
59
+ ```typescript
60
+ import { control, build, group } from '@reactables/forms';
61
+
62
+ const [state$, actions] = build(
63
+ group({
64
+ controls: {
65
+ name: control(['']),
66
+ },
67
+ })
68
+ );
69
+
70
+ // Cache the DOM
71
+ const nameControlEl = document.getElementById('name-control');
72
+
73
+ // Bind Event Handlers
74
+ nameControlEl.oninput = ({ target: { value } }) => {
75
+ actions.updateValues({
76
+ controlRef: ['name'],
77
+ value,
78
+ });
79
+ };
80
+
81
+ nameControlEl.onblur = () => {
82
+ actions.markControlAsTouched({ controlRef: ['name'] });
83
+ };
84
+
85
+ // Subscribe to state updates and bind to view.
86
+ state$.subscribe((state) => {
87
+ const { name } = state;
88
+
89
+ nameControlEl.value = name.value;
90
+ });
91
+
92
+ ```
93
+
94
+ ### Validation <a name="validation-example"></a>
95
+
96
+ `@reactable/forms` only comes with 3 built in validators, `required`, `email` & `arrayNotEmpty`. The developer can implement their own `ValidatorFn`s and provide them when building the reactable.
97
+
98
+ [See full example on StackBlitz](https://stackblitz.com/edit/github-qtpo1k-f6tz82?file=src%2Findex.js)
99
+
100
+ ```typescript
101
+ import { control, build, group } from '@reactables/forms';
102
+
103
+ // Create Reactable
104
+ const rxForm = build(
105
+ group({
106
+ controls: {
107
+ donuts: control(['0', ['required', 'min4']]),
108
+ },
109
+ }),
110
+ {
111
+ providers: {
112
+ validators: {
113
+ min4: (value) => ({ min4: Number(value) < 4 }),
114
+ },
115
+ },
116
+ }
117
+ );
118
+
119
+ const [state$, actions] = rxForm;
120
+
121
+ // ...Cache the DOM and bind event handlers
122
+
123
+ // Subscribe to state updates and bind to view.
124
+ state$.subscribe((state) => {
125
+ const { donuts } = state;
126
+
127
+ donutControlEl.value = donuts.value;
128
+
129
+ const handleErrors = (el, show) => {
130
+ el.className = show ? 'form-error show' : 'form-error';
131
+ };
132
+
133
+ handleErrors(donuntMinOrderErrorEl, donuts.touched && donuts.errors.min4);
134
+ handleErrors(donuntRequiredErrorEl, donuts.touched && donuts.errors.required);
135
+ });
136
+
137
+
138
+
139
+ ```
140
+
141
+ ### Async Validation <a name="async-validation-example"></a>
142
+
143
+ `FormControl`s have a `pending: boolean` state when their value changes and are awaiting the result from asynchronous validation.
144
+
145
+ [See full example on StackBlitz](https://stackblitz.com/edit/github-qtpo1k-wvznqm?file=src%2Findex.js)
146
+
147
+ ```typescript
148
+ import { control, build, group } from '@reactables/forms';
149
+ import { of, switchMap, delay } from 'rxjs';
150
+
151
+ const [state$, actions] = build(
152
+ group({
153
+ controls: {
154
+ email: control(['', ['required', 'email'], ['blacklistedEmail']]),
155
+ },
156
+ }),
157
+ {
158
+ providers: {
159
+ asyncValidators: {
160
+ blacklistedEmail: (control$) =>
161
+ control$.pipe(
162
+ switchMap(({ value }) =>
163
+ of({
164
+ blacklistedEmail: value === 'already@taken.com',
165
+ }).pipe(delay(1000))
166
+ )
167
+ ),
168
+ },
169
+ },
170
+ }
171
+ );
172
+
173
+ // ...Bind Event Handlers
174
+
175
+ // Subscribe to state updates and bind to view.
176
+ state$.subscribe((state) => {
177
+ const { email } = state;
178
+
179
+ emailControlEl.value = email.value;
180
+
181
+ const handleErrors = (el, show) => {
182
+ el.className = show ? 'form-error show' : 'form-error';
183
+ };
184
+
185
+ emailPendingEl.className = email.pending ? 'show' : '';
186
+
187
+ handleErrors(emailRequiredErrorEl, email.touched && email.errors.required);
188
+ handleErrors(emailAsyncErrorEl, email.errors.blacklistedEmail);
189
+ });
190
+
191
+
192
+ ```
193
+
194
+ ### Normalize Values <a name="normalizing-values"></a>
195
+
196
+ User input for a `FormControl` leaf (i.e having no child controls) can be normalized via normalizer functions provided during form initialization.
197
+
198
+ [See full example on StackBlitz](https://stackblitz.com/edit/github-qtpo1k-frpncu?file=src%2findex.js)
199
+
200
+ ```typescript
201
+ import { control, build, group } from '@reactables/forms';
202
+
203
+ export const normalizePhone = (value) => {
204
+ let input = value.replace(/\D/g, '').substring(0, 10); // First ten digits of input only
205
+ const areaCode = input.substring(0, 3);
206
+ const middle = input.substring(3, 6);
207
+ const last = input.substring(6, 10);
208
+
209
+ if (input.length > 6) {
210
+ input = `(${areaCode}) ${middle} - ${last}`;
211
+ } else if (input.length > 3) {
212
+ input = `(${areaCode}) ${middle}`;
213
+ } else if (input.length > 0) {
214
+ input = `(${areaCode}`;
215
+ }
216
+
217
+ return input;
218
+ };
219
+
220
+ const rxForm = build(
221
+ group({
222
+ controls: {
223
+ phone: control({
224
+ initialValue: '',
225
+ normalizers: ['phone']
226
+ }),
227
+ },
228
+ }),
229
+ {
230
+ providers: {
231
+ normalizers: {
232
+ phone: normalizePhone,
233
+ },
234
+ },
235
+ }
236
+ );
237
+
238
+ // ... Bind event handlers and view
239
+ ```
240
+
241
+ ### Custom Reducer <a name="custom-reducer-example"></a>
242
+
243
+ You can declare [`CustomReducer`s](#api-custom-reducers) during form initialization to implement custom behaviour.
244
+
245
+ Below the form reactable will have a `doubleOrder` action method which can be called to double the order amount.
246
+
247
+ [See full example on StackBlitz](https://stackblitz.com/edit/github-qtpo1k-3qppus?file=src%2Findex.js)
248
+
249
+ ```typescript
250
+ import { control, build, group } from '@reactables/forms';
251
+
252
+ const [state$, actions] = build(
253
+ group({
254
+ controls: {
255
+ donuts: control(['0', 'min4']),
256
+ },
257
+ }),
258
+ {
259
+ providers: {
260
+ validators: {
261
+ min4: (value) => ({ min4: Number(value) < 4 }),
262
+ },
263
+ },
264
+ reducers: {
265
+ doubleOrder: (formReducers, state) => {
266
+ /** Use built in Form Reducers for updating the form tree. **/
267
+ const { updateValues } = formReducers;
268
+
269
+ const orders = Number(state.form.donuts.value);
270
+ const value = (orders * 2).toString();
271
+
272
+ return updateValues(state, { controlRef: ['donuts'], value });
273
+ };,
274
+ },
275
+ }
276
+ );
277
+
278
+ // ... Bind event handlers and view
279
+
280
+ ```
38
281
 
39
282
  ## API <a name="api"></a>
40
283
 
41
- The API for building Reactable Forms is very similar to [Angular FormBuilder](https://angular.io/api/forms/FormBuilder). It has been adapted to support Reactable features.
284
+ The API for building Reactable Forms inspired by [Angular FormBuilder](https://angular.io/api/forms/FormBuilder). It has been adapted to support Reactable features.
42
285
 
43
286
  ### `RxActions` <a name="api-actions"></a>
44
287
 
@@ -51,7 +294,7 @@ Updates values of a [`FormControl`](#api-form-control). For form group and form
51
294
  ```typescript
52
295
  type updateValues = <T>(payload: UpdateValuesPayload<T>) => void;
53
296
 
54
- export interface UpdateValuesPayload<T> {
297
+ interface UpdateValuesPayload<T> {
55
298
  value: T;
56
299
  controlRef: ControlRef;
57
300
  }
@@ -65,7 +308,7 @@ Adds a control to a form group.
65
308
  ```typescript
66
309
  type addControl = (payload: AddControlPayload) => void;
67
310
 
68
- export interface AddControlPayload {
311
+ interface AddControlPayload {
69
312
  config: AbstractControlConfig;
70
313
  controlRef: ControlRef;
71
314
  }
@@ -79,7 +322,7 @@ Pushes a control to a form array.
79
322
  ```typescript
80
323
  type pushControl = (payload: PushControlPayload) => void;
81
324
 
82
- export interface PushControlPayload {
325
+ interface PushControlPayload {
83
326
  config: AbstractControlConfig;
84
327
  controlRef: ControlRef;
85
328
  }
@@ -106,12 +349,12 @@ type markControlAsPristine = (payload: ControlRef) => void;
106
349
 
107
350
  #### `markControlAsTouched` <a name="api-actions-mark-as-touched"></a>
108
351
 
109
- Marks a control and all ancestors as touched. Can set `markAll` to `true` to mark all descendants as touched as well.
352
+ Marks a control and all ancestors as touched. Can set `markAll` to `true` to mark all descendants as touched as well (defaults to `false`).
110
353
 
111
354
  ```typescript
112
355
  type markControlAsTouched = (payload: MarkTouchedPayload) => void;
113
356
 
114
- export interface MarkTouchedPayload {
357
+ interface MarkTouchedPayload {
115
358
  controlRef: ControlRef;
116
359
  markAll?: boolean;
117
360
  }
@@ -129,7 +372,7 @@ type markControlAsUnTouched = (payload: ControlRef) => void;
129
372
 
130
373
  #### `resetControl` <a name="api-actions-resetControl"></a>
131
374
 
132
- Marks a control and all descendants as untouched. This will recheck ancestor controls and update the touched status.
375
+ Resets a control by removing existing control and rebuilding it with the original configuration.
133
376
 
134
377
  ```typescript
135
378
  type resetControls = (payload: ControlRef) => void;
@@ -155,17 +398,10 @@ interface RxFormOptions {
155
398
  effects?: Effect<unknown, unknown>[];
156
399
  sources?: Observable<Action<unknown>>[] | { [key: string]: Observable<unknown> };
157
400
  }
158
-
159
- type CustomReducer = (
160
- reducers: FormReducers,
161
- state: BaseFormState<unknown>,
162
- action: Action<unknown>,
163
- ) => BaseFormState<unknown>;
164
-
165
401
  ```
166
402
  | Property | Description |
167
403
  | -------- | ----------- |
168
- | reducers (optional) | Dictionary of `CustomReducer`s to implement custom form behaviour. The `CustomReducer` provides built in [`FormReducers`](#api-form-reducers). **Form state updates need to be made with [`FormReducers`](#api-form-reducers) to maintain integrity of the form state tree (i.e validation states of parent and child controls)**. |
404
+ | reducers (optional) | Dictionary of [`CustomReducer`s](#api-custom-reducers) to implement custom form behaviour. The `CustomReducerFunc`(#api-custom-reducers) provides built in [`FormReducers`](#api-form-reducers). **Form state updates need to be made with [`FormReducers`](#api-form-reducers) to maintain integrity of the form state tree (i.e validation states of parent and child controls)**. |
169
405
  | effects (optional) | Array of [Effects](https://github.com/reactables/reactables/tree/main/packages/core#api-effect) to be registered to the Reactable. |
170
406
  | sources (optional) | Additional [Action](https://github.com/reactables/reactables/tree/main/packages/core#action-) Observables the Reactable is listening to. Can be an array or a dictionary where key is the action type and value is the Observable emitting the payload. |
171
407
 
@@ -217,7 +453,7 @@ interface FormArrayConfig {
217
453
  Form state. Dictionary of [`FormControl`](#api-form-control)(s) where the key is a period separated representation of the [`ControlRef`](#api-control-ref) tuple.
218
454
 
219
455
  ```typescript
220
- export interface Form<T> {
456
+ interface Form<T> {
221
457
  root?: FormControl<T>;
222
458
  [key: string]: FormControl<unknown>;
223
459
  }
@@ -228,7 +464,7 @@ export interface Form<T> {
228
464
 
229
465
  ```typescript
230
466
 
231
- export interface FormControl<T> extends BaseControl<T>, Hub2Fields {
467
+ interface FormControl<T> {
232
468
  pristineValue: T;
233
469
  controlRef: ControlRef;
234
470
  value: T;
@@ -269,18 +505,36 @@ Control Reference represented as a tuple for the [`FormControl`](#api-form-contr
269
505
  Dictionary of errors for the control.
270
506
 
271
507
  ```typescript
272
- export interface FormErrors {
508
+ interface FormErrors {
273
509
  [key: string]: boolean;
274
510
  }
275
511
  ```
276
512
 
513
+ #### `ValidatorFn` <a name="api-validator-fn"></a>
514
+
515
+ Validator function that reads the value of the `FormControl` and returns a `FormErrors` object.
516
+
517
+ ```typescript
518
+ type ValidatorFn = (value: unknown) => FormErrors
519
+ ```
520
+
521
+ #### `ValidatorFnAsync` <a name="api-validator-fn-async"></a>
522
+
523
+ Validator function takes in an `BaseControl` observable and returns an `Observable<FormErrors>`.
524
+
525
+ ```typescript
526
+ type ValidatorAsyncFn = <T>(control$: Observable<BaseControl<T>>) => Observable<FormErrors>;
527
+
528
+ ```
529
+
530
+
277
531
  #### `FormReducers` <a name="api-form-reducers"></a>
278
532
 
279
- Built in reducers which can be used to update the state of the form tree. Payload and behaviour is the same and descrbed in [`RxActions`](#api-actions);
533
+ Built in reducers which can be used to update the state of the form tree. Payload and behaviour is the same and described in [`RxActions`](#api-actions);
280
534
 
281
535
  ```typescript
282
536
 
283
- export interface FormReducers {
537
+ interface FormReducers {
284
538
  updateValues: <T>(state: BaseFormState<T>, payload: UpdateValuesPayload<unknown>,
285
539
  ) => BaseFormState<T>;
286
540
  removeControl: <T>(state: BaseFormState<T>, payload: ControlRef) => BaseFormState<T>;
@@ -292,4 +546,55 @@ export interface FormReducers {
292
546
  ) => BaseFormState<T>;
293
547
  resetControl: <T>(state: BaseFormState<T>, payload: ControlRef) => BaseFormState<T>;
294
548
  }
549
+ ```
550
+ #### `CustomReducers` <a name="api-custom-reducers"></a>
551
+
552
+ ```typescript
553
+ type CustomReducerFunc = (
554
+ reducers: FormReducers,
555
+ state: BaseFormState<unknown>,
556
+ action: Action<unknown>,
557
+ ) => BaseFormState<unknown>;
558
+
559
+ type CustomReducer =
560
+ | CustomReducerFunc
561
+ | {
562
+ reducer: CustomReducerFunc;
563
+ effects?: Effect<unknown, unknown>[] | ((payload?: unknown) => ScopedEffects<unknown>);
564
+ };
565
+
566
+ ```
567
+
568
+ #### `BaseFormState` <a name="api-base-form-state"></a>
569
+
570
+ Form state before it is fully validated. This is accessible in `CustomReducer`s so developer can read the current state and implement custom form behaviours.
571
+
572
+ ```typescript
573
+ interface BaseFormState<T> {
574
+ form: BaseForm<T>;
575
+ }
576
+
577
+ interface BaseForm<T> {
578
+ root?: BaseControl<T>;
579
+ [key: string]: BaseControl<unknown>;
580
+ }
581
+
582
+ ```
583
+
584
+ #### `BaseControl` <a name="api-base-control"></a>
585
+
586
+ `BaseControl` contains some control information before a fully validated `FormControl` is created.
587
+
588
+ ```typescript
589
+
590
+ interface BaseControl<T> {
591
+ pristineValue: T;
592
+ controlRef: ControlRef;
593
+ value: T;
594
+ dirty: boolean;
595
+ touched: boolean;
596
+ validatorErrors: FormErrors;
597
+ config: AbstractControlConfig;
598
+ key: string;
599
+ }
295
600
  ```
@@ -0,0 +1,5 @@
1
+ import { ControlRef } from '../Models';
2
+ /**
3
+ * @description see if any keys in a ControlRef has a "." probably a mistake by developer
4
+ */
5
+ export declare const controlRefCheck: (ref: ControlRef) => void;
@@ -0,0 +1 @@
1
+ export {};
@@ -1 +1,3 @@
1
1
  export { getArrayItems } from './getArrayItems';
2
+ export { getDescendantControls } from './getDescendantControls';
3
+ export { getAncestorControls } from './getAncestorControls';
@@ -29,10 +29,10 @@ export interface FormControl<T> extends BaseControl<T>, Hub2Fields {
29
29
  }
30
30
  export interface BaseFormState<T> {
31
31
  form: BaseForm<T>;
32
- changedControls?: {
32
+ _changedControls?: {
33
33
  [key: string]: BaseControl<unknown>;
34
34
  };
35
- removedControls?: {
35
+ _removedConrols?: {
36
36
  [key: string]: BaseControl<unknown>;
37
37
  };
38
38
  }
@@ -2,4 +2,4 @@ import { Action } from '@reactables/core';
2
2
  import { BaseFormState } from '../../Models/Controls';
3
3
  import { UpdateValuesPayload } from '../../Models/Payloads';
4
4
  import { RxFormProviders } from '../../RxForm/RxForm';
5
- export declare const updateValues: <T>({ form, changedControls, removedControls }: BaseFormState<T>, action: Action<UpdateValuesPayload<unknown>>, providers: RxFormProviders, mergeChanges?: boolean) => BaseFormState<T>;
5
+ export declare const updateValues: <T>({ form, _changedControls, _removedConrols }: BaseFormState<T>, action: Action<UpdateValuesPayload<unknown>>, providers: RxFormProviders, mergeChanges?: boolean) => BaseFormState<T>;
@@ -1,5 +1,5 @@
1
1
  import { Form, FormControl, BaseFormState } from '../../Models/Controls';
2
- export declare const mergeControls: <T>(state: Form<T>, { form, changedControls, removedControls }: BaseFormState<unknown>) => {
2
+ export declare const mergeControls: <T>(state: Form<T>, { form, _changedControls, _removedConrols }: BaseFormState<unknown>) => {
3
3
  [x: string]: FormControl<unknown>;
4
4
  root?: FormControl<unknown>;
5
5
  };
@@ -17,7 +17,7 @@ export type RxFormActions = {
17
17
  markControlAsTouched: (payload: MarkTouchedPayload) => void;
18
18
  markControlAsUntouched: (payload: ControlRef) => void;
19
19
  resetControl: (payload: ControlRef) => void;
20
- };
20
+ } & ActionMap;
21
21
  export interface FormReducers {
22
22
  updateValues: <T>(state: BaseFormState<T>, payload: UpdateValuesPayload<unknown>) => BaseFormState<T>;
23
23
  removeControl: <T>(state: BaseFormState<T>, payload: ControlRef) => BaseFormState<T>;
@@ -31,7 +31,7 @@ export interface FormReducers {
31
31
  export type CustomReducerFunc = (reducers: FormReducers, state: BaseFormState<unknown>, action: Action<unknown>) => BaseFormState<unknown>;
32
32
  export type CustomReducer = CustomReducerFunc | {
33
33
  reducer: CustomReducerFunc;
34
- effects?: ((payload?: unknown) => ScopedEffects<unknown>) | Effect<unknown, unknown>[];
34
+ effects?: Effect<unknown, unknown>[] | ((payload?: unknown) => ScopedEffects<unknown>);
35
35
  };
36
36
  export type CustomReducers<T> = {
37
37
  [key in keyof (T & {
@@ -57,6 +57,6 @@ export interface RxFormProviders {
57
57
  [key: string]: ValidatorAsyncFn;
58
58
  };
59
59
  }
60
- export declare const build: (config: AbstractControlConfig, options?: RxFormOptions) => Reactable<Form<unknown>, ActionMap & RxFormActions>;
61
- export declare const load: (state: Form<unknown>, options?: RxFormOptions) => Reactable<Form<unknown>, ActionMap & RxFormActions>;
60
+ export declare const build: (config: AbstractControlConfig, options?: RxFormOptions) => Reactable<Form<unknown>, RxFormActions>;
61
+ export declare const load: (state: Form<unknown>, options?: RxFormOptions) => Reactable<Form<unknown>, RxFormActions>;
62
62
  export {};
package/dist/index.js CHANGED
@@ -240,8 +240,8 @@ var getAsyncValidationActions = function (formControls) {
240
240
  var buildHub2Source = function (hub1State$) {
241
241
  var hub1StateMapped$ = hub1State$.pipe(operators.map(function (payload) { return ({ type: 'formChange', payload: payload }); }));
242
242
  var sourceForHub2$ = hub1StateMapped$.pipe(operators.mergeMap(function (formChangeAction) {
243
- var changedControls = formChangeAction.payload.changedControls;
244
- var controlsToCheck = changedControls ? Object.values(changedControls) : [];
243
+ var _changedControls = formChangeAction.payload._changedControls;
244
+ var controlsToCheck = _changedControls ? Object.values(_changedControls) : [];
245
245
  var asyncValidationActions = getAsyncValidationActions(controlsToCheck);
246
246
  return rxjs.of.apply(void 0, __spreadArray([formChangeAction], asyncValidationActions, false));
247
247
  }));
@@ -336,6 +336,46 @@ var getAncestorControls = function (controlRef, form, excludeSelf) {
336
336
  });
337
337
  };
338
338
 
339
+ /**
340
+ * @description see if any keys in a ControlRef has a "." probably a mistake by developer
341
+ */
342
+ var controlRefCheck = function (ref) {
343
+ try {
344
+ var hasError = ref.some(function (key) {
345
+ if (typeof key === 'string') {
346
+ return key.includes('.');
347
+ }
348
+ return false;
349
+ });
350
+ if (hasError) {
351
+ var refString = "[".concat(ref.reduce(function (acc, key, index) {
352
+ if (index > 0) {
353
+ acc = acc.concat(', ');
354
+ }
355
+ if (typeof key === 'number') {
356
+ return acc.concat(key.toString());
357
+ }
358
+ return acc.concat("'".concat(key, "'"));
359
+ }, ''), "]");
360
+ var suggestion = "[".concat(ref.reduce(function (acc, key, index) {
361
+ if (index > 0) {
362
+ acc = acc.concat(', ');
363
+ }
364
+ if (typeof key === 'number') {
365
+ return acc.concat(key.toString());
366
+ }
367
+ var splitted = key
368
+ .split('.')
369
+ .map(function (key) { return "'".concat(key, "'"); })
370
+ .join(', ');
371
+ return acc.concat("".concat(splitted));
372
+ }, ''), "]");
373
+ console.warn("You provided ".concat(refString, ". Did you mean ").concat(suggestion, "?"));
374
+ }
375
+ }
376
+ catch (_a) { }
377
+ };
378
+
339
379
  var UPDATE_DESCENDANT_VALUES = 'UPDATE_DESCENDANT_VALUES';
340
380
  var updateDescendantValues = function (form, _a, providers) {
341
381
  var _b = _a.payload, controlRef = _b.controlRef, value = _b.value;
@@ -364,10 +404,11 @@ var updateDescendantValues = function (form, _a, providers) {
364
404
  // Use AddControlPayload/RemoveControl action reducers to add/remove control
365
405
  var updateValues = function (_a, action, providers, mergeChanges) {
366
406
  var _b, _c;
367
- var form = _a.form, _d = _a.changedControls, changedControls = _d === void 0 ? {} : _d, _e = _a.removedControls, removedControls = _e === void 0 ? {} : _e;
407
+ var form = _a.form, _d = _a._changedControls, _changedControls = _d === void 0 ? {} : _d, _e = _a._removedConrols, _removedConrols = _e === void 0 ? {} : _e;
368
408
  if (mergeChanges === void 0) { mergeChanges = false; }
369
409
  var normalizers = providers.normalizers;
370
410
  var _f = action.payload, controlRef = _f.controlRef, value = _f.value;
411
+ controlRefCheck(controlRef);
371
412
  // Update its own value
372
413
  var ctrlKey = getFormKey(controlRef);
373
414
  var newValue = value;
@@ -384,7 +425,7 @@ var updateValues = function (_a, action, providers, mergeChanges) {
384
425
  var newControl = __assign(__assign({}, form[ctrlKey]), { validatorErrors: validatorErrors, dirty: !isEqual__default["default"](value, form[ctrlKey].pristineValue), value: newValue });
385
426
  var result = {
386
427
  form: __assign(__assign({}, form), (_b = {}, _b[ctrlKey] = newControl, _b)),
387
- changedControls: (_c = {}, _c[newControl.key] = newControl, _c)
428
+ _changedControls: (_c = {}, _c[newControl.key] = newControl, _c)
388
429
  };
389
430
  var configControls = config.controls;
390
431
  // Update its descendants
@@ -400,7 +441,7 @@ var updateValues = function (_a, action, providers, mergeChanges) {
400
441
  var _a;
401
442
  return (__assign(__assign({}, acc), (_a = {}, _a[control.key] = control, _a)));
402
443
  }, {});
403
- result = __assign(__assign({}, result), { form: updatedDescendants, changedControls: __assign(__assign({}, result.changedControls), changedDescendantControls) });
444
+ result = __assign(__assign({}, result), { form: updatedDescendants, _changedControls: __assign(__assign({}, result._changedControls), changedDescendantControls) });
404
445
  }
405
446
  // Update its Ancestors
406
447
  if (controlRef.length) {
@@ -413,7 +454,7 @@ var updateValues = function (_a, action, providers, mergeChanges) {
413
454
  var _a;
414
455
  return (__assign(__assign({}, acc), (_a = {}, _a[control.key] = control, _a)));
415
456
  }, {});
416
- var mergedResult = __assign(__assign({}, result), { changedControls: __assign(__assign(__assign({}, (mergeChanges ? changedControls : undefined)), changedAncestorControls), result.changedControls), removedControls: mergeChanges ? removedControls : undefined });
457
+ var mergedResult = __assign(__assign({}, result), { _changedControls: __assign(__assign(__assign({}, (mergeChanges ? _changedControls : undefined)), changedAncestorControls), result._changedControls), _removedConrols: mergeChanges ? _removedConrols : undefined });
417
458
  return mergedResult;
418
459
  };
419
460
 
@@ -450,6 +491,7 @@ var removeControl = function (state, action, providers, mergeChanges) {
450
491
  if (mergeChanges === void 0) { mergeChanges = false; }
451
492
  var form = state.form;
452
493
  var controlRef = action.payload;
494
+ controlRefCheck(controlRef);
453
495
  var controlToRemove = getControl(controlRef, form);
454
496
  if (!controlToRemove) {
455
497
  throw 'Control not found';
@@ -495,13 +537,13 @@ var removeControl = function (state, action, providers, mergeChanges) {
495
537
  type: UPDATE_ANCESTOR_VALUES_REMOVE_CONTROL,
496
538
  payload: controlRef
497
539
  }, providers);
498
- var changedControls = __assign(__assign({}, (mergeChanges ? state.changedControls || {} : undefined)), getAncestorControls(controlRef.slice(0, -1), result).reduce(function (acc, control) {
540
+ var _changedControls = __assign(__assign({}, (mergeChanges ? state._changedControls || {} : undefined)), getAncestorControls(controlRef.slice(0, -1), result).reduce(function (acc, control) {
499
541
  var _a;
500
542
  return (__assign(__assign({}, acc), (_a = {}, _a[control.key] = control, _a)));
501
543
  }, {}));
502
544
  // Check for reindexing for changed controls
503
545
  if (parentIsFormArray) {
504
- changedControls = Object.entries(changedControls).reduce(function (acc, _a) {
546
+ _changedControls = Object.entries(_changedControls).reduce(function (acc, _a) {
505
547
  var _b, _c;
506
548
  var key = _a[0], control = _a[1];
507
549
  var oldIndex = control.controlRef.at(parentRef.length);
@@ -522,19 +564,19 @@ var removeControl = function (state, action, providers, mergeChanges) {
522
564
  }
523
565
  }, {});
524
566
  }
525
- var removedControls = __assign(__assign({}, (mergeChanges ? state.removedControls || {} : undefined)), (_a = {}, _a[controlToRemove.key] = controlToRemove, _a));
567
+ var _removedConrols = __assign(__assign({}, (mergeChanges ? state._removedConrols || {} : undefined)), (_a = {}, _a[controlToRemove.key] = controlToRemove, _a));
526
568
  descendants
527
569
  .map(function (_a) {
528
570
  var key = _a.key;
529
571
  return key;
530
572
  })
531
573
  .forEach(function (key) {
532
- delete changedControls[key];
574
+ delete _changedControls[key];
533
575
  });
534
576
  return {
535
577
  form: result,
536
- changedControls: changedControls,
537
- removedControls: removedControls
578
+ _changedControls: _changedControls,
579
+ _removedConrols: _removedConrols
538
580
  };
539
581
  };
540
582
 
@@ -574,6 +616,7 @@ var getControlBranch = function (controlRef, form) {
574
616
  var addControl = function (state, action, providers, mergeChanges) {
575
617
  if (mergeChanges === void 0) { mergeChanges = false; }
576
618
  var _a = action.payload, config = _a.config, controlRef = _a.controlRef;
619
+ controlRefCheck(controlRef);
577
620
  // If controlRef does not exist we are adding control to a Form Group
578
621
  if (!getControl(controlRef.slice(0, -1), state.form)) {
579
622
  throw 'You are attempting to add a control to a non-existent form group';
@@ -584,14 +627,14 @@ var addControl = function (state, action, providers, mergeChanges) {
584
627
  type: UPDATE_ANCESTOR_VALUES_ADD_CONTROL,
585
628
  payload: { controlRef: controlRef, value: newValue }
586
629
  }, providers);
587
- var changedControls = getControlBranch(controlRef, ancestorsUpdated).reduce(function (acc, control) {
630
+ var _changedControls = getControlBranch(controlRef, ancestorsUpdated).reduce(function (acc, control) {
588
631
  var _a;
589
632
  return (__assign(__assign({}, acc), (_a = {}, _a[control.key] = control, _a)));
590
633
  }, {});
591
634
  return {
592
635
  form: ancestorsUpdated,
593
- changedControls: __assign(__assign({}, (mergeChanges ? state.changedControls || {} : undefined)), changedControls),
594
- removedControls: mergeChanges ? state.removedControls || {} : undefined
636
+ _changedControls: __assign(__assign({}, (mergeChanges ? state._changedControls || {} : undefined)), _changedControls),
637
+ _removedConrols: mergeChanges ? state._removedConrols || {} : undefined
595
638
  };
596
639
  };
597
640
 
@@ -599,6 +642,7 @@ var pushControl = function (state, action, providers, mergeChanges) {
599
642
  if (mergeChanges === void 0) { mergeChanges = false; }
600
643
  var newControlRef;
601
644
  var _a = action.payload, config = _a.config, controlRef = _a.controlRef;
645
+ controlRefCheck(controlRef);
602
646
  var existingControl = getControl(controlRef, state.form);
603
647
  if (!existingControl)
604
648
  throw 'You are attempting to push to a control that does not exist';
@@ -615,14 +659,14 @@ var pushControl = function (state, action, providers, mergeChanges) {
615
659
  type: UPDATE_ANCESTOR_VALUES_ADD_CONTROL,
616
660
  payload: { controlRef: newControlRef, value: newValue }
617
661
  }, providers);
618
- var changedControls = getControlBranch(newControlRef, ancestorsUpdated).reduce(function (acc, control) {
662
+ var _changedControls = getControlBranch(newControlRef, ancestorsUpdated).reduce(function (acc, control) {
619
663
  var _a;
620
664
  return (__assign(__assign({}, acc), (_a = {}, _a[control.key] = control, _a)));
621
665
  }, {});
622
666
  return {
623
667
  form: ancestorsUpdated,
624
- changedControls: __assign(__assign({}, (mergeChanges ? state.changedControls || {} : undefined)), changedControls),
625
- removedControls: mergeChanges ? state.removedControls || {} : undefined
668
+ _changedControls: __assign(__assign({}, (mergeChanges ? state._changedControls || {} : undefined)), _changedControls),
669
+ _removedConrols: mergeChanges ? state._removedConrols || {} : undefined
626
670
  };
627
671
  };
628
672
 
@@ -666,6 +710,7 @@ var markControlAsPristine = function (state, action, mergeChanges) {
666
710
  if (mergeChanges === void 0) { mergeChanges = false; }
667
711
  var form = state.form;
668
712
  var controlRef = action.payload;
713
+ controlRefCheck(controlRef);
669
714
  var descendants = getDescendantControls(controlRef, form).reduce(function (acc, control) {
670
715
  var _a;
671
716
  return (__assign(__assign({}, acc), (_a = {}, _a[getFormKey(control.controlRef)] = __assign(__assign({}, control), { dirty: false, pristineValue: control.value }), _a)));
@@ -677,21 +722,22 @@ var markControlAsPristine = function (state, action, mergeChanges) {
677
722
  payload: controlRef
678
723
  });
679
724
  }
680
- var changedControls = getControlBranch(controlRef, result).reduce(function (acc, control) {
725
+ var _changedControls = getControlBranch(controlRef, result).reduce(function (acc, control) {
681
726
  var _a;
682
727
  return (__assign(__assign({}, acc), (_a = {}, _a[control.key] = control, _a)));
683
728
  }, {});
684
729
  return {
685
730
  form: result,
686
- changedControls: __assign(__assign({}, (mergeChanges ? state.changedControls || {} : undefined)), changedControls),
687
- removedControls: mergeChanges ? state.removedControls || {} : undefined
731
+ _changedControls: __assign(__assign({}, (mergeChanges ? state._changedControls || {} : undefined)), _changedControls),
732
+ _removedConrols: mergeChanges ? state._removedConrols || {} : undefined
688
733
  };
689
734
  };
690
735
 
691
736
  var markControlAsTouched = function (state, action, mergeChanges) {
692
737
  if (mergeChanges === void 0) { mergeChanges = false; }
693
738
  var form = state.form;
694
- var _a = action.payload, controlRef = _a.controlRef, markAll = _a.markAll;
739
+ var _a = action.payload, controlRef = _a.controlRef, _b = _a.markAll, markAll = _b === void 0 ? false : _b;
740
+ controlRefCheck(controlRef);
695
741
  var controls = (markAll ? getControlBranch(controlRef, form) : getAncestorControls(controlRef, form)).reduce(function (acc, control) {
696
742
  var _a;
697
743
  return (__assign(__assign({}, acc), (_a = {}, _a[getFormKey(control.controlRef)] = __assign(__assign({}, control), { touched: true }), _a)));
@@ -700,14 +746,14 @@ var markControlAsTouched = function (state, action, mergeChanges) {
700
746
  form: __assign(__assign({}, form), controls),
701
747
  action: action
702
748
  };
703
- var changedControls = getControlBranch(controlRef, result.form).reduce(function (acc, control) {
749
+ var _changedControls = getControlBranch(controlRef, result.form).reduce(function (acc, control) {
704
750
  var _a;
705
751
  return (__assign(__assign({}, acc), (_a = {}, _a[control.key] = control, _a)));
706
752
  }, {});
707
753
  return {
708
754
  form: __assign(__assign({}, form), controls),
709
- changedControls: __assign(__assign({}, (mergeChanges ? state.changedControls || {} : undefined)), changedControls),
710
- removedControls: mergeChanges ? state.removedControls || {} : undefined
755
+ _changedControls: __assign(__assign({}, (mergeChanges ? state._changedControls || {} : undefined)), _changedControls),
756
+ _removedConrols: mergeChanges ? state._removedConrols || {} : undefined
711
757
  };
712
758
  };
713
759
 
@@ -716,6 +762,7 @@ var markControlAsUntouched = function (state, action, mergeChanges) {
716
762
  if (mergeChanges === void 0) { mergeChanges = false; }
717
763
  var form = state.form;
718
764
  var controlRef = action.payload;
765
+ controlRefCheck(controlRef);
719
766
  var result = getDescendantControls(controlRef, form).reduce(function (acc, control) {
720
767
  var _a;
721
768
  return (__assign(__assign({}, acc), (_a = {}, _a[getFormKey(control.controlRef)] = __assign(__assign({}, control), { touched: false }), _a)));
@@ -729,14 +776,14 @@ var markControlAsUntouched = function (state, action, mergeChanges) {
729
776
  key = getFormKey(currentRef);
730
777
  result = __assign(__assign({}, result), (_a = {}, _a[key] = __assign(__assign({}, result[key]), { touched: getDescendantControls(currentRef, result, true).some(function (control) { return control.touched; }) }), _a));
731
778
  }
732
- var changedControls = getControlBranch(controlRef, result).reduce(function (acc, control) {
779
+ var _changedControls = getControlBranch(controlRef, result).reduce(function (acc, control) {
733
780
  var _a;
734
781
  return (__assign(__assign({}, acc), (_a = {}, _a[control.key] = control, _a)));
735
782
  }, {});
736
783
  return {
737
784
  form: result,
738
- changedControls: __assign(__assign({}, (mergeChanges ? state.changedControls || {} : undefined)), changedControls),
739
- removedControls: mergeChanges ? state.removedControls || {} : undefined
785
+ _changedControls: __assign(__assign({}, (mergeChanges ? state._changedControls || {} : undefined)), _changedControls),
786
+ _removedConrols: mergeChanges ? state._removedConrols || {} : undefined
740
787
  };
741
788
  };
742
789
 
@@ -745,6 +792,7 @@ var resetControl = function (state, action, providers, mergeChanges) {
745
792
  if (mergeChanges === void 0) { mergeChanges = false; }
746
793
  var form = state.form;
747
794
  var controlRef = action.payload;
795
+ controlRefCheck(controlRef);
748
796
  var controlToReset = getControl(controlRef, form);
749
797
  var descendantsRemoved = __assign({}, form);
750
798
  var descendants = getDescendantControls(controlRef, form);
@@ -762,24 +810,24 @@ var resetControl = function (state, action, providers, mergeChanges) {
762
810
  type: UPDATE_ANCESTOR_VALUES,
763
811
  payload: { controlRef: controlRef, value: restoredControlValue }
764
812
  }, providers);
765
- var changedControls = __assign(__assign({}, (mergeChanges ? state.changedControls || {} : undefined)), getControlBranch(controlRef, result).reduce(function (acc, control) {
813
+ var _changedControls = __assign(__assign({}, (mergeChanges ? state._changedControls || {} : undefined)), getControlBranch(controlRef, result).reduce(function (acc, control) {
766
814
  var _a;
767
815
  return (__assign(__assign({}, acc), (_a = {}, _a[control.key] = control, _a)));
768
816
  }, {}));
769
- var removedControls = __assign(__assign({}, (mergeChanges ? state.removedControls || {} : undefined)), (_a = {}, _a[controlToReset.key] = controlToReset, _a));
770
- // If control is removed, we can delete it from the changedControls check
817
+ var _removedConrols = __assign(__assign({}, (mergeChanges ? state._removedConrols || {} : undefined)), (_a = {}, _a[controlToReset.key] = controlToReset, _a));
818
+ // If control is removed, we can delete it from the _changedControls check
771
819
  descendants
772
820
  .map(function (_a) {
773
821
  var key = _a.key;
774
822
  return key;
775
823
  })
776
824
  .forEach(function (key) {
777
- delete changedControls[key];
825
+ delete _changedControls[key];
778
826
  });
779
827
  return {
780
828
  form: result,
781
- changedControls: changedControls,
782
- removedControls: removedControls
829
+ _changedControls: _changedControls,
830
+ _removedConrols: _removedConrols
783
831
  };
784
832
  };
785
833
 
@@ -937,26 +985,26 @@ var mergeRemoveControl = function (state, form, controlRef) {
937
985
  }, {});
938
986
  var updatedControlBranchOrderRestored = reverseObjectKeys(updatedControlBranch);
939
987
  var descendants = existingBranch.filter(function (control) { return control.controlRef.length > parentRef.length; });
940
- var removedControls = __assign({}, state);
988
+ var _removedConrols = __assign({}, state);
941
989
  descendants.forEach(function (control) {
942
- delete removedControls[getFormKey(control.controlRef)];
990
+ delete _removedConrols[getFormKey(control.controlRef)];
943
991
  });
944
- delete removedControls[getFormKey(controlRef)];
945
- return __assign(__assign({}, removedControls), updatedControlBranchOrderRestored);
992
+ delete _removedConrols[getFormKey(controlRef)];
993
+ return __assign(__assign({}, _removedConrols), updatedControlBranchOrderRestored);
946
994
  };
947
995
 
948
996
  var hasErrors = function (errors) {
949
997
  return Object.values(errors).some(function (hasError) { return hasError; });
950
998
  };
951
999
  var mergeControls = function (state, _a) {
952
- var form = _a.form, _b = _a.changedControls, changedControls = _b === void 0 ? {} : _b, removedControls = _a.removedControls;
953
- var controlsRemoved = removedControls
954
- ? Object.values(removedControls).reduce(function (acc, _a) {
1000
+ var form = _a.form, _b = _a._changedControls, _changedControls = _b === void 0 ? {} : _b, _removedConrols = _a._removedConrols;
1001
+ var controlsRemoved = _removedConrols
1002
+ ? Object.values(_removedConrols).reduce(function (acc, _a) {
955
1003
  var controlRef = _a.controlRef;
956
1004
  return mergeRemoveControl(acc, form, controlRef);
957
1005
  }, state)
958
1006
  : state;
959
- var updatedBranch = Object.values(changedControls)
1007
+ var updatedBranch = Object.values(_changedControls)
960
1008
  .reverse()
961
1009
  .reduce(function (acc, control) {
962
1010
  var _a;
@@ -1149,6 +1197,8 @@ exports.Validators = index;
1149
1197
  exports.array = array;
1150
1198
  exports.build = build;
1151
1199
  exports.control = control;
1200
+ exports.getAncestorControls = getAncestorControls;
1152
1201
  exports.getArrayItems = getArrayItems;
1202
+ exports.getDescendantControls = getDescendantControls;
1153
1203
  exports.group = group;
1154
1204
  exports.load = load;
package/package.json CHANGED
@@ -14,7 +14,7 @@
14
14
  "author": "David Lai",
15
15
  "license": "ISC",
16
16
  "dependencies": {
17
- "@reactables/core": "^1.0.0-beta.5",
17
+ "@reactables/core": "^1.0.0",
18
18
  "lodash.isequal": "^4.5.0"
19
19
  },
20
20
  "peerDependencies": {
@@ -23,5 +23,5 @@
23
23
  "devDependencies": {
24
24
  "lodash.clonedeep": "^4.5.0"
25
25
  },
26
- "version": "1.0.0-beta.5"
26
+ "version": "1.0.0"
27
27
  }