@vaadin/custom-field 24.2.0-alpha1 → 24.2.0-alpha11
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/package.json +22 -19
- package/src/vaadin-custom-field-mixin.d.ts +83 -0
- package/src/vaadin-custom-field-mixin.js +301 -0
- package/src/vaadin-custom-field-styles.d.ts +8 -0
- package/src/vaadin-custom-field-styles.js +34 -0
- package/src/vaadin-custom-field.d.ts +11 -60
- package/src/vaadin-custom-field.js +8 -324
- package/web-types.json +31 -5
- package/web-types.lit.json +18 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/custom-field",
|
|
3
|
-
"version": "24.2.0-
|
|
3
|
+
"version": "24.2.0-alpha11",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
"type": "module",
|
|
22
22
|
"files": [
|
|
23
23
|
"src",
|
|
24
|
+
"!src/vaadin-lit-custom-field.d.ts",
|
|
25
|
+
"!src/vaadin-lit-custom-field.js",
|
|
24
26
|
"theme",
|
|
25
27
|
"vaadin-*.d.ts",
|
|
26
28
|
"vaadin-*.js",
|
|
@@ -34,32 +36,33 @@
|
|
|
34
36
|
"web-component"
|
|
35
37
|
],
|
|
36
38
|
"dependencies": {
|
|
39
|
+
"@open-wc/dedupe-mixin": "^1.3.0",
|
|
37
40
|
"@polymer/polymer": "^3.0.0",
|
|
38
|
-
"@vaadin/a11y-base": "24.2.0-
|
|
39
|
-
"@vaadin/component-base": "24.2.0-
|
|
40
|
-
"@vaadin/field-base": "24.2.0-
|
|
41
|
-
"@vaadin/vaadin-lumo-styles": "24.2.0-
|
|
42
|
-
"@vaadin/vaadin-material-styles": "24.2.0-
|
|
43
|
-
"@vaadin/vaadin-themable-mixin": "24.2.0-
|
|
41
|
+
"@vaadin/a11y-base": "24.2.0-alpha11",
|
|
42
|
+
"@vaadin/component-base": "24.2.0-alpha11",
|
|
43
|
+
"@vaadin/field-base": "24.2.0-alpha11",
|
|
44
|
+
"@vaadin/vaadin-lumo-styles": "24.2.0-alpha11",
|
|
45
|
+
"@vaadin/vaadin-material-styles": "24.2.0-alpha11",
|
|
46
|
+
"@vaadin/vaadin-themable-mixin": "24.2.0-alpha11"
|
|
44
47
|
},
|
|
45
48
|
"devDependencies": {
|
|
46
49
|
"@esm-bundle/chai": "^4.3.4",
|
|
47
|
-
"@vaadin/combo-box": "24.2.0-
|
|
48
|
-
"@vaadin/date-picker": "24.2.0-
|
|
49
|
-
"@vaadin/email-field": "24.2.0-
|
|
50
|
-
"@vaadin/form-layout": "24.2.0-
|
|
51
|
-
"@vaadin/number-field": "24.2.0-
|
|
52
|
-
"@vaadin/password-field": "24.2.0-
|
|
53
|
-
"@vaadin/select": "24.2.0-
|
|
54
|
-
"@vaadin/testing-helpers": "^0.
|
|
55
|
-
"@vaadin/text-area": "24.2.0-
|
|
56
|
-
"@vaadin/text-field": "24.2.0-
|
|
57
|
-
"@vaadin/time-picker": "24.2.0-
|
|
50
|
+
"@vaadin/combo-box": "24.2.0-alpha11",
|
|
51
|
+
"@vaadin/date-picker": "24.2.0-alpha11",
|
|
52
|
+
"@vaadin/email-field": "24.2.0-alpha11",
|
|
53
|
+
"@vaadin/form-layout": "24.2.0-alpha11",
|
|
54
|
+
"@vaadin/number-field": "24.2.0-alpha11",
|
|
55
|
+
"@vaadin/password-field": "24.2.0-alpha11",
|
|
56
|
+
"@vaadin/select": "24.2.0-alpha11",
|
|
57
|
+
"@vaadin/testing-helpers": "^0.5.0",
|
|
58
|
+
"@vaadin/text-area": "24.2.0-alpha11",
|
|
59
|
+
"@vaadin/text-field": "24.2.0-alpha11",
|
|
60
|
+
"@vaadin/time-picker": "24.2.0-alpha11",
|
|
58
61
|
"sinon": "^13.0.2"
|
|
59
62
|
},
|
|
60
63
|
"web-types": [
|
|
61
64
|
"web-types.json",
|
|
62
65
|
"web-types.lit.json"
|
|
63
66
|
],
|
|
64
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "a958207d5f6a09ca0e2dcf9f62194b3f92c8766a"
|
|
65
68
|
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2019 - 2023 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import type { Constructor } from '@open-wc/dedupe-mixin';
|
|
7
|
+
import type { FocusMixinClass } from '@vaadin/a11y-base/src/focus-mixin.js';
|
|
8
|
+
import type { KeyboardMixinClass } from '@vaadin/a11y-base/src/keyboard-mixin.js';
|
|
9
|
+
import type { FieldMixinClass } from '@vaadin/field-base/src/field-mixin.js';
|
|
10
|
+
import type { LabelMixinClass } from '@vaadin/field-base/src/label-mixin.js';
|
|
11
|
+
import type { ValidateMixinClass } from '@vaadin/field-base/src/validate-mixin.js';
|
|
12
|
+
|
|
13
|
+
export type CustomFieldParseValueFn = (value: string) => unknown[];
|
|
14
|
+
|
|
15
|
+
export type CustomFieldFormatValueFn = (inputValues: unknown[]) => string;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A mixin providing common custom field functionality.
|
|
19
|
+
*/
|
|
20
|
+
export declare function CustomFieldMixin<T extends Constructor<HTMLElement>>(
|
|
21
|
+
base: T,
|
|
22
|
+
): Constructor<CustomFieldMixinClass> &
|
|
23
|
+
Constructor<FieldMixinClass> &
|
|
24
|
+
Constructor<FocusMixinClass> &
|
|
25
|
+
Constructor<KeyboardMixinClass> &
|
|
26
|
+
Constructor<LabelMixinClass> &
|
|
27
|
+
Constructor<ValidateMixinClass> &
|
|
28
|
+
T;
|
|
29
|
+
|
|
30
|
+
export declare class CustomFieldMixinClass {
|
|
31
|
+
/**
|
|
32
|
+
* Array of available input nodes
|
|
33
|
+
*/
|
|
34
|
+
readonly inputs: HTMLElement[] | undefined;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A function to format the values of the individual fields contained by
|
|
38
|
+
* the custom field into a single component value. The function receives
|
|
39
|
+
* an array of all values of the individual fields in the order of their
|
|
40
|
+
* presence in the DOM, and must return a single component value.
|
|
41
|
+
* This function is called each time a value of an internal field is
|
|
42
|
+
* changed.
|
|
43
|
+
*
|
|
44
|
+
* Example:
|
|
45
|
+
* ```js
|
|
46
|
+
* customField.formatValue = (fieldValues) => {
|
|
47
|
+
* return fieldValues.join("-");
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
formatValue: CustomFieldFormatValueFn | undefined;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* A function to parse the component value into values for the individual
|
|
55
|
+
* fields contained by the custom field. The function receives the
|
|
56
|
+
* component value, and must return an array of values for the individual
|
|
57
|
+
* fields in the order of their presence in the DOM.
|
|
58
|
+
* The function is called each time the value of the component changes.
|
|
59
|
+
*
|
|
60
|
+
* Example:
|
|
61
|
+
* ```js
|
|
62
|
+
* customField.parseValue = (componentValue) => {
|
|
63
|
+
* return componentValue.split("-");
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
parseValue: CustomFieldParseValueFn | undefined;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The name of the control, which is submitted with the form data.
|
|
71
|
+
*/
|
|
72
|
+
name: string | null | undefined;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The value of the field. When wrapping several inputs, it will contain `\t`
|
|
76
|
+
* (Tab character) as a delimiter indicating parts intended to be used as the
|
|
77
|
+
* corresponding inputs values.
|
|
78
|
+
* Use the [`formatValue`](#/elements/vaadin-custom-field#property-formatValue)
|
|
79
|
+
* and [`parseValue`](#/elements/vaadin-custom-field#property-parseValue)
|
|
80
|
+
* properties to customize this behavior.
|
|
81
|
+
*/
|
|
82
|
+
value: string | null | undefined;
|
|
83
|
+
}
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2019 - 2023 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { FocusMixin } from '@vaadin/a11y-base/src/focus-mixin.js';
|
|
7
|
+
import { KeyboardMixin } from '@vaadin/a11y-base/src/keyboard-mixin.js';
|
|
8
|
+
import { getFlattenedElements } from '@vaadin/component-base/src/dom-utils.js';
|
|
9
|
+
import { TooltipController } from '@vaadin/component-base/src/tooltip-controller.js';
|
|
10
|
+
import { FieldMixin } from '@vaadin/field-base/src/field-mixin.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Default implementation of the parse function that creates individual field
|
|
14
|
+
* values from the single component value.
|
|
15
|
+
* @param value
|
|
16
|
+
* @returns {*}
|
|
17
|
+
*/
|
|
18
|
+
const defaultParseValue = (value) => {
|
|
19
|
+
return value.split('\t');
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Default implementation of the format function that creates a single component
|
|
24
|
+
* value from individual field values.
|
|
25
|
+
* @param inputValues
|
|
26
|
+
* @returns {*}
|
|
27
|
+
*/
|
|
28
|
+
const defaultFormatValue = (inputValues) => {
|
|
29
|
+
return inputValues.join('\t');
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @polymerMixin
|
|
34
|
+
* @mixes FieldMixin
|
|
35
|
+
* @mixes FocusMixin
|
|
36
|
+
* @mixes KeyboardMixin
|
|
37
|
+
*/
|
|
38
|
+
export const CustomFieldMixin = (superClass) =>
|
|
39
|
+
class CustomFieldMixin extends FieldMixin(FocusMixin(KeyboardMixin(superClass))) {
|
|
40
|
+
static get properties() {
|
|
41
|
+
return {
|
|
42
|
+
/**
|
|
43
|
+
* The name of the control, which is submitted with the form data.
|
|
44
|
+
*/
|
|
45
|
+
name: String,
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The value of the field. When wrapping several inputs, it will contain `\t`
|
|
49
|
+
* (Tab character) as a delimiter indicating parts intended to be used as the
|
|
50
|
+
* corresponding inputs values.
|
|
51
|
+
* Use the [`formatValue`](#/elements/vaadin-custom-field#property-formatValue)
|
|
52
|
+
* and [`parseValue`](#/elements/vaadin-custom-field#property-parseValue)
|
|
53
|
+
* properties to customize this behavior.
|
|
54
|
+
*/
|
|
55
|
+
value: {
|
|
56
|
+
type: String,
|
|
57
|
+
observer: '__valueChanged',
|
|
58
|
+
notify: true,
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Array of available input nodes
|
|
63
|
+
* @type {!Array<!HTMLElement> | undefined}
|
|
64
|
+
*/
|
|
65
|
+
inputs: {
|
|
66
|
+
type: Array,
|
|
67
|
+
readOnly: true,
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* A function to format the values of the individual fields contained by
|
|
72
|
+
* the custom field into a single component value. The function receives
|
|
73
|
+
* an array of all values of the individual fields in the order of their
|
|
74
|
+
* presence in the DOM, and must return a single component value.
|
|
75
|
+
* This function is called each time a value of an internal field is
|
|
76
|
+
* changed.
|
|
77
|
+
*
|
|
78
|
+
* Example:
|
|
79
|
+
* ```js
|
|
80
|
+
* customField.formatValue = (fieldValues) => {
|
|
81
|
+
* return fieldValues.join("-");
|
|
82
|
+
* }
|
|
83
|
+
* ```
|
|
84
|
+
* @type {!CustomFieldFormatValueFn | undefined}
|
|
85
|
+
*/
|
|
86
|
+
formatValue: {
|
|
87
|
+
type: Function,
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* A function to parse the component value into values for the individual
|
|
92
|
+
* fields contained by the custom field. The function receives the
|
|
93
|
+
* component value, and must return an array of values for the individual
|
|
94
|
+
* fields in the order of their presence in the DOM.
|
|
95
|
+
* The function is called each time the value of the component changes.
|
|
96
|
+
*
|
|
97
|
+
* Example:
|
|
98
|
+
* ```js
|
|
99
|
+
* customField.parseValue = (componentValue) => {
|
|
100
|
+
* return componentValue.split("-");
|
|
101
|
+
* }
|
|
102
|
+
* ```
|
|
103
|
+
* @type {!CustomFieldParseValueFn | undefined}
|
|
104
|
+
*/
|
|
105
|
+
parseValue: {
|
|
106
|
+
type: Function,
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Whether the field is dirty.
|
|
111
|
+
*
|
|
112
|
+
* The field is automatically marked as dirty once the user triggers
|
|
113
|
+
* an `input` or `change` event. Additionally, the field can be manually
|
|
114
|
+
* marked as dirty by setting the property to `true`.
|
|
115
|
+
*/
|
|
116
|
+
dirty: {
|
|
117
|
+
type: Boolean,
|
|
118
|
+
value: false,
|
|
119
|
+
notify: true,
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** @protected */
|
|
125
|
+
ready() {
|
|
126
|
+
super.ready();
|
|
127
|
+
|
|
128
|
+
// See https://github.com/vaadin/vaadin-web-components/issues/94
|
|
129
|
+
this.setAttribute('role', 'group');
|
|
130
|
+
|
|
131
|
+
this.ariaTarget = this;
|
|
132
|
+
|
|
133
|
+
this.__childrenObserver = new MutationObserver(() => {
|
|
134
|
+
this.__setInputsFromSlot();
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
this.__setInputsFromSlot();
|
|
138
|
+
this.$.slot.addEventListener('slotchange', () => {
|
|
139
|
+
this.__setInputsFromSlot();
|
|
140
|
+
|
|
141
|
+
// Observe changes to any children except inputs
|
|
142
|
+
// to allow wrapping `<input>` with `<div>` etc.
|
|
143
|
+
getFlattenedElements(this.$.slot)
|
|
144
|
+
.filter((el) => !this.__isInput(el))
|
|
145
|
+
.forEach((el) => {
|
|
146
|
+
this.__childrenObserver.observe(el, { childList: true });
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
this._tooltipController = new TooltipController(this);
|
|
151
|
+
this.addController(this._tooltipController);
|
|
152
|
+
this._tooltipController.setShouldShow((target) => {
|
|
153
|
+
const inputs = target.inputs || [];
|
|
154
|
+
return !inputs.some((el) => el.opened);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** @protected */
|
|
159
|
+
focus() {
|
|
160
|
+
if (this.inputs && this.inputs[0]) {
|
|
161
|
+
this.inputs[0].focus();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Override method inherited from `FocusMixin` to validate on blur.
|
|
167
|
+
* @param {boolean} focused
|
|
168
|
+
* @protected
|
|
169
|
+
*/
|
|
170
|
+
_setFocused(focused) {
|
|
171
|
+
super._setFocused(focused);
|
|
172
|
+
|
|
173
|
+
if (!focused) {
|
|
174
|
+
this.validate();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Override method inherited from `FocusMixin` to not remove focused
|
|
180
|
+
* state when focus moves to another input in the custom field.
|
|
181
|
+
* @param {FocusEvent} event
|
|
182
|
+
* @return {boolean}
|
|
183
|
+
* @protected
|
|
184
|
+
*/
|
|
185
|
+
_shouldRemoveFocus(event) {
|
|
186
|
+
const { relatedTarget } = event;
|
|
187
|
+
return !this.inputs.some((el) => relatedTarget === (el.focusElement || el));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Returns true if the current inputs values satisfy all constraints (if any).
|
|
192
|
+
*
|
|
193
|
+
* @return {boolean}
|
|
194
|
+
*/
|
|
195
|
+
checkValidity() {
|
|
196
|
+
const invalidFields = this.inputs.filter((input) => !(input.validate || input.checkValidity).call(input));
|
|
197
|
+
|
|
198
|
+
if (invalidFields.length || (this.required && !this.value.trim())) {
|
|
199
|
+
// Either 1. one of the input fields is invalid or
|
|
200
|
+
// 2. the custom field itself is required but doesn't have a value
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* @param {KeyboardEvent} e
|
|
208
|
+
* @protected
|
|
209
|
+
* @override
|
|
210
|
+
*/
|
|
211
|
+
_onKeyDown(e) {
|
|
212
|
+
if (e.key === 'Tab') {
|
|
213
|
+
if (
|
|
214
|
+
(this.inputs.indexOf(e.target) < this.inputs.length - 1 && !e.shiftKey) ||
|
|
215
|
+
(this.inputs.indexOf(e.target) > 0 && e.shiftKey)
|
|
216
|
+
) {
|
|
217
|
+
this.dispatchEvent(new CustomEvent('internal-tab'));
|
|
218
|
+
} else {
|
|
219
|
+
// FIXME(yuriy): remove this workaround when value should not be updated before focusout
|
|
220
|
+
this.__setValue();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** @protected */
|
|
226
|
+
_onInput() {
|
|
227
|
+
this.dirty = true;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** @protected */
|
|
231
|
+
_onChange(event) {
|
|
232
|
+
// Stop native change events
|
|
233
|
+
event.stopPropagation();
|
|
234
|
+
this.dirty = true;
|
|
235
|
+
|
|
236
|
+
this.__setValue();
|
|
237
|
+
this.validate();
|
|
238
|
+
this.dispatchEvent(
|
|
239
|
+
new CustomEvent('change', {
|
|
240
|
+
bubbles: true,
|
|
241
|
+
cancelable: false,
|
|
242
|
+
detail: {
|
|
243
|
+
value: this.value,
|
|
244
|
+
},
|
|
245
|
+
}),
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** @private */
|
|
250
|
+
__setValue() {
|
|
251
|
+
this.__settingValue = true;
|
|
252
|
+
const formatFn = this.formatValue || defaultFormatValue;
|
|
253
|
+
this.value = formatFn.apply(this, [this.inputs.map((input) => input.value)]);
|
|
254
|
+
this.__settingValue = false;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/** @private */
|
|
258
|
+
__isInput(node) {
|
|
259
|
+
const isSlottedInput = node.getAttribute('slot') === 'input' || node.getAttribute('slot') === 'textarea';
|
|
260
|
+
return !isSlottedInput && (node.validate || node.checkValidity);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/** @private */
|
|
264
|
+
__getInputsFromSlot() {
|
|
265
|
+
return getFlattenedElements(this.$.slot).filter((node) => this.__isInput(node));
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** @private */
|
|
269
|
+
__setInputsFromSlot() {
|
|
270
|
+
this._setInputs(this.__getInputsFromSlot());
|
|
271
|
+
this.__setValue();
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** @private */
|
|
275
|
+
__toggleHasValue(value) {
|
|
276
|
+
this.toggleAttribute('has-value', value !== null && value.trim() !== '');
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/** @private */
|
|
280
|
+
__valueChanged(value, oldValue) {
|
|
281
|
+
if (this.__settingValue || !this.inputs) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
this.__toggleHasValue(value);
|
|
286
|
+
|
|
287
|
+
const parseFn = this.parseValue || defaultParseValue;
|
|
288
|
+
const valuesArray = parseFn.apply(this, [value]);
|
|
289
|
+
if (!valuesArray || valuesArray.length === 0) {
|
|
290
|
+
console.warn('Value parser has not provided values array');
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
this.inputs.forEach((input, id) => {
|
|
295
|
+
input.value = valuesArray[id];
|
|
296
|
+
});
|
|
297
|
+
if (oldValue !== undefined) {
|
|
298
|
+
this.validate();
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2019 - 2023 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { css } from 'lit';
|
|
7
|
+
|
|
8
|
+
export const customFieldStyles = css`
|
|
9
|
+
:host {
|
|
10
|
+
display: inline-flex;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
:host::before {
|
|
14
|
+
content: '\\2003';
|
|
15
|
+
width: 0;
|
|
16
|
+
display: inline-block;
|
|
17
|
+
/* Size and position this element on the same vertical position as the input-field element
|
|
18
|
+
to make vertical align for the host element work as expected */
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
:host([hidden]) {
|
|
22
|
+
display: none !important;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.vaadin-custom-field-container {
|
|
26
|
+
width: 100%;
|
|
27
|
+
display: flex;
|
|
28
|
+
flex-direction: column;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.inputs-wrapper {
|
|
32
|
+
flex: none;
|
|
33
|
+
}
|
|
34
|
+
`;
|
|
@@ -3,15 +3,11 @@
|
|
|
3
3
|
* Copyright (c) 2019 - 2023 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import { FocusMixin } from '@vaadin/a11y-base/src/focus-mixin.js';
|
|
7
|
-
import { KeyboardMixin } from '@vaadin/a11y-base/src/keyboard-mixin.js';
|
|
8
6
|
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
9
|
-
import { FieldMixin } from '@vaadin/field-base/src/field-mixin.js';
|
|
10
7
|
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
8
|
+
import { CustomFieldMixin } from './vaadin-custom-field-mixin.js';
|
|
11
9
|
|
|
12
|
-
export
|
|
13
|
-
|
|
14
|
-
export type CustomFieldFormatValueFn = (inputValues: unknown[]) => string;
|
|
10
|
+
export { CustomFieldFormatValueFn, CustomFieldParseValueFn } from './vaadin-custom-field-mixin.js';
|
|
15
11
|
|
|
16
12
|
/**
|
|
17
13
|
* Fired when the user commits a value change.
|
|
@@ -20,6 +16,11 @@ export type CustomFieldChangeEvent = Event & {
|
|
|
20
16
|
target: CustomField;
|
|
21
17
|
};
|
|
22
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Fired when the `dirty` property changes.
|
|
21
|
+
*/
|
|
22
|
+
export type CustomFieldDirtyChangedEvent = CustomEvent<{ value: boolean }>;
|
|
23
|
+
|
|
23
24
|
/**
|
|
24
25
|
* Fired when the `invalid` property changes.
|
|
25
26
|
*/
|
|
@@ -43,6 +44,8 @@ export type CustomFieldInternalTabEvent = Event & {
|
|
|
43
44
|
};
|
|
44
45
|
|
|
45
46
|
export interface CustomFieldCustomEventMap {
|
|
47
|
+
'dirty-changed': CustomFieldDirtyChangedEvent;
|
|
48
|
+
|
|
46
49
|
'invalid-changed': CustomFieldInvalidChangedEvent;
|
|
47
50
|
|
|
48
51
|
'value-changed': CustomFieldValueChangedEvent;
|
|
@@ -95,64 +98,12 @@ export interface CustomFieldEventMap extends HTMLElementEventMap, CustomFieldCus
|
|
|
95
98
|
*
|
|
96
99
|
* @fires {Event} change - Fired when the user commits a value change for any of the internal inputs.
|
|
97
100
|
* @fires {Event} internal-tab - Fired on Tab keydown triggered from the internal inputs, meaning focus will not leave the inputs.
|
|
101
|
+
* @fires {CustomEvent} dirty-changed - Fired when the `dirty` property changes.
|
|
98
102
|
* @fires {CustomEvent} invalid-changed - Fired when the `invalid` property changes.
|
|
99
103
|
* @fires {CustomEvent} value-changed - Fired when the `value` property changes.
|
|
100
104
|
* @fires {CustomEvent} validated - Fired whenever the field is validated.
|
|
101
105
|
*/
|
|
102
|
-
declare class CustomField extends
|
|
103
|
-
/**
|
|
104
|
-
* Array of available input nodes
|
|
105
|
-
*/
|
|
106
|
-
readonly inputs: HTMLElement[] | undefined;
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* A function to format the values of the individual fields contained by
|
|
110
|
-
* the custom field into a single component value. The function receives
|
|
111
|
-
* an array of all values of the individual fields in the order of their
|
|
112
|
-
* presence in the DOM, and must return a single component value.
|
|
113
|
-
* This function is called each time a value of an internal field is
|
|
114
|
-
* changed.
|
|
115
|
-
*
|
|
116
|
-
* Example:
|
|
117
|
-
* ```js
|
|
118
|
-
* customField.formatValue = (fieldValues) => {
|
|
119
|
-
* return fieldValues.join("-");
|
|
120
|
-
* }
|
|
121
|
-
* ```
|
|
122
|
-
*/
|
|
123
|
-
formatValue: CustomFieldFormatValueFn | undefined;
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* A function to parse the component value into values for the individual
|
|
127
|
-
* fields contained by the custom field. The function receives the
|
|
128
|
-
* component value, and must return an array of values for the individual
|
|
129
|
-
* fields in the order of their presence in the DOM.
|
|
130
|
-
* The function is called each time the value of the component changes.
|
|
131
|
-
*
|
|
132
|
-
* Example:
|
|
133
|
-
* ```js
|
|
134
|
-
* customField.parseValue = (componentValue) => {
|
|
135
|
-
* return componentValue.split("-");
|
|
136
|
-
* }
|
|
137
|
-
* ```
|
|
138
|
-
*/
|
|
139
|
-
parseValue: CustomFieldParseValueFn | undefined;
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* The name of the control, which is submitted with the form data.
|
|
143
|
-
*/
|
|
144
|
-
name: string | null | undefined;
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* The value of the field. When wrapping several inputs, it will contain `\t`
|
|
148
|
-
* (Tab character) as a delimiter indicating parts intended to be used as the
|
|
149
|
-
* corresponding inputs values.
|
|
150
|
-
* Use the [`formatValue`](#/elements/vaadin-custom-field#property-formatValue)
|
|
151
|
-
* and [`parseValue`](#/elements/vaadin-custom-field#property-parseValue)
|
|
152
|
-
* properties to customize this behavior.
|
|
153
|
-
*/
|
|
154
|
-
value: string | null | undefined;
|
|
155
|
-
|
|
106
|
+
declare class CustomField extends CustomFieldMixin(ThemableMixin(ElementMixin(HTMLElement))) {
|
|
156
107
|
addEventListener<K extends keyof CustomFieldEventMap>(
|
|
157
108
|
type: K,
|
|
158
109
|
listener: (this: CustomField, ev: CustomFieldEventMap[K]) => void,
|
|
@@ -3,34 +3,13 @@
|
|
|
3
3
|
* Copyright (c) 2019 - 2023 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import { FlattenedNodesObserver } from '@polymer/polymer/lib/utils/flattened-nodes-observer.js';
|
|
7
6
|
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
8
|
-
import { FocusMixin } from '@vaadin/a11y-base/src/focus-mixin.js';
|
|
9
|
-
import { KeyboardMixin } from '@vaadin/a11y-base/src/keyboard-mixin.js';
|
|
10
7
|
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
8
|
+
import { registerStyles, ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
9
|
+
import { CustomFieldMixin } from './vaadin-custom-field-mixin.js';
|
|
10
|
+
import { customFieldStyles } from './vaadin-custom-field-styles.js';
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
* Default implementation of the parse function that creates individual field
|
|
17
|
-
* values from the single component value.
|
|
18
|
-
* @param value
|
|
19
|
-
* @returns {*}
|
|
20
|
-
*/
|
|
21
|
-
const defaultParseValue = (value) => {
|
|
22
|
-
return value.split('\t');
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Default implementation of the format function that creates a single component
|
|
27
|
-
* value from individual field values.
|
|
28
|
-
* @param inputValues
|
|
29
|
-
* @returns {*}
|
|
30
|
-
*/
|
|
31
|
-
const defaultFormatValue = (inputValues) => {
|
|
32
|
-
return inputValues.join('\t');
|
|
33
|
-
};
|
|
12
|
+
registerStyles('vaadin-custom-field', customFieldStyles, { moduleId: 'vaadin-custom-field-styles' });
|
|
34
13
|
|
|
35
14
|
/**
|
|
36
15
|
* `<vaadin-custom-field>` is a web component for wrapping multiple components as a single field.
|
|
@@ -71,59 +50,30 @@ const defaultFormatValue = (inputValues) => {
|
|
|
71
50
|
*
|
|
72
51
|
* @fires {Event} change - Fired when the user commits a value change for any of the internal inputs.
|
|
73
52
|
* @fires {Event} internal-tab - Fired on Tab keydown triggered from the internal inputs, meaning focus will not leave the inputs.
|
|
53
|
+
* @fires {CustomEvent} dirty-changed - Fired when the `dirty` property changes.
|
|
74
54
|
* @fires {CustomEvent} invalid-changed - Fired when the `invalid` property changes.
|
|
75
55
|
* @fires {CustomEvent} value-changed - Fired when the `value` property changes.
|
|
76
56
|
* @fires {CustomEvent} validated - Fired whenever the field is validated.
|
|
77
57
|
*
|
|
78
58
|
* @extends HTMLElement
|
|
79
|
-
* @mixes
|
|
80
|
-
* @mixes FocusMixin
|
|
59
|
+
* @mixes CustomFieldMixin
|
|
81
60
|
* @mixes ElementMixin
|
|
82
|
-
* @mixes KeyboardMixin
|
|
83
61
|
* @mixes ThemableMixin
|
|
84
62
|
*/
|
|
85
|
-
class CustomField extends
|
|
63
|
+
class CustomField extends CustomFieldMixin(ThemableMixin(ElementMixin(PolymerElement))) {
|
|
86
64
|
static get is() {
|
|
87
65
|
return 'vaadin-custom-field';
|
|
88
66
|
}
|
|
89
67
|
|
|
90
68
|
static get template() {
|
|
91
69
|
return html`
|
|
92
|
-
<style>
|
|
93
|
-
:host {
|
|
94
|
-
display: inline-flex;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
:host::before {
|
|
98
|
-
content: '\\2003';
|
|
99
|
-
width: 0;
|
|
100
|
-
display: inline-block;
|
|
101
|
-
/* Size and position this element on the same vertical position as the input-field element
|
|
102
|
-
to make vertical align for the host element work as expected */
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
:host([hidden]) {
|
|
106
|
-
display: none !important;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
.vaadin-custom-field-container {
|
|
110
|
-
width: 100%;
|
|
111
|
-
display: flex;
|
|
112
|
-
flex-direction: column;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
.inputs-wrapper {
|
|
116
|
-
flex: none;
|
|
117
|
-
}
|
|
118
|
-
</style>
|
|
119
|
-
|
|
120
70
|
<div class="vaadin-custom-field-container">
|
|
121
71
|
<div part="label" on-click="focus">
|
|
122
72
|
<slot name="label"></slot>
|
|
123
73
|
<span part="required-indicator" aria-hidden="true"></span>
|
|
124
74
|
</div>
|
|
125
75
|
|
|
126
|
-
<div class="inputs-wrapper" on-change="
|
|
76
|
+
<div class="inputs-wrapper" on-change="_onChange" on-input="_onInput">
|
|
127
77
|
<slot id="slot"></slot>
|
|
128
78
|
</div>
|
|
129
79
|
|
|
@@ -140,272 +90,6 @@ class CustomField extends FieldMixin(FocusMixin(KeyboardMixin(ThemableMixin(Elem
|
|
|
140
90
|
`;
|
|
141
91
|
}
|
|
142
92
|
|
|
143
|
-
static get properties() {
|
|
144
|
-
return {
|
|
145
|
-
/**
|
|
146
|
-
* The name of the control, which is submitted with the form data.
|
|
147
|
-
*/
|
|
148
|
-
name: String,
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* The value of the field. When wrapping several inputs, it will contain `\t`
|
|
152
|
-
* (Tab character) as a delimiter indicating parts intended to be used as the
|
|
153
|
-
* corresponding inputs values.
|
|
154
|
-
* Use the [`formatValue`](#/elements/vaadin-custom-field#property-formatValue)
|
|
155
|
-
* and [`parseValue`](#/elements/vaadin-custom-field#property-parseValue)
|
|
156
|
-
* properties to customize this behavior.
|
|
157
|
-
*/
|
|
158
|
-
value: {
|
|
159
|
-
type: String,
|
|
160
|
-
observer: '__valueChanged',
|
|
161
|
-
notify: true,
|
|
162
|
-
},
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Array of available input nodes
|
|
166
|
-
* @type {!Array<!HTMLElement> | undefined}
|
|
167
|
-
*/
|
|
168
|
-
inputs: {
|
|
169
|
-
type: Array,
|
|
170
|
-
readOnly: true,
|
|
171
|
-
},
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* A function to format the values of the individual fields contained by
|
|
175
|
-
* the custom field into a single component value. The function receives
|
|
176
|
-
* an array of all values of the individual fields in the order of their
|
|
177
|
-
* presence in the DOM, and must return a single component value.
|
|
178
|
-
* This function is called each time a value of an internal field is
|
|
179
|
-
* changed.
|
|
180
|
-
*
|
|
181
|
-
* Example:
|
|
182
|
-
* ```js
|
|
183
|
-
* customField.formatValue = (fieldValues) => {
|
|
184
|
-
* return fieldValues.join("-");
|
|
185
|
-
* }
|
|
186
|
-
* ```
|
|
187
|
-
* @type {!CustomFieldFormatValueFn | undefined}
|
|
188
|
-
*/
|
|
189
|
-
formatValue: {
|
|
190
|
-
type: Function,
|
|
191
|
-
},
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* A function to parse the component value into values for the individual
|
|
195
|
-
* fields contained by the custom field. The function receives the
|
|
196
|
-
* component value, and must return an array of values for the individual
|
|
197
|
-
* fields in the order of their presence in the DOM.
|
|
198
|
-
* The function is called each time the value of the component changes.
|
|
199
|
-
*
|
|
200
|
-
* Example:
|
|
201
|
-
* ```js
|
|
202
|
-
* customField.parseValue = (componentValue) => {
|
|
203
|
-
* return componentValue.split("-");
|
|
204
|
-
* }
|
|
205
|
-
* ```
|
|
206
|
-
* @type {!CustomFieldParseValueFn | undefined}
|
|
207
|
-
*/
|
|
208
|
-
parseValue: {
|
|
209
|
-
type: Function,
|
|
210
|
-
},
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/** @protected */
|
|
215
|
-
connectedCallback() {
|
|
216
|
-
super.connectedCallback();
|
|
217
|
-
|
|
218
|
-
if (this.__observer) {
|
|
219
|
-
this.__observer.connect();
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/** @protected */
|
|
224
|
-
disconnectedCallback() {
|
|
225
|
-
super.disconnectedCallback();
|
|
226
|
-
|
|
227
|
-
if (this.__observer) {
|
|
228
|
-
this.__observer.disconnect();
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/** @protected */
|
|
233
|
-
ready() {
|
|
234
|
-
super.ready();
|
|
235
|
-
|
|
236
|
-
// See https://github.com/vaadin/vaadin-web-components/issues/94
|
|
237
|
-
this.setAttribute('role', 'group');
|
|
238
|
-
|
|
239
|
-
this.ariaTarget = this;
|
|
240
|
-
|
|
241
|
-
this.__setInputsFromSlot();
|
|
242
|
-
this.__observer = new FlattenedNodesObserver(this.$.slot, () => {
|
|
243
|
-
this.__setInputsFromSlot();
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
this._tooltipController = new TooltipController(this);
|
|
247
|
-
this.addController(this._tooltipController);
|
|
248
|
-
this._tooltipController.setShouldShow((target) => {
|
|
249
|
-
const inputs = target.inputs || [];
|
|
250
|
-
return !inputs.some((el) => el.opened);
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/** @protected */
|
|
255
|
-
focus() {
|
|
256
|
-
if (this.inputs && this.inputs[0]) {
|
|
257
|
-
this.inputs[0].focus();
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Override method inherited from `FocusMixin` to validate on blur.
|
|
263
|
-
* @param {boolean} focused
|
|
264
|
-
* @protected
|
|
265
|
-
*/
|
|
266
|
-
_setFocused(focused) {
|
|
267
|
-
super._setFocused(focused);
|
|
268
|
-
|
|
269
|
-
if (!focused) {
|
|
270
|
-
this.validate();
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Override method inherited from `FocusMixin` to not remove focused
|
|
276
|
-
* state when focus moves to another input in the custom field.
|
|
277
|
-
* @param {FocusEvent} event
|
|
278
|
-
* @return {boolean}
|
|
279
|
-
* @protected
|
|
280
|
-
*/
|
|
281
|
-
_shouldRemoveFocus(event) {
|
|
282
|
-
const { relatedTarget } = event;
|
|
283
|
-
return !this.inputs.some((el) => relatedTarget === (el.focusElement || el));
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Returns true if the current inputs values satisfy all constraints (if any).
|
|
288
|
-
*
|
|
289
|
-
* @return {boolean}
|
|
290
|
-
*/
|
|
291
|
-
checkValidity() {
|
|
292
|
-
const invalidFields = this.inputs.filter((input) => !(input.validate || input.checkValidity).call(input));
|
|
293
|
-
|
|
294
|
-
if (invalidFields.length || (this.required && !this.value.trim())) {
|
|
295
|
-
// Either 1. one of the input fields is invalid or
|
|
296
|
-
// 2. the custom field itself is required but doesn't have a value
|
|
297
|
-
return false;
|
|
298
|
-
}
|
|
299
|
-
return true;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* @param {KeyboardEvent} e
|
|
304
|
-
* @protected
|
|
305
|
-
* @override
|
|
306
|
-
*/
|
|
307
|
-
_onKeyDown(e) {
|
|
308
|
-
if (e.key === 'Tab') {
|
|
309
|
-
if (
|
|
310
|
-
(this.inputs.indexOf(e.target) < this.inputs.length - 1 && !e.shiftKey) ||
|
|
311
|
-
(this.inputs.indexOf(e.target) > 0 && e.shiftKey)
|
|
312
|
-
) {
|
|
313
|
-
this.dispatchEvent(new CustomEvent('internal-tab'));
|
|
314
|
-
} else {
|
|
315
|
-
// FIXME(yuriy): remove this workaround when value should not be updated before focusout
|
|
316
|
-
this.__setValue();
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/** @private */
|
|
322
|
-
__onInputChange(event) {
|
|
323
|
-
// Stop native change events
|
|
324
|
-
event.stopPropagation();
|
|
325
|
-
|
|
326
|
-
this.__setValue();
|
|
327
|
-
this.validate();
|
|
328
|
-
this.dispatchEvent(
|
|
329
|
-
new CustomEvent('change', {
|
|
330
|
-
bubbles: true,
|
|
331
|
-
cancelable: false,
|
|
332
|
-
detail: {
|
|
333
|
-
value: this.value,
|
|
334
|
-
},
|
|
335
|
-
}),
|
|
336
|
-
);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
/** @private */
|
|
340
|
-
__setValue() {
|
|
341
|
-
this.__settingValue = true;
|
|
342
|
-
const formatFn = this.formatValue || defaultFormatValue;
|
|
343
|
-
this.value = formatFn.apply(this, [this.inputs.map((input) => input.value)]);
|
|
344
|
-
this.__settingValue = false;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* Like querySelectorAll('*') but also gets all elements through any nested slots recursively
|
|
349
|
-
* @private
|
|
350
|
-
*/
|
|
351
|
-
__queryAllAssignedElements(elem) {
|
|
352
|
-
const result = [];
|
|
353
|
-
let elements;
|
|
354
|
-
if (elem.tagName === 'SLOT') {
|
|
355
|
-
elements = elem.assignedElements({ flatten: true });
|
|
356
|
-
} else {
|
|
357
|
-
result.push(elem);
|
|
358
|
-
elements = Array.from(elem.children);
|
|
359
|
-
}
|
|
360
|
-
elements.forEach((elem) => result.push(...this.__queryAllAssignedElements(elem)));
|
|
361
|
-
return result;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
/** @private */
|
|
365
|
-
__isInput(node) {
|
|
366
|
-
const isSlottedInput = node.getAttribute('slot') === 'input' || node.getAttribute('slot') === 'textarea';
|
|
367
|
-
return !isSlottedInput && (node.validate || node.checkValidity);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
/** @private */
|
|
371
|
-
__getInputsFromSlot() {
|
|
372
|
-
return this.__queryAllAssignedElements(this.$.slot).filter((node) => this.__isInput(node));
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/** @private */
|
|
376
|
-
__setInputsFromSlot() {
|
|
377
|
-
this._setInputs(this.__getInputsFromSlot());
|
|
378
|
-
this.__setValue();
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/** @private */
|
|
382
|
-
__toggleHasValue(value) {
|
|
383
|
-
this.toggleAttribute('has-value', value !== null && value.trim() !== '');
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
/** @private */
|
|
387
|
-
__valueChanged(value, oldValue) {
|
|
388
|
-
if (this.__settingValue || !this.inputs) {
|
|
389
|
-
return;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
this.__toggleHasValue(value);
|
|
393
|
-
|
|
394
|
-
const parseFn = this.parseValue || defaultParseValue;
|
|
395
|
-
const valuesArray = parseFn.apply(this, [value]);
|
|
396
|
-
if (!valuesArray || valuesArray.length === 0) {
|
|
397
|
-
console.warn('Value parser has not provided values array');
|
|
398
|
-
return;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
this.inputs.forEach((input, id) => {
|
|
402
|
-
input.value = valuesArray[id];
|
|
403
|
-
});
|
|
404
|
-
if (oldValue !== undefined) {
|
|
405
|
-
this.validate();
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
|
|
409
93
|
/**
|
|
410
94
|
* Fired when the user commits a value change for any of the internal inputs.
|
|
411
95
|
*
|
package/web-types.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/web-types",
|
|
3
3
|
"name": "@vaadin/custom-field",
|
|
4
|
-
"version": "24.2.0-
|
|
4
|
+
"version": "24.2.0-alpha11",
|
|
5
5
|
"description-markup": "markdown",
|
|
6
6
|
"contributions": {
|
|
7
7
|
"html": {
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
},
|
|
101
101
|
{
|
|
102
102
|
"name": "value",
|
|
103
|
-
"description": "The value of the field. When wrapping several inputs, it will contain `\\t`\n(Tab character) as a delimiter indicating parts intended to be used as the\ncorresponding inputs values.\nUse the [`formatValue`](https://cdn.vaadin.com/vaadin-web-components/24.2.0-
|
|
103
|
+
"description": "The value of the field. When wrapping several inputs, it will contain `\\t`\n(Tab character) as a delimiter indicating parts intended to be used as the\ncorresponding inputs values.\nUse the [`formatValue`](https://cdn.vaadin.com/vaadin-web-components/24.2.0-alpha11/#/elements/vaadin-custom-field#property-formatValue)\nand [`parseValue`](https://cdn.vaadin.com/vaadin-web-components/24.2.0-alpha11/#/elements/vaadin-custom-field#property-parseValue)\nproperties to customize this behavior.",
|
|
104
104
|
"value": {
|
|
105
105
|
"type": [
|
|
106
106
|
"string",
|
|
@@ -109,6 +109,17 @@
|
|
|
109
109
|
]
|
|
110
110
|
}
|
|
111
111
|
},
|
|
112
|
+
{
|
|
113
|
+
"name": "dirty",
|
|
114
|
+
"description": "Whether the field is dirty.\n\nThe field is automatically marked as dirty once the user triggers\nan `input` or `change` event. Additionally, the field can be manually\nmarked as dirty by setting the property to `true`.",
|
|
115
|
+
"value": {
|
|
116
|
+
"type": [
|
|
117
|
+
"boolean",
|
|
118
|
+
"null",
|
|
119
|
+
"undefined"
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
},
|
|
112
123
|
{
|
|
113
124
|
"name": "theme",
|
|
114
125
|
"description": "The theme variants to apply to the component.",
|
|
@@ -213,7 +224,7 @@
|
|
|
213
224
|
},
|
|
214
225
|
{
|
|
215
226
|
"name": "value",
|
|
216
|
-
"description": "The value of the field. When wrapping several inputs, it will contain `\\t`\n(Tab character) as a delimiter indicating parts intended to be used as the\ncorresponding inputs values.\nUse the [`formatValue`](https://cdn.vaadin.com/vaadin-web-components/24.2.0-
|
|
227
|
+
"description": "The value of the field. When wrapping several inputs, it will contain `\\t`\n(Tab character) as a delimiter indicating parts intended to be used as the\ncorresponding inputs values.\nUse the [`formatValue`](https://cdn.vaadin.com/vaadin-web-components/24.2.0-alpha11/#/elements/vaadin-custom-field#property-formatValue)\nand [`parseValue`](https://cdn.vaadin.com/vaadin-web-components/24.2.0-alpha11/#/elements/vaadin-custom-field#property-parseValue)\nproperties to customize this behavior.",
|
|
217
228
|
"value": {
|
|
218
229
|
"type": [
|
|
219
230
|
"string",
|
|
@@ -241,6 +252,17 @@
|
|
|
241
252
|
"undefined"
|
|
242
253
|
]
|
|
243
254
|
}
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
"name": "dirty",
|
|
258
|
+
"description": "Whether the field is dirty.\n\nThe field is automatically marked as dirty once the user triggers\nan `input` or `change` event. Additionally, the field can be manually\nmarked as dirty by setting the property to `true`.",
|
|
259
|
+
"value": {
|
|
260
|
+
"type": [
|
|
261
|
+
"boolean",
|
|
262
|
+
"null",
|
|
263
|
+
"undefined"
|
|
264
|
+
]
|
|
265
|
+
}
|
|
244
266
|
}
|
|
245
267
|
],
|
|
246
268
|
"events": [
|
|
@@ -252,13 +274,17 @@
|
|
|
252
274
|
"name": "change",
|
|
253
275
|
"description": "Fired when the user commits a value change for any of the internal inputs."
|
|
254
276
|
},
|
|
277
|
+
{
|
|
278
|
+
"name": "invalid-changed",
|
|
279
|
+
"description": "Fired when the `invalid` property changes."
|
|
280
|
+
},
|
|
255
281
|
{
|
|
256
282
|
"name": "value-changed",
|
|
257
283
|
"description": "Fired when the `value` property changes."
|
|
258
284
|
},
|
|
259
285
|
{
|
|
260
|
-
"name": "
|
|
261
|
-
"description": "Fired when the `
|
|
286
|
+
"name": "dirty-changed",
|
|
287
|
+
"description": "Fired when the `dirty` property changes."
|
|
262
288
|
}
|
|
263
289
|
]
|
|
264
290
|
}
|
package/web-types.lit.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/web-types",
|
|
3
3
|
"name": "@vaadin/custom-field",
|
|
4
|
-
"version": "24.2.0-
|
|
4
|
+
"version": "24.2.0-alpha11",
|
|
5
5
|
"description-markup": "markdown",
|
|
6
6
|
"framework": "lit",
|
|
7
7
|
"framework-config": {
|
|
@@ -33,6 +33,13 @@
|
|
|
33
33
|
"kind": "expression"
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
|
+
{
|
|
37
|
+
"name": "?dirty",
|
|
38
|
+
"description": "Whether the field is dirty.\n\nThe field is automatically marked as dirty once the user triggers\nan `input` or `change` event. Additionally, the field can be manually\nmarked as dirty by setting the property to `true`.",
|
|
39
|
+
"value": {
|
|
40
|
+
"kind": "expression"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
36
43
|
{
|
|
37
44
|
"name": ".label",
|
|
38
45
|
"description": "The label text for the input node.\nWhen no light dom defined via [slot=label], this value will be used.",
|
|
@@ -77,7 +84,7 @@
|
|
|
77
84
|
},
|
|
78
85
|
{
|
|
79
86
|
"name": ".value",
|
|
80
|
-
"description": "The value of the field. When wrapping several inputs, it will contain `\\t`\n(Tab character) as a delimiter indicating parts intended to be used as the\ncorresponding inputs values.\nUse the [`formatValue`](https://cdn.vaadin.com/vaadin-web-components/24.2.0-
|
|
87
|
+
"description": "The value of the field. When wrapping several inputs, it will contain `\\t`\n(Tab character) as a delimiter indicating parts intended to be used as the\ncorresponding inputs values.\nUse the [`formatValue`](https://cdn.vaadin.com/vaadin-web-components/24.2.0-alpha11/#/elements/vaadin-custom-field#property-formatValue)\nand [`parseValue`](https://cdn.vaadin.com/vaadin-web-components/24.2.0-alpha11/#/elements/vaadin-custom-field#property-parseValue)\nproperties to customize this behavior.",
|
|
81
88
|
"value": {
|
|
82
89
|
"kind": "expression"
|
|
83
90
|
}
|
|
@@ -110,6 +117,13 @@
|
|
|
110
117
|
"kind": "expression"
|
|
111
118
|
}
|
|
112
119
|
},
|
|
120
|
+
{
|
|
121
|
+
"name": "@invalid-changed",
|
|
122
|
+
"description": "Fired when the `invalid` property changes.",
|
|
123
|
+
"value": {
|
|
124
|
+
"kind": "expression"
|
|
125
|
+
}
|
|
126
|
+
},
|
|
113
127
|
{
|
|
114
128
|
"name": "@value-changed",
|
|
115
129
|
"description": "Fired when the `value` property changes.",
|
|
@@ -118,8 +132,8 @@
|
|
|
118
132
|
}
|
|
119
133
|
},
|
|
120
134
|
{
|
|
121
|
-
"name": "@
|
|
122
|
-
"description": "Fired when the `
|
|
135
|
+
"name": "@dirty-changed",
|
|
136
|
+
"description": "Fired when the `dirty` property changes.",
|
|
123
137
|
"value": {
|
|
124
138
|
"kind": "expression"
|
|
125
139
|
}
|