import { createMemo } from "solid-js"; import type { DeckStore, DraggingState } from "./deckStore"; import type { LayerConfig } from "../types"; export interface LayerClickInfo { layerProp: string; gridX: number; gridY: number; } export interface LayerInteractionHandlers { onLayerClick: (index: number, e: MouseEvent) => void; onFrameMouseDown: ( action: "drag" | "resize-corner" | "resize-edge", anchor?: "nw" | "ne" | "sw" | "se", edge?: "n" | "s" | "e" | "w", e?: MouseEvent, ) => void; onCardMouseMove: (e: MouseEvent, cardEl: HTMLElement) => void; onCardMouseUp: () => void; onCardClick: (e: MouseEvent, cardEl: HTMLElement) => void; getOverlappingLayers: (gridX: number, gridY: number) => LayerConfig[]; } export function useLayerInteraction( store: DeckStore, ): LayerInteractionHandlers { let clickStack: { gridX: number; gridY: number; layers: number[]; currentIndex: number; } | null = null; const currentLayerConfigs = () => store.state.activeSide === "front" ? store.state.frontLayerConfigs.filter((l) => l.visible) : store.state.backLayerConfigs.filter((l) => l.visible); const allLayerConfigs = () => store.state.activeSide === "front" ? store.state.frontLayerConfigs : store.state.backLayerConfigs; const calculateGridCoords = (e: MouseEvent, cardEl: HTMLElement) => { const dims = store.state.dimensions; if (!dims) return { gridX: 1, gridY: 1 }; const rect = cardEl.getBoundingClientRect(); const mmToPxRatio = rect.width / dims.cardWidth; const offsetX = (e.clientX - rect.left) / mmToPxRatio; const offsetY = (e.clientY - rect.top) / mmToPxRatio; const gridX = Math.floor((offsetX - dims.gridOriginX) / dims.cellWidth) + 1; const gridY = Math.floor((offsetY - dims.gridOriginY) / dims.cellHeight) + 1; return { gridX: Math.max(1, Math.min(dims.gridW, gridX)), gridY: Math.max(1, Math.min(dims.gridH, gridY)), }; }; const isPointInLayer = ( gridX: number, gridY: number, layer: LayerConfig, ): boolean => { return ( gridX >= layer.x1 && gridX <= layer.x2 && gridY >= layer.y1 && gridY <= layer.y2 ); }; const getOverlappingLayers = ( gridX: number, gridY: number, ): LayerConfig[] => { return currentLayerConfigs().filter((layer) => isPointInLayer(gridX, gridY, layer), ); }; const handleLayerClick = (index: number, e: MouseEvent) => { e.stopPropagation(); if (!store.state.isEditing || store.state.fixed) return; const currentlySelected = store.state.selectedLayer; if (currentlySelected === index) { store.actions.setSelectedLayer(null); } else { store.actions.setSelectedLayer(index); } clickStack = null; }; const handleCardClick = (e: MouseEvent, cardEl: HTMLElement) => { if (store.state.draggingState) return; if (!store.state.isEditing || store.state.fixed) return; const { gridX, gridY } = calculateGridCoords(e, cardEl); const overlapping = getOverlappingLayers(gridX, gridY); if (overlapping.length === 0) { store.actions.setSelectedLayer(null); clickStack = null; return; } const allConfigs = allLayerConfigs(); const overlappingIndices = overlapping .map((l) => allConfigs.findIndex((c) => c.prop === l.prop && c.visible)) .filter((i) => i !== -1); if (overlappingIndices.length === 0) { store.actions.setSelectedLayer(null); clickStack = null; return; } if ( clickStack && clickStack.gridX === gridX && clickStack.gridY === gridY ) { clickStack.currentIndex = (clickStack.currentIndex + 1) % overlappingIndices.length; const nextIndex = overlappingIndices[clickStack.currentIndex]; store.actions.setSelectedLayer(nextIndex); } else { clickStack = { gridX, gridY, layers: overlappingIndices, currentIndex: 0, }; store.actions.setSelectedLayer(overlappingIndices[0]); } }; const handleFrameMouseDown = ( action: "drag" | "resize-corner" | "resize-edge", anchor?: "nw" | "ne" | "sw" | "se", edge?: "n" | "s" | "e" | "w", e?: MouseEvent, ) => { if (!store.state.isEditing || store.state.fixed) return; if (store.state.selectedLayer === null) return; if (e) e.stopPropagation(); const index = store.state.selectedLayer; const layer = allLayerConfigs()[index]; if (!layer) return; const startX = e?.clientX ?? 0; const startY = e?.clientY ?? 0; const draggingState: DraggingState = { layer: index, action, anchor, edge, startX, startY, startGrid: { x1: layer.x1, y1: layer.y1, x2: layer.x2, y2: layer.y2, }, }; store.actions.setDraggingState(draggingState); }; const handleCardMouseMove = (e: MouseEvent, cardEl: HTMLElement) => { const dragging = store.state.draggingState; if (!dragging) return; const dims = store.state.dimensions; if (!dims) return; const rect = cardEl.getBoundingClientRect(); const mmToPxRatio = rect.width / dims.cardWidth; const deltaX = e.clientX - dragging.startX; const deltaY = e.clientY - dragging.startY; const deltaMmX = deltaX / mmToPxRatio; const deltaMmY = deltaY / mmToPxRatio; const deltaGridX = deltaMmX / dims.cellWidth; const deltaGridY = deltaMmY / dims.cellHeight; const startGrid = dragging.startGrid; if (dragging.action === "drag") { store.actions.moveLayer( dragging.layer, Math.round(deltaGridX), Math.round(deltaGridY), startGrid, ); } else if (dragging.action === "resize-corner" && dragging.anchor) { store.actions.resizeLayerCorner( dragging.layer, dragging.anchor, Math.round(deltaGridX), Math.round(deltaGridY), startGrid, ); } else if (dragging.action === "resize-edge" && dragging.edge) { const delta = dragging.edge === "n" || dragging.edge === "s" ? Math.round(deltaGridY) : Math.round(deltaGridX); store.actions.resizeLayerEdge( dragging.layer, dragging.edge, delta, startGrid, ); } }; const handleCardMouseUp = () => { if (store.state.draggingState) { const dragging = store.state.draggingState; store.actions.setDraggingState(null); const newLayerConfig = allLayerConfigs()[dragging.layer]; if (newLayerConfig && dragging.startGrid) { store.actions.setDraggingState({ ...dragging, startGrid: { x1: newLayerConfig.x1, y1: newLayerConfig.y1, x2: newLayerConfig.x2, y2: newLayerConfig.y2, }, }); store.actions.setDraggingState(null); } } }; return { onLayerClick: handleLayerClick, onFrameMouseDown: handleFrameMouseDown, onCardMouseMove: handleCardMouseMove, onCardMouseUp: handleCardMouseUp, onCardClick: handleCardClick, getOverlappingLayers, }; }