second flow
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { StyleSheet, Text, View } from "react-native";
|
||||
import { Palette, Style } from "../styles";
|
||||
import { FONT_FAMILY } from "../styles/Fonts";
|
||||
import { LinearGradient } from "./LinearGradient/LinearGradient";
|
||||
|
||||
const INITIAL_BOX_SIZE = 6;
|
||||
const HANDLE_SIZE = 20;
|
||||
const HANDLE_TOP_OFFSET = (INITIAL_BOX_SIZE - HANDLE_SIZE) / 2;
|
||||
|
||||
const clamp01 = (value) => Math.min(1, Math.max(0, value));
|
||||
|
||||
const Slider = ({
|
||||
value,
|
||||
maxValue,
|
||||
progress,
|
||||
onSeek,
|
||||
onSeekStart,
|
||||
onSeekEnd,
|
||||
seekEnabled = false,
|
||||
}) => {
|
||||
const [layoutWidth, setLayoutWidth] = useState(0);
|
||||
const [ratio, setRatio] = useState(
|
||||
typeof progress === "number" ? clamp01(progress) : 0,
|
||||
);
|
||||
const draggingRef = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!draggingRef.current && typeof progress === "number") {
|
||||
setRatio(clamp01(progress));
|
||||
}
|
||||
}, [progress]);
|
||||
|
||||
const updateRatioFromX = useCallback(
|
||||
(x) => {
|
||||
if (layoutWidth <= 0) return;
|
||||
const available = Math.max(layoutWidth - INITIAL_BOX_SIZE, 0);
|
||||
if (available <= 0) {
|
||||
setRatio(0);
|
||||
return;
|
||||
}
|
||||
const clampedX = Math.min(Math.max(x, 0), layoutWidth);
|
||||
const nextRatio = clamp01(clampedX / available);
|
||||
setRatio(nextRatio);
|
||||
},
|
||||
[layoutWidth],
|
||||
);
|
||||
|
||||
const handleGrant = useCallback(
|
||||
(event) => {
|
||||
if (!seekEnabled) return;
|
||||
draggingRef.current = true;
|
||||
updateRatioFromX(event?.nativeEvent?.locationX || 0);
|
||||
if (typeof onSeekStart === "function") {
|
||||
onSeekStart();
|
||||
}
|
||||
},
|
||||
[seekEnabled, updateRatioFromX, onSeekStart],
|
||||
);
|
||||
|
||||
const handleMove = useCallback(
|
||||
(event) => {
|
||||
if (!seekEnabled || !draggingRef.current) return;
|
||||
updateRatioFromX(event?.nativeEvent?.locationX || 0);
|
||||
},
|
||||
[seekEnabled, updateRatioFromX],
|
||||
);
|
||||
|
||||
const finishSeeking = useCallback(() => {
|
||||
if (!seekEnabled || !draggingRef.current) return;
|
||||
draggingRef.current = false;
|
||||
const currentRatio = clamp01(ratio);
|
||||
if (typeof onSeek === "function") {
|
||||
onSeek(currentRatio);
|
||||
}
|
||||
if (typeof onSeekEnd === "function") {
|
||||
onSeekEnd();
|
||||
}
|
||||
}, [seekEnabled, ratio, onSeek, onSeekEnd]);
|
||||
|
||||
const handleLayout = useCallback((event) => {
|
||||
const width = event?.nativeEvent?.layout?.width || 0;
|
||||
if (width !== layoutWidth) {
|
||||
setLayoutWidth(width);
|
||||
}
|
||||
}, [layoutWidth]);
|
||||
|
||||
const maxOffset = Math.max(layoutWidth - INITIAL_BOX_SIZE, 0);
|
||||
const offset = maxOffset * ratio;
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View
|
||||
style={[
|
||||
styles.interactionArea,
|
||||
seekEnabled ? styles.pointerEnabled : null,
|
||||
]}
|
||||
onLayout={handleLayout}
|
||||
onStartShouldSetResponderCapture={() => seekEnabled}
|
||||
onStartShouldSetResponder={() => seekEnabled}
|
||||
onMoveShouldSetResponder={() => seekEnabled}
|
||||
onResponderGrant={handleGrant}
|
||||
onResponderMove={handleMove}
|
||||
onResponderRelease={finishSeeking}
|
||||
onResponderTerminate={finishSeeking}
|
||||
>
|
||||
<View style={styles.sliderTrack}>
|
||||
<View style={[styles.box, { width: INITIAL_BOX_SIZE + offset }]}>
|
||||
<LinearGradient
|
||||
colors={["#F94697", "#7023F7"]}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 0 }}
|
||||
style={{ flex: 1, borderRadius: 20 }}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<View style={[styles.sliderHandle, { left: offset }]} />
|
||||
</View>
|
||||
<View style={{ ...Style.containerSpaceBetween, marginTop: 10 }}>
|
||||
<Text style={styles.time}>{value}</Text>
|
||||
<Text style={styles.time}>{maxValue}</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
width: "100%",
|
||||
},
|
||||
interactionArea: {
|
||||
width: "100%",
|
||||
height: HANDLE_SIZE,
|
||||
justifyContent: "center",
|
||||
position: "relative",
|
||||
},
|
||||
pointerEnabled: {
|
||||
cursor: "pointer",
|
||||
},
|
||||
sliderTrack: {
|
||||
width: "100%",
|
||||
height: INITIAL_BOX_SIZE,
|
||||
backgroundColor: "#0F0C19",
|
||||
borderRadius: 25,
|
||||
overflow: "hidden",
|
||||
},
|
||||
box: {
|
||||
height: INITIAL_BOX_SIZE,
|
||||
borderRadius: 20,
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
top: 0,
|
||||
zIndex: 1,
|
||||
overflow: "hidden",
|
||||
},
|
||||
sliderHandle: {
|
||||
width: HANDLE_SIZE,
|
||||
height: HANDLE_SIZE,
|
||||
backgroundColor: "#f8f9ff",
|
||||
borderRadius: HANDLE_SIZE / 2,
|
||||
position: "absolute",
|
||||
top: HANDLE_TOP_OFFSET,
|
||||
zIndex: 2,
|
||||
borderWidth: 4,
|
||||
borderColor: "#9B4DFF",
|
||||
shadowColor: "#8951FC",
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 3,
|
||||
},
|
||||
shadowOpacity: 0.17,
|
||||
shadowRadius: 3.05,
|
||||
elevation: 4,
|
||||
},
|
||||
time: {
|
||||
fontSize: 14,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
},
|
||||
});
|
||||
|
||||
export default Slider;
|
||||
Reference in New Issue
Block a user