'use client';
/**
* Drag-and-drop ordering for the block editors (Journal Studio and the
* PageBuilder's essay editor), on the same dnd-kit setup as the photo order
* editor. Blocks have no identity of their own and both editors key them by
* position, so the sortable ids are positions too; the editors apply the
* move to their block array and re-render.
*
* The grip is the only activator: the cards are full of inputs, and a
* pointer that starts on a text field must keep selecting text. The up/down
* buttons stay as the keyboard alternative; the grip also takes Space and
* the arrow keys through dnd-kit's keyboard sensor.
*/
import { useMemo, type ReactNode } from 'react';
import {
DndContext,
closestCenter,
KeyboardSensor,
PointerSensor,
useSensor,
useSensors,
type DragEndEvent,
} from '@dnd-kit/core';
import {
SortableContext,
sortableKeyboardCoordinates,
useSortable,
verticalListSortingStrategy,
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { IconGripVertical } from './Icons';
const idAt = (index: number) => `block-${index}`;
interface SortableBlockListProps {
count: number;
onReorder: (from: number, to: number) => void;
children: ReactNode;
}
export function SortableBlockList({ count, onReorder, children }: SortableBlockListProps) {
const sensors = useSensors(
useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }),
);
const ids = useMemo(() => Array.from({ length: count }, (_, i) => idAt(i)), [count]);
const handleDragEnd = ({ active, over }: DragEndEvent) => {
if (!over || active.id === over.id) return;
const from = ids.indexOf(String(active.id));
const to = ids.indexOf(String(over.id));
if (from < 0 || to < 0) return;
onReorder(from, to);
};
return (