@symbiote-native/angular 0.8.0 → 2.0.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/README.md +13 -10
- package/babel-register-composed.cjs +58 -18
- package/bin/symbiote-angular-dev.cjs +16 -4
- package/build/angular/anchor-host-registry.js +1 -15
- package/build/angular/anchor-host-registry.js.map +1 -1
- package/build/angular/change-detection-flush.d.ts +31 -0
- package/build/angular/change-detection-flush.js +110 -0
- package/build/angular/change-detection-flush.js.map +1 -0
- package/build/angular/components/activity-indicator-props.d.ts +2 -0
- package/build/angular/components/activity-indicator-props.js +2 -0
- package/build/angular/components/activity-indicator-props.js.map +1 -0
- package/build/angular/components/button-props.d.ts +2 -0
- package/build/angular/components/button-props.js +2 -0
- package/build/angular/components/button-props.js.map +1 -0
- package/build/angular/components/flat-list/index.d.ts +2 -1
- package/build/angular/components/flat-list/index.js +22 -10
- package/build/angular/components/flat-list/index.js.map +1 -1
- package/build/angular/components/image-background-props.d.ts +6 -0
- package/build/angular/components/image-background-props.js +2 -0
- package/build/angular/components/image-background-props.js.map +1 -0
- package/build/angular/components/{image/shared.d.ts → image-shared.d.ts} +4 -4
- package/build/angular/components/{image/shared.js → image-shared.js} +39 -28
- package/build/angular/components/image-shared.js.map +1 -0
- package/build/angular/components/input-accessory-view-props.d.ts +7 -0
- package/build/angular/components/input-accessory-view-props.js +9 -0
- package/build/angular/components/input-accessory-view-props.js.map +1 -0
- package/build/angular/components/keyboard-avoiding-view/index.d.ts +2 -1
- package/build/angular/components/keyboard-avoiding-view/index.js +28 -27
- package/build/angular/components/keyboard-avoiding-view/index.js.map +1 -1
- package/build/angular/components/modal/index.d.ts +2 -0
- package/build/angular/components/modal/index.js +33 -20
- package/build/angular/components/modal/index.js.map +1 -1
- package/build/angular/components/pressable-props.d.ts +32 -0
- package/build/angular/components/pressable-props.js +11 -0
- package/build/angular/components/pressable-props.js.map +1 -0
- package/build/angular/components/refresh-control-props.d.ts +16 -0
- package/build/angular/components/refresh-control-props.js +14 -0
- package/build/angular/components/refresh-control-props.js.map +1 -0
- package/build/angular/components/safe-area-view-props.d.ts +7 -0
- package/build/angular/components/safe-area-view-props.js +11 -0
- package/build/angular/components/safe-area-view-props.js.map +1 -0
- package/build/angular/components/scroll-view-props.d.ts +76 -0
- package/build/angular/components/scroll-view-props.js +18 -0
- package/build/angular/components/scroll-view-props.js.map +1 -0
- package/build/angular/components/section-list/index.d.ts +3 -0
- package/build/angular/components/section-list/index.js +10 -1
- package/build/angular/components/section-list/index.js.map +1 -1
- package/build/angular/components/text-input-props.d.ts +45 -0
- package/build/angular/components/text-input-props.js +18 -0
- package/build/angular/components/text-input-props.js.map +1 -0
- package/build/angular/components/touchable-native-feedback/touchable-native-feedback-props.d.ts +6 -0
- package/build/angular/components/touchable-native-feedback/touchable-native-feedback-props.js +2 -0
- package/build/angular/components/touchable-native-feedback/touchable-native-feedback-props.js.map +1 -0
- package/build/angular/components/touchable-props.d.ts +14 -0
- package/build/angular/components/touchable-props.js +13 -0
- package/build/angular/components/touchable-props.js.map +1 -0
- package/build/angular/components/touchable-without-feedback/touchable-without-feedback-props.d.ts +3 -0
- package/build/angular/components/touchable-without-feedback/touchable-without-feedback-props.js +2 -0
- package/build/angular/components/touchable-without-feedback/touchable-without-feedback-props.js.map +1 -0
- package/build/angular/components/virtualized-list/index.d.ts +8 -1
- package/build/angular/components/virtualized-list/index.js +373 -278
- package/build/angular/components/virtualized-list/index.js.map +1 -1
- package/build/angular/components/virtualized-section-list/index.d.ts +3 -0
- package/build/angular/components/virtualized-section-list/index.js +10 -1
- package/build/angular/components/virtualized-section-list/index.js.map +1 -1
- package/build/angular/components.d.ts +21 -27
- package/build/angular/components.js +13 -15
- package/build/angular/components.js.map +1 -1
- package/build/angular/create-portal/index.d.ts +11 -2
- package/build/angular/create-portal/index.js +37 -11
- package/build/angular/create-portal/index.js.map +1 -1
- package/build/angular/create-tunnel/index.js +1 -1
- package/build/angular/element-props.d.ts +53 -0
- package/build/angular/element-props.js +2 -0
- package/build/angular/element-props.js.map +1 -0
- package/build/angular/elements.d.ts +528 -0
- package/build/angular/elements.js +1444 -0
- package/build/angular/elements.js.map +1 -0
- package/build/angular/gate-demand.d.ts +37 -0
- package/build/angular/gate-demand.js +84 -0
- package/build/angular/gate-demand.js.map +1 -0
- package/build/angular/host-instance/index.js +1 -1
- package/build/angular/index.d.ts +5 -2
- package/build/angular/index.js +16 -1
- package/build/angular/index.js.map +1 -1
- package/build/angular/modules/animated/create-animated-component.d.ts +6 -6
- package/build/angular/modules/animated/create-animated-component.js +73 -81
- package/build/angular/modules/animated/create-animated-component.js.map +1 -1
- package/build/angular/modules/animated/index.js +1 -1
- package/build/angular/modules/animated/index.js.map +1 -1
- package/build/angular/modules/image/index.d.ts +3 -0
- package/build/angular/modules/image/index.js +16 -0
- package/build/angular/modules/image/index.js.map +1 -0
- package/build/angular/primitives/index.d.ts +28 -16
- package/build/angular/primitives/index.js +77 -31
- package/build/angular/primitives/index.js.map +1 -1
- package/build/angular/primitives/shared.d.ts +2 -3
- package/build/angular/primitives/shared.js +8 -53
- package/build/angular/primitives/shared.js.map +1 -1
- package/build/angular/register.d.ts +1 -0
- package/build/angular/register.js +16 -0
- package/build/angular/register.js.map +1 -0
- package/build/angular/render/index.js +4 -2
- package/build/angular/render/index.js.map +1 -1
- package/build/angular/renderer/index.js +207 -60
- package/build/angular/renderer/index.js.map +1 -1
- package/build/angular/renderer/value-change.d.ts +2 -0
- package/build/angular/renderer/value-change.js +9 -0
- package/build/angular/renderer/value-change.js.map +1 -0
- package/metro-config.cjs +23 -5
- package/metro-css-parser.cjs +2 -1
- package/package.json +17 -7
- package/src/anchor-host-registry.ts +1 -15
- package/src/change-detection-flush.ts +120 -0
- package/src/components/activity-indicator-props.ts +21 -0
- package/src/components/button-props.ts +26 -0
- package/src/components/flat-list/index.ts +12 -4
- package/src/components/image-background-props.ts +24 -0
- package/src/components/{image/shared.ts → image-shared.ts} +57 -26
- package/src/components/input-accessory-view-props.ts +21 -0
- package/src/components/keyboard-avoiding-view/index.ts +27 -16
- package/src/components/modal/index.ts +32 -11
- package/src/components/pressable-props.ts +73 -0
- package/src/components/refresh-control-props.ts +43 -0
- package/src/components/safe-area-view-props.ts +28 -0
- package/src/components/scroll-view-props.ts +109 -0
- package/src/components/section-list/index.ts +18 -0
- package/src/components/text-input-props.ts +72 -0
- package/src/components/touchable-native-feedback/touchable-native-feedback-props.ts +23 -0
- package/src/components/touchable-props.ts +30 -0
- package/src/components/touchable-without-feedback/touchable-without-feedback-props.ts +21 -0
- package/src/components/virtualized-list/index.ts +254 -153
- package/src/components/virtualized-section-list/index.ts +18 -0
- package/src/components.ts +86 -35
- package/src/create-portal/index.ts +39 -9
- package/src/create-tunnel/index.ts +1 -1
- package/src/diagnostics.ts +1 -1
- package/src/element-props.ts +90 -0
- package/src/elements.ts +919 -0
- package/src/gate-demand.ts +109 -0
- package/src/host-instance/index.ts +1 -1
- package/src/index.ts +54 -15
- package/src/modules/animated/create-animated-component.ts +73 -59
- package/src/modules/animated/index.ts +1 -1
- package/src/modules/image/index.ts +18 -0
- package/src/primitives/index.ts +47 -16
- package/src/primitives/shared.ts +11 -44
- package/src/register.ts +15 -0
- package/src/render/index.ts +4 -2
- package/src/renderer/index.ts +239 -80
- package/src/renderer/value-change.ts +8 -0
- package/build/angular/components/activity-indicator/index.android.d.ts +0 -13
- package/build/angular/components/activity-indicator/index.android.js +0 -29
- package/build/angular/components/activity-indicator/index.android.js.map +0 -1
- package/build/angular/components/activity-indicator/index.d.ts +0 -1
- package/build/angular/components/activity-indicator/index.ios.d.ts +0 -9
- package/build/angular/components/activity-indicator/index.ios.js +0 -26
- package/build/angular/components/activity-indicator/index.ios.js.map +0 -1
- package/build/angular/components/activity-indicator/index.js +0 -4
- package/build/angular/components/activity-indicator/index.js.map +0 -1
- package/build/angular/components/activity-indicator/shared.d.ts +0 -67
- package/build/angular/components/activity-indicator/shared.js +0 -260
- package/build/angular/components/activity-indicator/shared.js.map +0 -1
- package/build/angular/components/button.d.ts +0 -65
- package/build/angular/components/button.js +0 -351
- package/build/angular/components/button.js.map +0 -1
- package/build/angular/components/image/index.android.d.ts +0 -15
- package/build/angular/components/image/index.android.js +0 -91
- package/build/angular/components/image/index.android.js.map +0 -1
- package/build/angular/components/image/index.d.ts +0 -1
- package/build/angular/components/image/index.ios.d.ts +0 -15
- package/build/angular/components/image/index.ios.js +0 -91
- package/build/angular/components/image/index.ios.js.map +0 -1
- package/build/angular/components/image/index.js +0 -3
- package/build/angular/components/image/index.js.map +0 -1
- package/build/angular/components/image/shared.js.map +0 -1
- package/build/angular/components/image-background.d.ts +0 -82
- package/build/angular/components/image-background.js +0 -434
- package/build/angular/components/image-background.js.map +0 -1
- package/build/angular/components/input-accessory-view/index.d.ts +0 -65
- package/build/angular/components/input-accessory-view/index.js +0 -290
- package/build/angular/components/input-accessory-view/index.js.map +0 -1
- package/build/angular/components/pressable/index.d.ts +0 -129
- package/build/angular/components/pressable/index.js +0 -473
- package/build/angular/components/pressable/index.js.map +0 -1
- package/build/angular/components/refresh-control/index.d.ts +0 -90
- package/build/angular/components/refresh-control/index.js +0 -380
- package/build/angular/components/refresh-control/index.js.map +0 -1
- package/build/angular/components/safe-area-view/index.d.ts +0 -62
- package/build/angular/components/safe-area-view/index.js +0 -287
- package/build/angular/components/safe-area-view/index.js.map +0 -1
- package/build/angular/components/scroll-view/index.android.d.ts +0 -20
- package/build/angular/components/scroll-view/index.android.js +0 -262
- package/build/angular/components/scroll-view/index.android.js.map +0 -1
- package/build/angular/components/scroll-view/index.d.ts +0 -3
- package/build/angular/components/scroll-view/index.ios.d.ts +0 -8
- package/build/angular/components/scroll-view/index.ios.js +0 -92
- package/build/angular/components/scroll-view/index.ios.js.map +0 -1
- package/build/angular/components/scroll-view/index.js +0 -6
- package/build/angular/components/scroll-view/index.js.map +0 -1
- package/build/angular/components/scroll-view/projection.d.ts +0 -67
- package/build/angular/components/scroll-view/projection.js +0 -560
- package/build/angular/components/scroll-view/projection.js.map +0 -1
- package/build/angular/components/scroll-view/shared.d.ts +0 -239
- package/build/angular/components/scroll-view/shared.js +0 -832
- package/build/angular/components/scroll-view/shared.js.map +0 -1
- package/build/angular/components/scroll-view/sticky-header.d.ts +0 -40
- package/build/angular/components/scroll-view/sticky-header.js +0 -224
- package/build/angular/components/scroll-view/sticky-header.js.map +0 -1
- package/build/angular/components/switch/index.android.d.ts +0 -24
- package/build/angular/components/switch/index.android.js +0 -112
- package/build/angular/components/switch/index.android.js.map +0 -1
- package/build/angular/components/switch/index.d.ts +0 -1
- package/build/angular/components/switch/index.ios.d.ts +0 -23
- package/build/angular/components/switch/index.ios.js +0 -111
- package/build/angular/components/switch/index.ios.js.map +0 -1
- package/build/angular/components/switch/index.js +0 -4
- package/build/angular/components/switch/index.js.map +0 -1
- package/build/angular/components/switch/shared.d.ts +0 -49
- package/build/angular/components/switch/shared.js +0 -191
- package/build/angular/components/switch/shared.js.map +0 -1
- package/build/angular/components/text-input.d.ts +0 -161
- package/build/angular/components/text-input.js +0 -659
- package/build/angular/components/text-input.js.map +0 -1
- package/build/angular/components/touchable/index.d.ts +0 -276
- package/build/angular/components/touchable/index.js +0 -1422
- package/build/angular/components/touchable/index.js.map +0 -1
- package/build/angular/components/touchable-native-feedback/index.d.ts +0 -86
- package/build/angular/components/touchable-native-feedback/index.js +0 -407
- package/build/angular/components/touchable-native-feedback/index.js.map +0 -1
- package/src/components/activity-indicator/index.android.ts +0 -23
- package/src/components/activity-indicator/index.ios.ts +0 -21
- package/src/components/activity-indicator/index.ts +0 -4
- package/src/components/activity-indicator/shared.ts +0 -225
- package/src/components/button.ts +0 -220
- package/src/components/image/index.android.ts +0 -99
- package/src/components/image/index.ios.ts +0 -99
- package/src/components/image/index.ts +0 -3
- package/src/components/image-background.ts +0 -290
- package/src/components/input-accessory-view/index.ts +0 -240
- package/src/components/pressable/index.ts +0 -462
- package/src/components/refresh-control/index.ts +0 -340
- package/src/components/safe-area-view/index.ts +0 -231
- package/src/components/scroll-view/index.android.ts +0 -205
- package/src/components/scroll-view/index.ios.ts +0 -81
- package/src/components/scroll-view/index.ts +0 -7
- package/src/components/scroll-view/projection.ts +0 -713
- package/src/components/scroll-view/shared.ts +0 -1066
- package/src/components/scroll-view/sticky-header.ts +0 -267
- package/src/components/switch/index.android.ts +0 -113
- package/src/components/switch/index.ios.ts +0 -112
- package/src/components/switch/index.ts +0 -4
- package/src/components/switch/shared.ts +0 -284
- package/src/components/text-input.ts +0 -613
- package/src/components/touchable/index.ts +0 -913
- package/src/components/touchable-native-feedback/index.ts +0 -317
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
// in ngAfterViewChecked. OnPush + ChangeDetectorRef.markForCheck() drives re-render off the native
|
|
10
10
|
// scroll/layout/measure callbacks (they fire outside Angular's own event bindings, so they must
|
|
11
11
|
// mark the view dirty). This is the Angular twin of the React useReducer/useEffect and Vue
|
|
12
|
-
// ref/computed/watch over the same shared functions. It
|
|
13
|
-
// as the React/Vue lists drive their
|
|
12
|
+
// ref/computed/watch over the same shared functions. It writes the `scroll-view`/
|
|
13
|
+
// `horizontal-scroll-view` TAG directly, exactly as the React/Vue lists drive their scroll tag.
|
|
14
14
|
//
|
|
15
15
|
// Per-item rendering is TEMPLATES, not a callback: React/Vue `renderItem: (info) => element` does
|
|
16
16
|
// not translate to Angular. The app supplies the cell via `<ng-template vListItem>` (and the
|
|
@@ -22,22 +22,27 @@
|
|
|
22
22
|
// shape differs, since a callback prop returning an element has no Angular equivalent.
|
|
23
23
|
//
|
|
24
24
|
// Lists have no Descriptor render fn — the cell content is the framework's own children.
|
|
25
|
-
// Cells/spacers are plain
|
|
25
|
+
// Cells/spacers are plain view host elements.
|
|
26
26
|
import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ElementRef, EventEmitter, Input, Output, ViewChild, computed, inject, signal, } from '@angular/core';
|
|
27
|
-
import { DEFAULT_END_REACHED_THRESHOLD, DEFAULT_INITIAL_NUM_TO_RENDER, DEFAULT_MAX_TO_RENDER_PER_BATCH, DEFAULT_START_REACHED_THRESHOLD, DEFAULT_UPDATE_CELLS_BATCHING_PERIOD, DEFAULT_WINDOW_SIZE, EMPTY_OFFSET, FIRST_INDEX, INVERTED_X_STYLE, INVERTED_Y_STYLE, buildListPlan, buildViewabilityPairs, computeWindow, createInitialListState, isSeparatorGapInRange, listEffectSignature, readLayoutLength, readLayoutOffset, readScrollOffset, reduceList, resolveAccessibilityProps, resolveItemKey, } from '@symbiote-native/components';
|
|
28
|
-
import { dlog, flattenStyle, } from '@symbiote-native/engine';
|
|
27
|
+
import { DEFAULT_END_REACHED_THRESHOLD, DEFAULT_INITIAL_NUM_TO_RENDER, DEFAULT_MAX_TO_RENDER_PER_BATCH, DEFAULT_START_REACHED_THRESHOLD, DEFAULT_UPDATE_CELLS_BATCHING_PERIOD, DEFAULT_WINDOW_SIZE, EMPTY_OFFSET, FIRST_INDEX, INVERTED_X_STYLE, INVERTED_Y_STYLE, buildListPlan, buildViewabilityPairs, computeWindow, createInitialListState, isSeparatorGapInRange, listEffectSignature, readLayoutLength, readLayoutOffset, readScrollOffset, reduceList, resolveAccessibilityProps, resolveItemKey, buildScrollViewHandle, } from '@symbiote-native/components';
|
|
28
|
+
import { dlog, flattenStyle, isSymbioteNode, } from '@symbiote-native/engine';
|
|
29
29
|
import { countAngular } from '../../diagnostics';
|
|
30
|
-
import {
|
|
31
|
-
import { RefreshControl } from '../refresh-control';
|
|
32
|
-
import { anchorHostStyle, SymbioteStyleInputDirective, ViewHost, } from '../../primitives';
|
|
30
|
+
import { anchorHostStyle, SymbioteHostPropsDirective, SymbioteStyleInputDirective, ViewHost, } from '../../primitives';
|
|
33
31
|
import { VListEmptyDirective, VListFooterDirective, VListHeaderDirective, VListItemDirective, VListOutletDirective, VListSeparatorDirective, } from './directives';
|
|
32
|
+
import { gateWanted, injectGateDemandAbove, } from '../../gate-demand';
|
|
34
33
|
import * as i0 from "@angular/core";
|
|
35
34
|
import * as i1 from "../../primitives/shared";
|
|
36
35
|
export { VListEmptyDirective, VListFooterDirective, VListHeaderDirective, VListItemDirective, VListSeparatorDirective, } from './directives';
|
|
36
|
+
// The scroll body is identical on both axes; only the outer tag differs (the axis comes from the
|
|
37
|
+
// TAG, never a prop — `<horizontal-scroll-view>` vs `<scroll-view>`, matching every other adapter,
|
|
38
|
+
// `.claude/rules/host-primitive-tier.md`). No shared-outlet primitive exists to de-duplicate this
|
|
39
|
+
// (this package's own NgTemplateOutlet twin is keyed on a per-cell context, not a static block, and
|
|
40
|
+
// ngc's partial evaluator needs the decorator's `template` to be one literal), so the body is
|
|
41
|
+
// duplicated across the two branches — the same shape Svelte's own `{#if horizontal}` takes.
|
|
37
42
|
export class VirtualizedList {
|
|
38
43
|
// The list's edge/viewability/failure events as real Angular events: `(endReached)="…"`, not
|
|
39
44
|
// `[onEndReached]="…"`. See handleRefresh/accessibility*Tick below for how the still-callback-
|
|
40
|
-
// shaped
|
|
45
|
+
// shaped scroll-view/refresh-control bag keys are fed from these.
|
|
41
46
|
endReached = new EventEmitter();
|
|
42
47
|
startReached = new EventEmitter();
|
|
43
48
|
refresh = new EventEmitter();
|
|
@@ -47,6 +52,13 @@ export class VirtualizedList {
|
|
|
47
52
|
accessibilityTap = new EventEmitter();
|
|
48
53
|
magicTap = new EventEmitter();
|
|
49
54
|
accessibilityEscape = new EventEmitter();
|
|
55
|
+
// This wrapper binds the four gated accessibility events on the component it renders, which
|
|
56
|
+
// Angular forces to be unconditional and which would light that component's gates on every
|
|
57
|
+
// instance. It answers for them instead — see `gate-demand.ts`.
|
|
58
|
+
gateDemandAbove = injectGateDemandAbove();
|
|
59
|
+
wantsGate(name) {
|
|
60
|
+
return gateWanted(this.gateDemandAbove, name, this[name]);
|
|
61
|
+
}
|
|
50
62
|
data;
|
|
51
63
|
getItem;
|
|
52
64
|
getItemCount;
|
|
@@ -129,10 +141,19 @@ export class VirtualizedList {
|
|
|
129
141
|
footerDir;
|
|
130
142
|
emptyDir;
|
|
131
143
|
separatorDir;
|
|
132
|
-
// The
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
|
|
144
|
+
// The inner scroll TAG's own engine node — a template ref on a bare intrinsic hands back the
|
|
145
|
+
// host node directly (`isSymbioteNode(elementRef.nativeElement)`, matching `anchorHostStyle`'s
|
|
146
|
+
// own reader below), so there is no wrapper component left to read a handle off. Present from
|
|
147
|
+
// whichever branch (`scroll-view` / `horizontal-scroll-view`) is currently mounted, once the
|
|
148
|
+
// template has run at least one CD pass.
|
|
149
|
+
scrollHostRef;
|
|
150
|
+
get scrollNode() {
|
|
151
|
+
const node = this.scrollHostRef?.nativeElement;
|
|
152
|
+
return isSymbioteNode(node) ? node : null;
|
|
153
|
+
}
|
|
154
|
+
// `buildScrollViewHandle` reads the node lazily through the getter above and no-ops before
|
|
155
|
+
// commit — the same function every other adapter feeds from its own host-instance accessor.
|
|
156
|
+
scrollHandle = buildScrollViewHandle(() => this.scrollNode);
|
|
136
157
|
// --- template-bound view state, assembled in ngDoCheck (recomputeView) ---
|
|
137
158
|
itemCount = EMPTY_OFFSET;
|
|
138
159
|
windowCells = [];
|
|
@@ -201,12 +222,12 @@ export class VirtualizedList {
|
|
|
201
222
|
lastRecompute = undefined;
|
|
202
223
|
cdr = inject(ChangeDetectorRef);
|
|
203
224
|
// This component's OWN host — the non-painting anchor `class="..."` at the use site resolves
|
|
204
|
-
// onto (see anchorHostStyle's doc comment) — NOT `
|
|
205
|
-
// inner `<
|
|
225
|
+
// onto (see anchorHostStyle's doc comment) — NOT `scrollHostRef` above, which targets the real
|
|
226
|
+
// inner `<scroll-view>`/`<horizontal-scroll-view>` tag one level down.
|
|
206
227
|
elementRef = inject(ElementRef);
|
|
207
|
-
// Bridges the non-reactive @Input fields into the reactive graph so
|
|
208
|
-
// memoize a bag derived from them (
|
|
209
|
-
//
|
|
228
|
+
// Bridges the non-reactive @Input fields into the reactive graph so `foldedAccessibility` below
|
|
229
|
+
// can memoize a bag derived from them (read its own comment for why signal inputs are not an
|
|
230
|
+
// option while this package's unit suite runs on JIT).
|
|
210
231
|
//
|
|
211
232
|
// ONLY safe for a bag whose every dependency is an @Input. This list's window machinery
|
|
212
233
|
// (listState, renderVersion, windowCells, the measured metrics) is driven from scroll/layout
|
|
@@ -215,12 +236,13 @@ export class VirtualizedList {
|
|
|
215
236
|
// instead of widening this one.
|
|
216
237
|
inputsRevision = signal(0, /* @ts-ignore */
|
|
217
238
|
...(ngDevMode ? [{ debugName: "inputsRevision" }] : /* istanbul ignore next */ []));
|
|
218
|
-
// MEASURED: the template
|
|
219
|
-
// not cache a getter across binding expressions - as a getter it
|
|
220
|
-
// refresh, and every consumer saw a fresh reference, so all 20
|
|
221
|
-
//
|
|
222
|
-
// input actually changes. Every dependency below is an @Input, which is what makes
|
|
223
|
-
// inputsRevision a complete dependency set here
|
|
239
|
+
// MEASURED (while ScrollView was still a wrapper): the template fed 20 separate inputs off this
|
|
240
|
+
// one bag, and Angular does not cache a getter across binding expressions - as a getter it
|
|
241
|
+
// rebuilt the object 20 times per refresh, and every consumer saw a fresh reference, so all 20
|
|
242
|
+
// reported "changed" and wrote through. A computed evaluates once and hands back the SAME object
|
|
243
|
+
// until an input actually changes. Every dependency below is an @Input, which is what makes
|
|
244
|
+
// inputsRevision a complete dependency set here — `scrollViewBag()` reuses this result rather
|
|
245
|
+
// than re-running the fold.
|
|
224
246
|
foldedAccessibility = computed(() => {
|
|
225
247
|
this.inputsRevision();
|
|
226
248
|
return resolveAccessibilityProps({
|
|
@@ -265,6 +287,54 @@ export class VirtualizedList {
|
|
|
265
287
|
get shouldRenderRefreshControl() {
|
|
266
288
|
return this.refreshRequested ?? this.refresh.observed;
|
|
267
289
|
}
|
|
290
|
+
// The flat bag fed to the scroll tag via `[symbioteHostProps]`. A plain method, not `computed()`:
|
|
291
|
+
// most of these fields (resolvedStyle, renderedStickyIndices, commandedOffset, …) are recomputed
|
|
292
|
+
// in ngDoCheck/dispatch as ordinary fields, not signals, so a computed() memoized only against
|
|
293
|
+
// `inputsRevision` would go stale the moment one of them changed outside an @Input() write. The
|
|
294
|
+
// directive itself diffs per key before writing to the engine (`primitives/shared.ts`), so a
|
|
295
|
+
// fresh object every CD pass costs nothing beyond the literal allocation.
|
|
296
|
+
scrollViewBag() {
|
|
297
|
+
return {
|
|
298
|
+
...this.foldedAccessibility(),
|
|
299
|
+
style: this.resolvedStyle,
|
|
300
|
+
contentContainerStyle: this.resolvedContentContainerStyle,
|
|
301
|
+
onScroll: this.onScrollTick,
|
|
302
|
+
onLayout: this.onLayoutTick,
|
|
303
|
+
onScrollBeginDrag: this.onScrollBeginDrag,
|
|
304
|
+
onScrollEndDrag: this.onScrollEndDrag,
|
|
305
|
+
onMomentumScrollBegin: this.onMomentumScrollBegin,
|
|
306
|
+
onMomentumScrollEnd: this.onMomentumScrollEnd,
|
|
307
|
+
scrollEventThrottle: this.scrollEventThrottle,
|
|
308
|
+
keyboardShouldPersistTaps: this.keyboardShouldPersistTaps,
|
|
309
|
+
keyboardDismissMode: this.keyboardDismissMode,
|
|
310
|
+
contentOffset: this.commandedOffset,
|
|
311
|
+
stickyHeaderIndices: this.renderedStickyIndices,
|
|
312
|
+
maintainVisibleContentPosition: this.resolvedMaintainVisibleContentPosition,
|
|
313
|
+
// The four boolean-gated Fabric events (`.claude/rules/fabric-boolean-event-gates.md`): the
|
|
314
|
+
// TAG has no `.observed` of its own, so `wantsGate` (own subscriber, or the demand from
|
|
315
|
+
// whatever wraps this list) is what decides whether the flag reaches Fabric at all.
|
|
316
|
+
onAccessibilityAction: this.wantsGate('accessibilityAction')
|
|
317
|
+
? this.accessibilityActionTick
|
|
318
|
+
: undefined,
|
|
319
|
+
onAccessibilityTap: this.wantsGate('accessibilityTap')
|
|
320
|
+
? this.accessibilityTapTick
|
|
321
|
+
: undefined,
|
|
322
|
+
onMagicTap: this.wantsGate('magicTap') ? this.magicTapTick : undefined,
|
|
323
|
+
onAccessibilityEscape: this.wantsGate('accessibilityEscape')
|
|
324
|
+
? this.accessibilityEscapeTick
|
|
325
|
+
: undefined,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
// The `refresh-control` tag's own bag — an ordinary first child, re-parented per platform by the
|
|
329
|
+
// scroll-view behavior (`../refresh-control-props.ts`). `onRefresh` is a plain listener prop, not
|
|
330
|
+
// a gated one: RefreshControl's `refresh` is unaffected by `fabric-boolean-event-gates.md`.
|
|
331
|
+
refreshControlBag() {
|
|
332
|
+
return {
|
|
333
|
+
refreshing: this.refreshing ?? false,
|
|
334
|
+
progressViewOffset: this.progressViewOffset,
|
|
335
|
+
onRefresh: this.handleRefresh,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
268
338
|
get isHorizontal() {
|
|
269
339
|
return this.horizontal === true;
|
|
270
340
|
}
|
|
@@ -289,9 +359,10 @@ export class VirtualizedList {
|
|
|
289
359
|
get onStartReachedThresholdValue() {
|
|
290
360
|
return this.onStartReachedThreshold ?? DEFAULT_START_REACHED_THRESHOLD;
|
|
291
361
|
}
|
|
292
|
-
//
|
|
293
|
-
//
|
|
294
|
-
//
|
|
362
|
+
// onScroll stays a plain callback bag key (an Animated.event(...) target must be able to flow
|
|
363
|
+
// through it, which an @Output() can't carry), so this is a stable arrow field read straight into
|
|
364
|
+
// scrollViewBag()'s `onScroll`. onLayout is unconditional too — this list always needs its own
|
|
365
|
+
// layout for windowing, regardless of whether an app subscribes to anything.
|
|
295
366
|
onScrollTick = (event) => {
|
|
296
367
|
countAngular('scrollTicks');
|
|
297
368
|
const offset = readScrollOffset(event, this.isHorizontal);
|
|
@@ -311,9 +382,9 @@ export class VirtualizedList {
|
|
|
311
382
|
dlog(`Angular VirtualizedList onLayout viewport=${length}`);
|
|
312
383
|
this.dispatch({ kind: 'layout', length });
|
|
313
384
|
};
|
|
314
|
-
// RefreshControl's refresh and
|
|
315
|
-
//
|
|
316
|
-
//
|
|
385
|
+
// RefreshControl's refresh and the scroll tag's accessibility events reach here as plain
|
|
386
|
+
// listener-prop values in scrollViewBag()/refreshControlBag() — these fields just adapt
|
|
387
|
+
// VirtualizedList's own @Output() into a plain re-emit callback.
|
|
317
388
|
handleRefresh = () => {
|
|
318
389
|
this.refresh.emit();
|
|
319
390
|
};
|
|
@@ -781,19 +852,19 @@ export class VirtualizedList {
|
|
|
781
852
|
});
|
|
782
853
|
}
|
|
783
854
|
flashScrollIndicators() {
|
|
784
|
-
this.
|
|
855
|
+
this.scrollHandle.flashScrollIndicators();
|
|
785
856
|
}
|
|
786
857
|
getNativeScrollRef() {
|
|
787
|
-
return this.
|
|
858
|
+
return this.scrollNode !== null ? this.scrollHandle : null;
|
|
788
859
|
}
|
|
789
860
|
getScrollableNode() {
|
|
790
|
-
return this.
|
|
861
|
+
return this.scrollNode !== null ? this.scrollHandle : null;
|
|
791
862
|
}
|
|
792
863
|
getScrollResponder() {
|
|
793
|
-
return this.
|
|
864
|
+
return this.scrollNode !== null ? this.scrollHandle : null;
|
|
794
865
|
}
|
|
795
866
|
getScrollNode() {
|
|
796
|
-
return this.
|
|
867
|
+
return this.scrollHandle.getScrollNode();
|
|
797
868
|
}
|
|
798
869
|
recordInteraction() {
|
|
799
870
|
this.dispatch({ kind: 'record-interaction' });
|
|
@@ -803,9 +874,9 @@ export class VirtualizedList {
|
|
|
803
874
|
const target = this.isHorizontal
|
|
804
875
|
? { x: clamped, y: EMPTY_OFFSET }
|
|
805
876
|
: { x: EMPTY_OFFSET, y: clamped };
|
|
806
|
-
if (this.
|
|
877
|
+
if (this.scrollNode !== null) {
|
|
807
878
|
dlog(`Angular VirtualizedList scrollTo offset=${clamped} animated=${animated} (horizontal=${this.isHorizontal})`);
|
|
808
|
-
this.
|
|
879
|
+
this.scrollHandle.scrollTo({ x: target.x, y: target.y, animated });
|
|
809
880
|
return;
|
|
810
881
|
}
|
|
811
882
|
dlog(`Angular VirtualizedList scrollTo offset=${clamped} pending-ref`);
|
|
@@ -813,136 +884,148 @@ export class VirtualizedList {
|
|
|
813
884
|
this.cdr.markForCheck();
|
|
814
885
|
}
|
|
815
886
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: VirtualizedList, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
816
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: VirtualizedList, isStandalone: true, selector: "VirtualizedList", inputs: { data: "data", getItem: "getItem", getItemCount: "getItemCount", keyExtractor: "keyExtractor", getItemLayout: "getItemLayout", horizontal: "horizontal", inverted: "inverted", extraData: "extraData", onEndReachedThreshold: "onEndReachedThreshold", onStartReachedThreshold: "onStartReachedThreshold", refreshing: "refreshing", progressViewOffset: "progressViewOffset", refreshRequested: "refreshRequested", viewabilityConfig: "viewabilityConfig", viewabilityConfigCallbackPairs: "viewabilityConfigCallbackPairs", initialNumToRender: "initialNumToRender", initialScrollIndex: "initialScrollIndex", maxToRenderPerBatch: "maxToRenderPerBatch", updateCellsBatchingPeriod: "updateCellsBatchingPeriod", windowSize: "windowSize", stickyHeaderIndices: "stickyHeaderIndices", maintainVisibleContentPosition: "maintainVisibleContentPosition", onScroll: "onScroll", onScrollBeginDrag: "onScrollBeginDrag", onScrollEndDrag: "onScrollEndDrag", onMomentumScrollBegin: "onMomentumScrollBegin", onMomentumScrollEnd: "onMomentumScrollEnd", scrollEventThrottle: "scrollEventThrottle", keyboardShouldPersistTaps: "keyboardShouldPersistTaps", keyboardDismissMode: "keyboardDismissMode", style: "style", contentContainerStyle: "contentContainerStyle", testID: "testID", nativeID: "nativeID", accessible: "accessible", accessibilityLabel: "accessibilityLabel", accessibilityHint: "accessibilityHint", accessibilityRole: "accessibilityRole", accessibilityState: "accessibilityState", accessibilityValue: "accessibilityValue", accessibilityActions: "accessibilityActions", accessibilityLabelledBy: "accessibilityLabelledBy", importantForAccessibility: "importantForAccessibility", accessibilityLiveRegion: "accessibilityLiveRegion", screenReaderFocusable: "screenReaderFocusable", accessibilityViewIsModal: "accessibilityViewIsModal", accessibilityElementsHidden: "accessibilityElementsHidden", accessibilityIgnoresInvertColors: "accessibilityIgnoresInvertColors", accessibilityLanguage: "accessibilityLanguage", accessibilityRespondsToUserInteraction: "accessibilityRespondsToUserInteraction", accessibilityShowsLargeContentViewer: "accessibilityShowsLargeContentViewer", accessibilityLargeContentTitle: "accessibilityLargeContentTitle", role: "role", ariaLabel: "ariaLabel", ariaLabelledBy: "ariaLabelledBy", ariaLive: "ariaLive", ariaHidden: "ariaHidden", ariaBusy: "ariaBusy", ariaChecked: "ariaChecked", ariaDisabled: "ariaDisabled", ariaExpanded: "ariaExpanded", ariaSelected: "ariaSelected", ariaModal: "ariaModal", ariaValueMax: "ariaValueMax", ariaValueMin: "ariaValueMin", ariaValueNow: "ariaValueNow", ariaValueText: "ariaValueText" }, outputs: { endReached: "endReached", startReached: "startReached", refresh: "refresh", viewableItemsChanged: "viewableItemsChanged", scrollToIndexFailed: "scrollToIndexFailed", accessibilityAction: "accessibilityAction", accessibilityTap: "accessibilityTap", magicTap: "magicTap", accessibilityEscape: "accessibilityEscape" }, queries: [{ propertyName: "itemDir", first: true, predicate: VListItemDirective, descendants: true }, { propertyName: "headerDir", first: true, predicate: VListHeaderDirective, descendants: true }, { propertyName: "footerDir", first: true, predicate: VListFooterDirective, descendants: true }, { propertyName: "emptyDir", first: true, predicate: VListEmptyDirective, descendants: true }, { propertyName: "separatorDir", first: true, predicate: VListSeparatorDirective, descendants: true }], viewQueries: [{ propertyName: "
|
|
817
|
-
|
|
818
|
-
[
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
[accessibilityLabel]="foldedAccessibility().accessibilityLabel"
|
|
825
|
-
[accessibilityHint]="foldedAccessibility().accessibilityHint"
|
|
826
|
-
[accessibilityRole]="foldedAccessibility().accessibilityRole"
|
|
827
|
-
[accessibilityState]="foldedAccessibility().accessibilityState"
|
|
828
|
-
[accessibilityValue]="foldedAccessibility().accessibilityValue"
|
|
829
|
-
[accessibilityActions]="foldedAccessibility().accessibilityActions"
|
|
830
|
-
[accessibilityLabelledBy]="foldedAccessibility().accessibilityLabelledBy"
|
|
831
|
-
[importantForAccessibility]="
|
|
832
|
-
foldedAccessibility().importantForAccessibility
|
|
833
|
-
"
|
|
834
|
-
[accessibilityLiveRegion]="foldedAccessibility().accessibilityLiveRegion"
|
|
835
|
-
[screenReaderFocusable]="foldedAccessibility().screenReaderFocusable"
|
|
836
|
-
[accessibilityViewIsModal]="
|
|
837
|
-
foldedAccessibility().accessibilityViewIsModal
|
|
838
|
-
"
|
|
839
|
-
[accessibilityElementsHidden]="
|
|
840
|
-
foldedAccessibility().accessibilityElementsHidden
|
|
841
|
-
"
|
|
842
|
-
[accessibilityIgnoresInvertColors]="
|
|
843
|
-
foldedAccessibility().accessibilityIgnoresInvertColors
|
|
844
|
-
"
|
|
845
|
-
[accessibilityLanguage]="foldedAccessibility().accessibilityLanguage"
|
|
846
|
-
[accessibilityRespondsToUserInteraction]="
|
|
847
|
-
foldedAccessibility().accessibilityRespondsToUserInteraction
|
|
848
|
-
"
|
|
849
|
-
[accessibilityShowsLargeContentViewer]="
|
|
850
|
-
foldedAccessibility().accessibilityShowsLargeContentViewer
|
|
851
|
-
"
|
|
852
|
-
[accessibilityLargeContentTitle]="
|
|
853
|
-
foldedAccessibility().accessibilityLargeContentTitle
|
|
854
|
-
"
|
|
855
|
-
(accessibilityAction)="accessibilityActionTick($event)"
|
|
856
|
-
(accessibilityTap)="accessibilityTapTick($event)"
|
|
857
|
-
(magicTap)="magicTapTick($event)"
|
|
858
|
-
(accessibilityEscape)="accessibilityEscapeTick($event)"
|
|
859
|
-
[onScroll]="onScrollTick"
|
|
860
|
-
(layout)="onLayoutTick($event)"
|
|
861
|
-
[onScrollBeginDrag]="onScrollBeginDrag"
|
|
862
|
-
[onScrollEndDrag]="onScrollEndDrag"
|
|
863
|
-
[onMomentumScrollBegin]="onMomentumScrollBegin"
|
|
864
|
-
[onMomentumScrollEnd]="onMomentumScrollEnd"
|
|
865
|
-
[scrollEventThrottle]="scrollEventThrottle"
|
|
866
|
-
[keyboardShouldPersistTaps]="keyboardShouldPersistTaps"
|
|
867
|
-
[keyboardDismissMode]="keyboardDismissMode"
|
|
868
|
-
[contentOffset]="commandedOffset"
|
|
869
|
-
[stickyHeaderIndices]="renderedStickyIndices"
|
|
870
|
-
[maintainVisibleContentPosition]="resolvedMaintainVisibleContentPosition"
|
|
871
|
-
>
|
|
872
|
-
@if (shouldRenderRefreshControl) {
|
|
873
|
-
<RefreshControl
|
|
874
|
-
[refreshing]="refreshing ?? false"
|
|
875
|
-
(refresh)="handleRefresh()"
|
|
876
|
-
[progressViewOffset]="progressViewOffset"
|
|
877
|
-
/>
|
|
878
|
-
}
|
|
887
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: VirtualizedList, isStandalone: true, selector: "VirtualizedList", inputs: { data: "data", getItem: "getItem", getItemCount: "getItemCount", keyExtractor: "keyExtractor", getItemLayout: "getItemLayout", horizontal: "horizontal", inverted: "inverted", extraData: "extraData", onEndReachedThreshold: "onEndReachedThreshold", onStartReachedThreshold: "onStartReachedThreshold", refreshing: "refreshing", progressViewOffset: "progressViewOffset", refreshRequested: "refreshRequested", viewabilityConfig: "viewabilityConfig", viewabilityConfigCallbackPairs: "viewabilityConfigCallbackPairs", initialNumToRender: "initialNumToRender", initialScrollIndex: "initialScrollIndex", maxToRenderPerBatch: "maxToRenderPerBatch", updateCellsBatchingPeriod: "updateCellsBatchingPeriod", windowSize: "windowSize", stickyHeaderIndices: "stickyHeaderIndices", maintainVisibleContentPosition: "maintainVisibleContentPosition", onScroll: "onScroll", onScrollBeginDrag: "onScrollBeginDrag", onScrollEndDrag: "onScrollEndDrag", onMomentumScrollBegin: "onMomentumScrollBegin", onMomentumScrollEnd: "onMomentumScrollEnd", scrollEventThrottle: "scrollEventThrottle", keyboardShouldPersistTaps: "keyboardShouldPersistTaps", keyboardDismissMode: "keyboardDismissMode", style: "style", contentContainerStyle: "contentContainerStyle", testID: "testID", nativeID: "nativeID", accessible: "accessible", accessibilityLabel: "accessibilityLabel", accessibilityHint: "accessibilityHint", accessibilityRole: "accessibilityRole", accessibilityState: "accessibilityState", accessibilityValue: "accessibilityValue", accessibilityActions: "accessibilityActions", accessibilityLabelledBy: "accessibilityLabelledBy", importantForAccessibility: "importantForAccessibility", accessibilityLiveRegion: "accessibilityLiveRegion", screenReaderFocusable: "screenReaderFocusable", accessibilityViewIsModal: "accessibilityViewIsModal", accessibilityElementsHidden: "accessibilityElementsHidden", accessibilityIgnoresInvertColors: "accessibilityIgnoresInvertColors", accessibilityLanguage: "accessibilityLanguage", accessibilityRespondsToUserInteraction: "accessibilityRespondsToUserInteraction", accessibilityShowsLargeContentViewer: "accessibilityShowsLargeContentViewer", accessibilityLargeContentTitle: "accessibilityLargeContentTitle", role: "role", ariaLabel: "ariaLabel", ariaLabelledBy: "ariaLabelledBy", ariaLive: "ariaLive", ariaHidden: "ariaHidden", ariaBusy: "ariaBusy", ariaChecked: "ariaChecked", ariaDisabled: "ariaDisabled", ariaExpanded: "ariaExpanded", ariaSelected: "ariaSelected", ariaModal: "ariaModal", ariaValueMax: "ariaValueMax", ariaValueMin: "ariaValueMin", ariaValueNow: "ariaValueNow", ariaValueText: "ariaValueText" }, outputs: { endReached: "endReached", startReached: "startReached", refresh: "refresh", viewableItemsChanged: "viewableItemsChanged", scrollToIndexFailed: "scrollToIndexFailed", accessibilityAction: "accessibilityAction", accessibilityTap: "accessibilityTap", magicTap: "magicTap", accessibilityEscape: "accessibilityEscape" }, queries: [{ propertyName: "itemDir", first: true, predicate: VListItemDirective, descendants: true }, { propertyName: "headerDir", first: true, predicate: VListHeaderDirective, descendants: true }, { propertyName: "footerDir", first: true, predicate: VListFooterDirective, descendants: true }, { propertyName: "emptyDir", first: true, predicate: VListEmptyDirective, descendants: true }, { propertyName: "separatorDir", first: true, predicate: VListSeparatorDirective, descendants: true }], viewQueries: [{ propertyName: "scrollHostRef", first: true, predicate: ["scrollHost"], descendants: true, read: ElementRef }], usesOnChanges: true, hostDirectives: [{ directive: i1.SymbioteStyleInputDirective, inputs: ["style", "style"] }], ngImport: i0, template: `
|
|
888
|
+
@if (isHorizontal) {
|
|
889
|
+
<horizontal-scroll-view #scrollHost [symbioteHostProps]="scrollViewBag()">
|
|
890
|
+
@if (shouldRenderRefreshControl) {
|
|
891
|
+
<refresh-control
|
|
892
|
+
[symbioteHostProps]="refreshControlBag()"
|
|
893
|
+
></refresh-control>
|
|
894
|
+
}
|
|
879
895
|
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
896
|
+
@if (headerDir !== undefined) {
|
|
897
|
+
<view>
|
|
898
|
+
<ng-container [vListOutlet]="headerDir.templateRef"></ng-container>
|
|
899
|
+
</view>
|
|
900
|
+
}
|
|
885
901
|
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
902
|
+
@if (itemCount === 0) {
|
|
903
|
+
@if (emptyDir !== undefined) {
|
|
904
|
+
<view>
|
|
905
|
+
<ng-container [vListOutlet]="emptyDir.templateRef"></ng-container>
|
|
906
|
+
</view>
|
|
907
|
+
}
|
|
908
|
+
} @else {
|
|
909
|
+
@if (leadingSpacerStyle !== null) {
|
|
910
|
+
<view [style]="leadingSpacerStyle"></view>
|
|
911
|
+
}
|
|
912
|
+
@if (forcedStickyCell !== null) {
|
|
913
|
+
<view
|
|
914
|
+
(layout)="handleCellLayout(forcedStickyCell.measure, $event)"
|
|
915
|
+
[style]="cellStyle"
|
|
916
|
+
>
|
|
917
|
+
<ng-container
|
|
918
|
+
[vListOutlet]="itemDir?.templateRef"
|
|
919
|
+
[vListOutletContext]="forcedStickyCell.context"
|
|
920
|
+
></ng-container>
|
|
921
|
+
</view>
|
|
922
|
+
}
|
|
923
|
+
@if (gapSpacerStyle !== null) {
|
|
924
|
+
<view [style]="gapSpacerStyle"></view>
|
|
925
|
+
}
|
|
926
|
+
@for (cell of windowCells; track cell.key) {
|
|
927
|
+
<view
|
|
928
|
+
(layout)="handleCellLayout(cell.measure, $event)"
|
|
929
|
+
[style]="cellStyle"
|
|
930
|
+
>
|
|
931
|
+
<ng-container
|
|
932
|
+
[vListOutlet]="itemDir?.templateRef"
|
|
933
|
+
[vListOutletContext]="cell.context"
|
|
934
|
+
></ng-container>
|
|
935
|
+
@if (cell.separatorContext !== undefined) {
|
|
936
|
+
<ng-container
|
|
937
|
+
[vListOutlet]="separatorDir?.templateRef"
|
|
938
|
+
[vListOutletContext]="cell.separatorContext"
|
|
939
|
+
></ng-container>
|
|
940
|
+
}
|
|
941
|
+
</view>
|
|
942
|
+
}
|
|
943
|
+
@if (trailingSpacerStyle !== null) {
|
|
944
|
+
<view [style]="trailingSpacerStyle"></view>
|
|
945
|
+
}
|
|
891
946
|
}
|
|
892
|
-
|
|
893
|
-
@if (
|
|
894
|
-
<
|
|
947
|
+
|
|
948
|
+
@if (footerDir !== undefined) {
|
|
949
|
+
<view>
|
|
950
|
+
<ng-container [vListOutlet]="footerDir.templateRef"></ng-container>
|
|
951
|
+
</view>
|
|
895
952
|
}
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
[vListOutletContext]="forcedStickyCell.context"
|
|
904
|
-
></ng-container>
|
|
905
|
-
</symbiote-view>
|
|
953
|
+
</horizontal-scroll-view>
|
|
954
|
+
} @else {
|
|
955
|
+
<scroll-view #scrollHost [symbioteHostProps]="scrollViewBag()">
|
|
956
|
+
@if (shouldRenderRefreshControl) {
|
|
957
|
+
<refresh-control
|
|
958
|
+
[symbioteHostProps]="refreshControlBag()"
|
|
959
|
+
></refresh-control>
|
|
906
960
|
}
|
|
907
|
-
|
|
908
|
-
|
|
961
|
+
|
|
962
|
+
@if (headerDir !== undefined) {
|
|
963
|
+
<view>
|
|
964
|
+
<ng-container [vListOutlet]="headerDir.templateRef"></ng-container>
|
|
965
|
+
</view>
|
|
909
966
|
}
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
@
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
<
|
|
923
|
-
|
|
924
|
-
[
|
|
925
|
-
|
|
926
|
-
@if (cell.separatorContext !== undefined) {
|
|
967
|
+
|
|
968
|
+
@if (itemCount === 0) {
|
|
969
|
+
@if (emptyDir !== undefined) {
|
|
970
|
+
<view>
|
|
971
|
+
<ng-container [vListOutlet]="emptyDir.templateRef"></ng-container>
|
|
972
|
+
</view>
|
|
973
|
+
}
|
|
974
|
+
} @else {
|
|
975
|
+
@if (leadingSpacerStyle !== null) {
|
|
976
|
+
<view [style]="leadingSpacerStyle"></view>
|
|
977
|
+
}
|
|
978
|
+
@if (forcedStickyCell !== null) {
|
|
979
|
+
<view
|
|
980
|
+
(layout)="handleCellLayout(forcedStickyCell.measure, $event)"
|
|
981
|
+
[style]="cellStyle"
|
|
982
|
+
>
|
|
927
983
|
<ng-container
|
|
928
|
-
[vListOutlet]="
|
|
929
|
-
[vListOutletContext]="
|
|
984
|
+
[vListOutlet]="itemDir?.templateRef"
|
|
985
|
+
[vListOutletContext]="forcedStickyCell.context"
|
|
930
986
|
></ng-container>
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
987
|
+
</view>
|
|
988
|
+
}
|
|
989
|
+
@if (gapSpacerStyle !== null) {
|
|
990
|
+
<view [style]="gapSpacerStyle"></view>
|
|
991
|
+
}
|
|
992
|
+
<!-- The separator sits INSIDE the measuring view, as RN's own cell renderer places it
|
|
993
|
+
(VirtualizedListCellRenderer.js:218-221). As a sibling it is an extra flex child, so
|
|
994
|
+
the chrome between two cells is gap + separator + gap while a spacer collapsing that
|
|
995
|
+
region replaces it with a single gap — every cell below the leading spacer then lands
|
|
996
|
+
short by (separator + gap) and the content jumps by that much whenever the window's
|
|
997
|
+
first index moves. Measured at exactly 17px on device 2026-08-19; see
|
|
998
|
+
.claude/rules/list-geometry-feedback-loop.md. -->
|
|
999
|
+
@for (cell of windowCells; track cell.key) {
|
|
1000
|
+
<view
|
|
1001
|
+
(layout)="handleCellLayout(cell.measure, $event)"
|
|
1002
|
+
[style]="cellStyle"
|
|
1003
|
+
>
|
|
1004
|
+
<ng-container
|
|
1005
|
+
[vListOutlet]="itemDir?.templateRef"
|
|
1006
|
+
[vListOutletContext]="cell.context"
|
|
1007
|
+
></ng-container>
|
|
1008
|
+
@if (cell.separatorContext !== undefined) {
|
|
1009
|
+
<ng-container
|
|
1010
|
+
[vListOutlet]="separatorDir?.templateRef"
|
|
1011
|
+
[vListOutletContext]="cell.separatorContext"
|
|
1012
|
+
></ng-container>
|
|
1013
|
+
}
|
|
1014
|
+
</view>
|
|
1015
|
+
}
|
|
1016
|
+
@if (trailingSpacerStyle !== null) {
|
|
1017
|
+
<view [style]="trailingSpacerStyle"></view>
|
|
1018
|
+
}
|
|
936
1019
|
}
|
|
937
|
-
}
|
|
938
1020
|
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
1021
|
+
@if (footerDir !== undefined) {
|
|
1022
|
+
<view>
|
|
1023
|
+
<ng-container [vListOutlet]="footerDir.templateRef"></ng-container>
|
|
1024
|
+
</view>
|
|
1025
|
+
}
|
|
1026
|
+
</scroll-view>
|
|
1027
|
+
}
|
|
1028
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: SymbioteHostPropsDirective, selector: "[symbioteHostProps]", inputs: ["symbioteHostProps"], exportAs: ["symbioteHost"] }, { kind: "directive", type: VListOutletDirective, selector: "[vListOutlet]", inputs: ["vListOutlet", "vListOutletContext"] }, { kind: "component", type: ViewHost, selector: "view" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
946
1029
|
}
|
|
947
1030
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: VirtualizedList, decorators: [{
|
|
948
1031
|
type: Component,
|
|
@@ -953,137 +1036,149 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
953
1036
|
{ directive: SymbioteStyleInputDirective, inputs: ['style'] },
|
|
954
1037
|
],
|
|
955
1038
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
956
|
-
imports: [
|
|
1039
|
+
imports: [SymbioteHostPropsDirective, VListOutletDirective, ViewHost],
|
|
957
1040
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
958
1041
|
template: `
|
|
959
|
-
|
|
960
|
-
[
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
[accessibilityLabel]="foldedAccessibility().accessibilityLabel"
|
|
967
|
-
[accessibilityHint]="foldedAccessibility().accessibilityHint"
|
|
968
|
-
[accessibilityRole]="foldedAccessibility().accessibilityRole"
|
|
969
|
-
[accessibilityState]="foldedAccessibility().accessibilityState"
|
|
970
|
-
[accessibilityValue]="foldedAccessibility().accessibilityValue"
|
|
971
|
-
[accessibilityActions]="foldedAccessibility().accessibilityActions"
|
|
972
|
-
[accessibilityLabelledBy]="foldedAccessibility().accessibilityLabelledBy"
|
|
973
|
-
[importantForAccessibility]="
|
|
974
|
-
foldedAccessibility().importantForAccessibility
|
|
975
|
-
"
|
|
976
|
-
[accessibilityLiveRegion]="foldedAccessibility().accessibilityLiveRegion"
|
|
977
|
-
[screenReaderFocusable]="foldedAccessibility().screenReaderFocusable"
|
|
978
|
-
[accessibilityViewIsModal]="
|
|
979
|
-
foldedAccessibility().accessibilityViewIsModal
|
|
980
|
-
"
|
|
981
|
-
[accessibilityElementsHidden]="
|
|
982
|
-
foldedAccessibility().accessibilityElementsHidden
|
|
983
|
-
"
|
|
984
|
-
[accessibilityIgnoresInvertColors]="
|
|
985
|
-
foldedAccessibility().accessibilityIgnoresInvertColors
|
|
986
|
-
"
|
|
987
|
-
[accessibilityLanguage]="foldedAccessibility().accessibilityLanguage"
|
|
988
|
-
[accessibilityRespondsToUserInteraction]="
|
|
989
|
-
foldedAccessibility().accessibilityRespondsToUserInteraction
|
|
990
|
-
"
|
|
991
|
-
[accessibilityShowsLargeContentViewer]="
|
|
992
|
-
foldedAccessibility().accessibilityShowsLargeContentViewer
|
|
993
|
-
"
|
|
994
|
-
[accessibilityLargeContentTitle]="
|
|
995
|
-
foldedAccessibility().accessibilityLargeContentTitle
|
|
996
|
-
"
|
|
997
|
-
(accessibilityAction)="accessibilityActionTick($event)"
|
|
998
|
-
(accessibilityTap)="accessibilityTapTick($event)"
|
|
999
|
-
(magicTap)="magicTapTick($event)"
|
|
1000
|
-
(accessibilityEscape)="accessibilityEscapeTick($event)"
|
|
1001
|
-
[onScroll]="onScrollTick"
|
|
1002
|
-
(layout)="onLayoutTick($event)"
|
|
1003
|
-
[onScrollBeginDrag]="onScrollBeginDrag"
|
|
1004
|
-
[onScrollEndDrag]="onScrollEndDrag"
|
|
1005
|
-
[onMomentumScrollBegin]="onMomentumScrollBegin"
|
|
1006
|
-
[onMomentumScrollEnd]="onMomentumScrollEnd"
|
|
1007
|
-
[scrollEventThrottle]="scrollEventThrottle"
|
|
1008
|
-
[keyboardShouldPersistTaps]="keyboardShouldPersistTaps"
|
|
1009
|
-
[keyboardDismissMode]="keyboardDismissMode"
|
|
1010
|
-
[contentOffset]="commandedOffset"
|
|
1011
|
-
[stickyHeaderIndices]="renderedStickyIndices"
|
|
1012
|
-
[maintainVisibleContentPosition]="resolvedMaintainVisibleContentPosition"
|
|
1013
|
-
>
|
|
1014
|
-
@if (shouldRenderRefreshControl) {
|
|
1015
|
-
<RefreshControl
|
|
1016
|
-
[refreshing]="refreshing ?? false"
|
|
1017
|
-
(refresh)="handleRefresh()"
|
|
1018
|
-
[progressViewOffset]="progressViewOffset"
|
|
1019
|
-
/>
|
|
1020
|
-
}
|
|
1042
|
+
@if (isHorizontal) {
|
|
1043
|
+
<horizontal-scroll-view #scrollHost [symbioteHostProps]="scrollViewBag()">
|
|
1044
|
+
@if (shouldRenderRefreshControl) {
|
|
1045
|
+
<refresh-control
|
|
1046
|
+
[symbioteHostProps]="refreshControlBag()"
|
|
1047
|
+
></refresh-control>
|
|
1048
|
+
}
|
|
1021
1049
|
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1050
|
+
@if (headerDir !== undefined) {
|
|
1051
|
+
<view>
|
|
1052
|
+
<ng-container [vListOutlet]="headerDir.templateRef"></ng-container>
|
|
1053
|
+
</view>
|
|
1054
|
+
}
|
|
1027
1055
|
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1056
|
+
@if (itemCount === 0) {
|
|
1057
|
+
@if (emptyDir !== undefined) {
|
|
1058
|
+
<view>
|
|
1059
|
+
<ng-container [vListOutlet]="emptyDir.templateRef"></ng-container>
|
|
1060
|
+
</view>
|
|
1061
|
+
}
|
|
1062
|
+
} @else {
|
|
1063
|
+
@if (leadingSpacerStyle !== null) {
|
|
1064
|
+
<view [style]="leadingSpacerStyle"></view>
|
|
1065
|
+
}
|
|
1066
|
+
@if (forcedStickyCell !== null) {
|
|
1067
|
+
<view
|
|
1068
|
+
(layout)="handleCellLayout(forcedStickyCell.measure, $event)"
|
|
1069
|
+
[style]="cellStyle"
|
|
1070
|
+
>
|
|
1071
|
+
<ng-container
|
|
1072
|
+
[vListOutlet]="itemDir?.templateRef"
|
|
1073
|
+
[vListOutletContext]="forcedStickyCell.context"
|
|
1074
|
+
></ng-container>
|
|
1075
|
+
</view>
|
|
1076
|
+
}
|
|
1077
|
+
@if (gapSpacerStyle !== null) {
|
|
1078
|
+
<view [style]="gapSpacerStyle"></view>
|
|
1079
|
+
}
|
|
1080
|
+
@for (cell of windowCells; track cell.key) {
|
|
1081
|
+
<view
|
|
1082
|
+
(layout)="handleCellLayout(cell.measure, $event)"
|
|
1083
|
+
[style]="cellStyle"
|
|
1084
|
+
>
|
|
1085
|
+
<ng-container
|
|
1086
|
+
[vListOutlet]="itemDir?.templateRef"
|
|
1087
|
+
[vListOutletContext]="cell.context"
|
|
1088
|
+
></ng-container>
|
|
1089
|
+
@if (cell.separatorContext !== undefined) {
|
|
1090
|
+
<ng-container
|
|
1091
|
+
[vListOutlet]="separatorDir?.templateRef"
|
|
1092
|
+
[vListOutletContext]="cell.separatorContext"
|
|
1093
|
+
></ng-container>
|
|
1094
|
+
}
|
|
1095
|
+
</view>
|
|
1096
|
+
}
|
|
1097
|
+
@if (trailingSpacerStyle !== null) {
|
|
1098
|
+
<view [style]="trailingSpacerStyle"></view>
|
|
1099
|
+
}
|
|
1033
1100
|
}
|
|
1034
|
-
|
|
1035
|
-
@if (
|
|
1036
|
-
<
|
|
1101
|
+
|
|
1102
|
+
@if (footerDir !== undefined) {
|
|
1103
|
+
<view>
|
|
1104
|
+
<ng-container [vListOutlet]="footerDir.templateRef"></ng-container>
|
|
1105
|
+
</view>
|
|
1037
1106
|
}
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
[vListOutletContext]="forcedStickyCell.context"
|
|
1046
|
-
></ng-container>
|
|
1047
|
-
</symbiote-view>
|
|
1107
|
+
</horizontal-scroll-view>
|
|
1108
|
+
} @else {
|
|
1109
|
+
<scroll-view #scrollHost [symbioteHostProps]="scrollViewBag()">
|
|
1110
|
+
@if (shouldRenderRefreshControl) {
|
|
1111
|
+
<refresh-control
|
|
1112
|
+
[symbioteHostProps]="refreshControlBag()"
|
|
1113
|
+
></refresh-control>
|
|
1048
1114
|
}
|
|
1049
|
-
|
|
1050
|
-
|
|
1115
|
+
|
|
1116
|
+
@if (headerDir !== undefined) {
|
|
1117
|
+
<view>
|
|
1118
|
+
<ng-container [vListOutlet]="headerDir.templateRef"></ng-container>
|
|
1119
|
+
</view>
|
|
1051
1120
|
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
@
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
<
|
|
1065
|
-
|
|
1066
|
-
[
|
|
1067
|
-
|
|
1068
|
-
@if (cell.separatorContext !== undefined) {
|
|
1121
|
+
|
|
1122
|
+
@if (itemCount === 0) {
|
|
1123
|
+
@if (emptyDir !== undefined) {
|
|
1124
|
+
<view>
|
|
1125
|
+
<ng-container [vListOutlet]="emptyDir.templateRef"></ng-container>
|
|
1126
|
+
</view>
|
|
1127
|
+
}
|
|
1128
|
+
} @else {
|
|
1129
|
+
@if (leadingSpacerStyle !== null) {
|
|
1130
|
+
<view [style]="leadingSpacerStyle"></view>
|
|
1131
|
+
}
|
|
1132
|
+
@if (forcedStickyCell !== null) {
|
|
1133
|
+
<view
|
|
1134
|
+
(layout)="handleCellLayout(forcedStickyCell.measure, $event)"
|
|
1135
|
+
[style]="cellStyle"
|
|
1136
|
+
>
|
|
1069
1137
|
<ng-container
|
|
1070
|
-
[vListOutlet]="
|
|
1071
|
-
[vListOutletContext]="
|
|
1138
|
+
[vListOutlet]="itemDir?.templateRef"
|
|
1139
|
+
[vListOutletContext]="forcedStickyCell.context"
|
|
1072
1140
|
></ng-container>
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1141
|
+
</view>
|
|
1142
|
+
}
|
|
1143
|
+
@if (gapSpacerStyle !== null) {
|
|
1144
|
+
<view [style]="gapSpacerStyle"></view>
|
|
1145
|
+
}
|
|
1146
|
+
<!-- The separator sits INSIDE the measuring view, as RN's own cell renderer places it
|
|
1147
|
+
(VirtualizedListCellRenderer.js:218-221). As a sibling it is an extra flex child, so
|
|
1148
|
+
the chrome between two cells is gap + separator + gap while a spacer collapsing that
|
|
1149
|
+
region replaces it with a single gap — every cell below the leading spacer then lands
|
|
1150
|
+
short by (separator + gap) and the content jumps by that much whenever the window's
|
|
1151
|
+
first index moves. Measured at exactly 17px on device 2026-08-19; see
|
|
1152
|
+
.claude/rules/list-geometry-feedback-loop.md. -->
|
|
1153
|
+
@for (cell of windowCells; track cell.key) {
|
|
1154
|
+
<view
|
|
1155
|
+
(layout)="handleCellLayout(cell.measure, $event)"
|
|
1156
|
+
[style]="cellStyle"
|
|
1157
|
+
>
|
|
1158
|
+
<ng-container
|
|
1159
|
+
[vListOutlet]="itemDir?.templateRef"
|
|
1160
|
+
[vListOutletContext]="cell.context"
|
|
1161
|
+
></ng-container>
|
|
1162
|
+
@if (cell.separatorContext !== undefined) {
|
|
1163
|
+
<ng-container
|
|
1164
|
+
[vListOutlet]="separatorDir?.templateRef"
|
|
1165
|
+
[vListOutletContext]="cell.separatorContext"
|
|
1166
|
+
></ng-container>
|
|
1167
|
+
}
|
|
1168
|
+
</view>
|
|
1169
|
+
}
|
|
1170
|
+
@if (trailingSpacerStyle !== null) {
|
|
1171
|
+
<view [style]="trailingSpacerStyle"></view>
|
|
1172
|
+
}
|
|
1078
1173
|
}
|
|
1079
|
-
}
|
|
1080
1174
|
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1175
|
+
@if (footerDir !== undefined) {
|
|
1176
|
+
<view>
|
|
1177
|
+
<ng-container [vListOutlet]="footerDir.templateRef"></ng-container>
|
|
1178
|
+
</view>
|
|
1179
|
+
}
|
|
1180
|
+
</scroll-view>
|
|
1181
|
+
}
|
|
1087
1182
|
`,
|
|
1088
1183
|
}]
|
|
1089
1184
|
}], propDecorators: { endReached: [{
|
|
@@ -1256,8 +1351,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
1256
1351
|
}], separatorDir: [{
|
|
1257
1352
|
type: ContentChild,
|
|
1258
1353
|
args: [VListSeparatorDirective]
|
|
1259
|
-
}],
|
|
1354
|
+
}], scrollHostRef: [{
|
|
1260
1355
|
type: ViewChild,
|
|
1261
|
-
args: [
|
|
1356
|
+
args: ['scrollHost', { read: ElementRef }]
|
|
1262
1357
|
}] } });
|
|
1263
1358
|
//# sourceMappingURL=index.js.map
|