@quandis/qbo4.ui 4.0.1-CI-20240403-131518 → 4.0.1-CI-20240405-163539

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.
@@ -0,0 +1,121 @@
1
+ import { ILoggerService, ILoggerServiceToken, services } from '@quandis/qbo4.logging';
2
+ import { html, css, LitElement, PropertyValues } from 'lit';
3
+ import { customElement, property } from 'lit/decorators.js';
4
+ import { substitute } from './qbo-json.js';
5
+
6
+ @customElement('qbo-form-element')
7
+ export class QboFormElement extends LitElement {
8
+ static formAssociated = true;
9
+
10
+ @property({ type: FormData })
11
+ value: FormData = new FormData();
12
+
13
+ @property({ type: String })
14
+ template = '';
15
+
16
+ @property({ type: String })
17
+ name = '';
18
+
19
+ @property({ type: Object })
20
+ data = null;
21
+
22
+ private templateNode: HTMLTemplateElement | null = null;
23
+
24
+ private internals: ElementInternals;
25
+ logger: ILoggerService;
26
+
27
+ constructor() {
28
+ super();
29
+ this.internals = this.attachInternals(); // Attach the form internals
30
+ this.logger = services.container.resolve<ILoggerService>(ILoggerServiceToken);
31
+ }
32
+
33
+ connectedCallback() {
34
+ super.connectedCallback();
35
+ this.templateNode = document.getElementById(this.template) as HTMLTemplateElement;
36
+
37
+ if (this.templateNode) {
38
+ this.appendChild(this.templateNode.content.cloneNode(true));
39
+ this.innerHTML = substitute(this.innerHTML, this.data);
40
+ }
41
+ this.shadowRoot?.addEventListener('change', this.handleFormChange);
42
+ this.shadowRoot?.addEventListener('qbo-form-update', this.augment);
43
+
44
+ }
45
+
46
+ disconnectedCallback() {
47
+ super.disconnectedCallback();
48
+ this.shadowRoot?.removeEventListener('change', this.handleFormChange);
49
+ this.shadowRoot?.removeEventListener('qbo-form-update', this.augment);
50
+ }
51
+
52
+ updated(changedProperties: PropertyValues) {
53
+ super.updated(changedProperties);
54
+ this.establishFormData();
55
+ }
56
+
57
+ render() {
58
+ return html`<slot></slot>`;
59
+ }
60
+
61
+ establishFormData() {
62
+ const elements = this.shadowRoot?.querySelectorAll('input, select, textarea');
63
+ const inputs = Array.from(elements!).filter(e => !e.assignedSlot
64
+ && (e instanceof HTMLInputElement
65
+ || e instanceof HTMLSelectElement
66
+ || e instanceof HTMLTextAreaElement
67
+ || e instanceof QboFormElement
68
+ ) && e.name
69
+ );
70
+ inputs!.forEach(element => {
71
+ const input = element as HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | QboFormElement;
72
+
73
+ if (input instanceof HTMLSelectElement) {
74
+ this.value.set(`${this.name}${input.name}`, input.value);
75
+ } else if (input instanceof HTMLTextAreaElement) {
76
+ this.value.set(`${this.name}${input.name}`, input.value);
77
+ } else if (input instanceof QboFormElement) {
78
+ for (let [key, value] of input.value.entries()) {
79
+ this.value.set(`${this.name}${key}`, value);
80
+ }
81
+ } else if (input instanceof HTMLInputElement) {
82
+ if (input.type === 'checkbox' || input.type === 'radio') {
83
+ if (input.checked) {
84
+ this.value.set(`${this.name}${input.name}`, input.value);
85
+ }
86
+ } else {
87
+ this.value.set(`${this.name}${input.name}`, input.value);
88
+ }
89
+ }
90
+ });
91
+ this.internals.setFormValue(this.value);
92
+ this.logger.logTrace(`Set ${this.name} values to `, this.value);
93
+ }
94
+
95
+
96
+ handleFormChange = (event) => {
97
+ const target = event.target;
98
+ if (target.matches('input, select, textarea') && target.name && !target.assignedSlot) {
99
+ // if the element is assigned to a slot, it's already visible to the parent. Ignore it.
100
+ this.value.set(`${this.name}${target.name}`, target.value);
101
+ this.logger.logTrace(`${this.name}${target.name} was set to ${target.value}`);
102
+ }
103
+ this.internals.setFormValue(this.value);
104
+ this.dispatchEvent(new CustomEvent('qbo-form-update', { bubbles: true, composed: true, detail: { formData: this.value } }));
105
+ }
106
+
107
+ augment = (event) => {
108
+ event.stopPropagation();
109
+ console.log(event)
110
+ if (event instanceof CustomEvent && event.detail.formData instanceof FormData) {
111
+ // To replace existing entries instead of appending
112
+ for (let [key, value] of event.detail.formData.entries()) {
113
+ this.value.set(key, value);
114
+ console.log(`Added ${key}=${value} to ${this.name}`);
115
+ }
116
+ }
117
+ this.internals.setFormValue(this.value);
118
+ this.dispatchEvent(new CustomEvent('qbo-form-update', { bubbles: true, composed: true, detail: { formData: this.value } }));
119
+ }
120
+ }
121
+
@@ -1,8 +1,7 @@
1
- declare function getArray(json: any, arrayName?: string): Array<{
1
+ export declare function getArray(json: any, arrayName?: string): Array<{
2
2
  [key: string]: string;
3
3
  }> | null;
4
- declare function substitute(inputString: string, ...jsonData: any): string;
5
- declare function replicate(target: HTMLElement | null, template: HTMLTemplateElement | null, sourceData: Array<{
4
+ export declare function substitute(inputString: string, ...jsonData: any): string;
5
+ export declare function replicate(target: HTMLElement | null, template: HTMLTemplateElement | null, sourceData: Array<{
6
6
  [key: string]: string;
7
7
  }>, emptyContent?: boolean): void;
8
- export { getArray, substitute, replicate };
@@ -3,7 +3,7 @@
3
3
  * @param arrayName {array} The name of a key that represents an array to use. If not specified, the first array found will be returned.
4
4
  * @returns {Array} An array if found, otherwise null.
5
5
  */
6
- function getArray(json, arrayName) {
6
+ export function getArray(json, arrayName) {
7
7
  if (typeof json === 'string') {
8
8
  try {
9
9
  json = JSON.parse(json);
@@ -46,7 +46,7 @@ function interpolate(template, data) {
46
46
  /* @description Perform string substitution of ${expression} using one or more JSON objects.
47
47
  * @example substitute('Hello ${name}!', { name: 'World' }) => 'Hello World!'
48
48
  */
49
- function substitute(inputString, ...jsonData) {
49
+ function substituteOld(inputString, ...jsonData) {
50
50
  // Use a regular expression to match ${key} expressions
51
51
  const regex = /\${(.*?)}/g;
52
52
  let resultString = inputString;
@@ -65,9 +65,30 @@ function substitute(inputString, ...jsonData) {
65
65
  resultString = resultString.replace(regex, () => '');
66
66
  return resultString;
67
67
  }
68
+ export function substitute(inputString, ...jsonData) {
69
+ // Use a regular expression to match ${key} expressions
70
+ const regex = /\${(.*?)}/g;
71
+ let resultString = inputString;
72
+ for (let i = 0; i < jsonData.length; i++) {
73
+ const data = jsonData[i];
74
+ resultString = resultString.replace(regex, (match, expression) => {
75
+ const parts = expression.split(/\.|\[|\]/).filter(part => part !== '');
76
+ let currentValue = data;
77
+ // Iterate over the parts to access the desired value
78
+ for (let part of parts) {
79
+ if (currentValue[part] === undefined)
80
+ return match;
81
+ currentValue = currentValue[part];
82
+ }
83
+ return currentValue;
84
+ });
85
+ }
86
+ resultString = resultString.replace(regex, () => '');
87
+ return resultString;
88
+ }
68
89
  /* @description Replicates content in a target element for each item in a JSON array.
69
90
  */
70
- function replicate(target, template, sourceData, emptyContent = true) {
91
+ export function replicate(target, template, sourceData, emptyContent = true) {
71
92
  if (target != null && template != null) {
72
93
  while (emptyContent && target.firstChild) {
73
94
  target.removeChild(target.firstChild);
@@ -82,4 +103,3 @@ function replicate(target, template, sourceData, emptyContent = true) {
82
103
  });
83
104
  }
84
105
  }
85
- export { getArray, substitute, replicate };
@@ -4,7 +4,7 @@
4
4
  * @param arrayName {array} The name of a key that represents an array to use. If not specified, the first array found will be returned.
5
5
  * @returns {Array} An array if found, otherwise null.
6
6
  */
7
- function getArray(json: any, arrayName?: string): Array<{ [key: string]: string }> | null {
7
+ export function getArray(json: any, arrayName?: string): Array<{ [key: string]: string }> | null {
8
8
  if (typeof json === 'string') {
9
9
  try {
10
10
  json = JSON.parse(json);
@@ -49,7 +49,7 @@ function interpolate(template: string, data: { [key: string]: string }) {
49
49
  /* @description Perform string substitution of ${expression} using one or more JSON objects.
50
50
  * @example substitute('Hello ${name}!', { name: 'World' }) => 'Hello World!'
51
51
  */
52
- function substitute(inputString: string, ...jsonData: any): string {
52
+ function substituteOld(inputString: string, ...jsonData: any): string {
53
53
  // Use a regular expression to match ${key} expressions
54
54
  const regex = /\${(.*?)}/g;
55
55
  let resultString = inputString;
@@ -70,10 +70,34 @@ function substitute(inputString: string, ...jsonData: any): string {
70
70
  return resultString;
71
71
  }
72
72
 
73
+ export function substitute(inputString: string, ...jsonData: any): string {
74
+ // Use a regular expression to match ${key} expressions
75
+ const regex = /\${(.*?)}/g;
76
+ let resultString = inputString;
77
+ for (let i = 0; i < jsonData.length; i++) {
78
+ const data = jsonData[i] as Record<string, any>;
79
+ resultString = resultString.replace(regex, (match: string, expression: string) => {
80
+ const parts = expression.split(/\.|\[|\]/).filter(part => part !== '');
81
+ let currentValue: any = data;
82
+
83
+ // Iterate over the parts to access the desired value
84
+ for (let part of parts) {
85
+ if (currentValue[part] === undefined)
86
+ return match;
87
+ currentValue = currentValue[part];
88
+ }
89
+ return currentValue;
90
+ });
91
+ }
92
+ resultString = resultString.replace(regex, () => '');
93
+
94
+ return resultString;
95
+ }
96
+
73
97
 
74
98
  /* @description Replicates content in a target element for each item in a JSON array.
75
99
  */
76
- function replicate(target: HTMLElement | null, template: HTMLTemplateElement | null, sourceData: Array<{ [key: string]: string }>, emptyContent: boolean = true) {
100
+ export function replicate(target: HTMLElement | null, template: HTMLTemplateElement | null, sourceData: Array<{ [key: string]: string }>, emptyContent: boolean = true) {
77
101
  if (target != null && template != null) {
78
102
  while (emptyContent && target.firstChild) {
79
103
  target.removeChild(target.firstChild);
@@ -89,4 +113,3 @@ function replicate(target: HTMLElement | null, template: HTMLTemplateElement | n
89
113
  }
90
114
  }
91
115
 
92
- export { getArray, substitute, replicate };
@@ -1,11 +1,28 @@
1
1
  import { LitElement } from 'lit';
2
- import { IValidate } from './qbo-validators.js';
2
+ import { IValidate } from './IValidate.js';
3
3
  /**
4
- * Renders a <form> element and applies extensible validators.
5
- *
6
- * @remarks
7
- * Use HTML5 validation attributes to specify the type of validation to apply.
8
- */
4
+ * `QboValidate` component is responsible for performing validation on forms and inputs.
5
+ * It emits two custom events to indicate the completion of validation processes:
6
+ * - `qbo-form-validated`: Fired when the entire form has been validated.
7
+ * - `qbo-input-validated`: Fired when a single input within the form has been validated.
8
+ *
9
+ * @fires qbo-form-validated - Event fired when the form validation is complete.
10
+ * @fires qbo-input-validated - Event fired when an individual input validation is complete.
11
+ * @example
12
+ * ```html
13
+ * <form id="myForm">
14
+ * <qbo-validate></qbo-validate>
15
+ * </form>
16
+ * ```
17
+ * ```javascript
18
+ * document.querySelector('qbo-validate').addEventListener('qbo-form-validated', (e) => {
19
+ * console.log('Form validation completed');
20
+ * });
21
+ * document.querySelector('qbo-validate').addEventListener('qbo-input-validated', (e) => {
22
+ * console.log('Input validation completed');
23
+ * });
24
+ * ```
25
+ */
9
26
  export declare class QboValidate extends LitElement {
10
27
  renderInHost: boolean;
11
28
  autoComplete: string;
@@ -15,6 +32,7 @@ export declare class QboValidate extends LitElement {
15
32
  validators: IValidate[];
16
33
  connectedCallback(): Promise<void>;
17
34
  disconnectedCallback(): Promise<void>;
18
- validate(event: any): void;
35
+ validate: (event: Event) => Promise<void>;
36
+ validateElement: (source: Event | HTMLElement) => Promise<void>;
19
37
  render(): import("lit").TemplateResult<1>;
20
38
  }
@@ -7,16 +7,33 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __metadata = (this && this.__metadata) || function (k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  };
10
- import { html, LitElement } from 'lit';
10
+ import { LitElement, html } from 'lit';
11
11
  import { customElement, property } from 'lit/decorators.js';
12
- import { container } from 'tsyringe';
13
- import { ValidateToken } from './qbo-validators.js';
12
+ import { ValidateToken } from './IValidate.js';
13
+ import { services } from '@quandis/qbo4.logging';
14
14
  /**
15
- * Renders a <form> element and applies extensible validators.
16
- *
17
- * @remarks
18
- * Use HTML5 validation attributes to specify the type of validation to apply.
19
- */
15
+ * `QboValidate` component is responsible for performing validation on forms and inputs.
16
+ * It emits two custom events to indicate the completion of validation processes:
17
+ * - `qbo-form-validated`: Fired when the entire form has been validated.
18
+ * - `qbo-input-validated`: Fired when a single input within the form has been validated.
19
+ *
20
+ * @fires qbo-form-validated - Event fired when the form validation is complete.
21
+ * @fires qbo-input-validated - Event fired when an individual input validation is complete.
22
+ * @example
23
+ * ```html
24
+ * <form id="myForm">
25
+ * <qbo-validate></qbo-validate>
26
+ * </form>
27
+ * ```
28
+ * ```javascript
29
+ * document.querySelector('qbo-validate').addEventListener('qbo-form-validated', (e) => {
30
+ * console.log('Form validation completed');
31
+ * });
32
+ * document.querySelector('qbo-validate').addEventListener('qbo-input-validated', (e) => {
33
+ * console.log('Input validation completed');
34
+ * });
35
+ * ```
36
+ */
20
37
  let QboValidate = class QboValidate extends LitElement {
21
38
  constructor() {
22
39
  super(...arguments);
@@ -26,40 +43,74 @@ let QboValidate = class QboValidate extends LitElement {
26
43
  this.workingClass = ['bg-light', 'text-secondary'];
27
44
  this.form = null;
28
45
  this.validators = [];
46
+ this.validate = async (event) => {
47
+ console.log('validating form');
48
+ event.preventDefault();
49
+ const targets = Array.from(this.form?.querySelectorAll('*'));
50
+ for (const target of targets) {
51
+ if (target instanceof HTMLInputElement || target instanceof HTMLSelectElement || target instanceof HTMLTextAreaElement) {
52
+ console.log('validating element ', target);
53
+ await this.validateElement(target);
54
+ }
55
+ else
56
+ console.log('ignoring element ', target);
57
+ }
58
+ if (!this.form?.checkValidity()) {
59
+ console.log('form is invalid');
60
+ event.preventDefault();
61
+ event.stopPropagation();
62
+ }
63
+ else {
64
+ console.log('form is valid, js submit');
65
+ // this.form.submit();
66
+ }
67
+ this.form?.classList.add(this.validatedClass);
68
+ this.dispatchEvent(new Event('qbo-form-validated', { bubbles: true }));
69
+ };
70
+ this.validateElement = async (source) => {
71
+ const target = source instanceof Event ? source.target : source;
72
+ console.log('validateElement start ', target);
73
+ if (target instanceof HTMLInputElement || target instanceof HTMLSelectElement || target instanceof HTMLTextAreaElement) {
74
+ target.setCustomValidity('');
75
+ // Re-verify build-in validation.
76
+ if (!target.checkValidity())
77
+ return;
78
+ for (const validator of this.validators) {
79
+ if (target.matches(validator.selector)) {
80
+ console.log('element matches selector ', validator.selector, ' for ', target.matches(validator.selector) ? 'true' : 'false');
81
+ if (!target.checkValidity())
82
+ break;
83
+ // Show some loading indication if necessary.
84
+ this.workingClass.forEach(c => target.classList.add(c));
85
+ // Validate the element.
86
+ const valid = await validator.validate(target);
87
+ // Clear loading indication.
88
+ this.workingClass.forEach(c => target.classList.remove(c));
89
+ // Set custom validity based on the validation result.
90
+ target.setCustomValidity(valid ? '' : validator.message);
91
+ console.log('input validity is ', target.checkValidity(), ' message is ', target.validationMessage);
92
+ }
93
+ else
94
+ console.log('element does not match selector ', validator.selector, ' for ', target.matches(validator.selector) ? 'true' : 'false');
95
+ }
96
+ }
97
+ console.log('validateElement end ', target);
98
+ target?.dispatchEvent(new Event('qbo-input-validated', { bubbles: true }));
99
+ };
29
100
  }
30
101
  async connectedCallback() {
31
102
  super.connectedCallback();
32
103
  this.form = this.closest('form');
33
104
  if (this.form == null)
34
105
  return;
35
- this.form?.addEventListener('submit', this.validate.bind(this));
36
- this.validators = container.resolveAll(ValidateToken);
37
- this.validators.forEach(validator => {
38
- this.form?.querySelectorAll(validator.selector).forEach(element => {
39
- element.addEventListener('change', async () => {
40
- if (element instanceof HTMLInputElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement) {
41
- element.setCustomValidity('');
42
- if (!element.checkValidity())
43
- return;
44
- this.workingClass.forEach(c => element.classList.add(c));
45
- var valid = await validator.validate(element);
46
- this.workingClass.forEach(c => element.classList.add(c));
47
- element.setCustomValidity(valid ? '' : validator.message);
48
- }
49
- });
50
- });
51
- });
106
+ this.form?.addEventListener('submit', this.validate);
107
+ this.form?.addEventListener('change', this.validateElement);
108
+ this.validators = services.container.resolveAll(ValidateToken);
52
109
  }
53
110
  async disconnectedCallback() {
54
111
  super.disconnectedCallback();
55
- this.form?.removeEventListener('submit', this.validate.bind(this));
56
- }
57
- validate(event) {
58
- if (!this.form?.checkValidity()) {
59
- event.preventDefault();
60
- event.stopPropagation();
61
- }
62
- this.form?.classList.add(this.validatedClass);
112
+ this.form?.addEventListener('change', this.validateElement);
113
+ this.form?.addEventListener('submit', this.validate);
63
114
  }
64
115
  render() {
65
116
  return html ``;
@@ -1,14 +1,31 @@
1
- import { html, css, LitElement } from 'lit';
1
+ import { LitElement, html } from 'lit';
2
2
  import { customElement, property } from 'lit/decorators.js';
3
- import { container, injectable, injectAll, InjectionToken } from 'tsyringe';
4
- import { IValidate, ValidateToken } from './qbo-validators.js';
3
+ import { IValidate, ValidateToken } from './IValidate.js';
4
+ import { services } from '@quandis/qbo4.logging';
5
5
 
6
6
  /**
7
- * Renders a <form> element and applies extensible validators.
8
- *
9
- * @remarks
10
- * Use HTML5 validation attributes to specify the type of validation to apply.
11
- */
7
+ * `QboValidate` component is responsible for performing validation on forms and inputs.
8
+ * It emits two custom events to indicate the completion of validation processes:
9
+ * - `qbo-form-validated`: Fired when the entire form has been validated.
10
+ * - `qbo-input-validated`: Fired when a single input within the form has been validated.
11
+ *
12
+ * @fires qbo-form-validated - Event fired when the form validation is complete.
13
+ * @fires qbo-input-validated - Event fired when an individual input validation is complete.
14
+ * @example
15
+ * ```html
16
+ * <form id="myForm">
17
+ * <qbo-validate></qbo-validate>
18
+ * </form>
19
+ * ```
20
+ * ```javascript
21
+ * document.querySelector('qbo-validate').addEventListener('qbo-form-validated', (e) => {
22
+ * console.log('Form validation completed');
23
+ * });
24
+ * document.querySelector('qbo-validate').addEventListener('qbo-input-validated', (e) => {
25
+ * console.log('Input validation completed');
26
+ * });
27
+ * ```
28
+ */
12
29
  @customElement('qbo-validate')
13
30
  export class QboValidate extends LitElement {
14
31
 
@@ -37,37 +54,73 @@ export class QboValidate extends LitElement {
37
54
  this.form = this.closest('form');
38
55
  if (this.form == null)
39
56
  return;
40
-
41
- this.form?.addEventListener('submit', this.validate.bind(this));
42
- this.validators = container.resolveAll<IValidate>(ValidateToken);
43
- this.validators.forEach(validator => {
44
- this.form?.querySelectorAll(validator.selector).forEach(element => {
45
- element.addEventListener('change', async () => {
46
- if (element instanceof HTMLInputElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement) {
47
- element.setCustomValidity('');
48
- if (!element.checkValidity())
49
- return;
50
- this.workingClass.forEach(c => element.classList.add(c));
51
- var valid = await validator.validate(element);
52
- this.workingClass.forEach(c => element.classList.add(c))
53
- element.setCustomValidity(valid ? '' : validator.message);
54
- }
55
- });
56
- });
57
- });
57
+ this.form?.addEventListener('submit', this.validate);
58
+ this.form?.addEventListener('change', this.validateElement);
59
+ this.validators = services.container.resolveAll<IValidate>(ValidateToken);
58
60
  }
59
61
 
60
62
  async disconnectedCallback() {
61
63
  super.disconnectedCallback();
62
- this.form?.removeEventListener('submit', this.validate.bind(this));
64
+ this.form?.addEventListener('change', this.validateElement);
65
+ this.form?.addEventListener('submit', this.validate);
63
66
  }
64
67
 
65
- validate(event: any) {
68
+ validate = async (event: Event) => {
69
+ console.log('validating form');
70
+ event.preventDefault();
71
+ const targets = Array.from(this.form?.querySelectorAll('*')!);
72
+ for (const target of targets) {
73
+ if (target instanceof HTMLInputElement || target instanceof HTMLSelectElement || target instanceof HTMLTextAreaElement) {
74
+ console.log('validating element ', target);
75
+ await this.validateElement(target);
76
+ } else
77
+ console.log('ignoring element ', target);
78
+ }
66
79
  if (!this.form?.checkValidity()) {
80
+ console.log('form is invalid');
67
81
  event.preventDefault()
68
82
  event.stopPropagation()
69
83
  }
84
+ else {
85
+ console.log('form is valid, js submit');
86
+ // this.form.submit();
87
+ }
70
88
  this.form?.classList.add(this.validatedClass);
89
+ this.dispatchEvent(new Event('qbo-form-validated', { bubbles: true }));
90
+ }
91
+
92
+ validateElement = async (source: Event | HTMLElement) => {
93
+ const target = source instanceof Event ? source.target : source;
94
+ console.log('validateElement start ', target)
95
+ if (target instanceof HTMLInputElement || target instanceof HTMLSelectElement || target instanceof HTMLTextAreaElement) {
96
+ target.setCustomValidity('');
97
+ // Re-verify build-in validation.
98
+ if (!target.checkValidity())
99
+ return;
100
+ for (const validator of this.validators) {
101
+ if (target.matches(validator.selector)) {
102
+ console.log('element matches selector ', validator.selector, ' for ', target.matches(validator.selector) ? 'true' : 'false');
103
+ if (!target.checkValidity()) break;
104
+
105
+ // Show some loading indication if necessary.
106
+ this.workingClass.forEach(c => target.classList.add(c));
107
+
108
+ // Validate the element.
109
+ const valid = await validator.validate(target);
110
+
111
+ // Clear loading indication.
112
+ this.workingClass.forEach(c => target.classList.remove(c));
113
+
114
+ // Set custom validity based on the validation result.
115
+ target.setCustomValidity(valid ? '' : validator.message);
116
+ console.log('input validity is ', target.checkValidity(), ' message is ', target.validationMessage);
117
+ }
118
+ else
119
+ console.log('element does not match selector ', validator.selector, ' for ', target.matches(validator.selector) ? 'true' : 'false');
120
+ }
121
+ }
122
+ console.log('validateElement end ', target);
123
+ target?.dispatchEvent(new Event('qbo-input-validated', { bubbles: true }));
71
124
  }
72
125
 
73
126
  render() {