1import { useEffect, useMemo, useRef } from "react";
2import { View } from "react-native";
3import { Gesture, GestureDetector } from "react-native-gesture-handler";
4
5import { SymbolView } from "../../../components/AppSymbol";
6
7/** Pan recognition wins over the settings scroll view only inside the handle. */
8export function SettingsDragHandle(props: {
9 readonly title: string;
10 readonly canMoveUp: boolean;
11 readonly canMoveDown: boolean;
12 readonly onStart: () => void;
13 readonly onMove: (translation: number) => void;
14 readonly onEnd: (translation: number, cancelled: boolean) => void;
15 readonly onStep: (direction: "up" | "down") => void;
16}) {
17 const latest = useRef(props);
18 useEffect(() => {
19 latest.current = props;
20 });
21 const translation = useRef(0);
22 const gesture = useMemo(
23 () =>
24 Gesture.Pan()
25 .minDistance(0)
26 .shouldCancelWhenOutside(false)
27 .runOnJS(true)
28 .onStart(() => {
29 translation.current = 0;
30 latest.current.onStart();
31 })
32 .onUpdate((event) => {
33 translation.current = event.translationY;
34 latest.current.onMove(event.translationY);
35 })
36 .onFinalize((_, success) => latest.current.onEnd(translation.current, !success)),
37 [],
38 );
39 return (
40 <GestureDetector gesture={gesture}>
41 <View
42 collapsable={false}
43 accessible
44 accessibilityRole="adjustable"
45 accessibilityLabel={`Reorder ${props.title}`}
46 accessibilityActions={[
47 ...(props.canMoveUp ? [{ name: "decrement", label: "Move up" }] : []),
48 ...(props.canMoveDown ? [{ name: "increment", label: "Move down" }] : []),
49 ]}
50 onAccessibilityAction={({ nativeEvent }) => {
51 if (nativeEvent.actionName === "decrement" && props.canMoveUp) props.onStep("up");
52 if (nativeEvent.actionName === "increment" && props.canMoveDown) props.onStep("down");
53 }}
54 style={{ width: 48, alignSelf: "stretch", alignItems: "center", justifyContent: "center" }}
55 >
56 <SymbolView
57 name="line.3.horizontal"
58 size={20}
59 tintColorClassName="accent-foreground-muted"
60 />
61 </View>
62 </GestureDetector>
63 );
64}