@descope/web-components-ui 1.0.370 → 1.0.372

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. package/dist/cjs/index.cjs.js +410 -41
  2. package/dist/cjs/index.cjs.js.map +1 -1
  3. package/dist/index.esm.js +413 -42
  4. package/dist/index.esm.js.map +1 -1
  5. package/dist/umd/4392.js +1 -1
  6. package/dist/umd/4978.js +1 -1
  7. package/dist/umd/DescopeDev.js +1 -1
  8. package/dist/umd/button-selection-group-fields-descope-button-multi-selection-group-index-js.js +1 -1
  9. package/dist/umd/button-selection-group-fields-descope-button-selection-group-index-js.js +1 -1
  10. package/dist/umd/button-selection-group-fields-descope-button-selection-group-item-index-js.js +1 -1
  11. package/dist/umd/descope-button-index-js.js +2 -2
  12. package/dist/umd/descope-email-field-index-js.js +2 -2
  13. package/dist/umd/descope-icon-index-js.js +1 -1
  14. package/dist/umd/descope-new-password-descope-new-password-internal-index-js.js +1 -1
  15. package/dist/umd/descope-new-password-index-js.js +1 -1
  16. package/dist/umd/descope-number-field-index-js.js +1 -1
  17. package/dist/umd/descope-passcode-index-js.js +1 -1
  18. package/dist/umd/descope-password-index-js.js +1 -1
  19. package/dist/umd/descope-radio-group-index-js.js +1 -1
  20. package/dist/umd/descope-text-field-index-js.js +1 -1
  21. package/dist/umd/descope-upload-file-index-js.js +1 -1
  22. package/dist/umd/descope-user-attribute-index-js.js +1 -1
  23. package/dist/umd/descope-user-auth-method-index-js.js +1 -1
  24. package/dist/umd/mapping-fields-descope-mappings-field-index-js.js +1 -1
  25. package/package.json +1 -1
  26. package/src/components/descope-email-field/EmailFieldClass.js +12 -0
  27. package/src/components/descope-icon/IconClass.js +17 -30
  28. package/src/components/descope-new-password/NewPasswordClass.js +35 -0
  29. package/src/components/descope-new-password/descope-new-password-internal/NewPasswordInternal.js +3 -1
  30. package/src/components/descope-password/PasswordClass.js +59 -1
  31. package/src/components/descope-text-field/textFieldMappings.js +37 -6
  32. package/src/mixins/externalInputHelpers.js +86 -0
  33. package/src/mixins/externalInputMixin.js +165 -0
  34. package/src/mixins/index.js +1 -0
  35. package/src/mixins/proxyInputMixin.js +5 -2
  36. package/src/theme/components/button.js +0 -1
@@ -4,6 +4,7 @@ import {
4
4
  createProxy,
5
5
  proxyInputMixin,
6
6
  componentNameValidationMixin,
7
+ externalInputMixin,
7
8
  } from '../../mixins';
8
9
  import { compose } from '../../helpers';
9
10
  import { getComponentName } from '../../helpers/componentHelpers';
@@ -33,6 +34,15 @@ const customMixin = (superclass) =>
33
34
  this.origSetPasswordVisible = this.baseElement._setPasswordVisible;
34
35
  this.origSetFocused = this.baseElement._setFocused;
35
36
  this.baseElement._setFocused = this.setFocus.bind(this);
37
+
38
+ this.initExternalInput();
39
+ }
40
+
41
+ initExternalInput() {
42
+ const externalInput = this.createExternalInput();
43
+ if (externalInput) {
44
+ this.handlePasswordVisibility(externalInput);
45
+ }
36
46
  }
37
47
 
38
48
  get caretPosition() {
@@ -47,6 +57,7 @@ const customMixin = (superclass) =>
47
57
  setTimeout(() => {
48
58
  origTogglePasswordVisibility();
49
59
  this.inputElement.setSelectionRange(this.caretPosition, this.caretPosition);
60
+ return false;
50
61
  });
51
62
  };
52
63
  }
@@ -73,6 +84,44 @@ const customMixin = (superclass) =>
73
84
  attributeChangedCallback(attrName, oldValue, newValue) {
74
85
  super.attributeChangedCallback?.(attrName, oldValue, newValue);
75
86
  }
87
+
88
+ // override vaadin's password visibility toggle.
89
+ // we need this override in order to to resolve the external input `focus` race condition,
90
+ // which is caused due to the focus sync between the two inputs.
91
+ handlePasswordVisibility(externalInput) {
92
+ // disable vaadin's `__boundRevealButtonMouseDown` mouse-down event lisetener
93
+ const origBoundRevealButtonMouseDown = this.baseElement.__boundRevealButtonMouseDown;
94
+ this.baseElement
95
+ .querySelector('vaadin-password-field-button')
96
+ .removeEventListener('mousedown', origBoundRevealButtonMouseDown);
97
+
98
+ // disable vaadin's `_passwordVisibleChanged` observer
99
+ this.baseElement._passwordVisibleChanged = () => {};
100
+
101
+ // override vaadin's `_togglePasswordVisibility`
102
+ this.baseElement._togglePasswordVisibility = () => {
103
+ const currVisibility = externalInput.getAttribute('type');
104
+ if (currVisibility === 'password') {
105
+ this.showPasswordVisibility(externalInput);
106
+ } else {
107
+ this.hidePasswordVisibility(externalInput);
108
+ }
109
+ };
110
+ }
111
+
112
+ showPasswordVisibility(input) {
113
+ // handle input element's type
114
+ input.setAttribute('type', 'text');
115
+ // handle vaadin's `password-visible` attribute
116
+ this.setAttribute('password-visible', 'true');
117
+ }
118
+
119
+ hidePasswordVisibility(input) {
120
+ // handle input element's type
121
+ input.setAttribute('type', 'password');
122
+ // handle vaadin's `password-visible` attribute
123
+ this.setAttribute('password-visible', 'false');
124
+ }
76
125
  };
77
126
 
78
127
  const {
@@ -167,7 +216,12 @@ export const PasswordClass = compose(
167
216
  },
168
217
  }),
169
218
  draggableMixin,
170
- proxyInputMixin({ proxyProps: ['value', 'selectionStart'] }),
219
+ externalInputMixin({
220
+ inputType: 'password',
221
+ includeAttrs: ['disabled', 'readonly', 'pattern', 'type', 'autocomplete'],
222
+ noBlurDispatch: true,
223
+ }),
224
+ proxyInputMixin({ proxyProps: ['value', 'selectionStart'], useProxyTargets: true }),
171
225
  componentNameValidationMixin,
172
226
  passwordDraggableMixin,
173
227
  customMixin
@@ -231,6 +285,10 @@ export const PasswordClass = compose(
231
285
  ::part(reveal-button) {
232
286
  align-self: center;
233
287
  }
288
+
289
+ vaadin-password-field[external-input="true"] > input:not(:placeholder-shown) {
290
+ opacity: 0;
291
+ }
234
292
  `,
235
293
  excludeAttrsSync: ['tabindex'],
236
294
  componentName,
@@ -11,6 +11,10 @@ const {
11
11
  disabledPlaceholder,
12
12
  inputDisabled,
13
13
  inputIcon,
14
+ externalInput,
15
+ externalInputDisabled,
16
+ externalPlaceholder,
17
+ externalDisabledPlaceholder,
14
18
  } = {
15
19
  host: { selector: () => ':host' },
16
20
  label: { selector: '::part(label)' },
@@ -27,6 +31,10 @@ const {
27
31
  helperText: { selector: '::part(helper-text)' },
28
32
  errorMessage: { selector: '::part(error-message)' },
29
33
  inputIcon: { selector: 'vaadin-icon' },
34
+ externalInput: { selector: () => '::slotted(input)' },
35
+ externalInputDisabled: { selector: () => '::slotted(input:disabled)' },
36
+ externalPlaceholder: { selector: () => '::slotted(input:placeholder-shown)' },
37
+ externalDisabledPlaceholder: { selector: () => '::slotted(input:disabled::placeholder)' },
30
38
  };
31
39
 
32
40
  export default {
@@ -59,8 +67,12 @@ export default {
59
67
  inputValueTextColor: [
60
68
  { ...inputField, property: 'color' },
61
69
  { ...inputDisabled, property: '-webkit-text-fill-color' },
70
+ { ...externalInputDisabled, property: '-webkit-text-fill-color' },
71
+ ],
72
+ inputCaretTextColor: [
73
+ { ...input, property: 'caret-color' },
74
+ { ...externalInput, property: 'caret-color' },
62
75
  ],
63
- inputCaretTextColor: [{ ...input, property: 'color' }],
64
76
 
65
77
  labelRequiredIndicator: { ...requiredIndicator, property: 'content' },
66
78
 
@@ -73,6 +85,8 @@ export default {
73
85
  inputHorizontalPadding: [
74
86
  { ...input, property: 'padding-left' },
75
87
  { ...input, property: 'padding-right' },
88
+ { ...externalInput, property: 'padding-left' },
89
+ { ...externalInput, property: 'padding-right' },
76
90
  ],
77
91
 
78
92
  inputOutlineColor: { ...inputField, property: 'outline-color' },
@@ -80,12 +94,17 @@ export default {
80
94
  inputOutlineWidth: { ...inputField, property: 'outline-width' },
81
95
  inputOutlineOffset: { ...inputField, property: 'outline-offset' },
82
96
 
83
- inputTextAlign: [{ ...input, property: 'text-align' }],
97
+ inputTextAlign: [
98
+ { ...input, property: 'text-align' },
99
+ { ...externalInput, property: 'text-align' },
100
+ ],
84
101
 
85
102
  inputPlaceholderColor: [
86
103
  { selector: () => ':host input:placeholder-shown', property: 'color' },
104
+ { ...externalPlaceholder, property: 'color' },
87
105
  { ...placeholder, property: 'color' },
88
106
  { ...disabledPlaceholder, property: '-webkit-text-fill-color' },
107
+ { ...externalDisabledPlaceholder, property: '-webkit-text-fill-color' },
89
108
  ],
90
109
 
91
110
  labelPosition: { ...label, property: 'position' },
@@ -97,10 +116,22 @@ export default {
97
116
  inputTransformY: { ...label, property: 'transform' },
98
117
  inputTransition: { ...label, property: 'transition' },
99
118
  marginInlineStart: { ...label, property: 'margin-inline-start' },
100
- placeholderOpacity: [{ selector: '> input:placeholder-shown', property: 'opacity' }],
101
- inputVerticalAlignment: [{ ...inputField, property: 'align-items' }],
102
- valueInputHeight: [{ ...input, property: 'height' }],
103
- valueInputMarginBottom: [{ ...input, property: 'margin-bottom' }],
119
+ placeholderOpacity: [
120
+ { selector: '> input:placeholder-shown', property: 'opacity' },
121
+ { ...externalPlaceholder, property: 'opacity' },
122
+ ],
123
+ inputVerticalAlignment: [
124
+ { ...inputField, property: 'align-items' },
125
+ { ...externalInput, property: 'align-items' },
126
+ ],
127
+ valueInputHeight: [
128
+ { ...input, property: 'height' },
129
+ { ...externalInput, property: 'height' },
130
+ ],
131
+ valueInputMarginBottom: [
132
+ { ...input, property: 'margin-bottom' },
133
+ { ...externalInput, property: 'margin-bottom' },
134
+ ],
104
135
 
105
136
  inputIconOffset: [
106
137
  { ...inputIcon, property: 'margin-right' },
@@ -0,0 +1,86 @@
1
+ // since on load we can only sample the color of the placeholder,
2
+ // we need to temporarily populate the input in order to sample the value color
3
+ const getValueColor = (ele, computedStyle) => {
4
+ // eslint-disable-next-line no-param-reassign
5
+ ele.value = '_';
6
+
7
+ const valueColor = computedStyle.getPropertyValue('color');
8
+
9
+ // eslint-disable-next-line no-param-reassign
10
+ ele.value = '';
11
+
12
+ return valueColor;
13
+ };
14
+
15
+ export const createExternalInputSlot = (slotName, targetSlotName) => {
16
+ const slotEle = document.createElement('slot');
17
+
18
+ slotEle.setAttribute('name', slotName);
19
+ slotEle.setAttribute('slot', targetSlotName);
20
+
21
+ return slotEle;
22
+ };
23
+
24
+ export const createExternalInputEle = (targetSlotName, type, autocompleteType) => {
25
+ const inputEle = document.createElement('input');
26
+
27
+ inputEle.setAttribute('slot', targetSlotName);
28
+ inputEle.setAttribute('type', type);
29
+ inputEle.setAttribute('data-hidden-input', 'true');
30
+ inputEle.setAttribute('autocomplete', autocompleteType);
31
+
32
+ return inputEle;
33
+ };
34
+
35
+ // We apply the original input's style to the external-input.
36
+ // Eventually, the user should interact directly with the external input.
37
+ // We keep the original input
38
+ export const applyExternalInputStyles = (sourceInputEle, targetInputEle, labelType) => {
39
+ // We set a timeout here to avoid "Double Print" cases,
40
+ // caused by sampling the computed style before it's ready.
41
+ setTimeout(() => {
42
+ const computedStyle = getComputedStyle(sourceInputEle);
43
+ // Get minimal set of computed theme properties to set on external input
44
+ const height = computedStyle.getPropertyValue('height');
45
+ const paddingLeft = computedStyle.getPropertyValue('padding-left');
46
+ const paddingRight = computedStyle.getPropertyValue('padding-right');
47
+ const fontSize = computedStyle.getPropertyValue('font-size');
48
+ const fontFamily = computedStyle.getPropertyValue('font-family');
49
+ const letterSpacing = computedStyle.getPropertyValue('letter-spacing');
50
+ const caretColor = computedStyle.getPropertyValue('caret-color');
51
+
52
+ const valueColor = getValueColor(sourceInputEle, computedStyle);
53
+
54
+ const commonThemeStyles = [
55
+ ['all', 'unset'],
56
+ ['position', 'absolute'],
57
+ ['background-color', 'transparent'],
58
+ ['height', height],
59
+ ['left', paddingLeft],
60
+ ['right', paddingRight],
61
+ ['font-size', fontSize],
62
+ ['font-family', fontFamily],
63
+ ['letter-spacing', letterSpacing],
64
+ ['caret-color', caretColor], // this is for seeing caret when focusing on external input
65
+ ['color', valueColor],
66
+ ];
67
+
68
+ commonThemeStyles.forEach(([key, val]) =>
69
+ targetInputEle.style.setProperty(key, val, 'important')
70
+ );
71
+
72
+ // Handle floating label theme properties
73
+ if (labelType === 'floating') {
74
+ const marginBottom = computedStyle.getPropertyValue('margin-bottom');
75
+ targetInputEle.style.setProperty('margin-bottom', marginBottom, 'important');
76
+ }
77
+
78
+ // sample and apply width only after original input is ready and fully rendered
79
+ const width = computedStyle.getPropertyValue('width');
80
+ targetInputEle.style.setProperty(
81
+ 'width',
82
+ `calc(${width} - ${paddingLeft} - ${paddingRight}`,
83
+ 'important'
84
+ );
85
+ }, 0);
86
+ };
@@ -0,0 +1,165 @@
1
+ import { syncAttrs } from '../helpers/componentHelpers';
2
+ import {
3
+ applyExternalInputStyles,
4
+ createExternalInputEle,
5
+ createExternalInputSlot,
6
+ } from './externalInputHelpers';
7
+
8
+ export const externalInputMixin =
9
+ ({ inputType, autocompleteType, includeAttrs = [], noBlurDispatch = false }) =>
10
+ (superclass) =>
11
+ class ExternalInputMixinClass extends superclass {
12
+ #timers = [];
13
+
14
+ get isExternalInput() {
15
+ return this.getAttribute('external-input') === 'true';
16
+ }
17
+
18
+ createExternalInput() {
19
+ if (!this.isExternalInput) {
20
+ return null;
21
+ }
22
+
23
+ // use original input element as reference
24
+ const origInput = this.baseElement.querySelector('input');
25
+
26
+ // to avoid focus loop between external-input and origInput
27
+ // we set origInput's tabindex to -1
28
+ // otherwise, shift-tab will never leave the component focus
29
+ origInput.setAttribute('tabindex', '-1');
30
+
31
+ // create external slot
32
+ const externalInputSlot = createExternalInputSlot('external-input', 'suffix');
33
+
34
+ // append external slot to base element
35
+ this.baseElement.appendChild(externalInputSlot);
36
+
37
+ this.externalInput = createExternalInputEle(
38
+ 'external-input',
39
+ inputType,
40
+ this.getAutocompleteType()
41
+ );
42
+
43
+ // apply original input's styles to external input
44
+ setTimeout(() => {
45
+ applyExternalInputStyles(origInput, this.externalInput, this.getAttribute('label-type'));
46
+ });
47
+
48
+ // 1Password catches the internal input, so we forward the value to the external input
49
+ this.forwardInputValue(origInput, this.externalInput);
50
+
51
+ syncAttrs(origInput, this.externalInput, {
52
+ includeAttrs,
53
+ });
54
+
55
+ // We disable Vaadin's original `_setFocused` function, and use handleFocusEvents
56
+ // and handleBlurEvents functions
57
+ this.baseElement
58
+ .querySelector('input')
59
+ .addEventListener('focusout', (e) => e.stopImmediatePropagation(), true);
60
+
61
+ // In order to manage focus/blur events when moving between parts of the component
62
+ // we're managing the event dispatching by ourselves, with the following strategy:
63
+ // - If one of the component parts is focused - it means that the component is still focused - so we stop the blur events.
64
+ // - When none of the component parts is focused, we dispatch blur event.
65
+ this.handleFocusEvents();
66
+ this.handleBlurEvents();
67
+
68
+ // sync input value
69
+ this.handlelInputEvents(this.externalInput);
70
+
71
+ // append external input to component's DOM
72
+ this.appendChild(this.externalInput);
73
+
74
+ return this.externalInput;
75
+ }
76
+
77
+ clearBlurTimers() {
78
+ this.#timers.forEach((timer) => clearTimeout(timer));
79
+ this.#timers.length = 0;
80
+ }
81
+
82
+ dispatchBlur() {
83
+ return setTimeout(() => {
84
+ this.dispatchEvent(new Event('blur', { bubbles: true, composed: true }));
85
+ this.removeAttribute('focused');
86
+ });
87
+ }
88
+
89
+ handleFocusEvents() {
90
+ // on baseElement `focus` we forward the focus to the external input.
91
+ // also, in order to avoid any blur within the component, we clear the blur timers.
92
+ this.baseElement.addEventListener('focus', () => {
93
+ this.externalInput.focus();
94
+ this.clearBlurTimers();
95
+ });
96
+
97
+ // on `focus` of the external input, we manually set the `focused` attribute
98
+ this.externalInput.addEventListener('focus', () => {
99
+ this.clearBlurTimers();
100
+ setTimeout(() => this.baseElement.setAttribute('focused', 'true'));
101
+ });
102
+ }
103
+
104
+ handleBlurEvents() {
105
+ this.baseElement.addEventListener(
106
+ 'blur',
107
+ (e) => {
108
+ e.stopImmediatePropagation();
109
+ // some components do not require this synthetic blur dispatch (e.g. Password)
110
+ // so we allow them to override this dispatch
111
+ if (noBlurDispatch) return;
112
+ this.#timers.push(this.dispatchBlur());
113
+ },
114
+ true
115
+ );
116
+
117
+ this.externalInput.addEventListener(
118
+ 'blur',
119
+ (e) => {
120
+ e.stopImmediatePropagation();
121
+ this.#timers.push(this.dispatchBlur());
122
+ },
123
+ true
124
+ );
125
+ }
126
+
127
+ handlelInputEvents(externalInput) {
128
+ // sync value of insible input back to original input
129
+ externalInput.addEventListener('input', (e) => {
130
+ this.value = e.target.value;
131
+ });
132
+
133
+ // handle has-value attr
134
+ externalInput.addEventListener('input', (e) => {
135
+ if (!e.target.value) {
136
+ this.removeAttribute('has-value');
137
+ } else {
138
+ this.setAttribute('has-value', 'true');
139
+ }
140
+ });
141
+ }
142
+
143
+ getAutocompleteType() {
144
+ return this.getAttribute('autocomplete') || autocompleteType;
145
+ }
146
+
147
+ forwardInputValue(source, target) {
148
+ // set internal sync events
149
+ const valueDescriptor = Object.getOwnPropertyDescriptor(
150
+ this.inputElement.constructor.prototype,
151
+ 'value'
152
+ );
153
+
154
+ Object.defineProperty(source, 'value', {
155
+ ...valueDescriptor,
156
+
157
+ set(v) {
158
+ valueDescriptor.set.call(this, v);
159
+ // eslint-disable-next-line no-param-reassign
160
+ target.value = v;
161
+ },
162
+ configurable: true,
163
+ });
164
+ }
165
+ };
@@ -10,3 +10,4 @@ export { changeMixin } from './changeMixin';
10
10
  export { normalizeBooleanAttributesMixin } from './normalizeBooleanAttributesMixin';
11
11
  export { lifecycleEventsMixin } from './lifecycleEventsMixin';
12
12
  export { inputEventsDispatchingMixin } from './inputEventsDispatchingMixin';
13
+ export { externalInputMixin } from './externalInputMixin';
@@ -42,7 +42,7 @@ const proxyInputMixin =
42
42
  ({
43
43
  proxyProps = [],
44
44
  // useProxyTargets flag allows to forwardProps to other targets, other than
45
- // `this.inputElement`.
45
+ // `this.inputElement`. It's to be used within `external-input` components,
46
46
  // if needed
47
47
  useProxyTargets = false,
48
48
  // allows us to set the event that should trigger validation
@@ -181,7 +181,10 @@ const proxyInputMixin =
181
181
 
182
182
  // sync properties
183
183
  proxyProps.forEach((prop) => {
184
- const proxyTargets = useProxyTargets ? [this.baseElement].filter(Boolean) : [];
184
+ const externalInput = this.querySelector('input[slot="external-input"]') || null;
185
+ const proxyTargets = useProxyTargets
186
+ ? [this.baseElement, externalInput].filter(Boolean)
187
+ : [];
185
188
  forwardProps(this, [this.inputElement, ...proxyTargets], prop);
186
189
  });
187
190
 
@@ -48,7 +48,6 @@ const button = {
48
48
  [compVars.outlineColor]: 'transparent',
49
49
 
50
50
  [compVars.iconSize]: '1.5em',
51
- [compVars.iconColor]: 'currentColor',
52
51
 
53
52
  size: {
54
53
  xs: { [compVars.fontSize]: '12px' },