@ng-native/fabric 0.13.0 → 0.14.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.
@@ -20,6 +20,13 @@ export declare const DROP_MATCHED_WEIGHT_AND_STYLE = true;
20
20
  * range is both its ends, `Inter-100to900`, so two files that split a family by range differ.
21
21
  */
22
22
  export declare function faceName(face: Pick<FontFace, 'family' | 'weight' | 'weightRange' | 'style'>): string;
23
+ /**
24
+ * Every name the faces are registered under, and the face each names, in the order they are
25
+ * registered. A face is registered under `faceName`, the last face of that name winning, and the
26
+ * first face of a family under the family as well. A face with both a weight and a style also
27
+ * answers to each alone, where no face declared exactly that.
28
+ */
29
+ export declare function registeredFaces<Face extends FontFace>(faces: readonly Face[]): Map<string, Face>;
23
30
  /**
24
31
  * The face CSS's matching picks among one family's, for a wanted weight and style.
25
32
  *
@@ -31,8 +38,20 @@ export declare function matchFace(faces: readonly FontFace[], weight: number, st
31
38
  /** Every face the engine has seen a sheet declare, by family. */
32
39
  export declare class FontFaces {
33
40
  private readonly byFamily;
41
+ /**
42
+ * Every face, in the order taken in, one declared twice under a name too: the name index takes
43
+ * the last of them, as expo-font registers it, where matching keeps the first.
44
+ */
45
+ private readonly faces;
46
+ /** What `postScriptName` reads, made again on the first read after the faces change. */
47
+ private names;
34
48
  /** Forget every face, before they are taken in again from the sheets still registered. */
35
49
  clear(): void;
50
+ /**
51
+ * The PostScript name of the face a name is registered under, as `registeredFaces` names them,
52
+ * where the build could read it from the face's file.
53
+ */
54
+ postScriptName(name: string): string | undefined;
36
55
  /** Take faces in; whether any was new, which changes what text already matched may match. */
37
56
  add(faces: readonly FontFace[]): boolean;
38
57
  /**
@@ -19,6 +19,29 @@ export function faceName(face) {
19
19
  name += `-${face.style}`;
20
20
  return name;
21
21
  }
22
+ /**
23
+ * Every name the faces are registered under, and the face each names, in the order they are
24
+ * registered. A face is registered under `faceName`, the last face of that name winning, and the
25
+ * first face of a family under the family as well. A face with both a weight and a style also
26
+ * answers to each alone, where no face declared exactly that.
27
+ */
28
+ export function registeredFaces(faces) {
29
+ const names = new Map();
30
+ for (const face of faces) {
31
+ if (!names.has(face.family))
32
+ names.set(face.family, face);
33
+ names.set(faceName(face), face);
34
+ }
35
+ for (const face of faces) {
36
+ if (face.weight === undefined || !face.style)
37
+ continue;
38
+ for (const name of [`${face.family}-${face.weight}`, `${face.family}-${face.style}`]) {
39
+ if (!names.has(name))
40
+ names.set(name, face);
41
+ }
42
+ }
43
+ return names;
44
+ }
22
45
  /** CSS's fallback order for a style: the closest slant first, upright last. */
23
46
  const STYLE_ORDER = {
24
47
  italic: ['italic', 'oblique', 'normal'],
@@ -67,13 +90,32 @@ const familyKey = (family) => family.toLowerCase();
67
90
  /** Every face the engine has seen a sheet declare, by family. */
68
91
  export class FontFaces {
69
92
  byFamily = new Map();
93
+ /**
94
+ * Every face, in the order taken in, one declared twice under a name too: the name index takes
95
+ * the last of them, as expo-font registers it, where matching keeps the first.
96
+ */
97
+ faces = [];
98
+ /** What `postScriptName` reads, made again on the first read after the faces change. */
99
+ names = null;
70
100
  /** Forget every face, before they are taken in again from the sheets still registered. */
71
101
  clear() {
72
102
  this.byFamily.clear();
103
+ this.faces.length = 0;
104
+ this.names = null;
105
+ }
106
+ /**
107
+ * The PostScript name of the face a name is registered under, as `registeredFaces` names them,
108
+ * where the build could read it from the face's file.
109
+ */
110
+ postScriptName(name) {
111
+ this.names ??= registeredFaces(this.faces);
112
+ return this.names.get(name)?.postScriptName;
73
113
  }
74
114
  /** Take faces in; whether any was new, which changes what text already matched may match. */
75
115
  add(faces) {
76
116
  let added = false;
117
+ this.faces.push(...faces);
118
+ this.names = null;
77
119
  for (const face of faces) {
78
120
  const known = this.byFamily.get(familyKey(face.family));
79
121
  if (known?.some((one) => faceName(one) === faceName(face)))
@@ -1 +1 @@
1
- {"version":3,"file":"font-faces.js","sourceRoot":"","sources":["../src/font-faces.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Font matching, which native does not do.\n *\n * A device finds a family by name and nothing else, so each `@font-face` is registered under a\n * name of its own (`faceName`), and a text asking for a declared family with a weight or a style\n * is pointed at the face CSS would pick for it. That happens on the resolved style, after the\n * cascade, because a family and a weight often come from different rules or from an ancestor.\n */\nimport type { StyleSheet } from './css.ts';\n\ntype FontFace = NonNullable<StyleSheet['fonts']>[number];\n\n/**\n * When a face was picked, whether to drop the `fontWeight` and `fontStyle` it was picked for from\n * what is committed. The face already draws them, and kept, the platform may embolden or slant it\n * again on top.\n */\nexport const DROP_MATCHED_WEIGHT_AND_STYLE = true;\n\n/**\n * The name a face is registered under: the family, then its weight and its style where it\n * declares them. `Inter`, `Inter-600`, `Inter-italic`, `Inter-600-italic`. A variable font's\n * range is both its ends, `Inter-100to900`, so two files that split a family by range differ.\n */\nexport function faceName(\n face: Pick<FontFace, 'family' | 'weight' | 'weightRange' | 'style'>,\n): string {\n let name = face.family;\n if (face.weight !== undefined) name += `-${face.weight}`;\n else if (face.weightRange) name += `-${face.weightRange[0]}to${face.weightRange[1]}`;\n if (face.style) name += `-${face.style}`;\n return name;\n}\n\n/** CSS's fallback order for a style: the closest slant first, upright last. */\nconst STYLE_ORDER: Readonly<Record<string, readonly string[]>> = {\n italic: ['italic', 'oblique', 'normal'],\n oblique: ['oblique', 'italic', 'normal'],\n normal: ['normal', 'oblique', 'italic'],\n};\n\n/** A `fontWeight` as a number: `normal` and unset are 400, `bold` 700. NaN for anything else. */\nfunction weightOf(value: unknown): number {\n if (value === undefined || value === 'normal') return 400;\n if (value === 'bold') return 700;\n return Number(value);\n}\n\n/**\n * The face CSS's matching picks among one family's, for a wanted weight and style.\n *\n * Style first, then weight, as in CSS Fonts 4 section 5.2. For a weight from 400 to 500, the\n * weights up to 500 are tried first, then lighter ones, then heavier; under 400, lighter then\n * heavier; over 500, heavier then lighter. Each direction nearest first.\n */\nexport function matchFace(\n faces: readonly FontFace[],\n weight: number,\n style: string,\n): FontFace | undefined {\n const slant = (face: FontFace) => face.style ?? 'normal';\n const order = STYLE_ORDER[style] ?? STYLE_ORDER['normal']!;\n const wantedStyle = order.find((one) => faces.some((face) => slant(face) === one));\n const candidates = faces.filter((face) => slant(face) === wantedStyle);\n\n // A face that covers a range has every weight in it: the nearest it has to the one wanted.\n const weightOfFace = (face: FontFace) =>\n face.weightRange\n ? Math.min(Math.max(weight, face.weightRange[0]), face.weightRange[1])\n : (face.weight ?? 400);\n const heavier = (from: number, to: number) =>\n candidates\n .filter((face) => weightOfFace(face) >= from && weightOfFace(face) <= to)\n .sort((a, b) => weightOfFace(a) - weightOfFace(b))[0];\n const lighter = (upTo: number) =>\n candidates\n .filter((face) => weightOfFace(face) <= upTo)\n .sort((a, b) => weightOfFace(b) - weightOfFace(a))[0];\n\n if (weight >= 400 && weight <= 500) {\n return heavier(weight, 500) ?? lighter(weight) ?? heavier(500, Infinity);\n }\n if (weight < 400) return lighter(weight) ?? heavier(weight, Infinity);\n return heavier(weight, Infinity) ?? lighter(weight);\n}\n\n/** A family as CSS compares it: without regard to case. */\nconst familyKey = (family: string) => family.toLowerCase();\n\n/** Every face the engine has seen a sheet declare, by family. */\nexport class FontFaces {\n private readonly byFamily = new Map<string, FontFace[]>();\n\n /** Forget every face, before they are taken in again from the sheets still registered. */\n clear(): void {\n this.byFamily.clear();\n }\n\n /** Take faces in; whether any was new, which changes what text already matched may match. */\n add(faces: readonly FontFace[]): boolean {\n let added = false;\n for (const face of faces) {\n const known = this.byFamily.get(familyKey(face.family));\n if (known?.some((one) => faceName(one) === faceName(face))) continue;\n if (known) known.push(face);\n else this.byFamily.set(familyKey(face.family), [face]);\n added = true;\n }\n return added;\n }\n\n /**\n * Point a resolved style's family at the face its weight and style ask for. A family nothing\n * declared is left alone, and so is the family of one whose match is the face registered under\n * the family itself.\n */\n apply(style: Record<string, unknown>): void {\n if (this.byFamily.size === 0) return;\n const family = style['fontFamily'];\n if (typeof family !== 'string') return;\n const faces = this.byFamily.get(familyKey(family));\n if (!faces) return;\n\n const weight = weightOf(style['fontWeight']);\n if (Number.isNaN(weight)) return;\n const face = matchFace(faces, weight, String(style['fontStyle'] ?? 'normal'));\n if (!face) return;\n const name = faceName(face);\n if (name !== family) style['fontFamily'] = name;\n\n if (!DROP_MATCHED_WEIGHT_AND_STYLE) return;\n // The style goes whichever face matched: a slanted face draws its slant, and beside an\n // upright one Android draws its system font slanted in place of the face, which iOS draws\n // upright anyway.\n delete style['fontStyle'];\n // A face of one weight draws it. One that covers a range is told which, so the weight stays.\n if (face.weight !== undefined) delete style['fontWeight'];\n }\n}\n"]}
1
+ {"version":3,"file":"font-faces.js","sourceRoot":"","sources":["../src/font-faces.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Font matching, which native does not do.\n *\n * A device finds a family by name and nothing else, so each `@font-face` is registered under a\n * name of its own (`faceName`), and a text asking for a declared family with a weight or a style\n * is pointed at the face CSS would pick for it. That happens on the resolved style, after the\n * cascade, because a family and a weight often come from different rules or from an ancestor.\n */\nimport type { StyleSheet } from './css.ts';\n\ntype FontFace = NonNullable<StyleSheet['fonts']>[number];\n\n/**\n * When a face was picked, whether to drop the `fontWeight` and `fontStyle` it was picked for from\n * what is committed. The face already draws them, and kept, the platform may embolden or slant it\n * again on top.\n */\nexport const DROP_MATCHED_WEIGHT_AND_STYLE = true;\n\n/**\n * The name a face is registered under: the family, then its weight and its style where it\n * declares them. `Inter`, `Inter-600`, `Inter-italic`, `Inter-600-italic`. A variable font's\n * range is both its ends, `Inter-100to900`, so two files that split a family by range differ.\n */\nexport function faceName(\n face: Pick<FontFace, 'family' | 'weight' | 'weightRange' | 'style'>,\n): string {\n let name = face.family;\n if (face.weight !== undefined) name += `-${face.weight}`;\n else if (face.weightRange) name += `-${face.weightRange[0]}to${face.weightRange[1]}`;\n if (face.style) name += `-${face.style}`;\n return name;\n}\n\n/**\n * Every name the faces are registered under, and the face each names, in the order they are\n * registered. A face is registered under `faceName`, the last face of that name winning, and the\n * first face of a family under the family as well. A face with both a weight and a style also\n * answers to each alone, where no face declared exactly that.\n */\nexport function registeredFaces<Face extends FontFace>(faces: readonly Face[]): Map<string, Face> {\n const names = new Map<string, Face>();\n for (const face of faces) {\n if (!names.has(face.family)) names.set(face.family, face);\n names.set(faceName(face), face);\n }\n for (const face of faces) {\n if (face.weight === undefined || !face.style) continue;\n for (const name of [`${face.family}-${face.weight}`, `${face.family}-${face.style}`]) {\n if (!names.has(name)) names.set(name, face);\n }\n }\n return names;\n}\n\n/** CSS's fallback order for a style: the closest slant first, upright last. */\nconst STYLE_ORDER: Readonly<Record<string, readonly string[]>> = {\n italic: ['italic', 'oblique', 'normal'],\n oblique: ['oblique', 'italic', 'normal'],\n normal: ['normal', 'oblique', 'italic'],\n};\n\n/** A `fontWeight` as a number: `normal` and unset are 400, `bold` 700. NaN for anything else. */\nfunction weightOf(value: unknown): number {\n if (value === undefined || value === 'normal') return 400;\n if (value === 'bold') return 700;\n return Number(value);\n}\n\n/**\n * The face CSS's matching picks among one family's, for a wanted weight and style.\n *\n * Style first, then weight, as in CSS Fonts 4 section 5.2. For a weight from 400 to 500, the\n * weights up to 500 are tried first, then lighter ones, then heavier; under 400, lighter then\n * heavier; over 500, heavier then lighter. Each direction nearest first.\n */\nexport function matchFace(\n faces: readonly FontFace[],\n weight: number,\n style: string,\n): FontFace | undefined {\n const slant = (face: FontFace) => face.style ?? 'normal';\n const order = STYLE_ORDER[style] ?? STYLE_ORDER['normal']!;\n const wantedStyle = order.find((one) => faces.some((face) => slant(face) === one));\n const candidates = faces.filter((face) => slant(face) === wantedStyle);\n\n // A face that covers a range has every weight in it: the nearest it has to the one wanted.\n const weightOfFace = (face: FontFace) =>\n face.weightRange\n ? Math.min(Math.max(weight, face.weightRange[0]), face.weightRange[1])\n : (face.weight ?? 400);\n const heavier = (from: number, to: number) =>\n candidates\n .filter((face) => weightOfFace(face) >= from && weightOfFace(face) <= to)\n .sort((a, b) => weightOfFace(a) - weightOfFace(b))[0];\n const lighter = (upTo: number) =>\n candidates\n .filter((face) => weightOfFace(face) <= upTo)\n .sort((a, b) => weightOfFace(b) - weightOfFace(a))[0];\n\n if (weight >= 400 && weight <= 500) {\n return heavier(weight, 500) ?? lighter(weight) ?? heavier(500, Infinity);\n }\n if (weight < 400) return lighter(weight) ?? heavier(weight, Infinity);\n return heavier(weight, Infinity) ?? lighter(weight);\n}\n\n/** A family as CSS compares it: without regard to case. */\nconst familyKey = (family: string) => family.toLowerCase();\n\n/** Every face the engine has seen a sheet declare, by family. */\nexport class FontFaces {\n private readonly byFamily = new Map<string, FontFace[]>();\n /**\n * Every face, in the order taken in, one declared twice under a name too: the name index takes\n * the last of them, as expo-font registers it, where matching keeps the first.\n */\n private readonly faces: FontFace[] = [];\n /** What `postScriptName` reads, made again on the first read after the faces change. */\n private names: Map<string, FontFace> | null = null;\n\n /** Forget every face, before they are taken in again from the sheets still registered. */\n clear(): void {\n this.byFamily.clear();\n this.faces.length = 0;\n this.names = null;\n }\n\n /**\n * The PostScript name of the face a name is registered under, as `registeredFaces` names them,\n * where the build could read it from the face's file.\n */\n postScriptName(name: string): string | undefined {\n this.names ??= registeredFaces(this.faces);\n return this.names.get(name)?.postScriptName;\n }\n\n /** Take faces in; whether any was new, which changes what text already matched may match. */\n add(faces: readonly FontFace[]): boolean {\n let added = false;\n this.faces.push(...faces);\n this.names = null;\n for (const face of faces) {\n const known = this.byFamily.get(familyKey(face.family));\n if (known?.some((one) => faceName(one) === faceName(face))) continue;\n if (known) known.push(face);\n else this.byFamily.set(familyKey(face.family), [face]);\n added = true;\n }\n return added;\n }\n\n /**\n * Point a resolved style's family at the face its weight and style ask for. A family nothing\n * declared is left alone, and so is the family of one whose match is the face registered under\n * the family itself.\n */\n apply(style: Record<string, unknown>): void {\n if (this.byFamily.size === 0) return;\n const family = style['fontFamily'];\n if (typeof family !== 'string') return;\n const faces = this.byFamily.get(familyKey(family));\n if (!faces) return;\n\n const weight = weightOf(style['fontWeight']);\n if (Number.isNaN(weight)) return;\n const face = matchFace(faces, weight, String(style['fontStyle'] ?? 'normal'));\n if (!face) return;\n const name = faceName(face);\n if (name !== family) style['fontFamily'] = name;\n\n if (!DROP_MATCHED_WEIGHT_AND_STYLE) return;\n // The style goes whichever face matched: a slanted face draws its slant, and beside an\n // upright one Android draws its system font slanted in place of the face, which iOS draws\n // upright anyway.\n delete style['fontStyle'];\n // A face of one weight draws it. One that covers a range is told which, so the weight stays.\n if (face.weight !== undefined) delete style['fontWeight'];\n }\n}\n"]}
@@ -2,7 +2,7 @@
2
2
  export declare function isFormsInput(name: string): boolean;
3
3
  /** An element, as far as this report needs one: enough to name it. */
4
4
  export interface NamedHostNode {
5
- readonly name: string;
5
+ readonly tag: string;
6
6
  }
7
7
  /**
8
8
  * Reports `name` on `el` as an unbound forms directive input, once per `owner`/element/input
@@ -42,12 +42,12 @@ export function reportUnboundFormsInput(owner, el, name) {
42
42
  return;
43
43
  const seen = reportedInputs.get(owner) ?? new Set();
44
44
  reportedInputs.set(owner, seen);
45
- const key = `${el.name} ${name}`;
45
+ const key = `${el.tag} ${name}`;
46
46
  if (seen.has(key))
47
47
  return;
48
48
  seen.add(key);
49
49
  const [directive, from] = directiveEntry;
50
- console.error(`[angular-native] Can't bind to '${name}' on <${el.name}>: no directive in its template ` +
50
+ console.error(`[angular-native] Can't bind to '${name}' on <${el.tag}>: no directive in its template ` +
51
51
  `takes it, so it binds nothing. Add ${directive} from '${from}' to the component's ` +
52
52
  `\`imports\`.`);
53
53
  }
@@ -1 +1 @@
1
- {"version":3,"file":"forms-inputs.js","sourceRoot":"","sources":["../src/forms-inputs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,YAAY,GAAyE;IACzF,SAAS,EAAE,CAAC,WAAW,EAAE,wBAAwB,CAAC;IAClD,OAAO,EAAE,CAAC,aAAa,EAAE,gBAAgB,CAAC;IAC1C,WAAW,EAAE,CAAC,qBAAqB,EAAE,gBAAgB,CAAC;IACtD,eAAe,EAAE,CAAC,qBAAqB,EAAE,gBAAgB,CAAC;IAC1D,SAAS,EAAE,CAAC,qBAAqB,EAAE,gBAAgB,CAAC;CACrD,CAAC;AAEF,4FAA4F;AAC5F,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;AAC3C,CAAC;AAOD,0FAA0F;AAC1F,MAAM,cAAc,GAAG,IAAI,OAAO,EAAuB,CAAC;AAE1D;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAa,EAAE,EAAiB,EAAE,IAAY;IACpF,MAAM,cAAc,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,CAAC,cAAc;QAAE,OAAO;IAC5B,MAAM,IAAI,GAAG,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;IAC5D,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAChC,MAAM,GAAG,GAAG,GAAG,EAAE,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;IACjC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO;IAC1B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACd,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,cAAc,CAAC;IACzC,OAAO,CAAC,KAAK,CACX,mCAAmC,IAAI,SAAS,EAAE,CAAC,IAAI,kCAAkC;QACvF,sCAAsC,SAAS,UAAU,IAAI,uBAAuB;QACpF,cAAc,CACjB,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Inputs of Angular's forms directives, by the directive that takes each and where it comes from,\n * and the report both `Renderer2` adapters make when one reaches them unclaimed.\n *\n * The names and package paths below are the one piece of Angular-specific knowledge in this\n * package - everything else here stays framework-agnostic, per `docs/ARCHITECTURE.md`. It lives here\n * anyway, rather than duplicated in `@ng-native/platform` and `@ng-native/web`, because both of\n * their `Renderer2`s hit exactly the same failure for exactly the same reason and neither imports\n * the other: a binding reaches `setProperty` only when no directive on the element took it, and\n * none of these names is a native prop or a DOM one, so one arriving there means the directive\n * was never imported. Angular would say so on a real `platform-browser` bootstrap - its template\n * type-checker is not in this build, and neither adapter's runtime check can help, because one\n * takes any hyphenated element for a web component that may have any property and the other has\n * no DOM to run its own check against. So `<text-input [formField]>` without `FormField` compiles,\n * renders, and binds nothing, and the form sits empty and invalid without a word - unless\n * something says so here.\n */\nconst FORMS_INPUTS: Readonly<Record<string, readonly [directive: string, from: string]>> = {\n formField: ['FormField', '@angular/forms/signals'],\n ngModel: ['FormsModule', '@angular/forms'],\n formControl: ['ReactiveFormsModule', '@angular/forms'],\n formControlName: ['ReactiveFormsModule', '@angular/forms'],\n formGroup: ['ReactiveFormsModule', '@angular/forms'],\n};\n\n/** Whether a name is a forms directive's input, which `reportUnboundFormsInput` reports. */\nexport function isFormsInput(name: string): boolean {\n return Object.hasOwn(FORMS_INPUTS, name);\n}\n\n/** An element, as far as this report needs one: enough to name it. */\nexport interface NamedHostNode {\n readonly name: string;\n}\n\n/** Elements and inputs already reported, per caller, so a list of fields says it once. */\nconst reportedInputs = new WeakMap<object, Set<string>>();\n\n/**\n * Reports `name` on `el` as an unbound forms directive input, once per `owner`/element/input\n * triple. A no-op when `name` is not one of `FORMS_INPUTS`, so a caller can run this unconditionally\n * from `setProperty` without checking first.\n *\n * `owner` keys the \"already reported\" set - each adapter passes its own engine, so two mounted\n * apps (or two tests) never suppress each other's first report.\n */\nexport function reportUnboundFormsInput(owner: object, el: NamedHostNode, name: string): void {\n const directiveEntry = FORMS_INPUTS[name];\n if (!directiveEntry) return;\n const seen = reportedInputs.get(owner) ?? new Set<string>();\n reportedInputs.set(owner, seen);\n const key = `${el.name} ${name}`;\n if (seen.has(key)) return;\n seen.add(key);\n const [directive, from] = directiveEntry;\n console.error(\n `[angular-native] Can't bind to '${name}' on <${el.name}>: no directive in its template ` +\n `takes it, so it binds nothing. Add ${directive} from '${from}' to the component's ` +\n `\\`imports\\`.`,\n );\n}\n"]}
1
+ {"version":3,"file":"forms-inputs.js","sourceRoot":"","sources":["../src/forms-inputs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,YAAY,GAAyE;IACzF,SAAS,EAAE,CAAC,WAAW,EAAE,wBAAwB,CAAC;IAClD,OAAO,EAAE,CAAC,aAAa,EAAE,gBAAgB,CAAC;IAC1C,WAAW,EAAE,CAAC,qBAAqB,EAAE,gBAAgB,CAAC;IACtD,eAAe,EAAE,CAAC,qBAAqB,EAAE,gBAAgB,CAAC;IAC1D,SAAS,EAAE,CAAC,qBAAqB,EAAE,gBAAgB,CAAC;CACrD,CAAC;AAEF,4FAA4F;AAC5F,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;AAC3C,CAAC;AAOD,0FAA0F;AAC1F,MAAM,cAAc,GAAG,IAAI,OAAO,EAAuB,CAAC;AAE1D;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAa,EAAE,EAAiB,EAAE,IAAY;IACpF,MAAM,cAAc,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,CAAC,cAAc;QAAE,OAAO;IAC5B,MAAM,IAAI,GAAG,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;IAC5D,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAChC,MAAM,GAAG,GAAG,GAAG,EAAE,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC;IAChC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO;IAC1B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACd,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,cAAc,CAAC;IACzC,OAAO,CAAC,KAAK,CACX,mCAAmC,IAAI,SAAS,EAAE,CAAC,GAAG,kCAAkC;QACtF,sCAAsC,SAAS,UAAU,IAAI,uBAAuB;QACpF,cAAc,CACjB,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Inputs of Angular's forms directives, by the directive that takes each and where it comes from,\n * and the report both `Renderer2` adapters make when one reaches them unclaimed.\n *\n * The names and package paths below are the one piece of Angular-specific knowledge in this\n * package - everything else here stays framework-agnostic, per `docs/ARCHITECTURE.md`. It lives here\n * anyway, rather than duplicated in `@ng-native/platform` and `@ng-native/web`, because both of\n * their `Renderer2`s hit exactly the same failure for exactly the same reason and neither imports\n * the other: a binding reaches `setProperty` only when no directive on the element took it, and\n * none of these names is a native prop or a DOM one, so one arriving there means the directive\n * was never imported. Angular would say so on a real `platform-browser` bootstrap - its template\n * type-checker is not in this build, and neither adapter's runtime check can help, because one\n * takes any hyphenated element for a web component that may have any property and the other has\n * no DOM to run its own check against. So `<text-input [formField]>` without `FormField` compiles,\n * renders, and binds nothing, and the form sits empty and invalid without a word - unless\n * something says so here.\n */\nconst FORMS_INPUTS: Readonly<Record<string, readonly [directive: string, from: string]>> = {\n formField: ['FormField', '@angular/forms/signals'],\n ngModel: ['FormsModule', '@angular/forms'],\n formControl: ['ReactiveFormsModule', '@angular/forms'],\n formControlName: ['ReactiveFormsModule', '@angular/forms'],\n formGroup: ['ReactiveFormsModule', '@angular/forms'],\n};\n\n/** Whether a name is a forms directive's input, which `reportUnboundFormsInput` reports. */\nexport function isFormsInput(name: string): boolean {\n return Object.hasOwn(FORMS_INPUTS, name);\n}\n\n/** An element, as far as this report needs one: enough to name it. */\nexport interface NamedHostNode {\n readonly tag: string;\n}\n\n/** Elements and inputs already reported, per caller, so a list of fields says it once. */\nconst reportedInputs = new WeakMap<object, Set<string>>();\n\n/**\n * Reports `name` on `el` as an unbound forms directive input, once per `owner`/element/input\n * triple. A no-op when `name` is not one of `FORMS_INPUTS`, so a caller can run this unconditionally\n * from `setProperty` without checking first.\n *\n * `owner` keys the \"already reported\" set - each adapter passes its own engine, so two mounted\n * apps (or two tests) never suppress each other's first report.\n */\nexport function reportUnboundFormsInput(owner: object, el: NamedHostNode, name: string): void {\n const directiveEntry = FORMS_INPUTS[name];\n if (!directiveEntry) return;\n const seen = reportedInputs.get(owner) ?? new Set<string>();\n reportedInputs.set(owner, seen);\n const key = `${el.tag} ${name}`;\n if (seen.has(key)) return;\n seen.add(key);\n const [directive, from] = directiveEntry;\n console.error(\n `[angular-native] Can't bind to '${name}' on <${el.tag}>: no directive in its template ` +\n `takes it, so it binds nothing. Add ${directive} from '${from}' to the component's ` +\n `\\`imports\\`.`,\n );\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -13,9 +13,9 @@
13
13
  export { StyleResolver, matches, styleSheetOf, type AttributeTest, type Combinator, type Compound, type Conditions, type DeferredDeclaration, type GradientTemplate, type HslChannel, type MediaCondition, type NthTest, type RuleIndex, type StyleCache, type StyleRule, type StyleSheet, type StyleTarget, type TokenKind, type TokenValue, } from './css.ts';
14
14
  export { ELEMENT_STYLES, ELEMENT_STYLES_CSS } from './element-styles.ts';
15
15
  export { EngineIntersectionObserver, installDeferTriggers } from './defer-triggers.ts';
16
- export { faceName } from './font-faces.ts';
16
+ export { faceName, registeredFaces } from './font-faces.ts';
17
17
  export { firstFamily } from './inline-token.ts';
18
- export { DIRECT_EVENTS, Engine, SyntheticEvent, claimHost, keepNativeView, declareNativeProps, fontsLoading, fontsRegistered, fontsSettled, markComponentHost, nativePlatform, onFontsRegistered, onFontsSettled, registerHoist, registerPlatformComponents, registerViewName, type PlatformViewName, type ViewNameOptions, viewNameOf, type EngineNode, type EngineOptions, type EngineStats, type FabricNode, type FabricNodeSet, type FabricUIManager, type HoistOptions, type NativeSyntheticEvent, type NodeKind, type ResponderEvent, type ResponderHandlers, type TouchPayload, type ViewNameNode, type WindowFrame, } from './engine.ts';
18
+ export { DIRECT_EVENTS, Engine, SyntheticEvent, claimHost, keepNativeView, declareNativeProps, fontsLoading, fontsRegistered, fontsSettled, markComponentHost, nativePlatform, onFontsRegistered, onFontsSettled, registerHoist, registerPlatformComponents, registerViewName, type PlatformViewName, type ViewNameOptions, type PostScriptNames, viewNameOf, type EngineNode, type EngineOptions, type EngineStats, type FabricNode, type FabricNodeSet, type FabricUIManager, type HoistOptions, type NativeSyntheticEvent, type NodeKind, type ResponderEvent, type ResponderHandlers, type TouchPayload, type ViewNameNode, type WindowFrame, } from './engine.ts';
19
19
  export { reportUnboundFormsInput, type NamedHostNode } from './forms-inputs.ts';
20
20
  export { HostEngine, type HostNode, type Settling } from './host.ts';
21
21
  export { getFabricUIManager } from './fabric.ts';
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@
13
13
  export { StyleResolver, matches, styleSheetOf, } from "./css.js";
14
14
  export { ELEMENT_STYLES, ELEMENT_STYLES_CSS } from "./element-styles.js";
15
15
  export { EngineIntersectionObserver, installDeferTriggers } from "./defer-triggers.js";
16
- export { faceName } from "./font-faces.js";
16
+ export { faceName, registeredFaces } from "./font-faces.js";
17
17
  export { firstFamily } from "./inline-token.js";
18
18
  export { DIRECT_EVENTS, Engine, SyntheticEvent, claimHost, keepNativeView, declareNativeProps, fontsLoading, fontsRegistered, fontsSettled, markComponentHost, nativePlatform, onFontsRegistered, onFontsSettled, registerHoist, registerPlatformComponents, registerViewName, viewNameOf, } from "./engine.js";
19
19
  export { reportUnboundFormsInput } from "./forms-inputs.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EACL,aAAa,EACb,OAAO,EACP,YAAY,GAiBb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AACvF,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EACL,aAAa,EACb,MAAM,EACN,cAAc,EACd,SAAS,EACT,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,0BAA0B,EAC1B,gBAAgB,EAGhB,UAAU,GAeX,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,uBAAuB,EAAsB,MAAM,mBAAmB,CAAC;AAChF,OAAO,EAAE,UAAU,EAAgC,MAAM,WAAW,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EACL,WAAW,GAQZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,MAAM,EACN,WAAW,EACX,QAAQ,EACR,MAAM,EACN,OAAO,EACP,IAAI,EACJ,QAAQ,EACR,KAAK,GAMN,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAyB,MAAM,wBAAwB,CAAC","sourcesContent":["/**\n * The engine's public surface.\n *\n * Nothing here imports Angular, and nothing here may start to: the engine stays\n * framework-agnostic (see docs/ARCHITECTURE.md), which is what keeps the commit logic testable without Angular in\n * the loop and a second adapter possible later.\n *\n * The CSS runtime lives here rather than in a package of its own because the cascade is driven\n * from the commit walk - the resolver runs per node, per tick - and because it is as\n * framework-agnostic as the rest of the engine. The build-time half of CSS is a different\n * animal and lives in `@ng-native/metro`.\n */\nexport {\n StyleResolver,\n matches,\n styleSheetOf,\n type AttributeTest,\n type Combinator,\n type Compound,\n type Conditions,\n type DeferredDeclaration,\n type GradientTemplate,\n type HslChannel,\n type MediaCondition,\n type NthTest,\n type RuleIndex,\n type StyleCache,\n type StyleRule,\n type StyleSheet,\n type StyleTarget,\n type TokenKind,\n type TokenValue,\n} from './css.ts';\nexport { ELEMENT_STYLES, ELEMENT_STYLES_CSS } from './element-styles.ts';\nexport { EngineIntersectionObserver, installDeferTriggers } from './defer-triggers.ts';\nexport { faceName } from './font-faces.ts';\nexport { firstFamily } from './inline-token.ts';\nexport {\n DIRECT_EVENTS,\n Engine,\n SyntheticEvent,\n claimHost,\n keepNativeView,\n declareNativeProps,\n fontsLoading,\n fontsRegistered,\n fontsSettled,\n markComponentHost,\n nativePlatform,\n onFontsRegistered,\n onFontsSettled,\n registerHoist,\n registerPlatformComponents,\n registerViewName,\n type PlatformViewName,\n type ViewNameOptions,\n viewNameOf,\n type EngineNode,\n type EngineOptions,\n type EngineStats,\n type FabricNode,\n type FabricNodeSet,\n type FabricUIManager,\n type HoistOptions,\n type NativeSyntheticEvent,\n type NodeKind,\n type ResponderEvent,\n type ResponderHandlers,\n type TouchPayload,\n type ViewNameNode,\n type WindowFrame,\n} from './engine.ts';\nexport { reportUnboundFormsInput, type NamedHostNode } from './forms-inputs.ts';\nexport { HostEngine, type HostNode, type Settling } from './host.ts';\nexport { getFabricUIManager } from './fabric.ts';\nexport {\n pinnedRange,\n type DrivenProperty,\n type EventFeed,\n type NativeAnimated,\n type ScrollAxis,\n type ScrollDrive,\n type ScrollRange,\n type StaticTransform,\n} from './native-drive.ts';\nexport {\n bezier,\n interpolate,\n redirect,\n sample,\n settled,\n step,\n tracksOf,\n tween,\n type AnimationSpec,\n type Keyframe,\n type RunningAnimation,\n type Transition,\n type TransitionSpec,\n} from './transition.ts';\nexport { backgroundLayers, type BackgroundLayers } from './background-layers.ts';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EACL,aAAa,EACb,OAAO,EACP,YAAY,GAiBb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AACvF,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EACL,aAAa,EACb,MAAM,EACN,cAAc,EACd,SAAS,EACT,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,0BAA0B,EAC1B,gBAAgB,EAIhB,UAAU,GAeX,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,uBAAuB,EAAsB,MAAM,mBAAmB,CAAC;AAChF,OAAO,EAAE,UAAU,EAAgC,MAAM,WAAW,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EACL,WAAW,GAQZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,MAAM,EACN,WAAW,EACX,QAAQ,EACR,MAAM,EACN,OAAO,EACP,IAAI,EACJ,QAAQ,EACR,KAAK,GAMN,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAyB,MAAM,wBAAwB,CAAC","sourcesContent":["/**\n * The engine's public surface.\n *\n * Nothing here imports Angular, and nothing here may start to: the engine stays\n * framework-agnostic (see docs/ARCHITECTURE.md), which is what keeps the commit logic testable without Angular in\n * the loop and a second adapter possible later.\n *\n * The CSS runtime lives here rather than in a package of its own because the cascade is driven\n * from the commit walk - the resolver runs per node, per tick - and because it is as\n * framework-agnostic as the rest of the engine. The build-time half of CSS is a different\n * animal and lives in `@ng-native/metro`.\n */\nexport {\n StyleResolver,\n matches,\n styleSheetOf,\n type AttributeTest,\n type Combinator,\n type Compound,\n type Conditions,\n type DeferredDeclaration,\n type GradientTemplate,\n type HslChannel,\n type MediaCondition,\n type NthTest,\n type RuleIndex,\n type StyleCache,\n type StyleRule,\n type StyleSheet,\n type StyleTarget,\n type TokenKind,\n type TokenValue,\n} from './css.ts';\nexport { ELEMENT_STYLES, ELEMENT_STYLES_CSS } from './element-styles.ts';\nexport { EngineIntersectionObserver, installDeferTriggers } from './defer-triggers.ts';\nexport { faceName, registeredFaces } from './font-faces.ts';\nexport { firstFamily } from './inline-token.ts';\nexport {\n DIRECT_EVENTS,\n Engine,\n SyntheticEvent,\n claimHost,\n keepNativeView,\n declareNativeProps,\n fontsLoading,\n fontsRegistered,\n fontsSettled,\n markComponentHost,\n nativePlatform,\n onFontsRegistered,\n onFontsSettled,\n registerHoist,\n registerPlatformComponents,\n registerViewName,\n type PlatformViewName,\n type ViewNameOptions,\n type PostScriptNames,\n viewNameOf,\n type EngineNode,\n type EngineOptions,\n type EngineStats,\n type FabricNode,\n type FabricNodeSet,\n type FabricUIManager,\n type HoistOptions,\n type NativeSyntheticEvent,\n type NodeKind,\n type ResponderEvent,\n type ResponderHandlers,\n type TouchPayload,\n type ViewNameNode,\n type WindowFrame,\n} from './engine.ts';\nexport { reportUnboundFormsInput, type NamedHostNode } from './forms-inputs.ts';\nexport { HostEngine, type HostNode, type Settling } from './host.ts';\nexport { getFabricUIManager } from './fabric.ts';\nexport {\n pinnedRange,\n type DrivenProperty,\n type EventFeed,\n type NativeAnimated,\n type ScrollAxis,\n type ScrollDrive,\n type ScrollRange,\n type StaticTransform,\n} from './native-drive.ts';\nexport {\n bezier,\n interpolate,\n redirect,\n sample,\n settled,\n step,\n tracksOf,\n tween,\n type AnimationSpec,\n type Keyframe,\n type RunningAnimation,\n type Transition,\n type TransitionSpec,\n} from './transition.ts';\nexport { backgroundLayers, type BackgroundLayers } from './background-layers.ts';\n"]}
@@ -4,9 +4,10 @@
4
4
  *
5
5
  * A stylesheet's custom properties are converted when the app is built, where there is a CSS
6
6
  * parser (`@ng-native/metro/css/values.cjs`). A bound one only exists here, where there is not,
7
- * so this takes the shapes a binding actually holds and nothing more: a length in `px` or `%`, a
8
- * number, a colour as React Native writes one, a colour's three channels, a word, another token,
9
- * `var(--brand)`, and one made of others, `calc(var(--gap) * 2)` or a `color-mix()` (see
7
+ * so this takes the shapes a binding actually holds and nothing more: a length in `px`, `%`,
8
+ * `rem`, `em` or a viewport unit, two to four lengths in `px`, a number, a colour as React Native
9
+ * writes one, a colour's three channels, a word, another token, `var(--brand)`, and one made of
10
+ * others, `calc(var(--gap) * 2)`, `calc(10px * 2)` or a `color-mix()` (see
10
11
  * inline-derived-token.ts).
11
12
  * Anything else is kept as a word, which leaves a use site that wants a length or a colour unset,
12
13
  * as a token of the wrong kind does.
@@ -4,9 +4,10 @@
4
4
  *
5
5
  * A stylesheet's custom properties are converted when the app is built, where there is a CSS
6
6
  * parser (`@ng-native/metro/css/values.cjs`). A bound one only exists here, where there is not,
7
- * so this takes the shapes a binding actually holds and nothing more: a length in `px` or `%`, a
8
- * number, a colour as React Native writes one, a colour's three channels, a word, another token,
9
- * `var(--brand)`, and one made of others, `calc(var(--gap) * 2)` or a `color-mix()` (see
7
+ * so this takes the shapes a binding actually holds and nothing more: a length in `px`, `%`,
8
+ * `rem`, `em` or a viewport unit, two to four lengths in `px`, a number, a colour as React Native
9
+ * writes one, a colour's three channels, a word, another token, `var(--brand)`, and one made of
10
+ * others, `calc(var(--gap) * 2)`, `calc(10px * 2)` or a `color-mix()` (see
10
11
  * inline-derived-token.ts).
11
12
  * Anything else is kept as a word, which leaves a use site that wants a length or a colour unset,
12
13
  * as a token of the wrong kind does.
@@ -16,6 +17,7 @@ import { derivedToken } from "./inline-derived-token.js";
16
17
  import { HUE_DEGREES, isNamedColor } from "./transition.js";
17
18
  const PX = /^(-?\d*\.?\d+)px$/;
18
19
  const PERCENT = /^-?\d*\.?\d+%$/;
20
+ const RELATIVE = /^(-?\d*\.?\d+)(rem|em|vw|vh|vmin|vmax)$/i;
19
21
  const NUMBER = /^-?\d*\.?\d+$/;
20
22
  const ANGLE = /^(-?\d*\.?\d+)(deg|grad|rad|turn)$/i;
21
23
  /** `#fff`, `rgb()`, `rgba()`, `hsl()`, `hsla()` and `hwb()`: what React Native's `processColor` reads. */
@@ -26,10 +28,10 @@ const CHANNEL = /^(-?(?:\d+\.?\d*|\.\d+))(%|deg|grad|rad|turn)?$/;
26
28
  /** `var(--name)` or `var(--name, <fallback>)`, the whole value. */
27
29
  const VAR = /^var\([ \t\n\r\f]*(--[\w-]+)[ \t\n\r\f]*(?:,([\s\S]*))?\)$/i;
28
30
  /**
29
- * A value worked out where it is set: one with a `var()` in it, a `color-mix()`, or a relative
30
- * colour, of tokens or of colours written out.
31
+ * A value worked out where it is set: one with a `var()` in it, a `calc()`, `min()` or `max()`,
32
+ * a `color-mix()`, or a relative colour, of tokens or of values written out.
31
33
  */
32
- const DERIVED = /var\(|^color-mix\(|^[a-z]+\([ \t\n\r\f]*from[ \t\n\r\f]/i;
34
+ const DERIVED = /var\(|^(?:calc|min|max|color-mix)\(|^[a-z]+\([ \t\n\r\f]*from[ \t\n\r\f]/i;
33
35
  const WEIGHTS = { normal: '400', bold: '700' };
34
36
  /**
35
37
  * CSS whitespace at either end: a space, a tab, a newline, a carriage return or a form feed. A
@@ -93,7 +95,13 @@ function fromNumber(value) {
93
95
  const weight = value >= 1 && value <= 1000
94
96
  ? { weight: String(Math.min(900, Math.max(100, Math.round(value / 100) * 100))) }
95
97
  : {};
96
- return value === 0 ? { number: 0, length: 0 } : { number: value, ...weight };
98
+ // And a line height, as a multiple of the font size, as the stylesheet's own reads it.
99
+ const lineHeight = {
100
+ lineHeight: { __defer: { unit: 'em', factor: value, multiple: true } },
101
+ };
102
+ return value === 0
103
+ ? { number: 0, length: 0, ...lineHeight }
104
+ : { number: value, ...weight, ...lineHeight };
97
105
  }
98
106
  /**
99
107
  * A word is a keyword, `row`, and may be a colour, `red`, or a weight, `bold`, as well: every form
@@ -171,6 +179,14 @@ function balanced(text) {
171
179
  }
172
180
  return depth === 0;
173
181
  }
182
+ /** Two to four lengths in `px`, a padding or a margin's sides, as the stylesheet's own reads them. */
183
+ function fromLengths(text) {
184
+ const parts = text.split(/[ \t\n\r\f]+/);
185
+ if (parts.length < 2 || parts.length > 4)
186
+ return undefined;
187
+ const lengths = parts.map((part) => (part === '0' ? 0 : Number(PX.exec(part)?.[1] ?? NaN)));
188
+ return lengths.every(Number.isFinite) ? { lengths } : undefined;
189
+ }
174
190
  /** A time as CSS writes one: a number of seconds or milliseconds. */
175
191
  const TIME = /^([+-]?(?:\d+\.?\d*|\.\d+))(ms|s)$/i;
176
192
  /** A number with a unit, as the token each is: a length, a percentage, an angle or a time. */
@@ -178,6 +194,18 @@ function withUnit(text) {
178
194
  const px = PX.exec(text);
179
195
  if (px)
180
196
  return { length: Number(px[1]) };
197
+ // A `rem` is 16 points, and the others are measured where they are used, as in a stylesheet.
198
+ const relative = RELATIVE.exec(text);
199
+ if (relative) {
200
+ const [, factor, unit] = relative;
201
+ const lower = unit.toLowerCase();
202
+ if (lower === 'rem')
203
+ return { length: Number(factor) * 16 };
204
+ const measured = lower;
205
+ // The shape the stylesheet compiles one to, which `TokenValue` does not spell out.
206
+ const deferred = { __defer: { unit: measured, factor: Number(factor) } };
207
+ return { length: deferred };
208
+ }
181
209
  // A percentage is a fraction as well, and an angle its degrees, as the build-time conversion
182
210
  // reads them: an hsl() of tokens takes its saturation and its hue so.
183
211
  if (PERCENT.test(text))
@@ -218,6 +246,11 @@ export function tokenFromValue(value) {
218
246
  return withTokens(text);
219
247
  if (COLOR_FUNCTION.test(text))
220
248
  return { color: text };
221
- return fromChannels(text) ?? (WORD.test(text) ? fromWord(text) : fromText(text));
249
+ // `0 0 0` is both, so it is kept as both, as the stylesheet's own token is.
250
+ const lengths = fromLengths(text);
251
+ const channels = fromChannels(text);
252
+ if (lengths)
253
+ return { ...channels, ...lengths };
254
+ return channels ?? (WORD.test(text) ? fromWord(text) : fromText(text));
222
255
  }
223
256
  //# sourceMappingURL=inline-token.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"inline-token.js","sourceRoot":"","sources":["../src/inline-token.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A custom property's value set on an element at run time, `[style.--tint]=\"tint()\"` or\n * `style=\"--gap: 4px\"`, as a token the cascade can read.\n *\n * A stylesheet's custom properties are converted when the app is built, where there is a CSS\n * parser (`@ng-native/metro/css/values.cjs`). A bound one only exists here, where there is not,\n * so this takes the shapes a binding actually holds and nothing more: a length in `px` or `%`, a\n * number, a colour as React Native writes one, a colour's three channels, a word, another token,\n * `var(--brand)`, and one made of others, `calc(var(--gap) * 2)` or a `color-mix()` (see\n * inline-derived-token.ts).\n * Anything else is kept as a word, which leaves a use site that wants a length or a colour unset,\n * as a token of the wrong kind does.\n */\nimport { CSS_SPACE, type TokenValue } from './css.ts';\nimport { derivedToken } from './inline-derived-token.ts';\nimport { HUE_DEGREES, isNamedColor } from './transition.ts';\n\nconst PX = /^(-?\\d*\\.?\\d+)px$/;\nconst PERCENT = /^-?\\d*\\.?\\d+%$/;\nconst NUMBER = /^-?\\d*\\.?\\d+$/;\nconst ANGLE = /^(-?\\d*\\.?\\d+)(deg|grad|rad|turn)$/i;\n/** `#fff`, `rgb()`, `rgba()`, `hsl()`, `hsla()` and `hwb()`: what React Native's `processColor` reads. */\nconst COLOR_FUNCTION = /^(#[\\da-f]{3,8}|(rgba?|hsla?|hwb)\\(.*\\))$/i;\nconst WORD = /^-?[a-z][\\w-]*$/i;\n/** One colour channel: a number, a percentage, or a hue with its unit. */\nconst CHANNEL = /^(-?(?:\\d+\\.?\\d*|\\.\\d+))(%|deg|grad|rad|turn)?$/;\n/** `var(--name)` or `var(--name, <fallback>)`, the whole value. */\nconst VAR = /^var\\([ \\t\\n\\r\\f]*(--[\\w-]+)[ \\t\\n\\r\\f]*(?:,([\\s\\S]*))?\\)$/i;\n/**\n * A value worked out where it is set: one with a `var()` in it, a `color-mix()`, or a relative\n * colour, of tokens or of colours written out.\n */\nconst DERIVED = /var\\(|^color-mix\\(|^[a-z]+\\([ \\t\\n\\r\\f]*from[ \\t\\n\\r\\f]/i;\nconst WEIGHTS: Record<string, string> = { normal: '400', bold: '700' };\n/**\n * CSS whitespace at either end: a space, a tab, a newline, a carriage return or a form feed. A\n * no-break space is part of the value, as it is in a browser, where `trim()` would drop it.\n */\nconst PADDED = new RegExp(`^${CSS_SPACE.source}|${CSS_SPACE.source}$`, 'g');\nexport const cssTrim = (text: string): string => text.replace(PADDED, '');\n/** A family name's parts: a quoted string, which may hold escapes, or a bare word. */\nconst FAMILY_PART = /^(?:'((?:[^'\\\\]|\\\\[\\s\\S])*)'|\"((?:[^\"\\\\]|\\\\[\\s\\S])*)\"|(-?[a-z_][\\w-]*))/i;\nconst LEADING_SPACE = new RegExp(`^${CSS_SPACE.source}`);\n/**\n * A CSS escape: up to six hex digits and one whitespace after them, a newline, which only joins\n * the lines either side, or any other character.\n */\nconst ESCAPE = /\\\\([\\da-f]{1,6})(?:\\r\\n|[ \\t\\n\\r\\f])?|\\\\(\\r\\n|[\\n\\r\\f])|\\\\([\\s\\S])/gi;\n\n/** A quoted string's text with its escapes decoded. One naming no character is U+FFFD. */\nconst decodeEscapes = (text: string): string =>\n text.replace(ESCAPE, (_, hex: string | undefined, newline: string | undefined, char: string) => {\n if (newline !== undefined) return '';\n if (hex === undefined) return char;\n const code = parseInt(hex, 16);\n return code && code <= 0x10ffff && (code < 0xd800 || code > 0xdfff)\n ? String.fromCodePoint(code)\n : '\\ufffd';\n });\n\n/**\n * The first family of a `font-family` stack, as a stylesheet reads it (`firstFamily` in\n * values.cjs): `'Inter Display', sans-serif` is `Inter Display`, and `Segoe UI` is one name of two\n * words. Undefined for anything that is not a stack.\n */\nexport function firstFamily(value: string): string | undefined {\n let rest = cssTrim(value);\n const words: string[] = [];\n while (rest && !rest.startsWith(',')) {\n const part = FAMILY_PART.exec(rest);\n if (!part) return undefined;\n const quoted = part[1] ?? part[2];\n words.push(quoted === undefined ? part[3]! : decodeEscapes(quoted));\n rest = rest.slice(part[0].length).replace(LEADING_SPACE, '');\n }\n return words.length ? words.join(' ') : undefined;\n}\n\n/**\n * A value kept as written, and a family too when it reads as one: `'Inter-Bold'` is the family\n * `Inter-Bold`, and a stack its first. The written word stays the keyword, so a quoted `\"none\"` is\n * still no display.\n */\nfunction fromText(text: string): TokenValue {\n const family = firstFamily(text);\n return family === undefined || family === text ? { keyword: text } : { keyword: text, family };\n}\n\n/**\n * CSS takes a bare 0 wherever it takes a length, and no other bare number. A number from 1 to 1000\n * is a weight as well, at the nearest hundred, which is every weight native draws.\n */\nfunction fromNumber(value: number): TokenValue | undefined {\n if (!Number.isFinite(value)) return undefined;\n const weight =\n value >= 1 && value <= 1000\n ? { weight: String(Math.min(900, Math.max(100, Math.round(value / 100) * 100))) }\n : {};\n return value === 0 ? { number: 0, length: 0 } : { number: value, ...weight };\n}\n\n/**\n * A word is a keyword, `row`, and may be a colour, `red`, or a weight, `bold`, as well: every form\n * it can take, as the build-time conversion gives, and a use site reads the one it needs.\n */\nfunction fromWord(text: string): TokenValue {\n const weight = WEIGHTS[text.toLowerCase()];\n return {\n keyword: text,\n ...(isNamedColor(text) ? { color: text } : {}),\n ...(weight ? { weight } : {}),\n };\n}\n\nconst clamp = (value: number, low: number, high: number) => Math.min(high, Math.max(low, value));\n\n/**\n * `13 110 253`, `13, 110, 253`, `100% 0% 0%` or `0 100% 50%`, in the forms `rgb()` and `hsl()` read\n * channels in, by the same rules the build-time conversion uses (`channelForms` in values.cjs).\n */\nfunction fromChannels(text: string): TokenValue | undefined {\n const commas = text.split(',');\n const spaced = commas.length === 1;\n const words = spaced ? text.split(CSS_SPACE) : commas.map(cssTrim);\n const parsed = words.map((word) => CHANNEL.exec(word));\n if (parsed.length !== 3 || parsed.some((match) => !match)) return undefined;\n const values = parsed.map((match) => ({ n: Number(match![1]), unit: match![2] ?? '' }));\n const channels = rgbChannels(values);\n const hslChannels = hslOf(values, spaced);\n if (!channels && !hslChannels) return undefined;\n return { ...(channels ? { channels } : {}), ...(hslChannels ? { hslChannels } : {}) };\n}\n\ninterface Channel {\n readonly n: number;\n readonly unit: string;\n}\n\n/** As `rgb()` reads them: three numbers, or three percentages of 255. */\nfunction rgbChannels(values: readonly Channel[]): number[] | undefined {\n const scale = values.every((v) => v.unit === '')\n ? 1\n : values.every((v) => v.unit === '%')\n ? 255 / 100\n : 0;\n if (!scale) return undefined;\n return values.map((v) => Math.round(clamp(v.n * scale, 0, 255) * 1000) / 1000);\n}\n\n/** As `hsl()` reads them: a hue, then two percentages, which the space syntax may write bare. */\nfunction hslOf([hue, ...rest]: readonly Channel[], spaced: boolean): number[] | undefined {\n const degrees = hue!.n * (hue!.unit === '' ? 1 : (HUE_DEGREES[hue!.unit] ?? NaN));\n const fractions = rest.map((v) =>\n v.unit === '%' || (spaced && v.unit === '') ? clamp(v.n / 100, 0, 1) : NaN,\n );\n const hsl = [degrees, ...fractions];\n return hsl.every(Number.isFinite) ? hsl : undefined;\n}\n\n/**\n * Another token, kept as an alias and resolved where it is set, as a stylesheet's `--x: var(--y)`\n * is. Undefined when the parentheses do not close where the value ends: `var(--a, 1px) var(--b)`.\n */\nfunction fromVar(text: string): TokenValue | undefined {\n const match = VAR.exec(text);\n if (!match) return undefined;\n const alias = match[1]!;\n const rest = match[2];\n if (rest === undefined) return { alias };\n if (!balanced(rest)) return undefined;\n const fallback = tokenFromValue(rest);\n return fallback ? { alias, fallback } : { alias };\n}\n\nfunction balanced(text: string): boolean {\n let depth = 0;\n for (const char of text) {\n if (char === '(') depth++;\n else if (char === ')' && --depth < 0) return false;\n }\n return depth === 0;\n}\n\n/** A time as CSS writes one: a number of seconds or milliseconds. */\nconst TIME = /^([+-]?(?:\\d+\\.?\\d*|\\.\\d+))(ms|s)$/i;\n\n/** A number with a unit, as the token each is: a length, a percentage, an angle or a time. */\nfunction withUnit(text: string): TokenValue | undefined {\n const px = PX.exec(text);\n if (px) return { length: Number(px[1]) };\n // A percentage is a fraction as well, and an angle its degrees, as the build-time conversion\n // reads them: an hsl() of tokens takes its saturation and its hue so.\n if (PERCENT.test(text)) return { length: text, number: Number(text.slice(0, -1)) / 100 };\n const angle = ANGLE.exec(text);\n if (angle) {\n const [, value, unit] = angle;\n return { angle: Number(value) * HUE_DEGREES[unit!.toLowerCase()]!, number: Number(value) };\n }\n const time = TIME.exec(text);\n // In milliseconds, as the build reads a time: what a transition or an animation takes.\n if (time) return { time: Number(time[1]) * (time[2]!.toLowerCase() === 's' ? 1000 : 1) };\n return undefined;\n}\n\n/**\n * A value with a token in it: another token, or one made of others, worked out where it is set.\n * Any other shape is a word no use site reads, as a colour function with a `var()` left in it is\n * not a colour native can draw.\n */\nfunction withTokens(text: string): TokenValue {\n return fromVar(text) ?? derivedToken(text) ?? { keyword: text };\n}\n\nexport function tokenFromValue(value: unknown): TokenValue | undefined {\n if (typeof value === 'number') return fromNumber(value);\n if (typeof value !== 'string') return undefined;\n const text = cssTrim(value);\n if (!text) return undefined;\n const measure = withUnit(text);\n if (measure) return measure;\n if (NUMBER.test(text)) return fromNumber(Number(text));\n if (DERIVED.test(text)) return withTokens(text);\n if (COLOR_FUNCTION.test(text)) return { color: text };\n return fromChannels(text) ?? (WORD.test(text) ? fromWord(text) : fromText(text));\n}\n"]}
1
+ {"version":3,"file":"inline-token.js","sourceRoot":"","sources":["../src/inline-token.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A custom property's value set on an element at run time, `[style.--tint]=\"tint()\"` or\n * `style=\"--gap: 4px\"`, as a token the cascade can read.\n *\n * A stylesheet's custom properties are converted when the app is built, where there is a CSS\n * parser (`@ng-native/metro/css/values.cjs`). A bound one only exists here, where there is not,\n * so this takes the shapes a binding actually holds and nothing more: a length in `px`, `%`,\n * `rem`, `em` or a viewport unit, two to four lengths in `px`, a number, a colour as React Native\n * writes one, a colour's three channels, a word, another token, `var(--brand)`, and one made of\n * others, `calc(var(--gap) * 2)`, `calc(10px * 2)` or a `color-mix()` (see\n * inline-derived-token.ts).\n * Anything else is kept as a word, which leaves a use site that wants a length or a colour unset,\n * as a token of the wrong kind does.\n */\nimport { CSS_SPACE, type DeferredDeclaration, type TokenValue } from './css.ts';\nimport { derivedToken } from './inline-derived-token.ts';\nimport { HUE_DEGREES, isNamedColor } from './transition.ts';\n\nconst PX = /^(-?\\d*\\.?\\d+)px$/;\nconst PERCENT = /^-?\\d*\\.?\\d+%$/;\nconst RELATIVE = /^(-?\\d*\\.?\\d+)(rem|em|vw|vh|vmin|vmax)$/i;\nconst NUMBER = /^-?\\d*\\.?\\d+$/;\nconst ANGLE = /^(-?\\d*\\.?\\d+)(deg|grad|rad|turn)$/i;\n/** `#fff`, `rgb()`, `rgba()`, `hsl()`, `hsla()` and `hwb()`: what React Native's `processColor` reads. */\nconst COLOR_FUNCTION = /^(#[\\da-f]{3,8}|(rgba?|hsla?|hwb)\\(.*\\))$/i;\nconst WORD = /^-?[a-z][\\w-]*$/i;\n/** One colour channel: a number, a percentage, or a hue with its unit. */\nconst CHANNEL = /^(-?(?:\\d+\\.?\\d*|\\.\\d+))(%|deg|grad|rad|turn)?$/;\n/** `var(--name)` or `var(--name, <fallback>)`, the whole value. */\nconst VAR = /^var\\([ \\t\\n\\r\\f]*(--[\\w-]+)[ \\t\\n\\r\\f]*(?:,([\\s\\S]*))?\\)$/i;\n/**\n * A value worked out where it is set: one with a `var()` in it, a `calc()`, `min()` or `max()`,\n * a `color-mix()`, or a relative colour, of tokens or of values written out.\n */\nconst DERIVED = /var\\(|^(?:calc|min|max|color-mix)\\(|^[a-z]+\\([ \\t\\n\\r\\f]*from[ \\t\\n\\r\\f]/i;\nconst WEIGHTS: Record<string, string> = { normal: '400', bold: '700' };\n/**\n * CSS whitespace at either end: a space, a tab, a newline, a carriage return or a form feed. A\n * no-break space is part of the value, as it is in a browser, where `trim()` would drop it.\n */\nconst PADDED = new RegExp(`^${CSS_SPACE.source}|${CSS_SPACE.source}$`, 'g');\nexport const cssTrim = (text: string): string => text.replace(PADDED, '');\n/** A family name's parts: a quoted string, which may hold escapes, or a bare word. */\nconst FAMILY_PART = /^(?:'((?:[^'\\\\]|\\\\[\\s\\S])*)'|\"((?:[^\"\\\\]|\\\\[\\s\\S])*)\"|(-?[a-z_][\\w-]*))/i;\nconst LEADING_SPACE = new RegExp(`^${CSS_SPACE.source}`);\n/**\n * A CSS escape: up to six hex digits and one whitespace after them, a newline, which only joins\n * the lines either side, or any other character.\n */\nconst ESCAPE = /\\\\([\\da-f]{1,6})(?:\\r\\n|[ \\t\\n\\r\\f])?|\\\\(\\r\\n|[\\n\\r\\f])|\\\\([\\s\\S])/gi;\n\n/** A quoted string's text with its escapes decoded. One naming no character is U+FFFD. */\nconst decodeEscapes = (text: string): string =>\n text.replace(ESCAPE, (_, hex: string | undefined, newline: string | undefined, char: string) => {\n if (newline !== undefined) return '';\n if (hex === undefined) return char;\n const code = parseInt(hex, 16);\n return code && code <= 0x10ffff && (code < 0xd800 || code > 0xdfff)\n ? String.fromCodePoint(code)\n : '\\ufffd';\n });\n\n/**\n * The first family of a `font-family` stack, as a stylesheet reads it (`firstFamily` in\n * values.cjs): `'Inter Display', sans-serif` is `Inter Display`, and `Segoe UI` is one name of two\n * words. Undefined for anything that is not a stack.\n */\nexport function firstFamily(value: string): string | undefined {\n let rest = cssTrim(value);\n const words: string[] = [];\n while (rest && !rest.startsWith(',')) {\n const part = FAMILY_PART.exec(rest);\n if (!part) return undefined;\n const quoted = part[1] ?? part[2];\n words.push(quoted === undefined ? part[3]! : decodeEscapes(quoted));\n rest = rest.slice(part[0].length).replace(LEADING_SPACE, '');\n }\n return words.length ? words.join(' ') : undefined;\n}\n\n/**\n * A value kept as written, and a family too when it reads as one: `'Inter-Bold'` is the family\n * `Inter-Bold`, and a stack its first. The written word stays the keyword, so a quoted `\"none\"` is\n * still no display.\n */\nfunction fromText(text: string): TokenValue {\n const family = firstFamily(text);\n return family === undefined || family === text ? { keyword: text } : { keyword: text, family };\n}\n\n/**\n * CSS takes a bare 0 wherever it takes a length, and no other bare number. A number from 1 to 1000\n * is a weight as well, at the nearest hundred, which is every weight native draws.\n */\nfunction fromNumber(value: number): TokenValue | undefined {\n if (!Number.isFinite(value)) return undefined;\n const weight =\n value >= 1 && value <= 1000\n ? { weight: String(Math.min(900, Math.max(100, Math.round(value / 100) * 100))) }\n : {};\n // And a line height, as a multiple of the font size, as the stylesheet's own reads it.\n const lineHeight = {\n lineHeight: { __defer: { unit: 'em' as const, factor: value, multiple: true as const } },\n };\n return value === 0\n ? { number: 0, length: 0, ...lineHeight }\n : { number: value, ...weight, ...lineHeight };\n}\n\n/**\n * A word is a keyword, `row`, and may be a colour, `red`, or a weight, `bold`, as well: every form\n * it can take, as the build-time conversion gives, and a use site reads the one it needs.\n */\nfunction fromWord(text: string): TokenValue {\n const weight = WEIGHTS[text.toLowerCase()];\n return {\n keyword: text,\n ...(isNamedColor(text) ? { color: text } : {}),\n ...(weight ? { weight } : {}),\n };\n}\n\nconst clamp = (value: number, low: number, high: number) => Math.min(high, Math.max(low, value));\n\n/**\n * `13 110 253`, `13, 110, 253`, `100% 0% 0%` or `0 100% 50%`, in the forms `rgb()` and `hsl()` read\n * channels in, by the same rules the build-time conversion uses (`channelForms` in values.cjs).\n */\nfunction fromChannels(text: string): TokenValue | undefined {\n const commas = text.split(',');\n const spaced = commas.length === 1;\n const words = spaced ? text.split(CSS_SPACE) : commas.map(cssTrim);\n const parsed = words.map((word) => CHANNEL.exec(word));\n if (parsed.length !== 3 || parsed.some((match) => !match)) return undefined;\n const values = parsed.map((match) => ({ n: Number(match![1]), unit: match![2] ?? '' }));\n const channels = rgbChannels(values);\n const hslChannels = hslOf(values, spaced);\n if (!channels && !hslChannels) return undefined;\n return { ...(channels ? { channels } : {}), ...(hslChannels ? { hslChannels } : {}) };\n}\n\ninterface Channel {\n readonly n: number;\n readonly unit: string;\n}\n\n/** As `rgb()` reads them: three numbers, or three percentages of 255. */\nfunction rgbChannels(values: readonly Channel[]): number[] | undefined {\n const scale = values.every((v) => v.unit === '')\n ? 1\n : values.every((v) => v.unit === '%')\n ? 255 / 100\n : 0;\n if (!scale) return undefined;\n return values.map((v) => Math.round(clamp(v.n * scale, 0, 255) * 1000) / 1000);\n}\n\n/** As `hsl()` reads them: a hue, then two percentages, which the space syntax may write bare. */\nfunction hslOf([hue, ...rest]: readonly Channel[], spaced: boolean): number[] | undefined {\n const degrees = hue!.n * (hue!.unit === '' ? 1 : (HUE_DEGREES[hue!.unit] ?? NaN));\n const fractions = rest.map((v) =>\n v.unit === '%' || (spaced && v.unit === '') ? clamp(v.n / 100, 0, 1) : NaN,\n );\n const hsl = [degrees, ...fractions];\n return hsl.every(Number.isFinite) ? hsl : undefined;\n}\n\n/**\n * Another token, kept as an alias and resolved where it is set, as a stylesheet's `--x: var(--y)`\n * is. Undefined when the parentheses do not close where the value ends: `var(--a, 1px) var(--b)`.\n */\nfunction fromVar(text: string): TokenValue | undefined {\n const match = VAR.exec(text);\n if (!match) return undefined;\n const alias = match[1]!;\n const rest = match[2];\n if (rest === undefined) return { alias };\n if (!balanced(rest)) return undefined;\n const fallback = tokenFromValue(rest);\n return fallback ? { alias, fallback } : { alias };\n}\n\nfunction balanced(text: string): boolean {\n let depth = 0;\n for (const char of text) {\n if (char === '(') depth++;\n else if (char === ')' && --depth < 0) return false;\n }\n return depth === 0;\n}\n\n/** Two to four lengths in `px`, a padding or a margin's sides, as the stylesheet's own reads them. */\nfunction fromLengths(text: string): TokenValue | undefined {\n const parts = text.split(/[ \\t\\n\\r\\f]+/);\n if (parts.length < 2 || parts.length > 4) return undefined;\n const lengths = parts.map((part) => (part === '0' ? 0 : Number(PX.exec(part)?.[1] ?? NaN)));\n return lengths.every(Number.isFinite) ? { lengths } : undefined;\n}\n\n/** A time as CSS writes one: a number of seconds or milliseconds. */\nconst TIME = /^([+-]?(?:\\d+\\.?\\d*|\\.\\d+))(ms|s)$/i;\n\n/** A number with a unit, as the token each is: a length, a percentage, an angle or a time. */\nfunction withUnit(text: string): TokenValue | undefined {\n const px = PX.exec(text);\n if (px) return { length: Number(px[1]) };\n // A `rem` is 16 points, and the others are measured where they are used, as in a stylesheet.\n const relative = RELATIVE.exec(text);\n if (relative) {\n const [, factor, unit] = relative;\n const lower = unit!.toLowerCase();\n if (lower === 'rem') return { length: Number(factor) * 16 };\n const measured = lower as NonNullable<DeferredDeclaration['compute']>['unit'];\n // The shape the stylesheet compiles one to, which `TokenValue` does not spell out.\n const deferred = { __defer: { unit: measured, factor: Number(factor) } };\n return { length: deferred } as unknown as TokenValue;\n }\n // A percentage is a fraction as well, and an angle its degrees, as the build-time conversion\n // reads them: an hsl() of tokens takes its saturation and its hue so.\n if (PERCENT.test(text)) return { length: text, number: Number(text.slice(0, -1)) / 100 };\n const angle = ANGLE.exec(text);\n if (angle) {\n const [, value, unit] = angle;\n return { angle: Number(value) * HUE_DEGREES[unit!.toLowerCase()]!, number: Number(value) };\n }\n const time = TIME.exec(text);\n // In milliseconds, as the build reads a time: what a transition or an animation takes.\n if (time) return { time: Number(time[1]) * (time[2]!.toLowerCase() === 's' ? 1000 : 1) };\n return undefined;\n}\n\n/**\n * A value with a token in it: another token, or one made of others, worked out where it is set.\n * Any other shape is a word no use site reads, as a colour function with a `var()` left in it is\n * not a colour native can draw.\n */\nfunction withTokens(text: string): TokenValue {\n return fromVar(text) ?? derivedToken(text) ?? { keyword: text };\n}\n\nexport function tokenFromValue(value: unknown): TokenValue | undefined {\n if (typeof value === 'number') return fromNumber(value);\n if (typeof value !== 'string') return undefined;\n const text = cssTrim(value);\n if (!text) return undefined;\n const measure = withUnit(text);\n if (measure) return measure;\n if (NUMBER.test(text)) return fromNumber(Number(text));\n if (DERIVED.test(text)) return withTokens(text);\n if (COLOR_FUNCTION.test(text)) return { color: text };\n // `0 0 0` is both, so it is kept as both, as the stylesheet's own token is.\n const lengths = fromLengths(text);\n const channels = fromChannels(text);\n if (lengths) return { ...channels, ...lengths };\n return channels ?? (WORD.test(text) ? fromWord(text) : fromText(text));\n}\n"]}
@@ -63,12 +63,53 @@ export interface ScrollDrive {
63
63
  shift(amount: number): void;
64
64
  stop(): void;
65
65
  }
66
+ /**
67
+ * Told of each view a graph is connected to, with the props it sets there. Once a graph has set
68
+ * a view's `transform` or `opacity`, React Native lets no commit change it again: iOS skips both
69
+ * for good (`propKeysManagedByAnimated`), and Android puts the last value set back over each
70
+ * commit's (`overrideBySynchronousMountPropsAtMountingAndroid`), until the view is made again.
71
+ */
72
+ export type Touched = (view: object, viewTag: number, keys: readonly string[]) => void;
73
+ /**
74
+ * What a view is set to on native with no animation, where a graph has set it before: see
75
+ * `NativeScrollDriver.set`. A transform is each entry's amount: points, a factor, or an angle in
76
+ * radians.
77
+ */
78
+ export interface SetValues {
79
+ opacity?: number;
80
+ transform?: readonly {
81
+ readonly property: string;
82
+ readonly value: number;
83
+ }[];
84
+ }
66
85
  export declare class NativeScrollDriver {
67
86
  private readonly native;
87
+ private readonly touched;
68
88
  /** One value per source view and feed, by the feed's key. */
69
89
  private readonly offsets;
70
90
  private readonly pending;
71
- constructor(native: NativeAnimated);
91
+ /** The keys each graph connected to a view sets, by its props node, while it is connected. */
92
+ private readonly live;
93
+ /** The graph setting each view's values, where one does: see `set`. */
94
+ private readonly setters;
95
+ constructor(native: NativeAnimated, touched?: Touched);
96
+ /** The style keys a graph of this driver's playing or driving a view sets on it now. */
97
+ held(view: object): ReadonlySet<string>;
98
+ /**
99
+ * Set a view's values on native, where a graph that has let it go set them before and no commit
100
+ * can now (see `Touched`). One graph per view, connected until the view is let go or a graph
101
+ * that plays it is connected: a value node for each number, set where it changed.
102
+ */
103
+ set(view: object, viewTag: number, values: SetValues): void;
104
+ /** Let go of a view `set` set values on: it is made again, or gone. */
105
+ forget(view: object): void;
106
+ private buildSetter;
107
+ /**
108
+ * Note a graph connected to a view, for `held`, and tell whoever asked of it. The graph that
109
+ * set the view's values lets go: the one that plays it sets them from here.
110
+ */
111
+ private hold;
112
+ private letGo;
72
113
  /**
73
114
  * Move a view by a scroll. `tagOf` answers a node's view tag once it has been committed, and
74
115
  * null before, which is why the connection may wait for a commit: `connect` is tried again by