@descope/web-components-ui 1.0.79 → 1.0.81
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 +1236 -598
- package/dist/index.esm.js.map +1 -1
- package/dist/umd/387.js +1 -0
- package/dist/umd/988.js +1 -1
- package/dist/umd/descope-checkbox-descope-checkbox-internal-index-js.js +1 -0
- package/dist/umd/descope-checkbox-index-js.js +1 -1
- package/dist/umd/descope-new-password-descope-new-password-internal-index-js.js +1 -0
- package/dist/umd/descope-new-password-index-js.js +1 -0
- package/dist/umd/descope-passcode-descope-passcode-internal-index-js.js +1 -1
- package/dist/umd/descope-password-field-index-js.js +1 -1
- package/dist/umd/descope-phone-field-descope-phone-field-internal-index-js.js +1 -1
- package/dist/umd/descope-phone-field-index-js.js +1 -1
- package/dist/umd/index.js +1 -1
- package/package.json +1 -1
- package/src/components/descope-checkbox/Checkbox.js +161 -13
- package/src/components/descope-checkbox/descope-checkbox-internal/CheckboxInternal.js +63 -0
- package/src/components/descope-checkbox/descope-checkbox-internal/index.js +3 -0
- package/src/components/descope-checkbox/index.js +3 -3
- package/src/components/descope-new-password/NewPassword.js +129 -0
- package/src/components/descope-new-password/descope-new-password-internal/NewPasswordInternal.js +189 -0
- package/src/components/descope-new-password/descope-new-password-internal/componentName.js +3 -0
- package/src/components/descope-new-password/descope-new-password-internal/index.js +4 -0
- package/src/components/descope-new-password/index.js +6 -0
- package/src/components/descope-passcode/descope-passcode-internal/PasscodeInternal.js +3 -0
- package/src/components/descope-password-field/PasswordField.js +37 -36
- package/src/components/descope-phone-field/PhoneField.js +3 -2
- package/src/components/descope-phone-field/descope-phone-field-internal/PhoneFieldInternal.js +4 -1
- package/src/components/descope-switch-toggle/SwitchToggle.js +1 -1
- package/src/components/descope-switch-toggle/index.js +1 -1
- package/src/helpers/componentHelpers.js +3 -3
- package/src/index.js +1 -0
- package/src/mixins/createProxy.js +2 -25
- package/src/mixins/inputEventsDispatchingMixin.js +15 -15
- package/src/mixins/inputValidationMixin.js +6 -2
- package/src/mixins/proxyInputMixin.js +6 -36
- package/src/theme/components/checkbox.js +71 -1
- package/src/theme/components/index.js +3 -1
- package/src/theme/components/newPassword.js +48 -0
- package/src/theme/components/passwordField.js +55 -3
- package/src/theme/components/switchToggle.js +4 -4
@@ -0,0 +1,129 @@
|
|
1
|
+
import { forwardAttrs, getComponentName } from '../../helpers/componentHelpers';
|
2
|
+
import { compose } from '../../helpers';
|
3
|
+
import {
|
4
|
+
createStyleMixin,
|
5
|
+
proxyInputMixin,
|
6
|
+
draggableMixin,
|
7
|
+
createProxy,
|
8
|
+
} from '../../mixins';
|
9
|
+
import { componentName as descopeInternalComponentName } from './descope-new-password-internal/componentName';
|
10
|
+
import PasswordField from "../descope-password-field/PasswordField";
|
11
|
+
|
12
|
+
export const componentName = getComponentName('new-password');
|
13
|
+
|
14
|
+
const customMixin = (superclass) =>
|
15
|
+
class NewPasswordMixinClass extends superclass {
|
16
|
+
constructor() {
|
17
|
+
super();
|
18
|
+
}
|
19
|
+
|
20
|
+
init() {
|
21
|
+
super.init?.();
|
22
|
+
|
23
|
+
const template = document.createElement('template');
|
24
|
+
|
25
|
+
template.innerHTML = `
|
26
|
+
<${descopeInternalComponentName}
|
27
|
+
name="new-password"
|
28
|
+
tabindex="-1"
|
29
|
+
slot="input"
|
30
|
+
></${descopeInternalComponentName}>
|
31
|
+
`;
|
32
|
+
|
33
|
+
this.baseElement.appendChild(template.content.cloneNode(true));
|
34
|
+
|
35
|
+
this.inputElement = this.shadowRoot.querySelector(descopeInternalComponentName);
|
36
|
+
|
37
|
+
forwardAttrs(this, this.inputElement, {
|
38
|
+
includeAttrs: [
|
39
|
+
'password-label',
|
40
|
+
'password-placeholder',
|
41
|
+
'confirm-label',
|
42
|
+
'confirm-placeholder',
|
43
|
+
'full-width',
|
44
|
+
'size',
|
45
|
+
'bordered',
|
46
|
+
'label',
|
47
|
+
'has-confirm',
|
48
|
+
'invalid',
|
49
|
+
]
|
50
|
+
});
|
51
|
+
}
|
52
|
+
};
|
53
|
+
|
54
|
+
const { host, internalInputsWrapper } = {
|
55
|
+
host: { selector: () => ':host' },
|
56
|
+
internalInputsWrapper: { selector: 'descope-new-password-internal .wrapper' }
|
57
|
+
}
|
58
|
+
const NewPassword = compose(
|
59
|
+
createStyleMixin({
|
60
|
+
mappings: {
|
61
|
+
componentWidth: { ...host, property: 'width' },
|
62
|
+
requiredContent: { ...host, property: 'content' },
|
63
|
+
inputLabelTextColor: {
|
64
|
+
selector: PasswordField.componentName,
|
65
|
+
property: PasswordField.cssVarList.labelTextColor
|
66
|
+
},
|
67
|
+
inputTextColor: {
|
68
|
+
selector: PasswordField.componentName,
|
69
|
+
property: PasswordField.cssVarList.inputTextColor
|
70
|
+
},
|
71
|
+
inputsGap: {...internalInputsWrapper, property: 'gap'}
|
72
|
+
}
|
73
|
+
}),
|
74
|
+
draggableMixin,
|
75
|
+
proxyInputMixin,
|
76
|
+
customMixin,
|
77
|
+
)(
|
78
|
+
createProxy({
|
79
|
+
slots: [],
|
80
|
+
wrappedEleName: 'vaadin-text-field',
|
81
|
+
style: () => overrides,
|
82
|
+
excludeAttrsSync: ['tabindex'],
|
83
|
+
componentName
|
84
|
+
})
|
85
|
+
);
|
86
|
+
|
87
|
+
const overrides = `
|
88
|
+
:host {
|
89
|
+
--vaadin-field-default-width: auto;
|
90
|
+
display: inline-block;
|
91
|
+
}
|
92
|
+
vaadin-text-field {
|
93
|
+
padding: 0;
|
94
|
+
width: 100%;
|
95
|
+
height: 100%;
|
96
|
+
}
|
97
|
+
vaadin-text-field::part(input-field) {
|
98
|
+
min-height: 0;
|
99
|
+
background: transparent;
|
100
|
+
overflow: hidden;
|
101
|
+
box-shadow: none;
|
102
|
+
}
|
103
|
+
vaadin-text-field::part(input-field)::after {
|
104
|
+
background: transparent;
|
105
|
+
opacity: 0;
|
106
|
+
}
|
107
|
+
descope-new-password-internal {
|
108
|
+
-webkit-mask-image: none;
|
109
|
+
padding: 0;
|
110
|
+
min-height: 0;
|
111
|
+
width: 100%;
|
112
|
+
height: 100%;
|
113
|
+
}
|
114
|
+
descope-new-password-internal > .wrapper {
|
115
|
+
width: 100%;
|
116
|
+
height: 100%;
|
117
|
+
display: flex;
|
118
|
+
flex-direction: column;
|
119
|
+
}
|
120
|
+
descope-password-field {
|
121
|
+
display: block;
|
122
|
+
width: 100%;
|
123
|
+
}
|
124
|
+
descope-new-password-internal vaadin-password-field::before {
|
125
|
+
height: initial;
|
126
|
+
}
|
127
|
+
`;
|
128
|
+
|
129
|
+
export default NewPassword;
|
package/src/components/descope-new-password/descope-new-password-internal/NewPasswordInternal.js
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
import { createBaseInputClass } from '../../../baseClasses/createBaseInputClass';
|
2
|
+
import { observeAttributes } from '../../../helpers/componentHelpers';
|
3
|
+
import NewPassword from '../NewPassword';
|
4
|
+
import { componentName } from './componentName';
|
5
|
+
|
6
|
+
const passwordAttrPrefixRegex = /^password-/
|
7
|
+
const confirmAttrPrefixRegex = /^confirm-/
|
8
|
+
|
9
|
+
const removeAttrPrefix = (attr, prefix) => attr.replace(prefix, '')
|
10
|
+
|
11
|
+
const passwordInputAttrs = ['password-label', 'password-placeholder'];
|
12
|
+
const confirmInputAttrs = ['confirm-label', 'confirm-placeholder'];
|
13
|
+
const commonAttrs = [
|
14
|
+
'disabled',
|
15
|
+
'bordered',
|
16
|
+
'size',
|
17
|
+
'full-width',
|
18
|
+
'maxlength',
|
19
|
+
'invalid',
|
20
|
+
];
|
21
|
+
|
22
|
+
const inputRelatedAttrs = [].concat(commonAttrs, passwordInputAttrs, confirmInputAttrs);
|
23
|
+
|
24
|
+
const BaseInputClass = createBaseInputClass({ componentName, baseSelector: 'div' });
|
25
|
+
|
26
|
+
class NewPasswordInternal extends BaseInputClass {
|
27
|
+
static get observedAttributes() {
|
28
|
+
return ['has-confirm'].concat(
|
29
|
+
BaseInputClass.observedAttributes || [],
|
30
|
+
inputRelatedAttrs,
|
31
|
+
);
|
32
|
+
}
|
33
|
+
|
34
|
+
constructor() {
|
35
|
+
super();
|
36
|
+
|
37
|
+
this.innerHTML = `
|
38
|
+
<div class="wrapper"></div>
|
39
|
+
`;
|
40
|
+
|
41
|
+
this.wrapperEle = this.querySelector('.wrapper')
|
42
|
+
}
|
43
|
+
|
44
|
+
get value() {
|
45
|
+
return this.passwordInput?.value || '';
|
46
|
+
}
|
47
|
+
|
48
|
+
set value(val) {
|
49
|
+
if (val === this.value) return;
|
50
|
+
this.value = val;
|
51
|
+
}
|
52
|
+
|
53
|
+
get hasConfirm() {
|
54
|
+
return this.getAttribute('has-confirm') === 'true';
|
55
|
+
}
|
56
|
+
|
57
|
+
getValidity() {
|
58
|
+
if (this.isRequired && !this.value) {
|
59
|
+
return { valueMissing: true };
|
60
|
+
}
|
61
|
+
if (this.hasConfirm && this.confirmInput && this.value !== this.confirmInput.value) {
|
62
|
+
return { patternMismatch: true };
|
63
|
+
}
|
64
|
+
|
65
|
+
const min = this.getAttribute('minlength');
|
66
|
+
const minVal = parseInt(min, 10) || 0;
|
67
|
+
const minValid = this.value.length >= minVal;
|
68
|
+
if (!minValid) {
|
69
|
+
return { tooShort: true }
|
70
|
+
}
|
71
|
+
|
72
|
+
return {}
|
73
|
+
};
|
74
|
+
|
75
|
+
init() {
|
76
|
+
this.addEventListener('focus', (e) => {
|
77
|
+
// we want to ignore focus events we are dispatching
|
78
|
+
if (e.isTrusted) {
|
79
|
+
this.passwordInput.focus()
|
80
|
+
}
|
81
|
+
});
|
82
|
+
|
83
|
+
super.init();
|
84
|
+
this.renderInputs(this.hasConfirm);
|
85
|
+
}
|
86
|
+
|
87
|
+
renderInputs(shouldRenderConfirm) {
|
88
|
+
let template = `<descope-password-field data-id="password"></descope-password-field>`;
|
89
|
+
|
90
|
+
if (shouldRenderConfirm) {
|
91
|
+
template += `<descope-password-field data-id="confirm"></descope-password-field>`
|
92
|
+
}
|
93
|
+
|
94
|
+
this.wrapperEle.innerHTML = template;
|
95
|
+
|
96
|
+
this.passwordInput = this.querySelector('[data-id="password"]');
|
97
|
+
this.confirmInput = this.querySelector('[data-id="confirm"]');
|
98
|
+
|
99
|
+
this.inputs = [this.passwordInput, this.confirmInput];
|
100
|
+
|
101
|
+
this.initInputs();
|
102
|
+
|
103
|
+
// we are calling attributeChangedCallback with all the input related attributes
|
104
|
+
// in order to set it on the newly generated input
|
105
|
+
[...passwordInputAttrs, ...confirmInputAttrs, ...commonAttrs].forEach(attr => {
|
106
|
+
this.attributeChangedCallback(attr, null, this.getAttribute(attr))
|
107
|
+
})
|
108
|
+
}
|
109
|
+
|
110
|
+
// the inputs are not required but we still want it to have a required
|
111
|
+
// indicator in case the root component is required
|
112
|
+
handleIndicatorStyle() {
|
113
|
+
for (const input of this.inputs) {
|
114
|
+
const styleTag = document.createElement('style');
|
115
|
+
styleTag.innerHTML = `
|
116
|
+
:host::part(required-indicator)::after {
|
117
|
+
content: var(${NewPassword.cssVarList.requiredContent});
|
118
|
+
}
|
119
|
+
`;
|
120
|
+
input?.shadowRoot.appendChild(styleTag);
|
121
|
+
}
|
122
|
+
}
|
123
|
+
|
124
|
+
get isInvalid() {
|
125
|
+
return this.hasAttribute('invalid') && this.getAttribute('invalid') !== 'false'
|
126
|
+
}
|
127
|
+
|
128
|
+
// for some reason, Vaadin is removing the invalid attribute on several events,
|
129
|
+
// e.g. focus, input, etc..., so we need to make sure the inputs will stay invalid
|
130
|
+
// if the root component is invalid
|
131
|
+
handleInputsInvalidAttribute() {
|
132
|
+
this.inputs.forEach(input => {
|
133
|
+
input && observeAttributes(input, (changedAttributes) => {
|
134
|
+
if (changedAttributes.includes('invalid')) {
|
135
|
+
const inputInvalidValue = input.getAttribute('invalid')
|
136
|
+
const rootInvalidValue = this.getAttribute('invalid')
|
137
|
+
|
138
|
+
if (this.isInvalid && rootInvalidValue !== inputInvalidValue) {
|
139
|
+
input.setAttribute('invalid', 'true')
|
140
|
+
}
|
141
|
+
}
|
142
|
+
}, {});
|
143
|
+
})
|
144
|
+
}
|
145
|
+
|
146
|
+
initInputs() {
|
147
|
+
this.handleIndicatorStyle()
|
148
|
+
this.handleInputsInvalidAttribute();
|
149
|
+
this.handleFocusEventsDispatching(this.inputs)
|
150
|
+
}
|
151
|
+
|
152
|
+
toggleBooleanAttribute(ele, name, value) {
|
153
|
+
value === null ?
|
154
|
+
ele?.removeAttribute(name) :
|
155
|
+
ele?.setAttribute(name, value)
|
156
|
+
}
|
157
|
+
|
158
|
+
attributeChangedCallback(attrName, oldValue, newValue) {
|
159
|
+
super.attributeChangedCallback?.(attrName, oldValue, newValue);
|
160
|
+
|
161
|
+
if (oldValue !== newValue) {
|
162
|
+
if (attrName === 'has-confirm') {
|
163
|
+
this.renderInputs(newValue !== null && newValue !== 'false');
|
164
|
+
}
|
165
|
+
else if (commonAttrs.includes(attrName)) {
|
166
|
+
this.inputs.forEach(
|
167
|
+
(input) => this.toggleBooleanAttribute(input, attrName, newValue)
|
168
|
+
);
|
169
|
+
}
|
170
|
+
else if (passwordInputAttrs.includes(attrName)) {
|
171
|
+
this.toggleBooleanAttribute(
|
172
|
+
this.passwordInput,
|
173
|
+
removeAttrPrefix(attrName, passwordAttrPrefixRegex),
|
174
|
+
newValue
|
175
|
+
);
|
176
|
+
}
|
177
|
+
else if (confirmInputAttrs.includes(attrName)) {
|
178
|
+
this.toggleBooleanAttribute(
|
179
|
+
this.confirmInput,
|
180
|
+
removeAttrPrefix(attrName, confirmAttrPrefixRegex),
|
181
|
+
newValue
|
182
|
+
);
|
183
|
+
|
184
|
+
}
|
185
|
+
}
|
186
|
+
}
|
187
|
+
}
|
188
|
+
|
189
|
+
export default NewPasswordInternal;
|
@@ -172,6 +172,9 @@ class PasscodeInternal extends BaseInputClass {
|
|
172
172
|
|
173
173
|
forwardAttrs(this, input, { includeAttrs: forwardAttributes })
|
174
174
|
})
|
175
|
+
|
176
|
+
this.handleFocusEventsDispatching(this.inputs);
|
177
|
+
this.handleInputEventDispatching();
|
175
178
|
}
|
176
179
|
|
177
180
|
attributeChangedCallback(attrName, oldValue, newValue) {
|
@@ -5,7 +5,6 @@ import {
|
|
5
5
|
proxyInputMixin,
|
6
6
|
componentNameValidationMixin
|
7
7
|
} from '../../mixins';
|
8
|
-
import textFieldMappings from '../descope-text-field/textFieldMappings';
|
9
8
|
import { compose } from '../../helpers';
|
10
9
|
import { getComponentName } from '../../helpers/componentHelpers';
|
11
10
|
|
@@ -13,16 +12,37 @@ export const componentName = getComponentName('password-field');
|
|
13
12
|
|
14
13
|
let overrides = ``;
|
15
14
|
|
15
|
+
const { host, inputWrapper, inputElement, inputElementPlaceholder, revealButton, label, requiredIndicator } = {
|
16
|
+
host: () => ':host',
|
17
|
+
inputWrapper: { selector: '::part(input-field)' },
|
18
|
+
inputElement: { selector: '> input' },
|
19
|
+
inputElementPlaceholder: { selector: '> input:placeholder-shown' },
|
20
|
+
revealButton: { selector: 'vaadin-password-field-button' },
|
21
|
+
label: { selector: '> label' },
|
22
|
+
requiredIndicator: { selector: '::part(required-indicator)::after' },
|
23
|
+
}
|
24
|
+
|
16
25
|
const PasswordField = compose(
|
17
26
|
createStyleMixin({
|
18
27
|
mappings: {
|
19
|
-
...
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
}
|
25
|
-
|
28
|
+
wrapperBorderStyle: { ...inputWrapper, property: 'border-style' },
|
29
|
+
wrapperBorderWidth: { ...inputWrapper, property: 'border-width' },
|
30
|
+
wrapperBorderColor: { ...inputWrapper, property: 'border-color' },
|
31
|
+
wrapperBorderRadius: { ...inputWrapper, property: 'border-radius' },
|
32
|
+
labelTextColor: [
|
33
|
+
{ ...label, property: 'color' },
|
34
|
+
{ ...requiredIndicator, property: 'color' }
|
35
|
+
],
|
36
|
+
inputTextColor: [{ ...inputElement, property: 'color' }],
|
37
|
+
placeholderTextColor: { ...inputElementPlaceholder, property: 'color' },
|
38
|
+
fontSize: [{}, host],
|
39
|
+
height: inputWrapper,
|
40
|
+
padding: inputWrapper,
|
41
|
+
pointerCursor: [
|
42
|
+
{ ...revealButton, property: 'cursor' },
|
43
|
+
{ ...label, property: 'cursor' },
|
44
|
+
{ ...requiredIndicator, property: 'cursor' }
|
45
|
+
],
|
26
46
|
}
|
27
47
|
}),
|
28
48
|
draggableMixin,
|
@@ -43,38 +63,19 @@ overrides = `
|
|
43
63
|
display: inline-block;
|
44
64
|
}
|
45
65
|
vaadin-password-field {
|
46
|
-
|
47
|
-
padding: 0;
|
48
|
-
}
|
49
|
-
vaadin-password-field::part(input-field) {
|
50
|
-
overflow: hidden;
|
66
|
+
width: 100%;
|
51
67
|
}
|
52
|
-
vaadin-password-field
|
53
|
-
|
68
|
+
vaadin-password-field > input {
|
69
|
+
min-height: 0;
|
54
70
|
}
|
55
|
-
vaadin-password-field input:-webkit-autofill,
|
56
|
-
vaadin-password-field input:-webkit-autofill::first-line,
|
57
|
-
vaadin-password-field input:-webkit-autofill:hover,
|
58
|
-
vaadin-password-field input:-webkit-autofill:active,
|
59
|
-
vaadin-password-field input:-webkit-autofill:focus {
|
60
|
-
-webkit-text-fill-color: var(${PasswordField.cssVarList.color});
|
61
|
-
box-shadow: 0 0 0 var(${PasswordField.cssVarList.height}) var(${PasswordField.cssVarList.backgroundColor}) inset;
|
62
|
-
}
|
63
|
-
vaadin-password-field > label,
|
64
71
|
vaadin-password-field::part(input-field) {
|
65
|
-
|
66
|
-
color: var(${PasswordField.cssVarList.color});
|
67
|
-
}
|
68
|
-
vaadin-password-field::part(input-field):focus {
|
69
|
-
cursor: text;
|
70
|
-
}
|
71
|
-
vaadin-password-field[required]::part(required-indicator)::after {
|
72
|
-
font-size: "12px";
|
73
|
-
content: "*";
|
74
|
-
color: var(${PasswordField.cssVarList.color});
|
72
|
+
background: transparent;
|
75
73
|
}
|
76
|
-
vaadin-password-field
|
77
|
-
|
74
|
+
vaadin-password-field::part(input-field)::after {
|
75
|
+
opacity: 0;
|
76
|
+
}
|
77
|
+
vaadin-password-field::before {
|
78
|
+
height: initial;
|
78
79
|
}
|
79
80
|
`;
|
80
81
|
|
@@ -7,7 +7,6 @@ import {
|
|
7
7
|
draggableMixin,
|
8
8
|
proxyInputMixin
|
9
9
|
} from '../../mixins';
|
10
|
-
|
11
10
|
import TextField from '../descope-text-field/TextField';
|
12
11
|
import ComboBox from '../descope-combo-box/ComboBox';
|
13
12
|
|
@@ -54,6 +53,7 @@ const customMixin = (superclass) =>
|
|
54
53
|
};
|
55
54
|
|
56
55
|
const {
|
56
|
+
host,
|
57
57
|
inputWrapper,
|
58
58
|
countryCodeInput,
|
59
59
|
phoneInput,
|
@@ -61,6 +61,7 @@ const {
|
|
61
61
|
requiredIndicator,
|
62
62
|
separator
|
63
63
|
} = {
|
64
|
+
host: { selector: () => ':host' },
|
64
65
|
inputWrapper: { selector: '::part(input-field)' },
|
65
66
|
phoneInput: { selector: () => 'descope-text-field' },
|
66
67
|
countryCodeInput: { selector: () => 'descope-combo-box' },
|
@@ -72,7 +73,7 @@ const {
|
|
72
73
|
const PhoneField = compose(
|
73
74
|
createStyleMixin({
|
74
75
|
mappings: {
|
75
|
-
componentWidth: {
|
76
|
+
componentWidth: { ...host, property: 'width' },
|
76
77
|
|
77
78
|
wrapperBorderStyle: [
|
78
79
|
{ ...inputWrapper, property: 'border-style' },
|
package/src/components/descope-phone-field/descope-phone-field-internal/PhoneFieldInternal.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { createBaseInputClass } from '../../../baseClasses/createBaseInputClass';
|
2
|
-
import { getComponentName
|
2
|
+
import { getComponentName } from '../../../helpers/componentHelpers';
|
3
3
|
import CountryCodes from '../CountryCodes';
|
4
4
|
import { comboBoxItem } from '../helpers';
|
5
5
|
|
@@ -160,6 +160,9 @@ class PhoneFieldInternal extends BaseInputClass {
|
|
160
160
|
.join('');
|
161
161
|
e.target.value = sanitizedInput;
|
162
162
|
});
|
163
|
+
|
164
|
+
this.handleFocusEventsDispatching(this.inputs)
|
165
|
+
this.handleInputEventDispatching()
|
163
166
|
}
|
164
167
|
|
165
168
|
attributeChangedCallback(attrName, oldValue, newValue) {
|
@@ -89,13 +89,13 @@ export const forwardProps = (src, target, props = []) => {
|
|
89
89
|
const config = props.reduce((acc, prop) => Object.assign(acc, {
|
90
90
|
[prop]: {
|
91
91
|
get() {
|
92
|
-
return
|
92
|
+
return src[prop]
|
93
93
|
},
|
94
94
|
set(v) {
|
95
|
-
|
95
|
+
src[prop] = v
|
96
96
|
}
|
97
97
|
}
|
98
98
|
}), {})
|
99
99
|
|
100
|
-
Object.defineProperties(
|
100
|
+
Object.defineProperties(target, config)
|
101
101
|
}
|
package/src/index.js
CHANGED
@@ -17,7 +17,7 @@ export const createProxy = ({
|
|
17
17
|
#dispatchFocus = createDispatchEvent.bind(this, 'focus')
|
18
18
|
|
19
19
|
constructor() {
|
20
|
-
super().attachShadow({ mode: 'open' }).innerHTML = `
|
20
|
+
super().attachShadow({ mode: 'open', delegatesFocus: true }).innerHTML = `
|
21
21
|
<style id="create-proxy">${isFunction(style) ? style() : style
|
22
22
|
}</style>
|
23
23
|
<${wrappedEleName}>
|
@@ -27,8 +27,6 @@ export const createProxy = ({
|
|
27
27
|
`;
|
28
28
|
}
|
29
29
|
|
30
|
-
focus = () => this.baseElement.focus()
|
31
|
-
|
32
30
|
init() {
|
33
31
|
super.init?.();
|
34
32
|
|
@@ -40,29 +38,8 @@ export const createProxy = ({
|
|
40
38
|
this.#dispatchFocus()
|
41
39
|
})
|
42
40
|
|
43
|
-
this.addEventListener('focus', (e) => {
|
44
|
-
// if we got a focus event we want to focus the proxy element
|
45
|
-
if (e.isTrusted) {
|
46
|
-
this.focus();
|
47
|
-
}
|
48
|
-
})
|
49
|
-
|
50
41
|
// this is needed for components that uses props, such as combo box
|
51
|
-
forwardProps(this, this
|
52
|
-
|
53
|
-
// `onkeydown` is set on `baseElement` support proper tab-index navigation
|
54
|
-
// this support is needed since both proxy host and element catch `focus`/`blur` event
|
55
|
-
// which causes faulty behavior.
|
56
|
-
// we need this to happen only when the proxy component is in the light DOM,
|
57
|
-
// otherwise it will focus the nested proxy element
|
58
|
-
this.baseElement.onkeydown = (e) => {
|
59
|
-
if (e.shiftKey && e.keyCode === 9 && this.getRootNode() === document) {
|
60
|
-
this.removeAttribute('tabindex');
|
61
|
-
// We want to defer the action of setting the tab index back
|
62
|
-
// so it will happen after focusing the previous element
|
63
|
-
setTimeout(() => this.setAttribute('tabindex', '0'), 0);
|
64
|
-
}
|
65
|
-
};
|
42
|
+
forwardProps(this.baseElement, this, includeForwardProps);
|
66
43
|
|
67
44
|
syncAttrs(this.baseElement, this, {
|
68
45
|
excludeAttrs: excludeAttrsSync,
|
@@ -3,8 +3,6 @@ import { createDispatchEvent } from "../helpers/mixinsHelpers";
|
|
3
3
|
export const inputEventsDispatchingMixin = (superclass) => class InputEventsDispatchingMixinClass extends superclass {
|
4
4
|
init() {
|
5
5
|
this.#blockNativeEvents();
|
6
|
-
this.#handleFocusEventsDispatching();
|
7
|
-
this.#handleInputEventDispatching();
|
8
6
|
|
9
7
|
super.init?.();
|
10
8
|
}
|
@@ -18,29 +16,28 @@ export const inputEventsDispatchingMixin = (superclass) => class InputEventsDisp
|
|
18
16
|
})
|
19
17
|
}
|
20
18
|
|
21
|
-
|
19
|
+
handleFocusEventsDispatching(inputs) {
|
22
20
|
let timerId
|
23
21
|
|
24
22
|
// in order to simulate blur & focusout on root input element
|
25
23
|
// we are checking if focus on one of the inner elements happened immediately after blur
|
26
24
|
// if not, the component is no longer focused and we should dispatch blur & focusout
|
27
|
-
|
28
|
-
|
25
|
+
inputs?.forEach(input => {
|
26
|
+
input?.addEventListener('focusout', (e) => {
|
29
27
|
e.stopImmediatePropagation();
|
30
28
|
timerId = setTimeout(() => {
|
31
29
|
timerId = null
|
30
|
+
|
32
31
|
createDispatchEvent.call(this, 'blur');
|
33
32
|
createDispatchEvent.call(this, 'focusout', { bubbles: true });
|
34
33
|
});
|
35
|
-
}
|
36
|
-
})
|
34
|
+
})
|
37
35
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
if (e.isTrusted) {
|
36
|
+
// in order to simulate focus & focusin on the root input element
|
37
|
+
// we are holding a timer id and clearing it on focusin
|
38
|
+
// if there is a timer id, it means that the root input is still focused
|
39
|
+
// otherwise, it was not focused before, and we should dispatch focus & focusin
|
40
|
+
const onFocus = (e) => {
|
44
41
|
e.stopImmediatePropagation();
|
45
42
|
clearTimeout(timerId)
|
46
43
|
if (!timerId) {
|
@@ -48,13 +45,17 @@ export const inputEventsDispatchingMixin = (superclass) => class InputEventsDisp
|
|
48
45
|
createDispatchEvent.call(this, 'focusin', { bubbles: true });
|
49
46
|
}
|
50
47
|
}
|
48
|
+
|
49
|
+
// some components are not dispatching focusin but only focus
|
50
|
+
input?.addEventListener('focusin', onFocus)
|
51
|
+
input?.addEventListener('focus', onFocus)
|
51
52
|
})
|
52
53
|
}
|
53
54
|
|
54
55
|
// we want to block the input events from propagating in case the value of the root input wasn't change
|
55
56
|
// this can happen if we are sanitizing characters on the internal inputs and do not want it to affect the root input element value
|
56
57
|
// in this case, on each input event, we are comparing the root input value to the previous one, and only if it does not match, we are allowing the input event to propagate
|
57
|
-
|
58
|
+
handleInputEventDispatching() {
|
58
59
|
let previousRootComponentValue = this.value
|
59
60
|
|
60
61
|
this.addEventListener('input', (e) => {
|
@@ -66,6 +67,5 @@ export const inputEventsDispatchingMixin = (superclass) => class InputEventsDisp
|
|
66
67
|
previousRootComponentValue = this.value
|
67
68
|
}
|
68
69
|
});
|
69
|
-
|
70
70
|
}
|
71
71
|
}
|
@@ -68,7 +68,7 @@ export const inputValidationMixin = (superclass) => class InputValidationMixinCl
|
|
68
68
|
|
69
69
|
#setValidity() {
|
70
70
|
const validity = this.getValidity()
|
71
|
-
this.#internals.setValidity(validity, this.getErrorMessage(validity))
|
71
|
+
this.#internals.setValidity(validity, this.getErrorMessage(validity), this.validationTarget)
|
72
72
|
}
|
73
73
|
|
74
74
|
get validationMessage() {
|
@@ -91,9 +91,13 @@ export const inputValidationMixin = (superclass) => class InputValidationMixinCl
|
|
91
91
|
return this.#internals.validity
|
92
92
|
}
|
93
93
|
|
94
|
+
get validationTarget () {
|
95
|
+
return this.inputElement
|
96
|
+
}
|
97
|
+
|
94
98
|
setCustomValidity(errorMessage) {
|
95
99
|
if (errorMessage) {
|
96
|
-
this.#internals.setValidity({ customError: true }, errorMessage)
|
100
|
+
this.#internals.setValidity({ customError: true }, errorMessage, this.validationTarget)
|
97
101
|
} else {
|
98
102
|
this.#internals.setValidity({})
|
99
103
|
this.#setValidity()
|