@ng-native/fabric 0.4.0 → 0.5.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.
@@ -13,7 +13,7 @@
13
13
  */
14
14
  import { CSS_SPACE } from "./css.js";
15
15
  import { derivedToken } from "./inline-derived-token.js";
16
- import { isNamedColor } from "./transition.js";
16
+ import { HUE_DEGREES, isNamedColor } from "./transition.js";
17
17
  const PX = /^(-?\d*\.?\d+)px$/;
18
18
  const PERCENT = /^-?\d*\.?\d+%$/;
19
19
  const NUMBER = /^-?\d*\.?\d+$/;
@@ -23,7 +23,6 @@ const COLOR_FUNCTION = /^(#[\da-f]{3,8}|(rgba?|hsla?|hwb)\(.*\))$/i;
23
23
  const WORD = /^-?[a-z][\w-]*$/i;
24
24
  /** One colour channel: a number, a percentage, or a hue with its unit. */
25
25
  const CHANNEL = /^(-?(?:\d+\.?\d*|\.\d+))(%|deg|grad|rad|turn)?$/;
26
- const HUE_UNITS = { deg: 1, grad: 0.9, rad: 180 / Math.PI, turn: 360 };
27
26
  /** `var(--name)` or `var(--name, <fallback>)`, the whole value. */
28
27
  const VAR = /^var\([ \t\n\r\f]*(--[\w-]+)[ \t\n\r\f]*(?:,([\s\S]*))?\)$/i;
29
28
  /**
@@ -140,7 +139,7 @@ function rgbChannels(values) {
140
139
  }
141
140
  /** As `hsl()` reads them: a hue, then two percentages, which the space syntax may write bare. */
142
141
  function hslOf([hue, ...rest], spaced) {
143
- const degrees = hue.n * (hue.unit === '' ? 1 : (HUE_UNITS[hue.unit] ?? NaN));
142
+ const degrees = hue.n * (hue.unit === '' ? 1 : (HUE_DEGREES[hue.unit] ?? NaN));
144
143
  const fractions = rest.map((v) => v.unit === '%' || (spaced && v.unit === '') ? clamp(v.n / 100, 0, 1) : NaN);
145
144
  const hsl = [degrees, ...fractions];
146
145
  return hsl.every(Number.isFinite) ? hsl : undefined;
@@ -198,7 +197,7 @@ export function tokenFromValue(value) {
198
197
  const angle = ANGLE.exec(text);
199
198
  if (angle) {
200
199
  const [, value, unit] = angle;
201
- return { angle: Number(value) * HUE_UNITS[unit.toLowerCase()], number: Number(value) };
200
+ return { angle: Number(value) * HUE_DEGREES[unit.toLowerCase()], number: Number(value) };
202
201
  }
203
202
  if (NUMBER.test(text))
204
203
  return fromNumber(Number(text));
@@ -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 { 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)?$/;\nconst HUE_UNITS: Record<string, number> = { deg: 1, grad: 0.9, rad: 180 / Math.PI, turn: 360 };\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_UNITS[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/**\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 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_UNITS[unit!.toLowerCase()]!, number: Number(value) };\n }\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` 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/**\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 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 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"]}
@@ -16,7 +16,13 @@
16
16
  /** The part of React Native's `NativeAnimatedHelper` default export this uses. */
17
17
  export interface NativeAnimated {
18
18
  generateNewNodeTag(): number;
19
+ generateNewAnimationId?(): number;
19
20
  readonly API: {
21
+ /** Run a value through `config` on the native side, and say when it has ended. */
22
+ startAnimatingNode?(animationId: number, nodeTag: number, config: object, end: (result: {
23
+ finished: boolean;
24
+ }) => void): void;
25
+ stopAnimation?(animationId: number): void;
20
26
  createAnimatedNode(tag: number, config: object): void;
21
27
  connectAnimatedNodes(parentTag: number, childTag: number): void;
22
28
  disconnectAnimatedNodes(parentTag: number, childTag: number): void;
@@ -78,6 +84,21 @@ export declare class NativeScrollDriver {
78
84
  update(channels: DrivenChannels): void;
79
85
  stop(): void;
80
86
  };
87
+ /**
88
+ * Play a view's opacity and transform by the clock: each channel an interpolation from a value
89
+ * native runs from nothing to `timing.toValue`, over the frames it is given, as many times as
90
+ * asked. Nothing runs in JavaScript while it plays. `ended` is told when native reaches the end
91
+ * of the last iteration; `stop` lets the view go, and stops an animation still playing.
92
+ *
93
+ * Null where the native module cannot start an animation, and the caller plays it itself.
94
+ */
95
+ play(view: object, channels: DrivenChannels, timing: {
96
+ frames: readonly number[];
97
+ toValue: number;
98
+ iterations: number;
99
+ }, tagOf: (node: object) => number | null, ended: () => void): {
100
+ stop(): void;
101
+ } | null;
81
102
  /** The nodes for a set of channels, connected to the view; the props node first. */
82
103
  private buildChannels;
83
104
  private dropChannels;
@@ -131,6 +131,66 @@ export class NativeScrollDriver {
131
131
  },
132
132
  };
133
133
  }
134
+ /**
135
+ * Play a view's opacity and transform by the clock: each channel an interpolation from a value
136
+ * native runs from nothing to `timing.toValue`, over the frames it is given, as many times as
137
+ * asked. Nothing runs in JavaScript while it plays. `ended` is told when native reaches the end
138
+ * of the last iteration; `stop` lets the view go, and stops an animation still playing.
139
+ *
140
+ * Null where the native module cannot start an animation, and the caller plays it itself.
141
+ */
142
+ play(view, channels, timing, tagOf, ended) {
143
+ const { API } = this.native;
144
+ const start = API.startAnimatingNode;
145
+ if (!start || !API.stopAnimation || !this.native.generateNewAnimationId)
146
+ return null;
147
+ const state = {
148
+ viewTag: null,
149
+ nodes: null,
150
+ value: 0,
151
+ id: 0,
152
+ over: false,
153
+ };
154
+ const connect = () => {
155
+ if (state.over)
156
+ return true;
157
+ const viewTag = tagOf(view);
158
+ if (viewTag === null)
159
+ return false;
160
+ state.viewTag = viewTag;
161
+ state.value = this.native.generateNewNodeTag();
162
+ API.createAnimatedNode(state.value, { type: 'value', value: 0, offset: 0 });
163
+ state.nodes = this.buildChannels(state.value, viewTag, channels);
164
+ state.id = this.native.generateNewAnimationId();
165
+ const config = { type: 'frames', frames: [...timing.frames], toValue: timing.toValue };
166
+ start(state.id, state.value, { ...config, iterations: timing.iterations }, (result) => {
167
+ // Stopped rather than finished is the caller's own doing, and it knows.
168
+ if (!result.finished || state.over)
169
+ return;
170
+ state.id = 0;
171
+ ended();
172
+ });
173
+ this.flush();
174
+ return true;
175
+ };
176
+ if (!connect())
177
+ this.pending.add(connect);
178
+ return {
179
+ stop: () => {
180
+ if (state.over)
181
+ return;
182
+ state.over = true;
183
+ this.pending.delete(connect);
184
+ if (!state.nodes || state.viewTag === null)
185
+ return;
186
+ if (state.id)
187
+ API.stopAnimation(state.id);
188
+ this.dropChannels(state.nodes, state.viewTag);
189
+ API.dropAnimatedNode(state.value);
190
+ this.flush();
191
+ },
192
+ };
193
+ }
134
194
  /** The nodes for a set of channels, connected to the view; the props node first. */
135
195
  buildChannels(value, viewTag, channels) {
136
196
  const { API } = this.native;
@@ -1 +1 @@
1
- {"version":3,"file":"native-drive.js","sourceRoot":"","sources":["../src/native-drive.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A view moved by a scroll view's offset on the native side, with no JavaScript in between.\n *\n * A sticky header moved by a transform set from each scroll event lags the scroll: the event\n * reaches JavaScript, the transform is committed, and by the time Fabric mounts it the content has\n * moved on, so on a fast fling the header shudders and comes away from the edge it is pinned to.\n * React Native's own sticky headers do not, because `ScrollViewStickyHeader` maps the offset onto\n * an animated value on the native side and interpolates the header's translate from it there.\n * This is that mapping, for the engine's nodes: the scroll view's offset along one axis feeds a\n * value, the value an interpolation, the interpolation a translate, and native applies it on every\n * frame the scroll view moves.\n *\n * It goes through React Native's own `NativeAnimatedHelper`, whose node tags every other animated\n * value in the app also comes from, so none of them can collide.\n */\n\n/** The part of React Native's `NativeAnimatedHelper` default export this uses. */\nexport interface NativeAnimated {\n generateNewNodeTag(): number;\n readonly API: {\n createAnimatedNode(tag: number, config: object): void;\n connectAnimatedNodes(parentTag: number, childTag: number): void;\n disconnectAnimatedNodes(parentTag: number, childTag: number): void;\n connectAnimatedNodeToView(nodeTag: number, viewTag: number): void;\n disconnectAnimatedNodeFromView(nodeTag: number, viewTag: number): void;\n dropAnimatedNode(tag: number): void;\n addAnimatedEventToView(\n viewTag: number,\n eventName: string,\n mapping: { nativeEventPath: string[]; animatedValueTag: number },\n ): void;\n removeAnimatedEventFromView(viewTag: number, eventName: string, nodeTag: number): void;\n setAnimatedNodeValue(nodeTag: number, value: number): void;\n flushQueue?(): void;\n };\n}\n\nimport type { DrivenChannels } from './scroll-animation.ts';\n\nexport type ScrollAxis = 'x' | 'y';\n\n/** A native event that feeds a value: the events carrying it, and the path to it in each. */\nexport interface EventFeed {\n readonly events: readonly string[];\n readonly path: readonly string[];\n}\n\n/** What a driven view moves along. */\nexport type DrivenProperty = 'translateX' | 'translateY';\n\n/** Where a driven view sits for each scroll offset, as an interpolation clamped at both ends. */\nexport interface ScrollRange {\n readonly input: readonly number[];\n readonly output: readonly number[];\n}\n\n/** A transform native keeps beside the driven translate, such as an inverted list's flip. */\nexport type StaticTransform = Readonly<Record<string, number>>;\n\n/**\n * A view moved by a scroll. `update` moves it by a new range; `shift` adds an amount to where the\n * range puts it, for a drive made with one; `stop` lets it go.\n */\nexport interface ScrollDrive {\n update(range: ScrollRange): void;\n shift(amount: number): void;\n stop(): void;\n}\n\ninterface Offset {\n readonly value: number;\n readonly viewTag: number;\n readonly feed: EventFeed;\n users: number;\n}\n\ninterface Nodes {\n interpolation: number;\n /** The shift JavaScript sets, and the sum of it and the interpolation, for a shifted drive. */\n shift: number | null;\n sum: number | null;\n transform: number;\n style: number;\n props: number;\n}\n\ninterface Drive {\n viewTag: number | null;\n range: ScrollRange;\n /** The amount added to the range's translate, or null for a drive without one. */\n shift: number | null;\n nodes: Nodes | null;\n stopped: boolean;\n}\n\nexport class NativeScrollDriver {\n private readonly native: NativeAnimated;\n /** One value per source view and feed, by the feed's key. */\n private readonly offsets = new Map<object, Map<string, Offset>>();\n private readonly pending = new Set<() => boolean>();\n\n constructor(native: NativeAnimated) {\n this.native = native;\n }\n\n /**\n * Move a view by a scroll. `tagOf` answers a node's view tag once it has been committed, and\n * null before, which is why the connection may wait for a commit: `connect` is tried again by\n * `afterCommit` until both views exist.\n */\n drive(\n view: object,\n source: object,\n feed: EventFeed,\n property: DrivenProperty,\n range: ScrollRange,\n statics: readonly StaticTransform[],\n tagOf: (node: object) => number | null,\n shift: number | null = null,\n ): ScrollDrive {\n const drive: Drive = { viewTag: null, range, shift, nodes: null, stopped: false };\n const key = feedKey(feed);\n const connect = (): boolean => {\n if (drive.stopped) return true;\n const viewTag = tagOf(view);\n const sourceTag = tagOf(source);\n if (viewTag === null || sourceTag === null) return false;\n drive.viewTag = viewTag;\n const offset = this.offset(source, sourceTag, feed, key);\n drive.nodes = this.build(offset.value, viewTag, property, drive, statics);\n this.flush();\n return true;\n };\n if (!connect()) this.pending.add(connect);\n return {\n update: (next) => {\n if (drive.stopped || sameRange(drive.range, next)) return;\n drive.range = next;\n if (!drive.nodes || drive.viewTag === null) return;\n this.tearDown(drive.nodes, drive.viewTag);\n const offset = this.offsets.get(source)!.get(key)!;\n drive.nodes = this.build(offset.value, drive.viewTag, property, drive, statics);\n this.flush();\n },\n shift: (amount) => {\n if (drive.stopped || drive.shift === null || drive.shift === amount) return;\n drive.shift = amount;\n if (drive.nodes?.shift == null) return;\n this.native.API.setAnimatedNodeValue(drive.nodes.shift, amount);\n this.flush();\n },\n stop: () => {\n if (drive.stopped) return;\n drive.stopped = true;\n this.pending.delete(connect);\n if (!drive.nodes || drive.viewTag === null) return;\n this.tearDown(drive.nodes, drive.viewTag);\n this.release(source, key);\n this.flush();\n },\n };\n }\n\n /**\n * Play a view's opacity and transform by a scroll: each channel an interpolation from the\n * offset, as `scroll-animation.ts` lays a `@keyframes` animation along it. `update` rebuilds\n * the channels, when the scroll view says how far it scrolls; `stop` lets the view go.\n */\n animate(\n view: object,\n source: object,\n feed: EventFeed,\n channels: DrivenChannels,\n tagOf: (node: object) => number | null,\n ): { update(channels: DrivenChannels): void; stop(): void } {\n const state = {\n channels,\n viewTag: null as number | null,\n nodes: null as number[] | null,\n stopped: false,\n };\n const key = feedKey(feed);\n const connect = (): boolean => {\n if (state.stopped) return true;\n const viewTag = tagOf(view);\n const sourceTag = tagOf(source);\n if (viewTag === null || sourceTag === null) return false;\n state.viewTag = viewTag;\n const offset = this.offset(source, sourceTag, feed, key);\n state.nodes = this.buildChannels(offset.value, viewTag, state.channels);\n this.flush();\n return true;\n };\n if (!connect()) this.pending.add(connect);\n return {\n update: (next) => {\n if (state.stopped) return;\n state.channels = next;\n if (!state.nodes || state.viewTag === null) return;\n this.dropChannels(state.nodes, state.viewTag);\n const offset = this.offsets.get(source)!.get(key)!;\n state.nodes = this.buildChannels(offset.value, state.viewTag, next);\n this.flush();\n },\n stop: () => {\n if (state.stopped) return;\n state.stopped = true;\n this.pending.delete(connect);\n if (!state.nodes || state.viewTag === null) return;\n this.dropChannels(state.nodes, state.viewTag);\n this.release(source, key);\n this.flush();\n },\n };\n }\n\n /** The nodes for a set of channels, connected to the view; the props node first. */\n private buildChannels(value: number, viewTag: number, channels: DrivenChannels): number[] {\n const { API } = this.native;\n const made: number[] = [];\n const interpolation = (range: ScrollRange): number => {\n const tag = this.native.generateNewNodeTag();\n API.createAnimatedNode(tag, {\n type: 'interpolation',\n inputRange: [...range.input],\n outputRange: [...range.output],\n extrapolateLeft: 'clamp',\n extrapolateRight: 'clamp',\n });\n API.connectAnimatedNodes(value, tag);\n made.push(tag);\n return tag;\n };\n const style: Record<string, number> = {};\n if (channels.opacity) style['opacity'] = interpolation(channels.opacity);\n if (channels.transform.length) {\n const transforms = channels.transform.map((channel) =>\n 'range' in channel\n ? { type: 'animated', property: channel.property, nodeTag: interpolation(channel.range) }\n : { type: 'static', property: channel.property, value: channel.value },\n );\n const transform = this.native.generateNewNodeTag();\n API.createAnimatedNode(transform, { type: 'transform', transforms });\n for (const entry of transforms) {\n if (entry.type === 'animated') API.connectAnimatedNodes(entry.nodeTag!, transform);\n }\n style['transform'] = transform;\n made.push(transform);\n }\n const styleTag = this.native.generateNewNodeTag();\n API.createAnimatedNode(styleTag, { type: 'style', style });\n for (const tag of Object.values(style)) API.connectAnimatedNodes(tag, styleTag);\n const props = this.native.generateNewNodeTag();\n API.createAnimatedNode(props, { type: 'props', props: { style: styleTag } });\n API.connectAnimatedNodes(styleTag, props);\n API.connectAnimatedNodeToView(props, viewTag);\n return [props, styleTag, ...made.reverse()];\n }\n\n private dropChannels(nodes: readonly number[], viewTag: number): void {\n const { API } = this.native;\n API.disconnectAnimatedNodeFromView(nodes[0]!, viewTag);\n for (const tag of nodes) API.dropAnimatedNode(tag);\n }\n\n /** Connects every drive still waiting for its views, now that a commit may have made them. */\n afterCommit(): void {\n for (const connect of [...this.pending]) if (connect()) this.pending.delete(connect);\n }\n\n /** One value per source view and feed, fed by its events, shared by every view it drives. */\n private offset(source: object, viewTag: number, feed: EventFeed, key: string): Offset {\n let feeds = this.offsets.get(source);\n if (!feeds) this.offsets.set(source, (feeds = new Map()));\n let offset = feeds.get(key);\n if (!offset) {\n const value = this.native.generateNewNodeTag();\n this.native.API.createAnimatedNode(value, { type: 'value', value: 0, offset: 0 });\n for (const event of feed.events) {\n this.native.API.addAnimatedEventToView(viewTag, event, {\n nativeEventPath: [...feed.path],\n animatedValueTag: value,\n });\n }\n offset = { value, viewTag, feed, users: 0 };\n feeds.set(key, offset);\n }\n offset.users++;\n return offset;\n }\n\n private release(source: object, key: string): void {\n const feeds = this.offsets.get(source);\n const offset = feeds?.get(key);\n if (!feeds || !offset || --offset.users > 0) return;\n feeds.delete(key);\n if (feeds.size === 0) this.offsets.delete(source);\n for (const event of offset.feed.events) {\n this.native.API.removeAnimatedEventFromView(offset.viewTag, event, offset.value);\n }\n this.native.API.dropAnimatedNode(offset.value);\n }\n\n private build(\n value: number,\n viewTag: number,\n property: DrivenProperty,\n drive: Drive,\n statics: readonly StaticTransform[],\n ): Nodes {\n const { API } = this.native;\n const range = drive.range;\n const interpolation = this.native.generateNewNodeTag();\n API.createAnimatedNode(interpolation, {\n type: 'interpolation',\n inputRange: [...range.input],\n outputRange: [...range.output],\n extrapolateLeft: 'clamp',\n extrapolateRight: 'clamp',\n });\n let shift: number | null = null;\n let sum: number | null = null;\n if (drive.shift !== null) {\n shift = this.native.generateNewNodeTag();\n API.createAnimatedNode(shift, { type: 'value', value: drive.shift, offset: 0 });\n sum = this.native.generateNewNodeTag();\n API.createAnimatedNode(sum, { type: 'addition', input: [interpolation, shift] });\n }\n const transform = this.native.generateNewNodeTag();\n API.createAnimatedNode(transform, {\n type: 'transform',\n transforms: [\n {\n type: 'animated',\n property,\n nodeTag: sum ?? interpolation,\n },\n ...statics.flatMap((entry) =>\n Object.entries(entry).map(([property, amount]) => ({\n type: 'static',\n property,\n value: amount,\n })),\n ),\n ],\n });\n // A style node between, as `Animated` builds it: RN's props node on iOS reads its values from\n // a style node and ignores a transform node connected to it directly.\n const style = this.native.generateNewNodeTag();\n API.createAnimatedNode(style, { type: 'style', style: { transform } });\n const props = this.native.generateNewNodeTag();\n API.createAnimatedNode(props, { type: 'props', props: { style } });\n API.connectAnimatedNodes(value, interpolation);\n if (shift !== null && sum !== null) {\n API.connectAnimatedNodes(interpolation, sum);\n API.connectAnimatedNodes(shift, sum);\n API.connectAnimatedNodes(sum, transform);\n } else {\n API.connectAnimatedNodes(interpolation, transform);\n }\n API.connectAnimatedNodes(transform, style);\n API.connectAnimatedNodes(style, props);\n API.connectAnimatedNodeToView(props, viewTag);\n return { interpolation, shift, sum, transform, style, props };\n }\n\n private tearDown(nodes: Nodes, viewTag: number): void {\n const { API } = this.native;\n API.disconnectAnimatedNodeFromView(nodes.props, viewTag);\n API.dropAnimatedNode(nodes.props);\n API.dropAnimatedNode(nodes.style);\n API.dropAnimatedNode(nodes.transform);\n if (nodes.sum !== null) API.dropAnimatedNode(nodes.sum);\n if (nodes.shift !== null) API.dropAnimatedNode(nodes.shift);\n API.dropAnimatedNode(nodes.interpolation);\n }\n\n private flush(): void {\n this.native.API.flushQueue?.();\n }\n}\n\nfunction feedKey(feed: EventFeed): string {\n return `${feed.events.join(',')}:${feed.path.join('.')}`;\n}\n\n/** A scroll view's offset along an axis, as `onScroll` carries it. */\nexport function scrollFeed(axis: ScrollAxis): EventFeed {\n return { events: ['onScroll'], path: ['contentOffset', axis] };\n}\n\nfunction sameRange(a: ScrollRange, b: ScrollRange): boolean {\n return (\n a.input.length === b.input.length &&\n a.input.every((value, i) => value === b.input[i]) &&\n a.output.every((value, i) => value === b.output[i])\n );\n}\n\n/**\n * The translate a pinned view needs at each scroll offset: none until the offset reaches where\n * the view starts, then the offset past that, until `until`, where the next pinned view pushes it\n * off and it stays. `start` and `until` are in the scroll view's own coordinates.\n */\nexport function pinnedRange(start: number, until = Infinity): ScrollRange {\n if (!Number.isFinite(until) || until <= start) {\n // A large finite end, since native interpolation takes numbers and extends nothing here.\n return { input: [start, start + 1e7], output: [0, 1e7] };\n }\n return { input: [start, until], output: [0, until - start] };\n}\n"]}
1
+ {"version":3,"file":"native-drive.js","sourceRoot":"","sources":["../src/native-drive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AA0FH,MAAM,OAAO,kBAAkB;IACZ,MAAM,CAAiB;IACxC,6DAA6D;IAC5C,OAAO,GAAG,IAAI,GAAG,EAA+B,CAAC;IACjD,OAAO,GAAG,IAAI,GAAG,EAAiB,CAAC;IAEpD,YAAY,MAAsB;QAChC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED;;;;OAIG;IACH,KAAK,CACH,IAAY,EACZ,MAAc,EACd,IAAe,EACf,QAAwB,EACxB,KAAkB,EAClB,OAAmC,EACnC,KAAsC,EACtC,QAAuB,IAAI;QAE3B,MAAM,KAAK,GAAU,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAClF,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC1B,MAAM,OAAO,GAAG,GAAY,EAAE;YAC5B,IAAI,KAAK,CAAC,OAAO;gBAAE,OAAO,IAAI,CAAC;YAC/B,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;YAC5B,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;YAChC,IAAI,OAAO,KAAK,IAAI,IAAI,SAAS,KAAK,IAAI;gBAAE,OAAO,KAAK,CAAC;YACzD,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC;YACxB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;YACzD,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;YAC1E,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,OAAO,IAAI,CAAC;QACd,CAAC,CAAC;QACF,IAAI,CAAC,OAAO,EAAE;YAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC1C,OAAO;YACL,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;gBACf,IAAI,KAAK,CAAC,OAAO,IAAI,SAAS,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC;oBAAE,OAAO;gBAC1D,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;gBACnB,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI;oBAAE,OAAO;gBACnD,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;gBAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAE,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC;gBACnD,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;gBAChF,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,CAAC;YACD,KAAK,EAAE,CAAC,MAAM,EAAE,EAAE;gBAChB,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,KAAK,KAAK,MAAM;oBAAE,OAAO;gBAC5E,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC;gBACrB,IAAI,KAAK,CAAC,KAAK,EAAE,KAAK,IAAI,IAAI;oBAAE,OAAO;gBACvC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,oBAAoB,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;gBAChE,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,CAAC;YACD,IAAI,EAAE,GAAG,EAAE;gBACT,IAAI,KAAK,CAAC,OAAO;oBAAE,OAAO;gBAC1B,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;gBACrB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBAC7B,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI;oBAAE,OAAO;gBACnD,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;gBAC1C,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;gBAC1B,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,CAAC;SACF,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,OAAO,CACL,IAAY,EACZ,MAAc,EACd,IAAe,EACf,QAAwB,EACxB,KAAsC;QAEtC,MAAM,KAAK,GAAG;YACZ,QAAQ;YACR,OAAO,EAAE,IAAqB;YAC9B,KAAK,EAAE,IAAuB;YAC9B,OAAO,EAAE,KAAK;SACf,CAAC;QACF,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC1B,MAAM,OAAO,GAAG,GAAY,EAAE;YAC5B,IAAI,KAAK,CAAC,OAAO;gBAAE,OAAO,IAAI,CAAC;YAC/B,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;YAC5B,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;YAChC,IAAI,OAAO,KAAK,IAAI,IAAI,SAAS,KAAK,IAAI;gBAAE,OAAO,KAAK,CAAC;YACzD,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC;YACxB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;YACzD,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;YACxE,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,OAAO,IAAI,CAAC;QACd,CAAC,CAAC;QACF,IAAI,CAAC,OAAO,EAAE;YAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC1C,OAAO;YACL,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;gBACf,IAAI,KAAK,CAAC,OAAO;oBAAE,OAAO;gBAC1B,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC;gBACtB,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI;oBAAE,OAAO;gBACnD,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;gBAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAE,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC;gBACnD,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;gBACpE,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,CAAC;YACD,IAAI,EAAE,GAAG,EAAE;gBACT,IAAI,KAAK,CAAC,OAAO;oBAAE,OAAO;gBAC1B,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;gBACrB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBAC7B,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI;oBAAE,OAAO;gBACnD,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;gBAC9C,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;gBAC1B,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,CAAC;SACF,CAAC;IACJ,CAAC;IAED;;;;;;;OAOG;IACH,IAAI,CACF,IAAY,EACZ,QAAwB,EACxB,MAA0E,EAC1E,KAAsC,EACtC,KAAiB;QAEjB,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC;QAC5B,MAAM,KAAK,GAAG,GAAG,CAAC,kBAAkB,CAAC;QACrC,IAAI,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC,aAAa,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,sBAAsB;YAAE,OAAO,IAAI,CAAC;QACrF,MAAM,KAAK,GAAG;YACZ,OAAO,EAAE,IAAqB;YAC9B,KAAK,EAAE,IAAuB;YAC9B,KAAK,EAAE,CAAC;YACR,EAAE,EAAE,CAAC;YACL,IAAI,EAAE,KAAK;SACZ,CAAC;QACF,MAAM,OAAO,GAAG,GAAY,EAAE;YAC5B,IAAI,KAAK,CAAC,IAAI;gBAAE,OAAO,IAAI,CAAC;YAC5B,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;YAC5B,IAAI,OAAO,KAAK,IAAI;gBAAE,OAAO,KAAK,CAAC;YACnC,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC;YACxB,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC;YAC/C,GAAG,CAAC,kBAAkB,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;YAC5E,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;YACjE,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,sBAAuB,EAAE,CAAC;YACjD,MAAM,MAAM,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;YACvF,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,KAAK,EAAE,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,EAAE,CAAC,MAAM,EAAE,EAAE;gBACpF,wEAAwE;gBACxE,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,KAAK,CAAC,IAAI;oBAAE,OAAO;gBAC3C,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;gBACb,KAAK,EAAE,CAAC;YACV,CAAC,CAAC,CAAC;YACH,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,OAAO,IAAI,CAAC;QACd,CAAC,CAAC;QACF,IAAI,CAAC,OAAO,EAAE;YAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC1C,OAAO;YACL,IAAI,EAAE,GAAG,EAAE;gBACT,IAAI,KAAK,CAAC,IAAI;oBAAE,OAAO;gBACvB,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC;gBAClB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBAC7B,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI;oBAAE,OAAO;gBACnD,IAAI,KAAK,CAAC,EAAE;oBAAE,GAAG,CAAC,aAAc,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBAC3C,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;gBAC9C,GAAG,CAAC,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBAClC,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,CAAC;SACF,CAAC;IACJ,CAAC;IAED,oFAAoF;IAC5E,aAAa,CAAC,KAAa,EAAE,OAAe,EAAE,QAAwB;QAC5E,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC;QAC5B,MAAM,IAAI,GAAa,EAAE,CAAC;QAC1B,MAAM,aAAa,GAAG,CAAC,KAAkB,EAAU,EAAE;YACnD,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC;YAC7C,GAAG,CAAC,kBAAkB,CAAC,GAAG,EAAE;gBAC1B,IAAI,EAAE,eAAe;gBACrB,UAAU,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC;gBAC5B,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;gBAC9B,eAAe,EAAE,OAAO;gBACxB,gBAAgB,EAAE,OAAO;aAC1B,CAAC,CAAC;YACH,GAAG,CAAC,oBAAoB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YACrC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACf,OAAO,GAAG,CAAC;QACb,CAAC,CAAC;QACF,MAAM,KAAK,GAA2B,EAAE,CAAC;QACzC,IAAI,QAAQ,CAAC,OAAO;YAAE,KAAK,CAAC,SAAS,CAAC,GAAG,aAAa,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACzE,IAAI,QAAQ,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;YAC9B,MAAM,UAAU,GAAG,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CACpD,OAAO,IAAI,OAAO;gBAChB,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;gBACzF,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CACzE,CAAC;YACF,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC;YACnD,GAAG,CAAC,kBAAkB,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,CAAC;YACrE,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;gBAC/B,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU;oBAAE,GAAG,CAAC,oBAAoB,CAAC,KAAK,CAAC,OAAQ,EAAE,SAAS,CAAC,CAAC;YACrF,CAAC;YACD,KAAK,CAAC,WAAW,CAAC,GAAG,SAAS,CAAC;YAC/B,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACvB,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC;QAClD,GAAG,CAAC,kBAAkB,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3D,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;YAAE,GAAG,CAAC,oBAAoB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAChF,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC;QAC/C,GAAG,CAAC,kBAAkB,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QAC7E,GAAG,CAAC,oBAAoB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC1C,GAAG,CAAC,yBAAyB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAC9C,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IAC9C,CAAC;IAEO,YAAY,CAAC,KAAwB,EAAE,OAAe;QAC5D,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC;QAC5B,GAAG,CAAC,8BAA8B,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,OAAO,CAAC,CAAC;QACvD,KAAK,MAAM,GAAG,IAAI,KAAK;YAAE,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACrD,CAAC;IAED,8FAA8F;IAC9F,WAAW;QACT,KAAK,MAAM,OAAO,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC;YAAE,IAAI,OAAO,EAAE;gBAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACvF,CAAC;IAED,6FAA6F;IACrF,MAAM,CAAC,MAAc,EAAE,OAAe,EAAE,IAAe,EAAE,GAAW;QAC1E,IAAI,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACrC,IAAI,CAAC,KAAK;YAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,KAAK,GAAG,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC;QAC1D,IAAI,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC;YAC/C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,kBAAkB,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;YAClF,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,sBAAsB,CAAC,OAAO,EAAE,KAAK,EAAE;oBACrD,eAAe,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC;oBAC/B,gBAAgB,EAAE,KAAK;iBACxB,CAAC,CAAC;YACL,CAAC;YACD,MAAM,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;YAC5C,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACzB,CAAC;QACD,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,OAAO,CAAC,MAAc,EAAE,GAAW;QACzC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,IAAI,EAAE,MAAM,CAAC,KAAK,GAAG,CAAC;YAAE,OAAO;QACpD,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAClB,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC;YAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAClD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACvC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,2BAA2B,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QACnF,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACjD,CAAC;IAEO,KAAK,CACX,KAAa,EACb,OAAe,EACf,QAAwB,EACxB,KAAY,EACZ,OAAmC;QAEnC,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC;QAC5B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QAC1B,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC;QACvD,GAAG,CAAC,kBAAkB,CAAC,aAAa,EAAE;YACpC,IAAI,EAAE,eAAe;YACrB,UAAU,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC;YAC5B,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;YAC9B,eAAe,EAAE,OAAO;YACxB,gBAAgB,EAAE,OAAO;SAC1B,CAAC,CAAC;QACH,IAAI,KAAK,GAAkB,IAAI,CAAC;QAChC,IAAI,GAAG,GAAkB,IAAI,CAAC;QAC9B,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;YACzB,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC;YACzC,GAAG,CAAC,kBAAkB,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;YAChF,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC;YACvC,GAAG,CAAC,kBAAkB,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;QACnF,CAAC;QACD,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC;QACnD,GAAG,CAAC,kBAAkB,CAAC,SAAS,EAAE;YAChC,IAAI,EAAE,WAAW;YACjB,UAAU,EAAE;gBACV;oBACE,IAAI,EAAE,UAAU;oBAChB,QAAQ;oBACR,OAAO,EAAE,GAAG,IAAI,aAAa;iBAC9B;gBACD,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAC3B,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;oBACjD,IAAI,EAAE,QAAQ;oBACd,QAAQ;oBACR,KAAK,EAAE,MAAM;iBACd,CAAC,CAAC,CACJ;aACF;SACF,CAAC,CAAC;QACH,8FAA8F;QAC9F,sEAAsE;QACtE,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC;QAC/C,GAAG,CAAC,kBAAkB,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;QACvE,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC;QAC/C,GAAG,CAAC,kBAAkB,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QACnE,GAAG,CAAC,oBAAoB,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;QAC/C,IAAI,KAAK,KAAK,IAAI,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACnC,GAAG,CAAC,oBAAoB,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;YAC7C,GAAG,CAAC,oBAAoB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YACrC,GAAG,CAAC,oBAAoB,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAC3C,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,oBAAoB,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;QACrD,CAAC;QACD,GAAG,CAAC,oBAAoB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QAC3C,GAAG,CAAC,oBAAoB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACvC,GAAG,CAAC,yBAAyB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAC9C,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAChE,CAAC;IAEO,QAAQ,CAAC,KAAY,EAAE,OAAe;QAC5C,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC;QAC5B,GAAG,CAAC,8BAA8B,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACzD,GAAG,CAAC,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAClC,GAAG,CAAC,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAClC,GAAG,CAAC,gBAAgB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACtC,IAAI,KAAK,CAAC,GAAG,KAAK,IAAI;YAAE,GAAG,CAAC,gBAAgB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACxD,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI;YAAE,GAAG,CAAC,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC5D,GAAG,CAAC,gBAAgB,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAC5C,CAAC;IAEO,KAAK;QACX,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,CAAC;IACjC,CAAC;CACF;AAED,SAAS,OAAO,CAAC,IAAe;IAC9B,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;AAC3D,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,UAAU,CAAC,IAAgB;IACzC,OAAO,EAAE,MAAM,EAAE,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,CAAC,eAAe,EAAE,IAAI,CAAC,EAAE,CAAC;AACjE,CAAC;AAED,SAAS,SAAS,CAAC,CAAc,EAAE,CAAc;IAC/C,OAAO,CACL,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM;QACjC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACjD,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CACpD,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa,EAAE,KAAK,GAAG,QAAQ;IACzD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,KAAK,EAAE,CAAC;QAC9C,yFAAyF;QACzF,OAAO,EAAE,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,GAAG,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;IAC3D,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,EAAE,CAAC;AAC/D,CAAC","sourcesContent":["/**\n * A view moved by a scroll view's offset on the native side, with no JavaScript in between.\n *\n * A sticky header moved by a transform set from each scroll event lags the scroll: the event\n * reaches JavaScript, the transform is committed, and by the time Fabric mounts it the content has\n * moved on, so on a fast fling the header shudders and comes away from the edge it is pinned to.\n * React Native's own sticky headers do not, because `ScrollViewStickyHeader` maps the offset onto\n * an animated value on the native side and interpolates the header's translate from it there.\n * This is that mapping, for the engine's nodes: the scroll view's offset along one axis feeds a\n * value, the value an interpolation, the interpolation a translate, and native applies it on every\n * frame the scroll view moves.\n *\n * It goes through React Native's own `NativeAnimatedHelper`, whose node tags every other animated\n * value in the app also comes from, so none of them can collide.\n */\n\n/** The part of React Native's `NativeAnimatedHelper` default export this uses. */\nexport interface NativeAnimated {\n generateNewNodeTag(): number;\n generateNewAnimationId?(): number;\n readonly API: {\n /** Run a value through `config` on the native side, and say when it has ended. */\n startAnimatingNode?(\n animationId: number,\n nodeTag: number,\n config: object,\n end: (result: { finished: boolean }) => void,\n ): void;\n stopAnimation?(animationId: number): void;\n createAnimatedNode(tag: number, config: object): void;\n connectAnimatedNodes(parentTag: number, childTag: number): void;\n disconnectAnimatedNodes(parentTag: number, childTag: number): void;\n connectAnimatedNodeToView(nodeTag: number, viewTag: number): void;\n disconnectAnimatedNodeFromView(nodeTag: number, viewTag: number): void;\n dropAnimatedNode(tag: number): void;\n addAnimatedEventToView(\n viewTag: number,\n eventName: string,\n mapping: { nativeEventPath: string[]; animatedValueTag: number },\n ): void;\n removeAnimatedEventFromView(viewTag: number, eventName: string, nodeTag: number): void;\n setAnimatedNodeValue(nodeTag: number, value: number): void;\n flushQueue?(): void;\n };\n}\n\nimport type { DrivenChannels } from './scroll-animation.ts';\n\nexport type ScrollAxis = 'x' | 'y';\n\n/** A native event that feeds a value: the events carrying it, and the path to it in each. */\nexport interface EventFeed {\n readonly events: readonly string[];\n readonly path: readonly string[];\n}\n\n/** What a driven view moves along. */\nexport type DrivenProperty = 'translateX' | 'translateY';\n\n/** Where a driven view sits for each scroll offset, as an interpolation clamped at both ends. */\nexport interface ScrollRange {\n readonly input: readonly number[];\n readonly output: readonly number[];\n}\n\n/** A transform native keeps beside the driven translate, such as an inverted list's flip. */\nexport type StaticTransform = Readonly<Record<string, number>>;\n\n/**\n * A view moved by a scroll. `update` moves it by a new range; `shift` adds an amount to where the\n * range puts it, for a drive made with one; `stop` lets it go.\n */\nexport interface ScrollDrive {\n update(range: ScrollRange): void;\n shift(amount: number): void;\n stop(): void;\n}\n\ninterface Offset {\n readonly value: number;\n readonly viewTag: number;\n readonly feed: EventFeed;\n users: number;\n}\n\ninterface Nodes {\n interpolation: number;\n /** The shift JavaScript sets, and the sum of it and the interpolation, for a shifted drive. */\n shift: number | null;\n sum: number | null;\n transform: number;\n style: number;\n props: number;\n}\n\ninterface Drive {\n viewTag: number | null;\n range: ScrollRange;\n /** The amount added to the range's translate, or null for a drive without one. */\n shift: number | null;\n nodes: Nodes | null;\n stopped: boolean;\n}\n\nexport class NativeScrollDriver {\n private readonly native: NativeAnimated;\n /** One value per source view and feed, by the feed's key. */\n private readonly offsets = new Map<object, Map<string, Offset>>();\n private readonly pending = new Set<() => boolean>();\n\n constructor(native: NativeAnimated) {\n this.native = native;\n }\n\n /**\n * Move a view by a scroll. `tagOf` answers a node's view tag once it has been committed, and\n * null before, which is why the connection may wait for a commit: `connect` is tried again by\n * `afterCommit` until both views exist.\n */\n drive(\n view: object,\n source: object,\n feed: EventFeed,\n property: DrivenProperty,\n range: ScrollRange,\n statics: readonly StaticTransform[],\n tagOf: (node: object) => number | null,\n shift: number | null = null,\n ): ScrollDrive {\n const drive: Drive = { viewTag: null, range, shift, nodes: null, stopped: false };\n const key = feedKey(feed);\n const connect = (): boolean => {\n if (drive.stopped) return true;\n const viewTag = tagOf(view);\n const sourceTag = tagOf(source);\n if (viewTag === null || sourceTag === null) return false;\n drive.viewTag = viewTag;\n const offset = this.offset(source, sourceTag, feed, key);\n drive.nodes = this.build(offset.value, viewTag, property, drive, statics);\n this.flush();\n return true;\n };\n if (!connect()) this.pending.add(connect);\n return {\n update: (next) => {\n if (drive.stopped || sameRange(drive.range, next)) return;\n drive.range = next;\n if (!drive.nodes || drive.viewTag === null) return;\n this.tearDown(drive.nodes, drive.viewTag);\n const offset = this.offsets.get(source)!.get(key)!;\n drive.nodes = this.build(offset.value, drive.viewTag, property, drive, statics);\n this.flush();\n },\n shift: (amount) => {\n if (drive.stopped || drive.shift === null || drive.shift === amount) return;\n drive.shift = amount;\n if (drive.nodes?.shift == null) return;\n this.native.API.setAnimatedNodeValue(drive.nodes.shift, amount);\n this.flush();\n },\n stop: () => {\n if (drive.stopped) return;\n drive.stopped = true;\n this.pending.delete(connect);\n if (!drive.nodes || drive.viewTag === null) return;\n this.tearDown(drive.nodes, drive.viewTag);\n this.release(source, key);\n this.flush();\n },\n };\n }\n\n /**\n * Play a view's opacity and transform by a scroll: each channel an interpolation from the\n * offset, as `scroll-animation.ts` lays a `@keyframes` animation along it. `update` rebuilds\n * the channels, when the scroll view says how far it scrolls; `stop` lets the view go.\n */\n animate(\n view: object,\n source: object,\n feed: EventFeed,\n channels: DrivenChannels,\n tagOf: (node: object) => number | null,\n ): { update(channels: DrivenChannels): void; stop(): void } {\n const state = {\n channels,\n viewTag: null as number | null,\n nodes: null as number[] | null,\n stopped: false,\n };\n const key = feedKey(feed);\n const connect = (): boolean => {\n if (state.stopped) return true;\n const viewTag = tagOf(view);\n const sourceTag = tagOf(source);\n if (viewTag === null || sourceTag === null) return false;\n state.viewTag = viewTag;\n const offset = this.offset(source, sourceTag, feed, key);\n state.nodes = this.buildChannels(offset.value, viewTag, state.channels);\n this.flush();\n return true;\n };\n if (!connect()) this.pending.add(connect);\n return {\n update: (next) => {\n if (state.stopped) return;\n state.channels = next;\n if (!state.nodes || state.viewTag === null) return;\n this.dropChannels(state.nodes, state.viewTag);\n const offset = this.offsets.get(source)!.get(key)!;\n state.nodes = this.buildChannels(offset.value, state.viewTag, next);\n this.flush();\n },\n stop: () => {\n if (state.stopped) return;\n state.stopped = true;\n this.pending.delete(connect);\n if (!state.nodes || state.viewTag === null) return;\n this.dropChannels(state.nodes, state.viewTag);\n this.release(source, key);\n this.flush();\n },\n };\n }\n\n /**\n * Play a view's opacity and transform by the clock: each channel an interpolation from a value\n * native runs from nothing to `timing.toValue`, over the frames it is given, as many times as\n * asked. Nothing runs in JavaScript while it plays. `ended` is told when native reaches the end\n * of the last iteration; `stop` lets the view go, and stops an animation still playing.\n *\n * Null where the native module cannot start an animation, and the caller plays it itself.\n */\n play(\n view: object,\n channels: DrivenChannels,\n timing: { frames: readonly number[]; toValue: number; iterations: number },\n tagOf: (node: object) => number | null,\n ended: () => void,\n ): { stop(): void } | null {\n const { API } = this.native;\n const start = API.startAnimatingNode;\n if (!start || !API.stopAnimation || !this.native.generateNewAnimationId) return null;\n const state = {\n viewTag: null as number | null,\n nodes: null as number[] | null,\n value: 0,\n id: 0,\n over: false,\n };\n const connect = (): boolean => {\n if (state.over) return true;\n const viewTag = tagOf(view);\n if (viewTag === null) return false;\n state.viewTag = viewTag;\n state.value = this.native.generateNewNodeTag();\n API.createAnimatedNode(state.value, { type: 'value', value: 0, offset: 0 });\n state.nodes = this.buildChannels(state.value, viewTag, channels);\n state.id = this.native.generateNewAnimationId!();\n const config = { type: 'frames', frames: [...timing.frames], toValue: timing.toValue };\n start(state.id, state.value, { ...config, iterations: timing.iterations }, (result) => {\n // Stopped rather than finished is the caller's own doing, and it knows.\n if (!result.finished || state.over) return;\n state.id = 0;\n ended();\n });\n this.flush();\n return true;\n };\n if (!connect()) this.pending.add(connect);\n return {\n stop: () => {\n if (state.over) return;\n state.over = true;\n this.pending.delete(connect);\n if (!state.nodes || state.viewTag === null) return;\n if (state.id) API.stopAnimation!(state.id);\n this.dropChannels(state.nodes, state.viewTag);\n API.dropAnimatedNode(state.value);\n this.flush();\n },\n };\n }\n\n /** The nodes for a set of channels, connected to the view; the props node first. */\n private buildChannels(value: number, viewTag: number, channels: DrivenChannels): number[] {\n const { API } = this.native;\n const made: number[] = [];\n const interpolation = (range: ScrollRange): number => {\n const tag = this.native.generateNewNodeTag();\n API.createAnimatedNode(tag, {\n type: 'interpolation',\n inputRange: [...range.input],\n outputRange: [...range.output],\n extrapolateLeft: 'clamp',\n extrapolateRight: 'clamp',\n });\n API.connectAnimatedNodes(value, tag);\n made.push(tag);\n return tag;\n };\n const style: Record<string, number> = {};\n if (channels.opacity) style['opacity'] = interpolation(channels.opacity);\n if (channels.transform.length) {\n const transforms = channels.transform.map((channel) =>\n 'range' in channel\n ? { type: 'animated', property: channel.property, nodeTag: interpolation(channel.range) }\n : { type: 'static', property: channel.property, value: channel.value },\n );\n const transform = this.native.generateNewNodeTag();\n API.createAnimatedNode(transform, { type: 'transform', transforms });\n for (const entry of transforms) {\n if (entry.type === 'animated') API.connectAnimatedNodes(entry.nodeTag!, transform);\n }\n style['transform'] = transform;\n made.push(transform);\n }\n const styleTag = this.native.generateNewNodeTag();\n API.createAnimatedNode(styleTag, { type: 'style', style });\n for (const tag of Object.values(style)) API.connectAnimatedNodes(tag, styleTag);\n const props = this.native.generateNewNodeTag();\n API.createAnimatedNode(props, { type: 'props', props: { style: styleTag } });\n API.connectAnimatedNodes(styleTag, props);\n API.connectAnimatedNodeToView(props, viewTag);\n return [props, styleTag, ...made.reverse()];\n }\n\n private dropChannels(nodes: readonly number[], viewTag: number): void {\n const { API } = this.native;\n API.disconnectAnimatedNodeFromView(nodes[0]!, viewTag);\n for (const tag of nodes) API.dropAnimatedNode(tag);\n }\n\n /** Connects every drive still waiting for its views, now that a commit may have made them. */\n afterCommit(): void {\n for (const connect of [...this.pending]) if (connect()) this.pending.delete(connect);\n }\n\n /** One value per source view and feed, fed by its events, shared by every view it drives. */\n private offset(source: object, viewTag: number, feed: EventFeed, key: string): Offset {\n let feeds = this.offsets.get(source);\n if (!feeds) this.offsets.set(source, (feeds = new Map()));\n let offset = feeds.get(key);\n if (!offset) {\n const value = this.native.generateNewNodeTag();\n this.native.API.createAnimatedNode(value, { type: 'value', value: 0, offset: 0 });\n for (const event of feed.events) {\n this.native.API.addAnimatedEventToView(viewTag, event, {\n nativeEventPath: [...feed.path],\n animatedValueTag: value,\n });\n }\n offset = { value, viewTag, feed, users: 0 };\n feeds.set(key, offset);\n }\n offset.users++;\n return offset;\n }\n\n private release(source: object, key: string): void {\n const feeds = this.offsets.get(source);\n const offset = feeds?.get(key);\n if (!feeds || !offset || --offset.users > 0) return;\n feeds.delete(key);\n if (feeds.size === 0) this.offsets.delete(source);\n for (const event of offset.feed.events) {\n this.native.API.removeAnimatedEventFromView(offset.viewTag, event, offset.value);\n }\n this.native.API.dropAnimatedNode(offset.value);\n }\n\n private build(\n value: number,\n viewTag: number,\n property: DrivenProperty,\n drive: Drive,\n statics: readonly StaticTransform[],\n ): Nodes {\n const { API } = this.native;\n const range = drive.range;\n const interpolation = this.native.generateNewNodeTag();\n API.createAnimatedNode(interpolation, {\n type: 'interpolation',\n inputRange: [...range.input],\n outputRange: [...range.output],\n extrapolateLeft: 'clamp',\n extrapolateRight: 'clamp',\n });\n let shift: number | null = null;\n let sum: number | null = null;\n if (drive.shift !== null) {\n shift = this.native.generateNewNodeTag();\n API.createAnimatedNode(shift, { type: 'value', value: drive.shift, offset: 0 });\n sum = this.native.generateNewNodeTag();\n API.createAnimatedNode(sum, { type: 'addition', input: [interpolation, shift] });\n }\n const transform = this.native.generateNewNodeTag();\n API.createAnimatedNode(transform, {\n type: 'transform',\n transforms: [\n {\n type: 'animated',\n property,\n nodeTag: sum ?? interpolation,\n },\n ...statics.flatMap((entry) =>\n Object.entries(entry).map(([property, amount]) => ({\n type: 'static',\n property,\n value: amount,\n })),\n ),\n ],\n });\n // A style node between, as `Animated` builds it: RN's props node on iOS reads its values from\n // a style node and ignores a transform node connected to it directly.\n const style = this.native.generateNewNodeTag();\n API.createAnimatedNode(style, { type: 'style', style: { transform } });\n const props = this.native.generateNewNodeTag();\n API.createAnimatedNode(props, { type: 'props', props: { style } });\n API.connectAnimatedNodes(value, interpolation);\n if (shift !== null && sum !== null) {\n API.connectAnimatedNodes(interpolation, sum);\n API.connectAnimatedNodes(shift, sum);\n API.connectAnimatedNodes(sum, transform);\n } else {\n API.connectAnimatedNodes(interpolation, transform);\n }\n API.connectAnimatedNodes(transform, style);\n API.connectAnimatedNodes(style, props);\n API.connectAnimatedNodeToView(props, viewTag);\n return { interpolation, shift, sum, transform, style, props };\n }\n\n private tearDown(nodes: Nodes, viewTag: number): void {\n const { API } = this.native;\n API.disconnectAnimatedNodeFromView(nodes.props, viewTag);\n API.dropAnimatedNode(nodes.props);\n API.dropAnimatedNode(nodes.style);\n API.dropAnimatedNode(nodes.transform);\n if (nodes.sum !== null) API.dropAnimatedNode(nodes.sum);\n if (nodes.shift !== null) API.dropAnimatedNode(nodes.shift);\n API.dropAnimatedNode(nodes.interpolation);\n }\n\n private flush(): void {\n this.native.API.flushQueue?.();\n }\n}\n\nfunction feedKey(feed: EventFeed): string {\n return `${feed.events.join(',')}:${feed.path.join('.')}`;\n}\n\n/** A scroll view's offset along an axis, as `onScroll` carries it. */\nexport function scrollFeed(axis: ScrollAxis): EventFeed {\n return { events: ['onScroll'], path: ['contentOffset', axis] };\n}\n\nfunction sameRange(a: ScrollRange, b: ScrollRange): boolean {\n return (\n a.input.length === b.input.length &&\n a.input.every((value, i) => value === b.input[i]) &&\n a.output.every((value, i) => value === b.output[i])\n );\n}\n\n/**\n * The translate a pinned view needs at each scroll offset: none until the offset reaches where\n * the view starts, then the offset past that, until `until`, where the next pinned view pushes it\n * off and it stays. `start` and `until` are in the scroll view's own coordinates.\n */\nexport function pinnedRange(start: number, until = Infinity): ScrollRange {\n if (!Number.isFinite(until) || until <= start) {\n // A large finite end, since native interpolation takes numbers and extends nothing here.\n return { input: [start, start + 1e7], output: [0, 1e7] };\n }\n return { input: [start, until], output: [0, until - start] };\n}\n"]}
@@ -0,0 +1,7 @@
1
+ /** One stop of a gradient as native takes it. */
2
+ interface GradientStop {
3
+ readonly color?: unknown;
4
+ readonly position?: unknown;
5
+ }
6
+ export declare function premultipliedStops(stops: readonly GradientStop[]): GradientStop[];
7
+ export {};
@@ -0,0 +1,131 @@
1
+ /**
2
+ * A gradient's stops, with each one at no opacity given the colour of the stop it fades to or from.
3
+ *
4
+ * CSS interpolates stops in premultiplied alpha, where a stop at no opacity adds no colour of its
5
+ * own: `transparent` to white is white throughout. iOS interpolates the channels as they stand,
6
+ * and halfway from `rgb(0 0 0 / 0)` to white is grey at half opacity. With its neighbour's
7
+ * channels the stop paints the same either way. One between two colours becomes two stops at one
8
+ * position, the first fading from the colour before and the second to the colour after.
9
+ *
10
+ * A stop at part opacity adds its colour in proportion, so between two stops of different colour
11
+ * and opacity the colour bends towards the more opaque one. Stops written along that curve stand
12
+ * in for it.
13
+ */
14
+ import { parseColor } from "./transition.js";
15
+ const BLACK = '0, 0, 0';
16
+ export function premultipliedStops(stops) {
17
+ const colours = stops.map((stop) => {
18
+ const parsed = parseColor(stop.color);
19
+ return parsed && { channels: parsed.slice(0, 3).join(', '), clear: parsed[3] === 0 };
20
+ });
21
+ const visible = (i) => (colours[i]?.clear === false ? colours[i].channels : undefined);
22
+ const out = [];
23
+ // The colour React Native's iOS painter gives a transparent black stop: the last stop that was
24
+ // not one. Right when that is the colour wanted, and otherwise avoided by a stop a shade off
25
+ // black, which at no opacity paints the same.
26
+ let lent;
27
+ const fading = (stop, channels) => {
28
+ const written = channels === BLACK && lent && lent !== BLACK ? '0, 0, 1' : channels;
29
+ if (written !== BLACK)
30
+ lent = written;
31
+ out.push({ ...stop, color: `rgba(${written}, 0)` });
32
+ };
33
+ stops.forEach((stop, i) => {
34
+ const colour = colours[i];
35
+ const lenders = colour?.clear ? [...new Set([visible(i - 1), visible(i + 1)])] : [];
36
+ const [first, second] = lenders.filter((channels) => channels !== undefined);
37
+ if (!first) {
38
+ const own = colour?.channels ?? 'unknown';
39
+ if (!colour?.clear || own !== BLACK)
40
+ lent = own;
41
+ out.push(stop);
42
+ return;
43
+ }
44
+ const position = second ? (stop.position ?? impliedPosition(stops, i)) : undefined;
45
+ if (position === undefined)
46
+ return fading(stop, first);
47
+ fading({ ...stop, position }, first);
48
+ fading({ ...stop, position }, second);
49
+ });
50
+ return curved(out);
51
+ }
52
+ /**
53
+ * How many stops stand in for the curve between two. They sit where the opacity has changed by
54
+ * equal ratios, which is where the curve bends evenly, and at 8 native's straight lines between
55
+ * them stay within a level of it from 2% opacity to full.
56
+ */
57
+ const STEPS = 8;
58
+ /** The stops, with those along the curve between two of different colour and opacity written in. */
59
+ function curved(stops) {
60
+ const spans = stops.map((_, i) => span(stops, i));
61
+ return stops.flatMap((stop, i) => {
62
+ const [before, after] = [spans[i - 1], spans[i]];
63
+ // A stop beside a curve is placed too: native would space an unplaced one between the stops
64
+ // written in around it.
65
+ const position = after?.start ?? before?.end ?? stop.position;
66
+ return [{ ...stop, position }, ...(after ? along(after) : [])];
67
+ });
68
+ }
69
+ /** The run from stop `i` to the next, when its colour bends and both ends can be placed. */
70
+ function span(stops, i) {
71
+ const from = parseColor(stops[i].color);
72
+ const to = parseColor(stops[i + 1]?.color);
73
+ if (!from || !to || !bends(from, to))
74
+ return undefined;
75
+ const start = stops[i].position ?? impliedPosition(stops, i);
76
+ const end = stops[i + 1].position ?? impliedPosition(stops, i + 1);
77
+ // Two stops at one position are a hard edge, with no run between them to curve.
78
+ if (unit(start) === undefined || unit(start) !== unit(end) || start === end)
79
+ return undefined;
80
+ return { from, to, start: start, end: end };
81
+ }
82
+ /** Whether the colour between two bends: both have some opacity, and differ in it and in colour. */
83
+ function bends(from, to) {
84
+ if (!from[3] || !to[3] || from[3] === to[3])
85
+ return false;
86
+ return from.slice(0, 3).join() !== to.slice(0, 3).join();
87
+ }
88
+ /** The stops along a span's curve, each the colour CSS paints at its position. */
89
+ function along({ from, to, start, end }) {
90
+ const [a, b] = [parseFloat(String(start)), parseFloat(String(end))];
91
+ return Array.from({ length: STEPS - 1 }, (_, k) => {
92
+ const alpha = from[3] * (to[3] / from[3]) ** ((k + 1) / STEPS);
93
+ const t = (alpha - from[3]) / (to[3] - from[3]);
94
+ const channel = (c) => Math.round((from[c] * from[3] * (1 - t) + to[c] * to[3] * t) / alpha);
95
+ const at = Math.round((a + (b - a) * t) * 1e4) / 1e4;
96
+ return {
97
+ color: `rgba(${channel(0)}, ${channel(1)}, ${channel(2)}, ${Math.round(alpha * 1000) / 1000})`,
98
+ position: unit(start) ? `${at}%` : at,
99
+ };
100
+ });
101
+ }
102
+ /**
103
+ * Where CSS puts a stop written without a position: spaced evenly between the placed stops either
104
+ * side of it, the first stop at 0% and the last at 100%.
105
+ */
106
+ function impliedPosition(stops, i) {
107
+ let from = i;
108
+ while (from > 0 && stops[from].position == null)
109
+ from--;
110
+ let to = i;
111
+ while (to < stops.length - 1 && stops[to].position == null)
112
+ to++;
113
+ const end = stops[to].position ?? '100%';
114
+ // The first stop is at the start of the line, in whichever unit the stop after it is placed.
115
+ const start = stops[from].position ?? (typeof end === 'number' ? 0 : '0%');
116
+ // ponytail: a percentage and a length either side need the box's size, which only native has, so
117
+ // the stop stays one stop in the colour before it. Positions written out of order are not
118
+ // clamped as CSS clamps them either. Resolve both in points if a design needs one.
119
+ if (unit(start) === undefined || unit(start) !== unit(end))
120
+ return undefined;
121
+ const [a, b] = [parseFloat(String(start)), parseFloat(String(end))];
122
+ const at = Math.round((a + ((b - a) * (i - from)) / (to - from)) * 1e4) / 1e4;
123
+ return unit(start) ? `${at}%` : at;
124
+ }
125
+ /** What a position is measured in: `%`, nothing for points, or undefined for neither. */
126
+ function unit(position) {
127
+ if (typeof position === 'number')
128
+ return '';
129
+ return typeof position === 'string' && position.endsWith('%') ? '%' : undefined;
130
+ }
131
+ //# sourceMappingURL=premultiplied-stops.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"premultiplied-stops.js","sourceRoot":"","sources":["../src/premultiplied-stops.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A gradient's stops, with each one at no opacity given the colour of the stop it fades to or from.\n *\n * CSS interpolates stops in premultiplied alpha, where a stop at no opacity adds no colour of its\n * own: `transparent` to white is white throughout. iOS interpolates the channels as they stand,\n * and halfway from `rgb(0 0 0 / 0)` to white is grey at half opacity. With its neighbour's\n * channels the stop paints the same either way. One between two colours becomes two stops at one\n * position, the first fading from the colour before and the second to the colour after.\n *\n * A stop at part opacity adds its colour in proportion, so between two stops of different colour\n * and opacity the colour bends towards the more opaque one. Stops written along that curve stand\n * in for it.\n */\nimport { parseColor } from './transition.ts';\n\n/** One stop of a gradient as native takes it. */\ninterface GradientStop {\n readonly color?: unknown;\n readonly position?: unknown;\n}\n\nconst BLACK = '0, 0, 0';\n\nexport function premultipliedStops(stops: readonly GradientStop[]): GradientStop[] {\n const colours = stops.map((stop) => {\n const parsed = parseColor(stop.color);\n return parsed && { channels: parsed.slice(0, 3).join(', '), clear: parsed[3] === 0 };\n });\n const visible = (i: number) => (colours[i]?.clear === false ? colours[i].channels : undefined);\n\n const out: GradientStop[] = [];\n // The colour React Native's iOS painter gives a transparent black stop: the last stop that was\n // not one. Right when that is the colour wanted, and otherwise avoided by a stop a shade off\n // black, which at no opacity paints the same.\n let lent: string | undefined;\n const fading = (stop: GradientStop, channels: string) => {\n const written = channels === BLACK && lent && lent !== BLACK ? '0, 0, 1' : channels;\n if (written !== BLACK) lent = written;\n out.push({ ...stop, color: `rgba(${written}, 0)` });\n };\n\n stops.forEach((stop, i) => {\n const colour = colours[i];\n const lenders = colour?.clear ? [...new Set([visible(i - 1), visible(i + 1)])] : [];\n const [first, second] = lenders.filter((channels) => channels !== undefined);\n if (!first) {\n const own = colour?.channels ?? 'unknown';\n if (!colour?.clear || own !== BLACK) lent = own;\n out.push(stop);\n return;\n }\n const position = second ? (stop.position ?? impliedPosition(stops, i)) : undefined;\n if (position === undefined) return fading(stop, first);\n fading({ ...stop, position }, first);\n fading({ ...stop, position }, second!);\n });\n return curved(out);\n}\n\n/**\n * How many stops stand in for the curve between two. They sit where the opacity has changed by\n * equal ratios, which is where the curve bends evenly, and at 8 native's straight lines between\n * them stay within a level of it from 2% opacity to full.\n */\nconst STEPS = 8;\n\n/** The stops, with those along the curve between two of different colour and opacity written in. */\nfunction curved(stops: readonly GradientStop[]): GradientStop[] {\n const spans = stops.map((_, i) => span(stops, i));\n return stops.flatMap((stop, i) => {\n const [before, after] = [spans[i - 1], spans[i]];\n // A stop beside a curve is placed too: native would space an unplaced one between the stops\n // written in around it.\n const position = after?.start ?? before?.end ?? stop.position;\n return [{ ...stop, position }, ...(after ? along(after) : [])];\n });\n}\n\ninterface Span {\n readonly from: readonly number[];\n readonly to: readonly number[];\n readonly start: string | number;\n readonly end: string | number;\n}\n\n/** The run from stop `i` to the next, when its colour bends and both ends can be placed. */\nfunction span(stops: readonly GradientStop[], i: number): Span | undefined {\n const from = parseColor(stops[i]!.color);\n const to = parseColor(stops[i + 1]?.color);\n if (!from || !to || !bends(from, to)) return undefined;\n const start = stops[i]!.position ?? impliedPosition(stops, i);\n const end = stops[i + 1]!.position ?? impliedPosition(stops, i + 1);\n // Two stops at one position are a hard edge, with no run between them to curve.\n if (unit(start) === undefined || unit(start) !== unit(end) || start === end) return undefined;\n return { from, to, start: start as string | number, end: end as string | number };\n}\n\n/** Whether the colour between two bends: both have some opacity, and differ in it and in colour. */\nfunction bends(from: readonly number[], to: readonly number[]): boolean {\n if (!from[3] || !to[3] || from[3] === to[3]) return false;\n return from.slice(0, 3).join() !== to.slice(0, 3).join();\n}\n\n/** The stops along a span's curve, each the colour CSS paints at its position. */\nfunction along({ from, to, start, end }: Span): GradientStop[] {\n const [a, b] = [parseFloat(String(start)), parseFloat(String(end))];\n return Array.from({ length: STEPS - 1 }, (_, k) => {\n const alpha = from[3]! * (to[3]! / from[3]!) ** ((k + 1) / STEPS);\n const t = (alpha - from[3]!) / (to[3]! - from[3]!);\n const channel = (c: number) =>\n Math.round((from[c]! * from[3]! * (1 - t) + to[c]! * to[3]! * t) / alpha);\n const at = Math.round((a + (b - a) * t) * 1e4) / 1e4;\n return {\n color: `rgba(${channel(0)}, ${channel(1)}, ${channel(2)}, ${Math.round(alpha * 1000) / 1000})`,\n position: unit(start) ? `${at}%` : at,\n };\n });\n}\n\n/**\n * Where CSS puts a stop written without a position: spaced evenly between the placed stops either\n * side of it, the first stop at 0% and the last at 100%.\n */\nfunction impliedPosition(stops: readonly GradientStop[], i: number): string | number | undefined {\n let from = i;\n while (from > 0 && stops[from]!.position == null) from--;\n let to = i;\n while (to < stops.length - 1 && stops[to]!.position == null) to++;\n const end = stops[to]!.position ?? '100%';\n // The first stop is at the start of the line, in whichever unit the stop after it is placed.\n const start = stops[from]!.position ?? (typeof end === 'number' ? 0 : '0%');\n // ponytail: a percentage and a length either side need the box's size, which only native has, so\n // the stop stays one stop in the colour before it. Positions written out of order are not\n // clamped as CSS clamps them either. Resolve both in points if a design needs one.\n if (unit(start) === undefined || unit(start) !== unit(end)) return undefined;\n const [a, b] = [parseFloat(String(start)), parseFloat(String(end))];\n const at = Math.round((a + ((b - a) * (i - from)) / (to - from)) * 1e4) / 1e4;\n return unit(start) ? `${at}%` : at;\n}\n\n/** What a position is measured in: `%`, nothing for points, or undefined for neither. */\nfunction unit(position: unknown): string | undefined {\n if (typeof position === 'number') return '';\n return typeof position === 'string' && position.endsWith('%') ? '%' : undefined;\n}\n"]}
@@ -36,6 +36,17 @@ export declare function scrollChannels(tracks: ReadonlyMap<string, Track>, spec:
36
36
  channels: DrivenChannels;
37
37
  held: string[];
38
38
  };
39
+ /**
40
+ * The channels for an animation played by the clock, along a value native runs from nothing to
41
+ * `span`: one iteration along 0 to 1, and for `alternate` the way back along 1 to 2, so that
42
+ * native repeats there and back as one animation. Both ends are the frames' own: what the
43
+ * element rests at is committed when the animation ends, and not played.
44
+ */
45
+ export declare function clockChannels(tracks: ReadonlyMap<string, Track>, spec: AnimationSpec, resting: Readonly<Record<string, unknown>>): {
46
+ channels: DrivenChannels;
47
+ held: string[];
48
+ span: number;
49
+ };
39
50
  /** The values a scroll-driven animation commits: its frame at the offset the view starts at. */
40
51
  export declare function firstFrame(tracks: ReadonlyMap<string, Track>, spec: AnimationSpec, range: readonly [number, number]): Record<string, unknown>;
41
52
  export {};
@@ -50,6 +50,35 @@ export function scrollChannels(tracks, spec, resting, range) {
50
50
  held,
51
51
  };
52
52
  }
53
+ /**
54
+ * The channels for an animation played by the clock, along a value native runs from nothing to
55
+ * `span`: one iteration along 0 to 1, and for `alternate` the way back along 1 to 2, so that
56
+ * native repeats there and back as one animation. Both ends are the frames' own: what the
57
+ * element rests at is committed when the animation ends, and not played.
58
+ */
59
+ export function clockChannels(tracks, spec, resting) {
60
+ const way = (direction, range) => scrollChannels(tracks, { ...spec, fill: 'both', direction }, resting, range);
61
+ if (spec.direction !== 'alternate' && spec.direction !== 'alternate-reverse') {
62
+ return { ...way(spec.direction, [0, 1]), span: 1 };
63
+ }
64
+ const reversed = spec.direction === 'alternate-reverse';
65
+ const out = way(reversed ? 'reverse' : undefined, [0, 1]);
66
+ const back = way(reversed ? undefined : 'reverse', [1, 2]);
67
+ const join = (a, b) => ({
68
+ input: [...a.input, ...b.input.slice(1)],
69
+ output: [...a.output, ...b.output.slice(1)],
70
+ });
71
+ const transform = out.channels.transform.map((channel, i) => {
72
+ const other = back.channels.transform[i];
73
+ return 'range' in channel && other && 'range' in other
74
+ ? { property: channel.property, range: join(channel.range, other.range) }
75
+ : channel;
76
+ });
77
+ const opacity = out.channels.opacity && back.channels.opacity
78
+ ? { opacity: join(out.channels.opacity, back.channels.opacity) }
79
+ : {};
80
+ return { channels: { ...opacity, transform }, held: out.held, span: 2 };
81
+ }
53
82
  /** One transform group's entries, each driven along the scroll; null if their shapes differ. */
54
83
  function drivenGroup(track, resting, along) {
55
84
  const shapes = track.map((point) => (Array.isArray(point.value) ? point.value : null));