@ng-native/components 0.12.0 → 0.14.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/keyboard-avoiding-view.js +3 -2
- package/dist/keyboard-avoiding-view.js.map +1 -1
- package/dist/modal.d.ts +8 -2
- package/dist/modal.js +8 -5
- package/dist/modal.js.map +1 -1
- package/dist/pressable.js +6 -6
- package/dist/pressable.js.map +1 -1
- package/dist/section-list.js +4 -4
- package/dist/section-list.js.map +1 -1
- package/dist/swipe-refresh-layout.js +1 -1
- package/dist/swipe-refresh-layout.js.map +1 -1
- package/dist/touchable-opacity.js +3 -2
- package/dist/touchable-opacity.js.map +1 -1
- package/dist/transforms.d.ts +6 -0
- package/dist/transforms.js +8 -0
- package/dist/transforms.js.map +1 -1
- package/dist/view-base.d.ts +1 -1
- package/dist/view-base.js +6 -4
- package/dist/view-base.js.map +1 -1
- package/dist/virtual-list.js +6 -5
- package/dist/virtual-list.js.map +1 -1
- package/dist/zero-size-warning.js +2 -2
- package/dist/zero-size-warning.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { Component,
|
|
1
|
+
import { Component, computed, effect, inject, input, signal, booleanAttribute, } from '@angular/core';
|
|
2
2
|
import { Keyboard, LayoutAnimation } from '@ng-native/device';
|
|
3
3
|
import { ContentContainer } from "./content-container.js";
|
|
4
4
|
import { View } from "./view.js";
|
|
5
5
|
import { ViewBase } from "./view-base.js";
|
|
6
|
+
import { numberOr } from "./transforms.js";
|
|
6
7
|
import * as i0 from "@angular/core";
|
|
7
8
|
/**
|
|
8
9
|
* The names `LayoutEasing` knows. `'keyboard'` is among them: iOS reports its keyboard's own
|
|
@@ -64,7 +65,7 @@ export class KeyboardAvoidingView extends ViewBase {
|
|
|
64
65
|
* Extra room to leave above the keyboard, in points. RN's name, and in RN the place to put a
|
|
65
66
|
* header's height; here the view is measured in the window, so a navigation bar needs none.
|
|
66
67
|
*/
|
|
67
|
-
keyboardVerticalOffset = input(0, { ...(ngDevMode ? { debugName: "keyboardVerticalOffset" } : /* istanbul ignore next */ {}), transform:
|
|
68
|
+
keyboardVerticalOffset = input(0, { ...(ngDevMode ? { debugName: "keyboardVerticalOffset" } : /* istanbul ignore next */ {}), transform: numberOr(0) });
|
|
68
69
|
keyboard = inject(Keyboard).metrics;
|
|
69
70
|
layoutAnimation = inject(LayoutAnimation);
|
|
70
71
|
frame = signal(null, /* @ts-ignore */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keyboard-avoiding-view.js","sourceRoot":"","sources":["../src/keyboard-avoiding-view.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n booleanAttribute,\n computed,\n effect,\n inject,\n input,\n numberAttribute,\n signal,\n} from '@angular/core';\nimport { Keyboard, LayoutAnimation, type LayoutEasing } from '@ng-native/device';\nimport type { NativeSyntheticEvent } from '@ng-native/fabric';\nimport type { Rect } from './events.ts';\nimport { ContentContainer } from './content-container.ts';\nimport { View } from './view.ts';\nimport { ViewBase } from './view-base.ts';\n\nexport type KeyboardAvoidingBehavior = 'padding' | 'height' | 'position';\n\n/**\n * The names `LayoutEasing` knows. `'keyboard'` is among them: iOS reports its keyboard's own\n * curve by that name, and RN's KeyboardAvoidingView animates on it.\n */\nconst LAYOUT_EASINGS: ReadonlySet<string> = new Set([\n 'linear',\n 'easeInEaseOut',\n 'easeIn',\n 'easeOut',\n 'spring',\n 'keyboard',\n]);\n\n/** A keyboard event's `easing`, narrowed to what `LayoutAnimation` accepts, or left to its default. */\nfunction layoutEasingOf(easing: string | undefined): LayoutEasing | undefined {\n return easing !== undefined && LAYOUT_EASINGS.has(easing) ? (easing as LayoutEasing) : undefined;\n}\n\n/**\n * Moves its content clear of the keyboard.\n *\n * RN's KeyboardAvoidingView is a JS composite: there is no native component, only a view whose\n * padding, height or position tracks the keyboard. Off a device the `Keyboard` service reports\n * nothing, so this renders as a plain view and does nothing, which is also the right behaviour on\n * a platform with no soft keyboard.\n *\n * The overlap is the view's own frame against the keyboard's top edge, so a view that does not\n * reach the bottom of the screen moves only as far as it must. Unlike RN, the frame's top is\n * measured in the window, which is what the keyboard's `screenY` is in: `(layout)` reports it in\n * the parent's coordinates, and on a screen under a navigation bar those start a bar's height\n * down. RN leaves that to `keyboardVerticalOffset`, and a view that was not told came up short by\n * exactly that much, its bottom edge still behind the keyboard. The overlap never exceeds the\n * view's own height either: padding a view by more than it has pushes its content out of it.\n * When the keyboard source reports no `screenY`, the keyboard's height stands in.\n *\n * The adjustment itself animates with the keyboard's own motion, the way RN's own\n * KeyboardAvoidingView does: a keyboard event's `duration` and `easing` configure the next\n * layout animation before the style change that moves this view, so the two run together rather\n * than the padding, height or position jumping to its resting place in one step.\n */\n@Component({\n selector: 'keyboard-avoiding-view',\n imports: [ContentContainer, View],\n template: `\n @if (behavior() === 'position') {\n <view\n [contentContainerOf]=\"node\"\n [contentContainerClass]=\"contentContainerClass()\"\n [style]=\"positioned()\"\n ><ng-content\n /></view>\n } @else {\n <ng-content />\n }\n `,\n host: {\n '[styleOverride]': 'avoidance()',\n '(layout)': 'onLayout($event)',\n },\n})\nexport class KeyboardAvoidingView extends ViewBase {\n /**\n * How to make room: pad the bottom, shrink the height, or shift the content up. RN leaves this\n * unset and does nothing; `padding` is the default here because that is what a form wants.\n */\n readonly behavior = input<KeyboardAvoidingBehavior>('padding');\n /** Styles for the inner view that `position` moves. */\n readonly contentContainerStyle = input<Record<string, unknown>>();\n /**\n * Classes for the inner view that `position` moves, matched as if it were written in the\n * template this view is. See `ContentContainer`.\n */\n readonly contentContainerClass = input<string>();\n /** Set to false to stop avoiding without removing the view. */\n readonly enabled = input(true, { transform: booleanAttribute });\n /**\n * Extra room to leave above the keyboard, in points. RN's name, and in RN the place to put a\n * header's height; here the view is measured in the window, so a navigation bar needs none.\n */\n readonly keyboardVerticalOffset = input(0, { transform: numberAttribute });\n\n private readonly keyboard = inject(Keyboard).metrics;\n private readonly layoutAnimation = inject(LayoutAnimation);\n private readonly frame = signal<Rect | null>(null);\n /** The height before any adjustment, which `height` mode shrinks from. */\n private initialHeight: number | null = null;\n\n /** How far the keyboard intrudes into this view, in points. */\n protected readonly overlap = computed(() => {\n if (!this.enabled()) return 0;\n const { height, screenY } = this.keyboard();\n if (height === 0) return 0;\n const frame = this.frame();\n const offset = this.keyboardVerticalOffset();\n if (!frame) return Math.max(0, height - offset);\n // In `height` mode the frame is the one this view already shrank, so measure the height it\n // had before, as RN adds its current adjustment back; otherwise the room it made reads as no\n // overlap and the height springs back under the keyboard.\n const extent =\n this.behavior() === 'height' ? (this.initialHeight ?? frame.height) : frame.height;\n const overlap = screenY === undefined ? height - offset : frame.y + extent - (screenY - offset);\n return Math.min(extent, Math.max(0, overlap));\n });\n\n /**\n * The adjustment, or nothing to adjust. Bound as the engine's style override rather than as\n * host style, because Angular ranks a host style binding under the template's: a caller's\n * `[style]=\"{flex: 1}\"` would otherwise keep its flex in height mode and the view would not\n * shrink at all. RN composes the adjustment after the caller's style, so it wins there too.\n */\n protected readonly avoidance = computed(() => {\n const overlap = this.overlap();\n const behavior = this.behavior();\n if (overlap === 0 || behavior === 'position') return undefined;\n if (behavior === 'height') {\n const base = this.initialHeight ?? this.frame()?.height;\n return base === undefined ? undefined : { height: base - overlap, flex: 0 };\n }\n return { paddingBottom: overlap };\n });\n\n protected readonly positioned = computed(() => ({\n ...this.contentContainerStyle(),\n bottom: this.overlap(),\n }));\n\n constructor() {\n super();\n // A duration of zero is Android's own keyboard event, which animates itself; there is\n // nothing to configure alongside it. `appear`/`leave` are turned off because this is an\n // update to a view already on screen, not one appearing or leaving - RN's own bare\n // `LayoutAnimation.configureNext({duration, update})` animates nothing else either.\n effect(() => {\n const { duration, easing } = this.keyboard();\n if (!this.enabled() || !duration) return;\n void this.layoutAnimation.animate(() => {}, {\n duration,\n easing: layoutEasingOf(easing),\n appear: 'none',\n leave: 'none',\n });\n });\n }\n\n protected onLayout(event: NativeSyntheticEvent<{ layout?: Rect }>): void {\n const layout = event.nativeEvent?.layout;\n if (!layout) return;\n this.frame.set({ ...layout, y: this.windowTop(layout.y) });\n // Only a layout that the keyboard did not cause counts as the natural height.\n if (this.overlap() === 0) this.initialHeight = layout.height;\n }\n\n /**\n * Where this view's top edge is in the window, or `fallback` where the platform cannot say.\n * Synchronous on the new architecture, so it answers for the layout being reported.\n */\n private windowTop(fallback: number): number {\n let top = fallback;\n this.engine.measure(this.node, (frame) => (top = frame.y));\n return top;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"keyboard-avoiding-view.js","sourceRoot":"","sources":["../src/keyboard-avoiding-view.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,QAAQ,EACR,MAAM,EACN,MAAM,EACN,KAAK,EACL,MAAM,EACN,gBAAgB,GACjB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAqB,MAAM,mBAAmB,CAAC;AAGjF,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;;AAI3C;;;GAGG;AACH,MAAM,cAAc,GAAwB,IAAI,GAAG,CAAC;IAClD,QAAQ;IACR,eAAe;IACf,QAAQ;IACR,SAAS;IACT,QAAQ;IACR,UAAU;CACX,CAAC,CAAC;AAEH,uGAAuG;AACvG,SAAS,cAAc,CAAC,MAA0B;IAChD,OAAO,MAAM,KAAK,SAAS,IAAI,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAE,MAAuB,CAAC,CAAC,CAAC,SAAS,CAAC;AACnG,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AAqBH,MAAM,OAAO,oBAAqB,SAAQ,QAAQ;IAChD;;;OAGG;IACM,QAAQ,GAAG,KAAK,CAA2B,SAAS;iFAAC,CAAC;IAC/D,uDAAuD;IAC9C,qBAAqB,GAAG,KAAK;yGAA2B,CAAC;IAClE;;;OAGG;IACM,qBAAqB,GAAG,KAAK;yGAAU,CAAC;IACjD,+DAA+D;IACtD,OAAO,GAAG,KAAK,CAAC,IAAI,+EAAI,SAAS,EAAE,gBAAgB,GAAG,CAAC;IAChE;;;OAGG;IACM,sBAAsB,GAAG,KAAK,CAAC,CAAC,8FAAI,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC;IAEtD,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC;IACpC,eAAe,GAAG,MAAM,CAAC,eAAe,CAAC,CAAC;IAC1C,KAAK,GAAG,MAAM,CAAc,IAAI;8EAAC,CAAC;IACnD,0EAA0E;IAClE,aAAa,GAAkB,IAAI,CAAC;IAE5C,+DAA+D;IAC5C,OAAO,GAAG,QAAQ,CAAC,GAAG,EAAE;QACzC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;YAAE,OAAO,CAAC,CAAC;QAC9B,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC5C,IAAI,MAAM,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC;QAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAC7C,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;QAChD,2FAA2F;QAC3F,6FAA6F;QAC7F,0DAA0D;QAC1D,MAAM,MAAM,GACV,IAAI,CAAC,QAAQ,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC;QACrF,MAAM,OAAO,GAAG,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,MAAM,GAAG,CAAC,OAAO,GAAG,MAAM,CAAC,CAAC;QAChG,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IAChD,CAAC;gFAAC,CAAC;IAEH;;;;;OAKG;IACgB,SAAS,GAAG,QAAQ,CAAC,GAAG,EAAE;QAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QACjC,IAAI,OAAO,KAAK,CAAC,IAAI,QAAQ,KAAK,UAAU;YAAE,OAAO,SAAS,CAAC;QAC/D,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,KAAK,EAAE,EAAE,MAAM,CAAC;YACxD,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,GAAG,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAC9E,CAAC;QACD,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC;IACpC,CAAC;kFAAC,CAAC;IAEgB,UAAU,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC;QAC9C,GAAG,IAAI,CAAC,qBAAqB,EAAE;QAC/B,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE;KACvB,CAAC;mFAAC,CAAC;IAEJ;QACE,KAAK,EAAE,CAAC;QACR,sFAAsF;QACtF,wFAAwF;QACxF,mFAAmF;QACnF,oFAAoF;QACpF,MAAM,CAAC,GAAG,EAAE;YACV,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC7C,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ;gBAAE,OAAO;YACzC,KAAK,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,EAAE,GAAE,CAAC,EAAE;gBAC1C,QAAQ;gBACR,MAAM,EAAE,cAAc,CAAC,MAAM,CAAC;gBAC9B,MAAM,EAAE,MAAM;gBACd,KAAK,EAAE,MAAM;aACd,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAES,QAAQ,CAAC,KAA8C;QAC/D,MAAM,MAAM,GAAG,KAAK,CAAC,WAAW,EAAE,MAAM,CAAC;QACzC,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,MAAM,EAAE,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC3D,8EAA8E;QAC9E,IAAI,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC;YAAE,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC;IAC/D,CAAC;IAED;;;OAGG;IACK,SAAS,CAAC,QAAgB;QAChC,IAAI,GAAG,GAAG,QAAQ,CAAC;QACnB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3D,OAAO,GAAG,CAAC;IACb,CAAC;uGApGU,oBAAoB;2FAApB,oBAAoB,s9BAjBrB;;;;;;;;;;;GAWT,4DAZS,gBAAgB,0HAAE,IAAI;;2FAkBrB,oBAAoB;kBApBhC,SAAS;mBAAC;oBACT,QAAQ,EAAE,wBAAwB;oBAClC,OAAO,EAAE,CAAC,gBAAgB,EAAE,IAAI,CAAC;oBACjC,QAAQ,EAAE;;;;;;;;;;;GAWT;oBACD,IAAI,EAAE;wBACJ,iBAAiB,EAAE,aAAa;wBAChC,UAAU,EAAE,kBAAkB;qBAC/B;iBACF","sourcesContent":["import {\n Component,\n computed,\n effect,\n inject,\n input,\n signal,\n booleanAttribute,\n} from '@angular/core';\nimport { Keyboard, LayoutAnimation, type LayoutEasing } from '@ng-native/device';\nimport type { NativeSyntheticEvent } from '@ng-native/fabric';\nimport type { Rect } from './events.ts';\nimport { ContentContainer } from './content-container.ts';\nimport { View } from './view.ts';\nimport { ViewBase } from './view-base.ts';\nimport { numberOr } from './transforms.ts';\n\nexport type KeyboardAvoidingBehavior = 'padding' | 'height' | 'position';\n\n/**\n * The names `LayoutEasing` knows. `'keyboard'` is among them: iOS reports its keyboard's own\n * curve by that name, and RN's KeyboardAvoidingView animates on it.\n */\nconst LAYOUT_EASINGS: ReadonlySet<string> = new Set([\n 'linear',\n 'easeInEaseOut',\n 'easeIn',\n 'easeOut',\n 'spring',\n 'keyboard',\n]);\n\n/** A keyboard event's `easing`, narrowed to what `LayoutAnimation` accepts, or left to its default. */\nfunction layoutEasingOf(easing: string | undefined): LayoutEasing | undefined {\n return easing !== undefined && LAYOUT_EASINGS.has(easing) ? (easing as LayoutEasing) : undefined;\n}\n\n/**\n * Moves its content clear of the keyboard.\n *\n * RN's KeyboardAvoidingView is a JS composite: there is no native component, only a view whose\n * padding, height or position tracks the keyboard. Off a device the `Keyboard` service reports\n * nothing, so this renders as a plain view and does nothing, which is also the right behaviour on\n * a platform with no soft keyboard.\n *\n * The overlap is the view's own frame against the keyboard's top edge, so a view that does not\n * reach the bottom of the screen moves only as far as it must. Unlike RN, the frame's top is\n * measured in the window, which is what the keyboard's `screenY` is in: `(layout)` reports it in\n * the parent's coordinates, and on a screen under a navigation bar those start a bar's height\n * down. RN leaves that to `keyboardVerticalOffset`, and a view that was not told came up short by\n * exactly that much, its bottom edge still behind the keyboard. The overlap never exceeds the\n * view's own height either: padding a view by more than it has pushes its content out of it.\n * When the keyboard source reports no `screenY`, the keyboard's height stands in.\n *\n * The adjustment itself animates with the keyboard's own motion, the way RN's own\n * KeyboardAvoidingView does: a keyboard event's `duration` and `easing` configure the next\n * layout animation before the style change that moves this view, so the two run together rather\n * than the padding, height or position jumping to its resting place in one step.\n */\n@Component({\n selector: 'keyboard-avoiding-view',\n imports: [ContentContainer, View],\n template: `\n @if (behavior() === 'position') {\n <view\n [contentContainerOf]=\"node\"\n [contentContainerClass]=\"contentContainerClass()\"\n [style]=\"positioned()\"\n ><ng-content\n /></view>\n } @else {\n <ng-content />\n }\n `,\n host: {\n '[styleOverride]': 'avoidance()',\n '(layout)': 'onLayout($event)',\n },\n})\nexport class KeyboardAvoidingView extends ViewBase {\n /**\n * How to make room: pad the bottom, shrink the height, or shift the content up. RN leaves this\n * unset and does nothing; `padding` is the default here because that is what a form wants.\n */\n readonly behavior = input<KeyboardAvoidingBehavior>('padding');\n /** Styles for the inner view that `position` moves. */\n readonly contentContainerStyle = input<Record<string, unknown>>();\n /**\n * Classes for the inner view that `position` moves, matched as if it were written in the\n * template this view is. See `ContentContainer`.\n */\n readonly contentContainerClass = input<string>();\n /** Set to false to stop avoiding without removing the view. */\n readonly enabled = input(true, { transform: booleanAttribute });\n /**\n * Extra room to leave above the keyboard, in points. RN's name, and in RN the place to put a\n * header's height; here the view is measured in the window, so a navigation bar needs none.\n */\n readonly keyboardVerticalOffset = input(0, { transform: numberOr(0) });\n\n private readonly keyboard = inject(Keyboard).metrics;\n private readonly layoutAnimation = inject(LayoutAnimation);\n private readonly frame = signal<Rect | null>(null);\n /** The height before any adjustment, which `height` mode shrinks from. */\n private initialHeight: number | null = null;\n\n /** How far the keyboard intrudes into this view, in points. */\n protected readonly overlap = computed(() => {\n if (!this.enabled()) return 0;\n const { height, screenY } = this.keyboard();\n if (height === 0) return 0;\n const frame = this.frame();\n const offset = this.keyboardVerticalOffset();\n if (!frame) return Math.max(0, height - offset);\n // In `height` mode the frame is the one this view already shrank, so measure the height it\n // had before, as RN adds its current adjustment back; otherwise the room it made reads as no\n // overlap and the height springs back under the keyboard.\n const extent =\n this.behavior() === 'height' ? (this.initialHeight ?? frame.height) : frame.height;\n const overlap = screenY === undefined ? height - offset : frame.y + extent - (screenY - offset);\n return Math.min(extent, Math.max(0, overlap));\n });\n\n /**\n * The adjustment, or nothing to adjust. Bound as the engine's style override rather than as\n * host style, because Angular ranks a host style binding under the template's: a caller's\n * `[style]=\"{flex: 1}\"` would otherwise keep its flex in height mode and the view would not\n * shrink at all. RN composes the adjustment after the caller's style, so it wins there too.\n */\n protected readonly avoidance = computed(() => {\n const overlap = this.overlap();\n const behavior = this.behavior();\n if (overlap === 0 || behavior === 'position') return undefined;\n if (behavior === 'height') {\n const base = this.initialHeight ?? this.frame()?.height;\n return base === undefined ? undefined : { height: base - overlap, flex: 0 };\n }\n return { paddingBottom: overlap };\n });\n\n protected readonly positioned = computed(() => ({\n ...this.contentContainerStyle(),\n bottom: this.overlap(),\n }));\n\n constructor() {\n super();\n // A duration of zero is Android's own keyboard event, which animates itself; there is\n // nothing to configure alongside it. `appear`/`leave` are turned off because this is an\n // update to a view already on screen, not one appearing or leaving - RN's own bare\n // `LayoutAnimation.configureNext({duration, update})` animates nothing else either.\n effect(() => {\n const { duration, easing } = this.keyboard();\n if (!this.enabled() || !duration) return;\n void this.layoutAnimation.animate(() => {}, {\n duration,\n easing: layoutEasingOf(easing),\n appear: 'none',\n leave: 'none',\n });\n });\n }\n\n protected onLayout(event: NativeSyntheticEvent<{ layout?: Rect }>): void {\n const layout = event.nativeEvent?.layout;\n if (!layout) return;\n this.frame.set({ ...layout, y: this.windowTop(layout.y) });\n // Only a layout that the keyboard did not cause counts as the natural height.\n if (this.overlap() === 0) this.initialHeight = layout.height;\n }\n\n /**\n * Where this view's top edge is in the window, or `fallback` where the platform cannot say.\n * Synchronous on the new architecture, so it answers for the layout being reported.\n */\n private windowTop(fallback: number): number {\n let top = fallback;\n this.engine.measure(this.node, (frame) => (top = frame.y));\n return top;\n }\n}\n"]}
|
package/dist/modal.d.ts
CHANGED
|
@@ -36,9 +36,15 @@ export declare class Modal extends ViewBase {
|
|
|
36
36
|
readonly supportedOrientations: import("@angular/core").InputSignal<readonly ModalOrientation[] | undefined>;
|
|
37
37
|
/** iOS: let a downward swipe dismiss the modal, reported through `(requestClose)`. */
|
|
38
38
|
readonly allowSwipeDismissal: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
|
39
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* Android: draw under the status bar. Always on under Android edge-to-edge (Expo SDK 57), where
|
|
41
|
+
* React Native ignores `false`.
|
|
42
|
+
*/
|
|
40
43
|
readonly statusBarTranslucent: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
|
41
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* Android: draw under the navigation bar. Always on under Android edge-to-edge (Expo SDK 57),
|
|
46
|
+
* where React Native ignores `false`. In an app that opts out, it needs `statusBarTranslucent`.
|
|
47
|
+
*/
|
|
42
48
|
readonly navigationBarTranslucent: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
|
43
49
|
/** Android: give the modal's window a hardware-accelerated surface. */
|
|
44
50
|
readonly hardwareAccelerated: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
|
package/dist/modal.js
CHANGED
|
@@ -43,9 +43,15 @@ export class Modal extends ViewBase {
|
|
|
43
43
|
...(ngDevMode ? [undefined, { debugName: "supportedOrientations" }] : /* istanbul ignore next */ []));
|
|
44
44
|
/** iOS: let a downward swipe dismiss the modal, reported through `(requestClose)`. */
|
|
45
45
|
allowSwipeDismissal = input(undefined, { ...(ngDevMode ? { debugName: "allowSwipeDismissal" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
46
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* Android: draw under the status bar. Always on under Android edge-to-edge (Expo SDK 57), where
|
|
48
|
+
* React Native ignores `false`.
|
|
49
|
+
*/
|
|
47
50
|
statusBarTranslucent = input(undefined, { ...(ngDevMode ? { debugName: "statusBarTranslucent" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
48
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* Android: draw under the navigation bar. Always on under Android edge-to-edge (Expo SDK 57),
|
|
53
|
+
* where React Native ignores `false`. In an app that opts out, it needs `statusBarTranslucent`.
|
|
54
|
+
*/
|
|
49
55
|
navigationBarTranslucent = input(undefined, { ...(ngDevMode ? { debugName: "navigationBarTranslucent" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
50
56
|
/** Android: give the modal's window a hardware-accelerated surface. */
|
|
51
57
|
hardwareAccelerated = input(undefined, { ...(ngDevMode ? { debugName: "hardwareAccelerated" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
@@ -72,9 +78,6 @@ export class Modal extends ViewBase {
|
|
|
72
78
|
if (this.transparent() && style && style !== 'overFullScreen') {
|
|
73
79
|
console.warn(`[angular-native] <modal> is transparent, which needs presentationStyle="overFullScreen" on iOS; '${style}' will not be see-through.`);
|
|
74
80
|
}
|
|
75
|
-
if (this.navigationBarTranslucent() && !this.statusBarTranslucent()) {
|
|
76
|
-
console.warn('[angular-native] <modal> navigationBarTranslucent has no effect without statusBarTranslucent.');
|
|
77
|
-
}
|
|
78
81
|
});
|
|
79
82
|
}
|
|
80
83
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: Modal, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
package/dist/modal.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modal.js","sourceRoot":"","sources":["../src/modal.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,UAAU,EACV,gBAAgB,EAChB,QAAQ,EACR,MAAM,EACN,MAAM,EACN,KAAK,GACN,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;;AAM1C;;;;;;;;;;;;;GAaG;AAiBH,MAAM,OAAO,KAAM,SAAQ,QAAQ;IACjC;;;;;OAKG;IACM,OAAO,GAAG,KAAK,CAAC,IAAI,+EAAI,SAAS,EAAE,gBAAgB,GAAG,CAAC;IAChE,wEAAwE;IAC/D,WAAW,GAAG,KAAK,CAAC,KAAK,mFAAI,SAAS,EAAE,gBAAgB,GAAG,CAAC;IACrE,0EAA0E;IACjE,aAAa,GAAG,KAAK;iGAAU,CAAC;IACzC,4DAA4D;IACnD,aAAa,GAAG,KAAK;iGAA6B,CAAC;IAC5D,+FAA+F;IACtF,iBAAiB,GAAG,KAAK;qGAA0B,CAAC;IAC7D,6EAA6E;IACpE,qBAAqB,GAAG,KAAK;yGAA+B,CAAC;IACtE,sFAAsF;IAC7E,mBAAmB,GAAG,KAAK,CAAC,SAAS,2FAAI,SAAS,EAAE,eAAe,GAAG,CAAC;IAChF
|
|
1
|
+
{"version":3,"file":"modal.js","sourceRoot":"","sources":["../src/modal.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n DestroyRef,\n booleanAttribute,\n computed,\n effect,\n inject,\n input,\n} from '@angular/core';\nimport { Direction } from '@ng-native/device';\nimport { HostEngine } from '@ng-native/fabric';\nimport { optionalBoolean } from './transforms.ts';\nimport { View } from './view.ts';\nimport { ViewBase } from './view-base.ts';\n\nexport type ModalPresentationStyle = 'fullScreen' | 'pageSheet' | 'formSheet' | 'overFullScreen';\nexport type ModalOrientation =\n 'portrait' | 'portrait-upside-down' | 'landscape' | 'landscape-left' | 'landscape-right';\n\n/**\n * Content presented over everything else. Commits as `ModalHostView`.\n *\n * RN's `Modal.js` gives the native host `{position: 'absolute'}` (the engine's default for it)\n * and puts the children inside a full-bleed view carrying the backdrop colour. Without the\n * container the content has no backdrop and no layout box, so a modal renders as nothing.\n *\n * Events are element events: `(requestClose)` for the Android back button and an iOS swipe,\n * `(show)`, `(dismiss)`, and `(orientationChange)`.\n *\n * The container is pinned to the leading edge with `[side]: 0`, the same as RN's own Modal.js -\n * `left` in a left-to-right layout, `right` in a right-to-left one - so the backdrop starts from\n * the edge content actually begins at rather than always the physical left.\n */\n@Component({\n selector: 'modal',\n imports: [View],\n template: `<view [style]=\"containerStyle()\" collapsable=\"false\"><ng-content /></view>`,\n host: {\n '[visible]': 'visible()',\n '[transparent]': 'transparent()',\n '[animationType]': 'animationType()',\n '[presentationStyle]': 'resolvedPresentationStyle()',\n '[supportedOrientations]': 'supportedOrientations()',\n '[allowSwipeDismissal]': 'allowSwipeDismissal()',\n '[statusBarTranslucent]': 'statusBarTranslucent()',\n '[navigationBarTranslucent]': 'navigationBarTranslucent()',\n '[hardwareAccelerated]': 'hardwareAccelerated()',\n },\n})\nexport class Modal extends ViewBase {\n /**\n * Whether the modal is showing. Defaults to true, as in RN. While false the engine leaves the\n * native host out of the commit, as RN's Modal returns null when hidden, because the host\n * covers the screen and takes every touch whether or not it presents; on iOS a modal that was\n * showing stays until native reports its dismissal.\n */\n readonly visible = input(true, { transform: booleanAttribute });\n /** See through to the screen behind; the container gets no backdrop. */\n readonly transparent = input(false, { transform: booleanAttribute });\n /** The container's background when not transparent. Defaults to white. */\n readonly backdropColor = input<string>();\n /** How the modal appears and leaves. Defaults to `none`. */\n readonly animationType = input<'none' | 'slide' | 'fade'>();\n /** iOS: full screen, a sheet, or over the current content. `overFullScreen` if transparent. */\n readonly presentationStyle = input<ModalPresentationStyle>();\n /** iOS: which orientations the modal may rotate to. Defaults to portrait. */\n readonly supportedOrientations = input<readonly ModalOrientation[]>();\n /** iOS: let a downward swipe dismiss the modal, reported through `(requestClose)`. */\n readonly allowSwipeDismissal = input(undefined, { transform: optionalBoolean });\n /**\n * Android: draw under the status bar. Always on under Android edge-to-edge (Expo SDK 57), where\n * React Native ignores `false`.\n */\n readonly statusBarTranslucent = input(undefined, { transform: optionalBoolean });\n /**\n * Android: draw under the navigation bar. Always on under Android edge-to-edge (Expo SDK 57),\n * where React Native ignores `false`. In an app that opts out, it needs `statusBarTranslucent`.\n */\n readonly navigationBarTranslucent = input(undefined, { transform: optionalBoolean });\n /** Android: give the modal's window a hardware-accelerated surface. */\n readonly hardwareAccelerated = input(undefined, { transform: optionalBoolean });\n\n private readonly direction = inject(Direction);\n\n protected readonly resolvedPresentationStyle = computed(\n () => this.presentationStyle() ?? (this.transparent() ? 'overFullScreen' : undefined),\n );\n\n protected readonly containerStyle = computed(() => ({\n [this.direction.rtl() ? 'right' : 'left']: 0,\n top: 0,\n flex: 1,\n backgroundColor: this.transparent() ? 'transparent' : (this.backdropColor() ?? 'white'),\n }));\n\n constructor() {\n super();\n // RN's host claims any touch that reaches it, so nothing inside the modal can hand a\n // gesture to the screen underneath.\n const stop = inject(HostEngine).setResponder(this.node, {\n onStartShouldSetResponder: () => true,\n });\n inject(DestroyRef).onDestroy(stop);\n\n effect(() => {\n const style = this.presentationStyle();\n if (this.transparent() && style && style !== 'overFullScreen') {\n console.warn(\n `[angular-native] <modal> is transparent, which needs presentationStyle=\"overFullScreen\" on iOS; '${style}' will not be see-through.`,\n );\n }\n });\n }\n}\n"]}
|
package/dist/pressable.js
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* press rectangle, delays and a long-press timer around them. This is that machine, on the same
|
|
6
6
|
* responder negotiation, so a button and an enclosing scroll view agree on who owns a drag.
|
|
7
7
|
*/
|
|
8
|
-
import { Directive,
|
|
8
|
+
import { Directive, computed, forwardRef, input, output, signal, booleanAttribute, } from '@angular/core';
|
|
9
9
|
import { nativePlatform } from '@ng-native/fabric';
|
|
10
|
-
import { optionalBoolean } from "./transforms.js";
|
|
10
|
+
import { numberOr, optionalBoolean } from "./transforms.js";
|
|
11
11
|
import { ViewBase } from "./view-base.js";
|
|
12
12
|
import * as i0 from "@angular/core";
|
|
13
13
|
/**
|
|
@@ -67,13 +67,13 @@ export class TouchableBase extends ViewBase {
|
|
|
67
67
|
pressRetentionOffset = input(/* @ts-ignore */
|
|
68
68
|
...(ngDevMode ? [undefined, { debugName: "pressRetentionOffset" }] : /* istanbul ignore next */ []));
|
|
69
69
|
/** Milliseconds a touch must be held before `(longPress)` fires. */
|
|
70
|
-
delayLongPress = input(DEFAULT_LONG_PRESS_DELAY, { ...(ngDevMode ? { debugName: "delayLongPress" } : /* istanbul ignore next */ {}), transform:
|
|
70
|
+
delayLongPress = input(DEFAULT_LONG_PRESS_DELAY, { ...(ngDevMode ? { debugName: "delayLongPress" } : /* istanbul ignore next */ {}), transform: numberOr(DEFAULT_LONG_PRESS_DELAY) });
|
|
71
71
|
/** Milliseconds between the touch landing and `(pressIn)`, so a scroll can start first. */
|
|
72
|
-
delayPressIn = input(0, { ...(ngDevMode ? { debugName: "delayPressIn" } : /* istanbul ignore next */ {}), transform:
|
|
72
|
+
delayPressIn = input(0, { ...(ngDevMode ? { debugName: "delayPressIn" } : /* istanbul ignore next */ {}), transform: numberOr(0) });
|
|
73
73
|
/** Milliseconds between the touch lifting and `(pressOut)`. */
|
|
74
|
-
delayPressOut = input(0, { ...(ngDevMode ? { debugName: "delayPressOut" } : /* istanbul ignore next */ {}), transform:
|
|
74
|
+
delayPressOut = input(0, { ...(ngDevMode ? { debugName: "delayPressOut" } : /* istanbul ignore next */ {}), transform: numberOr(0) });
|
|
75
75
|
/** The pressed state lasts at least this long, so a quick tap still shows it. */
|
|
76
|
-
minPressDuration = input(DEFAULT_MIN_PRESS_DURATION, { ...(ngDevMode ? { debugName: "minPressDuration" } : /* istanbul ignore next */ {}), transform:
|
|
76
|
+
minPressDuration = input(DEFAULT_MIN_PRESS_DURATION, { ...(ngDevMode ? { debugName: "minPressDuration" } : /* istanbul ignore next */ {}), transform: numberOr(DEFAULT_MIN_PRESS_DURATION) });
|
|
77
77
|
/** Whether a scrolling ancestor may take the gesture away mid-press. */
|
|
78
78
|
cancelable = input(true, { ...(ngDevMode ? { debugName: "cancelable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
79
79
|
/** Android: a native ripple on press. */
|
package/dist/pressable.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pressable.js","sourceRoot":"","sources":["../src/pressable.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Press handling, built on the engine's responder system as RN's `Pressability` is.\n *\n * `onPress` is not a native event: RN synthesises it in JS from the touch primitives, with a\n * press rectangle, delays and a long-press timer around them. This is that machine, on the same\n * responder negotiation, so a button and an enclosing scroll view agree on who owns a drag.\n */\nimport {\n Directive,\n booleanAttribute,\n computed,\n forwardRef,\n input,\n numberAttribute,\n output,\n signal,\n type Signal,\n} from '@angular/core';\nimport { nativePlatform, type NativeSyntheticEvent } from '@ng-native/fabric';\nimport type { Insets, TouchEvent } from './events.ts';\nimport { optionalBoolean } from './transforms.ts';\nimport { ViewBase } from './view-base.ts';\n\n/** What a press handler receives: RN's `{nativeEvent}` shape, passed straight through. */\nexport type PressEvent = TouchEvent;\n\n/**\n * Android's ripple, drawn natively. `foreground` paints it over the content rather than behind.\n */\nexport interface AndroidRipple {\n readonly color?: string | number;\n readonly borderless?: boolean;\n readonly radius?: number;\n readonly foreground?: boolean;\n}\n\n/**\n * How far outside its own bounds a control keeps a press: RN's `DEFAULT_PRESS_RECT_OFFSETS`.\n * More room below, because a thumb rolls downwards as it lifts.\n */\nconst DEFAULT_PRESS_RETENTION: Required<Insets> = { top: 20, left: 20, right: 20, bottom: 30 };\n/** RN's `DEFAULT_LONG_PRESS_DELAY_MS`. */\nconst DEFAULT_LONG_PRESS_DELAY = 500;\n/** RN's `DEFAULT_MIN_PRESS_DURATION`: the pressed look stays at least this long on a quick tap. */\nconst DEFAULT_MIN_PRESS_DURATION = 130;\n\n/**\n * Used only until a control has been measured. RN measures the view and cancels when the touch\n * leaves its bounds plus the retention offset; before the first layout arrives there are no\n * bounds to compare against, so fall back to a small movement threshold rather than treating a\n * drag of any length as a tap.\n */\nconst PRESS_CANCEL_DISTANCE = 15;\n\nconst insets = (value: Insets | number | undefined, fallback: Required<Insets>) => {\n if (value === undefined) return fallback;\n if (typeof value === 'number') return { top: value, left: value, right: value, bottom: value };\n return {\n top: value.top ?? 0,\n left: value.left ?? 0,\n right: value.right ?? 0,\n bottom: value.bottom ?? 0,\n };\n};\n\n/** Page-space touch point, tolerating the several shapes a Fabric touch payload can take. */\nfunction touchPoint(event: NativeSyntheticEvent): { x: number; y: number } {\n const native = (event.nativeEvent ?? {}) as {\n pageX?: number;\n pageY?: number;\n touches?: { pageX?: number; pageY?: number }[];\n };\n const touch = native.touches?.[0];\n return {\n x: native.pageX ?? touch?.pageX ?? 0,\n y: native.pageY ?? touch?.pageY ?? 0,\n };\n}\n\n/** What `rippleDrawable` builds: Android's `RippleAndroid` drawable description. */\ninterface RippleDrawable {\n readonly type: 'RippleAndroid';\n readonly color: unknown;\n readonly borderless: boolean;\n readonly rippleRadius: number | undefined;\n}\n\nconst now = (): number => globalThis.performance?.now?.() ?? Date.now();\n\n/**\n * The press machine, and the base of every primitive that takes a touch.\n *\n * It declares no host bindings of its own, because `Text` extends it and a host binding runs on\n * every element that carries it, on every pass. The controls bind theirs through `ControlBase`.\n */\n@Directive()\nexport abstract class TouchableBase extends ViewBase {\n /**\n * Ignore touches entirely. Undefined until set, because React Native tells \"not passed\" from\n * \"passed false\": only a passed `disabled` wins over `aria-disabled` in what is announced.\n */\n readonly disabled = input(undefined, { transform: optionalBoolean });\n\n /**\n * How far outside the hit area a touch may wander before the press is cancelled. Native's\n * `hitSlop` grows the area a touch can start in; this grows the area it may move within.\n */\n readonly pressRetentionOffset = input<Insets | number>();\n /** Milliseconds a touch must be held before `(longPress)` fires. */\n readonly delayLongPress = input(DEFAULT_LONG_PRESS_DELAY, { transform: numberAttribute });\n /** Milliseconds between the touch landing and `(pressIn)`, so a scroll can start first. */\n readonly delayPressIn = input(0, { transform: numberAttribute });\n /** Milliseconds between the touch lifting and `(pressOut)`. */\n readonly delayPressOut = input(0, { transform: numberAttribute });\n /** The pressed state lasts at least this long, so a quick tap still shows it. */\n readonly minPressDuration = input(DEFAULT_MIN_PRESS_DURATION, { transform: numberAttribute });\n /** Whether a scrolling ancestor may take the gesture away mid-press. */\n readonly cancelable = input(true, { transform: booleanAttribute });\n /** Android: a native ripple on press. */\n readonly androidRipple = input<AndroidRipple>(undefined, { alias: 'android_ripple' });\n\n /** The touch lifted inside the press area. Not fired after a `(longPress)` that has a listener. */\n readonly press = output<PressEvent>();\n readonly pressIn = output<PressEvent>();\n readonly pressOut = output<PressEvent>();\n /**\n * The touch has been held for `delayLongPress`. With no listener there is no long press, as in\n * React Native, so a slow tap is still a `press`.\n */\n readonly longPress = output<PressEvent>();\n\n /** Whether a press is in progress, for the pressed look. */\n readonly pressed = signal(false);\n\n /**\n * Whether a pointer is over the control, which on this platform means an iPad with a trackpad\n * or a mouse. Always false on a phone, because a phone never sends a pointer event.\n *\n * Opted into by exactly the elements that opt into being measured, which is the same question\n * asked twice: is this a control, or is it a piece of text that merely inherits the machinery.\n * Registering on everything was the first attempt and put two props on every paragraph on\n * screen, which is the most numerous node in an app paying for a state a label cannot use.\n */\n readonly hovered = signal(false);\n\n /*\n * Methods rather than `computed()`s, because nearly every instance has no ripple and a computed\n * is an allocation per element whether or not it ever holds anything. The drawable is kept for as\n * long as the ripple it came from, which is the memo the computed was there to provide: a fresh\n * object on every pass would re-send the prop on every pass.\n */\n protected rippleBackground(): RippleDrawable | undefined {\n const ripple = this.androidRipple();\n return ripple && !ripple.foreground ? this.drawable(ripple) : undefined;\n }\n\n protected rippleForeground(): RippleDrawable | undefined {\n const ripple = this.androidRipple();\n return ripple?.foreground ? this.drawable(ripple) : undefined;\n }\n\n private drawn: { ripple: AndroidRipple; drawable: RippleDrawable } | null = null;\n\n private drawable(ripple: AndroidRipple): RippleDrawable {\n if (this.drawn?.ripple !== ripple)\n this.drawn = { ripple, drawable: this.rippleDrawable(ripple) };\n return this.drawn.drawable;\n }\n\n /**\n * The drawable description Android's `ReactDrawableHelper` reads.\n *\n * `color` goes through the engine the way every other colour prop does, and has to: the helper\n * switches on the value's runtime type and takes a number or a platform-colour map, and anything\n * else - a `'#ff0000'` written at the call site, say - falls through to `null` and the ripple\n * draws in the theme's own `colorControlHighlight` instead. Nothing is logged and nothing throws,\n * so the ripple appears and is simply the wrong colour. RN's own `TouchableNativeFeedback.Ripple`\n * calls `processColor` here for the same reason.\n */\n private rippleDrawable(ripple: AndroidRipple): RippleDrawable {\n return {\n type: 'RippleAndroid' as const,\n color: this.engine.color(ripple.color),\n borderless: ripple.borderless ?? false,\n rippleRadius: ripple.radius,\n };\n }\n\n /** Where the touch started, so a drag can be told apart from a tap. */\n private origin: { x: number; y: number } | null = null;\n /** The control's own size, from its last layout. Null until it has been measured once. */\n private size: { width: number; height: number } | null = null;\n private cancelled = false;\n private longPressed = false;\n /** How many `(longPress)` listeners there are; the long-press clock only runs with one. */\n private longPressListeners = 0;\n private pressedAt = 0;\n private pressInTimer: ReturnType<typeof setTimeout> | null = null;\n private longPressTimer: ReturnType<typeof setTimeout> | null = null;\n private pressOutTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor() {\n super();\n // output() has to be a member's own initializer for the compiler to see it, so its listeners\n // are counted here, through its public subscribe.\n const subscribe = this.longPress.subscribe.bind(this.longPress);\n this.longPress.subscribe = (callback) => {\n this.longPressListeners++;\n const subscription = subscribe(callback);\n let open = true;\n return {\n unsubscribe: () => {\n if (open) this.longPressListeners--;\n open = false;\n subscription.unsubscribe();\n },\n };\n };\n }\n\n /** Called as a press starts and ends, for a subclass that shows it natively. `Text` does. */\n protected pressedChanged(_pressed: boolean): void {}\n\n /** Whether a touch starting on this node should be claimed. `Text` narrows it. */\n protected claims(): boolean {\n return !this.pressDisabled();\n }\n\n /**\n * Whether presses are refused. `Pressable.js` gives Pressability `disabled` alone, so here\n * `aria-disabled` and `accessibilityState.disabled` change what is announced and nothing else.\n * `TouchableOpacity` and `Text` widen it.\n */\n protected pressDisabled(): boolean {\n return !!this.disabled();\n }\n\n /**\n * `TouchableOpacity.js` and `Text.js`: `disabled ?? aria-disabled ?? accessibilityState.disabled`,\n * which is the merged announced state whenever `disabled` is unset.\n */\n protected disabledByAnyState(): boolean {\n return (this.disabled() ?? this.accessibilityStateProp()?.disabled) === true;\n }\n\n /**\n * Whether the control's size should be tracked. Native only measures a view that asks, so\n * this must not be true for every `<text>`: a pressable text opts in, a label never does.\n */\n protected measures(): boolean {\n return true;\n }\n\n /**\n * Whether a touch this far from where it started is still on the control.\n *\n * Distance from the origin is the wrong question on anything large: a thumb tap on a full-width\n * button easily travels twenty points without ever leaving it, and cancelling there reads as\n * \"the button doesn't work\", intermittently and only for some people. What matters is whether\n * the finger is still within the control plus its hit slop, plus the slop RN allows.\n *\n * The touch began somewhere inside the control but we do not know where, so the bound is the\n * full extent rather than half of it. That errs towards keeping the press, which is the right\n * way to be wrong: a press that survives a drag the user meant as a scroll is a smaller sin\n * than a tap that does nothing.\n */\n private stillOnControl(dx: number, dy: number): boolean {\n if (!this.size) return Math.hypot(dx, dy) <= PRESS_CANCEL_DISTANCE;\n const slop = insets(this.hitSlop(), { top: 0, left: 0, right: 0, bottom: 0 });\n const keep = insets(this.pressRetentionOffset(), DEFAULT_PRESS_RETENTION);\n const horizontal = dx < 0 ? slop.left + keep.left : slop.right + keep.right;\n const vertical = dy < 0 ? slop.top + keep.top : slop.bottom + keep.bottom;\n return (\n Math.abs(dx) <= this.size.width + horizontal && Math.abs(dy) <= this.size.height + vertical\n );\n }\n\n /**\n * A lifecycle hook rather than `inject(DestroyRef).onDestroy`: Angular registers a hook once per\n * directive type, where the callback was a closure and a lookup for every instance - and every\n * `<text>` on screen is one.\n */\n ngOnDestroy(): void {\n this.clearTimers();\n this.stopResponding?.();\n this.stopLayout?.();\n this.stopHover?.();\n this.stopClick?.();\n }\n\n private stopResponding: (() => void) | null = null;\n private stopLayout: (() => void) | null = null;\n private stopHover: (() => void) | null = null;\n private stopClick: (() => void) | null = null;\n\n override ngOnInit(): void {\n super.ngOnInit();\n this.respondIfNeeded();\n this.measureIfNeeded();\n this.hoverIfNeeded();\n this.clickIfNeeded();\n }\n\n override ngOnChanges(): void {\n super.ngOnChanges();\n this.respondIfNeeded();\n this.measureIfNeeded();\n this.hoverIfNeeded();\n this.clickIfNeeded();\n }\n\n /**\n * A press with no touch: Android's `performClick()`, which is what Enter, the D-pad centre and\n * TalkBack's double-tap do to a focused view, and which reaches us as `topClick` alone.\n *\n * `Pressability.js` answers it in `onClick`: `onPress` and nothing around it, not while disabled,\n * and not for a click carrying a `pointerType`, which is the pointer event a touch already\n * pressed through. It presses only the view that was clicked: a touchable the click only bubbled\n * through stops it there, so neither it nor anything above it acts on a click meant for a\n * focusable view inside it.\n *\n * Android only. iOS sends a click only as a pointer event, and the web host binds `topClick` to\n * the DOM's own click, which a mouse press already reached through the responder.\n */\n private clickIfNeeded(): void {\n if (this.stopClick || !this.measures() || nativePlatform() !== 'android') return;\n this.stopClick = this.engine.setEventListener(this.node, 'topClick', (event) => {\n const click = event as PressEvent;\n if (!this.measures() || Object.hasOwn(click.nativeEvent ?? {}, 'pointerType')) return;\n if (click.target !== this.node) {\n click.stopPropagation();\n return;\n }\n if (this.claims()) this.press.emit(click);\n });\n }\n\n /**\n * Join the responder negotiation, once this is something a touch can press.\n *\n * Behind `measures()` for the same reason as layout and hover: a `<text>` is the commonest node\n * on screen and almost never a control, and registering every one cost a handler object, six\n * closures and a responder entry apiece for a touch it would have refused anyway.\n */\n private respondIfNeeded(): void {\n if (this.stopResponding || !this.measures()) return;\n this.stopResponding = this.engine.setResponder(this.node, {\n // Claim the gesture on touch down unless disabled. Because this goes through the\n // negotiation, an ancestor can pre-empt us in the capture pass and a nested pressable\n // wins over us in the bubble pass, neither of which raw touch listeners could express.\n onStartShouldSetResponder: () => this.claims(),\n onResponderGrant: (event) => this.onGrant(event),\n onResponderMove: (event) => this.onMove(event),\n onResponderRelease: (event) => this.onRelease(event),\n onResponderTerminate: (event) => this.onTerminate(event),\n // Yield to an enclosing scroll view when it takes over the drag, unless told not to.\n onResponderTerminationRequest: () => this.cancelable(),\n });\n }\n\n /**\n * `onLayout` is opted into by the engine as soon as something listens for it, which is the only\n * way the native side reports a view's size at all, and the reason it is only asked for once\n * `measures()` says so - a `<text>` answers that from an input, and most of them say no.\n *\n * Called from the lifecycle rather than watched by an effect. An effect per element is an\n * allocation and a scheduler entry for every pressable and every piece of text on screen, to\n * notice a change these two hooks already see.\n */\n /**\n * Hover, on the same terms and behind the same question.\n *\n * Fabric will not dispatch a pointer event to a view whose props have not asked for one, and\n * the engine writes those props when something listens - so listening is the whole opt-in. The\n * gate is `measures()`, which is not what its name is about but is the right question underneath\n * it: is this an actual control, or a piece of text that merely inherits the machinery. A label\n * says no, and two props on every paragraph on screen is not a cost worth paying for a hover\n * state a label has no use for.\n *\n * Never true on a phone, which sends no pointer events at all. This is the one thing an iPad\n * with a trackpad has that a phone does not.\n */\n private hoverIfNeeded(): void {\n if (this.stopHover || !this.measures()) return;\n const enter = this.engine.setEventListener(this.node, 'topPointerEnter', () =>\n this.hovered.set(true),\n );\n const leave = this.engine.setEventListener(this.node, 'topPointerLeave', () =>\n this.hovered.set(false),\n );\n this.stopHover = () => {\n enter();\n leave();\n };\n }\n\n private measureIfNeeded(): void {\n if (this.stopLayout || !this.measures()) return;\n this.stopLayout = this.engine.setEventListener(this.node, 'topLayout', (event) => {\n const layout = (event as NativeSyntheticEvent<{ layout?: { width: number; height: number } }>)\n .nativeEvent?.layout;\n if (layout) this.size = { width: layout.width, height: layout.height };\n });\n }\n\n private onGrant(event: PressEvent): void {\n this.origin = touchPoint(event);\n this.cancelled = false;\n this.longPressed = false;\n this.clearTimers();\n if (this.androidRipple()) {\n const { x, y } = this.origin;\n this.engine.dispatchCommand(this.node, 'hotspotUpdate', [x, y]);\n this.engine.dispatchCommand(this.node, 'setPressed', [true]);\n }\n const delay = this.delayPressIn();\n if (delay > 0) this.pressInTimer = setTimeout(() => this.activate(event), delay);\n else this.activate(event);\n }\n\n /** `pressIn`, and the long-press clock starts from here. */\n private activate(event: PressEvent): void {\n this.pressInTimer = null;\n this.pressedAt = now();\n this.pressed.set(true);\n this.pressedChanged(true);\n this.pressIn.emit(event);\n if (!this.longPressListeners) return;\n this.longPressTimer = setTimeout(() => {\n this.longPressTimer = null;\n this.longPressed = true;\n this.longPress.emit(event);\n }, this.delayLongPress());\n }\n\n private onMove(event: PressEvent): void {\n if (this.cancelled || !this.origin) return;\n const point = touchPoint(event);\n if (this.stillOnControl(point.x - this.origin.x, point.y - this.origin.y)) return;\n // The finger has wandered: this is a drag, not a tap. Give up the press but stay the\n // responder, so a scroll view underneath still gets its gesture.\n this.cancelled = true;\n this.deactivate(event);\n }\n\n private onRelease(event: PressEvent): void {\n if (this.androidRipple()) this.engine.dispatchCommand(this.node, 'setPressed', [false]);\n if (this.cancelled) return;\n // Lifted before the press-in delay ran out: RN still reports a full press.\n if (this.pressInTimer) {\n clearTimeout(this.pressInTimer);\n this.activate(event);\n }\n const longPressed = this.longPressed;\n this.deactivate(event);\n if (!longPressed) this.press.emit(event);\n }\n\n private onTerminate(event: PressEvent): void {\n if (this.androidRipple()) this.engine.dispatchCommand(this.node, 'setPressed', [false]);\n if (this.cancelled) return;\n this.cancelled = true;\n this.deactivate(event);\n }\n\n /** `pressOut`, no sooner than `minPressDuration` after `pressIn` and after `delayPressOut`. */\n private deactivate(event: PressEvent): void {\n if (this.pressInTimer) {\n clearTimeout(this.pressInTimer);\n this.pressInTimer = null;\n return;\n }\n if (this.longPressTimer) {\n clearTimeout(this.longPressTimer);\n this.longPressTimer = null;\n }\n if (!this.pressed()) return;\n const held = now() - this.pressedAt;\n const delay = Math.max(this.delayPressOut(), this.minPressDuration() - held);\n const out = () => {\n this.pressOutTimer = null;\n this.pressed.set(false);\n this.pressedChanged(false);\n this.pressOut.emit(event);\n };\n if (delay > 0) this.pressOutTimer = setTimeout(out, delay);\n else out();\n }\n\n private clearTimers(): void {\n // Every touchable is destroyed through here, and nearly all of them were never pressed.\n if (!this.pressInTimer && !this.longPressTimer && !this.pressOutTimer) return;\n for (const timer of [this.pressInTimer, this.longPressTimer, this.pressOutTimer]) {\n if (timer) clearTimeout(timer);\n }\n this.pressInTimer = this.longPressTimer = this.pressOutTimer = null;\n }\n}\n\n/**\n * A touchable that is a control: `Pressable`, `TouchableOpacity` and `PressBehavior`.\n *\n * The host bindings every control needs live here rather than on `TouchableBase`, which `Text`\n * extends: the state that follows `disabled`, and the Android ripple. Controls are few enough on\n * a screen to pay for a binding on every pass; texts are not.\n */\n@Directive({\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 // Nor is it left on the node for `:disabled` to read, so this is what a stylesheet matches.\n '[attr.data-disabled]': \"pressDisabled() ? '' : null\",\n '[nativeBackgroundAndroid]': 'rippleBackground()',\n '[nativeForegroundAndroid]': 'rippleForeground()',\n },\n})\nexport abstract class ControlBase extends TouchableBase {}\n\n/**\n * The press machine on its own, for a component that wants to be pressable rather than to\n * contain something that is.\n *\n * No selector: this is only ever a `hostDirectives` entry. A component listing it gets the whole\n * of `TouchableBase` on its own host node - the responder negotiation, the press rectangle, the\n * delays and the long-press timer - instead of wrapping a `<pressable>` and paying for a second\n * native view.\n *\n * It provides `TouchableBase`, so a primitive on the same node can `inject(TouchableBase)` and\n * read `pressed` without caring whether it landed on this or on a real `<pressable>`.\n */\n@Directive({\n providers: [{ provide: TouchableBase, useExisting: forwardRef(() => PressBehavior) }],\n})\nexport class PressBehavior extends ControlBase {\n /** The conditions the composing component gave, any of which disables the control. */\n private readonly conditions = signal<readonly Signal<boolean>[]>([]);\n private readonly held = computed(() => this.conditions().some((condition) => condition()));\n\n /**\n * Disable the control while `condition` is true, from the component that composes this: a\n * button that is loading, which its caller's `disabled` knows nothing of. Either one disables\n * it, as `disabled={loading || disabled}` does on React Native's `Pressable`: presses are\n * refused, a stylesheet sees `[data-disabled]`, and a screen reader is told.\n *\n * constructor() {\n * inject(PressBehavior).disableWhile(this.loading);\n * }\n */\n disableWhile(condition: Signal<boolean>): void {\n this.conditions.update((conditions) => [...conditions, condition]);\n }\n\n protected override accessibleByDefault(): boolean {\n return true;\n }\n\n protected override focusableByDefault(): boolean {\n return true;\n }\n\n protected override pressDisabled(): boolean {\n return this.held() || super.pressDisabled();\n }\n\n protected override disabledForAccessibility(): boolean | undefined {\n return this.held() ? true : this.disabled();\n }\n}\n\n/**\n * A view that responds to presses. RN's `Pressable`, with the same configuration: hit slop,\n * retention offset, press-in and press-out delays, long press, and an Android ripple.\n *\n * `pressed` is a public signal, so a template can style from it through a template reference:\n * `<pressable #p [style.opacity]=\"p.pressed() ? 0.5 : 1\">`.\n */\n@Directive({\n selector: 'pressable',\n exportAs: 'pressable',\n providers: [{ provide: TouchableBase, useExisting: forwardRef(() => Pressable) }],\n})\nexport class Pressable extends ControlBase {\n /** `Pressable.js`: a control is an accessibility element, and focusable, unless refused. */\n protected override accessibleByDefault(): boolean {\n return true;\n }\n\n protected override focusableByDefault(): boolean {\n return true;\n }\n\n protected override disabledForAccessibility(): boolean | undefined {\n return this.disabled();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"pressable.js","sourceRoot":"","sources":["../src/pressable.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Press handling, built on the engine's responder system as RN's `Pressability` is.\n *\n * `onPress` is not a native event: RN synthesises it in JS from the touch primitives, with a\n * press rectangle, delays and a long-press timer around them. This is that machine, on the same\n * responder negotiation, so a button and an enclosing scroll view agree on who owns a drag.\n */\nimport {\n Directive,\n computed,\n forwardRef,\n input,\n output,\n signal,\n type Signal,\n booleanAttribute,\n} from '@angular/core';\nimport { nativePlatform, type NativeSyntheticEvent } from '@ng-native/fabric';\nimport type { Insets, TouchEvent } from './events.ts';\nimport { numberOr, optionalBoolean } from './transforms.ts';\nimport { ViewBase } from './view-base.ts';\n\n/** What a press handler receives: RN's `{nativeEvent}` shape, passed straight through. */\nexport type PressEvent = TouchEvent;\n\n/**\n * Android's ripple, drawn natively. `foreground` paints it over the content rather than behind.\n */\nexport interface AndroidRipple {\n readonly color?: string | number;\n readonly borderless?: boolean;\n readonly radius?: number;\n readonly foreground?: boolean;\n}\n\n/**\n * How far outside its own bounds a control keeps a press: RN's `DEFAULT_PRESS_RECT_OFFSETS`.\n * More room below, because a thumb rolls downwards as it lifts.\n */\nconst DEFAULT_PRESS_RETENTION: Required<Insets> = { top: 20, left: 20, right: 20, bottom: 30 };\n/** RN's `DEFAULT_LONG_PRESS_DELAY_MS`. */\nconst DEFAULT_LONG_PRESS_DELAY = 500;\n/** RN's `DEFAULT_MIN_PRESS_DURATION`: the pressed look stays at least this long on a quick tap. */\nconst DEFAULT_MIN_PRESS_DURATION = 130;\n\n/**\n * Used only until a control has been measured. RN measures the view and cancels when the touch\n * leaves its bounds plus the retention offset; before the first layout arrives there are no\n * bounds to compare against, so fall back to a small movement threshold rather than treating a\n * drag of any length as a tap.\n */\nconst PRESS_CANCEL_DISTANCE = 15;\n\nconst insets = (value: Insets | number | undefined, fallback: Required<Insets>) => {\n if (value === undefined) return fallback;\n if (typeof value === 'number') return { top: value, left: value, right: value, bottom: value };\n return {\n top: value.top ?? 0,\n left: value.left ?? 0,\n right: value.right ?? 0,\n bottom: value.bottom ?? 0,\n };\n};\n\n/** Page-space touch point, tolerating the several shapes a Fabric touch payload can take. */\nfunction touchPoint(event: NativeSyntheticEvent): { x: number; y: number } {\n const native = (event.nativeEvent ?? {}) as {\n pageX?: number;\n pageY?: number;\n touches?: { pageX?: number; pageY?: number }[];\n };\n const touch = native.touches?.[0];\n return {\n x: native.pageX ?? touch?.pageX ?? 0,\n y: native.pageY ?? touch?.pageY ?? 0,\n };\n}\n\n/** What `rippleDrawable` builds: Android's `RippleAndroid` drawable description. */\ninterface RippleDrawable {\n readonly type: 'RippleAndroid';\n readonly color: unknown;\n readonly borderless: boolean;\n readonly rippleRadius: number | undefined;\n}\n\nconst now = (): number => globalThis.performance?.now?.() ?? Date.now();\n\n/**\n * The press machine, and the base of every primitive that takes a touch.\n *\n * It declares no host bindings of its own, because `Text` extends it and a host binding runs on\n * every element that carries it, on every pass. The controls bind theirs through `ControlBase`.\n */\n@Directive()\nexport abstract class TouchableBase extends ViewBase {\n /**\n * Ignore touches entirely. Undefined until set, because React Native tells \"not passed\" from\n * \"passed false\": only a passed `disabled` wins over `aria-disabled` in what is announced.\n */\n readonly disabled = input(undefined, { transform: optionalBoolean });\n\n /**\n * How far outside the hit area a touch may wander before the press is cancelled. Native's\n * `hitSlop` grows the area a touch can start in; this grows the area it may move within.\n */\n readonly pressRetentionOffset = input<Insets | number>();\n /** Milliseconds a touch must be held before `(longPress)` fires. */\n readonly delayLongPress = input(DEFAULT_LONG_PRESS_DELAY, {\n transform: numberOr(DEFAULT_LONG_PRESS_DELAY),\n });\n /** Milliseconds between the touch landing and `(pressIn)`, so a scroll can start first. */\n readonly delayPressIn = input(0, { transform: numberOr(0) });\n /** Milliseconds between the touch lifting and `(pressOut)`. */\n readonly delayPressOut = input(0, { transform: numberOr(0) });\n /** The pressed state lasts at least this long, so a quick tap still shows it. */\n readonly minPressDuration = input(DEFAULT_MIN_PRESS_DURATION, {\n transform: numberOr(DEFAULT_MIN_PRESS_DURATION),\n });\n /** Whether a scrolling ancestor may take the gesture away mid-press. */\n readonly cancelable = input(true, { transform: booleanAttribute });\n /** Android: a native ripple on press. */\n readonly androidRipple = input<AndroidRipple>(undefined, { alias: 'android_ripple' });\n\n /** The touch lifted inside the press area. Not fired after a `(longPress)` that has a listener. */\n readonly press = output<PressEvent>();\n readonly pressIn = output<PressEvent>();\n readonly pressOut = output<PressEvent>();\n /**\n * The touch has been held for `delayLongPress`. With no listener there is no long press, as in\n * React Native, so a slow tap is still a `press`.\n */\n readonly longPress = output<PressEvent>();\n\n /** Whether a press is in progress, for the pressed look. */\n readonly pressed = signal(false);\n\n /**\n * Whether a pointer is over the control, which on this platform means an iPad with a trackpad\n * or a mouse. Always false on a phone, because a phone never sends a pointer event.\n *\n * Opted into by exactly the elements that opt into being measured, which is the same question\n * asked twice: is this a control, or is it a piece of text that merely inherits the machinery.\n * Registering on everything was the first attempt and put two props on every paragraph on\n * screen, which is the most numerous node in an app paying for a state a label cannot use.\n */\n readonly hovered = signal(false);\n\n /*\n * Methods rather than `computed()`s, because nearly every instance has no ripple and a computed\n * is an allocation per element whether or not it ever holds anything. The drawable is kept for as\n * long as the ripple it came from, which is the memo the computed was there to provide: a fresh\n * object on every pass would re-send the prop on every pass.\n */\n protected rippleBackground(): RippleDrawable | undefined {\n const ripple = this.androidRipple();\n return ripple && !ripple.foreground ? this.drawable(ripple) : undefined;\n }\n\n protected rippleForeground(): RippleDrawable | undefined {\n const ripple = this.androidRipple();\n return ripple?.foreground ? this.drawable(ripple) : undefined;\n }\n\n private drawn: { ripple: AndroidRipple; drawable: RippleDrawable } | null = null;\n\n private drawable(ripple: AndroidRipple): RippleDrawable {\n if (this.drawn?.ripple !== ripple)\n this.drawn = { ripple, drawable: this.rippleDrawable(ripple) };\n return this.drawn.drawable;\n }\n\n /**\n * The drawable description Android's `ReactDrawableHelper` reads.\n *\n * `color` goes through the engine the way every other colour prop does, and has to: the helper\n * switches on the value's runtime type and takes a number or a platform-colour map, and anything\n * else - a `'#ff0000'` written at the call site, say - falls through to `null` and the ripple\n * draws in the theme's own `colorControlHighlight` instead. Nothing is logged and nothing throws,\n * so the ripple appears and is simply the wrong colour. RN's own `TouchableNativeFeedback.Ripple`\n * calls `processColor` here for the same reason.\n */\n private rippleDrawable(ripple: AndroidRipple): RippleDrawable {\n return {\n type: 'RippleAndroid' as const,\n color: this.engine.color(ripple.color),\n borderless: ripple.borderless ?? false,\n rippleRadius: ripple.radius,\n };\n }\n\n /** Where the touch started, so a drag can be told apart from a tap. */\n private origin: { x: number; y: number } | null = null;\n /** The control's own size, from its last layout. Null until it has been measured once. */\n private size: { width: number; height: number } | null = null;\n private cancelled = false;\n private longPressed = false;\n /** How many `(longPress)` listeners there are; the long-press clock only runs with one. */\n private longPressListeners = 0;\n private pressedAt = 0;\n private pressInTimer: ReturnType<typeof setTimeout> | null = null;\n private longPressTimer: ReturnType<typeof setTimeout> | null = null;\n private pressOutTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor() {\n super();\n // output() has to be a member's own initializer for the compiler to see it, so its listeners\n // are counted here, through its public subscribe.\n const subscribe = this.longPress.subscribe.bind(this.longPress);\n this.longPress.subscribe = (callback) => {\n this.longPressListeners++;\n const subscription = subscribe(callback);\n let open = true;\n return {\n unsubscribe: () => {\n if (open) this.longPressListeners--;\n open = false;\n subscription.unsubscribe();\n },\n };\n };\n }\n\n /** Called as a press starts and ends, for a subclass that shows it natively. `Text` does. */\n protected pressedChanged(_pressed: boolean): void {}\n\n /** Whether a touch starting on this node should be claimed. `Text` narrows it. */\n protected claims(): boolean {\n return !this.pressDisabled();\n }\n\n /**\n * Whether presses are refused. `Pressable.js` gives Pressability `disabled` alone, so here\n * `aria-disabled` and `accessibilityState.disabled` change what is announced and nothing else.\n * `TouchableOpacity` and `Text` widen it.\n */\n protected pressDisabled(): boolean {\n return !!this.disabled();\n }\n\n /**\n * `TouchableOpacity.js` and `Text.js`: `disabled ?? aria-disabled ?? accessibilityState.disabled`,\n * which is the merged announced state whenever `disabled` is unset.\n */\n protected disabledByAnyState(): boolean {\n return (this.disabled() ?? this.accessibilityStateProp()?.disabled) === true;\n }\n\n /**\n * Whether the control's size should be tracked. Native only measures a view that asks, so\n * this must not be true for every `<text>`: a pressable text opts in, a label never does.\n */\n protected measures(): boolean {\n return true;\n }\n\n /**\n * Whether a touch this far from where it started is still on the control.\n *\n * Distance from the origin is the wrong question on anything large: a thumb tap on a full-width\n * button easily travels twenty points without ever leaving it, and cancelling there reads as\n * \"the button doesn't work\", intermittently and only for some people. What matters is whether\n * the finger is still within the control plus its hit slop, plus the slop RN allows.\n *\n * The touch began somewhere inside the control but we do not know where, so the bound is the\n * full extent rather than half of it. That errs towards keeping the press, which is the right\n * way to be wrong: a press that survives a drag the user meant as a scroll is a smaller sin\n * than a tap that does nothing.\n */\n private stillOnControl(dx: number, dy: number): boolean {\n if (!this.size) return Math.hypot(dx, dy) <= PRESS_CANCEL_DISTANCE;\n const slop = insets(this.hitSlop(), { top: 0, left: 0, right: 0, bottom: 0 });\n const keep = insets(this.pressRetentionOffset(), DEFAULT_PRESS_RETENTION);\n const horizontal = dx < 0 ? slop.left + keep.left : slop.right + keep.right;\n const vertical = dy < 0 ? slop.top + keep.top : slop.bottom + keep.bottom;\n return (\n Math.abs(dx) <= this.size.width + horizontal && Math.abs(dy) <= this.size.height + vertical\n );\n }\n\n /**\n * A lifecycle hook rather than `inject(DestroyRef).onDestroy`: Angular registers a hook once per\n * directive type, where the callback was a closure and a lookup for every instance - and every\n * `<text>` on screen is one.\n */\n ngOnDestroy(): void {\n this.clearTimers();\n this.stopResponding?.();\n this.stopLayout?.();\n this.stopHover?.();\n this.stopClick?.();\n }\n\n private stopResponding: (() => void) | null = null;\n private stopLayout: (() => void) | null = null;\n private stopHover: (() => void) | null = null;\n private stopClick: (() => void) | null = null;\n\n override ngOnInit(): void {\n super.ngOnInit();\n this.respondIfNeeded();\n this.measureIfNeeded();\n this.hoverIfNeeded();\n this.clickIfNeeded();\n }\n\n override ngOnChanges(): void {\n super.ngOnChanges();\n this.respondIfNeeded();\n this.measureIfNeeded();\n this.hoverIfNeeded();\n this.clickIfNeeded();\n }\n\n /**\n * A press with no touch: Android's `performClick()`, which is what Enter, the D-pad centre and\n * TalkBack's double-tap do to a focused view, and which reaches us as `topClick` alone.\n *\n * `Pressability.js` answers it in `onClick`: `onPress` and nothing around it, not while disabled,\n * and not for a click carrying a `pointerType`, which is the pointer event a touch already\n * pressed through. It presses only the view that was clicked: a touchable the click only bubbled\n * through stops it there, so neither it nor anything above it acts on a click meant for a\n * focusable view inside it.\n *\n * Android only. iOS sends a click only as a pointer event, and the web host binds `topClick` to\n * the DOM's own click, which a mouse press already reached through the responder.\n */\n private clickIfNeeded(): void {\n if (this.stopClick || !this.measures() || nativePlatform() !== 'android') return;\n this.stopClick = this.engine.setEventListener(this.node, 'topClick', (event) => {\n const click = event as PressEvent;\n if (!this.measures() || Object.hasOwn(click.nativeEvent ?? {}, 'pointerType')) return;\n if (click.target !== this.node) {\n click.stopPropagation();\n return;\n }\n if (this.claims()) this.press.emit(click);\n });\n }\n\n /**\n * Join the responder negotiation, once this is something a touch can press.\n *\n * Behind `measures()` for the same reason as layout and hover: a `<text>` is the commonest node\n * on screen and almost never a control, and registering every one cost a handler object, six\n * closures and a responder entry apiece for a touch it would have refused anyway.\n */\n private respondIfNeeded(): void {\n if (this.stopResponding || !this.measures()) return;\n this.stopResponding = this.engine.setResponder(this.node, {\n // Claim the gesture on touch down unless disabled. Because this goes through the\n // negotiation, an ancestor can pre-empt us in the capture pass and a nested pressable\n // wins over us in the bubble pass, neither of which raw touch listeners could express.\n onStartShouldSetResponder: () => this.claims(),\n onResponderGrant: (event) => this.onGrant(event),\n onResponderMove: (event) => this.onMove(event),\n onResponderRelease: (event) => this.onRelease(event),\n onResponderTerminate: (event) => this.onTerminate(event),\n // Yield to an enclosing scroll view when it takes over the drag, unless told not to.\n onResponderTerminationRequest: () => this.cancelable(),\n });\n }\n\n /**\n * `onLayout` is opted into by the engine as soon as something listens for it, which is the only\n * way the native side reports a view's size at all, and the reason it is only asked for once\n * `measures()` says so - a `<text>` answers that from an input, and most of them say no.\n *\n * Called from the lifecycle rather than watched by an effect. An effect per element is an\n * allocation and a scheduler entry for every pressable and every piece of text on screen, to\n * notice a change these two hooks already see.\n */\n /**\n * Hover, on the same terms and behind the same question.\n *\n * Fabric will not dispatch a pointer event to a view whose props have not asked for one, and\n * the engine writes those props when something listens - so listening is the whole opt-in. The\n * gate is `measures()`, which is not what its name is about but is the right question underneath\n * it: is this an actual control, or a piece of text that merely inherits the machinery. A label\n * says no, and two props on every paragraph on screen is not a cost worth paying for a hover\n * state a label has no use for.\n *\n * Never true on a phone, which sends no pointer events at all. This is the one thing an iPad\n * with a trackpad has that a phone does not.\n */\n private hoverIfNeeded(): void {\n if (this.stopHover || !this.measures()) return;\n const enter = this.engine.setEventListener(this.node, 'topPointerEnter', () =>\n this.hovered.set(true),\n );\n const leave = this.engine.setEventListener(this.node, 'topPointerLeave', () =>\n this.hovered.set(false),\n );\n this.stopHover = () => {\n enter();\n leave();\n };\n }\n\n private measureIfNeeded(): void {\n if (this.stopLayout || !this.measures()) return;\n this.stopLayout = this.engine.setEventListener(this.node, 'topLayout', (event) => {\n const layout = (event as NativeSyntheticEvent<{ layout?: { width: number; height: number } }>)\n .nativeEvent?.layout;\n if (layout) this.size = { width: layout.width, height: layout.height };\n });\n }\n\n private onGrant(event: PressEvent): void {\n this.origin = touchPoint(event);\n this.cancelled = false;\n this.longPressed = false;\n this.clearTimers();\n if (this.androidRipple()) {\n const { x, y } = this.origin;\n this.engine.dispatchCommand(this.node, 'hotspotUpdate', [x, y]);\n this.engine.dispatchCommand(this.node, 'setPressed', [true]);\n }\n const delay = this.delayPressIn();\n if (delay > 0) this.pressInTimer = setTimeout(() => this.activate(event), delay);\n else this.activate(event);\n }\n\n /** `pressIn`, and the long-press clock starts from here. */\n private activate(event: PressEvent): void {\n this.pressInTimer = null;\n this.pressedAt = now();\n this.pressed.set(true);\n this.pressedChanged(true);\n this.pressIn.emit(event);\n if (!this.longPressListeners) return;\n this.longPressTimer = setTimeout(() => {\n this.longPressTimer = null;\n this.longPressed = true;\n this.longPress.emit(event);\n }, this.delayLongPress());\n }\n\n private onMove(event: PressEvent): void {\n if (this.cancelled || !this.origin) return;\n const point = touchPoint(event);\n if (this.stillOnControl(point.x - this.origin.x, point.y - this.origin.y)) return;\n // The finger has wandered: this is a drag, not a tap. Give up the press but stay the\n // responder, so a scroll view underneath still gets its gesture.\n this.cancelled = true;\n this.deactivate(event);\n }\n\n private onRelease(event: PressEvent): void {\n if (this.androidRipple()) this.engine.dispatchCommand(this.node, 'setPressed', [false]);\n if (this.cancelled) return;\n // Lifted before the press-in delay ran out: RN still reports a full press.\n if (this.pressInTimer) {\n clearTimeout(this.pressInTimer);\n this.activate(event);\n }\n const longPressed = this.longPressed;\n this.deactivate(event);\n if (!longPressed) this.press.emit(event);\n }\n\n private onTerminate(event: PressEvent): void {\n if (this.androidRipple()) this.engine.dispatchCommand(this.node, 'setPressed', [false]);\n if (this.cancelled) return;\n this.cancelled = true;\n this.deactivate(event);\n }\n\n /** `pressOut`, no sooner than `minPressDuration` after `pressIn` and after `delayPressOut`. */\n private deactivate(event: PressEvent): void {\n if (this.pressInTimer) {\n clearTimeout(this.pressInTimer);\n this.pressInTimer = null;\n return;\n }\n if (this.longPressTimer) {\n clearTimeout(this.longPressTimer);\n this.longPressTimer = null;\n }\n if (!this.pressed()) return;\n const held = now() - this.pressedAt;\n const delay = Math.max(this.delayPressOut(), this.minPressDuration() - held);\n const out = () => {\n this.pressOutTimer = null;\n this.pressed.set(false);\n this.pressedChanged(false);\n this.pressOut.emit(event);\n };\n if (delay > 0) this.pressOutTimer = setTimeout(out, delay);\n else out();\n }\n\n private clearTimers(): void {\n // Every touchable is destroyed through here, and nearly all of them were never pressed.\n if (!this.pressInTimer && !this.longPressTimer && !this.pressOutTimer) return;\n for (const timer of [this.pressInTimer, this.longPressTimer, this.pressOutTimer]) {\n if (timer) clearTimeout(timer);\n }\n this.pressInTimer = this.longPressTimer = this.pressOutTimer = null;\n }\n}\n\n/**\n * A touchable that is a control: `Pressable`, `TouchableOpacity` and `PressBehavior`.\n *\n * The host bindings every control needs live here rather than on `TouchableBase`, which `Text`\n * extends: the state that follows `disabled`, and the Android ripple. Controls are few enough on\n * a screen to pay for a binding on every pass; texts are not.\n */\n@Directive({\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 // Nor is it left on the node for `:disabled` to read, so this is what a stylesheet matches.\n '[attr.data-disabled]': \"pressDisabled() ? '' : null\",\n '[nativeBackgroundAndroid]': 'rippleBackground()',\n '[nativeForegroundAndroid]': 'rippleForeground()',\n },\n})\nexport abstract class ControlBase extends TouchableBase {}\n\n/**\n * The press machine on its own, for a component that wants to be pressable rather than to\n * contain something that is.\n *\n * No selector: this is only ever a `hostDirectives` entry. A component listing it gets the whole\n * of `TouchableBase` on its own host node - the responder negotiation, the press rectangle, the\n * delays and the long-press timer - instead of wrapping a `<pressable>` and paying for a second\n * native view.\n *\n * It provides `TouchableBase`, so a primitive on the same node can `inject(TouchableBase)` and\n * read `pressed` without caring whether it landed on this or on a real `<pressable>`.\n */\n@Directive({\n providers: [{ provide: TouchableBase, useExisting: forwardRef(() => PressBehavior) }],\n})\nexport class PressBehavior extends ControlBase {\n /** The conditions the composing component gave, any of which disables the control. */\n private readonly conditions = signal<readonly Signal<boolean>[]>([]);\n private readonly held = computed(() => this.conditions().some((condition) => condition()));\n\n /**\n * Disable the control while `condition` is true, from the component that composes this: a\n * button that is loading, which its caller's `disabled` knows nothing of. Either one disables\n * it, as `disabled={loading || disabled}` does on React Native's `Pressable`: presses are\n * refused, a stylesheet sees `[data-disabled]`, and a screen reader is told.\n *\n * constructor() {\n * inject(PressBehavior).disableWhile(this.loading);\n * }\n */\n disableWhile(condition: Signal<boolean>): void {\n this.conditions.update((conditions) => [...conditions, condition]);\n }\n\n protected override accessibleByDefault(): boolean {\n return true;\n }\n\n protected override focusableByDefault(): boolean {\n return true;\n }\n\n protected override pressDisabled(): boolean {\n return this.held() || super.pressDisabled();\n }\n\n protected override disabledForAccessibility(): boolean | undefined {\n return this.held() ? true : this.disabled();\n }\n}\n\n/**\n * A view that responds to presses. RN's `Pressable`, with the same configuration: hit slop,\n * retention offset, press-in and press-out delays, long press, and an Android ripple.\n *\n * `pressed` is a public signal, so a template can style from it through a template reference:\n * `<pressable #p [style.opacity]=\"p.pressed() ? 0.5 : 1\">`.\n */\n@Directive({\n selector: 'pressable',\n exportAs: 'pressable',\n providers: [{ provide: TouchableBase, useExisting: forwardRef(() => Pressable) }],\n})\nexport class Pressable extends ControlBase {\n /** `Pressable.js`: a control is an accessibility element, and focusable, unless refused. */\n protected override accessibleByDefault(): boolean {\n return true;\n }\n\n protected override focusableByDefault(): boolean {\n return true;\n }\n\n protected override disabledForAccessibility(): boolean | undefined {\n return this.disabled();\n }\n}\n"]}
|
package/dist/section-list.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Component, Directive, TemplateRef, computed, contentChild, inject, input,
|
|
1
|
+
import { Component, Directive, TemplateRef, computed, contentChild, inject, input, output, viewChild, } from '@angular/core';
|
|
2
2
|
import { nativePlatform } from '@ng-native/fabric';
|
|
3
3
|
import { TemplateSlot } from "./template-slot.js";
|
|
4
|
-
import { optionalBoolean } from "./transforms.js";
|
|
4
|
+
import { numberOr, optionalBoolean } from "./transforms.js";
|
|
5
5
|
import { View } from "./view.js";
|
|
6
6
|
import { VirtualList } from "./virtual-list.js";
|
|
7
7
|
import * as i0 from "@angular/core";
|
|
@@ -161,8 +161,8 @@ export class SectionList {
|
|
|
161
161
|
...(ngDevMode ? [{ debugName: "sectionFooterHeight" }] : /* istanbul ignore next */ []));
|
|
162
162
|
/** Pin the current section's header. Defaults to true on iOS and false on Android, as RN. */
|
|
163
163
|
stickySectionHeadersEnabled = input(undefined, { ...(ngDevMode ? { debugName: "stickySectionHeadersEnabled" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
164
|
-
overscan = input(4, { ...(ngDevMode ? { debugName: "overscan" } : /* istanbul ignore next */ {}), transform:
|
|
165
|
-
endReachedThreshold = input(2, { ...(ngDevMode ? { debugName: "endReachedThreshold" } : /* istanbul ignore next */ {}), transform:
|
|
164
|
+
overscan = input(4, { ...(ngDevMode ? { debugName: "overscan" } : /* istanbul ignore next */ {}), transform: numberOr(4) });
|
|
165
|
+
endReachedThreshold = input(2, { ...(ngDevMode ? { debugName: "endReachedThreshold" } : /* istanbul ignore next */ {}), transform: numberOr(2) });
|
|
166
166
|
/** Space around the rows, inside the scrolling content. See `<virtual-list>`. */
|
|
167
167
|
contentPadding = input(0, /* @ts-ignore */
|
|
168
168
|
...(ngDevMode ? [{ debugName: "contentPadding" }] : /* istanbul ignore next */ []));
|
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 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"]}
|
|
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 output,\n viewChild,\n} from '@angular/core';\nimport { nativePlatform } from '@ng-native/fabric';\nimport { TemplateSlot } from './template-slot.ts';\nimport { numberOr, 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: numberOr(4) });\n readonly endReachedThreshold = input(2, { transform: numberOr(2) });\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"]}
|