Created framework for showing menus easily on top of a button click
This commit is contained in:
parent
d9420fffca
commit
258f69cc11
|
@ -17,7 +17,7 @@
|
|||
<?php tpl_styles($styles ?? null) ?>
|
||||
|
||||
<?php tpl('app.js') ?>
|
||||
<?php tpl_scripts(['base', 'navmenu', 'carousel', 'app']) ?>
|
||||
<?php tpl_scripts(['base', 'carousel', 'app']) ?>
|
||||
<?php tpl_scripts($scripts ?? null) ?>
|
||||
</head>
|
||||
<body class="app">
|
||||
|
@ -49,7 +49,7 @@
|
|||
</div>
|
||||
<?php if ($actor !== null): ?>
|
||||
<div class="flex icon button noback round">
|
||||
<a class="center" onclick="app.navmenu.show()">
|
||||
<a class="center" onclick="app.menu.show(this, 'navprofile')">
|
||||
<img class="avatar round"
|
||||
src="<?php echo htmlspecialchars($actor->icon) ?>"/>
|
||||
</a>
|
||||
|
|
|
@ -23,4 +23,14 @@ var app = {
|
|||
collapse_text_size: 80,
|
||||
},
|
||||
};
|
||||
app.menus = {
|
||||
navprofile: {
|
||||
position: 'left',
|
||||
maxwidth: 35,
|
||||
items: [
|
||||
{ text: 'Test', onclick: "alert('test')", fa: 'times' },
|
||||
{ separator: true },
|
||||
],
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -45,6 +45,17 @@ fieldset label > * {
|
|||
}
|
||||
|
||||
|
||||
#menu-container.left .component.button {
|
||||
text-align: right !important;
|
||||
}
|
||||
#menu-container.right .component.button {
|
||||
text-align: left !important;
|
||||
}
|
||||
#menu-container #menu .button {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
|
||||
hr {
|
||||
color: #00000029;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,65 @@ app.script = function(name, cback) {
|
|||
loadScript(name, file, cback);
|
||||
}
|
||||
|
||||
app.menu = {
|
||||
show: async function(elem, cfg) {
|
||||
if (typeof cfg === 'string')
|
||||
cfg = app.menus[cfg];
|
||||
if (cfg === undefined)
|
||||
return false;
|
||||
|
||||
const items = cfg.items || [];
|
||||
const position = cfg.position || 'left';
|
||||
const maxwidth = em2px(cfg.maxwidth || 35);
|
||||
|
||||
const starty = elem.offsetTop + elem.offsetHeight;
|
||||
var width = (document.body.clientWidth * 80 / 100);
|
||||
if (width > maxwidth) width = maxwidth;
|
||||
|
||||
let left
|
||||
if (position === 'left')
|
||||
left = Math.floor((elem.offsetLeft + elem.offsetWidth) - width);
|
||||
else if (position === 'right')
|
||||
left = Math.floor(elem.offsetLeft);
|
||||
if (left === undefined)
|
||||
return false;
|
||||
|
||||
await app.overlay.create('overlay.menu',{
|
||||
class: position,
|
||||
top: starty,
|
||||
width,
|
||||
left
|
||||
}, { removable: true }, function(k,v) {
|
||||
if (k === 'loop:menu') {
|
||||
var content = '';
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
var item = items[i];
|
||||
item.displayIconRight = 'none';
|
||||
item.displayIconLeft = 'none';
|
||||
if (item.fa !== undefined)
|
||||
if (position === 'left')
|
||||
item.displayIconLeft = 'initial';
|
||||
else item.displayIconRight = 'initial';
|
||||
item.isItem = item.separator === undefined;
|
||||
item.isSep = item.separator === true;
|
||||
item.class = item.class || '';
|
||||
content += app.template.fill(item, v);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
});
|
||||
|
||||
let elems;
|
||||
elems = document.querySelectorAll('#menu *[select-isitem="false"]');
|
||||
for (var i = 0; i < elems.length; i++) elems[i].remove();
|
||||
elems = document.querySelectorAll('#menu *[select-issep="false"]');
|
||||
for (var i = 0; i < elems.length; i++) elems[i].remove();
|
||||
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
app.template = {
|
||||
loadMany: async function(names, cback) {
|
||||
var templates = [];
|
||||
|
@ -297,9 +356,10 @@ app.emoji = {
|
|||
}
|
||||
|
||||
app.overlay = {
|
||||
create: function(template, data, options) {
|
||||
create: async function(template, data, options, cback) {
|
||||
options = options || {};
|
||||
app.template.load(template, function(tpl) {
|
||||
await app.template.loadMany([template], function(tpl) {
|
||||
tpl = tpl[0];
|
||||
const id = 'overlay-'+uuidv4();
|
||||
const div = document.createElement('div');
|
||||
data['oid'] = id;
|
||||
|
@ -309,12 +369,13 @@ app.overlay = {
|
|||
div.className += ' dark';
|
||||
if (typeof options.css === 'string')
|
||||
div.className += ` ${options.css}`;
|
||||
div.innerHTML = app.template.fill(data, tpl);
|
||||
div.innerHTML = app.template.fill(data, tpl, cback);
|
||||
if (options.removable)
|
||||
div.setAttribute('onclick', `app.overlay.remove("${id}")`);
|
||||
document.body.appendChild(div);
|
||||
calcHeightMobile();
|
||||
});
|
||||
return true;
|
||||
},
|
||||
remove: function(id) {
|
||||
const elem = document.getElementById(id);
|
||||
|
@ -494,7 +555,11 @@ window.onload = function(e) {
|
|||
handle('pages.quiz', /^quiz\/[^\/]+?$/);
|
||||
}
|
||||
|
||||
app.template.loadMany(['toast.confirm','toast.info','toast.warn','toast.error']);
|
||||
app.template.loadMany([
|
||||
'toast.confirm', 'toast.info',
|
||||
'toast.warn', 'toast.error',
|
||||
'overlay.menu',
|
||||
]);
|
||||
if (http.cache.expired('instance_emojis', 30*60)) {
|
||||
http.cache.del('instance_emojis');
|
||||
http.get('/api/v1/instance/emojis', {}, function(data) {
|
||||
|
|
|
@ -167,6 +167,15 @@ function isVisible(element) {
|
|||
);
|
||||
}
|
||||
|
||||
function em2px(em) {
|
||||
var div = document.createElement('div');
|
||||
div.style.width = `${em}em`;
|
||||
document.body.appendChild(div);
|
||||
const width = div.offsetWidth;
|
||||
div.remove();
|
||||
return width;
|
||||
}
|
||||
|
||||
function text2html(text) {
|
||||
return text.replaceAll('>','>').replaceAll('<','<')
|
||||
.replaceAll('"', '"').replaceAll('&', '&');
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
app.navmenu = {
|
||||
show: function() {
|
||||
console.log('TODO: show menu for profile');
|
||||
},
|
||||
};
|
|
@ -0,0 +1,35 @@
|
|||
<div id="menu-container" style="
|
||||
position: fixed;
|
||||
display: flex;
|
||||
top: {.top}px;
|
||||
width: 100%;
|
||||
" class="{.class}">
|
||||
<div class="panel" style="
|
||||
position: relative;
|
||||
left: {.left}px;
|
||||
width: {.width}px;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
background: none;
|
||||
">
|
||||
<div id="menu" class="card-item" style="
|
||||
box-shadow: 0px 1.5em 2em #0000005e;
|
||||
border: none;
|
||||
padding: 0;
|
||||
">
|
||||
{loop:menu}
|
||||
<div select-isitem="{.isItem}"
|
||||
class="component button noback"
|
||||
onclick="{.onclick}">
|
||||
<i style="display: {.displayIconRight}"
|
||||
class="fa fa-{.fa} fa-fw"></i>
|
||||
<span>{.text}</span>
|
||||
<i style="display: {.displayIconLeft}"
|
||||
class="fa fa-{.fa} fa-fw"></i>
|
||||
</div>
|
||||
<div select-issep="{.isSep}"
|
||||
class="separator {.class}"></div>
|
||||
{/loop}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in New Issue