fix(library+workspace): arborescence dans Workspace tab, icônes drag+multi-select visibles, inline rename au clic
- Library: ajout /api/library/children/{id} pour charger les enfants à la demande
- Library: drag handle (.drag-handle) et checkbox (.lib-checkbox) toujours visibles
- Library: SortableJS init pour drag-and-drop réordonner les rows
- Library: toggleExpand() async avec chargement des enfants depuis l'API
- Local workspace: drag handle 6-dots + checkbox toujours visible dans les tree items
- Local workspace: renderChildren() inclut checkbox + drag handle + inline rename
- Local workspace: clic sur nom de fichier = inline rename (plus navigation directe)
- Local workspace: bouton Open pour ouvrir le fichier (double-clic aussi)
- CSS: .item-checkbox toujours visible (plus opacity:0)
- CSS: .drag-handle avec opacité .45 par défaut, 1.0 au hover
This commit is contained in:
@@ -70,6 +70,7 @@ def _build_item(db_row: dict, uid: int = 1) -> dict:
|
|||||||
"id": page_id,
|
"id": page_id,
|
||||||
"title": title,
|
"title": title,
|
||||||
"icon": icon,
|
"icon": icon,
|
||||||
|
"is_folder": bool(db_row.get("is_folder", 0)),
|
||||||
"source_type": source_type,
|
"source_type": source_type,
|
||||||
"source_label": _source_label(source_type, workspace),
|
"source_label": _source_label(source_type, workspace),
|
||||||
"workspace": workspace,
|
"workspace": workspace,
|
||||||
@@ -254,6 +255,22 @@ async def library_private(
|
|||||||
return {"items": items}
|
return {"items": items}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/children/{page_id:int}")
|
||||||
|
async def library_children(page_id: int, request: Request):
|
||||||
|
"""Return child pages for a given parent page (for tree expansion in Library)."""
|
||||||
|
uid = _get_user_id(request)
|
||||||
|
with get_conn() as conn:
|
||||||
|
rows = conn.execute(
|
||||||
|
f"{BASE_SELECT}, p.is_folder, (SELECT COUNT(*) FROM pages c WHERE c.parent_id = p.id AND c.deleted_at IS NULL) as child_count "
|
||||||
|
f"FROM pages p WHERE p.parent_id = ? AND p.deleted_at IS NULL "
|
||||||
|
f"ORDER BY p.is_folder DESC, p.title COLLATE NOCASE",
|
||||||
|
[page_id],
|
||||||
|
).fetchall()
|
||||||
|
items = _rows_to_items(rows, uid)
|
||||||
|
_enrich_children(items)
|
||||||
|
return {"items": items}
|
||||||
|
|
||||||
|
|
||||||
@router.get("/workspace")
|
@router.get("/workspace")
|
||||||
async def library_workspace(
|
async def library_workspace(
|
||||||
request: Request,
|
request: Request,
|
||||||
|
|||||||
+79
-10
@@ -58,17 +58,17 @@
|
|||||||
.lib-row .row-cell{display:flex;align-items:center;padding:0 8px;height:100%;overflow:hidden;}
|
.lib-row .row-cell{display:flex;align-items:center;padding:0 8px;height:100%;overflow:hidden;}
|
||||||
|
|
||||||
/* Hover-only elements */
|
/* Hover-only elements */
|
||||||
.lib-row .hover-only{opacity:0;transition:opacity 0.1s;}
|
.lib-row .hover-only{opacity:0.35;transition:opacity 0.15s;}
|
||||||
.lib-row:hover .hover-only{opacity:1;}
|
.lib-row:hover .hover-only{opacity:1;}
|
||||||
|
|
||||||
/* Drag handle — 6 dots */
|
/* Drag handle — 6 dots */
|
||||||
.drag-handle{display:flex;flex-wrap:wrap;width:14px;height:14px;cursor:grab;align-content:space-between;justify-content:space-between;flex-shrink:0;}
|
.drag-handle{display:flex;flex-wrap:wrap;width:16px;height:16px;cursor:grab;align-content:space-between;justify-content:space-between;flex-shrink:0;opacity:0.45;}
|
||||||
.drag-handle:hover{opacity:1!important;}
|
.lib-row:hover .drag-handle{opacity:1;}
|
||||||
.drag-handle .dot{width:3px;height:3px;background:var(--text-tertiary);border-radius:50%;}
|
.drag-handle .dot{width:3px;height:3px;background:var(--text-tertiary);border-radius:50%;}
|
||||||
|
|
||||||
/* Checkbox */
|
/* Checkbox */
|
||||||
.lib-checkbox{width:14px;height:14px;border:1.5px solid var(--text-tertiary);border-radius:3px;cursor:pointer;flex-shrink:0;display:flex;align-items:center;justify-content:center;transition:all 0.1s;}
|
.lib-checkbox{width:15px;height:15px;border:1.5px solid var(--text-tertiary);border-radius:3px;cursor:pointer;flex-shrink:0;display:flex;align-items:center;justify-content:center;transition:all 0.15s;}
|
||||||
.lib-checkbox:hover{border-color:var(--text-primary);}
|
.lib-row:hover .lib-checkbox{border-color:var(--text-secondary);}
|
||||||
.lib-checkbox.checked{background:var(--accent);border-color:var(--accent);}
|
.lib-checkbox.checked{background:var(--accent);border-color:var(--accent);}
|
||||||
.lib-checkbox.checked::after{content:'✓';color:#fff;font-size:10px;font-weight:700;}
|
.lib-checkbox.checked::after{content:'✓';color:#fff;font-size:10px;font-weight:700;}
|
||||||
|
|
||||||
@@ -204,7 +204,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ── Table ── -->
|
<!-- ── Table ── -->
|
||||||
<div class="lib-table" x-show="!loading && flatItems.length > 0">
|
<div class="lib-table" x-show="!loading && flatItems.length > 0" id="lib-table">
|
||||||
<div class="lib-table-header">
|
<div class="lib-table-header">
|
||||||
<div class="th lib-col-drag"></div>
|
<div class="th lib-col-drag"></div>
|
||||||
<div class="th lib-col-select">
|
<div class="th lib-col-select">
|
||||||
@@ -217,9 +217,9 @@
|
|||||||
<div class="th lib-col-visited sortable" @click="sortBy='visited';doSort()">Last visited</div>
|
<div class="th lib-col-visited sortable" @click="sortBy='visited';doSort()">Last visited</div>
|
||||||
<div class="th lib-col-open"></div>
|
<div class="th lib-col-open"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="lib-table-body">
|
<div class="lib-table-body" id="lib-table-body">
|
||||||
<template x-for="(item, idx) in flatItems" :key="item.id">
|
<template x-for="(item, idx) in flatItems" :key="item.id">
|
||||||
<div class="lib-row" :class="{ selected: !!selected[item.id] }">
|
<div class="lib-row" :class="{ selected: !!selected[item.id] }" :data-lib-id="item.id">
|
||||||
<div class="row-cell lib-col-drag">
|
<div class="row-cell lib-col-drag">
|
||||||
<div class="drag-handle hover-only" style="margin:auto;" title="Drag to move">
|
<div class="drag-handle hover-only" style="margin:auto;" title="Drag to move">
|
||||||
<span class="dot"></span><span class="dot"></span>
|
<span class="dot"></span><span class="dot"></span>
|
||||||
@@ -484,9 +484,39 @@ function libraryPage() {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// ── Tree expand ──
|
// ── Tree expand ──
|
||||||
toggleExpand(item) {
|
async toggleExpand(item) {
|
||||||
this.expanded[item.id] = !this.expanded[item.id];
|
if (this.expanded[item.id]) {
|
||||||
|
// Collapse
|
||||||
|
delete this.expanded[item.id];
|
||||||
|
this.expanded = Object.assign({}, this.expanded);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Expand — load children if not loaded yet
|
||||||
|
this.expanded[item.id] = true;
|
||||||
this.expanded = Object.assign({}, this.expanded);
|
this.expanded = Object.assign({}, this.expanded);
|
||||||
|
if (!item.children || !item.children.length) {
|
||||||
|
try {
|
||||||
|
var r = await fetch('/api/library/children/' + item.id);
|
||||||
|
var d = await r.json();
|
||||||
|
item.children = d.items || [];
|
||||||
|
// Mark the tree for depth calculation
|
||||||
|
if (typeof this._markChildren === 'function') {
|
||||||
|
this._markChildren(item.children, (item.depth || 0) + 1);
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
item.children = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_markChildren(nodes, depth) {
|
||||||
|
if (!nodes) return;
|
||||||
|
for (var i = 0; i < nodes.length; i++) {
|
||||||
|
nodes[i].depth = depth;
|
||||||
|
if (nodes[i].children && nodes[i].children.length > 0) {
|
||||||
|
this._markChildren(nodes[i].children, depth + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// ── Selection ──
|
// ── Selection ──
|
||||||
@@ -712,5 +742,44 @@ function libraryPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
// SortableJS — drag-and-drop reorder in library table
|
||||||
|
document.addEventListener('alpine:init', function() {
|
||||||
|
var initSortable = function() {
|
||||||
|
var body = document.getElementById('lib-table-body');
|
||||||
|
if (!body || typeof Sortable === 'undefined') return;
|
||||||
|
// Clean up any existing instance
|
||||||
|
if (body._sortable) body._sortable.destroy();
|
||||||
|
body._sortable = new Sortable(body, {
|
||||||
|
handle: '.drag-handle',
|
||||||
|
animation: 150,
|
||||||
|
ghostClass: 'sortable-ghost',
|
||||||
|
onEnd: async function(evt) {
|
||||||
|
var itemEl = evt.item;
|
||||||
|
var itemId = itemEl.getAttribute('data-lib-id');
|
||||||
|
var prevEl = evt.item.previousElementSibling;
|
||||||
|
var nextEl = evt.item.nextElementSibling;
|
||||||
|
var prevId = prevEl ? prevEl.getAttribute('data-lib-id') : null;
|
||||||
|
var nextId = nextEl ? nextEl.getAttribute('data-lib-id') : null;
|
||||||
|
if (!itemId) return;
|
||||||
|
try {
|
||||||
|
await fetch('/api/pages/' + itemId + '/move', {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: JSON.stringify({before_id: nextId || null, after_id: prevId || null})
|
||||||
|
});
|
||||||
|
} catch(e) {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// Watch for table body updates with MutationObserver
|
||||||
|
var target = document.getElementById('lib-table');
|
||||||
|
if (target) {
|
||||||
|
var observer = new MutationObserver(function() {
|
||||||
|
setTimeout(initSortable, 200);
|
||||||
|
});
|
||||||
|
observer.observe(target, {childList: true, subtree: true});
|
||||||
|
}
|
||||||
|
initSortable();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -79,8 +79,7 @@
|
|||||||
.ws-more-item:hover{opacity:1;background:var(--bg-tertiary);}
|
.ws-more-item:hover{opacity:1;background:var(--bg-tertiary);}
|
||||||
|
|
||||||
/* Multi-select */
|
/* Multi-select */
|
||||||
.item-checkbox{width:14px;height:14px;flex-shrink:0;cursor:pointer;accent-color:var(--accent);opacity:0;transition:opacity 150ms;}
|
.item-checkbox{width:15px;height:15px;flex-shrink:0;cursor:pointer;accent-color:var(--accent);margin:0 4px;}
|
||||||
.ws-tree-item:hover .item-checkbox,.ws-tree-item.selected .item-checkbox{opacity:1;}
|
|
||||||
.ws-tree-item.selected{background:rgba(35,131,226,.1);}
|
.ws-tree-item.selected{background:rgba(35,131,226,.1);}
|
||||||
|
|
||||||
/* Bulk bar */
|
/* Bulk bar */
|
||||||
@@ -118,6 +117,10 @@
|
|||||||
|
|
||||||
/* Drag & drop */
|
/* Drag & drop */
|
||||||
.ws-layout.drop-active { outline: 2px dashed var(--accent); outline-offset: -4px; border-radius: 8px; }
|
.ws-layout.drop-active { outline: 2px dashed var(--accent); outline-offset: -4px; border-radius: 8px; }
|
||||||
|
/* Drag handle — 6 dots */
|
||||||
|
.drag-handle{display:flex;flex-wrap:wrap;width:16px;height:16px;cursor:grab;align-content:space-between;justify-content:space-between;flex-shrink:0;opacity:0.45;margin:0 4px;}
|
||||||
|
.ws-tree-item:hover .drag-handle{opacity:1;}
|
||||||
|
.drag-handle .dot{width:3px;height:3px;background:var(--text-tertiary);border-radius:50%;}
|
||||||
.drop-overlay{display:none;position:fixed;inset:0;background:rgba(35,131,226,.08);z-index:100;pointer-events:none;align-items:center;justify-content:center;}
|
.drop-overlay{display:none;position:fixed;inset:0;background:rgba(35,131,226,.08);z-index:100;pointer-events:none;align-items:center;justify-content:center;}
|
||||||
.drop-overlay.active{display:flex;}
|
.drop-overlay.active{display:flex;}
|
||||||
.drop-overlay .drop-hint{background:var(--bg);border:2px dashed var(--accent);border-radius:16px;padding:40px 60px;text-align:center;color:var(--text);font-size:16px;}
|
.drop-overlay .drop-hint{background:var(--bg);border:2px dashed var(--accent);border-radius:16px;padding:40px 60px;text-align:center;color:var(--text);font-size:16px;}
|
||||||
@@ -942,34 +945,60 @@ window._wsInitData = (function() {
|
|||||||
var hasKids = c.children && c.children.length > 0;
|
var hasKids = c.children && c.children.length > 0;
|
||||||
var icon = this._fileIcon(c.name, c.is_folder, c.content_format);
|
var icon = this._fileIcon(c.name, c.is_folder, c.content_format);
|
||||||
var escName = this.esc(c.name);
|
var escName = this.esc(c.name);
|
||||||
|
var escId = c.id;
|
||||||
html += '<li><div class="ws-tree-item" style="padding-left:'+(depth*20)+'px" draggable="true"'
|
html += '<li><div class="ws-tree-item" style="padding-left:'+(depth*20)+'px" draggable="true"'
|
||||||
+ ' data-folder-id="'+(c.is_folder?c.id:'')+'"'
|
+ ' data-folder-id="'+(c.is_folder?c.id:'')+'"'
|
||||||
+ ' data-ws-id="'+c.id+'"'
|
+ ' data-ws-id="'+escId+'"'
|
||||||
+ ' ondragstart="var d=window._wsData;if(d)d.onInternalDragStart(event,'+c.id+')"'
|
+ ' ondragstart="var d=window._wsData;if(d)d.onInternalDragStart(event,'+escId+')"'
|
||||||
+ ' ondragend="event.target.style.opacity=\'\'">';
|
+ ' ondragend="event.target.style.opacity=\\'\\'">';
|
||||||
|
// Checkbox
|
||||||
|
html += '<input type="checkbox" class="item-checkbox"'
|
||||||
|
+ ' data-ws-id="'+escId+'"'
|
||||||
|
+ ' onclick="event.stopPropagation();var d=window._wsData;if(d)d.toggleSelect({id:'+escId+',name:\\''+escName+'\\'},event)"'
|
||||||
|
+ ' title="Select item">';
|
||||||
|
// Drag handle
|
||||||
|
html += '<div class="drag-handle" title="Drag to move">'
|
||||||
|
+ '<span class="dot"></span><span class="dot"></span>'
|
||||||
|
+ '<span class="dot"></span><span class="dot"></span>'
|
||||||
|
+ '<span class="dot"></span><span class="dot"></span></div>';
|
||||||
if (c.is_folder && hasKids) {
|
if (c.is_folder && hasKids) {
|
||||||
html += '<button class="ws-chevron ws-expand-btn" data-ws-id="'+c.id+'" onclick="event.stopPropagation();var d=window._wsData;if(d)d.toggleExpand('+c.id+',this)">▶</button>';
|
html += '<button class="ws-chevron ws-expand-btn" data-ws-id="'+escId+'" onclick="event.stopPropagation();var d=window._wsData;if(d)d.toggleExpand('+escId+',this)">▶</button>';
|
||||||
} else {
|
} else {
|
||||||
html += '<span style="width:16px;flex-shrink:0;"></span>';
|
html += '<span style="width:16px;flex-shrink:0;"></span>';
|
||||||
}
|
}
|
||||||
html += '<span class="icon">'+icon+'</span>';
|
html += '<span class="icon">'+icon+'</span>';
|
||||||
if (c.is_folder) {
|
if (c.is_folder) {
|
||||||
var countBadge = (c.child_count > 0) ? ' <span class="child-count">('+c.child_count+')</span>' : '';
|
var countBadge = (c.child_count > 0) ? ' <span class="child-count">('+c.child_count+')</span>' : '';
|
||||||
html += '<span class="name folder" onclick="window.location=\'/local-workspace?folder='+c.id+'\'">'+c.name+countBadge+'</span>';
|
html += '<span class="name folder" onclick="window._wsData.navigateToFolder('+escId+')">'+c.name+countBadge+'</span>';
|
||||||
} else {
|
} else {
|
||||||
html += '<span class="name file" onclick="window.location=\'/pages/'+c.id+'\'"'
|
// Click to rename, double-click or button to open
|
||||||
+ ' onmouseenter="var d=window._wsData;if(d)d.showPreview(event,{id:'+c.id+',name:\''+escName+'\',is_folder:false,content_format:\''+(c.content_format||'')+'\'})"'
|
html += '<span class="name file" onclick="event.stopPropagation();window._wsData.startRenderChildRename('+escId+',\''+escName+'\')"'
|
||||||
+ ' onmouseleave="var d=window._wsData;if(d)d.hidePreview()">'+c.name+'</span>';
|
+ ' ondblclick="window.location=\\'/pages/'+escId+'\\'"'
|
||||||
|
+ ' title="Click to rename, double-click to open"'
|
||||||
|
+ ' onmouseenter="var d=window._wsData;if(d)d.showPreview(event,{id:'+escId+',name:\\''+escName+'\\',is_folder:false,content_format:\\''+(c.content_format||'')+'\\'})"'
|
||||||
|
+ ' onmouseleave="var d=window._wsData;if(d)d.hidePreview()">'
|
||||||
|
+ '<span id="rc-name-'+escId+'">'+c.name+'</span>'
|
||||||
|
+ '<input id="rc-input-'+escId+'" class="inline-rename-input" style="display:none;width:100%"'
|
||||||
|
+ ' onkeydown="if(event.key===\\'Enter\\')window._wsData.commitRenderChildRename('+escId+')"'
|
||||||
|
+ ' onblur="window._wsData.commitRenderChildRename('+escId+')"'
|
||||||
|
+ ' onclick="event.stopPropagation()">'
|
||||||
|
+ '</span>';
|
||||||
}
|
}
|
||||||
html += '<div class="actions">';
|
html += '<div class="actions">';
|
||||||
if (c.is_folder) {
|
if (c.is_folder) {
|
||||||
html += '<span style="display:flex;gap:2px">';
|
html += '<span style="display:flex;gap:2px">';
|
||||||
html += '<button class="ws-act" data-ws-action="new-file" data-ws-id="'+c.id+'" data-ws-name="'+escName+'">📄</button>';
|
html += '<button class="ws-act" data-ws-action="new-file" data-ws-id="'+escId+'" data-ws-name="'+escName+'">📄</button>';
|
||||||
html += '<button class="ws-act" data-ws-action="new-folder" data-ws-id="'+c.id+'" data-ws-name="'+escName+'">📁</button>';
|
html += '<button class="ws-act" data-ws-action="new-folder" data-ws-id="'+escId+'" data-ws-name="'+escName+'">📁</button>';
|
||||||
html += '</span>';
|
html += '</span>';
|
||||||
}
|
}
|
||||||
html += '<button class="ws-act" data-ws-action="rename" data-ws-id="'+c.id+'" data-ws-name="'+escName+'">✏️</button>';
|
html += '<button class="ws-act" data-ws-action="rename" data-ws-id="'+escId+'" data-ws-name="'+escName+'">✏️</button>';
|
||||||
html += '<button class="ws-act danger" data-ws-action="delete" data-ws-id="'+c.id+'" data-ws-name="'+escName+'">🗑</button>';
|
html += '<button class="ws-act danger" data-ws-action="delete" data-ws-id="'+escId+'" data-ws-name="'+escName+'">🗑</button>';
|
||||||
|
// Open button for files
|
||||||
|
if (!c.is_folder) {
|
||||||
|
html += '<a href="/pages/'+escId+'" class="btn-open" style="display:inline-flex;align-items:center;gap:4px;padding:3px 10px;border:1px solid var(--border);border-radius:4px;font-size:12px;color:var(--text-secondary);background:var(--bg-tertiary);cursor:pointer;text-decoration:none;margin-left:4px;opacity:0;"'
|
||||||
|
+ ' onmouseover="this.style.opacity=\\'1\\'" onmouseout="this.style.opacity=\\'0\\'"'
|
||||||
|
+ ' onclick="event.stopPropagation();return true">⤢ Open</a>';
|
||||||
|
}
|
||||||
// Tags
|
// Tags
|
||||||
if (c.tags && c.tags.length > 0) {
|
if (c.tags && c.tags.length > 0) {
|
||||||
html += '<span style="display:flex;gap:2px;margin-left:4px;">';
|
html += '<span style="display:flex;gap:2px;margin-left:4px;">';
|
||||||
@@ -990,6 +1019,41 @@ window._wsInitData = (function() {
|
|||||||
return html;
|
return html;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Inline rename for renderChildren (static HTML)
|
||||||
|
startRenderChildRename(id, currentName) {
|
||||||
|
var nameSpan = document.getElementById('rc-name-' + id);
|
||||||
|
var input = document.getElementById('rc-input-' + id);
|
||||||
|
if (!nameSpan || !input) return;
|
||||||
|
nameSpan.style.display = 'none';
|
||||||
|
input.style.display = '';
|
||||||
|
input.value = currentName;
|
||||||
|
input.focus();
|
||||||
|
input.select();
|
||||||
|
},
|
||||||
|
|
||||||
|
async commitRenderChildRename(id) {
|
||||||
|
var nameSpan = document.getElementById('rc-name-' + id);
|
||||||
|
var input = document.getElementById('rc-input-' + id);
|
||||||
|
if (!input) return;
|
||||||
|
var newName = (input.value || '').trim();
|
||||||
|
input.style.display = 'none';
|
||||||
|
if (nameSpan) nameSpan.style.display = '';
|
||||||
|
if (!newName) return;
|
||||||
|
try {
|
||||||
|
var r = await fetch('/api/local-workspace/items/' + id, {
|
||||||
|
method: 'PUT', headers: {'Content-Type':'application/json'},
|
||||||
|
body: JSON.stringify({name: newName})
|
||||||
|
});
|
||||||
|
if (r.ok) {
|
||||||
|
if (nameSpan) nameSpan.textContent = newName;
|
||||||
|
// Update tree node
|
||||||
|
var node = this._findNodeById(this.tree, id);
|
||||||
|
if (node) { node.name = newName; }
|
||||||
|
this._reloadAfterAction();
|
||||||
|
}
|
||||||
|
} catch(e) {}
|
||||||
|
},
|
||||||
|
|
||||||
esc(s) {
|
esc(s) {
|
||||||
if (!s) return '';
|
if (!s) return '';
|
||||||
return s.replace(/\\/g,'\\\\').replace(/'/g,"\\'").replace(/"/g,'"');
|
return s.replace(/\\/g,'\\\\').replace(/'/g,"\\'").replace(/"/g,'"');
|
||||||
@@ -2166,6 +2230,12 @@ console.log('FLOWDECK v2.4.6 — IIFE done, _wsInitData keys:', Object.keys(wind
|
|||||||
:checked="!!selectedIds[node.id]"
|
:checked="!!selectedIds[node.id]"
|
||||||
@click.stop="toggleSelect(node, $event)"
|
@click.stop="toggleSelect(node, $event)"
|
||||||
title="Select item">
|
title="Select item">
|
||||||
|
<!-- drag handle -->
|
||||||
|
<div class="drag-handle" style="margin-right:2px;" title="Drag to move">
|
||||||
|
<span class="dot"></span><span class="dot"></span>
|
||||||
|
<span class="dot"></span><span class="dot"></span>
|
||||||
|
<span class="dot"></span><span class="dot"></span>
|
||||||
|
</div>
|
||||||
<!-- chevron -->
|
<!-- chevron -->
|
||||||
<template x-if="node.is_folder && node.children && node.children.length > 0">
|
<template x-if="node.is_folder && node.children && node.children.length > 0">
|
||||||
<button class="ws-chevron"
|
<button class="ws-chevron"
|
||||||
@@ -2190,10 +2260,10 @@ console.log('FLOWDECK v2.4.6 — IIFE done, _wsInitData keys:', Object.keys(wind
|
|||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template x-if="!node.is_folder">
|
<template x-if="!node.is_folder">
|
||||||
<span class="name file" @click.stop="openPage(node.id)"
|
<span class="name file" @click.stop="startInlineRename(node, $event)"
|
||||||
@dblclick.stop="startInlineRename(node, $event)"
|
|
||||||
@mouseenter="showPreview($event, node)"
|
@mouseenter="showPreview($event, node)"
|
||||||
@mouseleave="hidePreview()">
|
@mouseleave="hidePreview()"
|
||||||
|
title="Click to rename, double-click to open">
|
||||||
<span x-show="renamingId !== node.id" x-text="node.name"></span>
|
<span x-show="renamingId !== node.id" x-text="node.name"></span>
|
||||||
<input x-show="renamingId === node.id" class="inline-rename-input" x-model="renamingName"
|
<input x-show="renamingId === node.id" class="inline-rename-input" x-model="renamingName"
|
||||||
@keydown.enter="commitRename(node)" @keydown.escape="cancelRename()"
|
@keydown.enter="commitRename(node)" @keydown.escape="cancelRename()"
|
||||||
|
|||||||
Reference in New Issue
Block a user