Files
HKSingleParty/99_references/beacon-main/src/components/swipeable-item.tsx
2025-05-28 09:55:51 +08:00

115 lines
2.3 KiB
TypeScript

/**
* @file Swipeable item component
*/
import {
IonItemOptions,
IonItemSliding,
ItemSlidingCustomEvent,
} from "@ionic/react";
import {FC, HTMLAttributes, ReactNode, useRef} from "react";
import {usePersistentStore} from "~/lib/stores/persistent";
/**
* Swipeable item component props
*/
interface SwipeableItemProps extends HTMLAttributes<HTMLIonItemSlidingElement> {
/**
* Item content
*/
children: ReactNode;
/**
* Start option (Left side)
*/
startOption?: ReactNode;
/**
* End option (Right side)
*/
endOption?: ReactNode;
/**
* Start action
*/
startAction?: () => void | Promise<void>;
/**
* End action
*/
endAction?: () => void | Promise<void>;
}
/**
* Swipeable item component
* @param props Swipeable item component props
* @returns JSX
*/
export const SwipeableItem: FC<SwipeableItemProps> = ({
children,
startOption,
endOption,
startAction,
endAction,
...props
}) => {
// Hooks
const previousRatio = useRef<number>();
const useSlidingActions = usePersistentStore(
state => state.useSlidingActions,
);
// Methods
/**
* Item sliding swipe event handler
* @param event Item sliding custom event
*/
const onItemSlidingSwipe = async (event: ItemSlidingCustomEvent) => {
if (!useSlidingActions) {
return;
}
// Cast the detail
const detail = event.detail as {
amount: number;
ratio: number;
};
// Upvote (Swiped left)
if (
previousRatio.current !== undefined &&
detail.ratio <= -1 &&
previousRatio.current < detail.ratio
) {
await startAction?.();
await event.target.closeOpened();
}
// Downvote (Swiped right)
else if (
previousRatio.current !== undefined &&
detail.ratio >= 1 &&
previousRatio.current > detail.ratio
) {
await endAction?.();
await event.target.closeOpened();
}
// Store the ratio
previousRatio.current = detail.ratio;
};
return (
<IonItemSliding {...props} onIonDrag={onItemSlidingSwipe}>
{useSlidingActions && (
<IonItemOptions side="start">{startOption}</IonItemOptions>
)}
{children}
{useSlidingActions && (
<IonItemOptions side="end">{endOption}</IonItemOptions>
)}
</IonItemSliding>
);
};