@vaadin/field-base 23.0.0-alpha1 → 23.0.0-alpha5
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/LICENSE +1 -1
- package/package.json +4 -3
- package/src/checked-mixin.d.ts +1 -1
- package/src/checked-mixin.js +1 -1
- package/src/delegate-focus-mixin.d.ts +1 -1
- package/src/delegate-focus-mixin.js +1 -1
- package/src/delegate-state-mixin.d.ts +1 -1
- package/src/delegate-state-mixin.js +1 -1
- package/src/field-aria-controller.d.ts +1 -1
- package/src/field-aria-controller.js +5 -8
- package/src/field-mixin.d.ts +3 -1
- package/src/field-mixin.js +49 -192
- package/src/helper-controller.d.ts +23 -0
- package/src/helper-controller.js +185 -0
- package/src/input-constraints-mixin.d.ts +1 -1
- package/src/input-constraints-mixin.js +2 -3
- package/src/input-control-mixin.d.ts +1 -1
- package/src/input-control-mixin.js +1 -1
- package/src/input-controller.d.ts +3 -3
- package/src/input-controller.js +5 -4
- package/src/input-field-mixin.d.ts +1 -1
- package/src/input-field-mixin.js +1 -1
- package/src/input-mixin.d.ts +1 -1
- package/src/input-mixin.js +1 -1
- package/src/label-controller.d.ts +26 -0
- package/src/label-controller.js +186 -0
- package/src/label-mixin.d.ts +4 -3
- package/src/label-mixin.js +10 -49
- package/src/labelled-input-controller.d.ts +1 -1
- package/src/labelled-input-controller.js +17 -4
- package/src/pattern-mixin.d.ts +1 -1
- package/src/pattern-mixin.js +1 -2
- package/src/shadow-focus-mixin.d.ts +1 -1
- package/src/shadow-focus-mixin.js +1 -1
- package/src/slot-label-mixin.d.ts +1 -1
- package/src/slot-label-mixin.js +1 -14
- package/src/slot-styles-mixin.d.ts +1 -1
- package/src/slot-styles-mixin.js +1 -1
- package/src/slot-target-mixin.d.ts +1 -1
- package/src/slot-target-mixin.js +1 -1
- package/src/styles/clear-button-styles.d.ts +1 -1
- package/src/styles/clear-button-styles.js +1 -1
- package/src/styles/field-shared-styles.d.ts +1 -1
- package/src/styles/field-shared-styles.js +1 -1
- package/src/styles/input-field-container-styles.d.ts +1 -1
- package/src/styles/input-field-container-styles.js +1 -1
- package/src/styles/input-field-shared-styles.d.ts +1 -1
- package/src/styles/input-field-shared-styles.js +1 -1
- package/src/text-area-controller.d.ts +3 -3
- package/src/text-area-controller.js +5 -4
- package/src/utils.d.ts +16 -0
- package/src/utils.js +56 -0
- package/src/validate-mixin.d.ts +1 -1
- package/src/validate-mixin.js +1 -1
- package/src/virtual-keyboard-controller.d.ts +14 -0
- package/src/virtual-keyboard-controller.js +38 -0
- package/src/slot-controller.d.ts +0 -8
- package/src/slot-controller.js +0 -36
|
@@ -0,0 +1,185 @@
|
|
|
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 { SlotController } from '@vaadin/component-base/src/slot-controller.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A controller that manages the helper node content.
|
|
10
|
+
*/
|
|
11
|
+
export class HelperController extends SlotController {
|
|
12
|
+
constructor(host) {
|
|
13
|
+
// Do not provide slot factory, as only create helper lazily.
|
|
14
|
+
super(host, 'helper');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
get helperId() {
|
|
18
|
+
return this.node && this.node.id;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Override to initialize the newly added custom helper.
|
|
23
|
+
*
|
|
24
|
+
* @param {Node} helperNode
|
|
25
|
+
* @protected
|
|
26
|
+
* @override
|
|
27
|
+
*/
|
|
28
|
+
initCustomNode(helperNode) {
|
|
29
|
+
this.__updateHelperId(helperNode);
|
|
30
|
+
|
|
31
|
+
this.__observeHelper(helperNode);
|
|
32
|
+
|
|
33
|
+
const hasHelper = this.__hasHelper(helperNode);
|
|
34
|
+
this.__toggleHasHelper(hasHelper);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Override to cleanup helper node when it's removed.
|
|
39
|
+
*
|
|
40
|
+
* @param {Node} _node
|
|
41
|
+
* @protected
|
|
42
|
+
* @override
|
|
43
|
+
*/
|
|
44
|
+
teardownNode(_node) {
|
|
45
|
+
// The observer does not exist when the default helper is removed.
|
|
46
|
+
if (this.__helperIdObserver) {
|
|
47
|
+
this.__helperIdObserver.disconnect();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const helperNode = this.getSlotChild();
|
|
51
|
+
|
|
52
|
+
// Custom node is added to helper slot
|
|
53
|
+
if (helperNode && helperNode !== this.defaultNode) {
|
|
54
|
+
const hasHelper = this.__hasHelper(helperNode);
|
|
55
|
+
this.__toggleHasHelper(hasHelper);
|
|
56
|
+
} else {
|
|
57
|
+
// Restore default helper if needed
|
|
58
|
+
this.__applyDefaultHelper(this.helperText, helperNode);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Set helper text based on corresponding host property.
|
|
64
|
+
* @param {string} helperText
|
|
65
|
+
*/
|
|
66
|
+
setHelperText(helperText) {
|
|
67
|
+
this.helperText = helperText;
|
|
68
|
+
|
|
69
|
+
const helperNode = this.getSlotChild();
|
|
70
|
+
if (!helperNode || helperNode === this.defaultNode) {
|
|
71
|
+
this.__applyDefaultHelper(helperText, helperNode);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @param {HTMLElement} helperNode
|
|
77
|
+
* @return {boolean}
|
|
78
|
+
* @private
|
|
79
|
+
*/
|
|
80
|
+
__hasHelper(helperNode) {
|
|
81
|
+
if (!helperNode) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return helperNode.children.length > 0 || this.__isNotEmpty(helperNode.textContent);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @param {string} helperText
|
|
90
|
+
* @private
|
|
91
|
+
*/
|
|
92
|
+
__isNotEmpty(helperText) {
|
|
93
|
+
return helperText && helperText.trim() !== '';
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @param {string} helperText
|
|
98
|
+
* @param {Node} helperNode
|
|
99
|
+
* @private
|
|
100
|
+
*/
|
|
101
|
+
__applyDefaultHelper(helperText, helperNode) {
|
|
102
|
+
const hasHelperText = this.__isNotEmpty(helperText);
|
|
103
|
+
|
|
104
|
+
if (hasHelperText && !helperNode) {
|
|
105
|
+
// Set slot factory lazily to only create helper node when needed.
|
|
106
|
+
this.slotFactory = () => document.createElement('div');
|
|
107
|
+
|
|
108
|
+
helperNode = this.attachDefaultNode();
|
|
109
|
+
|
|
110
|
+
this.__updateHelperId(helperNode);
|
|
111
|
+
this.__observeHelper(helperNode);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (helperNode) {
|
|
115
|
+
helperNode.textContent = helperText;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
this.__toggleHasHelper(hasHelperText);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* @param {HTMLElement} helperNode
|
|
123
|
+
* @private
|
|
124
|
+
*/
|
|
125
|
+
__observeHelper(helperNode) {
|
|
126
|
+
this.__helperObserver = new MutationObserver((mutations) => {
|
|
127
|
+
mutations.forEach((mutation) => {
|
|
128
|
+
const target = mutation.target;
|
|
129
|
+
|
|
130
|
+
// Ensure the mutation target is the currently connected helper
|
|
131
|
+
// to ignore async mutations dispatched for removed element.
|
|
132
|
+
const isHelperMutation = target === this.node;
|
|
133
|
+
|
|
134
|
+
if (mutation.type === 'attributes') {
|
|
135
|
+
// We use attributeFilter to only observe ID mutation,
|
|
136
|
+
// no need to check for attribute name separately.
|
|
137
|
+
if (isHelperMutation && target.id !== this.defaultId) {
|
|
138
|
+
this.__updateHelperId(target);
|
|
139
|
+
}
|
|
140
|
+
} else if (isHelperMutation || target.parentElement === this.node) {
|
|
141
|
+
// Update has-helper when textContent changes
|
|
142
|
+
const hasHelper = this.__hasHelper(this.node);
|
|
143
|
+
this.__toggleHasHelper(hasHelper);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Observe changes to helper ID attribute, text content and children.
|
|
149
|
+
this.__helperObserver.observe(helperNode, {
|
|
150
|
+
attributes: true,
|
|
151
|
+
attributeFilter: ['id'],
|
|
152
|
+
childList: true,
|
|
153
|
+
subtree: true,
|
|
154
|
+
characterData: true
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @param {boolean} hasHelper
|
|
160
|
+
* @private
|
|
161
|
+
*/
|
|
162
|
+
__toggleHasHelper(hasHelper) {
|
|
163
|
+
this.host.toggleAttribute('has-helper', hasHelper);
|
|
164
|
+
|
|
165
|
+
// Make it possible for other mixins to observe change
|
|
166
|
+
this.dispatchEvent(
|
|
167
|
+
new CustomEvent('helper-changed', {
|
|
168
|
+
detail: {
|
|
169
|
+
hasHelper,
|
|
170
|
+
node: this.node
|
|
171
|
+
}
|
|
172
|
+
})
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* @param {HTMLElement} helperNode
|
|
178
|
+
* @private
|
|
179
|
+
*/
|
|
180
|
+
__updateHelperId(helperNode) {
|
|
181
|
+
if (!helperNode.id) {
|
|
182
|
+
helperNode.id = this.defaultId;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @license
|
|
3
|
-
* Copyright (c) 2021 Vaadin Ltd.
|
|
3
|
+
* Copyright (c) 2021 - 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 { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
|
|
@@ -49,9 +49,8 @@ export const InputConstraintsMixin = dedupingMixin(
|
|
|
49
49
|
checkValidity() {
|
|
50
50
|
if (this.inputElement && this._hasValidConstraints(this.constructor.constraints.map((c) => this[c]))) {
|
|
51
51
|
return this.inputElement.checkValidity();
|
|
52
|
-
} else {
|
|
53
|
-
return !this.invalid;
|
|
54
52
|
}
|
|
53
|
+
return !this.invalid;
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @license
|
|
3
|
-
* Copyright (c) 2021 Vaadin Ltd.
|
|
3
|
+
* Copyright (c) 2021 - 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 { KeyboardMixin } from '@vaadin/component-base/src/keyboard-mixin.js';
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @license
|
|
3
|
-
* Copyright (c) 2021 Vaadin Ltd.
|
|
3
|
+
* Copyright (c) 2021 - 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 { SlotController } from '
|
|
6
|
+
import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* A controller to create and initialize slotted `<input>` element.
|
|
10
10
|
*/
|
|
11
|
-
export class InputController
|
|
11
|
+
export class InputController extends SlotController {}
|
package/src/input-controller.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @license
|
|
3
|
-
* Copyright (c) 2021 Vaadin Ltd.
|
|
3
|
+
* Copyright (c) 2021 - 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 { SlotController } from '
|
|
6
|
+
import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* A controller to create and initialize slotted `<input>` element.
|
|
10
10
|
*/
|
|
11
11
|
export class InputController extends SlotController {
|
|
12
12
|
constructor(host, callback) {
|
|
13
|
-
super(
|
|
13
|
+
super(
|
|
14
|
+
host,
|
|
14
15
|
'input',
|
|
15
16
|
() => document.createElement('input'),
|
|
16
17
|
(host, node) => {
|
|
@@ -30,6 +31,6 @@ export class InputController extends SlotController {
|
|
|
30
31
|
callback(node);
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
|
-
|
|
34
|
+
);
|
|
34
35
|
}
|
|
35
36
|
}
|
package/src/input-field-mixin.js
CHANGED
package/src/input-mixin.d.ts
CHANGED
package/src/input-mixin.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @license
|
|
3
|
-
* Copyright (c) 2021 Vaadin Ltd.
|
|
3
|
+
* Copyright (c) 2021 - 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 { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2021 - 2022 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A controller to manage the label element.
|
|
10
|
+
*/
|
|
11
|
+
export class LabelController extends SlotController {
|
|
12
|
+
/**
|
|
13
|
+
* String used for the label.
|
|
14
|
+
*/
|
|
15
|
+
protected label: string | null | undefined;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Set label based on corresponding host property.
|
|
19
|
+
*/
|
|
20
|
+
setLabel(label: string | null | undefined): void;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* ID attribute value set on the label element.
|
|
24
|
+
*/
|
|
25
|
+
labelId: string;
|
|
26
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2021 - 2022 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A controller to manage the label element.
|
|
10
|
+
*/
|
|
11
|
+
export class LabelController extends SlotController {
|
|
12
|
+
constructor(host) {
|
|
13
|
+
super(
|
|
14
|
+
host,
|
|
15
|
+
'label',
|
|
16
|
+
() => document.createElement('label'),
|
|
17
|
+
(_host, node) => {
|
|
18
|
+
// Set ID attribute or use the existing one.
|
|
19
|
+
this.__updateLabelId(node);
|
|
20
|
+
|
|
21
|
+
// Set text content for the default label.
|
|
22
|
+
this.__updateDefaultLabel(this.label);
|
|
23
|
+
|
|
24
|
+
this.__observeLabel(node);
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @return {string}
|
|
31
|
+
*/
|
|
32
|
+
get labelId() {
|
|
33
|
+
return this.node.id;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Override to initialize the newly added custom label.
|
|
38
|
+
*
|
|
39
|
+
* @param {Node} labelNode
|
|
40
|
+
* @protected
|
|
41
|
+
* @override
|
|
42
|
+
*/
|
|
43
|
+
initCustomNode(labelNode) {
|
|
44
|
+
const hasLabel = this.__hasLabel(labelNode);
|
|
45
|
+
this.__toggleHasLabel(hasLabel);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Override to cleanup label node when it's removed.
|
|
50
|
+
*
|
|
51
|
+
* @param {Node} node
|
|
52
|
+
* @protected
|
|
53
|
+
* @override
|
|
54
|
+
*/
|
|
55
|
+
teardownNode(node) {
|
|
56
|
+
if (this.__labelObserver) {
|
|
57
|
+
this.__labelObserver.disconnect();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let labelNode = this.getSlotChild();
|
|
61
|
+
|
|
62
|
+
// If custom label was removed, restore the default one.
|
|
63
|
+
if (!labelNode && node !== this.defaultNode) {
|
|
64
|
+
labelNode = this.attachDefaultNode();
|
|
65
|
+
|
|
66
|
+
// Run initializer to update default label and ID.
|
|
67
|
+
this.initNode(labelNode);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const hasLabel = this.__hasLabel(labelNode);
|
|
71
|
+
this.__toggleHasLabel(hasLabel);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Set label based on corresponding host property.
|
|
76
|
+
*
|
|
77
|
+
* @param {string} label
|
|
78
|
+
*/
|
|
79
|
+
setLabel(label) {
|
|
80
|
+
this.label = label;
|
|
81
|
+
|
|
82
|
+
this.__updateDefaultLabel(label);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @param {HTMLElement} labelNode
|
|
87
|
+
* @return {boolean}
|
|
88
|
+
* @private
|
|
89
|
+
*/
|
|
90
|
+
__hasLabel(labelNode) {
|
|
91
|
+
if (!labelNode) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return labelNode.children.length > 0 || this.__isNotEmpty(labelNode.textContent);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @param {string} label
|
|
100
|
+
* @private
|
|
101
|
+
*/
|
|
102
|
+
__isNotEmpty(label) {
|
|
103
|
+
return Boolean(label && label.trim() !== '');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @param {HTMLElement} labelNode
|
|
108
|
+
* @private
|
|
109
|
+
*/
|
|
110
|
+
__observeLabel(labelNode) {
|
|
111
|
+
this.__labelObserver = new MutationObserver((mutations) => {
|
|
112
|
+
mutations.forEach((mutation) => {
|
|
113
|
+
const target = mutation.target;
|
|
114
|
+
|
|
115
|
+
// Ensure the mutation target is the currently connected label
|
|
116
|
+
// to ignore async mutations dispatched for removed element.
|
|
117
|
+
const isLabelMutation = target === this.node;
|
|
118
|
+
|
|
119
|
+
if (mutation.type === 'attributes') {
|
|
120
|
+
// We use attributeFilter to only observe ID mutation,
|
|
121
|
+
// no need to check for attribute name separately.
|
|
122
|
+
if (isLabelMutation && target.id !== this.defaultId) {
|
|
123
|
+
this.__updateLabelId(target);
|
|
124
|
+
}
|
|
125
|
+
} else if (isLabelMutation || target.parentElement === this.node) {
|
|
126
|
+
// Update has-label when textContent changes
|
|
127
|
+
const hasLabel = this.__hasLabel(this.node);
|
|
128
|
+
this.__toggleHasLabel(hasLabel);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Observe changes to label ID attribute, text content and children.
|
|
134
|
+
this.__labelObserver.observe(labelNode, {
|
|
135
|
+
attributes: true,
|
|
136
|
+
attributeFilter: ['id'],
|
|
137
|
+
childList: true,
|
|
138
|
+
subtree: true,
|
|
139
|
+
characterData: true
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* @param {boolean} hasLabel
|
|
145
|
+
* @private
|
|
146
|
+
*/
|
|
147
|
+
__toggleHasLabel(hasLabel) {
|
|
148
|
+
this.host.toggleAttribute('has-label', hasLabel);
|
|
149
|
+
|
|
150
|
+
// Make it possible for other mixins to observe change
|
|
151
|
+
this.dispatchEvent(
|
|
152
|
+
new CustomEvent('label-changed', {
|
|
153
|
+
detail: {
|
|
154
|
+
hasLabel,
|
|
155
|
+
node: this.node
|
|
156
|
+
}
|
|
157
|
+
})
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* @param {string} label
|
|
163
|
+
* @private
|
|
164
|
+
*/
|
|
165
|
+
__updateDefaultLabel(label) {
|
|
166
|
+
if (this.defaultNode) {
|
|
167
|
+
this.defaultNode.textContent = label;
|
|
168
|
+
|
|
169
|
+
// Update has-label if default label is used
|
|
170
|
+
if (this.defaultNode === this.node) {
|
|
171
|
+
const hasLabel = this.__isNotEmpty(label);
|
|
172
|
+
this.__toggleHasLabel(hasLabel);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* @param {HTMLElement} labelNode
|
|
179
|
+
* @private
|
|
180
|
+
*/
|
|
181
|
+
__updateLabelId(labelNode) {
|
|
182
|
+
if (!labelNode.id) {
|
|
183
|
+
labelNode.id = this.defaultId;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
package/src/label-mixin.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @license
|
|
3
|
-
* Copyright (c) 2021 Vaadin Ltd.
|
|
3
|
+
* Copyright (c) 2021 - 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 { Constructor } from '@open-wc/dedupe-mixin';
|
|
7
7
|
import { SlotMixinClass } from '@vaadin/component-base/src/slot-mixin.js';
|
|
8
|
+
import { LabelController } from './label-controller.js';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* A mixin to provide label via corresponding property or named slot.
|
|
@@ -21,7 +22,7 @@ export declare class LabelMixinClass {
|
|
|
21
22
|
|
|
22
23
|
protected readonly _labelNode: HTMLLabelElement;
|
|
23
24
|
|
|
24
|
-
protected
|
|
25
|
+
protected _labelController: LabelController;
|
|
25
26
|
|
|
26
|
-
protected
|
|
27
|
+
protected _labelChanged(label: string | null | undefined): void;
|
|
27
28
|
}
|
package/src/label-mixin.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @license
|
|
3
|
-
* Copyright (c) 2021 Vaadin Ltd.
|
|
3
|
+
* Copyright (c) 2021 - 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 { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
|
|
7
|
-
import {
|
|
7
|
+
import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
|
|
8
|
+
import { LabelController } from './label-controller.js';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* A mixin to provide label via corresponding property or named slot.
|
|
@@ -14,7 +15,7 @@ import { SlotMixin } from '@vaadin/component-base/src/slot-mixin.js';
|
|
|
14
15
|
*/
|
|
15
16
|
export const LabelMixin = dedupingMixin(
|
|
16
17
|
(superclass) =>
|
|
17
|
-
class LabelMixinClass extends
|
|
18
|
+
class LabelMixinClass extends ControllerMixin(superclass) {
|
|
18
19
|
static get properties() {
|
|
19
20
|
return {
|
|
20
21
|
/**
|
|
@@ -29,65 +30,25 @@ export const LabelMixin = dedupingMixin(
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
/** @protected */
|
|
32
|
-
get
|
|
33
|
-
return
|
|
34
|
-
...super.slots,
|
|
35
|
-
label: () => {
|
|
36
|
-
const label = document.createElement('label');
|
|
37
|
-
label.textContent = this.label;
|
|
38
|
-
return label;
|
|
39
|
-
}
|
|
40
|
-
};
|
|
33
|
+
get _labelId() {
|
|
34
|
+
return this._labelController.labelId;
|
|
41
35
|
}
|
|
42
36
|
|
|
43
37
|
/** @protected */
|
|
44
38
|
get _labelNode() {
|
|
45
|
-
return this.
|
|
39
|
+
return this._labelController.node;
|
|
46
40
|
}
|
|
47
41
|
|
|
48
42
|
constructor() {
|
|
49
43
|
super();
|
|
50
44
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
this._labelId = `label-${this.localName}-${uniqueId}`;
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* @type {MutationObserver}
|
|
57
|
-
* @private
|
|
58
|
-
*/
|
|
59
|
-
this.__labelNodeObserver = new MutationObserver(() => {
|
|
60
|
-
this._toggleHasLabelAttribute();
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/** @protected */
|
|
65
|
-
ready() {
|
|
66
|
-
super.ready();
|
|
67
|
-
|
|
68
|
-
if (this._labelNode) {
|
|
69
|
-
this._labelNode.id = this._labelId;
|
|
70
|
-
this._toggleHasLabelAttribute();
|
|
71
|
-
|
|
72
|
-
this.__labelNodeObserver.observe(this._labelNode, { childList: true, subtree: true, characterData: true });
|
|
73
|
-
}
|
|
45
|
+
this._labelController = new LabelController(this);
|
|
46
|
+
this.addController(this._labelController);
|
|
74
47
|
}
|
|
75
48
|
|
|
76
49
|
/** @protected */
|
|
77
50
|
_labelChanged(label) {
|
|
78
|
-
|
|
79
|
-
this._labelNode.textContent = label;
|
|
80
|
-
this._toggleHasLabelAttribute();
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** @protected */
|
|
85
|
-
_toggleHasLabelAttribute() {
|
|
86
|
-
if (this._labelNode) {
|
|
87
|
-
const hasLabel = this._labelNode.children.length > 0 || this._labelNode.textContent.trim() !== '';
|
|
88
|
-
|
|
89
|
-
this.toggleAttribute('has-label', hasLabel);
|
|
90
|
-
}
|
|
51
|
+
this._labelController.setLabel(label);
|
|
91
52
|
}
|
|
92
53
|
}
|
|
93
54
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @license
|
|
3
|
-
* Copyright (c) 2021 Vaadin Ltd.
|
|
3
|
+
* Copyright (c) 2021 - 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
|
|
|
@@ -8,15 +8,28 @@
|
|
|
8
8
|
* A controller for linking a `<label>` element with an `<input>` element.
|
|
9
9
|
*/
|
|
10
10
|
export class LabelledInputController {
|
|
11
|
-
constructor(input,
|
|
11
|
+
constructor(input, labelController) {
|
|
12
12
|
this.input = input;
|
|
13
13
|
this.__preventDuplicateLabelClick = this.__preventDuplicateLabelClick.bind(this);
|
|
14
14
|
|
|
15
|
+
labelController.addEventListener('label-changed', (event) => {
|
|
16
|
+
this.__initLabel(event.detail.node);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// Initialize the default label element
|
|
20
|
+
this.__initLabel(labelController.node);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {HTMLElement} label
|
|
25
|
+
* @private
|
|
26
|
+
*/
|
|
27
|
+
__initLabel(label) {
|
|
15
28
|
if (label) {
|
|
16
29
|
label.addEventListener('click', this.__preventDuplicateLabelClick);
|
|
17
30
|
|
|
18
|
-
if (input) {
|
|
19
|
-
label.setAttribute('for', input.id);
|
|
31
|
+
if (this.input) {
|
|
32
|
+
label.setAttribute('for', this.input.id);
|
|
20
33
|
}
|
|
21
34
|
}
|
|
22
35
|
}
|