stoop 0.4.0 → 0.4.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/dist/create-stoop-ssr.js +16 -26
- package/dist/create-stoop.js +16 -156
- package/package.json +9 -7
- package/dist/api/core-api.js +0 -135
- package/dist/api/global-css.js +0 -42
- package/dist/api/styled.js +0 -419
- package/dist/api/theme-provider.js +0 -223
- package/dist/constants.js +0 -154
- package/dist/core/cache.js +0 -68
- package/dist/core/compiler.js +0 -206
- package/dist/core/theme-manager.js +0 -107
- package/dist/core/variants.js +0 -38
- package/dist/create-stoop-internal.js +0 -123
- package/dist/inject.js +0 -308
- package/dist/types/index.js +0 -5
- package/dist/utils/auto-preload.js +0 -167
- package/dist/utils/helpers.js +0 -150
- package/dist/utils/storage.js +0 -396
- package/dist/utils/theme-utils.js +0 -353
- package/dist/utils/theme.js +0 -304
package/dist/constants.js
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared constants used throughout the library.
|
|
3
|
-
* Includes cache size limits and nesting depth limits.
|
|
4
|
-
*/
|
|
5
|
-
export const EMPTY_CSS = Object.freeze({});
|
|
6
|
-
export const MAX_CSS_CACHE_SIZE = 10000;
|
|
7
|
-
export const MAX_CLASS_NAME_CACHE_SIZE = 5000;
|
|
8
|
-
export const MAX_CSS_NESTING_DEPTH = 10;
|
|
9
|
-
/**
|
|
10
|
-
* Cache size limits for various LRU caches.
|
|
11
|
-
*/
|
|
12
|
-
export const KEYFRAME_CACHE_LIMIT = 500;
|
|
13
|
-
export const SANITIZE_CACHE_SIZE_LIMIT = 1000;
|
|
14
|
-
/**
|
|
15
|
-
* Cookie defaults.
|
|
16
|
-
*/
|
|
17
|
-
export const DEFAULT_COOKIE_MAX_AGE = 31536000; // 1 year in seconds
|
|
18
|
-
export const DEFAULT_COOKIE_PATH = "/";
|
|
19
|
-
/**
|
|
20
|
-
* Approved theme scales - only these scales are allowed in theme objects.
|
|
21
|
-
*/
|
|
22
|
-
export const APPROVED_THEME_SCALES = [
|
|
23
|
-
"colors",
|
|
24
|
-
"opacities",
|
|
25
|
-
"space",
|
|
26
|
-
"radii",
|
|
27
|
-
"sizes",
|
|
28
|
-
"fonts",
|
|
29
|
-
"fontWeights",
|
|
30
|
-
"fontSizes",
|
|
31
|
-
"lineHeights",
|
|
32
|
-
"letterSpacings",
|
|
33
|
-
"shadows",
|
|
34
|
-
"zIndices",
|
|
35
|
-
"transitions",
|
|
36
|
-
];
|
|
37
|
-
/**
|
|
38
|
-
* Default themeMap mapping CSS properties to theme scales.
|
|
39
|
-
* Covers common CSS properties for zero-config experience.
|
|
40
|
-
* Missing properties gracefully fallback to pattern-based auto-detection.
|
|
41
|
-
*/
|
|
42
|
-
export const DEFAULT_THEME_MAP = {
|
|
43
|
-
accentColor: "colors",
|
|
44
|
-
animation: "transitions",
|
|
45
|
-
animationDelay: "transitions",
|
|
46
|
-
animationDuration: "transitions",
|
|
47
|
-
animationTimingFunction: "transitions",
|
|
48
|
-
backdropFilter: "shadows",
|
|
49
|
-
background: "colors",
|
|
50
|
-
backgroundColor: "colors",
|
|
51
|
-
blockSize: "sizes",
|
|
52
|
-
border: "colors",
|
|
53
|
-
borderBlockColor: "colors",
|
|
54
|
-
borderBlockEndColor: "colors",
|
|
55
|
-
borderBlockStartColor: "colors",
|
|
56
|
-
borderBottomColor: "colors",
|
|
57
|
-
borderBottomLeftRadius: "radii",
|
|
58
|
-
borderBottomRightRadius: "radii",
|
|
59
|
-
borderColor: "colors",
|
|
60
|
-
borderEndEndRadius: "radii",
|
|
61
|
-
borderEndStartRadius: "radii",
|
|
62
|
-
borderInlineColor: "colors",
|
|
63
|
-
borderInlineEndColor: "colors",
|
|
64
|
-
borderInlineStartColor: "colors",
|
|
65
|
-
borderLeftColor: "colors",
|
|
66
|
-
borderRadius: "radii",
|
|
67
|
-
borderRightColor: "colors",
|
|
68
|
-
borderStartEndRadius: "radii",
|
|
69
|
-
borderStartStartRadius: "radii",
|
|
70
|
-
borderTopColor: "colors",
|
|
71
|
-
borderTopLeftRadius: "radii",
|
|
72
|
-
borderTopRightRadius: "radii",
|
|
73
|
-
bottom: "space",
|
|
74
|
-
boxShadow: "shadows",
|
|
75
|
-
caretColor: "colors",
|
|
76
|
-
color: "colors",
|
|
77
|
-
columnGap: "space",
|
|
78
|
-
columnRuleColor: "colors",
|
|
79
|
-
fill: "colors",
|
|
80
|
-
filter: "shadows",
|
|
81
|
-
flexBasis: "sizes",
|
|
82
|
-
floodColor: "colors",
|
|
83
|
-
font: "fontSizes",
|
|
84
|
-
fontFamily: "fonts",
|
|
85
|
-
fontSize: "fontSizes",
|
|
86
|
-
fontWeight: "fontWeights",
|
|
87
|
-
gap: "space",
|
|
88
|
-
gridColumnGap: "space",
|
|
89
|
-
gridGap: "space",
|
|
90
|
-
gridRowGap: "space",
|
|
91
|
-
height: "sizes",
|
|
92
|
-
inlineSize: "sizes",
|
|
93
|
-
inset: "space",
|
|
94
|
-
insetBlock: "space",
|
|
95
|
-
insetBlockEnd: "space",
|
|
96
|
-
insetBlockStart: "space",
|
|
97
|
-
insetInline: "space",
|
|
98
|
-
insetInlineEnd: "space",
|
|
99
|
-
insetInlineStart: "space",
|
|
100
|
-
left: "space",
|
|
101
|
-
letterSpacing: "letterSpacings",
|
|
102
|
-
lightingColor: "colors",
|
|
103
|
-
lineHeight: "lineHeights",
|
|
104
|
-
margin: "space",
|
|
105
|
-
marginBlock: "space",
|
|
106
|
-
marginBlockEnd: "space",
|
|
107
|
-
marginBlockStart: "space",
|
|
108
|
-
marginBottom: "space",
|
|
109
|
-
marginInline: "space",
|
|
110
|
-
marginInlineEnd: "space",
|
|
111
|
-
marginInlineStart: "space",
|
|
112
|
-
marginLeft: "space",
|
|
113
|
-
marginRight: "space",
|
|
114
|
-
marginTop: "space",
|
|
115
|
-
maxBlockSize: "sizes",
|
|
116
|
-
maxHeight: "sizes",
|
|
117
|
-
maxInlineSize: "sizes",
|
|
118
|
-
maxWidth: "sizes",
|
|
119
|
-
minBlockSize: "sizes",
|
|
120
|
-
minHeight: "sizes",
|
|
121
|
-
minInlineSize: "sizes",
|
|
122
|
-
minWidth: "sizes",
|
|
123
|
-
opacity: "opacities",
|
|
124
|
-
outline: "colors",
|
|
125
|
-
outlineColor: "colors",
|
|
126
|
-
padding: "space",
|
|
127
|
-
paddingBlock: "space",
|
|
128
|
-
paddingBlockEnd: "space",
|
|
129
|
-
paddingBlockStart: "space",
|
|
130
|
-
paddingBottom: "space",
|
|
131
|
-
paddingInline: "space",
|
|
132
|
-
paddingInlineEnd: "space",
|
|
133
|
-
paddingInlineStart: "space",
|
|
134
|
-
paddingLeft: "space",
|
|
135
|
-
paddingRight: "space",
|
|
136
|
-
paddingTop: "space",
|
|
137
|
-
right: "space",
|
|
138
|
-
rowGap: "space",
|
|
139
|
-
size: "sizes",
|
|
140
|
-
stopColor: "colors",
|
|
141
|
-
stroke: "colors",
|
|
142
|
-
textDecorationColor: "colors",
|
|
143
|
-
textEmphasisColor: "colors",
|
|
144
|
-
textShadow: "shadows",
|
|
145
|
-
top: "space",
|
|
146
|
-
transition: "transitions",
|
|
147
|
-
transitionDelay: "transitions",
|
|
148
|
-
transitionDuration: "transitions",
|
|
149
|
-
transitionProperty: "transitions",
|
|
150
|
-
transitionTimingFunction: "transitions",
|
|
151
|
-
width: "sizes",
|
|
152
|
-
zIndex: "zIndices",
|
|
153
|
-
};
|
|
154
|
-
export const STOOP_COMPONENT_SYMBOL = Symbol.for("stoop.component");
|
package/dist/core/cache.js
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CSS compilation caching system.
|
|
3
|
-
* Tracks compiled CSS strings and class names to prevent duplicate work.
|
|
4
|
-
* Implements LRU (Least Recently Used) eviction when cache size limits are exceeded.
|
|
5
|
-
*/
|
|
6
|
-
import { MAX_CLASS_NAME_CACHE_SIZE, MAX_CSS_CACHE_SIZE } from "../constants";
|
|
7
|
-
/**
|
|
8
|
-
* LRU Cache implementation for class names and CSS strings.
|
|
9
|
-
* Automatically evicts least recently used entries when size limit is exceeded.
|
|
10
|
-
*/
|
|
11
|
-
export class LRUCache extends Map {
|
|
12
|
-
maxSize;
|
|
13
|
-
constructor(maxSize) {
|
|
14
|
-
super();
|
|
15
|
-
this.maxSize = maxSize;
|
|
16
|
-
}
|
|
17
|
-
get(key) {
|
|
18
|
-
const value = super.get(key);
|
|
19
|
-
if (value !== undefined) {
|
|
20
|
-
super.delete(key);
|
|
21
|
-
super.set(key, value);
|
|
22
|
-
}
|
|
23
|
-
return value;
|
|
24
|
-
}
|
|
25
|
-
set(key, value) {
|
|
26
|
-
if (super.has(key)) {
|
|
27
|
-
super.delete(key);
|
|
28
|
-
}
|
|
29
|
-
else if (this.size >= this.maxSize) {
|
|
30
|
-
const firstKey = this.keys().next().value;
|
|
31
|
-
if (firstKey !== undefined) {
|
|
32
|
-
super.delete(firstKey);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
super.set(key, value);
|
|
36
|
-
return this;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
export const classNameCache = new LRUCache(MAX_CLASS_NAME_CACHE_SIZE);
|
|
40
|
-
export const cssStringCache = new LRUCache(MAX_CSS_CACHE_SIZE);
|
|
41
|
-
// Separate cache for tracking injected CSS strings (used by browser injection)
|
|
42
|
-
const injectedStylesCache = new Set();
|
|
43
|
-
/**
|
|
44
|
-
* Checks if a CSS string has been injected.
|
|
45
|
-
* Uses a Set for O(1) lookup.
|
|
46
|
-
*
|
|
47
|
-
* @param css - CSS string to check
|
|
48
|
-
* @returns True if CSS has been injected
|
|
49
|
-
*/
|
|
50
|
-
export function isCachedStyle(css) {
|
|
51
|
-
return injectedStylesCache.has(css);
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Marks a CSS string as injected.
|
|
55
|
-
*
|
|
56
|
-
* @param css - CSS string to cache
|
|
57
|
-
*/
|
|
58
|
-
export function markStyleAsCached(css) {
|
|
59
|
-
injectedStylesCache.add(css);
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Clears all cached styles.
|
|
63
|
-
*/
|
|
64
|
-
export function clearStyleCache() {
|
|
65
|
-
classNameCache.clear();
|
|
66
|
-
cssStringCache.clear();
|
|
67
|
-
injectedStylesCache.clear();
|
|
68
|
-
}
|
package/dist/core/compiler.js
DELETED
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CSS compilation engine.
|
|
3
|
-
* Converts CSS objects to CSS strings and generates unique class names.
|
|
4
|
-
* Handles nested selectors, media queries, styled component targeting, and theme tokens.
|
|
5
|
-
*/
|
|
6
|
-
import { MAX_CSS_NESTING_DEPTH, STOOP_COMPONENT_SYMBOL } from "../constants";
|
|
7
|
-
import { injectCSS } from "../inject";
|
|
8
|
-
import { isValidCSSObject, applyUtilities, isStyledComponentRef } from "../utils/helpers";
|
|
9
|
-
import { replaceThemeTokensWithVars } from "../utils/theme";
|
|
10
|
-
import { escapeCSSValue, hash, sanitizeCSSPropertyName, sanitizeCSSSelector, sanitizeMediaQuery, sanitizePrefix, } from "../utils/theme-utils";
|
|
11
|
-
import { classNameCache, cssStringCache } from "./cache";
|
|
12
|
-
/**
|
|
13
|
-
* Checks if a key/value pair represents a styled component reference.
|
|
14
|
-
* Uses shared isStyledComponentRef helper for consistency.
|
|
15
|
-
*
|
|
16
|
-
* @param key - Property key to check
|
|
17
|
-
* @param value - Property value to check
|
|
18
|
-
* @returns True if key/value represents a styled component reference
|
|
19
|
-
*/
|
|
20
|
-
function isStyledComponentKey(key, value) {
|
|
21
|
-
// Check symbol key
|
|
22
|
-
if (typeof key === "symbol" && key === STOOP_COMPONENT_SYMBOL) {
|
|
23
|
-
return true;
|
|
24
|
-
}
|
|
25
|
-
// Check if value is a styled component ref (consolidated check)
|
|
26
|
-
if (isStyledComponentRef(value)) {
|
|
27
|
-
return true;
|
|
28
|
-
}
|
|
29
|
-
// Check string key prefix
|
|
30
|
-
if (typeof key === "string" && key.startsWith("__STOOP_COMPONENT_")) {
|
|
31
|
-
return true;
|
|
32
|
-
}
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Extracts the class name from a styled component reference.
|
|
37
|
-
*
|
|
38
|
-
* @param key - Property key
|
|
39
|
-
* @param value - Property value
|
|
40
|
-
* @returns Extracted class name or empty string
|
|
41
|
-
*/
|
|
42
|
-
function getClassNameFromKeyOrValue(key, value) {
|
|
43
|
-
if (typeof value === "object" &&
|
|
44
|
-
value !== null &&
|
|
45
|
-
"__stoopClassName" in value &&
|
|
46
|
-
typeof value.__stoopClassName === "string") {
|
|
47
|
-
return value.__stoopClassName;
|
|
48
|
-
}
|
|
49
|
-
if (typeof key === "string" && key.startsWith("__STOOP_COMPONENT_")) {
|
|
50
|
-
return key.replace("__STOOP_COMPONENT_", "");
|
|
51
|
-
}
|
|
52
|
-
return "";
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Converts a CSS object to a CSS string with proper nesting and selectors.
|
|
56
|
-
* Handles pseudo-selectors, media queries, combinators, and styled component targeting.
|
|
57
|
-
*
|
|
58
|
-
* @param obj - CSS object to convert
|
|
59
|
-
* @param selector - Current selector context
|
|
60
|
-
* @param depth - Current nesting depth
|
|
61
|
-
* @param media - Media query breakpoints
|
|
62
|
-
* @returns CSS string
|
|
63
|
-
*/
|
|
64
|
-
export function cssObjectToString(obj, selector = "", depth = 0, media) {
|
|
65
|
-
if (!obj || typeof obj !== "object") {
|
|
66
|
-
return "";
|
|
67
|
-
}
|
|
68
|
-
if (depth > MAX_CSS_NESTING_DEPTH) {
|
|
69
|
-
return "";
|
|
70
|
-
}
|
|
71
|
-
const cssProperties = [];
|
|
72
|
-
const nestedRulesList = [];
|
|
73
|
-
const keys = Object.keys(obj).sort();
|
|
74
|
-
for (const key of keys) {
|
|
75
|
-
const value = obj[key];
|
|
76
|
-
if (isStyledComponentKey(key, value)) {
|
|
77
|
-
const componentClassName = getClassNameFromKeyOrValue(key, value);
|
|
78
|
-
if (!componentClassName) {
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
const sanitizedClassName = sanitizeCSSSelector(componentClassName);
|
|
82
|
-
if (!sanitizedClassName) {
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
const componentSelector = selector
|
|
86
|
-
? `${selector} .${sanitizedClassName}`
|
|
87
|
-
: `.${sanitizedClassName}`;
|
|
88
|
-
const nestedCss = isValidCSSObject(value)
|
|
89
|
-
? cssObjectToString(value, componentSelector, depth + 1, media)
|
|
90
|
-
: "";
|
|
91
|
-
if (nestedCss) {
|
|
92
|
-
nestedRulesList.push(nestedCss);
|
|
93
|
-
}
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
if (isValidCSSObject(value)) {
|
|
97
|
-
if (media && key in media) {
|
|
98
|
-
const mediaQuery = sanitizeMediaQuery(media[key]);
|
|
99
|
-
if (mediaQuery) {
|
|
100
|
-
const nestedCss = cssObjectToString(value, selector, depth + 1, media);
|
|
101
|
-
if (nestedCss) {
|
|
102
|
-
nestedRulesList.push(`${mediaQuery} { ${nestedCss} }`);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
else if (key.startsWith("@")) {
|
|
107
|
-
const sanitizedKey = sanitizeCSSSelector(key);
|
|
108
|
-
if (sanitizedKey) {
|
|
109
|
-
const nestedCss = cssObjectToString(value, selector, depth + 1, media);
|
|
110
|
-
if (nestedCss) {
|
|
111
|
-
nestedRulesList.push(`${sanitizedKey} { ${nestedCss} }`);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
else if (key.includes("&")) {
|
|
116
|
-
const sanitizedKey = sanitizeCSSSelector(key);
|
|
117
|
-
if (sanitizedKey) {
|
|
118
|
-
const parts = sanitizedKey.split("&");
|
|
119
|
-
const nestedSelector = parts.join(selector);
|
|
120
|
-
const nestedCss = cssObjectToString(value, nestedSelector, depth + 1, media);
|
|
121
|
-
if (nestedCss) {
|
|
122
|
-
nestedRulesList.push(nestedCss);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
else if (key.startsWith(":")) {
|
|
127
|
-
const sanitizedKey = sanitizeCSSSelector(key);
|
|
128
|
-
if (sanitizedKey) {
|
|
129
|
-
const nestedSelector = `${selector}${sanitizedKey}`;
|
|
130
|
-
const nestedCss = cssObjectToString(value, nestedSelector, depth + 1, media);
|
|
131
|
-
if (nestedCss) {
|
|
132
|
-
nestedRulesList.push(nestedCss);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
else if (key.includes(" ") || key.includes(">") || key.includes("+") || key.includes("~")) {
|
|
137
|
-
const sanitizedKey = sanitizeCSSSelector(key);
|
|
138
|
-
if (sanitizedKey) {
|
|
139
|
-
const startsWithCombinator = /^[\s>+~]/.test(sanitizedKey.trim());
|
|
140
|
-
const nestedSelector = startsWithCombinator
|
|
141
|
-
? `${selector}${sanitizedKey}`
|
|
142
|
-
: `${selector} ${sanitizedKey}`;
|
|
143
|
-
const nestedCss = cssObjectToString(value, nestedSelector, depth + 1, media);
|
|
144
|
-
if (nestedCss) {
|
|
145
|
-
nestedRulesList.push(nestedCss);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
else {
|
|
150
|
-
const sanitizedKey = sanitizeCSSSelector(key);
|
|
151
|
-
if (sanitizedKey) {
|
|
152
|
-
const nestedSelector = selector ? `${selector} ${sanitizedKey}` : sanitizedKey;
|
|
153
|
-
const nestedCss = cssObjectToString(value, nestedSelector, depth + 1, media);
|
|
154
|
-
if (nestedCss) {
|
|
155
|
-
nestedRulesList.push(nestedCss);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
else if (value !== undefined) {
|
|
161
|
-
const property = sanitizeCSSPropertyName(key);
|
|
162
|
-
if (property && (typeof value === "string" || typeof value === "number")) {
|
|
163
|
-
const escapedValue = escapeCSSValue(value);
|
|
164
|
-
cssProperties.push(`${property}: ${escapedValue};`);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
// Use array join pattern for better performance with large stylesheets
|
|
169
|
-
const parts = [];
|
|
170
|
-
if (cssProperties.length > 0) {
|
|
171
|
-
parts.push(`${selector} { ${cssProperties.join(" ")} }`);
|
|
172
|
-
}
|
|
173
|
-
parts.push(...nestedRulesList);
|
|
174
|
-
return parts.join("");
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Compiles CSS objects into CSS strings and generates unique class names.
|
|
178
|
-
* Handles nested selectors, media queries, styled component targeting, and theme tokens.
|
|
179
|
-
*
|
|
180
|
-
* @param styles - CSS object to compile
|
|
181
|
-
* @param currentTheme - Theme for token resolution
|
|
182
|
-
* @param prefix - Optional prefix for generated class names
|
|
183
|
-
* @param media - Optional media query breakpoints
|
|
184
|
-
* @param utils - Optional utility functions
|
|
185
|
-
* @param themeMap - Optional theme scale mappings
|
|
186
|
-
* @returns Generated class name
|
|
187
|
-
*/
|
|
188
|
-
export function compileCSS(styles, currentTheme, prefix = "stoop", media, utils, themeMap) {
|
|
189
|
-
const sanitizedPrefix = sanitizePrefix(prefix);
|
|
190
|
-
const stylesWithUtils = applyUtilities(styles, utils);
|
|
191
|
-
const themedStyles = replaceThemeTokensWithVars(stylesWithUtils, currentTheme, themeMap);
|
|
192
|
-
const cssString = cssObjectToString(themedStyles, "", 0, media);
|
|
193
|
-
const stylesHash = hash(cssString);
|
|
194
|
-
const className = sanitizedPrefix ? `${sanitizedPrefix}-${stylesHash}` : `css-${stylesHash}`;
|
|
195
|
-
const cacheKey = `${sanitizedPrefix}:${className}`;
|
|
196
|
-
const cachedCSS = cssStringCache.get(cacheKey);
|
|
197
|
-
if (cachedCSS) {
|
|
198
|
-
injectCSS(cachedCSS, sanitizedPrefix, cacheKey);
|
|
199
|
-
return className;
|
|
200
|
-
}
|
|
201
|
-
const fullCSS = cssObjectToString(themedStyles, `.${className}`, 0, media);
|
|
202
|
-
cssStringCache.set(cacheKey, fullCSS);
|
|
203
|
-
classNameCache.set(cacheKey, className);
|
|
204
|
-
injectCSS(fullCSS, sanitizedPrefix, cacheKey);
|
|
205
|
-
return className;
|
|
206
|
-
}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Theme variable management.
|
|
3
|
-
* Updates CSS custom properties when theme changes.
|
|
4
|
-
* Ensures CSS variables are injected and kept in sync with theme updates.
|
|
5
|
-
* Automatically merges themes with the default theme when applied.
|
|
6
|
-
*/
|
|
7
|
-
import { injectAllThemeVariables } from "../inject";
|
|
8
|
-
import { isBrowser } from "../utils/helpers";
|
|
9
|
-
import { generateAllThemeVariables, themesAreEqual } from "../utils/theme";
|
|
10
|
-
const defaultThemes = new Map();
|
|
11
|
-
/**
|
|
12
|
-
* Registers the default theme for a given prefix.
|
|
13
|
-
* Called automatically by createStoop.
|
|
14
|
-
*
|
|
15
|
-
* @param theme - Default theme from createStoop
|
|
16
|
-
* @param prefix - Optional prefix for theme scoping
|
|
17
|
-
*/
|
|
18
|
-
export function registerDefaultTheme(theme, prefix = "stoop") {
|
|
19
|
-
const sanitizedPrefix = prefix || "";
|
|
20
|
-
defaultThemes.set(sanitizedPrefix, theme);
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Gets the default theme for a given prefix.
|
|
24
|
-
*
|
|
25
|
-
* @param prefix - Optional prefix for theme scoping
|
|
26
|
-
* @returns Default theme or null if not registered
|
|
27
|
-
*/
|
|
28
|
-
export function getDefaultTheme(prefix = "stoop") {
|
|
29
|
-
const sanitizedPrefix = prefix || "";
|
|
30
|
-
return defaultThemes.get(sanitizedPrefix) || null;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Core theme merging logic.
|
|
34
|
-
* Merges source theme into target theme, handling nested objects.
|
|
35
|
-
* Shared implementation used by both mergeWithDefaultTheme and createTheme.
|
|
36
|
-
*
|
|
37
|
-
* @param target - Target theme to merge into
|
|
38
|
-
* @param source - Source theme to merge from
|
|
39
|
-
* @returns Merged theme
|
|
40
|
-
*/
|
|
41
|
-
export function mergeThemes(target, source) {
|
|
42
|
-
const merged = { ...target };
|
|
43
|
-
const sourceKeys = Object.keys(source);
|
|
44
|
-
for (const key of sourceKeys) {
|
|
45
|
-
if (key === "media") {
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
const sourceValue = source[key];
|
|
49
|
-
const targetValue = target[key];
|
|
50
|
-
if (sourceValue &&
|
|
51
|
-
typeof sourceValue === "object" &&
|
|
52
|
-
!Array.isArray(sourceValue) &&
|
|
53
|
-
targetValue &&
|
|
54
|
-
typeof targetValue === "object" &&
|
|
55
|
-
!Array.isArray(targetValue)) {
|
|
56
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
57
|
-
merged[key] = { ...targetValue, ...sourceValue };
|
|
58
|
-
}
|
|
59
|
-
else if (sourceValue !== undefined) {
|
|
60
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
61
|
-
merged[key] = sourceValue;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return merged;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Merges a theme with the default theme if it's not already the default theme.
|
|
68
|
-
* This ensures all themes extend the default theme, keeping all original properties.
|
|
69
|
-
* Uses caching to avoid repeated merging of the same theme objects.
|
|
70
|
-
*
|
|
71
|
-
* @param theme - Theme to merge
|
|
72
|
-
* @param prefix - Optional prefix for theme scoping
|
|
73
|
-
* @returns Merged theme (or original if it's the default theme)
|
|
74
|
-
*/
|
|
75
|
-
export function mergeWithDefaultTheme(theme, prefix = "stoop") {
|
|
76
|
-
const defaultTheme = getDefaultTheme(prefix);
|
|
77
|
-
if (!defaultTheme) {
|
|
78
|
-
return theme;
|
|
79
|
-
}
|
|
80
|
-
if (themesAreEqual(theme, defaultTheme)) {
|
|
81
|
-
return theme;
|
|
82
|
-
}
|
|
83
|
-
// Merge theme with default theme
|
|
84
|
-
return mergeThemes(defaultTheme, theme);
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Injects CSS variables for all themes using attribute selectors.
|
|
88
|
-
* This allows all themes to be available simultaneously, with theme switching
|
|
89
|
-
* handled by changing the data-theme attribute. This prevents layout shifts
|
|
90
|
-
* and eliminates the need to replace CSS variables on theme change.
|
|
91
|
-
*
|
|
92
|
-
* @param themes - Map of theme names to theme objects
|
|
93
|
-
* @param prefix - Optional prefix for CSS variable names
|
|
94
|
-
* @param attribute - Attribute name for theme selection (defaults to 'data-theme')
|
|
95
|
-
*/
|
|
96
|
-
export function injectAllThemes(themes, prefix = "stoop", attribute = "data-theme") {
|
|
97
|
-
if (!isBrowser()) {
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
// Merge all themes with default theme
|
|
101
|
-
const mergedThemes = {};
|
|
102
|
-
for (const [themeName, theme] of Object.entries(themes)) {
|
|
103
|
-
mergedThemes[themeName] = mergeWithDefaultTheme(theme, prefix);
|
|
104
|
-
}
|
|
105
|
-
const allThemeVars = generateAllThemeVariables(mergedThemes, prefix, attribute);
|
|
106
|
-
injectAllThemeVariables(allThemeVars, prefix);
|
|
107
|
-
}
|
package/dist/core/variants.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Variant application logic.
|
|
3
|
-
* Merges variant styles with base styles based on component props.
|
|
4
|
-
* Optimized to avoid unnecessary object spreads when no variants are applied.
|
|
5
|
-
*/
|
|
6
|
-
/**
|
|
7
|
-
* Applies variant styles to base styles based on component props.
|
|
8
|
-
*
|
|
9
|
-
* @param variants - Variant configuration object
|
|
10
|
-
* @param props - Component props containing variant values
|
|
11
|
-
* @param baseStyles - Base styles to merge with variant styles
|
|
12
|
-
* @returns Merged CSS object
|
|
13
|
-
*/
|
|
14
|
-
export function applyVariants(variants, props, baseStyles) {
|
|
15
|
-
let hasVariants = false;
|
|
16
|
-
const appliedVariantStyles = [];
|
|
17
|
-
for (const variantName in variants) {
|
|
18
|
-
const propValue = props[variantName];
|
|
19
|
-
if (propValue === undefined) {
|
|
20
|
-
continue;
|
|
21
|
-
}
|
|
22
|
-
const variantOptions = variants[variantName];
|
|
23
|
-
const key = propValue === true ? "true" : propValue === false ? "false" : String(propValue);
|
|
24
|
-
if (variantOptions[key]) {
|
|
25
|
-
appliedVariantStyles.push(variantOptions[key]);
|
|
26
|
-
hasVariants = true;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
if (!hasVariants) {
|
|
30
|
-
return baseStyles;
|
|
31
|
-
}
|
|
32
|
-
// Merge all variant styles into a single object using spread for consistency
|
|
33
|
-
const mergedVariants = { ...appliedVariantStyles[0] };
|
|
34
|
-
for (let i = 1; i < appliedVariantStyles.length; i++) {
|
|
35
|
-
Object.assign(mergedVariants, appliedVariantStyles[i]);
|
|
36
|
-
}
|
|
37
|
-
return { ...baseStyles, ...mergedVariants };
|
|
38
|
-
}
|