import { ComponentChildren, Ref, RefObject } from "preact"; import { useCallback, useRef, useState } from "preact/hooks"; export function Window(props: Window.Props) { const windowRef: Ref = useRef(null); const dragTimeout: Ref> = useRef(null); const [x, setX] = useState(props.x ?? 0); const [y, setY] = useState(props.y ?? 0); const initialOffsetX = useRef(0); const initialOffsetY = useRef(0); const updatePosition = useCallback((e: MouseEvent) => { setX(e.clientX - initialOffsetX.current) setY(e.clientY - initialOffsetY.current) }, []) const startDrag = useCallback((e: MouseEvent) => { if (e.button === 0) { const BORDER_WIDTH = 1; console.log(e); // Naturally, I have to add the border width here, because offset* uses padding-edge for the calculations initialOffsetX.current = e.offsetX + BORDER_WIDTH initialOffsetY.current = e.offsetY + BORDER_WIDTH windowRef.current?.classList.add("dragging") if (dragTimeout.current !== null) { clearTimeout(dragTimeout.current); dragTimeout.current = null; } else { props.desktop.current?.addEventListener("mousemove", updatePosition) props.desktop.current?.addEventListener("mouseleave", stopDrag) } } }, []); const stopDrag = useCallback((e: MouseEvent) => { if (e.button === 0) { dragTimeout.current = setTimeout(() => { dragTimeout.current = null; windowRef.current?.classList.remove("dragging") props.desktop.current?.removeEventListener("mousemove", updatePosition) props.desktop.current?.removeEventListener("mouseleave", stopDrag) }, 50) // intrdouce a small delay where the user can still drag the window without actually pressing down lmb to prevent accidental drops } }, []); return
{props.title}
e.stopPropagation()}>
{props.children}
} namespace Window { export type Props = { title: string; width?: string | number; height?: string | number; x?: string | number; y?: string | number; desktop: RefObject; children: ComponentChildren } }