Unify toolbox drag and drop reorder behavior
All checks were successful
Deploy Sokko G / deploy (push) Successful in 7s

This commit is contained in:
Shinuwa 2026-08-01 20:00:47 +02:00
parent a6d35c6e6b
commit 1879b245fb
17 changed files with 1258 additions and 929 deletions

View file

@ -3,7 +3,7 @@ import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
import { createPortal } from "react-dom";
import { CompactDropdown } from "../../../components/CompactDropdown.jsx";
import { Icon } from "../../../components/Icon.jsx";
import { usePointerReorder } from "../../../hooks/usePointerReorder.js";
import { useGroupedReorder } from "../../../hooks/useGroupedReorder.js";
import { lockBodyScroll } from "../../../utils/bodyScrollLock.js";
import { CalculatorModule } from "./CalculatorModule.jsx";
import { ChecklistModule } from "./ChecklistModule.jsx";
@ -180,14 +180,18 @@ export function ToolboxModules({ toolbox, moduleColumns, context, onRename, onUp
const moduleElementsRef = useRef(new Map());
const moduleIdSignature = useMemo(() => toolbox.modules.map((module) => module.id).join("|"), [toolbox.modules]);
const {
draggingId: draggingModuleId,
dropTarget,
startDrag: startModuleDrag
} = usePointerReorder({
targetSelector: ".module",
getTargetId: (target) => target.dataset.moduleId,
canDropOn: (target) => target.dataset.toolboxId === toolbox.id,
onMove
itemReorder,
getItemProps,
isItemDragging,
isItemDropTarget,
getDropPlacement
} = useGroupedReorder({
namespace: "toolbox-modules",
items: toolbox.modules,
getItemId: (module) => module.id,
getParentId: () => toolbox.id,
onItemMove: (operation) => onMove(operation.sourceId, operation.targetId, operation.placement),
hierarchy: { enabled: true, stickyParents: true }
});
useEffect(() => {
@ -246,9 +250,7 @@ export function ToolboxModules({ toolbox, moduleColumns, context, onRename, onUp
toolbox={toolbox}
module={module}
context={context}
draggingModuleId={draggingModuleId}
dropTarget={dropTarget}
onDragStart={startModuleDrag}
reorder={{ itemReorder, getItemProps, isItemDragging, isItemDropTarget, getDropPlacement }}
onRename={onRename}
onUpdateModule={onUpdateModule}
onDelete={onDelete}
@ -273,9 +275,7 @@ export function ToolboxModules({ toolbox, moduleColumns, context, onRename, onUp
toolbox={toolbox}
module={module}
context={context}
draggingModuleId={draggingModuleId}
dropTarget={dropTarget}
onDragStart={startModuleDrag}
reorder={{ itemReorder, getItemProps, isItemDragging, isItemDropTarget, getDropPlacement }}
onRename={onRename}
onUpdateModule={onUpdateModule}
onDelete={onDelete}
@ -289,27 +289,24 @@ export function ToolboxModules({ toolbox, moduleColumns, context, onRename, onUp
);
}
function ModuleShell({ toolbox, module, context, draggingModuleId, dropTarget, onDragStart, onRename, onUpdateModule, onDelete, registerModuleElement }) {
function ModuleShell({ toolbox, module, context, reorder, onRename, onUpdateModule, onDelete, registerModuleElement }) {
const definition = MODULE_COMPONENTS[module.type] || MODULE_COMPONENTS.notepad;
const Component = definition.Component;
const label = definition.label || module.type;
const [editing, setEditing] = useState(false);
const scrollable = definition.scrollable && module.scrollable === true;
const isDragging = draggingModuleId === module.id;
const isDropTarget = dropTarget.id === module.id;
const className = [
"module",
isDragging ? "is-dragging" : "",
isDropTarget ? "is-drop-target" : "",
isDropTarget && dropTarget.placement === "after" ? "drop-after" : "",
reorder.isItemDragging(module.id) ? "is-dragging" : "",
reorder.isItemDropTarget(module.id) ? "is-drop-target" : "",
reorder.getDropPlacement("item", module.id) === "after" ? "drop-after" : "",
scrollable ? "is-scrollable" : ""
].filter(Boolean).join(" ");
return (
<article
className={className}
data-toolbox-id={toolbox.id}
data-module-id={module.id}
{...reorder.getItemProps({ itemId: module.id, parentId: toolbox.id })}
ref={(element) => registerModuleElement?.(module.id, element)}
>
<header>
@ -317,7 +314,7 @@ function ModuleShell({ toolbox, module, context, draggingModuleId, dropTarget, o
<button
className="module-drag-handle"
type="button"
onPointerDown={(event) => onDragStart(event, module.id)}
onPointerDown={(event) => reorder.itemReorder.startDrag(event, module.id)}
aria-label={`Déplacer ${module.title || label}`}
title="Déplacer"
>