diff --git a/components/pageblocks/ProblemRowReact.js b/components/pageblocks/ProblemRowReact.js
index f31d99e..0d28a22 100644
--- a/components/pageblocks/ProblemRowReact.js
+++ b/components/pageblocks/ProblemRowReact.js
@@ -15,6 +15,8 @@ import { TextField } from '@mui/material';
import { Button, Stack, Box } from "@mui/material";
import { Folder as FolderIcon } from '@mui/icons-material';
import { Download as DownloadIcon } from '@mui/icons-material';
+import { DragIndicator as DragIndicatorIcon } from '@mui/icons-material';
+import { IconButton } from '@mui/material';
import PopoverTooltipClick from '../widgets/PopoverTooltipClick';
import ProblemFilterMenu from '../widgets/ProblemFilterMenu';
@@ -38,7 +40,7 @@ const COMPLEXITY_CLASS_ORDER = ["P", "NPComplete", "NPHard", "NPIntermediate", "
/**
* Creates an accordion that has a nested autocomplete search bar, as well as an editable problem instance textbox
*/
-export default function ProblemRowReact({ url, problemName, setProblemName, problemNameMap, setProblemInstance }) {
+export default function ProblemRowReact({ url, problemName, setProblemName, problemNameMap, setProblemInstance, dragHandleProps }) {
const problemInfo = useProblemInfo(url, problemName);
const { problemIndex, reductionGraph } = useProblemIndex(url);
const {
@@ -166,28 +168,28 @@ export default function ProblemRowReact({ url, problemName, setProblemName, prob
const tip =
problemName
? {
- header: problemInfo.problemName ?? "",
- formalDef: problemInfo.formalDefinition ?? "",
- // It makes description clean
- info: problemInfo.problemDefinition ?? "",
- classification: [
- { label: "Complexity class", value: problemInfo.complexityClass || "Unclassified" },
- ],
- // Source shown on its own line here
- source:
- problemInfo.source ||
- (Array.isArray(problemInfo.citations) ? problemInfo.citations.join("; ") : "") ||
- "",
- // Contributors
- credit:
- Array.isArray(problemInfo.contributors) && problemInfo.contributors.length
- ? problemInfo.contributors.join(", ")
- : "",
- // Popover builds Wikipedia URL
- componentLink: problemInfo.problemLink || "",
- sourceLink: problemInfo.sourceLink || "",
- isMathDef: true, // only this file adds the flag
- }
+ header: problemInfo.problemName ?? "",
+ formalDef: problemInfo.formalDefinition ?? "",
+ // It makes description clean
+ info: problemInfo.problemDefinition ?? "",
+ classification: [
+ { label: "Complexity class", value: problemInfo.complexityClass || "Unclassified" },
+ ],
+ // Source shown on its own line here
+ source:
+ problemInfo.source ||
+ (Array.isArray(problemInfo.citations) ? problemInfo.citations.join("; ") : "") ||
+ "",
+ // Contributors
+ credit:
+ Array.isArray(problemInfo.contributors) && problemInfo.contributors.length
+ ? problemInfo.contributors.join(", ")
+ : "",
+ // Popover builds Wikipedia URL
+ componentLink: problemInfo.problemLink || "",
+ sourceLink: problemInfo.sourceLink || "",
+ isMathDef: true, // only this file adds the flag
+ }
: TOOLTIP;
return (
@@ -217,7 +219,24 @@ export default function ProblemRowReact({ url, problemName, setProblemName, prob
setSelectedSolverComplexityBuckets={setSelectedSolverComplexityBuckets}
clearFilters={clearFilters}
/>{" "}
-
+
+ {dragHandleProps && (
+
+
+
+ )}
diff --git a/components/pageblocks/ReduceToRowReact.js b/components/pageblocks/ReduceToRowReact.js
index 688db6f..fda2c1e 100644
--- a/components/pageblocks/ReduceToRowReact.js
+++ b/components/pageblocks/ReduceToRowReact.js
@@ -15,6 +15,8 @@ import 'bootstrap/dist/css/bootstrap.min.css'
import { Card } from 'react-bootstrap'
import { Button } from '@mui/material'
import { Download as DownloadIcon } from '@mui/icons-material';
+import { DragIndicator as DragIndicatorIcon } from '@mui/icons-material';
+import { IconButton } from '@mui/material';
import { requestReducedInstanceFromPath } from '../redux'
import { useProblemInfo, useReducerInfo } from '../hooks/ProblemProvider'
@@ -56,6 +58,7 @@ export default function ReduceToRowReact({
setChosenReductionType,
reducedInstance,
setReducedInstance,
+ dragHandleProps,
}) {
const reduceToInfo = useProblemInfo(url, chosenReduceTo);
const reducerInfo = useReducerInfo(url, chosenReductionType);
@@ -176,6 +179,23 @@ export default function ReduceToRowReact({
: TOOLTIP2
}
>
+ {dragHandleProps && (
+
+
+
+ )}
diff --git a/components/pageblocks/SolveRowReact.js b/components/pageblocks/SolveRowReact.js
index 2fb5f6c..c115a3e 100644
--- a/components/pageblocks/SolveRowReact.js
+++ b/components/pageblocks/SolveRowReact.js
@@ -13,6 +13,8 @@ import { useContext } from "react";
import "bootstrap/dist/css/bootstrap.min.css";
import { Button } from "@mui/material";
import { Download as DownloadIcon } from '@mui/icons-material';
+import { DragIndicator as DragIndicatorIcon } from '@mui/icons-material';
+import { IconButton } from '@mui/material';
import { requestSolvedInstance } from "../redux";
import PopoverTooltipClick from "../widgets/PopoverTooltipClick";
@@ -45,6 +47,7 @@ export default function SolveRowReact({
solverNameMap,
problemNameMap,
chosenReduceTo,
+ dragHandleProps,
}) {
const solverInfo = useSolverInfo(url, chosenSolver);
@@ -114,6 +117,23 @@ export default function SolveRowReact({
}}
/>{" "}
+ {dragHandleProps && (
+
+
+
+ )}
diff --git a/components/pageblocks/VerifyRowReact.js b/components/pageblocks/VerifyRowReact.js
index a15fc5a..8352c6c 100644
--- a/components/pageblocks/VerifyRowReact.js
+++ b/components/pageblocks/VerifyRowReact.js
@@ -13,6 +13,8 @@ import { useContext, useEffect, useState } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css'
import { FormControl } from 'react-bootstrap'
import { Button } from '@mui/material'
+import { DragIndicator as DragIndicatorIcon } from '@mui/icons-material';
+import { IconButton } from '@mui/material';
import { requestVerifiedInstance, requestIsCertificateValid } from '../redux';
import PopoverTooltipClick from '../widgets/PopoverTooltipClick';
@@ -34,6 +36,7 @@ export default function VerifyRowReact({
setChosenVerifier,
verifierOptions,
verifierNameMap,
+ dragHandleProps,
}) {
const [certificate, setCertificate] = useState("");
const [verifyResult, setVerifyResult] = useState("");
@@ -98,6 +101,23 @@ export default function VerifyRowReact({
: TOOLTIP
}
>
+ {dragHandleProps && (
+
+
+
+ )}
diff --git a/components/pageblocks/VisualizeRowReact.js b/components/pageblocks/VisualizeRowReact.js
index fccd002..8137e92 100644
--- a/components/pageblocks/VisualizeRowReact.js
+++ b/components/pageblocks/VisualizeRowReact.js
@@ -22,6 +22,7 @@ import {
FastForward,
} from "@mui/icons-material";
import RefreshIcon from "@mui/icons-material/Refresh";
+import { DragIndicator as DragIndicatorIcon } from '@mui/icons-material';
import Link from "next/link"; // <-- IMPORTANT for Quantum button
import PopoverTooltipClick from "../widgets/PopoverTooltipClick";
@@ -71,6 +72,7 @@ export default function VisualizeRowReact({
VisualizationOptions,
defaultVisualizationMap,
visualizationTypeMap,
+ dragHandleProps,
}) {
const visualizationInfo = useVisualizationInfo(url, chosenVisualization);
@@ -371,6 +373,23 @@ export default function VisualizeRowReact({
/>
+ {dragHandleProps && (
+
+
+
+ )}
diff --git a/package-lock.json b/package-lock.json
index 2892fa0..ac417f1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,6 +9,9 @@
"version": "0.1.0",
"hasInstallScript": true,
"dependencies": {
+ "@dnd-kit/core": "^6.3.1",
+ "@dnd-kit/sortable": "^10.0.0",
+ "@dnd-kit/utilities": "^3.2.2",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@fontsource/roboto": "^5.3.0",
@@ -601,6 +604,59 @@
"node": ">=18"
}
},
+ "node_modules/@dnd-kit/accessibility": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz",
+ "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@dnd-kit/core": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
+ "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@dnd-kit/accessibility": "^3.1.1",
+ "@dnd-kit/utilities": "^3.2.2",
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@dnd-kit/sortable": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz",
+ "integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==",
+ "license": "MIT",
+ "dependencies": {
+ "@dnd-kit/utilities": "^3.2.2",
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "@dnd-kit/core": "^6.3.0",
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@dnd-kit/utilities": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz",
+ "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
"node_modules/@emnapi/core": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
diff --git a/package.json b/package.json
index 2bb01d4..2766403 100644
--- a/package.json
+++ b/package.json
@@ -18,6 +18,9 @@
"postinstall": "next telemetry disable"
},
"dependencies": {
+ "@dnd-kit/core": "^6.3.1",
+ "@dnd-kit/sortable": "^10.0.0",
+ "@dnd-kit/utilities": "^3.2.2",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@fontsource/roboto": "^5.3.0",
diff --git a/pages/index.js b/pages/index.js
index 7bcadd9..da5190f 100644
--- a/pages/index.js
+++ b/pages/index.js
@@ -26,12 +26,28 @@ import {
} from "@mui/material";
import { Container } from "react-bootstrap";
import { useProblemProvider } from "../components/hooks/ProblemProvider";
-import { useEffect, memo } from "react";
+import { useEffect, memo, useState } from "react"; // CHANGED: added useState for row order
import { useUnload } from "../components/eventHandlers/handleUnload";
import ShareButton from "../components/widgets/ShareButton";
import TourLauncher from "../components/tour/TourLauncher";
import { useHandleParameters } from "../components/eventHandlers/handleParameters";
+import {
+ DndContext,
+ closestCenter,
+ PointerSensor,
+ TouchSensor,
+ useSensor,
+ useSensors,
+} from "@dnd-kit/core";
+import {
+ SortableContext,
+ verticalListSortingStrategy,
+ useSortable,
+ arrayMove,
+} from "@dnd-kit/sortable";
+import { CSS } from "@dnd-kit/utilities";
+
const SHOW_QUANTUM_VIS = false; //Flag to show a quantum circuit visualizer (sandbox feature)
const ProblemRowMemo = memo(ProblemRowReact);
const ReduceToRowMemo = memo(ReduceToRowReact);
@@ -40,6 +56,48 @@ const SolveRowMemo = memo(SolveRowReact);
const VerifyRowMemo = memo(VerifyRowReact);
const reduxBaseUrl = '/api/redux/';
+
+function SortableRow({ id, children }) {
+ const {
+ attributes,
+ listeners,
+ setNodeRef,
+ transform,
+ transition,
+ isDragging,
+ } = useSortable({ id });
+
+ const style = {
+ transform: CSS.Transform.toString(transform),
+ transition,
+ position: "relative",
+ opacity: isDragging ? 0.6 : 1,
+ };
+
+ const tourIds = {
+ reduce: "reduce-row",
+ solve: "solve-row",
+ verify: "verify-row",
+ };
+
+ const tourId = tourIds[id];
+
+ const childrenWithProps = React.cloneElement(children, {
+ dragHandleProps: { attributes, listeners },
+ });
+
+ return (
+
+ {childrenWithProps}
+
+ );
+}
+
/**
* Generates the actual page contents
*
@@ -79,7 +137,55 @@ function MainPageContent() {
const { problem, solver, verifier, reducer, visualization } =
useProblemProvider(reduxBaseUrl);
- //useUnload(problem, solver, verifier, reducer);
+ const [rowOrder, setRowOrder] = useState([
+ "problem",
+ "reduce",
+ "visualize",
+ "solve",
+ "verify",
+ ]);
+
+ // PointerSensor covers mouse; TouchSensor adds mobile/tablet support
+ const sensors = useSensors(
+ useSensor(PointerSensor),
+ useSensor(TouchSensor)
+ );
+
+ const rowMap = {
+ problem: ,
+ reduce: ,
+ visualize: (
+
+ ),
+ solve: (
+
+ ),
+ verify: ,
+ };
+
+ // Replaces the old handleDragStart/handleDragOver/handleDrop trio.
+ function handleDragEnd(event) {
+ const { active, over } = event;
+ if (over && active.id !== over.id) {
+ setRowOrder((items) => {
+ const oldIndex = items.indexOf(active.id);
+ const newIndex = items.indexOf(over.id);
+ return arrayMove(items, oldIndex, newIndex);
+ });
+ }
+ }
return (
<>
@@ -98,37 +204,22 @@ function MainPageContent() {
/>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+ {rowOrder.map((key) => (
+
+ {rowMap[key]}
+
+ ))}
+
+
@@ -162,4 +253,4 @@ export default function MainPage() {
>
);
-}
+}
\ No newline at end of file