diff --git a/src/components/ResizeHandler.tsx b/src/components/ResizeHandler.tsx
index 177de70..05692f9 100644
--- a/src/components/ResizeHandler.tsx
+++ b/src/components/ResizeHandler.tsx
@@ -1,10 +1,10 @@
import { Icon } from 'components/Icon'
import * as React from 'react'
-import { Size } from './SideBarBodyWrapper'
+import { Size2D } from './SideBarBodyWrapper'
type Props = {
- size: Size
- onResize(size: Size): void
+ size: Size2D
+ onResize(size: Size2D): void
onResetSize?(): void
onResizeStateChange?(state: ResizeState): void
style?: React.CSSProperties
@@ -12,55 +12,13 @@ type Props = {
type ResizeState = 'idle' | 'resizing'
-export function HorizontalResizeHandler({
- onResize,
- onResetSize,
- onResizeStateChange,
- size,
- style,
-}: Props) {
- const pointerDown = React.useRef(false)
- const startX = React.useRef(0)
- const baseSize = React.useRef(size)
- const latestPropSize = React.useRef(size)
-
- React.useEffect(() => {
- latestPropSize.current = size
- }, [size])
-
- const onPointerDown = React.useCallback(({ clientX }: React.MouseEvent) => {
- startX.current = clientX
- pointerDown.current = true
- baseSize.current = latestPropSize.current
- onResizeStateChange?.('resizing')
- }, [])
-
- React.useEffect(() => {
- const onPointerMove = ({ clientX }: MouseEvent) => {
- if (!pointerDown.current) return
- const shift = clientX - startX.current
- onResize(baseSize.current + shift)
- }
- window.addEventListener('mousemove', onPointerMove)
- return () => window.removeEventListener('mousemove', onPointerMove)
- }, [onResize])
-
- React.useEffect(() => {
- const onPointerUp = () => {
- if (pointerDown.current) {
- pointerDown.current = false
- baseSize.current = latestPropSize.current
- onResizeStateChange?.('idle')
- }
- }
- window.addEventListener('mouseup', onPointerUp)
- return () => window.removeEventListener('mouseup', onPointerUp)
- }, [])
+export function ResizeHandler({ onResize, onResetSize, onResizeStateChange, size, style }: Props) {
+ const { onPointerDown } = useResizeHandler(size, onResize, { onResizeStateChange })
return (
@@ -68,3 +26,61 @@ export function HorizontalResizeHandler({
)
}
+
+export function useResizeHandler(
+ size: Size2D,
+ onResize: (size: Size2D) => void,
+ {
+ onResizeStateChange,
+ onClick,
+ }: Partial<{
+ onResizeStateChange: (state: ResizeState) => void
+ onClick: (e: PointerEvent) => void
+ }> = {},
+) {
+ const pointerDown = React.useRef(false)
+ const pointerMoved = React.useRef(false)
+ const initialSizeRef = React.useRef([0, 0])
+ const baseSize = React.useRef(size)
+ const latestPropSize = React.useRef(size)
+
+ React.useEffect(() => {
+ latestPropSize.current = size
+ }, [size])
+
+ React.useEffect(() => {
+ const onPointerMove = ({ clientX, clientY }: PointerEvent) => {
+ if (!pointerDown.current) return
+ pointerMoved.current = true
+ const [x, y] = baseSize.current
+ onResize([x + clientX - initialSizeRef.current[0], y + clientY - initialSizeRef.current[1]])
+ }
+ window.addEventListener('pointermove', onPointerMove)
+ return () => window.removeEventListener('pointermove', onPointerMove)
+ }, [onResize])
+
+ React.useEffect(() => {
+ const onPointerUp = (e: PointerEvent) => {
+ if (pointerDown.current) {
+ pointerDown.current = false
+
+ if (!pointerMoved.current) onClick?.(e)
+ pointerMoved.current = false
+
+ baseSize.current = latestPropSize.current
+ onResizeStateChange?.('idle')
+ }
+ }
+ window.addEventListener('pointerup', onPointerUp)
+ return () => window.removeEventListener('pointerup', onPointerUp)
+ }, [])
+
+ const onPointerDown = React.useCallback(({ clientX, clientY }: React.PointerEvent) => {
+ pointerDown.current = true
+ initialSizeRef.current = [clientX, clientY]
+ baseSize.current = latestPropSize.current
+ onResizeStateChange?.('resizing')
+ }, [])
+
+ return { onPointerDown }
+}
diff --git a/src/components/SideBarBodyWrapper.tsx b/src/components/SideBarBodyWrapper.tsx
index 9d05b46..63dd118 100644
--- a/src/components/SideBarBodyWrapper.tsx
+++ b/src/components/SideBarBodyWrapper.tsx
@@ -1,4 +1,4 @@
-import { HorizontalResizeHandler } from 'components/ResizeHandler'
+import { ResizeHandler } from 'components/ResizeHandler'
import { useConfigs } from 'containers/ConfigsContext'
import * as React from 'react'
import { useDebounce, useWindowSize } from 'react-use'
@@ -7,7 +7,8 @@ import { cx } from 'utils/cx'
import { setCSSVariable } from 'utils/DOMHelper'
import * as features from 'utils/features'
-export type Size = number
+type Size = number
+export type Size2D = [Size, Size]
type Props = {
baseSize: Size
className?: string
@@ -67,7 +68,7 @@ export function SideBarBodyWrapper({
const onResize = React.useMemo(() => {
let sizeToApply: number,
applied = true
- return (size: number) => {
+ return ([size]: number[]) => {
// do NOT merge this with the above similar effect, side bar will jump otherwise
sizeToApply = getSafeSize(size, width)
setSize(sizeToApply)
@@ -94,6 +95,7 @@ export function SideBarBodyWrapper({
[onLeave],
)
+ const newLocal: [number, number] = React.useMemo(() => [size, size], [size])
return (
{children}
{features.resize && (
-
{
setSize(defaultConfigs.sideBarWidth)
@@ -111,7 +113,7 @@ export function SideBarBodyWrapper({
onResizeStateChange={state => {
blockLeaveRef.current = state === 'resizing'
}}
- size={size}
+ size={newLocal}
/>
)}
diff --git a/src/components/ToggleShowButton.tsx b/src/components/ToggleShowButton.tsx
index 85fdb4e..fada7ec 100644
--- a/src/components/ToggleShowButton.tsx
+++ b/src/components/ToggleShowButton.tsx
@@ -4,30 +4,31 @@ import * as React from 'react'
import { useDebounce, useWindowSize } from 'react-use'
import { cx } from 'utils/cx'
import { Icon } from './Icon'
+import { useResizeHandler } from './ResizeHandler'
type Props = {
error?: string | null
className?: React.HTMLAttributes['className']
onHover?: React.HTMLAttributes['onMouseEnter']
-} & Pick, 'onClick'>
+ onClick?: (e: PointerEvent) => void
+}
+
+const buttonHeight = 42
+
+function getSafeDistance(y: number, height: number) {
+ return Math.max(0, Math.min(y, height - buttonHeight))
+}
export function ToggleShowButton({ error, className, onClick, onHover }: Props) {
const ref = React.useRef(null)
const config = useConfigs()
const [distance, setDistance] = React.useState(config.value.toggleButtonVerticalDistance)
const { height } = useWindowSize()
- const buttonHeight = 42
React.useEffect(() => {
// make sure it is inside viewport
- if (height - buttonHeight < distance) {
- setDistance(Math.max(0, height - buttonHeight))
- }
+ const safeDistance = getSafeDistance(distance, height)
+ if (safeDistance !== distance) setDistance(safeDistance)
}, [height, distance])
- React.useLayoutEffect(() => {
- if (ref.current) {
- ref.current.style.top = distance + 'px'
- }
- }, [distance])
// updating context
useDebounce(
@@ -36,28 +37,34 @@ export function ToggleShowButton({ error, className, onClick, onHover }: Props)
[distance],
)
- const toggleIconMode = config.value.toggleButtonContent
+ React.useEffect(() => {
+ if (ref.current) {
+ ref.current.style.top = distance + 'px'
+ }
+ }, [])
+ const { onPointerDown } = useResizeHandler(
+ [distance, distance],
+ ([, y]) => {
+ const distance = getSafeDistance(y, height)
+ setDistance(distance)
+ if (ref.current) {
+ ref.current.style.top = distance + 'px'
+ }
+ },
+ { onClick },
+ )
+
return (
)
}
-
-function hideDragPreview(event: React.DragEvent) {
- const img = new Image()
- const EMPTY_IMAGE_BASE64 =
- 'data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs='
- img.src = EMPTY_IMAGE_BASE64
- event.dataTransfer.setDragImage(img, 0, 0)
-}