I have developed my own actionsheet that start from bottom.
now I am trying to include panresponder so the use could close it with drag down.
the actionsheet work but i have to drag my hand to the center of the sheet for the sheet to start moving down.
i have included interpolate which I am gussing that it couseing an issue as i do not start from 0.
could someone have a look and see if there is a solution?
here is the coponent
import View from "./ThemeView";
import Text from "./ThemeText";
import Icon from "./Icons";
import TouchableOpacity from "./TouchableOpacityView";
import { ElementsContext } from "./AppContainer";
import { proc, sleep, newId } from "../Methods";
import {
useContext,
useState,
useEffect,
useRef
} from "react";
import {
Animated,
KeyboardAvoidingView,
Platform,
Easing,
PanResponder
} from "react-native";
const g = require("../GlobalContext").default;
export default ({
title,
height,
children,
visible,
onHide,
speed,
ready,
toTop,
...props
}: {
speed?: number;
children?: any;
title?: any;
height: string;
visible: boolean;
onHide?: () => void;
toTop?: boolean;
}) => {
let getHeight = () => {
if (
typeof height === "number" &&
height > 100
)
return height;
return proc(
parseFloat(
(height?.toString() ?? "0").replace(
/%/g,
""
)
),
g.size.window.height
);
};
g.hook("size", "selection");
let context = useContext(ElementsContext);
const [started, setStarted] = useState(false);
const [isV, setIsV] = useState(false);
const panResponse = useRef();
const currentValue = useRef();
const [onReady, setOnReady] = useState(!ready);
let interpolate = [
g.size.window.height - getHeight() + 80,
g.size.window.height + 50
];
const animTop = useRef(
new Animated.ValueXY({
y: -getHeight(),
x: 0
})
).current;
const animating = useRef(false);
let id = useRef(newId());
const tAnimate = (value: number, fn: any) => {
Animated.timing(animTop.y, {
toValue: value,
duration: (100).sureValue(speed),
easing: Easing.linear,
useNativeDriver: true
}).start(() => {
fn?.();
});
};
let toggle = async (show: boolean) => {
while (animating.current || !animTop) return;
//await sleep(50);
if (show && ready) setOnReady(false);
animating.current = true;
// animTop.flattenOffset();
tAnimate(interpolate[!show ? 1 : 0], () => {
animating.current = false;
if (typeof visible !== "function")
setIsV(visible);
else setIsV(visible());
context.update();
if (show) setOnReady(true);
// if (show) animTop.flattenOffset();
//setIsV(visible);
});
};
g.subscribe(
() => {
//setAnimTop(new Animated.Value(-vHeight));
toggle(isV);
},
"size",
"theme.themeMode"
);
if (typeof visible === "function")
g.subscribe(() => {
if (!isV) setIsV(visible());
toggle(visible());
}, "selection");
if (typeof visible !== "function")
useEffect(() => {
if (!isV) setIsV(visible);
toggle(visible);
}, [visible]);
useEffect(() => {
setStarted(true);
let listner = animTop.addListener(
value => (currentValue.current = value)
);
return () => {
context.remove(id.current);
context.update();
listner.remove();
};
}, []);
useEffect(() => {
if (!panResponse.current && animTop) {
// animTop.extractOffset();
let startValue = 0;
let current = 0;
panResponse.current = PanResponder.create({
onPanResponderTerminationRequest: () =>
false,
onMoveShouldSetResponderCapture: () =>
true, //Tell iOS that we are allowing the movement
onMoveShouldSetPanResponderCapture: () =>
true, // Same here, tell iOS that we allow dragging
onPanResponderGrant: (
e,
gestureState
) => {
startValue = gestureState.dy;
animTop.setValue({
x: 0,
y: interpolate[0]
});
return true;
},
onPanResponderMove: Animated.event(
[
null,
{ dx: animTop.x, dy: animTop.y }
],
{ useNativeDriver: false }
),
onPanResponderRelease: (
evt,
gestureState
) => {
let old = interpolate[0];
let newValue = gestureState.dy;
let diff = newValue - startValue;
if (Math.abs(diff) > old / 2) {
onHide(!visible);
} else {
//animTop.flattenOffset();
tAnimate(old); // reset to start value
}
return false;
}
});
}
let op = !context.has(id.current)
? context.push.bind(context)
: context.updateProps.bind(context);
op(
<>
<TouchableOpacity
ifTrue={isV}
onPress={() => {
(onHide || setIsV)(false);
}}
css="blur flex"
/>
<Animated.View
style={[
{
height: getHeight(),
transform: [
{
translateY:
animTop.y.interpolate({
inputRange: interpolate,
outputRange: interpolate,
extrapolate: "clamp"
})
}
]
},
"absolute mah:95% overflow clearwidth juc:flex-start boTLR:25 botrr:25".css()
]}
{...(panResponse.current?.panHandlers ??
{})}>
<View
invertColor={true}
css="clearboth pa:10 flex">
<View css="he:30 zi:1">
<View
invertColor={false}
css="bor:5 zi:1 to:5 wi:40 he:15 juc:center ali:center absolute le:45%">
<TouchableOpacity
onPress={() => {
(onHide || setIsV)(false);
}}
css="clearboth flex juc:center ali:center">
<Icon
css="bold"
type="Entypo"
name="chevron-small-down"
size={14}
invertColor={false}
/>
</TouchableOpacity>
</View>
<Text
ifTrue={title != undefined}
invertColor={true}
css="header fos:18 bold co:white clearwidth flex">
{title}
</Text>
</View>
<View css="flex zi:5 maw:99%">
{onReady ? children : null}
</View>
</View>
</Animated.View>
</>,
id.current,
{
visible: isV,
toTop
}
);
});
return null;
};