Add toolbox spaces and recover from stale asset bundles
All checks were successful
Deploy Sokko G / deploy (push) Successful in 18s

This commit is contained in:
Shinuwa 2026-09-29 21:31:49 +02:00
parent 0959c6740b
commit 0acf5ecf21
30 changed files with 1252 additions and 179 deletions

View file

@ -1,6 +1,6 @@
// Rôle : répartit et réorganise les modules d'une toolbox dans leurs colonnes.
// Rôle : répartit les modules de l'espace actif dans leurs colonnes et cibles de dépôt.
import { useMemo } from "react";
import { useGroupedReorder } from "../../../hooks/useGroupedReorder.js";
import { encodeSpaceColumn } from "../toolboxSpaceUtils.js";
import { ModuleShell } from "./ModuleShell.jsx";
const MODULE_COLUMN_IDS = ["left", "right"];
@ -10,97 +10,29 @@ function getModulesByIds(modules, ids) {
return (Array.isArray(ids) ? ids : []).map((id) => moduleMap.get(id)).filter(Boolean);
}
function getModuleColumnId(moduleId, twoColumnOrder) {
return twoColumnOrder?.[1]?.includes(moduleId) ? MODULE_COLUMN_IDS[1] : MODULE_COLUMN_IDS[0];
}
function moveId(ids, sourceId, targetId, placement = "before") {
const nextIds = ids.filter((id) => id !== sourceId);
const targetIndex = nextIds.indexOf(targetId);
if (targetIndex < 0) return ids;
nextIds.splice(placement === "after" ? targetIndex + 1 : targetIndex, 0, sourceId);
return nextIds;
}
function moveModuleInOneColumnOrder(order, operation) {
if (!operation.sourceId || !operation.targetId || operation.sourceId === operation.targetId) return order;
return {
...order,
one: moveId(order.one, operation.sourceId, operation.targetId, operation.placement)
};
}
function moveModuleInTwoColumnOrder(order, operation) {
const columns = [
[...(order.two?.[0] || [])],
[...(order.two?.[1] || [])]
];
const sourceColumnIndex = columns.findIndex((column) => column.includes(operation.sourceId));
if (sourceColumnIndex < 0) return order;
const targetColumnIndex = operation.targetType === "item"
? columns.findIndex((column) => column.includes(operation.targetId))
: MODULE_COLUMN_IDS.indexOf(operation.targetGroup);
if (targetColumnIndex < 0) return order;
const [movedId] = columns[sourceColumnIndex].splice(columns[sourceColumnIndex].indexOf(operation.sourceId), 1);
if (operation.targetType === "item") {
const targetIndex = columns[targetColumnIndex].indexOf(operation.targetId);
if (targetIndex < 0) return order;
columns[targetColumnIndex].splice(operation.placement === "after" ? targetIndex + 1 : targetIndex, 0, movedId);
} else {
columns[targetColumnIndex].splice(operation.placement === "after" ? columns[targetColumnIndex].length : 0, 0, movedId);
}
return { ...order, two: columns };
}
export function ToolboxModules({ toolbox, moduleColumns, context, scrollToModuleId = "", onModuleScrolled, onRename, onUpdateModule, onDelete, onModuleOrderChange }) {
const twoColumnOrder = useMemo(() => toolbox.moduleOrder?.two || [[], []], [toolbox.moduleOrder?.two]);
const orderedModules = useMemo(() => getModulesByIds(toolbox.modules, toolbox.moduleOrder?.one || []), [toolbox.modules, toolbox.moduleOrder?.one]);
export function ToolboxModules({ toolbox, space, moduleColumns, context, reorder, spacesText, scrollToModuleId = "", onModuleScrolled, onRename, onUpdateModule, onDelete, onMoveToSpace }) {
const twoColumnOrder = useMemo(() => space.moduleOrder?.two || [[], []], [space.moduleOrder?.two]);
const orderedModules = useMemo(() => getModulesByIds(toolbox.modules, space.moduleOrder?.one || []), [toolbox.modules, space.moduleOrder?.one]);
const columns = useMemo(() => [
getModulesByIds(toolbox.modules, twoColumnOrder[0]),
getModulesByIds(toolbox.modules, twoColumnOrder[1])
], [toolbox.modules, twoColumnOrder]);
const {
itemReorder,
getItemProps,
getGroupBoundaryProps,
isItemDragging,
isItemDropTarget,
isBoundaryDropTarget,
getDropPlacement
} = useGroupedReorder({
namespace: "toolbox-modules",
items: toolbox.modules,
getItemId: (module) => module.id,
getItemGroup: (module) => moduleColumns === 2 ? getModuleColumnId(module.id, twoColumnOrder) : "",
getEffectiveGroup: (module) => moduleColumns === 2 ? getModuleColumnId(module.id, twoColumnOrder) : "",
getParentId: () => toolbox.id,
reorderFeatures: {
item: {
groupChange: moduleColumns === 2,
boundaryDrop: moduleColumns === 2
}
},
onItemMove: (operation) => {
const nextOrder = moduleColumns === 2
? moveModuleInTwoColumnOrder(toolbox.moduleOrder, operation)
: moveModuleInOneColumnOrder(toolbox.moduleOrder, operation);
onModuleOrderChange(nextOrder);
},
hierarchy: { enabled: true, stickyParents: true }
});
if (moduleColumns === 1) {
return (
<section className="modules" data-toolbox-id={toolbox.id} data-layout-cols={moduleColumns}>
{!orderedModules.length && <div className="empty toolbox-space-empty nds-empty-state"><p>{spacesText.emptyText}</p></div>}
{orderedModules.map((module) => (
<ModuleShell
key={module.id}
toolbox={toolbox}
module={module}
context={context}
reorder={{ itemReorder, getItemProps, isItemDragging, isItemDropTarget, getDropPlacement }}
itemGroup=""
reorder={reorder}
itemGroup={encodeSpaceColumn(space.id, "one")}
spaces={toolbox.spaces}
activeSpaceId={space.id}
moveText={spacesText.moveLabel}
onMoveToSpace={onMoveToSpace}
scrollIntoView={module.id === scrollToModuleId}
onScrolledIntoView={onModuleScrolled}
onRename={onRename}
@ -112,15 +44,15 @@ export function ToolboxModules({ toolbox, moduleColumns, context, scrollToModule
);
}
const hasEmptyColumn = toolbox.modules.length > 1 && columns.some((modules) => modules.length === 0);
const hasEmptyColumn = orderedModules.length > 1 && columns.some((modules) => modules.length === 0);
return (
<section className="modules" data-toolbox-id={toolbox.id} data-layout-cols={moduleColumns}>
{columns.map((modules, index) => (
<div className={`module-column ${hasEmptyColumn && !modules.length ? "is-empty-warning" : ""}`} key={MODULE_COLUMN_IDS[index]}>
<div
className={`module-column-boundary-drop-zone ${isBoundaryDropTarget({ groupId: MODULE_COLUMN_IDS[index], parentId: toolbox.id, placement: "before" }) ? "is-drop-target" : ""}`}
{...getGroupBoundaryProps({ groupId: MODULE_COLUMN_IDS[index], parentId: toolbox.id, placement: "before" })}
className={`module-column-boundary-drop-zone ${reorder.isBoundaryDropTarget({ groupId: encodeSpaceColumn(space.id, MODULE_COLUMN_IDS[index]), parentId: toolbox.id, placement: "before" }) ? "is-drop-target" : ""}`}
{...reorder.getGroupBoundaryProps({ groupId: encodeSpaceColumn(space.id, MODULE_COLUMN_IDS[index]), parentId: toolbox.id, placement: "before" })}
/>
{modules.map((module) => (
<ModuleShell
@ -128,8 +60,12 @@ export function ToolboxModules({ toolbox, moduleColumns, context, scrollToModule
toolbox={toolbox}
module={module}
context={context}
reorder={{ itemReorder, getItemProps, isItemDragging, isItemDropTarget, getDropPlacement }}
itemGroup={MODULE_COLUMN_IDS[index]}
reorder={reorder}
itemGroup={encodeSpaceColumn(space.id, MODULE_COLUMN_IDS[index])}
spaces={toolbox.spaces}
activeSpaceId={space.id}
moveText={spacesText.moveLabel}
onMoveToSpace={onMoveToSpace}
scrollIntoView={module.id === scrollToModuleId}
onScrolledIntoView={onModuleScrolled}
onRename={onRename}
@ -138,11 +74,12 @@ export function ToolboxModules({ toolbox, moduleColumns, context, scrollToModule
/>
))}
<div
className={`module-column-boundary-drop-zone ${isBoundaryDropTarget({ groupId: MODULE_COLUMN_IDS[index], parentId: toolbox.id, placement: "after" }) ? "is-drop-target" : ""}`}
{...getGroupBoundaryProps({ groupId: MODULE_COLUMN_IDS[index], parentId: toolbox.id, placement: "after" })}
className={`module-column-boundary-drop-zone ${reorder.isBoundaryDropTarget({ groupId: encodeSpaceColumn(space.id, MODULE_COLUMN_IDS[index]), parentId: toolbox.id, placement: "after" }) ? "is-drop-target" : ""}`}
{...reorder.getGroupBoundaryProps({ groupId: encodeSpaceColumn(space.id, MODULE_COLUMN_IDS[index]), parentId: toolbox.id, placement: "after" })}
/>
</div>
))}
{!orderedModules.length && <div className="empty toolbox-space-empty nds-empty-state"><p>{spacesText.emptyText}</p></div>}
</section>
);
}