@aiquants/drag-drop-panels 0.8.5 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +60 -0
- package/README.md +235 -54
- package/dist/DragDropLayout.d.ts +7 -2
- package/dist/drag-drop/DebugComponents.d.ts +2 -3
- package/dist/drag-drop/DebugOverlay.d.ts +2 -0
- package/dist/drag-drop/ResizeHandle.d.ts +5 -2
- package/dist/drag-drop/domHooks.d.ts +2 -0
- package/dist/drag-drop/dragImage.d.ts +6 -0
- package/dist/drag-drop/hooks/useDragDropColumns.d.ts +0 -1
- package/dist/drag-drop/hooks/useDragDropState.d.ts +1 -3
- package/dist/drag-drop/hooks/usePanelTouchDrag.d.ts +27 -0
- package/dist/drag-drop/labels.d.ts +44 -0
- package/dist/drag-drop/maximizeEscape.d.ts +9 -0
- package/dist/drag-drop/panelTheme.d.ts +2 -0
- package/dist/drag-drop/touchDragConfig.d.ts +6 -0
- package/dist/drag-drop/types.d.ts +1 -22
- package/dist/drag-drop/utils/dropTarget.d.ts +2 -0
- package/dist/drag-drop/utils/edgeScrollUtils.d.ts +2 -0
- package/dist/drag-drop/utils/panelMoves.d.ts +30 -0
- package/dist/drag-drop-panels.css +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.es.js +2271 -1790
- package/dist/index.umd.js +2 -2
- package/dist/styles/drag-drop-panels.standalone.css +1 -1
- package/package.json +2 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.9.0 (2026-10-06)
|
|
4
|
+
|
|
5
|
+
### Breaking
|
|
6
|
+
|
|
7
|
+
- The layout's scroll root is a `div` by default, so a host that places the layout inside its own `<main>` no longer gets nested main landmarks. Pass `scrollRootElement="main"` to keep a `<main>` scroll root; any other value throws a `RangeError`. The drag hooks find the root by `[data-aqdd-scroll-root]` (exported as `SCROLL_ROOT_SELECTOR`) whatever its element.
|
|
8
|
+
- `DragDropLayout` now calls `onColumnPanelsChange` with the next arrangement when a panel is moved with its header's new move buttons, Alt + Shift + an arrow or a touch or pen drop (see Added). A host that passed a no-op there must wire it to the state it passes back as `columnPanels` for these moves to take effect. `useDragDropColumns`' `onPanelMove` and `onColumnPanelsChange` options are not called for these moves: they report only the mouse drops the hook performs.
|
|
9
|
+
- The panel header is restructured: the title (`[data-aqdd-panel-title]`) comes first and the custom drag-mode badge and the buttons follow it inside `[data-aqdd-panel-controls]` (the buttons in `[data-aqdd-panel-buttons]`, each marked `data-aqdd-panel-action`). The badge no longer sits inside a title box.
|
|
10
|
+
- The panel chrome's colours and the stage's spacing moved from Tailwind utility classes into the package stylesheet, which reads neutral custom properties whose defaults keep the previous geometry:
|
|
11
|
+
`--aqdd-panel-surface`, `--aqdd-panel-border`, `--aqdd-panel-title-color`, `--aqdd-header-button-color`, `--aqdd-header-button-hover-color`, `--aqdd-header-button-hover-surface`,
|
|
12
|
+
`--aqdd-custom-badge-color`, `--aqdd-custom-badge-surface`, `--aqdd-stage-padding-block`, `--aqdd-stage-padding-inline` and `--aqdd-column-gap`.
|
|
13
|
+
Override them with ordinary declarations; overrides that targeted the old utility classes or the old header structure stop matching.
|
|
14
|
+
- Default colours changed for WCAG 2.2 AA: the header icons are `#475569` (were `slate-400`, 2.6:1), the custom badge text is `#9a3412` (was `orange-600`, 3.1:1), and under a `.dark` ancestor the layout gets dark defaults (surface, border, title, icons, badge). The title no longer darkens on hover.
|
|
15
|
+
- The hidden-panels bar, the empty-column notice and a hidden panel's stand-in during a drag lost their fixed light utility colours: they take the panel's surface, border and title colours (`--aqdd-panel-surface`, `--aqdd-panel-border`, `--aqdd-panel-title-color`) and the stand-in's badge the custom badge colours, so they follow the variables and `.dark` like the panels. In the light scheme the bar and the notice are `0.8` opaque (were `0.7` and `0.3`), the notice text is `#334155` (was `slate-400`, 2.6:1), and the stand-in loses its amber gradient.
|
|
16
|
+
- `InsertPlaceholder` takes a required `label`, `ResizeHandle` a required `label` and `ResizableColumn` a required `resizeHandleLabel` (each a `ResolvedLabel`). The resize handle's default name counts the column from 1 (`{column} 列目のカラム幅を調整`) instead of naming its id.
|
|
17
|
+
- The Touch Event handlers are gone: `DragDropLayout` no longer takes `onTouchStart`, `onTouchMove` and `onTouchEnd`, `useDragDropColumns` no longer returns `handleTouchStart`, `handleTouchMove`, `handleTouchEnd` and `touchDragState` nor takes `enableTouch`, `useDragDropState` no longer returns `touchDragState` and `setTouchDragState`, the `DragDropHookReturn` type loses `touchDragState`, `handleTouchStart`, `handleTouchMove` and `handleTouchEnd`, and `TouchDragState`, `ReactTouchEvent` and `DOMTouchEvent` are no longer exported.
|
|
18
|
+
Those handlers never dropped a panel: they put the layout into its drag state for as long as a finger rested on a panel (inserting the drop placeholders under every touch scroll) and cancelled touch moves inside a passive listener. Touch and pen now drag panels inside the layout (see Added), and the layout keeps that drag's state itself.
|
|
19
|
+
Migrate by deleting, with nothing in their place: the three props where you wired them, every read of `touchDragState` (from `useDragDropColumns` or `useDragDropState`) and every call of `setTouchDragState`, the touch members of objects you declare as `DragDropHookReturn`, `enableTouch`, and imports of the three types.
|
|
20
|
+
- A browser drag that a touch or a pen press inside the layout starts (`pointerType` `"touch"` or `"pen"`, from anywhere in a panel) is cancelled and never reaches your `onDragStart`. iPadOS, Android, ChromeOS and Windows start the browser's own drag from a touch or pen long-press, which would compete with the layout's long-press drag (see Added), so touch and pen drag a panel only by a long-press on its header. A mouse's drag is unaffected (a pen the system reports as a mouse included).
|
|
21
|
+
- The restore buttons of the hidden-panels bar lost their sky-to-indigo gradient and border (white text on `sky-500` was 2.8:1): they take `--aqdd-restore-button-color`, `--aqdd-restore-button-surface` and `--aqdd-restore-button-hover-surface`, white on `#0369a1` (`#075985` while hovered) in both schemes.
|
|
22
|
+
- The drop placeholders lost their fixed sky utilities, the target's ring and the shown placeholder's 80 % opacity (the text was 3.1:1 on the target): they take `--aqdd-drop-placeholder-color` (text, icon and dashed border), `--aqdd-drop-placeholder-surface` and `--aqdd-drop-placeholder-target-surface`, with light and `.dark` defaults that the placeholder carries itself, so the exported `InsertPlaceholder` keeps them outside `DragDropLayout` as well.
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- Moving panels without a drag: four move buttons in every panel header (previous or next column, up or down; an unavailable move is `aria-disabled` and stays focusable)
|
|
27
|
+
and Alt + Shift + arrow shortcuts anywhere inside a panel's DOM (not inside text inputs or editable content, not in content portaled out of the panel, not on a cancelled keydown, not while maximized or dragging; a held chord moves once).
|
|
28
|
+
A move is announced through a `role="status"` live region once the host passes back an arrangement that moved that panel and nothing else; a declined request is dropped silently when the next arrangement changes another panel or a drag starts.
|
|
29
|
+
A shortcut the panel cannot follow is announced as unavailable, the focus returns to the control that held it when the move remounts the panel, and the focused control is scrolled into view after the move.
|
|
30
|
+
`movePanelInColumns`, `availablePanelMoves`, `shownPanelPlace`, `panelMoveOfKey`, `PANEL_MOVES` and `PANEL_MOVE_SHORTCUTS` expose the computation.
|
|
31
|
+
- `labels` replaces every built-in label (`DEFAULT_DRAG_DROP_LAYOUT_LABELS` lists them): the header buttons' names (`aria-label`, also their tooltip), the badge, the announcements, the hidden panels list, the empty column, the drop zones' names, the drop placeholder and the resize handles.
|
|
32
|
+
A built-in label marks its element with its language (`lang="ja"` or `lang="en"`); a supplied label carries none, and an announcement marks each part with its own language (the template's words, the panel title, the move name).
|
|
33
|
+
A non-string label or a non-object `labels` throws a `TypeError`; an unknown key or template placeholder throws a `RangeError`.
|
|
34
|
+
- The panel header wraps at narrow widths: the title keeps the first line (at least 6em, truncated with an ellipsis) and the badge and the buttons wrap after it, aligned to the end.
|
|
35
|
+
- The header's buttons show while one of them holds the focus (`:focus-within`) as well as on hover, and always on devices that cannot hover.
|
|
36
|
+
- While a panel is maximized, everything else the layout renders is inert (also in the server render), and the resize handles are not rendered.
|
|
37
|
+
- New DOM hooks: `data-aqdd-layout`, `data-aqdd-columns`, `data-aqdd-panel`, `data-aqdd-drag-mode`, `data-aqdd-panel-header`, `data-aqdd-panel-title`, `data-aqdd-panel-controls`, `data-aqdd-panel-buttons`, `data-aqdd-panel-action` (`PANEL_ACTION_ATTRIBUTE`), `data-aqdd-custom-badge`, `data-aqdd-hidden-panels`, `data-aqdd-restore-panel`, `data-aqdd-empty-column`, `data-aqdd-hidden-panel-stand-in`, `data-aqdd-hidden-panel-badge`, `data-aqdd-drop-placeholder` (`hidden`, `shown`, `target`), `data-aqdd-announcer`. `ScrollRootElement` and `PanelDragHandle` are exported.
|
|
38
|
+
- Touch and pen drag, run by the layout itself with no wiring beyond the `onColumnPanelsChange` the move buttons already need: a long-press on a panel header (outside its buttons) lifts the panel after `longPressDelay` ms if the pointer stays within `slop` CSS px (the browser's own touch slop caps it); until then nothing is cancelled, so scrolling and taps that start on a header keep working, and no drag state or placeholder shows.
|
|
39
|
+
Once lifted, the header captures the pointer and its non-passive `touchmove` listener stops the touch's scrolling, a copy of the panel follows the pointer, the drop placeholder nearest to it in the column under it (over a layout nested in a panel, the column of this layout that holds it) is the target, re-measured whenever the stage, a host's ancestor or the page scrolls, and edge scrolling runs as for a mouse drag (see Fixed).
|
|
40
|
+
Releasing over a target asks for the arrangement through `DragDropLayout`'s `onColumnPanelsChange` (the one a mouse drop on that placeholder produces) and announces the move once it comes back; `useDragDropColumns`' `onPanelMove` and `onColumnPanelsChange` options are not called for it (they report the mouse drops the hook performs), so a host that observes them wires the same handler to the layout's prop. `pointercancel`, Escape, leaving the layout or releasing where nothing would move cancels.
|
|
41
|
+
- `touchDragConfig` (`TouchDragConfig`, default `DEFAULT_TOUCH_DRAG_CONFIG` = `{ longPressDelay: 500, slop: 10 }`); a value that is not a finite number of 0 or more throws a `RangeError` (a non-number a `TypeError`), and so does an unknown key.
|
|
42
|
+
- The labels `panelDragStarted` (`{title} を持ち上げました`) and `panelDragCancelled` (`{title} の移動を取り消しました`) announce a touch or pen drag's lift and cancel.
|
|
43
|
+
- `insertPanelAt` exposes the arrangement a drop produces; `useDragDropColumns`' drops and the touch drop both use it.
|
|
44
|
+
- For a reader who asks for reduced motion (`prefers-reduced-motion: reduce`), the hover lift of the panels and every transition the layout renders are left out (the utilities sit under `motion-safe:`, and the package stylesheets stop their transitions), and so is the reorder animation of moved and new panels.
|
|
45
|
+
- `CHANGELOG.md` ships in the package.
|
|
46
|
+
|
|
47
|
+
### Fixed
|
|
48
|
+
|
|
49
|
+
- A held Enter on a header button acts once: the repeated keydowns are cancelled, so maximize/restore and the drag-mode toggle no longer flip on every repeat.
|
|
50
|
+
- The maximize Escape ignores a keydown another handler already cancelled with `preventDefault()` (a menu or sheet that closed on that Escape), one that belongs to an input method composition (`isComposing`, or Safari's composition-ending keydown with `keyCode` 229), one pressed inside an open `<dialog>` and any while an auto or hint popover is open (the browser closes those itself), and cancels the Escape it uses.
|
|
51
|
+
- A `maximizedPanel` that names a hidden or unknown panel no longer listens to Escape or holds the scale at 1; like the inert cover, both apply only while the maximized panel is shown.
|
|
52
|
+
- The custom-drag clone and the scaled-down drag ghost carry the theme properties their panel resolves, so a host binding on an ancestor of the layout and a `.dark` class below `<html>` reach the drag image in `<body>`.
|
|
53
|
+
- The drop placeholder's icon is hidden from assistive technology (its text says the same).
|
|
54
|
+
- A double-click on a header button (a move, the drag mode, hide, maximize, restore or close) no longer toggles maximize as well; only a double-click on the header outside its buttons does.
|
|
55
|
+
- `EdgeScrollManager` scrolls toward the pointer's latest point while it stays in an edge band, so the direction and speed follow the pointer (a running scroll kept the point where it started).
|
|
56
|
+
- Edge scrolling during a drag (the mouse drags and the touch and pen drag alike) works when the layout's scroll root is not the element that scrolls or reaches beyond the viewport.
|
|
57
|
+
Its edge bands lie at the edges of the scroll root's visible part (its box clipped to the viewport); they lay at the box's own edges, out of reach below the fold.
|
|
58
|
+
Where the scroll root cannot scroll further that way — or does not scroll at all, as in a container of no definite height — the nearest ancestor a user can scroll that way (`overflow` `auto` or `scroll`) that hides part of the scroll root on that side scrolls instead, else the page while the viewport hides part of it,
|
|
59
|
+
each by no more than the hidden length, so the scroll stops once the scroll root's edge is in view; only the scroll root scrolled before, so nothing below the viewport could be reached. A scroll root that is entirely in view behaves as before.
|
|
60
|
+
`performScroll` with an element scrolls this way, axis by axis, and every edge scroll is instant (`behavior: "instant"`), so a host's `scroll-behavior: smooth` does not slow it.
|
package/README.md
CHANGED
|
@@ -10,13 +10,15 @@ Reusable Drag & Drop panel layout component package.
|
|
|
10
10
|
## Features
|
|
11
11
|
|
|
12
12
|
- Multi-column Drag & Drop layouts
|
|
13
|
-
- Panel reordering
|
|
14
|
-
-
|
|
13
|
+
- Panel reordering and movement across columns, by drag or without one: each panel header has move buttons, and Alt + Shift + an arrow moves the panel that holds the focus; every such move the host carries out is announced to assistive technology
|
|
14
|
+
- Touch and pen drag: a long-press on a panel header lifts the panel, and the layout runs it itself, so the host wires nothing for touch beyond the `onColumnPanelsChange` the move buttons already need
|
|
15
15
|
- Column width resizing
|
|
16
|
-
- Panel visibility toggle
|
|
16
|
+
- Panel visibility toggle, close and maximize
|
|
17
17
|
- Custom drag handler support
|
|
18
|
-
- FLIP animations
|
|
19
|
-
- Edge scrolling
|
|
18
|
+
- FLIP animations, left out for a reader who asks for reduced motion
|
|
19
|
+
- Edge scrolling during a drag, which also reaches content below the fold when the layout sits in a scrolling page or container
|
|
20
|
+
- Themable chrome (the panels, the hidden-panels bar and its restore buttons, the empty-column notice, a hidden panel's stand-in and the drop placeholders) through neutral `--aqdd-*` CSS custom properties, with light and `.dark` defaults that meet WCAG 2.2 AA
|
|
21
|
+
- Replaceable labels, each built-in label marked with its own language
|
|
20
22
|
- Debug overlay
|
|
21
23
|
|
|
22
24
|
## Installation
|
|
@@ -38,7 +40,7 @@ pnpm install
|
|
|
38
40
|
|
|
39
41
|
### CSS Integration
|
|
40
42
|
|
|
41
|
-
Structural styles (layouts, resize handles) are required for all hosts. Ships plain CSS without `:root` theme variables, Tailwind utilities, or preflight:
|
|
43
|
+
Structural styles (layouts, resize handles, the panel chrome's colours and the panel header's layout) are required for all hosts. Ships plain CSS without `:root` theme variables, Tailwind utilities, or preflight:
|
|
42
44
|
|
|
43
45
|
- `@aiquants/drag-drop-panels/css` (`./css` export, `dist/drag-drop-panels.css`) - Structural layout styles (import in JS or load per route via `?url`).
|
|
44
46
|
|
|
@@ -58,60 +60,225 @@ Tailwind utilities in JSX depend on the host environment:
|
|
|
58
60
|
@import "@aiquants/drag-drop-panels/styles/drag-drop-panels.standalone.css";
|
|
59
61
|
```
|
|
60
62
|
|
|
61
|
-
|
|
63
|
+
The dark defaults apply under an ancestor with the `.dark` class (such as `<html class="dark">`).
|
|
62
64
|
|
|
63
65
|
### Basic Usage Example
|
|
64
66
|
|
|
67
|
+
`useDragDropColumns` owns the arrangement and the drag state; `DragDropLayout` renders it.
|
|
68
|
+
|
|
65
69
|
```tsx
|
|
66
|
-
import { DragDropLayout } from "@aiquants/drag-drop-panels"
|
|
67
|
-
import type { ColumnConfig } from "@aiquants/drag-drop-panels"
|
|
70
|
+
import { DEFAULT_DEBUG_CONFIG, DragDropLayout, type ColumnConfigType, type PanelConfig, useDragDropColumns } from "@aiquants/drag-drop-panels"
|
|
68
71
|
import "@aiquants/drag-drop-panels/css"
|
|
69
|
-
import { useState } from "react"
|
|
70
72
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
id: "column-1",
|
|
76
|
-
key: "column1",
|
|
77
|
-
initialWidth: 400,
|
|
78
|
-
minWidth: 300,
|
|
79
|
-
maxWidth: 600,
|
|
80
|
-
resizable: true,
|
|
81
|
-
},
|
|
82
|
-
]
|
|
83
|
-
|
|
84
|
-
// Panel ID layout per column
|
|
85
|
-
const [columnPanels, setColumnPanels] = useState({
|
|
86
|
-
column1: ["panel1", "panel2"],
|
|
87
|
-
})
|
|
73
|
+
const columns: ColumnConfigType[] = [
|
|
74
|
+
{ id: "column-1", key: "column-1" },
|
|
75
|
+
{ id: "column-2", key: "column-2" },
|
|
76
|
+
]
|
|
88
77
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
})
|
|
78
|
+
const panels: PanelConfig[] = [
|
|
79
|
+
{ id: "panel-1", title: "Panel 1", component: MyPanel1 },
|
|
80
|
+
{ id: "panel-2", title: "Panel 2", component: MyPanel2 },
|
|
81
|
+
]
|
|
94
82
|
|
|
95
|
-
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
83
|
+
function MyApp() {
|
|
84
|
+
const dnd = useDragDropColumns({
|
|
85
|
+
initialColumnPanels: { "column-1": ["panel-1"], "column-2": ["panel-2"] },
|
|
86
|
+
initialPanelVisibility: { "panel-1": true, "panel-2": true },
|
|
87
|
+
debugConfig: DEFAULT_DEBUG_CONFIG,
|
|
88
|
+
})
|
|
100
89
|
|
|
101
90
|
return (
|
|
102
91
|
<DragDropLayout
|
|
103
92
|
columns={columns}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
93
|
+
panels={panels}
|
|
94
|
+
columnPanels={dnd.columnPanels}
|
|
95
|
+
onColumnPanelsChange={dnd.setColumnPanels}
|
|
96
|
+
panelVisibility={dnd.panelVisibility}
|
|
97
|
+
onPanelVisibilityChange={dnd.setPanelVisibility}
|
|
98
|
+
dragState={dnd.dragState}
|
|
99
|
+
dragOverPosition={dnd.dragOverPosition}
|
|
100
|
+
originalPanelPosition={dnd.originalPanelPosition}
|
|
101
|
+
dragModes={dnd.dragModes}
|
|
102
|
+
debugConfig={DEFAULT_DEBUG_CONFIG}
|
|
103
|
+
panelSizes={dnd.panelSizes}
|
|
104
|
+
customDrag={dnd.customDrag}
|
|
105
|
+
setPanelRef={dnd.setPanelRef}
|
|
106
|
+
onDragStart={dnd.handleDragStart}
|
|
107
|
+
onDragEnd={dnd.handleDragEnd}
|
|
108
|
+
onPlaceholderDragEnter={dnd.handlePlaceholderDragEnter}
|
|
109
|
+
onPlaceholderDragLeave={dnd.handlePlaceholderDragLeave}
|
|
110
|
+
onDragOverWithInsert={dnd.handleDragOverWithInsert}
|
|
111
|
+
onDropWithInsert={dnd.handleDropWithInsert}
|
|
112
|
+
onColumnDragLeave={dnd.handleColumnDragLeave}
|
|
113
|
+
onPanelMouseDown={dnd.handleMouseDown}
|
|
114
|
+
onToggleDragMode={dnd.toggleDragMode}
|
|
110
115
|
/>
|
|
111
116
|
)
|
|
112
117
|
}
|
|
113
118
|
```
|
|
114
119
|
|
|
120
|
+
### Observing Moves
|
|
121
|
+
|
|
122
|
+
Two callbacks report moves, and each move reaches exactly one of them:
|
|
123
|
+
|
|
124
|
+
| Move | Who changes the arrangement | Reported through |
|
|
125
|
+
| --- | --- | --- |
|
|
126
|
+
| A mouse drop (HTML5 drag or custom drag) | `useDragDropColumns`, in its own state | the hook's `onPanelMove` and `onColumnPanelsChange` options, in an animation frame |
|
|
127
|
+
| A header move button, Alt + Shift + an arrow, a touch or pen drop | you, answering the layout's request | `DragDropLayout`'s `onColumnPanelsChange` prop |
|
|
128
|
+
|
|
129
|
+
The layout's `onColumnPanelsChange` is a required prop. Wiring it to `dnd.setColumnPanels` (or to the state you pass back as `columnPanels`), as above, is all the move buttons and a touch screen need: the layout runs the touch and pen drag itself (see [Touch and Pen Drag](#touch-and-pen-drag)). The hook's options never see those moves, and the layout's prop never sees a mouse drop. To observe every change of the arrangement — to persist or log it — wire one handler to both, or observe `dnd.columnPanels`:
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
const dnd = useDragDropColumns({ ...options, onColumnPanelsChange: saveArrangement })
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<DragDropLayout
|
|
136
|
+
{...props}
|
|
137
|
+
columnPanels={dnd.columnPanels}
|
|
138
|
+
onColumnPanelsChange={(next) => {
|
|
139
|
+
dnd.setColumnPanels(next)
|
|
140
|
+
saveArrangement(next)
|
|
141
|
+
}}
|
|
142
|
+
/>
|
|
143
|
+
)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Theming
|
|
147
|
+
|
|
148
|
+
The package stylesheet reads these neutral custom properties. The package never declares them itself, so an ordinary declaration on an ancestor of the layout (or on a panel) overrides the default whatever its cascade layer, with no `!important`. Bind them to your own design tokens in your layout:
|
|
149
|
+
|
|
150
|
+
```css
|
|
151
|
+
.my-stage {
|
|
152
|
+
--aqdd-stage-padding-block: 21px;
|
|
153
|
+
--aqdd-stage-padding-inline: 13px;
|
|
154
|
+
--aqdd-column-gap: 13px;
|
|
155
|
+
--aqdd-panel-surface: var(--my-surface);
|
|
156
|
+
--aqdd-panel-border: var(--my-border);
|
|
157
|
+
--aqdd-panel-title-color: var(--my-text);
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
| Property | Styles | Default (light) | Default (`.dark` descendant) |
|
|
162
|
+
| --- | --- | --- | --- |
|
|
163
|
+
| `--aqdd-stage-padding-block` | Scroll root block padding | `1.5rem` | same |
|
|
164
|
+
| `--aqdd-stage-padding-inline` | Scroll root inline padding (one or two values) | `1rem 2rem`; `1.5rem 2.5rem` from `40rem` wide | same |
|
|
165
|
+
| `--aqdd-column-gap` | Gap between columns | `1.5rem` | same |
|
|
166
|
+
| `--aqdd-panel-surface` | Background of the panels, the hidden-panels bar, the empty-column notice and a hidden panel's stand-in | `rgb(255 255 255 / 0.8)`, `0.95` while maximized | `rgb(15 23 42 / 0.8)`, `0.95` while maximized |
|
|
167
|
+
| `--aqdd-panel-border` | Border colour of the same parts | `#e2e8f0` | `#334155` |
|
|
168
|
+
| `--aqdd-panel-title-color` | Panel title, and the text of the bar, the notice and the stand-in | `#334155` | `#e2e8f0` |
|
|
169
|
+
| `--aqdd-header-button-color` | Header button icons | `#475569` | `#94a3b8` |
|
|
170
|
+
| `--aqdd-header-button-hover-color` | Hovered header button icon | `#0f172a` (close: `#b91c1c`) | `#f1f5f9` (close: `#fca5a5`) |
|
|
171
|
+
| `--aqdd-header-button-hover-surface` | Hovered header button background | `#f1f5f9` | `#334155` |
|
|
172
|
+
| `--aqdd-custom-badge-color` | Custom drag mode badge, pressed toggle and the stand-in's hidden badge, text and icon | `#9a3412` | `#fdba74` |
|
|
173
|
+
| `--aqdd-custom-badge-surface` | Same, background | `#ffedd5` | `#431407` |
|
|
174
|
+
| `--aqdd-restore-button-color` | Restore buttons of the hidden-panels bar, text and icon | `#ffffff` | same |
|
|
175
|
+
| `--aqdd-restore-button-surface` | Same, background | `#0369a1` | same |
|
|
176
|
+
| `--aqdd-restore-button-hover-surface` | Same, background while hovered | `#075985` | same |
|
|
177
|
+
| `--aqdd-drop-placeholder-color` | Drop placeholders during a drag, text, icon and dashed border | `#075985` | `#bae6fd` |
|
|
178
|
+
| `--aqdd-drop-placeholder-surface` | Same, background | `rgb(240 249 255 / 0.9)` | `rgb(8 47 73 / 0.9)` |
|
|
179
|
+
| `--aqdd-drop-placeholder-target-surface` | Background of the placeholder a drop would land on | `#bae6fd` | `#075985` |
|
|
180
|
+
| `--aqdd-maximize-top` / `-right` / `-bottom` / `-left` | Insets of a maximized panel | `0px` | same |
|
|
181
|
+
|
|
182
|
+
The hidden-panels bar, the empty-column notice and a hidden panel's stand-in during a drag take the panel's surface, border and title colours, and the stand-in's "hidden" badge takes the custom badge colours, so one binding themes them all; the restore buttons and the drop placeholders take their own properties.
|
|
183
|
+
|
|
184
|
+
The defaults sit on the layout's root and on each panel, and a drop placeholder carries its own, so the exported `InsertPlaceholder` keeps its default colours (light and `.dark`) when you render it outside `DragDropLayout`; a property you set on any of its ancestors still overrides them.
|
|
185
|
+
|
|
186
|
+
The default icon colours reach at least 3:1 against the panel surface, and the badge, title, notice, restore button and placeholder text at least 4.5:1, in both schemes and over any page background. The dark defaults apply under an ancestor with the `.dark` class; a host that switches schemes another way sets the properties itself.
|
|
187
|
+
|
|
188
|
+
A drag image the layout appends to `<body>` (the custom-drag clone, and the ghost of a scaled-down layout) carries the values these properties resolve to on its panel, so a binding on an ancestor of the layout and a `.dark` class below `<html>` reach it too.
|
|
189
|
+
|
|
190
|
+
### Labels
|
|
191
|
+
|
|
192
|
+
Every user-visible label is replaceable through `labels`. A built-in label keeps its text and marks the element that bears it with the language it is written in (`lang="ja"` or `lang="en"`); a label you supply replaces it verbatim and carries no `lang`, so it is read in your page's language. A label that is not a string, or `labels` that is not an object, throws a `TypeError`; a key that names no label (a misspelling, or a shared dictionary that also holds other keys) throws a `RangeError` naming the key.
|
|
193
|
+
|
|
194
|
+
```tsx
|
|
195
|
+
<DragDropLayout
|
|
196
|
+
{...props}
|
|
197
|
+
labels={{
|
|
198
|
+
hidePanel: "Hide panel",
|
|
199
|
+
maximizePanel: "Maximize panel",
|
|
200
|
+
restorePanel: "Restore panel",
|
|
201
|
+
closePanel: "Close panel",
|
|
202
|
+
switchToCustomDragMode: "Switch to custom drag mode",
|
|
203
|
+
switchToNormalDragMode: "Back to normal drag mode",
|
|
204
|
+
movePanelToPreviousColumn: "Move to previous column",
|
|
205
|
+
movePanelToNextColumn: "Move to next column",
|
|
206
|
+
movePanelUp: "Move up",
|
|
207
|
+
movePanelDown: "Move down",
|
|
208
|
+
customDragModeBadge: "Custom",
|
|
209
|
+
panelMoved: "{title} moved to column {column}, position {position}",
|
|
210
|
+
panelMoveUnavailable: "{title} cannot {move}",
|
|
211
|
+
panelDragStarted: "Lifted {title}",
|
|
212
|
+
panelDragCancelled: "{title} stays where it was",
|
|
213
|
+
}}
|
|
214
|
+
/>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
`DEFAULT_DRAG_DROP_LAYOUT_LABELS` lists every key and its built-in text: the header buttons, the badge, the move announcements (`panelMoved`, `panelMoveUnavailable`, and a touch or pen drag's `panelDragStarted` and `panelDragCancelled`), the hidden panels list (`hiddenPanels`, `hiddenPanelBadge`, `hiddenPanelNotice`), `emptyColumn`, `dropZone`, `dropHere` and `resizeColumn`.
|
|
218
|
+
Six labels are templates (`TEMPLATE_PLACEHOLDERS`): `panelMoved` (`{title}`, `{column}`, `{position}`), `panelMoveUnavailable` (`{title}`, `{move}`), `panelDragStarted` (`{title}`), `panelDragCancelled` (`{title}`), `dropZone` (`{column}`) and `resizeColumn` (`{column}`); columns and positions count from 1, and a placeholder a key does not offer throws a `RangeError`.
|
|
219
|
+
|
|
220
|
+
An announcement keeps the language of every part: the template's own words and numbers are marked with the template's language, `{title}` (your panel title) is read in your page's language, and `{move}` keeps the language of the move button's label. Replacing only the template, or only a move name, therefore never reads one language in another's voice.
|
|
221
|
+
|
|
222
|
+
### Moving Panels Without a Drag
|
|
223
|
+
|
|
224
|
+
- Each panel header has four move buttons — to the previous or the next column, up or down in the column. A move the panel cannot make from where it sits is `aria-disabled` and stays focusable.
|
|
225
|
+
- Alt + Shift + ← / → / ↑ / ↓ moves the panel that holds the focus (the shortcuts are on the buttons' `aria-keyshortcuts`). They are ignored inside text inputs and editable content, in content the panel renders outside its own DOM (a portal, such as a menu in `<body>`), on a keydown another handler already cancelled, with Ctrl or ⌘, while a panel is maximized and during a drag; a held chord moves once. A shortcut the panel cannot follow is announced as unavailable.
|
|
226
|
+
- The layout asks for the move through `onColumnPanelsChange(next)` and announces it once you pass back, through `columnPanels`, an arrangement that moved that panel and nothing else — wherever you placed it, at once or later (wire `onColumnPanelsChange` to the state you pass in, such as `useDragDropColumns`' `setColumnPanels`). A request you decline is dropped without a word when the next arrangement also changes another panel or a drag starts; an arrangement that moves only that panel still counts as your answer.
|
|
227
|
+
- When a move to another column remounts the panel, the focus returns to the control that held it; after any announced move, the control in the panel that holds the focus is scrolled into view by the least distance.
|
|
228
|
+
- `movePanelInColumns`, `availablePanelMoves`, `shownPanelPlace` and `panelMoveOfKey` expose the same computation.
|
|
229
|
+
|
|
230
|
+
### Touch and Pen Drag
|
|
231
|
+
|
|
232
|
+
- A finger or a pen drags a panel by a long-press on its header (outside the header's buttons), whatever `dragMode` and `dragHandle` say — those choose how a mouse drags. The panel lifts once the pointer has rested there for `longPressDelay` ms (default 500) without moving more than `slop` CSS px (default 10). Moving further first, lifting the pointer sooner, a second finger or the browser taking the gesture over gives the press up, so scrolling and taps that start on a header keep working; until the panel lifts nothing is cancelled, no drag state shows and no drop placeholder appears.
|
|
233
|
+
- Because the browser keeps the gesture until the panel lifts, it starts scrolling and sends `pointercancel` — which gives the press up — as soon as the pointer moves beyond the browser's own touch slop (it varies by platform). A `slop` larger than the browser's therefore has no effect; only a smaller one narrows the tolerance.
|
|
234
|
+
- Once lifted, scrolling stops for that pointer (the header captures it and cancels its touch moves), a copy of the panel follows the pointer, and the drop placeholder nearest to it in the column under it is the target — over a layout nested in one of the panels, the column of this layout that holds it. The target is measured again whenever the stage, one of its ancestors or the page scrolls under a still finger, and [edge scrolling](#edge-scrolling) runs as for a mouse drag.
|
|
235
|
+
- Releasing over a target asks for the arrangement through the layout's `onColumnPanelsChange(next)` — the same arrangement a mouse drop on that placeholder produces (`insertPanelAt`) — and the layout announces the move once you pass it back, as it does for the move buttons; `useDragDropColumns`' `onPanelMove` and `onColumnPanelsChange` options are not called (see [Observing Moves](#observing-moves)).
|
|
236
|
+
- A `pointercancel`, Escape, leaving the layout, or releasing where the panel would stay where it is cancels without asking for anything. The lift and the cancel are announced (`panelDragStarted`, `panelDragCancelled`).
|
|
237
|
+
- A browser drag that a touch or a pen press starts (anywhere in a panel) is cancelled and never reaches your `onDragStart`: iPadOS, Android, ChromeOS and Windows start the browser's own drag from a touch or pen long-press, which would compete with this one, so touch and pen drag a panel only by a long-press on its header. A mouse's drag is unaffected, also from a pen the system reports as a mouse. Nothing starts from a maximized panel, during another drag or from a mouse press.
|
|
238
|
+
- Tune the timing with `touchDragConfig` (`DEFAULT_TOUCH_DRAG_CONFIG` holds the defaults); a value that is not a finite number of 0 or more, or an unknown key, throws.
|
|
239
|
+
|
|
240
|
+
```tsx
|
|
241
|
+
<DragDropLayout {...props} touchDragConfig={{ ...DEFAULT_TOUCH_DRAG_CONFIG, longPressDelay: 350 }} />
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Edge Scrolling
|
|
245
|
+
|
|
246
|
+
During any drag — a mouse's HTML5 or custom drag, or a touch or pen drag — a pointer within 80 px of an edge of the visible part of the layout's scroll root (`[data-aqdd-scroll-root]`, its box clipped to the viewport) scrolls toward that edge, faster the nearer it is, following the pointer as it moves within the band.
|
|
247
|
+
|
|
248
|
+
- The scroll root itself scrolls while it can move that way.
|
|
249
|
+
- Where it cannot — at its end, or not scrolling at all because the layout sits in a container of no definite height — the nearest ancestor you can scroll that way (`overflow` `auto` or `scroll`) that hides part of the scroll root on that side scrolls instead, else the page while the viewport hides part of it. Each scrolls by no more than the hidden length, so the scroll stops once the scroll root's edge is in view; a scroll root that is entirely in view never scrolls the page.
|
|
250
|
+
- Every step is instant, so a `scroll-behavior: smooth` on your page does not slow it.
|
|
251
|
+
|
|
252
|
+
### Panel Header
|
|
253
|
+
|
|
254
|
+
The title comes first; the custom drag-mode badge and the buttons follow it and wrap onto the next line, aligned to the end, when the title would get less than 6em (the title then truncates with an ellipsis), so at narrow widths and large text sizes every button stays inside the panel. The buttons show while the panel is hovered, while one of them holds the focus, and always on devices that cannot hover. A held Enter acts once on a header button.
|
|
255
|
+
|
|
256
|
+
### Maximize
|
|
257
|
+
|
|
258
|
+
With `onPanelMaximizeChange`, the header shows a maximize button and the panel named by `maximizedPanel` covers the viewport inside `--aqdd-maximize-*`. A double-click on the header outside its buttons toggles it, and an Escape keydown restores it (the layout cancels the Escape it uses) unless:
|
|
259
|
+
|
|
260
|
+
- a handler already called `preventDefault()` on that keydown;
|
|
261
|
+
- it belongs to an input method composition (`isComposing`, or Safari's composition-ending keydown with `keyCode` 229);
|
|
262
|
+
- it was pressed inside an open `<dialog>`, or a popover the browser hides on Escape (`popover` / `popover="auto"` / `popover="hint"`) is open — the browser closes those layers itself, which a cancelled Escape would prevent.
|
|
263
|
+
|
|
264
|
+
While a panel is maximized, everything else the layout renders is inert (also in the server render) and the scale is held at 1. All of this applies only while the named panel is shown: an id of a hidden or unknown panel covers nothing, keeps the scale and leaves Escape alone. Your own chrome is yours: keep it uncovered with `--aqdd-maximize-*`, or make it inert while a panel carries `data-maximized`.
|
|
265
|
+
|
|
266
|
+
### Reduced Motion
|
|
267
|
+
|
|
268
|
+
For a reader who asks for reduced motion (`prefers-reduced-motion: reduce`), the layout leaves out the hover lift of the panels, every transition it renders (its utilities sit under `motion-safe:`, and its stylesheets stop their transitions) and the reorder animation of moved and new panels. A drag image still follows the pointer.
|
|
269
|
+
|
|
270
|
+
### Scroll Root and Landmarks
|
|
271
|
+
|
|
272
|
+
The columns scroll inside a `div` marked `data-aqdd-scroll-root`, so the layout adds no landmark and can sit inside your `<main>`. Pass `scrollRootElement="main"` when your page renders no `<main>` of its own.
|
|
273
|
+
|
|
274
|
+
### DOM Hooks
|
|
275
|
+
|
|
276
|
+
Select the layout's elements by these attributes, never by class names:
|
|
277
|
+
|
|
278
|
+
- The layout: `data-aqdd-layout`, `data-aqdd-scroll-root`, `data-aqdd-columns`, `data-aqdd-hidden-panels` with `data-aqdd-restore-panel` (the panel id each restore button shows again), `data-aqdd-empty-column`, `data-aqdd-hidden-panel-stand-in` with `data-aqdd-hidden-panel-badge`, `data-aqdd-drop-placeholder` (`hidden`, `shown`, or `target` for the placeholder a drop would land on), and `data-aqdd-announcer`.
|
|
279
|
+
- A panel: `data-panel-id` with `data-aqdd-panel`, `data-aqdd-drag-mode` and `data-maximized`, `data-aqdd-panel-header`, `data-aqdd-panel-title`, `data-aqdd-panel-controls`, `data-aqdd-panel-buttons`, `data-aqdd-custom-badge` and `data-aqdd-scroll-area`.
|
|
280
|
+
- A header button: `data-aqdd-panel-action` (`move-previous-column`, `move-next-column`, `move-up`, `move-down`, `drag-mode`, `hide`, `maximize`, `restore`, `close`).
|
|
281
|
+
|
|
115
282
|
## Demo App
|
|
116
283
|
|
|
117
284
|
You can view the interactive demo in the web application.
|
|
@@ -145,16 +312,25 @@ You can view the interactive demo in the web application.
|
|
|
145
312
|
|
|
146
313
|
| Property | Type | Required | Description |
|
|
147
314
|
| --- | --- | --- | --- |
|
|
148
|
-
| `columns` | `ColumnConfig[]` | ✓ | Column configurations
|
|
149
|
-
| `
|
|
150
|
-
| `
|
|
315
|
+
| `columns` | `ColumnConfig[]` | ✓ | Column configurations, in display order |
|
|
316
|
+
| `panels` | `PanelConfig[]` | ✓ | Panel configurations |
|
|
317
|
+
| `columnPanels` | `Record<string, string[]>` | ✓ | Panel IDs per column key |
|
|
318
|
+
| `onColumnPanelsChange` | `(next: Record<string, string[]>) => void` | ✓ | Receives the next arrangement of every move the layout asks for itself: a move button, Alt + Shift + an arrow, a touch or pen drop (a mouse drop goes through `useDragDropColumns`' options instead; see [Observing Moves](#observing-moves)) |
|
|
151
319
|
| `panelVisibility` | `Record<string, boolean>` | ✓ | Panel visibility map |
|
|
152
|
-
| `onPanelVisibilityChange` | `(visibility: Record<string, boolean>) => void` | ✓ | Visibility change callback |
|
|
153
|
-
| `
|
|
154
|
-
| `
|
|
155
|
-
| `
|
|
156
|
-
| `
|
|
157
|
-
| `
|
|
320
|
+
| `onPanelVisibilityChange` | `(visibility: Record<string, boolean>) => void` | ✓ | Visibility change callback (hide button, hidden panels list) |
|
|
321
|
+
| `dragState`, `dragOverPosition`, `originalPanelPosition`, `dragModes`, `panelSizes`, `customDrag` | hook state | ✓ (`customDrag` optional) | Drag state from `useDragDropColumns` |
|
|
322
|
+
| `onDragStart`, `onDragEnd`, `onPlaceholderDragEnter`, `onPlaceholderDragLeave`, `onDragOverWithInsert`, `onDropWithInsert`, `onColumnDragLeave` | handlers | ✓ | Drag handlers from `useDragDropColumns` |
|
|
323
|
+
| `debugConfig` | `DebugConfig` | ✓ | Debug overlay settings |
|
|
324
|
+
| `onToggleDragMode` | `(panelId: string) => void` | - | Drag-mode toggle |
|
|
325
|
+
| `onPanelClose` | `(panelId: string) => void` | - | Shows a close button that removes the panel |
|
|
326
|
+
| `onPanelMaximizeChange` / `maximizedPanel` | `(panelId: string \| null) => void` / `string \| null` | - | Controlled maximize |
|
|
327
|
+
| `dragHandle` | `"panel" \| "header"` | - | Where a mouse's HTML5 drag starts (default `"panel"`); touch and pen always drag by a long-press on the header |
|
|
328
|
+
| `touchDragConfig` | `TouchDragConfig` | - | `{ longPressDelay, slop }` of the touch and pen drag (default `DEFAULT_TOUCH_DRAG_CONFIG`) |
|
|
329
|
+
| `scrollRootElement` | `"div" \| "main"` | - | Element of the scroll root (default `"div"`) |
|
|
330
|
+
| `labels` | `DragDropLayoutLabels` | - | Replacement labels |
|
|
331
|
+
| `panelClassName` | `string` | - | Classes appended to every panel frame |
|
|
332
|
+
| `scale` / `autoScale` / `onScaleChange` | `number` / `{ requiredWidth, minScale? }` / `(scale) => void` | - | Zoom out the layout |
|
|
333
|
+
| `enableResize`, `columnWidths`, `onResizeStart`, `onResize`, `onResizeEnd`, `isResizing`, `resizingColumn` | resize config | - | Column width resizing |
|
|
158
334
|
|
|
159
335
|
### ColumnConfig
|
|
160
336
|
|
|
@@ -174,9 +350,12 @@ interface ColumnConfig {
|
|
|
174
350
|
|
|
175
351
|
```typescript
|
|
176
352
|
interface PanelConfig {
|
|
177
|
-
|
|
178
|
-
|
|
353
|
+
id: string // Panel ID (referenced in columnPanels)
|
|
354
|
+
component: ComponentType // Panel component reference
|
|
355
|
+
title: string // Panel title
|
|
179
356
|
props?: Record<string, unknown> // Props passed to component
|
|
357
|
+
minHeight?: string // Minimum panel height (default 200px)
|
|
358
|
+
maxHeight?: string // Maximum panel height (default 500px)
|
|
180
359
|
}
|
|
181
360
|
```
|
|
182
361
|
|
|
@@ -199,6 +378,8 @@ Build outputs:
|
|
|
199
378
|
|
|
200
379
|
Detailed architectural specifications, API additions, and testing guides are available in the package `docs/` folder:
|
|
201
380
|
|
|
381
|
+
- [Touch and Pen Drag & Reduced Motion](docs/specs/2026.10.05%20%5BAI%5D%2010-touch-drag-and-reduced-motion.md)
|
|
382
|
+
- [Host Integration & Moving Panels Without a Drag](docs/specs/2026.10.05%20%5BAI%5D%2009-host-integration-and-keyboard-moves.md)
|
|
202
383
|
- [v0.8.0 / v0.8.1 Maximized Bounds & Test Suite Spec](docs/specs/2026.07.27%20%5BAI%5D%2008-v0.8.0-maximized-bounds-and-test-suite.md)
|
|
203
384
|
- [v0.6.0 Scale API Specification](docs/specs/2026.07.11%20%5BAI%5D%2007-v0.6.0-scale-api.md)
|
|
204
385
|
- [v0.5.0 API Additions Specification](docs/specs/2026.07.03%20%5BAI%5D%2006-v0.5.0-api-additions.md)
|
package/dist/DragDropLayout.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DragDropLayoutLabels } from "./drag-drop/labels";
|
|
2
|
+
import { TouchDragConfig } from "./drag-drop/touchDragConfig";
|
|
3
|
+
import { ColumnConfig, CustomDragState, DebugConfig, DragDropEventHandlers, DragDropStateUpdaters, DragOverPosition, DragState, OriginalPanelPosition, PanelConfig, PanelDragHandle, PanelSizeInfo, ResizeConfig, ScrollRootElement } from "./drag-drop/types";
|
|
2
4
|
export interface DragDropLayoutProps extends DragDropEventHandlers, DragDropStateUpdaters, ResizeConfig {
|
|
3
5
|
columns: ColumnConfig[];
|
|
4
6
|
panels: PanelConfig[];
|
|
@@ -20,5 +22,8 @@ export interface DragDropLayoutProps extends DragDropEventHandlers, DragDropStat
|
|
|
20
22
|
minScale?: number;
|
|
21
23
|
};
|
|
22
24
|
onScaleChange?: (scale: number) => void;
|
|
25
|
+
scrollRootElement?: ScrollRootElement;
|
|
26
|
+
labels?: DragDropLayoutLabels;
|
|
27
|
+
touchDragConfig?: TouchDragConfig;
|
|
23
28
|
}
|
|
24
|
-
export declare const DragDropLayout: ({ columns, panels, columnPanels, panelVisibility, onPanelVisibilityChange, dragState, dragOverPosition, originalPanelPosition, dragModes, onDragStart, onDragEnd, onPlaceholderDragEnter, onPlaceholderDragLeave, onDragOverWithInsert, onDropWithInsert, onColumnDragLeave, onPanelClick, onPanelMouseDown,
|
|
29
|
+
export declare const DragDropLayout: ({ columns, panels, columnPanels, onColumnPanelsChange, panelVisibility, onPanelVisibilityChange, dragState, dragOverPosition, originalPanelPosition, dragModes, onDragStart, onDragEnd, onPlaceholderDragEnter, onPlaceholderDragLeave, onDragOverWithInsert, onDropWithInsert, onColumnDragLeave, onPanelClick, onPanelMouseDown, debugConfig, panelSizes, setPanelRef, enableResize, columnWidths, onResizeStart, onResize, onResizeEnd, isResizing, resizingColumn, onPanelSizeInfoClick, onToggleDragMode, onPanelClose, onPanelMaximizeChange, customDrag, dragHandle, maximizedPanel, panelClassName, scale: propScale, autoScale, onScaleChange, scrollRootElement, labels, touchDragConfig, }: DragDropLayoutProps) => import("react").JSX.Element;
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { MouseEvent } from "react";
|
|
2
|
-
import { CustomDragState, DebugConfig, DragOverPosition, DragState, MousePosition, OriginalPanelPosition
|
|
2
|
+
import { CustomDragState, DebugConfig, DragOverPosition, DragState, MousePosition, OriginalPanelPosition } from "./types";
|
|
3
3
|
interface DebugPanelProps {
|
|
4
4
|
config: DebugConfig;
|
|
5
5
|
dragState: DragState;
|
|
6
6
|
dragOverPosition: DragOverPosition | null;
|
|
7
7
|
originalPanelPosition: OriginalPanelPosition | null;
|
|
8
8
|
mousePosition: MousePosition;
|
|
9
|
-
touchDragState: TouchDragState;
|
|
10
9
|
customDrag: CustomDragState;
|
|
11
10
|
}
|
|
12
|
-
export declare const DebugPanel: ({ config, dragState, dragOverPosition, originalPanelPosition, mousePosition,
|
|
11
|
+
export declare const DebugPanel: ({ config, dragState, dragOverPosition, originalPanelPosition, mousePosition, customDrag }: DebugPanelProps) => import("react").JSX.Element | null;
|
|
13
12
|
interface MousePositionDisplayProps {
|
|
14
13
|
config: DebugConfig;
|
|
15
14
|
mousePosition: MousePosition;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { default as React } from "react";
|
|
2
|
+
import { ResolvedLabel } from "./labels";
|
|
2
3
|
import { DebugConfig, DragOverPosition, OriginalPanelPosition } from "./types";
|
|
3
4
|
interface InsertPlaceholderProps {
|
|
4
5
|
index: number;
|
|
@@ -13,6 +14,7 @@ interface InsertPlaceholderProps {
|
|
|
13
14
|
isActive: boolean;
|
|
14
15
|
panelId: string;
|
|
15
16
|
};
|
|
17
|
+
label: ResolvedLabel;
|
|
16
18
|
}
|
|
17
19
|
export declare const InsertPlaceholder: React.NamedExoticComponent<InsertPlaceholderProps>;
|
|
18
20
|
interface DropZoneDebugOverlayProps {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ReactNode } from "react";
|
|
2
|
+
import { ResolvedLabel } from "./labels";
|
|
2
3
|
import { DebugConfig } from "./types";
|
|
3
4
|
export interface ResizeHandleProps {
|
|
4
5
|
columnId: string;
|
|
@@ -9,8 +10,9 @@ export interface ResizeHandleProps {
|
|
|
9
10
|
position?: "left" | "right";
|
|
10
11
|
className?: string;
|
|
11
12
|
debug?: DebugConfig;
|
|
13
|
+
label: ResolvedLabel;
|
|
12
14
|
}
|
|
13
|
-
export declare const ResizeHandle: ({ columnId, onResizeStart, onResize, onResizeEnd, isResizing, position, className, debug }: ResizeHandleProps) => import("react").JSX.Element;
|
|
15
|
+
export declare const ResizeHandle: ({ columnId, onResizeStart, onResize, onResizeEnd, isResizing, position, className, debug, label }: ResizeHandleProps) => import("react").JSX.Element;
|
|
14
16
|
export interface ResizableColumnProps {
|
|
15
17
|
columnId: string;
|
|
16
18
|
width: number;
|
|
@@ -23,6 +25,7 @@ export interface ResizableColumnProps {
|
|
|
23
25
|
resizable?: boolean;
|
|
24
26
|
className?: string;
|
|
25
27
|
debug?: DebugConfig;
|
|
28
|
+
resizeHandleLabel: ResolvedLabel;
|
|
26
29
|
children: ReactNode;
|
|
27
30
|
}
|
|
28
|
-
export declare const ResizableColumn: ({ columnId, width, minWidth, maxWidth, onResizeStart, onResize, onResizeEnd, isResizing, resizable, className, debug, children }: ResizableColumnProps) => import("react").JSX.Element;
|
|
31
|
+
export declare const ResizableColumn: ({ columnId, width, minWidth, maxWidth, onResizeStart, onResize, onResizeEnd, isResizing, resizable, className, debug, resizeHandleLabel, children }: ResizableColumnProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export interface DragImageOffset {
|
|
2
|
+
readonly x: number;
|
|
3
|
+
readonly y: number;
|
|
4
|
+
}
|
|
5
|
+
export declare const createPanelDragImage: (panel: HTMLElement, panelId: string, scale: number) => HTMLElement;
|
|
6
|
+
export declare const movePanelDragImage: (image: HTMLElement, x: number, y: number, offset: DragImageOffset, scale: number) => void;
|
|
@@ -5,7 +5,6 @@ export interface UseDragDropColumnsProps {
|
|
|
5
5
|
initialDragModes?: Record<string, "normal" | "custom">;
|
|
6
6
|
debugConfig: DebugConfig;
|
|
7
7
|
enableAnimation?: boolean;
|
|
8
|
-
enableTouch?: boolean;
|
|
9
8
|
onPanelMove?: (panelId: string, fromColumn: string, toColumn: string, insertIndex: number) => void;
|
|
10
9
|
onPanelVisibilityChange?: (panelId: string, isVisible: boolean) => void;
|
|
11
10
|
onDragModeChange?: (panelId: string, mode: "normal" | "custom") => void;
|
|
@@ -1,18 +1,16 @@
|
|
|
1
|
-
import { ClickState, CustomDragState, DragOverPosition, DragState, OriginalPanelPosition
|
|
1
|
+
import { ClickState, CustomDragState, DragOverPosition, DragState, OriginalPanelPosition } from "../types";
|
|
2
2
|
export declare const useDragDropState: () => {
|
|
3
3
|
dragState: DragState;
|
|
4
4
|
dragOverPosition: DragOverPosition | null;
|
|
5
5
|
originalPanelPosition: OriginalPanelPosition | null;
|
|
6
6
|
customDrag: CustomDragState;
|
|
7
7
|
clickState: ClickState;
|
|
8
|
-
touchDragState: TouchDragState;
|
|
9
8
|
isMouseDown: boolean;
|
|
10
9
|
setDragState: (state: DragState | ((prev: DragState) => DragState)) => void;
|
|
11
10
|
setDragOverPosition: (position: DragOverPosition | null) => void;
|
|
12
11
|
setOriginalPanelPosition: (position: OriginalPanelPosition | null) => void;
|
|
13
12
|
setCustomDrag: (state: CustomDragState | ((prev: CustomDragState) => CustomDragState)) => void;
|
|
14
13
|
setClickState: (state: ClickState | ((prev: ClickState) => ClickState)) => void;
|
|
15
|
-
setTouchDragState: (state: TouchDragState | ((prev: TouchDragState) => TouchDragState)) => void;
|
|
16
14
|
setIsMouseDown: (isDown: boolean) => void;
|
|
17
15
|
resetDragState: () => void;
|
|
18
16
|
resetClickState: () => void;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { MouseEvent as ReactMouseEvent, PointerEvent as ReactPointerEvent, RefObject } from "react";
|
|
2
|
+
import { TouchDragConfig } from "../touchDragConfig";
|
|
3
|
+
import { DragOverPosition, OriginalPanelPosition } from "../types";
|
|
4
|
+
export type TouchDrag = {
|
|
5
|
+
readonly panelId: string;
|
|
6
|
+
readonly over: DragOverPosition | null;
|
|
7
|
+
};
|
|
8
|
+
export type UsePanelTouchDragOptions = {
|
|
9
|
+
readonly config: TouchDragConfig;
|
|
10
|
+
readonly layoutRef: RefObject<HTMLElement | null>;
|
|
11
|
+
readonly scrollRootRef: RefObject<HTMLElement | null>;
|
|
12
|
+
readonly scale: number;
|
|
13
|
+
readonly canStart: boolean;
|
|
14
|
+
readonly panelElementOf: (panelId: string) => HTMLElement | null;
|
|
15
|
+
readonly originOf: (panelId: string) => OriginalPanelPosition | null;
|
|
16
|
+
readonly onLift: (panelId: string) => void;
|
|
17
|
+
readonly onDrop: (panelId: string, target: DragOverPosition) => void;
|
|
18
|
+
readonly onCancel: (panelId: string) => void;
|
|
19
|
+
};
|
|
20
|
+
export type PanelTouchDrag = {
|
|
21
|
+
readonly touchDrag: TouchDrag | null;
|
|
22
|
+
readonly pressHeader: (event: ReactPointerEvent<HTMLElement>, panelId: string) => void;
|
|
23
|
+
readonly holdContextMenu: (event: ReactMouseEvent<HTMLElement>) => void;
|
|
24
|
+
readonly bindHeader: (header: HTMLElement | null) => (() => void) | undefined;
|
|
25
|
+
readonly cancel: () => void;
|
|
26
|
+
};
|
|
27
|
+
export declare const usePanelTouchDrag: (options: UsePanelTouchDragOptions) => PanelTouchDrag;
|