mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2024-11-16 03:32:36 +08:00
feat: drag to reorder rules
This commit is contained in:
parent
720b46d790
commit
51a49b94d8
|
@ -8,6 +8,8 @@ import {
|
||||||
alpha,
|
alpha,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { DeleteForeverRounded, UndoRounded } from "@mui/icons-material";
|
import { DeleteForeverRounded, UndoRounded } from "@mui/icons-material";
|
||||||
|
import { useSortable } from "@dnd-kit/sortable";
|
||||||
|
import { CSS } from "@dnd-kit/utilities";
|
||||||
interface Props {
|
interface Props {
|
||||||
type: "prepend" | "original" | "delete" | "append";
|
type: "prepend" | "original" | "delete" | "append";
|
||||||
ruleRaw: string;
|
ruleRaw: string;
|
||||||
|
@ -17,7 +19,8 @@ interface Props {
|
||||||
export const RuleItem = (props: Props) => {
|
export const RuleItem = (props: Props) => {
|
||||||
let { type, ruleRaw, onDelete } = props;
|
let { type, ruleRaw, onDelete } = props;
|
||||||
const rule = ruleRaw.replace(",no-resolve", "").split(",");
|
const rule = ruleRaw.replace(",no-resolve", "").split(",");
|
||||||
|
const { attributes, listeners, setNodeRef, transform, transition } =
|
||||||
|
useSortable({ id: ruleRaw });
|
||||||
return (
|
return (
|
||||||
<ListItem
|
<ListItem
|
||||||
sx={({ palette }) => ({
|
sx={({ palette }) => ({
|
||||||
|
@ -31,9 +34,14 @@ export const RuleItem = (props: Props) => {
|
||||||
? alpha(palette.error.main, 0.5)
|
? alpha(palette.error.main, 0.5)
|
||||||
: alpha(palette.success.main, 0.5),
|
: alpha(palette.success.main, 0.5),
|
||||||
mb: 1,
|
mb: 1,
|
||||||
|
transform: CSS.Transform.toString(transform),
|
||||||
|
transition,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
|
{...attributes}
|
||||||
|
{...listeners}
|
||||||
|
ref={setNodeRef}
|
||||||
sx={{ px: 1 }}
|
sx={{ px: 1 }}
|
||||||
primary={
|
primary={
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -2,7 +2,19 @@ import { ReactNode, useEffect, useState } from "react";
|
||||||
import { useLockFn } from "ahooks";
|
import { useLockFn } from "ahooks";
|
||||||
import yaml from "js-yaml";
|
import yaml from "js-yaml";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import {
|
||||||
|
DndContext,
|
||||||
|
closestCenter,
|
||||||
|
KeyboardSensor,
|
||||||
|
PointerSensor,
|
||||||
|
useSensor,
|
||||||
|
useSensors,
|
||||||
|
DragEndEvent,
|
||||||
|
} from "@dnd-kit/core";
|
||||||
|
import {
|
||||||
|
SortableContext,
|
||||||
|
sortableKeyboardCoordinates,
|
||||||
|
} from "@dnd-kit/sortable";
|
||||||
import {
|
import {
|
||||||
Autocomplete,
|
Autocomplete,
|
||||||
Button,
|
Button,
|
||||||
|
@ -134,6 +146,38 @@ export const RulesEditorViewer = (props: Props) => {
|
||||||
const [appendSeq, setAppendSeq] = useState<string[]>([]);
|
const [appendSeq, setAppendSeq] = useState<string[]>([]);
|
||||||
const [deleteSeq, setDeleteSeq] = useState<string[]>([]);
|
const [deleteSeq, setDeleteSeq] = useState<string[]>([]);
|
||||||
|
|
||||||
|
const sensors = useSensors(
|
||||||
|
useSensor(PointerSensor),
|
||||||
|
useSensor(KeyboardSensor, {
|
||||||
|
coordinateGetter: sortableKeyboardCoordinates,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const reorder = (list: string[], startIndex: number, endIndex: number) => {
|
||||||
|
const result = Array.from(list);
|
||||||
|
const [removed] = result.splice(startIndex, 1);
|
||||||
|
result.splice(endIndex, 0, removed);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
const onPrependDragEnd = async (event: DragEndEvent) => {
|
||||||
|
const { active, over } = event;
|
||||||
|
if (over) {
|
||||||
|
if (active.id !== over.id) {
|
||||||
|
let activeIndex = prependSeq.indexOf(active.id.toString());
|
||||||
|
let overIndex = prependSeq.indexOf(over.id.toString());
|
||||||
|
setPrependSeq(reorder(prependSeq, activeIndex, overIndex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const onAppendDragEnd = async (event: DragEndEvent) => {
|
||||||
|
const { active, over } = event;
|
||||||
|
if (over) {
|
||||||
|
if (active.id !== over.id) {
|
||||||
|
let activeIndex = appendSeq.indexOf(active.id.toString());
|
||||||
|
let overIndex = appendSeq.indexOf(over.id.toString());
|
||||||
|
setAppendSeq(reorder(appendSeq, activeIndex, overIndex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
const fetchContent = async () => {
|
const fetchContent = async () => {
|
||||||
let data = await readProfileFile(property);
|
let data = await readProfileFile(property);
|
||||||
let obj = yaml.load(data) as { prepend: []; append: []; delete: [] };
|
let obj = yaml.load(data) as { prepend: []; append: []; delete: [] };
|
||||||
|
@ -325,20 +369,32 @@ export const RulesEditorViewer = (props: Props) => {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{prependSeq.length > 0 && (
|
{prependSeq.length > 0 && (
|
||||||
<List sx={{ borderBottom: "solid 1px var(--divider-color)" }}>
|
<DndContext
|
||||||
{prependSeq.map((item, index) => {
|
sensors={sensors}
|
||||||
return (
|
collisionDetection={closestCenter}
|
||||||
<RuleItem
|
onDragEnd={onPrependDragEnd}
|
||||||
key={`${item}-${index}`}
|
>
|
||||||
type="prepend"
|
<List sx={{ borderBottom: "solid 1px var(--divider-color)" }}>
|
||||||
ruleRaw={item}
|
<SortableContext
|
||||||
onDelete={() => {
|
items={prependSeq.map((x) => {
|
||||||
setPrependSeq(prependSeq.filter((v) => v !== item));
|
return x;
|
||||||
}}
|
})}
|
||||||
/>
|
>
|
||||||
);
|
{prependSeq.map((item, index) => {
|
||||||
})}
|
return (
|
||||||
</List>
|
<RuleItem
|
||||||
|
key={`${item}-${index}`}
|
||||||
|
type="prepend"
|
||||||
|
ruleRaw={item}
|
||||||
|
onDelete={() => {
|
||||||
|
setPrependSeq(prependSeq.filter((v) => v !== item));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</SortableContext>
|
||||||
|
</List>
|
||||||
|
</DndContext>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<List>
|
<List>
|
||||||
|
@ -361,20 +417,32 @@ export const RulesEditorViewer = (props: Props) => {
|
||||||
</List>
|
</List>
|
||||||
|
|
||||||
{appendSeq.length > 0 && (
|
{appendSeq.length > 0 && (
|
||||||
<List sx={{ borderTop: "solid 1px var(--divider-color)" }}>
|
<DndContext
|
||||||
{appendSeq.map((item, index) => {
|
sensors={sensors}
|
||||||
return (
|
collisionDetection={closestCenter}
|
||||||
<RuleItem
|
onDragEnd={onAppendDragEnd}
|
||||||
key={`${item}-${index}`}
|
>
|
||||||
type="append"
|
<SortableContext
|
||||||
ruleRaw={item}
|
items={appendSeq.map((x) => {
|
||||||
onDelete={() => {
|
return x;
|
||||||
setAppendSeq(appendSeq.filter((v) => v !== item));
|
})}
|
||||||
}}
|
>
|
||||||
/>
|
<List sx={{ borderTop: "solid 1px var(--divider-color)" }}>
|
||||||
);
|
{appendSeq.map((item, index) => {
|
||||||
})}
|
return (
|
||||||
</List>
|
<RuleItem
|
||||||
|
key={`${item}-${index}`}
|
||||||
|
type="append"
|
||||||
|
ruleRaw={item}
|
||||||
|
onDelete={() => {
|
||||||
|
setAppendSeq(appendSeq.filter((v) => v !== item));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</List>
|
||||||
|
</SortableContext>
|
||||||
|
</DndContext>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user