diff options
Diffstat (limited to 'makima/frontend/src')
| -rw-r--r-- | makima/frontend/src/routes/document-directives.tsx | 86 |
1 files changed, 85 insertions, 1 deletions
diff --git a/makima/frontend/src/routes/document-directives.tsx b/makima/frontend/src/routes/document-directives.tsx index 044c8af..b89e841 100644 --- a/makima/frontend/src/routes/document-directives.tsx +++ b/makima/frontend/src/routes/document-directives.tsx @@ -22,6 +22,7 @@ import { pauseDirectiveContract, completeDirectiveContract, unlockDirectiveContract, + reorderDirectiveContract, createDirectiveTask, startDirective, pauseDirective, @@ -283,6 +284,30 @@ function DirectiveFolder({ ? selection.documentId : null; + // Drag-to-reorder state — only active contracts (draft/queued/active) are + // reorderable. The drag id lives in the folder so the drop target can read + // it from props. `dragOverId` powers the visual indicator on hover. + const [dragId, setDragId] = useState<string | null>(null); + const [dragOverId, setDragOverId] = useState<string | null>(null); + + const handleReorder = useCallback( + async (draggedId: string, targetDoc: Contract) => { + setDragId(null); + setDragOverId(null); + // No-op if dropping on self. + if (draggedId === targetDoc.id) return; + try { + await reorderDirectiveContract(draggedId, targetDoc.position); + await refresh(); + } catch (e) { + // Fail open — sidebar will refresh on next refreshNonce bump. + // eslint-disable-next-line no-console + console.error("Reorder failed", e); + } + }, + [refresh], + ); + return ( <div className="select-none"> {/* Directive folder header. Status is shown as a colored dot on the @@ -383,6 +408,22 @@ function DirectiveFolder({ directive={directive} selected={doc.id === selectedDocumentId} onSelect={() => onSelectDocument(directive.id, doc)} + draggable + onDragStart={() => setDragId(doc.id)} + onDragEnd={() => { + setDragId(null); + setDragOverId(null); + }} + onDragOver={() => { + if (dragId && dragId !== doc.id) setDragOverId(doc.id); + }} + onDragLeave={() => { + if (dragOverId === doc.id) setDragOverId(null); + }} + onDrop={() => { + if (dragId) void handleReorder(dragId, doc); + }} + dragOver={dragOverId === doc.id} /> <DocumentTasksFolder documentId={doc.id} @@ -460,6 +501,16 @@ interface DocumentRowProps { selected: boolean; onSelect: () => void; indent?: "normal" | "deep"; + // ----- Drag-to-reorder props (optional — only wired by the active list) --- + /** Whether this row participates in HTML5 drag (active docs only). */ + draggable?: boolean; + onDragStart?: () => void; + onDragEnd?: () => void; + onDragOver?: () => void; + onDragLeave?: () => void; + onDrop?: () => void; + /** True while a drag is hovering over this row — drives the drop indicator. */ + dragOver?: boolean; } function DocumentRow({ @@ -468,17 +519,50 @@ function DocumentRow({ selected, onSelect, indent = "normal", + draggable = false, + onDragStart, + onDragEnd, + onDragOver, + onDragLeave, + onDrop, + dragOver = false, }: DocumentRowProps) { const dot = DOC_STATUS_DOT[doc.status] ?? DOC_STATUS_DOT.draft; const padLeft = indent === "deep" ? "pl-[88px]" : "pl-14"; const name = `${fileLabel(doc, directive)}.md`; + // Drop-indicator: a top border accent on the hovered target row. + const dropAccent = dragOver + ? "border-t-2 border-t-emerald-400" + : "border-t-2 border-t-transparent"; + return ( <button type="button" onClick={onSelect} title={name} - className={`w-full text-left flex items-center gap-1.5 ${padLeft} pr-3 py-1 font-mono text-[11px] transition-colors ${ + draggable={draggable} + onDragStart={(e) => { + if (!draggable) return; + // Required by Firefox to actually start the drag. + e.dataTransfer.effectAllowed = "move"; + e.dataTransfer.setData("text/plain", doc.id); + onDragStart?.(); + }} + onDragEnd={onDragEnd} + onDragOver={(e) => { + if (!draggable) return; + e.preventDefault(); + e.dataTransfer.dropEffect = "move"; + onDragOver?.(); + }} + onDragLeave={onDragLeave} + onDrop={(e) => { + if (!draggable) return; + e.preventDefault(); + onDrop?.(); + }} + className={`w-full text-left flex items-center gap-1.5 ${padLeft} pr-3 py-1 font-mono text-[11px] transition-colors ${dropAccent} ${ selected ? "bg-[rgba(117,170,252,0.12)] text-white border-l-2 border-[#75aafc]" : "text-[#9bc3ff] hover:bg-[rgba(117,170,252,0.06)] border-l-2 border-transparent" |
