@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.
Files changed (132) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/cjs/index.cjs.js +4 -2
  3. package/dist/cjs/index.cjs.js.map +1 -1
  4. package/dist/cjs/loader.cjs.js +1 -1
  5. package/dist/cjs/nano-components.cjs.js +1 -1
  6. package/dist/cjs/nano-field-validator.cjs.entry.js +266 -124
  7. package/dist/cjs/nano-field-validator.cjs.entry.js.map +1 -1
  8. package/dist/cjs/nano-file-upload.cjs.entry.js +1 -1
  9. package/dist/cjs/nano-file-upload.cjs.entry.js.map +1 -1
  10. package/dist/cjs/nano-input.cjs.entry.js +16 -3
  11. package/dist/cjs/nano-input.cjs.entry.js.map +1 -1
  12. package/dist/cjs/nano-nav-item_2.cjs.entry.js +1 -0
  13. package/dist/cjs/nano-nav-item_2.cjs.entry.js.map +1 -1
  14. package/dist/collection/components/accordion/accordion.js +1 -1
  15. package/dist/collection/components/alert/alert.helpers.js +2 -2
  16. package/dist/collection/components/alert/alert.helpers.js.map +1 -1
  17. package/dist/collection/components/alert/alert.js +1 -1
  18. package/dist/collection/components/algolia/algolia-filter.js +2 -2
  19. package/dist/collection/components/algolia/algolia-input.js +5 -5
  20. package/dist/collection/components/algolia/algolia-results.js +1 -1
  21. package/dist/collection/components/algolia/algolia.js +6 -6
  22. package/dist/collection/components/checkbox/checkbox-group.js +2 -2
  23. package/dist/collection/components/checkbox/checkbox.js +3 -3
  24. package/dist/collection/components/datalist/datalist.js +1 -1
  25. package/dist/collection/components/date-input/date-input.js +8 -8
  26. package/dist/collection/components/date-picker/date-picker.js +5 -5
  27. package/dist/collection/components/details/details.js +1 -1
  28. package/dist/collection/components/dialog/dialog.js +1 -1
  29. package/dist/collection/components/dropdown/dropdown.js +1 -1
  30. package/dist/collection/components/field-validator/field-validator-interface.js.map +1 -1
  31. package/dist/collection/components/field-validator/field-validator.js +345 -130
  32. package/dist/collection/components/field-validator/field-validator.js.map +1 -1
  33. package/dist/collection/components/file-upload/file-upload.css +0 -1
  34. package/dist/collection/components/file-upload/file-upload.js +4 -4
  35. package/dist/collection/components/global-nav/global-nav.js +4 -4
  36. package/dist/collection/components/grid/grid-item.js +1 -1
  37. package/dist/collection/components/icon/icon.js +1 -1
  38. package/dist/collection/components/input/input.js +37 -8
  39. package/dist/collection/components/input/input.js.map +1 -1
  40. package/dist/collection/components/nav-item/nav-item.js +4 -4
  41. package/dist/collection/components/range/range.js +4 -4
  42. package/dist/collection/components/resize-observe/resize-observe.js +1 -1
  43. package/dist/collection/components/select/select.js +8 -7
  44. package/dist/collection/components/select/select.js.map +1 -1
  45. package/dist/collection/components/slides/slides.js +7 -7
  46. package/dist/collection/components/tabs/tab-group.js +2 -2
  47. package/dist/collection/index.js +1 -0
  48. package/dist/collection/index.js.map +1 -1
  49. package/dist/components/index.js +3 -2
  50. package/dist/components/index.js.map +1 -1
  51. package/dist/components/input.js +17 -3
  52. package/dist/components/input.js.map +1 -1
  53. package/dist/components/nano-field-validator.js +271 -126
  54. package/dist/components/nano-field-validator.js.map +1 -1
  55. package/dist/components/nano-file-upload.js +1 -1
  56. package/dist/components/nano-file-upload.js.map +1 -1
  57. package/dist/components/select.js +1 -0
  58. package/dist/components/select.js.map +1 -1
  59. package/dist/custom-elements/index.js +288 -132
  60. package/dist/custom-elements/index.js.map +1 -1
  61. package/dist/esm/index.js +3 -2
  62. package/dist/esm/index.js.map +1 -1
  63. package/dist/esm/loader.js +1 -1
  64. package/dist/esm/nano-components.js +1 -1
  65. package/dist/esm/nano-field-validator.entry.js +266 -124
  66. package/dist/esm/nano-field-validator.entry.js.map +1 -1
  67. package/dist/esm/nano-file-upload.entry.js +1 -1
  68. package/dist/esm/nano-file-upload.entry.js.map +1 -1
  69. package/dist/esm/nano-input.entry.js +16 -3
  70. package/dist/esm/nano-input.entry.js.map +1 -1
  71. package/dist/esm/nano-nav-item_2.entry.js +1 -0
  72. package/dist/esm/nano-nav-item_2.entry.js.map +1 -1
  73. package/dist/esm-es5/index.js +2 -2
  74. package/dist/esm-es5/index.js.map +1 -1
  75. package/dist/esm-es5/loader.js +1 -1
  76. package/dist/esm-es5/loader.js.map +1 -1
  77. package/dist/esm-es5/nano-components.js +1 -1
  78. package/dist/esm-es5/nano-components.js.map +1 -1
  79. package/dist/esm-es5/nano-field-validator.entry.js +2 -2
  80. package/dist/esm-es5/nano-field-validator.entry.js.map +1 -1
  81. package/dist/esm-es5/nano-file-upload.entry.js +1 -1
  82. package/dist/esm-es5/nano-file-upload.entry.js.map +1 -1
  83. package/dist/esm-es5/nano-input.entry.js +1 -1
  84. package/dist/esm-es5/nano-input.entry.js.map +1 -1
  85. package/dist/esm-es5/nano-nav-item_2.entry.js +1 -1
  86. package/dist/esm-es5/nano-nav-item_2.entry.js.map +1 -1
  87. package/dist/nano-components/index.esm.js +1 -1
  88. package/dist/nano-components/index.esm.js.map +1 -1
  89. package/dist/nano-components/nano-components.esm.js +1 -1
  90. package/dist/nano-components/nano-components.esm.js.map +1 -1
  91. package/dist/nano-components/p-085e03db.system.entry.js +5 -0
  92. package/dist/nano-components/p-085e03db.system.entry.js.map +1 -0
  93. package/dist/nano-components/p-53957ec6.system.js +1 -1
  94. package/dist/nano-components/p-53957ec6.system.js.map +1 -1
  95. package/dist/nano-components/{p-01667573.entry.js → p-634a58f7.entry.js} +2 -2
  96. package/dist/nano-components/p-634a58f7.entry.js.map +1 -0
  97. package/dist/nano-components/p-a07cf44c.system.entry.js +5 -0
  98. package/dist/nano-components/{p-4558a9c6.system.entry.js.map → p-a07cf44c.system.entry.js.map} +1 -1
  99. package/dist/nano-components/{p-96d9b8b9.system.entry.js → p-c2bbf0fb.system.entry.js} +2 -2
  100. package/dist/nano-components/p-c2bbf0fb.system.entry.js.map +1 -0
  101. package/dist/nano-components/{p-72893d12.system.entry.js → p-cb512cff.system.entry.js} +2 -2
  102. package/dist/nano-components/p-cb512cff.system.entry.js.map +1 -0
  103. package/dist/nano-components/p-e35226a9.entry.js +5 -0
  104. package/dist/nano-components/p-e35226a9.entry.js.map +1 -0
  105. package/dist/nano-components/p-e9fddc1a.entry.js +5 -0
  106. package/dist/nano-components/{p-91614b43.entry.js.map → p-e9fddc1a.entry.js.map} +1 -1
  107. package/dist/nano-components/{p-055f7d35.entry.js → p-ed0bdea9.entry.js} +2 -2
  108. package/dist/nano-components/p-ed0bdea9.entry.js.map +1 -0
  109. package/dist/nano-components/p-f62a40ea.system.js +5 -0
  110. package/dist/nano-components/{p-3258c568.system.js.map → p-f62a40ea.system.js.map} +1 -1
  111. package/dist/themes/nanopore.css +1 -1
  112. package/dist/themes/nanopore.css.map +1 -1
  113. package/dist/types/components/alert/alert.helpers.d.ts +2 -2
  114. package/dist/types/components/field-validator/field-validator-interface.d.ts +5 -1
  115. package/dist/types/components/field-validator/field-validator.d.ts +68 -12
  116. package/dist/types/components/input/input.d.ts +6 -1
  117. package/dist/types/components.d.ts +25 -3
  118. package/dist/types/index.d.ts +1 -0
  119. package/docs-json.json +65 -3
  120. package/docs-vscode.json +6 -2
  121. package/package.json +2 -2
  122. package/dist/nano-components/p-01667573.entry.js.map +0 -1
  123. package/dist/nano-components/p-055f7d35.entry.js.map +0 -1
  124. package/dist/nano-components/p-2b478ca1.system.entry.js +0 -5
  125. package/dist/nano-components/p-2b478ca1.system.entry.js.map +0 -1
  126. package/dist/nano-components/p-3258c568.system.js +0 -5
  127. package/dist/nano-components/p-4558a9c6.system.entry.js +0 -5
  128. package/dist/nano-components/p-5f4fc2b4.entry.js +0 -5
  129. package/dist/nano-components/p-5f4fc2b4.entry.js.map +0 -1
  130. package/dist/nano-components/p-72893d12.system.entry.js.map +0 -1
  131. package/dist/nano-components/p-91614b43.entry.js +0 -5
  132. 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.fields = [];
202
- // annoyingly, whenever we attempt to checkValidty it fires `invalid` events.
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
- /** Fired whenever store values change and potentially checks validity */
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.fields.find((field) => field.name === key);
215
- if (found && found.value !== newVal)
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
- /** Handles field value changes and passes to store */
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
- /** Handles default field validation events */
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
- ev.preventDefault();
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
- /** stops default form submission, checks if valid, then submits manually */
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.fields.forEach((field) => {
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.fields.forEach(async (field) => {
337
- const found = validationState.find((v) => v.name === field.name);
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
- found.validityMessage = field.validityMessage.length
340
- ? field.validityMessage
341
- : found.validityMessage;
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
- if (found.valid && field.invalid)
345
- found.valid = false;
346
- return;
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.name,
351
- valid: !field.invalid,
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
- validityMessage: field.validityMessage,
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 fields = Array.from(this.host.querySelectorAll(`
386
- nano-input,
387
- nano-select,
388
- nano-file-upload,
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 (!fields.filter((f) => !this.fields.includes(f)).length)
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.fields = fields;
398
- this.storeToFields(this.fields);
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.fields);
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
- const fieldName = field.name;
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
- switch (field.tagName) {
410
- case 'NANO-CHECKBOX':
411
- let cb = field;
412
- if (cb.type === 'radio' ||
413
- cb.type === 'segment' ||
414
- cb.type === 'segment-pill') {
415
- if (this._store.state[fieldName] === cb.value)
416
- cb.checked = true;
417
- else
418
- cb.checked = false;
419
- }
420
- else if (Array.isArray(this._store.state[fieldName])) {
421
- if (this._store.state[fieldName].includes(cb.value))
422
- cb.checked = true;
423
- else
424
- cb.checked = false;
425
- }
426
- else {
427
- if (this._store.state[fieldName] === cb.value)
428
- cb.checked = true;
429
- else
430
- cb.checked = false;
431
- }
432
- break;
433
- case 'NANO-FILE-UPLOAD':
434
- field.files =
435
- this._store.state[fieldName];
436
- break;
437
- default:
438
- field.value = this._store.state[fieldName];
439
- break;
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.name;
558
+ const fieldName = this.getName(field);
447
559
  if (!fieldName.length)
448
560
  return;
449
- switch (field.tagName) {
450
- case 'NANO-CHECKBOX':
451
- let cb = field;
452
- if (cb.type === 'radio' ||
453
- cb.type === 'segment' ||
454
- cb.type === 'segment-pill') {
455
- // radio type control - only one can be checked
456
- if (cb.checked)
457
- this._store.state[fieldName] = cb.value;
458
- else if (!cb.checked &&
459
- (cb.value === this._store.state[fieldName] ||
460
- !this._store.state[fieldName]))
461
- this._store.state[fieldName] = '';
462
- }
463
- else if (this.fields.filter((f) => f.name === fieldName && f.tagName === 'NANO-CHECKBOX').length > 1) {
464
- // multiple checkbox type control
465
- const currentArr = Array.isArray(this._store.state[fieldName])
466
- ? this._store.state[fieldName]
467
- : [];
468
- if (cb.checked) {
469
- if (!this._store.state[fieldName].includes(cb.value)) {
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
- // single checkbox - on or off
479
- if (cb.checked)
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
- break;
485
- case 'NANO-FILE-UPLOAD':
486
- this._store.state[fieldName] = field.files;
487
- break;
488
- default:
489
- this._store.state[fieldName] = field.value;
490
- break;
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
- /** Checks for user defined validations */
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.fields.find((f) => f.name === key);
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
- // status is now valid - clear the error
514
- if (validityTarget.validityMessage === o.msg && o.valid)
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
- // status is invalid. Set the error
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
- else
673
+ }
674
+ else if (field['setError'])
532
675
  await field.setError(msg);
533
- }
534
- /** Loops through all store entries and checks field validity */
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