@vaadin/field-base 24.0.0-alpha5 → 24.0.0-alpha7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +0 -1
- package/index.js +0 -1
- package/package.json +3 -3
- package/src/error-controller.d.ts +0 -5
- package/src/error-controller.js +52 -61
- package/src/field-mixin.js +13 -17
- package/src/helper-controller.d.ts +0 -2
- package/src/helper-controller.js +44 -146
- package/src/input-mixin.d.ts +2 -0
- package/src/input-mixin.js +15 -5
- package/src/label-controller.d.ts +0 -5
- package/src/label-controller.js +55 -141
- package/src/label-mixin.js +6 -1
- package/src/labelled-input-controller.js +1 -1
- package/src/slot-target-controller.js +1 -1
- package/src/shadow-focus-mixin.d.ts +0 -23
- package/src/shadow-focus-mixin.js +0 -97
package/index.d.ts
CHANGED
|
@@ -10,7 +10,6 @@ export { InputMixin } from './src/input-mixin.js';
|
|
|
10
10
|
export { LabelledInputController } from './src/labelled-input-controller.js';
|
|
11
11
|
export { LabelMixin } from './src/label-mixin.js';
|
|
12
12
|
export { PatternMixin } from './src/pattern-mixin.js';
|
|
13
|
-
export { ShadowFocusMixin } from './src/shadow-focus-mixin.js';
|
|
14
13
|
export { SlotStylesMixin } from './src/slot-styles-mixin.js';
|
|
15
14
|
export { TextAreaController } from './src/text-area-controller.js';
|
|
16
15
|
export { ValidateMixin } from './src/validate-mixin.js';
|
package/index.js
CHANGED
|
@@ -10,7 +10,6 @@ export { InputMixin } from './src/input-mixin.js';
|
|
|
10
10
|
export { LabelledInputController } from './src/labelled-input-controller.js';
|
|
11
11
|
export { LabelMixin } from './src/label-mixin.js';
|
|
12
12
|
export { PatternMixin } from './src/pattern-mixin.js';
|
|
13
|
-
export { ShadowFocusMixin } from './src/shadow-focus-mixin.js';
|
|
14
13
|
export { SlotStylesMixin } from './src/slot-styles-mixin.js';
|
|
15
14
|
export { TextAreaController } from './src/text-area-controller.js';
|
|
16
15
|
export { ValidateMixin } from './src/validate-mixin.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/field-base",
|
|
3
|
-
"version": "24.0.0-
|
|
3
|
+
"version": "24.0.0-alpha7",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@open-wc/dedupe-mixin": "^1.3.0",
|
|
34
34
|
"@polymer/polymer": "^3.0.0",
|
|
35
|
-
"@vaadin/component-base": "24.0.0-
|
|
35
|
+
"@vaadin/component-base": "24.0.0-alpha7",
|
|
36
36
|
"lit": "^2.0.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
@@ -40,5 +40,5 @@
|
|
|
40
40
|
"@vaadin/testing-helpers": "^0.3.2",
|
|
41
41
|
"sinon": "^13.0.2"
|
|
42
42
|
},
|
|
43
|
-
"gitHead": "
|
|
43
|
+
"gitHead": "aeb4535336813636736759e0a5de148b26bfc3b6"
|
|
44
44
|
}
|
|
@@ -9,11 +9,6 @@ import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
|
|
|
9
9
|
* A controller that manages the error message node content.
|
|
10
10
|
*/
|
|
11
11
|
export class ErrorController extends SlotController {
|
|
12
|
-
/**
|
|
13
|
-
* ID attribute value set on the error message element.
|
|
14
|
-
*/
|
|
15
|
-
readonly errorId: string;
|
|
16
|
-
|
|
17
12
|
/**
|
|
18
13
|
* String used for the error message text content.
|
|
19
14
|
*/
|
package/src/error-controller.js
CHANGED
|
@@ -3,30 +3,14 @@
|
|
|
3
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 {
|
|
6
|
+
import { SlotObserveController } from '@vaadin/component-base/src/slot-observe-controller.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* A controller that manages the error message node content.
|
|
10
10
|
*/
|
|
11
|
-
export class ErrorController extends
|
|
11
|
+
export class ErrorController extends SlotObserveController {
|
|
12
12
|
constructor(host) {
|
|
13
|
-
super(host, 'error-message', 'div'
|
|
14
|
-
initializer: (node) => {
|
|
15
|
-
this.__updateErrorId(node);
|
|
16
|
-
|
|
17
|
-
this.__updateHasError();
|
|
18
|
-
},
|
|
19
|
-
useUniqueId: true,
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* ID attribute value set on the error message element.
|
|
25
|
-
*
|
|
26
|
-
* @return {string}
|
|
27
|
-
*/
|
|
28
|
-
get errorId() {
|
|
29
|
-
return this.node && this.node.id;
|
|
13
|
+
super(host, 'error-message', 'div');
|
|
30
14
|
}
|
|
31
15
|
|
|
32
16
|
/**
|
|
@@ -37,7 +21,7 @@ export class ErrorController extends SlotController {
|
|
|
37
21
|
setErrorMessage(errorMessage) {
|
|
38
22
|
this.errorMessage = errorMessage;
|
|
39
23
|
|
|
40
|
-
this.
|
|
24
|
+
this.updateDefaultNode(this.node);
|
|
41
25
|
}
|
|
42
26
|
|
|
43
27
|
/**
|
|
@@ -48,7 +32,33 @@ export class ErrorController extends SlotController {
|
|
|
48
32
|
setInvalid(invalid) {
|
|
49
33
|
this.invalid = invalid;
|
|
50
34
|
|
|
51
|
-
this.
|
|
35
|
+
this.updateDefaultNode(this.node);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Override method inherited from `SlotController` to not run
|
|
40
|
+
* initializer on the custom slotted node unnecessarily.
|
|
41
|
+
*
|
|
42
|
+
* @param {Node} node
|
|
43
|
+
* @protected
|
|
44
|
+
* @override
|
|
45
|
+
*/
|
|
46
|
+
initAddedNode(node) {
|
|
47
|
+
if (node !== this.defaultNode) {
|
|
48
|
+
// There is no need to run `initNode`.
|
|
49
|
+
this.initCustomNode(node);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Override to initialize the newly added default error message.
|
|
55
|
+
*
|
|
56
|
+
* @param {Node} errorNode
|
|
57
|
+
* @protected
|
|
58
|
+
* @override
|
|
59
|
+
*/
|
|
60
|
+
initNode(errorNode) {
|
|
61
|
+
this.updateDefaultNode(errorNode);
|
|
52
62
|
}
|
|
53
63
|
|
|
54
64
|
/**
|
|
@@ -59,53 +69,43 @@ export class ErrorController extends SlotController {
|
|
|
59
69
|
* @override
|
|
60
70
|
*/
|
|
61
71
|
initCustomNode(errorNode) {
|
|
62
|
-
this.__updateErrorId(errorNode);
|
|
63
|
-
|
|
64
72
|
// Save the custom error message content on the host.
|
|
65
73
|
if (errorNode.textContent && !this.errorMessage) {
|
|
66
74
|
this.errorMessage = errorNode.textContent.trim();
|
|
67
75
|
}
|
|
68
76
|
|
|
69
|
-
|
|
77
|
+
// Notify the host about custom node.
|
|
78
|
+
super.initCustomNode(errorNode);
|
|
70
79
|
}
|
|
71
80
|
|
|
72
81
|
/**
|
|
73
|
-
* Override
|
|
82
|
+
* Override method inherited from `SlotObserveController`
|
|
83
|
+
* to restore and the default error message element.
|
|
74
84
|
*
|
|
75
|
-
* @param {Node} node
|
|
76
85
|
* @protected
|
|
77
86
|
* @override
|
|
78
87
|
*/
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
// If custom error was removed, restore the default one.
|
|
83
|
-
if (!errorNode && node !== this.defaultNode) {
|
|
84
|
-
errorNode = this.attachDefaultNode();
|
|
85
|
-
|
|
86
|
-
// Run initializer to update default error message ID.
|
|
87
|
-
this.initNode(errorNode);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
this.__updateHasError();
|
|
88
|
+
restoreDefaultNode() {
|
|
89
|
+
this.attachDefaultNode();
|
|
91
90
|
}
|
|
92
91
|
|
|
93
92
|
/**
|
|
94
|
-
*
|
|
95
|
-
*
|
|
93
|
+
* Override method inherited from `SlotObserveController`
|
|
94
|
+
* to update the error message text and hidden state.
|
|
95
|
+
*
|
|
96
|
+
* Note: unlike with other controllers, this method is
|
|
97
|
+
* called for both default and custom error message.
|
|
98
|
+
*
|
|
99
|
+
* @param {Node | undefined} node
|
|
100
|
+
* @protected
|
|
101
|
+
* @override
|
|
96
102
|
*/
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
/** @private */
|
|
102
|
-
__updateHasError() {
|
|
103
|
-
const errorNode = this.node;
|
|
104
|
-
const hasError = Boolean(this.invalid && this.__isNotEmpty(this.errorMessage));
|
|
103
|
+
updateDefaultNode(errorNode) {
|
|
104
|
+
const { errorMessage, invalid } = this;
|
|
105
|
+
const hasError = Boolean(invalid && errorMessage && errorMessage.trim() !== '');
|
|
105
106
|
|
|
106
|
-
// Update both default and custom error message node.
|
|
107
107
|
if (errorNode) {
|
|
108
|
-
errorNode.textContent = hasError ?
|
|
108
|
+
errorNode.textContent = hasError ? errorMessage : '';
|
|
109
109
|
errorNode.hidden = !hasError;
|
|
110
110
|
|
|
111
111
|
// Role alert will make the error message announce immediately
|
|
@@ -117,16 +117,7 @@ export class ErrorController extends SlotController {
|
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* @param {HTMLElement} errorNode
|
|
125
|
-
* @private
|
|
126
|
-
*/
|
|
127
|
-
__updateErrorId(errorNode) {
|
|
128
|
-
if (!errorNode.id) {
|
|
129
|
-
errorNode.id = this.defaultId;
|
|
130
|
-
}
|
|
120
|
+
// Notify the host after update.
|
|
121
|
+
super.updateDefaultNode(errorNode);
|
|
131
122
|
}
|
|
132
123
|
}
|
package/src/field-mixin.js
CHANGED
|
@@ -56,11 +56,6 @@ export const FieldMixin = (superclass) =>
|
|
|
56
56
|
return ['_invalidChanged(invalid)', '_requiredChanged(required)'];
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
/** @protected */
|
|
60
|
-
get _errorId() {
|
|
61
|
-
return this._errorController.errorId;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
59
|
/**
|
|
65
60
|
* @protected
|
|
66
61
|
* @return {HTMLElement}
|
|
@@ -69,11 +64,6 @@ export const FieldMixin = (superclass) =>
|
|
|
69
64
|
return this._errorController.node;
|
|
70
65
|
}
|
|
71
66
|
|
|
72
|
-
/** @protected */
|
|
73
|
-
get _helperId() {
|
|
74
|
-
return this._helperController.helperId;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
67
|
/**
|
|
78
68
|
* @protected
|
|
79
69
|
* @return {HTMLElement}
|
|
@@ -89,14 +79,19 @@ export const FieldMixin = (superclass) =>
|
|
|
89
79
|
this._helperController = new HelperController(this);
|
|
90
80
|
this._errorController = new ErrorController(this);
|
|
91
81
|
|
|
92
|
-
this.
|
|
93
|
-
|
|
94
|
-
|
|
82
|
+
this._errorController.addEventListener('slot-content-changed', (event) => {
|
|
83
|
+
this.toggleAttribute('has-error-message', event.detail.hasContent);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
this._labelController.addEventListener('slot-content-changed', (event) => {
|
|
87
|
+
const { hasContent, node } = event.detail;
|
|
88
|
+
this.__labelChanged(hasContent, node);
|
|
95
89
|
});
|
|
96
90
|
|
|
97
|
-
this._helperController.addEventListener('
|
|
98
|
-
const {
|
|
99
|
-
this.
|
|
91
|
+
this._helperController.addEventListener('slot-content-changed', (event) => {
|
|
92
|
+
const { hasContent, node } = event.detail;
|
|
93
|
+
this.toggleAttribute('has-helper', hasContent);
|
|
94
|
+
this.__helperChanged(hasContent, node);
|
|
100
95
|
});
|
|
101
96
|
}
|
|
102
97
|
|
|
@@ -178,7 +173,8 @@ export const FieldMixin = (superclass) =>
|
|
|
178
173
|
// Error message ID needs to be dynamically added / removed based on the validity
|
|
179
174
|
// Otherwise assistive technologies would announce the error, even if we hide it.
|
|
180
175
|
if (invalid) {
|
|
181
|
-
this.
|
|
176
|
+
const node = this._errorNode;
|
|
177
|
+
this._fieldAriaController.setErrorId(node && node.id);
|
|
182
178
|
} else {
|
|
183
179
|
this._fieldAriaController.setErrorId(null);
|
|
184
180
|
}
|
package/src/helper-controller.js
CHANGED
|
@@ -3,189 +3,87 @@
|
|
|
3
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 {
|
|
6
|
+
import { SlotObserveController } from '@vaadin/component-base/src/slot-observe-controller.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* A controller that manages the helper node content.
|
|
10
10
|
*/
|
|
11
|
-
export class HelperController extends
|
|
11
|
+
export class HelperController extends SlotObserveController {
|
|
12
12
|
constructor(host) {
|
|
13
13
|
// Do not provide tag name, as we create helper lazily.
|
|
14
|
-
super(host, 'helper', null
|
|
15
|
-
useUniqueId: true,
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
get helperId() {
|
|
20
|
-
return this.node && this.node.id;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Override to initialize the newly added custom helper.
|
|
25
|
-
*
|
|
26
|
-
* @param {Node} helperNode
|
|
27
|
-
* @protected
|
|
28
|
-
* @override
|
|
29
|
-
*/
|
|
30
|
-
initCustomNode(helperNode) {
|
|
31
|
-
this.__updateHelperId(helperNode);
|
|
32
|
-
|
|
33
|
-
this.__observeHelper(helperNode);
|
|
34
|
-
|
|
35
|
-
const hasHelper = this.__hasHelper(helperNode);
|
|
36
|
-
this.__toggleHasHelper(hasHelper);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Override to cleanup helper node when it's removed.
|
|
41
|
-
*
|
|
42
|
-
* @param {Node} _node
|
|
43
|
-
* @protected
|
|
44
|
-
* @override
|
|
45
|
-
*/
|
|
46
|
-
teardownNode(_node) {
|
|
47
|
-
// The observer does not exist when the default helper is removed.
|
|
48
|
-
if (this.__helperIdObserver) {
|
|
49
|
-
this.__helperIdObserver.disconnect();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const helperNode = this.getSlotChild();
|
|
53
|
-
|
|
54
|
-
// Custom node is added to helper slot
|
|
55
|
-
if (helperNode && helperNode !== this.defaultNode) {
|
|
56
|
-
const hasHelper = this.__hasHelper(helperNode);
|
|
57
|
-
this.__toggleHasHelper(hasHelper);
|
|
58
|
-
} else {
|
|
59
|
-
// Restore default helper if needed
|
|
60
|
-
this.__applyDefaultHelper(this.helperText, helperNode);
|
|
61
|
-
}
|
|
14
|
+
super(host, 'helper', null);
|
|
62
15
|
}
|
|
63
16
|
|
|
64
17
|
/**
|
|
65
18
|
* Set helper text based on corresponding host property.
|
|
19
|
+
*
|
|
66
20
|
* @param {string} helperText
|
|
67
21
|
*/
|
|
68
22
|
setHelperText(helperText) {
|
|
69
23
|
this.helperText = helperText;
|
|
70
24
|
|
|
25
|
+
// Restore the default helper, if needed.
|
|
71
26
|
const helperNode = this.getSlotChild();
|
|
72
|
-
if (!helperNode || helperNode === this.defaultNode) {
|
|
73
|
-
this.__applyDefaultHelper(helperText, helperNode);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* @param {HTMLElement} helperNode
|
|
79
|
-
* @return {boolean}
|
|
80
|
-
* @private
|
|
81
|
-
*/
|
|
82
|
-
__hasHelper(helperNode) {
|
|
83
27
|
if (!helperNode) {
|
|
84
|
-
|
|
28
|
+
this.restoreDefaultNode();
|
|
85
29
|
}
|
|
86
30
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* @param {string} helperText
|
|
96
|
-
* @private
|
|
97
|
-
*/
|
|
98
|
-
__isNotEmpty(helperText) {
|
|
99
|
-
return helperText && helperText.trim() !== '';
|
|
31
|
+
// When default helper is used, update it.
|
|
32
|
+
if (this.node === this.defaultNode) {
|
|
33
|
+
this.updateDefaultNode(this.node);
|
|
34
|
+
}
|
|
100
35
|
}
|
|
101
36
|
|
|
102
37
|
/**
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
38
|
+
* Override method inherited from `SlotObserveController`
|
|
39
|
+
* to create the default helper element lazily as needed.
|
|
40
|
+
*
|
|
41
|
+
* @param {Node | undefined} node
|
|
42
|
+
* @protected
|
|
43
|
+
* @override
|
|
106
44
|
*/
|
|
107
|
-
|
|
108
|
-
const
|
|
45
|
+
restoreDefaultNode() {
|
|
46
|
+
const { helperText } = this;
|
|
109
47
|
|
|
110
|
-
|
|
111
|
-
|
|
48
|
+
// No helper yet, create one.
|
|
49
|
+
if (helperText && helperText.trim() !== '') {
|
|
112
50
|
this.tagName = 'div';
|
|
113
51
|
|
|
114
|
-
helperNode = this.attachDefaultNode();
|
|
52
|
+
const helperNode = this.attachDefaultNode();
|
|
115
53
|
|
|
116
|
-
|
|
117
|
-
this.
|
|
54
|
+
// Observe the default node.
|
|
55
|
+
this.observeNode(helperNode);
|
|
118
56
|
}
|
|
119
|
-
|
|
120
|
-
if (helperNode) {
|
|
121
|
-
helperNode.textContent = helperText;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
this.__toggleHasHelper(hasHelperText);
|
|
125
57
|
}
|
|
126
58
|
|
|
127
59
|
/**
|
|
128
|
-
*
|
|
129
|
-
*
|
|
60
|
+
* Override method inherited from `SlotObserveController`
|
|
61
|
+
* to update the default helper element text content.
|
|
62
|
+
*
|
|
63
|
+
* @param {Node | undefined} node
|
|
64
|
+
* @protected
|
|
65
|
+
* @override
|
|
130
66
|
*/
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
// Ensure the mutation target is the currently connected helper
|
|
137
|
-
// to ignore async mutations dispatched for removed element.
|
|
138
|
-
const isHelperMutation = target === this.node;
|
|
139
|
-
|
|
140
|
-
if (mutation.type === 'attributes') {
|
|
141
|
-
// We use attributeFilter to only observe ID mutation,
|
|
142
|
-
// no need to check for attribute name separately.
|
|
143
|
-
if (isHelperMutation && target.id !== this.defaultId) {
|
|
144
|
-
this.__updateHelperId(target);
|
|
145
|
-
}
|
|
146
|
-
} else if (isHelperMutation || target.parentElement === this.node) {
|
|
147
|
-
// Update has-helper when textContent changes
|
|
148
|
-
const hasHelper = this.__hasHelper(this.node);
|
|
149
|
-
this.__toggleHasHelper(hasHelper);
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
});
|
|
67
|
+
updateDefaultNode(node) {
|
|
68
|
+
if (node) {
|
|
69
|
+
node.textContent = this.helperText;
|
|
70
|
+
}
|
|
153
71
|
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
attributes: true,
|
|
157
|
-
attributeFilter: ['id'],
|
|
158
|
-
childList: true,
|
|
159
|
-
subtree: true,
|
|
160
|
-
characterData: true,
|
|
161
|
-
});
|
|
72
|
+
// Notify the host after update.
|
|
73
|
+
super.updateDefaultNode(node);
|
|
162
74
|
}
|
|
163
75
|
|
|
164
76
|
/**
|
|
165
|
-
*
|
|
166
|
-
*
|
|
77
|
+
* Override to observe the newly added custom node.
|
|
78
|
+
*
|
|
79
|
+
* @param {Node} node
|
|
80
|
+
* @protected
|
|
81
|
+
* @override
|
|
167
82
|
*/
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
// Make it possible for other mixins to observe change
|
|
172
|
-
this.dispatchEvent(
|
|
173
|
-
new CustomEvent('helper-changed', {
|
|
174
|
-
detail: {
|
|
175
|
-
hasHelper,
|
|
176
|
-
node: this.node,
|
|
177
|
-
},
|
|
178
|
-
}),
|
|
179
|
-
);
|
|
180
|
-
}
|
|
83
|
+
initCustomNode(node) {
|
|
84
|
+
// Notify the host about a custom slotted helper.
|
|
85
|
+
super.initCustomNode(node);
|
|
181
86
|
|
|
182
|
-
|
|
183
|
-
* @param {HTMLElement} helperNode
|
|
184
|
-
* @private
|
|
185
|
-
*/
|
|
186
|
-
__updateHelperId(helperNode) {
|
|
187
|
-
if (!helperNode.id) {
|
|
188
|
-
helperNode.id = this.defaultId;
|
|
189
|
-
}
|
|
87
|
+
this.observeNode(node);
|
|
190
88
|
}
|
|
191
89
|
}
|
package/src/input-mixin.d.ts
CHANGED
package/src/input-mixin.js
CHANGED
|
@@ -161,11 +161,7 @@ export const InputMixin = dedupingMixin(
|
|
|
161
161
|
* @private
|
|
162
162
|
*/
|
|
163
163
|
__onInput(event) {
|
|
164
|
-
|
|
165
|
-
// the actual native input element, on which the event occurred,
|
|
166
|
-
// can be inside shadow trees.
|
|
167
|
-
const target = event.composedPath()[0];
|
|
168
|
-
this._hasInputValue = target.value.length > 0;
|
|
164
|
+
this._setHasInputValue(event);
|
|
169
165
|
this._onInput(event);
|
|
170
166
|
}
|
|
171
167
|
|
|
@@ -236,5 +232,19 @@ export const InputMixin = dedupingMixin(
|
|
|
236
232
|
get _hasValue() {
|
|
237
233
|
return this.value != null && this.value !== '';
|
|
238
234
|
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Sets the `_hasInputValue` property based on the `input` event.
|
|
238
|
+
*
|
|
239
|
+
* @param {InputEvent} event
|
|
240
|
+
* @protected
|
|
241
|
+
*/
|
|
242
|
+
_setHasInputValue(event) {
|
|
243
|
+
// In the case a custom web component is passed as `inputElement`,
|
|
244
|
+
// the actual native input element, on which the event occurred,
|
|
245
|
+
// can be inside shadow trees.
|
|
246
|
+
const target = event.composedPath()[0];
|
|
247
|
+
this._hasInputValue = target.value.length > 0;
|
|
248
|
+
}
|
|
239
249
|
},
|
|
240
250
|
);
|
|
@@ -9,11 +9,6 @@ import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
|
|
|
9
9
|
* A controller to manage the label element.
|
|
10
10
|
*/
|
|
11
11
|
export class LabelController extends SlotController {
|
|
12
|
-
/**
|
|
13
|
-
* ID attribute value set on the label element.
|
|
14
|
-
*/
|
|
15
|
-
labelId: string;
|
|
16
|
-
|
|
17
12
|
/**
|
|
18
13
|
* String used for the label.
|
|
19
14
|
*/
|
package/src/label-controller.js
CHANGED
|
@@ -3,72 +3,14 @@
|
|
|
3
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 {
|
|
6
|
+
import { SlotObserveController } from '@vaadin/component-base/src/slot-observe-controller.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* A controller to manage the label element.
|
|
10
10
|
*/
|
|
11
|
-
export class LabelController extends
|
|
11
|
+
export class LabelController extends SlotObserveController {
|
|
12
12
|
constructor(host) {
|
|
13
|
-
super(host, 'label', 'label'
|
|
14
|
-
initializer: (node) => {
|
|
15
|
-
// Set ID attribute or use the existing one.
|
|
16
|
-
this.__updateLabelId(node);
|
|
17
|
-
|
|
18
|
-
// Set text content for the default label.
|
|
19
|
-
this.__updateDefaultLabel(this.label);
|
|
20
|
-
|
|
21
|
-
this.__observeLabel(node);
|
|
22
|
-
},
|
|
23
|
-
useUniqueId: true,
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @return {string}
|
|
29
|
-
*/
|
|
30
|
-
get labelId() {
|
|
31
|
-
return this.node.id;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Override to initialize the newly added custom label.
|
|
36
|
-
*
|
|
37
|
-
* @param {Node} labelNode
|
|
38
|
-
* @protected
|
|
39
|
-
* @override
|
|
40
|
-
*/
|
|
41
|
-
initCustomNode(labelNode) {
|
|
42
|
-
this.__updateLabelId(labelNode);
|
|
43
|
-
|
|
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);
|
|
13
|
+
super(host, 'label', 'label');
|
|
72
14
|
}
|
|
73
15
|
|
|
74
16
|
/**
|
|
@@ -79,108 +21,80 @@ export class LabelController extends SlotController {
|
|
|
79
21
|
setLabel(label) {
|
|
80
22
|
this.label = label;
|
|
81
23
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* @param {HTMLElement} labelNode
|
|
87
|
-
* @return {boolean}
|
|
88
|
-
* @private
|
|
89
|
-
*/
|
|
90
|
-
__hasLabel(labelNode) {
|
|
24
|
+
// Restore the default label, if needed.
|
|
25
|
+
const labelNode = this.getSlotChild();
|
|
91
26
|
if (!labelNode) {
|
|
92
|
-
|
|
27
|
+
this.restoreDefaultNode();
|
|
93
28
|
}
|
|
94
29
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
* @param {string} label
|
|
100
|
-
* @private
|
|
101
|
-
*/
|
|
102
|
-
__isNotEmpty(label) {
|
|
103
|
-
return Boolean(label && label.trim() !== '');
|
|
30
|
+
// When default label is used, update it.
|
|
31
|
+
if (this.node === this.defaultNode) {
|
|
32
|
+
this.updateDefaultNode(this.node);
|
|
33
|
+
}
|
|
104
34
|
}
|
|
105
35
|
|
|
106
36
|
/**
|
|
107
|
-
*
|
|
108
|
-
*
|
|
37
|
+
* Override method inherited from `SlotObserveController`
|
|
38
|
+
* to restore and observe the default label element.
|
|
39
|
+
*
|
|
40
|
+
* @protected
|
|
41
|
+
* @override
|
|
109
42
|
*/
|
|
110
|
-
|
|
111
|
-
|
|
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;
|
|
43
|
+
restoreDefaultNode() {
|
|
44
|
+
const { label } = this;
|
|
118
45
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
});
|
|
46
|
+
// Restore the default label.
|
|
47
|
+
if (label && label.trim() !== '') {
|
|
48
|
+
const labelNode = this.attachDefaultNode();
|
|
132
49
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
attributeFilter: ['id'],
|
|
137
|
-
childList: true,
|
|
138
|
-
subtree: true,
|
|
139
|
-
characterData: true,
|
|
140
|
-
});
|
|
50
|
+
// Observe the default label.
|
|
51
|
+
this.observeNode(labelNode);
|
|
52
|
+
}
|
|
141
53
|
}
|
|
142
54
|
|
|
143
55
|
/**
|
|
144
|
-
*
|
|
145
|
-
*
|
|
56
|
+
* Override method inherited from `SlotObserveController`
|
|
57
|
+
* to update the default label element text content.
|
|
58
|
+
*
|
|
59
|
+
* @param {Node | undefined} node
|
|
60
|
+
* @protected
|
|
61
|
+
* @override
|
|
146
62
|
*/
|
|
147
|
-
|
|
148
|
-
|
|
63
|
+
updateDefaultNode(node) {
|
|
64
|
+
if (node) {
|
|
65
|
+
node.textContent = this.label;
|
|
66
|
+
}
|
|
149
67
|
|
|
150
|
-
//
|
|
151
|
-
|
|
152
|
-
new CustomEvent('label-changed', {
|
|
153
|
-
detail: {
|
|
154
|
-
hasLabel,
|
|
155
|
-
node: this.node,
|
|
156
|
-
},
|
|
157
|
-
}),
|
|
158
|
-
);
|
|
68
|
+
// Notify the host after update.
|
|
69
|
+
super.updateDefaultNode(node);
|
|
159
70
|
}
|
|
160
71
|
|
|
161
72
|
/**
|
|
162
|
-
*
|
|
163
|
-
*
|
|
73
|
+
* Override method inherited from `SlotMixin` to observe
|
|
74
|
+
* the default label node, but not the custom one.
|
|
75
|
+
*
|
|
76
|
+
* @param {Node} node
|
|
77
|
+
* @protected
|
|
78
|
+
* @override
|
|
164
79
|
*/
|
|
165
|
-
|
|
166
|
-
if (this.defaultNode) {
|
|
167
|
-
this.
|
|
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
|
-
}
|
|
80
|
+
initNode(node) {
|
|
81
|
+
if (node === this.defaultNode) {
|
|
82
|
+
this.updateDefaultNode(node);
|
|
83
|
+
this.observeNode(node);
|
|
174
84
|
}
|
|
175
85
|
}
|
|
176
86
|
|
|
177
87
|
/**
|
|
178
|
-
*
|
|
179
|
-
*
|
|
88
|
+
* Override to observe the newly added custom node.
|
|
89
|
+
*
|
|
90
|
+
* @param {Node} node
|
|
91
|
+
* @protected
|
|
92
|
+
* @override
|
|
180
93
|
*/
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
94
|
+
initCustomNode(node) {
|
|
95
|
+
// Notify the host about adding a custom node.
|
|
96
|
+
super.initCustomNode(node);
|
|
97
|
+
|
|
98
|
+
this.observeNode(node);
|
|
185
99
|
}
|
|
186
100
|
}
|
package/src/label-mixin.js
CHANGED
|
@@ -31,7 +31,8 @@ export const LabelMixin = dedupingMixin(
|
|
|
31
31
|
|
|
32
32
|
/** @protected */
|
|
33
33
|
get _labelId() {
|
|
34
|
-
|
|
34
|
+
const node = this._labelNode;
|
|
35
|
+
return node && node.id;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
/** @protected */
|
|
@@ -43,6 +44,10 @@ export const LabelMixin = dedupingMixin(
|
|
|
43
44
|
super();
|
|
44
45
|
|
|
45
46
|
this._labelController = new LabelController(this);
|
|
47
|
+
|
|
48
|
+
this._labelController.addEventListener('slot-content-changed', (event) => {
|
|
49
|
+
this.toggleAttribute('has-label', event.detail.hasContent);
|
|
50
|
+
});
|
|
46
51
|
}
|
|
47
52
|
|
|
48
53
|
/** @protected */
|
|
@@ -12,7 +12,7 @@ export class LabelledInputController {
|
|
|
12
12
|
this.input = input;
|
|
13
13
|
this.__preventDuplicateLabelClick = this.__preventDuplicateLabelClick.bind(this);
|
|
14
14
|
|
|
15
|
-
labelController.addEventListener('
|
|
15
|
+
labelController.addEventListener('slot-content-changed', (event) => {
|
|
16
16
|
this.__initLabel(event.detail.node);
|
|
17
17
|
});
|
|
18
18
|
|
|
@@ -97,7 +97,7 @@ export class SlotTargetController {
|
|
|
97
97
|
* @private
|
|
98
98
|
*/
|
|
99
99
|
__copyNodesToSlotTarget(nodes, slotTarget) {
|
|
100
|
-
this.__slotTargetClones
|
|
100
|
+
this.__slotTargetClones ||= [];
|
|
101
101
|
|
|
102
102
|
nodes.forEach((node) => {
|
|
103
103
|
// Clone the nodes and append the clones to the target
|
|
@@ -1,23 +0,0 @@
|
|
|
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 type { Constructor } from '@open-wc/dedupe-mixin';
|
|
7
|
-
import type { DisabledMixinClass } from '@vaadin/component-base/src/disabled-mixin.js';
|
|
8
|
-
import type { FocusMixinClass } from '@vaadin/component-base/src/focus-mixin.js';
|
|
9
|
-
import type { KeyboardMixinClass } from '@vaadin/component-base/src/keyboard-mixin.js';
|
|
10
|
-
import type { TabindexMixinClass } from '@vaadin/component-base/src/tabindex-mixin.js';
|
|
11
|
-
import type { DelegateFocusMixinClass } from './delegate-focus-mixin.js';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* A mixin to forward focus to an element in the shadow DOM.
|
|
15
|
-
*/
|
|
16
|
-
export declare function ShadowFocusMixin<T extends Constructor<HTMLElement>>(
|
|
17
|
-
base: T,
|
|
18
|
-
): Constructor<DelegateFocusMixinClass> &
|
|
19
|
-
Constructor<DisabledMixinClass> &
|
|
20
|
-
Constructor<FocusMixinClass> &
|
|
21
|
-
Constructor<KeyboardMixinClass> &
|
|
22
|
-
Constructor<TabindexMixinClass> &
|
|
23
|
-
T;
|
|
@@ -1,97 +0,0 @@
|
|
|
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 { KeyboardMixin } from '@vaadin/component-base/src/keyboard-mixin.js';
|
|
7
|
-
import { DelegateFocusMixin } from './delegate-focus-mixin.js';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* A mixin to forward focus to an element in the shadow DOM.
|
|
11
|
-
*
|
|
12
|
-
* @polymerMixin
|
|
13
|
-
* @mixes DelegateFocusMixin
|
|
14
|
-
* @mixes KeyboardMixin
|
|
15
|
-
*/
|
|
16
|
-
export const ShadowFocusMixin = (superClass) =>
|
|
17
|
-
class ShadowFocusMixinClass extends DelegateFocusMixin(KeyboardMixin(superClass)) {
|
|
18
|
-
static get properties() {
|
|
19
|
-
return {
|
|
20
|
-
/**
|
|
21
|
-
* Indicates whether the element can be focused and where it participates in sequential keyboard navigation.
|
|
22
|
-
*
|
|
23
|
-
* @override
|
|
24
|
-
* @protected
|
|
25
|
-
*/
|
|
26
|
-
tabindex: {
|
|
27
|
-
type: Number,
|
|
28
|
-
value: 0,
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Override an event listener from `KeyboardMixin`
|
|
35
|
-
* to prevent focusing the host element on Shift Tab.
|
|
36
|
-
* @param {KeyboardEvent} event
|
|
37
|
-
* @protected
|
|
38
|
-
* @override
|
|
39
|
-
*/
|
|
40
|
-
_onKeyDown(event) {
|
|
41
|
-
super._onKeyDown(event);
|
|
42
|
-
|
|
43
|
-
// When focus moves with Shift + Tab, skip focusing the host element
|
|
44
|
-
// by focusing it before the default browser focus handling runs
|
|
45
|
-
if (!event.defaultPrevented && event.keyCode === 9 && event.shiftKey) {
|
|
46
|
-
HTMLElement.prototype.focus.apply(this);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Override method inherited from `FocusMixin`
|
|
52
|
-
* to support focusElement in Shadow DOM.
|
|
53
|
-
* @param {Event} event
|
|
54
|
-
* @return {boolean}
|
|
55
|
-
* @protected
|
|
56
|
-
* @override
|
|
57
|
-
*/
|
|
58
|
-
_shouldSetFocus(event) {
|
|
59
|
-
if (!this.disabled && this.focusElement) {
|
|
60
|
-
const path = event.composedPath();
|
|
61
|
-
|
|
62
|
-
// When focus moves to the host element itself, then delegate it to the focusElement
|
|
63
|
-
// This should only move focus when using keyboard navigation, for clicks we don't want to interfere,
|
|
64
|
-
// for example when the user tries to select some text
|
|
65
|
-
if (path[0] === this && this._keyboardActive) {
|
|
66
|
-
this.focusElement.focus();
|
|
67
|
-
}
|
|
68
|
-
if (path[0] === this || path.includes(this.focusElement)) {
|
|
69
|
-
return true;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Override an observer from `TabindexMixin`.
|
|
78
|
-
* Do not call super to remove tabindex attribute
|
|
79
|
-
* from host when disabled by setting undefined.
|
|
80
|
-
* @param {string} tabindex
|
|
81
|
-
* @protected
|
|
82
|
-
* @override
|
|
83
|
-
*/
|
|
84
|
-
_tabindexChanged(tabindex) {
|
|
85
|
-
if (tabindex !== undefined) {
|
|
86
|
-
this.focusElement.tabIndex = tabindex;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (this.disabled && tabindex) {
|
|
90
|
-
// If tabindex attribute was changed while component was disabled
|
|
91
|
-
if (tabindex !== -1) {
|
|
92
|
-
this._lastTabIndex = tabindex;
|
|
93
|
-
}
|
|
94
|
-
this.tabindex = undefined;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
};
|