Ver código fonte

feat: drag to reorder rules

MystiPanda 11 meses atrás
pai
commit
51a49b94d8

+ 9 - 1
src/components/profile/rule-item.tsx

@@ -8,6 +8,8 @@ import {
   alpha,
 } from "@mui/material";
 import { DeleteForeverRounded, UndoRounded } from "@mui/icons-material";
+import { useSortable } from "@dnd-kit/sortable";
+import { CSS } from "@dnd-kit/utilities";
 interface Props {
   type: "prepend" | "original" | "delete" | "append";
   ruleRaw: string;
@@ -17,7 +19,8 @@ interface Props {
 export const RuleItem = (props: Props) => {
   let { type, ruleRaw, onDelete } = props;
   const rule = ruleRaw.replace(",no-resolve", "").split(",");
-
+  const { attributes, listeners, setNodeRef, transform, transition } =
+    useSortable({ id: ruleRaw });
   return (
     <ListItem
       sx={({ palette }) => ({
@@ -31,9 +34,14 @@ export const RuleItem = (props: Props) => {
             ? alpha(palette.error.main, 0.5)
             : alpha(palette.success.main, 0.5),
         mb: 1,
+        transform: CSS.Transform.toString(transform),
+        transition,
       })}
     >
       <ListItemText
+        {...attributes}
+        {...listeners}
+        ref={setNodeRef}
         sx={{ px: 1 }}
         primary={
           <>

+ 97 - 29
src/components/profile/rules-editor-viewer.tsx

@@ -2,7 +2,19 @@ import { ReactNode, useEffect, useState } from "react";
 import { useLockFn } from "ahooks";
 import yaml from "js-yaml";
 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 {
   Autocomplete,
   Button,
@@ -134,6 +146,38 @@ export const RulesEditorViewer = (props: Props) => {
   const [appendSeq, setAppendSeq] = 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 () => {
     let data = await readProfileFile(property);
     let obj = yaml.load(data) as { prepend: []; append: []; delete: [] };
@@ -325,20 +369,32 @@ export const RulesEditorViewer = (props: Props) => {
           }}
         >
           {prependSeq.length > 0 && (
-            <List sx={{ borderBottom: "solid 1px var(--divider-color)" }}>
-              {prependSeq.map((item, index) => {
-                return (
-                  <RuleItem
-                    key={`${item}-${index}`}
-                    type="prepend"
-                    ruleRaw={item}
-                    onDelete={() => {
-                      setPrependSeq(prependSeq.filter((v) => v !== item));
-                    }}
-                  />
-                );
-              })}
-            </List>
+            <DndContext
+              sensors={sensors}
+              collisionDetection={closestCenter}
+              onDragEnd={onPrependDragEnd}
+            >
+              <List sx={{ borderBottom: "solid 1px var(--divider-color)" }}>
+                <SortableContext
+                  items={prependSeq.map((x) => {
+                    return x;
+                  })}
+                >
+                  {prependSeq.map((item, index) => {
+                    return (
+                      <RuleItem
+                        key={`${item}-${index}`}
+                        type="prepend"
+                        ruleRaw={item}
+                        onDelete={() => {
+                          setPrependSeq(prependSeq.filter((v) => v !== item));
+                        }}
+                      />
+                    );
+                  })}
+                </SortableContext>
+              </List>
+            </DndContext>
           )}
 
           <List>
@@ -361,20 +417,32 @@ export const RulesEditorViewer = (props: Props) => {
           </List>
 
           {appendSeq.length > 0 && (
-            <List sx={{ borderTop: "solid 1px var(--divider-color)" }}>
-              {appendSeq.map((item, index) => {
-                return (
-                  <RuleItem
-                    key={`${item}-${index}`}
-                    type="append"
-                    ruleRaw={item}
-                    onDelete={() => {
-                      setAppendSeq(appendSeq.filter((v) => v !== item));
-                    }}
-                  />
-                );
-              })}
-            </List>
+            <DndContext
+              sensors={sensors}
+              collisionDetection={closestCenter}
+              onDragEnd={onAppendDragEnd}
+            >
+              <SortableContext
+                items={appendSeq.map((x) => {
+                  return x;
+                })}
+              >
+                <List sx={{ borderTop: "solid 1px var(--divider-color)" }}>
+                  {appendSeq.map((item, index) => {
+                    return (
+                      <RuleItem
+                        key={`${item}-${index}`}
+                        type="append"
+                        ruleRaw={item}
+                        onDelete={() => {
+                          setAppendSeq(appendSeq.filter((v) => v !== item));
+                        }}
+                      />
+                    );
+                  })}
+                </List>
+              </SortableContext>
+            </DndContext>
           )}
         </div>
       </DialogContent>