@ng-native/fabric 0.1.3 → 0.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/dist/css.d.ts CHANGED
@@ -184,6 +184,11 @@ export interface DeferredDeclaration {
184
184
  */
185
185
  readonly alternatives?: readonly string[];
186
186
  readonly fallback?: unknown;
187
+ /**
188
+ * A fallback made of other tokens, `var(--missing, calc(var(--gap) * 2))`, worked out from the
189
+ * tokens in scope where it is used, as a browser substitutes it there.
190
+ */
191
+ readonly fallbackToken?: TokenValue;
187
192
  /**
188
193
  * What to write when the reference resolves, whatever it resolves to: `flex: var(--grow)` sets a
189
194
  * shrink of 1 and a basis of 0%, but only when there is a grow for them to go with.
@@ -646,7 +651,7 @@ type ChannelSpace = 'rgb' | 'hsl';
646
651
  */
647
652
  type CalcExpression = number | {
648
653
  readonly reference: string;
649
- readonly fallback?: number;
654
+ readonly fallback?: CalcExpression;
650
655
  } | readonly ['+' | '-' | '*' | '/' | 'max' | 'min', CalcExpression, CalcExpression];
651
656
  interface CalcMarker {
652
657
  readonly expression: CalcExpression;
package/dist/css.js CHANGED
@@ -896,7 +896,9 @@ export class StyleResolver {
896
896
  /** A reference with nothing defined for it and nothing to fall back to. */
897
897
  reportUndefined(declaration, tokens) {
898
898
  const name = declaration.reference;
899
- if (name === undefined || declaration.fallback !== undefined || name in tokens)
899
+ if (name === undefined || name in tokens)
900
+ return;
901
+ if (declaration.fallback !== undefined || declaration.fallbackToken)
900
902
  return;
901
903
  if (declaration.alternatives?.some((alternative) => alternative in tokens))
902
904
  return;
@@ -1017,32 +1019,79 @@ function resolveAliases(own, merged) {
1017
1019
  const names = Object.keys(own);
1018
1020
  const aliases = names.filter((name) => own[name].alias);
1019
1021
  followAliases(aliases, merged);
1020
- // Until a pass settles nothing more: one made of another defined after it, or of one not yet
1021
- // settled, cannot be worked out until that one is. What is left then is a cycle, or reads a
1022
- // token that is not there, and is unset.
1023
- const derivedNames = names.filter((name) => isDerived(own[name]));
1024
- let pending = derivedNames;
1025
- for (let settled = true; settled && pending.length;) {
1026
- settled = false;
1027
- pending = pending.filter((name) => {
1028
- const value = derived(own[name], merged);
1029
- if (!value)
1030
- return true;
1031
- merged[name] = value;
1022
+ // Left to work out: each token made of others and each alias that came to one, by a link or a
1023
+ // fallback, `var(--missing, calc(var(--gap) * 2))`, which is worked out here as well.
1024
+ const pending = new Set(names.filter((name) => isDerived(merged[name])));
1025
+ if (pending.size)
1026
+ settleDerived(own, merged, pending);
1027
+ return merged;
1028
+ }
1029
+ /** What a token not yet worked out reads as while another is: set, with no form of any kind. */
1030
+ const PENDING = Object.freeze({});
1031
+ /**
1032
+ * The tokens left to work out, each settled once none it reads is still to be, whatever order they
1033
+ * are defined in. One that reads another still to be waits, rather than taking a fallback: a
1034
+ * fallback is for a token that is unset or invalid. When every one left waits for another, those
1035
+ * in a cycle, counting only the references read, are invalid, and the rest go on without them,
1036
+ * taking their fallbacks, as CSS has it.
1037
+ */
1038
+ function settleDerived(own, merged, pending) {
1039
+ const waitingOn = new Map();
1040
+ let read = new Set();
1041
+ const view = new Proxy(merged, {
1042
+ get: (tokens, name) => {
1043
+ if (!pending.has(name))
1044
+ return tokens[name];
1045
+ read.add(name);
1046
+ return PENDING;
1047
+ },
1048
+ });
1049
+ while (pending.size) {
1050
+ let settled = false;
1051
+ for (const name of pending) {
1052
+ read = new Set();
1053
+ const token = own[name];
1054
+ const value = token.alias ? substitutedIn(token, view) : derived(token, view);
1055
+ if (read.size) {
1056
+ waitingOn.set(name, read);
1057
+ continue;
1058
+ }
1059
+ pending.delete(name);
1060
+ settle(merged, name, value);
1032
1061
  settled = true;
1033
- return false;
1034
- });
1062
+ }
1063
+ if (settled)
1064
+ continue;
1065
+ for (const name of inCycles(pending, waitingOn)) {
1066
+ pending.delete(name);
1067
+ delete merged[name];
1068
+ }
1035
1069
  }
1036
- for (const name of pending)
1037
- delete merged[name];
1038
- // A chain that ends at one of those, by a link or a fallback, copied it unsettled above into
1039
- // every alias on the way: follow them all again, from what they were defined as, now it is.
1040
- if (derivedNames.length && aliases.length) {
1041
- for (const name of aliases)
1042
- merged[name] = own[name];
1043
- followAliases(aliases, merged);
1070
+ }
1071
+ /** An alias's target, or the first of its fallbacks that is set, with one made of others worked out. */
1072
+ function substitutedIn(token, tokens) {
1073
+ let link = token;
1074
+ while (link?.alias) {
1075
+ const target = tokens[link.alias];
1076
+ if (target !== undefined)
1077
+ return target;
1078
+ link = link.fallback;
1044
1079
  }
1045
- return merged;
1080
+ return link && isDerived(link) ? derived(link, tokens) : link;
1081
+ }
1082
+ /** The names among `names` that reach themselves by what each waits on. */
1083
+ function inCycles(names, waitingOn) {
1084
+ const reaches = (from, to, seen) => {
1085
+ for (const next of waitingOn.get(from) ?? []) {
1086
+ if (next === to)
1087
+ return true;
1088
+ if (names.has(next) && !seen.has(next) && seen.add(next) && reaches(next, to, seen)) {
1089
+ return true;
1090
+ }
1091
+ }
1092
+ return false;
1093
+ };
1094
+ return [...names].filter((name) => reaches(name, name, new Set()));
1046
1095
  }
1047
1096
  /** Whether a token is made of others, and so worked out where it is defined. */
1048
1097
  function isDerived(token) {
@@ -1090,7 +1139,7 @@ function calcToken(markers, tokens) {
1090
1139
  }
1091
1140
  /** An `hsl()` token's channels, read from the tokens in scope, as the colour they make. */
1092
1141
  function resolveHsl(hsl, tokens) {
1093
- const h = hslChannel(hsl.h, tokens);
1142
+ const h = hslChannel(hsl.h, tokens, true);
1094
1143
  const s = hslChannel(hsl.s, tokens);
1095
1144
  const l = hslChannel(hsl.l, tokens);
1096
1145
  const alpha = hsl.alpha === undefined ? 1 : hslChannel(hsl.alpha, tokens);
@@ -1099,10 +1148,22 @@ function resolveHsl(hsl, tokens) {
1099
1148
  }
1100
1149
  return hslToRgb(h, s, l, alpha);
1101
1150
  }
1102
- function hslChannel(channel, tokens) {
1103
- return typeof channel === 'number'
1104
- ? channel
1105
- : (tokens[channel.reference]?.number ?? channel.fallback);
1151
+ /**
1152
+ * A channel's number, or undefined when its token is the wrong kind for the slot: a hue is a
1153
+ * number or an angle, read in degrees, as `0.5turn` is 180 and not 0.5, and never a percentage; a
1154
+ * saturation, a lightness or an alpha is never an angle. The fallback is only for a token that is
1155
+ * not set, as a set one is substituted, whatever it holds.
1156
+ */
1157
+ function hslChannel(channel, tokens, hue = false) {
1158
+ if (typeof channel === 'number')
1159
+ return channel;
1160
+ const token = tokens[channel.reference];
1161
+ if (!token)
1162
+ return channel.fallback;
1163
+ if (!hue)
1164
+ return token.angle === undefined ? token.number : undefined;
1165
+ const percentage = typeof token.length === 'string' && token.length.endsWith('%');
1166
+ return percentage ? undefined : (token.angle ?? token.number);
1106
1167
  }
1107
1168
  /**
1108
1169
  * `hsl(h, s, l)` converted to sRGB, printed the same way `fromChannels` prints an rgba() built
@@ -1111,7 +1172,11 @@ function hslChannel(channel, tokens) {
1111
1172
  * `h` is degrees; `s` and `l` are fractions, which is what a percentage token already reads as
1112
1173
  * (see `formOf`), so nothing here rescales them.
1113
1174
  */
1114
- function hslToRgb(h, s, l, alpha) {
1175
+ function hslToRgb(h, saturation, l, opacity) {
1176
+ // Out of range as CSS Color 4 takes it: a saturation below 0 is 0, an alpha is clamped, and
1177
+ // anything else is converted as it is, each channel it makes then clamped into sRGB.
1178
+ const s = Math.max(0, saturation);
1179
+ const alpha = Math.min(1, Math.max(0, opacity));
1115
1180
  const hue = ((h % 360) + 360) % 360;
1116
1181
  const chroma = (1 - Math.abs(2 * l - 1)) * s;
1117
1182
  const x = chroma * (1 - Math.abs(((hue / 60) % 2) - 1));
@@ -1127,9 +1192,7 @@ function hslToRgb(h, s, l, alpha) {
1127
1192
  : hue < 300
1128
1193
  ? [x, 0, chroma]
1129
1194
  : [chroma, 0, x];
1130
- const r = Math.round((r1 + m) * 255);
1131
- const g = Math.round((g1 + m) * 255);
1132
- const b = Math.round((b1 + m) * 255);
1195
+ const [r, g, b] = [r1, g1, b1].map((c) => Math.round(Math.min(1, Math.max(0, c + m)) * 255));
1133
1196
  return alpha >= 1
1134
1197
  ? `rgb(${r}, ${g}, ${b})`
1135
1198
  : `rgba(${r}, ${g}, ${b}, ${Math.round(alpha * 1000) / 1000})`;
@@ -1180,12 +1243,17 @@ function referenced(declaration, tokens) {
1180
1243
  break;
1181
1244
  value = formOf(tokens[alternative], declaration.kind);
1182
1245
  }
1183
- value ??= declaration.fallback;
1246
+ value ??= fallbackOf(declaration, tokens);
1184
1247
  const base = CHANNEL_KINDS.has(declaration.kind)
1185
1248
  ? fromChannels(value, declaration.alpha, tokens, declaration.space)
1186
1249
  : value;
1187
1250
  return declaration.adjust ? adjusted(base, declaration.adjust) : base;
1188
1251
  }
1252
+ /** The fallback written, or one made of other tokens, worked out from the tokens where it is used. */
1253
+ function fallbackOf(declaration, tokens) {
1254
+ const token = declaration.fallbackToken;
1255
+ return declaration.fallback ?? (token && formOf(derived(token, tokens), declaration.kind));
1256
+ }
1189
1257
  const CHANNEL_KINDS = new Set(['channels', 'hslChannels']);
1190
1258
  /**
1191
1259
  * `13, 110, 253` as the colour it is, at the alpha the token beside it says, if one does: the
@@ -1356,7 +1424,8 @@ function calculated(expression, kind, tokens) {
1356
1424
  return expression;
1357
1425
  if (!Array.isArray(expression)) {
1358
1426
  const leaf = expression;
1359
- return tokenNumber(tokens[leaf.reference], kind) ?? leaf.fallback;
1427
+ const own = tokenNumber(tokens[leaf.reference], kind);
1428
+ return (own ?? (leaf.fallback === undefined ? undefined : calculated(leaf.fallback, kind, tokens)));
1360
1429
  }
1361
1430
  const [op, a, b] = expression;
1362
1431
  const left = calculated(a, kind, tokens);