@triniwiz/nativescript-masonkit 1.0.0-beta.117 → 1.0.0-beta.119

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.
Files changed (88) hide show
  1. package/DOCS.md +15 -2
  2. package/common.d.ts +10 -9
  3. package/common.js +60 -58
  4. package/common.js.map +1 -1
  5. package/css-math.d.ts +34 -0
  6. package/css-math.js +301 -0
  7. package/css-math.js.map +1 -0
  8. package/css-shorthands.d.ts +24 -0
  9. package/css-shorthands.js +145 -0
  10. package/css-shorthands.js.map +1 -1
  11. package/index.d.ts +45 -0
  12. package/input/common.d.ts +13 -0
  13. package/input/common.js +97 -0
  14. package/input/common.js.map +1 -1
  15. package/package.json +3 -3
  16. package/platforms/android/masonkit-release.aar +0 -0
  17. package/platforms/ios/Mason.xcframework/Info.plist +11 -11
  18. package/platforms/ios/Mason.xcframework/ios-arm64/Mason.framework/Headers/Mason-Swift.h +2 -0
  19. package/platforms/ios/Mason.xcframework/ios-arm64/Mason.framework/Mason +0 -0
  20. package/platforms/ios/Mason.xcframework/ios-arm64/Mason.framework/Modules/Mason.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo +0 -0
  21. package/platforms/ios/Mason.xcframework/ios-arm64/Mason.framework/Modules/Mason.swiftmodule/arm64-apple-ios.abi.json +6152 -14642
  22. package/platforms/ios/Mason.xcframework/ios-arm64/Mason.framework/Modules/Mason.swiftmodule/arm64-apple-ios.private.swiftinterface +31 -7
  23. package/platforms/ios/Mason.xcframework/ios-arm64/Mason.framework/Modules/Mason.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  24. package/platforms/ios/Mason.xcframework/ios-arm64/Mason.framework/Modules/Mason.swiftmodule/arm64-apple-ios.swiftinterface +31 -7
  25. package/platforms/ios/Mason.xcframework/ios-arm64/dSYMs/Mason.framework.dSYM/Contents/Resources/DWARF/Mason +0 -0
  26. package/platforms/ios/Mason.xcframework/ios-arm64/dSYMs/Mason.framework.dSYM/Contents/Resources/Relocations/aarch64/Mason.yml +9394 -9349
  27. package/platforms/ios/Mason.xcframework/ios-arm64_x86_64-simulator/Mason.framework/Headers/Mason-Swift.h +4 -0
  28. package/platforms/ios/Mason.xcframework/ios-arm64_x86_64-simulator/Mason.framework/Mason +0 -0
  29. package/platforms/ios/Mason.xcframework/ios-arm64_x86_64-simulator/Mason.framework/Modules/Mason.swiftmodule/Project/arm64-apple-ios-simulator.swiftsourceinfo +0 -0
  30. package/platforms/ios/Mason.xcframework/ios-arm64_x86_64-simulator/Mason.framework/Modules/Mason.swiftmodule/Project/x86_64-apple-ios-simulator.swiftsourceinfo +0 -0
  31. package/platforms/ios/Mason.xcframework/ios-arm64_x86_64-simulator/Mason.framework/Modules/Mason.swiftmodule/arm64-apple-ios-simulator.abi.json +6152 -14642
  32. package/platforms/ios/Mason.xcframework/ios-arm64_x86_64-simulator/Mason.framework/Modules/Mason.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +31 -7
  33. package/platforms/ios/Mason.xcframework/ios-arm64_x86_64-simulator/Mason.framework/Modules/Mason.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  34. package/platforms/ios/Mason.xcframework/ios-arm64_x86_64-simulator/Mason.framework/Modules/Mason.swiftmodule/arm64-apple-ios-simulator.swiftinterface +31 -7
  35. package/platforms/ios/Mason.xcframework/ios-arm64_x86_64-simulator/Mason.framework/Modules/Mason.swiftmodule/x86_64-apple-ios-simulator.abi.json +6152 -14642
  36. package/platforms/ios/Mason.xcframework/ios-arm64_x86_64-simulator/Mason.framework/Modules/Mason.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +31 -7
  37. package/platforms/ios/Mason.xcframework/ios-arm64_x86_64-simulator/Mason.framework/Modules/Mason.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  38. package/platforms/ios/Mason.xcframework/ios-arm64_x86_64-simulator/Mason.framework/Modules/Mason.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +31 -7
  39. package/platforms/ios/Mason.xcframework/ios-arm64_x86_64-simulator/Mason.framework/_CodeSignature/CodeResources +26 -26
  40. package/platforms/ios/Mason.xcframework/ios-arm64_x86_64-simulator/dSYMs/Mason.framework.dSYM/Contents/Resources/DWARF/Mason +2 -2
  41. package/platforms/ios/Mason.xcframework/ios-arm64_x86_64-simulator/dSYMs/Mason.framework.dSYM/Contents/Resources/Relocations/aarch64/Mason.yml +9388 -9343
  42. package/platforms/ios/Mason.xcframework/ios-arm64_x86_64-simulator/dSYMs/Mason.framework.dSYM/Contents/Resources/Relocations/x86_64/Mason.yml +8953 -8915
  43. package/platforms/ios/Mason.xcframework/xros-arm64/Mason.framework/Headers/Mason-Swift.h +2 -0
  44. package/platforms/ios/Mason.xcframework/xros-arm64/Mason.framework/Mason +0 -0
  45. package/platforms/ios/Mason.xcframework/xros-arm64/Mason.framework/Modules/Mason.swiftmodule/Project/arm64-apple-xros.swiftsourceinfo +0 -0
  46. package/platforms/ios/Mason.xcframework/xros-arm64/Mason.framework/Modules/Mason.swiftmodule/arm64-apple-xros.abi.json +6152 -14642
  47. package/platforms/ios/Mason.xcframework/xros-arm64/Mason.framework/Modules/Mason.swiftmodule/arm64-apple-xros.private.swiftinterface +31 -7
  48. package/platforms/ios/Mason.xcframework/xros-arm64/Mason.framework/Modules/Mason.swiftmodule/arm64-apple-xros.swiftdoc +0 -0
  49. package/platforms/ios/Mason.xcframework/xros-arm64/Mason.framework/Modules/Mason.swiftmodule/arm64-apple-xros.swiftinterface +31 -7
  50. package/platforms/ios/Mason.xcframework/xros-arm64/dSYMs/Mason.framework.dSYM/Contents/Resources/DWARF/Mason +0 -0
  51. package/platforms/ios/Mason.xcframework/xros-arm64/dSYMs/Mason.framework.dSYM/Contents/Resources/Relocations/aarch64/Mason.yml +8883 -8839
  52. package/platforms/ios/Mason.xcframework/xros-arm64-simulator/Mason.framework/Headers/Mason-Swift.h +2 -0
  53. package/platforms/ios/Mason.xcframework/xros-arm64-simulator/Mason.framework/Mason +0 -0
  54. package/platforms/ios/Mason.xcframework/xros-arm64-simulator/Mason.framework/Modules/Mason.swiftmodule/Project/arm64-apple-xros-simulator.swiftsourceinfo +0 -0
  55. package/platforms/ios/Mason.xcframework/xros-arm64-simulator/Mason.framework/Modules/Mason.swiftmodule/arm64-apple-xros-simulator.abi.json +6152 -14642
  56. package/platforms/ios/Mason.xcframework/xros-arm64-simulator/Mason.framework/Modules/Mason.swiftmodule/arm64-apple-xros-simulator.private.swiftinterface +31 -7
  57. package/platforms/ios/Mason.xcframework/xros-arm64-simulator/Mason.framework/Modules/Mason.swiftmodule/arm64-apple-xros-simulator.swiftdoc +0 -0
  58. package/platforms/ios/Mason.xcframework/xros-arm64-simulator/Mason.framework/Modules/Mason.swiftmodule/arm64-apple-xros-simulator.swiftinterface +31 -7
  59. package/platforms/ios/Mason.xcframework/xros-arm64-simulator/Mason.framework/_CodeSignature/CodeResources +14 -14
  60. package/platforms/ios/Mason.xcframework/xros-arm64-simulator/dSYMs/Mason.framework.dSYM/Contents/Resources/DWARF/Mason +0 -0
  61. package/platforms/ios/Mason.xcframework/xros-arm64-simulator/dSYMs/Mason.framework.dSYM/Contents/Resources/Relocations/aarch64/Mason.yml +8882 -8838
  62. package/platforms/windows/arm64/NativeScript.Mason.dll +0 -0
  63. package/platforms/windows/arm64/NativeScript.Mason.winmd +0 -0
  64. package/platforms/windows/x64/NativeScript.Mason.dll +0 -0
  65. package/platforms/windows/x64/NativeScript.Mason.winmd +0 -0
  66. package/properties.d.ts +20 -9
  67. package/properties.js +285 -70
  68. package/properties.js.map +1 -1
  69. package/style.d.ts +53 -4
  70. package/style.js +382 -66
  71. package/style.js.map +1 -1
  72. package/svelte/README.md +6 -1
  73. package/svelte/package.json +1 -1
  74. package/svelte/src/element-registry.js +1 -1
  75. package/svelte/src/element-registry.js.map +1 -1
  76. package/svelte/src/mason-element-node.d.ts +2 -2
  77. package/svelte/src/mason-element-node.d.ts.map +1 -1
  78. package/svelte/src/mason-element-node.js +1 -1
  79. package/svelte/src/mason-element-node.js.map +1 -1
  80. package/typings/windows.d.ts +1 -1
  81. package/view/index.ios.d.ts +7 -0
  82. package/view/index.ios.js +68 -18
  83. package/view/index.ios.js.map +1 -1
  84. package/web.js +2 -0
  85. package/web.js.map +1 -1
  86. package/windows-mask.d.ts +2 -0
  87. package/windows-mask.js +2 -18
  88. package/windows-mask.js.map +1 -1
package/style.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { layout as coreLayout } from '@nativescript/core/utils';
2
2
  import { cssUnits } from './units';
3
+ import { style_ } from './symbols';
3
4
  import { pickFontFamily } from './fonts';
4
5
  import { masonEngine } from './windows-panel-helpers';
5
6
  import { reportCssDiagnostic } from './diagnostics';
@@ -7,6 +8,7 @@ import { expandColorStops, resolveStopPositions } from './gradient-stops';
7
8
  import { encodeBoxShadows, parseBoxShadows } from './box-shadow';
8
9
  import { encodeCssFilter, parseCssFilter } from './css-filter';
9
10
  import { resolveCssColors } from './css-colors';
11
+ import { evaluateCssMath, hasCssMathFunction, isCssMathFunction, substituteCssMath } from './css-math';
10
12
  import { encodeWindowsBackground, encodeWindowsBorderImage, encodeWindowsMask, WINDOWS_MASK_PROPERTIES } from './windows-mask';
11
13
  import { Color, Length as CoreLength, PercentLength as CorePercentLength, Screen, knownFolders } from '@nativescript/core';
12
14
  // The Windows shell lays out in XAML DIPs, so its style buffer holds DIPs. Core's Windows density
@@ -14,6 +16,7 @@ import { Color, Length as CoreLength, PercentLength as CorePercentLength, Screen
14
16
  const layout = __WINDOWS__ ? { ...coreLayout, getDisplayDensity: () => 1, toDevicePixels: (value) => value, toDeviceIndependentPixels: (value) => value } : coreLayout;
15
17
  var StyleKeys;
16
18
  (function (StyleKeys) {
19
+ // <style-keys:offsets> generated from tools/scripts/style-keys/style-keys.schema, run `npm run gen:style-keys`
17
20
  StyleKeys[StyleKeys["DISPLAY"] = 0] = "DISPLAY";
18
21
  StyleKeys[StyleKeys["POSITION"] = 1] = "POSITION";
19
22
  StyleKeys[StyleKeys["DIRECTION"] = 2] = "DIRECTION";
@@ -27,7 +30,7 @@ var StyleKeys;
27
30
  StyleKeys[StyleKeys["JUSTIFY_ITEMS"] = 10] = "JUSTIFY_ITEMS";
28
31
  StyleKeys[StyleKeys["JUSTIFY_SELF"] = 11] = "JUSTIFY_SELF";
29
32
  StyleKeys[StyleKeys["JUSTIFY_CONTENT"] = 12] = "JUSTIFY_CONTENT";
30
- // Contiguous types, then contiguous values per group
33
+ // Each box side group: four types, then four values.
31
34
  StyleKeys[StyleKeys["INSET_LEFT_TYPE"] = 13] = "INSET_LEFT_TYPE";
32
35
  StyleKeys[StyleKeys["INSET_RIGHT_TYPE"] = 14] = "INSET_RIGHT_TYPE";
33
36
  StyleKeys[StyleKeys["INSET_TOP_TYPE"] = 15] = "INSET_TOP_TYPE";
@@ -102,16 +105,15 @@ var StyleKeys;
102
105
  StyleKeys[StyleKeys["MIN_CONTENT_HEIGHT"] = 186] = "MIN_CONTENT_HEIGHT";
103
106
  StyleKeys[StyleKeys["MAX_CONTENT_WIDTH"] = 190] = "MAX_CONTENT_WIDTH";
104
107
  StyleKeys[StyleKeys["MAX_CONTENT_HEIGHT"] = 194] = "MAX_CONTENT_HEIGHT";
105
- // Border Style (per side)
106
108
  StyleKeys[StyleKeys["BORDER_LEFT_STYLE"] = 198] = "BORDER_LEFT_STYLE";
107
109
  StyleKeys[StyleKeys["BORDER_RIGHT_STYLE"] = 199] = "BORDER_RIGHT_STYLE";
108
110
  StyleKeys[StyleKeys["BORDER_TOP_STYLE"] = 200] = "BORDER_TOP_STYLE";
109
111
  StyleKeys[StyleKeys["BORDER_BOTTOM_STYLE"] = 201] = "BORDER_BOTTOM_STYLE";
110
- // Border Color (per side)
111
112
  StyleKeys[StyleKeys["BORDER_LEFT_COLOR"] = 202] = "BORDER_LEFT_COLOR";
112
113
  StyleKeys[StyleKeys["BORDER_RIGHT_COLOR"] = 206] = "BORDER_RIGHT_COLOR";
113
114
  StyleKeys[StyleKeys["BORDER_TOP_COLOR"] = 210] = "BORDER_TOP_COLOR";
114
115
  StyleKeys[StyleKeys["BORDER_BOTTOM_COLOR"] = 214] = "BORDER_BOTTOM_COLOR";
116
+ // Elliptical radii: eight types, eight values, then four squircle exponents.
115
117
  StyleKeys[StyleKeys["BORDER_RADIUS_TOP_LEFT_X_TYPE"] = 218] = "BORDER_RADIUS_TOP_LEFT_X_TYPE";
116
118
  StyleKeys[StyleKeys["BORDER_RADIUS_TOP_LEFT_Y_TYPE"] = 219] = "BORDER_RADIUS_TOP_LEFT_Y_TYPE";
117
119
  StyleKeys[StyleKeys["BORDER_RADIUS_TOP_RIGHT_X_TYPE"] = 220] = "BORDER_RADIUS_TOP_RIGHT_X_TYPE";
@@ -132,7 +134,6 @@ var StyleKeys;
132
134
  StyleKeys[StyleKeys["BORDER_RADIUS_TOP_RIGHT_EXPONENT"] = 262] = "BORDER_RADIUS_TOP_RIGHT_EXPONENT";
133
135
  StyleKeys[StyleKeys["BORDER_RADIUS_BOTTOM_RIGHT_EXPONENT"] = 266] = "BORDER_RADIUS_BOTTOM_RIGHT_EXPONENT";
134
136
  StyleKeys[StyleKeys["BORDER_RADIUS_BOTTOM_LEFT_EXPONENT"] = 270] = "BORDER_RADIUS_BOTTOM_LEFT_EXPONENT";
135
- // Float
136
137
  StyleKeys[StyleKeys["FLOAT"] = 274] = "FLOAT";
137
138
  StyleKeys[StyleKeys["CLEAR"] = 275] = "CLEAR";
138
139
  StyleKeys[StyleKeys["OBJECT_FIT"] = 276] = "OBJECT_FIT";
@@ -153,6 +154,7 @@ var StyleKeys;
153
154
  StyleKeys[StyleKeys["LIST_STYLE_POSITION_STATE"] = 318] = "LIST_STYLE_POSITION_STATE";
154
155
  StyleKeys[StyleKeys["LIST_STYLE_TYPE_STATE"] = 319] = "LIST_STYLE_TYPE_STATE";
155
156
  StyleKeys[StyleKeys["REF_COUNT"] = 320] = "REF_COUNT";
157
+ // Inherited text fields. A *_STATE byte is nonzero once the node sets the field.
156
158
  StyleKeys[StyleKeys["FONT_COLOR"] = 324] = "FONT_COLOR";
157
159
  StyleKeys[StyleKeys["FONT_COLOR_STATE"] = 328] = "FONT_COLOR_STATE";
158
160
  StyleKeys[StyleKeys["FONT_SIZE"] = 329] = "FONT_SIZE";
@@ -197,14 +199,22 @@ var StyleKeys;
197
199
  StyleKeys[StyleKeys["TEXT_SHADOW_STATE"] = 395] = "TEXT_SHADOW_STATE";
198
200
  StyleKeys[StyleKeys["TEXT_OVERFLOW"] = 396] = "TEXT_OVERFLOW";
199
201
  StyleKeys[StyleKeys["TEXT_OVERFLOW_STATE"] = 397] = "TEXT_OVERFLOW_STATE";
200
- // Pseudo set mask: 128-bit bitmask (two longs) tracking which properties
201
- // were explicitly set on a pseudo style buffer. Uses the same bit layout
202
- // as StateKeys. Zero-copy: lives in the style buffer itself.
202
+ // Which StateKeys a pseudo-class buffer sets explicitly (same bit layout).
203
203
  StyleKeys[StyleKeys["PSEUDO_SET_MASK_LOW"] = 398] = "PSEUDO_SET_MASK_LOW";
204
204
  StyleKeys[StyleKeys["PSEUDO_SET_MASK_HIGH"] = 406] = "PSEUDO_SET_MASK_HIGH";
205
- // font-variant-numeric bitmask (byte) + state
205
+ // 414-418 free.
206
206
  StyleKeys[StyleKeys["FONT_VARIANT_NUMERIC"] = 419] = "FONT_VARIANT_NUMERIC";
207
207
  StyleKeys[StyleKeys["FONT_VARIANT_NUMERIC_STATE"] = 420] = "FONT_VARIANT_NUMERIC_STATE";
208
+ // Up to six inline transform ops, or a full matrix.
209
+ StyleKeys[StyleKeys["TRANSFORM_COUNT"] = 422] = "TRANSFORM_COUNT";
210
+ StyleKeys[StyleKeys["TRANSFORM_FLAGS"] = 423] = "TRANSFORM_FLAGS";
211
+ StyleKeys[StyleKeys["TRANSFORM_OP_0"] = 424] = "TRANSFORM_OP_0";
212
+ StyleKeys[StyleKeys["TRANSFORM_OP_1"] = 436] = "TRANSFORM_OP_1";
213
+ StyleKeys[StyleKeys["TRANSFORM_OP_2"] = 448] = "TRANSFORM_OP_2";
214
+ StyleKeys[StyleKeys["TRANSFORM_OP_3"] = 460] = "TRANSFORM_OP_3";
215
+ StyleKeys[StyleKeys["TRANSFORM_OP_4"] = 472] = "TRANSFORM_OP_4";
216
+ StyleKeys[StyleKeys["TRANSFORM_OP_5"] = 484] = "TRANSFORM_OP_5";
217
+ StyleKeys[StyleKeys["TRANSFORM_MATRIX"] = 496] = "TRANSFORM_MATRIX";
208
218
  StyleKeys[StyleKeys["OBJECT_POSITION_X_TYPE"] = 560] = "OBJECT_POSITION_X_TYPE";
209
219
  StyleKeys[StyleKeys["OBJECT_POSITION_Y_TYPE"] = 561] = "OBJECT_POSITION_Y_TYPE";
210
220
  StyleKeys[StyleKeys["OBJECT_POSITION_X_VALUE"] = 562] = "OBJECT_POSITION_X_VALUE";
@@ -223,6 +233,14 @@ var StyleKeys;
223
233
  StyleKeys[StyleKeys["WORD_SPACING_STATE"] = 587] = "WORD_SPACING_STATE";
224
234
  StyleKeys[StyleKeys["FONT_STRETCH"] = 588] = "FONT_STRETCH";
225
235
  StyleKeys[StyleKeys["FONT_STRETCH_STATE"] = 592] = "FONT_STRETCH_STATE";
236
+ StyleKeys[StyleKeys["LINE_CLAMP"] = 593] = "LINE_CLAMP";
237
+ StyleKeys[StyleKeys["LINE_CLAMP_STATE"] = 597] = "LINE_CLAMP_STATE";
238
+ StyleKeys[StyleKeys["TRANSFORM_ORIGIN_X_TYPE"] = 598] = "TRANSFORM_ORIGIN_X_TYPE";
239
+ StyleKeys[StyleKeys["TRANSFORM_ORIGIN_Y_TYPE"] = 599] = "TRANSFORM_ORIGIN_Y_TYPE";
240
+ StyleKeys[StyleKeys["TRANSFORM_ORIGIN_X_VALUE"] = 600] = "TRANSFORM_ORIGIN_X_VALUE";
241
+ StyleKeys[StyleKeys["TRANSFORM_ORIGIN_Y_VALUE"] = 604] = "TRANSFORM_ORIGIN_Y_VALUE";
242
+ StyleKeys[StyleKeys["TRANSFORM_ORIGIN_STATE"] = 608] = "TRANSFORM_ORIGIN_STATE";
243
+ // </style-keys:offsets>
226
244
  })(StyleKeys || (StyleKeys = {}));
227
245
  // Windows: the native Style.UpdateGrid takes all grid strings at once. To set one while preserving
228
246
  // the others, read the current values back from the Grid*Css getters. (Layout grid props like
@@ -313,20 +331,96 @@ function finite(n) {
313
331
  reportCssDiagnostic({ kind: 'unparsable-value', name: 'length', value: String(n), detail: 'not a finite number, treated as 0' });
314
332
  return 0;
315
333
  }
334
+ const EM_UNIT = /em/i;
316
335
  /** 1pt = 1/72in and 1 CSS px = 1/96in, so a point is 96/72 CSS px. */
317
336
  const PX_PER_PT = 96 / 72;
337
+ /** `n<unit>` in CSS px inside a math function, or undefined for an unknown unit. */
338
+ function unitToCssPx(n, unit, emBasis) {
339
+ switch (unit) {
340
+ case 'px':
341
+ case 'dip':
342
+ return n;
343
+ case 'dppx':
344
+ return layout.toDeviceIndependentPixels(n);
345
+ case 'rem':
346
+ return n * cssUnits.rootFontSize;
347
+ case 'em':
348
+ return n * (emBasis && emBasis > 0 ? emBasis : cssUnits.rootFontSize);
349
+ case 'pt':
350
+ return n * PX_PER_PT;
351
+ case 'vw':
352
+ case 'vh':
353
+ case 'vmin':
354
+ case 'vmax': {
355
+ const { viewportWidth: w, viewportHeight: h } = cssUnits;
356
+ if (w <= 0 || h <= 0) {
357
+ reportCssDiagnostic({ kind: 'unsupported-unit', name: 'length', value: `${n}${unit}`, detail: 'viewport size not known yet, resolved to 0' });
358
+ return 0;
359
+ }
360
+ const basis = unit === 'vw' ? w : unit === 'vh' ? h : unit === 'vmin' ? Math.min(w, h) : Math.max(w, h);
361
+ return (n / 100) * basis;
362
+ }
363
+ default:
364
+ return undefined;
365
+ }
366
+ }
367
+ /**
368
+ * A math function `font-size`: `em` is parent-relative like `%`, so `calc(1.5em)` is `150%`.
369
+ * Mixed with absolute lengths, `em` and `%` resolve against the root font size instead.
370
+ */
371
+ function fontSizeMath(value) {
372
+ const parentRelative = evaluateCssMath(value, (n, unit) => (unit === 'em' ? { percent: n * 100 } : unitToCssPx(n, unit)));
373
+ return parentRelative ?? evaluateCssMath(value, (n, unit) => (unit === '%' ? (n / 100) * cssUnits.rootFontSize : unitToCssPx(n, unit)));
374
+ }
375
+ /** A calc()/min()/max()/clamp() length, with `em` against `emBasis`. */
376
+ function evaluateLengthMath(value, emBasis) {
377
+ return evaluateCssMath(value, (n, unit) => unitToCssPx(n, unit, emBasis));
378
+ }
379
+ /** `value` with every resolvable math function replaced by its result, for the native parsers. */
380
+ export function resolveCssMathIn(value, emBasis) {
381
+ return substituteCssMath(value, (n, unit) => unitToCssPx(n, unit, emBasis));
382
+ }
383
+ /**
384
+ * A math function in a `<length-percentage>` as the buffer's type/value pair,
385
+ * or undefined when `t` isn't one or doesn't resolve.
386
+ */
387
+ function mathLengthPercentage(t, emBasis, lengthType, percentType) {
388
+ if (!isCssMathFunction(t)) {
389
+ return undefined;
390
+ }
391
+ const math = evaluateLengthMath(t, emBasis);
392
+ switch (math?.kind) {
393
+ case 'length':
394
+ return { type: lengthType, value: layout.toDevicePixels(finite(math.px)) };
395
+ case 'number':
396
+ return { type: lengthType, value: layout.toDevicePixels(finite(math.value)) };
397
+ case 'percent':
398
+ // percentages are stored as a [0.0, 1.0] fraction, not percentage points
399
+ return { type: percentType, value: finite(math.percent) / 100 };
400
+ default:
401
+ return undefined;
402
+ }
403
+ }
318
404
  /**
319
405
  * Resolve a CSS length token to the device pixels the geometry buffers store.
320
406
  *
321
- * `emBasis` is the font size `em` is relative to, in CSS px — the element's own
407
+ * `emBasis` is the font size `em` is relative to, in CSS px: the element's own
322
408
  * for a length, the parent's for `font-size` itself. It falls back to the root
323
409
  * font size when the element has no font size of its own yet.
324
410
  *
325
411
  * Suffix order matters: `rem` also ends with `em`, and `dppx` with `px`.
326
412
  */
327
413
  function tokenToDevicePx(token, raw, emBasis) {
328
- const n = finite(raw);
329
414
  const t = token.trim().toLowerCase();
415
+ if (isCssMathFunction(t)) {
416
+ const math = evaluateLengthMath(t, emBasis);
417
+ if (math?.kind === 'length')
418
+ return layout.toDevicePixels(finite(math.px));
419
+ // A bare number is a CSS px to Mason, as outside calc().
420
+ if (math?.kind === 'number')
421
+ return layout.toDevicePixels(finite(math.value));
422
+ }
423
+ const n = finite(raw);
330
424
  if (t.endsWith(DEVICE_PIXEL_UNIT))
331
425
  return n;
332
426
  if (t.endsWith('rem'))
@@ -348,13 +442,12 @@ function tokenToDevicePx(token, raw, emBasis) {
348
442
  return layout.toDevicePixels((n / 100) * cssUnits.viewportWidth);
349
443
  return layout.toDevicePixels((n / 100) * cssUnits.viewportHeight);
350
444
  }
351
- // A function value — calc(), clamp(), min(), max(), var() — leaves parseFloat
352
- // with nothing, so the declaration collapses to 0. Core evaluates calc() and
353
- // var() for stylesheet declarations before mason sees them, but an inline
354
- // style or a direct JSX assignment comes straight here.
445
+ // A function value Mason can't evaluate (var(), or math mixing % with a length)
446
+ // collapses to 0.
355
447
  const fn = /^([a-z-]+)\(/.exec(t);
356
448
  if (fn) {
357
- reportCssDiagnostic({ kind: 'not-implemented', name: 'length', value: t, detail: `${fn[1]}() is not evaluated here; it resolves only in a stylesheet, where core expands it first` });
449
+ const detail = isCssMathFunction(t) ? `${fn[1]}() mixes % with a length, or doesn't parse, so it can't be evaluated before layout` : `${fn[1]}() is not evaluated here; it resolves only in a stylesheet, where core expands it first`;
450
+ reportCssDiagnostic({ kind: 'not-implemented', name: 'length', value: t, detail });
358
451
  return 0;
359
452
  }
360
453
  // A trailing alphabetic run that isn't a unit we know is a silent
@@ -426,6 +519,15 @@ export function cssLengthToDip(value, emBasis) {
426
519
  if (t === '' || t === 'auto' || t.endsWith('%')) {
427
520
  return undefined;
428
521
  }
522
+ if (isCssMathFunction(t)) {
523
+ const math = evaluateLengthMath(t, emBasis);
524
+ if (math?.kind === 'length')
525
+ return math.px;
526
+ if (math?.kind === 'number')
527
+ return math.value;
528
+ // A percentage, or % mixed with a length, keeps its own path.
529
+ return undefined;
530
+ }
429
531
  // Sizing keywords and function values have their own handling.
430
532
  if (!/^[+-]?(\d+\.?\d*|\.\d+)(e[+-]?\d+)?[a-z]*$/.test(t)) {
431
533
  return undefined;
@@ -453,6 +555,10 @@ function dimensionFromString(value, emBasis) {
453
555
  case 'content':
454
556
  return { type: 9, value: 0 };
455
557
  }
558
+ const math = mathLengthPercentage(t, emBasis, 1, 2);
559
+ if (math) {
560
+ return math;
561
+ }
456
562
  if (t.startsWith('fit-content(') && t.endsWith(')')) {
457
563
  const limit = t.slice('fit-content('.length, -1).trim();
458
564
  if (limit.endsWith('%')) {
@@ -475,6 +581,10 @@ function lengthPercentageAutoFromString(value, emBasis) {
475
581
  if (t === '' || t === 'auto') {
476
582
  return { type: 0, value: 0 };
477
583
  }
584
+ const math = mathLengthPercentage(t, emBasis, 1, 2);
585
+ if (math) {
586
+ return math;
587
+ }
478
588
  if (t.endsWith('%')) {
479
589
  return { type: 2, value: finite(parseFloat(t) || 0) / 100 };
480
590
  }
@@ -484,6 +594,10 @@ function lengthPercentageAutoFromString(value, emBasis) {
484
594
  // Same as lengthPercentageAutoFromString but for padding (no "auto", type 0/1 swapped)
485
595
  function lengthPercentageFromString(value, emBasis) {
486
596
  const t = value.trim();
597
+ const math = mathLengthPercentage(t, emBasis, 0, 1);
598
+ if (math) {
599
+ return math;
600
+ }
487
601
  if (t.endsWith('%')) {
488
602
  return { type: 1, value: finite(parseFloat(t) || 0) / 100 };
489
603
  }
@@ -535,6 +649,7 @@ class StateKeys {
535
649
  }
536
650
  }
537
651
  StateKeys.NONE = new StateKeys(0, 0, 0, 0);
652
+ // <style-keys:state> generated from tools/scripts/style-keys/style-keys.schema, run `npm run gen:style-keys`
538
653
  StateKeys.DISPLAY = StateKeys.flag(0);
539
654
  StateKeys.POSITION = StateKeys.flag(1);
540
655
  StateKeys.DIRECTION = StateKeys.flag(2);
@@ -584,7 +699,7 @@ StateKeys.OBJECT_FIT = StateKeys.flag(45);
584
699
  StateKeys.Z_INDEX = StateKeys.flag(46);
585
700
  StateKeys.LIST_STYLE_POSITION = StateKeys.flag(47);
586
701
  StateKeys.LIST_STYLE_TYPE = StateKeys.flag(48);
587
- StateKeys.INVALIDATE_TEXT = StateKeys.flag(49);
702
+ StateKeys.INVALIDATE_TEXT = StateKeys.flag(49); // Kotlin's INVALIDATE_TEXT is every bit
588
703
  StateKeys.FONT_COLOR = StateKeys.flag(50);
589
704
  StateKeys.DECORATION_LINE = StateKeys.flag(51);
590
705
  StateKeys.DECORATION_COLOR = StateKeys.flag(52);
@@ -614,9 +729,13 @@ StateKeys.HYPHENS = StateKeys.flag(75);
614
729
  StateKeys.CARET_COLOR = StateKeys.flag(76);
615
730
  StateKeys.WORD_SPACING = StateKeys.flag(77);
616
731
  StateKeys.FONT_STRETCH = StateKeys.flag(78);
732
+ StateKeys.TEXT_INDENT = StateKeys.flag(79);
733
+ StateKeys.LINE_CLAMP = StateKeys.flag(80);
734
+ StateKeys.TRANSFORM_ORIGIN = StateKeys.flag(81);
735
+ // </style-keys:state>
617
736
  StateKeys.DISPLAY_AND_MODE = StateKeys.DISPLAY.or(StateKeys.DISPLAY_MODE);
618
737
  StateKeys.FLOW_TYPE = StateKeys.DISPLAY.or(StateKeys.DISPLAY_MODE).or(StateKeys.POSITION).or(StateKeys.FLOAT);
619
- StateKeys.WINDOWS_TEXT = [49, 50, 51, 52, 53, 54, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 73, 74, 75, 77, 78].reduce((mask, n) => mask.or(StateKeys.flag(n)), StateKeys.NONE);
738
+ StateKeys.WINDOWS_TEXT = [StateKeys.INVALIDATE_TEXT, StateKeys.FONT_COLOR, StateKeys.DECORATION_LINE, StateKeys.DECORATION_COLOR, StateKeys.TEXT_ALIGN, StateKeys.TEXT_JUSTIFY, StateKeys.FONT_SIZE, StateKeys.TEXT_TRANSFORM, StateKeys.FONT_STYLE, StateKeys.FONT_STYLE_SLANT, StateKeys.TEXT_WRAP, StateKeys.TEXT_OVERFLOW, StateKeys.DECORATION_STYLE, StateKeys.WHITE_SPACE, StateKeys.FONT_WEIGHT, StateKeys.LINE_HEIGHT, StateKeys.VERTICAL_ALIGN, StateKeys.DECORATION_THICKNESS, StateKeys.TEXT_SHADOWS, StateKeys.FONT_FAMILY, StateKeys.LETTER_SPACING, StateKeys.FONT_VARIANT_NUMERIC, StateKeys.WRITING_MODE, StateKeys.UNICODE_BIDI, StateKeys.HYPHENS, StateKeys.WORD_SPACING, StateKeys.FONT_STRETCH, StateKeys.TEXT_INDENT, StateKeys.LINE_CLAMP].reduce((mask, key) => mask.or(key), StateKeys.NONE);
620
739
  function wordsToBigInt(w0, w1, w2, w3) {
621
740
  return BigInt(w0 >>> 0) | (BigInt(w1 >>> 0) << 32n) | (BigInt(w2 >>> 0) << 64n) | (BigInt(w3 >>> 0) << 96n);
622
741
  }
@@ -707,6 +826,20 @@ function parseGradientAngle(token) {
707
826
  return 315;
708
827
  return 180; // CSS default is 'to bottom'
709
828
  }
829
+ function parseGradientCorner(token) {
830
+ const words = token.trim().toLowerCase().split(/\s+/);
831
+ if (words.length !== 3 || words[0] !== 'to')
832
+ return undefined;
833
+ let x = 0;
834
+ let y = 0;
835
+ for (const word of words.slice(1)) {
836
+ if (word === 'left' || word === 'right')
837
+ x = word === 'left' ? -1 : 1;
838
+ else if (word === 'top' || word === 'bottom')
839
+ y = word === 'top' ? -1 : 1;
840
+ }
841
+ return x && y ? [x, y] : undefined;
842
+ }
710
843
  const LINEAR_GRADIENT = /linear-gradient/i;
711
844
  const ANY_GRADIENT = /gradient/i;
712
845
  const LINEAR_GRADIENT_ARGS = /linear-gradient\s*\(([\s\S]*)\)\s*$/i;
@@ -722,6 +855,7 @@ export function parseLinearGradientCss(value) {
722
855
  if (parts.length < 2)
723
856
  return null;
724
857
  let angle = 180;
858
+ let corner;
725
859
  let start = 0;
726
860
  let head = parts[0];
727
861
  let interpolation = '';
@@ -733,6 +867,7 @@ export function parseLinearGradientCss(value) {
733
867
  }
734
868
  if (/^(to\s|-?[\d.]+(deg|rad|turn))/i.test(head)) {
735
869
  angle = parseGradientAngle(head);
870
+ corner = parseGradientCorner(head);
736
871
  start = 1;
737
872
  }
738
873
  const colors = [];
@@ -750,7 +885,7 @@ export function parseLinearGradientCss(value) {
750
885
  }
751
886
  if (colors.length < 1)
752
887
  return null;
753
- return { angle, offsets: resolveStopPositions(positions), colors, interpolation };
888
+ return { angle, ...(corner && { corner }), offsets: resolveStopPositions(positions), colors, interpolation };
754
889
  }
755
890
  const RADIAL_GRADIENT_ARGS = /radial-gradient\s*\(([\s\S]*)\)\s*$/i;
756
891
  function positionFraction(token, axis) {
@@ -1186,6 +1321,30 @@ function parseObjectPosition(value) {
1186
1321
  return null;
1187
1322
  return { xType: x.type, xVal: x.val, yType: y.type, yVal: y.val };
1188
1323
  }
1324
+ const ORIGIN_X_KEYWORDS = { left: 0, center: 50, right: 100 };
1325
+ const ORIGIN_Y_KEYWORDS = { top: 0, center: 50, bottom: 100 };
1326
+ /** `transform-origin` as two (type, value) pairs: type 0 is device px, 1 is percent. A z offset is ignored. */
1327
+ export function parseTransformOrigin(value, emBasis) {
1328
+ const parts = value.trim().toLowerCase().split(/\s+/).filter(Boolean);
1329
+ if (parts.length === 0 || parts.length > 3)
1330
+ return null;
1331
+ const length = (token) => {
1332
+ const n = parseFloat(token);
1333
+ if (!Number.isFinite(n))
1334
+ return null;
1335
+ return token.endsWith('%') ? { type: 1, value: n } : { type: 0, value: tokenToDevicePx(token, n, emBasis) };
1336
+ };
1337
+ let [first, second = 'center'] = parts;
1338
+ // Keywords may come in either order (`top left`); lengths are always x then y.
1339
+ if (ORIGIN_Y_KEYWORDS[first] !== undefined && first !== 'center' && (ORIGIN_X_KEYWORDS[second] !== undefined || parts.length === 1)) {
1340
+ [first, second] = [second, first];
1341
+ }
1342
+ const x = ORIGIN_X_KEYWORDS[first] !== undefined ? { type: 1, value: ORIGIN_X_KEYWORDS[first] } : length(first);
1343
+ const y = ORIGIN_Y_KEYWORDS[second] !== undefined ? { type: 1, value: ORIGIN_Y_KEYWORDS[second] } : length(second);
1344
+ if (!x || !y)
1345
+ return null;
1346
+ return { xType: x.type, x: x.value, yType: y.type, y: y.value };
1347
+ }
1189
1348
  const FONT_STRETCH_KEYWORDS = {
1190
1349
  'ultra-condensed': 5000,
1191
1350
  'extra-condensed': 6250,
@@ -1490,7 +1649,9 @@ export class Style {
1490
1649
  // whose parsers don't read modern colour syntax; see css-colors.ts.
1491
1650
  coerceCssStringValue(value) {
1492
1651
  if (typeof value === 'string') {
1493
- return resolveCssColors(value, cssColorToRgba);
1652
+ // Nor do they evaluate calc()/min()/max()/clamp(); see css-math.ts.
1653
+ const resolved = hasCssMathFunction(value) ? resolveCssMathIn(value, this.emBasisFor(value)) : value;
1654
+ return resolveCssColors(resolved, cssColorToRgba);
1494
1655
  }
1495
1656
  if (value == null) {
1496
1657
  return '';
@@ -1543,7 +1704,7 @@ export class Style {
1543
1704
  if (/^linear-gradient/i.test(layer)) {
1544
1705
  const g = parseLinearGradientCss(layer);
1545
1706
  if (g && g.colors.length)
1546
- NativeScript.Mason.Css.ApplyLinearGradient(this.nativeView, g.angle, stopsOf(g), g.interpolation);
1707
+ NativeScript.Mason.Css.ApplyLinearGradient(this.nativeView, g.angle, g.corner?.[0] ?? 0, g.corner?.[1] ?? 0, stopsOf(g), g.interpolation);
1547
1708
  return;
1548
1709
  }
1549
1710
  if (url) {
@@ -1579,7 +1740,7 @@ export class Style {
1579
1740
  }
1580
1741
  windowsMaskContext() {
1581
1742
  return {
1582
- length: (token) => cssLengthToDip(token, this.emBasis()),
1743
+ length: (token) => cssLengthToDip(token, this.emBasisFor(token)),
1583
1744
  color: (color) => normalizeColorValue(color),
1584
1745
  linear: parseLinearGradientCss,
1585
1746
  radial: parseRadialGradientCss,
@@ -1657,7 +1818,7 @@ export class Style {
1657
1818
  if (!view)
1658
1819
  return;
1659
1820
  const current = this.color >>> 0 || 0xff000000;
1660
- const spec = encodeCssFilter(parseCssFilter(typeof value === 'string' ? value : '', (token) => cssLengthToDip(token, this.emBasis())), (color) => (color && color.toLowerCase() !== 'currentcolor' ? (normalizeColorValue(color) ?? current) : current));
1821
+ const spec = encodeCssFilter(parseCssFilter(typeof value === 'string' ? value : '', (token) => cssLengthToDip(token, this.emBasisFor(token))), (color) => (color && color.toLowerCase() !== 'currentcolor' ? (normalizeColorValue(color) ?? current) : current));
1661
1822
  if (name === 'filter')
1662
1823
  NativeScript.Mason.Css.SetFilter(view, spec);
1663
1824
  else
@@ -1674,7 +1835,7 @@ export class Style {
1674
1835
  const view = this.nativeView;
1675
1836
  if (view && typeof view.SetTextShadow === 'function') {
1676
1837
  const current = this.color >>> 0 || 0xff000000;
1677
- const shadows = parseBoxShadows(typeof value === 'string' ? value : '', (token) => cssLengthToDip(token, this.emBasis()));
1838
+ const shadows = parseBoxShadows(typeof value === 'string' ? value : '', (token) => cssLengthToDip(token, this.emBasisFor(token)));
1678
1839
  view.SetTextShadow(encodeBoxShadows(shadows, (color) => (color && color.toLowerCase() !== 'currentcolor' ? (normalizeColorValue(color) ?? current) : current)));
1679
1840
  }
1680
1841
  return;
@@ -1698,7 +1859,7 @@ export class Style {
1698
1859
  return;
1699
1860
  }
1700
1861
  if (name === 'border-radius') {
1701
- const r = parseBorderRadiusShorthand(value, this.emBasis());
1862
+ const r = parseBorderRadiusShorthand(value, this.emBasisFor(value));
1702
1863
  this.prepareMut();
1703
1864
  for (const [corner, keys] of RADIUS_KEYS) {
1704
1865
  const { type, value: v } = r[corner];
@@ -1711,7 +1872,7 @@ export class Style {
1711
1872
  return;
1712
1873
  }
1713
1874
  if (name === 'border' || name === 'border-top' || name === 'border-right' || name === 'border-bottom' || name === 'border-left') {
1714
- const p = parseBorderShorthand(value, this.emBasis());
1875
+ const p = parseBorderShorthand(value, this.emBasisFor(value));
1715
1876
  let style = p.style;
1716
1877
  if (style == null && (p.width != null || p.color != null))
1717
1878
  style = 4; // solid
@@ -1758,7 +1919,7 @@ export class Style {
1758
1919
  let thickness = null;
1759
1920
  switch (name) {
1760
1921
  case 'text-decoration': {
1761
- const d = parseTextDecoration(value, (token) => cssLengthToDip(token, this.emBasis()));
1922
+ const d = parseTextDecoration(value, (token) => cssLengthToDip(token, this.emBasisFor(token)));
1762
1923
  if (!d)
1763
1924
  return;
1764
1925
  ({ line, style, color, thickness } = d);
@@ -1774,7 +1935,7 @@ export class Style {
1774
1935
  color = parseTextDecorationColor(value);
1775
1936
  break;
1776
1937
  case 'text-decoration-thickness':
1777
- thickness = cssLengthToDip(value, this.emBasis()) ?? null;
1938
+ thickness = cssLengthToDip(value, this.emBasisFor(value)) ?? null;
1778
1939
  break;
1779
1940
  }
1780
1941
  if (line == null && style == null && color == null && thickness == null)
@@ -1820,6 +1981,34 @@ export class Style {
1820
1981
  this.writeBorderSide(side, null, null, color);
1821
1982
  this.commitState(StateKeys.BORDER_COLOR);
1822
1983
  }
1984
+ // Per-side colors by name, as pseudo-class styles and the border shorthands assign them.
1985
+ borderSideColor(key) {
1986
+ return new Color(getUint32(this.style_view, key)).hex;
1987
+ }
1988
+ get borderTopColor() {
1989
+ return this.borderSideColor(StyleKeys.BORDER_TOP_COLOR);
1990
+ }
1991
+ set borderTopColor(value) {
1992
+ this.setBorderSideColor('top', value);
1993
+ }
1994
+ get borderRightColor() {
1995
+ return this.borderSideColor(StyleKeys.BORDER_RIGHT_COLOR);
1996
+ }
1997
+ set borderRightColor(value) {
1998
+ this.setBorderSideColor('right', value);
1999
+ }
2000
+ get borderBottomColor() {
2001
+ return this.borderSideColor(StyleKeys.BORDER_BOTTOM_COLOR);
2002
+ }
2003
+ set borderBottomColor(value) {
2004
+ this.setBorderSideColor('bottom', value);
2005
+ }
2006
+ get borderLeftColor() {
2007
+ return this.borderSideColor(StyleKeys.BORDER_LEFT_COLOR);
2008
+ }
2009
+ set borderLeftColor(value) {
2010
+ this.setBorderSideColor('left', value);
2011
+ }
1823
2012
  resetState() {
1824
2013
  this._hasDirty = false;
1825
2014
  this._d0 = this._d1 = this._d2 = this._d3 = 0;
@@ -1875,7 +2064,7 @@ export class Style {
1875
2064
  const target = new Uint8Array(NSWinRT.interop.arrayBufferFromBuffer(masonEngine().StyleValues(text)));
1876
2065
  target.set(this.u8View.subarray(StyleKeys.FONT_COLOR, StyleKeys.BACKGROUND_COLOR), StyleKeys.FONT_COLOR);
1877
2066
  target.set(this.u8View.subarray(StyleKeys.DECORATION_LINE, StyleKeys.PSEUDO_SET_MASK_LOW), StyleKeys.DECORATION_LINE);
1878
- target.set(this.u8View.subarray(StyleKeys.HYPHENS, StyleKeys.FONT_STRETCH_STATE + 1), StyleKeys.HYPHENS);
2067
+ target.set(this.u8View.subarray(StyleKeys.HYPHENS, StyleKeys.LINE_CLAMP_STATE + 1), StyleKeys.HYPHENS);
1879
2068
  if (block) {
1880
2069
  target[StyleKeys.DISPLAY] = 3;
1881
2070
  target[StyleKeys.DISPLAY_MODE] = 0 /* DisplayMode.None */;
@@ -1918,19 +2107,35 @@ export class Style {
1918
2107
  return this.view_;
1919
2108
  }
1920
2109
  /**
1921
- * The font size `em` resolves against, in CSS px: the element's own when it has
1922
- * one, otherwise the root font size. FONT_SIZE is stored in dip, which is the
1923
- * same as a CSS px, so no conversion is needed.
2110
+ * The font size `em` resolves against, in CSS px: the element's computed one,
2111
+ * with a percentage of the parent's, or the inherited one when unset.
1924
2112
  */
1925
2113
  emBasis() {
1926
- if (!this.style_view) {
1927
- return cssUnits.rootFontSize;
1928
- }
1929
- if (getInt8(this.style_view, StyleKeys.FONT_SIZE_STATE) !== 1) {
1930
- return cssUnits.rootFontSize;
2114
+ if (!this.style_view || getInt8(this.style_view, StyleKeys.FONT_SIZE_STATE) !== 1) {
2115
+ return this.parentEmBasis();
1931
2116
  }
1932
2117
  const own = getInt32(this.style_view, StyleKeys.FONT_SIZE);
1933
- return own > 0 ? own : cssUnits.rootFontSize;
2118
+ if (own <= 0) {
2119
+ return this.parentEmBasis();
2120
+ }
2121
+ // `0.75em` is stored as 75 (%), not 75px.
2122
+ return getInt8(this.style_view, StyleKeys.FONT_SIZE_TYPE) === 1 ? (own / 100) * this.parentEmBasis() : own;
2123
+ }
2124
+ emBasisFor(value) {
2125
+ return typeof value === 'string' && EM_UNIT.test(value) ? this.emBasis() : undefined;
2126
+ }
2127
+ /** The nearest existing Mason ancestor style's font size (its own for a pseudo-class style). */
2128
+ parentEmBasis() {
2129
+ const view = this.view_;
2130
+ let ancestor = (this._pseudo ? view : view?.parent);
2131
+ for (let depth = 0; ancestor && depth < 256; depth++) {
2132
+ const style = ancestor[style_];
2133
+ if (style instanceof Style && style !== this) {
2134
+ return style.emBasis();
2135
+ }
2136
+ ancestor = ancestor.parent;
2137
+ }
2138
+ return cssUnits.rootFontSize;
1934
2139
  }
1935
2140
  prepareMut() {
1936
2141
  if (this._pseudo) {
@@ -2061,7 +2266,12 @@ export class Style {
2061
2266
  this.prepareMut();
2062
2267
  let resolved = n;
2063
2268
  let isPercent = false;
2064
- if (t.endsWith('%')) {
2269
+ const math = isCssMathFunction(t) ? fontSizeMath(t) : undefined;
2270
+ if (math) {
2271
+ isPercent = math.kind === 'percent';
2272
+ resolved = finite(math.kind === 'length' ? math.px : math.kind === 'percent' ? math.percent : math.value);
2273
+ }
2274
+ else if (t.endsWith('%')) {
2065
2275
  isPercent = true;
2066
2276
  }
2067
2277
  else if (t.endsWith('rem')) {
@@ -2511,7 +2721,7 @@ export class Style {
2511
2721
  switch (typeof value) {
2512
2722
  case 'string': {
2513
2723
  this.prepareMut();
2514
- const dim = lengthPercentageAutoFromString(value, this.emBasis());
2724
+ const dim = lengthPercentageAutoFromString(value, this.emBasisFor(value));
2515
2725
  setInt8(this.style_view, StyleKeys.MIN_WIDTH_TYPE, dim.type);
2516
2726
  setFloat32(this.style_view, StyleKeys.MIN_WIDTH_VALUE, dim.value);
2517
2727
  break;
@@ -2556,7 +2766,7 @@ export class Style {
2556
2766
  switch (typeof value) {
2557
2767
  case 'string': {
2558
2768
  this.prepareMut();
2559
- const dim = lengthPercentageAutoFromString(value, this.emBasis());
2769
+ const dim = lengthPercentageAutoFromString(value, this.emBasisFor(value));
2560
2770
  setInt8(this.style_view, StyleKeys.MIN_HEIGHT_TYPE, dim.type);
2561
2771
  setFloat32(this.style_view, StyleKeys.MIN_HEIGHT_VALUE, dim.value);
2562
2772
  break;
@@ -2601,7 +2811,7 @@ export class Style {
2601
2811
  switch (typeof value) {
2602
2812
  case 'string': {
2603
2813
  this.prepareMut();
2604
- const dim = dimensionFromString(value, this.emBasis());
2814
+ const dim = dimensionFromString(value, this.emBasisFor(value));
2605
2815
  setInt8(this.style_view, StyleKeys.WIDTH_TYPE, dim.type);
2606
2816
  setFloat32(this.style_view, StyleKeys.WIDTH_VALUE, dim.value);
2607
2817
  break;
@@ -2646,7 +2856,7 @@ export class Style {
2646
2856
  switch (typeof value) {
2647
2857
  case 'string': {
2648
2858
  this.prepareMut();
2649
- const dim = dimensionFromString(value, this.emBasis());
2859
+ const dim = dimensionFromString(value, this.emBasisFor(value));
2650
2860
  setInt8(this.style_view, StyleKeys.HEIGHT_TYPE, dim.type);
2651
2861
  setFloat32(this.style_view, StyleKeys.HEIGHT_VALUE, dim.value);
2652
2862
  break;
@@ -2691,7 +2901,7 @@ export class Style {
2691
2901
  switch (typeof value) {
2692
2902
  case 'string': {
2693
2903
  this.prepareMut();
2694
- const dim = lengthPercentageAutoFromString(value, this.emBasis());
2904
+ const dim = lengthPercentageAutoFromString(value, this.emBasisFor(value));
2695
2905
  setInt8(this.style_view, StyleKeys.MAX_WIDTH_TYPE, dim.type);
2696
2906
  setFloat32(this.style_view, StyleKeys.MAX_WIDTH_VALUE, dim.value);
2697
2907
  break;
@@ -2736,7 +2946,7 @@ export class Style {
2736
2946
  switch (typeof value) {
2737
2947
  case 'string': {
2738
2948
  this.prepareMut();
2739
- const dim = lengthPercentageAutoFromString(value, this.emBasis());
2949
+ const dim = lengthPercentageAutoFromString(value, this.emBasisFor(value));
2740
2950
  setInt8(this.style_view, StyleKeys.MAX_HEIGHT_TYPE, dim.type);
2741
2951
  setFloat32(this.style_view, StyleKeys.MAX_HEIGHT_VALUE, dim.value);
2742
2952
  break;
@@ -2781,7 +2991,7 @@ export class Style {
2781
2991
  switch (typeof value) {
2782
2992
  case 'string': {
2783
2993
  this.prepareMut();
2784
- const dim = lengthPercentageFromString(value, this.emBasis());
2994
+ const dim = lengthPercentageFromString(value, this.emBasisFor(value));
2785
2995
  setInt8(this.style_view, StyleKeys.BORDER_LEFT_TYPE, dim.type);
2786
2996
  setFloat32(this.style_view, StyleKeys.BORDER_LEFT_VALUE, dim.value);
2787
2997
  break;
@@ -2822,7 +3032,7 @@ export class Style {
2822
3032
  switch (typeof value) {
2823
3033
  case 'string': {
2824
3034
  this.prepareMut();
2825
- const dim = lengthPercentageFromString(value, this.emBasis());
3035
+ const dim = lengthPercentageFromString(value, this.emBasisFor(value));
2826
3036
  setInt8(this.style_view, StyleKeys.BORDER_RIGHT_TYPE, dim.type);
2827
3037
  setFloat32(this.style_view, StyleKeys.BORDER_RIGHT_VALUE, dim.value);
2828
3038
  break;
@@ -2863,7 +3073,7 @@ export class Style {
2863
3073
  switch (typeof value) {
2864
3074
  case 'string': {
2865
3075
  this.prepareMut();
2866
- const dim = lengthPercentageFromString(value, this.emBasis());
3076
+ const dim = lengthPercentageFromString(value, this.emBasisFor(value));
2867
3077
  setInt8(this.style_view, StyleKeys.BORDER_TOP_TYPE, dim.type);
2868
3078
  setFloat32(this.style_view, StyleKeys.BORDER_TOP_VALUE, dim.value);
2869
3079
  break;
@@ -2904,7 +3114,7 @@ export class Style {
2904
3114
  switch (typeof value) {
2905
3115
  case 'string': {
2906
3116
  this.prepareMut();
2907
- const dim = lengthPercentageFromString(value, this.emBasis());
3117
+ const dim = lengthPercentageFromString(value, this.emBasisFor(value));
2908
3118
  setInt8(this.style_view, StyleKeys.BORDER_BOTTOM_TYPE, dim.type);
2909
3119
  setFloat32(this.style_view, StyleKeys.BORDER_BOTTOM_VALUE, dim.value);
2910
3120
  break;
@@ -2955,7 +3165,7 @@ export class Style {
2955
3165
  var insetValue;
2956
3166
  switch (typeof value) {
2957
3167
  case 'string': {
2958
- const dim = lengthPercentageAutoFromString(value, this.emBasis());
3168
+ const dim = lengthPercentageAutoFromString(value, this.emBasisFor(value));
2959
3169
  type = dim.type;
2960
3170
  insetValue = dim.value;
2961
3171
  break;
@@ -2999,7 +3209,7 @@ export class Style {
2999
3209
  switch (typeof value) {
3000
3210
  case 'string': {
3001
3211
  this.prepareMut();
3002
- const dim = lengthPercentageAutoFromString(value, this.emBasis());
3212
+ const dim = lengthPercentageAutoFromString(value, this.emBasisFor(value));
3003
3213
  setInt8(this.style_view, StyleKeys.INSET_LEFT_TYPE, dim.type);
3004
3214
  setFloat32(this.style_view, StyleKeys.INSET_LEFT_VALUE, dim.value);
3005
3215
  break;
@@ -3040,7 +3250,7 @@ export class Style {
3040
3250
  switch (typeof value) {
3041
3251
  case 'string': {
3042
3252
  this.prepareMut();
3043
- const dim = lengthPercentageAutoFromString(value, this.emBasis());
3253
+ const dim = lengthPercentageAutoFromString(value, this.emBasisFor(value));
3044
3254
  setInt8(this.style_view, StyleKeys.INSET_RIGHT_TYPE, dim.type);
3045
3255
  setFloat32(this.style_view, StyleKeys.INSET_RIGHT_VALUE, dim.value);
3046
3256
  break;
@@ -3081,7 +3291,7 @@ export class Style {
3081
3291
  switch (typeof value) {
3082
3292
  case 'string': {
3083
3293
  this.prepareMut();
3084
- const dim = lengthPercentageAutoFromString(value, this.emBasis());
3294
+ const dim = lengthPercentageAutoFromString(value, this.emBasisFor(value));
3085
3295
  setInt8(this.style_view, StyleKeys.INSET_TOP_TYPE, dim.type);
3086
3296
  setFloat32(this.style_view, StyleKeys.INSET_TOP_VALUE, dim.value);
3087
3297
  break;
@@ -3122,7 +3332,7 @@ export class Style {
3122
3332
  switch (typeof value) {
3123
3333
  case 'string': {
3124
3334
  this.prepareMut();
3125
- const dim = lengthPercentageAutoFromString(value, this.emBasis());
3335
+ const dim = lengthPercentageAutoFromString(value, this.emBasisFor(value));
3126
3336
  setInt8(this.style_view, StyleKeys.INSET_BOTTOM_TYPE, dim.type);
3127
3337
  setFloat32(this.style_view, StyleKeys.INSET_BOTTOM_VALUE, dim.value);
3128
3338
  break;
@@ -3173,7 +3383,7 @@ export class Style {
3173
3383
  var marginValue;
3174
3384
  switch (typeof value) {
3175
3385
  case 'string': {
3176
- const dim = lengthPercentageAutoFromString(value, this.emBasis());
3386
+ const dim = lengthPercentageAutoFromString(value, this.emBasisFor(value));
3177
3387
  type = dim.type;
3178
3388
  marginValue = dim.value;
3179
3389
  break;
@@ -3217,7 +3427,7 @@ export class Style {
3217
3427
  switch (typeof value) {
3218
3428
  case 'string': {
3219
3429
  this.prepareMut();
3220
- const dim = lengthPercentageAutoFromString(value, this.emBasis());
3430
+ const dim = lengthPercentageAutoFromString(value, this.emBasisFor(value));
3221
3431
  setInt8(this.style_view, StyleKeys.MARGIN_LEFT_TYPE, dim.type);
3222
3432
  setFloat32(this.style_view, StyleKeys.MARGIN_LEFT_VALUE, dim.value);
3223
3433
  break;
@@ -3258,7 +3468,7 @@ export class Style {
3258
3468
  switch (typeof value) {
3259
3469
  case 'string': {
3260
3470
  this.prepareMut();
3261
- const dim = lengthPercentageAutoFromString(value, this.emBasis());
3471
+ const dim = lengthPercentageAutoFromString(value, this.emBasisFor(value));
3262
3472
  setInt8(this.style_view, StyleKeys.MARGIN_RIGHT_TYPE, dim.type);
3263
3473
  setFloat32(this.style_view, StyleKeys.MARGIN_RIGHT_VALUE, dim.value);
3264
3474
  break;
@@ -3299,7 +3509,7 @@ export class Style {
3299
3509
  switch (typeof value) {
3300
3510
  case 'string': {
3301
3511
  this.prepareMut();
3302
- const dim = lengthPercentageAutoFromString(value, this.emBasis());
3512
+ const dim = lengthPercentageAutoFromString(value, this.emBasisFor(value));
3303
3513
  setInt8(this.style_view, StyleKeys.MARGIN_TOP_TYPE, dim.type);
3304
3514
  setFloat32(this.style_view, StyleKeys.MARGIN_TOP_VALUE, dim.value);
3305
3515
  break;
@@ -3340,7 +3550,7 @@ export class Style {
3340
3550
  switch (typeof value) {
3341
3551
  case 'string': {
3342
3552
  this.prepareMut();
3343
- const dim = lengthPercentageAutoFromString(value, this.emBasis());
3553
+ const dim = lengthPercentageAutoFromString(value, this.emBasisFor(value));
3344
3554
  setInt8(this.style_view, StyleKeys.MARGIN_BOTTOM_TYPE, dim.type);
3345
3555
  setFloat32(this.style_view, StyleKeys.MARGIN_BOTTOM_VALUE, dim.value);
3346
3556
  break;
@@ -3402,7 +3612,7 @@ export class Style {
3402
3612
  switch (typeof value) {
3403
3613
  case 'string': {
3404
3614
  this.prepareMut();
3405
- const dim = lengthPercentageFromString(value, this.emBasis());
3615
+ const dim = lengthPercentageFromString(value, this.emBasisFor(value));
3406
3616
  setInt8(this.style_view, StyleKeys.PADDING_LEFT_TYPE, dim.type);
3407
3617
  setFloat32(this.style_view, StyleKeys.PADDING_LEFT_VALUE, dim.value);
3408
3618
  break;
@@ -3443,7 +3653,7 @@ export class Style {
3443
3653
  switch (typeof value) {
3444
3654
  case 'string': {
3445
3655
  this.prepareMut();
3446
- const dim = lengthPercentageFromString(value, this.emBasis());
3656
+ const dim = lengthPercentageFromString(value, this.emBasisFor(value));
3447
3657
  setInt8(this.style_view, StyleKeys.PADDING_RIGHT_TYPE, dim.type);
3448
3658
  setFloat32(this.style_view, StyleKeys.PADDING_RIGHT_VALUE, dim.value);
3449
3659
  break;
@@ -3484,7 +3694,7 @@ export class Style {
3484
3694
  switch (typeof value) {
3485
3695
  case 'string': {
3486
3696
  this.prepareMut();
3487
- const dim = lengthPercentageFromString(value, this.emBasis());
3697
+ const dim = lengthPercentageFromString(value, this.emBasisFor(value));
3488
3698
  setInt8(this.style_view, StyleKeys.PADDING_TOP_TYPE, dim.type);
3489
3699
  setFloat32(this.style_view, StyleKeys.PADDING_TOP_VALUE, dim.value);
3490
3700
  break;
@@ -3525,7 +3735,7 @@ export class Style {
3525
3735
  switch (typeof value) {
3526
3736
  case 'string': {
3527
3737
  this.prepareMut();
3528
- const dim = lengthPercentageFromString(value, this.emBasis());
3738
+ const dim = lengthPercentageFromString(value, this.emBasisFor(value));
3529
3739
  setInt8(this.style_view, StyleKeys.PADDING_BOTTOM_TYPE, dim.type);
3530
3740
  setFloat32(this.style_view, StyleKeys.PADDING_BOTTOM_VALUE, dim.value);
3531
3741
  break;
@@ -3598,7 +3808,7 @@ export class Style {
3598
3808
  switch (typeof value) {
3599
3809
  case 'string': {
3600
3810
  this.prepareMut();
3601
- const dim = lengthPercentageFromString(value, this.emBasis());
3811
+ const dim = lengthPercentageFromString(value, this.emBasisFor(value));
3602
3812
  setInt8(this.style_view, StyleKeys.GAP_ROW_TYPE, dim.type);
3603
3813
  setFloat32(this.style_view, StyleKeys.GAP_ROW_VALUE, dim.value);
3604
3814
  break;
@@ -3639,7 +3849,7 @@ export class Style {
3639
3849
  switch (typeof value) {
3640
3850
  case 'string': {
3641
3851
  this.prepareMut();
3642
- const dim = lengthPercentageFromString(value, this.emBasis());
3852
+ const dim = lengthPercentageFromString(value, this.emBasisFor(value));
3643
3853
  setInt8(this.style_view, StyleKeys.GAP_COLUMN_TYPE, dim.type);
3644
3854
  setFloat32(this.style_view, StyleKeys.GAP_COLUMN_VALUE, dim.value);
3645
3855
  break;
@@ -3696,7 +3906,7 @@ export class Style {
3696
3906
  switch (typeof value) {
3697
3907
  case 'string': {
3698
3908
  this.prepareMut();
3699
- const dim = dimensionFromString(value, this.emBasis());
3909
+ const dim = dimensionFromString(value, this.emBasisFor(value));
3700
3910
  setInt8(this.style_view, StyleKeys.FLEX_BASIS_TYPE, dim.type);
3701
3911
  setFloat32(this.style_view, StyleKeys.FLEX_BASIS_VALUE, dim.value);
3702
3912
  break;
@@ -5091,7 +5301,7 @@ export class Style {
5091
5301
  /** Read by TextNode when stroking the decoration line. */
5092
5302
  set textDecorationThickness(value) {
5093
5303
  this.prepareMut();
5094
- const devicePx = typeof value === 'number' ? layout.toDevicePixels(finite(value)) : tokenToDevicePx(value, parseFloat(value) || 0, this.emBasis());
5304
+ const devicePx = typeof value === 'number' ? layout.toDevicePixels(finite(value)) : tokenToDevicePx(value, parseFloat(value) || 0, this.emBasisFor(value));
5095
5305
  setFloat32(this.style_view, StyleKeys.DECORATION_THICKNESS, devicePx);
5096
5306
  setInt8(this.style_view, StyleKeys.DECORATION_THICKNESS_STATE, 1);
5097
5307
  this.commitState(StateKeys.DECORATION_THICKNESS);
@@ -5927,6 +6137,109 @@ export class Style {
5927
6137
  setUint8(this.style_view, StyleKeys.HYPHENS_STATE, 1);
5928
6138
  this.commitState(StateKeys.HYPHENS);
5929
6139
  }
6140
+ get textIndent() {
6141
+ if (!this.style_view || !getUint8(this.style_view, StyleKeys.TEXT_INDENT_STATE))
6142
+ return '0px';
6143
+ const value = getFloat32(this.style_view, StyleKeys.TEXT_INDENT);
6144
+ return getUint8(this.style_view, StyleKeys.TEXT_INDENT_TYPE) === 1 ? `${value}%` : `${layout.toDeviceIndependentPixels(value)}px`;
6145
+ }
6146
+ set textIndent(value) {
6147
+ if (!this.style_view)
6148
+ return;
6149
+ let type = 0;
6150
+ let indent;
6151
+ if (typeof value === 'number') {
6152
+ indent = layout.toDevicePixels(value);
6153
+ }
6154
+ else {
6155
+ // `each-line` and `hanging` are not supported; the length still applies.
6156
+ const token = String(value ?? '')
6157
+ .trim()
6158
+ .split(/\s+/)
6159
+ .find((t) => t !== 'each-line' && t !== 'hanging');
6160
+ if (!token)
6161
+ return;
6162
+ const n = parseFloat(token);
6163
+ if (!Number.isFinite(n))
6164
+ return;
6165
+ if (token.endsWith('%')) {
6166
+ type = 1;
6167
+ indent = n;
6168
+ }
6169
+ else {
6170
+ indent = tokenToDevicePx(token, n, this.emBasisFor(token));
6171
+ }
6172
+ }
6173
+ this.prepareMut();
6174
+ setFloat32(this.style_view, StyleKeys.TEXT_INDENT, indent);
6175
+ setUint8(this.style_view, StyleKeys.TEXT_INDENT_TYPE, type);
6176
+ setUint8(this.style_view, StyleKeys.TEXT_INDENT_STATE, 1);
6177
+ this.commitState(StateKeys.TEXT_INDENT);
6178
+ }
6179
+ set 'text-indent'(value) {
6180
+ this.textIndent = value;
6181
+ }
6182
+ get 'text-indent'() {
6183
+ return this.textIndent;
6184
+ }
6185
+ get lineClamp() {
6186
+ if (!this.style_view || !getUint8(this.style_view, StyleKeys.LINE_CLAMP_STATE))
6187
+ return 'none';
6188
+ const lines = getInt32(this.style_view, StyleKeys.LINE_CLAMP);
6189
+ return lines > 0 ? lines : 'none';
6190
+ }
6191
+ set lineClamp(value) {
6192
+ if (!this.style_view)
6193
+ return;
6194
+ const lines = value === 'none' ? 0 : Math.trunc(Number(value));
6195
+ if (!Number.isFinite(lines) || lines < 0)
6196
+ return;
6197
+ this.prepareMut();
6198
+ setInt32(this.style_view, StyleKeys.LINE_CLAMP, lines);
6199
+ setUint8(this.style_view, StyleKeys.LINE_CLAMP_STATE, 1);
6200
+ this.commitState(StateKeys.LINE_CLAMP);
6201
+ }
6202
+ set 'line-clamp'(value) {
6203
+ this.lineClamp = value;
6204
+ }
6205
+ get 'line-clamp'() {
6206
+ return this.lineClamp;
6207
+ }
6208
+ set '-webkit-line-clamp'(value) {
6209
+ this.lineClamp = value;
6210
+ }
6211
+ get '-webkit-line-clamp'() {
6212
+ return this.lineClamp;
6213
+ }
6214
+ get transformOrigin() {
6215
+ if (!this.style_view || !getUint8(this.style_view, StyleKeys.TRANSFORM_ORIGIN_STATE))
6216
+ return '50% 50%';
6217
+ const side = (typeKey, valueKey) => {
6218
+ const value = getFloat32(this.style_view, valueKey);
6219
+ return getUint8(this.style_view, typeKey) === 1 ? `${value}%` : `${layout.toDeviceIndependentPixels(value)}px`;
6220
+ };
6221
+ return `${side(StyleKeys.TRANSFORM_ORIGIN_X_TYPE, StyleKeys.TRANSFORM_ORIGIN_X_VALUE)} ${side(StyleKeys.TRANSFORM_ORIGIN_Y_TYPE, StyleKeys.TRANSFORM_ORIGIN_Y_VALUE)}`;
6222
+ }
6223
+ set transformOrigin(value) {
6224
+ if (!this.style_view)
6225
+ return;
6226
+ const parsed = parseTransformOrigin(String(value ?? ''), this.emBasisFor(value));
6227
+ if (!parsed)
6228
+ return;
6229
+ this.prepareMut();
6230
+ setUint8(this.style_view, StyleKeys.TRANSFORM_ORIGIN_X_TYPE, parsed.xType);
6231
+ setUint8(this.style_view, StyleKeys.TRANSFORM_ORIGIN_Y_TYPE, parsed.yType);
6232
+ setFloat32(this.style_view, StyleKeys.TRANSFORM_ORIGIN_X_VALUE, parsed.x);
6233
+ setFloat32(this.style_view, StyleKeys.TRANSFORM_ORIGIN_Y_VALUE, parsed.y);
6234
+ setUint8(this.style_view, StyleKeys.TRANSFORM_ORIGIN_STATE, 1);
6235
+ this.commitState(StateKeys.TRANSFORM_ORIGIN);
6236
+ }
6237
+ set 'transform-origin'(value) {
6238
+ this.transformOrigin = value;
6239
+ }
6240
+ get 'transform-origin'() {
6241
+ return this.transformOrigin;
6242
+ }
5930
6243
  get backdropFilter() {
5931
6244
  if (!this.nativeView)
5932
6245
  return '';
@@ -6463,6 +6776,9 @@ export class Style {
6463
6776
  caretColor: this.caretColor,
6464
6777
  wordSpacing: this.wordSpacing,
6465
6778
  fontStretch: this.fontStretch,
6779
+ textIndent: this.textIndent,
6780
+ lineClamp: this.lineClamp,
6781
+ transformOrigin: this.transformOrigin,
6466
6782
  };
6467
6783
  }
6468
6784
  }