@descope/web-components-ui 1.0.61 → 1.0.63

Sign up to get free protection for your applications and to get access to all the features.
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';