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>
56 lines
1.6 KiB
JavaScript
56 lines
1.6 KiB
JavaScript
// ── hyprcrd Test Index ──
|
|
//
|
|
// Loads all test suites via dynamic imports with a shared cache-busting
|
|
// query string. This ensures edits to any test file are picked up without
|
|
// requiring a hard refresh — each module URL becomes unique per page load.
|
|
|
|
const V = '?v=' + Date.now();
|
|
|
|
const FILES = [
|
|
'./shell.test.js',
|
|
'./settings.test.js',
|
|
'./board.test.js',
|
|
'./toolbar.test.js',
|
|
'./buckets.test.js',
|
|
'./bucket-context.test.js',
|
|
'./super-buckets.test.js',
|
|
'./context-menu.test.js',
|
|
'./card-interactions.test.js',
|
|
'./filtering.test.js',
|
|
'./sorting.test.js',
|
|
'./state-functions.test.js',
|
|
'./favorites.test.js',
|
|
'./search.test.js',
|
|
'./search-logic.test.js',
|
|
'./calendar.test.js',
|
|
'./calendar-popup.test.js',
|
|
'./rrule.test.js',
|
|
'./datepicker.test.js',
|
|
'./gallery.test.js',
|
|
'./help.test.js',
|
|
'./multiselect.test.js',
|
|
'./detail.test.js',
|
|
'./detail-interactions.test.js',
|
|
'./editor.test.js',
|
|
'./editor-features.test.js',
|
|
'./editor-save.test.js',
|
|
'./editor-toolbar.test.js',
|
|
'./line-numbers.test.js',
|
|
'./canvas.test.js',
|
|
'./snippets.test.js',
|
|
'./image-fullscreen.test.js',
|
|
'./markdown.test.js',
|
|
'./markdown-edge.test.js',
|
|
'./undo.test.js',
|
|
'./api.test.js',
|
|
'./api-edge.test.js',
|
|
'./export.test.js',
|
|
'./relay.test.js',
|
|
'./notifications.test.js',
|
|
'./quick-capture.test.js',
|
|
'./theme.test.js',
|
|
];
|
|
|
|
// Await all imports so the runner can start in a deterministic state.
|
|
await Promise.all(FILES.map(f => import(f + V)));
|