@octane-xplat/patches 0.16.1 → 0.17.1

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.
@@ -1,16 +1,45 @@
1
+ diff --git a/dist/driver.d.ts b/dist/driver.d.ts
2
+ index 80e30299c44e889e3a066ce2c9a8feb6b7ceaa42..b17dc120c5c00ba4be701233fb95b6d2b3eb72d9 100644
3
+ --- a/dist/driver.d.ts
4
+ +++ b/dist/driver.d.ts
5
+ @@ -14,6 +14,9 @@ interface HostNode {
6
+ readonly muted: Set<string>;
7
+ /** Whether this node's `text` has been driven by `#text` children. */
8
+ textApplied: boolean;
9
+ + readonly propDefaults: Map<string, unknown>;
10
+ + readonly styleDefaults: Map<string, unknown>;
11
+ + styleKeys: Set<string>;
12
+ }
13
+ /**
14
+ * A command `parent`: a host node id's node, the root container (`null`), or a
1
15
  diff --git a/dist/driver.js b/dist/driver.js
2
- index 8bc563a..4cb1f95 100644
16
+ index 8bc563add4cb675a12651184b63805b10224274d..ca251841c91abdd8c254fb6307e5af09f0707c0f 100644
3
17
  --- a/dist/driver.js
4
18
  +++ b/dist/driver.js
5
- @@ -3,6 +3,7 @@ import { createUniversalRoot, } from 'octane/universal/native';
19
+ @@ -1,8 +1,11 @@
20
+ -import { ActionBar, ContentView, FormattedString, LayoutBase, ListView, Span, TabView, TabViewItem, TextBase, View, unsetValue, } from '@nativescript/core';
21
+ +import { cssTreeParse } from '@nativescript/core/css/css-tree-parser';
22
+ +import { _getProperties, _getStyleProperties, } from '@nativescript/core/ui/core/properties';
23
+ +import { ActionBar, ContentView, FormattedString, LayoutBase, ListView, Span, TabView, TabViewItem, TextBase, View, Utils, unsetValue, } from '@nativescript/core';
24
+ import { createUniversalRoot, } from 'octane/universal/native';
6
25
  import { NATIVESCRIPT_RENDERER_ID } from './config.js';
7
26
  import { ELEMENTS, EVENT_PROP, eventNameFor, onElementReplaced, } from './elements.js';
8
27
  import { releaseListView, setRenderItem, } from './list-view.js';
9
- +import { Style as MasonStyle } from '@triniwiz/nativescript-masonkit/style';
28
+ +import { Style as MasonStyle } from '@octane-xplat/nativescript-masonkit/style';
10
29
  /** `#text` nodes carry no view; the driver folds them into the parent's `text`. */
11
30
  const TEXT = '#text';
12
31
  /**
13
- @@ -73,7 +74,10 @@ function instantiate(type) {
32
+ @@ -62,6 +65,9 @@ function createNode(container, id, type, props) {
33
+ listeners: new Map(),
34
+ muted: new Set(),
35
+ textApplied: false,
36
+ + propDefaults: new Map(),
37
+ + styleDefaults: new Map(),
38
+ + styleKeys: new Set(),
39
+ };
40
+ if (node.view !== null)
41
+ viewOwners.set(node.view, { container, node });
42
+ @@ -73,7 +79,10 @@ function instantiate(type) {
14
43
  if (Element === undefined) {
15
44
  throw new Error(`NativeScript driver: <${type}> is not a registered element.`);
16
45
  }
@@ -22,35 +51,46 @@ index 8bc563a..4cb1f95 100644
22
51
  }
23
52
  function applyProps(node, props) {
24
53
  if (node.view === null) {
25
- @@ -91,13 +95,23 @@ function applyProps(node, props) {
54
+ @@ -90,14 +99,16 @@ function applyProps(node, props) {
55
+ setProp(node, node.view, name, props[name]);
26
56
  }
27
57
  }
28
- /**
58
+ -/**
29
59
  - * `update` carries the host's dynamic-prop snapshot; attributes that were
30
60
  - * static in the compiled plan arrive only with `create`. Merge instead of
31
61
  - * replace, or a first update would strip a node's static layout props.
32
- + * `update` carries the host's complete prop bag: a key present in the last
33
- + * applied bag but absent from the next is a removal, and the view state it
34
- + * installed returns to the prop's documented default. `undefined` never
35
- + * crosses the wire (the runtime elides it); `null` is "not supplied" for
36
- + * props without an explicit nullable contract — both reset through setProp.
37
- */
62
+ - */
63
+ +/** `update` is the complete next bag, including the plan's static props. */
38
64
  function updateProps(node, next) {
39
- + const dropped = [];
40
- + for (const name in node.props) {
41
- + if (node.props[name] != null && !(name in next))
42
- + dropped.push(name);
65
+ + for (const name of Object.keys(node.props)) {
66
+ + if (!Object.hasOwn(next, name) && node.view !== null)
67
+ + setProp(node, node.view, name, undefined);
43
68
  + }
44
69
  applyProps(node, next);
45
70
  - node.props = { ...node.props, ...next };
46
- + for (const name of dropped) {
47
- + resetProp(node, node.view, name);
48
- + }
49
71
  + node.props = next;
72
+ + if (node.parent !== null && MASON_CONTAINER_TAGS.has(node.parent.type))
73
+ + applyMasonChildLayout(node, node.parent);
50
74
  }
51
75
  function setProp(node, view, name, value) {
52
76
  if (name === 'children' || name === 'key' || name === 'ref')
53
- @@ -113,13 +127,36 @@ function setProp(node, view, name, value) {
77
+ @@ -105,6 +116,15 @@ function setProp(node, view, name, value) {
78
+ // Event props arrive as their own `event` commands.
79
+ if (EVENT_PROP.test(name))
80
+ return;
81
+ + if (value === undefined &&
82
+ + !node.propDefaults.has(name) &&
83
+ + (name !== 'style' || node.styleKeys.size === 0))
84
+ + return;
85
+ + if (value != null &&
86
+ + value !== unsetValue &&
87
+ + name !== 'style' &&
88
+ + !node.propDefaults.has(name))
89
+ + node.propDefaults.set(name, view[name]);
90
+ if (name === 'className' || name === 'class') {
91
+ view.className =
92
+ value == null || value === unsetValue
93
+ @@ -113,13 +133,31 @@ function setProp(node, view, name, value) {
54
94
  return;
55
95
  }
56
96
  if (name === 'style') {
@@ -70,147 +110,126 @@ index 8bc563a..4cb1f95 100644
70
110
  + (MASON_CONTAINER_TAGS.has(node.type) ||
71
111
  + (node.parent !== null && MASON_CONTAINER_TAGS.has(node.parent.type))))
72
112
  + warnStyleOnlyProp(node, name);
73
- + // `null`/`unsetValue` restore the prop's default rather than writing a
74
- + // literal null through a native setter. `undefined` is a removal: skip it
75
- + // when nothing applied (NativeScript setters coerce it badly) and reset
76
- + // when a real value did.
113
+ + // Never forward nullish values to coercing native setters. A never-applied
114
+ + // prop has nothing to restore, including when its initial value is undefined.
115
+ + const target = view;
77
116
  + if (value == null || value === unsetValue) {
78
- + if (value === undefined && node.props[name] == null)
117
+ + if (!node.propDefaults.has(name))
79
118
  + return;
80
- + resetProp(node, view, name);
81
- + return;
119
+ + value = isRegisteredProperty(view, name)
120
+ + ? unsetValue
121
+ + : node.propDefaults.get(name);
82
122
  + }
83
- + // Capture the pre-prop value once so a plain (non-Property) field can be
84
- + // restored when the prop is removed later.
85
- + const defaults = (node.propDefaults ??= {});
86
- + if (!(name in defaults))
87
- + defaults[name] = view[name];
88
123
  // Core raises `<name>Change` for a script write exactly as for a user
89
124
  // edit; a controlled input must not see its own value come back as one.
90
125
  const echo = `${name}Change`;
91
- @@ -134,6 +171,44 @@ function setProp(node, view, name, value) {
92
- node.muted.delete(echo);
126
+ @@ -127,7 +165,10 @@ function setProp(node, view, name, value) {
127
+ if (listening)
128
+ node.muted.add(echo);
129
+ try {
130
+ - view[name] = value;
131
+ + target[name] = value;
132
+ + if (value === unsetValue &&
133
+ + _getStyleProperties().some((p) => p.name === name))
134
+ + refreshCss(view);
93
135
  }
94
- }
95
- +/** Restore a removed prop to the value it holds when never supplied. */
96
- +function resetProp(node, view, name) {
97
- + if (view === null)
98
- + return;
99
- + if (name === 'children' || name === 'key' || name === 'ref')
100
- + return;
101
- + // `on*` handlers unbind through `event` commands, not props.
102
- + if (EVENT_PROP.test(name))
103
- + return;
104
- + if (name === 'className' || name === 'class') {
105
- + view.className = unsetValue;
106
- + return;
107
- + }
108
- + if (name === 'style') {
109
- + applyStyle(node, view, null);
110
- + return;
111
- + }
112
- + if (name === 'renderItem' && view instanceof ListView) {
113
- + setRenderItem(view, null, createNativeScriptRoot);
114
- + return;
115
- + }
116
- + const echo = `${name}Change`;
117
- + const listening = node.listeners.has(echo);
118
- + if (listening)
119
- + node.muted.add(echo);
120
- + try {
121
- + // Property/CssProperty setters consume the sentinel and restore the
122
- + // declared default. A plain field stores it raw — detect that and
123
- + // write back the captured pre-prop value instead.
124
- + view[name] = unsetValue;
125
- + if (view[name] === unsetValue)
126
- + view[name] = node.propDefaults?.[name];
127
- + }
128
- + finally {
129
- + if (listening)
130
- + node.muted.delete(echo);
131
- + }
132
- +}
136
+ finally {
137
+ if (listening)
138
+ @@ -137,6 +178,8 @@ function setProp(node, view, name, value) {
133
139
  // `class`/`className` compose clsx-style on the DOM renderer; join arrays
134
140
  // with spaces so NativeScript's class selectors can match each class.
135
141
  function normalizeClass(value) {
136
- @@ -147,16 +222,214 @@ function normalizeClass(value) {
142
+ + if (typeof value === 'number')
143
+ + return value ? String(value) : '';
144
+ if (typeof value === 'string')
145
+ return value;
146
+ if (Array.isArray(value))
147
+ @@ -147,16 +190,298 @@ function normalizeClass(value) {
137
148
  .join(' ');
138
149
  return '';
139
150
  }
140
151
  -function applyStyle(view, value) {
141
152
  - if (value == null || value === unsetValue)
142
153
  - return;
143
- +/**
144
- + * The `style` prop replaces wholesale — a key dropped or nulled since the last
145
- + * apply resets what it installed (`unsetValue` clears a local CssProperty
146
- + * value so the cascade can re-apply). String styles are parsed for their
147
- + * declared names so a later object or removal can reset them.
148
- + */
154
+ +/** Match the actual registered setter, not merely a globally registered name. */
155
+ +function isRegisteredProperty(view, name) {
156
+ + let prototype = view;
157
+ + while (prototype !== null) {
158
+ + const descriptor = Object.getOwnPropertyDescriptor(prototype, name);
159
+ + if (descriptor !== undefined) {
160
+ + return (_getProperties().some((property) => property.name === name && property.set === descriptor.set) ||
161
+ + (_getStyleProperties().some((property) => property.name === name) &&
162
+ + descriptor.set !== undefined &&
163
+ + name in view.style));
164
+ + }
165
+ + prototype = Object.getPrototypeOf(prototype);
166
+ + }
167
+ + return false;
168
+ +}
169
+ +/** Core discards the cascaded value when a local value occupies its slot. */
170
+ +function refreshCss(view) {
171
+ + // No public single-key refresh exists in core 9.1. setInlineStyle only writes
172
+ + // declarations; the recursive _onCssStateChange would also touch descendants.
173
+ + view._cssState?.onChange();
174
+ +}
175
+ +/** Replace authored keys; clearing a local CssProperty must restore class CSS. */
149
176
  +function applyStyle(node, view, value) {
150
- + const applied = node.styleKeys;
151
- + node.styleKeys = undefined;
152
- if (typeof value === 'string') {
153
- + clearStyleKeys(view, applied);
154
- view.setInlineStyle(value);
155
- + node.styleKeys = inlineStyleKeys(value);
156
- + return;
177
+ + const style = view.style;
178
+ + const next = typeof value === 'string'
179
+ + ? inlineStyleKeys(value)
180
+ + : value !== null && typeof value === 'object' && value !== unsetValue
181
+ + ? new Set(Object.keys(value))
182
+ + : new Set();
183
+ + for (const key of node.styleKeys) {
184
+ + if (!next.has(key))
185
+ + unsetStyleKey(node, view, key);
157
186
  + }
158
- + if (value == null || value === unsetValue || typeof value !== 'object') {
159
- + clearStyleKeys(view, applied);
160
- + return;
187
+ + for (const key of next) {
188
+ + if (!node.styleDefaults.has(key))
189
+ + node.styleDefaults.set(key, style[key]);
161
190
  + }
162
- + if (applied !== undefined) {
163
- + for (const key of applied) {
164
- + if (!(key in value))
165
- + unsetStyleKey(view, key);
166
- + }
191
+ if (typeof value === 'string') {
192
+ view.setInlineStyle(value);
167
193
  + }
168
- + // Mason layout keys target the leaf style under a mason parent —
169
- + // `view.style` is the NS surface where they land as dead fields. The leaf
170
- + // style exists only once the parent attaches the view, so entries are
171
- + // stashed and replayed by applyMasonChildLayout.
172
- + const parentIsMason = node.parent !== null && MASON_CONTAINER_TAGS.has(node.parent.type);
173
- + const masonLeaf = parentIsMason ? masonChildStyle(view) : undefined;
174
- + for (const [key, nextValue] of Object.entries(value)) {
175
- + if (MASON_LEAF_STYLE_KEYS.has(key)) {
176
- + const leafStash = view[MASON_LEAF_STYLE_STASH] ?? (view[MASON_LEAF_STYLE_STASH] = {});
177
- + leafStash[key] = nextValue == null ? undefined : nextValue;
178
- + if (masonLeaf !== undefined)
179
- + masonLeaf[key] = nextValue == null ? undefined : nextValue;
194
+ + else if (value !== null &&
195
+ + typeof value === 'object' &&
196
+ + value !== unsetValue) {
197
+ + for (const key of next) {
198
+ + const entry = value[key];
199
+ + if (entry == null || entry === unsetValue)
200
+ + unsetStyleKey(node, view, key);
201
+ + else
202
+ + style[key] = entry;
180
203
  + }
181
- + if (nextValue == null)
182
- + unsetStyleKey(view, key);
183
- + else
184
- + view.style[key] = nextValue;
185
204
  + }
186
- + node.styleKeys = new Set(Object.keys(value));
187
- +}
188
- +function clearStyleKeys(view, keys) {
189
- + if (keys === undefined)
190
- + return;
191
- + for (const key of keys)
192
- + unsetStyleKey(view, key);
205
+ + node.styleKeys = next;
193
206
  +}
194
- +function unsetStyleKey(view, name) {
207
+ +function unsetStyleKey(node, view, name) {
208
+ + // The foreign-leaf binding observes the resolved style, so removal needs
209
+ + // no leaf write here — the cascade restore raises the longhand's Change
210
+ + // event and the binding re-syncs.
195
211
  + const style = view.style;
196
212
  + if (name.startsWith('--')) {
197
- + style.setUnscopedCssVariable?.(name, unsetValue);
198
- + return;
213
+ + view.style.removeUnscopedCssVariable(name);
199
214
  + }
200
- + style[name] = unsetValue;
201
- + // A style field outside the CssProperty registry stores the sentinel raw.
202
- + if (style[name] === unsetValue)
203
- + style[name] = undefined;
215
+ + else {
216
+ + const registered = _getStyleProperties().some((p) => p.name === name || p.cssLocalName === name);
217
+ + style[name] = registered ? unsetValue : node.styleDefaults.get(name);
218
+ + }
219
+ + refreshCss(view);
204
220
  +}
205
221
  +function inlineStyleKeys(style) {
206
222
  + const keys = new Set();
207
- + for (const declaration of style.split(';')) {
208
- + const colon = declaration.indexOf(':');
209
- + if (colon === -1)
210
- + continue;
211
- + const name = declaration.slice(0, colon).trim();
212
- + if (name !== '')
213
- + keys.add(name.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()));
223
+ + const sheet = cssTreeParse(`local { ${style} }`, undefined);
224
+ + for (const rule of sheet.stylesheet.rules) {
225
+ + for (const declaration of rule.declarations ?? []) {
226
+ + if (declaration.type !== 'declaration')
227
+ + continue;
228
+ + const name = declaration.property;
229
+ + keys.add(name.startsWith('--')
230
+ + ? name
231
+ + : name.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()));
232
+ + }
214
233
  + }
215
234
  + return keys;
216
235
  +}
@@ -240,12 +259,16 @@ index 8bc563a..4cb1f95 100644
240
259
  + 'flexWrapBefore', 'stretchLastChild', 'itemWidth', 'itemHeight',
241
260
  + 'rows', 'columns',
242
261
  +]);
243
- +// Style-bag keys that mason owns — under a mason parent these are written to
244
- +// the child's mason leaf style in addition to the NS style surface.
245
- +const MASON_LEAF_STYLE_KEYS = new Set([
246
- + ...MASON_STYLE_ONLY_PROPS, 'left', 'top', 'right', 'bottom',
247
- +]);
248
- +const MASON_LEAF_STYLE_STASH = Symbol.for('octane-xplat.mason-leaf-style');
262
+ +// Only leaf layout longhands: core expands shorthands before notifying.
263
+ +// Read the effective NS style, never copy authored values into another tier.
264
+ +const MASON_LEAF_STYLE_KEYS = [
265
+ + 'width', 'height', 'minWidth', 'minHeight', 'maxWidth', 'maxHeight',
266
+ + 'marginTop', 'marginRight', 'marginBottom', 'marginLeft',
267
+ + 'flexGrow', 'flexShrink', 'flexBasis', 'alignSelf', 'justifySelf',
268
+ + 'order', 'position', 'left', 'top', 'right', 'bottom', 'aspectRatio',
269
+ + 'gridRow', 'gridColumn', 'gridRowStart', 'gridRowEnd', 'gridColumnStart', 'gridColumnEnd',
270
+ +];
271
+ +const masonLeafBindings = new WeakMap();
249
272
  +const warnedStyleOnlyProps = new Set();
250
273
  +function warnStyleOnlyProp(node, name) {
251
274
  + const key = node.type + ':' + name;
@@ -303,7 +326,8 @@ index 8bc563a..4cb1f95 100644
303
326
  + style.overflowY = 'scroll';
304
327
  + style.overflowX = 'hidden';
305
328
  + }
306
- + style.alignItems = 'stretch';
329
+ + // Core's alignItems CssProperty already defaults to stretch. A local
330
+ + // write here would outrank every stylesheet rule, including centering.
307
331
  + // Containers establish the containing block for absolute descendants.
308
332
  + style.position = 'relative';
309
333
  +}
@@ -329,31 +353,102 @@ index 8bc563a..4cb1f95 100644
329
353
  + }
330
354
  +}
331
355
  +
332
- +/** Replay a child's authored mason style entries onto its leaf — the leaf
333
- + * style only exists once the parent attaches the view, so `applyStyle`
334
- + * stashes leaf-intended keys and every attach replays them. Sibling order is
335
- + * irrelevant to the replay, but the pass runs per sibling anyway so a child
336
- + * inserted later still gets its stash flushed. */
356
+ +/** Keep foreign leaves in sync with the effective NativeScript cascade.
357
+ + * MasonKit's property handlers only forward to its own _styleHelper views.
358
+ + * Observe longhand changes so classes, pseudo states, inline removal and
359
+ + * stylesheet refreshes follow the same path. Rebind on loaded because the
360
+ + * native leaf may not exist at insert, or may have been recreated. */
337
361
  +function applyMasonChildLayout(node, parent) {
338
362
  + const view = node.view;
339
- + if (view === null || view === undefined)
340
- return;
341
- + const style = masonChildStyle(view);
342
- + if (style === undefined)
363
+ + if (!view || view._styleHelper !== undefined)
343
364
  + return;
344
- + const leafStash = view[MASON_LEAF_STYLE_STASH];
345
- + if (leafStash !== undefined) {
346
- + for (const key of Object.keys(leafStash))
347
- + style[key] = leafStash[key];
365
+ + if (masonLeafBindings.has(view)) {
366
+ + masonLeafBindings.get(view).sync();
367
+ return;
348
368
  }
349
369
  - if (typeof value === 'object')
350
370
  - Object.assign(view.style, value);
371
+ + let native;
372
+ + let leaf;
373
+ + const applied = new Map();
374
+ + const syncKey = (key) => {
375
+ + if (!leaf)
376
+ + return;
377
+ + let value = view.style[key];
378
+ + if (value === undefined || value === unsetValue) {
379
+ + // Plain/unregistered fields can vanish without a CssProperty
380
+ + // default. Mason requires an explicit grid reset (undefined is
381
+ + // ignored by its setters).
382
+ + if ((key === 'gridRow' || key === 'gridColumn') && applied.has(key))
383
+ + value = 'auto';
384
+ + else
385
+ + return;
386
+ + }
387
+ + // Core's auto self-alignment is Mason's normal (-1 / parent default).
388
+ + if ((key === 'alignSelf' || key === 'justifySelf') && value === 'auto')
389
+ + value = 'normal';
390
+ + if (typeof value === 'number' && !Number.isFinite(value))
391
+ + value = 'auto';
392
+ + // Mason's object px setter treats the number as DIP; core's parsed
393
+ + // px length is physical pixels. Convert once at this boundary.
394
+ + if (value && typeof value === 'object' && value.unit === 'px')
395
+ + value = { value: Utils.layout.toDeviceIndependentPixels(value.value), unit: 'dip' };
396
+ + const previous = applied.get(key);
397
+ + if (Object.is(previous, value) || (value && previous && typeof value === 'object' &&
398
+ + value.unit === previous.unit && value.value === previous.value))
399
+ + return;
400
+ + leaf[key] = value;
401
+ + applied.set(key, value);
402
+ + return true;
403
+ + };
404
+ + const sync = () => {
405
+ + if (native !== view.nativeViewProtected || !leaf) {
406
+ + native = view.nativeViewProtected;
407
+ + leaf = masonChildStyle(view);
408
+ + applied.clear();
409
+ + }
410
+ + let changed = false;
411
+ + const update = () => {
412
+ + for (const key of MASON_LEAF_STYLE_KEYS)
413
+ + changed = syncKey(key) || changed;
414
+ + };
415
+ + // Foreign native style writes normally flush on a microtask, after
416
+ + // core may already have measured the parent. Commit first, then ask
417
+ + // the Mason container to recompute the foreign frame.
418
+ + if (leaf?.batch)
419
+ + leaf.batch(update);
420
+ + else
421
+ + update();
422
+ + if (changed)
423
+ + parent.view?.requestLayout?.();
424
+ + };
425
+ + const handlers = MASON_LEAF_STYLE_KEYS.map(key => {
426
+ + const handler = () => sync();
427
+ + view.style.on?.(key + 'Change', handler);
428
+ + return [key + 'Change', handler];
429
+ + });
430
+ + view.on('loaded', sync);
431
+ + masonLeafBindings.set(view, {
432
+ + sync,
433
+ + dispose() {
434
+ + for (const [event, handler] of handlers)
435
+ + view.style.off?.(event, handler);
436
+ + view.off('loaded', sync);
437
+ + },
438
+ + });
439
+ + sync();
440
+ +}
441
+ +function releaseMasonChildLayout(view) {
442
+ + if (!view)
443
+ + return;
444
+ + masonLeafBindings.get(view)?.dispose();
445
+ + masonLeafBindings.delete(view);
351
446
  }
352
447
  +
353
448
  /** Recompute a text host's `text` from its `#text` children. */
354
449
  function syncText(parent) {
355
450
  if (parent === null || isPortalTarget(parent))
356
- @@ -190,6 +463,15 @@ function addViewChild(parentView, view, index) {
451
+ @@ -190,6 +515,15 @@ function addViewChild(parentView, view, index) {
357
452
  parentView.insertChild(view, Math.min(index, parentView.getChildrenCount()));
358
453
  return;
359
454
  }
@@ -369,7 +464,7 @@ index 8bc563a..4cb1f95 100644
369
464
  if (parentView instanceof ContentView) {
370
465
  parentView.content = view;
371
466
  return;
372
- @@ -234,6 +516,13 @@ function removeViewChild(parentView, view) {
467
+ @@ -234,6 +568,13 @@ function removeViewChild(parentView, view) {
373
468
  parentView.removeChild(view);
374
469
  return;
375
470
  }
@@ -383,13 +478,20 @@ index 8bc563a..4cb1f95 100644
383
478
  if (parentView instanceof ContentView) {
384
479
  if (parentView.content === view)
385
480
  parentView.content = CLEARED;
386
- @@ -370,6 +659,13 @@ function attach(container, node) {
481
+ @@ -315,6 +656,7 @@ function tabViewOwning(node) {
482
+ return null;
483
+ }
484
+ function detach(container, node) {
485
+ + releaseMasonChildLayout(node.view);
486
+ const parent = node.parent;
487
+ const siblings = childrenOf(container, parent);
488
+ const index = siblings.indexOf(node);
489
+ @@ -370,6 +712,12 @@ function attach(container, node) {
387
490
  }
388
491
  }
389
492
  addViewChild(parentView, node.view, index);
390
493
  + // The child's mason leaf node exists only once the parent has attached it;
391
- + // replay placement for every sibling (dock's stretchLastChild depends on
392
- + // the full sibling list).
494
+ + // synchronize effective styles after attachment.
393
495
  + if (parent !== null && MASON_CONTAINER_TAGS.has(parent.type)) {
394
496
  + for (const sibling of childrenOf(container, parent))
395
497
  + applyMasonChildLayout(sibling, parent);
@@ -397,7 +499,46 @@ index 8bc563a..4cb1f95 100644
397
499
  markTabView(tabViewOwning(node));
398
500
  }
399
501
  function insert(container, parent, node, before) {
400
- @@ -548,7 +844,7 @@ function preparePortalTarget(context) {
502
+ @@ -429,6 +777,7 @@ function setEvent(container, node, type, listener) {
503
+ view.on(type, handler);
504
+ }
505
+ function disposeListeners(node) {
506
+ + releaseMasonChildLayout(node.view);
507
+ const view = node.view;
508
+ if (view !== null) {
509
+ for (const [type, handler] of node.listeners)
510
+ @@ -460,6 +809,7 @@ function recreateView(container, node) {
511
+ // no longer host them.
512
+ throw new Error(`NativeScript driver: re-registered <${node.type}> is a <${view.typeName}>, which cannot host portal children; the registration stays on the previous view.`);
513
+ }
514
+ + releaseMasonChildLayout(previous);
515
+ const parentView = hostViewOf(container, node.parent);
516
+ for (const child of node.children) {
517
+ if (child.view !== null)
518
+ @@ -480,6 +830,9 @@ function recreateView(container, node) {
519
+ viewOwners.set(view, { container, node });
520
+ node.view = view;
521
+ node.textApplied = false;
522
+ + node.propDefaults.clear();
523
+ + node.styleDefaults.clear();
524
+ + node.styleKeys.clear();
525
+ applyProps(node, node.props);
526
+ for (const [type, handler] of node.listeners)
527
+ view.on(type, handler);
528
+ @@ -500,6 +853,12 @@ function recreateView(container, node) {
529
+ addViewChild(view, child.view, index++);
530
+ }
531
+ }
532
+ + if (MASON_CONTAINER_TAGS.has(node.type)) {
533
+ + for (const child of node.children) {
534
+ + releaseMasonChildLayout(child.view);
535
+ + applyMasonChildLayout(child, node);
536
+ + }
537
+ + }
538
+ syncText(node);
539
+ markTabView(node);
540
+ attach(container, node);
541
+ @@ -548,7 +907,7 @@ function preparePortalTarget(context) {
401
542
  }
402
543
  const target = owner?.node.view ?? suppliedTarget;
403
544
  if (!(target instanceof LayoutBase)) {
@@ -406,7 +547,7 @@ index 8bc563a..4cb1f95 100644
406
547
  }
407
548
  let id = portalTargetIds.get(target);
408
549
  if (id === undefined) {
409
- @@ -641,7 +937,7 @@ function applyCommand(container, command) {
550
+ @@ -641,7 +1000,7 @@ function applyCommand(container, command) {
410
551
  }
411
552
  export const nativeScriptDriver = {
412
553
  id: NATIVESCRIPT_RENDERER_ID,
@@ -416,7 +557,7 @@ index 8bc563a..4cb1f95 100644
416
557
  classify(name) {
417
558
  if (!EVENT_PROP.test(name))
418
559
  diff --git a/dist/elements.js b/dist/elements.js
419
- index a13bcea..e9988e1 100644
560
+ index a13bcea75176146f58f047be1d9c7b0cdda1120a..e9988e174d0b89dcd70220d3014ca6b9bb1820e5 100644
420
561
  --- a/dist/elements.js
421
562
  +++ b/dist/elements.js
422
563
  @@ -1,4 +1,7 @@
@@ -424,7 +565,7 @@ index a13bcea..e9988e1 100644
424
565
  +import { ActionBar, ActionItem, ActivityIndicator, Button, LiquidGlass, ContentView, DatePicker, FormattedString, Frame, HtmlView, Image, Label, ListPicker, ListView, NavigationButton, Page, Placeholder, Progress, ProxyViewContainer, RootLayout, SearchBar, SegmentedBar, SegmentedBarItem, Slider, Span, Switch, TabView, TabViewItem, TextField, TextView, TimePicker, WebView, } from '@nativescript/core';
425
566
  +// Mason cutover: container intrinsics resolve to MasonKit views; the driver
426
567
  +// translates their NativeScript-shaped props into mason style writes.
427
- +import { View as MasonView, Scroll as MasonScroll } from '@triniwiz/nativescript-masonkit';
568
+ +import { View as MasonView, Scroll as MasonScroll } from '@octane-xplat/nativescript-masonkit';
428
569
  /**
429
570
  * Lowercase tag name -> view constructor. The keys are the JSX tag names of
430
571
  * `intrinsics.ts`; an app extends the vocabulary with `registerElement`.
@@ -473,12 +614,14 @@ index a13bcea..e9988e1 100644
473
614
  for (const [tag, element] of BUILTIN_ELEMENTS)
474
615
  registerElement(tag, element);
475
616
  diff --git a/dist/intrinsics.d.ts b/dist/intrinsics.d.ts
476
- index 45a47cb..668c920 100644
617
+ index 45a47cbbce199df9635852ca4cdb2849e9bbc83d..23b81c44b6860e6099e8c1a70f975f6e792aa2b2 100644
477
618
  --- a/dist/intrinsics.d.ts
478
619
  +++ b/dist/intrinsics.d.ts
479
- @@ -24,9 +24,9 @@
620
+ @@ -23,10 +23,11 @@
621
+ * ```
480
622
  */
481
623
  import type * as NS from '@nativescript/core';
624
+ +import type { CSSProperties } from 'octane/jsx-runtime';
482
625
  export type NativeScriptNode = unknown;
483
626
  -type Ref<TInstance> = ((instance: TInstance | null) => void) | {
484
627
  +type Ref<TInstance> = ((instance: TInstance | null) => void | (() => void)) | {
@@ -488,10 +631,17 @@ index 45a47cb..668c920 100644
488
631
  /**
489
632
  * The driver assigns every non-event prop onto the view instance, so a JSX
490
633
  * attribute is a NativeScript view property rather than a DOM attribute.
491
- @@ -76,34 +76,67 @@ export interface CommonEvents<TInstance> {
492
- export type ClassValue = string | boolean | null | undefined | readonly ClassValue[] | {
493
- readonly [className: string]: unknown;
494
- };
634
+ @@ -72,16 +73,64 @@ export interface CommonEvents<TInstance> {
635
+ onFocus?: (event: NSEventData<TInstance>) => void;
636
+ onBlur?: (event: NSEventData<TInstance>) => void;
637
+ }
638
+ -/** Values accepted by the driver's `className` and `class` props. */
639
+ -export type ClassValue = string | boolean | null | undefined | readonly ClassValue[] | {
640
+ - readonly [className: string]: unknown;
641
+ -};
642
+ +/** Octane's class composition contract, shared unchanged with the DOM. */
643
+ +export type { ClassValue } from 'octane/jsx-runtime';
644
+ +import type { ClassValue } from 'octane/jsx-runtime';
495
645
  +/** Mason engine style keys accepted through `style`/`className` — under a
496
646
  + * masonview parent a child's leaf style consumes them; on a masonview they
497
647
  + * are the container's own layout. These are the only way to set raw layout
@@ -547,17 +697,19 @@ index 45a47cb..668c920 100644
547
697
  class?: ClassValue;
548
698
  /** A string is parsed as inline CSS; an object is assigned onto `view.style`. */
549
699
  - style?: string | Partial<NS.Style>;
550
- + style?: string | Partial<NS.Style> | MasonStyleProps;
700
+ + style?: string | Partial<NS.Style> | MasonStyleProps | CSSProperties;
551
701
  /**
552
702
  * Name of the property on a slot-hosting parent (e.g. a drawer's
553
703
  * `mainContent` / `leftDrawer`) this child should be assigned to instead of
554
- * being added as a regular layout child.
704
+ @@ -89,21 +138,18 @@ export interface CommonAttributes {
555
705
  */
556
706
  hostSlot?: string;
557
- - /**
707
+ /**
558
708
  - * Attached layout properties. The layout modules register these on `View` at
559
709
  - * runtime, so they are absent from the `@nativescript/core` class typings.
560
- - */
710
+ + * ARIA-shaped accessibility values; shared components emit plain strings
711
+ + * through the escape-props helpers rather than the NativeScript enums.
712
+ */
561
713
  - row?: number | string;
562
714
  - col?: number | string;
563
715
  - rowSpan?: number | string;
@@ -570,10 +722,19 @@ index 45a47cb..668c920 100644
570
722
  - flexWrapBefore?: boolean | string;
571
723
  - alignSelf?: 'auto' | 'flex-start' | 'flex-end' | 'center' | 'baseline' | 'stretch';
572
724
  - order?: number | string;
725
+ + accessibilityRole?: string;
726
+ + accessibilityState?: string;
727
+ + accessibilityLiveRegion?: string;
728
+ + /** Platform escape bags — applied onto the native view by the leaf's
729
+ + * applyEscapeProps, not by the driver. */
730
+ + ios?: Record<string, unknown>;
731
+ + android?: Record<string, unknown>;
732
+ + windows?: Record<string, unknown>;
733
+ + macos?: Record<string, unknown>;
573
734
  }
574
735
  export interface ListViewAttributes {
575
736
  /**
576
- @@ -117,25 +150,21 @@ export interface ListViewAttributes {
737
+ @@ -117,25 +163,21 @@ export interface ListViewAttributes {
577
738
  }
578
739
  export interface OctaneAttributes<TInstance> {
579
740
  key?: string | number;
@@ -600,7 +761,7 @@ index 45a47cb..668c920 100644
600
761
  htmlview: Attributes<typeof NS.HtmlView>;
601
762
  image: Attributes<typeof NS.Image>;
602
763
  label: Attributes<typeof NS.Label>;
603
- @@ -153,7 +182,6 @@ export interface NativeScriptElements {
764
+ @@ -153,7 +195,6 @@ export interface NativeScriptElements {
604
765
  segmentedbaritem: Attributes<typeof NS.SegmentedBarItem>;
605
766
  slider: Attributes<typeof NS.Slider>;
606
767
  span: Attributes<typeof NS.Span>;
@@ -608,7 +769,7 @@ index 45a47cb..668c920 100644
608
769
  switch: Attributes<typeof NS.Switch>;
609
770
  tabview: Attributes<typeof NS.TabView>;
610
771
  tabviewitem: Attributes<typeof NS.TabViewItem>;
611
- @@ -161,7 +189,8 @@ export interface NativeScriptElements {
772
+ @@ -161,7 +202,8 @@ export interface NativeScriptElements {
612
773
  textview: Attributes<typeof NS.TextView>;
613
774
  timepicker: Attributes<typeof NS.TimePicker>;
614
775
  webview: Attributes<typeof NS.WebView>;