移植 Axolotl 的拖拽式实例分组界面到 Starlight,接入阶段1的规范化分组后端: - 新增 helpers/analytics.ts(trackEvent 遥测桩) - 补全 helpers/instance-groups.ts(6 个 invoke 包装) - 新增 composables/useInstanceGroups.ts(分组状态 + 事件监听) - 升级 composables/useGridGrouping.ts(排序方向 isSortAscending/toggleSortDirection) - helpers/events.js 补 instance_groups_listener(对接 instance_groups_changed 事件) - 新增 components/ui/library/DraggableInstanceCard.vue - 新增 components/ui/library/InstanceGroup.vue(并补 group-drag-handle 修复组拖拽重排) - 新增 components/ui/modal/InstanceGroupModal.vue - GridDisplay.vue 替换为 Axolotl 拖拽版(DragDropProvider + vuedraggable) - 补 en-US/zh-CN i18n 键各 39 条
This commit is contained in:
1 parent
253141b6ff
commit
f253d5529b
11 files changed
+2214
-297
No files matched your search
@@ -0,0 +1,92 @@
|
||||
<script setup lang="ts">
|
||||
import { KeyboardSensor, PointerSensor, useDraggable } from '@dnd-kit/vue'
|
||||
import { useEventListener } from '@vueuse/core'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import type { GameInstance } from '@/helpers/types'
|
||||
|
||||
const props = defineProps<{
|
||||
instance: GameInstance
|
||||
instanceGroupId: string
|
||||
disabled: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'contextmenu' | 'card-click', event: MouseEvent): void
|
||||
(e: 'start-long-press' | 'cancel-long-press'): void
|
||||
}>()
|
||||
|
||||
const cardElement = ref<HTMLElement>()
|
||||
|
||||
const instanceCardSensors = [
|
||||
PointerSensor.configure({
|
||||
preventActivation: () => false,
|
||||
}),
|
||||
KeyboardSensor,
|
||||
]
|
||||
|
||||
const dragDisabled = computed(() => props.disabled)
|
||||
|
||||
const { isDragging } = useDraggable({
|
||||
id: computed(() => `instance:${props.instanceGroupId}:${props.instance.id}`),
|
||||
element: cardElement,
|
||||
disabled: dragDisabled,
|
||||
sensors: instanceCardSensors,
|
||||
data: computed(() => ({
|
||||
instanceId: props.instance.id,
|
||||
fromGroup: props.instanceGroupId,
|
||||
})),
|
||||
})
|
||||
|
||||
let wantsClick = false
|
||||
|
||||
useEventListener(window, 'pointerdown', () => {
|
||||
wantsClick = true
|
||||
})
|
||||
useEventListener(window, 'pointerup', () => {
|
||||
if (isDragging.value) {
|
||||
wantsClick = false
|
||||
}
|
||||
})
|
||||
useEventListener(window, 'blur', () => {
|
||||
wantsClick = false
|
||||
})
|
||||
|
||||
function onCardClick(event: MouseEvent) {
|
||||
if (!wantsClick || isDragging.value) return
|
||||
wantsClick = false
|
||||
emit('card-click', event)
|
||||
}
|
||||
|
||||
function onContextMenu(event: MouseEvent) {
|
||||
emit('contextmenu', event)
|
||||
}
|
||||
|
||||
function onMouseDown() {
|
||||
if (!dragDisabled.value) {
|
||||
emit('start-long-press')
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseUp() {
|
||||
emit('cancel-long-press')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
ref="cardElement"
|
||||
class="group relative min-w-0 w-full"
|
||||
@click="onCardClick"
|
||||
@mousedown="onMouseDown"
|
||||
@mouseup="onMouseUp"
|
||||
@mouseleave="onMouseUp"
|
||||
@touchstart="!dragDisabled && onMouseDown()"
|
||||
@touchend="onMouseUp"
|
||||
@touchcancel="onMouseUp"
|
||||
@contextmenu.prevent.stop="onContextMenu"
|
||||
>
|
||||
<slot :is-dragging="isDragging" />
|
||||
<slot v-if="!isDragging" name="overlay" />
|
||||
</div>
|
||||
</template>
|
||||
Reference in new issue
Block a user