@homebound/truss 2.0.13 → 2.1.0-next.2

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/build/index.js CHANGED
@@ -850,6 +850,7 @@ function makeBreakpoints(breakpoints) {
850
850
  const r = {
851
851
  print: "@media print"
852
852
  };
853
+ const screenMedia = "@media screen and";
853
854
  const bps = Object.keys(breakpoints);
854
855
  Object.entries(breakpoints).forEach(([bp, px], i) => {
855
856
  const isFirst = i === 0;
@@ -857,11 +858,11 @@ function makeBreakpoints(breakpoints) {
857
858
  const min = !isFirst ? `${px}px` : "0";
858
859
  const max = !isLast ? `${breakpoints[bps[i + 1]] - 1}px` : "0";
859
860
  if (isFirst) {
860
- r[bp] = `@media (max-width: ${max})`;
861
+ r[bp] = `${screenMedia} (max-width: ${max})`;
861
862
  } else if (isLast) {
862
- r[bp] = `@media (min-width: ${min})`;
863
+ r[bp] = `${screenMedia} (min-width: ${min})`;
863
864
  } else {
864
- r[bp] = `@media (min-width: ${min}) and (max-width: ${max})`;
865
+ r[bp] = `${screenMedia} (min-width: ${min}) and (max-width: ${max})`;
865
866
  }
866
867
  if (!isFirst) {
867
868
  const isSecond = i === 1;
@@ -875,11 +876,11 @@ function makeBreakpoints(breakpoints) {
875
876
  if (!isLast) {
876
877
  parts.push(`(max-width: ${max})`);
877
878
  }
878
- r[name] = `@media ${parts.join(" and ")}`;
879
+ r[name] = `${screenMedia} ${parts.join(" and ")}`;
879
880
  }
880
881
  if (!isFirst && !isLast) {
881
- r[`${bp}AndUp`] = `@media (min-width: ${min})`;
882
- r[`${bp}AndDown`] = `@media (max-width: ${max})`;
882
+ r[`${bp}AndUp`] = `${screenMedia} (min-width: ${min})`;
883
+ r[`${bp}AndDown`] = `${screenMedia} (max-width: ${max})`;
883
884
  }
884
885
  });
885
886
  return r;
@@ -1009,9 +1010,32 @@ type Opts<T> = {
1009
1010
  rules: T,
1010
1011
  enabled: boolean,
1011
1012
  important: boolean,
1012
- selector: string | undefined
1013
+ selector: string | undefined,
1014
+ elseApplied: boolean,
1013
1015
  };
1014
1016
 
1017
+ function invertMediaQuery(query: string): string {
1018
+ const screenPrefix = "@media screen and ";
1019
+ if (query.startsWith(screenPrefix)) {
1020
+ const conditions = query.slice(screenPrefix.length).trim();
1021
+ const rangeMatch = conditions.match(/^\(min-width: (\d+)px\) and \(max-width: (\d+)px\)$/);
1022
+ if (rangeMatch) {
1023
+ const min = Number(rangeMatch[1]);
1024
+ const max = Number(rangeMatch[2]);
1025
+ return \`@media screen and (max-width: \${min - 1}px), screen and (min-width: \${max + 1}px)\`;
1026
+ }
1027
+ const minMatch = conditions.match(/^\(min-width: (\d+)px\)$/);
1028
+ if (minMatch) {
1029
+ return \`@media screen and (max-width: \${Number(minMatch[1]) - 1}px)\`;
1030
+ }
1031
+ const maxMatch = conditions.match(/^\(max-width: (\d+)px\)$/);
1032
+ if (maxMatch) {
1033
+ return \`@media screen and (min-width: \${Number(maxMatch[1]) + 1}px)\`;
1034
+ }
1035
+ }
1036
+ return query.replace("@media", "@media not");
1037
+ }
1038
+
1015
1039
  class CssBuilder<T extends Properties> {
1016
1040
  constructor(private opts: Opts<T>) {}
1017
1041
 
@@ -1055,13 +1079,13 @@ class CssBuilder<T extends Properties> {
1055
1079
 
1056
1080
  get else() {
1057
1081
  if (this.selector !== undefined) {
1058
- if (this.selector.includes("not")) {
1082
+ if (this.opts.elseApplied) {
1059
1083
  throw new Error("else was already called");
1060
1084
  } else {
1061
- return this.newCss({ selector: this.selector.replace("@media", "@media not") });
1085
+ return this.newCss({ selector: invertMediaQuery(this.selector), elseApplied: true });
1062
1086
  }
1063
1087
  }
1064
- return this.newCss({ enabled: !this.enabled });
1088
+ return this.newCss({ enabled: !this.enabled, elseApplied: true });
1065
1089
  }
1066
1090
 
1067
1091
  get important() { return this.newCss({ important: true }); }
@@ -1091,10 +1115,6 @@ class CssBuilder<T extends Properties> {
1091
1115
  return this.newCss({ rules: rules as any });
1092
1116
  }
1093
1117
 
1094
- /** Marker helper for legacy object-spread composition. */
1095
- spread<P extends object>(props: P): P {
1096
- return props;
1097
- }
1098
1118
  }
1099
1119
 
1100
1120
  /** Sort keys so equivalent rule objects have deterministic shape. */
@@ -1142,7 +1162,7 @@ export enum Palette {
1142
1162
  export type Xss<P extends keyof Properties> = Pick<Properties, P>;
1143
1163
 
1144
1164
  /** An entry point for Css expressions. CssBuilder is immutable so this is safe to share. */
1145
- export const Css = new CssBuilder({ rules: {}, enabled: true, important: false, selector: undefined });
1165
+ export const Css = new CssBuilder({ rules: {}, enabled: true, important: false, selector: undefined, elseApplied: false });
1146
1166
 
1147
1167
  ${typeAliasCode}
1148
1168
 
@@ -1205,9 +1225,8 @@ function generateStylexCssBuilder(config, sections) {
1205
1225
  return code`
1206
1226
  // This file is auto-generated by truss: https://github.com/homebound-team/truss.
1207
1227
  // See your project's \`truss-config.ts\` to make configuration changes (fonts, increments, etc).
1208
- // Target: stylex (build-time plugin)
1228
+ // Target: native (build-time plugin)
1209
1229
 
1210
- import * as stylex from "@stylexjs/stylex";
1211
1230
  import { trussProps } from "@homebound/truss/runtime";
1212
1231
 
1213
1232
  /** Given a type X, and the user's proposed type T, only allow keys in X and nothing else. */
@@ -1215,8 +1234,8 @@ export type Only<X, T> = X & Record<Exclude<keyof T, keyof X>, never>;
1215
1234
 
1216
1235
  export type ${def("Properties")} = ${CssProperties}<string | 0, string>;
1217
1236
 
1218
- /** A marker returned by \`stylex.defineMarker()\`, used with \`when\`/\`markerOf\` etc. */
1219
- export type Marker = ReturnType<typeof stylex.defineMarker>;
1237
+ /** A marker token used with \`when\`/\`markerOf\` etc. */
1238
+ export type Marker = symbol;
1220
1239
 
1221
1240
  ${typographyType}
1222
1241
 
@@ -1234,6 +1253,7 @@ type Opts<T> = {
1234
1253
  rules: T;
1235
1254
  enabled: boolean;
1236
1255
  selector: string | undefined;
1256
+ elseApplied: boolean;
1237
1257
  };
1238
1258
 
1239
1259
  class CssBuilder<T extends Properties> {
@@ -1266,11 +1286,16 @@ class CssBuilder<T extends Properties> {
1266
1286
  return this;
1267
1287
  }
1268
1288
 
1269
- /** Marks this element with a user-defined marker (return value of stylex.defineMarker()). */
1289
+ /** Marks this element with a user-defined marker. */
1270
1290
  markerOf(_marker: Marker): CssBuilder<T> {
1271
1291
  return this;
1272
1292
  }
1273
1293
 
1294
+ /** Creates a marker token for use with markerOf() and when(). */
1295
+ newMarker(): Marker {
1296
+ return Symbol("truss-marker");
1297
+ }
1298
+
1274
1299
  typography(key: Typography): CssBuilder<T> {
1275
1300
  return (this as any)[key];
1276
1301
  }
@@ -1310,13 +1335,22 @@ class CssBuilder<T extends Properties> {
1310
1335
  if(mediaQuery: string): CssBuilder<T>;
1311
1336
  if(condOrMediaQuery: boolean | string): CssBuilder<T> {
1312
1337
  if (typeof condOrMediaQuery === "boolean") {
1313
- return new CssBuilder({ ...this.opts, enabled: condOrMediaQuery });
1338
+ return new CssBuilder({ ...this.opts, enabled: condOrMediaQuery, elseApplied: false });
1314
1339
  }
1315
- return this.newCss({ selector: condOrMediaQuery });
1340
+ return this.newCss({ selector: condOrMediaQuery, elseApplied: false });
1316
1341
  }
1317
1342
 
1318
1343
  get else(): CssBuilder<T> {
1319
- return new CssBuilder({ ...this.opts, enabled: !this.enabled });
1344
+ if (this.selector !== undefined) {
1345
+ if (this.opts.elseApplied) {
1346
+ throw new Error("else was already called");
1347
+ }
1348
+ return this.newCss({ selector: invertMediaQuery(this.selector), elseApplied: true });
1349
+ }
1350
+ if (this.opts.elseApplied) {
1351
+ throw new Error("else was already called");
1352
+ }
1353
+ return new CssBuilder({ ...this.opts, enabled: !this.enabled, elseApplied: true });
1320
1354
  }
1321
1355
 
1322
1356
  add<P extends Properties>(props: P): CssBuilder<T & P>;
@@ -1331,14 +1365,9 @@ class CssBuilder<T extends Properties> {
1331
1365
  return this.newCss({ rules: rules as any });
1332
1366
  }
1333
1367
 
1334
- /** Marker helper for legacy object-spread composition. */
1335
- spread<P extends object>(props: P): P {
1336
- return props;
1337
- }
1338
-
1339
- /** Convert a style array into \`{ className, style }\` props for manual spreading into non-\`css=\` contexts. */
1368
+ /** Convert a style hash into \`{ className, style }\` props for manual spreading into non-\`css=\` contexts. */
1340
1369
  props(styles: Properties): Record<string, unknown> {
1341
- return trussProps(stylex, ...(Array.isArray(styles) ? styles : [styles]));
1370
+ return trussProps(styles as any);
1342
1371
  }
1343
1372
 
1344
1373
  private get rules(): T {
@@ -1380,12 +1409,35 @@ export enum Palette {
1380
1409
  export type Xss<P extends keyof Properties> = Pick<Properties, P>;
1381
1410
 
1382
1411
  /** An entry point for Css expressions. CssBuilder is immutable so this is safe to share. */
1383
- export const Css = new CssBuilder({ rules: {}, enabled: true, selector: undefined });
1412
+ export const Css = new CssBuilder({ rules: {}, enabled: true, selector: undefined, elseApplied: false });
1384
1413
 
1385
1414
  ${typeAliasCode}
1386
1415
 
1387
1416
  ${breakpointCode}
1388
1417
 
1418
+ function invertMediaQuery(query: string): string {
1419
+ const screenPrefix = "@media screen and ";
1420
+ if (query.startsWith(screenPrefix)) {
1421
+ const conditions = query.slice(screenPrefix.length).trim();
1422
+ const rangeMatch = conditions.match(/^\(min-width: (\d+)px\) and \(max-width: (\d+)px\)$/);
1423
+ if (rangeMatch) {
1424
+ const min = Number(rangeMatch[1]);
1425
+ const max = Number(rangeMatch[2]);
1426
+ return \`@media screen and (max-width: \${min - 1}px), screen and (min-width: \${max + 1}px)\`;
1427
+ }
1428
+ const minMatch = conditions.match(/^\(min-width: (\d+)px\)$/);
1429
+ if (minMatch) {
1430
+ return \`@media screen and (max-width: \${Number(minMatch[1]) - 1}px)\`;
1431
+ }
1432
+ const maxMatch = conditions.match(/^\(max-width: (\d+)px\)$/);
1433
+ if (maxMatch) {
1434
+ return \`@media screen and (min-width: \${Number(maxMatch[1]) + 1}px)\`;
1435
+ }
1436
+ }
1437
+ return query.replace("@media", "@media not");
1438
+ }
1439
+
1440
+
1389
1441
  ${extras || ""}
1390
1442
  `;
1391
1443
  }
@@ -1400,7 +1452,7 @@ function generateTrussMapping(config, entries) {
1400
1452
  break;
1401
1453
  case "param":
1402
1454
  abbreviations[entry.abbr] = {
1403
- kind: "dynamic",
1455
+ kind: "variable",
1404
1456
  props: entry.props || [],
1405
1457
  incremented: false,
1406
1458
  extraDefs: entry.extraDefs
@@ -1408,7 +1460,7 @@ function generateTrussMapping(config, entries) {
1408
1460
  break;
1409
1461
  case "increment-param":
1410
1462
  abbreviations[entry.abbr] = {
1411
- kind: "dynamic",
1463
+ kind: "variable",
1412
1464
  props: entry.props || [],
1413
1465
  incremented: true,
1414
1466
  extraDefs: entry.extraDefs