@liuman/react-dragwindow
v0.1.2
Published
Draggable, resizable, snapping floating windows for React — grid/edge/window snapping, minimize-to-bar or taskbar, overflow title toolbar, and a pluggable host adapter for your own window store
Maintainers
Readme
@liuman/react-dragwindow
Draggable, resizable, snapping floating windows for React.
Extracted from a production 3D mine-ventilation workbench where dozens of these windows coexist over a WebGL canvas, so it is built for many windows at once rather than a single modal-ish dialog.
- drag / resize from 8 directions, grid snapping, edge snapping, window-to-window snapping
- maximize by double-clicking the title bar, restore to the pre-maximize bounds
- minimize to a compact bar (horizontal or vertical) or to a taskbar
- title bar with
titleExtra, an overflow "…" menu that自动 collapses when the title bar runs out of width, a footer slot and a left side slot - optional per-window opacity and hidden title bar (native-overlay look)
- pluggable host adapter — the package holds no global state; plug your own store in for managed windows, or use it fully controlled
Install
npm install @liuman/react-dragwindowPeer deps: react, react-dom, antd, @ant-design/icons, styled-components.
Controlled usage
import { DragWindow } from '@liuman/react-dragwindow';
<DragWindow
id="inspector"
title="Inspector"
visible={open}
onClose={() => setOpen(false)}
initialBounds={{ x: 120, y: 80, width: 480, height: 360 }}
onBoundsChange={setBounds}
getOtherWindowsBounds={() => otherBounds}
>
<YourContent />
</DragWindow>Managed usage (your own store)
Pass managedWindowId and provide a host. The host owns minimize / opacity /
restore-bounds state; the window reads it through useConfig.
import { DragWindowHostProvider, type DragWindowHost } from '@liuman/react-dragwindow';
const host: DragWindowHost = {
useConfig: (id) => useMyStore((s) => (id ? s.windows[id] : {})),
minimize: (id) => myStore.minimize(id),
restore: (id) => myStore.restore(id),
setRestoreBounds: (id, b) => myStore.setRestoreBounds(id, b),
registerMeta: (id, meta) => myRegistry.set(id, meta),
unregisterMeta: (id) => myRegistry.delete(id),
};
<DragWindowHostProvider value={host}>{app}</DragWindowHostProvider>useConfig must obey the rules of hooks — it is called once, unconditionally,
inside every window.
Toolbar docking (optional)
Set host.dock to let a window shell accept drag-and-dropped toolbar buttons:
dock: {
resolveBarId: (managedId, id) => (isBar(managedId ?? id) ? (managedId ?? id) : null),
itemMime: 'application/x-toolbar-item',
fromMime: 'application/x-toolbar-from',
onDropItem: (barId, itemId, fromBar) => moveItem(itemId, fromBar, barId),
}License
MIT
