typestyles 0.3.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.cjs CHANGED
@@ -67,12 +67,9 @@ function serializeStyle(selector, properties) {
67
67
  const declarations = [];
68
68
  for (const [prop, value] of Object.entries(properties)) {
69
69
  if (value == null) continue;
70
- if (prop.startsWith("&")) {
71
- const nestedSelector = prop.replace(/&/g, selector);
70
+ const nestedSelector = resolveNestedSelector(selector, prop);
71
+ if (nestedSelector) {
72
72
  rules.push(...serializeStyle(nestedSelector, value));
73
- } else if (prop.startsWith("[")) {
74
- const attrSelector = selector + prop;
75
- rules.push(...serializeStyle(attrSelector, value));
76
73
  } else if (prop.startsWith("@")) {
77
74
  const innerRules = serializeStyle(selector, value);
78
75
  for (const inner of innerRules) {
@@ -94,10 +91,58 @@ function serializeStyle(selector, properties) {
94
91
  }
95
92
  return rules;
96
93
  }
94
+ function resolveNestedSelector(parentSelector, key) {
95
+ if (key.startsWith("&")) {
96
+ return key.replace(/&/g, parentSelector);
97
+ }
98
+ if (key.startsWith("[")) {
99
+ const parts = splitSelectorList(key);
100
+ return parts.map((part) => `${parentSelector}${part.trimStart()}`).join(", ");
101
+ }
102
+ return null;
103
+ }
104
+ function splitSelectorList(selector) {
105
+ const result = [];
106
+ let current2 = "";
107
+ let bracketDepth = 0;
108
+ let parenDepth = 0;
109
+ let quote = null;
110
+ for (let i = 0; i < selector.length; i++) {
111
+ const char = selector[i];
112
+ const prev = i > 0 ? selector[i - 1] : "";
113
+ if (quote) {
114
+ current2 += char;
115
+ if (char === quote && prev !== "\\") {
116
+ quote = null;
117
+ }
118
+ continue;
119
+ }
120
+ if (char === '"' || char === "'") {
121
+ quote = char;
122
+ current2 += char;
123
+ continue;
124
+ }
125
+ if (char === "[") bracketDepth++;
126
+ if (char === "]") bracketDepth = Math.max(0, bracketDepth - 1);
127
+ if (char === "(") parenDepth++;
128
+ if (char === ")") parenDepth = Math.max(0, parenDepth - 1);
129
+ if (char === "," && bracketDepth === 0 && parenDepth === 0) {
130
+ result.push(current2.trim());
131
+ current2 = "";
132
+ continue;
133
+ }
134
+ current2 += char;
135
+ }
136
+ if (current2.trim()) {
137
+ result.push(current2.trim());
138
+ }
139
+ return result;
140
+ }
97
141
 
98
142
  // src/sheet.ts
99
143
  var STYLE_ELEMENT_ID = "typestyles";
100
144
  var insertedRules = /* @__PURE__ */ new Set();
145
+ var RUNTIME_DISABLED = typeof __TYPESTYLES_RUNTIME_DISABLED__ !== "undefined" && __TYPESTYLES_RUNTIME_DISABLED__ === "true";
101
146
  var pendingRules = [];
102
147
  var allRules = [];
103
148
  var flushScheduled = false;
@@ -127,7 +172,7 @@ function flush() {
127
172
  ssrBuffer.push(...rules);
128
173
  return;
129
174
  }
130
- if (!isBrowser) return;
175
+ if (!isBrowser || RUNTIME_DISABLED) return;
131
176
  const el = getStyleElement();
132
177
  const sheet = el.sheet;
133
178
  if (sheet) {
@@ -149,15 +194,16 @@ function scheduleFlush() {
149
194
  flush();
150
195
  return;
151
196
  }
152
- if (isBrowser) {
197
+ if (isBrowser && !RUNTIME_DISABLED) {
153
198
  queueMicrotask(flush);
154
199
  }
155
200
  }
156
201
  function insertRule(key, css) {
157
202
  if (insertedRules.has(key)) return;
158
203
  insertedRules.add(key);
159
- pendingRules.push(css);
160
204
  allRules.push(css);
205
+ if (RUNTIME_DISABLED && !ssrBuffer) return;
206
+ pendingRules.push(css);
161
207
  scheduleFlush();
162
208
  }
163
209
  function insertRules(rules) {
@@ -165,9 +211,11 @@ function insertRules(rules) {
165
211
  for (const { key, css } of rules) {
166
212
  if (insertedRules.has(key)) continue;
167
213
  insertedRules.add(key);
168
- pendingRules.push(css);
169
214
  allRules.push(css);
170
- added = true;
215
+ if (!RUNTIME_DISABLED || ssrBuffer) {
216
+ pendingRules.push(css);
217
+ added = true;
218
+ }
171
219
  }
172
220
  if (added) scheduleFlush();
173
221
  }
@@ -192,8 +240,97 @@ function flushSync() {
192
240
  // src/registry.ts
193
241
  var registeredNamespaces = /* @__PURE__ */ new Set();
194
242
 
243
+ // src/class-naming.ts
244
+ var defaultConfig = {
245
+ mode: "semantic",
246
+ prefix: "ts",
247
+ scopeId: ""
248
+ };
249
+ var current = { ...defaultConfig };
250
+ function getClassNamingConfig() {
251
+ return current;
252
+ }
253
+ function configureClassNaming(partial) {
254
+ current = { ...current, ...partial };
255
+ }
256
+ function resetClassNaming() {
257
+ current = { ...defaultConfig };
258
+ }
259
+ function stableSerialize(value) {
260
+ if (value === null) return "null";
261
+ if (typeof value !== "object") return JSON.stringify(value);
262
+ if (Array.isArray(value)) return `[${value.map((v) => stableSerialize(v)).join(",")}]`;
263
+ const entries = Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${JSON.stringify(k)}:${stableSerialize(v)}`);
264
+ return `{${entries.join(",")}}`;
265
+ }
266
+ function hashString(input) {
267
+ let hash = 2166136261;
268
+ for (let i = 0; i < input.length; i++) {
269
+ hash ^= input.charCodeAt(i);
270
+ hash = Math.imul(hash, 16777619);
271
+ }
272
+ return (hash >>> 0).toString(36);
273
+ }
274
+ function sanitizeClassSegment(label) {
275
+ const normalized = label.trim().toLowerCase().replace(/[^a-z0-9-]/g, "-");
276
+ return normalized.replace(/-+/g, "-").replace(/^-|-$/g, "") || "style";
277
+ }
278
+ function buildSingleClassName(name, properties) {
279
+ const cfg = getClassNamingConfig();
280
+ if (cfg.mode === "semantic") return name;
281
+ const payload = stableSerialize({
282
+ ...cfg.scopeId ? { scope: cfg.scopeId } : {},
283
+ namespace: name,
284
+ suffix: "",
285
+ properties
286
+ });
287
+ const h = hashString(payload);
288
+ if (cfg.mode === "atomic") return `${cfg.prefix}-${h}`;
289
+ return `${cfg.prefix}-${sanitizeClassSegment(name)}-${h}`;
290
+ }
291
+ function buildRecipeClassName(namespace, suffix, properties) {
292
+ const cfg = getClassNamingConfig();
293
+ if (cfg.mode === "semantic") return `${namespace}-${suffix}`;
294
+ const payload = stableSerialize({
295
+ ...cfg.scopeId ? { scope: cfg.scopeId } : {},
296
+ namespace,
297
+ suffix,
298
+ properties
299
+ });
300
+ const h = hashString(payload);
301
+ if (cfg.mode === "atomic") return `${cfg.prefix}-${h}`;
302
+ return `${cfg.prefix}-${sanitizeClassSegment(namespace)}-${h}`;
303
+ }
304
+
195
305
  // src/styles.ts
196
- function createStyles(namespace, definitions) {
306
+ function createClass(name, properties) {
307
+ if (process.env.NODE_ENV !== "production") {
308
+ if (registeredNamespaces.has(name)) {
309
+ console.warn(
310
+ `[typestyles] styles.class('${name}', ...) called more than once. This will cause class name collisions. Each class name should be unique.`
311
+ );
312
+ }
313
+ }
314
+ registeredNamespaces.add(name);
315
+ const className = buildSingleClassName(name, properties);
316
+ const selector = `.${className}`;
317
+ const rules = serializeStyle(selector, properties);
318
+ insertRules(rules);
319
+ return className;
320
+ }
321
+ function createHashClass(properties, label) {
322
+ const cfg = getClassNamingConfig();
323
+ const serialized = cfg.scopeId !== "" ? stableSerialize({ scope: cfg.scopeId, properties }) : stableSerialize(properties);
324
+ const hash = hashString(serialized);
325
+ const className = label ? `${cfg.prefix}-${sanitizeClassSegment(label)}-${hash}` : `${cfg.prefix}-${hash}`;
326
+ const selector = `.${className}`;
327
+ const rules = serializeStyle(selector, properties);
328
+ insertRules(rules);
329
+ return className;
330
+ }
331
+ function createStyles(namespace, baseOrDefinitions, variants) {
332
+ const definitions = variants !== void 0 ? { base: baseOrDefinitions, ...variants } : baseOrDefinitions;
333
+ const withBase = variants !== void 0;
197
334
  if (process.env.NODE_ENV !== "production") {
198
335
  if (registeredNamespaces.has(namespace)) {
199
336
  console.warn(
@@ -203,15 +340,20 @@ function createStyles(namespace, definitions) {
203
340
  }
204
341
  registeredNamespaces.add(namespace);
205
342
  const rules = [];
343
+ const variantToClass = {};
206
344
  for (const [variant, properties] of Object.entries(definitions)) {
207
- const className = `${namespace}-${variant}`;
345
+ const props = properties;
346
+ const className = buildRecipeClassName(namespace, variant, props);
347
+ variantToClass[variant] = className;
208
348
  const selector = `.${className}`;
209
- const variantRules = serializeStyle(selector, properties);
349
+ const variantRules = serializeStyle(selector, props);
210
350
  rules.push(...variantRules);
211
351
  }
212
352
  insertRules(rules);
213
- const selectorFn = (...variants) => {
214
- return variants.filter(Boolean).map((v) => `${namespace}-${v}`).join(" ");
353
+ const selectorFn = (...variants2) => {
354
+ const filtered = variants2.filter(Boolean);
355
+ const classes = withBase ? ["base", ...filtered.filter((v) => v !== "base")] : filtered;
356
+ return classes.map((v) => variantToClass[v] ?? "").filter(Boolean).join(" ");
215
357
  };
216
358
  return selectorFn;
217
359
  }
@@ -230,6 +372,82 @@ function compose(...selectors) {
230
372
  return classNames.join(" ");
231
373
  };
232
374
  }
375
+ function createStylesWithUtils(utils) {
376
+ const apply = (properties) => expandStyleWithUtils(properties, utils);
377
+ function create(namespace, baseOrDefinitions, variants) {
378
+ if (variants !== void 0) {
379
+ const transformedVariants = Object.fromEntries(
380
+ Object.entries(variants).map(([variant, properties]) => [variant, apply(properties)])
381
+ );
382
+ return createStyles(namespace, apply(baseOrDefinitions), transformedVariants);
383
+ }
384
+ const transformedDefinitions = Object.fromEntries(
385
+ Object.entries(
386
+ baseOrDefinitions
387
+ ).map(([variant, properties]) => [variant, apply(properties)])
388
+ );
389
+ return createStyles(namespace, transformedDefinitions);
390
+ }
391
+ return {
392
+ class: (name, properties) => createClass(name, apply(properties)),
393
+ hashClass: (properties, label) => createHashClass(apply(properties), label),
394
+ create,
395
+ compose
396
+ };
397
+ }
398
+ function expandStyleWithUtils(properties, utils) {
399
+ const expanded = {};
400
+ for (const [key, value] of Object.entries(properties)) {
401
+ if (value == null) continue;
402
+ if (key.startsWith("&") || key.startsWith("[") || key.startsWith("@")) {
403
+ if (isObject(value)) {
404
+ assignStyleEntry(
405
+ expanded,
406
+ key,
407
+ expandStyleWithUtils(value, utils)
408
+ );
409
+ }
410
+ continue;
411
+ }
412
+ if (Object.prototype.hasOwnProperty.call(utils, key)) {
413
+ const utilFn = utils[key];
414
+ const utilResult = utilFn(value);
415
+ const normalized = expandStyleWithUtils(utilResult, utils);
416
+ for (const [utilKey, utilValue] of Object.entries(normalized)) {
417
+ assignStyleEntry(expanded, utilKey, utilValue);
418
+ }
419
+ continue;
420
+ }
421
+ assignStyleEntry(expanded, key, value);
422
+ }
423
+ return expanded;
424
+ }
425
+ function assignStyleEntry(target, key, value) {
426
+ const targetRecord = target;
427
+ if (isObject(value)) {
428
+ const existing = targetRecord[key];
429
+ if (isObject(existing)) {
430
+ targetRecord[key] = mergeStyleObjects(
431
+ existing,
432
+ value
433
+ );
434
+ return;
435
+ }
436
+ targetRecord[key] = value;
437
+ return;
438
+ }
439
+ targetRecord[key] = value;
440
+ }
441
+ function mergeStyleObjects(base, next) {
442
+ const merged = { ...base };
443
+ for (const [key, value] of Object.entries(next)) {
444
+ assignStyleEntry(merged, key, value);
445
+ }
446
+ return merged;
447
+ }
448
+ function isObject(value) {
449
+ return typeof value === "object" && value !== null && !Array.isArray(value);
450
+ }
233
451
 
234
452
  // src/tokens.ts
235
453
  var registeredNamespaces2 = /* @__PURE__ */ new Set();
@@ -348,6 +566,15 @@ function alpha(colorValue, opacity, colorSpace = "srgb") {
348
566
 
349
567
  // src/component.ts
350
568
  function createComponent(namespace, config) {
569
+ if ("slots" in config) {
570
+ return createSlotComponent(
571
+ namespace,
572
+ config
573
+ );
574
+ }
575
+ return createSingleComponent(namespace, config);
576
+ }
577
+ function createSingleComponent(namespace, config) {
351
578
  const { base, variants = {}, compoundVariants = [], defaultVariants = {} } = config;
352
579
  if (process.env.NODE_ENV !== "production") {
353
580
  if (registeredNamespaces.has(namespace)) {
@@ -358,46 +585,169 @@ function createComponent(namespace, config) {
358
585
  }
359
586
  registeredNamespaces.add(namespace);
360
587
  const rules = [];
588
+ let baseClassName;
589
+ const variantClassByKey = {};
590
+ const compoundClassByIndex = [];
361
591
  if (base) {
362
- rules.push(...serializeStyle(`.${namespace}-base`, base));
592
+ baseClassName = buildRecipeClassName(namespace, "base", base);
593
+ rules.push(...serializeStyle(`.${baseClassName}`, base));
363
594
  }
364
595
  for (const [dimension, options] of Object.entries(variants)) {
365
596
  for (const [option, properties] of Object.entries(options)) {
366
- const className = `.${namespace}-${dimension}-${option}`;
367
- rules.push(...serializeStyle(className, properties));
597
+ const segment = `${dimension}-${option}`;
598
+ const className = buildRecipeClassName(namespace, segment, properties);
599
+ variantClassByKey[segment] = className;
600
+ rules.push(...serializeStyle(`.${className}`, properties));
368
601
  }
369
602
  }
370
603
  compoundVariants.forEach(
371
604
  (cv, index) => {
372
- const className = `.${namespace}-compound-${index}`;
373
- rules.push(...serializeStyle(className, cv.style));
605
+ const className = buildRecipeClassName(namespace, `compound-${index}`, cv.style);
606
+ compoundClassByIndex[index] = className;
607
+ rules.push(...serializeStyle(`.${className}`, cv.style));
374
608
  }
375
609
  );
376
610
  insertRules(rules);
377
611
  return ((selections = {}) => {
378
612
  const classes = [];
379
- if (base) classes.push(`${namespace}-base`);
613
+ if (base && baseClassName) classes.push(baseClassName);
380
614
  const resolvedSelections = {};
381
- for (const dimension of Object.keys(variants)) {
382
- resolvedSelections[dimension] = selections[dimension] ?? defaultVariants[dimension];
615
+ for (const [dimension, options] of Object.entries(variants)) {
616
+ const optionMap = options;
617
+ const explicit = selections[dimension];
618
+ const fallback = defaultVariants[dimension];
619
+ resolvedSelections[dimension] = normalizeSelection(explicit ?? fallback, optionMap);
383
620
  }
384
- for (const dimension of Object.keys(variants)) {
621
+ for (const [dimension, options] of Object.entries(variants)) {
385
622
  const selected = resolvedSelections[dimension];
386
- if (selected != null && selected !== false) {
387
- classes.push(`${namespace}-${dimension}-${String(selected)}`);
623
+ const optionMap = options;
624
+ const selectedKey = normalizeSelection(selected, optionMap);
625
+ if (selectedKey != null) {
626
+ const variantKey = `${dimension}-${selectedKey}`;
627
+ const cn = variantClassByKey[variantKey];
628
+ if (cn) classes.push(cn);
388
629
  }
389
630
  }
390
631
  compoundVariants.forEach(
391
632
  (cv, index) => {
392
- const matches = Object.entries(cv.variants).every(
393
- ([k, v]) => resolvedSelections[k] === v
394
- );
395
- if (matches) classes.push(`${namespace}-compound-${index}`);
633
+ const matches = Object.entries(cv.variants).every(([k, expected]) => {
634
+ const options = variants[k];
635
+ if (!options) return false;
636
+ const selected = normalizeSelection(resolvedSelections[k], options);
637
+ if (selected == null) return false;
638
+ if (Array.isArray(expected)) {
639
+ return expected.some((value) => normalizeSelection(value, options) === selected);
640
+ }
641
+ return normalizeSelection(expected, options) === selected;
642
+ });
643
+ if (matches) {
644
+ const cn = compoundClassByIndex[index];
645
+ if (cn) classes.push(cn);
646
+ }
396
647
  }
397
648
  );
398
649
  return classes.join(" ");
399
650
  });
400
651
  }
652
+ function normalizeSelection(value, options) {
653
+ if (value == null) return void 0;
654
+ if (typeof value === "boolean") {
655
+ const boolKey = String(value);
656
+ if (Object.prototype.hasOwnProperty.call(options, boolKey)) return boolKey;
657
+ if (value === false) return void 0;
658
+ return boolKey;
659
+ }
660
+ return String(value);
661
+ }
662
+ function createSlotComponent(namespace, config) {
663
+ const { slots, base = {}, variants = {}, compoundVariants = [], defaultVariants = {} } = config;
664
+ if (process.env.NODE_ENV !== "production") {
665
+ if (registeredNamespaces.has(namespace)) {
666
+ console.warn(
667
+ `[typestyles] styles.component('${namespace}', ...) called more than once. This will cause class name collisions. Each namespace should be unique.`
668
+ );
669
+ }
670
+ }
671
+ registeredNamespaces.add(namespace);
672
+ const rules = [];
673
+ const baseClassBySlot = {};
674
+ for (const [slot, properties] of Object.entries(base)) {
675
+ const className = buildRecipeClassName(namespace, slot, properties);
676
+ baseClassBySlot[slot] = className;
677
+ rules.push(...serializeStyle(`.${className}`, properties));
678
+ }
679
+ const variantClassByKey = {};
680
+ for (const [dimension, options] of Object.entries(variants)) {
681
+ for (const [option, slotStyles] of Object.entries(options)) {
682
+ for (const [slot, properties] of Object.entries(slotStyles)) {
683
+ const segment = `${slot}-${dimension}-${option}`;
684
+ const className = buildRecipeClassName(namespace, segment, properties);
685
+ variantClassByKey[segment] = className;
686
+ rules.push(...serializeStyle(`.${className}`, properties));
687
+ }
688
+ }
689
+ }
690
+ const slotCompoundClassByKey = {};
691
+ compoundVariants.forEach(
692
+ (cv, index) => {
693
+ for (const [slot, properties] of Object.entries(cv.style)) {
694
+ const segment = `${slot}-compound-${index}`;
695
+ const className = buildRecipeClassName(namespace, segment, properties);
696
+ slotCompoundClassByKey[`${slot}::${index}`] = className;
697
+ rules.push(...serializeStyle(`.${className}`, properties));
698
+ }
699
+ }
700
+ );
701
+ insertRules(rules);
702
+ return ((selections = {}) => {
703
+ const classes = Object.fromEntries(slots.map((slot) => [slot, []]));
704
+ const resolvedSelections = {};
705
+ for (const [dimension, options] of Object.entries(variants)) {
706
+ const optionMap = options;
707
+ const explicit = selections[dimension];
708
+ const fallback = defaultVariants[dimension];
709
+ resolvedSelections[dimension] = normalizeSelection(explicit ?? fallback, optionMap);
710
+ }
711
+ for (const slot of Object.keys(base)) {
712
+ const cn = baseClassBySlot[slot];
713
+ if (cn && classes[slot]) classes[slot].push(cn);
714
+ }
715
+ for (const [dimension, options] of Object.entries(variants)) {
716
+ const optionMap = options;
717
+ const selected = normalizeSelection(resolvedSelections[dimension], optionMap);
718
+ if (selected == null) continue;
719
+ const slotStyles = optionMap[selected];
720
+ if (!slotStyles) continue;
721
+ for (const slot of Object.keys(slotStyles)) {
722
+ const segment = `${slot}-${dimension}-${selected}`;
723
+ const cn = variantClassByKey[segment];
724
+ if (cn && classes[slot]) classes[slot].push(cn);
725
+ }
726
+ }
727
+ compoundVariants.forEach(
728
+ (cv, index) => {
729
+ const matches = Object.entries(cv.variants).every(([k, expected]) => {
730
+ const options = variants[k];
731
+ if (!options) return false;
732
+ const selected = normalizeSelection(resolvedSelections[k], options);
733
+ if (selected == null) return false;
734
+ if (Array.isArray(expected)) {
735
+ return expected.some((value) => normalizeSelection(value, options) === selected);
736
+ }
737
+ return normalizeSelection(expected, options) === selected;
738
+ });
739
+ if (!matches) return;
740
+ for (const slot of Object.keys(cv.style)) {
741
+ const cn = slotCompoundClassByKey[`${slot}::${index}`];
742
+ if (cn && classes[slot]) classes[slot].push(cn);
743
+ }
744
+ }
745
+ );
746
+ return Object.fromEntries(
747
+ slots.map((slot) => [slot, classes[slot].join(" ")])
748
+ );
749
+ });
750
+ }
401
751
 
402
752
  // src/global.ts
403
753
  function globalStyle(selector, properties) {
@@ -434,7 +784,10 @@ function assignVars(vars) {
434
784
  // src/index.ts
435
785
  var styles = {
436
786
  create: createStyles,
787
+ class: createClass,
788
+ hashClass: createHashClass,
437
789
  component: createComponent,
790
+ withUtils: createStylesWithUtils,
438
791
  compose
439
792
  };
440
793
  var global = {
@@ -453,13 +806,16 @@ var color = color_exports;
453
806
 
454
807
  exports.assignVars = assignVars;
455
808
  exports.color = color;
809
+ exports.configureClassNaming = configureClassNaming;
456
810
  exports.createVar = createVar;
457
811
  exports.flushSync = flushSync;
812
+ exports.getClassNamingConfig = getClassNamingConfig;
458
813
  exports.getRegisteredCss = getRegisteredCss;
459
814
  exports.global = global;
460
815
  exports.insertRules = insertRules;
461
816
  exports.keyframes = keyframes;
462
817
  exports.reset = reset;
818
+ exports.resetClassNaming = resetClassNaming;
463
819
  exports.styles = styles;
464
820
  exports.tokens = tokens;
465
821
  //# sourceMappingURL=index.cjs.map