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