@ng-native/components 0.1.3 → 0.3.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/dist/content-container.d.ts +25 -0
- package/dist/content-container.js +35 -0
- package/dist/content-container.js.map +1 -0
- package/dist/gestures-web.d.ts +12 -0
- package/dist/gestures-web.js +32 -0
- package/dist/gestures-web.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/keyboard-avoiding-view.d.ts +6 -1
- package/dist/keyboard-avoiding-view.js +23 -6
- package/dist/keyboard-avoiding-view.js.map +1 -1
- package/dist/pressable.d.ts +33 -4
- package/dist/pressable.js +55 -3
- package/dist/pressable.js.map +1 -1
- package/dist/reanimated-web.d.ts +26 -0
- package/dist/reanimated-web.js +59 -0
- package/dist/reanimated-web.js.map +1 -0
- package/dist/refresh-control.d.ts +4 -1
- package/dist/refresh-control.js +4 -1
- package/dist/refresh-control.js.map +1 -1
- package/dist/scroll-view.d.ts +17 -1
- package/dist/scroll-view.js +59 -6
- package/dist/scroll-view.js.map +1 -1
- package/dist/section-list.d.ts +14 -2
- package/dist/section-list.js +20 -4
- package/dist/section-list.js.map +1 -1
- package/dist/sticky-headers.d.ts +6 -0
- package/dist/sticky-headers.js +7 -2
- package/dist/sticky-headers.js.map +1 -1
- package/dist/switch.d.ts +11 -1
- package/dist/switch.js +16 -3
- package/dist/switch.js.map +1 -1
- package/dist/text.d.ts +15 -0
- package/dist/text.js +26 -1
- package/dist/text.js.map +1 -1
- package/dist/touchable-opacity.d.ts +2 -1
- package/dist/touchable-opacity.js +3 -0
- package/dist/touchable-opacity.js.map +1 -1
- package/dist/view-base.d.ts +34 -18
- package/dist/view-base.js +52 -22
- package/dist/view-base.js.map +1 -1
- package/dist/virtual-list.d.ts +49 -5
- package/dist/virtual-list.js +129 -42
- package/dist/virtual-list.js.map +1 -1
- package/dist/zero-size-warning.d.ts +30 -0
- package/dist/zero-size-warning.js +58 -0
- package/dist/zero-size-warning.js.map +1 -0
- package/package.json +7 -4
package/dist/section-list.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { TemplateRef } from '@angular/core';
|
|
2
|
+
import type { KeyboardShouldPersistTaps } from './keyboard-taps.ts';
|
|
3
|
+
import { type VirtualListPadding } from './virtual-list.ts';
|
|
2
4
|
import * as i0 from "@angular/core";
|
|
3
5
|
/** A section as RN's SectionList takes it: its rows in `data`, plus whatever else it carries. */
|
|
4
6
|
export interface SectionListSection<T> {
|
|
@@ -132,8 +134,14 @@ type Height<A extends unknown[]> = number | ((...args: A) => number);
|
|
|
132
134
|
* It defaults to on for iOS and off for Android, as RN's does. `listHeader` and `listFooter`
|
|
133
135
|
* content and a `<refresh-control>` pass through to the list.
|
|
134
136
|
*
|
|
137
|
+
* `contentPadding` and `keyboardShouldPersistTaps` pass through to the list as `<virtual-list>`
|
|
138
|
+
* takes them.
|
|
139
|
+
*
|
|
135
140
|
* ponytail: no `horizontal` or `inverted`, no viewability events, and no `highlighted` on a
|
|
136
|
-
* separator.
|
|
141
|
+
* separator. No `keyExtractor` or `maintainVisibleContentPosition` either: rows are fixed height,
|
|
142
|
+
* so there is no measured height for a key to carry across an insert. The host is a plain view
|
|
143
|
+
* with the list filling it, where RN's host is the scroll view, so scroll-view props are not
|
|
144
|
+
* taken.
|
|
137
145
|
*/
|
|
138
146
|
export declare class SectionList<T, S extends SectionListSection<T> = SectionListSection<T>> {
|
|
139
147
|
readonly sections: import("@angular/core").InputSignal<readonly S[]>;
|
|
@@ -145,6 +153,10 @@ export declare class SectionList<T, S extends SectionListSection<T> = SectionLis
|
|
|
145
153
|
readonly stickySectionHeadersEnabled: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
|
146
154
|
readonly overscan: import("@angular/core").InputSignalWithTransform<number, unknown>;
|
|
147
155
|
readonly endReachedThreshold: import("@angular/core").InputSignalWithTransform<number, unknown>;
|
|
156
|
+
/** Space around the rows, inside the scrolling content. See `<virtual-list>`. */
|
|
157
|
+
readonly contentPadding: import("@angular/core").InputSignal<VirtualListPadding>;
|
|
158
|
+
/** What a tap in the list does while a text input has the keyboard up. See `ScrollView`. */
|
|
159
|
+
readonly keyboardShouldPersistTaps: import("@angular/core").InputSignal<KeyboardShouldPersistTaps>;
|
|
148
160
|
/** The end of the last section is within `endReachedThreshold`. See `<virtual-list>`. */
|
|
149
161
|
readonly endReached: import("@angular/core").OutputEmitterRef<{
|
|
150
162
|
distanceFromEnd: number;
|
|
@@ -178,6 +190,6 @@ export declare class SectionList<T, S extends SectionListSection<T> = SectionLis
|
|
|
178
190
|
animated?: boolean;
|
|
179
191
|
}): void;
|
|
180
192
|
static ɵfac: i0.ɵɵFactoryDeclaration<SectionList<any, any>, never>;
|
|
181
|
-
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", "edge"], ["refresh-control", "[listHeader]", "*", "[listFooter]"], true, never>;
|
|
193
|
+
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; }; "contentPadding": { "alias": "contentPadding"; "required": false; "isSignal": true; }; "keyboardShouldPersistTaps": { "alias": "keyboardShouldPersistTaps"; "required": false; "isSignal": true; }; }, { "endReached": "endReached"; }, ["header", "footer", "item", "separator", "edge"], ["refresh-control", "[listHeader]", "*", "[listFooter]"], true, never>;
|
|
182
194
|
}
|
|
183
195
|
export {};
|
package/dist/section-list.js
CHANGED
|
@@ -140,8 +140,14 @@ const measure = (height, ...args) => typeof height === 'number' ? height : heigh
|
|
|
140
140
|
* It defaults to on for iOS and off for Android, as RN's does. `listHeader` and `listFooter`
|
|
141
141
|
* content and a `<refresh-control>` pass through to the list.
|
|
142
142
|
*
|
|
143
|
+
* `contentPadding` and `keyboardShouldPersistTaps` pass through to the list as `<virtual-list>`
|
|
144
|
+
* takes them.
|
|
145
|
+
*
|
|
143
146
|
* ponytail: no `horizontal` or `inverted`, no viewability events, and no `highlighted` on a
|
|
144
|
-
* separator.
|
|
147
|
+
* separator. No `keyExtractor` or `maintainVisibleContentPosition` either: rows are fixed height,
|
|
148
|
+
* so there is no measured height for a key to carry across an insert. The host is a plain view
|
|
149
|
+
* with the list filling it, where RN's host is the scroll view, so scroll-view props are not
|
|
150
|
+
* taken.
|
|
145
151
|
*/
|
|
146
152
|
export class SectionList {
|
|
147
153
|
sections = input.required(/* @ts-ignore */
|
|
@@ -157,6 +163,12 @@ export class SectionList {
|
|
|
157
163
|
stickySectionHeadersEnabled = input(undefined, { ...(ngDevMode ? { debugName: "stickySectionHeadersEnabled" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
158
164
|
overscan = input(4, { ...(ngDevMode ? { debugName: "overscan" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
159
165
|
endReachedThreshold = input(2, { ...(ngDevMode ? { debugName: "endReachedThreshold" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
166
|
+
/** Space around the rows, inside the scrolling content. See `<virtual-list>`. */
|
|
167
|
+
contentPadding = input(0, /* @ts-ignore */
|
|
168
|
+
...(ngDevMode ? [{ debugName: "contentPadding" }] : /* istanbul ignore next */ []));
|
|
169
|
+
/** What a tap in the list does while a text input has the keyboard up. See `ScrollView`. */
|
|
170
|
+
keyboardShouldPersistTaps = input('never', /* @ts-ignore */
|
|
171
|
+
...(ngDevMode ? [{ debugName: "keyboardShouldPersistTaps" }] : /* istanbul ignore next */ []));
|
|
160
172
|
/** The end of the last section is within `endReachedThreshold`. See `<virtual-list>`. */
|
|
161
173
|
endReached = output();
|
|
162
174
|
header = contentChild(SectionHeader, /* @ts-ignore */
|
|
@@ -216,7 +228,7 @@ export class SectionList {
|
|
|
216
228
|
this.list().scrollToIndex({ index, viewOffset, animated: options.animated });
|
|
217
229
|
}
|
|
218
230
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: SectionList, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
219
|
-
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 }, { propertyName: "edge", first: true, predicate: SectionEdgeSeparator, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "list", first: true, predicate: ["list"], descendants: true, isSignal: true }], exportAs: ["sectionList"], ngImport: i0, template: `
|
|
231
|
+
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 }, contentPadding: { classPropertyName: "contentPadding", publicName: "contentPadding", isSignal: true, isRequired: false, transformFunction: null }, keyboardShouldPersistTaps: { classPropertyName: "keyboardShouldPersistTaps", publicName: "keyboardShouldPersistTaps", 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 }, { propertyName: "edge", first: true, predicate: SectionEdgeSeparator, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "list", first: true, predicate: ["list"], descendants: true, isSignal: true }], exportAs: ["sectionList"], ngImport: i0, template: `
|
|
220
232
|
<virtual-list
|
|
221
233
|
#list
|
|
222
234
|
[items]="rows()"
|
|
@@ -224,6 +236,8 @@ export class SectionList {
|
|
|
224
236
|
[stickyIndices]="stickyRows()"
|
|
225
237
|
[overscan]="overscan()"
|
|
226
238
|
[endReachedThreshold]="endReachedThreshold()"
|
|
239
|
+
[contentPadding]="contentPadding()"
|
|
240
|
+
[keyboardShouldPersistTaps]="keyboardShouldPersistTaps()"
|
|
227
241
|
[style]="fill"
|
|
228
242
|
(endReached)="endReached.emit($event)"
|
|
229
243
|
>
|
|
@@ -279,7 +293,7 @@ export class SectionList {
|
|
|
279
293
|
}
|
|
280
294
|
<view listFooter><ng-content select="[listFooter]" /></view>
|
|
281
295
|
</virtual-list>
|
|
282
|
-
`, 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"] }] });
|
|
296
|
+
`, 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", "contentPadding", "rowGap", "keyExtractor", "itemType", "maintainVisibleContentPosition", "overscan", "endReachedThreshold", "horizontal", "inverted", "stickyIndices", "stickyHeader", "keyboardShouldPersistTaps", "itemVisiblePercentThreshold"], outputs: ["endReached", "viewableItemsChanged"], exportAs: ["virtualList"] }] });
|
|
283
297
|
}
|
|
284
298
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: SectionList, decorators: [{
|
|
285
299
|
type: Component,
|
|
@@ -295,6 +309,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImpor
|
|
|
295
309
|
[stickyIndices]="stickyRows()"
|
|
296
310
|
[overscan]="overscan()"
|
|
297
311
|
[endReachedThreshold]="endReachedThreshold()"
|
|
312
|
+
[contentPadding]="contentPadding()"
|
|
313
|
+
[keyboardShouldPersistTaps]="keyboardShouldPersistTaps()"
|
|
298
314
|
[style]="fill"
|
|
299
315
|
(endReached)="endReached.emit($event)"
|
|
300
316
|
>
|
|
@@ -352,5 +368,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImpor
|
|
|
352
368
|
</virtual-list>
|
|
353
369
|
`,
|
|
354
370
|
}]
|
|
355
|
-
}], 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 }] }], edge: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SectionEdgeSeparator), { isSignal: true }] }], list: [{ type: i0.ViewChild, args: ['list', { isSignal: true }] }] } });
|
|
371
|
+
}], 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 }] }], contentPadding: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentPadding", required: false }] }], keyboardShouldPersistTaps: [{ type: i0.Input, args: [{ isSignal: true, alias: "keyboardShouldPersistTaps", 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 }] }], edge: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SectionEdgeSeparator), { isSignal: true }] }], list: [{ type: i0.ViewChild, args: ['list', { isSignal: true }] }] } });
|
|
356
372
|
//# sourceMappingURL=section-list.js.map
|
package/dist/section-list.js.map
CHANGED
|
@@ -1 +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/**\n * What a section separator template is given: the section, and what is either side of the edge\n * it sits at. At a section's start `leadingItem` is undefined, and at its end `trailingItem` is.\n */\nexport interface SectionEdgeSeparatorContext<T, S> {\n readonly $implicit: S;\n readonly section: S;\n readonly leadingItem: T | undefined;\n readonly leadingSection: S | undefined;\n readonly trailingItem: T | undefined;\n readonly trailingSection: S | undefined;\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/**\n * `<ng-template sectionEdgeSeparator let-section>`: RN's `SectionSeparatorComponent`, drawn at\n * both edges of a section that has items, between its header and first item and between its\n * last item and footer.\n */\n@Directive({ selector: 'ng-template[sectionEdgeSeparator]' })\nexport class SectionEdgeSeparator<T = unknown, S = unknown> {\n readonly template = inject<TemplateRef<SectionEdgeSeparatorContext<T, S>>>(TemplateRef);\n\n static ngTemplateContextGuard<T, S>(\n _directive: SectionEdgeSeparator<T, S>,\n context: unknown,\n ): context is SectionEdgeSeparatorContext<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 readonly leading: null;\n readonly trailing: null;\n }\n | {\n readonly kind: 'item';\n readonly context: SectionItemContext<T, S>;\n /** Null for a section's last item, where the section separator takes its place, as in RN. */\n readonly separator: SectionSeparatorContext<T, S> | null;\n /** The section separator before a section's first item, and null on every other. */\n readonly leading: SectionEdgeSeparatorContext<T, S> | null;\n /** The section separator after a section's last item, and null on every other. */\n readonly trailing: SectionEdgeSeparatorContext<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 *\n * The section separators ride on the first and last item, as RN's do: `SectionSeparatorComponent`\n * is the first item's leading separator and the last item's trailing one, so an empty section has\n * none, and a last item has it in place of the item separator.\n */\nexport function flattenSections<T, S extends SectionListSection<T>>(\n sections: readonly S[],\n): SectionRow<T, S>[] {\n const rows: SectionRow<T, S>[] = [];\n const edges = { leading: null, trailing: null };\n sections.forEach((section, sectionIndex) => {\n const context = { $implicit: section, section, sectionIndex };\n rows.push({ kind: 'header', context, separator: null, ...edges });\n const data = section.data;\n const last = data.length - 1;\n const around = {\n $implicit: section,\n section,\n leadingSection: sections[sectionIndex - 1],\n trailingSection: sections[sectionIndex + 1],\n };\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 < last\n ? { $implicit: item, leadingItem: item, trailingItem: trailingItem!, section }\n : null,\n leading: index === 0 ? { ...around, leadingItem: undefined, trailingItem: item } : null,\n trailing: index === last ? { ...around, leadingItem: item, trailingItem: undefined } : null,\n });\n });\n rows.push({ kind: 'footer', context, separator: null, ...edges });\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 * <ng-template sectionEdgeSeparator><view [style]=\"rule\"></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 separators,\n * which are drawn in the item's slot: the item separator at its bottom, only between items of the\n * same section, and the section separator at the top of a section's first item and the bottom of\n * its last, as RN's `SectionSeparatorComponent` is.\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 `horizontal` or `inverted`, no viewability events, and no `highlighted` on a\n * separator. 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 @if (row.item.leading; as start) {\n @if (edge(); as edge) {\n <ng-container [templateSlot]=\"edge.template\" [templateSlotContext]=\"start\" />\n }\n }\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 @if (row.item.trailing; as end) {\n @if (edge(); as edge) {\n <ng-container [templateSlot]=\"edge.template\" [templateSlotContext]=\"end\" />\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, including every separator drawn in its slot. */\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 protected readonly edge = contentChild<SectionEdgeSeparator<T, S>>(SectionEdgeSeparator);\n private readonly list = viewChild.required<VirtualList<SectionRow<T, S>>>('list');\n\n protected readonly fill = { flex: 1 };\n /** The item fills its slot between its separators, as RN's cell draws them around 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"]}
|
|
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 type { KeyboardShouldPersistTaps } from './keyboard-taps.ts';\nimport { VirtualList, type VirtualListPadding } 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/**\n * What a section separator template is given: the section, and what is either side of the edge\n * it sits at. At a section's start `leadingItem` is undefined, and at its end `trailingItem` is.\n */\nexport interface SectionEdgeSeparatorContext<T, S> {\n readonly $implicit: S;\n readonly section: S;\n readonly leadingItem: T | undefined;\n readonly leadingSection: S | undefined;\n readonly trailingItem: T | undefined;\n readonly trailingSection: S | undefined;\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/**\n * `<ng-template sectionEdgeSeparator let-section>`: RN's `SectionSeparatorComponent`, drawn at\n * both edges of a section that has items, between its header and first item and between its\n * last item and footer.\n */\n@Directive({ selector: 'ng-template[sectionEdgeSeparator]' })\nexport class SectionEdgeSeparator<T = unknown, S = unknown> {\n readonly template = inject<TemplateRef<SectionEdgeSeparatorContext<T, S>>>(TemplateRef);\n\n static ngTemplateContextGuard<T, S>(\n _directive: SectionEdgeSeparator<T, S>,\n context: unknown,\n ): context is SectionEdgeSeparatorContext<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 readonly leading: null;\n readonly trailing: null;\n }\n | {\n readonly kind: 'item';\n readonly context: SectionItemContext<T, S>;\n /** Null for a section's last item, where the section separator takes its place, as in RN. */\n readonly separator: SectionSeparatorContext<T, S> | null;\n /** The section separator before a section's first item, and null on every other. */\n readonly leading: SectionEdgeSeparatorContext<T, S> | null;\n /** The section separator after a section's last item, and null on every other. */\n readonly trailing: SectionEdgeSeparatorContext<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 *\n * The section separators ride on the first and last item, as RN's do: `SectionSeparatorComponent`\n * is the first item's leading separator and the last item's trailing one, so an empty section has\n * none, and a last item has it in place of the item separator.\n */\nexport function flattenSections<T, S extends SectionListSection<T>>(\n sections: readonly S[],\n): SectionRow<T, S>[] {\n const rows: SectionRow<T, S>[] = [];\n const edges = { leading: null, trailing: null };\n sections.forEach((section, sectionIndex) => {\n const context = { $implicit: section, section, sectionIndex };\n rows.push({ kind: 'header', context, separator: null, ...edges });\n const data = section.data;\n const last = data.length - 1;\n const around = {\n $implicit: section,\n section,\n leadingSection: sections[sectionIndex - 1],\n trailingSection: sections[sectionIndex + 1],\n };\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 < last\n ? { $implicit: item, leadingItem: item, trailingItem: trailingItem!, section }\n : null,\n leading: index === 0 ? { ...around, leadingItem: undefined, trailingItem: item } : null,\n trailing: index === last ? { ...around, leadingItem: item, trailingItem: undefined } : null,\n });\n });\n rows.push({ kind: 'footer', context, separator: null, ...edges });\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 * <ng-template sectionEdgeSeparator><view [style]=\"rule\"></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 separators,\n * which are drawn in the item's slot: the item separator at its bottom, only between items of the\n * same section, and the section separator at the top of a section's first item and the bottom of\n * its last, as RN's `SectionSeparatorComponent` is.\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 * `contentPadding` and `keyboardShouldPersistTaps` pass through to the list as `<virtual-list>`\n * takes them.\n *\n * ponytail: no `horizontal` or `inverted`, no viewability events, and no `highlighted` on a\n * separator. No `keyExtractor` or `maintainVisibleContentPosition` either: rows are fixed height,\n * so there is no measured height for a key to carry across an insert. The host is a plain view\n * with the list filling it, where RN's host is the scroll view, so scroll-view props are not\n * taken.\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 [contentPadding]=\"contentPadding()\"\n [keyboardShouldPersistTaps]=\"keyboardShouldPersistTaps()\"\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 @if (row.item.leading; as start) {\n @if (edge(); as edge) {\n <ng-container [templateSlot]=\"edge.template\" [templateSlotContext]=\"start\" />\n }\n }\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 @if (row.item.trailing; as end) {\n @if (edge(); as edge) {\n <ng-container [templateSlot]=\"edge.template\" [templateSlotContext]=\"end\" />\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, including every separator drawn in its slot. */\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 /** Space around the rows, inside the scrolling content. See `<virtual-list>`. */\n readonly contentPadding = input<VirtualListPadding>(0);\n /** What a tap in the list does while a text input has the keyboard up. See `ScrollView`. */\n readonly keyboardShouldPersistTaps = input<KeyboardShouldPersistTaps>('never');\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 protected readonly edge = contentChild<SectionEdgeSeparator<T, S>>(SectionEdgeSeparator);\n private readonly list = viewChild.required<VirtualList<SectionRow<T, S>>>('list');\n\n protected readonly fill = { flex: 1 };\n /** The item fills its slot between its separators, as RN's cell draws them around 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"]}
|
package/dist/sticky-headers.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { type HostEngine, type HostNode } from '@ng-native/fabric';
|
|
2
|
+
/**
|
|
3
|
+
* How long scrolling has to pause before a natively moved header's translate is written, so the
|
|
4
|
+
* shadow tree, which a touch is hit-tested against, catches up with where native drew it. RN's
|
|
5
|
+
* `ScrollViewStickyHeader` debounces the same write, by 64 ms on iOS.
|
|
6
|
+
*/
|
|
7
|
+
export declare const STICKY_SETTLE_MS = 64;
|
|
2
8
|
/**
|
|
3
9
|
* `stickyHeaderIndices` for `<scroll-view>`, done the way RN's `ScrollView.js` does it.
|
|
4
10
|
*
|
package/dist/sticky-headers.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { pinnedRange } from '@ng-native/fabric';
|
|
2
|
+
/**
|
|
3
|
+
* How long scrolling has to pause before a natively moved header's translate is written, so the
|
|
4
|
+
* shadow tree, which a touch is hit-tested against, catches up with where native drew it. RN's
|
|
5
|
+
* `ScrollViewStickyHeader` debounces the same write, by 64 ms on iOS.
|
|
6
|
+
*/
|
|
7
|
+
export const STICKY_SETTLE_MS = 64;
|
|
2
8
|
/**
|
|
3
9
|
* `stickyHeaderIndices` for `<scroll-view>`, done the way RN's `ScrollView.js` does it.
|
|
4
10
|
*
|
|
@@ -54,9 +60,8 @@ export class StickyHeaders {
|
|
|
54
60
|
this.offset = offset;
|
|
55
61
|
if (!this.engine.drivesScroll)
|
|
56
62
|
return this.apply();
|
|
57
|
-
// RN's sticky header commits its translate after 64 ms without a scroll event.
|
|
58
63
|
clearTimeout(this.settleTimer);
|
|
59
|
-
this.settleTimer = setTimeout(() => this.apply(),
|
|
64
|
+
this.settleTimer = setTimeout(() => this.apply(), STICKY_SETTLE_MS);
|
|
60
65
|
}
|
|
61
66
|
destroy() {
|
|
62
67
|
clearTimeout(this.settleTimer);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sticky-headers.js","sourceRoot":"","sources":["../src/sticky-headers.ts"],"names":[],"mappings":"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","sourcesContent":["import { type HostEngine, type HostNode, type ScrollDrive, pinnedRange } from '@ng-native/fabric';\n\ninterface Extent {\n readonly start: number;\n readonly size: number;\n}\n\ntype LayoutEvent = {\n nativeEvent?: { layout?: { x?: number; y?: number; width?: number; height?: number } };\n};\n\n/**\n * `stickyHeaderIndices` for `<scroll-view>`, done the way RN's `ScrollView.js` does it.\n *\n * There is no native prop for it on either platform. RN wraps each sticky child in\n * `ScrollViewStickyHeader`, which translates it by the scroll offset past its own position and\n * stops where the next sticky child would push it off. The same sums run here, over the content\n * view's element children: control-flow anchors are not children in RN's sense and are skipped,\n * so an index counts what is committed.\n *\n * Each sticky child's position comes from its own layout event, and the translation is written\n * as its `transform` prop, with RN's `zIndex: 10` so it draws over the content scrolling under\n * it. A style of the caller's own that sets either wins, as it would in RN.\n *\n * Where the host can, native moves each one on every frame the scroll view moves, as RN's\n * animated interpolation does, and the translation is written only once scrolling pauses: a\n * translate written from each scroll event lands late, and on a fast fling the header shudders.\n */\nexport class StickyHeaders {\n private readonly engine: HostEngine;\n private readonly horizontal: () => boolean;\n private headers: HostNode[] = [];\n private readonly extents = new Map<HostNode, Extent>();\n private readonly listening = new Map<HostNode, () => void>();\n /** The translation last written per header, so an unchanged one is not written again. */\n private readonly written = new Map<HostNode, number>();\n private readonly drives = new Map<HostNode, ScrollDrive>();\n private settleTimer: ReturnType<typeof setTimeout> | undefined;\n private offset = 0;\n\n constructor(engine: HostEngine, scroll: HostNode, horizontal: () => boolean) {\n this.engine = engine;\n this.scroll = scroll;\n this.horizontal = horizontal;\n }\n\n private readonly scroll: HostNode;\n\n /** Find the sticky children again, after a pass that may have changed them. */\n track(content: HostNode | undefined, indices: readonly number[]): void {\n const children = (content?.children ?? []).filter((child) => child.kind === 'element');\n const headers = indices.flatMap((index) => children[index] ?? []);\n for (const node of this.headers) {\n if (!headers.includes(node)) this.release(node);\n }\n for (const node of headers) {\n if (this.listening.has(node)) continue;\n this.engine.setProp(node, 'zIndex', 10);\n this.listening.set(\n node,\n this.engine.setEventListener(node, 'topLayout', (event) => this.measured(node, event)),\n );\n }\n this.headers = headers;\n this.apply();\n }\n\n scrolled(offset: number): void {\n this.offset = offset;\n if (!this.engine.drivesScroll) return this.apply();\n // RN's sticky header commits its translate after 64 ms without a scroll event.\n clearTimeout(this.settleTimer);\n this.settleTimer = setTimeout(() => this.apply(), 64);\n }\n\n destroy(): void {\n clearTimeout(this.settleTimer);\n for (const node of this.headers) this.release(node);\n this.headers = [];\n }\n\n private measured(node: HostNode, event: unknown): void {\n const layout = (event as LayoutEvent).nativeEvent?.layout;\n if (!layout) return;\n const horizontal = this.horizontal();\n this.extents.set(node, {\n start: (horizontal ? layout.x : layout.y) ?? 0,\n size: (horizontal ? layout.width : layout.height) ?? 0,\n });\n this.apply();\n }\n\n private release(node: HostNode): void {\n this.listening.get(node)?.();\n this.listening.delete(node);\n this.extents.delete(node);\n this.written.delete(node);\n this.drives.get(node)?.stop();\n this.drives.delete(node);\n this.engine.setProp(node, 'zIndex', undefined);\n this.engine.setProp(node, 'transform', undefined);\n }\n\n private drive(node: HostNode, start: number, stop: number): void {\n if (!this.engine.drivesScroll) return;\n const range = pinnedRange(start, stop);\n const drive = this.drives.get(node);\n if (drive) return drive.update(range);\n const axis = this.horizontal() ? 'x' : 'y';\n const next = this.engine.driveByScroll(node, this.scroll, axis, range);\n if (next) this.drives.set(node, next);\n }\n\n /** `ScrollViewStickyHeader`'s interpolation, as plain arithmetic. */\n private apply(): void {\n const key = this.horizontal() ? 'translateX' : 'translateY';\n this.headers.forEach((node, at) => {\n const own = this.extents.get(node);\n if (!own) return;\n const next = this.extents.get(this.headers[at + 1]!);\n const stop = next ? next.start - own.size : Infinity;\n this.drive(node, own.start, stop);\n const shift = Math.min(Math.max(0, this.offset - own.start), Math.max(0, stop - own.start));\n if (this.written.get(node) === shift) return;\n this.written.set(node, shift);\n this.engine.setProp(node, 'transform', shift > 0 ? [{ [key]: shift }] : undefined);\n });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sticky-headers.js","sourceRoot":"","sources":["../src/sticky-headers.ts"],"names":[],"mappings":"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","sourcesContent":["import { type HostEngine, type HostNode, type ScrollDrive, pinnedRange } from '@ng-native/fabric';\n\ninterface Extent {\n readonly start: number;\n readonly size: number;\n}\n\n/**\n * How long scrolling has to pause before a natively moved header's translate is written, so the\n * shadow tree, which a touch is hit-tested against, catches up with where native drew it. RN's\n * `ScrollViewStickyHeader` debounces the same write, by 64 ms on iOS.\n */\nexport const STICKY_SETTLE_MS = 64;\n\ntype LayoutEvent = {\n nativeEvent?: { layout?: { x?: number; y?: number; width?: number; height?: number } };\n};\n\n/**\n * `stickyHeaderIndices` for `<scroll-view>`, done the way RN's `ScrollView.js` does it.\n *\n * There is no native prop for it on either platform. RN wraps each sticky child in\n * `ScrollViewStickyHeader`, which translates it by the scroll offset past its own position and\n * stops where the next sticky child would push it off. The same sums run here, over the content\n * view's element children: control-flow anchors are not children in RN's sense and are skipped,\n * so an index counts what is committed.\n *\n * Each sticky child's position comes from its own layout event, and the translation is written\n * as its `transform` prop, with RN's `zIndex: 10` so it draws over the content scrolling under\n * it. A style of the caller's own that sets either wins, as it would in RN.\n *\n * Where the host can, native moves each one on every frame the scroll view moves, as RN's\n * animated interpolation does, and the translation is written only once scrolling pauses: a\n * translate written from each scroll event lands late, and on a fast fling the header shudders.\n */\nexport class StickyHeaders {\n private readonly engine: HostEngine;\n private readonly horizontal: () => boolean;\n private headers: HostNode[] = [];\n private readonly extents = new Map<HostNode, Extent>();\n private readonly listening = new Map<HostNode, () => void>();\n /** The translation last written per header, so an unchanged one is not written again. */\n private readonly written = new Map<HostNode, number>();\n private readonly drives = new Map<HostNode, ScrollDrive>();\n private settleTimer: ReturnType<typeof setTimeout> | undefined;\n private offset = 0;\n\n constructor(engine: HostEngine, scroll: HostNode, horizontal: () => boolean) {\n this.engine = engine;\n this.scroll = scroll;\n this.horizontal = horizontal;\n }\n\n private readonly scroll: HostNode;\n\n /** Find the sticky children again, after a pass that may have changed them. */\n track(content: HostNode | undefined, indices: readonly number[]): void {\n const children = (content?.children ?? []).filter((child) => child.kind === 'element');\n const headers = indices.flatMap((index) => children[index] ?? []);\n for (const node of this.headers) {\n if (!headers.includes(node)) this.release(node);\n }\n for (const node of headers) {\n if (this.listening.has(node)) continue;\n this.engine.setProp(node, 'zIndex', 10);\n this.listening.set(\n node,\n this.engine.setEventListener(node, 'topLayout', (event) => this.measured(node, event)),\n );\n }\n this.headers = headers;\n this.apply();\n }\n\n scrolled(offset: number): void {\n this.offset = offset;\n if (!this.engine.drivesScroll) return this.apply();\n clearTimeout(this.settleTimer);\n this.settleTimer = setTimeout(() => this.apply(), STICKY_SETTLE_MS);\n }\n\n destroy(): void {\n clearTimeout(this.settleTimer);\n for (const node of this.headers) this.release(node);\n this.headers = [];\n }\n\n private measured(node: HostNode, event: unknown): void {\n const layout = (event as LayoutEvent).nativeEvent?.layout;\n if (!layout) return;\n const horizontal = this.horizontal();\n this.extents.set(node, {\n start: (horizontal ? layout.x : layout.y) ?? 0,\n size: (horizontal ? layout.width : layout.height) ?? 0,\n });\n this.apply();\n }\n\n private release(node: HostNode): void {\n this.listening.get(node)?.();\n this.listening.delete(node);\n this.extents.delete(node);\n this.written.delete(node);\n this.drives.get(node)?.stop();\n this.drives.delete(node);\n this.engine.setProp(node, 'zIndex', undefined);\n this.engine.setProp(node, 'transform', undefined);\n }\n\n private drive(node: HostNode, start: number, stop: number): void {\n if (!this.engine.drivesScroll) return;\n const range = pinnedRange(start, stop);\n const drive = this.drives.get(node);\n if (drive) return drive.update(range);\n const axis = this.horizontal() ? 'x' : 'y';\n const next = this.engine.driveByScroll(node, this.scroll, axis, range);\n if (next) this.drives.set(node, next);\n }\n\n /** `ScrollViewStickyHeader`'s interpolation, as plain arithmetic. */\n private apply(): void {\n const key = this.horizontal() ? 'translateX' : 'translateY';\n this.headers.forEach((node, at) => {\n const own = this.extents.get(node);\n if (!own) return;\n const next = this.extents.get(this.headers[at + 1]!);\n const stop = next ? next.start - own.size : Infinity;\n this.drive(node, own.start, stop);\n const shift = Math.min(Math.max(0, this.offset - own.start), Math.max(0, stop - own.start));\n if (this.written.get(node) === shift) return;\n this.written.set(node, shift);\n this.engine.setProp(node, 'transform', shift > 0 ? [{ [key]: shift }] : undefined);\n });\n }\n}\n"]}
|
package/dist/switch.d.ts
CHANGED
|
@@ -6,6 +6,10 @@ import * as i0 from "@angular/core";
|
|
|
6
6
|
* A toggle, wired for signal forms as a `FormCheckboxControl` (`checked`, not `value`). Commits
|
|
7
7
|
* as `Switch` on iOS and `AndroidSwitch` on Android.
|
|
8
8
|
*
|
|
9
|
+
* `invalid` and `touched` come out as `data-invalid` and `data-touched` for a stylesheet, as on
|
|
10
|
+
* `<text-input>`, and `touch` fires when the user flips it: a switch has no blur, so the flip is
|
|
11
|
+
* the moment the user has dealt with it, which a form that shows errors once touched waits for.
|
|
12
|
+
*
|
|
9
13
|
* The two platforms spell the same props differently, so both spellings are sent and each
|
|
10
14
|
* native side reads its own; a prop it does not know is ignored.
|
|
11
15
|
*
|
|
@@ -20,6 +24,12 @@ export declare class Switch extends ViewBase {
|
|
|
20
24
|
readonly checked: import("@angular/core").ModelSignal<boolean>;
|
|
21
25
|
/** Ignore touches and grey out. */
|
|
22
26
|
readonly disabled: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
|
27
|
+
/** Published as `data-invalid`; nothing native reads it. */
|
|
28
|
+
readonly invalid: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
|
|
29
|
+
/** Published as `data-touched`; nothing native reads it. */
|
|
30
|
+
readonly touched: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
|
|
31
|
+
/** The user flipped it. What Signal Forms marks a field touched from. */
|
|
32
|
+
readonly touch: import("@angular/core").OutputEmitterRef<void>;
|
|
23
33
|
/** `Switch.js`: `accessibilityRole={props.accessibilityRole ?? 'switch'}`. */
|
|
24
34
|
protected roleByDefault(): 'switch';
|
|
25
35
|
protected disabledForAccessibility(): boolean | undefined;
|
|
@@ -50,5 +60,5 @@ export declare class Switch extends ViewBase {
|
|
|
50
60
|
*/
|
|
51
61
|
private reconcile;
|
|
52
62
|
static ɵfac: i0.ɵɵFactoryDeclaration<Switch, never>;
|
|
53
|
-
static ɵdir: i0.ɵɵDirectiveDeclaration<Switch, "switch", never, { "checked": { "alias": "checked"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "thumbColor": { "alias": "thumbColor"; "required": false; "isSignal": true; }; "trackColor": { "alias": "trackColor"; "required": false; "isSignal": true; }; "iosBackgroundColor": { "alias": "ios_backgroundColor"; "required": false; "isSignal": true; }; }, { "checked": "checkedChange"; }, never, never, true, never>;
|
|
63
|
+
static ɵdir: i0.ɵɵDirectiveDeclaration<Switch, "switch", never, { "checked": { "alias": "checked"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "invalid": { "alias": "invalid"; "required": false; "isSignal": true; }; "touched": { "alias": "touched"; "required": false; "isSignal": true; }; "thumbColor": { "alias": "thumbColor"; "required": false; "isSignal": true; }; "trackColor": { "alias": "trackColor"; "required": false; "isSignal": true; }; "iosBackgroundColor": { "alias": "ios_backgroundColor"; "required": false; "isSignal": true; }; }, { "checked": "checkedChange"; "touch": "touch"; }, never, never, true, never>;
|
|
54
64
|
}
|
package/dist/switch.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Directive, Injector, afterNextRender, computed, effect, inject, input, model, } from '@angular/core';
|
|
1
|
+
import { Directive, Injector, afterNextRender, booleanAttribute, computed, effect, inject, input, model, output, } from '@angular/core';
|
|
2
2
|
import { nativePlatform } from '@ng-native/fabric';
|
|
3
3
|
import { ControlledModel } from "./controlled-model.js";
|
|
4
4
|
import { optionalBoolean } from "./transforms.js";
|
|
@@ -8,6 +8,10 @@ import * as i0 from "@angular/core";
|
|
|
8
8
|
* A toggle, wired for signal forms as a `FormCheckboxControl` (`checked`, not `value`). Commits
|
|
9
9
|
* as `Switch` on iOS and `AndroidSwitch` on Android.
|
|
10
10
|
*
|
|
11
|
+
* `invalid` and `touched` come out as `data-invalid` and `data-touched` for a stylesheet, as on
|
|
12
|
+
* `<text-input>`, and `touch` fires when the user flips it: a switch has no blur, so the flip is
|
|
13
|
+
* the moment the user has dealt with it, which a form that shows errors once touched waits for.
|
|
14
|
+
*
|
|
11
15
|
* The two platforms spell the same props differently, so both spellings are sent and each
|
|
12
16
|
* native side reads its own; a prop it does not know is ignored.
|
|
13
17
|
*
|
|
@@ -23,6 +27,12 @@ export class Switch extends ViewBase {
|
|
|
23
27
|
...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
|
|
24
28
|
/** Ignore touches and grey out. */
|
|
25
29
|
disabled = input(undefined, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
30
|
+
/** Published as `data-invalid`; nothing native reads it. */
|
|
31
|
+
invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
32
|
+
/** Published as `data-touched`; nothing native reads it. */
|
|
33
|
+
touched = input(false, { ...(ngDevMode ? { debugName: "touched" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
34
|
+
/** The user flipped it. What Signal Forms marks a field touched from. */
|
|
35
|
+
touch = output();
|
|
26
36
|
/** `Switch.js`: `accessibilityRole={props.accessibilityRole ?? 'switch'}`. */
|
|
27
37
|
roleByDefault() {
|
|
28
38
|
return 'switch';
|
|
@@ -66,6 +76,7 @@ export class Switch extends ViewBase {
|
|
|
66
76
|
onNativeChange(event) {
|
|
67
77
|
this.nativeValue = event.nativeEvent?.value ?? false;
|
|
68
78
|
this.control.propose(this.nativeValue);
|
|
79
|
+
this.touch.emit();
|
|
69
80
|
afterNextRender({ write: () => this.reconcile() }, { injector: this.injector });
|
|
70
81
|
}
|
|
71
82
|
/**
|
|
@@ -83,7 +94,7 @@ export class Switch extends ViewBase {
|
|
|
83
94
|
this.engine.dispatchCommand(this.node, command, [desired]);
|
|
84
95
|
}
|
|
85
96
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Switch, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
86
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.2.0", type: Switch, isStandalone: true, selector: "switch", inputs: { checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, thumbColor: { classPropertyName: "thumbColor", publicName: "thumbColor", isSignal: true, isRequired: false, transformFunction: null }, trackColor: { classPropertyName: "trackColor", publicName: "trackColor", isSignal: true, isRequired: false, transformFunction: null }, iosBackgroundColor: { classPropertyName: "iosBackgroundColor", publicName: "ios_backgroundColor", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange" }, host: { listeners: { "change": "onNativeChange($event)" }, properties: { "accessibilityState": "accessibilityStateProp()", "value": "checked()", "disabled": "disabled()", "attr.data-disabled": "disabled() ? '' : null", "enabled": "enabled()", "thumbTintColor": "thumbColor()", "tintColor": "trackColor()?.false", "trackColorForTrue": "trackColor()?.true", "trackColorForFalse": "trackColor()?.false", "trackTintColor": "checked() ? trackColor()?.true : trackColor()?.false", "style.backgroundColor": "iosBackgroundColor()", "style.borderRadius": "iosBackgroundColor() ? 16 : undefined" } }, usesInheritance: true, usesOnChanges: true, ngImport: i0 });
|
|
97
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.2.0", type: Switch, isStandalone: true, selector: "switch", inputs: { checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, thumbColor: { classPropertyName: "thumbColor", publicName: "thumbColor", isSignal: true, isRequired: false, transformFunction: null }, trackColor: { classPropertyName: "trackColor", publicName: "trackColor", isSignal: true, isRequired: false, transformFunction: null }, iosBackgroundColor: { classPropertyName: "iosBackgroundColor", publicName: "ios_backgroundColor", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange", touch: "touch" }, host: { listeners: { "change": "onNativeChange($event)" }, properties: { "accessibilityState": "accessibilityStateProp()", "value": "checked()", "disabled": "disabled()", "attr.data-disabled": "disabled() ? '' : null", "attr.data-invalid": "invalid() ? '' : null", "attr.data-touched": "touched() ? '' : null", "enabled": "enabled()", "thumbTintColor": "thumbColor()", "tintColor": "trackColor()?.false", "trackColorForTrue": "trackColor()?.true", "trackColorForFalse": "trackColor()?.false", "trackTintColor": "checked() ? trackColor()?.true : trackColor()?.false", "style.backgroundColor": "iosBackgroundColor()", "style.borderRadius": "iosBackgroundColor() ? 16 : undefined" } }, usesInheritance: true, usesOnChanges: true, ngImport: i0 });
|
|
87
98
|
}
|
|
88
99
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Switch, decorators: [{
|
|
89
100
|
type: Directive,
|
|
@@ -96,6 +107,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImpor
|
|
|
96
107
|
'[value]': 'checked()',
|
|
97
108
|
'[disabled]': 'disabled()',
|
|
98
109
|
'[attr.data-disabled]': "disabled() ? '' : null",
|
|
110
|
+
'[attr.data-invalid]': "invalid() ? '' : null",
|
|
111
|
+
'[attr.data-touched]': "touched() ? '' : null",
|
|
99
112
|
'[enabled]': 'enabled()',
|
|
100
113
|
'[thumbTintColor]': 'thumbColor()',
|
|
101
114
|
'[tintColor]': 'trackColor()?.false',
|
|
@@ -107,5 +120,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.0", ngImpor
|
|
|
107
120
|
'(change)': 'onNativeChange($event)',
|
|
108
121
|
},
|
|
109
122
|
}]
|
|
110
|
-
}], ctorParameters: () => [], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], thumbColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "thumbColor", required: false }] }], trackColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackColor", required: false }] }], iosBackgroundColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "ios_backgroundColor", required: false }] }] } });
|
|
123
|
+
}], ctorParameters: () => [], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }], touch: [{ type: i0.Output, args: ["touch"] }], thumbColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "thumbColor", required: false }] }], trackColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackColor", required: false }] }], iosBackgroundColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "ios_backgroundColor", required: false }] }] } });
|
|
111
124
|
//# sourceMappingURL=switch.js.map
|
package/dist/switch.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"switch.js","sourceRoot":"","sources":["../src/switch.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,QAAQ,EAER,eAAe,EACf,QAAQ,EACR,MAAM,EACN,MAAM,EACN,KAAK,EACL,KAAK,
|
|
1
|
+
{"version":3,"file":"switch.js","sourceRoot":"","sources":["../src/switch.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Directive,\n Injector,\n type SimpleChanges,\n afterNextRender,\n booleanAttribute,\n computed,\n effect,\n inject,\n input,\n model,\n output,\n} from '@angular/core';\nimport { type NativeSyntheticEvent, nativePlatform } from '@ng-native/fabric';\nimport { ControlledModel } from './controlled-model.ts';\nimport { optionalBoolean } from './transforms.ts';\nimport { ViewBase } from './view-base.ts';\n\n/**\n * A toggle, wired for signal forms as a `FormCheckboxControl` (`checked`, not `value`). Commits\n * as `Switch` on iOS and `AndroidSwitch` on Android.\n *\n * `invalid` and `touched` come out as `data-invalid` and `data-touched` for a stylesheet, as on\n * `<text-input>`, and `touch` fires when the user flips it: a switch has no blur, so the flip is\n * the moment the user has dealt with it, which a form that shows errors once touched waits for.\n *\n * The two platforms spell the same props differently, so both spellings are sent and each\n * native side reads its own; a prop it does not know is ignored.\n *\n * Controlled as React Native's `Switch` is: bind `checked` and the app's value is what the switch\n * shows. The user flips it, the app keeps the old value, and a prop diff has nothing to send,\n * because the prop never changed; native is the only side that moved. So once the pass the flip\n * started has rendered, the switch compares the model with what native reported and, if they\n * differ, sends the command RN's wrapper sends - `setValue` on iOS, `setNativeValue` on Android.\n * See `ControlledModel` for how the model comes to say what the app says.\n */\n@Directive({\n selector: 'switch',\n host: {\n // `disabled` is an input of ours, so it cannot reach `ngOnChanges` on the base; this is\n // the one binding that keeps the announced state in step with it.\n '[accessibilityState]': 'accessibilityStateProp()',\n '[value]': 'checked()',\n '[disabled]': 'disabled()',\n '[attr.data-disabled]': \"disabled() ? '' : null\",\n '[attr.data-invalid]': \"invalid() ? '' : null\",\n '[attr.data-touched]': \"touched() ? '' : null\",\n '[enabled]': 'enabled()',\n '[thumbTintColor]': 'thumbColor()',\n '[tintColor]': 'trackColor()?.false',\n '[trackColorForTrue]': 'trackColor()?.true',\n '[trackColorForFalse]': 'trackColor()?.false',\n '[trackTintColor]': 'checked() ? trackColor()?.true : trackColor()?.false',\n '[style.backgroundColor]': 'iosBackgroundColor()',\n '[style.borderRadius]': 'iosBackgroundColor() ? 16 : undefined',\n '(change)': 'onNativeChange($event)',\n },\n})\nexport class Switch extends ViewBase {\n readonly checked = model<boolean>(false);\n /** Ignore touches and grey out. */\n readonly disabled = input(undefined, { transform: optionalBoolean });\n /** Published as `data-invalid`; nothing native reads it. */\n readonly invalid = input(false, { transform: booleanAttribute });\n /** Published as `data-touched`; nothing native reads it. */\n readonly touched = input(false, { transform: booleanAttribute });\n /** The user flipped it. What Signal Forms marks a field touched from. */\n readonly touch = output<void>();\n\n /** `Switch.js`: `accessibilityRole={props.accessibilityRole ?? 'switch'}`. */\n protected override roleByDefault(): 'switch' {\n return 'switch';\n }\n\n protected override disabledForAccessibility(): boolean | undefined {\n return this.disabled();\n }\n\n protected override checkedForAccessibility(): boolean {\n return this.checked();\n }\n /** The colour of the knob. */\n readonly thumbColor = input<string>();\n /** The track colour for each state. */\n readonly trackColor = input<{ readonly false?: string; readonly true?: string }>();\n /** iOS: the colour behind the track when off, seen where the track is transparent. */\n readonly iosBackgroundColor = input<string>(undefined, { alias: 'ios_backgroundColor' });\n\n protected readonly enabled = computed(() => {\n const disabled = this.disabled();\n return disabled === undefined ? undefined : !disabled;\n });\n\n private readonly injector = inject(Injector);\n private readonly control = new ControlledModel(this.checked, 'checked');\n /** What native reported last, until the pass it started has rendered and been compared. */\n private nativeValue: boolean | null = null;\n\n constructor() {\n super();\n effect(() => {\n // Android spells the value `on` and iOS the on-track colour `onTintColor`. Angular refuses\n // both as host bindings because they read as event handlers, so they go to the node.\n this.engine.setProp(this.node, 'on', this.checked());\n this.engine.setProp(this.node, 'onTintColor', this.trackColor()?.true);\n });\n }\n\n override ngOnChanges(changes: SimpleChanges): void {\n super.ngOnChanges(changes);\n this.control.noteChanges(changes);\n }\n\n protected onNativeChange(event: NativeSyntheticEvent<{ value?: boolean }>): void {\n this.nativeValue = event.nativeEvent?.value ?? false;\n this.control.propose(this.nativeValue);\n this.touch.emit();\n afterNextRender({ write: () => this.reconcile() }, { injector: this.injector });\n }\n\n /**\n * After the app's bindings have settled, not before: a flip the app accepts reaches the model\n * during change detection, and comparing any earlier would send native back the value it is\n * about to be given. A value set from code later needs none of this, as the prop changes.\n */\n private reconcile(): void {\n const native = this.nativeValue;\n this.nativeValue = null;\n const desired = this.checked();\n if (native === null || native === desired) return;\n const command = nativePlatform() === 'android' ? 'setNativeValue' : 'setValue';\n this.engine.dispatchCommand(this.node, command, [desired]);\n }\n}\n"]}
|
package/dist/text.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { TouchableBase } from './pressable.ts';
|
|
2
|
+
import type { AccessibilityRole } from './view-base.ts';
|
|
2
3
|
import * as i0 from "@angular/core";
|
|
3
4
|
export type EllipsizeMode = 'head' | 'middle' | 'tail' | 'clip';
|
|
4
5
|
export type DynamicTypeRamp = 'caption2' | 'caption1' | 'footnote' | 'subheadline' | 'callout' | 'body' | 'headline' | 'title3' | 'title2' | 'title1' | 'largeTitle';
|
|
@@ -22,6 +23,19 @@ export declare class Text extends TouchableBase {
|
|
|
22
23
|
* `Platform.OS` decides, exactly as React Native's own wrapper does.
|
|
23
24
|
*/
|
|
24
25
|
protected accessibleByDefault(): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* `Text.js` announces `disabled` on every text, pressable or not, nested or not. Written from
|
|
28
|
+
* `ngOnChanges` like the rest of the state, since `disabled` is an input of this directive: no
|
|
29
|
+
* host binding, which `ControlBase` needs and a text cannot afford.
|
|
30
|
+
*/
|
|
31
|
+
protected disabledForAccessibility(): boolean | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* `Text.js`: a pressable text is a `link` unless a role is set or it is disabled, as the
|
|
34
|
+
* merged state says: `disabled` wins, and `aria-disabled`, `accessibilityState` or a
|
|
35
|
+
* contributing directive stand in when it is unset. Nested or not. Its inputs are this
|
|
36
|
+
* directive's, so `ngOnChanges` keeps it current without a host binding.
|
|
37
|
+
*/
|
|
38
|
+
protected roleByDefault(): AccessibilityRole | undefined;
|
|
25
39
|
/** Truncate after this many lines, with `ellipsizeMode` deciding where the ellipsis goes. */
|
|
26
40
|
readonly numberOfLines: import("@angular/core").InputSignalWithTransform<number | undefined, unknown>;
|
|
27
41
|
/**
|
|
@@ -64,6 +78,7 @@ export declare class Text extends TouchableBase {
|
|
|
64
78
|
/** The pass-through props, written when one of them changes. See `ViewBase.ngOnChanges`. */
|
|
65
79
|
ngOnChanges(): void;
|
|
66
80
|
protected claims(): boolean;
|
|
81
|
+
protected pressDisabled(): boolean;
|
|
67
82
|
protected measures(): boolean;
|
|
68
83
|
static ɵfac: i0.ɵɵFactoryDeclaration<Text, never>;
|
|
69
84
|
static ɵdir: i0.ɵɵDirectiveDeclaration<Text, "text", ["text"], { "numberOfLines": { "alias": "numberOfLines"; "required": false; "isSignal": true; }; "ellipsizeMode": { "alias": "ellipsizeMode"; "required": false; "isSignal": true; }; "selectable": { "alias": "selectable"; "required": false; "isSignal": true; }; "selectionColor": { "alias": "selectionColor"; "required": false; "isSignal": true; }; "allowFontScaling": { "alias": "allowFontScaling"; "required": false; "isSignal": true; }; "maxFontSizeMultiplier": { "alias": "maxFontSizeMultiplier"; "required": false; "isSignal": true; }; "dynamicTypeRamp": { "alias": "dynamicTypeRamp"; "required": false; "isSignal": true; }; "adjustsFontSizeToFit": { "alias": "adjustsFontSizeToFit"; "required": false; "isSignal": true; }; "minimumFontScale": { "alias": "minimumFontScale"; "required": false; "isSignal": true; }; "lineBreakStrategyIOS": { "alias": "lineBreakStrategyIOS"; "required": false; "isSignal": true; }; "textBreakStrategy": { "alias": "textBreakStrategy"; "required": false; "isSignal": true; }; "dataDetectorType": { "alias": "dataDetectorType"; "required": false; "isSignal": true; }; "hyphenationFrequency": { "alias": "android_hyphenationFrequency"; "required": false; "isSignal": true; }; "pressable": { "alias": "pressable"; "required": false; "isSignal": true; }; "suppressHighlighting": { "alias": "suppressHighlighting"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
package/dist/text.js
CHANGED
|
@@ -25,6 +25,25 @@ export class Text extends TouchableBase {
|
|
|
25
25
|
accessibleByDefault() {
|
|
26
26
|
return nativePlatform() === 'android' ? this.pressable() : true;
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* `Text.js` announces `disabled` on every text, pressable or not, nested or not. Written from
|
|
30
|
+
* `ngOnChanges` like the rest of the state, since `disabled` is an input of this directive: no
|
|
31
|
+
* host binding, which `ControlBase` needs and a text cannot afford.
|
|
32
|
+
*/
|
|
33
|
+
disabledForAccessibility() {
|
|
34
|
+
return this.disabled();
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* `Text.js`: a pressable text is a `link` unless a role is set or it is disabled, as the
|
|
38
|
+
* merged state says: `disabled` wins, and `aria-disabled`, `accessibilityState` or a
|
|
39
|
+
* contributing directive stand in when it is unset. Nested or not. Its inputs are this
|
|
40
|
+
* directive's, so `ngOnChanges` keeps it current without a host binding.
|
|
41
|
+
*/
|
|
42
|
+
roleByDefault() {
|
|
43
|
+
if (!this.pressable())
|
|
44
|
+
return undefined;
|
|
45
|
+
return this.accessibilityStateProp()?.disabled === true ? undefined : 'link';
|
|
46
|
+
}
|
|
28
47
|
/** Truncate after this many lines, with `ellipsizeMode` deciding where the ellipsis goes. */
|
|
29
48
|
numberOfLines = input(undefined, { ...(ngDevMode ? { debugName: "numberOfLines" } : /* istanbul ignore next */ {}), transform: optionalNumber });
|
|
30
49
|
/**
|
|
@@ -91,9 +110,15 @@ export class Text extends TouchableBase {
|
|
|
91
110
|
write('dataDetectorType', this.dataDetectorType());
|
|
92
111
|
write('android_hyphenationFrequency', this.hyphenationFrequency());
|
|
93
112
|
write('isPressable', this.pressable() || undefined);
|
|
113
|
+
// `disabled` is consumed as an input, so this is what `[data-disabled]` and `disabled:` match,
|
|
114
|
+
// as on a control. Written here rather than bound from the host; see `pressedChanged`.
|
|
115
|
+
write('data-disabled', this.disabled() ? '' : undefined);
|
|
94
116
|
}
|
|
95
117
|
claims() {
|
|
96
|
-
return this.pressable() && !this.
|
|
118
|
+
return this.pressable() && !this.pressDisabled();
|
|
119
|
+
}
|
|
120
|
+
pressDisabled() {
|
|
121
|
+
return this.disabledByAnyState();
|
|
97
122
|
}
|
|
98
123
|
measures() {
|
|
99
124
|
return this.pressable();
|