@ng-native/components 0.0.1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +78 -1
- package/dist/activity-indicator.d.ts +41 -0
- package/dist/activity-indicator.js +66 -0
- package/dist/activity-indicator.js.map +1 -0
- package/dist/animated-style.d.ts +31 -0
- package/dist/animated-style.js +102 -0
- package/dist/animated-style.js.map +1 -0
- package/dist/animated.d.ts +125 -0
- package/dist/animated.js +538 -0
- package/dist/animated.js.map +1 -0
- package/dist/animation.d.ts +40 -0
- package/dist/animation.js +20 -0
- package/dist/animation.js.map +1 -0
- package/dist/animations-web.d.ts +8 -0
- package/dist/animations-web.js +31 -0
- package/dist/animations-web.js.map +1 -0
- package/dist/animations.d.ts +7 -0
- package/dist/animations.js +58 -0
- package/dist/animations.js.map +1 -0
- package/dist/controlled-model.d.ts +33 -0
- package/dist/controlled-model.js +47 -0
- package/dist/controlled-model.js.map +1 -0
- package/dist/easing.d.ts +21 -0
- package/dist/easing.js +50 -0
- package/dist/easing.js.map +1 -0
- package/dist/events.d.ts +169 -0
- package/dist/events.js +2 -0
- package/dist/events.js.map +1 -0
- package/dist/gesture-backend.d.ts +26 -0
- package/dist/gesture-backend.js +14 -0
- package/dist/gesture-backend.js.map +1 -0
- package/dist/gesture-dispatch.d.ts +67 -0
- package/dist/gesture-dispatch.js +106 -0
- package/dist/gesture-dispatch.js.map +1 -0
- package/dist/gestures.d.ts +23 -0
- package/dist/gestures.js +164 -0
- package/dist/gestures.js.map +1 -0
- package/dist/height-index.d.ts +31 -0
- package/dist/height-index.js +80 -0
- package/dist/height-index.js.map +1 -0
- package/dist/image-background.d.ts +32 -0
- package/dist/image-background.js +80 -0
- package/dist/image-background.js.map +1 -0
- package/dist/image.d.ts +79 -0
- package/dist/image.js +155 -0
- package/dist/image.js.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -0
- package/dist/input-accessory-view.d.ts +16 -0
- package/dist/input-accessory-view.js +29 -0
- package/dist/input-accessory-view.js.map +1 -0
- package/dist/keyboard-avoiding-view.d.ts +79 -0
- package/dist/keyboard-avoiding-view.js +172 -0
- package/dist/keyboard-avoiding-view.js.map +1 -0
- package/dist/keyboard-dock.d.ts +126 -0
- package/dist/keyboard-dock.js +327 -0
- package/dist/keyboard-dock.js.map +1 -0
- package/dist/keyboard-taps.d.ts +12 -0
- package/dist/keyboard-taps.js +51 -0
- package/dist/keyboard-taps.js.map +1 -0
- package/dist/modal.d.ts +56 -0
- package/dist/modal.js +102 -0
- package/dist/modal.js.map +1 -0
- package/dist/native-gesture.d.ts +21 -0
- package/dist/native-gesture.js +56 -0
- package/dist/native-gesture.js.map +1 -0
- package/dist/native-ref.d.ts +7 -0
- package/dist/native-ref.js +13 -0
- package/dist/native-ref.js.map +1 -0
- package/dist/pressable.d.ts +220 -0
- package/dist/pressable.js +484 -0
- package/dist/pressable.js.map +1 -0
- package/dist/reanimated.d.ts +19 -0
- package/dist/reanimated.js +111 -0
- package/dist/reanimated.js.map +1 -0
- package/dist/refresh-control.d.ts +50 -0
- package/dist/refresh-control.js +103 -0
- package/dist/refresh-control.js.map +1 -0
- package/dist/safe-area-provider.d.ts +11 -0
- package/dist/safe-area-provider.js +70 -0
- package/dist/safe-area-provider.js.map +1 -0
- package/dist/safe-area-view.d.ts +22 -0
- package/dist/safe-area-view.js +77 -0
- package/dist/safe-area-view.js.map +1 -0
- package/dist/safe-area.d.ts +12 -0
- package/dist/safe-area.js +27 -0
- package/dist/safe-area.js.map +1 -0
- package/dist/scroll-view-props.d.ts +89 -0
- package/dist/scroll-view-props.js +153 -0
- package/dist/scroll-view-props.js.map +1 -0
- package/dist/scroll-view.d.ts +95 -0
- package/dist/scroll-view.js +181 -0
- package/dist/scroll-view.js.map +1 -0
- package/dist/section-list.d.ts +146 -0
- package/dist/section-list.js +300 -0
- package/dist/section-list.js.map +1 -0
- package/dist/sticky-headers.d.ts +41 -0
- package/dist/sticky-headers.js +118 -0
- package/dist/sticky-headers.js.map +1 -0
- package/dist/swipe-refresh-layout.d.ts +51 -0
- package/dist/swipe-refresh-layout.js +162 -0
- package/dist/swipe-refresh-layout.js.map +1 -0
- package/dist/switch.d.ts +54 -0
- package/dist/switch.js +110 -0
- package/dist/switch.js.map +1 -0
- package/dist/template-slot.d.ts +18 -0
- package/dist/template-slot.js +30 -0
- package/dist/template-slot.js.map +1 -0
- package/dist/text-input.d.ts +168 -0
- package/dist/text-input.js +312 -0
- package/dist/text-input.js.map +1 -0
- package/dist/text.d.ts +70 -0
- package/dist/text.js +111 -0
- package/dist/text.js.map +1 -0
- package/dist/touchable-opacity.d.ts +36 -0
- package/dist/touchable-opacity.js +76 -0
- package/dist/touchable-opacity.js.map +1 -0
- package/dist/transforms.d.ts +2 -0
- package/dist/transforms.js +17 -0
- package/dist/transforms.js.map +1 -0
- package/dist/view-base.d.ts +332 -0
- package/dist/view-base.js +534 -0
- package/dist/view-base.js.map +1 -0
- package/dist/view.d.ts +12 -0
- package/dist/view.js +18 -0
- package/dist/view.js.map +1 -0
- package/dist/virtual-list.d.ts +530 -0
- package/dist/virtual-list.js +1150 -0
- package/dist/virtual-list.js.map +1 -0
- package/dist/worklet-scroll.d.ts +13 -0
- package/dist/worklet-scroll.js +49 -0
- package/dist/worklet-scroll.js.map +1 -0
- package/dist/worklet-style.d.ts +13 -0
- package/dist/worklet-style.js +47 -0
- package/dist/worklet-style.js.map +1 -0
- package/dist/worklets.d.ts +89 -0
- package/dist/worklets.js +42 -0
- package/dist/worklets.js.map +1 -0
- package/dist/written-props.d.ts +6 -0
- package/dist/written-props.js +180 -0
- package/dist/written-props.js.map +1 -0
- package/package.json +88 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scroll-view.js","sourceRoot":"","sources":["../src/scroll-view.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n DestroyRef,\n ElementRef,\n afterEveryRender,\n computed,\n contentChild,\n inject,\n input,\n output,\n viewChild,\n} from '@angular/core';\nimport { type HostNode, type NativeSyntheticEvent, nativePlatform } from '@ng-native/fabric';\nimport type { Point, Rect, Size } from './events.ts';\nimport { type KeyboardShouldPersistTaps, dismissKeyboardOnTap } from './keyboard-taps.ts';\nimport { RefreshControl } from './refresh-control.ts';\nimport { StickyHeaders } from './sticky-headers.ts';\nimport { optionalBoolean } from './transforms.ts';\nimport { ScrollViewProps } from './scroll-view-props.ts';\nimport { View } from './view.ts';\n\n/**\n * A scrolling container. Commits as `RCTScrollView`, with the children inside a content view.\n *\n * The native scroll view has no `contentContainerStyle`: RN's `ScrollView.js` wraps children in\n * a content view and applies it there, which is done here. The engine gives the host RN's base\n * style (`overflow: 'scroll'` and the flex defaults); without it the content spills out of the\n * scroll view's bounds.\n *\n * `horizontal` changes two things rather than one, and both are needed. RN swaps its whole base\n * style for `baseHorizontal` - the same defaults but `flexDirection: 'row'` - and puts\n * `contentContainerHorizontal` on the content view. With the host left as a column the content\n * view is stretched to the scroll view's width instead of growing past it, so there is nothing to\n * scroll to and a sideways drag does nothing at all. It looks like a scroll view that simply has\n * too little in it.\n *\n * Keyboard policy is the other half of what `ScrollView.js` does in JavaScript. With a text\n * input focused, a tap elsewhere in the scroll view dismisses the keyboard and goes no further\n * (`keyboardShouldPersistTaps: 'never'`, the default), reaches the child first and dismisses only\n * if nothing handled it (`'handled'`), or never dismisses (`'always'`).\n *\n * `stickyHeaderIndices` pins those children of the content to the leading edge, each until the\n * next pushes it off. It is JavaScript in RN as well; see `StickyHeaders`.\n *\n * Events are element events: `(scroll)`, `(scrollBeginDrag)`, `(scrollEndDrag)`,\n * `(momentumScrollBegin)`, `(momentumScrollEnd)`, `(scrollToTop)`. `(contentSizeChange)` is an\n * output, measured from the content view's layout as RN does.\n */\n@Component({\n selector: 'scroll-view',\n exportAs: 'scrollView',\n imports: [View],\n // `collapsable: false` is load-bearing, not a hint. Fabric flattens views whose props are\n // layout-only, and a content container with just padding qualifies: it gets no UIView at all.\n // Touches in its empty areas then land on the scroll view itself, which does not scroll from a\n // direct hit, so dragging anywhere the children do not cover silently does nothing. RN sets the\n // same flag on its content container for the same reason.\n //\n // A refresh control is the scroll view's own child, before the content view, as RN's\n // ScrollView.js commits it. On iOS it finds its scroll view once, when it is inserted, and\n // Fabric mounts bottom-up: inside the content view it would look before that view was in the\n // scroll view, and never attach.\n template: `\n <ng-content select=\"refresh-control\" />\n <view #content [style]=\"contentStyle()\" collapsable=\"false\" (layout)=\"onContentLayout($event)\">\n <ng-content />\n </view>\n `,\n host: {\n // The base style's direction, which `horizontal` decides. A top-level prop rather than a\n // host `[style]`, so the caller's own `[style]` still applies over it as it does in RN.\n '[flexDirection]': \"horizontal() ? 'row' : 'column'\",\n '[horizontal]': 'horizontal()',\n '[contentOffset]': 'contentOffset()',\n '[maintainVisibleContentPosition]': 'maintainVisibleContentPosition()',\n '[scrollEventThrottle]': 'resolvedThrottle()',\n '[nestedScrollEnabled]': 'resolvedNestedScroll()',\n },\n})\nexport class ScrollView extends ScrollViewProps {\n /** Styles for the view that holds the children, e.g. padding and gap. */\n readonly contentContainerStyle = input<Record<string, unknown>>();\n\n /**\n * The content view's style, with the row direction a horizontal scroll view needs.\n *\n * RN's `contentContainerHorizontal`, and in the same order: the caller's own style is applied\n * over it, so a horizontal scroll view whose content really should stack in a column can still\n * say so.\n */\n protected readonly contentStyle = computed(() =>\n this.horizontal()\n ? { flexDirection: 'row', ...this.contentContainerStyle() }\n : this.contentContainerStyle(),\n );\n /** Lay children out in a row and scroll sideways. */\n readonly horizontal = input(undefined, { transform: optionalBoolean });\n /** Where the content starts, before the user scrolls. */\n readonly contentOffset = input<Point>();\n /** Keep what is on screen still when content is added above it. */\n readonly maintainVisibleContentPosition = input<{\n readonly minIndexForVisible: number;\n readonly autoscrollToTopThreshold?: number;\n }>();\n /** What a tap does with the keyboard up. See the class note. Defaults to `never`. */\n readonly keyboardShouldPersistTaps = input<KeyboardShouldPersistTaps>('never');\n /** Android: cooperate with a scrolling ancestor. */\n readonly nestedScrollEnabled = input(undefined, { transform: optionalBoolean });\n\n /** Children of the content, by position, that stick to the leading edge while it scrolls. */\n readonly stickyHeaderIndices = input<readonly number[]>([]);\n\n /** The content view's size changed; the value is its new width and height. */\n readonly contentSizeChange = output<Size>();\n\n private readonly refreshControl = contentChild(RefreshControl);\n private readonly content = viewChild.required('content', { read: ElementRef });\n private readonly sticky = new StickyHeaders(this.engine, this.node, () => !!this.horizontal());\n\n /** RN asks for every scroll event while anything is sticky, which is what keeps it pinned. */\n protected readonly resolvedThrottle = computed(() =>\n this.stickyHeaderIndices().length > 0 ? 1 : this.scrollEventThrottle(),\n );\n\n /**\n * RN's `nestedScrollEnabled ?? true` for a scroll view inside Android's swipe layout: the scroll\n * view has to see a drag before the layout wrapped around it does, or every drag is a pull.\n */\n protected readonly resolvedNestedScroll = computed(\n () =>\n this.nestedScrollEnabled() ??\n (nativePlatform() === 'android' && this.refreshControl() ? true : undefined),\n );\n\n constructor() {\n super();\n const stop = dismissKeyboardOnTap(this.engine, this.node, () =>\n this.keyboardShouldPersistTaps(),\n );\n inject(DestroyRef).onDestroy(stop);\n this.trackStickyHeaders();\n }\n\n /**\n * Sticky children are found again after each pass, because a pass can add or move them, and the\n * scroll offset is listened for only while there are any.\n */\n private trackStickyHeaders(): void {\n let stopScroll: (() => void) | null = null;\n afterEveryRender(() => {\n const indices = this.stickyHeaderIndices();\n if (indices.length === 0 && !stopScroll) return;\n this.sticky.track(this.content().nativeElement as HostNode, indices);\n if (indices.length > 0 && !stopScroll) {\n stopScroll = this.engine.setEventListener(this.node, 'topScroll', (event) => {\n const offset = (event as NativeSyntheticEvent<{ contentOffset?: Point }>).nativeEvent\n ?.contentOffset;\n this.sticky.scrolled((this.horizontal() ? offset?.x : offset?.y) ?? 0);\n });\n } else if (indices.length === 0 && stopScroll) {\n stopScroll();\n stopScroll = null;\n }\n });\n inject(DestroyRef).onDestroy(() => {\n stopScroll?.();\n this.sticky.destroy();\n });\n }\n\n /** Scroll to a position. */\n scrollTo(options: { x?: number; y?: number; animated?: boolean }): void {\n const { x = 0, y = 0, animated = true } = options;\n this.engine.dispatchCommand(this.node, 'scrollTo', [x, y, animated]);\n }\n\n scrollToEnd(options: { animated?: boolean } = {}): void {\n this.engine.dispatchCommand(this.node, 'scrollToEnd', [options.animated ?? true]);\n }\n\n /** Briefly show the scroll indicators, to hint that there is more. */\n flashScrollIndicators(): void {\n this.engine.dispatchCommand(this.node, 'flashScrollIndicators');\n }\n\n /** iOS: zoom so that a rectangle of the content fills the viewport. */\n zoomToRect(rect: Rect, animated = true): void {\n this.engine.dispatchCommand(this.node, 'zoomToRect', [rect, animated]);\n }\n\n protected onContentLayout(event: NativeSyntheticEvent<{ layout?: Size }>): void {\n const layout = event.nativeEvent?.layout;\n if (layout) this.contentSizeChange.emit({ width: layout.width, height: layout.height });\n }\n}\n"]}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { TemplateRef } from '@angular/core';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
/** A section as RN's SectionList takes it: its rows in `data`, plus whatever else it carries. */
|
|
4
|
+
export interface SectionListSection<T> {
|
|
5
|
+
readonly key?: string;
|
|
6
|
+
readonly title?: string;
|
|
7
|
+
readonly data: readonly T[];
|
|
8
|
+
}
|
|
9
|
+
/** What a section header or footer template is given. */
|
|
10
|
+
export interface SectionContext<S> {
|
|
11
|
+
readonly $implicit: S;
|
|
12
|
+
readonly section: S;
|
|
13
|
+
readonly sectionIndex: number;
|
|
14
|
+
}
|
|
15
|
+
/** What an item template is given. `index` counts within the section, as `renderItem` does. */
|
|
16
|
+
export interface SectionItemContext<T, S> {
|
|
17
|
+
readonly $implicit: T;
|
|
18
|
+
readonly item: T;
|
|
19
|
+
readonly index: number;
|
|
20
|
+
readonly section: S;
|
|
21
|
+
readonly sectionIndex: number;
|
|
22
|
+
}
|
|
23
|
+
/** What a separator template is given: the items either side of it, in the same section. */
|
|
24
|
+
export interface SectionSeparatorContext<T, S> {
|
|
25
|
+
readonly $implicit: T;
|
|
26
|
+
readonly leadingItem: T;
|
|
27
|
+
readonly trailingItem: T;
|
|
28
|
+
readonly section: S;
|
|
29
|
+
}
|
|
30
|
+
/** `<ng-template sectionHeader let-section>`: RN's `renderSectionHeader`. */
|
|
31
|
+
export declare class SectionHeader<S = unknown> {
|
|
32
|
+
readonly template: TemplateRef<SectionContext<S>>;
|
|
33
|
+
static ngTemplateContextGuard<S>(_directive: SectionHeader<S>, context: unknown): context is SectionContext<S>;
|
|
34
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SectionHeader<any>, never>;
|
|
35
|
+
static ɵdir: i0.ɵɵDirectiveDeclaration<SectionHeader<any>, "ng-template[sectionHeader]", never, {}, {}, never, never, true, never>;
|
|
36
|
+
}
|
|
37
|
+
/** `<ng-template sectionFooter let-section>`: RN's `renderSectionFooter`. */
|
|
38
|
+
export declare class SectionFooter<S = unknown> {
|
|
39
|
+
readonly template: TemplateRef<SectionContext<S>>;
|
|
40
|
+
static ngTemplateContextGuard<S>(_directive: SectionFooter<S>, context: unknown): context is SectionContext<S>;
|
|
41
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SectionFooter<any>, never>;
|
|
42
|
+
static ɵdir: i0.ɵɵDirectiveDeclaration<SectionFooter<any>, "ng-template[sectionFooter]", never, {}, {}, never, never, true, never>;
|
|
43
|
+
}
|
|
44
|
+
/** `<ng-template sectionItem let-item let-index="index">`: RN's `renderItem`. */
|
|
45
|
+
export declare class SectionItem<T = unknown, S = unknown> {
|
|
46
|
+
readonly template: TemplateRef<SectionItemContext<T, S>>;
|
|
47
|
+
static ngTemplateContextGuard<T, S>(_directive: SectionItem<T, S>, context: unknown): context is SectionItemContext<T, S>;
|
|
48
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SectionItem<any, any>, never>;
|
|
49
|
+
static ɵdir: i0.ɵɵDirectiveDeclaration<SectionItem<any, any>, "ng-template[sectionItem]", never, {}, {}, never, never, true, never>;
|
|
50
|
+
}
|
|
51
|
+
/** `<ng-template sectionSeparator let-leading>`: RN's `ItemSeparatorComponent`. */
|
|
52
|
+
export declare class SectionSeparator<T = unknown, S = unknown> {
|
|
53
|
+
readonly template: TemplateRef<SectionSeparatorContext<T, S>>;
|
|
54
|
+
static ngTemplateContextGuard<T, S>(_directive: SectionSeparator<T, S>, context: unknown): context is SectionSeparatorContext<T, S>;
|
|
55
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SectionSeparator<any, any>, never>;
|
|
56
|
+
static ɵdir: i0.ɵɵDirectiveDeclaration<SectionSeparator<any, any>, "ng-template[sectionSeparator]", never, {}, {}, never, never, true, never>;
|
|
57
|
+
}
|
|
58
|
+
/** One row of the flattened list. */
|
|
59
|
+
export type SectionRow<T, S> = {
|
|
60
|
+
readonly kind: 'header' | 'footer';
|
|
61
|
+
readonly context: SectionContext<S>;
|
|
62
|
+
readonly separator: null;
|
|
63
|
+
} | {
|
|
64
|
+
readonly kind: 'item';
|
|
65
|
+
readonly context: SectionItemContext<T, S>;
|
|
66
|
+
/** Null for a section's last item, which RN draws no separator after. */
|
|
67
|
+
readonly separator: SectionSeparatorContext<T, S> | null;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Flatten sections into the rows RN's `VirtualizedSectionList` gives its list: for each section a
|
|
71
|
+
* header, its items, then a footer. The header and footer rows are there whether or not anything
|
|
72
|
+
* is drawn in them, as in RN, which is what keeps `scrollToLocation`'s arithmetic the same.
|
|
73
|
+
*/
|
|
74
|
+
export declare function flattenSections<T, S extends SectionListSection<T>>(sections: readonly S[]): SectionRow<T, S>[];
|
|
75
|
+
type Height<A extends unknown[]> = number | ((...args: A) => number);
|
|
76
|
+
/**
|
|
77
|
+
* RN's SectionList: sections of items, each with a header and footer, over `<virtual-list>`.
|
|
78
|
+
*
|
|
79
|
+
* RN's own is a VirtualizedList over flattened rows, and so is this. Sections become one list of
|
|
80
|
+
* header, item and footer rows, the rows are placed by `<virtual-list>`'s variable-height windowing,
|
|
81
|
+
* and this component draws each from the caller's templates:
|
|
82
|
+
*
|
|
83
|
+
* ```html
|
|
84
|
+
* <section-list [sections]="sections" [itemHeight]="44" [sectionHeaderHeight]="28">
|
|
85
|
+
* <ng-template sectionHeader let-section><text>{{ section.title }}</text></ng-template>
|
|
86
|
+
* <ng-template sectionItem let-item><text>{{ item }}</text></ng-template>
|
|
87
|
+
* <ng-template sectionSeparator><view [style]="line"></view></ng-template>
|
|
88
|
+
* </section-list>
|
|
89
|
+
* ```
|
|
90
|
+
*
|
|
91
|
+
* Rows are fixed height, as `getItemLayout` makes them in RN: `itemHeight`, `sectionHeaderHeight`
|
|
92
|
+
* and `sectionFooterHeight` are numbers or functions. An item's height includes its separator,
|
|
93
|
+
* which is drawn at the bottom of the item's slot and only between items of the same section.
|
|
94
|
+
*
|
|
95
|
+
* `stickySectionHeadersEnabled` pins the current section's header until the next pushes it off.
|
|
96
|
+
* It defaults to on for iOS and off for Android, as RN's does. `listHeader` and `listFooter`
|
|
97
|
+
* content and a `<refresh-control>` pass through to the list.
|
|
98
|
+
*
|
|
99
|
+
* ponytail: no `SectionSeparatorComponent`, no `horizontal` or `inverted`, and no viewability
|
|
100
|
+
* events. The host is a plain view with the list filling it, where RN's host is the scroll view.
|
|
101
|
+
*/
|
|
102
|
+
export declare class SectionList<T, S extends SectionListSection<T> = SectionListSection<T>> {
|
|
103
|
+
readonly sections: import("@angular/core").InputSignal<readonly S[]>;
|
|
104
|
+
/** Each item's height, separator included. */
|
|
105
|
+
readonly itemHeight: import("@angular/core").InputSignal<Height<[item: T, index: number, section: S]>>;
|
|
106
|
+
readonly sectionHeaderHeight: import("@angular/core").InputSignal<Height<[section: S]>>;
|
|
107
|
+
readonly sectionFooterHeight: import("@angular/core").InputSignal<Height<[section: S]>>;
|
|
108
|
+
/** Pin the current section's header. Defaults to true on iOS and false on Android, as RN. */
|
|
109
|
+
readonly stickySectionHeadersEnabled: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
|
110
|
+
readonly overscan: import("@angular/core").InputSignalWithTransform<number, unknown>;
|
|
111
|
+
readonly endReachedThreshold: import("@angular/core").InputSignalWithTransform<number, unknown>;
|
|
112
|
+
/** The end of the last section is within `endReachedThreshold`. See `<virtual-list>`. */
|
|
113
|
+
readonly endReached: import("@angular/core").OutputEmitterRef<{
|
|
114
|
+
distanceFromEnd: number;
|
|
115
|
+
}>;
|
|
116
|
+
protected readonly header: import("@angular/core").Signal<SectionHeader<S> | undefined>;
|
|
117
|
+
protected readonly footer: import("@angular/core").Signal<SectionFooter<S> | undefined>;
|
|
118
|
+
protected readonly item: import("@angular/core").Signal<SectionItem<T, S> | undefined>;
|
|
119
|
+
protected readonly separator: import("@angular/core").Signal<SectionSeparator<T, S> | undefined>;
|
|
120
|
+
private readonly list;
|
|
121
|
+
protected readonly fill: {
|
|
122
|
+
flex: number;
|
|
123
|
+
};
|
|
124
|
+
/** The item fills its slot above the separator, as RN's cell puts the separator after it. */
|
|
125
|
+
protected readonly cell: {
|
|
126
|
+
flex: number;
|
|
127
|
+
};
|
|
128
|
+
protected readonly rows: import("@angular/core").Signal<SectionRow<T, S>[]>;
|
|
129
|
+
private readonly sticky;
|
|
130
|
+
/** The header rows, pinned in turn, when headers stick and there is a header to draw. */
|
|
131
|
+
protected readonly stickyRows: import("@angular/core").Signal<number[]>;
|
|
132
|
+
protected readonly rowHeight: (row: SectionRow<T, S>) => number;
|
|
133
|
+
/**
|
|
134
|
+
* Scroll to an item, RN's way: `itemIndex` 0 is the section's header and 1 its first item, and
|
|
135
|
+
* with sticky headers the header's height is allowed for so the item is not hidden under it.
|
|
136
|
+
*/
|
|
137
|
+
scrollToLocation(options: {
|
|
138
|
+
sectionIndex: number;
|
|
139
|
+
itemIndex: number;
|
|
140
|
+
viewOffset?: number;
|
|
141
|
+
animated?: boolean;
|
|
142
|
+
}): void;
|
|
143
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SectionList<any, any>, never>;
|
|
144
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<SectionList<any, any>, "section-list", ["sectionList"], { "sections": { "alias": "sections"; "required": true; "isSignal": true; }; "itemHeight": { "alias": "itemHeight"; "required": true; "isSignal": true; }; "sectionHeaderHeight": { "alias": "sectionHeaderHeight"; "required": false; "isSignal": true; }; "sectionFooterHeight": { "alias": "sectionFooterHeight"; "required": false; "isSignal": true; }; "stickySectionHeadersEnabled": { "alias": "stickySectionHeadersEnabled"; "required": false; "isSignal": true; }; "overscan": { "alias": "overscan"; "required": false; "isSignal": true; }; "endReachedThreshold": { "alias": "endReachedThreshold"; "required": false; "isSignal": true; }; }, { "endReached": "endReached"; }, ["header", "footer", "item", "separator"], ["refresh-control", "[listHeader]", "*", "[listFooter]"], true, never>;
|
|
145
|
+
}
|
|
146
|
+
export {};
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { Component, Directive, TemplateRef, computed, contentChild, inject, input, numberAttribute, output, viewChild, } from '@angular/core';
|
|
2
|
+
import { nativePlatform } from '@ng-native/fabric';
|
|
3
|
+
import { TemplateSlot } from "./template-slot.js";
|
|
4
|
+
import { optionalBoolean } from "./transforms.js";
|
|
5
|
+
import { View } from "./view.js";
|
|
6
|
+
import { VirtualList } from "./virtual-list.js";
|
|
7
|
+
import * as i0 from "@angular/core";
|
|
8
|
+
/** `<ng-template sectionHeader let-section>`: RN's `renderSectionHeader`. */
|
|
9
|
+
export class SectionHeader {
|
|
10
|
+
template = inject(TemplateRef);
|
|
11
|
+
static ngTemplateContextGuard(_directive, context) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: SectionHeader, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
15
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.2.0", type: SectionHeader, isStandalone: true, selector: "ng-template[sectionHeader]", ngImport: i0 });
|
|
16
|
+
}
|
|
17
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: SectionHeader, decorators: [{
|
|
18
|
+
type: Directive,
|
|
19
|
+
args: [{ selector: 'ng-template[sectionHeader]' }]
|
|
20
|
+
}] });
|
|
21
|
+
/** `<ng-template sectionFooter let-section>`: RN's `renderSectionFooter`. */
|
|
22
|
+
export class SectionFooter {
|
|
23
|
+
template = inject(TemplateRef);
|
|
24
|
+
static ngTemplateContextGuard(_directive, context) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: SectionFooter, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
28
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.2.0", type: SectionFooter, isStandalone: true, selector: "ng-template[sectionFooter]", ngImport: i0 });
|
|
29
|
+
}
|
|
30
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: SectionFooter, decorators: [{
|
|
31
|
+
type: Directive,
|
|
32
|
+
args: [{ selector: 'ng-template[sectionFooter]' }]
|
|
33
|
+
}] });
|
|
34
|
+
/** `<ng-template sectionItem let-item let-index="index">`: RN's `renderItem`. */
|
|
35
|
+
export class SectionItem {
|
|
36
|
+
template = inject(TemplateRef);
|
|
37
|
+
static ngTemplateContextGuard(_directive, context) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: SectionItem, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
41
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.2.0", type: SectionItem, isStandalone: true, selector: "ng-template[sectionItem]", ngImport: i0 });
|
|
42
|
+
}
|
|
43
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: SectionItem, decorators: [{
|
|
44
|
+
type: Directive,
|
|
45
|
+
args: [{ selector: 'ng-template[sectionItem]' }]
|
|
46
|
+
}] });
|
|
47
|
+
/** `<ng-template sectionSeparator let-leading>`: RN's `ItemSeparatorComponent`. */
|
|
48
|
+
export class SectionSeparator {
|
|
49
|
+
template = inject(TemplateRef);
|
|
50
|
+
static ngTemplateContextGuard(_directive, context) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: SectionSeparator, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
54
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.2.0", type: SectionSeparator, isStandalone: true, selector: "ng-template[sectionSeparator]", ngImport: i0 });
|
|
55
|
+
}
|
|
56
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: SectionSeparator, decorators: [{
|
|
57
|
+
type: Directive,
|
|
58
|
+
args: [{ selector: 'ng-template[sectionSeparator]' }]
|
|
59
|
+
}] });
|
|
60
|
+
/**
|
|
61
|
+
* Flatten sections into the rows RN's `VirtualizedSectionList` gives its list: for each section a
|
|
62
|
+
* header, its items, then a footer. The header and footer rows are there whether or not anything
|
|
63
|
+
* is drawn in them, as in RN, which is what keeps `scrollToLocation`'s arithmetic the same.
|
|
64
|
+
*/
|
|
65
|
+
export function flattenSections(sections) {
|
|
66
|
+
const rows = [];
|
|
67
|
+
sections.forEach((section, sectionIndex) => {
|
|
68
|
+
const context = { $implicit: section, section, sectionIndex };
|
|
69
|
+
rows.push({ kind: 'header', context, separator: null });
|
|
70
|
+
const data = section.data;
|
|
71
|
+
data.forEach((item, index) => {
|
|
72
|
+
const trailingItem = data[index + 1];
|
|
73
|
+
rows.push({
|
|
74
|
+
kind: 'item',
|
|
75
|
+
context: { $implicit: item, item, index, section, sectionIndex },
|
|
76
|
+
separator: index < data.length - 1
|
|
77
|
+
? { $implicit: item, leadingItem: item, trailingItem: trailingItem, section }
|
|
78
|
+
: null,
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
rows.push({ kind: 'footer', context, separator: null });
|
|
82
|
+
});
|
|
83
|
+
return rows;
|
|
84
|
+
}
|
|
85
|
+
const measure = (height, ...args) => typeof height === 'number' ? height : height(...args);
|
|
86
|
+
/**
|
|
87
|
+
* RN's SectionList: sections of items, each with a header and footer, over `<virtual-list>`.
|
|
88
|
+
*
|
|
89
|
+
* RN's own is a VirtualizedList over flattened rows, and so is this. Sections become one list of
|
|
90
|
+
* header, item and footer rows, the rows are placed by `<virtual-list>`'s variable-height windowing,
|
|
91
|
+
* and this component draws each from the caller's templates:
|
|
92
|
+
*
|
|
93
|
+
* ```html
|
|
94
|
+
* <section-list [sections]="sections" [itemHeight]="44" [sectionHeaderHeight]="28">
|
|
95
|
+
* <ng-template sectionHeader let-section><text>{{ section.title }}</text></ng-template>
|
|
96
|
+
* <ng-template sectionItem let-item><text>{{ item }}</text></ng-template>
|
|
97
|
+
* <ng-template sectionSeparator><view [style]="line"></view></ng-template>
|
|
98
|
+
* </section-list>
|
|
99
|
+
* ```
|
|
100
|
+
*
|
|
101
|
+
* Rows are fixed height, as `getItemLayout` makes them in RN: `itemHeight`, `sectionHeaderHeight`
|
|
102
|
+
* and `sectionFooterHeight` are numbers or functions. An item's height includes its separator,
|
|
103
|
+
* which is drawn at the bottom of the item's slot and only between items of the same section.
|
|
104
|
+
*
|
|
105
|
+
* `stickySectionHeadersEnabled` pins the current section's header until the next pushes it off.
|
|
106
|
+
* It defaults to on for iOS and off for Android, as RN's does. `listHeader` and `listFooter`
|
|
107
|
+
* content and a `<refresh-control>` pass through to the list.
|
|
108
|
+
*
|
|
109
|
+
* ponytail: no `SectionSeparatorComponent`, no `horizontal` or `inverted`, and no viewability
|
|
110
|
+
* events. The host is a plain view with the list filling it, where RN's host is the scroll view.
|
|
111
|
+
*/
|
|
112
|
+
export class SectionList {
|
|
113
|
+
sections = input.required(/* @ts-ignore */
|
|
114
|
+
...(ngDevMode ? [{ debugName: "sections" }] : /* istanbul ignore next */ []));
|
|
115
|
+
/** Each item's height, separator included. */
|
|
116
|
+
itemHeight = input.required(/* @ts-ignore */
|
|
117
|
+
...(ngDevMode ? [{ debugName: "itemHeight" }] : /* istanbul ignore next */ []));
|
|
118
|
+
sectionHeaderHeight = input(0, /* @ts-ignore */
|
|
119
|
+
...(ngDevMode ? [{ debugName: "sectionHeaderHeight" }] : /* istanbul ignore next */ []));
|
|
120
|
+
sectionFooterHeight = input(0, /* @ts-ignore */
|
|
121
|
+
...(ngDevMode ? [{ debugName: "sectionFooterHeight" }] : /* istanbul ignore next */ []));
|
|
122
|
+
/** Pin the current section's header. Defaults to true on iOS and false on Android, as RN. */
|
|
123
|
+
stickySectionHeadersEnabled = input(undefined, { ...(ngDevMode ? { debugName: "stickySectionHeadersEnabled" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
124
|
+
overscan = input(4, { ...(ngDevMode ? { debugName: "overscan" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
125
|
+
endReachedThreshold = input(2, { ...(ngDevMode ? { debugName: "endReachedThreshold" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
126
|
+
/** The end of the last section is within `endReachedThreshold`. See `<virtual-list>`. */
|
|
127
|
+
endReached = output();
|
|
128
|
+
header = contentChild(SectionHeader, /* @ts-ignore */
|
|
129
|
+
...(ngDevMode ? [{ debugName: "header" }] : /* istanbul ignore next */ []));
|
|
130
|
+
footer = contentChild(SectionFooter, /* @ts-ignore */
|
|
131
|
+
...(ngDevMode ? [{ debugName: "footer" }] : /* istanbul ignore next */ []));
|
|
132
|
+
item = contentChild(SectionItem, /* @ts-ignore */
|
|
133
|
+
...(ngDevMode ? [{ debugName: "item" }] : /* istanbul ignore next */ []));
|
|
134
|
+
separator = contentChild(SectionSeparator, /* @ts-ignore */
|
|
135
|
+
...(ngDevMode ? [{ debugName: "separator" }] : /* istanbul ignore next */ []));
|
|
136
|
+
list = viewChild.required('list', /* @ts-ignore */
|
|
137
|
+
...(ngDevMode ? [{ debugName: "list" }] : /* istanbul ignore next */ []));
|
|
138
|
+
fill = { flex: 1 };
|
|
139
|
+
/** The item fills its slot above the separator, as RN's cell puts the separator after it. */
|
|
140
|
+
cell = { flex: 1 };
|
|
141
|
+
rows = computed(() => flattenSections(this.sections()), /* @ts-ignore */
|
|
142
|
+
...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
|
|
143
|
+
sticky = computed(() => this.stickySectionHeadersEnabled() ?? nativePlatform() === 'ios', /* @ts-ignore */
|
|
144
|
+
...(ngDevMode ? [{ debugName: "sticky" }] : /* istanbul ignore next */ []));
|
|
145
|
+
/** The header rows, pinned in turn, when headers stick and there is a header to draw. */
|
|
146
|
+
stickyRows = computed(() => {
|
|
147
|
+
if (!this.sticky() || !this.header())
|
|
148
|
+
return [];
|
|
149
|
+
const indices = [];
|
|
150
|
+
this.rows().forEach((row, index) => {
|
|
151
|
+
if (row.kind === 'header')
|
|
152
|
+
indices.push(index);
|
|
153
|
+
});
|
|
154
|
+
return indices;
|
|
155
|
+
}, /* @ts-ignore */
|
|
156
|
+
...(ngDevMode ? [{ debugName: "stickyRows" }] : /* istanbul ignore next */ []));
|
|
157
|
+
rowHeight = (row) => {
|
|
158
|
+
const { section } = row.context;
|
|
159
|
+
if (row.kind === 'item') {
|
|
160
|
+
return measure(this.itemHeight(), row.context.item, row.context.index, section);
|
|
161
|
+
}
|
|
162
|
+
return row.kind === 'header'
|
|
163
|
+
? measure(this.sectionHeaderHeight(), section)
|
|
164
|
+
: measure(this.sectionFooterHeight(), section);
|
|
165
|
+
};
|
|
166
|
+
/**
|
|
167
|
+
* Scroll to an item, RN's way: `itemIndex` 0 is the section's header and 1 its first item, and
|
|
168
|
+
* with sticky headers the header's height is allowed for so the item is not hidden under it.
|
|
169
|
+
*/
|
|
170
|
+
scrollToLocation(options) {
|
|
171
|
+
const sections = this.sections();
|
|
172
|
+
let index = options.itemIndex;
|
|
173
|
+
for (let i = 0; i < options.sectionIndex; i++)
|
|
174
|
+
index += sections[i].data.length + 2;
|
|
175
|
+
let viewOffset = options.viewOffset ?? 0;
|
|
176
|
+
const section = sections[options.sectionIndex];
|
|
177
|
+
if (options.itemIndex > 0 && this.sticky() && section) {
|
|
178
|
+
viewOffset += measure(this.sectionHeaderHeight(), section);
|
|
179
|
+
}
|
|
180
|
+
this.list().scrollToIndex({ index, viewOffset, animated: options.animated });
|
|
181
|
+
}
|
|
182
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: SectionList, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
183
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.2.0", type: SectionList, isStandalone: true, selector: "section-list", inputs: { sections: { classPropertyName: "sections", publicName: "sections", isSignal: true, isRequired: true, transformFunction: null }, itemHeight: { classPropertyName: "itemHeight", publicName: "itemHeight", isSignal: true, isRequired: true, transformFunction: null }, sectionHeaderHeight: { classPropertyName: "sectionHeaderHeight", publicName: "sectionHeaderHeight", isSignal: true, isRequired: false, transformFunction: null }, sectionFooterHeight: { classPropertyName: "sectionFooterHeight", publicName: "sectionFooterHeight", isSignal: true, isRequired: false, transformFunction: null }, stickySectionHeadersEnabled: { classPropertyName: "stickySectionHeadersEnabled", publicName: "stickySectionHeadersEnabled", isSignal: true, isRequired: false, transformFunction: null }, overscan: { classPropertyName: "overscan", publicName: "overscan", isSignal: true, isRequired: false, transformFunction: null }, endReachedThreshold: { classPropertyName: "endReachedThreshold", publicName: "endReachedThreshold", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { endReached: "endReached" }, queries: [{ propertyName: "header", first: true, predicate: SectionHeader, descendants: true, isSignal: true }, { propertyName: "footer", first: true, predicate: SectionFooter, descendants: true, isSignal: true }, { propertyName: "item", first: true, predicate: SectionItem, descendants: true, isSignal: true }, { propertyName: "separator", first: true, predicate: SectionSeparator, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "list", first: true, predicate: ["list"], descendants: true, isSignal: true }], exportAs: ["sectionList"], ngImport: i0, template: `
|
|
184
|
+
<virtual-list
|
|
185
|
+
#list
|
|
186
|
+
[items]="rows()"
|
|
187
|
+
[itemHeight]="rowHeight"
|
|
188
|
+
[stickyIndices]="stickyRows()"
|
|
189
|
+
[overscan]="overscan()"
|
|
190
|
+
[endReachedThreshold]="endReachedThreshold()"
|
|
191
|
+
[style]="fill"
|
|
192
|
+
(endReached)="endReached.emit($event)"
|
|
193
|
+
>
|
|
194
|
+
<ng-content select="refresh-control" ngProjectAs="refresh-control" />
|
|
195
|
+
<view listHeader><ng-content select="[listHeader]" /></view>
|
|
196
|
+
<ng-content />
|
|
197
|
+
@for (row of list.window(); track row.index) {
|
|
198
|
+
<view [style]="row.style">
|
|
199
|
+
@switch (row.item.kind) {
|
|
200
|
+
@case ('header') {
|
|
201
|
+
@if (header(); as header) {
|
|
202
|
+
<ng-container
|
|
203
|
+
[templateSlot]="header.template"
|
|
204
|
+
[templateSlotContext]="row.item.context"
|
|
205
|
+
/>
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
@case ('footer') {
|
|
209
|
+
@if (footer(); as footer) {
|
|
210
|
+
<ng-container
|
|
211
|
+
[templateSlot]="footer.template"
|
|
212
|
+
[templateSlotContext]="row.item.context"
|
|
213
|
+
/>
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
@case ('item') {
|
|
217
|
+
<view [style]="cell">
|
|
218
|
+
@if (item(); as item) {
|
|
219
|
+
<ng-container
|
|
220
|
+
[templateSlot]="item.template"
|
|
221
|
+
[templateSlotContext]="row.item.context"
|
|
222
|
+
/>
|
|
223
|
+
}
|
|
224
|
+
</view>
|
|
225
|
+
@if (separator(); as separator) {
|
|
226
|
+
@if (row.item.separator; as gap) {
|
|
227
|
+
<ng-container [templateSlot]="separator.template" [templateSlotContext]="gap" />
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
</view>
|
|
233
|
+
}
|
|
234
|
+
<view listFooter><ng-content select="[listFooter]" /></view>
|
|
235
|
+
</virtual-list>
|
|
236
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: TemplateSlot, selector: "[templateSlot]", inputs: ["templateSlot", "templateSlotContext"] }, { kind: "directive", type: View, selector: "view" }, { kind: "component", type: VirtualList, selector: "virtual-list", inputs: ["items", "itemHeight", "estimatedItemHeight", "keyExtractor", "itemType", "maintainVisibleContentPosition", "overscan", "endReachedThreshold", "horizontal", "inverted", "stickyIndices", "stickyHeader", "keyboardShouldPersistTaps", "itemVisiblePercentThreshold"], outputs: ["endReached", "viewableItemsChanged"], exportAs: ["virtualList"] }] });
|
|
237
|
+
}
|
|
238
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: SectionList, decorators: [{
|
|
239
|
+
type: Component,
|
|
240
|
+
args: [{
|
|
241
|
+
selector: 'section-list',
|
|
242
|
+
exportAs: 'sectionList',
|
|
243
|
+
imports: [TemplateSlot, View, VirtualList],
|
|
244
|
+
template: `
|
|
245
|
+
<virtual-list
|
|
246
|
+
#list
|
|
247
|
+
[items]="rows()"
|
|
248
|
+
[itemHeight]="rowHeight"
|
|
249
|
+
[stickyIndices]="stickyRows()"
|
|
250
|
+
[overscan]="overscan()"
|
|
251
|
+
[endReachedThreshold]="endReachedThreshold()"
|
|
252
|
+
[style]="fill"
|
|
253
|
+
(endReached)="endReached.emit($event)"
|
|
254
|
+
>
|
|
255
|
+
<ng-content select="refresh-control" ngProjectAs="refresh-control" />
|
|
256
|
+
<view listHeader><ng-content select="[listHeader]" /></view>
|
|
257
|
+
<ng-content />
|
|
258
|
+
@for (row of list.window(); track row.index) {
|
|
259
|
+
<view [style]="row.style">
|
|
260
|
+
@switch (row.item.kind) {
|
|
261
|
+
@case ('header') {
|
|
262
|
+
@if (header(); as header) {
|
|
263
|
+
<ng-container
|
|
264
|
+
[templateSlot]="header.template"
|
|
265
|
+
[templateSlotContext]="row.item.context"
|
|
266
|
+
/>
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
@case ('footer') {
|
|
270
|
+
@if (footer(); as footer) {
|
|
271
|
+
<ng-container
|
|
272
|
+
[templateSlot]="footer.template"
|
|
273
|
+
[templateSlotContext]="row.item.context"
|
|
274
|
+
/>
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
@case ('item') {
|
|
278
|
+
<view [style]="cell">
|
|
279
|
+
@if (item(); as item) {
|
|
280
|
+
<ng-container
|
|
281
|
+
[templateSlot]="item.template"
|
|
282
|
+
[templateSlotContext]="row.item.context"
|
|
283
|
+
/>
|
|
284
|
+
}
|
|
285
|
+
</view>
|
|
286
|
+
@if (separator(); as separator) {
|
|
287
|
+
@if (row.item.separator; as gap) {
|
|
288
|
+
<ng-container [templateSlot]="separator.template" [templateSlotContext]="gap" />
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
</view>
|
|
294
|
+
}
|
|
295
|
+
<view listFooter><ng-content select="[listFooter]" /></view>
|
|
296
|
+
</virtual-list>
|
|
297
|
+
`,
|
|
298
|
+
}]
|
|
299
|
+
}], propDecorators: { sections: [{ type: i0.Input, args: [{ isSignal: true, alias: "sections", required: true }] }], itemHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemHeight", required: true }] }], sectionHeaderHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "sectionHeaderHeight", required: false }] }], sectionFooterHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "sectionFooterHeight", required: false }] }], stickySectionHeadersEnabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "stickySectionHeadersEnabled", required: false }] }], overscan: [{ type: i0.Input, args: [{ isSignal: true, alias: "overscan", required: false }] }], endReachedThreshold: [{ type: i0.Input, args: [{ isSignal: true, alias: "endReachedThreshold", required: false }] }], endReached: [{ type: i0.Output, args: ["endReached"] }], header: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SectionHeader), { isSignal: true }] }], footer: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SectionFooter), { isSignal: true }] }], item: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SectionItem), { isSignal: true }] }], separator: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SectionSeparator), { isSignal: true }] }], list: [{ type: i0.ViewChild, args: ['list', { isSignal: true }] }] } });
|
|
300
|
+
//# sourceMappingURL=section-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"section-list.js","sourceRoot":"","sources":["../src/section-list.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n Directive,\n TemplateRef,\n computed,\n contentChild,\n inject,\n input,\n numberAttribute,\n output,\n viewChild,\n} from '@angular/core';\nimport { nativePlatform } from '@ng-native/fabric';\nimport { TemplateSlot } from './template-slot.ts';\nimport { optionalBoolean } from './transforms.ts';\nimport { View } from './view.ts';\nimport { VirtualList } from './virtual-list.ts';\n\n/** A section as RN's SectionList takes it: its rows in `data`, plus whatever else it carries. */\nexport interface SectionListSection<T> {\n readonly key?: string;\n readonly title?: string;\n readonly data: readonly T[];\n}\n\n/** What a section header or footer template is given. */\nexport interface SectionContext<S> {\n readonly $implicit: S;\n readonly section: S;\n readonly sectionIndex: number;\n}\n\n/** What an item template is given. `index` counts within the section, as `renderItem` does. */\nexport interface SectionItemContext<T, S> {\n readonly $implicit: T;\n readonly item: T;\n readonly index: number;\n readonly section: S;\n readonly sectionIndex: number;\n}\n\n/** What a separator template is given: the items either side of it, in the same section. */\nexport interface SectionSeparatorContext<T, S> {\n readonly $implicit: T;\n readonly leadingItem: T;\n readonly trailingItem: T;\n readonly section: S;\n}\n\n/** `<ng-template sectionHeader let-section>`: RN's `renderSectionHeader`. */\n@Directive({ selector: 'ng-template[sectionHeader]' })\nexport class SectionHeader<S = unknown> {\n readonly template = inject<TemplateRef<SectionContext<S>>>(TemplateRef);\n\n static ngTemplateContextGuard<S>(\n _directive: SectionHeader<S>,\n context: unknown,\n ): context is SectionContext<S> {\n return true;\n }\n}\n\n/** `<ng-template sectionFooter let-section>`: RN's `renderSectionFooter`. */\n@Directive({ selector: 'ng-template[sectionFooter]' })\nexport class SectionFooter<S = unknown> {\n readonly template = inject<TemplateRef<SectionContext<S>>>(TemplateRef);\n\n static ngTemplateContextGuard<S>(\n _directive: SectionFooter<S>,\n context: unknown,\n ): context is SectionContext<S> {\n return true;\n }\n}\n\n/** `<ng-template sectionItem let-item let-index=\"index\">`: RN's `renderItem`. */\n@Directive({ selector: 'ng-template[sectionItem]' })\nexport class SectionItem<T = unknown, S = unknown> {\n readonly template = inject<TemplateRef<SectionItemContext<T, S>>>(TemplateRef);\n\n static ngTemplateContextGuard<T, S>(\n _directive: SectionItem<T, S>,\n context: unknown,\n ): context is SectionItemContext<T, S> {\n return true;\n }\n}\n\n/** `<ng-template sectionSeparator let-leading>`: RN's `ItemSeparatorComponent`. */\n@Directive({ selector: 'ng-template[sectionSeparator]' })\nexport class SectionSeparator<T = unknown, S = unknown> {\n readonly template = inject<TemplateRef<SectionSeparatorContext<T, S>>>(TemplateRef);\n\n static ngTemplateContextGuard<T, S>(\n _directive: SectionSeparator<T, S>,\n context: unknown,\n ): context is SectionSeparatorContext<T, S> {\n return true;\n }\n}\n\n/** One row of the flattened list. */\nexport type SectionRow<T, S> =\n | {\n readonly kind: 'header' | 'footer';\n readonly context: SectionContext<S>;\n readonly separator: null;\n }\n | {\n readonly kind: 'item';\n readonly context: SectionItemContext<T, S>;\n /** Null for a section's last item, which RN draws no separator after. */\n readonly separator: SectionSeparatorContext<T, S> | null;\n };\n\n/**\n * Flatten sections into the rows RN's `VirtualizedSectionList` gives its list: for each section a\n * header, its items, then a footer. The header and footer rows are there whether or not anything\n * is drawn in them, as in RN, which is what keeps `scrollToLocation`'s arithmetic the same.\n */\nexport function flattenSections<T, S extends SectionListSection<T>>(\n sections: readonly S[],\n): SectionRow<T, S>[] {\n const rows: SectionRow<T, S>[] = [];\n sections.forEach((section, sectionIndex) => {\n const context = { $implicit: section, section, sectionIndex };\n rows.push({ kind: 'header', context, separator: null });\n const data = section.data;\n data.forEach((item, index) => {\n const trailingItem = data[index + 1];\n rows.push({\n kind: 'item',\n context: { $implicit: item, item, index, section, sectionIndex },\n separator:\n index < data.length - 1\n ? { $implicit: item, leadingItem: item, trailingItem: trailingItem!, section }\n : null,\n });\n });\n rows.push({ kind: 'footer', context, separator: null });\n });\n return rows;\n}\n\ntype Height<A extends unknown[]> = number | ((...args: A) => number);\n\nconst measure = <A extends unknown[]>(height: Height<A>, ...args: A): number =>\n typeof height === 'number' ? height : height(...args);\n\n/**\n * RN's SectionList: sections of items, each with a header and footer, over `<virtual-list>`.\n *\n * RN's own is a VirtualizedList over flattened rows, and so is this. Sections become one list of\n * header, item and footer rows, the rows are placed by `<virtual-list>`'s variable-height windowing,\n * and this component draws each from the caller's templates:\n *\n * ```html\n * <section-list [sections]=\"sections\" [itemHeight]=\"44\" [sectionHeaderHeight]=\"28\">\n * <ng-template sectionHeader let-section><text>{{ section.title }}</text></ng-template>\n * <ng-template sectionItem let-item><text>{{ item }}</text></ng-template>\n * <ng-template sectionSeparator><view [style]=\"line\"></view></ng-template>\n * </section-list>\n * ```\n *\n * Rows are fixed height, as `getItemLayout` makes them in RN: `itemHeight`, `sectionHeaderHeight`\n * and `sectionFooterHeight` are numbers or functions. An item's height includes its separator,\n * which is drawn at the bottom of the item's slot and only between items of the same section.\n *\n * `stickySectionHeadersEnabled` pins the current section's header until the next pushes it off.\n * It defaults to on for iOS and off for Android, as RN's does. `listHeader` and `listFooter`\n * content and a `<refresh-control>` pass through to the list.\n *\n * ponytail: no `SectionSeparatorComponent`, no `horizontal` or `inverted`, and no viewability\n * events. The host is a plain view with the list filling it, where RN's host is the scroll view.\n */\n@Component({\n selector: 'section-list',\n exportAs: 'sectionList',\n imports: [TemplateSlot, View, VirtualList],\n template: `\n <virtual-list\n #list\n [items]=\"rows()\"\n [itemHeight]=\"rowHeight\"\n [stickyIndices]=\"stickyRows()\"\n [overscan]=\"overscan()\"\n [endReachedThreshold]=\"endReachedThreshold()\"\n [style]=\"fill\"\n (endReached)=\"endReached.emit($event)\"\n >\n <ng-content select=\"refresh-control\" ngProjectAs=\"refresh-control\" />\n <view listHeader><ng-content select=\"[listHeader]\" /></view>\n <ng-content />\n @for (row of list.window(); track row.index) {\n <view [style]=\"row.style\">\n @switch (row.item.kind) {\n @case ('header') {\n @if (header(); as header) {\n <ng-container\n [templateSlot]=\"header.template\"\n [templateSlotContext]=\"row.item.context\"\n />\n }\n }\n @case ('footer') {\n @if (footer(); as footer) {\n <ng-container\n [templateSlot]=\"footer.template\"\n [templateSlotContext]=\"row.item.context\"\n />\n }\n }\n @case ('item') {\n <view [style]=\"cell\">\n @if (item(); as item) {\n <ng-container\n [templateSlot]=\"item.template\"\n [templateSlotContext]=\"row.item.context\"\n />\n }\n </view>\n @if (separator(); as separator) {\n @if (row.item.separator; as gap) {\n <ng-container [templateSlot]=\"separator.template\" [templateSlotContext]=\"gap\" />\n }\n }\n }\n }\n </view>\n }\n <view listFooter><ng-content select=\"[listFooter]\" /></view>\n </virtual-list>\n `,\n})\nexport class SectionList<T, S extends SectionListSection<T> = SectionListSection<T>> {\n readonly sections = input.required<readonly S[]>();\n /** Each item's height, separator included. */\n readonly itemHeight = input.required<Height<[item: T, index: number, section: S]>>();\n readonly sectionHeaderHeight = input<Height<[section: S]>>(0);\n readonly sectionFooterHeight = input<Height<[section: S]>>(0);\n /** Pin the current section's header. Defaults to true on iOS and false on Android, as RN. */\n readonly stickySectionHeadersEnabled = input(undefined, { transform: optionalBoolean });\n readonly overscan = input(4, { transform: numberAttribute });\n readonly endReachedThreshold = input(2, { transform: numberAttribute });\n\n /** The end of the last section is within `endReachedThreshold`. See `<virtual-list>`. */\n readonly endReached = output<{ distanceFromEnd: number }>();\n\n protected readonly header = contentChild<SectionHeader<S>>(SectionHeader);\n protected readonly footer = contentChild<SectionFooter<S>>(SectionFooter);\n protected readonly item = contentChild<SectionItem<T, S>>(SectionItem);\n protected readonly separator = contentChild<SectionSeparator<T, S>>(SectionSeparator);\n private readonly list = viewChild.required<VirtualList<SectionRow<T, S>>>('list');\n\n protected readonly fill = { flex: 1 };\n /** The item fills its slot above the separator, as RN's cell puts the separator after it. */\n protected readonly cell = { flex: 1 };\n\n protected readonly rows = computed(() => flattenSections<T, S>(this.sections()));\n\n private readonly sticky = computed(\n () => this.stickySectionHeadersEnabled() ?? nativePlatform() === 'ios',\n );\n\n /** The header rows, pinned in turn, when headers stick and there is a header to draw. */\n protected readonly stickyRows = computed(() => {\n if (!this.sticky() || !this.header()) return [];\n const indices: number[] = [];\n this.rows().forEach((row, index) => {\n if (row.kind === 'header') indices.push(index);\n });\n return indices;\n });\n\n protected readonly rowHeight = (row: SectionRow<T, S>): number => {\n const { section } = row.context;\n if (row.kind === 'item') {\n return measure(this.itemHeight(), row.context.item, row.context.index, section);\n }\n return row.kind === 'header'\n ? measure(this.sectionHeaderHeight(), section)\n : measure(this.sectionFooterHeight(), section);\n };\n\n /**\n * Scroll to an item, RN's way: `itemIndex` 0 is the section's header and 1 its first item, and\n * with sticky headers the header's height is allowed for so the item is not hidden under it.\n */\n scrollToLocation(options: {\n sectionIndex: number;\n itemIndex: number;\n viewOffset?: number;\n animated?: boolean;\n }): void {\n const sections = this.sections();\n let index = options.itemIndex;\n for (let i = 0; i < options.sectionIndex; i++) index += sections[i]!.data.length + 2;\n let viewOffset = options.viewOffset ?? 0;\n const section = sections[options.sectionIndex];\n if (options.itemIndex > 0 && this.sticky() && section) {\n viewOffset += measure(this.sectionHeaderHeight(), section);\n }\n this.list().scrollToIndex({ index, viewOffset, animated: options.animated });\n }\n}\n"]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type HostEngine, type HostNode } from '@ng-native/fabric';
|
|
2
|
+
/**
|
|
3
|
+
* `stickyHeaderIndices` for `<scroll-view>`, done the way RN's `ScrollView.js` does it.
|
|
4
|
+
*
|
|
5
|
+
* There is no native prop for it on either platform. RN wraps each sticky child in
|
|
6
|
+
* `ScrollViewStickyHeader`, which translates it by the scroll offset past its own position and
|
|
7
|
+
* stops where the next sticky child would push it off. The same sums run here, over the content
|
|
8
|
+
* view's element children: control-flow anchors are not children in RN's sense and are skipped,
|
|
9
|
+
* so an index counts what is committed.
|
|
10
|
+
*
|
|
11
|
+
* Each sticky child's position comes from its own layout event, and the translation is written
|
|
12
|
+
* as its `transform` prop, with RN's `zIndex: 10` so it draws over the content scrolling under
|
|
13
|
+
* it. A style of the caller's own that sets either wins, as it would in RN.
|
|
14
|
+
*
|
|
15
|
+
* Where the host can, native moves each one on every frame the scroll view moves, as RN's
|
|
16
|
+
* animated interpolation does, and the translation is written only once scrolling pauses: a
|
|
17
|
+
* translate written from each scroll event lands late, and on a fast fling the header shudders.
|
|
18
|
+
*/
|
|
19
|
+
export declare class StickyHeaders {
|
|
20
|
+
private readonly engine;
|
|
21
|
+
private readonly horizontal;
|
|
22
|
+
private headers;
|
|
23
|
+
private readonly extents;
|
|
24
|
+
private readonly listening;
|
|
25
|
+
/** The translation last written per header, so an unchanged one is not written again. */
|
|
26
|
+
private readonly written;
|
|
27
|
+
private readonly drives;
|
|
28
|
+
private settleTimer;
|
|
29
|
+
private offset;
|
|
30
|
+
constructor(engine: HostEngine, scroll: HostNode, horizontal: () => boolean);
|
|
31
|
+
private readonly scroll;
|
|
32
|
+
/** Find the sticky children again, after a pass that may have changed them. */
|
|
33
|
+
track(content: HostNode | undefined, indices: readonly number[]): void;
|
|
34
|
+
scrolled(offset: number): void;
|
|
35
|
+
destroy(): void;
|
|
36
|
+
private measured;
|
|
37
|
+
private release;
|
|
38
|
+
private drive;
|
|
39
|
+
/** `ScrollViewStickyHeader`'s interpolation, as plain arithmetic. */
|
|
40
|
+
private apply;
|
|
41
|
+
}
|