From 7d2458a74b11c4a4154c28c9f40a16fa4c8e7b55 Mon Sep 17 00:00:00 2001 From: zipg Date: Mon, 17 Aug 2026 14:47:00 +0800 Subject: [PATCH] fix(data-grid): restore desktop filter rule dragging --- .../components/grid/DataGridFilterBuilder.vue | 150 +++++++++++--- .../grid/__tests__/DataGridSurfaces.spec.ts | 21 +- .../dataGridFilterBuilderPointerDrag.spec.ts | 195 ++++++++++++++++++ apps/desktop/src/i18n/locales/zh-CN.ts | 2 +- apps/desktop/src/i18n/locales/zh-TW.ts | 2 +- 5 files changed, 335 insertions(+), 35 deletions(-) create mode 100644 apps/desktop/src/components/grid/__tests__/dataGridFilterBuilderPointerDrag.spec.ts diff --git a/apps/desktop/src/components/grid/DataGridFilterBuilder.vue b/apps/desktop/src/components/grid/DataGridFilterBuilder.vue index b7582050db..c81f142990 100644 --- a/apps/desktop/src/components/grid/DataGridFilterBuilder.vue +++ b/apps/desktop/src/components/grid/DataGridFilterBuilder.vue @@ -67,7 +67,12 @@ const dropTargetIndex = ref(); let ruleIdsBeforeKeyboardAdd: Set | undefined; let ruleDragContainer: HTMLElement | undefined; let ruleDragPointerY: number | undefined; +let ruleDragPointerId: number | undefined; +let ruleDragHandle: HTMLElement | undefined; let ruleAutoScrollFrame: number | undefined; +let ruleDragBodyStyleActive = false; +let ruleDragPreviousBodyUserSelect = ""; +let ruleDragPreviousBodyCursor = ""; function columnNameDisplayUnits(value: string) { return Array.from(value).reduce((total, character) => total + ((character.codePointAt(0) ?? 0) > 0xff ? 2 : 1), 0); @@ -140,8 +145,11 @@ function stopRuleAutoScroll() { } function removeRuleDragListeners() { - window.removeEventListener("dragover", handleRuleContainerDragOver, true); - window.removeEventListener("drop", handleRuleContainerDrop, true); + window.removeEventListener("pointermove", handleRulePointerMove, true); + window.removeEventListener("pointerup", handleRulePointerUp, true); + window.removeEventListener("pointercancel", handleRulePointerCancel, true); + window.removeEventListener("keydown", handleRuleDragKeydown, true); + window.removeEventListener("blur", handleRuleDragWindowBlur); } function clearRuleDropTarget() { @@ -153,14 +161,22 @@ function clearRuleDropTarget() { function clearRuleDragState() { stopRuleAutoScroll(); removeRuleDragListeners(); + if (ruleDragHandle && ruleDragPointerId !== undefined && ruleDragHandle.hasPointerCapture?.(ruleDragPointerId)) ruleDragHandle.releasePointerCapture(ruleDragPointerId); + if (ruleDragBodyStyleActive) { + document.body.style.userSelect = ruleDragPreviousBodyUserSelect; + document.body.style.cursor = ruleDragPreviousBodyCursor; + } draggingRuleId.value = undefined; ruleDragContainer = undefined; ruleDragPointerY = undefined; + ruleDragPointerId = undefined; + ruleDragHandle = undefined; + ruleDragBodyStyleActive = false; clearRuleDropTarget(); } -function ruleDropZoneContains(event: DragEvent, bounds: DOMRect) { - return event.clientX >= bounds.left && event.clientX <= bounds.right && event.clientY >= bounds.top - RULE_DROP_ZONE_EXTENSION && event.clientY <= bounds.bottom + RULE_DROP_ZONE_EXTENSION; +function ruleDropZoneContains(clientX: number, clientY: number, bounds: DOMRect) { + return clientX >= bounds.left && clientX <= bounds.right && clientY >= bounds.top - RULE_DROP_ZONE_EXTENSION && clientY <= bounds.bottom + RULE_DROP_ZONE_EXTENSION; } function updateRuleDropTarget(clientY: number) { @@ -214,25 +230,24 @@ function scheduleRuleAutoScroll() { if (ruleAutoScrollFrame === undefined) ruleAutoScrollFrame = window.requestAnimationFrame(runRuleAutoScroll); } -function handleRuleContainerDragOver(event: DragEvent) { - if (!draggingRuleId.value || !ruleDragContainer) return; +function handleRulePointerMove(event: PointerEvent) { + if (!draggingRuleId.value || !ruleDragContainer || event.pointerId !== ruleDragPointerId) return; const bounds = ruleDragContainer.getBoundingClientRect(); - if (!ruleDropZoneContains(event, bounds)) { + if (!ruleDropZoneContains(event.clientX, event.clientY, bounds)) { stopRuleAutoScroll(); clearRuleDropTarget(); return; } event.preventDefault(); - if (event.dataTransfer) event.dataTransfer.dropEffect = "move"; ruleDragPointerY = event.clientY; updateRuleDropTarget(event.clientY); scheduleRuleAutoScroll(); } -function handleRuleContainerDrop(event: DragEvent) { - if (!draggingRuleId.value || !ruleDragContainer) return; +function handleRulePointerUp(event: PointerEvent) { + if (!draggingRuleId.value || !ruleDragContainer || event.pointerId !== ruleDragPointerId) return; const bounds = ruleDragContainer.getBoundingClientRect(); - if (!ruleDropZoneContains(event, bounds)) { + if (!ruleDropZoneContains(event.clientX, event.clientY, bounds)) { clearRuleDragState(); return; } @@ -242,23 +257,44 @@ function handleRuleContainerDrop(event: DragEvent) { clearRuleDragState(); } -function startRuleDrag(event: DragEvent, id: string) { - if (props.disabled || props.rules.length < 2 || !event.dataTransfer) { - event.preventDefault(); - return; - } +function handleRulePointerCancel(event?: PointerEvent) { + if (event && event.pointerId !== ruleDragPointerId) return; + clearRuleDragState(); +} + +function handleRuleDragWindowBlur() { + clearRuleDragState(); +} + +function handleRuleDragKeydown(event: KeyboardEvent) { + if (event.key !== "Escape" || !draggingRuleId.value) return; + event.preventDefault(); + clearRuleDragState(); +} + +function startRulePointerDrag(event: PointerEvent, id: string) { + if (event.button !== 0 || props.disabled || props.rules.length < 2) return; const handle = event.currentTarget instanceof HTMLElement ? event.currentTarget : undefined; ruleDragContainer = handle?.closest("[data-filter-rules-scroll]") ?? filterBuilderRootRef.value; - if (!ruleDragContainer) { - event.preventDefault(); - return; - } + if (!ruleDragContainer || !handle) return; + event.preventDefault(); + event.stopPropagation(); clearRuleDropTarget(); draggingRuleId.value = id; - window.addEventListener("dragover", handleRuleContainerDragOver, true); - window.addEventListener("drop", handleRuleContainerDrop, true); - event.dataTransfer.effectAllowed = "move"; - event.dataTransfer.setData("text/plain", id); + ruleDragPointerId = event.pointerId; + ruleDragPointerY = event.clientY; + ruleDragHandle = handle; + ruleDragPreviousBodyUserSelect = document.body.style.userSelect; + ruleDragPreviousBodyCursor = document.body.style.cursor; + ruleDragBodyStyleActive = true; + document.body.style.userSelect = "none"; + document.body.style.cursor = "grabbing"; + handle.setPointerCapture?.(event.pointerId); + window.addEventListener("pointermove", handleRulePointerMove, true); + window.addEventListener("pointerup", handleRulePointerUp, true); + window.addEventListener("pointercancel", handleRulePointerCancel, true); + window.addEventListener("keydown", handleRuleDragKeydown, true); + window.addEventListener("blur", handleRuleDragWindowBlur); } function moveRuleByKeyboard(event: KeyboardEvent, id: string, offset: -1 | 1) { @@ -466,14 +502,16 @@ function blurValueRule(id: string) { -
+