@descope/web-components-ui 1.0.61 → 1.0.63

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 (86) hide show
  1. package/dist/cjs/index.cjs.js +95 -21
  2. package/dist/cjs/index.cjs.js.map +1 -1
  3. package/dist/index.esm.js +967 -784
  4. package/dist/index.esm.js.map +1 -1
  5. package/dist/umd/447.js +1 -1
  6. package/dist/umd/666.js +37 -0
  7. package/dist/umd/725.js +1 -37
  8. package/dist/umd/descope-button-index-js.js +1 -1
  9. package/dist/umd/descope-checkbox-index-js.js +1 -1
  10. package/dist/umd/descope-container-index-js.js +1 -1
  11. package/dist/umd/descope-date-picker-index-js.js +1 -1
  12. package/dist/umd/descope-divider-index-js.js +1 -1
  13. package/dist/umd/descope-email-field-index-js.js +1 -1
  14. package/dist/umd/descope-link-index-js.js +1 -1
  15. package/dist/umd/descope-loader-linear-index-js.js +1 -1
  16. package/dist/umd/descope-loader-radial-index-js.js +1 -1
  17. package/dist/umd/descope-logo-index-js.js +1 -1
  18. package/dist/umd/descope-number-field-index-js.js +1 -1
  19. package/dist/umd/descope-passcode-descope-passcode-internal-index-js.js +1 -1
  20. package/dist/umd/descope-passcode-index-js.js +1 -1
  21. package/dist/umd/descope-password-field-index-js.js +1 -1
  22. package/dist/umd/descope-switch-toggle-index-js.js +1 -1
  23. package/dist/umd/descope-text-area-index-js.js +1 -1
  24. package/dist/umd/descope-text-field-index-js.js +1 -1
  25. package/dist/umd/descope-text-index-js.js +1 -1
  26. package/dist/umd/index.js +1 -1
  27. package/package.json +1 -1
  28. package/src/baseClasses/BaseInputClass.js +3 -0
  29. package/src/components/descope-button/Button.js +14 -10
  30. package/src/components/descope-checkbox/Checkbox.js +10 -7
  31. package/src/components/descope-container/Container.js +12 -7
  32. package/src/components/descope-date-picker/index.js +3 -3
  33. package/src/components/descope-divider/Divider.js +35 -30
  34. package/src/components/descope-email-field/EmailField.js +5 -5
  35. package/src/components/descope-link/Link.js +19 -27
  36. package/src/components/descope-loader-linear/LoaderLinear.js +12 -7
  37. package/src/components/descope-loader-radial/LoaderRadial.js +5 -5
  38. package/src/components/descope-logo/Logo.js +4 -4
  39. package/src/components/descope-number-field/NumberField.js +5 -5
  40. package/src/components/descope-passcode/Passcode.js +52 -71
  41. package/src/components/descope-passcode/descope-passcode-internal/PasscodeInternal.js +43 -71
  42. package/src/components/descope-password-field/PasswordField.js +5 -5
  43. package/src/components/descope-switch-toggle/SwitchToggle.js +10 -7
  44. package/src/components/descope-text/Text.js +18 -9
  45. package/src/components/descope-text-area/TextArea.js +9 -8
  46. package/src/components/descope-text-field/TextField.js +5 -5
  47. package/src/components/descope-text-field/textFieldMappings.js +3 -5
  48. package/src/constants.js +1 -0
  49. package/src/dev/index.js +2 -2
  50. package/src/{componentsHelpers/createProxy/helpers.js → helpers/componentHelpers.js} +13 -5
  51. package/src/{helpers.js → helpers/index.js} +5 -2
  52. package/src/helpers/themeHelpers/componentsThemeManager.js +40 -0
  53. package/src/{themeHelpers → helpers/themeHelpers}/index.js +56 -21
  54. package/src/index.cjs.js +1 -1
  55. package/src/index.js +5 -5
  56. package/src/index.umd.js +4 -0
  57. package/src/{componentsHelpers/createProxy/index.js → mixins/createProxy.js} +4 -4
  58. package/src/{componentsHelpers → mixins}/createStyleMixin/helpers.js +14 -15
  59. package/src/mixins/createStyleMixin/index.js +109 -0
  60. package/src/mixins/index.js +7 -0
  61. package/src/mixins/inputMixin.js +173 -0
  62. package/src/mixins/proxyInputMixin.js +152 -0
  63. package/src/theme/components/button.js +2 -2
  64. package/src/theme/components/checkbox.js +2 -1
  65. package/src/theme/components/container.js +2 -2
  66. package/src/theme/components/divider.js +17 -9
  67. package/src/theme/components/link.js +1 -1
  68. package/src/theme/components/loader/loaderLinear.js +1 -1
  69. package/src/theme/components/loader/loaderRadial.js +1 -1
  70. package/src/theme/components/text.js +1 -1
  71. package/src/theme/components/textArea.js +2 -1
  72. package/src/theme/components/textField.js +2 -2
  73. package/src/theme/globals.js +3 -3
  74. package/dist/umd/832.js +0 -1
  75. package/dist/umd/942.js +0 -1
  76. package/dist/umd/descope-combo-index-js.js +0 -1
  77. package/src/components/descope-combo/index.js +0 -27
  78. package/src/componentsHelpers/createStyleMixin/index.js +0 -114
  79. package/src/componentsHelpers/enforceNestingElementsStylesMixin.js +0 -95
  80. package/src/componentsHelpers/index.js +0 -15
  81. package/src/componentsHelpers/inputMixin.js +0 -94
  82. /package/dist/umd/{725.js.LICENSE.txt → 666.js.LICENSE.txt} +0 -0
  83. /package/src/{themeHelpers/processColors.js → helpers/themeHelpers/colorsHelpers.js} +0 -0
  84. /package/src/{componentsHelpers → mixins}/componentNameValidationMixin.js +0 -0
  85. /package/src/{componentsHelpers → mixins}/draggableMixin.js +0 -0
  86. /package/src/{componentsHelpers → mixins}/hoverableMixin.js +0 -0
@@ -1,16 +1,16 @@
1
- import { getComponentName } from '../../../componentsHelpers';
1
+ import BaseInputClass from '../../../baseClasses/BaseInputClass';
2
+ import { getComponentName } from '../../../helpers/componentHelpers';
2
3
  import { getSanitizedCharacters, focusElement } from './helpers';
3
4
 
4
5
  export const componentName = getComponentName('passcode-internal');
5
6
 
6
- class PasscodeInternal extends HTMLElement {
7
+ class PasscodeInternal extends BaseInputClass {
7
8
  static get observedAttributes() {
8
9
  return [
10
+ ...BaseInputClass.observedAttributes,
9
11
  'disabled',
10
12
  'bordered',
11
- 'size',
12
- 'required',
13
- 'pattern'
13
+ 'size'
14
14
  ];
15
15
  }
16
16
 
@@ -18,12 +18,6 @@ class PasscodeInternal extends HTMLElement {
18
18
  return componentName;
19
19
  }
20
20
 
21
- static get formAssociated() {
22
- return true;
23
- }
24
-
25
- #internals
26
-
27
21
  constructor() {
28
22
  super();
29
23
  const template = document.createElement('template');
@@ -47,23 +41,9 @@ class PasscodeInternal extends HTMLElement {
47
41
 
48
42
  this.baseSelector = ':host > div';
49
43
 
50
- this.#internals = this.attachInternals();
51
-
52
44
  this.inputs = Array.from(this.querySelectorAll('descope-text-field'))
53
45
  }
54
46
 
55
- checkValidity() {
56
- // we need to toggle the has-error-message so the text inside the digits will become red when there is an error
57
- if (this.#internals.validity.valid) {
58
- this.inputs.forEach(input => input.removeAttribute('has-error-message'))
59
- } else {
60
- this.inputs.forEach(input => input.setAttribute('has-error-message', 'true'))
61
- // we need to call it so the has-error-message with have the correct format (="true")
62
- this.oninvalid?.()
63
- }
64
- return this.#internals.validity.valid
65
- }
66
-
67
47
  get digits() {
68
48
  return Number.parseInt(this.getAttribute('digits')) || 6
69
49
  }
@@ -72,59 +52,54 @@ class PasscodeInternal extends HTMLElement {
72
52
  return this.inputs.map(({ value }) => value).join('')
73
53
  }
74
54
 
75
- set value(val) { }
55
+ set value(val) {
56
+ if(val === this.value) return;
76
57
 
77
- get isRequired() {
78
- return this.hasAttribute('required') && this.getAttribute('required') !== 'false'
58
+ const charArr = getSanitizedCharacters(val);
59
+
60
+ if (charArr.length) {
61
+ this.fillDigits(charArr, this.inputs[0]);
62
+ }
79
63
  }
80
64
 
81
65
  get pattern() {
82
66
  return `^$|^\\d{${this.digits},}$`
83
67
  }
84
68
 
85
- get valueMissingErrMsg() {
86
- return 'Please fill out this field.'
87
- }
88
-
89
- get patternMismatchErrMsg() {
90
- return `Must be a ${this.digits} digits number.`
91
- }
92
-
93
- get validity() {
94
- return this.#internals.validity;
95
- }
96
-
97
- get validationMessage() {
98
- return this.#internals.validationMessage;
99
- }
100
-
101
- reportValidity() {
102
- this.#internals.reportValidity()
69
+ handleInputsInvalid() {
70
+ setTimeout(() => {
71
+ if (this.hasAttribute('invalid')) {
72
+ this.inputs.forEach(input => input.setAttribute('invalid', 'true'))
73
+ }
74
+ })
103
75
  }
104
76
 
105
- formAssociatedCallback() {
106
- this.setValidity?.();
77
+ handleInputsValid() {
78
+ this.inputs.forEach(input => input.removeAttribute('invalid'))
107
79
  }
108
80
 
109
- setValidity = () => {
81
+ getValidity() {
110
82
  if (this.isRequired && !this.value) {
111
- this.#internals.setValidity({ valueMissing: true }, this.valueMissingErrMsg);
83
+ return { valueMissing: true };
112
84
  }
113
85
  else if (this.pattern && !new RegExp(this.pattern).test(this.value)) {
114
- this.#internals.setValidity({ patternMismatch: true }, this.patternMismatchErrMsg);
86
+ return { patternMismatch: true };
115
87
  }
116
88
  else {
117
- this.#internals.setValidity({})
89
+ return {}
118
90
  }
119
91
  };
120
92
 
93
+ handleFocus() {
94
+ this.inputs[0].focus();
95
+ }
96
+
121
97
  async connectedCallback() {
122
- this.setValidity();
98
+ super.connectedCallback();
123
99
  this.initInputs()
124
100
 
125
- this.onfocus = () => {
126
- this.inputs[0].focus();
127
- }
101
+ this.addEventListener('invalid', this.handleInputsInvalid)
102
+ this.addEventListener('valid', this.handleInputsValid)
128
103
  }
129
104
 
130
105
  getInputIdx(inputEle) {
@@ -155,6 +130,10 @@ class PasscodeInternal extends HTMLElement {
155
130
  !currentInput.hasAttribute('focused') && focusElement(currentInput);
156
131
  };
157
132
 
133
+ handleBlur() {
134
+ this.handleInputsInvalid()
135
+ }
136
+
158
137
  initInputs() {
159
138
  this.inputs.forEach((input) => {
160
139
 
@@ -163,7 +142,7 @@ class PasscodeInternal extends HTMLElement {
163
142
  // if not, the component is no longer focused and we should simulate blur
164
143
  input.addEventListener('blur', () => {
165
144
  const timerId = setTimeout(() => {
166
- this.dispatchEvent(new Event('blur'))
145
+ this.dispatchBlur()
167
146
  });
168
147
 
169
148
  this.inputs.forEach((ele) =>
@@ -171,13 +150,13 @@ class PasscodeInternal extends HTMLElement {
171
150
  );
172
151
  })
173
152
 
174
- input.oninput = (e) => {
153
+ input.oninput = () => {
175
154
  const charArr = getSanitizedCharacters(input.value);
176
155
 
177
156
  if (!charArr.length) input.value = ''; // if we got an invalid value we want to clear the input
178
157
  else this.fillDigits(charArr, input);
179
158
 
180
- this.setValidity();
159
+ this.dispatchInput()
181
160
  };
182
161
 
183
162
  input.onkeydown = ({ key }) => {
@@ -190,27 +169,21 @@ class PasscodeInternal extends HTMLElement {
190
169
  !prevInput.hasAttribute('focused') && setTimeout(() => {
191
170
  focusElement(prevInput);
192
171
  });
172
+
173
+ this.dispatchInput()
193
174
  } else if (key.match(/^(\d)$/g)) { // if input is a digit
194
175
  input.value = ''; // we are clearing the previous value so we can override it with the new value
195
176
  }
196
177
 
197
- this.setValidity()
198
178
  };
199
179
  })
200
180
  }
201
181
 
202
- attributeChangedCallback(
203
- attrName,
204
- oldValue,
205
- newValue
206
- ) {
207
- const validationRelatedAttributes = ['required', 'pattern'];
182
+ attributeChangedCallback(attrName, oldValue, newValue) {
183
+ super.attributeChangedCallback(attrName, oldValue, newValue)
208
184
 
209
185
  if (oldValue !== newValue) {
210
- if (validationRelatedAttributes.includes(attrName)) {
211
- this.setValidity()
212
- }
213
- else if (PasscodeInternal.observedAttributes.includes(attrName)) {
186
+ if (PasscodeInternal.observedAttributes.includes(attrName) && !BaseInputClass.observedAttributes.includes(attrName)) {
214
187
  this.inputs.forEach((input) => input.setAttribute(attrName, newValue))
215
188
  }
216
189
  }
@@ -218,4 +191,3 @@ class PasscodeInternal extends HTMLElement {
218
191
  }
219
192
 
220
193
  export default PasscodeInternal;
221
-
@@ -1,13 +1,13 @@
1
1
  import {
2
- getComponentName,
3
2
  createStyleMixin,
4
3
  draggableMixin,
5
4
  createProxy,
6
- inputMixin,
7
- compose,
5
+ proxyInputMixin,
8
6
  componentNameValidationMixin
9
- } from '../../componentsHelpers';
7
+ } from '../../mixins';
10
8
  import textFieldMappings from '../descope-text-field/textFieldMappings';
9
+ import { compose } from '../../helpers';
10
+ import { getComponentName } from '../../helpers/componentHelpers';
11
11
 
12
12
  export const componentName = getComponentName('password-field');
13
13
 
@@ -26,7 +26,7 @@ const PasswordField = compose(
26
26
  }
27
27
  }),
28
28
  draggableMixin,
29
- inputMixin,
29
+ proxyInputMixin,
30
30
  componentNameValidationMixin
31
31
  )(
32
32
  createProxy({
@@ -1,13 +1,12 @@
1
1
  import {
2
- getComponentName,
3
2
  createStyleMixin,
4
3
  draggableMixin,
5
4
  createProxy,
6
- inputMixin,
7
- compose,
5
+ proxyInputMixin,
8
6
  componentNameValidationMixin,
9
- } from '../../componentsHelpers';
10
- import { matchHostStyle } from '../../componentsHelpers/createStyleMixin/helpers';
7
+ } from '../../mixins';
8
+ import { compose } from '../../helpers';
9
+ import { getComponentName } from '../../helpers/componentHelpers';
11
10
 
12
11
  export const componentName = getComponentName('switch-toggle');
13
12
 
@@ -16,12 +15,12 @@ let overrides = ``;
16
15
  const SwitchToggle = compose(
17
16
  createStyleMixin({
18
17
  mappings: {
19
- width: matchHostStyle(),
18
+ width: {selector: () => ':host'},
20
19
  cursor: [{}, { selector: '> label' }]
21
20
  }
22
21
  }),
23
22
  draggableMixin,
24
- inputMixin,
23
+ proxyInputMixin,
25
24
  componentNameValidationMixin
26
25
  )(
27
26
  createProxy({
@@ -47,6 +46,9 @@ overrides = `
47
46
  --bgColor: #fff;
48
47
  --knobBgColor: #000;
49
48
  }
49
+ vaadin-checkbox : {
50
+ width: 100%;
51
+ }
50
52
  vaadin-checkbox>label {
51
53
  cursor: pointer;
52
54
  border: 1px solid;
@@ -73,6 +75,7 @@ overrides = `
73
75
  height: 0;
74
76
  width: 0;
75
77
  visibility: hidden;
78
+ margin: 0;
76
79
  }
77
80
  vaadin-checkbox[checked]>label::after {
78
81
  transform: translateX(-100%);
@@ -1,12 +1,11 @@
1
1
  import {
2
- getComponentName,
3
2
  createStyleMixin,
4
3
  draggableMixin,
5
- compose,
6
4
  componentNameValidationMixin
7
- } from '../../componentsHelpers';
8
- import { matchHostStyle } from '../../componentsHelpers/createStyleMixin/helpers';
5
+ } from '../../mixins';
9
6
  import { DescopeBaseClass } from '../../baseClasses/DescopeBaseClass';
7
+ import { compose } from '../../helpers';
8
+ import { getComponentName } from '../../helpers/componentHelpers';
10
9
 
11
10
  export const componentName = getComponentName('text');
12
11
 
@@ -17,7 +16,18 @@ class RawText extends DescopeBaseClass {
17
16
  constructor() {
18
17
  super();
19
18
  const template = document.createElement('template');
20
- template.innerHTML = `<slot></slot>`;
19
+ template.innerHTML = `
20
+ <style>
21
+ :host {
22
+ display: inline-block;
23
+ }
24
+ :host > slot {
25
+ width: 100%;
26
+ display: inline-block;
27
+ }
28
+ </style>
29
+ <slot></slot>
30
+ `;
21
31
 
22
32
  this.attachShadow({ mode: 'open' });
23
33
  this.shadowRoot.appendChild(template.content.cloneNode(true));
@@ -34,7 +44,7 @@ const Text = compose(
34
44
  fontStyle: {},
35
45
  fontSize: {},
36
46
  fontWeight: {},
37
- width: {},
47
+ width: { selector: () => ':host' },
38
48
  color: {},
39
49
  letterSpacing: {},
40
50
  textShadow: {},
@@ -42,9 +52,8 @@ const Text = compose(
42
52
  borderStyle: {},
43
53
  borderColor: {},
44
54
  textTransform: {},
45
- textAlign: matchHostStyle(),
46
- display: matchHostStyle()
47
- }
55
+ textAlign: {},
56
+ },
48
57
  }),
49
58
  draggableMixin,
50
59
  componentNameValidationMixin
@@ -1,20 +1,20 @@
1
1
  import {
2
- getComponentName,
3
2
  createStyleMixin,
4
3
  draggableMixin,
5
4
  createProxy,
6
- inputMixin,
7
- compose,
5
+ proxyInputMixin,
8
6
  componentNameValidationMixin
9
- } from '../../componentsHelpers';
10
- import { matchHostStyle } from '../../componentsHelpers/createStyleMixin/helpers';
7
+ } from '../../mixins';
8
+ import { compose } from '../../helpers';
9
+ import { getComponentName } from '../../helpers/componentHelpers';
11
10
 
12
11
  export const componentName = getComponentName('text-area');
13
12
 
14
13
  const selectors = {
15
14
  label: '::part(label)',
16
15
  input: '::part(input-field)',
17
- required: '::part(required-indicator)::after'
16
+ required: '::part(required-indicator)::after',
17
+ host: () => ':host'
18
18
  };
19
19
 
20
20
  let overrides = ``;
@@ -25,7 +25,7 @@ const TextArea = compose(
25
25
  resize: { selector: '> textarea' },
26
26
  color: { selector: selectors.label },
27
27
  cursor: {},
28
- width: matchHostStyle(),
28
+ width: { selector: selectors.host },
29
29
  backgroundColor: { selector: selectors.input },
30
30
  borderWidth: { selector: selectors.input },
31
31
  borderStyle: { selector: selectors.input },
@@ -36,7 +36,7 @@ const TextArea = compose(
36
36
  }
37
37
  }),
38
38
  draggableMixin,
39
- inputMixin,
39
+ proxyInputMixin,
40
40
  componentNameValidationMixin
41
41
  )(
42
42
  createProxy({
@@ -55,6 +55,7 @@ overrides = `
55
55
 
56
56
  vaadin-text-area {
57
57
  margin: 0;
58
+ width: 100%;
58
59
  }
59
60
  vaadin-text-area > label,
60
61
  vaadin-text-area::part(input-field) {
@@ -1,13 +1,13 @@
1
1
  import {
2
- getComponentName,
3
2
  createStyleMixin,
4
3
  draggableMixin,
5
4
  createProxy,
6
- inputMixin,
7
- compose,
5
+ proxyInputMixin,
8
6
  componentNameValidationMixin
9
- } from '../../componentsHelpers';
7
+ } from '../../mixins';
10
8
  import textFieldMappings from '../descope-text-field/textFieldMappings';
9
+ import { compose } from '../../helpers';
10
+ import { getComponentName } from '../../helpers/componentHelpers';
11
11
 
12
12
  export const componentName = getComponentName('text-field');
13
13
 
@@ -18,7 +18,7 @@ const TextField = compose(
18
18
  mappings: textFieldMappings
19
19
  }),
20
20
  draggableMixin,
21
- inputMixin,
21
+ proxyInputMixin,
22
22
  componentNameValidationMixin
23
23
  )(
24
24
  createProxy({
@@ -1,17 +1,15 @@
1
- import { matchHostStyle } from '../../componentsHelpers/createStyleMixin/helpers';
2
-
3
1
  const selectors = {
4
2
  label: '::part(label)',
5
3
  input: '::part(input-field)',
6
4
  readOnlyInput: '[readonly]::part(input-field)::after',
7
- placeholder: '> input:placeholder-shown'
5
+ placeholder: '> input:placeholder-shown',
6
+ host: () => ':host'
8
7
  };
9
8
 
10
9
  export default {
11
- color: { selector: selectors.input },
12
10
  backgroundColor: { selector: selectors.input },
13
11
  color: { selector: selectors.input },
14
- width: matchHostStyle({}),
12
+ width: { selector: selectors.host },
15
13
  borderColor: [
16
14
  { selector: selectors.input },
17
15
  { selector: selectors.readOnlyInput }
package/src/constants.js CHANGED
@@ -1 +1,2 @@
1
1
  export const DESCOPE_PREFIX = 'descope';
2
+ export const CSS_SELECTOR_SPECIFIER_MULTIPLY = 3
package/src/dev/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export {default as defaultTheme} from '../theme';
2
- export { themeToStyle } from '../themeHelpers';
1
+ export { default as defaultTheme } from '../theme';
2
+ export { themeToStyle } from '../helpers/themeHelpers';
@@ -1,3 +1,6 @@
1
+ import { kebabCaseJoin } from '.';
2
+ import { DESCOPE_PREFIX } from '../constants';
3
+
1
4
  const observeAttributes = (
2
5
  ele,
3
6
  callback,
@@ -50,12 +53,17 @@ export const syncAttrs = (ele1, ele2, options) => {
50
53
  observeAttributes(ele2, createSyncAttrsCb(ele2, ele1), options);
51
54
  };
52
55
 
56
+ export const getComponentName = (name) => kebabCaseJoin(DESCOPE_PREFIX, name);
57
+
58
+ export const getCssVarName = (...args) =>
59
+ `--${kebabCaseJoin(...args.filter((arg) => !!arg))}`;
60
+
53
61
  export const forwardAttrs = (source, dest, options = {}) => {
54
- observeAttributes(
55
- source,
56
- createSyncAttrsCb(source, dest, options.mapAttrs),
57
- options
58
- );
62
+ observeAttributes(
63
+ source,
64
+ createSyncAttrsCb(source, dest, options.mapAttrs),
65
+ options
66
+ );
59
67
  };
60
68
 
61
69
  export const forwardProps = (src, target, props = []) => {
@@ -1,3 +1,4 @@
1
+
1
2
  export const kebabCase = (str) =>
2
3
  str
3
4
  .replace(/([a-z])([A-Z])/g, '$1-$2')
@@ -6,5 +7,7 @@ export const kebabCase = (str) =>
6
7
 
7
8
  export const kebabCaseJoin = (...args) => kebabCase(args.join('-'));
8
9
 
9
- export const getCssVarName = (...args) =>
10
- `--${kebabCaseJoin(...args.filter((arg) => !!arg))}`;
10
+ export const compose = (...fns) =>
11
+ (val) =>
12
+ fns.reduceRight((res, fn) => fn(res), val);
13
+
@@ -0,0 +1,40 @@
1
+
2
+ class ComponentsThemeManager {
3
+ static mountOnPropName = 'DescopeThemeManager';
4
+
5
+ #themes = {};
6
+
7
+ #currentThemeName = 'light';
8
+
9
+ #callbacks = new Set();
10
+
11
+ #notify() {
12
+ this.#callbacks.forEach(cb => cb?.());
13
+ };
14
+
15
+ get currentThemeName() {
16
+ return this.#currentThemeName;
17
+ }
18
+
19
+ set currentThemeName(themeName) {
20
+ this.#currentThemeName = themeName;
21
+ this.#notify();
22
+ }
23
+
24
+ get currentTheme() {
25
+ return this.#themes[this.currentThemeName];
26
+ }
27
+
28
+ onCurrentThemeChange(cb) {
29
+ this.#callbacks.add(cb);
30
+
31
+ return () => { this.#callbacks.delete(cb); };
32
+ };
33
+
34
+ set themes(themes) {
35
+ this.#themes = themes;
36
+ this.#notify();
37
+ }
38
+ }
39
+
40
+ export const componentsThemeManager = new ComponentsThemeManager()
@@ -1,8 +1,8 @@
1
1
  import merge from 'lodash.merge';
2
2
  import set from 'lodash.set';
3
- import { DESCOPE_PREFIX } from '../constants';
4
- import { getCssVarName, kebabCase } from '../helpers';
5
- import { getComponentName } from '../componentsHelpers';
3
+ import { DESCOPE_PREFIX } from '../../constants';
4
+ import { kebabCase } from '..';
5
+ import { getComponentName, getCssVarName } from '../componentHelpers';
6
6
 
7
7
  const getVarName = (path) => getCssVarName(DESCOPE_PREFIX, ...path);
8
8
 
@@ -32,9 +32,9 @@ export const getThemeRefs = (theme, prefix) =>
32
32
  export const globalsThemeToStyle = (theme, themeName = '') => `
33
33
  *[data-theme="${themeName}"] {
34
34
  ${Object.entries(themeToCSSVarsObj(theme)).reduce(
35
- (acc, entry) => (acc += `${entry.join(':')};\n`),
36
- ''
37
- )}
35
+ (acc, entry) => (acc += `${entry.join(':')};\n`),
36
+ ''
37
+ )}
38
38
  }
39
39
  `;
40
40
 
@@ -42,10 +42,20 @@ const componentsThemeToStyleObj = (componentsTheme) =>
42
42
  transformTheme(componentsTheme, [], (path, val) => {
43
43
  const [component, ...restPath] = path;
44
44
  const property = restPath.pop();
45
+ const componentName = getComponentName(component);
46
+
47
+ // we need a support for portal components theme (e.g. overlay)
48
+ // this allows us to generate those themes under different sections
49
+ // if the theme has root level attribute that starts with #
50
+ // we are generating a new theme
51
+ let themeName = 'theme'
52
+
53
+ if (restPath[0] && restPath[0].startsWith('#')) {
54
+ themeName = restPath.shift();
55
+ }
45
56
 
46
57
  // do not start with underscore -> key:value, must have 2 no underscore attrs in a row
47
58
  // starts with underscore -> attribute selector
48
-
49
59
  const attrsSelector = restPath.reduce((acc, section, idx) => {
50
60
  if (section.startsWith('_'))
51
61
  return (acc += `[${kebabCase(section.replace(/^_/, ''))}="true"]`);
@@ -65,30 +75,53 @@ const componentsThemeToStyleObj = (componentsTheme) =>
65
75
  .join('')}"]`);
66
76
  }, '');
67
77
 
68
- let selector = `${getComponentName(component)}${attrsSelector}`;
78
+ let selector = `:host${attrsSelector ? `(${attrsSelector})` : ''}`;
69
79
 
70
80
  return {
71
- [selector]: {
72
- [property]: val
81
+ [componentName]: {
82
+ [themeName]: {
83
+ [selector]: {
84
+ [property]: val
85
+ }
86
+ }
73
87
  }
74
88
  };
75
89
  });
76
90
 
77
- export const componentsThemeToStyle = (componentsTheme, themeName = '') =>
78
- Object.entries(componentsThemeToStyleObj(componentsTheme)).reduce(
91
+
92
+ export const createComponentsTheme = (componentsTheme) => {
93
+ const styleObj = componentsThemeToStyleObj(componentsTheme);
94
+
95
+ return Object.keys(styleObj).reduce(
96
+ (acc, componentName) => {
97
+ const componentThemes = styleObj[componentName];
98
+
99
+ return Object.assign(acc, {
100
+ [componentName]: Object.keys(componentThemes)
101
+ .reduce((acc, theme) =>
102
+ Object.assign(acc, { [theme]: componentsThemeToStyle(componentThemes[theme]) }),
103
+ {})
104
+ })
105
+ },
106
+ {}
107
+ );
108
+ }
109
+
110
+ const componentsThemeToStyle = (componentsTheme) =>
111
+ Object.entries(componentsTheme).reduce(
79
112
  (acc, [selector, vars]) =>
80
- (acc += `*[data-theme="${themeName}"] ${selector} { \n${Object.entries(
81
- vars
82
- )
83
- .map(([key, val]) => `${key}: ${val}`)
84
- .join(';\n')} \n}\n\n`),
113
+ (acc += `${selector} { \n${Object.entries(
114
+ vars
115
+ )
116
+ .map(([key, val]) => `${key}: ${val}`)
117
+ .join(';\n')} \n}\n\n`),
85
118
  ''
86
119
  );
87
120
 
88
- export const themeToStyle = ({ globals, components }, themeName) => `
89
- ${globalsThemeToStyle(globals, themeName)}
90
- ${componentsThemeToStyle(components, themeName)}
91
- `;
121
+ export const themeToStyle = ({ globals, components }, themeName) => ({
122
+ globals: globalsThemeToStyle(globals, themeName),
123
+ components: createComponentsTheme(components)
124
+ });
92
125
 
93
126
  const useVar = (varName) => `var(${varName})`;
94
127
 
@@ -107,3 +140,5 @@ export const createHelperVars = (theme, prefix) => {
107
140
 
108
141
  return [res.theme, res.useVars, res.vars];
109
142
  };
143
+
144
+ export { componentsThemeManager } from './componentsThemeManager'
package/src/index.cjs.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // CJS is used by screen-renderer-service, we cannot load vaadin there,
2
2
  // so we are exporting only the css generation stuff
3
- export * from './themeHelpers';
3
+ export * from './helpers/themeHelpers';