@vaadin/field-base 22.0.0-alpha1 → 22.0.0-alpha10
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/index.d.ts +10 -9
- package/index.js +10 -9
- package/package.json +25 -19
- package/src/aria-label-controller.d.ts +11 -0
- package/src/aria-label-controller.js +58 -0
- package/src/checked-mixin.d.ts +26 -0
- package/src/checked-mixin.js +54 -0
- package/src/delegate-focus-mixin.d.ts +7 -4
- package/src/delegate-focus-mixin.js +154 -72
- package/src/delegate-state-mixin.d.ts +23 -0
- package/src/delegate-state-mixin.js +125 -0
- package/src/field-mixin.d.ts +39 -0
- package/src/field-mixin.js +317 -0
- package/src/input-constraints-mixin.d.ts +28 -0
- package/src/input-constraints-mixin.js +126 -0
- package/src/input-control-mixin.d.ts +52 -0
- package/src/input-control-mixin.js +170 -0
- package/src/input-controller.d.ts +11 -0
- package/src/input-controller.js +35 -0
- package/src/input-field-mixin.d.ts +2 -22
- package/src/input-field-mixin.js +117 -168
- package/src/input-mixin.d.ts +22 -6
- package/src/input-mixin.js +161 -51
- package/src/label-mixin.d.ts +2 -2
- package/src/label-mixin.js +74 -60
- package/src/pattern-mixin.d.ts +32 -0
- package/src/pattern-mixin.js +72 -0
- package/src/shadow-focus-mixin.d.ts +21 -0
- package/src/shadow-focus-mixin.js +87 -0
- package/src/slot-controller.d.ts +8 -0
- package/src/slot-controller.js +36 -0
- package/src/slot-label-mixin.d.ts +20 -0
- package/src/slot-label-mixin.js +38 -0
- package/src/slot-styles-mixin.d.ts +24 -0
- package/src/slot-styles-mixin.js +76 -0
- package/src/slot-target-mixin.d.ts +32 -0
- package/src/slot-target-mixin.js +110 -0
- package/src/styles/clear-button-styles.d.ts +8 -0
- package/src/styles/clear-button-styles.js +21 -0
- package/src/styles/field-shared-styles.d.ts +8 -0
- package/src/styles/field-shared-styles.js +29 -0
- package/src/styles/input-field-container-styles.d.ts +8 -0
- package/src/styles/input-field-container-styles.js +16 -0
- package/src/styles/input-field-shared-styles.d.ts +8 -0
- package/src/styles/input-field-shared-styles.js +10 -0
- package/src/text-area-controller.d.ts +11 -0
- package/src/text-area-controller.js +38 -0
- package/src/validate-mixin.d.ts +1 -9
- package/src/validate-mixin.js +43 -118
- package/src/clear-button-mixin.d.ts +0 -32
- package/src/clear-button-mixin.js +0 -87
- package/src/disabled-mixin.d.ts +0 -23
- package/src/disabled-mixin.js +0 -48
- package/src/field-aria-mixin.d.ts +0 -24
- package/src/field-aria-mixin.js +0 -61
- package/src/focus-mixin.d.ts +0 -33
- package/src/focus-mixin.js +0 -104
- package/src/helper-text-mixin.d.ts +0 -24
- package/src/helper-text-mixin.js +0 -109
- package/src/input-aria-mixin.d.ts +0 -20
- package/src/input-aria-mixin.js +0 -69
- package/src/input-props-mixin.d.ts +0 -40
- package/src/input-props-mixin.js +0 -106
- package/src/slot-mixin.d.ts +0 -23
- package/src/slot-mixin.js +0 -55
- package/src/text-field-mixin.d.ts +0 -47
- package/src/text-field-mixin.js +0 -125
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2021 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A mixin to delegate properties and attributes to a target element.
|
|
10
|
+
*
|
|
11
|
+
* @polymerMixin
|
|
12
|
+
*/
|
|
13
|
+
export const DelegateStateMixin = dedupingMixin(
|
|
14
|
+
(superclass) =>
|
|
15
|
+
class DelegateStateMixinClass extends superclass {
|
|
16
|
+
static get properties() {
|
|
17
|
+
return {
|
|
18
|
+
/**
|
|
19
|
+
* A target element to which attributes and properties are delegated.
|
|
20
|
+
* @protected
|
|
21
|
+
*/
|
|
22
|
+
stateTarget: {
|
|
23
|
+
type: Object,
|
|
24
|
+
observer: '_stateTargetChanged'
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* An array of the host attributes to delegate to the target element.
|
|
31
|
+
*/
|
|
32
|
+
static get delegateAttrs() {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* An array of the host properties to delegate to the target element.
|
|
38
|
+
*/
|
|
39
|
+
static get delegateProps() {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** @protected */
|
|
44
|
+
ready() {
|
|
45
|
+
super.ready();
|
|
46
|
+
|
|
47
|
+
this._createDelegateAttrsObserver();
|
|
48
|
+
this._createDelegatePropsObserver();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** @protected */
|
|
52
|
+
_stateTargetChanged(target) {
|
|
53
|
+
if (target) {
|
|
54
|
+
this._ensureAttrsDelegated();
|
|
55
|
+
this._ensurePropsDelegated();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** @protected */
|
|
60
|
+
_createDelegateAttrsObserver() {
|
|
61
|
+
this._createMethodObserver(`_delegateAttrsChanged(${this.constructor.delegateAttrs.join(', ')})`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** @protected */
|
|
65
|
+
_createDelegatePropsObserver() {
|
|
66
|
+
this._createMethodObserver(`_delegatePropsChanged(${this.constructor.delegateProps.join(', ')})`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** @protected */
|
|
70
|
+
_ensureAttrsDelegated() {
|
|
71
|
+
this.constructor.delegateAttrs.forEach((name) => {
|
|
72
|
+
this._delegateAttribute(name, this[name]);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** @protected */
|
|
77
|
+
_ensurePropsDelegated() {
|
|
78
|
+
this.constructor.delegateProps.forEach((name) => {
|
|
79
|
+
this._delegateProperty(name, this[name]);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** @protected */
|
|
84
|
+
_delegateAttrsChanged(...values) {
|
|
85
|
+
this.constructor.delegateAttrs.forEach((name, index) => {
|
|
86
|
+
this._delegateAttribute(name, values[index]);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** @protected */
|
|
91
|
+
_delegatePropsChanged(...values) {
|
|
92
|
+
this.constructor.delegateProps.forEach((name, index) => {
|
|
93
|
+
this._delegateProperty(name, values[index]);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** @protected */
|
|
98
|
+
_delegateAttribute(name, value) {
|
|
99
|
+
if (!this.stateTarget) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (name === 'invalid') {
|
|
104
|
+
this._delegateAttribute('aria-invalid', value ? 'true' : false);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (typeof value === 'boolean') {
|
|
108
|
+
this.stateTarget.toggleAttribute(name, value);
|
|
109
|
+
} else if (value) {
|
|
110
|
+
this.stateTarget.setAttribute(name, value);
|
|
111
|
+
} else {
|
|
112
|
+
this.stateTarget.removeAttribute(name);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** @protected */
|
|
117
|
+
_delegateProperty(name, value) {
|
|
118
|
+
if (!this.stateTarget) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
this.stateTarget[name] = value;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2021 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { LabelMixin } from './label-mixin.js';
|
|
7
|
+
import { ValidateMixin } from './validate-mixin.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A mixin to provide common field logic: label, error message and helper text.
|
|
11
|
+
*/
|
|
12
|
+
declare function FieldMixin<T extends new (...args: any[]) => {}>(base: T): T & FieldMixinConstructor;
|
|
13
|
+
|
|
14
|
+
interface FieldMixinConstructor {
|
|
15
|
+
new (...args: any[]): FieldMixin;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface FieldMixin extends LabelMixin, ValidateMixin {
|
|
19
|
+
/**
|
|
20
|
+
* A target element to which ARIA attributes are set.
|
|
21
|
+
*/
|
|
22
|
+
ariaTarget: HTMLElement;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* String used for the helper text.
|
|
26
|
+
*
|
|
27
|
+
* @attr {string} helper-text
|
|
28
|
+
*/
|
|
29
|
+
helperText: string | null | undefined;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Error to show when the field is invalid.
|
|
33
|
+
*
|
|
34
|
+
* @attr {string} error-message
|
|
35
|
+
*/
|
|
36
|
+
errorMessage: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export { FieldMixin, FieldMixinConstructor };
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2021 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { FlattenedNodesObserver } from '@polymer/polymer/lib/utils/flattened-nodes-observer.js';
|
|
7
|
+
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
|
|
8
|
+
import { animationFrame } from '@vaadin/component-base/src/async.js';
|
|
9
|
+
import { LabelMixin } from './label-mixin.js';
|
|
10
|
+
import { ValidateMixin } from './validate-mixin.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A mixin to provide common field logic: label, error message and helper text.
|
|
14
|
+
*
|
|
15
|
+
* @polymerMixin
|
|
16
|
+
* @mixes LabelMixin
|
|
17
|
+
* @mixes ValidateMixin
|
|
18
|
+
*/
|
|
19
|
+
export const FieldMixin = (superclass) =>
|
|
20
|
+
class FieldMixinClass extends ValidateMixin(LabelMixin(superclass)) {
|
|
21
|
+
static get properties() {
|
|
22
|
+
return {
|
|
23
|
+
/**
|
|
24
|
+
* A target element to which ARIA attributes are set.
|
|
25
|
+
* @protected
|
|
26
|
+
*/
|
|
27
|
+
ariaTarget: {
|
|
28
|
+
type: Object,
|
|
29
|
+
observer: '_ariaTargetChanged'
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Error to show when the field is invalid.
|
|
34
|
+
*
|
|
35
|
+
* @attr {string} error-message
|
|
36
|
+
*/
|
|
37
|
+
errorMessage: {
|
|
38
|
+
type: String
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* String used for the helper text.
|
|
43
|
+
* @attr {string} helper-text
|
|
44
|
+
*/
|
|
45
|
+
helperText: {
|
|
46
|
+
type: String,
|
|
47
|
+
observer: '_helperTextChanged'
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
/** @protected */
|
|
51
|
+
_helperId: String
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** @protected */
|
|
56
|
+
get slots() {
|
|
57
|
+
return {
|
|
58
|
+
...super.slots,
|
|
59
|
+
'error-message': () => {
|
|
60
|
+
const error = document.createElement('div');
|
|
61
|
+
error.textContent = this.errorMessage;
|
|
62
|
+
error.setAttribute('aria-live', 'assertive');
|
|
63
|
+
return error;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static get observers() {
|
|
69
|
+
return [
|
|
70
|
+
'__ariaChanged(invalid, _helperId)',
|
|
71
|
+
'__observeOffsetHeight(errorMessage, invalid, label, helperText)',
|
|
72
|
+
'_updateErrorMessage(invalid, errorMessage)'
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** @protected */
|
|
77
|
+
get _errorNode() {
|
|
78
|
+
return this._getDirectSlotChild('error-message');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** @protected */
|
|
82
|
+
get _helperNode() {
|
|
83
|
+
return this._getDirectSlotChild('helper');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** @protected */
|
|
87
|
+
get _ariaAttr() {
|
|
88
|
+
return 'aria-describedby';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
constructor() {
|
|
92
|
+
super();
|
|
93
|
+
|
|
94
|
+
// Ensure every instance has unique ID
|
|
95
|
+
const uniqueId = (FieldMixinClass._uniqueFieldId = 1 + FieldMixinClass._uniqueFieldId || 0);
|
|
96
|
+
this._errorId = `error-${this.localName}-${uniqueId}`;
|
|
97
|
+
this._helperId = `helper-${this.localName}-${uniqueId}`;
|
|
98
|
+
|
|
99
|
+
// Save generated ID to restore later
|
|
100
|
+
this.__savedHelperId = this._helperId;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** @protected */
|
|
104
|
+
ready() {
|
|
105
|
+
super.ready();
|
|
106
|
+
|
|
107
|
+
const error = this._errorNode;
|
|
108
|
+
if (error) {
|
|
109
|
+
error.id = this._errorId;
|
|
110
|
+
|
|
111
|
+
this.__applyCustomError();
|
|
112
|
+
|
|
113
|
+
this._updateErrorMessage(this.invalid, this.errorMessage);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const helper = this._helperNode;
|
|
117
|
+
if (helper) {
|
|
118
|
+
this.__applyCustomHelper(helper);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
this.__helperSlot = this.shadowRoot.querySelector('[name="helper"]');
|
|
122
|
+
|
|
123
|
+
this.__helperSlotObserver = new FlattenedNodesObserver(this.__helperSlot, (info) => {
|
|
124
|
+
const helper = this._currentHelper;
|
|
125
|
+
|
|
126
|
+
const newHelper = info.addedNodes.find((node) => node !== helper);
|
|
127
|
+
const oldHelper = info.removedNodes.find((node) => node === helper);
|
|
128
|
+
|
|
129
|
+
if (newHelper) {
|
|
130
|
+
// Custom helper is added, remove the previous one.
|
|
131
|
+
if (helper && helper.isConnected) {
|
|
132
|
+
this.removeChild(helper);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
this.__applyCustomHelper(newHelper);
|
|
136
|
+
|
|
137
|
+
this.__helperIdObserver = new MutationObserver((mutations) => {
|
|
138
|
+
mutations.forEach((mutation) => {
|
|
139
|
+
// only handle helper nodes
|
|
140
|
+
if (
|
|
141
|
+
mutation.type === 'attributes' &&
|
|
142
|
+
mutation.attributeName === 'id' &&
|
|
143
|
+
mutation.target === this._currentHelper &&
|
|
144
|
+
mutation.target.id !== this.__savedHelperId
|
|
145
|
+
) {
|
|
146
|
+
this.__updateHelperId(mutation.target);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
this.__helperIdObserver.observe(newHelper, { attributes: true });
|
|
152
|
+
} else if (oldHelper) {
|
|
153
|
+
// The observer does not exist when default helper is removed.
|
|
154
|
+
if (this.__helperIdObserver) {
|
|
155
|
+
this.__helperIdObserver.disconnect();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
this.__applyDefaultHelper(this.helperText);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** @private */
|
|
164
|
+
__applyCustomError() {
|
|
165
|
+
const error = this.__errorMessage;
|
|
166
|
+
if (error && error !== this.errorMessage) {
|
|
167
|
+
this.errorMessage = error;
|
|
168
|
+
delete this.__errorMessage;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** @private */
|
|
173
|
+
__applyCustomHelper(helper) {
|
|
174
|
+
this.__updateHelperId(helper);
|
|
175
|
+
this._currentHelper = helper;
|
|
176
|
+
this.__toggleHasHelper(helper.children.length > 0 || this.__isNotEmpty(helper.textContent));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** @private */
|
|
180
|
+
__isNotEmpty(helperText) {
|
|
181
|
+
return helperText && helperText.trim() !== '';
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** @private */
|
|
185
|
+
__attachDefaultHelper() {
|
|
186
|
+
let helper = this.__defaultHelper;
|
|
187
|
+
|
|
188
|
+
if (!helper) {
|
|
189
|
+
helper = document.createElement('div');
|
|
190
|
+
helper.setAttribute('slot', 'helper');
|
|
191
|
+
this.__defaultHelper = helper;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
helper.id = this.__savedHelperId;
|
|
195
|
+
this.appendChild(helper);
|
|
196
|
+
this._currentHelper = helper;
|
|
197
|
+
|
|
198
|
+
return helper;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** @private */
|
|
202
|
+
__applyDefaultHelper(helperText) {
|
|
203
|
+
let helper = this._helperNode;
|
|
204
|
+
|
|
205
|
+
const hasHelperText = this.__isNotEmpty(helperText);
|
|
206
|
+
if (hasHelperText && !helper) {
|
|
207
|
+
// Create helper lazily
|
|
208
|
+
helper = this.__attachDefaultHelper();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Only set text content for default helper
|
|
212
|
+
if (helper && helper === this.__defaultHelper) {
|
|
213
|
+
helper.textContent = helperText;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
this.__toggleHasHelper(hasHelperText);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** @private */
|
|
220
|
+
__toggleHasHelper(hasHelper) {
|
|
221
|
+
this.toggleAttribute('has-helper', hasHelper);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Dispatch an event if a specific size measurement property has changed.
|
|
226
|
+
* Supporting multiple properties here is needed for `vaadin-text-area`.
|
|
227
|
+
* @protected
|
|
228
|
+
*/
|
|
229
|
+
_dispatchIronResizeEventIfNeeded(prop, value) {
|
|
230
|
+
const oldSize = '__old' + prop;
|
|
231
|
+
if (this[oldSize] !== undefined && this[oldSize] !== value) {
|
|
232
|
+
this.dispatchEvent(new CustomEvent('iron-resize', { bubbles: true, composed: true }));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
this[oldSize] = value;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** @private */
|
|
239
|
+
__observeOffsetHeight() {
|
|
240
|
+
this.__observeOffsetHeightDebouncer = Debouncer.debounce(
|
|
241
|
+
this.__observeOffsetHeightDebouncer,
|
|
242
|
+
animationFrame,
|
|
243
|
+
() => {
|
|
244
|
+
this._dispatchIronResizeEventIfNeeded('Height', this.offsetHeight);
|
|
245
|
+
}
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* @param {boolean} invalid
|
|
251
|
+
* @protected
|
|
252
|
+
*/
|
|
253
|
+
_updateErrorMessage(invalid, errorMessage) {
|
|
254
|
+
const error = this._errorNode;
|
|
255
|
+
if (!error) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// save the custom error message content
|
|
260
|
+
if (error.textContent && !errorMessage) {
|
|
261
|
+
this.__errorMessage = error.textContent.trim();
|
|
262
|
+
}
|
|
263
|
+
const hasError = Boolean(invalid && errorMessage);
|
|
264
|
+
error.textContent = hasError ? errorMessage : '';
|
|
265
|
+
this.toggleAttribute('has-error-message', hasError);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** @private */
|
|
269
|
+
__updateHelperId(customHelper) {
|
|
270
|
+
let newId;
|
|
271
|
+
|
|
272
|
+
if (customHelper.id) {
|
|
273
|
+
newId = customHelper.id;
|
|
274
|
+
} else {
|
|
275
|
+
newId = this.__savedHelperId;
|
|
276
|
+
customHelper.id = newId;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
this._helperId = newId;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/** @protected */
|
|
283
|
+
_helperTextChanged(helperText) {
|
|
284
|
+
this.__applyDefaultHelper(helperText);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/** @protected */
|
|
288
|
+
_ariaTargetChanged(target) {
|
|
289
|
+
if (target) {
|
|
290
|
+
this._updateAriaAttribute(this.invalid, this._helperId);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** @protected */
|
|
295
|
+
_updateAriaAttribute(invalid, helperId) {
|
|
296
|
+
const attr = this._ariaAttr;
|
|
297
|
+
|
|
298
|
+
if (this.ariaTarget && attr) {
|
|
299
|
+
// For groups, add all IDs to aria-labelledby rather than aria-describedby -
|
|
300
|
+
// that should guarantee that it's announced when the group is entered.
|
|
301
|
+
const ariaIds = attr === 'aria-describedby' ? [helperId] : [this._labelId, helperId];
|
|
302
|
+
|
|
303
|
+
// Error message ID needs to be dynamically added / removed based on the validity
|
|
304
|
+
// Otherwise assistive technologies would announce the error, even if we hide it.
|
|
305
|
+
if (invalid) {
|
|
306
|
+
ariaIds.push(this._errorId);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
this.ariaTarget.setAttribute(attr, ariaIds.join(' '));
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/** @private */
|
|
314
|
+
__ariaChanged(invalid, helperId) {
|
|
315
|
+
this._updateAriaAttribute(invalid, helperId);
|
|
316
|
+
}
|
|
317
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2021 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { DelegateStateMixin } from './delegate-state-mixin.js';
|
|
7
|
+
import { InputMixin } from './input-mixin.js';
|
|
8
|
+
import { ValidateMixin } from './validate-mixin.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A mixin to combine multiple input validation constraints.
|
|
12
|
+
*/
|
|
13
|
+
declare function InputConstraintsMixin<T extends new (...args: any[]) => {}>(
|
|
14
|
+
base: T
|
|
15
|
+
): T & InputConstraintsMixinConstructor;
|
|
16
|
+
|
|
17
|
+
interface InputConstraintsMixinConstructor {
|
|
18
|
+
new (...args: any[]): InputConstraintsMixin;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface InputConstraintsMixin extends DelegateStateMixin, InputMixin, ValidateMixin {
|
|
22
|
+
/**
|
|
23
|
+
* Returns true if the current input value satisfies all constraints (if any).
|
|
24
|
+
*/
|
|
25
|
+
checkValidity(): boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { InputConstraintsMixin, InputConstraintsMixinConstructor };
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2021 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
|
|
7
|
+
import { DelegateStateMixin } from './delegate-state-mixin.js';
|
|
8
|
+
import { InputMixin } from './input-mixin.js';
|
|
9
|
+
import { ValidateMixin } from './validate-mixin.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A mixin to combine multiple input validation constraints.
|
|
13
|
+
*
|
|
14
|
+
* @polymerMixin
|
|
15
|
+
* @mixes DelegateStateMixin
|
|
16
|
+
* @mixes InputMixin
|
|
17
|
+
* @mixes ValidateMixin
|
|
18
|
+
*/
|
|
19
|
+
export const InputConstraintsMixin = dedupingMixin(
|
|
20
|
+
(superclass) =>
|
|
21
|
+
class InputConstraintsMixinClass extends DelegateStateMixin(ValidateMixin(InputMixin(superclass))) {
|
|
22
|
+
/**
|
|
23
|
+
* An array of attributes which participate in the input validation.
|
|
24
|
+
* Changing these attributes will cause the input to re-validate.
|
|
25
|
+
*
|
|
26
|
+
* IMPORTANT: The attributes should be properly delegated to the input element
|
|
27
|
+
* from the host using `delegateAttrs` getter (see `DelegateStateMixin`).
|
|
28
|
+
* The `required` attribute is already delegated.
|
|
29
|
+
*/
|
|
30
|
+
static get constraints() {
|
|
31
|
+
return ['required'];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static get delegateAttrs() {
|
|
35
|
+
return [...super.delegateAttrs, 'required'];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** @protected */
|
|
39
|
+
ready() {
|
|
40
|
+
super.ready();
|
|
41
|
+
|
|
42
|
+
this._createConstraintsObserver();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Returns true if the current input value satisfies all constraints (if any).
|
|
47
|
+
* @return {boolean}
|
|
48
|
+
*/
|
|
49
|
+
checkValidity() {
|
|
50
|
+
if (this.inputElement && this._hasValidConstraints(this.constructor.constraints.map((c) => this[c]))) {
|
|
51
|
+
return this.inputElement.checkValidity();
|
|
52
|
+
} else {
|
|
53
|
+
return !this.invalid;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Returns true if some of the provided set of constraints are valid.
|
|
59
|
+
* @param {Array} constraints
|
|
60
|
+
* @return {boolean}
|
|
61
|
+
* @protected
|
|
62
|
+
*/
|
|
63
|
+
_hasValidConstraints(constraints) {
|
|
64
|
+
return constraints.some((c) => this.__isValidConstraint(c));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Override this method to customize setting up constraints observer.
|
|
69
|
+
* @protected
|
|
70
|
+
*/
|
|
71
|
+
_createConstraintsObserver() {
|
|
72
|
+
// This complex observer needs to be added dynamically instead of using `static get observers()`
|
|
73
|
+
// to make it possible to tweak this behavior in classes that apply this mixin.
|
|
74
|
+
this._createMethodObserver(`_constraintsChanged(${this.constructor.constraints.join(', ')})`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Override this method to implement custom validation constraints.
|
|
79
|
+
* @param {unknown[]} constraints
|
|
80
|
+
* @protected
|
|
81
|
+
*/
|
|
82
|
+
_constraintsChanged(...constraints) {
|
|
83
|
+
// Prevent marking field as invalid when setting required state
|
|
84
|
+
// or any other constraint before a user has entered the value.
|
|
85
|
+
if (!this.invalid) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (this._hasValidConstraints(constraints)) {
|
|
90
|
+
this.validate();
|
|
91
|
+
} else {
|
|
92
|
+
this.invalid = false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Override an event listener inherited from `InputMixin`
|
|
98
|
+
* to capture native `change` event and make sure that
|
|
99
|
+
* a new one is dispatched after validation runs.
|
|
100
|
+
* @param {Event} event
|
|
101
|
+
* @protected
|
|
102
|
+
* @override
|
|
103
|
+
*/
|
|
104
|
+
_onChange(event) {
|
|
105
|
+
event.stopPropagation();
|
|
106
|
+
|
|
107
|
+
this.validate();
|
|
108
|
+
|
|
109
|
+
this.dispatchEvent(
|
|
110
|
+
new CustomEvent('change', {
|
|
111
|
+
detail: {
|
|
112
|
+
sourceEvent: event
|
|
113
|
+
},
|
|
114
|
+
bubbles: event.bubbles,
|
|
115
|
+
cancelable: event.cancelable
|
|
116
|
+
})
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** @private */
|
|
121
|
+
__isValidConstraint(constraint) {
|
|
122
|
+
// 0 is valid for `minlength` and `maxlength`
|
|
123
|
+
return Boolean(constraint) || constraint === 0;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2021 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { KeyboardMixin } from '@vaadin/component-base/src/keyboard-mixin.js';
|
|
7
|
+
import { FieldMixin } from './field-mixin.js';
|
|
8
|
+
import { InputConstraintsMixin } from './input-constraints-mixin.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A mixin to provide shared logic for the editable form input controls.
|
|
12
|
+
*/
|
|
13
|
+
declare function InputControlMixin<T extends new (...args: any[]) => {}>(base: T): T & InputControlMixinConstructor;
|
|
14
|
+
|
|
15
|
+
interface InputControlMixinConstructor {
|
|
16
|
+
new (...args: any[]): InputControlMixin;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface InputControlMixin extends KeyboardMixin, InputConstraintsMixin, FieldMixin {
|
|
20
|
+
/**
|
|
21
|
+
* If true, the input text gets fully selected when the field is focused using click or touch / tap.
|
|
22
|
+
*/
|
|
23
|
+
autoselect: boolean;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Set to true to display the clear icon which clears the input.
|
|
27
|
+
* @attr {boolean} clear-button-visible
|
|
28
|
+
*/
|
|
29
|
+
clearButtonVisible: boolean;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The name of this field.
|
|
33
|
+
*/
|
|
34
|
+
name: string;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A hint to the user of what can be entered in the field.
|
|
38
|
+
*/
|
|
39
|
+
placeholder: string;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* When present, it specifies that the field is read-only.
|
|
43
|
+
*/
|
|
44
|
+
readonly: boolean;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The text usually displayed in a tooltip popup when the mouse is over the field.
|
|
48
|
+
*/
|
|
49
|
+
title: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export { InputControlMixin, InputControlMixinConstructor };
|