@symbiote-native/angular 3.3.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 +1 -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/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 +7 -3
- package/build/angular/components.js +2 -1
- package/build/angular/components.js.map +1 -1
- package/build/angular/element-base.d.ts +17 -1
- package/build/angular/element-base.js +33 -1
- package/build/angular/element-base.js.map +1 -1
- 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 +8 -2
- package/build/angular/elements-controls.js +13 -1
- package/build/angular/elements-controls.js.map +1 -1
- package/build/angular/elements-layout.d.ts +20 -3
- package/build/angular/elements-layout.js +37 -3
- package/build/angular/elements-layout.js.map +1 -1
- package/build/angular/elements-press.d.ts +10 -3
- package/build/angular/elements-press.js +13 -1
- package/build/angular/elements-press.js.map +1 -1
- package/build/angular/elements.d.ts +3 -3
- package/build/angular/elements.js +3 -2
- package/build/angular/elements.js.map +1 -1
- package/build/angular/index.d.ts +14 -7
- package/build/angular/index.js +21 -25
- 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/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/package.json +6 -6
- package/src/accessibility-inputs.ts +3 -0
- package/src/callback-host.ts +56 -8
- 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 +8 -0
- package/src/element-base.ts +17 -0
- package/src/element-props.ts +35 -33
- package/src/element-text-input-native.ts +31 -0
- package/src/elements-controls.ts +9 -1
- package/src/elements-layout.ts +27 -0
- package/src/elements-press.ts +10 -2
- package/src/elements.ts +3 -0
- package/src/index.ts +72 -22
- 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/primitives/index.ts +7 -0
- package/src/services/color-scheme.service.ts +1 -1
|
@@ -12,17 +12,21 @@ import {
|
|
|
12
12
|
ViewChild,
|
|
13
13
|
inject,
|
|
14
14
|
type DoCheck,
|
|
15
|
+
type TemplateRef,
|
|
15
16
|
} from '@angular/core';
|
|
16
17
|
import {
|
|
18
|
+
cellGapsFor,
|
|
19
|
+
createSeparatorBoard,
|
|
17
20
|
flattenSections,
|
|
18
21
|
resolveStickySectionHeaders,
|
|
19
|
-
|
|
22
|
+
resolveScrollLocation,
|
|
23
|
+
SECTION_ENTRY_KIND,
|
|
24
|
+
SEPARATOR_GAP_KIND,
|
|
25
|
+
SEPARATOR_SIDE,
|
|
20
26
|
sectionEntryKey,
|
|
21
|
-
|
|
22
|
-
type IAccessibilityProps,
|
|
23
|
-
type IAriaProps,
|
|
24
|
-
type ISection,
|
|
27
|
+
type IInnerViewRef,
|
|
25
28
|
type ISectionEntry,
|
|
29
|
+
type ISeparatorGap,
|
|
26
30
|
type ISeparators,
|
|
27
31
|
type IScrollViewHandle,
|
|
28
32
|
type IVirtualizedSectionListHandle,
|
|
@@ -37,12 +41,13 @@ import {
|
|
|
37
41
|
} from '@symbiote-native/engine';
|
|
38
42
|
import {
|
|
39
43
|
VirtualizedList,
|
|
44
|
+
VListCellDirective,
|
|
40
45
|
VListEmptyDirective,
|
|
41
46
|
VListFooterDirective,
|
|
42
47
|
VListHeaderDirective,
|
|
43
48
|
VListItemDirective,
|
|
44
49
|
VListSeparatorDirective,
|
|
45
|
-
type
|
|
50
|
+
type IVListCellContext,
|
|
46
51
|
} from '../virtualized-list';
|
|
47
52
|
import { VListOutletDirective } from '../virtualized-list/directives';
|
|
48
53
|
import { ListEventsBase } from '../virtualized-list/list-events';
|
|
@@ -56,12 +61,18 @@ import {
|
|
|
56
61
|
VSectionHeaderDirective,
|
|
57
62
|
VSectionItemDirective,
|
|
58
63
|
VSectionSeparatorDirective,
|
|
64
|
+
type ISection,
|
|
59
65
|
type IVSectionContext,
|
|
60
66
|
type IVSectionItemContext,
|
|
61
67
|
} from './directives';
|
|
68
|
+
import { VSectionCellDirective, type ISeparatorSlot } from './section-cell';
|
|
69
|
+
import type {
|
|
70
|
+
IItemLayout,
|
|
71
|
+
IVirtualizedSectionListInputs,
|
|
72
|
+
IVirtualizedSectionListProps,
|
|
73
|
+
} from './section-list-props';
|
|
62
74
|
import { VIRTUALIZED_SECTION_LIST_TEMPLATE } from './section-list-template';
|
|
63
75
|
|
|
64
|
-
export type { ISection } from '@symbiote-native/components';
|
|
65
76
|
export type { IVirtualizedSectionListHandle } from '@symbiote-native/components';
|
|
66
77
|
export {
|
|
67
78
|
VSectionFooterDirective,
|
|
@@ -69,66 +80,30 @@ export {
|
|
|
69
80
|
VSectionItemDirective,
|
|
70
81
|
VSectionSeparatorDirective,
|
|
71
82
|
} from './directives';
|
|
72
|
-
export type {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
keyExtractor?: (item: ItemT, index: number) => string;
|
|
81
|
-
// Flat like RN's: the sections array plus a flat entry index, where every section adds two rows
|
|
82
|
-
// beyond its items (header, footer). Without it a fast scroll outruns measurement
|
|
83
|
-
getItemLayout?: (
|
|
84
|
-
data: ReadonlyArray<ISection<ItemT>> | null,
|
|
85
|
-
index: number,
|
|
86
|
-
) => IItemLayout;
|
|
87
|
-
// Defaults to `Platform.OS === 'ios'`, pass true or false to override
|
|
88
|
-
stickySectionHeadersEnabled?: boolean;
|
|
89
|
-
extraData?: unknown;
|
|
90
|
-
onEndReached?: (info: { distanceFromEnd: number }) => void;
|
|
91
|
-
onEndReachedThreshold?: number;
|
|
92
|
-
onStartReached?: (info: { distanceFromStart: number }) => void;
|
|
93
|
-
onStartReachedThreshold?: number;
|
|
94
|
-
onRefresh?: () => void;
|
|
95
|
-
refreshing?: boolean | null;
|
|
96
|
-
progressViewOffset?: number;
|
|
97
|
-
initialNumToRender?: number;
|
|
98
|
-
initialScrollIndex?: number;
|
|
99
|
-
maxToRenderPerBatch?: number;
|
|
100
|
-
updateCellsBatchingPeriod?: number;
|
|
101
|
-
windowSize?: number;
|
|
102
|
-
inverted?: boolean;
|
|
103
|
-
maintainVisibleContentPosition?: {
|
|
104
|
-
minIndexForVisible: number;
|
|
105
|
-
autoscrollToTopThreshold?: number;
|
|
106
|
-
};
|
|
107
|
-
onScroll?: (event: ISymbioteEvent) => void;
|
|
108
|
-
onScrollBeginDrag?: (event: ISymbioteEvent) => void;
|
|
109
|
-
onScrollEndDrag?: (event: ISymbioteEvent) => void;
|
|
110
|
-
onMomentumScrollBegin?: (event: ISymbioteEvent) => void;
|
|
111
|
-
onMomentumScrollEnd?: (event: ISymbioteEvent) => void;
|
|
112
|
-
scrollEventThrottle?: number;
|
|
113
|
-
keyboardShouldPersistTaps?: boolean | 'always' | 'never' | 'handled';
|
|
114
|
-
keyboardDismissMode?: 'none' | 'on-drag' | 'interactive';
|
|
115
|
-
removeClippedSubviews?: boolean;
|
|
116
|
-
nestedScrollEnabled?: boolean;
|
|
117
|
-
style?: IStyleProp<IViewStyle>;
|
|
118
|
-
contentContainerStyle?: IStyleProp<IViewStyle>;
|
|
119
|
-
}
|
|
83
|
+
export type {
|
|
84
|
+
ISection,
|
|
85
|
+
IVSectionContext,
|
|
86
|
+
IVSectionItemContext,
|
|
87
|
+
IVSectionSeparatorContext,
|
|
88
|
+
} from './directives';
|
|
89
|
+
|
|
90
|
+
type IEntry<ItemT> = ISectionEntry<ItemT, ISection<ItemT>>;
|
|
120
91
|
|
|
121
|
-
//
|
|
122
|
-
export type
|
|
123
|
-
|
|
124
|
-
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
92
|
+
// Everything an item cell needs to paint itself, separators in painting order
|
|
93
|
+
export type ICellPlan<ItemT> = {
|
|
94
|
+
cellKey: string;
|
|
95
|
+
prevCellKey: string | undefined;
|
|
96
|
+
hasLeading: boolean;
|
|
97
|
+
hasTrailing: boolean;
|
|
98
|
+
itemTemplate: TemplateRef<IVSectionItemContext<ItemT>> | undefined;
|
|
99
|
+
first: ISeparatorSlot<ItemT>;
|
|
100
|
+
second: ISeparatorSlot<ItemT>;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export type {
|
|
104
|
+
IVirtualizedSectionListInputs,
|
|
105
|
+
IVirtualizedSectionListProps,
|
|
106
|
+
} from './section-list-props';
|
|
132
107
|
|
|
133
108
|
@Component({
|
|
134
109
|
selector: 'VirtualizedSectionList',
|
|
@@ -143,8 +118,8 @@ export type IVirtualizedSectionListInputs<ItemT> = Omit<
|
|
|
143
118
|
VListHeaderDirective,
|
|
144
119
|
VListFooterDirective,
|
|
145
120
|
VListEmptyDirective,
|
|
146
|
-
VListSeparatorDirective,
|
|
147
121
|
VListOutletDirective,
|
|
122
|
+
VSectionCellDirective,
|
|
148
123
|
],
|
|
149
124
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
150
125
|
template: VIRTUALIZED_SECTION_LIST_TEMPLATE,
|
|
@@ -176,7 +151,9 @@ export class VirtualizedSectionList<ItemT = unknown>
|
|
|
176
151
|
@Input() maxToRenderPerBatch?: number;
|
|
177
152
|
@Input() updateCellsBatchingPeriod?: number;
|
|
178
153
|
@Input() windowSize?: number;
|
|
154
|
+
@Input() disableVirtualization?: boolean;
|
|
179
155
|
@Input() inverted?: boolean;
|
|
156
|
+
@Input() horizontal?: boolean;
|
|
180
157
|
@Input() maintainVisibleContentPosition?: {
|
|
181
158
|
minIndexForVisible: number;
|
|
182
159
|
autoscrollToTopThreshold?: number;
|
|
@@ -186,13 +163,18 @@ export class VirtualizedSectionList<ItemT = unknown>
|
|
|
186
163
|
@Input() onScrollEndDrag?: (event: ISymbioteEvent) => void;
|
|
187
164
|
@Input() onMomentumScrollBegin?: (event: ISymbioteEvent) => void;
|
|
188
165
|
@Input() onMomentumScrollEnd?: (event: ISymbioteEvent) => void;
|
|
166
|
+
@Input() onContentSizeChange?: (width: number, height: number) => void;
|
|
189
167
|
@Input() scrollEventThrottle?: number;
|
|
190
168
|
@Input() keyboardShouldPersistTaps?: boolean | 'always' | 'never' | 'handled';
|
|
191
169
|
@Input() keyboardDismissMode?: 'none' | 'on-drag' | 'interactive';
|
|
192
170
|
@Input() removeClippedSubviews?: boolean;
|
|
193
171
|
@Input() nestedScrollEnabled?: boolean;
|
|
172
|
+
@Input() stickyHeaderHiddenOnScroll?: boolean;
|
|
173
|
+
@Input() innerViewRef?: IInnerViewRef;
|
|
194
174
|
@Input() style?: IStyleProp<IViewStyle>;
|
|
195
175
|
@Input() contentContainerStyle?: IStyleProp<IViewStyle>;
|
|
176
|
+
@Input() listHeaderComponentStyle?: IStyleProp<IViewStyle>;
|
|
177
|
+
@Input() listFooterComponentStyle?: IStyleProp<IViewStyle>;
|
|
196
178
|
@Input() testID?: string;
|
|
197
179
|
@Input() nativeID?: string;
|
|
198
180
|
|
|
@@ -216,7 +198,7 @@ export class VirtualizedSectionList<ItemT = unknown>
|
|
|
216
198
|
@ContentChild(VSectionFooterDirective)
|
|
217
199
|
sectionFooterDir?: VSectionFooterDirective<ItemT>;
|
|
218
200
|
@ContentChild(VSectionSeparatorDirective)
|
|
219
|
-
sectionSeparatorDir?: VSectionSeparatorDirective
|
|
201
|
+
sectionSeparatorDir?: VSectionSeparatorDirective<ItemT>;
|
|
220
202
|
|
|
221
203
|
// List-level slots reuse the `VirtualizedList` directives and are forwarded to the inner list
|
|
222
204
|
@ContentChild(VListHeaderDirective) listHeaderDir?: VListHeaderDirective;
|
|
@@ -224,41 +206,38 @@ export class VirtualizedSectionList<ItemT = unknown>
|
|
|
224
206
|
@ContentChild(VListEmptyDirective) listEmptyDir?: VListEmptyDirective;
|
|
225
207
|
@ContentChild(VListSeparatorDirective)
|
|
226
208
|
itemSeparatorDir?: VListSeparatorDirective<ItemT>;
|
|
209
|
+
// The cell wrapper, from projection or handed in by `SectionList`, the item is the list's entry
|
|
210
|
+
@ContentChild(VListCellDirective) cellDir?: VListCellDirective;
|
|
211
|
+
@Input() cellRendererTemplate?: TemplateRef<IVListCellContext>;
|
|
212
|
+
|
|
213
|
+
get cellRendererTpl(): TemplateRef<IVListCellContext> | undefined {
|
|
214
|
+
return this.cellRendererTemplate ?? this.cellDir?.templateRef;
|
|
215
|
+
}
|
|
227
216
|
|
|
228
217
|
// The inner list, whose instance is the scroll handle, reads lazily and no-ops before commit
|
|
229
|
-
@ViewChild(VirtualizedList) private list?: VirtualizedList<
|
|
230
|
-
ISectionEntry<ItemT>
|
|
231
|
-
>;
|
|
218
|
+
@ViewChild(VirtualizedList) private list?: VirtualizedList<IEntry<ItemT>>;
|
|
232
219
|
|
|
233
220
|
// This component's own host, the anchor `class="..."` resolves onto, not the inner list's
|
|
234
221
|
private readonly elementRef = inject(ElementRef);
|
|
235
222
|
|
|
236
|
-
flatEntries:
|
|
223
|
+
flatEntries: IEntry<ItemT>[] = [];
|
|
237
224
|
stickyHeaderIndices: number[] | undefined = undefined;
|
|
225
|
+
readonly board = createSeparatorBoard<Record<string, unknown>>();
|
|
238
226
|
|
|
239
227
|
// Flat positions of every section header, so `scrollToLocation` skips re-deriving the layout
|
|
240
228
|
private headerIndices: number[] = [];
|
|
241
|
-
// Memo
|
|
229
|
+
// Memo guard, the inner list marks for check on every scroll tick and re-runs `ngDoCheck`
|
|
242
230
|
private lastSectionsRef: ReadonlyArray<ISection<ItemT>> | null = null;
|
|
243
|
-
private lastHasSectionSeparator = false;
|
|
244
231
|
|
|
245
232
|
private readonly cdr = inject(ChangeDetectorRef);
|
|
246
233
|
|
|
247
234
|
ngDoCheck(): void {
|
|
248
|
-
|
|
249
|
-
if (
|
|
250
|
-
this.sections === this.lastSectionsRef &&
|
|
251
|
-
hasSectionSeparator === this.lastHasSectionSeparator
|
|
252
|
-
) {
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
235
|
+
if (this.sections === this.lastSectionsRef) return;
|
|
255
236
|
this.lastSectionsRef = this.sections;
|
|
256
|
-
this.lastHasSectionSeparator = hasSectionSeparator;
|
|
257
237
|
|
|
258
238
|
const sections = this.sections ?? [];
|
|
259
|
-
const { entries, headerIndices } = flattenSections(
|
|
239
|
+
const { entries, headerIndices } = flattenSections<ItemT, ISection<ItemT>>(
|
|
260
240
|
sections,
|
|
261
|
-
hasSectionSeparator,
|
|
262
241
|
);
|
|
263
242
|
this.flatEntries = entries;
|
|
264
243
|
this.headerIndices = headerIndices;
|
|
@@ -277,14 +256,13 @@ export class VirtualizedSectionList<ItemT = unknown>
|
|
|
277
256
|
}
|
|
278
257
|
|
|
279
258
|
// Stable arrow fields, so the bindings to the inner list keep a constant identity
|
|
280
|
-
getEntry = (_source: unknown, index: number):
|
|
259
|
+
getEntry = (_source: unknown, index: number): IEntry<ItemT> =>
|
|
281
260
|
this.flatEntries[index];
|
|
282
261
|
getEntryCount = (): number => this.flatEntries.length;
|
|
283
|
-
entryKeyExtractor = (entry:
|
|
284
|
-
sectionEntryKey(entry,
|
|
262
|
+
entryKeyExtractor = (entry: IEntry<ItemT>): string =>
|
|
263
|
+
sectionEntryKey(entry, this.keyExtractor);
|
|
285
264
|
|
|
286
|
-
// RN hands the callback `sections`, not the flattened entries
|
|
287
|
-
// while no section separator template is set, which adds one row per boundary here
|
|
265
|
+
// RN hands the callback `sections`, not the flattened entries
|
|
288
266
|
// Cached on the input's identity, the inner list folds it into its dedup array
|
|
289
267
|
private cachedEntryItemLayout?: (data: unknown, index: number) => IItemLayout;
|
|
290
268
|
private lastGetItemLayout?: IVirtualizedSectionListProps<ItemT>['getItemLayout'];
|
|
@@ -323,11 +301,11 @@ export class VirtualizedSectionList<ItemT = unknown>
|
|
|
323
301
|
}
|
|
324
302
|
|
|
325
303
|
// The inner cell context is typed `unknown` under strict templates, so entries are narrowed
|
|
326
|
-
private isEntry(value: unknown): value is
|
|
304
|
+
private isEntry(value: unknown): value is IEntry<ItemT> {
|
|
327
305
|
return typeof value === 'object' && value !== null && 'kind' in value;
|
|
328
306
|
}
|
|
329
307
|
|
|
330
|
-
entryKind(value: unknown):
|
|
308
|
+
entryKind(value: unknown): IEntry<ItemT>['kind'] | 'unknown' {
|
|
331
309
|
return this.isEntry(value) ? value.kind : 'unknown';
|
|
332
310
|
}
|
|
333
311
|
|
|
@@ -351,22 +329,37 @@ export class VirtualizedSectionList<ItemT = unknown>
|
|
|
351
329
|
};
|
|
352
330
|
}
|
|
353
331
|
|
|
354
|
-
// The
|
|
355
|
-
//
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
332
|
+
// The separator template for a gap: the section one, or the item one where a section's own
|
|
333
|
+
// template beats the list's
|
|
334
|
+
private slotFor(
|
|
335
|
+
gap: ISeparatorGap<ItemT, ISection<ItemT>>,
|
|
336
|
+
side: ISeparatorSlot<ItemT>['side'],
|
|
337
|
+
): ISeparatorSlot<ItemT> {
|
|
338
|
+
if (gap.props === undefined) return { template: undefined, gap, side };
|
|
339
|
+
const template =
|
|
340
|
+
gap.kind === SEPARATOR_GAP_KIND.section
|
|
341
|
+
? this.sectionSeparatorDir?.templateRef
|
|
342
|
+
: (gap.props.section.separator ?? this.itemSeparatorDir?.templateRef);
|
|
343
|
+
return { template, gap, side };
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
cellPlanOf(value: unknown, index: number): ICellPlan<ItemT> | undefined {
|
|
347
|
+
if (!this.isEntry(value) || value.kind !== SECTION_ENTRY_KIND.item)
|
|
348
|
+
return undefined;
|
|
349
|
+
const gaps = cellGapsFor(this.flatEntries, index);
|
|
350
|
+
const leading = this.slotFor(gaps.leading, SEPARATOR_SIDE.leading);
|
|
351
|
+
const trailing = this.slotFor(gaps.trailing, SEPARATOR_SIDE.trailing);
|
|
352
|
+
const previous = this.flatEntries[index - 1];
|
|
353
|
+
const isInverted = this.inverted === true;
|
|
361
354
|
return {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
355
|
+
cellKey: this.entryKeyExtractor(value),
|
|
356
|
+
prevCellKey:
|
|
357
|
+
previous === undefined ? undefined : this.entryKeyExtractor(previous),
|
|
358
|
+
hasLeading: leading.template !== undefined,
|
|
359
|
+
hasTrailing: trailing.template !== undefined,
|
|
360
|
+
itemTemplate: value.section.item ?? this.sectionItemDir?.templateRef,
|
|
361
|
+
first: isInverted ? trailing : leading,
|
|
362
|
+
second: isInverted ? leading : trailing,
|
|
370
363
|
};
|
|
371
364
|
}
|
|
372
365
|
|
|
@@ -377,12 +370,12 @@ export class VirtualizedSectionList<ItemT = unknown>
|
|
|
377
370
|
viewPosition?: number;
|
|
378
371
|
animated?: boolean;
|
|
379
372
|
}): void {
|
|
380
|
-
const
|
|
373
|
+
const target = resolveScrollLocation(
|
|
381
374
|
this.headerIndices,
|
|
382
|
-
|
|
383
|
-
params
|
|
375
|
+
this.stickyHeaderIndices,
|
|
376
|
+
params,
|
|
384
377
|
);
|
|
385
|
-
if (
|
|
378
|
+
if (target === undefined) {
|
|
386
379
|
dlog(
|
|
387
380
|
`Angular VirtualizedSectionList scrollToLocation: section ${params.sectionIndex} out of range`,
|
|
388
381
|
);
|
|
@@ -390,21 +383,16 @@ export class VirtualizedSectionList<ItemT = unknown>
|
|
|
390
383
|
}
|
|
391
384
|
dlog(
|
|
392
385
|
`Angular VirtualizedSectionList scrollToLocation section=${params.sectionIndex} ` +
|
|
393
|
-
`item=${params.itemIndex} -> flat ${
|
|
386
|
+
`item=${params.itemIndex} -> flat ${target.index}`,
|
|
394
387
|
);
|
|
395
|
-
this.list?.scrollToIndex(
|
|
396
|
-
index: flatIndex,
|
|
397
|
-
viewOffset: params.viewOffset,
|
|
398
|
-
viewPosition: params.viewPosition,
|
|
399
|
-
animated: params.animated,
|
|
400
|
-
});
|
|
388
|
+
this.list?.scrollToIndex(target);
|
|
401
389
|
}
|
|
402
390
|
|
|
403
391
|
flashScrollIndicators(): void {
|
|
404
392
|
this.list?.flashScrollIndicators();
|
|
405
393
|
}
|
|
406
394
|
|
|
407
|
-
getNativeScrollRef():
|
|
395
|
+
getNativeScrollRef(): ISymbioteNode | null {
|
|
408
396
|
return this.list?.getNativeScrollRef() ?? null;
|
|
409
397
|
}
|
|
410
398
|
|
|
@@ -420,7 +408,15 @@ export class VirtualizedSectionList<ItemT = unknown>
|
|
|
420
408
|
return this.list?.getScrollNode() ?? null;
|
|
421
409
|
}
|
|
422
410
|
|
|
411
|
+
getScrollRef(): ISymbioteNode | null {
|
|
412
|
+
return this.list?.getScrollRef() ?? null;
|
|
413
|
+
}
|
|
414
|
+
|
|
423
415
|
recordInteraction(): void {
|
|
424
416
|
this.list?.recordInteraction();
|
|
425
417
|
}
|
|
418
|
+
|
|
419
|
+
setNativeProps(props: Record<string, unknown>): void {
|
|
420
|
+
this.list?.setNativeProps(props);
|
|
421
|
+
}
|
|
426
422
|
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// An item cell with its separators around it, as in RN's `ItemWithSeparator`
|
|
2
|
+
// The state of those separators lives in a board shared by all cells, т.к. `highlight()` also
|
|
3
|
+
// lights the previous cell's one, this directive ties one cell to its key in that board
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
ChangeDetectorRef,
|
|
7
|
+
Directive,
|
|
8
|
+
Input,
|
|
9
|
+
inject,
|
|
10
|
+
type OnChanges,
|
|
11
|
+
type OnDestroy,
|
|
12
|
+
type SimpleChanges,
|
|
13
|
+
type TemplateRef,
|
|
14
|
+
} from '@angular/core';
|
|
15
|
+
import {
|
|
16
|
+
createCellSeparators,
|
|
17
|
+
SEPARATOR_SIDE,
|
|
18
|
+
type ICellSeparatorState,
|
|
19
|
+
type ISeparatorBoard,
|
|
20
|
+
type ISeparatorGap,
|
|
21
|
+
type ISeparators,
|
|
22
|
+
type ISeparatorSide,
|
|
23
|
+
} from '@symbiote-native/components';
|
|
24
|
+
import type { IVListSeparatorContext } from '../virtualized-list';
|
|
25
|
+
import type { ISection } from './directives';
|
|
26
|
+
|
|
27
|
+
export type ISeparatorSlot<ItemT> = {
|
|
28
|
+
template: TemplateRef<IVListSeparatorContext<ItemT>> | undefined;
|
|
29
|
+
gap: ISeparatorGap<ItemT, ISection<ItemT>>;
|
|
30
|
+
side: ISeparatorSide;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// The separator props RN passes, the app's `updateProps` overrides laid over them
|
|
34
|
+
export function separatorContextOf<ItemT>(
|
|
35
|
+
slot: ISeparatorSlot<ItemT>,
|
|
36
|
+
state: ICellSeparatorState<Record<string, unknown>>,
|
|
37
|
+
): IVListSeparatorContext<ItemT> | undefined {
|
|
38
|
+
if (slot.template === undefined || slot.gap.props === undefined)
|
|
39
|
+
return undefined;
|
|
40
|
+
const isLeading = slot.side === SEPARATOR_SIDE.leading;
|
|
41
|
+
const override = isLeading ? state.leadingOverride : state.trailingOverride;
|
|
42
|
+
const isHighlighted = isLeading
|
|
43
|
+
? state.leadingHighlighted
|
|
44
|
+
: state.trailingHighlighted;
|
|
45
|
+
const bag: IVListSeparatorContext<ItemT> = {
|
|
46
|
+
$implicit: isHighlighted,
|
|
47
|
+
highlighted: isHighlighted,
|
|
48
|
+
...slot.gap.props,
|
|
49
|
+
...override,
|
|
50
|
+
};
|
|
51
|
+
return { ...bag, props: bag };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@Directive({
|
|
55
|
+
selector: '[vSectionCell]',
|
|
56
|
+
exportAs: 'vSectionCell',
|
|
57
|
+
standalone: true,
|
|
58
|
+
})
|
|
59
|
+
export class VSectionCellDirective implements OnChanges, OnDestroy {
|
|
60
|
+
@Input({ required: true }) board!: ISeparatorBoard<Record<string, unknown>>;
|
|
61
|
+
@Input({ required: true }) cellKey!: string;
|
|
62
|
+
@Input() prevCellKey?: string;
|
|
63
|
+
@Input() hasLeading = false;
|
|
64
|
+
@Input() hasTrailing = false;
|
|
65
|
+
|
|
66
|
+
private readonly cdr = inject(ChangeDetectorRef);
|
|
67
|
+
private unsubscribe: (() => void) | undefined;
|
|
68
|
+
private subscribedKey: string | undefined;
|
|
69
|
+
|
|
70
|
+
// Built on first read, once the inputs are set, and stable so a template holding it keeps working
|
|
71
|
+
private cachedSeparators: ISeparators | undefined;
|
|
72
|
+
get separators(): ISeparators {
|
|
73
|
+
this.cachedSeparators ??= createCellSeparators(this.board, () => ({
|
|
74
|
+
cellKey: this.cellKey,
|
|
75
|
+
prevCellKey: this.prevCellKey,
|
|
76
|
+
has: { leading: this.hasLeading, trailing: this.hasTrailing },
|
|
77
|
+
}));
|
|
78
|
+
return this.cachedSeparators;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
contextOf<ItemT>(
|
|
82
|
+
slot: ISeparatorSlot<ItemT>,
|
|
83
|
+
): IVListSeparatorContext<ItemT> | undefined {
|
|
84
|
+
return separatorContextOf(slot, this.board.read(this.cellKey));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
ngOnChanges(changes: SimpleChanges): void {
|
|
88
|
+
if (changes['cellKey'] === undefined && changes['board'] === undefined)
|
|
89
|
+
return;
|
|
90
|
+
this.detach();
|
|
91
|
+
this.subscribedKey = this.cellKey;
|
|
92
|
+
this.unsubscribe = this.board.subscribe(this.cellKey, () =>
|
|
93
|
+
this.cdr.markForCheck(),
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
ngOnDestroy(): void {
|
|
98
|
+
this.detach();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// A recycled cell starts clean, as a remounted RN cell does
|
|
102
|
+
private detach(): void {
|
|
103
|
+
this.unsubscribe?.();
|
|
104
|
+
if (this.subscribedKey !== undefined)
|
|
105
|
+
this.board.release(this.subscribedKey);
|
|
106
|
+
this.unsubscribe = undefined;
|
|
107
|
+
this.subscribedKey = undefined;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// The public prop surface of the Angular VirtualizedSectionList, apart from the class
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
IAccessibilityProps,
|
|
5
|
+
IAriaProps,
|
|
6
|
+
IInnerViewRef,
|
|
7
|
+
} from '@symbiote-native/components';
|
|
8
|
+
import type {
|
|
9
|
+
IStyleProp,
|
|
10
|
+
ISymbioteEvent,
|
|
11
|
+
IViewStyle,
|
|
12
|
+
} from '@symbiote-native/engine';
|
|
13
|
+
import type { ISection } from './directives';
|
|
14
|
+
|
|
15
|
+
export type IItemLayout = { length: number; offset: number; index: number };
|
|
16
|
+
|
|
17
|
+
// The React and Vue surface minus element-returning props, which are `<ng-template>` here
|
|
18
|
+
export type IVirtualizedSectionListProps<ItemT> = IAccessibilityProps &
|
|
19
|
+
IAriaProps & {
|
|
20
|
+
sections: ReadonlyArray<ISection<ItemT>>;
|
|
21
|
+
keyExtractor?: (item: ItemT, index: number) => string;
|
|
22
|
+
// Flat like RN's: the sections array plus a flat entry index, where every section adds two rows
|
|
23
|
+
// beyond its items (header, footer). Without it a fast scroll outruns measurement
|
|
24
|
+
getItemLayout?: (
|
|
25
|
+
data: ReadonlyArray<ISection<ItemT>> | null,
|
|
26
|
+
index: number,
|
|
27
|
+
) => IItemLayout;
|
|
28
|
+
// Defaults to `Platform.OS === 'ios'`, pass true or false to override
|
|
29
|
+
stickySectionHeadersEnabled?: boolean;
|
|
30
|
+
extraData?: unknown;
|
|
31
|
+
onEndReached?: (info: { distanceFromEnd: number }) => void;
|
|
32
|
+
onEndReachedThreshold?: number;
|
|
33
|
+
onStartReached?: (info: { distanceFromStart: number }) => void;
|
|
34
|
+
onStartReachedThreshold?: number;
|
|
35
|
+
onRefresh?: () => void;
|
|
36
|
+
refreshing?: boolean | null;
|
|
37
|
+
progressViewOffset?: number;
|
|
38
|
+
initialNumToRender?: number;
|
|
39
|
+
initialScrollIndex?: number;
|
|
40
|
+
maxToRenderPerBatch?: number;
|
|
41
|
+
updateCellsBatchingPeriod?: number;
|
|
42
|
+
windowSize?: number;
|
|
43
|
+
disableVirtualization?: boolean;
|
|
44
|
+
inverted?: boolean;
|
|
45
|
+
horizontal?: boolean;
|
|
46
|
+
maintainVisibleContentPosition?: {
|
|
47
|
+
minIndexForVisible: number;
|
|
48
|
+
autoscrollToTopThreshold?: number;
|
|
49
|
+
};
|
|
50
|
+
onScroll?: (event: ISymbioteEvent) => void;
|
|
51
|
+
onScrollBeginDrag?: (event: ISymbioteEvent) => void;
|
|
52
|
+
onScrollEndDrag?: (event: ISymbioteEvent) => void;
|
|
53
|
+
onMomentumScrollBegin?: (event: ISymbioteEvent) => void;
|
|
54
|
+
onMomentumScrollEnd?: (event: ISymbioteEvent) => void;
|
|
55
|
+
onContentSizeChange?: (width: number, height: number) => void;
|
|
56
|
+
scrollEventThrottle?: number;
|
|
57
|
+
keyboardShouldPersistTaps?: boolean | 'always' | 'never' | 'handled';
|
|
58
|
+
keyboardDismissMode?: 'none' | 'on-drag' | 'interactive';
|
|
59
|
+
removeClippedSubviews?: boolean;
|
|
60
|
+
nestedScrollEnabled?: boolean;
|
|
61
|
+
stickyHeaderHiddenOnScroll?: boolean;
|
|
62
|
+
innerViewRef?: IInnerViewRef;
|
|
63
|
+
style?: IStyleProp<IViewStyle>;
|
|
64
|
+
contentContainerStyle?: IStyleProp<IViewStyle>;
|
|
65
|
+
listHeaderComponentStyle?: IStyleProp<IViewStyle>;
|
|
66
|
+
listFooterComponentStyle?: IStyleProp<IViewStyle>;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// The plain inputs: the full surface minus the events exposed as real outputs
|
|
70
|
+
export type IVirtualizedSectionListInputs<ItemT> = Omit<
|
|
71
|
+
IVirtualizedSectionListProps<ItemT>,
|
|
72
|
+
| 'onEndReached'
|
|
73
|
+
| 'onStartReached'
|
|
74
|
+
| 'onRefresh'
|
|
75
|
+
| 'onAccessibilityAction'
|
|
76
|
+
| 'onAccessibilityTap'
|
|
77
|
+
| 'onMagicTap'
|
|
78
|
+
| 'onAccessibilityEscape'
|
|
79
|
+
>;
|