@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
@@ -198,8 +198,15 @@ let FieldValidator = class {
|
|
198
198
|
this.nanoSubmit = index.createEvent(this, "nanoSubmit", 7);
|
199
199
|
this.nanoInvalid = index.createEvent(this, "nanoInvalid", 7);
|
200
200
|
this.submitted = false;
|
201
|
-
this.
|
202
|
-
|
201
|
+
this.allFields = [];
|
202
|
+
this.nanoFieldSelector = `
|
203
|
+
nano-input,
|
204
|
+
nano-select,
|
205
|
+
nano-file-upload,
|
206
|
+
nano-date-input,
|
207
|
+
nano-checkbox
|
208
|
+
`;
|
209
|
+
// annoyingly, whenever we attempt to `checkValidty()` it fires `invalid` events.
|
203
210
|
// this is used to prevent infinite loops / multiple calls
|
204
211
|
this.internalValidate = false;
|
205
212
|
// Public API
|
@@ -208,12 +215,27 @@ let FieldValidator = class {
|
|
208
215
|
/** Tries to scroll to the first invalid field on submit */
|
209
216
|
this.scrollToInvalid = true;
|
210
217
|
this._dirty = false;
|
218
|
+
/** By default, `nano-field-validator` will also track all native form field elements.
|
219
|
+
* You can add extra web-component form fields to listen to
|
220
|
+
* (as long as they match the standard form field spec) by using the `fieldSelector` prop.
|
221
|
+
*/
|
222
|
+
this.extraFieldSelector = 'input, select, textarea';
|
211
223
|
// Event handlers
|
212
|
-
/**
|
224
|
+
/**
|
225
|
+
* Fired whenever store values change and potentially checks validity
|
226
|
+
* @param key - the key of the store that's just changed
|
227
|
+
* @param newVal - the incoming, new value
|
228
|
+
*/
|
213
229
|
this.handleStoreChange = async (key, newVal) => {
|
214
|
-
const found = this.
|
215
|
-
|
230
|
+
const found = this.allFields.find((field) => this.getName(field) === key);
|
231
|
+
// field update has come programmatically (not from ui),
|
232
|
+
// so let's update the underlying ui field
|
233
|
+
if ((found &&
|
234
|
+
found.tagName === 'NANO-FILE-UPLOAD' &&
|
235
|
+
!this.fileStateEqual(key, found)) ||
|
236
|
+
(found.tagName !== 'NANO-FILE-UPLOAD' && found.value !== newVal)) {
|
216
237
|
this.storeToFields([found]);
|
238
|
+
}
|
217
239
|
if (this.validateOn === 'dirty' && this.dirty) {
|
218
240
|
this.internalValidate = true;
|
219
241
|
await this.validateAllFields();
|
@@ -222,17 +244,40 @@ let FieldValidator = class {
|
|
222
244
|
}
|
223
245
|
this.nanoPayloadChange.emit(this._store.state);
|
224
246
|
};
|
225
|
-
/**
|
247
|
+
/**
|
248
|
+
* Handles nano field value changes and passes to store
|
249
|
+
* @param ev - the incoming change event
|
250
|
+
*/
|
226
251
|
this.handleFieldChange = (ev) => {
|
252
|
+
if (!this.nanoFields.includes(ev.target))
|
253
|
+
return;
|
227
254
|
this._dirty = true;
|
228
255
|
this.fieldsToStore([ev.target]);
|
229
256
|
};
|
230
|
-
/**
|
257
|
+
/**
|
258
|
+
* Handles non-nano field value changes and passes to store
|
259
|
+
* @param ev - the incoming change event
|
260
|
+
*/
|
261
|
+
this.handlePlainFieldChange = (ev) => {
|
262
|
+
if (!this.plainFields.includes(ev.target))
|
263
|
+
return;
|
264
|
+
this.fieldsToStore([ev.target]);
|
265
|
+
};
|
266
|
+
/**
|
267
|
+
* Handles default field validation events
|
268
|
+
* @param ev - the invalid event
|
269
|
+
*/
|
231
270
|
this.handleFormInvalid = async (ev) => {
|
232
|
-
|
271
|
+
// if it's a non-nano field, we'll let default html5 validation do it's thing
|
272
|
+
if (!this.plainFields.includes(ev.target)) {
|
273
|
+
ev.preventDefault();
|
274
|
+
}
|
233
275
|
this._valid = false;
|
276
|
+
// whenever `checkValidity` is called, this handler is in-turn called.
|
277
|
+
// this flag is used to stop infinite loops
|
234
278
|
if (this.internalValidate)
|
235
279
|
return;
|
280
|
+
// a submit must have happened to if 'submitThenDirty' turn on 'dirty' checking now
|
236
281
|
if (this.validateOn === 'submitThenDirty')
|
237
282
|
this.validateOn = 'dirty';
|
238
283
|
this.submitted = true;
|
@@ -252,7 +297,10 @@ let FieldValidator = class {
|
|
252
297
|
this.scrollToFirstInvalid();
|
253
298
|
this.nanoInvalid.emit();
|
254
299
|
};
|
255
|
-
/**
|
300
|
+
/**
|
301
|
+
* stops default form submission, checks if valid, then submits manually
|
302
|
+
* @param e - a submit event from the nested form element
|
303
|
+
*/
|
256
304
|
this.handleSubmit = async (e) => {
|
257
305
|
e.preventDefault();
|
258
306
|
if (this.validateOn === 'submitThenDirty')
|
@@ -277,6 +325,7 @@ let FieldValidator = class {
|
|
277
325
|
return this._activeForm;
|
278
326
|
}
|
279
327
|
set activeForm(form) {
|
328
|
+
// manages event listners on whatever form is used (slotted on created here)
|
280
329
|
if (!form)
|
281
330
|
return;
|
282
331
|
if (this._activeForm) {
|
@@ -287,7 +336,7 @@ let FieldValidator = class {
|
|
287
336
|
}
|
288
337
|
/** Sync up validateOn with all fields */
|
289
338
|
validateOnChange() {
|
290
|
-
this.
|
339
|
+
this.nanoFields.forEach((field) => {
|
291
340
|
if (field.tagName === 'NANO-CHECKBOX') {
|
292
341
|
const cbg = field.closest('nano-checkbox-group');
|
293
342
|
if (cbg)
|
@@ -315,7 +364,7 @@ let FieldValidator = class {
|
|
315
364
|
get payload() {
|
316
365
|
return this._store.state;
|
317
366
|
}
|
318
|
-
/** Returns true if validation errors will be displayed to the user */
|
367
|
+
/** Returns true if validation errors will be displayed to the user. @readonly */
|
319
368
|
get showValidation() {
|
320
369
|
return (this.validateOn === 'dirty' && this.dirty) || this.submitted;
|
321
370
|
}
|
@@ -332,26 +381,58 @@ let FieldValidator = class {
|
|
332
381
|
```
|
333
382
|
*/
|
334
383
|
get validationState() {
|
384
|
+
// TODO - migrate nano-fields away from using proprietary methods in a bid to be closer to the spec
|
385
|
+
// this is big and ugly.
|
386
|
+
// why? Cos' it must unify checking validity state for both
|
387
|
+
// `nano-...` and plain form fields.
|
335
388
|
const validationState = [];
|
336
|
-
this.
|
337
|
-
const found = validationState.find((v) => v.name === field
|
389
|
+
this.allFields.forEach(async (field) => {
|
390
|
+
const found = validationState.find((v) => v.name === this.getName(field));
|
391
|
+
let pf;
|
392
|
+
let nf;
|
338
393
|
if (found) {
|
339
|
-
|
340
|
-
|
341
|
-
|
394
|
+
if (field.validationMessage) {
|
395
|
+
pf = field;
|
396
|
+
found.validityMessage = pf.validationMessage.length
|
397
|
+
? pf.validationMessage
|
398
|
+
: found.validityMessage;
|
399
|
+
this.internalValidate = true;
|
400
|
+
if (found.valid && !pf.checkValidity())
|
401
|
+
found.valid = false;
|
402
|
+
this.internalValidate = false;
|
403
|
+
}
|
404
|
+
else if (field.validityMessage) {
|
405
|
+
nf = field;
|
406
|
+
found.validityMessage = nf.validityMessage.length
|
407
|
+
? nf.validityMessage
|
408
|
+
: nf.validityMessage;
|
409
|
+
if (found.valid && nf.invalid)
|
410
|
+
found.valid = false;
|
411
|
+
}
|
342
412
|
if (!found.fields.find((f) => f === field))
|
343
413
|
found.fields.push(field);
|
344
|
-
|
345
|
-
|
346
|
-
|
414
|
+
}
|
415
|
+
let valid;
|
416
|
+
let validityMessage;
|
417
|
+
if (field.checkValidity) {
|
418
|
+
pf = field;
|
419
|
+
this.internalValidate = true;
|
420
|
+
valid = pf.checkValidity();
|
421
|
+
this.internalValidate = false;
|
422
|
+
validityMessage = pf.validationMessage;
|
423
|
+
}
|
424
|
+
else {
|
425
|
+
nf = field;
|
426
|
+
valid = !nf.invalid;
|
427
|
+
validityMessage = nf.validityMessage;
|
347
428
|
}
|
348
429
|
validationState.push({
|
349
430
|
fields: [field],
|
350
|
-
name: field
|
351
|
-
|
352
|
-
value: this._store.state[field.name],
|
431
|
+
name: this.getName(field),
|
432
|
+
value: this._store.state[this.getName(field)],
|
353
433
|
dirty: false,
|
354
|
-
|
434
|
+
valid,
|
435
|
+
validityMessage,
|
355
436
|
});
|
356
437
|
});
|
357
438
|
return validationState;
|
@@ -363,6 +444,24 @@ let FieldValidator = class {
|
|
363
444
|
async setStore(state) {
|
364
445
|
Object.entries(state).forEach(([key, val]) => (this.store.state[key] = val));
|
365
446
|
}
|
447
|
+
/**
|
448
|
+
* Sets custom validity for all / some form fields.
|
449
|
+
* @param validity - a validity object of `{fieldName: errorString}` pairs. Set as an empty string to clear the error.
|
450
|
+
*/
|
451
|
+
async setCustomValidity(validity) {
|
452
|
+
return await Promise.all(Object.entries(validity).map(async ([key, err]) => {
|
453
|
+
const field = this.allFields.find((f) => this.getName(f) === key);
|
454
|
+
if (!!field)
|
455
|
+
await this.setFieldError(field, err);
|
456
|
+
}));
|
457
|
+
}
|
458
|
+
/**
|
459
|
+
* Clear all custom validation.
|
460
|
+
* @param validity
|
461
|
+
*/
|
462
|
+
async resetValidity() {
|
463
|
+
return await Promise.all(this.allFields.map(async (field) => await this.setFieldError(field, '')));
|
464
|
+
}
|
366
465
|
// private methods
|
367
466
|
attachSlotObserver() {
|
368
467
|
if (!!this.mo)
|
@@ -380,118 +479,149 @@ let FieldValidator = class {
|
|
380
479
|
subtree: true,
|
381
480
|
});
|
382
481
|
}
|
482
|
+
/**
|
483
|
+
* During spec tests, mockelement props aren't set - only attributes.
|
484
|
+
* This irons out that kink
|
485
|
+
* @param field
|
486
|
+
* @returns
|
487
|
+
*/
|
488
|
+
getName(field) {
|
489
|
+
return field.name || field.getAttribute('name');
|
490
|
+
}
|
383
491
|
/** Checks for new `nano-...` fields and adds them to our watch array and value store */
|
384
492
|
setupFields() {
|
385
|
-
let
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
nano-date-input,
|
390
|
-
nano-checkbox
|
391
|
-
`));
|
392
|
-
fields = fields.filter((f) => !!f.name && !!f.name.length);
|
493
|
+
let nanoFields = Array.from(this.host.querySelectorAll(this.nanoFieldSelector));
|
494
|
+
let plainFields = Array.from(this.host.querySelectorAll(this.extraFieldSelector)).filter((e) => !e.closest(this.nanoFieldSelector));
|
495
|
+
nanoFields = nanoFields.filter((f) => !!this.getName(f) && !!this.getName(f).length);
|
496
|
+
plainFields = plainFields.filter((f) => !!this.getName(f) && !!this.getName(f).length);
|
393
497
|
// do we have any currently un-watched fields?
|
394
|
-
if (!
|
498
|
+
if (![...nanoFields, ...plainFields].filter((f) => !this.allFields.includes(f)).length)
|
395
499
|
return;
|
396
500
|
// setup the initial store state / refresh on new fields
|
397
|
-
this.
|
398
|
-
this.
|
501
|
+
this.nanoFields = nanoFields;
|
502
|
+
this.plainFields = plainFields;
|
503
|
+
this.allFields = [...nanoFields, ...plainFields];
|
504
|
+
this.storeToFields(this.allFields);
|
399
505
|
this.validateOnChange();
|
400
|
-
this.fieldsToStore(this.
|
506
|
+
this.fieldsToStore(this.allFields);
|
401
507
|
this.nanoPayloadChange.emit(this._store.state);
|
402
508
|
}
|
403
509
|
storeToFields(fields) {
|
404
510
|
fields.forEach((field) => {
|
405
|
-
|
511
|
+
var _a;
|
512
|
+
const fieldName = this.getName(field);
|
406
513
|
if (!fieldName.length ||
|
407
514
|
typeof this._store.state[fieldName] === 'undefined')
|
408
515
|
return;
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
516
|
+
if (field.tagName === 'NANO-CHECKBOX' ||
|
517
|
+
['radio', 'checkbox'].includes(field.type)) {
|
518
|
+
let cb = field;
|
519
|
+
if (cb.type === 'radio' ||
|
520
|
+
cb.type === 'segment' ||
|
521
|
+
cb.type === 'segment-pill') {
|
522
|
+
// single radio type control
|
523
|
+
if (this._store.state[fieldName] === cb.value)
|
524
|
+
cb.checked = true;
|
525
|
+
else
|
526
|
+
cb.checked = false;
|
527
|
+
}
|
528
|
+
else if (Array.isArray(this._store.state[fieldName])) {
|
529
|
+
// multiple checkbox like controls
|
530
|
+
if (this._store.state[fieldName].includes(cb.value))
|
531
|
+
cb.checked = true;
|
532
|
+
else
|
533
|
+
cb.checked = false;
|
534
|
+
}
|
535
|
+
else {
|
536
|
+
// single checkbox like control
|
537
|
+
if (this._store.state[fieldName] === cb.value)
|
538
|
+
cb.checked = true;
|
539
|
+
else
|
540
|
+
cb.checked = false;
|
541
|
+
}
|
542
|
+
return;
|
543
|
+
}
|
544
|
+
if (field.tagName === 'NANO-FILE-UPLOAD') {
|
545
|
+
const ff = field;
|
546
|
+
// this can only work if the field is empty rn... a one-time deal
|
547
|
+
if (!((_a = ff.files) === null || _a === void 0 ? void 0 : _a.length))
|
548
|
+
ff.files = this._store.state[fieldName];
|
549
|
+
return;
|
440
550
|
}
|
551
|
+
// default
|
552
|
+
field.value = this._store.state[fieldName];
|
441
553
|
});
|
442
554
|
}
|
443
555
|
/** Loops through all `nano-...` fields and extracts their values into our store */
|
444
556
|
fieldsToStore(fields) {
|
445
557
|
fields.forEach((field) => {
|
446
|
-
const fieldName = field
|
558
|
+
const fieldName = this.getName(field);
|
447
559
|
if (!fieldName.length)
|
448
560
|
return;
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
if (cb.
|
469
|
-
|
470
|
-
this._store.state[fieldName] = [...currentArr, cb.value];
|
471
|
-
}
|
472
|
-
}
|
473
|
-
else {
|
474
|
-
this._store.state[fieldName] = currentArr.filter((v) => v !== cb.value);
|
561
|
+
if (field.tagName === 'NANO-CHECKBOX' ||
|
562
|
+
['radio', 'checkbox'].includes(field.type)) {
|
563
|
+
let cb = field;
|
564
|
+
if (cb.type === 'radio' ||
|
565
|
+
cb.type === 'segment' ||
|
566
|
+
cb.type === 'segment-pill') {
|
567
|
+
// radio type control - only one can be checked
|
568
|
+
if (cb.checked)
|
569
|
+
this._store.state[fieldName] = cb.value;
|
570
|
+
}
|
571
|
+
else if (this.allFields.filter((f) => !!this.getName(field) &&
|
572
|
+
(f.tagName === 'NANO-CHECKBOX' ||
|
573
|
+
f.type === 'checkbox')).length > 1) {
|
574
|
+
// multiple checkbox type control
|
575
|
+
const currentArr = Array.isArray(this._store.state[fieldName])
|
576
|
+
? this._store.state[fieldName]
|
577
|
+
: [];
|
578
|
+
if (cb.checked) {
|
579
|
+
// checked
|
580
|
+
if (!this._store.state[fieldName].includes(cb.value)) {
|
581
|
+
this._store.state[fieldName] = [...currentArr, cb.value];
|
475
582
|
}
|
476
583
|
}
|
477
584
|
else {
|
478
|
-
//
|
479
|
-
|
480
|
-
this._store.state[fieldName] = cb.value;
|
481
|
-
else
|
482
|
-
this._store.state[fieldName] = '';
|
585
|
+
// unchecked
|
586
|
+
this._store.state[fieldName] = currentArr.filter((v) => v !== cb.value);
|
483
587
|
}
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
588
|
+
}
|
589
|
+
else {
|
590
|
+
// single checkbox - on or off
|
591
|
+
if (cb.checked)
|
592
|
+
this._store.state[fieldName] = cb.value;
|
593
|
+
else
|
594
|
+
this._store.state[fieldName] = '';
|
595
|
+
}
|
596
|
+
return;
|
597
|
+
}
|
598
|
+
if (field.tagName === 'NANO-FILE-UPLOAD') {
|
599
|
+
const ff = field;
|
600
|
+
if (!this.fileStateEqual(fieldName, ff))
|
601
|
+
this._store.state[fieldName] = ff.files;
|
602
|
+
return;
|
491
603
|
}
|
604
|
+
// default
|
605
|
+
this._store.state[fieldName] = field.value;
|
492
606
|
});
|
493
607
|
}
|
494
|
-
/**
|
608
|
+
/**
|
609
|
+
* Tries to ascertain whether the current model
|
610
|
+
* value is the same as the `nano-file-upload` value
|
611
|
+
* @param fieldName - the key to access from the data store
|
612
|
+
* @param field - the nano-file-upload field to assess against
|
613
|
+
* @returns true for equal, false for not equal
|
614
|
+
*/
|
615
|
+
fileStateEqual(fieldName, field) {
|
616
|
+
return (JSON.stringify(this._store.state[fieldName]) ===
|
617
|
+
JSON.stringify(field.files) ||
|
618
|
+
this._store.state[fieldName] == field.files);
|
619
|
+
}
|
620
|
+
/**
|
621
|
+
* Checks for user defined validations
|
622
|
+
* @param key - current key of the data model to validate
|
623
|
+
* @param newVal - the newly set, incoming value to validate
|
624
|
+
*/
|
495
625
|
async validate(key, newVal) {
|
496
626
|
if (!this.validation)
|
497
627
|
return;
|
@@ -504,21 +634,33 @@ let FieldValidator = class {
|
|
504
634
|
// collection loop into a promise
|
505
635
|
await Promise.all(Object.entries(res).map(async ([key, o]) => {
|
506
636
|
// switch on/off validation messages
|
507
|
-
const field = this.
|
637
|
+
const field = this.allFields.find((f) => this.getName(f) === key);
|
508
638
|
let validityTarget = field;
|
509
639
|
if (field.tagName === 'NANO-CHECKBOX') {
|
640
|
+
// if we have a checkbox-group, set the validation message there
|
510
641
|
const cbg = field.closest('nano-checkbox-group');
|
511
642
|
validityTarget = cbg || field;
|
512
643
|
}
|
513
|
-
|
514
|
-
|
644
|
+
if ((validityTarget.validityMessage ||
|
645
|
+
validityTarget.validationMessage) === o.msg &&
|
646
|
+
o.valid) {
|
647
|
+
// status is now valid - clear the error
|
515
648
|
await this.setFieldError(validityTarget, '');
|
516
|
-
|
649
|
+
}
|
517
650
|
else if (!o.valid) {
|
651
|
+
// status is invalid. Set the error
|
518
652
|
await this.setFieldError(validityTarget, o.msg);
|
519
653
|
}
|
520
654
|
}));
|
521
655
|
}
|
656
|
+
/** Loops through all store entries and checks custom validation */
|
657
|
+
async validateAllFields() {
|
658
|
+
// This forces our loop to `await` and finish sequentially ... silly async stencil methods
|
659
|
+
await Object.entries(this._store.state).reduce(async (memo, [key, value]) => {
|
660
|
+
await memo;
|
661
|
+
await this.validate(key, value);
|
662
|
+
}, undefined);
|
663
|
+
}
|
522
664
|
/**
|
523
665
|
* Utility to smooth out setting error messages
|
524
666
|
* (it's a different method on `nano-checkbox` 'cos they don't show errors themselves)
|
@@ -526,18 +668,13 @@ let FieldValidator = class {
|
|
526
668
|
* @param msg
|
527
669
|
*/
|
528
670
|
async setFieldError(field, msg) {
|
529
|
-
if (field['showError'])
|
671
|
+
if (field['showError']) {
|
530
672
|
await field.showError(msg);
|
531
|
-
|
673
|
+
}
|
674
|
+
else if (field['setError'])
|
532
675
|
await field.setError(msg);
|
533
|
-
|
534
|
-
|
535
|
-
async validateAllFields() {
|
536
|
-
// This forces our loop to `await` and finish sequentially ... silly async stencil methods
|
537
|
-
await Object.entries(this._store.state).reduce(async (memo, [key, value]) => {
|
538
|
-
await memo;
|
539
|
-
await this.validate(key, value);
|
540
|
-
}, undefined);
|
676
|
+
else
|
677
|
+
field.setCustomValidity(msg);
|
541
678
|
}
|
542
679
|
scrollToFirstInvalid() {
|
543
680
|
if (!this.scrollToInvalid)
|
@@ -566,17 +703,21 @@ let FieldValidator = class {
|
|
566
703
|
requestAnimationFrame(() => {
|
567
704
|
this.setupFields();
|
568
705
|
this.attachSlotObserver();
|
569
|
-
this._store.on('set', (key, value) => this.handleStoreChange(key, value));
|
570
706
|
this.host.addEventListener('nanoChange', this.handleFieldChange);
|
707
|
+
this.host.addEventListener('input', this.handlePlainFieldChange);
|
708
|
+
this.host.addEventListener('change', this.handlePlainFieldChange);
|
571
709
|
this.host.addEventListener('submit', this.handleSubmit);
|
710
|
+
this._store.on('set', this.handleStoreChange);
|
572
711
|
});
|
573
712
|
}
|
574
713
|
disconnectedCallback() {
|
575
714
|
if (this.mo)
|
576
715
|
this.mo.disconnect();
|
577
|
-
this._store.reset();
|
578
716
|
this.host.removeEventListener('nanoChange', this.handleFieldChange);
|
717
|
+
this.host.removeEventListener('input', this.handlePlainFieldChange);
|
718
|
+
this.host.removeEventListener('change', this.handlePlainFieldChange);
|
579
719
|
this.host.removeEventListener('submit', this.handleSubmit);
|
720
|
+
this._store.reset();
|
580
721
|
if (this.activeForm)
|
581
722
|
this.activeForm.removeEventListener('invalid', this.handleFormInvalid, true);
|
582
723
|
}
|
@@ -586,7 +727,8 @@ let FieldValidator = class {
|
|
586
727
|
get host() { return index.getElement(this); }
|
587
728
|
static get watchers() { return {
|
588
729
|
"userForm": ["userFormChange"],
|
589
|
-
"validateOn": ["validateOnChange"]
|
730
|
+
"validateOn": ["validateOnChange"],
|
731
|
+
"extraFieldSelector": ["attachSlotObserver"]
|
590
732
|
}; }
|
591
733
|
};
|
592
734
|
|