feat: unify list reordering, fix DnD cross-group bug, and use existing icons

This commit is contained in:
MasonDye 2025-07-01 20:58:04 +08:00
parent bd712f506f
commit db13c312ab
5 changed files with 73 additions and 29 deletions

View File

@ -11,8 +11,7 @@ import {
} from "../../../Common/StyledComponents.tsx";
import Add from "../../../Icons/Add.tsx";
import Dismiss from "../../../Icons/Dismiss.tsx";
import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
import ArrowDown from "../../../Icons/ArrowDown.tsx";
import { DndProvider, useDrag, useDrop } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
@ -140,10 +139,21 @@ function DraggableEmojiRow({
<Dismiss fontSize={"small"} />
</IconButton>
<IconButton size="small" onClick={() => moveRow(i, i - 1)} disabled={isFirst}>
<KeyboardArrowUpIcon fontSize="small" />
<ArrowDown
sx={{
width: "18px",
height: "18px",
transform: "rotate(180deg)",
}}
/>
</IconButton>
<IconButton size="small" onClick={() => moveRow(i, i + 1)} disabled={isLast}>
<KeyboardArrowDownIcon fontSize="small" />
<ArrowDown
sx={{
width: "18px",
height: "18px",
}}
/>
</IconButton>
</NoWrapCell>
</TableRow>

View File

@ -38,8 +38,7 @@ import Dismiss from "../../../../Icons/Dismiss.tsx";
import SettingForm from "../../../../Pages/Setting/SettingForm.tsx";
import MagicVarDialog, { MagicVar } from "../../../Common/MagicVarDialog.tsx";
import { NoMarginHelperText } from "../../Settings.tsx";
import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
import ArrowDown from "../../../../Icons/ArrowDown.tsx";
import { DndProvider, useDrag, useDrop } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import { SelectChangeEvent } from "@mui/material";
@ -112,21 +111,21 @@ function DraggableTemplateRow({ i, moveRow, onExtChange, onNameChange, onDelete,
accept: DND_TYPE,
hover(item: any, monitor) {
if (!ref.current) return;
const dragIndex = item.index;
const hoverIndex = i;
if (dragIndex === hoverIndex) return;
const hoverBoundingRect = ref.current.getBoundingClientRect();
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
const clientOffset = monitor.getClientOffset();
if (!clientOffset) return;
const hoverClientY = clientOffset.y - hoverBoundingRect.top;
if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) return;
if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) return;
moveRow(dragIndex, hoverIndex);
item.index = hoverIndex;
},
@ -171,10 +170,21 @@ function DraggableTemplateRow({ i, moveRow, onExtChange, onNameChange, onDelete,
<Dismiss fontSize={"small"} />
</IconButton>
<IconButton size="small" onClick={() => moveRow(i, i - 1)} disabled={isFirst}>
<KeyboardArrowUpIcon fontSize="small" />
<ArrowDown
sx={{
width: "18px",
height: "18px",
transform: "rotate(180deg)",
}}
/>
</IconButton>
<IconButton size="small" onClick={() => moveRow(i, i + 1)} disabled={isLast}>
<KeyboardArrowDownIcon fontSize="small" />
<ArrowDown
sx={{
width: "18px",
height: "18px",
}}
/>
</IconButton>
</NoWrapTableCell>
</TableRow>

View File

@ -35,6 +35,7 @@ interface ViewerGroupProps {
index: number;
onDelete: (e: React.MouseEvent<HTMLElement>) => void;
onGroupChange: (g: ViewerGroup) => void;
dndType: string;
}
const DND_TYPE = "viewer-row";
@ -49,10 +50,11 @@ const DraggableViewerRow = memo(function DraggableViewerRow({
onMoveDown,
isLast,
isFirst,
dndType,
}: any) {
const ref = React.useRef<HTMLTableRowElement>(null);
const [, drop] = useDrop({
accept: DND_TYPE,
accept: dndType,
hover(item: any, monitor) {
if (!ref.current) return;
@ -75,7 +77,7 @@ const DraggableViewerRow = memo(function DraggableViewerRow({
},
});
const [{ isDragging }, drag] = useDrag({
type: DND_TYPE,
type: dndType,
item: { index },
collect: (monitor) => ({
isDragging: monitor.isDragging(),
@ -97,7 +99,7 @@ const DraggableViewerRow = memo(function DraggableViewerRow({
);
});
const ViewerGroupRow = memo(({ group, index, onDelete, onGroupChange }: ViewerGroupProps) => {
const ViewerGroupRow = memo(({ group, index, onDelete, onGroupChange, dndType }: ViewerGroupProps) => {
const { t } = useTranslation("dashboard");
const onViewerChange = useMemo(() => {
@ -195,6 +197,7 @@ const ViewerGroupRow = memo(({ group, index, onDelete, onGroupChange }: ViewerGr
onMoveDown={() => handleMoveDown(idx)}
isFirst={idx === 0}
isLast={idx === viewers.length - 1}
dndType={dndType}
/>
))}
</TableBody>
@ -289,6 +292,7 @@ const FileViewerList = memo(({ config, onChange }: FileViewerListProps) => {
key={index}
onDelete={onGroupDelete[index]}
onGroupChange={onGroupChange[index]}
dndType={`viewer-row-${index}`}
/>
))}
<SecondaryButton variant={"contained"} {...bindTrigger(addNewPopupState)} startIcon={<Add />} sx={{ mt: 1 }}>

View File

@ -8,8 +8,7 @@ import { ViewerIcon } from "../../../../FileManager/Dialogs/OpenWith.tsx";
import Dismiss from "../../../../Icons/Dismiss.tsx";
import Edit from "../../../../Icons/Edit.tsx";
import FileViewerEditDialog from "./FileViewerEditDialog.tsx";
import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
import ArrowDown from "../../../../Icons/ArrowDown.tsx";
export interface FileViewerRowProps {
viewer: Viewer;
@ -103,10 +102,21 @@ const FileViewerRow = React.memo(
</NoWrapCell>
<NoWrapCell>
<IconButton size="small" onClick={onMoveUp} disabled={isFirst}>
<KeyboardArrowUpIcon fontSize="small" />
<ArrowDown
sx={{
width: "18px",
height: "18px",
transform: "rotate(180deg)",
}}
/>
</IconButton>
<IconButton size="small" onClick={onMoveDown} disabled={isLast}>
<KeyboardArrowDownIcon fontSize="small" />
<ArrowDown
sx={{
width: "18px",
height: "18px",
}}
/>
</IconButton>
</NoWrapCell>
</TableRow>

View File

@ -23,8 +23,7 @@ import Dismiss from "../../../Icons/Dismiss.tsx";
import { FileManagerIndex } from "../../FileManager.tsx";
import AddColumn from "./AddColumn.tsx";
import { getColumnTypeDefaults, ListViewColumnSetting } from "./Column.tsx";
import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
import ArrowDown from "../../../Icons/ArrowDown.tsx";
import { DndProvider, useDrag, useDrop } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import React from "react";
@ -50,21 +49,21 @@ const DraggableColumnRow: React.FC<DraggableColumnRowProps> = ({ column, index,
accept: DND_TYPE,
hover(item: any, monitor) {
if (!ref.current) return;
const dragIndex = item.index;
const hoverIndex = index;
if (dragIndex === hoverIndex) return;
const hoverBoundingRect = ref.current.getBoundingClientRect();
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
const clientOffset = monitor.getClientOffset();
if (!clientOffset) return;
const hoverClientY = clientOffset.y - hoverBoundingRect.top;
if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) return;
if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) return;
moveRow(dragIndex, hoverIndex);
item.index = hoverIndex;
},
@ -90,10 +89,21 @@ const DraggableColumnRow: React.FC<DraggableColumnRowProps> = ({ column, index,
<TableCell>
<Box sx={{ display: "flex" }}>
<IconButton size="small" onClick={() => moveRow(index, index - 1)} disabled={isFirst}>
<KeyboardArrowUpIcon fontSize="small" />
<ArrowDown
sx={{
width: "18px",
height: "18px",
transform: "rotate(180deg)",
}}
/>
</IconButton>
<IconButton size="small" onClick={() => moveRow(index, index + 1)} disabled={isLast}>
<KeyboardArrowDownIcon fontSize="small" />
<ArrowDown
sx={{
width: "18px",
height: "18px",
}}
/>
</IconButton>
<IconButton size="small" onClick={() => onDelete(index)} disabled={columns.length <= 1}>
<Dismiss sx={{ width: "18px", height: "18px" }} />