"use client"; import * as React from "react"; import { FolderSimpleIcon, PlusIcon, StackPlusIcon, } from "@phosphor-icons/react"; import { Button, PanelContentSurface, PanelIconButton, PanelSurface, Popover, PopoverContent, PopoverTrigger, PrimitiveArrowIcon, stopPanelHeaderButtonPointerDown, } from "@repo/ui"; import type { ToolcraftPanelState, ToolcraftState } from "../../state/types"; import { getToolcraftLayerDepth, getToolcraftVisibleLayerRows, isToolcraftLayerVisibleInTree, } from "./layer-tree"; import { canMoveLayerIntoGroup, getLayerSubtreeEndIndex, } from "./layers-panel-reorder"; import { LayerRow } from "./layers-panel-row"; import { PanelContainer } from "../panel-host/panel-host"; import { useToolcraftPanelBinding } from "../panel-host/use-toolcraft-panel-binding"; import type { PanelPlacement, PanelStateChange } from "../panel-host/panel-host-types"; import { useLayersPanelDragController } from "./use-layers-panel-drag-controller"; import { useToolcraftCommittedSelector } from "../app-shell/toolcraft-selectors"; import { useToolcraftDispatch } from "../app-shell/use-toolcraft"; export type LayersPanelProps = { className?: string; framed?: boolean; groupCreation?: boolean; onPanelStateChange?: PanelStateChange; panelPlacement?: PanelPlacement; panelState?: ToolcraftPanelState; }; const selectLayers = (state: ToolcraftState) => state.layers; const selectMediaAssets = (state: ToolcraftState) => state.mediaAssets; const selectSelectedLayerId = (state: ToolcraftState) => state.selectedLayerId; const selectLayersEnabled = (state: ToolcraftState) => state.schema.panels.layers; function cn(...classNames: Array): string { return classNames.filter(Boolean).join(" "); } function AddLayerPicker({ groupCreation, onAddGroup, onAddLayer, }: { groupCreation: boolean; onAddGroup: () => void; onAddLayer: () => void; }): React.JSX.Element { const [open, setOpen] = React.useState(false); const addLayer = (): void => { onAddLayer(); setOpen(false); }; const addGroup = (): void => { onAddGroup(); setOpen(false); }; if (!groupCreation) { return ( ); } return ( } > ); } function LayersPanelHeader({ collapsed, groupCreation, onAddGroup, onAddLayer, onToggleCollapsed, }: { collapsed: boolean; groupCreation: boolean; onAddGroup: () => void; onAddLayer: () => void; onToggleCollapsed: () => void; }): React.JSX.Element { return (

Layers

{collapsed ? null : ( )}
); } export function LayersPanel({ className, framed = true, groupCreation = true, onPanelStateChange, panelPlacement, panelState, }: LayersPanelProps): React.JSX.Element | null { const dispatch = useToolcraftDispatch(); const layers = useToolcraftCommittedSelector(selectLayers); const mediaAssets = useToolcraftCommittedSelector(selectMediaAssets); const selectedLayerId = useToolcraftCommittedSelector(selectSelectedLayerId); const layersEnabled = useToolcraftCommittedSelector(selectLayersEnabled); const panelBinding = useToolcraftPanelBinding({ onPanelStateChange, panelId: "layers", panelState, }); const resolvedPanelState = panelBinding.panelState; const listRef = React.useRef(null); const collapsed = resolvedPanelState.collapsed ?? false; const placement = panelPlacement ?? (framed ? "frame" : "surface"); const visibleLayers = React.useMemo( () => getToolcraftVisibleLayerRows(layers), [layers], ); const { dragState, dropTargetGroupId, getInsertIndicatorTarget, getRowDragHandlers, highlightedGroupId, insertTarget, } = useLayersPanelDragController({ dispatch, layers, listRef, visibleLayers, }); if (!layersEnabled || resolvedPanelState.hidden) { return null; } const updateCollapsed = (nextCollapsed: boolean): void => { panelBinding.onPanelStateChange({ collapsed: nextCollapsed }); }; const addLayer = (): void => { const selectedLayer = layers.find((layer) => layer.id === selectedLayerId); const parentGroupId = selectedLayer?.kind === "group" ? selectedLayer.id : selectedLayer?.parentGroupId; const insertIndex = selectedLayer ? selectedLayer.kind === "group" ? getLayerSubtreeEndIndex(layers, selectedLayer.id) : layers.findIndex((layer) => layer.id === selectedLayer.id) + 1 : layers.length; dispatch({ insertIndex, layer: { kind: "layer", parentGroupId }, type: "layers.add", }); }; const addGroup = (): void => { const selectedLayer = layers.find((layer) => layer.id === selectedLayerId); const parentGroupId = selectedLayer?.kind === "group" ? selectedLayer.id : selectedLayer?.parentGroupId; const insertIndex = selectedLayer ? selectedLayer.kind === "group" ? getLayerSubtreeEndIndex(layers, selectedLayer.id) : layers.findIndex((layer) => layer.id === selectedLayer.id) + 1 : layers.length; dispatch({ insertIndex, layer: { kind: "group", parentGroupId }, type: "layers.add", }); }; const panelSurface = ( updateCollapsed(!collapsed)} /> {collapsed ? null : (
    {visibleLayers.map((layer) => { const depth = getToolcraftLayerDepth(layers, layer); const dragHandlers = getRowDragHandlers(layer); const isDragging = dragState?.layerId === layer.id && dragState.dragging; const isReorderDragging = dragState?.dragging === true; const isVisible = isToolcraftLayerVisibleInTree(layers, layer); const hasMedia = mediaAssets.some((asset) => asset.layerId === layer.id); const insertIndicatorTarget = insertTarget ? getInsertIndicatorTarget(insertTarget) : null; const rowInsertPlacement = insertIndicatorTarget?.layerId === layer.id ? insertIndicatorTarget.placement : undefined; return ( dispatch({ layerId: layer.id, type: "layers.delete" })} onPointerCancel={dragHandlers.onPointerCancel} onPointerDown={dragHandlers.onPointerDown} onPointerMove={dragHandlers.onPointerMove} onPointerUp={dragHandlers.onPointerUp} onRename={(name) => dispatch({ layerId: layer.id, name, type: "layers.rename", }) } onSelect={() => dispatch({ layerId: layer.id, type: "layers.select" })} onToggleCollapsed={() => dispatch({ layerId: layer.id, type: "layers.toggleCollapsed" }) } onToggleVisibility={() => dispatch({ layerId: layer.id, type: "layers.toggleVisibility" }) } /> ); })} {visibleLayers.length === 0 ? (
  • No layers
  • ) : null}
)}
); if (placement === "surface") { return panelSurface; } return ( {panelSurface} ); }