gui: sidebar tree UX fixes — has_children, preserve expanded, double-click, DnD visual
- Backend: add HasChildren field to NodeDTO; ListWorkspaceTree/ListChildren populate it by querying CountChildren for container types - Node repository: add CountChildren(parentID, types...) method - TreeNode: toggle shown only when has_children (not isContainer); double-click on row = expand/collapse; icon click = expand/collapse; drop-valid class via DOM classList for DnD highlight; auto-expand collapsed container on 500ms hover during drag; auto-scroll near edge during drag - App.svelte: selectNode no longer resets workspaceTree/expanded; new reloadTreePreservingExpanded() helper re-fetches children for all expanded nodes after DnD move / delete; deleteWorkspaceNode preserves expanded state
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"verstak/internal/core/storage"
|
||||
@@ -168,6 +169,22 @@ func (r *Repository) ListRoots(includeDeleted bool) ([]Node, error) {
|
||||
return scanNodes(rows)
|
||||
}
|
||||
|
||||
// CountChildren returns the number of non-deleted children for a parent,
|
||||
// optionally filtered by one or more types.
|
||||
func (r *Repository) CountChildren(parentID string, types ...string) (int, error) {
|
||||
q := `SELECT COUNT(*) FROM nodes WHERE parent_id = ? AND deleted_at IS NULL`
|
||||
args := []interface{}{parentID}
|
||||
if len(types) > 0 {
|
||||
q += " AND type IN (" + strings.Repeat("?,", len(types)-1) + "?)"
|
||||
for _, t := range types {
|
||||
args = append(args, t)
|
||||
}
|
||||
}
|
||||
var count int
|
||||
err := r.db.QueryRow(q, args...).Scan(&count)
|
||||
return count, err
|
||||
}
|
||||
|
||||
// ListByParent returns children as *Node pointers. parentID must not be empty.
|
||||
func (r *Repository) ListByParent(parentID string) ([]*Node, error) {
|
||||
rows, err := r.db.Query(
|
||||
|
||||
Reference in New Issue
Block a user