feat(dashboard): make widget groups reorderable via drag-and-drop
This commit is contained in:
@@ -1,37 +1,60 @@
|
||||
import React from 'react';
|
||||
import { Box, Typography, IconButton } from '@mui/material';
|
||||
import { Delete as DeleteIcon } from '@mui/icons-material';
|
||||
import { Delete as DeleteIcon, DragIndicator as DragIndicatorIcon } from '@mui/icons-material';
|
||||
import { useDroppable } from '@dnd-kit/core';
|
||||
import { useSortable } from '@dnd-kit/sortable';
|
||||
import { CSS } from '@dnd-kit/utilities';
|
||||
|
||||
interface WidgetGroupProps {
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
gridColumn?: string;
|
||||
groupId?: string;
|
||||
sortableId?: string;
|
||||
editMode?: boolean;
|
||||
onDelete?: () => void;
|
||||
}
|
||||
|
||||
function WidgetGroup({ title, children, gridColumn, groupId, editMode, onDelete }: WidgetGroupProps) {
|
||||
const { setNodeRef, isOver } = useDroppable({
|
||||
function WidgetGroup({ title, children, groupId, sortableId, editMode, onDelete }: WidgetGroupProps) {
|
||||
const { setNodeRef: setDropRef, isOver } = useDroppable({
|
||||
id: groupId ? `group-${groupId}` : 'group-default',
|
||||
disabled: !editMode,
|
||||
});
|
||||
|
||||
const {
|
||||
setNodeRef: setSortableRef,
|
||||
attributes,
|
||||
listeners,
|
||||
transform,
|
||||
transition,
|
||||
isDragging,
|
||||
} = useSortable({
|
||||
id: sortableId ?? '',
|
||||
disabled: !editMode || !sortableId,
|
||||
});
|
||||
|
||||
// Count non-null children to hide empty groups
|
||||
const validChildren = React.Children.toArray(children).filter(Boolean);
|
||||
|
||||
if (validChildren.length === 0 && !editMode) return null;
|
||||
|
||||
const sortableStyle = sortableId ? {
|
||||
transform: CSS.Transform.toString(transform),
|
||||
transition,
|
||||
opacity: isDragging ? 0.4 : 1,
|
||||
zIndex: isDragging ? 1 : undefined,
|
||||
} : {};
|
||||
|
||||
return (
|
||||
<Box
|
||||
ref={setNodeRef}
|
||||
ref={setSortableRef}
|
||||
style={sortableStyle}
|
||||
sx={{
|
||||
position: 'relative',
|
||||
borderRadius: 2,
|
||||
p: 2.5,
|
||||
pt: 3.5,
|
||||
gridColumn,
|
||||
gridColumn: '1 / -1',
|
||||
bgcolor: isOver && editMode ? 'rgba(25, 118, 210, 0.04)' : 'rgba(0, 0, 0, 0.02)',
|
||||
border: '1px solid',
|
||||
borderColor: isOver && editMode ? 'primary.light' : 'rgba(0, 0, 0, 0.04)',
|
||||
@@ -40,6 +63,7 @@ function WidgetGroup({ title, children, gridColumn, groupId, editMode, onDelete
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
ref={setDropRef}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: -9,
|
||||
@@ -53,6 +77,16 @@ function WidgetGroup({ title, children, gridColumn, groupId, editMode, onDelete
|
||||
borderRadius: 1,
|
||||
}}
|
||||
>
|
||||
{editMode && sortableId && (
|
||||
<IconButton
|
||||
size="small"
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
sx={{ p: 0, mr: 0.25, color: 'text.disabled', cursor: 'grab', '&:active': { cursor: 'grabbing' } }}
|
||||
>
|
||||
<DragIndicatorIcon sx={{ fontSize: 14 }} />
|
||||
</IconButton>
|
||||
)}
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: '0.6875rem',
|
||||
|
||||
Reference in New Issue
Block a user