-
Notifications
You must be signed in to change notification settings - Fork 83
/
input-constraints-mixin.js
125 lines (112 loc) · 3.8 KB
/
input-constraints-mixin.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/**
* @license
* Copyright (c) 2021 - 2022 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
import { DelegateStateMixin } from './delegate-state-mixin.js';
import { InputMixin } from './input-mixin.js';
import { ValidateMixin } from './validate-mixin.js';
/**
* A mixin to combine multiple input validation constraints.
*
* @polymerMixin
* @mixes DelegateStateMixin
* @mixes InputMixin
* @mixes ValidateMixin
*/
export const InputConstraintsMixin = dedupingMixin(
(superclass) =>
class InputConstraintsMixinClass extends DelegateStateMixin(ValidateMixin(InputMixin(superclass))) {
/**
* An array of attributes which participate in the input validation.
* Changing these attributes will cause the input to re-validate.
*
* IMPORTANT: The attributes should be properly delegated to the input element
* from the host using `delegateAttrs` getter (see `DelegateStateMixin`).
* The `required` attribute is already delegated.
*/
static get constraints() {
return ['required'];
}
static get delegateAttrs() {
return [...super.delegateAttrs, 'required'];
}
/** @protected */
ready() {
super.ready();
this._createConstraintsObserver();
}
/**
* Returns true if the current input value satisfies all constraints (if any).
* @return {boolean}
*/
checkValidity() {
if (this.inputElement && this._hasValidConstraints(this.constructor.constraints.map((c) => this[c]))) {
return this.inputElement.checkValidity();
}
return !this.invalid;
}
/**
* Returns true if some of the provided set of constraints are valid.
* @param {Array} constraints
* @return {boolean}
* @protected
*/
_hasValidConstraints(constraints) {
return constraints.some((c) => this.__isValidConstraint(c));
}
/**
* Override this method to customize setting up constraints observer.
* @protected
*/
_createConstraintsObserver() {
// This complex observer needs to be added dynamically instead of using `static get observers()`
// to make it possible to tweak this behavior in classes that apply this mixin.
this._createMethodObserver(`_constraintsChanged(${this.constructor.constraints.join(', ')})`);
}
/**
* Override this method to implement custom validation constraints.
* @param {unknown[]} constraints
* @protected
*/
_constraintsChanged(...constraints) {
// Prevent marking field as invalid when setting required state
// or any other constraint before a user has entered the value.
if (!this.invalid) {
return;
}
if (this._hasValidConstraints(constraints)) {
this.validate();
} else {
this._setInvalid(false);
}
}
/**
* Override an event listener inherited from `InputMixin`
* to capture native `change` event and make sure that
* a new one is dispatched after validation runs.
* @param {Event} event
* @protected
* @override
*/
_onChange(event) {
event.stopPropagation();
this.validate();
this.dispatchEvent(
new CustomEvent('change', {
detail: {
sourceEvent: event,
},
bubbles: event.bubbles,
cancelable: event.cancelable,
}),
);
}
/** @private */
__isValidConstraint(constraint) {
// 0 is valid for `minlength` and `maxlength`
return Boolean(constraint) || constraint === 0;
}
},
);