@symbiote-native/angular 3.2.0 → 3.4.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/babel-register-composed.cjs +2 -0
- package/build/angular/accessibility-inputs.d.ts +2 -1
- package/build/angular/accessibility-inputs.js +4 -1
- package/build/angular/accessibility-inputs.js.map +1 -1
- package/build/angular/callback-host.d.ts +1 -1
- package/build/angular/callback-host.js +57 -9
- package/build/angular/callback-host.js.map +1 -1
- package/build/angular/components/checkbox-props.d.ts +2 -0
- package/build/angular/components/checkbox-props.js +2 -0
- package/build/angular/components/checkbox-props.js.map +1 -0
- package/build/angular/components/flat-list/flat-list-handle.d.ts +35 -0
- package/build/angular/components/flat-list/flat-list-handle.js +48 -0
- package/build/angular/components/flat-list/flat-list-handle.js.map +1 -0
- package/build/angular/components/flat-list/flat-list-template.d.ts +1 -1
- package/build/angular/components/flat-list/flat-list-template.js +16 -0
- package/build/angular/components/flat-list/flat-list-template.js.map +1 -1
- package/build/angular/components/flat-list/index.d.ts +6 -30
- package/build/angular/components/flat-list/index.js +6 -36
- package/build/angular/components/flat-list/index.js.map +1 -1
- package/build/angular/components/image-inputs.d.ts +4 -0
- package/build/angular/components/image-inputs.js +81 -0
- package/build/angular/components/image-inputs.js.map +1 -0
- package/build/angular/components/image-props-resolve.d.ts +3 -0
- package/build/angular/components/image-props-resolve.js +133 -0
- package/build/angular/components/image-props-resolve.js.map +1 -0
- package/build/angular/components/image-shared.d.ts +8 -6
- package/build/angular/components/image-shared.js +23 -212
- package/build/angular/components/image-shared.js.map +1 -1
- package/build/angular/components/input-accessory-view-props.d.ts +4 -4
- package/build/angular/components/keyboard-avoiding-view/index.d.ts +3 -6
- package/build/angular/components/keyboard-avoiding-view/index.js +29 -56
- package/build/angular/components/keyboard-avoiding-view/index.js.map +1 -1
- package/build/angular/components/layout-conformance-props.d.ts +4 -0
- package/build/angular/components/layout-conformance-props.js +2 -0
- package/build/angular/components/layout-conformance-props.js.map +1 -0
- package/build/angular/components/modal/index.d.ts +6 -5
- package/build/angular/components/modal/index.js +17 -2
- package/build/angular/components/modal/index.js.map +1 -1
- package/build/angular/components/refresh-control-props.d.ts +7 -7
- package/build/angular/components/scroll-view-props.d.ts +8 -5
- package/build/angular/components/section-list/index.d.ts +17 -8
- package/build/angular/components/section-list/index.js +37 -28
- package/build/angular/components/section-list/index.js.map +1 -1
- package/build/angular/components/touchable-props.d.ts +2 -2
- package/build/angular/components/virtualized-list/cell-renderer-context.d.ts +12 -0
- package/build/angular/components/virtualized-list/cell-renderer-context.js +16 -0
- package/build/angular/components/virtualized-list/cell-renderer-context.js.map +1 -0
- package/build/angular/components/virtualized-list/directives.d.ts +26 -6
- package/build/angular/components/virtualized-list/directives.js +59 -5
- package/build/angular/components/virtualized-list/directives.js.map +1 -1
- package/build/angular/components/virtualized-list/index.d.ts +19 -15
- package/build/angular/components/virtualized-list/index.js +91 -61
- package/build/angular/components/virtualized-list/index.js.map +1 -1
- package/build/angular/components/virtualized-list/list-bags.d.ts +5 -1
- package/build/angular/components/virtualized-list/list-bags.js +31 -19
- package/build/angular/components/virtualized-list/list-bags.js.map +1 -1
- package/build/angular/components/virtualized-list/list-effects.d.ts +2 -2
- package/build/angular/components/virtualized-list/list-effects.js +17 -27
- package/build/angular/components/virtualized-list/list-effects.js.map +1 -1
- package/build/angular/components/virtualized-list/list-handle.d.ts +4 -1
- package/build/angular/components/virtualized-list/list-handle.js +13 -3
- package/build/angular/components/virtualized-list/list-handle.js.map +1 -1
- package/build/angular/components/virtualized-list/list-inputs.d.ts +19 -5
- package/build/angular/components/virtualized-list/list-inputs.js +47 -4
- package/build/angular/components/virtualized-list/list-inputs.js.map +1 -1
- package/build/angular/components/virtualized-list/list-props.d.ts +9 -3
- package/build/angular/components/virtualized-list/list-rows.d.ts +25 -0
- package/build/angular/components/virtualized-list/list-rows.js +25 -0
- package/build/angular/components/virtualized-list/list-rows.js.map +1 -0
- package/build/angular/components/virtualized-list/list-styles.d.ts +4 -0
- package/build/angular/components/virtualized-list/list-styles.js +10 -2
- package/build/angular/components/virtualized-list/list-styles.js.map +1 -1
- package/build/angular/components/virtualized-list/list-template.d.ts +1 -1
- package/build/angular/components/virtualized-list/list-template.js +159 -73
- package/build/angular/components/virtualized-list/list-template.js.map +1 -1
- package/build/angular/components/virtualized-list/nested-scope.d.ts +6 -0
- package/build/angular/components/virtualized-list/nested-scope.js +17 -0
- package/build/angular/components/virtualized-list/nested-scope.js.map +1 -0
- package/build/angular/components/virtualized-section-list/directives.d.ts +21 -9
- package/build/angular/components/virtualized-section-list/directives.js +8 -28
- package/build/angular/components/virtualized-section-list/directives.js.map +1 -1
- package/build/angular/components/virtualized-section-list/index.d.ts +39 -60
- package/build/angular/components/virtualized-section-list/index.js +67 -33
- package/build/angular/components/virtualized-section-list/index.js.map +1 -1
- package/build/angular/components/virtualized-section-list/section-cell.d.ts +29 -0
- package/build/angular/components/virtualized-section-list/section-cell.js +82 -0
- package/build/angular/components/virtualized-section-list/section-cell.js.map +1 -0
- package/build/angular/components/virtualized-section-list/section-list-props.d.ts +56 -0
- package/build/angular/components/virtualized-section-list/section-list-props.js +3 -0
- package/build/angular/components/virtualized-section-list/section-list-props.js.map +1 -0
- package/build/angular/components/virtualized-section-list/section-list-template.d.ts +1 -1
- package/build/angular/components/virtualized-section-list/section-list-template.js +34 -26
- package/build/angular/components/virtualized-section-list/section-list-template.js.map +1 -1
- package/build/angular/components.d.ts +8 -3
- package/build/angular/components.js +2 -1
- package/build/angular/components.js.map +1 -1
- package/build/angular/descriptor-to-angular/descriptor-host.d.ts +8 -0
- package/build/angular/descriptor-to-angular/descriptor-host.js +38 -0
- package/build/angular/descriptor-to-angular/descriptor-host.js.map +1 -0
- package/build/angular/descriptor-to-angular/index.d.ts +1 -0
- package/build/angular/descriptor-to-angular/index.js +4 -0
- package/build/angular/descriptor-to-angular/index.js.map +1 -1
- package/build/angular/element-base.d.ts +169 -0
- package/build/angular/element-base.js +304 -0
- package/build/angular/element-base.js.map +1 -0
- package/build/angular/element-props.d.ts +7 -7
- package/build/angular/element-text-input-native.d.ts +27 -0
- package/build/angular/element-text-input-native.js +52 -0
- package/build/angular/element-text-input-native.js.map +1 -0
- package/build/angular/elements-controls.d.ts +171 -0
- package/build/angular/elements-controls.js +394 -0
- package/build/angular/elements-controls.js.map +1 -0
- package/build/angular/elements-layout.d.ts +139 -0
- package/build/angular/elements-layout.js +276 -0
- package/build/angular/elements-layout.js.map +1 -0
- package/build/angular/elements-press.d.ts +103 -0
- package/build/angular/elements-press.js +239 -0
- package/build/angular/elements-press.js.map +1 -0
- package/build/angular/elements.d.ts +6 -516
- package/build/angular/elements.js +9 -1091
- package/build/angular/elements.js.map +1 -1
- package/build/angular/host-instance/index.d.ts +2 -0
- package/build/angular/host-instance/index.js +4 -4
- package/build/angular/host-instance/index.js.map +1 -1
- package/build/angular/index.d.ts +17 -9
- package/build/angular/index.js +24 -27
- package/build/angular/index.js.map +1 -1
- package/build/angular/modules/animated/create-animated-component.d.ts +1 -1
- package/build/angular/modules/animated/create-animated-component.js +1 -1
- package/build/angular/modules/animated/create-animated-component.js.map +1 -1
- package/build/angular/modules/animated/index.d.ts +54 -41
- package/build/angular/modules/animated/index.js +2 -26
- package/build/angular/modules/animated/index.js.map +1 -1
- package/build/angular/modules/app-registry/index.d.ts +3 -3
- package/build/angular/modules/app-registry/index.js +2 -2
- package/build/angular/modules/app-registry/index.js.map +1 -1
- package/build/angular/modules/status-bar/index.d.ts +9 -5
- package/build/angular/modules/status-bar/index.js +21 -10
- package/build/angular/modules/status-bar/index.js.map +1 -1
- package/build/angular/native-view-base.d.ts +10 -2
- package/build/angular/native-view-base.js +23 -2
- package/build/angular/native-view-base.js.map +1 -1
- package/build/angular/primitives/index.d.ts +4 -0
- package/build/angular/primitives/index.js +12 -0
- package/build/angular/primitives/index.js.map +1 -1
- package/build/angular/services/color-scheme.service.d.ts +1 -1
- package/build/angular/services/index.d.ts +1 -0
- package/build/angular/services/index.js +1 -0
- package/build/angular/services/index.js.map +1 -1
- package/build/angular/services/inject-event.d.ts +6 -0
- package/build/angular/services/inject-event.js +24 -0
- package/build/angular/services/inject-event.js.map +1 -0
- package/package.json +6 -6
- package/src/accessibility-inputs.ts +3 -0
- package/src/callback-host.ts +56 -8
- package/src/components/checkbox-props.ts +12 -0
- package/src/components/flat-list/flat-list-handle.ts +64 -0
- package/src/components/flat-list/flat-list-template.ts +16 -0
- package/src/components/flat-list/index.ts +6 -48
- package/src/components/image-inputs.ts +94 -0
- package/src/components/image-props-resolve.ts +172 -0
- package/src/components/image-shared.ts +26 -291
- package/src/components/input-accessory-view-props.ts +12 -8
- package/src/components/keyboard-avoiding-view/index.ts +39 -79
- package/src/components/layout-conformance-props.ts +8 -0
- package/src/components/modal/index.ts +42 -20
- package/src/components/refresh-control-props.ts +29 -25
- package/src/components/scroll-view-props.ts +84 -76
- package/src/components/section-list/index.ts +34 -37
- package/src/components/touchable-props.ts +6 -2
- package/src/components/virtualized-list/cell-renderer-context.ts +36 -0
- package/src/components/virtualized-list/directives.ts +84 -8
- package/src/components/virtualized-list/index.ts +118 -85
- package/src/components/virtualized-list/list-bags.ts +38 -19
- package/src/components/virtualized-list/list-effects.ts +20 -32
- package/src/components/virtualized-list/list-handle.ts +17 -4
- package/src/components/virtualized-list/list-inputs.ts +47 -0
- package/src/components/virtualized-list/list-props.ts +57 -50
- package/src/components/virtualized-list/list-rows.ts +66 -0
- package/src/components/virtualized-list/list-styles.ts +30 -1
- package/src/components/virtualized-list/list-template.ts +159 -73
- package/src/components/virtualized-list/nested-scope.ts +22 -0
- package/src/components/virtualized-section-list/directives.ts +41 -40
- package/src/components/virtualized-section-list/index.ts +114 -118
- package/src/components/virtualized-section-list/section-cell.ts +109 -0
- package/src/components/virtualized-section-list/section-list-props.ts +79 -0
- package/src/components/virtualized-section-list/section-list-template.ts +34 -26
- package/src/components.ts +9 -0
- package/src/descriptor-to-angular/descriptor-host.ts +27 -0
- package/src/descriptor-to-angular/index.ts +5 -0
- package/src/element-base.ts +289 -0
- package/src/element-props.ts +35 -33
- package/src/element-text-input-native.ts +31 -0
- package/src/elements-controls.ts +290 -0
- package/src/elements-layout.ts +168 -0
- package/src/elements-press.ts +153 -0
- package/src/elements.ts +76 -788
- package/src/host-instance/index.ts +4 -4
- package/src/index.ts +78 -23
- package/src/modules/animated/index.ts +2 -47
- package/src/modules/app-registry/index.ts +3 -1
- package/src/modules/status-bar/index.ts +24 -11
- package/src/native-view-base.ts +37 -2
- package/src/primitives/index.ts +7 -0
- package/src/services/color-scheme.service.ts +1 -1
- package/src/services/index.ts +1 -0
- package/src/services/inject-event.ts +52 -0
|
@@ -30,36 +30,60 @@ import {
|
|
|
30
30
|
TemplateRef,
|
|
31
31
|
ViewContainerRef,
|
|
32
32
|
inject,
|
|
33
|
+
reflectComponentType,
|
|
34
|
+
type ComponentRef,
|
|
33
35
|
type EmbeddedViewRef,
|
|
34
36
|
type OnChanges,
|
|
35
37
|
type OnDestroy,
|
|
36
38
|
type SimpleChanges,
|
|
39
|
+
type Type,
|
|
37
40
|
} from '@angular/core';
|
|
38
|
-
import { dlog, isDebug } from '@symbiote-native/engine';
|
|
41
|
+
import { dlog, isDebug, type IViewStyle } from '@symbiote-native/engine';
|
|
39
42
|
import { countAngular } from '../../diagnostics';
|
|
40
43
|
import type { ISeparators } from '@symbiote-native/components';
|
|
41
44
|
|
|
42
45
|
let vListOutletInstanceCounter = 0;
|
|
43
46
|
|
|
47
|
+
function nextOutletInstanceId(): number {
|
|
48
|
+
vListOutletInstanceCounter += 1;
|
|
49
|
+
return vListOutletInstanceCounter;
|
|
50
|
+
}
|
|
51
|
+
|
|
44
52
|
// The context a `vListItem` template receives, mirroring RN's renderItem info arg
|
|
45
53
|
// ({ item, index, separators }). `$implicit` is the item, so `let-item` (no key) binds it.
|
|
46
|
-
export
|
|
54
|
+
export type IVListItemContext<ItemT> = {
|
|
47
55
|
$implicit: ItemT;
|
|
48
56
|
index: number;
|
|
49
57
|
separators: ISeparators;
|
|
50
|
-
}
|
|
58
|
+
};
|
|
51
59
|
|
|
52
60
|
// The context a `vListSeparator` template receives, mirroring RN's ItemSeparatorComponent props
|
|
53
61
|
// (the highlight flag the cell toggles + the items flanking the gap). `$implicit` is the highlight
|
|
54
62
|
// flag, so `let-highlighted` binds it.
|
|
55
|
-
export
|
|
63
|
+
export type IVListSeparatorContext<ItemT> = {
|
|
56
64
|
$implicit: boolean;
|
|
57
65
|
highlighted: boolean;
|
|
58
66
|
leadingItem?: ItemT;
|
|
59
67
|
trailingItem?: ItemT;
|
|
60
68
|
// RN lets a row drive arbitrary separator props via separators.updateProps; they ride here.
|
|
61
69
|
[key: string]: unknown;
|
|
62
|
-
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// The context a `vListCell` template receives, RN's `CellRendererComponent` props
|
|
73
|
+
// The template draws the wrapper and stamps the item and its separator through `content`
|
|
74
|
+
// The item is `unknown`, a wrapping list hands it an entry or a row, not its own item type
|
|
75
|
+
export type IVListCellContext = {
|
|
76
|
+
$implicit: unknown;
|
|
77
|
+
cellKey: string;
|
|
78
|
+
index: number;
|
|
79
|
+
style: IViewStyle | undefined;
|
|
80
|
+
// Wire both onto the wrapper: `(layout)="cell.layout($event)"`, `(focus)="cell.focus()"`
|
|
81
|
+
layout: (event: unknown) => void;
|
|
82
|
+
focus: () => void;
|
|
83
|
+
// Stamp it with `[vListOutlet]="cell.content" [vListOutletContext]="cell.contentContext"`
|
|
84
|
+
content: TemplateRef<unknown> | undefined;
|
|
85
|
+
contentContext: unknown;
|
|
86
|
+
};
|
|
63
87
|
|
|
64
88
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
65
89
|
return typeof value === 'object' && value !== null;
|
|
@@ -114,6 +138,20 @@ export class VListSeparatorDirective<ItemT = unknown> {
|
|
|
114
138
|
}
|
|
115
139
|
}
|
|
116
140
|
|
|
141
|
+
// `<ng-template vListCell>` replaces the view around each cell (RN CellRendererComponent)
|
|
142
|
+
// It must wire `layout` and `focus` itself, as RN's custom cell must wire `onLayout`
|
|
143
|
+
@Directive({ selector: '[vListCell]', standalone: true })
|
|
144
|
+
export class VListCellDirective {
|
|
145
|
+
readonly templateRef = inject<TemplateRef<IVListCellContext>>(TemplateRef);
|
|
146
|
+
|
|
147
|
+
static ngTemplateContextGuard(
|
|
148
|
+
_dir: VListCellDirective,
|
|
149
|
+
_ctx: unknown,
|
|
150
|
+
): _ctx is IVListCellContext {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
117
155
|
// Core-only NgTemplateOutlet twin. The adapter cannot import @angular/common's NgTemplateOutlet
|
|
118
156
|
// (not a dependency), so the list stamps templates through this: it creates the embedded view when
|
|
119
157
|
// the template changes and updates the live view's context IN PLACE on a context change, so a
|
|
@@ -122,19 +160,32 @@ export class VListSeparatorDirective<ItemT = unknown> {
|
|
|
122
160
|
export class VListOutletDirective<C = unknown> implements OnChanges, OnDestroy {
|
|
123
161
|
@Input({ alias: 'vListOutlet' }) templateRef?: TemplateRef<C>;
|
|
124
162
|
@Input({ alias: 'vListOutletContext' }) context?: C;
|
|
163
|
+
// RN's `ListItemComponent`: stamped instead of the template, its inputs are the item context
|
|
164
|
+
@Input({ alias: 'vListOutletComponent' }) component?: Type<unknown>;
|
|
125
165
|
|
|
126
166
|
private viewRef: EmbeddedViewRef<C> | null = null;
|
|
167
|
+
private componentRef: ComponentRef<unknown> | null = null;
|
|
127
168
|
private readonly viewContainer = inject(ViewContainerRef);
|
|
128
|
-
private readonly instanceId = (
|
|
169
|
+
private readonly instanceId = nextOutletInstanceId();
|
|
129
170
|
|
|
130
171
|
ngOnChanges(changes: SimpleChanges): void {
|
|
131
|
-
if (
|
|
172
|
+
if (
|
|
173
|
+
changes['templateRef'] !== undefined ||
|
|
174
|
+
changes['component'] !== undefined
|
|
175
|
+
) {
|
|
132
176
|
if (isDebug())
|
|
133
177
|
dlog(
|
|
134
|
-
`Angular VListOutlet#${this.instanceId} templateRef CHANGED (was=${changes['templateRef']
|
|
178
|
+
`Angular VListOutlet#${this.instanceId} templateRef CHANGED (was=${changes['templateRef']?.previousValue !== undefined} now=${this.templateRef !== undefined}) -> clear + recreate`,
|
|
135
179
|
);
|
|
136
180
|
this.viewContainer.clear();
|
|
137
181
|
countAngular('outletCreates');
|
|
182
|
+
this.componentRef = null;
|
|
183
|
+
this.viewRef = null;
|
|
184
|
+
if (this.component !== undefined) {
|
|
185
|
+
this.componentRef = this.viewContainer.createComponent(this.component);
|
|
186
|
+
this.writeComponentInputs(this.componentRef);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
138
189
|
this.viewRef =
|
|
139
190
|
this.templateRef === undefined
|
|
140
191
|
? null
|
|
@@ -144,6 +195,11 @@ export class VListOutletDirective<C = unknown> implements OnChanges, OnDestroy {
|
|
|
144
195
|
);
|
|
145
196
|
return;
|
|
146
197
|
}
|
|
198
|
+
if (this.componentRef !== null) {
|
|
199
|
+
countAngular('outletUpdates');
|
|
200
|
+
this.writeComponentInputs(this.componentRef);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
147
203
|
if (this.viewRef !== null && this.context !== undefined) {
|
|
148
204
|
countAngular('outletUpdates');
|
|
149
205
|
if (isDebug())
|
|
@@ -161,6 +217,26 @@ export class VListOutletDirective<C = unknown> implements OnChanges, OnDestroy {
|
|
|
161
217
|
this.viewContainer.clear();
|
|
162
218
|
}
|
|
163
219
|
|
|
220
|
+
// The item context becomes the component's inputs, only those it declares: RN hands a
|
|
221
|
+
// `ListItemComponent` item, index and separators, and one may not read all three
|
|
222
|
+
private writeComponentInputs(ref: ComponentRef<unknown>): void {
|
|
223
|
+
const { context } = this;
|
|
224
|
+
if (!isRecord(context)) return;
|
|
225
|
+
const declared = new Set(
|
|
226
|
+
reflectComponentType(ref.componentType)?.inputs.map(
|
|
227
|
+
input => input.propName,
|
|
228
|
+
),
|
|
229
|
+
);
|
|
230
|
+
const inputs = {
|
|
231
|
+
item: context['$implicit'],
|
|
232
|
+
index: context['index'],
|
|
233
|
+
separators: context['separators'],
|
|
234
|
+
};
|
|
235
|
+
for (const [name, value] of Object.entries(inputs)) {
|
|
236
|
+
if (declared.has(name)) ref.setInput(name, value);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
164
240
|
// Copy the new context's fields onto the live embedded view's context object (whose identity the
|
|
165
241
|
// consumer's `let-` bindings already read), dropping keys that disappeared. The generic `C` can't
|
|
166
242
|
// be index-WRITTEN even once narrowed (TS2862), so the copy runs in a non-generic helper the
|
|
@@ -9,25 +9,32 @@ import {
|
|
|
9
9
|
Component,
|
|
10
10
|
ElementRef,
|
|
11
11
|
ViewChild,
|
|
12
|
+
forwardRef,
|
|
12
13
|
inject,
|
|
13
14
|
type AfterViewChecked,
|
|
14
15
|
type DoCheck,
|
|
15
16
|
type OnDestroy,
|
|
17
|
+
type TemplateRef,
|
|
16
18
|
} from '@angular/core';
|
|
17
19
|
import {
|
|
18
20
|
EMPTY_OFFSET,
|
|
19
21
|
FIRST_INDEX,
|
|
20
22
|
LIST_ACTION_KIND,
|
|
21
|
-
|
|
23
|
+
LIST_SEGMENT_KIND,
|
|
22
24
|
buildScrollViewHandle,
|
|
23
25
|
buildViewabilityPairs,
|
|
24
|
-
computeWindow,
|
|
25
26
|
createInitialListState,
|
|
27
|
+
createListNesting,
|
|
28
|
+
deriveWindow,
|
|
26
29
|
listEffectSignature,
|
|
30
|
+
listHasMore,
|
|
31
|
+
planFromMetrics,
|
|
27
32
|
reduceList,
|
|
28
33
|
resolveItemKey,
|
|
29
34
|
type IListAction,
|
|
35
|
+
type IListNesting,
|
|
30
36
|
type IListReducerInputs,
|
|
37
|
+
type IListScope,
|
|
31
38
|
type IListState,
|
|
32
39
|
type IScrollViewHandle,
|
|
33
40
|
type IViewableItemsChangedInfo,
|
|
@@ -46,11 +53,14 @@ import {
|
|
|
46
53
|
SymbioteStyleInputDirective,
|
|
47
54
|
ViewHost,
|
|
48
55
|
} from '../../primitives';
|
|
49
|
-
import { CellRegistry
|
|
56
|
+
import { CellRegistry } from './cell-registry';
|
|
57
|
+
import { buildCellRendererContext } from './cell-renderer-context';
|
|
50
58
|
import { VListOutletDirective } from './directives';
|
|
51
59
|
import { ListEffectRunner } from './list-effects';
|
|
52
60
|
import { VirtualizedListBagsBase } from './list-bags';
|
|
53
61
|
import type { IVirtualizedListInputs } from './list-props';
|
|
62
|
+
import { injectVirtualizedListScope, ListScopeProvider } from './nested-scope';
|
|
63
|
+
import { stampPlanRows, type IListRow, type IRowsParams } from './list-rows';
|
|
54
64
|
import { resolveListStyles } from './list-styles';
|
|
55
65
|
import { VIRTUALIZED_LIST_TEMPLATE } from './list-template';
|
|
56
66
|
|
|
@@ -66,15 +76,19 @@ export type {
|
|
|
66
76
|
IVirtualizedListHandle,
|
|
67
77
|
} from '@symbiote-native/components';
|
|
68
78
|
export {
|
|
79
|
+
VListCellDirective,
|
|
69
80
|
VListEmptyDirective,
|
|
70
81
|
VListFooterDirective,
|
|
71
82
|
VListHeaderDirective,
|
|
72
83
|
VListItemDirective,
|
|
84
|
+
VListOutletDirective,
|
|
73
85
|
VListSeparatorDirective,
|
|
74
86
|
} from './directives';
|
|
75
|
-
export type {
|
|
76
|
-
|
|
77
|
-
|
|
87
|
+
export type {
|
|
88
|
+
IVListCellContext,
|
|
89
|
+
IVListItemContext,
|
|
90
|
+
IVListSeparatorContext,
|
|
91
|
+
} from './directives';
|
|
78
92
|
|
|
79
93
|
export type {
|
|
80
94
|
IVirtualizedListInputs,
|
|
@@ -89,17 +103,32 @@ export type {
|
|
|
89
103
|
],
|
|
90
104
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
91
105
|
imports: [SymbioteHostPropsDirective, VListOutletDirective, ViewHost],
|
|
106
|
+
providers: [
|
|
107
|
+
{
|
|
108
|
+
provide: ListScopeProvider,
|
|
109
|
+
useExisting: forwardRef(() => VirtualizedList),
|
|
110
|
+
},
|
|
111
|
+
],
|
|
92
112
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
93
113
|
template: VIRTUALIZED_LIST_TEMPLATE,
|
|
94
114
|
})
|
|
95
115
|
export class VirtualizedList<ItemT = unknown>
|
|
96
116
|
extends VirtualizedListBagsBase<ItemT>
|
|
97
|
-
implements
|
|
117
|
+
implements
|
|
118
|
+
IVirtualizedListInputs<ItemT>,
|
|
119
|
+
ListScopeProvider,
|
|
120
|
+
DoCheck,
|
|
121
|
+
AfterViewChecked,
|
|
122
|
+
OnDestroy
|
|
98
123
|
{
|
|
99
124
|
// A template ref on a bare intrinsic hands back the host node directly
|
|
100
125
|
@ViewChild('scrollHost', { read: ElementRef })
|
|
101
126
|
private scrollHostRef?: ElementRef<unknown>;
|
|
102
127
|
|
|
128
|
+
// The template holding a cell's item and separator, sits at the root so it resolves up front
|
|
129
|
+
@ViewChild('cellBody', { static: true })
|
|
130
|
+
private cellBody?: TemplateRef<unknown>;
|
|
131
|
+
|
|
103
132
|
protected get scrollNode(): ISymbioteNode | null {
|
|
104
133
|
const node = this.scrollHostRef?.nativeElement;
|
|
105
134
|
return isSymbioteNode(node) ? node : null;
|
|
@@ -109,15 +138,16 @@ export class VirtualizedList<ItemT = unknown>
|
|
|
109
138
|
() => this.scrollNode,
|
|
110
139
|
);
|
|
111
140
|
|
|
141
|
+
// The template tells a spacer row from a cell row by this, a bare literal would repeat per host
|
|
142
|
+
protected readonly spacerKind = LIST_SEGMENT_KIND.spacer;
|
|
143
|
+
|
|
112
144
|
// Template-bound view state, assembled by `recomputeView` in `ngDoCheck`
|
|
113
145
|
itemCount = EMPTY_OFFSET;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
forcedStickyCell: IWindowCell<ItemT> | null = null;
|
|
117
|
-
leadingSpacerStyle: IViewStyle | null = null;
|
|
118
|
-
gapSpacerStyle: IViewStyle | null = null;
|
|
119
|
-
trailingSpacerStyle: IViewStyle | null = null;
|
|
146
|
+
// The plan's spacers and cells in document order
|
|
147
|
+
rows: IListRow<ItemT>[] = [];
|
|
120
148
|
cellStyle: IViewStyle | undefined = undefined;
|
|
149
|
+
headerStyle: IViewStyle | undefined = undefined;
|
|
150
|
+
footerStyle: IViewStyle | undefined = undefined;
|
|
121
151
|
|
|
122
152
|
// `renderVersion` bumps on every transition that changes render state, for the recompute dedup
|
|
123
153
|
private readonly listState: IListState<ItemT> =
|
|
@@ -149,11 +179,34 @@ export class VirtualizedList<ItemT = unknown>
|
|
|
149
179
|
// This component's own host, the anchor that `class="..."` at the use site resolves onto
|
|
150
180
|
private readonly elementRef = inject(ElementRef);
|
|
151
181
|
|
|
152
|
-
|
|
182
|
+
protected readonly keyFor = (index: number): string => {
|
|
153
183
|
const item = this.getItem(this.data, index);
|
|
154
184
|
return resolveItemKey(item, index, this.keyExtractor);
|
|
155
185
|
};
|
|
156
186
|
|
|
187
|
+
private readonly parentScope = injectVirtualizedListScope();
|
|
188
|
+
private nestingMemo: IListNesting<ItemT> | undefined = undefined;
|
|
189
|
+
|
|
190
|
+
// Built on first use, the inputs that pick its axis are set only after construction
|
|
191
|
+
protected get nesting(): IListNesting<ItemT> {
|
|
192
|
+
this.nestingMemo ??= createListNesting<ItemT>({
|
|
193
|
+
parent: this.parentScope,
|
|
194
|
+
horizontal: this.isHorizontal,
|
|
195
|
+
getState: () => this.listState,
|
|
196
|
+
dispatch: action => this.dispatch(action),
|
|
197
|
+
getContainerNode: () => this.scrollNode,
|
|
198
|
+
getHasMore: () => listHasMore(this.listState),
|
|
199
|
+
keyFor: this.keyFor,
|
|
200
|
+
handlers: () => this.listHandlers,
|
|
201
|
+
});
|
|
202
|
+
return this.nestingMemo;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// The scope the lists in this list's cells inject
|
|
206
|
+
get listScope(): IListScope {
|
|
207
|
+
return this.nesting.scope;
|
|
208
|
+
}
|
|
209
|
+
|
|
157
210
|
// Edge and viewability listeners map to `.observed`, so the reducer emits only if listened to
|
|
158
211
|
private buildInputs(): IListReducerInputs<ItemT> {
|
|
159
212
|
return {
|
|
@@ -164,6 +217,7 @@ export class VirtualizedList<ItemT = unknown>
|
|
|
164
217
|
getItemLayout: this.getItemLayout,
|
|
165
218
|
horizontal: this.isHorizontal,
|
|
166
219
|
windowSize: this.windowSizeValue,
|
|
220
|
+
disableVirtualization: this.disableVirtualization,
|
|
167
221
|
initialNumToRender: this.initialNumToRenderValue,
|
|
168
222
|
maxToRenderPerBatch: this.maxToRenderPerBatchValue,
|
|
169
223
|
updateCellsBatchingPeriod: this.updateCellsBatchingPeriodValue,
|
|
@@ -181,6 +235,8 @@ export class VirtualizedList<ItemT = unknown>
|
|
|
181
235
|
),
|
|
182
236
|
maintainVisibleContentPosition: this.maintainVisibleContentPosition,
|
|
183
237
|
initialScrollIndex: this.initialScrollIndex,
|
|
238
|
+
findFirstChildWithMore: (first, last) =>
|
|
239
|
+
this.nesting.findFirstChildWithMore(first, last),
|
|
184
240
|
};
|
|
185
241
|
}
|
|
186
242
|
|
|
@@ -204,15 +260,12 @@ export class VirtualizedList<ItemT = unknown>
|
|
|
204
260
|
const m = this.listState.metrics;
|
|
205
261
|
// Mid-fill the window climbs one batch step per render, only a render advances it
|
|
206
262
|
if (m.first !== m.target.first || m.last !== m.target.last) return false;
|
|
207
|
-
const next =
|
|
263
|
+
const next = deriveWindow(
|
|
264
|
+
this.listState,
|
|
265
|
+
this.buildInputs(),
|
|
208
266
|
m.count,
|
|
209
|
-
m
|
|
210
|
-
|
|
211
|
-
this.listState.scrollOffset,
|
|
212
|
-
this.listState.viewportLength,
|
|
213
|
-
this.windowSizeValue,
|
|
214
|
-
this.initialNumToRenderValue,
|
|
215
|
-
);
|
|
267
|
+
m,
|
|
268
|
+
).target;
|
|
216
269
|
const isSettled =
|
|
217
270
|
next.first === m.target.first && next.last === m.target.last;
|
|
218
271
|
if (!isSettled)
|
|
@@ -234,6 +287,7 @@ export class VirtualizedList<ItemT = unknown>
|
|
|
234
287
|
this.isHorizontal,
|
|
235
288
|
this.isInverted,
|
|
236
289
|
this.windowSizeValue,
|
|
290
|
+
this.disableVirtualization,
|
|
237
291
|
this.initialNumToRenderValue,
|
|
238
292
|
this.maxToRenderPerBatchValue,
|
|
239
293
|
this.stickyHeaderIndices,
|
|
@@ -242,11 +296,14 @@ export class VirtualizedList<ItemT = unknown>
|
|
|
242
296
|
// Arrives through `addClass` and `removeClass`, never as an input
|
|
243
297
|
anchorHostStyle(this.elementRef),
|
|
244
298
|
this.contentContainerStyle,
|
|
299
|
+
this.listHeaderComponentStyle,
|
|
300
|
+
this.listFooterComponentStyle,
|
|
245
301
|
this.renderVersion,
|
|
246
302
|
this.headerDir !== undefined,
|
|
247
303
|
this.footerDir !== undefined,
|
|
248
304
|
this.emptyDir !== undefined,
|
|
249
305
|
this.separatorTemplate !== undefined,
|
|
306
|
+
this.cellRendererTpl,
|
|
250
307
|
];
|
|
251
308
|
const previous = this.lastRecompute;
|
|
252
309
|
this.lastRecompute = recomputeInputs;
|
|
@@ -281,6 +338,7 @@ export class VirtualizedList<ItemT = unknown>
|
|
|
281
338
|
|
|
282
339
|
ngOnDestroy(): void {
|
|
283
340
|
this.effects.dispose();
|
|
341
|
+
this.nestingMemo?.detach();
|
|
284
342
|
}
|
|
285
343
|
|
|
286
344
|
private recomputeView(): void {
|
|
@@ -297,29 +355,35 @@ export class VirtualizedList<ItemT = unknown>
|
|
|
297
355
|
this.showEmpty();
|
|
298
356
|
return;
|
|
299
357
|
}
|
|
300
|
-
|
|
301
|
-
this.stickyHeaderIndices
|
|
302
|
-
|
|
303
|
-
: undefined;
|
|
304
|
-
const plan = buildListPlan({
|
|
358
|
+
this.rows = stampPlanRows({
|
|
359
|
+
windowPlan: planFromMetrics(m, this.keyFor, this.stickyHeaderIndices),
|
|
360
|
+
cells: this.cells,
|
|
305
361
|
count: m.count,
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
lengths: m.lengths,
|
|
310
|
-
total: m.total,
|
|
311
|
-
keyFor: this.keyFor,
|
|
312
|
-
stickyIndices: stickySet,
|
|
313
|
-
hasHeader: this.headerDir !== undefined,
|
|
362
|
+
isHorizontal: this.isHorizontal,
|
|
363
|
+
hasSeparators: this.separatorTemplate !== undefined,
|
|
364
|
+
rendererFor: this.rendererFor(),
|
|
314
365
|
});
|
|
315
|
-
this.
|
|
316
|
-
this.buildCells(plan, stickySet);
|
|
366
|
+
this.cells.endPass();
|
|
317
367
|
dlog(
|
|
318
368
|
`Angular VirtualizedList window [${m.first}, ${m.last}] of ${m.count} ` +
|
|
319
|
-
`(offset=${this.listState.scrollOffset}, viewport=${this.listState.viewportLength},
|
|
369
|
+
`(offset=${this.listState.scrollOffset}, viewport=${this.listState.viewportLength}, rows=${this.rows.length})`,
|
|
320
370
|
);
|
|
321
371
|
}
|
|
322
372
|
|
|
373
|
+
// Only a list given a `vListCell` template builds the wrapper contexts
|
|
374
|
+
private rendererFor(): IRowsParams<ItemT>['rendererFor'] {
|
|
375
|
+
if (this.cellRendererTpl === undefined) return undefined;
|
|
376
|
+
return cell =>
|
|
377
|
+
buildCellRendererContext({
|
|
378
|
+
cell,
|
|
379
|
+
style: this.cellStyle,
|
|
380
|
+
body: this.cellBody,
|
|
381
|
+
layout: (measure, event, index) =>
|
|
382
|
+
this.handleCellLayout(measure, event, index),
|
|
383
|
+
focus: index => this.handleCellFocus(index),
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
|
|
323
387
|
private applyStyles(total: number): void {
|
|
324
388
|
const styles = resolveListStyles({
|
|
325
389
|
isHorizontal: this.isHorizontal,
|
|
@@ -328,75 +392,44 @@ export class VirtualizedList<ItemT = unknown>
|
|
|
328
392
|
hasHeader: this.headerDir !== undefined,
|
|
329
393
|
style: this.style,
|
|
330
394
|
contentContainerStyle: this.contentContainerStyle,
|
|
395
|
+
listHeaderComponentStyle: this.listHeaderComponentStyle,
|
|
396
|
+
listFooterComponentStyle: this.listFooterComponentStyle,
|
|
331
397
|
anchorStyle: anchorHostStyle(this.elementRef),
|
|
332
398
|
maintainVisibleContentPosition: this.maintainVisibleContentPosition,
|
|
333
399
|
});
|
|
334
400
|
this.resolvedStyle = styles.style;
|
|
335
401
|
this.resolvedContentContainerStyle = styles.contentContainerStyle;
|
|
336
402
|
this.cellStyle = styles.cellStyle;
|
|
403
|
+
this.headerStyle = styles.headerStyle;
|
|
404
|
+
this.footerStyle = styles.footerStyle;
|
|
337
405
|
this.resolvedMaintainVisibleContentPosition =
|
|
338
406
|
styles.maintainVisibleContentPosition;
|
|
339
407
|
}
|
|
340
408
|
|
|
341
409
|
private showEmpty(): void {
|
|
342
410
|
this.cells.clear();
|
|
343
|
-
this.
|
|
344
|
-
this.forcedStickyCell = null;
|
|
345
|
-
this.leadingSpacerStyle = null;
|
|
346
|
-
this.gapSpacerStyle = null;
|
|
347
|
-
this.trailingSpacerStyle = null;
|
|
411
|
+
this.rows = [];
|
|
348
412
|
dlog(
|
|
349
413
|
`Angular VirtualizedList empty (viewport=${this.listState.viewportLength})`,
|
|
350
414
|
);
|
|
351
415
|
}
|
|
352
416
|
|
|
353
|
-
private applySpacers(plan: IListPlan): void {
|
|
354
|
-
this.leadingSpacerStyle = this.spacerStyle(plan.leadingExtent);
|
|
355
|
-
this.gapSpacerStyle = this.spacerStyle(plan.gapExtent);
|
|
356
|
-
this.trailingSpacerStyle = this.spacerStyle(plan.trailingExtent);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
private spacerStyle(extent: number): IViewStyle | null {
|
|
360
|
-
if (extent <= EMPTY_OFFSET) return null;
|
|
361
|
-
return this.isHorizontal ? { width: extent } : { height: extent };
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
private buildCells(
|
|
365
|
-
plan: IListPlan,
|
|
366
|
-
stickySet: Set<number> | undefined,
|
|
367
|
-
): void {
|
|
368
|
-
const count = this.listState.metrics.count;
|
|
369
|
-
const forced = plan.forcedStickyCell;
|
|
370
|
-
dlog(
|
|
371
|
-
`STICKY[list] stickySet=${stickySet === undefined ? 'undefined' : JSON.stringify([...stickySet])} ` +
|
|
372
|
-
`childPositions=${JSON.stringify(plan.stickyChildPositions)} ` +
|
|
373
|
-
`forcedStickyCell=${forced === undefined ? 'none' : forced.index}`,
|
|
374
|
-
);
|
|
375
|
-
// Sticky by construction: a forced cell exists only for an index in `stickySet`
|
|
376
|
-
this.forcedStickyCell =
|
|
377
|
-
forced !== undefined
|
|
378
|
-
? this.cells.windowCell(forced.index, forced.key, false, true)
|
|
379
|
-
: null;
|
|
380
|
-
const hasSeparators = this.separatorTemplate !== undefined;
|
|
381
|
-
// The separator gates on the last index of the data, not of the window, or a cell's height
|
|
382
|
-
// would shift as it slides past
|
|
383
|
-
this.windowCells = plan.cells.map(planned =>
|
|
384
|
-
this.cells.windowCell(
|
|
385
|
-
planned.index,
|
|
386
|
-
planned.key,
|
|
387
|
-
hasSeparators && planned.index < count - 1,
|
|
388
|
-
stickySet?.has(planned.index) === true,
|
|
389
|
-
),
|
|
390
|
-
);
|
|
391
|
-
this.cells.endPass();
|
|
392
|
-
}
|
|
393
|
-
|
|
394
417
|
// Angular forbids an `[onLayout]` binding, so the event arrives untyped and is narrowed here
|
|
395
418
|
handleCellLayout(
|
|
396
419
|
measure: (event: ISymbioteEvent) => void,
|
|
397
420
|
event: unknown,
|
|
421
|
+
index: number,
|
|
398
422
|
): void {
|
|
399
|
-
if (this.isSymbioteEvent(event))
|
|
423
|
+
if (!this.isSymbioteEvent(event)) return;
|
|
424
|
+
measure(event);
|
|
425
|
+
const cellKey = this.keyFor(index);
|
|
426
|
+
this.nesting.scope.registerCellNode(event.currentTarget, cellKey);
|
|
427
|
+
this.nesting.remeasureCell(cellKey);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Focus inside a cell keeps a viewport of cells around it mounted, like RN's focus capture
|
|
431
|
+
handleCellFocus(index: number): void {
|
|
432
|
+
this.dispatch({ kind: LIST_ACTION_KIND.cellFocused, index });
|
|
400
433
|
}
|
|
401
434
|
|
|
402
435
|
private isSymbioteEvent(value: unknown): value is ISymbioteEvent {
|
|
@@ -3,15 +3,14 @@
|
|
|
3
3
|
|
|
4
4
|
import { Directive, computed, signal, type OnChanges } from '@angular/core';
|
|
5
5
|
import {
|
|
6
|
-
|
|
7
|
-
readLayoutLength,
|
|
8
|
-
readScrollOffset,
|
|
6
|
+
createListHandlers,
|
|
9
7
|
resolveAccessibilityProps,
|
|
10
8
|
type IAccessibilityProps,
|
|
11
9
|
type IAriaProps,
|
|
10
|
+
type IListHandlers,
|
|
11
|
+
type IListNesting,
|
|
12
12
|
} from '@symbiote-native/components';
|
|
13
13
|
import {
|
|
14
|
-
dlog,
|
|
15
14
|
type IStyleProp,
|
|
16
15
|
type ISymbioteEvent,
|
|
17
16
|
type IViewStyle,
|
|
@@ -32,6 +31,23 @@ export abstract class VirtualizedListBagsBase<ItemT>
|
|
|
32
31
|
// A fresh object per push, so the commit re-applies a repeated offset
|
|
33
32
|
commandedOffset: { x: number; y: number } | undefined = undefined;
|
|
34
33
|
|
|
34
|
+
protected abstract get nesting(): IListNesting<ItemT>;
|
|
35
|
+
|
|
36
|
+
protected abstract readonly keyFor: (index: number) => string;
|
|
37
|
+
|
|
38
|
+
// Reads of `this` are deferred to the event, so the inputs are set by the time they run
|
|
39
|
+
protected readonly listHandlers: IListHandlers = createListHandlers<ItemT>({
|
|
40
|
+
isHorizontal: () => this.isHorizontal,
|
|
41
|
+
user: () => this,
|
|
42
|
+
nesting: () => this.nesting,
|
|
43
|
+
dispatch: action => this.dispatch(action),
|
|
44
|
+
clearCommandedOffset: () => {
|
|
45
|
+
this.commandedOffset = undefined;
|
|
46
|
+
},
|
|
47
|
+
getNode: () => this.scrollNode,
|
|
48
|
+
keyFor: index => this.keyFor(index),
|
|
49
|
+
});
|
|
50
|
+
|
|
35
51
|
// Only inputs may feed this signal, window state changes skip `ngOnChanges`
|
|
36
52
|
private readonly inputsRevision = signal(0);
|
|
37
53
|
|
|
@@ -64,15 +80,18 @@ export abstract class VirtualizedListBagsBase<ItemT>
|
|
|
64
80
|
contentContainerStyle: this.resolvedContentContainerStyle,
|
|
65
81
|
onScroll: this.onScrollTick,
|
|
66
82
|
onLayout: this.onLayoutTick,
|
|
67
|
-
onScrollBeginDrag: this.onScrollBeginDrag,
|
|
68
|
-
onScrollEndDrag: this.onScrollEndDrag,
|
|
69
|
-
onMomentumScrollBegin: this.onMomentumScrollBegin,
|
|
70
|
-
onMomentumScrollEnd: this.onMomentumScrollEnd,
|
|
83
|
+
onScrollBeginDrag: this.listHandlers.onScrollBeginDrag,
|
|
84
|
+
onScrollEndDrag: this.listHandlers.onScrollEndDrag,
|
|
85
|
+
onMomentumScrollBegin: this.listHandlers.onMomentumScrollBegin,
|
|
86
|
+
onMomentumScrollEnd: this.listHandlers.onMomentumScrollEnd,
|
|
87
|
+
onContentSizeChange: this.listHandlers.onContentSizeChange,
|
|
71
88
|
scrollEventThrottle: this.scrollEventThrottle,
|
|
72
89
|
keyboardShouldPersistTaps: this.keyboardShouldPersistTaps,
|
|
73
90
|
keyboardDismissMode: this.keyboardDismissMode,
|
|
74
91
|
removeClippedSubviews: this.removeClippedSubviews,
|
|
75
92
|
nestedScrollEnabled: this.nestedScrollEnabled,
|
|
93
|
+
stickyHeaderHiddenOnScroll: this.stickyHeaderHiddenOnScroll,
|
|
94
|
+
innerViewRef: this.innerViewRef,
|
|
76
95
|
// Android moves the scrollbar back after the `scale: -1` flip
|
|
77
96
|
isInvertedVirtualizedList: this.isInverted ? true : undefined,
|
|
78
97
|
contentOffset: this.commandedOffset,
|
|
@@ -101,23 +120,23 @@ export abstract class VirtualizedListBagsBase<ItemT>
|
|
|
101
120
|
};
|
|
102
121
|
}
|
|
103
122
|
|
|
123
|
+
// The list above scrolls, so RN renders a `View` with no content container
|
|
124
|
+
nestedViewBag(): Record<string, unknown> {
|
|
125
|
+
return {
|
|
126
|
+
...this.foldedAccessibility(),
|
|
127
|
+
style: this.resolvedStyle,
|
|
128
|
+
onLayout: this.onLayoutTick,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
104
132
|
// `onScroll` stays a callback so an `Animated.event` target can flow through it
|
|
105
133
|
onScrollTick = (event: ISymbioteEvent): void => {
|
|
106
134
|
countAngular('scrollTicks');
|
|
107
|
-
|
|
108
|
-
if (offset === undefined) return;
|
|
109
|
-
dlog(`Angular VirtualizedList onScroll offset=${offset}`);
|
|
110
|
-
// A real native scroll supersedes any pending commanded offset
|
|
111
|
-
this.commandedOffset = undefined;
|
|
112
|
-
this.dispatch({ kind: LIST_ACTION_KIND.scroll, offset });
|
|
113
|
-
this.onScroll?.(event);
|
|
135
|
+
this.listHandlers.onScroll(event);
|
|
114
136
|
};
|
|
115
137
|
|
|
116
138
|
onLayoutTick = (event: ISymbioteEvent): void => {
|
|
117
|
-
|
|
118
|
-
if (length === undefined) return;
|
|
119
|
-
dlog(`Angular VirtualizedList onLayout viewport=${length}`);
|
|
120
|
-
this.dispatch({ kind: LIST_ACTION_KIND.layout, length });
|
|
139
|
+
this.listHandlers.onViewportLayout(event);
|
|
121
140
|
};
|
|
122
141
|
|
|
123
142
|
handleRefresh = (): void => {
|