@vaadin/form-layout 22.0.1 → 23.0.0-alpha3
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 +8 -8
- package/src/vaadin-form-item.d.ts +13 -17
- package/src/vaadin-form-item.js +180 -27
- package/src/vaadin-form-layout.d.ts +11 -2
- package/src/vaadin-form-layout.js +39 -18
- package/theme/lumo/vaadin-form-item-styles.js +18 -0
- package/theme/material/vaadin-form-item-styles.js +9 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/form-layout",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "23.0.0-alpha3",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -32,18 +32,18 @@
|
|
|
32
32
|
"polymer"
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@polymer/iron-resizable-behavior": "^3.0.0",
|
|
36
35
|
"@polymer/polymer": "^3.0.0",
|
|
37
|
-
"@vaadin/component-base": "
|
|
38
|
-
"@vaadin/vaadin-lumo-styles": "
|
|
39
|
-
"@vaadin/vaadin-material-styles": "
|
|
40
|
-
"@vaadin/vaadin-themable-mixin": "
|
|
36
|
+
"@vaadin/component-base": "23.0.0-alpha3",
|
|
37
|
+
"@vaadin/vaadin-lumo-styles": "23.0.0-alpha3",
|
|
38
|
+
"@vaadin/vaadin-material-styles": "23.0.0-alpha3",
|
|
39
|
+
"@vaadin/vaadin-themable-mixin": "23.0.0-alpha3"
|
|
41
40
|
},
|
|
42
41
|
"devDependencies": {
|
|
43
42
|
"@esm-bundle/chai": "^4.3.4",
|
|
43
|
+
"@vaadin/custom-field": "23.0.0-alpha3",
|
|
44
44
|
"@vaadin/testing-helpers": "^0.3.2",
|
|
45
|
-
"@vaadin/text-field": "
|
|
45
|
+
"@vaadin/text-field": "23.0.0-alpha3",
|
|
46
46
|
"sinon": "^9.2.1"
|
|
47
47
|
},
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "490037919a9e054cc002c1b3be0c94a1603e1a44"
|
|
49
49
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @license
|
|
3
|
-
* Copyright (c)
|
|
3
|
+
* Copyright (c) 2017 - 2022 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
6
|
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
@@ -9,7 +9,7 @@ import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mix
|
|
|
9
9
|
* `<vaadin-form-item>` is a Web Component providing labelled form item wrapper
|
|
10
10
|
* for using inside `<vaadin-form-layout>`.
|
|
11
11
|
*
|
|
12
|
-
* `<vaadin-form-item>` accepts
|
|
12
|
+
* `<vaadin-form-item>` accepts a single child as the input content,
|
|
13
13
|
* and also has a separate named `label` slot:
|
|
14
14
|
*
|
|
15
15
|
* ```html
|
|
@@ -19,19 +19,6 @@ import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mix
|
|
|
19
19
|
* </vaadin-form-item>
|
|
20
20
|
* ```
|
|
21
21
|
*
|
|
22
|
-
* Any content can be used. For instance, you can have multiple input elements
|
|
23
|
-
* with surrounding text. The label can be an element of any type:
|
|
24
|
-
*
|
|
25
|
-
* ```html
|
|
26
|
-
* <vaadin-form-item>
|
|
27
|
-
* <span slot="label">Date of Birth</span>
|
|
28
|
-
* <input placeholder="YYYY" size="4"> -
|
|
29
|
-
* <input placeholder="MM" size="2"> -
|
|
30
|
-
* <input placeholder="DD" size="2"><br>
|
|
31
|
-
* <em>Example: 1900-01-01</em>
|
|
32
|
-
* </vaadin-form-item>
|
|
33
|
-
* ```
|
|
34
|
-
*
|
|
35
22
|
* The label is optional and can be omitted:
|
|
36
23
|
*
|
|
37
24
|
* ```html
|
|
@@ -58,7 +45,7 @@ import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mix
|
|
|
58
45
|
* ### Input Width
|
|
59
46
|
*
|
|
60
47
|
* By default, `<vaadin-form-item>` does not manipulate the width of the slotted
|
|
61
|
-
* input
|
|
48
|
+
* input element. Optionally you can stretch the child input element to fill
|
|
62
49
|
* the available width for the input content by adding the `full-width` class:
|
|
63
50
|
*
|
|
64
51
|
* ```html
|
|
@@ -97,7 +84,16 @@ import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mix
|
|
|
97
84
|
*
|
|
98
85
|
* See [Styling Components](https://vaadin.com/docs/latest/ds/customization/styling-components) documentation.
|
|
99
86
|
*/
|
|
100
|
-
declare class FormItem extends ThemableMixin(HTMLElement) {
|
|
87
|
+
declare class FormItem extends ThemableMixin(HTMLElement) {
|
|
88
|
+
/**
|
|
89
|
+
* Returns a target element to add ARIA attributes to for a field.
|
|
90
|
+
*
|
|
91
|
+
* - For Vaadin field components, the method returns an element
|
|
92
|
+
* obtained through the `ariaTarget` property defined in `FieldMixin`.
|
|
93
|
+
* - In other cases, the method returns the field element itself.
|
|
94
|
+
*/
|
|
95
|
+
protected _getFieldAriaTarget(field: HTMLElement): HTMLElement;
|
|
96
|
+
}
|
|
101
97
|
|
|
102
98
|
declare global {
|
|
103
99
|
interface HTMLElementTagNameMap {
|
package/src/vaadin-form-item.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @license
|
|
3
|
-
* Copyright (c)
|
|
3
|
+
* Copyright (c) 2017 - 2022 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
6
|
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
7
|
+
import { addValueToAttribute, removeValueFromAttribute } from '@vaadin/field-base/src/utils.js';
|
|
7
8
|
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* `<vaadin-form-item>` is a Web Component providing labelled form item wrapper
|
|
11
12
|
* for using inside `<vaadin-form-layout>`.
|
|
12
13
|
*
|
|
13
|
-
* `<vaadin-form-item>` accepts
|
|
14
|
+
* `<vaadin-form-item>` accepts a single child as the input content,
|
|
14
15
|
* and also has a separate named `label` slot:
|
|
15
16
|
*
|
|
16
17
|
* ```html
|
|
@@ -20,19 +21,6 @@ import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mix
|
|
|
20
21
|
* </vaadin-form-item>
|
|
21
22
|
* ```
|
|
22
23
|
*
|
|
23
|
-
* Any content can be used. For instance, you can have multiple input elements
|
|
24
|
-
* with surrounding text. The label can be an element of any type:
|
|
25
|
-
*
|
|
26
|
-
* ```html
|
|
27
|
-
* <vaadin-form-item>
|
|
28
|
-
* <span slot="label">Date of Birth</span>
|
|
29
|
-
* <input placeholder="YYYY" size="4"> -
|
|
30
|
-
* <input placeholder="MM" size="2"> -
|
|
31
|
-
* <input placeholder="DD" size="2"><br>
|
|
32
|
-
* <em>Example: 1900-01-01</em>
|
|
33
|
-
* </vaadin-form-item>
|
|
34
|
-
* ```
|
|
35
|
-
*
|
|
36
24
|
* The label is optional and can be omitted:
|
|
37
25
|
*
|
|
38
26
|
* ```html
|
|
@@ -59,7 +47,7 @@ import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mix
|
|
|
59
47
|
* ### Input Width
|
|
60
48
|
*
|
|
61
49
|
* By default, `<vaadin-form-item>` does not manipulate the width of the slotted
|
|
62
|
-
* input
|
|
50
|
+
* input element. Optionally you can stretch the child input element to fill
|
|
63
51
|
* the available width for the input content by adding the `full-width` class:
|
|
64
52
|
*
|
|
65
53
|
* ```html
|
|
@@ -145,12 +133,13 @@ class FormItem extends ThemableMixin(PolymerElement) {
|
|
|
145
133
|
min-width: 0;
|
|
146
134
|
}
|
|
147
135
|
</style>
|
|
148
|
-
<div id="label" part="label" on-click="
|
|
149
|
-
<slot name="label" id="labelSlot"></slot>
|
|
136
|
+
<div id="label" part="label" on-click="__onLabelClick">
|
|
137
|
+
<slot name="label" id="labelSlot" on-slotchange="__onLabelSlotChange"></slot>
|
|
138
|
+
<span part="required-indicator" aria-hidden="true"></span>
|
|
150
139
|
</div>
|
|
151
140
|
<div id="spacing"></div>
|
|
152
141
|
<div id="content">
|
|
153
|
-
<slot id="contentSlot"></slot>
|
|
142
|
+
<slot id="contentSlot" on-slotchange="__onContentSlotChange"></slot>
|
|
154
143
|
</div>
|
|
155
144
|
`;
|
|
156
145
|
}
|
|
@@ -159,17 +148,181 @@ class FormItem extends ThemableMixin(PolymerElement) {
|
|
|
159
148
|
return 'vaadin-form-item';
|
|
160
149
|
}
|
|
161
150
|
|
|
151
|
+
constructor() {
|
|
152
|
+
super();
|
|
153
|
+
this.__updateInvalidState = this.__updateInvalidState.bind(this);
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* An observer for a field node to reflect its `required` and `invalid` attributes to the component.
|
|
157
|
+
*
|
|
158
|
+
* @type {MutationObserver}
|
|
159
|
+
* @private
|
|
160
|
+
*/
|
|
161
|
+
this.__fieldNodeObserver = new MutationObserver(() => this.__updateRequiredState(this.__fieldNode.required));
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* The first label node in the label slot.
|
|
165
|
+
*
|
|
166
|
+
* @type {HTMLElement | null}
|
|
167
|
+
* @private
|
|
168
|
+
*/
|
|
169
|
+
this.__labelNode = null;
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* The first field node in the content slot.
|
|
173
|
+
*
|
|
174
|
+
* An element is considered a field when it has the `checkValidity` or `validate` method.
|
|
175
|
+
*
|
|
176
|
+
* @type {HTMLElement | null}
|
|
177
|
+
* @private
|
|
178
|
+
*/
|
|
179
|
+
this.__fieldNode = null;
|
|
180
|
+
|
|
181
|
+
// Ensure every instance has unique ID
|
|
182
|
+
const uniqueId = (FormItem._uniqueLabelId = 1 + FormItem._uniqueLabelId || 0);
|
|
183
|
+
this.__labelId = `label-${this.localName}-${uniqueId}`;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Returns a target element to add ARIA attributes to for a field.
|
|
188
|
+
*
|
|
189
|
+
* - For Vaadin field components, the method returns an element
|
|
190
|
+
* obtained through the `ariaTarget` property defined in `FieldMixin`.
|
|
191
|
+
* - In other cases, the method returns the field element itself.
|
|
192
|
+
*
|
|
193
|
+
* @param {HTMLElement} field
|
|
194
|
+
* @protected
|
|
195
|
+
*/
|
|
196
|
+
_getFieldAriaTarget(field) {
|
|
197
|
+
return field.ariaTarget || field;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Links the label to a field by adding the label id to
|
|
202
|
+
* the `aria-labelledby` attribute of the field's ARIA target element.
|
|
203
|
+
*
|
|
204
|
+
* @param {HTMLElement} field
|
|
205
|
+
* @private
|
|
206
|
+
*/
|
|
207
|
+
__linkLabelToField(field) {
|
|
208
|
+
addValueToAttribute(this._getFieldAriaTarget(field), 'aria-labelledby', this.__labelId);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Unlinks the label from a field by removing the label id from
|
|
213
|
+
* the `aria-labelledby` attribute of the field's ARIA target element.
|
|
214
|
+
*
|
|
215
|
+
* @param {HTMLElement} field
|
|
216
|
+
* @private
|
|
217
|
+
*/
|
|
218
|
+
__unlinkLabelFromField(field) {
|
|
219
|
+
removeValueFromAttribute(this._getFieldAriaTarget(field), 'aria-labelledby', this.__labelId);
|
|
220
|
+
}
|
|
221
|
+
|
|
162
222
|
/** @private */
|
|
163
|
-
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
(
|
|
167
|
-
|
|
168
|
-
if (firstContentElementChild) {
|
|
169
|
-
firstContentElementChild.focus();
|
|
170
|
-
firstContentElementChild.click();
|
|
223
|
+
__onLabelClick() {
|
|
224
|
+
const fieldNode = this.__fieldNode;
|
|
225
|
+
if (fieldNode) {
|
|
226
|
+
fieldNode.focus();
|
|
227
|
+
fieldNode.click();
|
|
171
228
|
}
|
|
172
229
|
}
|
|
230
|
+
|
|
231
|
+
/** @private */
|
|
232
|
+
__getValidateFunction(field) {
|
|
233
|
+
return field.validate || field.checkValidity;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* A `slotchange` event handler for the label slot.
|
|
238
|
+
*
|
|
239
|
+
* - Ensures the label id is only assigned to the first label node.
|
|
240
|
+
* - Ensures the label node is linked to the first field node via the `aria-labelledby` attribute
|
|
241
|
+
* if both nodes are provided, and unlinked otherwise.
|
|
242
|
+
*
|
|
243
|
+
* @private
|
|
244
|
+
*/
|
|
245
|
+
__onLabelSlotChange() {
|
|
246
|
+
if (this.__labelNode) {
|
|
247
|
+
this.__labelNode.id = '';
|
|
248
|
+
this.__labelNode = null;
|
|
249
|
+
|
|
250
|
+
if (this.__fieldNode) {
|
|
251
|
+
this.__unlinkLabelFromField(this.__fieldNode);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const newLabelNode = this.$.labelSlot.assignedElements()[0];
|
|
256
|
+
if (newLabelNode) {
|
|
257
|
+
this.__labelNode = newLabelNode;
|
|
258
|
+
this.__labelNode.id = this.__labelId;
|
|
259
|
+
|
|
260
|
+
if (this.__fieldNode) {
|
|
261
|
+
this.__linkLabelToField(this.__fieldNode);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* A `slotchange` event handler for the content slot.
|
|
268
|
+
*
|
|
269
|
+
* - Ensures the label node is only linked to the first field node via the `aria-labelledby` attribute.
|
|
270
|
+
* - Sets up an observer for the `required` attribute changes on the first field
|
|
271
|
+
* to reflect the attribute on the component. Ensures the observer is disconnected from the field
|
|
272
|
+
* as soon as it is removed or replaced by another one.
|
|
273
|
+
*
|
|
274
|
+
* @private
|
|
275
|
+
*/
|
|
276
|
+
__onContentSlotChange() {
|
|
277
|
+
if (this.__fieldNode) {
|
|
278
|
+
// Discard the old field
|
|
279
|
+
this.__unlinkLabelFromField(this.__fieldNode);
|
|
280
|
+
this.__updateRequiredState(false);
|
|
281
|
+
this.__fieldNodeObserver.disconnect();
|
|
282
|
+
this.__fieldNode = null;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const fieldNodes = this.$.contentSlot.assignedElements();
|
|
286
|
+
if (fieldNodes.length > 1) {
|
|
287
|
+
console.warn(
|
|
288
|
+
`WARNING: Since Vaadin 23, placing multiple fields directly to a <vaadin-form-item> is deprecated.
|
|
289
|
+
Please wrap fields with a <vaadin-custom-field> instead.`
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const newFieldNode = fieldNodes.find((field) => {
|
|
294
|
+
return !!this.__getValidateFunction(field);
|
|
295
|
+
});
|
|
296
|
+
if (newFieldNode) {
|
|
297
|
+
this.__fieldNode = newFieldNode;
|
|
298
|
+
this.__updateRequiredState(this.__fieldNode.required);
|
|
299
|
+
this.__fieldNodeObserver.observe(this.__fieldNode, { attributes: true, attributeFilter: ['required'] });
|
|
300
|
+
|
|
301
|
+
if (this.__labelNode) {
|
|
302
|
+
this.__linkLabelToField(this.__fieldNode);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/** @private */
|
|
308
|
+
__updateRequiredState(required) {
|
|
309
|
+
if (required) {
|
|
310
|
+
this.setAttribute('required', '');
|
|
311
|
+
this.__fieldNode.addEventListener('blur', this.__updateInvalidState);
|
|
312
|
+
this.__fieldNode.addEventListener('change', this.__updateInvalidState);
|
|
313
|
+
} else {
|
|
314
|
+
this.removeAttribute('invalid');
|
|
315
|
+
this.removeAttribute('required');
|
|
316
|
+
this.__fieldNode.removeEventListener('blur', this.__updateInvalidState);
|
|
317
|
+
this.__fieldNode.removeEventListener('change', this.__updateInvalidState);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/** @private */
|
|
322
|
+
__updateInvalidState() {
|
|
323
|
+
const isValid = this.__getValidateFunction(this.__fieldNode).call(this.__fieldNode);
|
|
324
|
+
this.toggleAttribute('invalid', isValid === false);
|
|
325
|
+
}
|
|
173
326
|
}
|
|
174
327
|
|
|
175
328
|
customElements.define(FormItem.is, FormItem);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @license
|
|
3
|
-
* Copyright (c)
|
|
3
|
+
* Copyright (c) 2017 - 2022 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
6
|
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
7
|
+
import { ResizeMixin } from '@vaadin/component-base/src/resize-mixin.js';
|
|
7
8
|
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
8
9
|
|
|
9
10
|
export type FormLayoutLabelsPosition = 'aside' | 'top';
|
|
@@ -104,7 +105,7 @@ export type FormLayoutResponsiveStep = {
|
|
|
104
105
|
* ---|---|---
|
|
105
106
|
* `--vaadin-form-layout-column-spacing` | Length of the spacing between columns | `2em`
|
|
106
107
|
*/
|
|
107
|
-
declare class FormLayout extends ElementMixin(ThemableMixin(HTMLElement)) {
|
|
108
|
+
declare class FormLayout extends ResizeMixin(ElementMixin(ThemableMixin(HTMLElement))) {
|
|
108
109
|
/**
|
|
109
110
|
* Allows specifying a responsive behavior with the number of columns
|
|
110
111
|
* and the label position depending on the layout width.
|
|
@@ -146,8 +147,16 @@ declare class FormLayout extends ElementMixin(ThemableMixin(HTMLElement)) {
|
|
|
146
147
|
|
|
147
148
|
/**
|
|
148
149
|
* Set custom CSS property values and update the layout.
|
|
150
|
+
*
|
|
151
|
+
* @deprecated Since Vaadin 23, `updateStyles()` is deprecated.
|
|
152
|
+
* Use the native element.style.setProperty API to set custom CSS property values.
|
|
149
153
|
*/
|
|
150
154
|
updateStyles(properties?: { [key: string]: string }): void;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Update the layout.
|
|
158
|
+
*/
|
|
159
|
+
protected _updateLayout(): void;
|
|
151
160
|
}
|
|
152
161
|
|
|
153
162
|
declare global {
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @license
|
|
3
|
-
* Copyright (c)
|
|
3
|
+
* Copyright (c) 2017 - 2022 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import { IronResizableBehavior } from '@polymer/iron-resizable-behavior/iron-resizable-behavior.js';
|
|
7
|
-
import { mixinBehaviors } from '@polymer/polymer/lib/legacy/class.js';
|
|
8
6
|
import { FlattenedNodesObserver } from '@polymer/polymer/lib/utils/flattened-nodes-observer.js';
|
|
9
|
-
import { beforeNextRender } from '@polymer/polymer/lib/utils/render-status.js';
|
|
10
7
|
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
11
8
|
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
9
|
+
import { ResizeMixin } from '@vaadin/component-base/src/resize-mixin.js';
|
|
12
10
|
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
13
11
|
|
|
14
12
|
/**
|
|
@@ -104,8 +102,9 @@ import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mix
|
|
|
104
102
|
* @extends HTMLElement
|
|
105
103
|
* @mixes ElementMixin
|
|
106
104
|
* @mixes ThemableMixin
|
|
105
|
+
* @mixes ResizeMixin
|
|
107
106
|
*/
|
|
108
|
-
class FormLayout extends ElementMixin(ThemableMixin(
|
|
107
|
+
class FormLayout extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement))) {
|
|
109
108
|
static get template() {
|
|
110
109
|
return html`
|
|
111
110
|
<style>
|
|
@@ -243,7 +242,7 @@ class FormLayout extends ElementMixin(ThemableMixin(mixinBehaviors([IronResizabl
|
|
|
243
242
|
}
|
|
244
243
|
|
|
245
244
|
static get observers() {
|
|
246
|
-
return ['
|
|
245
|
+
return ['_invokeUpdateLayout(_columnCount, _labelsOnTop)'];
|
|
247
246
|
}
|
|
248
247
|
|
|
249
248
|
/** @protected */
|
|
@@ -254,13 +253,12 @@ class FormLayout extends ElementMixin(ThemableMixin(mixinBehaviors([IronResizabl
|
|
|
254
253
|
// `super.ready()`, because `super.ready()` invokes property observers,
|
|
255
254
|
// and the observer for `responsiveSteps` does CSS value validation.
|
|
256
255
|
this._styleElement = document.createElement('style');
|
|
257
|
-
this.
|
|
256
|
+
this.appendChild(this._styleElement);
|
|
258
257
|
// Ensure there is a child text node in the style element
|
|
259
258
|
this._styleElement.textContent = ' ';
|
|
260
259
|
|
|
261
260
|
super.ready();
|
|
262
261
|
|
|
263
|
-
this.addEventListener('iron-resize', this._selectResponsiveStep);
|
|
264
262
|
this.addEventListener('animationend', this.__onAnimationEnd);
|
|
265
263
|
}
|
|
266
264
|
|
|
@@ -268,8 +266,8 @@ class FormLayout extends ElementMixin(ThemableMixin(mixinBehaviors([IronResizabl
|
|
|
268
266
|
connectedCallback() {
|
|
269
267
|
super.connectedCallback();
|
|
270
268
|
|
|
271
|
-
|
|
272
|
-
|
|
269
|
+
requestAnimationFrame(() => this._selectResponsiveStep());
|
|
270
|
+
requestAnimationFrame(() => this._updateLayout());
|
|
273
271
|
|
|
274
272
|
this._observeChildrenColspanChange();
|
|
275
273
|
}
|
|
@@ -293,7 +291,7 @@ class FormLayout extends ElementMixin(ThemableMixin(mixinBehaviors([IronResizabl
|
|
|
293
291
|
mutation.type === 'attributes' &&
|
|
294
292
|
(mutation.attributeName === 'colspan' || mutation.attributeName === 'hidden')
|
|
295
293
|
) {
|
|
296
|
-
this.
|
|
294
|
+
this._updateLayout();
|
|
297
295
|
}
|
|
298
296
|
});
|
|
299
297
|
});
|
|
@@ -307,7 +305,7 @@ class FormLayout extends ElementMixin(ThemableMixin(mixinBehaviors([IronResizabl
|
|
|
307
305
|
});
|
|
308
306
|
|
|
309
307
|
if (addedNodes.length > 0 || removedNodes.length > 0) {
|
|
310
|
-
this.
|
|
308
|
+
this._updateLayout();
|
|
311
309
|
}
|
|
312
310
|
});
|
|
313
311
|
}
|
|
@@ -433,18 +431,33 @@ class FormLayout extends ElementMixin(ThemableMixin(mixinBehaviors([IronResizabl
|
|
|
433
431
|
}
|
|
434
432
|
|
|
435
433
|
/** @private */
|
|
436
|
-
|
|
437
|
-
this.
|
|
434
|
+
_invokeUpdateLayout() {
|
|
435
|
+
this._updateLayout();
|
|
438
436
|
}
|
|
439
437
|
|
|
440
438
|
/**
|
|
441
439
|
* Set custom CSS property values and update the layout.
|
|
442
|
-
*
|
|
443
|
-
* @
|
|
440
|
+
*
|
|
441
|
+
* @deprecated Since Vaadin 23, `updateStyles()` is deprecated.
|
|
442
|
+
* Use the native element.style.setProperty API to set custom CSS property values.
|
|
444
443
|
*/
|
|
445
|
-
updateStyles(properties) {
|
|
446
|
-
|
|
444
|
+
updateStyles(properties = {}) {
|
|
445
|
+
console.warn(
|
|
446
|
+
`WARNING: Since Vaadin 23, updateStyles() is deprecated. Use the native element.style.setProperty API to set custom CSS property values.`
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
Object.entries(properties).forEach(([key, value]) => {
|
|
450
|
+
this.style.setProperty(key, value);
|
|
451
|
+
});
|
|
447
452
|
|
|
453
|
+
this._updateLayout();
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Update the layout.
|
|
458
|
+
* @protected
|
|
459
|
+
*/
|
|
460
|
+
_updateLayout() {
|
|
448
461
|
/*
|
|
449
462
|
The item width formula:
|
|
450
463
|
|
|
@@ -525,6 +538,14 @@ class FormLayout extends ElementMixin(ThemableMixin(mixinBehaviors([IronResizabl
|
|
|
525
538
|
}
|
|
526
539
|
});
|
|
527
540
|
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* @protected
|
|
544
|
+
* @override
|
|
545
|
+
*/
|
|
546
|
+
_onResize() {
|
|
547
|
+
this._selectResponsiveStep();
|
|
548
|
+
}
|
|
528
549
|
}
|
|
529
550
|
|
|
530
551
|
customElements.define(FormLayout.is, FormLayout);
|
|
@@ -23,6 +23,24 @@ registerStyles(
|
|
|
23
23
|
transition: color 0.4s;
|
|
24
24
|
line-height: 1.333;
|
|
25
25
|
}
|
|
26
|
+
|
|
27
|
+
[part='required-indicator']::after {
|
|
28
|
+
content: var(--lumo-required-field-indicator, '•');
|
|
29
|
+
transition: opacity 0.2s;
|
|
30
|
+
opacity: 0;
|
|
31
|
+
color: var(--lumo-required-field-indicator-color, var(--lumo-primary-text-color));
|
|
32
|
+
position: relative;
|
|
33
|
+
width: 1em;
|
|
34
|
+
text-align: center;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
:host([required]) [part='required-indicator']::after {
|
|
38
|
+
opacity: 1;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
:host([invalid]) [part='required-indicator']::after {
|
|
42
|
+
color: var(--lumo-required-field-indicator-color, var(--lumo-error-text-color));
|
|
43
|
+
}
|
|
26
44
|
`,
|
|
27
45
|
{ moduleId: 'lumo-form-item' }
|
|
28
46
|
);
|
|
@@ -14,6 +14,15 @@ registerStyles(
|
|
|
14
14
|
margin-top: 16px;
|
|
15
15
|
margin-bottom: 8px;
|
|
16
16
|
}
|
|
17
|
+
|
|
18
|
+
:host([required]) [part='required-indicator']::after {
|
|
19
|
+
content: ' *';
|
|
20
|
+
color: inherit;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
:host([invalid]) [part='label'] {
|
|
24
|
+
color: var(--material-error-text-color);
|
|
25
|
+
}
|
|
17
26
|
`,
|
|
18
27
|
{ moduleId: 'material-form-item' }
|
|
19
28
|
);
|