Fix structure tree drag feedback: stable rows, on-row child drops

Rework the site structure tree's drag-and-drop to match Sortable's native
model instead of mid-drag drop zones:

- Drop the dashed empty-list drop zones that appeared on drag start
  (flicker, space reservation, and mis-targeted drops: the last item's
  child zone sat exactly where 'end of parent list' was).
- Every non-empty list (and the root) ends with a non-draggable  footer
  row (vuedraggable #footer slot): click to start a new page at that
  level, and while dragging it is the 'end of this list' drop target.
  It renders last even mid-drag via flex order, since Sortable appends
  end-of-list previews after it in the DOM.
- Dropping ON the lower part of a row makes the page that row's first
  child: the child list's container invisibly overlaps its own row's
  bottom (negative margin + equal padding, no layout effect) and becomes
  hit-testable only while dragging. A row's exposed top strip remains
  the 'sibling before' target. Leaf rows work too, creating a sublist.
- Indentation is structural (each nested list margin-indents itself), so
  a dragged row previews its whole subtree at the target list's depth.
  Rows get a little vertical padding to widen the drop zones.
This commit is contained in:
2026-08-16 18:05:33 +00:00
parent e38063a522
commit 3dc438f8df
5 changed files with 121 additions and 61 deletions
+11 -7
View File
@@ -167,14 +167,18 @@ evolves.
insert `![alt](/_/f/hash.ext)` at the cursor.
- The **structure panel** (vue-draggable tree of the whole site, in site
mode) covers page management: reorder any menu level, drag across
sections (empty child lists appear as drop zones while dragging), add,
delete (two clicks: the button arms, then deletes — no dialogs).
Every node is a real label — content-less category rows offer a
to give them a landing page.
sections, add, delete (two clicks: the button arms, then deletes — no
dialogs). Every node is a real label — content-less category rows offer
a to give them a landing page.
Deleting a category removes only its landing page (the label and its
subpages stay). The in the panel header starts a new page as a
local-only tree row that can be dragged into place before its title and
slug are filled in; it is persisted only on commit. Rows are always
subpages stay). Every non-empty list ends with a row that starts a
new page as a local-only tree row at that level; the row can be dragged
into place before its title and slug are filled in and is persisted only
on commit. While dragging, these rows double as "end of this list"
drop targets; dropping ON the lower part of a row makes the page that
row's first child (even a leaf's, creating a sublist), while a row's
exposed top edge inserts a sibling before it. A dragged row's
indentation previews the target list's depth. Rows are always
editable: titles save while typing, slug edits commit on blur/Enter
since they rename the path (moving the whole subtree). The front page
is the root row with an empty slug — renaming it away leaves no front