mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-03-11 17:15:48 +00:00
93 lines
2.9 KiB
TypeScript
93 lines
2.9 KiB
TypeScript
// Copyright (C) 2017-2024 Smart code 203358507
|
|
|
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
import { createPortal } from 'react-dom';
|
|
import classNames from 'classnames';
|
|
import useBinaryState from 'stremio/common/useBinaryState';
|
|
import useOrientation from 'stremio/common/useOrientation';
|
|
import styles from './BottomSheet.less';
|
|
|
|
const CLOSE_THRESHOLD = 100;
|
|
|
|
type Props = {
|
|
children: JSX.Element,
|
|
title: string,
|
|
show: boolean,
|
|
onClose: () => void,
|
|
};
|
|
|
|
const BottomSheet = ({ children, title, show, onClose }: Props) => {
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
const orientation = useOrientation();
|
|
const [startOffset, setStartOffset] = useState(0);
|
|
const [offset, setOffset] = useState(0);
|
|
|
|
const [opened, open, close] = useBinaryState();
|
|
|
|
const containerStyle = useMemo(() => ({
|
|
transform: `translateY(${offset}px)`
|
|
}), [offset]);
|
|
|
|
const containerHeight = () => containerRef.current?.offsetHeight ?? 0;
|
|
|
|
const onCloseRequest = () => setOffset(containerHeight());
|
|
|
|
const onTouchStart = ({ touches }: React.TouchEvent<HTMLDivElement>) => {
|
|
const { clientY } = touches[0];
|
|
setStartOffset(clientY);
|
|
};
|
|
|
|
const onTouchMove = useCallback(({ touches }: React.TouchEvent<HTMLDivElement>) => {
|
|
const { clientY } = touches[0];
|
|
setOffset(Math.max(0, clientY - startOffset));
|
|
}, [startOffset]);
|
|
|
|
const onTouchEnd = () => {
|
|
setOffset((offset) => offset > CLOSE_THRESHOLD ? containerHeight() : 0);
|
|
setStartOffset(0);
|
|
};
|
|
|
|
const onTransitionEnd = useCallback(() => {
|
|
(offset === containerHeight()) && close();
|
|
}, [offset]);
|
|
|
|
useEffect(() => {
|
|
setOffset(0);
|
|
show ? open() : close();
|
|
}, [show]);
|
|
|
|
useEffect(() => {
|
|
!opened && onClose();
|
|
}, [opened]);
|
|
|
|
useEffect(() => {
|
|
opened && close();
|
|
}, [orientation]);
|
|
|
|
return opened && createPortal((
|
|
<div className={styles['bottom-sheet']}>
|
|
<div className={styles['backdrop']} onClick={onCloseRequest} />
|
|
<div
|
|
ref={containerRef}
|
|
className={classNames(styles['container'], { [styles['dragging']]: startOffset }, 'animation-slide-up')}
|
|
style={containerStyle}
|
|
onTouchStart={onTouchStart}
|
|
onTouchMove={onTouchMove}
|
|
onTouchEnd={onTouchEnd}
|
|
onTransitionEnd={onTransitionEnd}
|
|
>
|
|
<div className={styles['heading']}>
|
|
<div className={styles['handle']} />
|
|
<div className={styles['title']}>
|
|
{title}
|
|
</div>
|
|
</div>
|
|
<div className={styles['content']} onClick={onCloseRequest}>
|
|
{children}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
), document.body);
|
|
};
|
|
|
|
export default BottomSheet;
|