Add drag-to-reorder for fundraising grid views
Sidebar view list is now drag-reorderable (HTML5 DnD mirroring the column-reorder idiom: moveViewBefore + draggingViewId/dragOverViewId). Order persists via the grid page's existing autosave (views is already in its snapshot + deps), the same path rename/delete use; no backend change. Render-smoke green.
This commit is contained in:
+54
-1
@@ -190,6 +190,14 @@
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.sub-nav-item.view-dragging {
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.sub-nav-item.view-drag-over {
|
||||
box-shadow: inset 0 2px 0 #4a9adf;
|
||||
}
|
||||
|
||||
.column-header-inner {
|
||||
position: relative;
|
||||
display: flex;
|
||||
@@ -11172,6 +11180,25 @@
|
||||
const [activeGridView, setActiveGridView] = useState('view-main');
|
||||
const [gridUiAction, setGridUiAction] = useState(null);
|
||||
const [sidebarContextMenu, setSidebarContextMenu] = useState(null);
|
||||
const [draggingViewId, setDraggingViewId] = useState(null);
|
||||
const [dragOverViewId, setDragOverViewId] = useState(null);
|
||||
|
||||
// Drag-reorder the saved grid views in the sidebar. Mirrors moveColumnBefore:
|
||||
// mutate gridViews only — the grid page's autosave (which has `views` in its
|
||||
// snapshot + deps) persists the new order to /api/fundraising/state, same path
|
||||
// rename/delete already use.
|
||||
const moveViewBefore = (fromId, targetId) => {
|
||||
if (!fromId || !targetId || fromId === targetId) return;
|
||||
setGridViews((prev) => {
|
||||
const fromIndex = prev.findIndex((v) => v.id === fromId);
|
||||
const targetIndex = prev.findIndex((v) => v.id === targetId);
|
||||
if (fromIndex < 0 || targetIndex < 0 || fromIndex === targetIndex) return prev;
|
||||
const next = [...prev];
|
||||
const [moved] = next.splice(fromIndex, 1);
|
||||
next.splice(targetIndex, 0, moved);
|
||||
return next;
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem(GRID_VIEW_STORAGE_KEY, JSON.stringify(gridViews));
|
||||
@@ -11308,12 +11335,38 @@
|
||||
{gridViews.map((v) => (
|
||||
<button
|
||||
key={v.id}
|
||||
className={`sub-nav-item ${activeGridView === v.id ? 'active' : ''}`}
|
||||
className={`sub-nav-item ${activeGridView === v.id ? 'active' : ''} ${draggingViewId === v.id ? 'view-dragging' : ''} ${dragOverViewId === v.id ? 'view-drag-over' : ''}`.trim()}
|
||||
draggable={true}
|
||||
onClick={() => {
|
||||
setPage('fundraising-grid');
|
||||
setActiveGridView(v.id);
|
||||
}}
|
||||
onContextMenu={(e) => openSidebarContextMenu(e, { kind: 'view', viewId: v.id })}
|
||||
onDragStart={(e) => {
|
||||
setDraggingViewId(v.id);
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
e.dataTransfer.setData('text/plain', v.id);
|
||||
}}
|
||||
onDragOver={(e) => {
|
||||
e.preventDefault();
|
||||
if (!draggingViewId || draggingViewId === v.id) return;
|
||||
setDragOverViewId(v.id);
|
||||
e.dataTransfer.dropEffect = 'move';
|
||||
}}
|
||||
onDragLeave={() => {
|
||||
setDragOverViewId((cur) => (cur === v.id ? null : cur));
|
||||
}}
|
||||
onDrop={(e) => {
|
||||
e.preventDefault();
|
||||
const droppedId = e.dataTransfer.getData('text/plain') || draggingViewId;
|
||||
moveViewBefore(droppedId, v.id);
|
||||
setDragOverViewId(null);
|
||||
setDraggingViewId(null);
|
||||
}}
|
||||
onDragEnd={() => {
|
||||
setDragOverViewId(null);
|
||||
setDraggingViewId(null);
|
||||
}}
|
||||
>
|
||||
• {v.name}
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user