@descope/web-components-ui 1.0.371 → 1.0.373

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. package/dist/cjs/index.cjs.js +409 -10
  2. package/dist/cjs/index.cjs.js.map +1 -1
  3. package/dist/index.esm.js +412 -11
  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/descope-email-field-index-js.js +2 -2
  9. package/dist/umd/descope-enriched-text-index-js.js +1 -1
  10. package/dist/umd/descope-new-password-descope-new-password-internal-index-js.js +1 -1
  11. package/dist/umd/descope-new-password-index-js.js +1 -1
  12. package/dist/umd/descope-number-field-index-js.js +1 -1
  13. package/dist/umd/descope-passcode-index-js.js +1 -1
  14. package/dist/umd/descope-password-index-js.js +1 -1
  15. package/dist/umd/descope-radio-group-index-js.js +1 -1
  16. package/dist/umd/descope-text-field-index-js.js +1 -1
  17. package/dist/umd/mapping-fields-descope-mappings-field-index-js.js +1 -1
  18. package/package.json +1 -1
  19. package/src/components/descope-email-field/EmailFieldClass.js +12 -0
  20. package/src/components/descope-enriched-text/EnrichedTextClass.js +9 -1
  21. package/src/components/descope-new-password/NewPasswordClass.js +35 -0
  22. package/src/components/descope-new-password/descope-new-password-internal/NewPasswordInternal.js +3 -1
  23. package/src/components/descope-password/PasswordClass.js +59 -1
  24. package/src/components/descope-text-field/textFieldMappings.js +37 -6
  25. package/src/mixins/externalInputHelpers.js +86 -0
  26. package/src/mixins/externalInputMixin.js +165 -0
  27. package/src/mixins/index.js +1 -0
  28. package/src/mixins/proxyInputMixin.js +5 -2
  29. package/src/theme/components/enrichedText.js +8 -0
@@ -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
 
@@ -26,6 +26,14 @@ const enrichedText = {
26
26
  [vars.fontWeightBold]: '900',
27
27
  [vars.minWidth]: '0.25em',
28
28
  [vars.minHeight]: '1.35em',
29
+
30
+ [vars.hostDisplay]: 'inline-block',
31
+
32
+ _empty: {
33
+ _hideWhenEmpty: {
34
+ [vars.hostDisplay]: 'none',
35
+ },
36
+ },
29
37
  };
30
38
 
31
39
  export default enrichedText;