75 lines
No EOL
1.9 KiB
TypeScript
75 lines
No EOL
1.9 KiB
TypeScript
"use client"
|
|
|
|
import styles from "./styles.module.scss"
|
|
import { useEffect, useMemo, useState } from 'react'
|
|
import classNames from 'classnames'
|
|
|
|
type SideSliderProps = {
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
export const SideSlider = ({ children }: SideSliderProps) => {
|
|
const [isVisible, setIsVisible] = useState(false)
|
|
const [startX, setStartX] = useState<number|undefined>(undefined)
|
|
const [endX, setEndX] = useState<number|undefined>(undefined)
|
|
|
|
const [isMobile, setIsMobile] = useState(false)
|
|
useEffect(() => {
|
|
if(window.innerWidth < 700) {
|
|
setIsMobile(true)
|
|
}
|
|
|
|
window.addEventListener("resize", () => {
|
|
if(window.innerWidth < 700) {
|
|
setIsMobile(true)
|
|
} else {
|
|
setIsMobile(false)
|
|
}
|
|
})
|
|
}, [setIsMobile])
|
|
|
|
// translateX the container when startX or endX changed
|
|
const style = useMemo(() => {
|
|
if (typeof endX === "number" && typeof startX === "number")
|
|
return {
|
|
transform: `translateX(${endX-startX}px)`
|
|
}
|
|
|
|
return undefined
|
|
}, [endX, startX])
|
|
|
|
const className = classNames({
|
|
[styles.container]: true,
|
|
[styles.isVisible]: isVisible,
|
|
});
|
|
|
|
return (
|
|
<div className={styles.wrapper}>
|
|
<div
|
|
className={className}
|
|
onMouseEnter={!isMobile ? () => setIsVisible(true): undefined}
|
|
onMouseLeave={!isMobile ? () => setIsVisible(false): undefined}
|
|
style={style}
|
|
>
|
|
<div
|
|
className={styles.icon}
|
|
onTouchStart={e => {
|
|
if(typeof startX === "undefined" && typeof endX === "undefined") {
|
|
setStartX(e.touches[0].clientX)
|
|
setEndX(e.touches[0].clientX)
|
|
}
|
|
}}
|
|
onTouchMove={e => {
|
|
setEndX(e.changedTouches[0].clientX)
|
|
}}
|
|
>
|
|
</div>
|
|
<div className={styles.content}>
|
|
<div className={styles.padding}>
|
|
{children}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
} |