feat(sidebar): preview workspace board on card drag start with trigger cue (#5777)
This commit is contained in:
parent
692d8d0124
commit
238476f441
|
|
@ -1426,6 +1426,29 @@
|
|||
opacity: 0.42;
|
||||
}
|
||||
|
||||
/* Why: while a card drag previews the board, pulse the toolbar trigger with the
|
||||
same ring the drawer uses so it reads as another way to open the board. */
|
||||
@keyframes workspace-board-trigger-preview-pulse {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 color-mix(in srgb, var(--sidebar-ring) 55%, transparent);
|
||||
}
|
||||
|
||||
70%,
|
||||
100% {
|
||||
box-shadow: 0 0 0 5px color-mix(in srgb, var(--sidebar-ring) 0%, transparent);
|
||||
}
|
||||
}
|
||||
|
||||
[data-workspace-board-trigger][data-workspace-board-preview='true'] {
|
||||
animation: workspace-board-trigger-preview-pulse 1.4s ease-out infinite;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
[data-workspace-board-trigger][data-workspace-board-preview='true'] {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes workspace-kanban-sheet-in {
|
||||
from {
|
||||
clip-path: inset(0 100% 0 0);
|
||||
|
|
|
|||
|
|
@ -13,11 +13,13 @@ const WORKSPACE_BOARD_MOVED_HINT_DURATION_MS = 12000
|
|||
|
||||
type SidebarToolbarProps = {
|
||||
workspaceBoardOpen: boolean
|
||||
workspaceBoardDragPreviewOpen?: boolean
|
||||
onWorkspaceBoardToggle: () => void
|
||||
}
|
||||
|
||||
const SidebarToolbar = React.memo(function SidebarToolbar({
|
||||
workspaceBoardOpen,
|
||||
workspaceBoardDragPreviewOpen = false,
|
||||
onWorkspaceBoardToggle
|
||||
}: SidebarToolbarProps) {
|
||||
const [workspaceBoardMovedHintOpen, setWorkspaceBoardMovedHintOpen] = React.useState(false)
|
||||
|
|
@ -69,7 +71,11 @@ const SidebarToolbar = React.memo(function SidebarToolbar({
|
|||
<Tooltip open={workspaceBoardMovedHintOpen ? true : undefined}>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant={workspaceBoardOpen ? 'secondary' : 'ghost'}
|
||||
// Why: previewing the board from a card drag lights up the
|
||||
// trigger so it's clear the drag is another way to open it.
|
||||
variant={
|
||||
workspaceBoardOpen || workspaceBoardDragPreviewOpen ? 'secondary' : 'ghost'
|
||||
}
|
||||
size="icon-xs"
|
||||
type="button"
|
||||
aria-label={translate(
|
||||
|
|
@ -78,6 +84,7 @@ const SidebarToolbar = React.memo(function SidebarToolbar({
|
|||
)}
|
||||
aria-pressed={workspaceBoardOpen}
|
||||
data-workspace-board-trigger=""
|
||||
data-workspace-board-preview={workspaceBoardDragPreviewOpen ? 'true' : undefined}
|
||||
onClick={handleWorkspaceBoardClick}
|
||||
className="text-muted-foreground"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -159,7 +159,6 @@ import {
|
|||
setSidebarPointerDragDocumentStyles,
|
||||
updateSidebarDragPreviewPosition
|
||||
} from './worktree-sidebar-pointer-drag-dom'
|
||||
import { shouldStartWorkspaceBoardDragPreview } from './workspace-board-drag-preview-intent'
|
||||
import {
|
||||
getWorktreeSidebarDragAutoscroll,
|
||||
getWorktreeSidebarDragRectsForGroup,
|
||||
|
|
@ -2180,24 +2179,16 @@ const VirtualizedWorktreeViewport = React.memo(function VirtualizedWorktreeViewp
|
|||
clearWorktreeDrag()
|
||||
return
|
||||
}
|
||||
const previewSidebarContainer = scrollRef.current
|
||||
// Why: reveal the companion board preview the moment a card drag begins so the
|
||||
// user sees the drop target on the right and can choose whether to aim for it,
|
||||
// rather than discovering it only after dragging into the sidebar edge.
|
||||
if (
|
||||
!drag.workspaceBoardDragPreviewRequested &&
|
||||
!workspaceBoardOpen &&
|
||||
previewSidebarContainer
|
||||
!hasWorkspaceKanbanSidebarDropBoard()
|
||||
) {
|
||||
const sidebarRect = previewSidebarContainer.getBoundingClientRect()
|
||||
if (
|
||||
shouldStartWorkspaceBoardDragPreview({
|
||||
pointerX: drag.currentX,
|
||||
startX: drag.startX,
|
||||
sidebarRight: sidebarRect.right
|
||||
}) &&
|
||||
!hasWorkspaceKanbanSidebarDropBoard()
|
||||
) {
|
||||
drag.workspaceBoardDragPreviewRequested = true
|
||||
onWorkspaceBoardDragPreviewStart()
|
||||
}
|
||||
drag.workspaceBoardDragPreviewRequested = true
|
||||
onWorkspaceBoardDragPreviewStart()
|
||||
}
|
||||
const boardTarget = updateWorkspaceKanbanSidebarDropTargetVisual({
|
||||
x: drag.currentX,
|
||||
|
|
|
|||
|
|
@ -153,6 +153,7 @@ function Sidebar({
|
|||
{/* Fixed bottom toolbar */}
|
||||
<SidebarToolbar
|
||||
workspaceBoardOpen={workspaceBoardOpen}
|
||||
workspaceBoardDragPreviewOpen={workspaceBoardDragPreviewOpen}
|
||||
onWorkspaceBoardToggle={toggleWorkspaceBoard}
|
||||
/>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
import { shouldStartWorkspaceBoardDragPreview } from './workspace-board-drag-preview-intent'
|
||||
|
||||
describe('workspace board drag preview intent', () => {
|
||||
it('does not start the expensive board preview for small sidebar reorder drags', () => {
|
||||
expect(
|
||||
shouldStartWorkspaceBoardDragPreview({
|
||||
pointerX: 132,
|
||||
startX: 96,
|
||||
sidebarRight: 285
|
||||
})
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('starts the board preview after a rightward drag reaches the sidebar edge zone', () => {
|
||||
expect(
|
||||
shouldStartWorkspaceBoardDragPreview({
|
||||
pointerX: 252,
|
||||
startX: 96,
|
||||
sidebarRight: 285
|
||||
})
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('ignores edge-zone drags that did not move right enough to signal board intent', () => {
|
||||
expect(
|
||||
shouldStartWorkspaceBoardDragPreview({
|
||||
pointerX: 252,
|
||||
startX: 242,
|
||||
sidebarRight: 285
|
||||
})
|
||||
).toBe(false)
|
||||
})
|
||||
})
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
const WORKSPACE_BOARD_DRAG_PREVIEW_EDGE_ZONE_PX = 48
|
||||
const WORKSPACE_BOARD_DRAG_PREVIEW_MIN_RIGHTWARD_PX = 16
|
||||
|
||||
export function shouldStartWorkspaceBoardDragPreview(args: {
|
||||
pointerX: number
|
||||
startX: number
|
||||
sidebarRight: number
|
||||
}): boolean {
|
||||
return (
|
||||
args.pointerX >= args.sidebarRight - WORKSPACE_BOARD_DRAG_PREVIEW_EDGE_ZONE_PX &&
|
||||
args.pointerX - args.startX >= WORKSPACE_BOARD_DRAG_PREVIEW_MIN_RIGHTWARD_PX
|
||||
)
|
||||
}
|
||||
Loading…
Reference in New Issue