@nanoporetech-digital/components 2.12.0 → 2.13.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.
- package/CHANGELOG.md +26 -0
- package/dist/cjs/index.cjs.js +4 -2
- package/dist/cjs/index.cjs.js.map +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/nano-components.cjs.js +1 -1
- package/dist/cjs/nano-field-validator.cjs.entry.js +266 -124
- package/dist/cjs/nano-field-validator.cjs.entry.js.map +1 -1
- package/dist/cjs/nano-file-upload.cjs.entry.js +1 -1
- package/dist/cjs/nano-file-upload.cjs.entry.js.map +1 -1
- package/dist/cjs/nano-input.cjs.entry.js +16 -3
- package/dist/cjs/nano-input.cjs.entry.js.map +1 -1
- package/dist/cjs/nano-nav-item_2.cjs.entry.js +1 -0
- package/dist/cjs/nano-nav-item_2.cjs.entry.js.map +1 -1
- package/dist/collection/components/accordion/accordion.js +1 -1
- package/dist/collection/components/alert/alert.helpers.js +2 -2
- package/dist/collection/components/alert/alert.helpers.js.map +1 -1
- package/dist/collection/components/alert/alert.js +1 -1
- package/dist/collection/components/algolia/algolia-filter.js +2 -2
- package/dist/collection/components/algolia/algolia-input.js +5 -5
- package/dist/collection/components/algolia/algolia-results.js +1 -1
- package/dist/collection/components/algolia/algolia.js +6 -6
- package/dist/collection/components/checkbox/checkbox-group.js +2 -2
- package/dist/collection/components/checkbox/checkbox.js +3 -3
- package/dist/collection/components/datalist/datalist.js +1 -1
- package/dist/collection/components/date-input/date-input.js +8 -8
- package/dist/collection/components/date-picker/date-picker.js +5 -5
- package/dist/collection/components/details/details.js +1 -1
- package/dist/collection/components/dialog/dialog.js +1 -1
- package/dist/collection/components/dropdown/dropdown.js +1 -1
- package/dist/collection/components/field-validator/field-validator-interface.js.map +1 -1
- package/dist/collection/components/field-validator/field-validator.js +345 -130
- package/dist/collection/components/field-validator/field-validator.js.map +1 -1
- package/dist/collection/components/file-upload/file-upload.css +0 -1
- package/dist/collection/components/file-upload/file-upload.js +4 -4
- package/dist/collection/components/global-nav/global-nav.js +4 -4
- package/dist/collection/components/grid/grid-item.js +1 -1
- package/dist/collection/components/icon/icon.js +1 -1
- package/dist/collection/components/input/input.js +37 -8
- package/dist/collection/components/input/input.js.map +1 -1
- package/dist/collection/components/nav-item/nav-item.js +4 -4
- package/dist/collection/components/range/range.js +4 -4
- package/dist/collection/components/resize-observe/resize-observe.js +1 -1
- package/dist/collection/components/select/select.js +8 -7
- package/dist/collection/components/select/select.js.map +1 -1
- package/dist/collection/components/slides/slides.js +7 -7
- package/dist/collection/components/tabs/tab-group.js +2 -2
- package/dist/collection/index.js +1 -0
- package/dist/collection/index.js.map +1 -1
- package/dist/components/index.js +3 -2
- package/dist/components/index.js.map +1 -1
- package/dist/components/input.js +17 -3
- package/dist/components/input.js.map +1 -1
- package/dist/components/nano-field-validator.js +271 -126
- package/dist/components/nano-field-validator.js.map +1 -1
- package/dist/components/nano-file-upload.js +1 -1
- package/dist/components/nano-file-upload.js.map +1 -1
- package/dist/components/select.js +1 -0
- package/dist/components/select.js.map +1 -1
- package/dist/custom-elements/index.js +288 -132
- package/dist/custom-elements/index.js.map +1 -1
- package/dist/esm/index.js +3 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/esm/nano-components.js +1 -1
- package/dist/esm/nano-field-validator.entry.js +266 -124
- package/dist/esm/nano-field-validator.entry.js.map +1 -1
- package/dist/esm/nano-file-upload.entry.js +1 -1
- package/dist/esm/nano-file-upload.entry.js.map +1 -1
- package/dist/esm/nano-input.entry.js +16 -3
- package/dist/esm/nano-input.entry.js.map +1 -1
- package/dist/esm/nano-nav-item_2.entry.js +1 -0
- package/dist/esm/nano-nav-item_2.entry.js.map +1 -1
- package/dist/esm-es5/index.js +2 -2
- package/dist/esm-es5/index.js.map +1 -1
- package/dist/esm-es5/loader.js +1 -1
- package/dist/esm-es5/loader.js.map +1 -1
- package/dist/esm-es5/nano-components.js +1 -1
- package/dist/esm-es5/nano-components.js.map +1 -1
- package/dist/esm-es5/nano-field-validator.entry.js +2 -2
- package/dist/esm-es5/nano-field-validator.entry.js.map +1 -1
- package/dist/esm-es5/nano-file-upload.entry.js +1 -1
- package/dist/esm-es5/nano-file-upload.entry.js.map +1 -1
- package/dist/esm-es5/nano-input.entry.js +1 -1
- package/dist/esm-es5/nano-input.entry.js.map +1 -1
- package/dist/esm-es5/nano-nav-item_2.entry.js +1 -1
- package/dist/esm-es5/nano-nav-item_2.entry.js.map +1 -1
- package/dist/nano-components/index.esm.js +1 -1
- package/dist/nano-components/index.esm.js.map +1 -1
- package/dist/nano-components/nano-components.esm.js +1 -1
- package/dist/nano-components/nano-components.esm.js.map +1 -1
- package/dist/nano-components/p-085e03db.system.entry.js +5 -0
- package/dist/nano-components/p-085e03db.system.entry.js.map +1 -0
- package/dist/nano-components/p-53957ec6.system.js +1 -1
- package/dist/nano-components/p-53957ec6.system.js.map +1 -1
- package/dist/nano-components/{p-01667573.entry.js → p-634a58f7.entry.js} +2 -2
- package/dist/nano-components/p-634a58f7.entry.js.map +1 -0
- package/dist/nano-components/p-a07cf44c.system.entry.js +5 -0
- package/dist/nano-components/{p-4558a9c6.system.entry.js.map → p-a07cf44c.system.entry.js.map} +1 -1
- package/dist/nano-components/{p-96d9b8b9.system.entry.js → p-c2bbf0fb.system.entry.js} +2 -2
- package/dist/nano-components/p-c2bbf0fb.system.entry.js.map +1 -0
- package/dist/nano-components/{p-72893d12.system.entry.js → p-cb512cff.system.entry.js} +2 -2
- package/dist/nano-components/p-cb512cff.system.entry.js.map +1 -0
- package/dist/nano-components/p-e35226a9.entry.js +5 -0
- package/dist/nano-components/p-e35226a9.entry.js.map +1 -0
- package/dist/nano-components/p-e9fddc1a.entry.js +5 -0
- package/dist/nano-components/{p-91614b43.entry.js.map → p-e9fddc1a.entry.js.map} +1 -1
- package/dist/nano-components/{p-055f7d35.entry.js → p-ed0bdea9.entry.js} +2 -2
- package/dist/nano-components/p-ed0bdea9.entry.js.map +1 -0
- package/dist/nano-components/p-f62a40ea.system.js +5 -0
- package/dist/nano-components/{p-3258c568.system.js.map → p-f62a40ea.system.js.map} +1 -1
- package/dist/themes/nanopore.css +1 -1
- package/dist/themes/nanopore.css.map +1 -1
- package/dist/types/components/alert/alert.helpers.d.ts +2 -2
- package/dist/types/components/field-validator/field-validator-interface.d.ts +5 -1
- package/dist/types/components/field-validator/field-validator.d.ts +68 -12
- package/dist/types/components/input/input.d.ts +6 -1
- package/dist/types/components.d.ts +25 -3
- package/dist/types/index.d.ts +1 -0
- package/docs-json.json +65 -3
- package/docs-vscode.json +6 -2
- package/package.json +2 -2
- package/dist/nano-components/p-01667573.entry.js.map +0 -1
- package/dist/nano-components/p-055f7d35.entry.js.map +0 -1
- package/dist/nano-components/p-2b478ca1.system.entry.js +0 -5
- package/dist/nano-components/p-2b478ca1.system.entry.js.map +0 -1
- package/dist/nano-components/p-3258c568.system.js +0 -5
- package/dist/nano-components/p-4558a9c6.system.entry.js +0 -5
- package/dist/nano-components/p-5f4fc2b4.entry.js +0 -5
- package/dist/nano-components/p-5f4fc2b4.entry.js.map +0 -1
- package/dist/nano-components/p-72893d12.system.entry.js.map +0 -1
- package/dist/nano-components/p-91614b43.entry.js +0 -5
- package/dist/nano-components/p-96d9b8b9.system.entry.js.map +0 -1
@@ -5,7 +5,6 @@ import { Component, Prop, h, Host, Element, State, Watch, Event, Method, } from
|
|
5
5
|
import { createStore } from '@stencil/store';
|
6
6
|
/**
|
7
7
|
* A toolbox for `nano-...` form fields and form validation.
|
8
|
-
*
|
9
8
|
* - Easy to add validation accross field dependencies - e.g. "When Field1 contains '123' Field2 must contain '456'"
|
10
9
|
* - Easy access to whole form and individual field validity states
|
11
10
|
* - Easy access to form data payload
|
@@ -15,8 +14,15 @@ import { createStore } from '@stencil/store';
|
|
15
14
|
export class FieldValidator {
|
16
15
|
constructor() {
|
17
16
|
this.submitted = false;
|
18
|
-
this.
|
19
|
-
|
17
|
+
this.allFields = [];
|
18
|
+
this.nanoFieldSelector = `
|
19
|
+
nano-input,
|
20
|
+
nano-select,
|
21
|
+
nano-file-upload,
|
22
|
+
nano-date-input,
|
23
|
+
nano-checkbox
|
24
|
+
`;
|
25
|
+
// annoyingly, whenever we attempt to `checkValidty()` it fires `invalid` events.
|
20
26
|
// this is used to prevent infinite loops / multiple calls
|
21
27
|
this.internalValidate = false;
|
22
28
|
// Public API
|
@@ -25,12 +31,27 @@ export class FieldValidator {
|
|
25
31
|
/** Tries to scroll to the first invalid field on submit */
|
26
32
|
this.scrollToInvalid = true;
|
27
33
|
this._dirty = false;
|
34
|
+
/** By default, `nano-field-validator` will also track all native form field elements.
|
35
|
+
* You can add extra web-component form fields to listen to
|
36
|
+
* (as long as they match the standard form field spec) by using the `fieldSelector` prop.
|
37
|
+
*/
|
38
|
+
this.extraFieldSelector = 'input, select, textarea';
|
28
39
|
// Event handlers
|
29
|
-
/**
|
40
|
+
/**
|
41
|
+
* Fired whenever store values change and potentially checks validity
|
42
|
+
* @param key - the key of the store that's just changed
|
43
|
+
* @param newVal - the incoming, new value
|
44
|
+
*/
|
30
45
|
this.handleStoreChange = async (key, newVal) => {
|
31
|
-
const found = this.
|
32
|
-
|
46
|
+
const found = this.allFields.find((field) => this.getName(field) === key);
|
47
|
+
// field update has come programmatically (not from ui),
|
48
|
+
// so let's update the underlying ui field
|
49
|
+
if ((found &&
|
50
|
+
found.tagName === 'NANO-FILE-UPLOAD' &&
|
51
|
+
!this.fileStateEqual(key, found)) ||
|
52
|
+
(found.tagName !== 'NANO-FILE-UPLOAD' && found.value !== newVal)) {
|
33
53
|
this.storeToFields([found]);
|
54
|
+
}
|
34
55
|
if (this.validateOn === 'dirty' && this.dirty) {
|
35
56
|
this.internalValidate = true;
|
36
57
|
await this.validateAllFields();
|
@@ -39,17 +60,40 @@ export class FieldValidator {
|
|
39
60
|
}
|
40
61
|
this.nanoPayloadChange.emit(this._store.state);
|
41
62
|
};
|
42
|
-
/**
|
63
|
+
/**
|
64
|
+
* Handles nano field value changes and passes to store
|
65
|
+
* @param ev - the incoming change event
|
66
|
+
*/
|
43
67
|
this.handleFieldChange = (ev) => {
|
68
|
+
if (!this.nanoFields.includes(ev.target))
|
69
|
+
return;
|
44
70
|
this._dirty = true;
|
45
71
|
this.fieldsToStore([ev.target]);
|
46
72
|
};
|
47
|
-
/**
|
73
|
+
/**
|
74
|
+
* Handles non-nano field value changes and passes to store
|
75
|
+
* @param ev - the incoming change event
|
76
|
+
*/
|
77
|
+
this.handlePlainFieldChange = (ev) => {
|
78
|
+
if (!this.plainFields.includes(ev.target))
|
79
|
+
return;
|
80
|
+
this.fieldsToStore([ev.target]);
|
81
|
+
};
|
82
|
+
/**
|
83
|
+
* Handles default field validation events
|
84
|
+
* @param ev - the invalid event
|
85
|
+
*/
|
48
86
|
this.handleFormInvalid = async (ev) => {
|
49
|
-
|
87
|
+
// if it's a non-nano field, we'll let default html5 validation do it's thing
|
88
|
+
if (!this.plainFields.includes(ev.target)) {
|
89
|
+
ev.preventDefault();
|
90
|
+
}
|
50
91
|
this._valid = false;
|
92
|
+
// whenever `checkValidity` is called, this handler is in-turn called.
|
93
|
+
// this flag is used to stop infinite loops
|
51
94
|
if (this.internalValidate)
|
52
95
|
return;
|
96
|
+
// a submit must have happened to if 'submitThenDirty' turn on 'dirty' checking now
|
53
97
|
if (this.validateOn === 'submitThenDirty')
|
54
98
|
this.validateOn = 'dirty';
|
55
99
|
this.submitted = true;
|
@@ -69,7 +113,10 @@ export class FieldValidator {
|
|
69
113
|
this.scrollToFirstInvalid();
|
70
114
|
this.nanoInvalid.emit();
|
71
115
|
};
|
72
|
-
/**
|
116
|
+
/**
|
117
|
+
* stops default form submission, checks if valid, then submits manually
|
118
|
+
* @param e - a submit event from the nested form element
|
119
|
+
*/
|
73
120
|
this.handleSubmit = async (e) => {
|
74
121
|
e.preventDefault();
|
75
122
|
if (this.validateOn === 'submitThenDirty')
|
@@ -94,6 +141,7 @@ export class FieldValidator {
|
|
94
141
|
return this._activeForm;
|
95
142
|
}
|
96
143
|
set activeForm(form) {
|
144
|
+
// manages event listners on whatever form is used (slotted on created here)
|
97
145
|
if (!form)
|
98
146
|
return;
|
99
147
|
if (this._activeForm) {
|
@@ -104,7 +152,7 @@ export class FieldValidator {
|
|
104
152
|
}
|
105
153
|
/** Sync up validateOn with all fields */
|
106
154
|
validateOnChange() {
|
107
|
-
this.
|
155
|
+
this.nanoFields.forEach((field) => {
|
108
156
|
if (field.tagName === 'NANO-CHECKBOX') {
|
109
157
|
const cbg = field.closest('nano-checkbox-group');
|
110
158
|
if (cbg)
|
@@ -132,7 +180,7 @@ export class FieldValidator {
|
|
132
180
|
get payload() {
|
133
181
|
return this._store.state;
|
134
182
|
}
|
135
|
-
/** Returns true if validation errors will be displayed to the user */
|
183
|
+
/** Returns true if validation errors will be displayed to the user. @readonly */
|
136
184
|
get showValidation() {
|
137
185
|
return (this.validateOn === 'dirty' && this.dirty) || this.submitted;
|
138
186
|
}
|
@@ -149,26 +197,58 @@ export class FieldValidator {
|
|
149
197
|
```
|
150
198
|
*/
|
151
199
|
get validationState() {
|
200
|
+
// TODO - migrate nano-fields away from using proprietary methods in a bid to be closer to the spec
|
201
|
+
// this is big and ugly.
|
202
|
+
// why? Cos' it must unify checking validity state for both
|
203
|
+
// `nano-...` and plain form fields.
|
152
204
|
const validationState = [];
|
153
|
-
this.
|
154
|
-
const found = validationState.find((v) => v.name === field
|
205
|
+
this.allFields.forEach(async (field) => {
|
206
|
+
const found = validationState.find((v) => v.name === this.getName(field));
|
207
|
+
let pf;
|
208
|
+
let nf;
|
155
209
|
if (found) {
|
156
|
-
|
157
|
-
|
158
|
-
|
210
|
+
if (field.validationMessage) {
|
211
|
+
pf = field;
|
212
|
+
found.validityMessage = pf.validationMessage.length
|
213
|
+
? pf.validationMessage
|
214
|
+
: found.validityMessage;
|
215
|
+
this.internalValidate = true;
|
216
|
+
if (found.valid && !pf.checkValidity())
|
217
|
+
found.valid = false;
|
218
|
+
this.internalValidate = false;
|
219
|
+
}
|
220
|
+
else if (field.validityMessage) {
|
221
|
+
nf = field;
|
222
|
+
found.validityMessage = nf.validityMessage.length
|
223
|
+
? nf.validityMessage
|
224
|
+
: nf.validityMessage;
|
225
|
+
if (found.valid && nf.invalid)
|
226
|
+
found.valid = false;
|
227
|
+
}
|
159
228
|
if (!found.fields.find((f) => f === field))
|
160
229
|
found.fields.push(field);
|
161
|
-
|
162
|
-
|
163
|
-
|
230
|
+
}
|
231
|
+
let valid;
|
232
|
+
let validityMessage;
|
233
|
+
if (field.checkValidity) {
|
234
|
+
pf = field;
|
235
|
+
this.internalValidate = true;
|
236
|
+
valid = pf.checkValidity();
|
237
|
+
this.internalValidate = false;
|
238
|
+
validityMessage = pf.validationMessage;
|
239
|
+
}
|
240
|
+
else {
|
241
|
+
nf = field;
|
242
|
+
valid = !nf.invalid;
|
243
|
+
validityMessage = nf.validityMessage;
|
164
244
|
}
|
165
245
|
validationState.push({
|
166
246
|
fields: [field],
|
167
|
-
name: field
|
168
|
-
|
169
|
-
value: this._store.state[field.name],
|
247
|
+
name: this.getName(field),
|
248
|
+
value: this._store.state[this.getName(field)],
|
170
249
|
dirty: false,
|
171
|
-
|
250
|
+
valid,
|
251
|
+
validityMessage,
|
172
252
|
});
|
173
253
|
});
|
174
254
|
return validationState;
|
@@ -180,6 +260,24 @@ export class FieldValidator {
|
|
180
260
|
async setStore(state) {
|
181
261
|
Object.entries(state).forEach(([key, val]) => (this.store.state[key] = val));
|
182
262
|
}
|
263
|
+
/**
|
264
|
+
* Sets custom validity for all / some form fields.
|
265
|
+
* @param validity - a validity object of `{fieldName: errorString}` pairs. Set as an empty string to clear the error.
|
266
|
+
*/
|
267
|
+
async setCustomValidity(validity) {
|
268
|
+
return await Promise.all(Object.entries(validity).map(async ([key, err]) => {
|
269
|
+
const field = this.allFields.find((f) => this.getName(f) === key);
|
270
|
+
if (!!field)
|
271
|
+
await this.setFieldError(field, err);
|
272
|
+
}));
|
273
|
+
}
|
274
|
+
/**
|
275
|
+
* Clear all custom validation.
|
276
|
+
* @param validity
|
277
|
+
*/
|
278
|
+
async resetValidity() {
|
279
|
+
return await Promise.all(this.allFields.map(async (field) => await this.setFieldError(field, '')));
|
280
|
+
}
|
183
281
|
// private methods
|
184
282
|
attachSlotObserver() {
|
185
283
|
if (!!this.mo)
|
@@ -197,118 +295,149 @@ export class FieldValidator {
|
|
197
295
|
subtree: true,
|
198
296
|
});
|
199
297
|
}
|
298
|
+
/**
|
299
|
+
* During spec tests, mockelement props aren't set - only attributes.
|
300
|
+
* This irons out that kink
|
301
|
+
* @param field
|
302
|
+
* @returns
|
303
|
+
*/
|
304
|
+
getName(field) {
|
305
|
+
return field.name || field.getAttribute('name');
|
306
|
+
}
|
200
307
|
/** Checks for new `nano-...` fields and adds them to our watch array and value store */
|
201
308
|
setupFields() {
|
202
|
-
let
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
nano-date-input,
|
207
|
-
nano-checkbox
|
208
|
-
`));
|
209
|
-
fields = fields.filter((f) => !!f.name && !!f.name.length);
|
309
|
+
let nanoFields = Array.from(this.host.querySelectorAll(this.nanoFieldSelector));
|
310
|
+
let plainFields = Array.from(this.host.querySelectorAll(this.extraFieldSelector)).filter((e) => !e.closest(this.nanoFieldSelector));
|
311
|
+
nanoFields = nanoFields.filter((f) => !!this.getName(f) && !!this.getName(f).length);
|
312
|
+
plainFields = plainFields.filter((f) => !!this.getName(f) && !!this.getName(f).length);
|
210
313
|
// do we have any currently un-watched fields?
|
211
|
-
if (!
|
314
|
+
if (![...nanoFields, ...plainFields].filter((f) => !this.allFields.includes(f)).length)
|
212
315
|
return;
|
213
316
|
// setup the initial store state / refresh on new fields
|
214
|
-
this.
|
215
|
-
this.
|
317
|
+
this.nanoFields = nanoFields;
|
318
|
+
this.plainFields = plainFields;
|
319
|
+
this.allFields = [...nanoFields, ...plainFields];
|
320
|
+
this.storeToFields(this.allFields);
|
216
321
|
this.validateOnChange();
|
217
|
-
this.fieldsToStore(this.
|
322
|
+
this.fieldsToStore(this.allFields);
|
218
323
|
this.nanoPayloadChange.emit(this._store.state);
|
219
324
|
}
|
220
325
|
storeToFields(fields) {
|
221
326
|
fields.forEach((field) => {
|
222
|
-
|
327
|
+
var _a;
|
328
|
+
const fieldName = this.getName(field);
|
223
329
|
if (!fieldName.length ||
|
224
330
|
typeof this._store.state[fieldName] === 'undefined')
|
225
331
|
return;
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
332
|
+
if (field.tagName === 'NANO-CHECKBOX' ||
|
333
|
+
['radio', 'checkbox'].includes(field.type)) {
|
334
|
+
let cb = field;
|
335
|
+
if (cb.type === 'radio' ||
|
336
|
+
cb.type === 'segment' ||
|
337
|
+
cb.type === 'segment-pill') {
|
338
|
+
// single radio type control
|
339
|
+
if (this._store.state[fieldName] === cb.value)
|
340
|
+
cb.checked = true;
|
341
|
+
else
|
342
|
+
cb.checked = false;
|
343
|
+
}
|
344
|
+
else if (Array.isArray(this._store.state[fieldName])) {
|
345
|
+
// multiple checkbox like controls
|
346
|
+
if (this._store.state[fieldName].includes(cb.value))
|
347
|
+
cb.checked = true;
|
348
|
+
else
|
349
|
+
cb.checked = false;
|
350
|
+
}
|
351
|
+
else {
|
352
|
+
// single checkbox like control
|
353
|
+
if (this._store.state[fieldName] === cb.value)
|
354
|
+
cb.checked = true;
|
355
|
+
else
|
356
|
+
cb.checked = false;
|
357
|
+
}
|
358
|
+
return;
|
359
|
+
}
|
360
|
+
if (field.tagName === 'NANO-FILE-UPLOAD') {
|
361
|
+
const ff = field;
|
362
|
+
// this can only work if the field is empty rn... a one-time deal
|
363
|
+
if (!((_a = ff.files) === null || _a === void 0 ? void 0 : _a.length))
|
364
|
+
ff.files = this._store.state[fieldName];
|
365
|
+
return;
|
257
366
|
}
|
367
|
+
// default
|
368
|
+
field.value = this._store.state[fieldName];
|
258
369
|
});
|
259
370
|
}
|
260
371
|
/** Loops through all `nano-...` fields and extracts their values into our store */
|
261
372
|
fieldsToStore(fields) {
|
262
373
|
fields.forEach((field) => {
|
263
|
-
const fieldName = field
|
374
|
+
const fieldName = this.getName(field);
|
264
375
|
if (!fieldName.length)
|
265
376
|
return;
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
if (cb.
|
286
|
-
|
287
|
-
this._store.state[fieldName] = [...currentArr, cb.value];
|
288
|
-
}
|
289
|
-
}
|
290
|
-
else {
|
291
|
-
this._store.state[fieldName] = currentArr.filter((v) => v !== cb.value);
|
377
|
+
if (field.tagName === 'NANO-CHECKBOX' ||
|
378
|
+
['radio', 'checkbox'].includes(field.type)) {
|
379
|
+
let cb = field;
|
380
|
+
if (cb.type === 'radio' ||
|
381
|
+
cb.type === 'segment' ||
|
382
|
+
cb.type === 'segment-pill') {
|
383
|
+
// radio type control - only one can be checked
|
384
|
+
if (cb.checked)
|
385
|
+
this._store.state[fieldName] = cb.value;
|
386
|
+
}
|
387
|
+
else if (this.allFields.filter((f) => !!this.getName(field) &&
|
388
|
+
(f.tagName === 'NANO-CHECKBOX' ||
|
389
|
+
f.type === 'checkbox')).length > 1) {
|
390
|
+
// multiple checkbox type control
|
391
|
+
const currentArr = Array.isArray(this._store.state[fieldName])
|
392
|
+
? this._store.state[fieldName]
|
393
|
+
: [];
|
394
|
+
if (cb.checked) {
|
395
|
+
// checked
|
396
|
+
if (!this._store.state[fieldName].includes(cb.value)) {
|
397
|
+
this._store.state[fieldName] = [...currentArr, cb.value];
|
292
398
|
}
|
293
399
|
}
|
294
400
|
else {
|
295
|
-
//
|
296
|
-
|
297
|
-
this._store.state[fieldName] = cb.value;
|
298
|
-
else
|
299
|
-
this._store.state[fieldName] = '';
|
401
|
+
// unchecked
|
402
|
+
this._store.state[fieldName] = currentArr.filter((v) => v !== cb.value);
|
300
403
|
}
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
404
|
+
}
|
405
|
+
else {
|
406
|
+
// single checkbox - on or off
|
407
|
+
if (cb.checked)
|
408
|
+
this._store.state[fieldName] = cb.value;
|
409
|
+
else
|
410
|
+
this._store.state[fieldName] = '';
|
411
|
+
}
|
412
|
+
return;
|
308
413
|
}
|
414
|
+
if (field.tagName === 'NANO-FILE-UPLOAD') {
|
415
|
+
const ff = field;
|
416
|
+
if (!this.fileStateEqual(fieldName, ff))
|
417
|
+
this._store.state[fieldName] = ff.files;
|
418
|
+
return;
|
419
|
+
}
|
420
|
+
// default
|
421
|
+
this._store.state[fieldName] = field.value;
|
309
422
|
});
|
310
423
|
}
|
311
|
-
/**
|
424
|
+
/**
|
425
|
+
* Tries to ascertain whether the current model
|
426
|
+
* value is the same as the `nano-file-upload` value
|
427
|
+
* @param fieldName - the key to access from the data store
|
428
|
+
* @param field - the nano-file-upload field to assess against
|
429
|
+
* @returns true for equal, false for not equal
|
430
|
+
*/
|
431
|
+
fileStateEqual(fieldName, field) {
|
432
|
+
return (JSON.stringify(this._store.state[fieldName]) ===
|
433
|
+
JSON.stringify(field.files) ||
|
434
|
+
this._store.state[fieldName] == field.files);
|
435
|
+
}
|
436
|
+
/**
|
437
|
+
* Checks for user defined validations
|
438
|
+
* @param key - current key of the data model to validate
|
439
|
+
* @param newVal - the newly set, incoming value to validate
|
440
|
+
*/
|
312
441
|
async validate(key, newVal) {
|
313
442
|
if (!this.validation)
|
314
443
|
return;
|
@@ -321,21 +450,33 @@ export class FieldValidator {
|
|
321
450
|
// collection loop into a promise
|
322
451
|
await Promise.all(Object.entries(res).map(async ([key, o]) => {
|
323
452
|
// switch on/off validation messages
|
324
|
-
const field = this.
|
453
|
+
const field = this.allFields.find((f) => this.getName(f) === key);
|
325
454
|
let validityTarget = field;
|
326
455
|
if (field.tagName === 'NANO-CHECKBOX') {
|
456
|
+
// if we have a checkbox-group, set the validation message there
|
327
457
|
const cbg = field.closest('nano-checkbox-group');
|
328
458
|
validityTarget = cbg || field;
|
329
459
|
}
|
330
|
-
|
331
|
-
|
460
|
+
if ((validityTarget.validityMessage ||
|
461
|
+
validityTarget.validationMessage) === o.msg &&
|
462
|
+
o.valid) {
|
463
|
+
// status is now valid - clear the error
|
332
464
|
await this.setFieldError(validityTarget, '');
|
333
|
-
|
465
|
+
}
|
334
466
|
else if (!o.valid) {
|
467
|
+
// status is invalid. Set the error
|
335
468
|
await this.setFieldError(validityTarget, o.msg);
|
336
469
|
}
|
337
470
|
}));
|
338
471
|
}
|
472
|
+
/** Loops through all store entries and checks custom validation */
|
473
|
+
async validateAllFields() {
|
474
|
+
// This forces our loop to `await` and finish sequentially ... silly async stencil methods
|
475
|
+
await Object.entries(this._store.state).reduce(async (memo, [key, value]) => {
|
476
|
+
await memo;
|
477
|
+
await this.validate(key, value);
|
478
|
+
}, undefined);
|
479
|
+
}
|
339
480
|
/**
|
340
481
|
* Utility to smooth out setting error messages
|
341
482
|
* (it's a different method on `nano-checkbox` 'cos they don't show errors themselves)
|
@@ -343,18 +484,13 @@ export class FieldValidator {
|
|
343
484
|
* @param msg
|
344
485
|
*/
|
345
486
|
async setFieldError(field, msg) {
|
346
|
-
if (field['showError'])
|
487
|
+
if (field['showError']) {
|
347
488
|
await field.showError(msg);
|
348
|
-
|
489
|
+
}
|
490
|
+
else if (field['setError'])
|
349
491
|
await field.setError(msg);
|
350
|
-
|
351
|
-
|
352
|
-
async validateAllFields() {
|
353
|
-
// This forces our loop to `await` and finish sequentially ... silly async stencil methods
|
354
|
-
await Object.entries(this._store.state).reduce(async (memo, [key, value]) => {
|
355
|
-
await memo;
|
356
|
-
await this.validate(key, value);
|
357
|
-
}, undefined);
|
492
|
+
else
|
493
|
+
field.setCustomValidity(msg);
|
358
494
|
}
|
359
495
|
scrollToFirstInvalid() {
|
360
496
|
if (!this.scrollToInvalid)
|
@@ -383,17 +519,21 @@ export class FieldValidator {
|
|
383
519
|
requestAnimationFrame(() => {
|
384
520
|
this.setupFields();
|
385
521
|
this.attachSlotObserver();
|
386
|
-
this._store.on('set', (key, value) => this.handleStoreChange(key, value));
|
387
522
|
this.host.addEventListener('nanoChange', this.handleFieldChange);
|
523
|
+
this.host.addEventListener('input', this.handlePlainFieldChange);
|
524
|
+
this.host.addEventListener('change', this.handlePlainFieldChange);
|
388
525
|
this.host.addEventListener('submit', this.handleSubmit);
|
526
|
+
this._store.on('set', this.handleStoreChange);
|
389
527
|
});
|
390
528
|
}
|
391
529
|
disconnectedCallback() {
|
392
530
|
if (this.mo)
|
393
531
|
this.mo.disconnect();
|
394
|
-
this._store.reset();
|
395
532
|
this.host.removeEventListener('nanoChange', this.handleFieldChange);
|
533
|
+
this.host.removeEventListener('input', this.handlePlainFieldChange);
|
534
|
+
this.host.removeEventListener('change', this.handlePlainFieldChange);
|
396
535
|
this.host.removeEventListener('submit', this.handleSubmit);
|
536
|
+
this._store.reset();
|
397
537
|
if (this.activeForm)
|
398
538
|
this.activeForm.removeEventListener('invalid', this.handleFormInvalid, true);
|
399
539
|
}
|
@@ -530,7 +670,7 @@ export class FieldValidator {
|
|
530
670
|
"optional": false,
|
531
671
|
"docs": {
|
532
672
|
"tags": [],
|
533
|
-
"text": "Returns true if validation errors will be displayed to the user"
|
673
|
+
"text": "Returns true if validation errors will be displayed to the user. @readonly"
|
534
674
|
},
|
535
675
|
"getter": true,
|
536
676
|
"setter": false,
|
@@ -546,7 +686,15 @@ export class FieldValidator {
|
|
546
686
|
"references": {
|
547
687
|
"ValidationState": {
|
548
688
|
"location": "import",
|
549
|
-
"path": "/builds/
|
689
|
+
"path": "/builds/YUQZGnoL/0/Digital/nano-components/packages/components/src/interface.d.ts"
|
690
|
+
},
|
691
|
+
"PlainFormEles": {
|
692
|
+
"location": "import",
|
693
|
+
"path": "/builds/YUQZGnoL/0/Digital/nano-components/packages/components/src/interface.d.ts"
|
694
|
+
},
|
695
|
+
"NanoFormEles": {
|
696
|
+
"location": "import",
|
697
|
+
"path": "/builds/YUQZGnoL/0/Digital/nano-components/packages/components/src/interface.d.ts"
|
550
698
|
}
|
551
699
|
}
|
552
700
|
},
|
@@ -559,6 +707,26 @@ export class FieldValidator {
|
|
559
707
|
"getter": true,
|
560
708
|
"setter": false
|
561
709
|
},
|
710
|
+
"extraFieldSelector": {
|
711
|
+
"type": "string",
|
712
|
+
"mutable": false,
|
713
|
+
"complexType": {
|
714
|
+
"original": "string",
|
715
|
+
"resolved": "string",
|
716
|
+
"references": {}
|
717
|
+
},
|
718
|
+
"required": false,
|
719
|
+
"optional": false,
|
720
|
+
"docs": {
|
721
|
+
"tags": [],
|
722
|
+
"text": "By default, `nano-field-validator` will also track all native form field elements.\nYou can add extra web-component form fields to listen to\n(as long as they match the standard form field spec) by using the `fieldSelector` prop."
|
723
|
+
},
|
724
|
+
"getter": false,
|
725
|
+
"setter": false,
|
726
|
+
"attribute": "extra-field-selector",
|
727
|
+
"reflect": false,
|
728
|
+
"defaultValue": "'input, select, textarea'"
|
729
|
+
},
|
562
730
|
"validation": {
|
563
731
|
"type": "unknown",
|
564
732
|
"mutable": false,
|
@@ -568,7 +736,7 @@ export class FieldValidator {
|
|
568
736
|
"references": {
|
569
737
|
"ValidatorValueStore": {
|
570
738
|
"location": "import",
|
571
|
-
"path": "/builds/
|
739
|
+
"path": "/builds/YUQZGnoL/0/Digital/nano-components/packages/components/src/interface.d.ts"
|
572
740
|
}
|
573
741
|
}
|
574
742
|
},
|
@@ -595,8 +763,8 @@ export class FieldValidator {
|
|
595
763
|
}
|
596
764
|
}; }
|
597
765
|
static get states() { return {
|
598
|
-
"userForm": {},
|
599
766
|
"submitted": {},
|
767
|
+
"userForm": {},
|
600
768
|
"_dirty": {},
|
601
769
|
"_valid": {},
|
602
770
|
"_store": {}
|
@@ -617,7 +785,7 @@ export class FieldValidator {
|
|
617
785
|
"references": {
|
618
786
|
"ValidatorValueStore": {
|
619
787
|
"location": "import",
|
620
|
-
"path": "/builds/
|
788
|
+
"path": "/builds/YUQZGnoL/0/Digital/nano-components/packages/components/src/interface.d.ts"
|
621
789
|
}
|
622
790
|
}
|
623
791
|
}
|
@@ -669,7 +837,7 @@ export class FieldValidator {
|
|
669
837
|
},
|
670
838
|
"ValidatorValueStore": {
|
671
839
|
"location": "import",
|
672
|
-
"path": "/builds/
|
840
|
+
"path": "/builds/YUQZGnoL/0/Digital/nano-components/packages/components/src/interface.d.ts"
|
673
841
|
}
|
674
842
|
},
|
675
843
|
"return": "Promise<void>"
|
@@ -681,6 +849,50 @@ export class FieldValidator {
|
|
681
849
|
"text": "state - the state to load in the store"
|
682
850
|
}]
|
683
851
|
}
|
852
|
+
},
|
853
|
+
"setCustomValidity": {
|
854
|
+
"complexType": {
|
855
|
+
"signature": "(validity: { [key: string]: string; }) => Promise<void[]>",
|
856
|
+
"parameters": [{
|
857
|
+
"tags": [{
|
858
|
+
"name": "param",
|
859
|
+
"text": "validity - a validity object of `{fieldName: errorString}` pairs. Set as an empty string to clear the error."
|
860
|
+
}],
|
861
|
+
"text": "- a validity object of `{fieldName: errorString}` pairs. Set as an empty string to clear the error."
|
862
|
+
}],
|
863
|
+
"references": {
|
864
|
+
"Promise": {
|
865
|
+
"location": "global"
|
866
|
+
}
|
867
|
+
},
|
868
|
+
"return": "Promise<void[]>"
|
869
|
+
},
|
870
|
+
"docs": {
|
871
|
+
"text": "Sets custom validity for all / some form fields.",
|
872
|
+
"tags": [{
|
873
|
+
"name": "param",
|
874
|
+
"text": "validity - a validity object of `{fieldName: errorString}` pairs. Set as an empty string to clear the error."
|
875
|
+
}]
|
876
|
+
}
|
877
|
+
},
|
878
|
+
"resetValidity": {
|
879
|
+
"complexType": {
|
880
|
+
"signature": "() => Promise<void[]>",
|
881
|
+
"parameters": [],
|
882
|
+
"references": {
|
883
|
+
"Promise": {
|
884
|
+
"location": "global"
|
885
|
+
}
|
886
|
+
},
|
887
|
+
"return": "Promise<void[]>"
|
888
|
+
},
|
889
|
+
"docs": {
|
890
|
+
"text": "Clear all custom validation.",
|
891
|
+
"tags": [{
|
892
|
+
"name": "param",
|
893
|
+
"text": "validity"
|
894
|
+
}]
|
895
|
+
}
|
684
896
|
}
|
685
897
|
}; }
|
686
898
|
static get elementRef() { return "host"; }
|
@@ -690,6 +902,9 @@ export class FieldValidator {
|
|
690
902
|
}, {
|
691
903
|
"propName": "validateOn",
|
692
904
|
"methodName": "validateOnChange"
|
905
|
+
}, {
|
906
|
+
"propName": "extraFieldSelector",
|
907
|
+
"methodName": "attachSlotObserver"
|
693
908
|
}]; }
|
694
909
|
}
|
695
910
|
//# sourceMappingURL=field-validator.js.map
|