@animus-ui/properties 0.1.18 → 0.1.20

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.d.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  export { SHORTHAND_PROPERTIES } from './shorthands';
2
2
  export { isUnitlessProperty, UNITLESS_PROPERTIES } from './unitless';
3
+ export { dependsOnContext, foldInitialValue, registeredInitialValue, substitutesValue, } from './registration-values.js';
4
+ export type { FoldedInitialValue } from './registration-values.js';
3
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC"}
package/dist/index.js CHANGED
@@ -83,4 +83,298 @@ function isUnitlessProperty(property) {
83
83
  return UNITLESS_PROPERTY_SPELLINGS.has(property);
84
84
  }
85
85
  //#endregion
86
- export { SHORTHAND_PROPERTIES, UNITLESS_PROPERTIES, isUnitlessProperty };
86
+ //#region src/registration-values.ts
87
+ const NUMBER = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?/i;
88
+ const IDENT = /^(?:--|-?[a-zA-Z_])[\w-]*/;
89
+ /** The component values of `text`; `undefined` when it does not parse. */
90
+ function parse(text) {
91
+ let at = 0;
92
+ function values(nested) {
93
+ const out = [];
94
+ while (at < text.length) {
95
+ const char = text[at];
96
+ if (/\s/.test(char)) {
97
+ at += 1;
98
+ continue;
99
+ }
100
+ if (char === ")") {
101
+ at += 1;
102
+ return nested ? out : void 0;
103
+ }
104
+ if (char === "\"" || char === "'") {
105
+ const end = stringEnd(text, at);
106
+ if (end === void 0) return void 0;
107
+ out.push({
108
+ kind: "token",
109
+ text: text.slice(at, end)
110
+ });
111
+ at = end;
112
+ continue;
113
+ }
114
+ const rest = text.slice(at);
115
+ const ident = IDENT.exec(rest)?.[0];
116
+ if (char === "(" || ident && rest[ident.length] === "(") {
117
+ const name = (ident ?? "").toLowerCase();
118
+ at += (ident?.length ?? 0) + 1;
119
+ if (name === "url") {
120
+ const end = text.indexOf(")", at);
121
+ if (end < 0) return void 0;
122
+ out.push({
123
+ kind: "token",
124
+ text: "url()"
125
+ });
126
+ at = end + 1;
127
+ continue;
128
+ }
129
+ const args = values(true);
130
+ if (args === void 0) return void 0;
131
+ out.push({
132
+ kind: "function",
133
+ name,
134
+ args
135
+ });
136
+ continue;
137
+ }
138
+ const number = NUMBER.exec(rest)?.[0];
139
+ if (number !== void 0) {
140
+ const unit = /^(?:%|[a-zA-Z]+)/.exec(rest.slice(number.length))?.[0];
141
+ out.push({
142
+ kind: "numeric",
143
+ value: Number(number),
144
+ unit: (unit ?? "").toLowerCase()
145
+ });
146
+ at += number.length + (unit?.length ?? 0);
147
+ continue;
148
+ }
149
+ out.push({
150
+ kind: "token",
151
+ text: ident ?? char
152
+ });
153
+ at += ident?.length ?? 1;
154
+ }
155
+ return nested ? void 0 : out;
156
+ }
157
+ return values(false);
158
+ }
159
+ function stringEnd(text, start) {
160
+ for (let at = start + 1; at < text.length; at += 1) if (text[at] === "\\") at += 1;
161
+ else if (text[at] === text[start]) return at + 1;
162
+ }
163
+ function some(values, test) {
164
+ return values.some((value) => test(value) || value.kind === "function" && some(value.args, test));
165
+ }
166
+ const SUBSTITUTIONS = /* @__PURE__ */ new Set([
167
+ "var",
168
+ "env",
169
+ "attr"
170
+ ]);
171
+ /** Whether `value` substitutes another value (`var()`, `env()`, `attr()`),
172
+ * which no registration's initial value may. Unparseable text is read as
173
+ * substituting when it names one of them. */
174
+ function substitutesValue(value) {
175
+ const values = parse(value);
176
+ if (values === void 0) return /\b(?:var|env|attr)\(/i.test(value);
177
+ return some(values, (component) => component.kind === "function" && SUBSTITUTIONS.has(component.name));
178
+ }
179
+ const CONTEXT_UNITS = /* @__PURE__ */ new Set([
180
+ "em",
181
+ "rem",
182
+ "ex",
183
+ "rex",
184
+ "cap",
185
+ "rcap",
186
+ "ch",
187
+ "rch",
188
+ "ic",
189
+ "ric",
190
+ "lh",
191
+ "rlh",
192
+ "cqw",
193
+ "cqh",
194
+ "cqi",
195
+ "cqb",
196
+ "cqmin",
197
+ "cqmax"
198
+ ]);
199
+ /** Whether `value` uses a font- or container-relative unit, inside math
200
+ * functions too, which makes a typed initial value depend on context. */
201
+ function dependsOnContext(value) {
202
+ const values = parse(value);
203
+ if (values === void 0) return false;
204
+ return some(values, (component) => component.kind === "numeric" && CONTEXT_UNITS.has(component.unit));
205
+ }
206
+ /** Absolute units, by the canonical unit each converts to. */
207
+ const ABSOLUTE_UNITS = /* @__PURE__ */ new Map([
208
+ ["px", ["length", 1]],
209
+ ["cm", ["length", 96 / 2.54]],
210
+ ["mm", ["length", 96 / 25.4]],
211
+ ["q", ["length", 96 / 101.6]],
212
+ ["in", ["length", 96]],
213
+ ["pt", ["length", 4 / 3]],
214
+ ["pc", ["length", 16]],
215
+ ["deg", ["angle", 1]],
216
+ ["grad", ["angle", .9]],
217
+ ["rad", ["angle", 180 / Math.PI]],
218
+ ["turn", ["angle", 360]],
219
+ ["s", ["time", 1]],
220
+ ["ms", ["time", .001]],
221
+ ["dppx", ["resolution", 1]],
222
+ ["x", ["resolution", 1]],
223
+ ["dpi", ["resolution", 1 / 96]],
224
+ ["dpcm", ["resolution", 2.54 / 96]]
225
+ ]);
226
+ const CANONICAL_UNITS = {
227
+ number: "",
228
+ length: "px",
229
+ angle: "deg",
230
+ time: "s",
231
+ resolution: "dppx"
232
+ };
233
+ const MATH_FUNCTIONS = /* @__PURE__ */ new Set([
234
+ "calc",
235
+ "min",
236
+ "max",
237
+ "clamp"
238
+ ]);
239
+ /** Evaluates a math function's arguments: `+ - * /` over numbers and
240
+ * absolute dimensions, nested `calc()`, `min()`, `max()` and `clamp()`. */
241
+ function evaluate(values) {
242
+ let at = 0;
243
+ const operator = (...ops) => {
244
+ const value = values[at];
245
+ if (value?.kind === "token" && ops.includes(value.text)) {
246
+ at += 1;
247
+ return value.text;
248
+ }
249
+ };
250
+ function factor() {
251
+ const value = values[at];
252
+ at += 1;
253
+ if (value === void 0) return void 0;
254
+ if (value.kind === "numeric") {
255
+ if (value.unit === "") return {
256
+ type: "number",
257
+ value: value.value
258
+ };
259
+ const unit = ABSOLUTE_UNITS.get(value.unit);
260
+ return unit && {
261
+ type: unit[0],
262
+ value: value.value * unit[1]
263
+ };
264
+ }
265
+ if (value.kind === "function") return mathFunction(value);
266
+ }
267
+ function product() {
268
+ let left = factor();
269
+ for (let op = operator("*", "/"); op && left; op = operator("*", "/")) {
270
+ const right = factor();
271
+ if (right === void 0) return void 0;
272
+ if (op === "*") {
273
+ if (left.type !== "number" && right.type !== "number") return void 0;
274
+ left = {
275
+ type: left.type === "number" ? right.type : left.type,
276
+ value: left.value * right.value
277
+ };
278
+ } else if (right.type === "number") left = {
279
+ type: left.type,
280
+ value: left.value / right.value
281
+ };
282
+ else if (right.type === left.type) left = {
283
+ type: "number",
284
+ value: left.value / right.value
285
+ };
286
+ else return;
287
+ }
288
+ return left;
289
+ }
290
+ function sum() {
291
+ let left = product();
292
+ for (let op = operator("+", "-"); op && left; op = operator("+", "-")) {
293
+ const right = product();
294
+ if (right === void 0 || right.type !== left.type) return void 0;
295
+ left = {
296
+ type: left.type,
297
+ value: op === "+" ? left.value + right.value : left.value - right.value
298
+ };
299
+ }
300
+ return left;
301
+ }
302
+ const result = sum();
303
+ return at === values.length ? result : void 0;
304
+ }
305
+ function mathFunction(value) {
306
+ if (value.name === "calc" || value.name === "") return evaluate(value.args);
307
+ if (!MATH_FUNCTIONS.has(value.name)) return void 0;
308
+ const args = [];
309
+ let start = 0;
310
+ for (let at = 0; at <= value.args.length; at += 1) {
311
+ const arg = value.args[at];
312
+ if (at === value.args.length || arg?.kind === "token" && arg.text === ",") {
313
+ const computed = evaluate(value.args.slice(start, at));
314
+ if (computed === void 0) return void 0;
315
+ args.push(computed);
316
+ start = at + 1;
317
+ }
318
+ }
319
+ const type = args[0]?.type;
320
+ if (type === void 0 || args.some((arg) => arg.type !== type)) return;
321
+ const values = args.map((arg) => arg.value);
322
+ if (value.name === "min") return {
323
+ type,
324
+ value: Math.min(...values)
325
+ };
326
+ if (value.name === "max") return {
327
+ type,
328
+ value: Math.max(...values)
329
+ };
330
+ if (values.length !== 3) return void 0;
331
+ const [low, middle, high] = values;
332
+ return {
333
+ type,
334
+ value: Math.max(low, Math.min(middle, high))
335
+ };
336
+ }
337
+ /** The data type each folded value is written as, by syntax type. */
338
+ const ACCEPTING_SYNTAX_TYPES = /* @__PURE__ */ new Map([
339
+ ["number", "number"],
340
+ ["integer", "number"],
341
+ ["length", "length"],
342
+ ["length-percentage", "length"],
343
+ ["angle", "angle"],
344
+ ["time", "time"],
345
+ ["resolution", "resolution"]
346
+ ]);
347
+ /**
348
+ * The computed value of an initial value that is one math function (`calc()`,
349
+ * `min()`, `max()`, `clamp()`) over numbers and absolute units, such as
350
+ * `calc(1px / 1px)` → `1`. The browser computes the same value, and CSS
351
+ * tooling that cannot parse the function in that position reads the result.
352
+ * `undefined` for anything else, and for the universal syntax, whose initial
353
+ * value is kept as written.
354
+ */
355
+ function foldInitialValue(syntax, initialValue) {
356
+ if (syntax.trim() === "*") return void 0;
357
+ const values = parse(initialValue);
358
+ const only = values?.length === 1 ? values[0] : void 0;
359
+ if (only?.kind !== "function" || !MATH_FUNCTIONS.has(only.name)) return;
360
+ const computed = mathFunction(only);
361
+ if (computed === void 0) return void 0;
362
+ const accepted = syntax.split("|").some((component) => {
363
+ const type = /<([a-z-]+)>/.exec(component)?.[1];
364
+ if (type === void 0 || ACCEPTING_SYNTAX_TYPES.get(type) !== computed.type) return false;
365
+ return type !== "integer" || Number.isInteger(computed.value);
366
+ });
367
+ return {
368
+ text: `${Number(computed.value.toPrecision(12))}${CANONICAL_UNITS[computed.type]}`,
369
+ accepted,
370
+ type: computed.type
371
+ };
372
+ }
373
+ /** An initial value folded when its syntax accepts the computed value, and
374
+ * unchanged otherwise: what a registration records and emits. */
375
+ function registeredInitialValue(syntax, initialValue) {
376
+ const folded = foldInitialValue(syntax, initialValue);
377
+ return folded?.accepted ? folded.text : initialValue;
378
+ }
379
+ //#endregion
380
+ export { SHORTHAND_PROPERTIES, UNITLESS_PROPERTIES, dependsOnContext, foldInitialValue, isUnitlessProperty, registeredInitialValue, substitutesValue };
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Reads a custom-property registration's initial value as CSS component
3
+ * values: what it substitutes, which units make it depend on context, and
4
+ * what an all-absolute math function computes to. Quoted text and `url()`
5
+ * stay text.
6
+ */
7
+ /** Whether `value` substitutes another value (`var()`, `env()`, `attr()`),
8
+ * which no registration's initial value may. Unparseable text is read as
9
+ * substituting when it names one of them. */
10
+ export declare function substitutesValue(value: string): boolean;
11
+ /** Whether `value` uses a font- or container-relative unit, inside math
12
+ * functions too, which makes a typed initial value depend on context. */
13
+ export declare function dependsOnContext(value: string): boolean;
14
+ /** What an all-absolute math expression computes to. */
15
+ interface Computed {
16
+ type: 'number' | 'length' | 'angle' | 'time' | 'resolution';
17
+ value: number;
18
+ }
19
+ /** A math initial value folded to its computed value. */
20
+ export interface FoldedInitialValue {
21
+ /** The computed value as CSS text, such as `1`, `16px` or `90deg`. */
22
+ text: string;
23
+ /** Whether the registration's syntax accepts it. */
24
+ accepted: boolean;
25
+ /** The computed value's data type, for a diagnostic. */
26
+ type: Computed['type'];
27
+ }
28
+ /**
29
+ * The computed value of an initial value that is one math function (`calc()`,
30
+ * `min()`, `max()`, `clamp()`) over numbers and absolute units, such as
31
+ * `calc(1px / 1px)` → `1`. The browser computes the same value, and CSS
32
+ * tooling that cannot parse the function in that position reads the result.
33
+ * `undefined` for anything else, and for the universal syntax, whose initial
34
+ * value is kept as written.
35
+ */
36
+ export declare function foldInitialValue(syntax: string, initialValue: string): FoldedInitialValue | undefined;
37
+ /** An initial value folded when its syntax accepts the computed value, and
38
+ * unchanged otherwise: what a registration records and emits. */
39
+ export declare function registeredInitialValue(syntax: string, initialValue: string): string;
40
+ export {};
41
+ //# sourceMappingURL=registration-values.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registration-values.d.ts","sourceRoot":"","sources":["../src/registration-values.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAyFH;;8CAE8C;AAC9C,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAQvD;AAuBD;0EAC0E;AAC1E,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAQvD;AAED,wDAAwD;AACxD,UAAU,QAAQ;IAChB,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,YAAY,CAAC;IAC5D,KAAK,EAAE,MAAM,CAAC;CACf;AAwID,yDAAyD;AACzD,MAAM,WAAW,kBAAkB;IACjC,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,QAAQ,EAAE,OAAO,CAAC;IAClB,wDAAwD;IACxD,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GACnB,kBAAkB,GAAG,SAAS,CAyBhC;AAED;kEACkE;AAClE,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GACnB,MAAM,CAGR"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@animus-ui/properties",
3
- "version": "0.1.18",
3
+ "version": "0.1.20",
4
4
  "description": "CSS property classification data — unitless set, shorthand list, type re-exports",
5
5
  "keywords": [
6
6
  "animus",