typestyles 0.2.0 → 0.4.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/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { __export, insertRules, insertRule } from './chunk-BB7C47TQ.js';
2
- export { flushSync, getRegisteredCss, insertRules, reset } from './chunk-BB7C47TQ.js';
1
+ import { __export, insertRules, insertRule } from './chunk-AYFIQGCF.js';
2
+ export { flushSync, getRegisteredCss, insertRules, reset } from './chunk-AYFIQGCF.js';
3
3
 
4
4
  // src/css.ts
5
5
  function toKebabCase(prop) {
@@ -62,11 +62,9 @@ function serializeStyle(selector, properties) {
62
62
  const declarations = [];
63
63
  for (const [prop, value] of Object.entries(properties)) {
64
64
  if (value == null) continue;
65
- if (prop.startsWith("&")) {
66
- const nestedSelector = prop.replace(/&/g, selector);
67
- rules.push(
68
- ...serializeStyle(nestedSelector, value)
69
- );
65
+ const nestedSelector = resolveNestedSelector(selector, prop);
66
+ if (nestedSelector) {
67
+ rules.push(...serializeStyle(nestedSelector, value));
70
68
  } else if (prop.startsWith("@")) {
71
69
  const innerRules = serializeStyle(selector, value);
72
70
  for (const inner of innerRules) {
@@ -77,9 +75,7 @@ function serializeStyle(selector, properties) {
77
75
  }
78
76
  } else {
79
77
  const kebabProp = toKebabCase(prop);
80
- declarations.push(
81
- `${kebabProp}: ${serializeValue(prop, value)}`
82
- );
78
+ declarations.push(`${kebabProp}: ${serializeValue(prop, value)}`);
83
79
  }
84
80
  }
85
81
  if (declarations.length > 0) {
@@ -90,12 +86,148 @@ function serializeStyle(selector, properties) {
90
86
  }
91
87
  return rules;
92
88
  }
89
+ function resolveNestedSelector(parentSelector, key) {
90
+ if (key.startsWith("&")) {
91
+ return key.replace(/&/g, parentSelector);
92
+ }
93
+ if (key.startsWith("[")) {
94
+ const parts = splitSelectorList(key);
95
+ return parts.map((part) => `${parentSelector}${part.trimStart()}`).join(", ");
96
+ }
97
+ return null;
98
+ }
99
+ function splitSelectorList(selector) {
100
+ const result = [];
101
+ let current2 = "";
102
+ let bracketDepth = 0;
103
+ let parenDepth = 0;
104
+ let quote = null;
105
+ for (let i = 0; i < selector.length; i++) {
106
+ const char = selector[i];
107
+ const prev = i > 0 ? selector[i - 1] : "";
108
+ if (quote) {
109
+ current2 += char;
110
+ if (char === quote && prev !== "\\") {
111
+ quote = null;
112
+ }
113
+ continue;
114
+ }
115
+ if (char === '"' || char === "'") {
116
+ quote = char;
117
+ current2 += char;
118
+ continue;
119
+ }
120
+ if (char === "[") bracketDepth++;
121
+ if (char === "]") bracketDepth = Math.max(0, bracketDepth - 1);
122
+ if (char === "(") parenDepth++;
123
+ if (char === ")") parenDepth = Math.max(0, parenDepth - 1);
124
+ if (char === "," && bracketDepth === 0 && parenDepth === 0) {
125
+ result.push(current2.trim());
126
+ current2 = "";
127
+ continue;
128
+ }
129
+ current2 += char;
130
+ }
131
+ if (current2.trim()) {
132
+ result.push(current2.trim());
133
+ }
134
+ return result;
135
+ }
93
136
 
94
137
  // src/registry.ts
95
138
  var registeredNamespaces = /* @__PURE__ */ new Set();
96
139
 
140
+ // src/class-naming.ts
141
+ var defaultConfig = {
142
+ mode: "semantic",
143
+ prefix: "ts",
144
+ scopeId: ""
145
+ };
146
+ var current = { ...defaultConfig };
147
+ function getClassNamingConfig() {
148
+ return current;
149
+ }
150
+ function configureClassNaming(partial) {
151
+ current = { ...current, ...partial };
152
+ }
153
+ function resetClassNaming() {
154
+ current = { ...defaultConfig };
155
+ }
156
+ function stableSerialize(value) {
157
+ if (value === null) return "null";
158
+ if (typeof value !== "object") return JSON.stringify(value);
159
+ if (Array.isArray(value)) return `[${value.map((v) => stableSerialize(v)).join(",")}]`;
160
+ const entries = Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${JSON.stringify(k)}:${stableSerialize(v)}`);
161
+ return `{${entries.join(",")}}`;
162
+ }
163
+ function hashString(input) {
164
+ let hash = 2166136261;
165
+ for (let i = 0; i < input.length; i++) {
166
+ hash ^= input.charCodeAt(i);
167
+ hash = Math.imul(hash, 16777619);
168
+ }
169
+ return (hash >>> 0).toString(36);
170
+ }
171
+ function sanitizeClassSegment(label) {
172
+ const normalized = label.trim().toLowerCase().replace(/[^a-z0-9-]/g, "-");
173
+ return normalized.replace(/-+/g, "-").replace(/^-|-$/g, "") || "style";
174
+ }
175
+ function buildSingleClassName(name, properties) {
176
+ const cfg = getClassNamingConfig();
177
+ if (cfg.mode === "semantic") return name;
178
+ const payload = stableSerialize({
179
+ ...cfg.scopeId ? { scope: cfg.scopeId } : {},
180
+ namespace: name,
181
+ suffix: "",
182
+ properties
183
+ });
184
+ const h = hashString(payload);
185
+ if (cfg.mode === "atomic") return `${cfg.prefix}-${h}`;
186
+ return `${cfg.prefix}-${sanitizeClassSegment(name)}-${h}`;
187
+ }
188
+ function buildRecipeClassName(namespace, suffix, properties) {
189
+ const cfg = getClassNamingConfig();
190
+ if (cfg.mode === "semantic") return `${namespace}-${suffix}`;
191
+ const payload = stableSerialize({
192
+ ...cfg.scopeId ? { scope: cfg.scopeId } : {},
193
+ namespace,
194
+ suffix,
195
+ properties
196
+ });
197
+ const h = hashString(payload);
198
+ if (cfg.mode === "atomic") return `${cfg.prefix}-${h}`;
199
+ return `${cfg.prefix}-${sanitizeClassSegment(namespace)}-${h}`;
200
+ }
201
+
97
202
  // src/styles.ts
98
- function createStyles(namespace, definitions) {
203
+ function createClass(name, properties) {
204
+ if (process.env.NODE_ENV !== "production") {
205
+ if (registeredNamespaces.has(name)) {
206
+ console.warn(
207
+ `[typestyles] styles.class('${name}', ...) called more than once. This will cause class name collisions. Each class name should be unique.`
208
+ );
209
+ }
210
+ }
211
+ registeredNamespaces.add(name);
212
+ const className = buildSingleClassName(name, properties);
213
+ const selector = `.${className}`;
214
+ const rules = serializeStyle(selector, properties);
215
+ insertRules(rules);
216
+ return className;
217
+ }
218
+ function createHashClass(properties, label) {
219
+ const cfg = getClassNamingConfig();
220
+ const serialized = cfg.scopeId !== "" ? stableSerialize({ scope: cfg.scopeId, properties }) : stableSerialize(properties);
221
+ const hash = hashString(serialized);
222
+ const className = label ? `${cfg.prefix}-${sanitizeClassSegment(label)}-${hash}` : `${cfg.prefix}-${hash}`;
223
+ const selector = `.${className}`;
224
+ const rules = serializeStyle(selector, properties);
225
+ insertRules(rules);
226
+ return className;
227
+ }
228
+ function createStyles(namespace, baseOrDefinitions, variants) {
229
+ const definitions = variants !== void 0 ? { base: baseOrDefinitions, ...variants } : baseOrDefinitions;
230
+ const withBase = variants !== void 0;
99
231
  if (process.env.NODE_ENV !== "production") {
100
232
  if (registeredNamespaces.has(namespace)) {
101
233
  console.warn(
@@ -105,15 +237,20 @@ function createStyles(namespace, definitions) {
105
237
  }
106
238
  registeredNamespaces.add(namespace);
107
239
  const rules = [];
240
+ const variantToClass = {};
108
241
  for (const [variant, properties] of Object.entries(definitions)) {
109
- const className = `${namespace}-${variant}`;
242
+ const props = properties;
243
+ const className = buildRecipeClassName(namespace, variant, props);
244
+ variantToClass[variant] = className;
110
245
  const selector = `.${className}`;
111
- const variantRules = serializeStyle(selector, properties);
246
+ const variantRules = serializeStyle(selector, props);
112
247
  rules.push(...variantRules);
113
248
  }
114
249
  insertRules(rules);
115
- const selectorFn = (...variants) => {
116
- return variants.filter(Boolean).map((v) => `${namespace}-${v}`).join(" ");
250
+ const selectorFn = (...variants2) => {
251
+ const filtered = variants2.filter(Boolean);
252
+ const classes = withBase ? ["base", ...filtered.filter((v) => v !== "base")] : filtered;
253
+ return classes.map((v) => variantToClass[v] ?? "").filter(Boolean).join(" ");
117
254
  };
118
255
  return selectorFn;
119
256
  }
@@ -132,6 +269,82 @@ function compose(...selectors) {
132
269
  return classNames.join(" ");
133
270
  };
134
271
  }
272
+ function createStylesWithUtils(utils) {
273
+ const apply = (properties) => expandStyleWithUtils(properties, utils);
274
+ function create(namespace, baseOrDefinitions, variants) {
275
+ if (variants !== void 0) {
276
+ const transformedVariants = Object.fromEntries(
277
+ Object.entries(variants).map(([variant, properties]) => [variant, apply(properties)])
278
+ );
279
+ return createStyles(namespace, apply(baseOrDefinitions), transformedVariants);
280
+ }
281
+ const transformedDefinitions = Object.fromEntries(
282
+ Object.entries(
283
+ baseOrDefinitions
284
+ ).map(([variant, properties]) => [variant, apply(properties)])
285
+ );
286
+ return createStyles(namespace, transformedDefinitions);
287
+ }
288
+ return {
289
+ class: (name, properties) => createClass(name, apply(properties)),
290
+ hashClass: (properties, label) => createHashClass(apply(properties), label),
291
+ create,
292
+ compose
293
+ };
294
+ }
295
+ function expandStyleWithUtils(properties, utils) {
296
+ const expanded = {};
297
+ for (const [key, value] of Object.entries(properties)) {
298
+ if (value == null) continue;
299
+ if (key.startsWith("&") || key.startsWith("[") || key.startsWith("@")) {
300
+ if (isObject(value)) {
301
+ assignStyleEntry(
302
+ expanded,
303
+ key,
304
+ expandStyleWithUtils(value, utils)
305
+ );
306
+ }
307
+ continue;
308
+ }
309
+ if (Object.prototype.hasOwnProperty.call(utils, key)) {
310
+ const utilFn = utils[key];
311
+ const utilResult = utilFn(value);
312
+ const normalized = expandStyleWithUtils(utilResult, utils);
313
+ for (const [utilKey, utilValue] of Object.entries(normalized)) {
314
+ assignStyleEntry(expanded, utilKey, utilValue);
315
+ }
316
+ continue;
317
+ }
318
+ assignStyleEntry(expanded, key, value);
319
+ }
320
+ return expanded;
321
+ }
322
+ function assignStyleEntry(target, key, value) {
323
+ const targetRecord = target;
324
+ if (isObject(value)) {
325
+ const existing = targetRecord[key];
326
+ if (isObject(existing)) {
327
+ targetRecord[key] = mergeStyleObjects(
328
+ existing,
329
+ value
330
+ );
331
+ return;
332
+ }
333
+ targetRecord[key] = value;
334
+ return;
335
+ }
336
+ targetRecord[key] = value;
337
+ }
338
+ function mergeStyleObjects(base, next) {
339
+ const merged = { ...base };
340
+ for (const [key, value] of Object.entries(next)) {
341
+ assignStyleEntry(merged, key, value);
342
+ }
343
+ return merged;
344
+ }
345
+ function isObject(value) {
346
+ return typeof value === "object" && value !== null && !Array.isArray(value);
347
+ }
135
348
 
136
349
  // src/tokens.ts
137
350
  var registeredNamespaces2 = /* @__PURE__ */ new Set();
@@ -250,6 +463,15 @@ function alpha(colorValue, opacity, colorSpace = "srgb") {
250
463
 
251
464
  // src/component.ts
252
465
  function createComponent(namespace, config) {
466
+ if ("slots" in config) {
467
+ return createSlotComponent(
468
+ namespace,
469
+ config
470
+ );
471
+ }
472
+ return createSingleComponent(namespace, config);
473
+ }
474
+ function createSingleComponent(namespace, config) {
253
475
  const { base, variants = {}, compoundVariants = [], defaultVariants = {} } = config;
254
476
  if (process.env.NODE_ENV !== "production") {
255
477
  if (registeredNamespaces.has(namespace)) {
@@ -260,46 +482,169 @@ function createComponent(namespace, config) {
260
482
  }
261
483
  registeredNamespaces.add(namespace);
262
484
  const rules = [];
485
+ let baseClassName;
486
+ const variantClassByKey = {};
487
+ const compoundClassByIndex = [];
263
488
  if (base) {
264
- rules.push(...serializeStyle(`.${namespace}-base`, base));
489
+ baseClassName = buildRecipeClassName(namespace, "base", base);
490
+ rules.push(...serializeStyle(`.${baseClassName}`, base));
265
491
  }
266
492
  for (const [dimension, options] of Object.entries(variants)) {
267
493
  for (const [option, properties] of Object.entries(options)) {
268
- const className = `.${namespace}-${dimension}-${option}`;
269
- rules.push(...serializeStyle(className, properties));
494
+ const segment = `${dimension}-${option}`;
495
+ const className = buildRecipeClassName(namespace, segment, properties);
496
+ variantClassByKey[segment] = className;
497
+ rules.push(...serializeStyle(`.${className}`, properties));
270
498
  }
271
499
  }
272
500
  compoundVariants.forEach(
273
501
  (cv, index) => {
274
- const className = `.${namespace}-compound-${index}`;
275
- rules.push(...serializeStyle(className, cv.style));
502
+ const className = buildRecipeClassName(namespace, `compound-${index}`, cv.style);
503
+ compoundClassByIndex[index] = className;
504
+ rules.push(...serializeStyle(`.${className}`, cv.style));
276
505
  }
277
506
  );
278
507
  insertRules(rules);
279
508
  return ((selections = {}) => {
280
509
  const classes = [];
281
- if (base) classes.push(`${namespace}-base`);
510
+ if (base && baseClassName) classes.push(baseClassName);
282
511
  const resolvedSelections = {};
283
- for (const dimension of Object.keys(variants)) {
284
- resolvedSelections[dimension] = selections[dimension] ?? defaultVariants[dimension];
512
+ for (const [dimension, options] of Object.entries(variants)) {
513
+ const optionMap = options;
514
+ const explicit = selections[dimension];
515
+ const fallback = defaultVariants[dimension];
516
+ resolvedSelections[dimension] = normalizeSelection(explicit ?? fallback, optionMap);
285
517
  }
286
- for (const dimension of Object.keys(variants)) {
518
+ for (const [dimension, options] of Object.entries(variants)) {
287
519
  const selected = resolvedSelections[dimension];
288
- if (selected != null && selected !== false) {
289
- classes.push(`${namespace}-${dimension}-${String(selected)}`);
520
+ const optionMap = options;
521
+ const selectedKey = normalizeSelection(selected, optionMap);
522
+ if (selectedKey != null) {
523
+ const variantKey = `${dimension}-${selectedKey}`;
524
+ const cn = variantClassByKey[variantKey];
525
+ if (cn) classes.push(cn);
290
526
  }
291
527
  }
292
528
  compoundVariants.forEach(
293
529
  (cv, index) => {
294
- const matches = Object.entries(cv.variants).every(
295
- ([k, v]) => resolvedSelections[k] === v
296
- );
297
- if (matches) classes.push(`${namespace}-compound-${index}`);
530
+ const matches = Object.entries(cv.variants).every(([k, expected]) => {
531
+ const options = variants[k];
532
+ if (!options) return false;
533
+ const selected = normalizeSelection(resolvedSelections[k], options);
534
+ if (selected == null) return false;
535
+ if (Array.isArray(expected)) {
536
+ return expected.some((value) => normalizeSelection(value, options) === selected);
537
+ }
538
+ return normalizeSelection(expected, options) === selected;
539
+ });
540
+ if (matches) {
541
+ const cn = compoundClassByIndex[index];
542
+ if (cn) classes.push(cn);
543
+ }
298
544
  }
299
545
  );
300
546
  return classes.join(" ");
301
547
  });
302
548
  }
549
+ function normalizeSelection(value, options) {
550
+ if (value == null) return void 0;
551
+ if (typeof value === "boolean") {
552
+ const boolKey = String(value);
553
+ if (Object.prototype.hasOwnProperty.call(options, boolKey)) return boolKey;
554
+ if (value === false) return void 0;
555
+ return boolKey;
556
+ }
557
+ return String(value);
558
+ }
559
+ function createSlotComponent(namespace, config) {
560
+ const { slots, base = {}, variants = {}, compoundVariants = [], defaultVariants = {} } = config;
561
+ if (process.env.NODE_ENV !== "production") {
562
+ if (registeredNamespaces.has(namespace)) {
563
+ console.warn(
564
+ `[typestyles] styles.component('${namespace}', ...) called more than once. This will cause class name collisions. Each namespace should be unique.`
565
+ );
566
+ }
567
+ }
568
+ registeredNamespaces.add(namespace);
569
+ const rules = [];
570
+ const baseClassBySlot = {};
571
+ for (const [slot, properties] of Object.entries(base)) {
572
+ const className = buildRecipeClassName(namespace, slot, properties);
573
+ baseClassBySlot[slot] = className;
574
+ rules.push(...serializeStyle(`.${className}`, properties));
575
+ }
576
+ const variantClassByKey = {};
577
+ for (const [dimension, options] of Object.entries(variants)) {
578
+ for (const [option, slotStyles] of Object.entries(options)) {
579
+ for (const [slot, properties] of Object.entries(slotStyles)) {
580
+ const segment = `${slot}-${dimension}-${option}`;
581
+ const className = buildRecipeClassName(namespace, segment, properties);
582
+ variantClassByKey[segment] = className;
583
+ rules.push(...serializeStyle(`.${className}`, properties));
584
+ }
585
+ }
586
+ }
587
+ const slotCompoundClassByKey = {};
588
+ compoundVariants.forEach(
589
+ (cv, index) => {
590
+ for (const [slot, properties] of Object.entries(cv.style)) {
591
+ const segment = `${slot}-compound-${index}`;
592
+ const className = buildRecipeClassName(namespace, segment, properties);
593
+ slotCompoundClassByKey[`${slot}::${index}`] = className;
594
+ rules.push(...serializeStyle(`.${className}`, properties));
595
+ }
596
+ }
597
+ );
598
+ insertRules(rules);
599
+ return ((selections = {}) => {
600
+ const classes = Object.fromEntries(slots.map((slot) => [slot, []]));
601
+ const resolvedSelections = {};
602
+ for (const [dimension, options] of Object.entries(variants)) {
603
+ const optionMap = options;
604
+ const explicit = selections[dimension];
605
+ const fallback = defaultVariants[dimension];
606
+ resolvedSelections[dimension] = normalizeSelection(explicit ?? fallback, optionMap);
607
+ }
608
+ for (const slot of Object.keys(base)) {
609
+ const cn = baseClassBySlot[slot];
610
+ if (cn && classes[slot]) classes[slot].push(cn);
611
+ }
612
+ for (const [dimension, options] of Object.entries(variants)) {
613
+ const optionMap = options;
614
+ const selected = normalizeSelection(resolvedSelections[dimension], optionMap);
615
+ if (selected == null) continue;
616
+ const slotStyles = optionMap[selected];
617
+ if (!slotStyles) continue;
618
+ for (const slot of Object.keys(slotStyles)) {
619
+ const segment = `${slot}-${dimension}-${selected}`;
620
+ const cn = variantClassByKey[segment];
621
+ if (cn && classes[slot]) classes[slot].push(cn);
622
+ }
623
+ }
624
+ compoundVariants.forEach(
625
+ (cv, index) => {
626
+ const matches = Object.entries(cv.variants).every(([k, expected]) => {
627
+ const options = variants[k];
628
+ if (!options) return false;
629
+ const selected = normalizeSelection(resolvedSelections[k], options);
630
+ if (selected == null) return false;
631
+ if (Array.isArray(expected)) {
632
+ return expected.some((value) => normalizeSelection(value, options) === selected);
633
+ }
634
+ return normalizeSelection(expected, options) === selected;
635
+ });
636
+ if (!matches) return;
637
+ for (const slot of Object.keys(cv.style)) {
638
+ const cn = slotCompoundClassByKey[`${slot}::${index}`];
639
+ if (cn && classes[slot]) classes[slot].push(cn);
640
+ }
641
+ }
642
+ );
643
+ return Object.fromEntries(
644
+ slots.map((slot) => [slot, classes[slot].join(" ")])
645
+ );
646
+ });
647
+ }
303
648
 
304
649
  // src/global.ts
305
650
  function globalStyle(selector, properties) {
@@ -336,7 +681,10 @@ function assignVars(vars) {
336
681
  // src/index.ts
337
682
  var styles = {
338
683
  create: createStyles,
684
+ class: createClass,
685
+ hashClass: createHashClass,
339
686
  component: createComponent,
687
+ withUtils: createStylesWithUtils,
340
688
  compose
341
689
  };
342
690
  var global = {
@@ -353,6 +701,6 @@ var keyframes = {
353
701
  };
354
702
  var color = color_exports;
355
703
 
356
- export { assignVars, color, createVar, global, keyframes, styles, tokens };
704
+ export { assignVars, color, configureClassNaming, createVar, getClassNamingConfig, global, keyframes, resetClassNaming, styles, tokens };
357
705
  //# sourceMappingURL=index.js.map
358
706
  //# sourceMappingURL=index.js.map