import { closestCenter, DndContext, DragEndEvent, DragOverEvent, DragOverlay, DragStartEvent, KeyboardSensor, PointerSensor, useDroppable, useSensor, useSensors, } from "@dnd-kit/core"; import { SortableContext, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy, } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; import { t, Trans } from "@lingui/macro"; import { ArrowCounterClockwise, DotsSixVertical, Plus, TrashSimple } from "@phosphor-icons/react"; import { defaultMetadata } from "@reactive-resume/schema"; import { Button, Portal, Tooltip } from "@reactive-resume/ui"; import { cn, LayoutLocator, moveItemInLayout, parseLayoutLocator, SortablePayload, } from "@reactive-resume/utils"; import get from "lodash.get"; import { useState } from "react"; import { useResumeStore } from "@/client/stores/resume"; import { getSectionIcon } from "../shared/section-icon"; type ColumnProps = { id: string; name: string; items: string[]; }; const Column = ({ id, name, items }: ColumnProps) => { const { setNodeRef } = useDroppable({ id }); return (

{name}

{items.map((section) => ( ))}
); }; type SortableSectionProps = { id: string; }; const SortableSection = ({ id }: SortableSectionProps) => { const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id, }); const style = { transition, opacity: isDragging ? 0.5 : 1, transform: CSS.Translate.toString(transform), }; return (
); }; type SectionProps = { id: string; isDragging?: boolean; }; const Section = ({ id, isDragging = false }: SectionProps) => { const name = useResumeStore((state) => get(state.resume.data.sections, `${id}.name`, id), ) as string; return (

{name}

); }; export const LayoutSection = () => { const setValue = useResumeStore((state) => state.setValue); const layout = useResumeStore((state) => state.resume.data.metadata.layout); const [activeId, setActiveId] = useState(null); const sensors = useSensors( useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates, }), ); const onDragStart = ({ active }: DragStartEvent) => { setActiveId(active.id as string); }; const onDragCancel = () => { setActiveId(null); }; const onDragEvent = ({ active, over }: DragOverEvent | DragEndEvent) => { if (!over || !active.data.current) return; const currentPayload = active.data.current.sortable as SortablePayload | null; const current = parseLayoutLocator(currentPayload); if (active.id === over.id) return; if (!over.data.current) { const [page, column] = (over.id as string).split(".").map(Number); const target = { page, column, section: 0 } as LayoutLocator; const newLayout = moveItemInLayout(current, target, layout); setValue("metadata.layout", newLayout); return; } const targetPayload = over.data.current.sortable as SortablePayload | null; const target = parseLayoutLocator(targetPayload); const newLayout = moveItemInLayout(current, target, layout); setValue("metadata.layout", newLayout); }; const onDragEnd = (event: DragEndEvent) => { onDragEvent(event); setActiveId(null); }; const onAddPage = () => { const layoutCopy = JSON.parse(JSON.stringify(layout)) as string[][][]; layoutCopy.push([[], []]); setValue("metadata.layout", layoutCopy); }; const onRemovePage = (page: number) => { const layoutCopy = JSON.parse(JSON.stringify(layout)) as string[][][]; layoutCopy[0][0].push(...layoutCopy[page][0]); // Main layoutCopy[0][1].push(...layoutCopy[page][1]); // Sidebar layoutCopy.splice(page, 1); setValue("metadata.layout", layoutCopy); }; const onResetLayout = () => { const layoutCopy = JSON.parse(JSON.stringify(defaultMetadata.layout)) as string[][][]; // Loop through all pages and columns, and get any sections that start with "custom." // These should be appended to the first page of the new layout. const customSections: string[] = []; layout.forEach((page) => { page.forEach((column) => { customSections.push(...column.filter((section) => section.startsWith("custom."))); }); }); if (customSections.length > 0) layoutCopy[0][0].push(...customSections); setValue("metadata.layout", layoutCopy); }; return (
{getSectionIcon("layout")}

{t`Layout`}

{/* Pages */} {layout.map((page, pageIndex) => { const mainIndex = `${pageIndex}.0`; const sidebarIndex = `${pageIndex}.1`; const main = page[0]; const sidebar = page[1]; return (

Page {pageIndex + 1}

{pageIndex !== 0 && ( )}
); })} {activeId &&
}
); };