'use client' import { DndContext, KeyboardSensor, PointerSensor, closestCenter, useSensor, useSensors, type DragEndEvent, } from '@dnd-kit/core' import { SortableContext, arrayMove, rectSortingStrategy, sortableKeyboardCoordinates, useSortable, } from '@dnd-kit/sortable' import { CSS } from '@dnd-kit/utilities' import { GripVerticalIcon, Loader2Icon } from 'lucide-react' import { useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { Button } from '@/components/ui/button' import { Dialog, DialogContent, DialogDescription, DialogTitle } from '@/components/ui/dialog' import { useScene } from '@/hooks/useScene' import { getGetPageThumbnailUrl } from '@/lib/api/default/default' import { applyOp } from '@/lib/io/scene' import { ops } from '@/lib/ops' const THUMBNAIL_DPR = typeof window !== 'undefined' ? Math.min(Math.ceil(window.devicePixelRatio || 1), 3) : 2 type PageManagerDialogProps = { open: boolean onOpenChange: (open: boolean) => void } export function PageManagerDialog({ open, onOpenChange }: PageManagerDialogProps) { const { t } = useTranslation() const { scene } = useScene() const pagesMap = scene?.pages const pages = useMemo(() => (pagesMap ? Object.values(pagesMap) : []), [pagesMap]) const [orderedIds, setOrderedIds] = useState([]) const [saving, setSaving] = useState(false) const pagesById = useMemo(() => Object.fromEntries(pages.map((p) => [p.id, p])), [pages]) useEffect(() => { if (open) setOrderedIds(pages.map((p) => p.id)) }, [open, pages]) const hasChanges = useMemo(() => { if (orderedIds.length !== pages.length) return false return orderedIds.some((id, i) => id !== pages[i]?.id) }, [orderedIds, pages]) const sensors = useSensors( useSensor(PointerSensor, { activationConstraint: { distance: 5 } }), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }), ) const handleDragEnd = useCallback((event: DragEndEvent) => { const { active, over } = event if (!over || active.id === over.id) return setOrderedIds((prev) => { const oldIndex = prev.indexOf(String(active.id)) const newIndex = prev.indexOf(String(over.id)) return arrayMove(prev, oldIndex, newIndex) }) }, []) const handleSave = useCallback(async () => { if (!hasChanges) { onOpenChange(false) return } setSaving(true) try { const prevOrder = pages.map((p) => p.id) await applyOp(ops.reorderPages(orderedIds, prevOrder)) onOpenChange(false) } finally { setSaving(false) } }, [hasChanges, orderedIds, pages, onOpenChange]) return (
{t('navigator.pageManager.title')} {t('navigator.pageManager.description')}
{orderedIds.map((id, index) => ( ))}
) } function SortablePageCard({ id, index, name }: { id: string; index: number; name?: string }) { const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id, }) const style: React.CSSProperties = { transform: CSS.Transform.toString(transform), transition, zIndex: isDragging ? 10 : undefined, } return (
) } function PageCard({ id, index, name, dragging, }: { id: string index: number name?: string dragging?: boolean }) { const src = `${getGetPageThumbnailUrl(id)}?size=${200 * THUMBNAIL_DPR}` return (
{name
{index + 1}
) }