diff options
| author | darkuss <darkussdelta@gmail.com> | 2026-08-13 05:13:32 +0200 |
|---|---|---|
| committer | darkuss <darkussdelta@gmail.com> | 2026-08-13 05:13:32 +0200 |
| commit | 5ac62cc1a4d9a5f50ea26d5bc1afb0fbf95da23c (patch) | |
| tree | fd50520f319ec22798f8b3629899cd1bbef5af23 /src/desktop/window.tsx | |
init
Diffstat (limited to 'src/desktop/window.tsx')
| -rw-r--r-- | src/desktop/window.tsx | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/src/desktop/window.tsx b/src/desktop/window.tsx new file mode 100644 index 0000000..b2deaec --- /dev/null +++ b/src/desktop/window.tsx @@ -0,0 +1,81 @@ +import { ComponentChildren, Ref, RefObject } from "preact"; +import { useCallback, useRef, useState } from "preact/hooks"; + +export function Window(props: Window.Props) { + const windowRef: Ref<HTMLDivElement> = useRef(null); + const dragTimeout: Ref<ReturnType<typeof setTimeout>> = 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 <div class="window" ref={windowRef} style={{ width: props.width, height: props.height, left: x, top: y}}> + <div class="titlebar" onPointerDown={startDrag} onPointerUp={stopDrag}> + <div class="title">{props.title}</div> + <div class="buttons" onPointerDown={e => e.stopPropagation()}> + <button> + <img src="/assets/minimize.png" /> + </button> + <button> + <img src="/assets/maximize.png" /> + </button> + <button> + <img src="/assets/close.png" /> + </button> + </div> + </div> + <div class="content"> + {props.children} + </div> + </div> +} + +namespace Window { + export type Props = { + title: string; + width?: string | number; + height?: string | number; + x?: string | number; + y?: string | number; + desktop: RefObject<HTMLDivElement>; + children: ComponentChildren + } +} |
