@ng-native/fabric 0.18.2 → 0.19.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.
package/dist/engine.d.ts CHANGED
@@ -228,12 +228,8 @@ export interface EngineNode extends HostNode {
228
228
  nodeText: string;
229
229
  childList: EngineNode[];
230
230
  parent: EngineNode | null;
231
- /**
232
- * A child of this node that holds what is put in it: `appendChild` and `insertBefore` on this
233
- * node put the child there instead, so a component's template lands inside a view its owner
234
- * placed in the host element. Only the view itself goes in this node.
235
- */
236
- contentView?: EngineNode;
231
+ /** Set by `Engine.wrapChildren`: the view this node's children are committed in. */
232
+ wrapper?: Wrapper;
237
233
  listeners: Map<string, Set<(event: unknown) => void>> | null;
238
234
  /** CSS classes on this node, for selector matching. */
239
235
  classes: Set<string> | null;
@@ -311,11 +307,11 @@ export interface EngineNode extends HostNode {
311
307
  scrollsOverflow?: true;
312
308
  /** Set while this element is one: see `Engine.settleScrolling`. */
313
309
  scrollView?: true;
314
- /** The view a scrolling element's children are in, as it was last committed. */
315
- inside?: {
316
- handle: FabricNode;
317
- childHandles: FabricNode[];
318
- };
310
+ /**
311
+ * The view a scrolling element's children are in, or a wrapped one's (`wrapper`), as it was
312
+ * last committed: a wrapper's with the props it was committed with.
313
+ */
314
+ inside?: Inside;
319
315
  /** Set while `settleLine` has given an always-inline element a line for the boxes it holds. */
320
316
  lined?: true;
321
317
  /** Set while an always-inline element is a flex box or a grid by its own style. */
@@ -545,6 +541,21 @@ export declare function markPage(node: EngineNode): void;
545
541
  * and its `overflow` is layout alone.
546
542
  */
547
543
  export declare function scrollsOverflow(node: EngineNode): void;
544
+ /**
545
+ * A view the engine commits an element's children in, which is no node of the tree: see
546
+ * `Engine.wrapChildren`.
547
+ */
548
+ export interface Wrapper {
549
+ readonly viewName: string;
550
+ readonly props: Readonly<Record<string, unknown>>;
551
+ }
552
+ /** The view an element's children are committed in, as `inside` keeps it. */
553
+ interface Inside {
554
+ handle: FabricNode;
555
+ childHandles: FabricNode[];
556
+ /** A wrapper's props, as it was committed with them. */
557
+ props?: Record<string, unknown>;
558
+ }
548
559
  /**
549
560
  * Mark an element as the host of a component. The platform adapter calls this from
550
561
  * `RendererFactory2.createRenderer`, which Angular calls once for every component it mounts,
@@ -1362,8 +1373,8 @@ export declare class Engine implements HostEngine {
1362
1373
  /** Angular's `@if`/`@for`/`ViewContainerRef` markers. Ordered, never committed. */
1363
1374
  createAnchor(): EngineNode;
1364
1375
  createText(value: string): EngineNode;
1365
- appendChild(into: EngineNode, child: EngineNode): void;
1366
- insertBefore(into: EngineNode, child: EngineNode, ref: EngineNode | null): void;
1376
+ appendChild(parent: EngineNode, child: EngineNode): void;
1377
+ insertBefore(parent: EngineNode, child: EngineNode, ref: EngineNode | null): void;
1367
1378
  /**
1368
1379
  * A node coming under a parent other than the one it is under, or under any once it has been
1369
1380
  * taken out: what is over it may be other than it was styled under. The resolver sees that for
@@ -1372,6 +1383,19 @@ export declare class Engine implements HostEngine {
1372
1383
  */
1373
1384
  private markAdopted;
1374
1385
  removeChild(parent: EngineNode | null, child: EngineNode): void;
1386
+ /**
1387
+ * Commit an element's children inside a view of the engine's own, `wrapper.viewName` with
1388
+ * `wrapper.props`, and the element's own view around it; null commits them in the element
1389
+ * again. For a native view that wants its content in a child of a kind it names, where the
1390
+ * element is a component's host: the tree keeps the children in the element, so the
1391
+ * component's `:host` rules, a `:host >` selector and what is inherited reach them as written,
1392
+ * and how the element places its children (`flex-direction`, `justify-content`, `align-items`,
1393
+ * `gap`) is committed on the wrapper, which holds them. What is hoisted into the element and
1394
+ * its fixed boxes stay its own, after the wrapper. Called again with another wrapper's props,
1395
+ * the wrapper is cloned with them; another view name makes the element and what it holds again,
1396
+ * as native gives a view one parent for as long as it lives.
1397
+ */
1398
+ wrapChildren(node: EngineNode, wrapper: Wrapper | null): void;
1375
1399
  /**
1376
1400
  * A child came or went under a view that takes its text as a prop: that prop changed. A span
1377
1401
  * changes it too, by keeping or dropping the space before it.
@@ -3114,6 +3138,18 @@ export declare class Engine implements HostEngine {
3114
3138
  * for a content that was cloned.
3115
3139
  */
3116
3140
  private reconcileScroller;
3141
+ /**
3142
+ * `reconcile` for an element whose children are in a wrapper (`wrapChildren`): its own view,
3143
+ * holding the wrapper and then `beside`, and the wrapper holding its children. The wrapper is
3144
+ * created with the element and cloned for what changed of its own, as a scroll view's content
3145
+ * is.
3146
+ */
3147
+ private reconcileWrapped;
3148
+ /** `noteClone` for a wrapped element, whose children are counted in its wrapper. */
3149
+ private noteWrappedClone;
3150
+ /** A wrapper cloned for what changed of its props, and for children that moved. */
3151
+ private clonedWrapper;
3152
+ private createWrapped;
3117
3153
  /** A view cloned for the props and the children that changed, or itself where neither did. */
3118
3154
  private clonedFor;
3119
3155
  private createScroller;
@@ -3251,7 +3287,8 @@ export declare class Engine implements HostEngine {
3251
3287
  /**
3252
3288
  * `reconcileChildren` for a node other nodes hoist into: its own children, each followed by
3253
3289
  * the hoisted nodes written inside it, then those written in it directly, then the views it
3254
- * keeps for hoisted nodes that are gone.
3290
+ * keeps for hoisted nodes that are gone. A wrapped node's hoisted nodes and kept views go in
3291
+ * `beside`, beside its wrapper and not in it, in the same order.
3255
3292
  */
3256
3293
  private reconcileHoistTarget;
3257
3294
  /**
package/dist/engine.js CHANGED
@@ -461,15 +461,33 @@ export function markPage(node) {
461
461
  export function scrollsOverflow(node) {
462
462
  node.scrollsOverflow = true;
463
463
  }
464
+ /** How a flex container places its children, which the view they are in takes. */
465
+ const CHILD_PLACING_KEYS = [
466
+ ...['flexDirection', 'flexWrap', 'alignItems', 'alignContent', 'justifyContent'],
467
+ ...['gap', 'rowGap', 'columnGap'],
468
+ ];
464
469
  /** What a scrolling element lays out what it holds by, which its content view takes. */
465
470
  const CONTENT_KEYS = new Set([
466
471
  ...['padding', 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft'],
467
472
  ...['paddingHorizontal', 'paddingVertical', 'paddingStart', 'paddingEnd'],
468
473
  ...['paddingInline', 'paddingBlock', 'paddingInlineStart', 'paddingInlineEnd'],
469
474
  ...['paddingBlockStart', 'paddingBlockEnd'],
470
- ...['flexDirection', 'flexWrap', 'alignItems', 'alignContent', 'justifyContent'],
471
- ...['gap', 'rowGap', 'columnGap'],
475
+ ...CHILD_PLACING_KEYS,
472
476
  ]);
477
+ const CHILD_PLACING = new Set(CHILD_PLACING_KEYS);
478
+ /**
479
+ * A wrapped element's props split between its two views: the box it is, on its own view, and how
480
+ * its children are placed, on the wrapper, after the wrapper's own props. Its padding stays on
481
+ * the element, outside what the wrapper insets by.
482
+ */
483
+ function splitWrapped(props, own = {}) {
484
+ const box = {};
485
+ const content = { ...own };
486
+ for (const [key, value] of Object.entries(props)) {
487
+ (CHILD_PLACING.has(key) ? content : box)[key] = value;
488
+ }
489
+ return { box, content };
490
+ }
473
491
  /** A scrolling element's props split between its two views: see `scrollerProps`. */
474
492
  function splitScroller(props) {
475
493
  const box = {};
@@ -4793,6 +4811,7 @@ class RetainedNode {
4793
4811
  inlineInherits = undefined;
4794
4812
  line = undefined;
4795
4813
  inside = undefined;
4814
+ wrapper = undefined;
4796
4815
  transitions;
4797
4816
  playing;
4798
4817
  scrolled;
@@ -5012,7 +5031,7 @@ const ENGINE_NODE_FIELDS = {
5012
5031
  structureDirty: true,
5013
5032
  subtreeDirty: true,
5014
5033
  inside: true,
5015
- contentView: true,
5034
+ wrapper: true,
5016
5035
  scrollsOverflow: true,
5017
5036
  engine: true,
5018
5037
  };
@@ -6613,8 +6632,7 @@ export class Engine {
6613
6632
  node.nodeText = value;
6614
6633
  return node;
6615
6634
  }
6616
- appendChild(into, child) {
6617
- const parent = into.contentView && into.contentView !== child ? into.contentView : into;
6635
+ appendChild(parent, child) {
6618
6636
  this.markAdopted(child, parent);
6619
6637
  this.detach(child);
6620
6638
  this.markReturned(child);
@@ -6629,8 +6647,7 @@ export class Engine {
6629
6647
  this.markStructure(parent, child, parent.childList.length - 1);
6630
6648
  this.markTextContent(parent, child);
6631
6649
  }
6632
- insertBefore(into, child, ref) {
6633
- const parent = into.contentView && into.contentView !== child ? into.contentView : into;
6650
+ insertBefore(parent, child, ref) {
6634
6651
  this.markAdopted(child, parent);
6635
6652
  this.detach(child);
6636
6653
  this.markReturned(child);
@@ -6674,6 +6691,25 @@ export class Engine {
6674
6691
  this.markStructure(target, child, at);
6675
6692
  this.markTextContent(target, child);
6676
6693
  }
6694
+ /**
6695
+ * Commit an element's children inside a view of the engine's own, `wrapper.viewName` with
6696
+ * `wrapper.props`, and the element's own view around it; null commits them in the element
6697
+ * again. For a native view that wants its content in a child of a kind it names, where the
6698
+ * element is a component's host: the tree keeps the children in the element, so the
6699
+ * component's `:host` rules, a `:host >` selector and what is inherited reach them as written,
6700
+ * and how the element places its children (`flex-direction`, `justify-content`, `align-items`,
6701
+ * `gap`) is committed on the wrapper, which holds them. What is hoisted into the element and
6702
+ * its fixed boxes stay its own, after the wrapper. Called again with another wrapper's props,
6703
+ * the wrapper is cloned with them; another view name makes the element and what it holds again,
6704
+ * as native gives a view one parent for as long as it lives.
6705
+ */
6706
+ wrapChildren(node, wrapper) {
6707
+ const was = node.wrapper;
6708
+ node.wrapper = wrapper ?? undefined;
6709
+ if (was?.viewName !== wrapper?.viewName && node.committed)
6710
+ this.forgetCommitted(node);
6711
+ this.markProps(node, false);
6712
+ }
6677
6713
  /**
6678
6714
  * A child came or went under a view that takes its text as a prop: that prop changed. A span
6679
6715
  * changes it too, by keeping or dropping the space before it.
@@ -12755,15 +12791,19 @@ export class Engine {
12755
12791
  this.forgetRenamed(node, viewName);
12756
12792
  this.noteBaselineFields(node);
12757
12793
  this.freshBaselines(node);
12758
- const childHandles = this.reconcileChildren(node, viewName, style);
12794
+ // A wrapped node's own views beside the wrapper: what is hoisted into it, and its fixed boxes.
12795
+ const beside = node.wrapper && [];
12796
+ const childHandles = this.reconcileChildren(node, viewName, style, beside);
12759
12797
  if (this.heldMoves.size)
12760
12798
  this.heldHandles(node, childHandles);
12761
12799
  if (this.fixedIn.size)
12762
- this.fixedHandles(node, childHandles);
12800
+ this.fixedHandles(node, beside ?? childHandles);
12763
12801
  this.raise(node, viewName);
12764
12802
  noteTouches(node, style);
12765
12803
  if (scroller)
12766
12804
  return this.reconcileScroller(node, viewName, childHandles, styleChanged);
12805
+ if (beside)
12806
+ return this.reconcileWrapped(node, viewName, childHandles, beside, styleChanged);
12767
12807
  const previous = node.committed;
12768
12808
  this.notePresented(node, viewName);
12769
12809
  if (!previous)
@@ -12863,6 +12903,62 @@ export class Engine {
12863
12903
  this.clearFlags(node);
12864
12904
  return handle;
12865
12905
  }
12906
+ /**
12907
+ * `reconcile` for an element whose children are in a wrapper (`wrapChildren`): its own view,
12908
+ * holding the wrapper and then `beside`, and the wrapper holding its children. The wrapper is
12909
+ * created with the element and cloned for what changed of its own, as a scroll view's content
12910
+ * is.
12911
+ */
12912
+ reconcileWrapped(node, viewName, childHandles, beside, styleChanged) {
12913
+ const previous = node.committed;
12914
+ const held = node.inside;
12915
+ if (!previous || !held?.props)
12916
+ return this.createWrapped(node, viewName, childHandles, beside);
12917
+ const propsMoved = node.propsDirty || styleChanged;
12918
+ const props = propsMoved ? this.mergeProps(node, viewName) : previous.props;
12919
+ const payload = propsMoved ? this.propsPayload(node, previous.props, props) : null;
12920
+ if (this.asked & LAYOUTS)
12921
+ this.noteHow(node, payload);
12922
+ const moved = this.childrenMoved(node, childHandles, held.childHandles);
12923
+ this.noteWrappedClone(node, payload, moved, childHandles, previous, held);
12924
+ const content = propsMoved ? splitWrapped(props, node.wrapper.props).content : held.props;
12925
+ const inside = this.clonedWrapper(held, content, moved ? childHandles : null);
12926
+ const around = [inside, ...beside];
12927
+ const aroundMoved = !sameHandles(around, previous.childHandles);
12928
+ const box = payload && someProps(splitWrapped(payload).box);
12929
+ const boxPayload = this.paintNatively(node, viewName, props, box, aroundMoved);
12930
+ const handle = this.clonedFor(previous.handle, boxPayload, aroundMoved ? around : null);
12931
+ node.committed = { handle, tag: previous.tag, props, childHandles: around, viewName };
12932
+ node.inside = { handle: inside, childHandles, props: content };
12933
+ this.clearFlags(node);
12934
+ return handle;
12935
+ }
12936
+ /** `noteClone` for a wrapped element, whose children are counted in its wrapper. */
12937
+ noteWrappedClone(node, payload, moved, childHandles, previous, held) {
12938
+ if (!this.notesMoves)
12939
+ return;
12940
+ const before = { ...previous, childHandles: held.childHandles };
12941
+ this.noteClone(node, payload, moved, childHandles, before);
12942
+ }
12943
+ /** A wrapper cloned for what changed of its props, and for children that moved. */
12944
+ clonedWrapper(held, props, children) {
12945
+ const payload = props === held.props ? null : diffProps(held.props, props);
12946
+ return this.clonedFor(held.handle, payload, children);
12947
+ }
12948
+ createWrapped(node, viewName, childHandles, beside) {
12949
+ const wrapper = node.wrapper;
12950
+ const props = this.mergeProps(node, viewName);
12951
+ const { box, content } = splitWrapped(props, wrapper.props);
12952
+ // A touch on the wrapper lands on the element, as one on a scroll view's content does.
12953
+ const inside = this.fabric.createNode(nextTag(), wrapper.viewName, this.rootTag, this.processed(content, false), node);
12954
+ this.stats.createdNodes++;
12955
+ for (const child of childHandles)
12956
+ this.fabric.appendChild(inside, child);
12957
+ const handle = this.create(node, viewName, box, [inside, ...beside]);
12958
+ node.committed = { ...node.committed, props };
12959
+ node.inside = { handle: inside, childHandles, props: content };
12960
+ return handle;
12961
+ }
12866
12962
  /** A view cloned for the props and the children that changed, or itself where neither did. */
12867
12963
  clonedFor(handle, props, children) {
12868
12964
  if (!props && !children)
@@ -12922,12 +13018,12 @@ export class Engine {
12922
13018
  // Exempt from the complexity limit as `mergeProps` is: run for every node with children, where
12923
13019
  // the loop in a function of its own was a call more a node in `pnpm bench`.
12924
13020
  // eslint-disable-next-line complexity
12925
- reconcileChildren(node, viewName, style) {
13021
+ reconcileChildren(node, viewName, style, beside) {
12926
13022
  const context = style?.context;
12927
13023
  if (HOIST_ONLY.has(node.tag))
12928
13024
  return this.reconcileOnlyHoisted(node);
12929
13025
  if (HOIST_TARGETS.has(node.tag))
12930
- return this.reconcileHoistTarget(node, context);
13026
+ return this.reconcileHoistTarget(node, context, beside);
12931
13027
  if (node.flow !== undefined)
12932
13028
  return this.reconcileInFlow(node, node.flow, viewName, context);
12933
13029
  // A node with no flow of its own holds nothing in a line: each child is its own view under it.
@@ -13325,10 +13421,12 @@ export class Engine {
13325
13421
  /**
13326
13422
  * `reconcileChildren` for a node other nodes hoist into: its own children, each followed by
13327
13423
  * the hoisted nodes written inside it, then those written in it directly, then the views it
13328
- * keeps for hoisted nodes that are gone.
13424
+ * keeps for hoisted nodes that are gone. A wrapped node's hoisted nodes and kept views go in
13425
+ * `beside`, beside its wrapper and not in it, in the same order.
13329
13426
  */
13330
- reconcileHoistTarget(node, context) {
13427
+ reconcileHoistTarget(node, context, beside) {
13331
13428
  const handles = [];
13429
+ const landing = beside ?? handles;
13332
13430
  const hoisted = this.hoistedInto(node);
13333
13431
  const landed = new Set();
13334
13432
  // One written directly in the node goes after its siblings: a page writes its header first,
@@ -13346,13 +13444,13 @@ export class Engine {
13346
13444
  const moved = hoisted?.get(child);
13347
13445
  for (const written of moved ?? []) {
13348
13446
  if (!this.undisplayedUnder(node, written))
13349
- handles.push(this.land(node, written, landed));
13447
+ landing.push(this.land(node, written, landed));
13350
13448
  }
13351
13449
  }
13352
13450
  for (const child of direct)
13353
- handles.push(this.land(node, child, landed));
13451
+ landing.push(this.land(node, child, landed));
13354
13452
  if (node.kept)
13355
- this.standIn(node, landed, handles);
13453
+ this.standIn(node, landed, landing);
13356
13454
  return handles;
13357
13455
  }
13358
13456
  /**