Files
kandev/apps/web/components/settings/sidebar-layout-editor-dnd.ts
T
2026-09-20 00:09:02 +01:00

93 lines
2.9 KiB
TypeScript

import type { DragEndEvent } from "@dnd-kit/core";
import { moveSection, moveShortcut } from "@/lib/sidebar/layout-operations";
import type { SidebarLayout } from "@/lib/sidebar/layout-types";
import type { DraftOperation } from "./sidebar-layout-editor-types";
export type SidebarLayoutDragData = {
type: "node" | "shortcut" | "group";
nodeId: string;
shortcutId?: string;
};
export function nodeDragId(nodeId: string): string {
return `sidebar-node:${encodeURIComponent(nodeId)}`;
}
export function groupDropId(nodeId: string): string {
return `sidebar-group:${encodeURIComponent(nodeId)}`;
}
export function shortcutDragId(nodeId: string, shortcutId: string): string {
return `sidebar-shortcut:${encodeURIComponent(nodeId)}|${encodeURIComponent(shortcutId)}`;
}
export function moveShortcutToSection(
layout: SidebarLayout,
shortcutId: string,
sourceNodeId: string,
destinationNodeId: string,
): SidebarLayout {
const destination = layout.nodes.find((node) => node.id === destinationNodeId);
return moveShortcut(
layout,
shortcutId,
sourceNodeId,
destinationNodeId,
destination?.shortcuts?.length ?? 0,
);
}
function applyNodeDrag(
draft: SidebarLayout,
active: SidebarLayoutDragData,
over: SidebarLayoutDragData,
onApply: (operation: DraftOperation) => boolean,
): void {
if (over.type !== "node") return;
const from = draft.nodes.findIndex((node) => node.id === active.nodeId);
const to = draft.nodes.findIndex((node) => node.id === over.nodeId);
if (from >= 0 && to >= 0 && from !== to) {
onApply((current) => moveSection(current, active.nodeId, to));
}
}
function applyShortcutDrag(
draft: SidebarLayout,
active: SidebarLayoutDragData,
over: SidebarLayoutDragData,
onApply: (operation: DraftOperation) => boolean,
): void {
if (active.type !== "shortcut" || !active.shortcutId) return;
if (over.type === "shortcut") {
const destination = draft.nodes.find((node) => node.id === over.nodeId);
const position =
destination?.shortcuts?.findIndex((shortcut) => shortcut.id === over.shortcutId) ?? -1;
if (position >= 0) {
onApply((current) =>
moveShortcut(current, active.shortcutId!, active.nodeId, over.nodeId, position),
);
}
return;
}
if (over.type === "group") {
onApply((current) =>
moveShortcutToSection(current, active.shortcutId!, active.nodeId, over.nodeId),
);
}
}
export function handleSidebarLayoutDragEnd(
event: DragEndEvent,
draft: SidebarLayout,
onApply: (operation: DraftOperation) => boolean,
): void {
const active = event.active.data.current as SidebarLayoutDragData | undefined;
const over = event.over?.data.current as SidebarLayoutDragData | undefined;
if (!active || !over) return;
if (active.type === "node") {
applyNodeDrag(draft, active, over, onApply);
return;
}
applyShortcutDrag(draft, active, over, onApply);
}