uraniyum 1.1.9 → 1.2.0

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/src/styles.ts CHANGED
@@ -11,25 +11,9 @@ export interface StyleRule {
11
11
  export type StylesMap = Record<string, StyleRule>;
12
12
  export type ClassNames<T extends StylesMap> = { [K in keyof T]: string };
13
13
 
14
- const styleCache = new Map<string, true>();
15
- const keyframeCache = new Map<string, string>();
16
14
  let styleElement: HTMLStyleElement | null = null;
17
15
  let styleSheet: CSSStyleSheet | null = null;
18
16
 
19
- const ALLOWED_PREFIXES = [
20
- "root",
21
- "button",
22
- "icon",
23
- "text",
24
- "container",
25
- "wrapper",
26
- "card",
27
- "header",
28
- "section",
29
- ];
30
-
31
- const KEYFRAMES_PREFIX = "@keyframes ";
32
-
33
17
  function ensureStyleSheet(): CSSStyleSheet {
34
18
  if (!styleSheet) {
35
19
  if (!styleElement) {
@@ -42,43 +26,73 @@ function ensureStyleSheet(): CSSStyleSheet {
42
26
  return styleSheet;
43
27
  }
44
28
 
45
- function hashString(str: string): string {
46
- let hash = 5381;
47
- for (let i = 0; i < str.length; i++) {
48
- hash = (hash * 33) ^ str.charCodeAt(i);
49
- }
50
- return (hash >>> 0).toString(36);
51
- }
52
-
53
29
  const camelToKebabCache = new Map<string, string>();
54
30
 
55
31
  function camelToKebab(str: string): string {
56
32
  const cached = camelToKebabCache.get(str);
57
33
  if (cached) return cached;
58
34
 
59
- const result = str.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
35
+ const result = str.replace(/[A-Z]/g, m => "-" + m.toLowerCase());
60
36
  camelToKebabCache.set(str, result);
61
37
  return result;
62
38
  }
63
39
 
64
- function stableHashRule(rule: Record<string, any>): string {
65
- const parts: string[] = [];
40
+ type AtomicKey = string;
66
41
 
67
- for (const key in rule) {
68
- if (!Object.prototype.hasOwnProperty.call(rule, key)) continue;
69
- const val = rule[key];
42
+ const atomicClassCache = new Map<AtomicKey, string>();
43
+ let atomicCounter = 0;
70
44
 
71
- if (key.startsWith("@") || typeof val === "object" || val == null) continue;
45
+ function nextAtomicClass(): string {
46
+ return `a-${atomicCounter++}`;
47
+ }
72
48
 
73
- let v: string;
74
- if (typeof val === "function") v = val.toString();
75
- else v = String(val);
49
+ function registerAtomicRule(
50
+ selector: string,
51
+ decl: string,
52
+ ): string {
53
+ const key = `${selector}|${decl}`;
54
+
55
+ const cached = atomicClassCache.get(key);
56
+ if (cached) return cached;
57
+
58
+ const className = nextAtomicClass();
59
+ atomicClassCache.set(key, className);
60
+
61
+ ensureStyleSheet().insertRule(
62
+ `${selector.replace(/&/g, `.${className}`)}{${decl}}`,
63
+ styleSheet!.cssRules.length,
64
+ );
76
65
 
77
- parts.push(`${key}:${v}`);
66
+ return className;
67
+ }
68
+
69
+ const KEYFRAMES_PREFIX = "@keyframes ";
70
+ const keyframeCache = new Map<string, string>();
71
+ let keyframeCounter = 0;
72
+
73
+ function registerKeyframes(
74
+ name: string,
75
+ frames: Record<string, any>,
76
+ ): string {
77
+ const signature = name + JSON.stringify(frames);
78
+ const cached = keyframeCache.get(signature);
79
+ if (cached) return cached;
80
+
81
+ const unique = `${name}-${keyframeCounter++}`;
82
+
83
+ const steps: string[] = [];
84
+ for (const step in frames) {
85
+ const decls = stringifyDecls(frames[step] ?? {}, new Map());
86
+ steps.push(`${step}{${decls}}`);
78
87
  }
79
88
 
80
- parts.sort();
81
- return hashString(parts.join("|"));
89
+ ensureStyleSheet().insertRule(
90
+ `@keyframes ${unique}{${steps.join("")}}`,
91
+ styleSheet!.cssRules.length,
92
+ );
93
+
94
+ keyframeCache.set(signature, unique);
95
+ return unique;
82
96
  }
83
97
 
84
98
  function stringifyDecls(
@@ -88,19 +102,15 @@ function stringifyDecls(
88
102
  const out: string[] = [];
89
103
 
90
104
  for (const key in props) {
91
- if (!Object.prototype.hasOwnProperty.call(props, key)) continue;
92
105
  const raw = props[key];
93
-
94
106
  if (raw == null || typeof raw === "object") continue;
95
107
 
96
- let val: string | number =
97
- typeof raw === "function" ? raw() : (raw as string | number);
108
+ let val =
109
+ typeof raw === "function" ? raw() : raw;
98
110
 
99
- if (typeof val === "string" && keyframes.size > 0) {
111
+ if (typeof val === "string") {
100
112
  for (const [orig, uniq] of keyframes) {
101
- if (val.includes(`$${orig}`)) {
102
- val = val.replaceAll(`$${orig}`, uniq);
103
- }
113
+ val = val.replaceAll(`$${orig}`, uniq);
104
114
  }
105
115
  }
106
116
 
@@ -110,123 +120,83 @@ function stringifyDecls(
110
120
  return out.join(";");
111
121
  }
112
122
 
113
- function registerKeyframes(
114
- name: string,
115
- frames: Record<string, any>,
116
- ): string {
117
- const hash = hashString(name + JSON.stringify(frames));
118
- const unique = `${name}-${hash}`;
119
-
120
- const cached = keyframeCache.get(unique);
121
- if (cached) return cached;
122
-
123
- const sheet = ensureStyleSheet();
124
-
125
- const steps: string[] = [];
126
- for (const step in frames) {
127
- if (!Object.prototype.hasOwnProperty.call(frames, step)) continue;
128
- const decls = stringifyDecls(frames[step] ?? {}, new Map());
129
- steps.push(`${step}{${decls}}`);
130
- }
131
-
132
- const ruleText = `@keyframes ${unique}{${steps.join("")}}`;
133
- sheet.insertRule(ruleText, sheet.cssRules.length);
134
- keyframeCache.set(unique, unique);
135
-
136
- return unique;
137
- }
138
-
139
- function buildRules(
140
- className: string,
141
- rule: Record<string, any>,
123
+ function buildAtomicClasses(
124
+ rule: StyleRule,
142
125
  keyframes: Map<string, string>,
143
126
  ): string[] {
144
- const rules: string[] = [];
145
- const baseDecls: string[] = [];
127
+ const classes: string[] = [];
146
128
 
147
129
  for (const key in rule) {
148
- if (!Object.prototype.hasOwnProperty.call(rule, key)) continue;
149
130
  const val = rule[key];
150
-
151
- if (key.startsWith("@keyframes")) {
152
- continue;
153
- }
154
-
155
131
  if (!val) continue;
156
132
 
157
133
  if (key[0] === ":" && typeof val === "object") {
158
- const decls = stringifyDecls(val ?? {}, keyframes);
159
- if (decls) {
160
- rules.push(`.${className}${key}{${decls}}`);
161
- }
134
+ const decls = stringifyDecls(val, keyframes);
135
+ if (!decls) continue;
136
+
137
+ classes.push(
138
+ registerAtomicRule(`&${key}`, decls),
139
+ );
162
140
  continue;
163
141
  }
164
142
 
165
143
  if (key.startsWith("@media") && typeof val === "object") {
166
- const decls = stringifyDecls(val ?? {}, keyframes);
167
- if (decls) {
168
- rules.push(`${key}{.${className}{${decls}}}`);
169
- }
144
+ const decls = stringifyDecls(val, keyframes);
145
+ if (!decls) continue;
146
+
147
+ const className = nextAtomicClass();
148
+ atomicClassCache.set(`${key}|${decls}`, className);
149
+
150
+ ensureStyleSheet().insertRule(
151
+ `${key}{.${className}{${decls}}}`,
152
+ styleSheet!.cssRules.length,
153
+ );
154
+
155
+ classes.push(className);
170
156
  continue;
171
157
  }
172
158
 
173
- if (key.includes("&") && typeof val === "object") {
174
- const selector = key.replace(/&/g, `.${className}`);
175
- const decls = stringifyDecls(val ?? {}, keyframes);
176
- if (decls) {
177
- rules.push(`${selector}{${decls}}`);
159
+ if (typeof val !== "object") {
160
+ let resolved =
161
+ typeof val === "function" ? val() : val;
162
+
163
+ if (typeof resolved === "string") {
164
+ for (const [orig, uniq] of keyframes) {
165
+ resolved = resolved.replaceAll(`$${orig}`, uniq);
166
+ }
178
167
  }
179
- continue;
180
- }
181
168
 
182
- if (!key.startsWith("@") && typeof val !== "object") {
183
- const raw = typeof val === "function" ? val() : val;
184
- baseDecls.push(`${camelToKebab(key)}:${raw}`);
169
+ const decl = `${camelToKebab(key)}:${resolved}`;
170
+ classes.push(
171
+ registerAtomicRule("&", decl),
172
+ );
185
173
  }
186
174
  }
187
175
 
188
- if (baseDecls.length > 0) {
189
- rules.push(`.${className}{${baseDecls.join(";")}}`);
190
- }
191
-
192
- return rules;
176
+ return classes;
193
177
  }
194
178
 
195
179
  export function makeStyles<T extends StylesMap>(styles: T) {
196
180
  return (): ClassNames<T> => {
197
181
  const classNames = {} as ClassNames<T>;
198
182
  const keyframesMap = new Map<string, string>();
199
- const sheet = ensureStyleSheet();
200
183
 
201
184
  for (const key in styles) {
202
- if (!Object.prototype.hasOwnProperty.call(styles, key)) continue;
203
185
  if (!key.startsWith(KEYFRAMES_PREFIX)) continue;
204
186
 
205
187
  const name = key.slice(KEYFRAMES_PREFIX.length);
206
- const frames = styles[key] as unknown as Record<string, any>;
188
+ const frames = styles[key] as any;
207
189
  const unique = registerKeyframes(name, frames);
208
190
  keyframesMap.set(name, unique);
209
191
  }
210
192
 
211
193
  for (const slot in styles) {
212
- if (!Object.prototype.hasOwnProperty.call(styles, slot)) continue;
213
194
  if (slot.startsWith(KEYFRAMES_PREFIX)) continue;
214
195
 
215
- const rule = styles[slot] as StyleRule;
216
- const hash = stableHashRule(rule);
217
- const className = ALLOWED_PREFIXES.includes(slot)
218
- ? `${slot}-${hash}`
219
- : `css-${hash}`;
196
+ const rule = (styles[slot] ?? {}) as StyleRule;
197
+ const classes = buildAtomicClasses(rule, keyframesMap);
220
198
 
221
- if (!styleCache.has(className)) {
222
- const rules = buildRules(className, rule, keyframesMap);
223
- for (const r of rules) {
224
- sheet.insertRule(r, sheet.cssRules.length);
225
- }
226
- styleCache.set(className, true);
227
- }
228
-
229
- (classNames as any)[slot] = className;
199
+ (classNames as any)[slot] = classes.join(" ");
230
200
  }
231
201
 
232
202
  return classNames;
@@ -245,13 +215,11 @@ export function mergeStyleSets<T extends StylesMap>(
245
215
  for (const set of sets) {
246
216
  if (!set) continue;
247
217
  for (const key in set) {
248
- if (!Object.prototype.hasOwnProperty.call(set, key)) continue;
249
-
250
218
  const prev = (merged[key] as StyleRule) ?? {};
251
219
  const cur = (set[key] as StyleRule) ?? {};
252
- merged[key] = { ...prev, ...cur } as StyleRule;
220
+ merged[key] = { ...prev, ...cur };
253
221
  }
254
222
  }
255
223
 
256
224
  return makeStyles(merged as T)();
257
- }
225
+ }