super-buckets, dnd insert cursor for cards + buckets, drag-state survives polls
Super-buckets: a bucket can act as a dropdown of nested children, 1-level deep, created by dragging one bucket onto the center of another. The parent shows up as a single tile in the bar with a stack icon and the active member's name; clicking opens a popover that lists parent + all children. Selecting an entry sets state.activeBucket. Implementation: * src/Database.php — ALTER TABLE buckets ADD COLUMN parent_id + index. * public/index.php — PATCH /api/buckets accepts parent_id with depth / cycle / collab guards. DELETE on a parent un-nests its children rather than cascade-deleting. The /api/import handler does a second pass to remap parent_id through the new bucket id map so backup + restore round-trips the hierarchy. * public/js/modules/buckets.js renderBar() — top-level buckets only; buckets with ≥1 child render as a `.bucket-super` tile with data-bucket-super=<parent.id> and data-super-shown=<active member>. Three drop zones per tile: left/right 25% → reorder via insert cursor, center 50% → nest (PATCH parent_id) with .bucket-nest-target outline. canNestInto() mirrors the backend rules client-side. openSuperDropdown() renders a popover under the tile with parent + children as real .bucket-tab[data-bucket=…] entries (so the collab plugin's MutationObserver still picks them up); appended inside #bucket-bar but `position:fixed` so the bar's overflow:auto can't clip it. showBucketCtx() now scopes move-left/right to siblings (top-level peers OR same parent_id) so the action works in both the bar and the dropdown. Drag-and-drop insertion cursor for cards (matches the bucket fix): * public/js/modules/board.js bindCardEvents() — single #card-drop-cursor element inside #board, repositioned by per-card dragover. Three zones (left/right 25% → cursor in gap, center 50% → card-drop-group highlight). dragleave does NOT hide the cursor (was flickering as the mouse moved between adjacent cards); only dragend clears it. handleCardDrop recomputes the drop zone from e.clientX since the cursor approach no longer tags the target with .card-drop-before / .card-drop-after. * "Vor der ersten Card" works automatically: the first card's left 25% positions the cursor at offsetLeft - 6, which sits inside #board's px-6 padding and stays visible. * public/css/style.css — old .card-drop-before::before / .card-drop-after::after pseudo-elements removed in favour of the #card-drop-cursor rule. Drag ghost survives background polls: * board.js draggingCardIds: Set<string> at module scope. dragstart fills it; dragend clears. renderBoard() re-applies the .card-dragging class to every member of the set after every render so caldav / collab polls calling _reloadAll mid-drag don't visually pop the source card back to its normal state. Tests (public/plugins/tests/super-buckets.test.js): * API parent_id: nest, un-nest, self-parent rejected, level-2 rejected (bucket with own children, target is itself a child), DELETE un-nests, export → import preserves parent_id. * Bar rendering: super-tile DOM markers, label = parent name when no child active, label = active child name when selected, popover opens with parent + children entries, click child sets activeBucket, click super-tile twice toggles popover closed, plain bucket stays plain. * Drop cursor element exists. * Both describe blocks reset state.activeBucket = 'all' in afterEach — the rendering tests set it to a child id and the leak was breaking downstream Card: Due Date & Done / Card: Grouping tests by filtering every newly-created card with bucket_id=null off the board. chore: bump version to 0.9.3 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
2059e50ce3
commit
08f73dfd19
8 files changed
+696
-51
No files matched your search
+54
-11
@@ -426,21 +426,25 @@ body::after {
|
||||
pointer-events: none; z-index: 10;
|
||||
}
|
||||
|
||||
/* Drop indicators — thick glowing bar */
|
||||
.card-drop-before::before,
|
||||
.card-drop-after::after {
|
||||
content: '';
|
||||
/* Card drop cursor — single absolutely-positioned vertical line that
|
||||
lives inside #board (which is `position: relative`) and gets
|
||||
repositioned by the dragover handlers in `board.js`. Replaces the
|
||||
old `.card-drop-before::before` / `.card-drop-after::after` pseudo-
|
||||
elements so the indicator is a real DOM node in the gap between cards
|
||||
instead of a sliver glued to one card. The "before first card"
|
||||
case works automatically: the first card's left-25% zone positions
|
||||
the cursor at `card.offsetLeft - 6`, which sits in #board's left
|
||||
padding and stays visible. */
|
||||
#card-drop-cursor {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
bottom: 8px;
|
||||
width: 4px;
|
||||
background: var(--t-accent);
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 0 12px var(--t-accent), 0 0 4px var(--t-accent);
|
||||
border-radius: 2px;
|
||||
pointer-events: none;
|
||||
z-index: 10;
|
||||
display: none;
|
||||
}
|
||||
.card-drop-before::before { left: -6px; }
|
||||
.card-drop-after::after { right: -6px; }
|
||||
|
||||
/* Group fan preview */
|
||||
.group-fan {
|
||||
@@ -665,8 +669,47 @@ body.detail-image-fs-open { overflow: hidden; }
|
||||
}
|
||||
.bucket-add.drag-over i,
|
||||
.bucket-add.drag-over span { color: inherit !important; }
|
||||
.bucket-drop-left { border-left: 3px solid var(--t-accent) !important; }
|
||||
.bucket-drop-right { border-right: 3px solid var(--t-accent) !important; }
|
||||
/* Bucket reorder drop cursor — a single absolutely-positioned vertical
|
||||
line that lives inside #bucket-bar and gets repositioned by the
|
||||
dragover handler in `public/js/modules/buckets.js`. Showing the cursor
|
||||
*between* tiles makes the insert point unambiguous next to the
|
||||
super-bucket nest gesture (center 50% → nest, edges 25% → reorder). */
|
||||
#bucket-drop-cursor {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
bottom: 4px;
|
||||
width: 3px;
|
||||
background: var(--t-accent);
|
||||
box-shadow: 0 0 6px color-mix(in srgb, var(--t-accent) 70%, transparent);
|
||||
border-radius: 1px;
|
||||
pointer-events: none;
|
||||
z-index: 4;
|
||||
display: none;
|
||||
}
|
||||
/* Drop-into-center highlight: target tile gets an outline that says
|
||||
"release here to nest". Distinct from the insertion cursor so the
|
||||
user can tell reorder vs. nest at a glance. */
|
||||
.bucket-tab.bucket-nest-target {
|
||||
outline: 2px dashed var(--t-accent) !important;
|
||||
outline-offset: 1px;
|
||||
background: color-mix(in srgb, var(--t-accent) 12%, transparent) !important;
|
||||
}
|
||||
|
||||
/* Super-bucket dropdown popover. Positioned via inline styles by
|
||||
openSuperDropdown() in buckets.js. Lives on document.body (not
|
||||
inside #bucket-bar) so it can escape the bar's overflow:auto. */
|
||||
#bucket-super-popover {
|
||||
z-index: 220;
|
||||
background: var(--t-surface);
|
||||
border: 1px solid var(--t-border);
|
||||
border-radius: var(--ui-radius-modal);
|
||||
box-shadow: var(--ui-shadow-modal, 0 10px 30px rgba(0,0,0,0.4));
|
||||
padding: 4px;
|
||||
min-width: 180px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
/* Tag chips */
|
||||
.tag-chip {
|
||||
|
||||
Reference in new issue
Block a user