@descope/web-components-ui 1.0.67 → 1.0.69
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/cjs/index.cjs.js.map +1 -1
- package/dist/index.esm.js +510 -395
- package/dist/index.esm.js.map +1 -1
- package/dist/umd/135.js +1 -0
- package/dist/umd/descope-button-index-js.js +1 -1
- package/dist/umd/descope-checkbox-index-js.js +1 -1
- package/dist/umd/descope-combo-box-index-js.js +1 -1
- package/dist/umd/descope-container-index-js.js +1 -1
- package/dist/umd/descope-date-picker-index-js.js +1 -1
- package/dist/umd/descope-divider-index-js.js +1 -1
- package/dist/umd/descope-email-field-index-js.js +1 -1
- package/dist/umd/descope-link-index-js.js +1 -1
- package/dist/umd/descope-loader-linear-index-js.js +1 -1
- package/dist/umd/descope-loader-radial-index-js.js +1 -1
- package/dist/umd/descope-logo-index-js.js +1 -1
- package/dist/umd/descope-number-field-index-js.js +1 -1
- package/dist/umd/descope-passcode-descope-passcode-internal-index-js.js +1 -1
- package/dist/umd/descope-passcode-index-js.js +1 -1
- package/dist/umd/descope-password-field-index-js.js +1 -1
- package/dist/umd/descope-switch-toggle-index-js.js +1 -1
- package/dist/umd/descope-text-area-index-js.js +1 -1
- package/dist/umd/descope-text-field-index-js.js +1 -1
- package/dist/umd/descope-text-index-js.js +1 -1
- package/dist/umd/index.js +1 -1
- package/package.json +1 -1
- package/src/baseClasses/BaseInputClass.js +3 -2
- package/src/baseClasses/createBaseClass.js +29 -0
- package/src/components/descope-combo-box/ComboBox.js +18 -4
- package/src/components/descope-container/Container.js +17 -25
- package/src/components/descope-divider/Divider.js +32 -40
- package/src/components/descope-link/Link.js +8 -17
- package/src/components/descope-loader-linear/LoaderLinear.js +24 -29
- package/src/components/descope-loader-radial/LoaderRadial.js +18 -26
- package/src/components/descope-logo/Logo.js +4 -12
- package/src/components/descope-passcode/Passcode.js +4 -11
- package/src/components/descope-passcode/descope-passcode-internal/PasscodeInternal.js +41 -53
- package/src/components/descope-text/Text.js +4 -12
- package/src/helpers/index.js +2 -0
- package/src/helpers/mixinsHelpers.js +18 -0
- package/src/mixins/changeMixin.js +47 -0
- package/src/mixins/componentNameValidationMixin.js +1 -1
- package/src/mixins/createProxy.js +26 -28
- package/src/mixins/createStyleMixin/helpers.js +3 -3
- package/src/mixins/createStyleMixin/index.js +10 -9
- package/src/mixins/draggableMixin.js +1 -1
- package/src/mixins/focusMixin.js +130 -0
- package/src/mixins/hoverableMixin.js +14 -13
- package/src/mixins/index.js +3 -1
- package/src/mixins/{inputMixin.js → inputValidationMixin.js} +17 -58
- package/src/mixins/portalMixin.js +11 -7
- package/src/mixins/proxyInputMixin.js +50 -45
- package/src/theme/components/comboBox.js +2 -9
- package/dist/umd/860.js +0 -1
- package/src/baseClasses/DescopeBaseClass.js +0 -1
@@ -2,23 +2,19 @@ import {
|
|
2
2
|
createStyleMixin,
|
3
3
|
draggableMixin,
|
4
4
|
componentNameValidationMixin,
|
5
|
-
hoverableMixin
|
6
5
|
} from '../../mixins';
|
7
|
-
import {
|
6
|
+
import { createBaseClass } from '../../baseClasses/createBaseClass';
|
8
7
|
import Text from '../descope-text/Text';
|
9
8
|
import { compose } from '../../helpers';
|
10
9
|
import { forwardAttrs, getComponentName } from '../../helpers/componentHelpers';
|
11
10
|
|
12
11
|
export const componentName = getComponentName('link');
|
13
|
-
class RawLink extends
|
14
|
-
static get componentName() {
|
15
|
-
return componentName;
|
16
|
-
}
|
12
|
+
class RawLink extends createBaseClass({ componentName, baseSelector: ':host a' }) {
|
17
13
|
constructor() {
|
18
14
|
super();
|
19
15
|
const template = document.createElement('template');
|
20
16
|
|
21
|
-
|
17
|
+
this.attachShadow({ mode: 'open' }).innerHTML = `
|
22
18
|
<style>
|
23
19
|
:host {
|
24
20
|
display: inline-block;
|
@@ -36,9 +32,6 @@ class RawLink extends DescopeBaseClass {
|
|
36
32
|
</div>
|
37
33
|
`;
|
38
34
|
|
39
|
-
this.attachShadow({ mode: 'open' });
|
40
|
-
this.shadowRoot.appendChild(template.content.cloneNode(true));
|
41
|
-
|
42
35
|
forwardAttrs(this, this.shadowRoot.querySelector('a'), {
|
43
36
|
includeAttrs: ['href', 'target', 'tooltip'],
|
44
37
|
mapAttrs: {
|
@@ -49,24 +42,23 @@ class RawLink extends DescopeBaseClass {
|
|
49
42
|
forwardAttrs(this, this.shadowRoot.querySelector('descope-text'), {
|
50
43
|
includeAttrs: ['mode', 'variant'],
|
51
44
|
});
|
52
|
-
|
53
|
-
this.baseSelector = ':host > div';
|
54
45
|
}
|
55
46
|
}
|
56
47
|
|
57
48
|
const selectors = {
|
58
49
|
host: { selector: () => 'host' },
|
59
|
-
anchor: {
|
60
|
-
|
50
|
+
anchor: {},
|
51
|
+
wrapper: {selector: () => ':host > div'},
|
52
|
+
text: { selector: () => Text.componentName }
|
61
53
|
};
|
62
54
|
|
63
|
-
const { anchor, text, host } = selectors;
|
55
|
+
const { anchor, text, host, wrapper } = selectors;
|
64
56
|
|
65
57
|
const Link = compose(
|
66
58
|
createStyleMixin({
|
67
59
|
mappings: {
|
68
60
|
width: host,
|
69
|
-
textAlign:
|
61
|
+
textAlign: wrapper,
|
70
62
|
color: [anchor, { ...text, property: Text.cssVarList.color }],
|
71
63
|
cursor: anchor,
|
72
64
|
borderBottomWidth: anchor,
|
@@ -74,7 +66,6 @@ const Link = compose(
|
|
74
66
|
borderBottomColor: anchor
|
75
67
|
},
|
76
68
|
}),
|
77
|
-
hoverableMixin(anchor.selector),
|
78
69
|
draggableMixin,
|
79
70
|
componentNameValidationMixin
|
80
71
|
)(RawLink);
|
@@ -3,47 +3,42 @@ import {
|
|
3
3
|
draggableMixin,
|
4
4
|
componentNameValidationMixin
|
5
5
|
} from '../../mixins';
|
6
|
-
import {
|
6
|
+
import { createBaseClass } from '../../baseClasses/createBaseClass';
|
7
7
|
import { compose } from '../../helpers';
|
8
8
|
import { getComponentName } from '../../helpers/componentHelpers';
|
9
9
|
|
10
10
|
export const componentName = getComponentName('loader-linear');
|
11
11
|
|
12
|
-
class RawLoaderLinear extends
|
12
|
+
class RawLoaderLinear extends createBaseClass({ componentName, baseSelector: ':host > div' }) {
|
13
13
|
static get componentName() {
|
14
14
|
return componentName;
|
15
15
|
}
|
16
16
|
constructor() {
|
17
17
|
super();
|
18
|
-
const template = document.createElement('template');
|
19
|
-
template.innerHTML = `
|
20
|
-
<style>
|
21
|
-
@keyframes tilt {
|
22
|
-
0% { transform: translateX(0); }
|
23
|
-
50% { transform: translateX(400%); }
|
24
|
-
}
|
25
|
-
:host {
|
26
|
-
position: relative;
|
27
|
-
display: inline-block
|
28
|
-
}
|
29
|
-
div::after {
|
30
|
-
content: '';
|
31
|
-
animation-name: tilt;
|
32
|
-
position: absolute;
|
33
|
-
left: 0;
|
34
|
-
}
|
35
18
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
19
|
+
this.attachShadow({ mode: 'open' }).innerHTML = `
|
20
|
+
<style>
|
21
|
+
@keyframes tilt {
|
22
|
+
0% { transform: translateX(0); }
|
23
|
+
50% { transform: translateX(400%); }
|
24
|
+
}
|
25
|
+
:host {
|
26
|
+
position: relative;
|
27
|
+
display: inline-block
|
28
|
+
}
|
29
|
+
div::after {
|
30
|
+
content: '';
|
31
|
+
animation-name: tilt;
|
32
|
+
position: absolute;
|
33
|
+
left: 0;
|
34
|
+
}
|
42
35
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
36
|
+
:host > div {
|
37
|
+
width: 100%;
|
38
|
+
}
|
39
|
+
</style>
|
40
|
+
<div></div>
|
41
|
+
`;
|
47
42
|
}
|
48
43
|
}
|
49
44
|
|
@@ -3,40 +3,32 @@ import {
|
|
3
3
|
draggableMixin,
|
4
4
|
componentNameValidationMixin
|
5
5
|
} from '../../mixins';
|
6
|
-
import {
|
6
|
+
import { createBaseClass } from '../../baseClasses/createBaseClass';
|
7
7
|
import { compose } from '../../helpers';
|
8
8
|
import { getComponentName } from '../../helpers/componentHelpers';
|
9
9
|
|
10
10
|
export const componentName = getComponentName('loader-radial');
|
11
11
|
|
12
|
-
class RawLoaderRadial extends
|
13
|
-
static get componentName() {
|
14
|
-
return componentName;
|
15
|
-
}
|
12
|
+
class RawLoaderRadial extends createBaseClass({ componentName, baseSelector: ':host > div' }) {
|
16
13
|
constructor() {
|
17
14
|
super();
|
18
|
-
const template = document.createElement('template');
|
19
|
-
template.innerHTML = `
|
20
|
-
<style>
|
21
|
-
@keyframes spin {
|
22
|
-
0% { transform: rotate(0deg); }
|
23
|
-
100% { transform: rotate(360deg); }
|
24
|
-
}
|
25
|
-
:host {
|
26
|
-
position: relative;
|
27
|
-
display: inline-flex;
|
28
|
-
}
|
29
|
-
:host > div {
|
30
|
-
animation-name: spin;
|
31
|
-
}
|
32
|
-
</style>
|
33
|
-
<div></div>
|
34
|
-
`;
|
35
|
-
|
36
|
-
this.attachShadow({ mode: 'open' });
|
37
|
-
this.shadowRoot.appendChild(template.content.cloneNode(true));
|
38
15
|
|
39
|
-
this.
|
16
|
+
this.attachShadow({ mode: 'open' }).innerHTML = `
|
17
|
+
<style>
|
18
|
+
@keyframes spin {
|
19
|
+
0% { transform: rotate(0deg); }
|
20
|
+
100% { transform: rotate(360deg); }
|
21
|
+
}
|
22
|
+
:host {
|
23
|
+
position: relative;
|
24
|
+
display: inline-flex;
|
25
|
+
}
|
26
|
+
:host > div {
|
27
|
+
animation-name: spin;
|
28
|
+
}
|
29
|
+
</style>
|
30
|
+
<div></div>
|
31
|
+
`;
|
40
32
|
}
|
41
33
|
}
|
42
34
|
|
@@ -3,7 +3,7 @@ import {
|
|
3
3
|
draggableMixin,
|
4
4
|
componentNameValidationMixin
|
5
5
|
} from '../../mixins';
|
6
|
-
import {
|
6
|
+
import { createBaseClass } from '../../baseClasses/createBaseClass';
|
7
7
|
import { compose } from '../../helpers';
|
8
8
|
import { getComponentName } from '../../helpers/componentHelpers';
|
9
9
|
|
@@ -12,23 +12,15 @@ export const componentName = getComponentName('logo');
|
|
12
12
|
let style;
|
13
13
|
const getStyle = () => style;
|
14
14
|
|
15
|
-
class RawLogo extends
|
16
|
-
static get componentName() {
|
17
|
-
return componentName;
|
18
|
-
}
|
15
|
+
class RawLogo extends createBaseClass({ componentName, baseSelector: ':host > div' }) {
|
19
16
|
constructor() {
|
20
17
|
super();
|
21
|
-
|
22
|
-
|
18
|
+
|
19
|
+
this.attachShadow({ mode: 'open' }).innerHTML = `
|
23
20
|
<style>
|
24
21
|
${getStyle()}
|
25
22
|
</style>
|
26
23
|
<div></div>`;
|
27
|
-
|
28
|
-
this.attachShadow({ mode: 'open' });
|
29
|
-
this.shadowRoot.appendChild(template.content.cloneNode(true));
|
30
|
-
|
31
|
-
this.baseSelector = ':host > div';
|
32
24
|
}
|
33
25
|
}
|
34
26
|
|
@@ -38,21 +38,10 @@ const customMixin = (superclass) =>
|
|
38
38
|
|
39
39
|
this.proxyElement.appendChild(template.content.cloneNode(true));
|
40
40
|
|
41
|
-
// we want to control when the element is out of focus
|
42
|
-
// so the validations will be triggered blur event is dispatched from descope-passcode internal (and not every time focusing a digit)
|
43
|
-
this.proxyElement._setFocused = () => { };
|
44
|
-
|
45
41
|
this.inputElement = this.shadowRoot.querySelector(descopeInternalComponentName);
|
46
42
|
|
47
43
|
forwardAttrs(this.shadowRoot.host, this.inputElement, { includeAttrs: ['required', 'pattern'] })
|
48
44
|
|
49
|
-
// we want to trigger validation only when dispatching a blur event from the descope-passcode-internal
|
50
|
-
this.inputElement.addEventListener('blur', (e) => {
|
51
|
-
// we do not want native blur events, only the ones that we are sending
|
52
|
-
if (!e.isTrusted){
|
53
|
-
this.proxyElement.validate();
|
54
|
-
}
|
55
|
-
});
|
56
45
|
}
|
57
46
|
};
|
58
47
|
|
@@ -89,6 +78,10 @@ const Passcode = compose(
|
|
89
78
|
display: inline-block;
|
90
79
|
}
|
91
80
|
|
81
|
+
:host([readonly]) descope-passcode-internal > div {
|
82
|
+
pointer-events: none;
|
83
|
+
}
|
84
|
+
|
92
85
|
descope-passcode-internal {
|
93
86
|
-webkit-mask-image: none;
|
94
87
|
display: flex;
|
@@ -7,11 +7,10 @@ export const componentName = getComponentName('passcode-internal');
|
|
7
7
|
class PasscodeInternal extends BaseInputClass {
|
8
8
|
static get observedAttributes() {
|
9
9
|
return [
|
10
|
-
...BaseInputClass.observedAttributes,
|
10
|
+
...(BaseInputClass.observedAttributes || []),
|
11
11
|
'disabled',
|
12
12
|
'bordered',
|
13
13
|
'size',
|
14
|
-
'readonly'
|
15
14
|
];
|
16
15
|
}
|
17
16
|
|
@@ -19,10 +18,12 @@ class PasscodeInternal extends BaseInputClass {
|
|
19
18
|
return componentName;
|
20
19
|
}
|
21
20
|
|
21
|
+
#boundHandleInvalid = this.#handleInvalid.bind(this)
|
22
|
+
#boundHandleValid = this.#handleValid.bind(this)
|
23
|
+
#boundHandleBlur = this.#handleBlur.bind(this)
|
24
|
+
|
22
25
|
constructor() {
|
23
26
|
super();
|
24
|
-
const template = document.createElement('template');
|
25
|
-
|
26
27
|
const inputs = [...Array(this.digits).keys()].map((idx) => `
|
27
28
|
<descope-text-field
|
28
29
|
st-width="35px"
|
@@ -32,14 +33,12 @@ class PasscodeInternal extends BaseInputClass {
|
|
32
33
|
></descope-text-field>
|
33
34
|
`)
|
34
35
|
|
35
|
-
|
36
|
+
this.innerHTML = `
|
36
37
|
<div>
|
37
38
|
${inputs.join('')}
|
38
39
|
</div>
|
39
40
|
`;
|
40
41
|
|
41
|
-
this.appendChild(template.content.cloneNode(true));
|
42
|
-
|
43
42
|
this.baseSelector = ':host > div';
|
44
43
|
|
45
44
|
this.inputs = Array.from(this.querySelectorAll('descope-text-field'))
|
@@ -67,15 +66,13 @@ class PasscodeInternal extends BaseInputClass {
|
|
67
66
|
return `^$|^\\d{${this.digits},}$`
|
68
67
|
}
|
69
68
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
}
|
75
|
-
})
|
69
|
+
#handleInvalid() {
|
70
|
+
if (this.hasAttribute('invalid')) {
|
71
|
+
this.inputs.forEach(input => input.setAttribute('invalid', 'true'))
|
72
|
+
}
|
76
73
|
}
|
77
74
|
|
78
|
-
|
75
|
+
#handleValid() {
|
79
76
|
this.inputs.forEach(input => input.removeAttribute('invalid'))
|
80
77
|
}
|
81
78
|
|
@@ -91,17 +88,25 @@ class PasscodeInternal extends BaseInputClass {
|
|
91
88
|
}
|
92
89
|
};
|
93
90
|
|
94
|
-
|
95
|
-
this.inputs[0].focus()
|
91
|
+
onFocus(){
|
92
|
+
this.inputs[0].focus()
|
96
93
|
}
|
97
94
|
|
98
|
-
|
99
|
-
super.connectedCallback();
|
95
|
+
connectedCallback() {
|
96
|
+
super.connectedCallback?.();
|
100
97
|
|
101
98
|
this.initInputs()
|
102
99
|
|
103
|
-
this.addEventListener('invalid', this
|
104
|
-
this.addEventListener('valid', this
|
100
|
+
this.addEventListener('invalid', this.#boundHandleInvalid)
|
101
|
+
this.addEventListener('valid', this.#boundHandleValid)
|
102
|
+
this.addEventListener('blur', this.#boundHandleBlur)
|
103
|
+
}
|
104
|
+
|
105
|
+
disconnectedCallback() {
|
106
|
+
super.connectedCallback?.();
|
107
|
+
this.removeEventListener('invalid', this.#boundHandleInvalid)
|
108
|
+
this.removeEventListener('valid', this.#boundHandleValid)
|
109
|
+
this.removeEventListener('blur', this.#boundHandleBlur)
|
105
110
|
}
|
106
111
|
|
107
112
|
getInputIdx(inputEle) {
|
@@ -125,66 +130,49 @@ class PasscodeInternal extends BaseInputClass {
|
|
125
130
|
currentInput.value = charArr[i] ?? '';
|
126
131
|
|
127
132
|
const nextInput = this.getNextInput(currentInput);
|
133
|
+
|
128
134
|
if (nextInput === currentInput) break;
|
129
135
|
currentInput = nextInput;
|
130
136
|
}
|
131
137
|
|
132
|
-
|
138
|
+
focusElement(currentInput);
|
133
139
|
};
|
134
140
|
|
135
|
-
handleBlur() {
|
136
|
-
this
|
141
|
+
#handleBlur() {
|
142
|
+
this.#handleInvalid()
|
137
143
|
}
|
138
144
|
|
139
145
|
initInputs() {
|
140
146
|
this.inputs.forEach((input) => {
|
141
|
-
|
142
|
-
// in order to simulate blur on the input
|
143
|
-
// we are checking if focus on one of the digits happened immediately after blur on another digit
|
144
|
-
// if not, the component is no longer focused and we should simulate blur
|
145
|
-
input.addEventListener('blur', (e) => {
|
146
|
-
e.stopPropagation()
|
147
|
-
const timerId = setTimeout(() => {
|
148
|
-
this.dispatchBlur()
|
149
|
-
});
|
150
|
-
|
151
|
-
this.inputs.forEach((ele) =>
|
152
|
-
ele.addEventListener('focus', () => clearTimeout(timerId), { once: true })
|
153
|
-
);
|
154
|
-
})
|
155
|
-
|
156
147
|
input.oninput = (e) => {
|
157
|
-
e.stopPropagation()
|
158
148
|
const charArr = getSanitizedCharacters(input.value);
|
159
149
|
|
160
|
-
if (!charArr.length)
|
150
|
+
if (!charArr.length) {
|
151
|
+
// if we got an invalid value we want to clear the input
|
152
|
+
input.value = '';
|
153
|
+
if (e.data === null) {
|
154
|
+
// if the user deleted the char, we want to focus the prev digit
|
155
|
+
focusElement(this.getPrevInput(input));
|
156
|
+
}
|
157
|
+
}
|
161
158
|
else this.fillDigits(charArr, input);
|
162
|
-
|
163
|
-
this.dispatchInput()
|
164
159
|
};
|
165
160
|
|
166
161
|
input.onkeydown = ({ key }) => {
|
162
|
+
// when user deletes a digit, we want to focus the previous digit
|
167
163
|
if (key === 'Backspace') {
|
168
|
-
|
169
|
-
|
170
|
-
// if the user deleted the digit we want to focus the previous digit
|
171
|
-
const prevInput = this.getPrevInput(input)
|
172
|
-
|
173
|
-
!prevInput.hasAttribute('focused') && setTimeout(() => {
|
174
|
-
focusElement(prevInput);
|
164
|
+
setTimeout(() => {
|
165
|
+
focusElement(this.getPrevInput(input));
|
175
166
|
});
|
176
|
-
|
177
|
-
this.dispatchInput()
|
178
167
|
} else if (key.match(/^(\d)$/g)) { // if input is a digit
|
179
168
|
input.value = ''; // we are clearing the previous value so we can override it with the new value
|
180
169
|
}
|
181
|
-
|
182
170
|
};
|
183
171
|
})
|
184
172
|
}
|
185
173
|
|
186
174
|
attributeChangedCallback(attrName, oldValue, newValue) {
|
187
|
-
super.attributeChangedCallback(attrName, oldValue, newValue)
|
175
|
+
super.attributeChangedCallback?.(attrName, oldValue, newValue)
|
188
176
|
|
189
177
|
if (oldValue !== newValue) {
|
190
178
|
if (PasscodeInternal.observedAttributes.includes(attrName) && !BaseInputClass.observedAttributes.includes(attrName)) {
|
@@ -3,20 +3,17 @@ import {
|
|
3
3
|
draggableMixin,
|
4
4
|
componentNameValidationMixin
|
5
5
|
} from '../../mixins';
|
6
|
-
import {
|
6
|
+
import { createBaseClass } from '../../baseClasses/createBaseClass';
|
7
7
|
import { compose } from '../../helpers';
|
8
8
|
import { getComponentName } from '../../helpers/componentHelpers';
|
9
9
|
|
10
10
|
export const componentName = getComponentName('text');
|
11
11
|
|
12
|
-
class RawText extends
|
13
|
-
static get componentName() {
|
14
|
-
return componentName;
|
15
|
-
}
|
12
|
+
class RawText extends createBaseClass({ componentName, baseSelector: ':host > slot' }) {
|
16
13
|
constructor() {
|
17
14
|
super();
|
18
|
-
|
19
|
-
|
15
|
+
|
16
|
+
this.attachShadow({ mode: 'open' }).innerHTML = `
|
20
17
|
<style>
|
21
18
|
:host {
|
22
19
|
display: inline-block;
|
@@ -28,11 +25,6 @@ class RawText extends DescopeBaseClass {
|
|
28
25
|
</style>
|
29
26
|
<slot></slot>
|
30
27
|
`;
|
31
|
-
|
32
|
-
this.attachShadow({ mode: 'open' });
|
33
|
-
this.shadowRoot.appendChild(template.content.cloneNode(true));
|
34
|
-
|
35
|
-
this.baseSelector = ':host > slot';
|
36
28
|
}
|
37
29
|
}
|
38
30
|
|
package/src/helpers/index.js
CHANGED
@@ -0,0 +1,18 @@
|
|
1
|
+
// move create event to here
|
2
|
+
|
3
|
+
// usage example:
|
4
|
+
// #dispatchSomething = createDispatchEvent.bind(this, 'something')
|
5
|
+
export function createDispatchEvent(eventName) {
|
6
|
+
this[`on${eventName}`]?.(); // in case we got an event callback as property
|
7
|
+
this.dispatchEvent(new Event(eventName));
|
8
|
+
}
|
9
|
+
|
10
|
+
// usage example:
|
11
|
+
// #removeChangeListener = createEventListener.call(this,'change', this.onChange)
|
12
|
+
export function createEventListener(event, callback) {
|
13
|
+
const boundCallback = callback.bind(this);
|
14
|
+
|
15
|
+
this.addEventListener(event, boundCallback);
|
16
|
+
|
17
|
+
return () => this.removeEventListener(event, boundCallback)
|
18
|
+
}
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import { createDispatchEvent } from "../helpers/mixinsHelpers";
|
2
|
+
|
3
|
+
export const changeMixin = (superclass) => class ChangeMixinClass extends superclass {
|
4
|
+
|
5
|
+
#boundedHandleChange
|
6
|
+
#boundedHandleBlur
|
7
|
+
|
8
|
+
#removeChangeListener
|
9
|
+
#removeBlurListener
|
10
|
+
|
11
|
+
#dispatchChange = createDispatchEvent.bind(this, 'change')
|
12
|
+
|
13
|
+
constructor() {
|
14
|
+
super();
|
15
|
+
|
16
|
+
this.#boundedHandleChange = this.#handleChange.bind(this);
|
17
|
+
this.#boundedHandleBlur = this.#handleBlur.bind(this);
|
18
|
+
}
|
19
|
+
|
20
|
+
#handleChange(e) {
|
21
|
+
// we want to listen only to browser events
|
22
|
+
// and not to events we are dispatching
|
23
|
+
if (e.isTrusted) {
|
24
|
+
// we want to control the change events that dispatched by the component
|
25
|
+
// so we are stopping propagation and handling it in handleBlur
|
26
|
+
e.stopPropagation()
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
#handleBlur() {
|
31
|
+
// on blur, we want to dispatch a change event
|
32
|
+
this.#dispatchChange()
|
33
|
+
}
|
34
|
+
|
35
|
+
connectedCallback() {
|
36
|
+
super.connectedCallback?.();
|
37
|
+
|
38
|
+
this.#removeChangeListener = addEventListener.bind()
|
39
|
+
this.addEventListener('change', this.#boundedHandleChange, true)
|
40
|
+
this.addEventListener('blur', this.#boundedHandleBlur)
|
41
|
+
}
|
42
|
+
|
43
|
+
disconnectedCallback() {
|
44
|
+
this.removeEventListener('change', this.#boundedHandleChange)
|
45
|
+
this.removeEventListener('blur', this.#boundedHandleBlur)
|
46
|
+
}
|
47
|
+
}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
export const componentNameValidationMixin = (superclass) =>
|
2
|
-
class
|
2
|
+
class ComponentNameValidationMixinClass extends superclass {
|
3
3
|
#checkComponentName() {
|
4
4
|
const currentComponentName = this.shadowRoot.host.tagName.toLowerCase();
|
5
5
|
|
@@ -1,7 +1,6 @@
|
|
1
|
-
import {
|
2
|
-
import {
|
1
|
+
import { createBaseClass } from '../baseClasses/createBaseClass';
|
2
|
+
import { isFunction } from '../helpers';
|
3
3
|
import { forwardProps, syncAttrs } from '../helpers/componentHelpers';
|
4
|
-
import { hoverableMixin } from './hoverableMixin';
|
5
4
|
|
6
5
|
export const createProxy = ({
|
7
6
|
componentName,
|
@@ -20,38 +19,39 @@ export const createProxy = ({
|
|
20
19
|
</${wrappedEleName}>
|
21
20
|
`;
|
22
21
|
|
23
|
-
class
|
24
|
-
static get componentName() {
|
25
|
-
return componentName;
|
26
|
-
}
|
27
|
-
|
22
|
+
class ProxyClass extends createBaseClass({ componentName, baseSelector: wrappedEleName }) {
|
28
23
|
constructor() {
|
29
24
|
super().attachShadow({ mode: 'open' }).innerHTML = template;
|
30
25
|
this.hostElement = this.shadowRoot.host;
|
31
|
-
this.baseSelector = wrappedEleName;
|
32
26
|
this.shadowRoot.getElementById('create-proxy').innerHTML =
|
33
|
-
|
27
|
+
isFunction(style) ? style() : style;
|
28
|
+
}
|
29
|
+
|
30
|
+
#boundOnFocus = this.#onFocus.bind(this);
|
31
|
+
|
32
|
+
// we want to focus on the proxy element when focusing our WCP
|
33
|
+
#onFocus() {
|
34
|
+
this.proxyElement.focus();
|
34
35
|
}
|
35
36
|
|
37
|
+
focus = this.#onFocus
|
38
|
+
|
36
39
|
connectedCallback() {
|
37
40
|
if (this.shadowRoot.isConnected) {
|
38
41
|
this.proxyElement = this.shadowRoot.querySelector(wrappedEleName);
|
39
42
|
|
43
|
+
this.addEventListener('focus', this.#boundOnFocus);
|
44
|
+
|
40
45
|
// this is needed for components that uses props, such as combo box
|
41
46
|
forwardProps(this.hostElement, this.proxyElement, includeForwardProps)
|
42
47
|
|
43
|
-
this.setAttribute('tabindex', '0');
|
44
|
-
|
45
|
-
// we want to focus on the proxy element when focusing our WC
|
46
|
-
this.addEventListener('focus', () => {
|
47
|
-
this.proxyElement.focus();
|
48
|
-
});
|
49
|
-
|
50
48
|
// `onkeydown` is set on `proxyElement` support proper tab-index navigation
|
51
49
|
// this support is needed since both proxy host and element catch `focus`/`blur` event
|
52
|
-
// which causes faulty
|
50
|
+
// which causes faulty behavior.
|
51
|
+
// we need this to happen only when the proxy component is in the light DOM,
|
52
|
+
// otherwise it will focus the nested proxy element
|
53
53
|
this.proxyElement.onkeydown = (e) => {
|
54
|
-
if (e.shiftKey && e.keyCode === 9) {
|
54
|
+
if (e.shiftKey && e.keyCode === 9 && this.getRootNode() === document) {
|
55
55
|
this.removeAttribute('tabindex');
|
56
56
|
// We want to defer the action of setting the tab index back
|
57
57
|
// so it will happen after focusing the previous element
|
@@ -59,10 +59,6 @@ export const createProxy = ({
|
|
59
59
|
}
|
60
60
|
};
|
61
61
|
|
62
|
-
// sync events
|
63
|
-
this.addEventListener = (...args) =>
|
64
|
-
this.proxyElement.addEventListener(...args);
|
65
|
-
|
66
62
|
syncAttrs(this.proxyElement, this.hostElement, {
|
67
63
|
excludeAttrs: excludeAttrsSync,
|
68
64
|
includeAttrs: includeAttrsSync
|
@@ -70,16 +66,18 @@ export const createProxy = ({
|
|
70
66
|
}
|
71
67
|
}
|
72
68
|
|
73
|
-
disconnectedCallback() {
|
74
|
-
this.proxyElement.removeEventListener('mouseover', this.mouseoverCbRef);
|
75
|
-
}
|
76
|
-
|
77
69
|
attributeChangedCallback() {
|
78
70
|
if (!this.proxyElement) {
|
79
71
|
return;
|
80
72
|
}
|
81
73
|
}
|
74
|
+
|
75
|
+
disconnectedCallback() {
|
76
|
+
super.disconnectedCallback?.()
|
77
|
+
|
78
|
+
this.removeEventListener('focus', this.#boundOnFocus);
|
79
|
+
}
|
82
80
|
}
|
83
81
|
|
84
|
-
return
|
82
|
+
return ProxyClass;
|
85
83
|
};
|