Keep keyboard drag targets aligned with movement axis
This commit is contained in:
1 parent
ca2fcb4016
commit
6afffe9f08
3 files changed
+37
-2
No files matched your search
@@ -23,10 +23,24 @@ export function createDndManager(options: DndManagerOptions): DndManager {
|
||||
let activeIds: Array<string | number> = [];
|
||||
let generation = 0;
|
||||
let destroyed = false;
|
||||
let keyboardAxis: "x" | "y" | undefined;
|
||||
const keyboardAligned = (id: string | number, target: HTMLElement) => {
|
||||
if (!keyboardAxis) return true;
|
||||
const element = entries.get(id)?.current.element;
|
||||
const a = element?.getBoundingClientRect?.(), b = target.getBoundingClientRect?.();
|
||||
if (!a || !b) return true;
|
||||
return keyboardAxis === "x" ? Math.min(a.bottom, b.bottom) > Math.max(a.top, b.top) : Math.min(a.right, b.right) > Math.max(a.left, b.left);
|
||||
};
|
||||
manager.monitor.addEventListener("dragmove", event => {
|
||||
const code = (event.nativeEvent as KeyboardEvent | undefined)?.code;
|
||||
if (code === "ArrowLeft" || code === "ArrowRight") keyboardAxis = "x";
|
||||
else if (code === "ArrowUp" || code === "ArrowDown") keyboardAxis = "y";
|
||||
});
|
||||
|
||||
manager.monitor.addEventListener("dragstart", (event) => {
|
||||
if (destroyed) return;
|
||||
generation++;
|
||||
keyboardAxis = undefined;
|
||||
const source = event.operation.source;
|
||||
if (!source) return;
|
||||
activeIds = [source.id];
|
||||
@@ -101,7 +115,7 @@ export function createDndManager(options: DndManagerOptions): DndManager {
|
||||
if (destroyed) throw new Error("Cannot register a drop zone after manager destruction.");
|
||||
if (entries.has(registration.id) || zones.has(registration.id)) throw new Error(`Duplicate draggable or drop zone id ${String(registration.id)}.`);
|
||||
if (!Number.isSafeInteger(registration.index) || registration.index < 0) throw new Error("Drop zone index must be a non-negative safe integer.");
|
||||
const droppable = new Droppable({ id: registration.id, element: registration.element, accept: registration.accepts, disabled: registration.disabled }, manager);
|
||||
const droppable = new Droppable({ id: registration.id, element: registration.element, accept: source => keyboardAligned(source.id, registration.element) && (!registration.accepts || registration.accepts.some(type => type === source.type)) && (options.accept?.([source.id], { group: registration.group, index: registration.index }) ?? true), disabled: registration.disabled }, manager);
|
||||
zones.set(registration.id, { droppable, location: { group: registration.group, index: registration.index } });
|
||||
return () => {
|
||||
if (zones.get(registration.id)?.droppable !== droppable) return;
|
||||
@@ -122,6 +136,7 @@ export function createDndManager(options: DndManagerOptions): DndManager {
|
||||
handle: registration.handle,
|
||||
type: registration.type,
|
||||
accept: (source) => {
|
||||
if (!keyboardAligned(source.id, registration.element)) return false;
|
||||
const origin = entries.get(source.id)?.current.element;
|
||||
// A nested ancestor overlaps its own descendants and must not steal
|
||||
// their next keyboard/pointer target just because its box is larger.
|
||||
|
||||
Reference in new issue
Block a user