From ba6e283d5d8be3dc8d1fa2a25d04d70ae432c357 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8C=85=E5=91=A8=E6=B6=9B?= Date: Wed, 8 Jul 2026 21:27:42 -0700 Subject: [PATCH] fix(plugin-gantt): taller bars, split resize/progress grips, no drawer on bar click (#2352) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Bar height ~27px (even 28px keeps exact centering / link-anchor alignment) - Length (resize) grips live in the bar's top band, progress grip in the bottom band, so the two hit areas never overlap (dhtmlx-style) - Clicking a bar only selects it; the detail drawer stays reachable via the task-name column, context menu, and keyboard Enter (avoids mis-tap 易误触) --- packages/plugin-gantt/src/GanttView.tsx | 36 +++++++++++++++++++------ 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/packages/plugin-gantt/src/GanttView.tsx b/packages/plugin-gantt/src/GanttView.tsx index 880be0890..01befc8e5 100644 --- a/packages/plugin-gantt/src/GanttView.tsx +++ b/packages/plugin-gantt/src/GanttView.tsx @@ -2220,8 +2220,16 @@ export function GanttView({ // Shared row geometry: bars/diamonds/brackets and link anchors must agree // on these or arrows visibly miss their targets. - const barTop = Math.round(rowHeight * 0.2); // task bar inset from the row top + // Task bar geometry. Target a ~27px-tall bar (dhtmlx-like) so its top edge can + // host the length (resize) grips and its bottom edge the progress grip without + // the two hit areas overlapping. `barTop` is kept an integer and `barHeight` + // derived as `rowHeight - 2*barTop`, so the bar stays *exactly* centered + // (link anchors assume rowHeight/2) with no sub-pixel drift. + const barTop = Math.max(2, Math.round(rowHeight / 2) - 14); // bar inset from the row top const barHeight = rowHeight - barTop * 2; + // Split the bar vertically: top band drives length (resize), bottom band drives + // progress — so the two grips never compete for the same pixels. + const resizeHandleHeight = Math.round(barHeight / 2); const milestoneSize = Math.max(Math.round(rowHeight * 0.4), 12); // The diamond is a square rotated 45° around its center at the task date; // its horizontal tips sit half a diagonal out from that center. @@ -3341,7 +3349,11 @@ export function GanttView({ }} onClick={() => { if (suppressNextClickRef.current) return; - onTaskClick?.(task); + // Clicking the bar only selects it — opening the detail + // drawer is reserved for the task-name column, the + // context menu, and keyboard Enter, so a mis-tap while + // aiming to drag never pops the side panel (易误触). + setSelectedTaskId(task.id); }} onContextMenu={(e) => openContextMenu(task, e)} > @@ -3403,7 +3415,11 @@ export function GanttView({ onMouseLeave={() => setHoveredTaskId((cur) => (cur === task.id ? null : cur))} onClick={() => { if (suppressNextClickRef.current) return; - onTaskClick?.(task); + // Clicking the bar only selects it — opening the detail + // drawer is reserved for the task-name column, the + // context menu, and keyboard Enter, so a mis-tap while + // aiming to drag never pops the side panel (易误触). + setSelectedTaskId(task.id); }} onContextMenu={(e) => openContextMenu(task, e)} onPointerMove={captureLinkTarget} @@ -3470,7 +3486,11 @@ export function GanttView({ data-testid={`gantt-task-bar-${task.id}`} onClick={() => { if (suppressNextClickRef.current) return; - onTaskClick?.(task); + // Clicking the bar only selects it — opening the detail + // drawer is reserved for the task-name column, the + // context menu, and keyboard Enter, so a mis-tap while + // aiming to drag never pops the side panel (易误触). + setSelectedTaskId(task.id); }} onContextMenu={(e) => openContextMenu(task, e)} onPointerMove={captureLinkTarget} @@ -3485,8 +3505,8 @@ export function GanttView({ {canDrag && liveStyle.width >= 14 && ( <>
{ if (e.button !== 0) return; @@ -3495,8 +3515,8 @@ export function GanttView({ onClick={(e) => e.stopPropagation()} />
{ if (e.button !== 0) return;