@descope/web-components-ui 1.0.370 → 1.0.372

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.
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' },