From a Bilibili comment (buttons small and in five places, the file name twice, a title bar of little use), layout B chosen by the user from three mockups: - The title bar goes. The head (new model_detail_head.js) tops the right column: a crumb back (Models / LoRA, or where history leads; still .anomalous-detail-back, so Esc presses it), the name, the source line with the source links, and one action row: Add node (the one accent button), Edit, Folder, Outputs, and ... with Copy path, Copy file name, Delete. - Delete asks in AMB's dialog instead of window.confirm, and is no longer beside Add node. - model_location.js: one path line with Copy; Show in folder is exported for Folder. - action_menu.js: the small menu the gallery card's right-click had, shared with the ... - Narrow (no cover column), a 64 px cover sits beside the name. - Gone: the jump-to-bottom button, the full-width Outputs button, the location card's file name row, and their locale keys (back, backToPrev, detailJumpToBottom, detailPhysicalFilename, detailFullPath). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
71 lines
2.7 KiB
JavaScript
71 lines
2.7 KiB
JavaScript
/**
|
|
* A small menu of actions, looking like the keep menu: at the pointer (a right-click) or under
|
|
* a button. A pick, a click elsewhere, the wheel or Esc closes it; its Esc goes no further, so
|
|
* the page behind stays open. One at a time.
|
|
*/
|
|
|
|
let closeOpenMenu = null;
|
|
|
|
function row({ icon, label, run, danger }, close) {
|
|
const item = document.createElement('button');
|
|
item.type = 'button';
|
|
item.className = `anomalous-keep-row${danger ? ' is-danger' : ''}`;
|
|
item.setAttribute('role', 'menuitem');
|
|
const mark = document.createElement('span');
|
|
mark.className = 'anomalous-keep-row-icon';
|
|
mark.textContent = icon;
|
|
const text = document.createElement('strong');
|
|
text.textContent = label;
|
|
item.append(mark, text);
|
|
item.onclick = (event) => {
|
|
close();
|
|
run(event);
|
|
};
|
|
return item;
|
|
}
|
|
|
|
/**
|
|
* `at`: a pointer event (the menu opens there) or a button (the menu opens under it,
|
|
* right-aligned; a click on that button is left to it, so it can close the menu again).
|
|
* `onClose()` runs however it closes. Returns the close function.
|
|
*/
|
|
export function openActionMenu(at, items, { className = '', onClose } = {}) {
|
|
closeOpenMenu?.();
|
|
const menu = document.createElement('div');
|
|
menu.className = `anomalous-keep-menu anomalous-action-menu ${className}`.trim();
|
|
menu.setAttribute('role', 'menu');
|
|
const onPointer = (pointer) => {
|
|
if (!menu.contains(pointer.target) && !(at instanceof Element && at.contains(pointer.target))) close();
|
|
};
|
|
const onKey = (key) => {
|
|
if (key.key !== 'Escape') return;
|
|
key.stopPropagation();
|
|
close();
|
|
};
|
|
const close = () => {
|
|
menu.remove();
|
|
window.removeEventListener('pointerdown', onPointer, true);
|
|
window.removeEventListener('keydown', onKey, true);
|
|
window.removeEventListener('wheel', close, true);
|
|
if (closeOpenMenu === close) closeOpenMenu = null;
|
|
onClose?.();
|
|
};
|
|
menu.append(...items.map(item => row(item, close)));
|
|
document.body.append(menu);
|
|
const { width, height } = menu.getBoundingClientRect();
|
|
let x = at.clientX;
|
|
let y = at.clientY;
|
|
if (at instanceof Element) {
|
|
const anchor = at.getBoundingClientRect();
|
|
x = anchor.right - width;
|
|
y = anchor.bottom + 6;
|
|
}
|
|
menu.style.left = `${Math.max(8, Math.min(x, window.innerWidth - width - 8))}px`;
|
|
menu.style.top = `${Math.max(8, Math.min(y, window.innerHeight - height - 8))}px`;
|
|
window.addEventListener('pointerdown', onPointer, true);
|
|
window.addEventListener('keydown', onKey, true);
|
|
window.addEventListener('wheel', close, true);
|
|
closeOpenMenu = close;
|
|
return close;
|
|
}
|