diff --git a/frontend/src/pages/xray/dns/DnsTab.tsx b/frontend/src/pages/xray/dns/DnsTab.tsx index b52193936..003fbfdf3 100644 --- a/frontend/src/pages/xray/dns/DnsTab.tsx +++ b/frontend/src/pages/xray/dns/DnsTab.tsx @@ -27,6 +27,7 @@ import { selectTagRender } from '@/components/form'; import { onNumber } from '@/utils/onNumber'; import { SettingListItem } from '@/components/ui'; import { useMediaQuery } from '@/hooks/useMediaQuery'; +import { usePointerRowReorder } from '@/hooks/usePointerRowReorder'; import { catTabLabel } from '@/pages/settings/catTabLabel'; import DnsServerModal from './DnsServerModal'; import type { DnsServerValue } from './DnsServerModal'; @@ -52,6 +53,7 @@ export default function DnsTab({ templateSettings, setTemplateSettings }: DnsTab const [editingServer, setEditingServer] = useState(null); const [editingIndex, setEditingIndex] = useState(null); const [presetsModalOpen, setPresetsModalOpen] = useState(false); + const serversContainerRef = useRef(null); const dns = (templateSettings?.dns as DnsConfig | undefined) ?? null; const dnsEnabled = !!dns; @@ -166,8 +168,33 @@ export default function DnsTab({ templateSettings, setTemplateSettings }: DnsTab }, [mutate], ); + const moveServer = useCallback( + (from: number, to: number) => { + mutate((tt) => { + const servers = (tt.dns as DnsConfig | undefined)?.servers; + if (!servers) return; + const [moved] = servers.splice(from, 1); + servers.splice(to, 0, moved); + }); + }, + [mutate], + ); - const dnsColumns = useDnsServerColumns({ openEditServer, deleteServer }); + const { drag, onPointerDown: onHandlePointerDown } = usePointerRowReorder({ + containerRef: serversContainerRef, + getRowKey: (row) => Number(row.dataset.rowKey), + onDrop: ({ from, to }) => moveServer(from, to), + }); + const draggedIndex = drag?.from ?? null; + const dropTargetIndex = drag?.to ?? null; + + const dnsColumns = useDnsServerColumns({ + rowsLength: dnsServers.length, + onHandlePointerDown, + openEditServer, + moveServer, + deleteServer, + }); function openAddServer() { setEditingServer(null); @@ -475,14 +502,21 @@ export default function DnsTab({ templateSettings, setTemplateSettings }: DnsTab {t('pages.xray.dns.clearAll')} - r.key} - pagination={false} - size="small" - bordered - /> +
+
r.key} + rowClassName={(_r, i) => { + if (i === draggedIndex) return 'row-dragging'; + if (i !== dropTargetIndex || draggedIndex == null) return ''; + return i > draggedIndex ? 'drop-after' : 'drop-before'; + }} + pagination={false} + size="small" + bordered + /> + ), }); @@ -517,7 +551,17 @@ export default function DnsTab({ templateSettings, setTemplateSettings }: DnsTab return out; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [t, isMobile, dnsEnabled, dns, hostsList, dnsServers, fakeDnsList]); + }, [ + t, + isMobile, + dnsEnabled, + dns, + hostsList, + dnsServers, + fakeDnsList, + draggedIndex, + dropTargetIndex, + ]); return ( <> diff --git a/frontend/src/pages/xray/dns/useDnsColumns.tsx b/frontend/src/pages/xray/dns/useDnsColumns.tsx index 70f12f25b..1c7ec608a 100644 --- a/frontend/src/pages/xray/dns/useDnsColumns.tsx +++ b/frontend/src/pages/xray/dns/useDnsColumns.tsx @@ -1,7 +1,14 @@ import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Button, Dropdown, Input, InputNumber, Space } from 'antd'; -import { MoreOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons'; +import { + MoreOutlined, + EditOutlined, + DeleteOutlined, + ArrowUpOutlined, + ArrowDownOutlined, + HolderOutlined, +} from '@ant-design/icons'; import type { ColumnsType } from 'antd/es/table'; import { onNumber } from '@/utils/onNumber'; @@ -13,10 +20,16 @@ export type DnsServerRow = { key: number; server: DnsServerValue }; export type FakednsTableRow = { key: number; ipPool: string; poolSize: number }; export function useDnsServerColumns({ + rowsLength, + onHandlePointerDown, openEditServer, + moveServer, deleteServer, }: { + rowsLength: number; + onHandlePointerDown: (idx: number, ev: React.PointerEvent) => void; openEditServer: (idx: number) => void; + moveServer: (from: number, to: number) => void; deleteServer: (idx: number) => void; }): ColumnsType { const { t } = useTranslation(); @@ -29,6 +42,12 @@ export function useDnsServerColumns({ width: 60, render: (_v, _record, index) => ( +