Polish mobile and web experience
This commit is contained in:
@@ -4,9 +4,15 @@ import { Pressable, type PressableProps, StyleSheet, Text, View } from "react-na
|
||||
import Swipeable, {
|
||||
type SwipeableMethods,
|
||||
} from "react-native-gesture-handler/ReanimatedSwipeable";
|
||||
import Animated, {
|
||||
interpolate,
|
||||
useAnimatedStyle,
|
||||
type SharedValue,
|
||||
} from "react-native-reanimated";
|
||||
|
||||
import { fonts, radii, spacing } from "@/constants/theme";
|
||||
import { useAppTheme } from "@/contexts/ThemeContext";
|
||||
import { resolveActionForeground } from "@/lib/action-contrast";
|
||||
import type { ThemeColors } from "@/lib/theme-palette";
|
||||
import { useThemedStyles } from "@/lib/use-themed-styles";
|
||||
|
||||
@@ -29,6 +35,38 @@ type SwipeableRowProps = {
|
||||
contentStyle?: PressableProps["style"];
|
||||
};
|
||||
|
||||
type SwipeActionsProps = {
|
||||
actions: SwipeAction[];
|
||||
progress: SharedValue<number>;
|
||||
onActionPress: (action: SwipeAction) => void;
|
||||
};
|
||||
|
||||
function SwipeActions({ actions, progress, onActionPress }: SwipeActionsProps) {
|
||||
const styles = useThemedStyles(createSwipeableRowStyles);
|
||||
const revealStyle = useAnimatedStyle(() => ({
|
||||
opacity: interpolate(progress.value, [0, 0.02, 0.15], [0, 0, 1], "clamp"),
|
||||
}));
|
||||
|
||||
return (
|
||||
<Animated.View style={[styles.actions, revealStyle]}>
|
||||
{actions.map((action) => {
|
||||
const foreground = resolveActionForeground(action.backgroundColor, action.color);
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
key={action.key}
|
||||
style={[styles.actionButton, { backgroundColor: action.backgroundColor }]}
|
||||
onPress={() => onActionPress(action)}
|
||||
>
|
||||
<Ionicons name={action.icon} size={20} color={foreground} />
|
||||
<Text style={[styles.actionLabel, { color: foreground }]}>{action.label}</Text>
|
||||
</Pressable>
|
||||
);
|
||||
})}
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
|
||||
export function SwipeableRow({
|
||||
children,
|
||||
actions,
|
||||
@@ -92,25 +130,20 @@ export function SwipeableRow({
|
||||
);
|
||||
}
|
||||
|
||||
function renderRightActions() {
|
||||
function handleActionPress(action: SwipeAction) {
|
||||
suppressContentPress();
|
||||
swipeRef.current?.close();
|
||||
rowOpenRef.current = false;
|
||||
action.onPress();
|
||||
}
|
||||
|
||||
function renderRightActions(progress: SharedValue<number>) {
|
||||
return (
|
||||
<View style={styles.actions}>
|
||||
{actions.map((action) => (
|
||||
<Pressable
|
||||
key={action.key}
|
||||
style={[styles.actionButton, { backgroundColor: action.backgroundColor }]}
|
||||
onPress={() => {
|
||||
suppressContentPress();
|
||||
swipeRef.current?.close();
|
||||
rowOpenRef.current = false;
|
||||
action.onPress();
|
||||
}}
|
||||
>
|
||||
<Ionicons name={action.icon} size={20} color={action.color} />
|
||||
<Text style={[styles.actionLabel, { color: action.color }]}>{action.label}</Text>
|
||||
</Pressable>
|
||||
))}
|
||||
</View>
|
||||
<SwipeActions
|
||||
actions={actions}
|
||||
progress={progress}
|
||||
onActionPress={handleActionPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -121,6 +154,7 @@ export function SwipeableRow({
|
||||
return (
|
||||
<Swipeable
|
||||
ref={swipeRef}
|
||||
containerStyle={styles.container}
|
||||
renderRightActions={renderRightActions}
|
||||
overshootRight={false}
|
||||
onSwipeableOpenStartDrag={suppressContentPress}
|
||||
@@ -141,11 +175,15 @@ export function SwipeableRow({
|
||||
|
||||
const createSwipeableRowStyles = (colors: ThemeColors) =>
|
||||
StyleSheet.create({
|
||||
container: {
|
||||
borderRadius: radii.lg,
|
||||
overflow: "hidden",
|
||||
marginBottom: spacing.xs,
|
||||
},
|
||||
row: {
|
||||
backgroundColor: colors.background,
|
||||
borderRadius: radii.lg,
|
||||
overflow: "hidden",
|
||||
marginBottom: spacing.xs,
|
||||
},
|
||||
actions: {
|
||||
flexDirection: "row",
|
||||
|
||||
Reference in New Issue
Block a user