@descope/web-components-ui 1.0.74 → 1.0.75
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/index.esm.js +2424 -621
- package/dist/index.esm.js.map +1 -1
- package/dist/umd/447.js +1 -1
- package/dist/umd/744.js +1 -1
- package/dist/umd/878.js +1 -0
- package/dist/umd/descope-combo-box-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-phone-field-descope-phone-field-internal-index-js.js +1 -0
- package/dist/umd/descope-phone-field-index-js.js +1 -0
- package/dist/umd/descope-text-field-index-js.js +1 -1
- package/dist/umd/index.js +1 -1
- package/package.json +1 -1
- package/src/components/descope-combo-box/ComboBox.js +85 -50
- package/src/components/descope-passcode/Passcode.js +2 -5
- package/src/components/descope-phone-field/CountryCodes.js +1212 -0
- package/src/components/descope-phone-field/PhoneField.js +179 -0
- package/src/components/descope-phone-field/descope-phone-field-internal/PhoneFieldInternal.js +213 -0
- package/src/components/descope-phone-field/descope-phone-field-internal/index.js +6 -0
- package/src/components/descope-phone-field/helpers.js +23 -0
- package/src/components/descope-phone-field/index.js +9 -0
- package/src/components/descope-text-field/TextField.js +31 -3
- package/src/components/descope-text-field/textFieldMappings.js +4 -1
- package/src/index.js +1 -0
- package/src/mixins/inputValidationMixin.js +21 -2
- package/src/mixins/portalMixin.js +8 -3
- package/src/theme/components/comboBox.js +39 -9
- package/src/theme/components/index.js +3 -1
- package/src/theme/components/phoneField.js +74 -0
- package/src/theme/components/textField.js +2 -2
@@ -0,0 +1,179 @@
|
|
1
|
+
import { componentName as descopeInternalComponentName } from './descope-phone-field-internal/PhoneFieldInternal';
|
2
|
+
import { forwardAttrs, getComponentName } from '../../helpers/componentHelpers';
|
3
|
+
import { compose } from '../../helpers';
|
4
|
+
import {
|
5
|
+
createProxy,
|
6
|
+
createStyleMixin,
|
7
|
+
draggableMixin,
|
8
|
+
proxyInputMixin
|
9
|
+
} from '../../mixins';
|
10
|
+
|
11
|
+
import TextField from '../descope-text-field/TextField';
|
12
|
+
import ComboBox from '../descope-combo-box/ComboBox';
|
13
|
+
|
14
|
+
const textVars = TextField.cssVarList;
|
15
|
+
const comboVars = ComboBox.cssVarList;
|
16
|
+
|
17
|
+
export const componentName = getComponentName('phone-field');
|
18
|
+
|
19
|
+
const customMixin = (superclass) =>
|
20
|
+
class PhoneFieldClass extends superclass {
|
21
|
+
constructor() {
|
22
|
+
super();
|
23
|
+
}
|
24
|
+
|
25
|
+
init() {
|
26
|
+
super.init?.();
|
27
|
+
|
28
|
+
const template = document.createElement('template');
|
29
|
+
|
30
|
+
template.innerHTML = `
|
31
|
+
<${descopeInternalComponentName}
|
32
|
+
tabindex="-1"
|
33
|
+
slot="input"
|
34
|
+
></${descopeInternalComponentName}>
|
35
|
+
`;
|
36
|
+
|
37
|
+
this.baseElement.appendChild(template.content.cloneNode(true));
|
38
|
+
|
39
|
+
this.inputElement = this.shadowRoot.querySelector(descopeInternalComponentName);
|
40
|
+
|
41
|
+
forwardAttrs(this.shadowRoot.host, this.inputElement, {
|
42
|
+
includeAttrs: [
|
43
|
+
'size',
|
44
|
+
'bordered',
|
45
|
+
'invalid',
|
46
|
+
'minlength',
|
47
|
+
'maxlength',
|
48
|
+
'default-code',
|
49
|
+
'country-input-placeholder',
|
50
|
+
'phone-input-placeholder',
|
51
|
+
]
|
52
|
+
});
|
53
|
+
}
|
54
|
+
};
|
55
|
+
|
56
|
+
const {
|
57
|
+
inputWrapper,
|
58
|
+
countryCodeInput,
|
59
|
+
phoneInput,
|
60
|
+
label,
|
61
|
+
requiredIndicator,
|
62
|
+
separator
|
63
|
+
} = {
|
64
|
+
inputWrapper: { selector: '::part(input-field)' },
|
65
|
+
phoneInput: { selector: () => 'descope-text-field' },
|
66
|
+
countryCodeInput: { selector: () => 'descope-combo-box' },
|
67
|
+
label: { selector: '> label' },
|
68
|
+
requiredIndicator: { selector: '[required]::part(required-indicator)::after' },
|
69
|
+
separator: { selector: 'descope-phone-field-internal .separator' }
|
70
|
+
};
|
71
|
+
|
72
|
+
const PhoneField = compose(
|
73
|
+
createStyleMixin({
|
74
|
+
mappings: {
|
75
|
+
componentWidth: { selector: () => ':host', property: 'width' },
|
76
|
+
|
77
|
+
wrapperBorderStyle: [
|
78
|
+
{ ...inputWrapper, property: 'border-style' },
|
79
|
+
{ ...separator, property: 'border-left-style' }
|
80
|
+
],
|
81
|
+
wrapperBorderWidth: [
|
82
|
+
{ ...inputWrapper, property: 'border-width' },
|
83
|
+
{ ...separator, property: 'border-left-width' }
|
84
|
+
],
|
85
|
+
wrapperBorderColor: [
|
86
|
+
{ ...inputWrapper, property: 'border-color' },
|
87
|
+
{ ...separator, property: 'border-left-color' }
|
88
|
+
],
|
89
|
+
wrapperBorderRadius: { ...inputWrapper, property: 'border-radius' },
|
90
|
+
|
91
|
+
inputHeight: { ...inputWrapper, property: 'height' },
|
92
|
+
|
93
|
+
countryCodeInputWidth: { ...countryCodeInput, property: comboVars.width },
|
94
|
+
countryCodeDropdownWidth: {
|
95
|
+
...countryCodeInput,
|
96
|
+
property: '--vaadin-combo-box-overlay-width'
|
97
|
+
},
|
98
|
+
|
99
|
+
phoneInputWidth: { ...phoneInput, property: 'width' },
|
100
|
+
|
101
|
+
color: [label, requiredIndicator],
|
102
|
+
|
103
|
+
placeholderColor: {
|
104
|
+
...phoneInput,
|
105
|
+
property: textVars.placeholderColor
|
106
|
+
},
|
107
|
+
|
108
|
+
overlayItemBackgroundColor: {
|
109
|
+
selector: 'descope-combo-box',
|
110
|
+
property: comboVars.overlayItemBackgroundColor
|
111
|
+
},
|
112
|
+
},
|
113
|
+
}),
|
114
|
+
draggableMixin,
|
115
|
+
proxyInputMixin,
|
116
|
+
customMixin,
|
117
|
+
)(
|
118
|
+
createProxy({
|
119
|
+
slots: [],
|
120
|
+
wrappedEleName: 'vaadin-text-field',
|
121
|
+
style: () => `
|
122
|
+
:host {
|
123
|
+
--vaadin-field-default-width: auto;
|
124
|
+
display: inline-block;
|
125
|
+
}
|
126
|
+
div {
|
127
|
+
display: inline-flex;
|
128
|
+
}
|
129
|
+
vaadin-text-field {
|
130
|
+
padding: 0;
|
131
|
+
width: 100%;
|
132
|
+
height: 100%;
|
133
|
+
}
|
134
|
+
vaadin-text-field::part(input-field) {
|
135
|
+
padding: 0;
|
136
|
+
min-height: 0;
|
137
|
+
background: transparent;
|
138
|
+
overflow: hidden;
|
139
|
+
}
|
140
|
+
descope-phone-field-internal {
|
141
|
+
-webkit-mask-image: none;
|
142
|
+
padding: 0;
|
143
|
+
min-height: 0;
|
144
|
+
width: 100%;
|
145
|
+
height: 100%;
|
146
|
+
}
|
147
|
+
descope-phone-field-internal > div {
|
148
|
+
width: 100%;
|
149
|
+
height: 100%;
|
150
|
+
}
|
151
|
+
descope-phone-field-internal .separator {
|
152
|
+
flex: 0;
|
153
|
+
border: none;
|
154
|
+
}
|
155
|
+
descope-combo-box {
|
156
|
+
flex-shrink: 0;
|
157
|
+
height: 100%;
|
158
|
+
${comboVars.borderWidth}: 0;
|
159
|
+
}
|
160
|
+
descope-text-field {
|
161
|
+
flex-grow: 1;
|
162
|
+
min-height: 0;
|
163
|
+
height: 100%;
|
164
|
+
${textVars.borderWidth}: 0;
|
165
|
+
${textVars.borderRadius}: 0;
|
166
|
+
}
|
167
|
+
vaadin-text-field[required]::part(required-indicator)::after {
|
168
|
+
content: "*";
|
169
|
+
}
|
170
|
+
vaadin-text-field[readonly] > input:placeholder-shown {
|
171
|
+
opacity: 1;
|
172
|
+
}
|
173
|
+
`,
|
174
|
+
excludeAttrsSync: ['tabindex'],
|
175
|
+
componentName
|
176
|
+
})
|
177
|
+
);
|
178
|
+
|
179
|
+
export default PhoneField;
|
@@ -0,0 +1,213 @@
|
|
1
|
+
import { createBaseInputClass } from '../../../baseClasses/createBaseInputClass';
|
2
|
+
import { getComponentName } from '../../../helpers/componentHelpers';
|
3
|
+
import { createDispatchEvent } from '../../../helpers/mixinsHelpers';
|
4
|
+
import CountryCodes from '../CountryCodes';
|
5
|
+
import { comboBoxItem } from '../helpers';
|
6
|
+
|
7
|
+
export const componentName = getComponentName('phone-field-internal');
|
8
|
+
|
9
|
+
const commonAttrs = [
|
10
|
+
'disabled',
|
11
|
+
'size',
|
12
|
+
'bordered',
|
13
|
+
'invalid',
|
14
|
+
];
|
15
|
+
const countryAttrs = ['country-input-placeholder', 'default-code'];
|
16
|
+
const phoneAttrs = ['phone-input-placeholder', 'maxlength'];
|
17
|
+
const mapAttrs = {
|
18
|
+
'country-input-placeholder': 'placeholder',
|
19
|
+
'phone-input-placeholder': 'placeholder',
|
20
|
+
}
|
21
|
+
|
22
|
+
const inputRelatedAttrs = [].concat(commonAttrs, countryAttrs, phoneAttrs);
|
23
|
+
|
24
|
+
const BaseInputClass = createBaseInputClass({ componentName, baseSelector: 'div' });
|
25
|
+
|
26
|
+
class PhoneFieldInternal extends BaseInputClass {
|
27
|
+
static get observedAttributes() {
|
28
|
+
return [].concat(
|
29
|
+
BaseInputClass.observedAttributes || [],
|
30
|
+
inputRelatedAttrs,
|
31
|
+
);
|
32
|
+
}
|
33
|
+
|
34
|
+
#dispatchBlur = createDispatchEvent.bind(this, 'blur');
|
35
|
+
#dispatchFocus = createDispatchEvent.bind(this, 'focus');
|
36
|
+
|
37
|
+
constructor() {
|
38
|
+
super();
|
39
|
+
|
40
|
+
this.innerHTML = `
|
41
|
+
<div>
|
42
|
+
<descope-combo-box
|
43
|
+
item-label-path="data-name"
|
44
|
+
item-value-path="data-id"
|
45
|
+
>
|
46
|
+
${CountryCodes.map(item => comboBoxItem(item)).join('')}
|
47
|
+
</descope-combo-box>
|
48
|
+
<div class="separator"></div>
|
49
|
+
<descope-text-field type="tel"></descope-text-field>
|
50
|
+
</div>`;
|
51
|
+
|
52
|
+
this.countryCodeInput = this.querySelector('descope-combo-box');
|
53
|
+
this.phoneNumberInput = this.querySelector('descope-text-field');
|
54
|
+
this.inputs = [
|
55
|
+
this.countryCodeInput,
|
56
|
+
this.phoneNumberInput
|
57
|
+
];
|
58
|
+
}
|
59
|
+
|
60
|
+
get value() {
|
61
|
+
return this.inputs.map(({ value }) => value).join('-');
|
62
|
+
}
|
63
|
+
|
64
|
+
set value(val) {
|
65
|
+
const [countryCode = '', phoneNumber = ''] = val.split('-');
|
66
|
+
this.countryCodeInput.value = countryCode;
|
67
|
+
this.phoneNumberInput.value = phoneNumber;
|
68
|
+
}
|
69
|
+
|
70
|
+
get phoneNumberValue() {
|
71
|
+
return this.phoneNumberInput.value;
|
72
|
+
}
|
73
|
+
|
74
|
+
get countryCodeValue() {
|
75
|
+
return this.countryCodeInput.shadowRoot.querySelector('input').value;
|
76
|
+
}
|
77
|
+
|
78
|
+
get minLength() {
|
79
|
+
return parseInt(this.getAttribute('minlength'), 10) || 0;
|
80
|
+
}
|
81
|
+
|
82
|
+
getValidity() {
|
83
|
+
const hasCode = this.countryCodeInput.value;
|
84
|
+
const hasPhone = this.phoneNumberInput.value;
|
85
|
+
const emptyValue = !hasCode || !hasPhone;
|
86
|
+
const hasMinPhoneLength = this.phoneNumberInput.value.length && this.phoneNumberInput.value.length < this.minLength;
|
87
|
+
|
88
|
+
if (this.isRequired && emptyValue) {
|
89
|
+
return { valueMissing: true };
|
90
|
+
}
|
91
|
+
if (hasMinPhoneLength) {
|
92
|
+
return { tooShort: true };
|
93
|
+
}
|
94
|
+
if ((hasCode && !hasPhone) || (!hasCode && hasPhone)) {
|
95
|
+
return { valueMissing: true };
|
96
|
+
}
|
97
|
+
return {}
|
98
|
+
};
|
99
|
+
|
100
|
+
init() {
|
101
|
+
super.init();
|
102
|
+
this.initInputs();
|
103
|
+
this.setComboBoxDescriptor();
|
104
|
+
}
|
105
|
+
|
106
|
+
handleDefaultCountryCode(countryCode) {
|
107
|
+
if (!this.countryCodeInput.value) {
|
108
|
+
const countryData = this.countryCodeInput.items.find(c => c['data-id'] === countryCode)?.['data-name'];
|
109
|
+
|
110
|
+
// When replacing the input component (inserting internal component into text-field) -
|
111
|
+
// Vaadin resets the input's value. We use setTimeout in order to make sure this happens
|
112
|
+
// after the reset.
|
113
|
+
if (countryData) {
|
114
|
+
setTimeout(() => this.countryCodeInput.value = countryData);
|
115
|
+
}
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
// We want to override Vaadin's Combo Box value setter. This is needed since Vaadin couples between the
|
120
|
+
// field that it searches the value, and the finaly display value of the input. We want to search ALL
|
121
|
+
// the values (so we made a field with all the values), but display ONLY the dial code, so we added
|
122
|
+
// this setter, which does that.
|
123
|
+
setComboBoxDescriptor() {
|
124
|
+
const comboBox = this.countryCodeInput;
|
125
|
+
const input = comboBox.shadowRoot.querySelector('input');
|
126
|
+
const valueDescriptor = Object.getOwnPropertyDescriptor(
|
127
|
+
input.constructor.prototype,
|
128
|
+
'value'
|
129
|
+
);
|
130
|
+
Object.defineProperties(input, {
|
131
|
+
value: {
|
132
|
+
...valueDescriptor,
|
133
|
+
set(val) {
|
134
|
+
if (!comboBox.items?.length) {
|
135
|
+
return;
|
136
|
+
}
|
137
|
+
|
138
|
+
const [_code, dialCode] = val.split(' ');
|
139
|
+
|
140
|
+
if (val === this.value) {
|
141
|
+
return;
|
142
|
+
}
|
143
|
+
|
144
|
+
valueDescriptor.set.call(this, dialCode || '');
|
145
|
+
}
|
146
|
+
}
|
147
|
+
});
|
148
|
+
}
|
149
|
+
|
150
|
+
initInputs() {
|
151
|
+
let prevVal = this.value
|
152
|
+
let blurTimerId
|
153
|
+
|
154
|
+
// Sanitize phone input value to filter everything but digits
|
155
|
+
this.phoneNumberInput.addEventListener('input', (e) => {
|
156
|
+
const telDigitsRegExp = /^\d$/;
|
157
|
+
const sanitizedInput = e.target.value
|
158
|
+
.split('')
|
159
|
+
.filter(char => telDigitsRegExp.test(char))
|
160
|
+
.join('');
|
161
|
+
e.target.value = sanitizedInput;
|
162
|
+
});
|
163
|
+
|
164
|
+
this.inputs.forEach(input => {
|
165
|
+
input.addEventListener('blur', (e) => {
|
166
|
+
e.stopImmediatePropagation();
|
167
|
+
blurTimerId = setTimeout(() => {
|
168
|
+
blurTimerId = null
|
169
|
+
this.#dispatchBlur()
|
170
|
+
});
|
171
|
+
})
|
172
|
+
|
173
|
+
input.addEventListener('focus', (e) => {
|
174
|
+
e.stopImmediatePropagation();
|
175
|
+
clearTimeout(blurTimerId)
|
176
|
+
if (!blurTimerId) {
|
177
|
+
this.#dispatchFocus()
|
178
|
+
}
|
179
|
+
})
|
180
|
+
|
181
|
+
input.addEventListener('input', (e) => {
|
182
|
+
if (prevVal === this.value) {
|
183
|
+
e.stopImmediatePropagation();
|
184
|
+
}
|
185
|
+
});
|
186
|
+
});
|
187
|
+
}
|
188
|
+
|
189
|
+
attributeChangedCallback(attrName, oldValue, newValue) {
|
190
|
+
super.attributeChangedCallback(attrName, oldValue, newValue);
|
191
|
+
|
192
|
+
if (oldValue !== newValue) {
|
193
|
+
if (attrName === 'default-code' && newValue) {
|
194
|
+
this.handleDefaultCountryCode(newValue);
|
195
|
+
}
|
196
|
+
else if (inputRelatedAttrs.includes(attrName)) {
|
197
|
+
const attr = mapAttrs[attrName] || attrName;
|
198
|
+
|
199
|
+
if (commonAttrs.includes(attrName)) {
|
200
|
+
this.inputs.forEach(input => input.setAttribute(attr, newValue));
|
201
|
+
}
|
202
|
+
else if (countryAttrs.includes(attrName)) {
|
203
|
+
this.countryCodeInput.setAttribute(attr, newValue);
|
204
|
+
}
|
205
|
+
else if (phoneAttrs.includes(attrName)) {
|
206
|
+
this.phoneNumberInput.setAttribute(attr, newValue);
|
207
|
+
}
|
208
|
+
}
|
209
|
+
}
|
210
|
+
}
|
211
|
+
}
|
212
|
+
|
213
|
+
export default PhoneFieldInternal;
|
@@ -0,0 +1,23 @@
|
|
1
|
+
// We use JSDelivr in order to fetch the images as image file from this library (svg-country-flags)
|
2
|
+
// This reduces our bundle size, and we use it as a static remote resource.
|
3
|
+
export const getCountryFlag = (code) =>
|
4
|
+
`https://cdn.jsdelivr.net/npm/svg-country-flags@1.2.10/svg/${code.toLowerCase()}.svg`;
|
5
|
+
|
6
|
+
export const comboBoxItem = ({ code, dialCode, name: country }) => (`
|
7
|
+
<div
|
8
|
+
style="display:flex; flex-direction: column;"
|
9
|
+
data-id="${code}"
|
10
|
+
data-name="${code} ${dialCode} ${country}"
|
11
|
+
>
|
12
|
+
<div>
|
13
|
+
<span>
|
14
|
+
<img src="${getCountryFlag(code)}" width="20"/>
|
15
|
+
</span>
|
16
|
+
<span>${country}</span>
|
17
|
+
</div>
|
18
|
+
<div>
|
19
|
+
<span>${code}</span>
|
20
|
+
<span>${dialCode}</span>
|
21
|
+
</div>
|
22
|
+
</div>
|
23
|
+
`);
|
@@ -13,13 +13,36 @@ export const componentName = getComponentName('text-field');
|
|
13
13
|
|
14
14
|
let overrides = ``;
|
15
15
|
|
16
|
+
const observedAttrs = ['type'];
|
17
|
+
|
18
|
+
const customMixin = (superclass) =>
|
19
|
+
class TextFieldClass extends superclass {
|
20
|
+
static get observedAttributes() {
|
21
|
+
return observedAttrs.concat(superclass.observedAttributes || []);
|
22
|
+
}
|
23
|
+
|
24
|
+
attributeChangedCallback(attrName, oldVal, newVal) {
|
25
|
+
super.attributeChangeCallback?.(attrName, oldVal, newVal);
|
26
|
+
|
27
|
+
// Vaadin doesn't allow to change the input type attribute.
|
28
|
+
// We need the ability to do that, so we're overriding their
|
29
|
+
// behavior with their private API.
|
30
|
+
// When receiving a `type` attribute, we use their private API
|
31
|
+
// to set it on the input.
|
32
|
+
if (attrName === 'type') {
|
33
|
+
this.baseElement._setType(newVal);
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
16
38
|
const TextField = compose(
|
17
39
|
createStyleMixin({
|
18
40
|
mappings: textFieldMappings
|
19
41
|
}),
|
20
42
|
draggableMixin,
|
21
43
|
proxyInputMixin,
|
22
|
-
componentNameValidationMixin
|
44
|
+
componentNameValidationMixin,
|
45
|
+
customMixin
|
23
46
|
)(
|
24
47
|
createProxy({
|
25
48
|
slots: ['prefix', 'suffix'],
|
@@ -33,14 +56,20 @@ const TextField = compose(
|
|
33
56
|
overrides = `
|
34
57
|
:host {
|
35
58
|
display: inline-block;
|
59
|
+
--vaadin-field-default-width: auto;
|
36
60
|
}
|
37
|
-
|
38
61
|
vaadin-text-field {
|
39
62
|
margin: 0;
|
40
63
|
padding: 0;
|
64
|
+
width: 100%;
|
65
|
+
height: 100%;
|
66
|
+
}
|
67
|
+
vaadin-text-field input {
|
68
|
+
min-height: 0;
|
41
69
|
}
|
42
70
|
vaadin-text-field::part(input-field) {
|
43
71
|
overflow: hidden;
|
72
|
+
padding: 0;
|
44
73
|
}
|
45
74
|
vaadin-text-field[readonly] > input:placeholder-shown {
|
46
75
|
opacity: 1;
|
@@ -55,7 +84,6 @@ overrides = `
|
|
55
84
|
}
|
56
85
|
vaadin-text-field > label,
|
57
86
|
vaadin-text-field::part(input-field) {
|
58
|
-
cursor: pointer;
|
59
87
|
color: var(${TextField.cssVarList.color});
|
60
88
|
}
|
61
89
|
vaadin-text-field::part(input-field):focus {
|
@@ -24,7 +24,10 @@ export default {
|
|
24
24
|
],
|
25
25
|
borderRadius: { selector: selectors.input },
|
26
26
|
boxShadow: { selector: selectors.input },
|
27
|
-
|
27
|
+
|
28
|
+
// we apply font-size also on the host so we can set its width with em
|
29
|
+
fontSize: [{}, { selector: selectors.host }],
|
30
|
+
|
28
31
|
height: { selector: selectors.input },
|
29
32
|
padding: { selector: selectors.input },
|
30
33
|
outline: { selector: selectors.input },
|
package/src/index.js
CHANGED
@@ -5,7 +5,9 @@ const observedAttributes = [
|
|
5
5
|
|
6
6
|
const errorAttributes = {
|
7
7
|
valueMissing: 'data-errormessage-value-missing',
|
8
|
-
patternMismatch: 'data-errormessage-pattern-mismatch'
|
8
|
+
patternMismatch: 'data-errormessage-pattern-mismatch',
|
9
|
+
tooShort: 'data-errormessage-pattern-mismatch-too-short',
|
10
|
+
tooLong: 'data-errormessage-pattern-mismatch-too-long',
|
9
11
|
}
|
10
12
|
export const inputValidationMixin = (superclass) => class InputValidationMixinClass extends superclass {
|
11
13
|
static get observedAttributes() {
|
@@ -35,6 +37,14 @@ export const inputValidationMixin = (superclass) => class InputValidationMixinCl
|
|
35
37
|
return 'Please match the requested format.'
|
36
38
|
}
|
37
39
|
|
40
|
+
get defaultErrorMsgTooShort() {
|
41
|
+
return `Minimum length is ${this.getAttribute('minlength')}.`
|
42
|
+
}
|
43
|
+
|
44
|
+
get defaultErrorMsgTooLong() {
|
45
|
+
return `Maximum length is ${this.getAttribute('maxlength')}. `
|
46
|
+
}
|
47
|
+
|
38
48
|
getErrorMessage(flags) {
|
39
49
|
switch (true) {
|
40
50
|
case flags.valueMissing:
|
@@ -43,6 +53,12 @@ export const inputValidationMixin = (superclass) => class InputValidationMixinCl
|
|
43
53
|
case flags.patternMismatch || flags.typeMismatch:
|
44
54
|
return this.getAttribute(errorAttributes.patternMismatch) ||
|
45
55
|
this.defaultErrorMsgPatternMismatch
|
56
|
+
case flags.tooShort:
|
57
|
+
return this.getAttribute(errorAttributes.tooShort) ||
|
58
|
+
this.defaultErrorMsgTooShort
|
59
|
+
case flags.tooLong:
|
60
|
+
return this.getAttribute(errorAttributes.tooLong) ||
|
61
|
+
this.defaultErrorMsgTooLong
|
46
62
|
case flags.customError:
|
47
63
|
return this.validationMessage
|
48
64
|
default:
|
@@ -110,6 +126,9 @@ export const inputValidationMixin = (superclass) => class InputValidationMixinCl
|
|
110
126
|
this.addEventListener('invalid', (e) => e.stopPropagation())
|
111
127
|
this.addEventListener('input', this.#setValidity)
|
112
128
|
|
113
|
-
|
129
|
+
// The attribute sync takes time, so getValidity returns valid,
|
130
|
+
// even when it shouldn't. This way allows all attributes to sync
|
131
|
+
// after render, before checking the validity.
|
132
|
+
setTimeout(() => this.#setValidity());
|
114
133
|
}
|
115
134
|
}
|
@@ -15,7 +15,12 @@ const sanitizeSelector = (selector) => selector.replace(/[^\w\s]/gi, '');
|
|
15
15
|
|
16
16
|
const getDomNode = (maybeDomNode) => maybeDomNode.host || maybeDomNode;
|
17
17
|
|
18
|
-
export const portalMixin = ({
|
18
|
+
export const portalMixin = ({
|
19
|
+
name,
|
20
|
+
selector,
|
21
|
+
mappings = {},
|
22
|
+
forward: { attributes = [], include = true } = {}
|
23
|
+
}) => (superclass) => {
|
19
24
|
const eleDisplayName = name || sanitizeSelector(selector)
|
20
25
|
|
21
26
|
const BaseClass = createStyleMixin({
|
@@ -58,9 +63,9 @@ export const portalMixin = ({ name, selector, mappings = {} }) => (superclass) =
|
|
58
63
|
|
59
64
|
init() {
|
60
65
|
super.init?.();
|
61
|
-
forwardAttrs(this, this.#portalEle, {
|
66
|
+
forwardAttrs(this, this.#portalEle, { [include ? 'includeAttrs' : 'excludeAttrs']: attributes })
|
62
67
|
|
63
68
|
this.#handleHoverAttribute();
|
64
69
|
}
|
65
70
|
}
|
66
|
-
}
|
71
|
+
}
|
@@ -1,6 +1,5 @@
|
|
1
1
|
import globals from '../globals';
|
2
2
|
import ComboBox from '../../components/descope-combo-box/ComboBox';
|
3
|
-
import { textField } from './textField';
|
4
3
|
import { getThemeRefs } from '../../helpers/themeHelpers';
|
5
4
|
|
6
5
|
const globalRefs = getThemeRefs(globals);
|
@@ -8,18 +7,49 @@ const globalRefs = getThemeRefs(globals);
|
|
8
7
|
const vars = ComboBox.cssVarList;
|
9
8
|
|
10
9
|
export const comboBox = {
|
11
|
-
|
10
|
+
[vars.borderColor]: globalRefs.colors.surface.main,
|
11
|
+
[vars.borderWidth]: '1px',
|
12
|
+
[vars.borderStyle]: 'solid',
|
13
|
+
[vars.cursor]: 'pointer',
|
14
|
+
[vars.padding]: '0',
|
15
|
+
[vars.placeholderColor]: globalRefs.colors.surface.main,
|
16
|
+
[vars.toggleColor]: globalRefs.colors.surface.contrast,
|
17
|
+
[vars.toggleCursor]: 'pointer',
|
12
18
|
size: {
|
13
19
|
xs: {
|
14
|
-
[vars.
|
20
|
+
[vars.height]: '14px',
|
21
|
+
[vars.fontSize]: '8px',
|
22
|
+
[vars.padding]: `0 ${globalRefs.spacing.xs}`
|
23
|
+
},
|
24
|
+
sm: {
|
25
|
+
[vars.height]: '20px',
|
26
|
+
[vars.fontSize]: '10px',
|
27
|
+
[vars.padding]: `0 ${globalRefs.spacing.sm}`
|
28
|
+
},
|
29
|
+
md: {
|
30
|
+
[vars.height]: '30px',
|
31
|
+
[vars.fontSize]: '14px',
|
32
|
+
[vars.padding]: `0 ${globalRefs.spacing.md}`
|
33
|
+
},
|
34
|
+
lg: {
|
35
|
+
[vars.height]: '40px',
|
36
|
+
[vars.fontSize]: '20px',
|
37
|
+
[vars.padding]: `0 ${globalRefs.spacing.lg}`
|
38
|
+
},
|
39
|
+
xl: {
|
40
|
+
[vars.height]: '50px',
|
41
|
+
[vars.fontSize]: '25px',
|
42
|
+
[vars.padding]: `0 ${globalRefs.spacing.xl}`
|
15
43
|
}
|
16
44
|
},
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
// [vars.
|
45
|
+
_invalid: {
|
46
|
+
[vars.borderColor]: globalRefs.colors.error.main,
|
47
|
+
[vars.placeholderColor]: globalRefs.colors.error.light,
|
48
|
+
[vars.toggleColor]: globalRefs.colors.error.main,
|
49
|
+
},
|
50
|
+
// [vars.overlayCursor]: 'pointer',
|
51
|
+
// [vars.overlayBackground]: globalRefs.colors.surface.light,
|
52
|
+
// [vars.overlayBorder]: `2px solid red`,
|
23
53
|
};
|
24
54
|
|
25
55
|
export default comboBox;
|
@@ -15,6 +15,7 @@ import passcode from './passcode';
|
|
15
15
|
import { loaderRadial, loaderLinear } from './loader';
|
16
16
|
import comboBox from './comboBox';
|
17
17
|
import image from './image';
|
18
|
+
import phoneField from './phoneField';
|
18
19
|
|
19
20
|
export default {
|
20
21
|
button,
|
@@ -34,5 +35,6 @@ export default {
|
|
34
35
|
loaderRadial,
|
35
36
|
loaderLinear,
|
36
37
|
comboBox,
|
37
|
-
image
|
38
|
+
image,
|
39
|
+
phoneField
|
38
40
|
};
|