@symbiote-native/angular 2.0.1 → 3.0.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.
Files changed (96) hide show
  1. package/README.md +5 -4
  2. package/babel-register-composed.cjs +0 -8
  3. package/build/angular/callback-host.d.ts +25 -0
  4. package/build/angular/callback-host.js +211 -0
  5. package/build/angular/callback-host.js.map +1 -0
  6. package/build/angular/change-detection-flush.d.ts +14 -2
  7. package/build/angular/change-detection-flush.js +54 -6
  8. package/build/angular/change-detection-flush.js.map +1 -1
  9. package/build/angular/components/flat-list/index.js +39 -86
  10. package/build/angular/components/flat-list/index.js.map +1 -1
  11. package/build/angular/components/image-shared.js +49 -110
  12. package/build/angular/components/image-shared.js.map +1 -1
  13. package/build/angular/components/keyboard-avoiding-view/index.js +33 -72
  14. package/build/angular/components/keyboard-avoiding-view/index.js.map +1 -1
  15. package/build/angular/components/modal/index.js +58 -103
  16. package/build/angular/components/modal/index.js.map +1 -1
  17. package/build/angular/components/pressable-props.d.ts +2 -0
  18. package/build/angular/components/section-list/index.js +20 -103
  19. package/build/angular/components/section-list/index.js.map +1 -1
  20. package/build/angular/components/touchable-props.d.ts +1 -0
  21. package/build/angular/components/virtualized-list/directives.js +32 -22
  22. package/build/angular/components/virtualized-list/directives.js.map +1 -1
  23. package/build/angular/components/virtualized-list/index.d.ts +7 -1
  24. package/build/angular/components/virtualized-list/index.js +350 -359
  25. package/build/angular/components/virtualized-list/index.js.map +1 -1
  26. package/build/angular/components/virtualized-section-list/directives.js +20 -12
  27. package/build/angular/components/virtualized-section-list/directives.js.map +1 -1
  28. package/build/angular/components/virtualized-section-list/index.js +36 -142
  29. package/build/angular/components/virtualized-section-list/index.js.map +1 -1
  30. package/build/angular/components.d.ts +1 -1
  31. package/build/angular/components.js +1 -1
  32. package/build/angular/components.js.map +1 -1
  33. package/build/angular/create-portal/index.js +9 -8
  34. package/build/angular/create-portal/index.js.map +1 -1
  35. package/build/angular/create-tunnel/index.js +13 -12
  36. package/build/angular/create-tunnel/index.js.map +1 -1
  37. package/build/angular/descriptor-to-angular/index.js +6 -6
  38. package/build/angular/descriptor-to-angular/index.js.map +1 -1
  39. package/build/angular/diagnostics.d.ts +13 -0
  40. package/build/angular/diagnostics.js +18 -0
  41. package/build/angular/diagnostics.js.map +1 -1
  42. package/build/angular/element-props.d.ts +1 -3
  43. package/build/angular/elements.d.ts +46 -32
  44. package/build/angular/elements.js +217 -434
  45. package/build/angular/elements.js.map +1 -1
  46. package/build/angular/index.d.ts +3 -1
  47. package/build/angular/index.js +44 -13
  48. package/build/angular/index.js.map +1 -1
  49. package/build/angular/modules/animated/animated-leaf-binder.js +0 -2
  50. package/build/angular/modules/animated/animated-leaf-binder.js.map +1 -1
  51. package/build/angular/modules/animated/create-animated-component.js +69 -74
  52. package/build/angular/modules/animated/create-animated-component.js.map +1 -1
  53. package/build/angular/modules/animated/index.d.ts +30 -19
  54. package/build/angular/modules/status-bar/index.js +2 -8
  55. package/build/angular/modules/status-bar/index.js.map +1 -1
  56. package/build/angular/primitives/index.d.ts +14 -28
  57. package/build/angular/primitives/index.js +44 -107
  58. package/build/angular/primitives/index.js.map +1 -1
  59. package/build/angular/primitives/shared.d.ts +1 -0
  60. package/build/angular/primitives/shared.js +47 -32
  61. package/build/angular/primitives/shared.js.map +1 -1
  62. package/build/angular/render/index.js +40 -3
  63. package/build/angular/render/index.js.map +1 -1
  64. package/build/angular/renderer/index.d.ts +80 -0
  65. package/build/angular/renderer/index.js +324 -114
  66. package/build/angular/renderer/index.js.map +1 -1
  67. package/build/angular/runtime-matching.d.ts +9 -0
  68. package/build/angular/runtime-matching.js +82 -0
  69. package/build/angular/runtime-matching.js.map +1 -0
  70. package/build/angular/services/color-scheme.service.js +4 -4
  71. package/build/angular/services/color-scheme.service.js.map +1 -1
  72. package/build/angular/services/window-dimensions.service.js +4 -4
  73. package/build/angular/services/window-dimensions.service.js.map +1 -1
  74. package/build/angular/style-host.d.ts +11 -0
  75. package/build/angular/style-host.js +86 -0
  76. package/build/angular/style-host.js.map +1 -0
  77. package/package.json +6 -6
  78. package/src/callback-host.ts +229 -0
  79. package/src/change-detection-flush.ts +62 -9
  80. package/src/components/flat-list/index.ts +2 -1
  81. package/src/components/keyboard-avoiding-view/index.ts +8 -0
  82. package/src/components/pressable-props.ts +8 -0
  83. package/src/components/touchable-props.ts +4 -0
  84. package/src/components/virtualized-list/index.ts +98 -45
  85. package/src/components.ts +0 -2
  86. package/src/diagnostics.ts +18 -0
  87. package/src/element-props.ts +1 -5
  88. package/src/elements.ts +159 -38
  89. package/src/index.ts +47 -14
  90. package/src/primitives/index.ts +14 -70
  91. package/src/primitives/shared.ts +21 -6
  92. package/src/render/index.ts +56 -2
  93. package/src/renderer/index.ts +378 -104
  94. package/src/runtime-matching.ts +80 -0
  95. package/src/style-host.ts +94 -0
  96. package/tsconfig.angular.base.json +11 -0
@@ -0,0 +1,80 @@
1
+ // Keeping a directive for the TEMPLATE CHECKER and taking it out of the runtime.
2
+ //
3
+ // An element directive here exists so `<view [testID]="42">` is a compile error and `<view
4
+ // [testID]="'row'">` is not. That is ngtsc's job, done over the TypeScript source before anything
5
+ // runs. What it costs at RUN time is an instance per element: Angular matches the directive, builds
6
+ // it, injects into it, and calls `ngOnChanges`, which forwards each changed input with
7
+ // `renderer.setProperty(host, name, value)` — the same call `ɵɵproperty` makes DIRECTLY on an element
8
+ // no directive claimed. Same destination, one hop shorter.
9
+ //
10
+ // Measured on JavaScriptCore at ~8.5-9.4 us per element (`core/engine/cpp/tests/js/
11
+ // angular-directive-cost.itest.ts`, the `a directive at all` row), and between the two Angular bench
12
+ // arms at ~86 ms on a thousand-row screen — the largest single cost this adapter still carries.
13
+ //
14
+ // HOW: `findDirectiveDefMatches` walks `tView.directiveRegistry` and asks `isNodeMatchingSelectorList`
15
+ // with `def.selectors` (upstream `render3/instructions/shared.ts:466-501`). A def whose selector list
16
+ // is EMPTY is registered, iterated, and never matched — while its class, its decorator and its
17
+ // declared inputs are untouched, which is all the template checker reads. So this is one mutation per
18
+ // class at module load rather than a build step: no linker output to rewrite, and it behaves the same
19
+ // under JIT as under the partial-compiled path a device runs.
20
+ //
21
+ // WHAT A WITHHELD DIRECTIVE STOPS DOING, stated in full because none of it fails a type check:
22
+ //
23
+ // the forward `ngOnChanges` no longer runs. `ɵɵproperty` writes the same name and value
24
+ // straight to the renderer, so the engine sees the identical prop — asserted by
25
+ // census in the ladder (`unmatched` reads created=10002 setProps=10002).
26
+ // the `on*` wrap an `on*` PROP is called by the ENGINE, so Angular learns nothing unless
27
+ // something marks the view. That moved to `SymbioteRenderer.setProperty`, which
28
+ // every path reaches whether a directive claimed the binding or not.
29
+ // the style shadow `[style]` and `[class]` are no longer claimed by an input, so `ɵɵstyleMap` and
30
+ // `ɵɵclassMap` resolve them per KEY again — ~7 000 renderer calls on the bench
31
+ // row where the shadow made 4 000. That is a REGRESSION and it is accepted: the
32
+ // bare bench arm already pays it and is still ~86 ms ahead of the matched one.
33
+ //
34
+ // WHICH IS WHY THE LIST IS NOT "ALL OF THEM". A directive with real runtime behaviour must go on
35
+ // matching, and here that is the `ValueChangeElement` chain (`text-input`, `switch` — they listen for
36
+ // the engine's value event and register a view flush) and `refresh-control`. `SymbioteCallbackHost`
37
+ // and the two form accessors match on ATTRIBUTES rather than on a tag, so they land only where they
38
+ // are needed and are never withheld.
39
+
40
+ /**
41
+ * Take these directives out of Angular's runtime matcher, leaving ngtsc's view of them untouched.
42
+ *
43
+ * Idempotent, and silent on anything that is not a compiled directive — a class whose `ɵdir` cannot
44
+ * be read is left alone rather than reported, because the only way to reach that state is a build
45
+ * that never compiled it, which fails far louder elsewhere.
46
+ */
47
+ export function withholdFromRuntimeMatching(types: readonly unknown[]): void {
48
+ for (const type of types) {
49
+ if (typeof type !== 'function') continue;
50
+ const definition: unknown = Reflect.get(type, 'ɵdir');
51
+ if (typeof definition !== 'object' || definition === null) continue;
52
+ const selectors: unknown = Reflect.get(definition, 'selectors');
53
+ if (!Array.isArray(selectors)) continue;
54
+ // Ivy holds a `CssSelectorList` — one array per alternative, whose first entry is the tag name
55
+ // when the alternative names one. Recorded before the emptying, because afterwards there is
56
+ // nothing to read and the guard below would have no subject.
57
+ for (const alternative of selectors)
58
+ if (Array.isArray(alternative)) {
59
+ const tag: unknown = alternative[0];
60
+ if (typeof tag === 'string' && tag.length > 0) withheldTags.add(tag);
61
+ }
62
+ // Emptied in place rather than replaced: the def holds this array, and reading `ɵdir` under JIT
63
+ // is what compiles the directive, so the object in hand is the one the matcher will walk.
64
+ selectors.length = 0;
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Every TAG that lost its directive, for the guard that keeps `SymbioteStyleHost` covering them.
70
+ *
71
+ * A withheld tag whose style is claimed by nothing sends an RN style ARRAY into Angular's styling
72
+ * engine, which throws inside change detection — a device failure with no compile-time tell. So the
73
+ * set is recorded here at the moment it is emptied rather than maintained as a second list, and
74
+ * `style-host-covers-withheld-tags.test.ts` reads it.
75
+ */
76
+ const withheldTags = new Set<string>();
77
+
78
+ export function tagsWithheldFromRuntimeMatching(): ReadonlySet<string> {
79
+ return withheldTags;
80
+ }
@@ -0,0 +1,94 @@
1
+ // The directive that claims `[style]` and `[class]` on the elements that bind them.
2
+ //
3
+ // IT IS A CORRECTNESS REQUIREMENT BEFORE IT IS AN OPTIMISATION, which is what separates it from
4
+ // `callback-host.ts`. An RN `StyleProp` is allowed to be an ARRAY, and Angular's own styling engine
5
+ // cannot represent one: `ɵɵstyleMap` decomposes the value key by key, so `applyStyling` takes each
6
+ // array MEMBER as a style key and throws inside change detection. Device-diagnosed 2026-09-02 on
7
+ // ImageBackground, and pinned ever since by `renderer/style-input.test.ts`.
8
+ //
9
+ // A declared input is what keeps the value away from that engine — `checkStylingMap` hands the whole
10
+ // value to the input instead of walking it (`render3/instructions/styling.ts:259-288`). So when the
11
+ // tag directives were withheld from runtime matching, the claim had to come from somewhere, or an
12
+ // array style would throw on every screen that writes one.
13
+ //
14
+ // AND IT IS ALSO THE FASTER PATH, which is why `class` rides the same selector. Unclaimed, a style
15
+ // object reaches the renderer once per KEY — measured at ~7 000 renderer calls on the thousand-row
16
+ // bench row against 4 000 when an input claims it.
17
+ //
18
+ // MATCHED ON THE TAG, and the first spelling of this directive matched on `[style],[class]` instead
19
+ // — which does not work, for a reason worth keeping because it is invisible from the outside.
20
+ //
21
+ // `[style]` IS NOT A PROPERTY BINDING. The compiler routes it to `ɵɵstyleMap`, a styling instruction,
22
+ // and the name never lands in the `AttributeMarker.Bindings` run that `findAttrIndexInNode` walks
23
+ // (upstream `node_selector_matcher.ts:240-282`, where `Classes` and `Styles` markers are SKIPPED
24
+ // outright). So a directive selected on `[style]` never matches an element that binds one, and the
25
+ // value goes to the styling engine exactly as if no directive existed. Measured: the AOT fixture in
26
+ // `style-input-aot.test.ts` imports `SYMBIOTE_ELEMENTS`, carries this directive, and still threw
27
+ // `Unsupported styling type: function`. `[onPress]` and every other `on*` name DO work that way,
28
+ // which is why `callback-host.ts` can be attribute-matched and this one cannot.
29
+ //
30
+ // SO IT IS A TAG DIRECTIVE, and it is instantiated per element like the ones it replaces — including
31
+ // on elements that bind no style at all, because a tag selector is the only one that can match. What
32
+ // is bought is its SHAPE: two inputs and two injections against the withheld classes' 279 and three.
33
+ //
34
+ // MEASURED, AND SMALLER THAN THE IDEA PROMISED. On the JavaScriptCore ladder, withholding a directive
35
+ // outright is worth ~70-91 ms over ten thousand elements (`WITHHOLDING it`, 7.0-9.1 us each) — but
36
+ // the adapter cannot take that, because this directive has to stay. What it reads against a bare tag
37
+ // AFTER the change is 58.2 / 58.8 / 71.0 ms, against `a directive at all` at 50.2 / 55.9 / 68.3 in
38
+ // the same runs. Those two are the same number: a directive costs what a directive costs, thin or
39
+ // fat, and nearly all of it is matching and instantiating rather than inputs or injections.
40
+ //
41
+ // So the change is worth the FAT-TO-THIN difference and not the whole crossing — the ladder's own
42
+ // `279 inputs, not 1` (~19.9 ms) plus `the ChangeDetectorRef` (~10-31 ms). Recorded at that size
43
+ // rather than at the one the plan was aimed at.
44
+ //
45
+ // NO `ChangeDetectorRef` HERE. A style write is an ordinary prop write and marks nothing; the view
46
+ // marking belongs to the callback path and lives with it.
47
+ //
48
+ // THE FOUR TAGS THAT KEEP THEIR OWN DIRECTIVE ARE ABSENT from the selector — `text-input`,
49
+ // `text-input-multiline`, `switch` and `refresh-control`. Their fat directive still matches and
50
+ // already declares `style`, and two directives claiming one input both receive it and both forward
51
+ // it, which is the `unchanged` double write this adapter has measured before.
52
+
53
+ import {
54
+ Directive,
55
+ ElementRef,
56
+ Renderer2,
57
+ type OnChanges,
58
+ type SimpleChanges,
59
+ inject,
60
+ } from '@angular/core';
61
+
62
+ /** Exported for the guard, which derives the same set from the withheld directives' own selectors. */
63
+ export const STYLE_HOST_SELECTOR =
64
+ 'view,symbiote-view,text,symbiote-text,image,symbiote-image,image-background,' +
65
+ 'pressable,symbiote-pressable,button,symbiote-button,touchable-opacity,touchable-highlight,' +
66
+ 'touchable-native-feedback,touchable-without-feedback,scroll-view,horizontal-scroll-view,' +
67
+ 'scroll-content,horizontal-scroll-content,activity-indicator,activity-indicator-spinner,' +
68
+ 'safe-area-view,modal,symbiote-modal,sticky-header,input-accessory-view';
69
+
70
+ @Directive({
71
+ selector:
72
+ 'view,symbiote-view,text,symbiote-text,image,symbiote-image,image-background,' +
73
+ 'pressable,symbiote-pressable,button,symbiote-button,touchable-opacity,touchable-highlight,' +
74
+ 'touchable-native-feedback,touchable-without-feedback,scroll-view,horizontal-scroll-view,' +
75
+ 'scroll-content,horizontal-scroll-content,activity-indicator,activity-indicator-spinner,' +
76
+ 'safe-area-view,modal,symbiote-modal,sticky-header,input-accessory-view',
77
+ // Spelled out rather than referenced: ngtsc statically evaluates decorator metadata, and a
78
+ // computed selector makes it report the class as not standalone — see `callback-host.ts`.
79
+ inputs: ['style', 'class'],
80
+ standalone: true,
81
+ })
82
+ export class SymbioteStyleHost implements OnChanges {
83
+ private readonly renderer = inject(Renderer2);
84
+ private readonly host = inject(ElementRef);
85
+
86
+ ngOnChanges(changes: SimpleChanges): void {
87
+ for (const name of Object.keys(changes))
88
+ this.renderer.setProperty(
89
+ this.host.nativeElement,
90
+ name,
91
+ changes[name]?.currentValue,
92
+ );
93
+ }
94
+ }
@@ -9,6 +9,17 @@
9
9
  "compilerOptions": {
10
10
  "target": "ES2022",
11
11
  "module": "ES2022",
12
+ // ANGULAR'S OWN SETTING, and it defaults the other way at this target. Every `@Input()` on a
13
+ // directive is a bare field DECLARATION, so with ES2022 class-field semantics each one is a real
14
+ // `[[Define]]` on every instance — `SymbioteElement` declares 279 of them, and a screen written
15
+ // against `SYMBIOTE_ELEMENTS` instantiates a directive per TAG. Angular's own integration
16
+ // configs carry this line beside the same target (`.vendors/angular/integration/*/tsconfig.json`),
17
+ // and a declaration with no initializer emits nothing at all under it.
18
+ //
19
+ // It is a CORRECTNESS setting there before it is a speed one: a subclass field declaration
20
+ // re-defines an inherited property to `undefined` under define semantics, which is how an input
21
+ // set before construction finishes gets silently erased.
22
+ "useDefineForClassFields": false,
12
23
  "moduleResolution": "Bundler",
13
24
  "strict": true,
14
25
  "skipLibCheck": true,