diff --git a/app/components/MarketSettingModal.jsx b/app/components/MarketSettingModal.jsx index 893f0e1..9ad9ccf 100644 --- a/app/components/MarketSettingModal.jsx +++ b/app/components/MarketSettingModal.jsx @@ -1,7 +1,23 @@ "use client"; import { useMemo, useState } from "react"; -import { AnimatePresence, Reorder } from "framer-motion"; +import { AnimatePresence } from "framer-motion"; +import { + DndContext, + KeyboardSensor, + PointerSensor, + useSensor, + useSensors, + closestCenter, +} from "@dnd-kit/core"; +import { restrictToParentElement } from "@dnd-kit/modifiers"; +import { + SortableContext, + rectSortingStrategy, + useSortable, + arrayMove, +} from "@dnd-kit/sortable"; +import { CSS } from "@dnd-kit/utilities"; import { Drawer, DrawerContent, @@ -20,6 +36,94 @@ import { CloseIcon, MinusIcon, ResetIcon, SettingsIcon } from "./Icons"; import ConfirmModal from "./ConfirmModal"; import { cn } from "@/lib/utils"; +function SortableIndexItem({ item, canRemove, onRemove }) { + const { + attributes, + listeners, + setNodeRef, + transform, + transition, + isDragging, + } = useSortable({ id: item.code }); + + const style = { + transform: CSS.Transform.toString(transform), + transition, + cursor: isDragging ? "grabbing" : "grab", + flex: "0 0 calc((100% - 24px) / 3)", + touchAction: "none", + ...(isDragging && { + position: "relative", + zIndex: 10, + opacity: 0.9, + boxShadow: "0 8px 24px rgba(0,0,0,0.15)", + }), + }; + + const isUp = item.change >= 0; + const color = isUp ? "var(--danger)" : "var(--success)"; + + return ( +
+ {canRemove && ( + + )} +
+ {item.name} +
+
+ {item.price?.toFixed ? item.price.toFixed(2) : String(item.price ?? "-")} +
+
+ {(item.change >= 0 ? "+" : "") + item.change.toFixed(2)}{" "} + {(item.changePercent >= 0 ? "+" : "") + item.changePercent.toFixed(2)}% +
+
+ ); +} + /** * 指数个性化设置弹框 * @@ -60,6 +164,11 @@ export default function MarketSettingModal({ const [resetConfirmOpen, setResetConfirmOpen] = useState(false); + const sensors = useSensors( + useSensor(PointerSensor, { activationConstraint: { distance: 5 } }), + useSensor(KeyboardSensor) + ); + const handleToggleCode = (code) => { if (!code) return; if (selectedSet.has(code)) { @@ -73,8 +182,14 @@ export default function MarketSettingModal({ } }; - const handleReorder = (newOrder) => { - onChangeSelected?.(newOrder); + const handleDragEnd = (event) => { + const { active, over } = event; + if (!over || active.id === over.id) return; + const oldIndex = selectedCodes.indexOf(active.id); + const newIndex = selectedCodes.indexOf(over.id); + if (oldIndex === -1 || newIndex === -1) return; + const next = arrayMove(selectedCodes, oldIndex, newIndex); + onChangeSelected?.(next); }; const body = ( @@ -112,98 +227,28 @@ export default function MarketSettingModal({ 暂未添加指数,请在下方选择想要关注的指数。 ) : ( - - - {selectedList.map((item) => { - const isUp = item.change >= 0; - const color = - isUp ? "var(--danger)" : "var(--success)"; - return ( - +
+ {selectedList.map((item) => ( + - {selectedCodes.length > 1 && ( - - )} -
- {item.name} -
-
- {item.price?.toFixed - ? item.price.toFixed(2) - : String(item.price ?? "-")} -
-
- {(item.change >= 0 ? "+" : "") + - item.change.toFixed(2)}{" "} - {(item.changePercent >= 0 ? "+" : "") + - item.changePercent.toFixed(2)} - % -
- - ); - })} - - + item={item} + canRemove={selectedCodes.length > 1} + onRemove={handleToggleCode} + /> + ))} +
+ + )}