Add right-click menu (#1135)

This commit is contained in:
stackxp 2026-01-01 13:44:55 +01:00 committed by GitHub
parent 8551472bf0
commit 82c496092f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 298 additions and 0 deletions

View file

@ -4125,3 +4125,46 @@ html.e #doc {
html.e #detree {
padding: 0px;
}
#rcm {
position: absolute;
display: none;
background: var(--bg);
border: 1px solid var(--bg-u3);
outline: none;
border-radius: .3rem;
box-shadow: 0 0 .3rem var(--bg-d3);
z-index: 3;
}
#rcm > * {
display: block;
}
#rcm > a {
padding: .2rem .3rem;
}
#rcm > .hide {
display: none;
}
#rcm > a:hover {
background: var(--bg-d2);
}
#rcm > .sep {
margin: 0 .2rem;
border-bottom: 1px solid var(--a-gray);
}
#tempname {
color: var(--fg);
background: var(--txt-bg);
border: none;
box-shadow: 0 0 2px var(--txt-sh);
border-bottom: 1px solid #999;
border-color: var(--a);
border-radius: .2em;
padding: .2em .3em;
}

View file

@ -129,6 +129,8 @@
<div id="widget"></div>
<div id="rcm" tabindex="0"></div>
<script>
var SR = "{{ r }}",
CGV1 = {{ cgv1 }},

View file

@ -223,6 +223,7 @@ if (1)
"cl_reset": "reset",
"cl_hpick": "tap on column headers to hide in the table below",
"cl_hcancel": "column hiding aborted",
"cl_rcm": "right-click menu",
"ct_grid": '田 the grid',
"ct_ttips": '◔ ◡ ◔"> tooltips',
@ -265,6 +266,7 @@ if (1)
"cdt_lim": "max number of files to show in a folder",
"cdt_ask": "when scrolling to the bottom,$Ninstead of loading more files,$Nask what to do",
"cdt_hsort": "how many sorting rules (&lt;code&gt;,sorthref&lt;/code&gt;) to include in media-URLs. Setting this to 0 will also ignore sorting-rules included in media links when clicking them",
"cdt_ren": "enable custom right-click menu, you can still access the regular menu by pressing the shift key and right-clicking",
"tt_entree": "show navpane (directory tree sidebar)$NHotkey: B",
"tt_detree": "show breadcrumbs$NHotkey: B",
@ -640,6 +642,22 @@ if (1)
"ur_um": "Finished;\n{0} uploads OK,\n{1} uploads failed, sorry",
"ur_sm": "Finished;\n{0} files found on server,\n{1} files NOT found on server",
"rc_opn": "Open",
"rc_ply": "Play",
"rc_pla": "Play as audio",
"rc_txt": "Open in file viewer",
"rc_md": "Open in markdown editor",
"rc_dl": "Download",
"rc_zip": "Download as archive",
"rc_del": "Delete",
"rc_cut": "Cut",
"rc_cpy": "Copy",
"rc_pst": "Paste",
"rc_nfo": "New folder",
"rc_nfi": "New file",
"rc_sal": "Select all",
"rc_sin": "Invert selection",
"lang_set": "refresh to make the change take effect?",
};
@ -966,6 +984,7 @@ ebi('op_cfg').innerHTML = (
' </div>\n' +
'</div>\n' +
'<div><h3>' + L.cl_keytype + '</h3><div><select id="key_notation"></select></div></div>\n' +
(!MOBILE ? '<div><h3>' + L.cl_rcm + '</h3><div><a id="ren" class="tgl btn" href="#" tt="' + L.cdt_ren + '">enable</a></div></div>' : '') +
'<div><h3>' + L.cl_hiddenc + ' &nbsp;' + (MOBILE ? '<a href="#" id="hcolsh">' + L.cl_hidec + '</a> / ' : '') + '<a href="#" id="hcolsr">' + L.cl_reset + '</a></h3><div id="hcols"></div></div>'
);
@ -995,6 +1014,34 @@ QS('#op_mkdir input[type="submit"]').value = L.ab_mkdir;
QS('#op_new_md input[type="submit"]').value = L.ab_mkdoc;
QS('#op_msg input[type="submit"]').value = L.ab_msg;
// right-click menu
ebi('rcm').innerHTML = (
'<a href="#" id="ropn">' + L.rc_opn + '</a>' +
'<a href="#" id="rply">' + L.rc_ply + '</a>' +
'<a href="#" id="rpla">' + L.rc_pla + '</a>' +
'<a href="#" id="rtxt">' + L.rc_txt + '</a>' +
'<a href="#" id="rmd">' + L.rc_md + '</a>' +
'<div id="rs1" class="sep"></div>' +
'<a href="#" id="rdl">' + L.rc_dl + '</a>' +
(have_zip ?
'<a href="#" id="rzip">' + L.rc_zip + '</a>'
: '') +
'<div id="rs2" class="sep"></div>' +
(has(perms, "move") ?
'<a href="#" id="rdel">' + L.rc_del + '</a>' +
'<a href="#" id="rcut">' + L.rc_cut + '</a>'
: '') +
'<a href="#" id="rcpy">' + L.rc_cpy + '</a>' +
(has(perms, "write") ?
'<a href="#" id="rpst">' + L.rc_pst + '</a>' +
'<div id="rs3" class="sep"></div>' +
'<a href="#" id="rnfo">' + L.rc_nfo + '</a>' +
'<a href="#" id="rnfi">' + L.rc_nfi + '</a>'
: '') +
'<div id="rs4" class="sep"></div>' +
'<a href="#" id="rsal">' + L.rc_sal + '</a>' +
'<a href="#" id="rsin">' + L.rc_sin + '</a>'
);
(function () {
var ops = QSA('#ops>a');
@ -9395,6 +9442,212 @@ ebi('files').onclick = ebi('docul').onclick = function (e) {
};
var rcm = (function () {
if (MOBILE)
return {enabled: false}
var r = {
enabled: true
};
bcfg_bind(r, 'enabled', 'ren', true);
var menu = ebi('rcm');
var selFile = {
elem: null,
type: null,
path: null,
id: null,
relpath: null,
no_dsel: false
};
function mktemp(is_dir) {
var row = mknod('tr', 'temp',
'<td>-new-</td>' +
'<td colspan="' + (QSA("#files thead th").length - 1) + '"><input id="tempname" class="i" type="text" placeholder="' + (is_dir ? 'Folder' : "File") + ' Name"></td>'
);
QS("#files tbody").appendChild(row);
function sendit(name) {
name = ('' + name).trim();
if (!name)
return;
var data = new FormData();
data.set("act", is_dir ? "mkdir" : "new_md");
data.set("name", name);
var req = new XHR();
req.open("POST", get_evpath());
req.onload = req.onerror = function() {
if (req.status == 405 || req.status == 500)
return toast.err(3, "a " + (is_dir ? "folder" : "file") + " with that name already exists.");
if (req.status < 200 || req.status > 399)
return toast.err(3, "couldn't create " + (is_dir ? "folder" : "file") + ": <br><code>" + esc(req.responseText) + '</code>');
location.reload();
};
req.send(data);
}
var input = ebi("tempname");
input.onblur = function() {
sendit(input.value);
// Chrome blurs elements when calling remove for some reason
input.onblur = null;
row.remove();
};
input.onkeydown = function(e) {
if (e.key == "Enter")
sendit(input.value);
if (e.key == "Enter" || e.key == "Escape") {
input.onblur = null;
row.remove();
ev(e);
}
};
input.focus();
}
var opts = QSA('#rcm a');
for (var i = 0; i < opts.length; i++) {
opts[i].onclick = function(e) {
ev(e);
switch(e.target.id.slice(1)) {
case 'opn':
var a = mknod('a');
a.href = selFile.path;
a.target = selFile.type == "dir" ? '' : '_blank';
a.click();
break;
case 'ply':
if (selFile.type == 'gf')
thegrid.imshow(selFile.relpath);
else
play('f-' + selFile.id);
break;
case 'pla':
play('f-' + selFile.id);
break;
case 'txt':
location = '?doc=' + selFile.relpath;
break;
case 'md':
location = selFile.path + '?v';
break;
case 'dl':
ebi('seldl').click();
break;
case 'zip':
ebi('selzip').click();
break;
case 'del':
fileman.delete();
break;
case 'cut':
fileman.cut();
break;
case 'cpy':
fileman.cpy();
break;
case 'pst':
fileman.paste();
fileman.clip = [];
break;
case 'nfo':
mktemp(true);
break;
case 'nfi':
mktemp();
break;
case 'sal':
msel.evsel(null, true);
selFile.no_dsel = true;
break;
case 'sin':
msel.evsel(null, 't');
break;
default:
console.warn('Invalid rcm option "' + e.target.id + '"');
}
hide(true);
};
}
function show(x, y, target) {
selFile.elem = selFile.type = selFile.path = selFile.id = selFile.relpath = null;
selFile.no_dsel = false;
if (target) {
var file = target.closest("#files tbody tr");
if (file) {
selFile.no_dsel = clgot(file, "sel");
clmod(file, "sel", true);
selFile.elem = file;
selFile.path = basenames(file.children[1].firstChild.href).split('?')[0];
selFile.relpath = selFile.path.split('/').slice(-1)[0];
if (file.children[3].innerHTML == "---")
selFile.type = "dir";
else {
var lead = file.firstChild.firstChild;
selFile.id = lead.id.split('-')[1];
selFile.type = lead.innerHTML[0] == '(' ? 'gf' : lead.id.split('-')[0];
}
}
}
msel.selui();
var has_sel = msel.getsel().length;
var has_clip = fileman.clip.length;
clmod(ebi('ropn'), 'hide', !selFile.path);
clmod(ebi('rply'), 'hide', selFile.type != 'gf' && selFile.type != 'af');
clmod(ebi('rpla'), 'hide', selFile.type != 'gf');
clmod(ebi('rtxt'), 'hide', !selFile.id);
clmod(ebi('rs1'), 'hide', !selFile.path);
clmod(ebi('rmd'), 'hide', !selFile.id || selFile.path.slice(-3) != '.md');
clmod(ebi('rdl'), 'hide', !has_sel);
clmod(ebi('rzip'), 'hide', !has_sel);
clmod(ebi('rs2'), 'hide', !has_sel);
clmod(ebi('rcut'), 'hide', !has_sel);
clmod(ebi('rdel'), 'hide', !has_sel);
clmod(ebi('rcpy'), 'hide', !has_sel);
clmod(ebi('rpst'), 'hide', !has_clip);
clmod(ebi('rs3'), 'hide', !has_sel || !has_clip);
menu.style.left = x + 5 + 'px';
menu.style.top = y + 5 + 'px';
menu.style.display = 'block';
menu.focus();
}
function hide(force) {
if (!menu.style.display || (!force && menu.contains(document.activeElement)))
return;
if (selFile.elem && !selFile.no_dsel)
clmod(selFile.elem, "sel", false);
selFile.elem = selFile.type = selFile.path = selFile.id = selFile.relpath = null;
selFile.no_dsel = false;
menu.style.display = '';
}
ebi('wrap').oncontextmenu = function(e) {
if (thegrid.en || !r.enabled || e.shiftKey || menu.style.display) {
hide(true);
return true;
}
else {
ev(e);
show(e.clientX, e.clientY, e.target);
return false;
}
};
menu.onblur = function() {setTimeout(hide)};
return r;
})();
function reload_mp() {
if (mp && mp.au) {
mpo.au = mp.au;