diff --git a/src/components/base/sortable-item.tsx b/src/components/base/sortable-item.tsx
index 0fb923ba9..8af365e85 100644
--- a/src/components/base/sortable-item.tsx
+++ b/src/components/base/sortable-item.tsx
@@ -13,12 +13,24 @@ interface SortableItemProps {
index: number
group?: string
disabled?: boolean
+ // 用于给 tanstack virtual 列表测量当前拖动元素尺寸
+ measureElement?: (element: HTMLDivElement | null) => void
+ dataIndex?: number
style?: CSSProperties
children: ReactNode | ((props: SortableItemRenderProps) => ReactNode)
}
export const SortableItem = (props: SortableItemProps) => {
- const { id, index, group, disabled, children, style } = props
+ const {
+ id,
+ index,
+ group,
+ disabled,
+ measureElement,
+ dataIndex,
+ children,
+ style,
+ } = props
const { ref, handleRef, sortable, isDragging } = useSortable({
id,
index,
@@ -30,8 +42,9 @@ export const SortableItem = (props: SortableItemProps) => {
(element: HTMLDivElement | null) => {
handleRef(element?.querySelector('[data-sortable-handle]') ?? null)
ref(element)
+ measureElement?.(element)
},
- [handleRef, ref],
+ [handleRef, ref, measureElement],
)
const mergedStyle: CSSProperties = {
@@ -53,7 +66,7 @@ export const SortableItem = (props: SortableItemProps) => {
}
return (
-
+
{children}
)
diff --git a/src/components/profile/grouped-virtual-list.tsx b/src/components/profile/grouped-virtual-list.tsx
new file mode 100644
index 000000000..07b657f37
--- /dev/null
+++ b/src/components/profile/grouped-virtual-list.tsx
@@ -0,0 +1,184 @@
+import { move } from '@dnd-kit/helpers'
+import {
+ DragDropProvider,
+ type DragEndEvent,
+ type DragOverEvent,
+ KeyboardSensor,
+ PointerSensor,
+} from '@dnd-kit/react'
+import { isSortable, isSortableOperation } from '@dnd-kit/react/sortable'
+import { useVirtualizer } from '@tanstack/react-virtual'
+import { type CSSProperties, type ReactNode, useRef, useState } from 'react'
+
+import { SortableItem } from '../base'
+
+type GroupedVirtualCategory = 'prepend' | 'original' | 'append'
+
+export interface GroupedVirtualItem
{
+ id: string
+ category: GroupedVirtualCategory
+ item: T
+}
+
+// eslint-disable-next-line react-refresh/only-export-components
+export function buildGroupedItems(
+ prepend: T[],
+ original: T[],
+ append: T[],
+ getKey: (item: T) => string,
+): GroupedVirtualItem[] {
+ const build = (
+ category: GroupedVirtualCategory,
+ list: T[],
+ ): GroupedVirtualItem[] =>
+ list.map((item) => ({
+ id: `${category}:${getKey(item)}`,
+ category,
+ item,
+ }))
+
+ return [
+ ...build('prepend', prepend),
+ ...build('original', original),
+ ...build('append', append),
+ ]
+}
+
+interface GroupedVirtualListProps {
+ items: GroupedVirtualItem[]
+ estimateSize?: number
+ renderItem: (entry: GroupedVirtualItem) => ReactNode
+ onReorder: (
+ category: 'prepend' | 'append',
+ activeIndex: number,
+ overIndex: number,
+ ) => void
+ style?: CSSProperties
+}
+
+/**
+ * 用于编辑规则/节点/代理组功能的 prepend/original/append 分组虚拟化列表
+ */
+export function GroupedVirtualList(props: GroupedVirtualListProps) {
+ const {
+ items: itemsProp,
+ estimateSize = 56,
+ renderItem,
+ onReorder,
+ style,
+ } = props
+ const [items, setItems] = useState(itemsProp)
+ const [prevItemsProp, setPrevItemsProp] = useState(itemsProp)
+ const snapshotRef = useRef(itemsProp)
+ const scrollRef = useRef(null)
+
+ if (itemsProp !== prevItemsProp) {
+ setPrevItemsProp(itemsProp)
+ setItems(itemsProp)
+ }
+
+ const virtualizer = useVirtualizer({
+ count: items.length,
+ getScrollElement: () => scrollRef.current,
+ estimateSize: () => estimateSize,
+ overscan: 15,
+ gap: 8,
+ getItemKey: (index) => items[index]?.id ?? index,
+ })
+
+ const onDragStart = () => {
+ snapshotRef.current = items
+ }
+
+ const onDragOver = (event: DragOverEvent) => {
+ const { operation } = event
+ if (!isSortableOperation(operation)) return
+
+ const { source, target } = operation
+ if (!source || !target) return
+
+ const sourceEntry = items.find((entry) => entry.id === source.id)
+ const targetEntry = items.find((entry) => entry.id === target.id)
+ if (
+ !sourceEntry ||
+ !targetEntry ||
+ sourceEntry.category !== targetEntry.category
+ ) {
+ event.preventDefault()
+ return
+ }
+
+ setItems((current) => move(current, event))
+ }
+
+ const onDragEnd = (event: DragEndEvent) => {
+ const source = event.operation.source
+
+ if (event.canceled) {
+ setItems(snapshotRef.current)
+ return
+ }
+
+ if (!isSortable(source)) return
+
+ const snapshot = snapshotRef.current
+ const sourceEntry = snapshot.find((entry) => entry.id === source.id)
+ if (!sourceEntry) return
+
+ const { category } = sourceEntry
+ if (category !== 'prepend' && category !== 'append') return
+
+ const activeIndex = snapshot
+ .filter((entry) => entry.category === category)
+ .findIndex((entry) => entry.id === source.id)
+ const overIndex = items
+ .filter((entry) => entry.category === category)
+ .findIndex((entry) => entry.id === source.id)
+ if (activeIndex < 0 || overIndex < 0 || activeIndex === overIndex) return
+
+ onReorder(category, activeIndex, overIndex)
+ }
+
+ return (
+
+
+
+ {virtualizer.getVirtualItems().map((vi) => {
+ const entry = items[vi.index]
+ if (!entry) return null
+
+ return (
+
+ {renderItem(entry)}
+
+ )
+ })}
+
+
+
+ )
+}
diff --git a/src/components/profile/groups-editor-viewer.tsx b/src/components/profile/groups-editor-viewer.tsx
index 93749658b..66fc3d836 100644
--- a/src/components/profile/groups-editor-viewer.tsx
+++ b/src/components/profile/groups-editor-viewer.tsx
@@ -1,12 +1,4 @@
import { arrayMove } from '@dnd-kit/helpers'
-import {
- DragDropProvider,
- type DragOverEvent,
- KeyboardSensor,
- PointerSensor,
- type DragEndEvent,
-} from '@dnd-kit/react'
-import { isSortable, isSortableOperation } from '@dnd-kit/react/sortable'
import {
VerticalAlignBottomRounded,
VerticalAlignTopRounded,
@@ -43,13 +35,7 @@ import {
import { Controller, useForm } from 'react-hook-form'
import { useTranslation } from 'react-i18next'
-import {
- BaseSearchBox,
- MonacoEditor,
- SortableItem,
- Switch,
- VirtualList,
-} from '@/components/base'
+import { BaseSearchBox, MonacoEditor, Switch } from '@/components/base'
import { GroupItem } from '@/components/profile/group-item'
import {
getNetworkInterfaces,
@@ -63,6 +49,12 @@ import type { MonacoEditorInstance } from '@/types/monaco'
import { MONACO_FONT_FAMILY } from '@/utils/font-family'
import { parseYamlSafe } from '@/utils/yaml'
+import {
+ buildGroupedItems,
+ type GroupedVirtualItem,
+ GroupedVirtualList,
+} from './grouped-virtual-list'
+
interface Props {
proxiesUid: string
mergeUid: string
@@ -203,106 +195,71 @@ export const GroupsEditorViewer = (props: Props) => {
[appendSeq, match],
)
- const renderItem = (index: number): React.ReactNode => {
- const shift = filteredPrependSeq.length > 0 ? 1 : 0
- if (filteredPrependSeq.length > 0 && index === 0) {
+ const items = useMemo(
+ () =>
+ buildGroupedItems(
+ filteredPrependSeq,
+ filteredGroupList,
+ filteredAppendSeq,
+ (group) => group.name,
+ ),
+ [filteredPrependSeq, filteredGroupList, filteredAppendSeq],
+ )
+
+ const renderItem = (entry: GroupedVirtualItem) => {
+ const { category, item } = entry
+
+ if (category === 'original') {
+ const isDeleted = deleteSeq.includes(item.name)
return (
- <>
- {filteredPrependSeq.map((item, itemIndex) => {
- return (
-
- {
- setPrependSeq(
- prependSeq.filter((v) => v.name !== item.name),
- )
- }}
- />
-
- )
- })}
- >
- )
- } else if (index < filteredGroupList.length + shift) {
- const newIndex = index - shift
- return (
-
- {
+ if (isDeleted) {
+ setDeleteSeq(deleteSeq.filter((v) => v !== item.name))
+ } else {
+ setDeleteSeq((prev) => [...prev, item.name])
}
- group={filteredGroupList[newIndex]}
- onDelete={() => {
- if (deleteSeq.includes(filteredGroupList[newIndex].name)) {
- setDeleteSeq(
- deleteSeq.filter(
- (v) => v !== filteredGroupList[newIndex].name,
- ),
- )
- } else {
- setDeleteSeq((prev) => [
- ...prev,
- filteredGroupList[newIndex].name,
- ])
- }
- }}
- />
-
+ }}
+ />
)
- } else {
+ }
+
+ if (category === 'prepend') {
return (
- <>
- {filteredAppendSeq.map((item, itemIndex) => {
- return (
-
- {
- setAppendSeq(appendSeq.filter((v) => v.name !== item.name))
- }}
- />
-
- )
- })}
- >
+ {
+ setPrependSeq(prependSeq.filter((v) => v.name !== item.name))
+ }}
+ />
)
}
+
+ return (
+ {
+ setAppendSeq(appendSeq.filter((v) => v.name !== item.name))
+ }}
+ />
+ )
}
- const onPrependDragEnd = async (event: DragEndEvent) => {
- const { operation, canceled } = event
- const { source, target } = operation
- if (canceled || !target || !isSortable(source)) return
-
- const { index: overIndex, initialIndex: activeIndex } = source.sortable
- const activeRealIndex = findRealIndex(
- prependSeq,
- filteredPrependSeq,
- activeIndex,
- )
- const overRealIndex = findRealIndex(
- prependSeq,
- filteredPrependSeq,
- overIndex,
- )
+ const onReorder = (
+ category: 'prepend' | 'append',
+ activeIndex: number,
+ overIndex: number,
+ ) => {
+ const list = category === 'prepend' ? prependSeq : appendSeq
+ const filtered =
+ category === 'prepend' ? filteredPrependSeq : filteredAppendSeq
+ const setList = category === 'prepend' ? setPrependSeq : setAppendSeq
+ const activeRealIndex = findRealIndex(list, filtered, activeIndex)
+ const overRealIndex = findRealIndex(list, filtered, overIndex)
if (
activeRealIndex < 0 ||
overRealIndex < 0 ||
@@ -311,55 +268,9 @@ export const GroupsEditorViewer = (props: Props) => {
return
}
- setPrependSeq(arrayMove(prependSeq, activeRealIndex, overRealIndex))
- }
- const onAppendDragEnd = async (event: DragEndEvent) => {
- const { operation, canceled } = event
- const { source, target } = operation
- if (canceled || !target || !isSortable(source)) return
-
- const { index: overIndex, initialIndex: activeIndex } = source.sortable
- const activeRealIndex = findRealIndex(
- appendSeq,
- filteredAppendSeq,
- activeIndex,
- )
- const overRealIndex = findRealIndex(appendSeq, filteredAppendSeq, overIndex)
- if (
- activeRealIndex < 0 ||
- overRealIndex < 0 ||
- activeRealIndex === overRealIndex
- ) {
- return
- }
-
- setAppendSeq(arrayMove(appendSeq, activeRealIndex, overRealIndex))
+ setList(arrayMove(list, activeRealIndex, overRealIndex))
}
- // prevent drag over if source and target are in different groups
- const onDragOver = (event: DragOverEvent) => {
- const { operation } = event
- if (!isSortableOperation(operation)) return
-
- const { source, target } = operation
- if (source?.group !== target?.group) {
- event.preventDefault()
- }
- }
-
- const onDragEnd = async (event: DragEndEvent) => {
- const source = event.operation.source
- if (!isSortable(source)) return
-
- const { initialGroup, group } = source.sortable
- if (initialGroup !== group) return
-
- if (group === 'prepend') {
- await onPrependDragEnd(event)
- } else if (group === 'append') {
- await onAppendDragEnd(event)
- }
- }
const fetchContent = useCallback(async () => {
hasLoadedSeqConfigRef.current = false
const data = await readProfileFile(property)
@@ -1152,22 +1063,12 @@ export const GroupsEditorViewer = (props: Props) => {
}}
>
setMatch(() => match)} />
-
- 0 ? 1 : 0) +
- (filteredAppendSeq.length > 0 ? 1 : 0)
- }
- estimateSize={56}
- renderItem={renderItem}
- style={{ height: 'calc(100% - 24px)', marginTop: '8px' }}
- />
-
+
>
) : (
diff --git a/src/components/profile/proxies-editor-viewer.tsx b/src/components/profile/proxies-editor-viewer.tsx
index 79f2895c5..9d3666752 100644
--- a/src/components/profile/proxies-editor-viewer.tsx
+++ b/src/components/profile/proxies-editor-viewer.tsx
@@ -1,12 +1,4 @@
import { arrayMove } from '@dnd-kit/helpers'
-import {
- DragDropProvider,
- type DragOverEvent,
- KeyboardSensor,
- PointerSensor,
- type DragEndEvent,
-} from '@dnd-kit/react'
-import { isSortable, isSortableOperation } from '@dnd-kit/react/sortable'
import {
VerticalAlignBottomRounded,
VerticalAlignTopRounded,
@@ -35,12 +27,7 @@ import {
} from 'react'
import { useTranslation } from 'react-i18next'
-import {
- BaseSearchBox,
- MonacoEditor,
- SortableItem,
- VirtualList,
-} from '@/components/base'
+import { BaseSearchBox, MonacoEditor } from '@/components/base'
import { ProxyItem } from '@/components/profile/proxy-item'
import { readProfileFile, saveProfileFile } from '@/services/cmds'
import { showNotice } from '@/services/notice-service'
@@ -50,6 +37,12 @@ import { MONACO_FONT_FAMILY } from '@/utils/font-family'
import parseUri from '@/utils/uri-parser'
import { parseYamlSafe } from '@/utils/yaml'
+import {
+ buildGroupedItems,
+ type GroupedVirtualItem,
+ GroupedVirtualList,
+} from './grouped-virtual-list'
+
interface Props {
profileUid: string
property: string
@@ -107,106 +100,71 @@ export const ProxiesEditorViewer = (props: Props) => {
[appendSeq, match],
)
- const renderItem = (index: number): React.ReactNode => {
- const shift = filteredPrependSeq.length > 0 ? 1 : 0
- if (filteredPrependSeq.length > 0 && index === 0) {
+ const items = useMemo(
+ () =>
+ buildGroupedItems(
+ filteredPrependSeq,
+ filteredProxyList,
+ filteredAppendSeq,
+ (proxy) => proxy.name,
+ ),
+ [filteredPrependSeq, filteredProxyList, filteredAppendSeq],
+ )
+
+ const renderItem = (entry: GroupedVirtualItem) => {
+ const { category, item } = entry
+
+ if (category === 'original') {
+ const isDeleted = deleteSeq.includes(item.name)
return (
- <>
- {filteredPrependSeq.map((item, itemIndex) => {
- return (
-
- {
- setPrependSeq(
- prependSeq.filter((v) => v.name !== item.name),
- )
- }}
- />
-
- )
- })}
- >
- )
- } else if (index < filteredProxyList.length + shift) {
- const newIndex = index - shift
- return (
-
- {
+ if (isDeleted) {
+ setDeleteSeq(deleteSeq.filter((v) => v !== item.name))
+ } else {
+ setDeleteSeq((prev) => [...prev, item.name])
}
- proxy={filteredProxyList[newIndex]}
- onDelete={() => {
- if (deleteSeq.includes(filteredProxyList[newIndex].name)) {
- setDeleteSeq(
- deleteSeq.filter(
- (v) => v !== filteredProxyList[newIndex].name,
- ),
- )
- } else {
- setDeleteSeq((prev) => [
- ...prev,
- filteredProxyList[newIndex].name,
- ])
- }
- }}
- />
-
+ }}
+ />
)
- } else {
+ }
+
+ if (category === 'prepend') {
return (
- <>
- {filteredAppendSeq.map((item, itemIndex) => {
- return (
-
- {
- setAppendSeq(appendSeq.filter((v) => v.name !== item.name))
- }}
- />
-
- )
- })}
- >
+ {
+ setPrependSeq(prependSeq.filter((v) => v.name !== item.name))
+ }}
+ />
)
}
+
+ return (
+ {
+ setAppendSeq(appendSeq.filter((v) => v.name !== item.name))
+ }}
+ />
+ )
}
- const onPrependDragEnd = async (event: DragEndEvent) => {
- const { operation, canceled } = event
- const { source, target } = operation
- if (canceled || !target || !isSortable(source)) return
-
- const { index: overIndex, initialIndex: activeIndex } = source.sortable
- const activeRealIndex = findRealIndex(
- prependSeq,
- filteredPrependSeq,
- activeIndex,
- )
- const overRealIndex = findRealIndex(
- prependSeq,
- filteredPrependSeq,
- overIndex,
- )
+ const onReorder = (
+ category: 'prepend' | 'append',
+ activeIndex: number,
+ overIndex: number,
+ ) => {
+ const list = category === 'prepend' ? prependSeq : appendSeq
+ const filtered =
+ category === 'prepend' ? filteredPrependSeq : filteredAppendSeq
+ const setList = category === 'prepend' ? setPrependSeq : setAppendSeq
+ const activeRealIndex = findRealIndex(list, filtered, activeIndex)
+ const overRealIndex = findRealIndex(list, filtered, overIndex)
if (
activeRealIndex < 0 ||
overRealIndex < 0 ||
@@ -215,52 +173,9 @@ export const ProxiesEditorViewer = (props: Props) => {
return
}
- setPrependSeq(arrayMove(prependSeq, activeRealIndex, overRealIndex))
+ setList(arrayMove(list, activeRealIndex, overRealIndex))
}
- const onAppendDragEnd = async (event: DragEndEvent) => {
- const { operation, canceled } = event
- const { source, target } = operation
- if (canceled || !target || !isSortable(source)) return
- const { index: overIndex, initialIndex: activeIndex } = source.sortable
- const activeRealIndex = findRealIndex(
- appendSeq,
- filteredAppendSeq,
- activeIndex,
- )
- const overRealIndex = findRealIndex(appendSeq, filteredAppendSeq, overIndex)
- if (
- activeRealIndex < 0 ||
- overRealIndex < 0 ||
- activeRealIndex === overRealIndex
- ) {
- return
- }
-
- setAppendSeq(arrayMove(appendSeq, activeRealIndex, overRealIndex))
- }
- const onDragOver = (event: DragOverEvent) => {
- const { operation } = event
- if (!isSortableOperation(operation)) return
-
- const { source, target } = operation
- if (source?.group !== target?.group) {
- event.preventDefault()
- }
- }
- const onDragEnd = async (event: DragEndEvent) => {
- const source = event.operation.source
- if (!isSortable(source)) return
-
- const { initialGroup, group } = source.sortable
- if (initialGroup !== group) return
-
- if (group === 'prepend') {
- await onPrependDragEnd(event)
- } else if (group === 'append') {
- await onAppendDragEnd(event)
- }
- }
// 优化:异步分片解析,避免主线程阻塞,解析完成后批量setState
const handleParseAsync = (cb: (proxies: IProxyConfig[]) => void) => {
const proxies: IProxyConfig[] = []
@@ -522,22 +437,12 @@ export const ProxiesEditorViewer = (props: Props) => {
}}
>
setMatch(() => match)} />
-
- 0 ? 1 : 0) +
- (filteredAppendSeq.length > 0 ? 1 : 0)
- }
- estimateSize={56}
- renderItem={renderItem}
- style={{ height: 'calc(100% - 24px)', marginTop: '8px' }}
- />
-
+
>
) : (
diff --git a/src/components/profile/rules-editor-viewer.tsx b/src/components/profile/rules-editor-viewer.tsx
index 483ace9a1..eccb59730 100644
--- a/src/components/profile/rules-editor-viewer.tsx
+++ b/src/components/profile/rules-editor-viewer.tsx
@@ -1,11 +1,4 @@
-import {
- DragDropProvider,
- type DragOverEvent,
- KeyboardSensor,
- PointerSensor,
- type DragEndEvent,
-} from '@dnd-kit/react'
-import { isSortable, isSortableOperation } from '@dnd-kit/react/sortable'
+import { arrayMove } from '@dnd-kit/helpers'
import {
VerticalAlignBottomRounded,
VerticalAlignTopRounded,
@@ -36,13 +29,7 @@ import {
} from 'react'
import { useTranslation } from 'react-i18next'
-import {
- BaseSearchBox,
- MonacoEditor,
- SortableItem,
- Switch,
- VirtualList,
-} from '@/components/base'
+import { BaseSearchBox, MonacoEditor, Switch } from '@/components/base'
import { RuleItem } from '@/components/profile/rule-item'
import { readProfileFile, saveProfileFile } from '@/services/cmds'
import { showNotice } from '@/services/notice-service'
@@ -54,6 +41,12 @@ import getSystem from '@/utils/get-system'
import { isValidIpCidr } from '@/utils/network'
import { parseYamlSafe } from '@/utils/yaml'
+import {
+ buildGroupedItems,
+ type GroupedVirtualItem,
+ GroupedVirtualList,
+} from './grouped-virtual-list'
+
interface Props {
groupsUid: string
mergeUid: string
@@ -302,117 +295,83 @@ export const RulesEditorViewer = (props: Props) => {
[appendSeq, match],
)
- const renderItem = (index: number): React.ReactNode => {
- const shift = filteredPrependSeq.length > 0 ? 1 : 0
- if (filteredPrependSeq.length > 0 && index === 0) {
+ const items = useMemo(
+ () =>
+ buildGroupedItems(
+ filteredPrependSeq,
+ filteredRuleList,
+ filteredAppendSeq,
+ (rule) => rule,
+ ),
+ [filteredPrependSeq, filteredRuleList, filteredAppendSeq],
+ )
+
+ const renderItem = (entry: GroupedVirtualItem) => {
+ const { category, item } = entry
+
+ if (category === 'original') {
+ const isDeleted = deleteSeq.includes(item)
return (
- <>
- {filteredPrependSeq.map((item, itemIndex) => {
- return (
-
- {
- setPrependSeq(prependSeq.filter((v) => v !== item))
- }}
- onAppend={() => {
- setAppendSeq((prev) =>
- prev.includes(item) ? prev : [...prev, item],
- )
- setPrependSeq(prependSeq.filter((v) => v !== item))
- }}
- />
-
- )
- })}
- >
- )
- } else if (index < filteredRuleList.length + shift) {
- const newIndex = index - shift
- return (
-
- {
+ if (isDeleted) {
+ setDeleteSeq(deleteSeq.filter((v) => v !== item))
+ } else {
+ setDeleteSeq((prev) => [...prev, item])
}
- ruleRaw={filteredRuleList[newIndex]}
- onDelete={() => {
- if (deleteSeq.includes(filteredRuleList[newIndex])) {
- setDeleteSeq(
- deleteSeq.filter((v) => v !== filteredRuleList[newIndex]),
- )
- } else {
- setDeleteSeq((prev) => [...prev, filteredRuleList[newIndex]])
- }
- }}
- />
-
+ }}
+ />
)
- } else {
+ }
+
+ if (category === 'prepend') {
return (
- <>
- {filteredAppendSeq.map((item, itemIndex) => {
- return (
-
- {
- setAppendSeq(appendSeq.filter((v) => v !== item))
- }}
- onPrepend={() => {
- setPrependSeq((prev) =>
- prev.includes(item) ? prev : [...prev, item],
- )
- setAppendSeq(appendSeq.filter((v) => v !== item))
- }}
- />
-
+ {
+ setPrependSeq(prependSeq.filter((v) => v !== item))
+ }}
+ onAppend={() => {
+ setAppendSeq((prev) =>
+ prev.includes(item) ? prev : [...prev, item],
)
- })}
- >
+ setPrependSeq(prependSeq.filter((v) => v !== item))
+ }}
+ />
)
}
+
+ return (
+ {
+ setAppendSeq(appendSeq.filter((v) => v !== item))
+ }}
+ onPrepend={() => {
+ setPrependSeq((prev) =>
+ prev.includes(item) ? prev : [...prev, item],
+ )
+ setAppendSeq(appendSeq.filter((v) => v !== item))
+ }}
+ />
+ )
}
- 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 { operation, canceled } = event
- const { source, target } = operation
- if (canceled || !target || !isSortable(source)) return
-
- const { index: overIndex, initialIndex: activeIndex } = source.sortable
- const activeRealIndex = findRealIndex(
- prependSeq,
- filteredPrependSeq,
- activeIndex,
- )
- const overRealIndex = findRealIndex(
- prependSeq,
- filteredPrependSeq,
- overIndex,
- )
+ const onReorder = (
+ category: 'prepend' | 'append',
+ activeIndex: number,
+ overIndex: number,
+ ) => {
+ const list = category === 'prepend' ? prependSeq : appendSeq
+ const filtered =
+ category === 'prepend' ? filteredPrependSeq : filteredAppendSeq
+ const setList = category === 'prepend' ? setPrependSeq : setAppendSeq
+ const activeRealIndex = findRealIndex(list, filtered, activeIndex)
+ const overRealIndex = findRealIndex(list, filtered, overIndex)
if (
activeRealIndex < 0 ||
overRealIndex < 0 ||
@@ -421,52 +380,9 @@ export const RulesEditorViewer = (props: Props) => {
return
}
- setPrependSeq(reorder(prependSeq, activeRealIndex, overRealIndex))
+ setList(arrayMove(list, activeRealIndex, overRealIndex))
}
- const onAppendDragEnd = async (event: DragEndEvent) => {
- const { operation, canceled } = event
- const { source, target } = operation
- if (canceled || !target || !isSortable(source)) return
- const { index: overIndex, initialIndex: activeIndex } = source.sortable
- const activeRealIndex = findRealIndex(
- appendSeq,
- filteredAppendSeq,
- activeIndex,
- )
- const overRealIndex = findRealIndex(appendSeq, filteredAppendSeq, overIndex)
- if (
- activeRealIndex < 0 ||
- overRealIndex < 0 ||
- activeRealIndex === overRealIndex
- ) {
- return
- }
-
- setAppendSeq(reorder(appendSeq, activeRealIndex, overRealIndex))
- }
- const onDragOver = (event: DragOverEvent) => {
- const { operation } = event
- if (!isSortableOperation(operation)) return
-
- const { source, target } = operation
- if (source?.group !== target?.group) {
- event.preventDefault()
- }
- }
- const onDragEnd = async (event: DragEndEvent) => {
- const source = event.operation.source
- if (!isSortable(source)) return
-
- const { initialGroup, group } = source.sortable
- if (initialGroup !== group) return
-
- if (group === 'prepend') {
- await onPrependDragEnd(event)
- } else if (group === 'append') {
- await onAppendDragEnd(event)
- }
- }
const fetchContent = useCallback(async () => {
hasLoadedSeqConfigRef.current = false
const data = await readProfileFile(property)
@@ -854,22 +770,12 @@ export const RulesEditorViewer = (props: Props) => {
}}
>
setMatch(() => match)} />
-
- 0 ? 1 : 0) +
- (filteredAppendSeq.length > 0 ? 1 : 0)
- }
- estimateSize={56}
- renderItem={renderItem}
- style={{ height: 'calc(100% - 24px)', marginTop: '8px' }}
- />
-
+
>
) : (