Skip to content
Source
Sign inSign up

swipe-row-activation.ts

52 lines2.0 KB

1import { createContext, use, useSyncExternalStore } from "react";
2​
3/**
4 * Only rows around the viewport enable swipe gestures and render actions.
5 * Dormant rows keep the same child views so waking them preserves touches.
6 */
7export function createSwipeRowActivation() {
8 let activeKeys = new Set<string>();
9 // Turning a row's swipe gesture off ends a swipe in progress, so changes
10 // wait until every finger that started on the list has lifted.
11 const listTouches = new Set<string>();
12 let pendingKeys: ReadonlyArray<string> | null = null;
13 const listeners = new Set<() => void>();
14 const apply = (keys: ReadonlyArray<string>) => {
15 if (keys.length === activeKeys.size && keys.every((key) => activeKeys.has(key))) return;
16 activeKeys = new Set(keys);
17 for (const listener of listeners) listener();
18 };
19 return {
20 subscribe(listener: () => void) {
21 listeners.add(listener);
22 return () => void listeners.delete(listener);
23 },
24 isActive: (key: string) => activeKeys.has(key),
25 activate(keys: ReadonlyArray<string>) {
26 if (listTouches.size > 0) pendingKeys = keys;
27 else apply(keys);
28 },
29 trackTouches(started: ReadonlyArray<string>, onScreen: ReadonlyArray<string>) {
30 for (const id of started) listTouches.add(id);
31 for (const id of listTouches) if (!onScreen.includes(id)) listTouches.delete(id);
32 if (listTouches.size > 0 || pendingKeys === null) return;
33 const keys = pendingKeys;
34 pendingKeys = null;
35 apply(keys);
36 },
37 };
39​
40export type SwipeRowActivation = ReturnType<typeof createSwipeRowActivation>;
41​
42export const SwipeRowActivationContext = createContext<SwipeRowActivation | null>(null);
43​
44const subscribeNever = () => () => {};
45​
46export function useSwipeRowDormant(key: string | undefined): boolean {
47 const activation = use(SwipeRowActivationContext);
48 return useSyncExternalStore(
49 activation?.subscribe ?? subscribeNever,
50 () => activation !== null && key !== undefined && !activation.isActive(key),
51 );