feat(layout): add pointer cancel handling to ActivityBar and improve drag end detection in TabBar

- Implemented a new `handlePointerCancel` function in the ActivityBar to manage pointer cancellation events, enhancing drag-and-drop interactions.
- Updated the TabBar component to utilize a callback for detecting drag end proximity to the tab strip, improving the user experience during tab management.
This commit is contained in:
Kang committed 2026-06-25 14:27:05 +08:00
1 parent 9e87feed2a
commit e62561feb9
2 files changed
+39 -15

No files matched your search

+20 -1
View File
@@ -326,6 +326,22 @@ function DropZone({
[requestPointerDrop, resetDragState],
);
const handlePointerCancel = useCallback(
(event: PointerEvent) => {
const state = pointerDragRef.current;
if (!state || state.pointerId !== event.pointerId) return;
if (event.currentTarget.hasPointerCapture(event.pointerId)) {
event.currentTarget.releasePointerCapture(event.pointerId);
}
resetDragState();
window.setTimeout(() => {
suppressClickRef.current = false;
}, 0);
},
[resetDragState],
);
const handleDragStart = useCallback(
(e: DragEvent, itemId: string) => {
dragItemIdRef.current = itemId;
@@ -402,6 +418,7 @@ function DropZone({
onPointerDown={(e) => handlePointerDown(e, item.id)}
onPointerMove={handlePointerMove}
onPointerEnd={handlePointerEnd}
onPointerCancel={handlePointerCancel}
draggable={!usePointerDrag}
suppressClickRef={suppressClickRef}
showDropIndicator={dropIndex === idx}
@@ -439,6 +456,7 @@ function ActivityBarButton({
onPointerDown,
onPointerMove,
onPointerEnd,
onPointerCancel,
draggable,
suppressClickRef,
showDropIndicator,
@@ -461,6 +479,7 @@ function ActivityBarButton({
onPointerDown: (e: PointerEvent) => void;
onPointerMove: (e: PointerEvent) => void;
onPointerEnd: (e: PointerEvent) => void;
onPointerCancel: (e: PointerEvent) => void;
draggable: boolean;
suppressClickRef: MutableRefObject<boolean>;
showDropIndicator: boolean;
@@ -483,7 +502,7 @@ function ActivityBarButton({
onPointerDown={onPointerDown}
onPointerMove={onPointerMove}
onPointerUp={onPointerEnd}
onPointerCancel={onPointerEnd}
onPointerCancel={onPointerCancel}
className={`relative flex flex-col items-center justify-center w-full transition-colors ${showLabel ? "min-h-12 gap-0.5 py-1" : "h-9"}`}
style={{
color: active ? "var(--df-primary)" : "var(--df-text-muted)",
+19 -14
View File
@@ -710,6 +710,22 @@ function TabBar({
[tabs],
);
const isDragEndFallbackNearTabStrip = useCallback((clientX: number, clientY: number) => {
const strip = tabStripRef.current;
if (!strip || clientX === 0) return false;
const rect = strip.getBoundingClientRect();
const horizontalTolerance = 48;
const verticalTolerance = Math.max(96, rect.height * 3);
const isWithinHorizontalRange =
clientX >= rect.left - horizontalTolerance && clientX <= rect.right + horizontalTolerance;
if (!isWithinHorizontalRange) return false;
if (clientY === 0) return true;
return clientY >= rect.top - verticalTolerance && clientY <= rect.bottom + verticalTolerance;
}, []);
const handleDropAtIndex = useCallback(
(insertionIndex: number, event?: DragEvent<HTMLDivElement>) => {
const externalTabId = event?.dataTransfer.getData("application/nyaterm-tab");
@@ -759,20 +775,9 @@ function TabBar({
}
const fallbackTabId = draggedTabIdRef.current;
const strip = tabStripRef.current;
if (fallbackTabId && strip && event.clientX !== 0) {
const rect = strip.getBoundingClientRect();
const horizontalTolerance = 48;
const verticalTolerance = 16;
const isNearTabStrip =
event.clientX >= rect.left - horizontalTolerance &&
event.clientX <= rect.right + horizontalTolerance &&
event.clientY >= rect.top - verticalTolerance &&
event.clientY <= rect.bottom + verticalTolerance;
if (isNearTabStrip) {
handleDropAtIndex(getInsertionIndexFromClientX(event.clientX));
return;
}
if (fallbackTabId && isDragEndFallbackNearTabStrip(event.clientX, event.clientY)) {
handleDropAtIndex(getInsertionIndexFromClientX(event.clientX));
return;
}
resetDragState();