import { commandIds } from "@commands/ids"; import type { Dispatch } from "@commands/dispatcher"; import type { ImageDocument } from "@core/document"; import type { ArtboardId, LayerId } from "@core/id"; import type { Layer } from "@core/layer"; import type { KeybindEvent } from "./keyboard"; export type LayerInfo = { artboardId: ArtboardId; parentGroupId?: LayerId; layer: Layer; }; export type LayerDropTarget = { artboardId: ArtboardId; layer: Layer; }; export type DeleteSelectionState = { artboardId?: ArtboardId; layerIds: LayerId[]; }; export function handleDeleteSelectionKey(options: { event: KeybindEvent; selection: DeleteSelectionState; dispatch: Dispatch }): boolean { if (options.event.altKey || options.event.ctrlKey || options.event.metaKey) return false; if (options.event.key !== "Backspace" && options.event.key !== "Delete") return false; for (const layerId of options.selection.layerIds) options.dispatch(commandIds.documentRemoveLayer, { layerId }); if (options.selection.layerIds.length === 0 && options.selection.artboardId) { options.dispatch(commandIds.documentRemoveArtboard, { id: options.selection.artboardId }); } return true; } export function resolveLayerDrop(options: { document: ImageDocument; sourceLayerId: LayerId; target: LayerDropTarget; verticalRatio: number; }): { layerId: LayerId; toArtboardId: ArtboardId; toParentGroupId?: LayerId; toIndex: number } | undefined { const targetInfo = findLayerInfoInDocument(options.document, options.target.layer.id); const sourceInfo = findLayerInfoInDocument(options.document, options.sourceLayerId); if (!targetInfo || !sourceInfo || options.sourceLayerId === options.target.layer.id) return undefined; if (isDescendantLayer(sourceInfo.layer, options.target.layer.id)) return undefined; const verticalRatio = Math.max(0, Math.min(1, options.verticalRatio)); const dropIntoGroup = options.target.layer.type === "group" && verticalRatio >= 0.33 && verticalRatio <= 0.66; if (dropIntoGroup) { return { layerId: options.sourceLayerId, toArtboardId: options.target.artboardId, toParentGroupId: options.target.layer.id, toIndex: options.target.layer.children.length, }; } const siblings = targetInfo.parentGroupId ? findGroup(options.document, targetInfo.parentGroupId)?.children : options.document.artboards.find((artboard) => artboard.id === targetInfo.artboardId)?.layers; if (!siblings) return undefined; const targetIndex = siblings.findIndex((layer) => layer.id === options.target.layer.id); const sourceIndex = sourceInfo.parentGroupId === targetInfo.parentGroupId && sourceInfo.artboardId === targetInfo.artboardId ? siblings.findIndex((layer) => layer.id === options.sourceLayerId) : -1; const rawIndex = verticalRatio < 0.5 ? targetIndex : targetIndex + 1; const adjustedIndex = sourceIndex >= 0 && sourceIndex < rawIndex ? rawIndex - 1 : rawIndex; return { layerId: options.sourceLayerId, toArtboardId: targetInfo.artboardId, toParentGroupId: targetInfo.parentGroupId, toIndex: adjustedIndex, }; } export function findLayerInfoInDocument(document: ImageDocument, layerId?: LayerId): LayerInfo | undefined { if (!layerId) return undefined; for (const artboard of document.artboards) { const found = findLayerInfo(artboard.layers, layerId, artboard.id); if (found) return found; } return undefined; } export function findGroup(document: ImageDocument, groupId: LayerId): Extract | undefined { const info = findLayerInfoInDocument(document, groupId); return info?.layer.type === "group" ? info.layer : undefined; } function findLayerInfo(layers: Layer[], layerId: LayerId, artboardId: ArtboardId, parentGroupId?: LayerId): LayerInfo | undefined { for (const layer of layers) { if (layer.id === layerId) return { artboardId, parentGroupId, layer }; if (layer.type === "group") { const found = findLayerInfo(layer.children, layerId, artboardId, layer.id); if (found) return found; } } return undefined; } function isDescendantLayer(layer: Layer, descendantLayerId: LayerId): boolean { if (layer.type !== "group") return false; for (const child of layer.children) { if (child.id === descendantLayerId || isDescendantLayer(child, descendantLayerId)) return true; } return false; }