@descope/web-components-ui 1.0.73 → 1.0.75
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/dist/index.esm.js +2425 -622
- 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-logo-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-logo/Logo.js +1 -1
- 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
|
};
|