From cbfc2cf974e6d366b198b122e00c380b7797aad5 Mon Sep 17 00:00:00 2001 From: yinsx Date: Thu, 5 Feb 2026 11:41:47 +0800 Subject: [PATCH] 1 --- .gitignore | 2 +- app/src/components/FolderCard/index.vue | 10 +- app/src/components/GridCanvas/index.vue | 137 ++++++++++++++---------- 3 files changed, 89 insertions(+), 60 deletions(-) diff --git a/.gitignore b/.gitignore index 8d032d5..6f9adb9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ /node_modules/ /public/ /dist/ -nul \ No newline at end of file +nul/ \ No newline at end of file diff --git a/app/src/components/FolderCard/index.vue b/app/src/components/FolderCard/index.vue index 5f1bbd7..b467ba8 100644 --- a/app/src/components/FolderCard/index.vue +++ b/app/src/components/FolderCard/index.vue @@ -75,17 +75,21 @@ const initialText = (child: FolderChild) => { } .folder-preview { + --folder-chip-size: min(20px, calc(var(--icon-size, 72px) * 0.28)); width: 100%; height: 100%; display: grid; - grid-template-columns: repeat(2, 1fr); - grid-template-rows: repeat(2, 1fr); + grid-template-columns: repeat(2, var(--folder-chip-size)); + grid-template-rows: repeat(2, var(--folder-chip-size)); gap: 6px; - padding-top: 6px; + padding: 4px; + place-content: center; box-sizing: border-box; } .folder-chip { + width: var(--folder-chip-size); + height: var(--folder-chip-size); border-radius: 8px; background: rgba(255, 255, 255, 0.85); color: #222; diff --git a/app/src/components/GridCanvas/index.vue b/app/src/components/GridCanvas/index.vue index 24d6eac..b763ef9 100644 --- a/app/src/components/GridCanvas/index.vue +++ b/app/src/components/GridCanvas/index.vue @@ -98,18 +98,21 @@ const mergeOrderSnapshot = ref(null); const mergePendingFolderId = ref(null); const mergePendingInsertIndex = ref(null); const OVERLAP_MERGE_THRESHOLD = 0.6; -const OVERLAP_HOLD_MS = 100; +const OVERLAP_HOLD_MS = 0; const OVERLAP_DROP_THRESHOLD = 0.35; -const MERGE_HIT_INSET_RATIO = 0.18; -const MERGE_HIT_INSET_PX = 6; +const MERGE_HIT_INSET_RATIO = 0; +const MERGE_HIT_INSET_PX = 0; const POINTER_ROW_TOLERANCE_RATIO = 0.6; const POINTER_EDGE_PADDING = 6; const lastPointer = ref<{ x: number; y: number } | null>(null); const pointerTargetEl = ref(null); +const isPressing = ref(false); +const pressTimer = ref(null); const layoutAnimationMs = 240; const layoutEasing = 'cubic-bezier(0.4, 0, 0.2, 1)'; const DELETE_ANIM_MS = 240; const DELETE_ANIM_EASING = 'cubic-bezier(0.4, 0, 0.2, 1)'; +const PRESS_DELAY_MS = 100; const normalizeGroupId = (groupId?: string | null) => groupId ?? DEFAULT_GROUP_ID; const activeGroupId = computed(() => normalizeGroupId(props.activeGroupId)); @@ -415,6 +418,32 @@ const handleClick = (event: MouseEvent) => { } }; +const clearPressTimer = () => { + if (pressTimer.value !== null) { + window.clearTimeout(pressTimer.value); + pressTimer.value = null; + } +}; + +const stopPressing = () => { + clearPressTimer(); + isPressing.value = false; +}; + +const handlePointerDown = (event: PointerEvent) => { + if (event.button !== 0) return; + const target = event.target as HTMLElement | null; + if (!target?.closest('.grid-item')) return; + clearPressTimer(); + pressTimer.value = window.setTimeout(() => { + isPressing.value = true; + }, PRESS_DELAY_MS); +}; + +const handleGlobalPointerUp = () => { + stopPressing(); +}; + const handleClickCapture = (event: MouseEvent) => { if (isDragging.value || suppressClick.value || Date.now() < clickBlockUntil.value) { event.preventDefault(); @@ -798,29 +827,11 @@ const handleMergeDrop = () => { const dragId = draggingMeta.value.id; const dragType = draggingMeta.value.type; if (!dragId || !dragType) return; - let targetEl = mergeTargetEl.value; - if (!mergeActive.value || !targetEl) { - targetEl = hoverTargetEl.value; - const dragEl = draggingEl.value; - if (!targetEl && dragEl) { - const stats = findTargetStats(dragEl); - const best = stats?.best ?? null; - if (best && best.ratio >= OVERLAP_MERGE_THRESHOLD) { - targetEl = best.el; - } - } - } + let targetEl = mergeTargetEl.value ?? hoverTargetEl.value ?? pointerTargetEl.value; if (!targetEl) { clearHoverTarget(); return; } - if (!mergeActive.value && draggingEl.value) { - const ratio = getOverlapRatio(draggingEl.value, targetEl); - if (ratio < OVERLAP_DROP_THRESHOLD) { - clearHoverTarget(); - return; - } - } const targetId = targetEl.dataset.id; const targetType = targetEl.dataset.type as GridItemType | undefined; if (!targetId || !targetType) return; @@ -1232,6 +1243,8 @@ onMounted(async () => { ensureOrderConsistency(); previousIconIds.value = new Set(layoutStore.icons.map(icon => icon.id)); previousWidgetIds.value = new Set(widgetsStore.widgets.map(widget => widget.id)); + window.addEventListener('pointerup', handleGlobalPointerUp); + window.addEventListener('pointercancel', handleGlobalPointerUp); await nextTick(); if (!gridRef.value) return; grid.value = new Muuri(gridRef.value, { @@ -1266,6 +1279,7 @@ onMounted(async () => { grid.value.on('dragStart', (item: any) => { isDragging.value = true; suppressClick.value = true; + stopPressing(); const el = item.getElement() as HTMLElement; draggingEl.value = el; draggingItemRef.value = item; @@ -1305,42 +1319,35 @@ onMounted(async () => { const now = performance.now(); const pointerTarget = pointerTargetEl.value; - if (mergeActive.value) { - const locked = mergeTargetEl.value; - if (!locked || !locked.isConnected) { - deactivateMergeTarget(true); - } else if (pointerTarget && pointerTarget !== locked) { - deactivateMergeTarget(true); - mergeCandidateEl.value = pointerTarget; - mergeCandidateAt.value = now; - setHoverTarget(pointerTarget); - } else if (!pointerTarget) { - deactivateMergeTarget(true); - } else { - setHoverTarget(locked); - } - } else { if (pointerTarget && pointerTarget.isConnected) { - setHoverTarget(pointerTarget); - if (mergeCandidateEl.value === pointerTarget) { - if (now - mergeCandidateAt.value >= OVERLAP_HOLD_MS) { - activateMergeTarget(pointerTarget); - } - } else { + if (mergeCandidateEl.value !== pointerTarget) { mergeCandidateEl.value = pointerTarget; mergeCandidateAt.value = now; + if (mergeActive.value && mergeTargetEl.value !== pointerTarget) { + deactivateMergeTarget(false); + } + } + const holdReady = now - mergeCandidateAt.value >= OVERLAP_HOLD_MS; + if (holdReady) { + if (!mergeActive.value || mergeTargetEl.value !== pointerTarget) { + activateMergeTarget(pointerTarget); + } else { + setHoverTarget(pointerTarget); + } + } else { + clearHoverTarget(); } } else { mergeCandidateEl.value = null; mergeCandidateAt.value = 0; + deactivateMergeTarget(true); clearHoverTarget(); } - } - applySortLock(mergeActive.value); - }); - } - }); + applySortLock(!!pointerTarget); + }); + } + }); grid.value.on('dragEnd', () => { isDragging.value = false; @@ -1497,20 +1504,24 @@ watch( onUnmounted(() => { pendingResizedKeys.clear(); + stopPressing(); + window.removeEventListener('pointerup', handleGlobalPointerUp); + window.removeEventListener('pointercancel', handleGlobalPointerUp); grid.value?.destroy(); grid.value = null; });