@descope-ui/common 0.0.18 → 0.1.1
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.
- package/CHANGELOG.md +9 -0
- package/package.json +1 -1
- package/src/componentsHelpers/index.js +35 -23
- package/src/componentsMixins/mixins/createDynamicDataMixin.js +3 -1
- package/src/componentsMixins/mixins/createStyleMixin/helpers.js +1 -2
- package/src/componentsMixins/mixins/createStyleMixin/index.js +10 -6
- package/src/constants.js +1 -1
- package/src/themeHelpers/index.js +20 -11
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
4
|
|
|
5
|
+
## [0.1.1](https://github.com/descope/web-components-ui/compare/@descope-ui/common-0.1.0...@descope-ui/common-0.1.1) (2025-09-29)
|
|
6
|
+
|
|
7
|
+
## [0.1.0](https://github.com/descope/web-components-ui/compare/@descope-ui/common-0.0.18...@descope-ui/common-0.1.0) (2025-09-02)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* Trusted Devices ([#697](https://github.com/descope/web-components-ui/issues/697)) ([fb2f0eb](https://github.com/descope/web-components-ui/commit/fb2f0eb6773ed624354ed9f0b97d713bb8b10fce))
|
|
13
|
+
|
|
5
14
|
## [0.0.18](https://github.com/descope/web-components-ui/compare/@descope-ui/common-0.0.17...@descope-ui/common-0.0.18) (2025-07-21)
|
|
6
15
|
|
|
7
16
|
## [0.0.17](https://github.com/descope/web-components-ui/compare/@descope-ui/common-0.0.16...@descope-ui/common-0.0.17) (2025-07-15)
|
package/package.json
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { kebabCaseJoin } from '../utils';
|
|
2
2
|
import { DESCOPE_PREFIX } from '../constants';
|
|
3
3
|
|
|
4
|
-
export const observeAttributes = (
|
|
4
|
+
export const observeAttributes = (
|
|
5
|
+
ele,
|
|
6
|
+
callback,
|
|
7
|
+
{ excludeAttrs = [], includeAttrs = [] },
|
|
8
|
+
) => {
|
|
5
9
|
// sync all attrs on init
|
|
6
10
|
const filteredAttrs = Array.from(ele.attributes)
|
|
7
11
|
.filter(
|
|
8
12
|
(attr) =>
|
|
9
13
|
!excludeAttrs.includes(attr.name) &&
|
|
10
|
-
(!includeAttrs.length || includeAttrs.includes(attr.name))
|
|
14
|
+
(!includeAttrs.length || includeAttrs.includes(attr.name)),
|
|
11
15
|
)
|
|
12
16
|
.map((attr) => attr.name);
|
|
13
17
|
|
|
@@ -40,24 +44,28 @@ export const observeChildren = (ele, callback) => {
|
|
|
40
44
|
});
|
|
41
45
|
});
|
|
42
46
|
|
|
43
|
-
observer.observe(ele, {
|
|
47
|
+
observer.observe(ele, {
|
|
48
|
+
childList: true,
|
|
49
|
+
characterData: true,
|
|
50
|
+
subtree: true,
|
|
51
|
+
});
|
|
44
52
|
};
|
|
45
53
|
|
|
46
54
|
const createSyncAttrsCb =
|
|
47
55
|
(srcEle, targetEle, mapAttrs = {}) =>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
} else {
|
|
57
|
-
targetEle.removeAttribute(targetAttrName);
|
|
56
|
+
(attrNames) => {
|
|
57
|
+
attrNames.forEach((attrName) => {
|
|
58
|
+
const targetAttrName = mapAttrs[attrName] || attrName;
|
|
59
|
+
const srcAttrVal = srcEle.getAttribute(attrName);
|
|
60
|
+
if (srcAttrVal !== null) {
|
|
61
|
+
if (targetEle.getAttribute(targetAttrName) !== srcAttrVal) {
|
|
62
|
+
targetEle.setAttribute(targetAttrName, srcAttrVal);
|
|
58
63
|
}
|
|
59
|
-
}
|
|
60
|
-
|
|
64
|
+
} else {
|
|
65
|
+
targetEle.removeAttribute(targetAttrName);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
};
|
|
61
69
|
|
|
62
70
|
export const syncAttrs = (ele1, ele2, options) => {
|
|
63
71
|
observeAttributes(ele1, createSyncAttrsCb(ele1, ele2), options);
|
|
@@ -69,7 +77,11 @@ export const getComponentName = (name) => kebabCaseJoin(DESCOPE_PREFIX, name);
|
|
|
69
77
|
export const getCssVarName = (...args) => `--${kebabCaseJoin(...args)}`;
|
|
70
78
|
|
|
71
79
|
export const forwardAttrs = (source, dest, options = {}) => {
|
|
72
|
-
observeAttributes(
|
|
80
|
+
observeAttributes(
|
|
81
|
+
source,
|
|
82
|
+
createSyncAttrsCb(source, dest, options.mapAttrs),
|
|
83
|
+
options,
|
|
84
|
+
);
|
|
73
85
|
};
|
|
74
86
|
|
|
75
87
|
export const forwardProps = (src, target, props = []) => {
|
|
@@ -88,14 +100,13 @@ export const forwardProps = (src, target, props = []) => {
|
|
|
88
100
|
},
|
|
89
101
|
},
|
|
90
102
|
}),
|
|
91
|
-
{}
|
|
103
|
+
{},
|
|
92
104
|
);
|
|
93
105
|
|
|
94
106
|
Object.defineProperties(target, config);
|
|
95
107
|
};
|
|
96
108
|
|
|
97
109
|
export const injectStyle = (cssString, ref, { prepend = false } = {}) => {
|
|
98
|
-
|
|
99
110
|
let style;
|
|
100
111
|
try {
|
|
101
112
|
style = new CSSStyleSheet();
|
|
@@ -109,14 +120,14 @@ export const injectStyle = (cssString, ref, { prepend = false } = {}) => {
|
|
|
109
120
|
style.replaceSync(cssString);
|
|
110
121
|
}
|
|
111
122
|
if (_ref) {
|
|
112
|
-
const adoptedStyleSheets = [...(_ref.adoptedStyleSheets || [])]
|
|
123
|
+
const adoptedStyleSheets = [...(_ref.adoptedStyleSheets || [])];
|
|
113
124
|
adoptedStyleSheets[prepend ? 'unshift' : 'push'](style);
|
|
114
125
|
|
|
115
126
|
_ref.adoptedStyleSheets = adoptedStyleSheets;
|
|
116
127
|
}
|
|
117
128
|
|
|
118
129
|
return style;
|
|
119
|
-
}
|
|
130
|
+
};
|
|
120
131
|
|
|
121
132
|
// we should mimic the CSSStyleSheet API for the fns we are using
|
|
122
133
|
class CSSStyleSheetMock {
|
|
@@ -127,7 +138,7 @@ class CSSStyleSheetMock {
|
|
|
127
138
|
this.ref = ref?.shadowRoot || ref;
|
|
128
139
|
|
|
129
140
|
if (!this.ref) {
|
|
130
|
-
return
|
|
141
|
+
return;
|
|
131
142
|
}
|
|
132
143
|
|
|
133
144
|
if (prepend) {
|
|
@@ -146,7 +157,8 @@ class CSSStyleSheetMock {
|
|
|
146
157
|
}
|
|
147
158
|
}
|
|
148
159
|
|
|
149
|
-
const generateStyleTagFallback = (cssString, ref, { prepend = false } = {}) =>
|
|
160
|
+
const generateStyleTagFallback = (cssString, ref, { prepend = false } = {}) =>
|
|
161
|
+
new CSSStyleSheetMock(cssString, ref, { prepend });
|
|
150
162
|
|
|
151
163
|
export const limitAbbreviation = (str, limit = 2) =>
|
|
152
164
|
str
|
|
@@ -154,4 +166,4 @@ export const limitAbbreviation = (str, limit = 2) =>
|
|
|
154
166
|
.split(' ')
|
|
155
167
|
.splice(0, limit)
|
|
156
168
|
.map((s) => s[0]?.toUpperCase())
|
|
157
|
-
.join('');
|
|
169
|
+
.join('');
|
|
@@ -32,6 +32,7 @@ export const createDynamicDataMixin =
|
|
|
32
32
|
slotName,
|
|
33
33
|
rerenderAttrsList = [],
|
|
34
34
|
targetSelector,
|
|
35
|
+
sortFn = (data) => data,
|
|
35
36
|
}) =>
|
|
36
37
|
(superclass) =>
|
|
37
38
|
class DynamicDataMixinClass extends superclass {
|
|
@@ -62,7 +63,8 @@ export const createDynamicDataMixin =
|
|
|
62
63
|
|
|
63
64
|
#renderItems() {
|
|
64
65
|
this.#removeOldItems();
|
|
65
|
-
this.data.
|
|
66
|
+
const items = sortFn ? this.data.sort(sortFn) : this.data;
|
|
67
|
+
items.forEach((item, index) => {
|
|
66
68
|
const content = getTemplateContent(itemRenderer(item, index, this));
|
|
67
69
|
if (!this.#targetEle) return;
|
|
68
70
|
this.#targetEle.appendChild(content?.cloneNode(true));
|
|
@@ -102,5 +102,4 @@ export const createCssVarsList = (componentName, mappings) =>
|
|
|
102
102
|
|
|
103
103
|
// on some cases we need a selector to be more specific than another
|
|
104
104
|
// for this we have this fn that generate a class selector multiple times
|
|
105
|
-
export const createClassSelectorSpecifier = (className, numOfRepeats) =>
|
|
106
|
-
Array(numOfRepeats).fill(`.${className}`).join('');
|
|
105
|
+
export const createClassSelectorSpecifier = (className, numOfRepeats) => `.${className}`.repeat(numOfRepeats);
|
|
@@ -100,18 +100,22 @@ export const createStyleMixin =
|
|
|
100
100
|
|
|
101
101
|
#createOverridesStyle() {
|
|
102
102
|
if (this.#styleAttributes.length) {
|
|
103
|
-
|
|
103
|
+
let classSpecifier = createClassSelectorSpecifier(
|
|
104
104
|
componentName,
|
|
105
|
-
CSS_SELECTOR_SPECIFIER_MULTIPLY
|
|
105
|
+
CSS_SELECTOR_SPECIFIER_MULTIPLY
|
|
106
106
|
);
|
|
107
|
+
const elementId = this.getAttribute('id');
|
|
108
|
+
if (elementId) {
|
|
109
|
+
// basically this is enough to make the selector more specific
|
|
110
|
+
// but just in case there is no id, we will also add the class multiple times
|
|
111
|
+
classSpecifier += `#${elementId}`;
|
|
112
|
+
}
|
|
107
113
|
|
|
108
|
-
this.#overrideStyleEle = injectStyle(
|
|
109
|
-
`:host(${classSpecifier}) {}`,
|
|
110
|
-
this.#rootElement,
|
|
111
|
-
);
|
|
114
|
+
this.#overrideStyleEle = injectStyle(`:host(${classSpecifier}) {}`, this.#rootElement);
|
|
112
115
|
}
|
|
113
116
|
}
|
|
114
117
|
|
|
118
|
+
|
|
115
119
|
#setAttrOverride(attrName, value) {
|
|
116
120
|
const style = this.#overrideStyleEle?.cssRules[0].style;
|
|
117
121
|
|
package/src/constants.js
CHANGED
|
@@ -71,6 +71,15 @@ export const globalsThemeToStyle = (theme, themeName = '') => {
|
|
|
71
71
|
return `*[data-theme="${themeName}"] {${style}}`;
|
|
72
72
|
};
|
|
73
73
|
|
|
74
|
+
function splitAmpersands(str) {
|
|
75
|
+
const match = str.match(/^(&+)?(.*)$/);
|
|
76
|
+
return [match[1] || "", (match[2] || "")];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// st attributes are also using selector multiplication
|
|
80
|
+
// so we need to limit the multiplication
|
|
81
|
+
const MAX_SELECTOR_MULTIPLY = 3;
|
|
82
|
+
|
|
74
83
|
const componentsThemeToStyleObj = (componentsTheme) =>
|
|
75
84
|
transformTheme(componentsTheme, [], (path, val) => {
|
|
76
85
|
const [component, ...restPath] = path;
|
|
@@ -79,10 +88,7 @@ const componentsThemeToStyleObj = (componentsTheme) =>
|
|
|
79
88
|
|
|
80
89
|
if (property === 'undefined') {
|
|
81
90
|
// eslint-disable-next-line no-console
|
|
82
|
-
console.warn(
|
|
83
|
-
componentName,
|
|
84
|
-
`theme value: "${val}" is mapped to an invalid property`,
|
|
85
|
-
);
|
|
91
|
+
console.warn(componentName, `theme value: "${val}" is mapped to an invalid property`);
|
|
86
92
|
}
|
|
87
93
|
|
|
88
94
|
// we need a support for portal components theme (e.g. overlay)
|
|
@@ -98,23 +104,26 @@ const componentsThemeToStyleObj = (componentsTheme) =>
|
|
|
98
104
|
// do not start with underscore -> key:value, must have 2 no underscore attrs in a row
|
|
99
105
|
// starts with underscore -> attribute selector
|
|
100
106
|
const attrsSelector = restPath.reduce((acc, section, idx) => {
|
|
101
|
-
|
|
102
|
-
|
|
107
|
+
const [ampersands, content] = splitAmpersands(section);
|
|
108
|
+
const selectorMultiplier = Math.min(
|
|
109
|
+
ampersands.length + 1, // if there are no & we need to multiply by 1
|
|
110
|
+
MAX_SELECTOR_MULTIPLY
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
if (content.startsWith('_')) return acc + `[${kebabCase(content.replace(/^_/, ''))}="true"]`.repeat(selectorMultiplier);
|
|
103
114
|
|
|
104
115
|
const nextSection = restPath[idx + 1];
|
|
105
116
|
|
|
106
|
-
if (typeof nextSection !== 'string' || nextSection.startsWith('_')) {
|
|
117
|
+
if (typeof nextSection !== 'string' || nextSection.startsWith('_') || nextSection.startsWith('&')) {
|
|
107
118
|
// eslint-disable-next-line no-console
|
|
108
119
|
console.error(
|
|
109
120
|
'theme generator',
|
|
110
|
-
`your theme structure is invalid, attribute "${section}" is followed by "${nextSection}" which is not allowed
|
|
121
|
+
`your theme structure is invalid, attribute "${section}" is followed by "${nextSection}" which is not allowed`
|
|
111
122
|
);
|
|
112
123
|
return acc;
|
|
113
124
|
}
|
|
114
125
|
|
|
115
|
-
return
|
|
116
|
-
.splice(idx + 1, 1)
|
|
117
|
-
.join('')}"]`;
|
|
126
|
+
return acc + `[${kebabCase(content)}="${restPath.splice(idx + 1, 1).join('')}"]`.repeat(selectorMultiplier);
|
|
118
127
|
}, '');
|
|
119
128
|
|
|
120
129
|
const selector = `:host${attrsSelector ? `(${attrsSelector})` : ''}`;
|