@vaadin/field-base 22.0.0-alpha6 → 22.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 +6 -9
- package/index.js +6 -9
- package/package.json +22 -17
- package/src/aria-label-controller.d.ts +11 -0
- package/src/aria-label-controller.js +53 -0
- package/src/checked-mixin.js +37 -67
- package/src/delegate-focus-mixin.js +147 -131
- package/src/delegate-state-mixin.d.ts +1 -1
- package/src/delegate-state-mixin.js +100 -88
- package/src/field-mixin.d.ts +39 -0
- package/src/field-mixin.js +317 -0
- package/src/input-constraints-mixin.d.ts +4 -2
- package/src/input-constraints-mixin.js +105 -86
- package/src/input-control-mixin.d.ts +52 -0
- package/src/input-control-mixin.js +170 -0
- package/src/input-controller.d.ts +11 -0
- package/src/input-controller.js +35 -0
- package/src/input-field-mixin.d.ts +2 -16
- package/src/input-field-mixin.js +17 -89
- package/src/input-mixin.d.ts +1 -1
- package/src/input-mixin.js +156 -145
- package/src/label-mixin.d.ts +1 -1
- package/src/label-mixin.js +72 -59
- package/src/pattern-mixin.js +9 -9
- package/src/shadow-focus-mixin.d.ts +21 -0
- package/src/shadow-focus-mixin.js +87 -0
- package/src/slot-controller.d.ts +8 -0
- package/src/slot-controller.js +36 -0
- package/src/slot-label-mixin.d.ts +20 -0
- package/src/slot-label-mixin.js +38 -0
- package/src/slot-styles-mixin.js +38 -31
- package/src/slot-target-mixin.d.ts +8 -19
- package/src/slot-target-mixin.js +93 -73
- package/src/styles/clear-button-styles.d.ts +8 -0
- package/src/styles/clear-button-styles.js +21 -0
- package/src/styles/field-shared-styles.d.ts +8 -0
- package/src/styles/field-shared-styles.js +29 -0
- package/src/styles/input-field-container-styles.d.ts +8 -0
- package/src/styles/input-field-container-styles.js +16 -0
- package/src/styles/input-field-shared-styles.d.ts +8 -0
- package/src/styles/input-field-shared-styles.js +10 -0
- package/src/text-area-controller.d.ts +11 -0
- package/src/text-area-controller.js +38 -0
- package/src/validate-mixin.d.ts +1 -9
- package/src/validate-mixin.js +43 -120
- package/src/aria-label-mixin.d.ts +0 -20
- package/src/aria-label-mixin.js +0 -71
- package/src/char-length-mixin.d.ts +0 -30
- package/src/char-length-mixin.js +0 -42
- package/src/clear-button-mixin.d.ts +0 -28
- package/src/clear-button-mixin.js +0 -82
- package/src/delegate-input-state-mixin.d.ts +0 -43
- package/src/delegate-input-state-mixin.js +0 -63
- package/src/field-aria-mixin.d.ts +0 -24
- package/src/field-aria-mixin.js +0 -61
- package/src/helper-text-mixin.d.ts +0 -24
- package/src/helper-text-mixin.js +0 -144
- package/src/input-slot-mixin.d.ts +0 -26
- package/src/input-slot-mixin.js +0 -71
- package/src/text-area-slot-mixin.d.ts +0 -21
- package/src/text-area-slot-mixin.js +0 -56
- package/src/text-field-mixin.d.ts +0 -21
- package/src/text-field-mixin.js +0 -17
package/src/input-mixin.js
CHANGED
|
@@ -5,165 +5,176 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
8
|
+
/**
|
|
9
|
+
* A mixin to store the reference to an input element
|
|
10
|
+
* and add input and change event listeners to it.
|
|
11
|
+
*
|
|
12
|
+
* @polymerMixin
|
|
13
|
+
*/
|
|
14
|
+
export const InputMixin = dedupingMixin(
|
|
15
|
+
(superclass) =>
|
|
16
|
+
class InputMixinClass extends superclass {
|
|
17
|
+
static get properties() {
|
|
18
|
+
return {
|
|
19
|
+
/**
|
|
20
|
+
* A reference to the input element controlled by the mixin.
|
|
21
|
+
* Any component implementing this mixin is expected to provide it
|
|
22
|
+
* by using `this._setInputElement(input)` Polymer API.
|
|
23
|
+
*
|
|
24
|
+
* A typical case is using `InputController` that does this automatically.
|
|
25
|
+
* However, the input element does not have to always be native <input>:
|
|
26
|
+
* as an example, <vaadin-combo-box-light> accepts other components.
|
|
27
|
+
*
|
|
28
|
+
* @protected
|
|
29
|
+
* @type {!HTMLElement}
|
|
30
|
+
*/
|
|
31
|
+
inputElement: {
|
|
32
|
+
type: Object,
|
|
33
|
+
readOnly: true,
|
|
34
|
+
observer: '_inputElementChanged'
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* String used to define input type.
|
|
39
|
+
* @protected
|
|
40
|
+
*/
|
|
41
|
+
type: {
|
|
42
|
+
type: String,
|
|
43
|
+
readOnly: true
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The value of the field.
|
|
48
|
+
*/
|
|
49
|
+
value: {
|
|
50
|
+
type: String,
|
|
51
|
+
value: '',
|
|
52
|
+
observer: '_valueChanged',
|
|
53
|
+
notify: true
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
41
57
|
|
|
42
|
-
|
|
43
|
-
|
|
58
|
+
constructor() {
|
|
59
|
+
super();
|
|
44
60
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
61
|
+
this._boundOnInput = this._onInput.bind(this);
|
|
62
|
+
this._boundOnChange = this._onChange.bind(this);
|
|
63
|
+
}
|
|
48
64
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Clear the value of the field.
|
|
67
|
+
*/
|
|
68
|
+
clear() {
|
|
69
|
+
this.value = '';
|
|
70
|
+
}
|
|
55
71
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
72
|
+
/**
|
|
73
|
+
* Add event listeners to the input element instance.
|
|
74
|
+
* Override this method to add custom listeners.
|
|
75
|
+
* @param {!HTMLElement} input
|
|
76
|
+
*/
|
|
77
|
+
_addInputListeners(input) {
|
|
78
|
+
input.addEventListener('input', this._boundOnInput);
|
|
79
|
+
input.addEventListener('change', this._boundOnChange);
|
|
80
|
+
}
|
|
65
81
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Remove event listeners from the input element instance.
|
|
84
|
+
* @param {!HTMLElement} input
|
|
85
|
+
*/
|
|
86
|
+
_removeInputListeners(input) {
|
|
87
|
+
input.removeEventListener('input', this._boundOnInput);
|
|
88
|
+
input.removeEventListener('change', this._boundOnChange);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* A method to forward the value property set on the field
|
|
93
|
+
* programmatically back to the input element value.
|
|
94
|
+
* Override this method to perform additional checks,
|
|
95
|
+
* for example to skip this in certain conditions.
|
|
96
|
+
* @param {string} value
|
|
97
|
+
* @protected
|
|
98
|
+
* @override
|
|
99
|
+
*/
|
|
100
|
+
_forwardInputValue(value) {
|
|
101
|
+
// Value might be set before an input element is initialized.
|
|
102
|
+
// This case should be handled separately by a component that
|
|
103
|
+
// implements this mixin, for example in `connectedCallback`.
|
|
104
|
+
if (!this.inputElement) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
74
107
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
* @param {string} value
|
|
81
|
-
* @protected
|
|
82
|
-
* @override
|
|
83
|
-
*/
|
|
84
|
-
_forwardInputValue(value) {
|
|
85
|
-
// Value might be set before an input element is initialized.
|
|
86
|
-
// This case should be handled separately by a component that
|
|
87
|
-
// implements this mixin, for example in `connectedCallback`.
|
|
88
|
-
if (!this.inputElement) {
|
|
89
|
-
return;
|
|
108
|
+
if (value != undefined) {
|
|
109
|
+
this.inputElement.value = value;
|
|
110
|
+
} else {
|
|
111
|
+
this.inputElement.value = '';
|
|
112
|
+
}
|
|
90
113
|
}
|
|
91
114
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
115
|
+
/** @protected */
|
|
116
|
+
_inputElementChanged(input, oldInput) {
|
|
117
|
+
if (input) {
|
|
118
|
+
this._addInputListeners(input);
|
|
119
|
+
} else if (oldInput) {
|
|
120
|
+
this._removeInputListeners(oldInput);
|
|
121
|
+
}
|
|
96
122
|
}
|
|
97
|
-
}
|
|
98
123
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
124
|
+
/**
|
|
125
|
+
* An input event listener used to update the field value.
|
|
126
|
+
* Override this method with an actual implementation.
|
|
127
|
+
* @param {Event} _event
|
|
128
|
+
* @protected
|
|
129
|
+
* @override
|
|
130
|
+
*/
|
|
131
|
+
_onInput(event) {
|
|
132
|
+
// Ignore fake input events e.g. used by clear button.
|
|
133
|
+
this.__userInput = event.isTrusted;
|
|
134
|
+
this.value = event.target.value;
|
|
135
|
+
this.__userInput = false;
|
|
105
136
|
}
|
|
106
|
-
}
|
|
107
137
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
138
|
+
/**
|
|
139
|
+
* A change event listener.
|
|
140
|
+
* Override this method with an actual implementation.
|
|
141
|
+
* @param {Event} _event
|
|
142
|
+
* @protected
|
|
143
|
+
* @override
|
|
144
|
+
*/
|
|
145
|
+
_onChange(_event) {}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Toggle the has-value attribute based on the value property.
|
|
149
|
+
* @param {boolean} hasValue
|
|
150
|
+
* @protected
|
|
151
|
+
*/
|
|
152
|
+
_toggleHasValue(hasValue) {
|
|
153
|
+
this.toggleAttribute('has-value', hasValue);
|
|
154
|
+
}
|
|
121
155
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
_toggleHasValue(hasValue) {
|
|
137
|
-
this.toggleAttribute('has-value', hasValue);
|
|
138
|
-
}
|
|
156
|
+
/**
|
|
157
|
+
* Observer called when a value property changes.
|
|
158
|
+
* @param {string | undefined} newVal
|
|
159
|
+
* @param {string | undefined} oldVal
|
|
160
|
+
* @protected
|
|
161
|
+
* @override
|
|
162
|
+
*/
|
|
163
|
+
_valueChanged(newVal, oldVal) {
|
|
164
|
+
this._toggleHasValue(newVal !== '' && newVal != null);
|
|
165
|
+
|
|
166
|
+
// Setting initial value to empty string, do nothing.
|
|
167
|
+
if (newVal === '' && oldVal === undefined) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
139
170
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
* @protected
|
|
145
|
-
* @override
|
|
146
|
-
*/
|
|
147
|
-
_valueChanged(newVal, oldVal) {
|
|
148
|
-
this._toggleHasValue(newVal !== '' && newVal != null);
|
|
149
|
-
|
|
150
|
-
// Setting initial value to empty string, do nothing.
|
|
151
|
-
if (newVal === '' && oldVal === undefined) {
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
171
|
+
// Value is set by the user, no need to sync it back to input.
|
|
172
|
+
if (this.__userInput) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
154
175
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
return;
|
|
176
|
+
// Setting a value programmatically, sync it to input element.
|
|
177
|
+
this._forwardInputValue(newVal);
|
|
158
178
|
}
|
|
159
|
-
|
|
160
|
-
// Setting a value programmatically, sync it to input element.
|
|
161
|
-
this._forwardInputValue(newVal);
|
|
162
179
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* A mixin to store the reference to an input element
|
|
167
|
-
* and add input and change event listeners to it.
|
|
168
|
-
*/
|
|
169
|
-
export const InputMixin = dedupingMixin(InputMixinImplementation);
|
|
180
|
+
);
|
package/src/label-mixin.d.ts
CHANGED
package/src/label-mixin.js
CHANGED
|
@@ -6,75 +6,88 @@
|
|
|
6
6
|
import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
|
|
7
7
|
import { SlotMixin } from '@vaadin/component-base/src/slot-mixin.js';
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
9
|
+
/**
|
|
10
|
+
* A mixin to provide label via corresponding property or named slot.
|
|
11
|
+
*
|
|
12
|
+
* @polymerMixin
|
|
13
|
+
* @mixes SlotMixin
|
|
14
|
+
*/
|
|
15
|
+
export const LabelMixin = dedupingMixin(
|
|
16
|
+
(superclass) =>
|
|
17
|
+
class LabelMixinClass extends SlotMixin(superclass) {
|
|
18
|
+
static get properties() {
|
|
19
|
+
return {
|
|
20
|
+
/**
|
|
21
|
+
* The label text for the input node.
|
|
22
|
+
* When no light dom defined via [slot=label], this value will be used.
|
|
23
|
+
*/
|
|
24
|
+
label: {
|
|
25
|
+
type: String,
|
|
26
|
+
observer: '_labelChanged'
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
23
30
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
/** @protected */
|
|
32
|
+
get slots() {
|
|
33
|
+
return {
|
|
34
|
+
...super.slots,
|
|
35
|
+
label: () => {
|
|
36
|
+
const label = document.createElement('label');
|
|
37
|
+
label.textContent = this.label;
|
|
38
|
+
return label;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
34
42
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
43
|
+
/** @protected */
|
|
44
|
+
get _labelNode() {
|
|
45
|
+
return this._getDirectSlotChild('label');
|
|
46
|
+
}
|
|
39
47
|
|
|
40
|
-
|
|
41
|
-
|
|
48
|
+
constructor() {
|
|
49
|
+
super();
|
|
42
50
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
51
|
+
// Ensure every instance has unique ID
|
|
52
|
+
const uniqueId = (LabelMixinClass._uniqueLabelId = 1 + LabelMixinClass._uniqueLabelId || 0);
|
|
53
|
+
this._labelId = `label-${this.localName}-${uniqueId}`;
|
|
47
54
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
55
|
+
/**
|
|
56
|
+
* @type {MutationObserver}
|
|
57
|
+
* @private
|
|
58
|
+
*/
|
|
59
|
+
this.__labelNodeObserver = new MutationObserver(() => {
|
|
60
|
+
this._toggleHasLabelAttribute();
|
|
61
|
+
});
|
|
62
|
+
}
|
|
51
63
|
|
|
52
|
-
|
|
53
|
-
|
|
64
|
+
/** @protected */
|
|
65
|
+
ready() {
|
|
66
|
+
super.ready();
|
|
54
67
|
|
|
55
|
-
this.
|
|
56
|
-
|
|
57
|
-
|
|
68
|
+
if (this._labelNode) {
|
|
69
|
+
this._labelNode.id = this._labelId;
|
|
70
|
+
this._toggleHasLabelAttribute();
|
|
58
71
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const label = this._labelNode.textContent;
|
|
62
|
-
if (label !== this.label) {
|
|
63
|
-
this.label = label;
|
|
72
|
+
this.__labelNodeObserver.observe(this._labelNode, { childList: true });
|
|
73
|
+
}
|
|
64
74
|
}
|
|
65
|
-
}
|
|
66
75
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
76
|
+
/** @protected */
|
|
77
|
+
_labelChanged(label) {
|
|
78
|
+
if (this._labelNode) {
|
|
79
|
+
this._labelNode.textContent = label;
|
|
80
|
+
this._toggleHasLabelAttribute();
|
|
81
|
+
}
|
|
71
82
|
}
|
|
72
83
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
84
|
+
/** @protected */
|
|
85
|
+
_toggleHasLabelAttribute() {
|
|
86
|
+
if (this._labelNode) {
|
|
87
|
+
const hasLabel = this._labelNode.children.length > 0 || this._labelNode.textContent.trim() !== '';
|
|
76
88
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
89
|
+
this.toggleAttribute('has-label', hasLabel);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
);
|
package/src/pattern-mixin.js
CHANGED
|
@@ -3,12 +3,17 @@
|
|
|
3
3
|
* Copyright (c) 2021 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import { Debouncer } from '@
|
|
7
|
-
import { timeOut } from '@
|
|
8
|
-
import { dedupingMixin } from '@polymer/polymer/lib/utils/mixin.js';
|
|
6
|
+
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
|
|
7
|
+
import { timeOut } from '@vaadin/component-base/src/async.js';
|
|
9
8
|
import { InputConstraintsMixin } from './input-constraints-mixin.js';
|
|
10
9
|
|
|
11
|
-
|
|
10
|
+
/**
|
|
11
|
+
* A mixin to provide `pattern` and `preventInvalidInput` properties.
|
|
12
|
+
*
|
|
13
|
+
* @polymerMixin
|
|
14
|
+
* @mixes InputConstraintsMixin
|
|
15
|
+
*/
|
|
16
|
+
export const PatternMixin = (superclass) =>
|
|
12
17
|
class PatternMixinClass extends InputConstraintsMixin(superclass) {
|
|
13
18
|
static get properties() {
|
|
14
19
|
return {
|
|
@@ -65,8 +70,3 @@ const PatternMixinImplementation = (superclass) =>
|
|
|
65
70
|
super._onInput(event);
|
|
66
71
|
}
|
|
67
72
|
};
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* A mixin to provide `pattern` and `preventInvalidInput` properties.
|
|
71
|
-
*/
|
|
72
|
-
export const PatternMixin = dedupingMixin(PatternMixinImplementation);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2021 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { KeyboardMixin } from '@vaadin/component-base/src/keyboard-mixin.js';
|
|
7
|
+
import { TabindexMixin } from '@vaadin/component-base/src/tabindex-mixin.js';
|
|
8
|
+
import { DelegateFocusMixin } from './delegate-focus-mixin.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A mixin to forward focus to an element in the shadow DOM.
|
|
12
|
+
*/
|
|
13
|
+
declare function ShadowFocusMixin<T extends new (...args: any[]) => {}>(base: T): T & ShadowFocusMixinConstructor;
|
|
14
|
+
|
|
15
|
+
interface ShadowFocusMixinConstructor {
|
|
16
|
+
new (...args: any[]): ShadowFocusMixin;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface ShadowFocusMixin extends KeyboardMixin, TabindexMixin, DelegateFocusMixin {}
|
|
20
|
+
|
|
21
|
+
export { ShadowFocusMixinConstructor, ShadowFocusMixin };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2021 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { KeyboardMixin } from '@vaadin/component-base/src/keyboard-mixin.js';
|
|
7
|
+
import { TabindexMixin } from '@vaadin/component-base/src/tabindex-mixin.js';
|
|
8
|
+
import { DelegateFocusMixin } from './delegate-focus-mixin.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A mixin to forward focus to an element in the shadow DOM.
|
|
12
|
+
*
|
|
13
|
+
* @polymerMixin
|
|
14
|
+
* @mixes DelegateFocusMixin
|
|
15
|
+
* @mixes KeyboardMixin
|
|
16
|
+
* @mixes TabindexMixin
|
|
17
|
+
*/
|
|
18
|
+
export const ShadowFocusMixin = (superClass) =>
|
|
19
|
+
class ShadowFocusMixinClass extends TabindexMixin(DelegateFocusMixin(KeyboardMixin(superClass))) {
|
|
20
|
+
/**
|
|
21
|
+
* Override an event listener from `KeyboardMixin`
|
|
22
|
+
* to prevent setting `focused` on Shift Tab.
|
|
23
|
+
* @param {KeyboardEvent} event
|
|
24
|
+
* @protected
|
|
25
|
+
* @override
|
|
26
|
+
*/
|
|
27
|
+
_onKeyDown(event) {
|
|
28
|
+
super._onKeyDown(event);
|
|
29
|
+
|
|
30
|
+
// When focus moves with Shift + Tab, do not mark host as focused.
|
|
31
|
+
// The flag set here will be later used in focusin event listener.
|
|
32
|
+
if (!event.defaultPrevented && event.keyCode === 9 && event.shiftKey) {
|
|
33
|
+
this._isShiftTabbing = true;
|
|
34
|
+
HTMLElement.prototype.focus.apply(this);
|
|
35
|
+
this._setFocused(false);
|
|
36
|
+
setTimeout(() => (this._isShiftTabbing = false), 0);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Override method inherited from `FocusMixin`
|
|
42
|
+
* to support focusElement in Shadow DOM.
|
|
43
|
+
* @param {Event} event
|
|
44
|
+
* @return {boolean}
|
|
45
|
+
* @protected
|
|
46
|
+
* @override
|
|
47
|
+
*/
|
|
48
|
+
_shouldSetFocus(event) {
|
|
49
|
+
if (!this.disabled && this.focusElement) {
|
|
50
|
+
const path = event.composedPath();
|
|
51
|
+
|
|
52
|
+
// When focus moves from outside and not with Shift + Tab, delegate it to focusElement.
|
|
53
|
+
if (path[0] === this && !this.contains(event.relatedTarget) && !this._isShiftTabbing) {
|
|
54
|
+
this.focusElement.focus();
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (path.includes(this.focusElement)) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Override an observer from `TabindexMixin`.
|
|
68
|
+
* Do not call super to remove tabindex attribute
|
|
69
|
+
* from host when disabled by setting undefined.
|
|
70
|
+
* @param {string} tabindex
|
|
71
|
+
* @protected
|
|
72
|
+
* @override
|
|
73
|
+
*/
|
|
74
|
+
_tabindexChanged(tabindex) {
|
|
75
|
+
if (tabindex !== undefined) {
|
|
76
|
+
this.focusElement.tabIndex = tabindex;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (this.disabled && tabindex) {
|
|
80
|
+
// If tabindex attribute was changed while component was disabled
|
|
81
|
+
if (tabindex !== -1) {
|
|
82
|
+
this.__lastTabIndex = tabindex;
|
|
83
|
+
}
|
|
84
|
+
this.tabindex = undefined;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
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 { ReactiveController } from 'lit';
|
|
7
|
+
|
|
8
|
+
export class SlotController implements ReactiveController {}
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
+
export class SlotController {
|
|
7
|
+
constructor(host, [slotName, slotFactory, slotInitializer]) {
|
|
8
|
+
this.host = host;
|
|
9
|
+
this.slotName = slotName;
|
|
10
|
+
this.slotFactory = slotFactory;
|
|
11
|
+
this.slotInitializer = slotInitializer;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
hostConnected() {
|
|
15
|
+
if (!this.__initialized) {
|
|
16
|
+
const { host, slotName, slotFactory } = this;
|
|
17
|
+
|
|
18
|
+
const slotted = host.querySelector(`[slot=${slotName}]`);
|
|
19
|
+
|
|
20
|
+
if (!slotted) {
|
|
21
|
+
const slotContent = slotFactory(host);
|
|
22
|
+
if (slotContent instanceof Element) {
|
|
23
|
+
slotContent.setAttribute('slot', slotName);
|
|
24
|
+
host.appendChild(slotContent);
|
|
25
|
+
this.__slotContent = slotContent;
|
|
26
|
+
}
|
|
27
|
+
} else {
|
|
28
|
+
this.__slotContent = slotted;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
this.slotInitializer(host, this.__slotContent);
|
|
32
|
+
|
|
33
|
+
this.__initialized = true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|