fix(ctxmenu): repair library rendering and complete unified row menu
- library.html: remove leftover syntax error + orphan </template></div> that broke libraryPage() (page showed 'undefined' and no files), call the correct fdCtx.openMenu and use self instead of a throwaway libraryPage() instance - _ctx_menu.html: full conventional menu with shortcuts, tags + colour picker, edit-icon picker; closes on outside click and Escape - local_workspace.html: migrate onContextMenu to the shared fdCtx store with complete handlers; drop legacy window._ctxMenuData DOM hacks - dashboard.py: expose page_icon + favorited in the workspace tree
This commit is contained in:
+52
-27
@@ -364,8 +364,6 @@
|
||||
</div>
|
||||
|
||||
{% include "_ctx_menu.html" %}
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- ── Move-to modal ── -->
|
||||
<div class="move-modal-overlay" x-show="moveModalOpen" @click.self="moveModalOpen=false" x-transition>
|
||||
@@ -475,11 +473,6 @@ function libraryPage() {
|
||||
// More menu
|
||||
moreOpen: false,
|
||||
|
||||
// Row context menu
|
||||
ctxMenuItem: null,
|
||||
ctxMenuX: 0,
|
||||
ctxMenuY: 0,
|
||||
|
||||
// Move-to modal
|
||||
moveModalOpen: false,
|
||||
moveIds: [],
|
||||
@@ -598,8 +591,10 @@ function libraryPage() {
|
||||
var name = icon || fallback || 'file';
|
||||
// Only allow known icon names, otherwise use fallback
|
||||
var valid = ['folder','file','star','link','trash','book','home','settings','lock','globe','image','edit','calendar','users','user','search','tag','bar-chart','grid','list','align-left','zap','paperclip','key','refresh','upload'];
|
||||
if (valid.indexOf(name) === -1) name = fallback || 'file';
|
||||
return getSvgIcon(name, 14);
|
||||
if (valid.indexOf(name) !== -1) return getSvgIcon(name, 14);
|
||||
// Custom emoji / non-ASCII icon (e.g. 📝) — render as-is
|
||||
if (/[^\x00-\x7F]/.test(name)) return '<span style="font-size:14px;line-height:1;">' + this._escHtml(name) + '</span>';
|
||||
return getSvgIcon(fallback || 'file', 14);
|
||||
},
|
||||
|
||||
_recomputeFlatItems() {
|
||||
@@ -673,27 +668,25 @@ function libraryPage() {
|
||||
evt.preventDefault();
|
||||
var item = this._findItem(id);
|
||||
if (!item) return;
|
||||
if (!(window.Alpine && Alpine.store('fdCtx'))) return;
|
||||
/* MENU UNIFIÉ (partial _ctx_menu.html + store fdCtx) — handlers library
|
||||
complète l'union (8 items). Open folder/tags non prévus côté library :
|
||||
complète l'union. Open folder/tags non prévus côté library :
|
||||
fdCtx les cache automatiquement via handlers absents. */
|
||||
var handlers = {
|
||||
open: function (n) { libraryPage().openItem(n); },
|
||||
peek: function (n) { libraryPage().openPeek(n); },
|
||||
rename: function (n) { libraryPage().startRename(n); },
|
||||
fav: function (n) { libraryPage().toggleFavoriteItem(n); },
|
||||
duplicate: function (n) { libraryPage().duplicateItem(n); },
|
||||
link: function (n) { libraryPage().copyLink(n); },
|
||||
move: function (n) { libraryPage().openMovePicker([n.id]); },
|
||||
del: function (n) { libraryPage().deleteItem(n); }
|
||||
};
|
||||
var self = this;
|
||||
if (window.Alpine && Alpine.store('fdCtx')) {
|
||||
Alpine.store('fdCtx').open(evt, item, 'library', handlers);
|
||||
Alpine.store('fdCtx').closeMenu = function () { ctxMenuItem = null; };
|
||||
}
|
||||
this.ctxMenuItem = item parallels ?? null;
|
||||
this.ctxMenuX = Math.min(evt.clientX, window.innerWidth - 220);
|
||||
this.ctxMenuY = Math.min(evt.clientY, window.innerHeight - 320);
|
||||
var handlers = {
|
||||
open: function (n) { self.openItem(n); },
|
||||
openTab: function (n) { self.openInNewTab(n); },
|
||||
peek: function (n) { self.openPeek(n); },
|
||||
rename: function (n) { self.startRename(n); },
|
||||
setIcon: function (icon) { self.setItemIcon(item, icon); },
|
||||
duplicate: function (n) { self.duplicateItem(n); },
|
||||
link: function (n) { self.copyLink(n); },
|
||||
move: function (n) { self.openMovePicker([n.id]); },
|
||||
fav: function (n) { self.toggleFavoriteItem(n); },
|
||||
delete: function (n) { self.deleteItem(n); }
|
||||
};
|
||||
if (this.tab === 'recents') handlers.recent = function (n) { self.removeItemFromRecents(n); };
|
||||
Alpine.store('fdCtx').openMenu(evt, item, 'library', handlers);
|
||||
},
|
||||
|
||||
// ── Drag & drop (move / reparent) ──
|
||||
@@ -1058,6 +1051,38 @@ function libraryPage() {
|
||||
if (window.showToast) window.showToast('Link copied', 'success');
|
||||
},
|
||||
|
||||
openInNewTab(item) {
|
||||
if (!item || !item.id) return;
|
||||
window.open('/pages/' + item.id, '_blank');
|
||||
},
|
||||
|
||||
removeItemFromRecents(item) {
|
||||
if (!item) return;
|
||||
var id = item.id;
|
||||
this.items = this.items.filter(function(it) { return it.id !== id; });
|
||||
this._recomputeFlatItems();
|
||||
if (window.showToast) window.showToast('Removed from Recents', 'success');
|
||||
},
|
||||
|
||||
async setItemIcon(item, icon) {
|
||||
if (!item || !item.id) return;
|
||||
icon = (icon || '').trim();
|
||||
if (!icon) return;
|
||||
try {
|
||||
var r = await fetch('/board/api/pages/' + item.id + '/icon', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json', 'X-CSRF-Token': this._getCsrf()},
|
||||
body: JSON.stringify({icon: icon})
|
||||
});
|
||||
if (!r.ok) throw new Error('icon update failed');
|
||||
item.icon = icon;
|
||||
this._renderTable();
|
||||
if (window.showToast) window.showToast('Icon updated', 'success');
|
||||
} catch(e) {
|
||||
if (window.showToast) window.showToast('Failed to update icon', 'error');
|
||||
}
|
||||
},
|
||||
|
||||
async toggleFavoriteItem(item) {
|
||||
if (!item) return;
|
||||
var csrf = this._getCsrf();
|
||||
|
||||
Reference in New Issue
Block a user