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_styles($styles ?? null) ?>
|
||||||
|
|
||||||
<?php tpl('app.js') ?>
|
<?php tpl('app.js') ?>
|
||||||
<?php tpl_scripts(['base', 'navmenu', 'carousel', 'app']) ?>
|
<?php tpl_scripts(['base', 'carousel', 'app']) ?>
|
||||||
<?php tpl_scripts($scripts ?? null) ?>
|
<?php tpl_scripts($scripts ?? null) ?>
|
||||||
</head>
|
</head>
|
||||||
<body class="app">
|
<body class="app">
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
</div>
|
</div>
|
||||||
<?php if ($actor !== null): ?>
|
<?php if ($actor !== null): ?>
|
||||||
<div class="flex icon button noback round">
|
<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"
|
<img class="avatar round"
|
||||||
src="<?php echo htmlspecialchars($actor->icon) ?>"/>
|
src="<?php echo htmlspecialchars($actor->icon) ?>"/>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -23,4 +23,14 @@ var app = {
|
||||||
collapse_text_size: 80,
|
collapse_text_size: 80,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
app.menus = {
|
||||||
|
navprofile: {
|
||||||
|
position: 'left',
|
||||||
|
maxwidth: 35,
|
||||||
|
items: [
|
||||||
|
{ text: 'Test', onclick: "alert('test')", fa: 'times' },
|
||||||
|
{ separator: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</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 {
|
hr {
|
||||||
color: #00000029;
|
color: #00000029;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,65 @@ app.script = function(name, cback) {
|
||||||
loadScript(name, file, 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 = {
|
app.template = {
|
||||||
loadMany: async function(names, cback) {
|
loadMany: async function(names, cback) {
|
||||||
var templates = [];
|
var templates = [];
|
||||||
|
@ -297,9 +356,10 @@ app.emoji = {
|
||||||
}
|
}
|
||||||
|
|
||||||
app.overlay = {
|
app.overlay = {
|
||||||
create: function(template, data, options) {
|
create: async function(template, data, options, cback) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
app.template.load(template, function(tpl) {
|
await app.template.loadMany([template], function(tpl) {
|
||||||
|
tpl = tpl[0];
|
||||||
const id = 'overlay-'+uuidv4();
|
const id = 'overlay-'+uuidv4();
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
data['oid'] = id;
|
data['oid'] = id;
|
||||||
|
@ -309,12 +369,13 @@ app.overlay = {
|
||||||
div.className += ' dark';
|
div.className += ' dark';
|
||||||
if (typeof options.css === 'string')
|
if (typeof options.css === 'string')
|
||||||
div.className += ` ${options.css}`;
|
div.className += ` ${options.css}`;
|
||||||
div.innerHTML = app.template.fill(data, tpl);
|
div.innerHTML = app.template.fill(data, tpl, cback);
|
||||||
if (options.removable)
|
if (options.removable)
|
||||||
div.setAttribute('onclick', `app.overlay.remove("${id}")`);
|
div.setAttribute('onclick', `app.overlay.remove("${id}")`);
|
||||||
document.body.appendChild(div);
|
document.body.appendChild(div);
|
||||||
calcHeightMobile();
|
calcHeightMobile();
|
||||||
});
|
});
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
remove: function(id) {
|
remove: function(id) {
|
||||||
const elem = document.getElementById(id);
|
const elem = document.getElementById(id);
|
||||||
|
@ -494,7 +555,11 @@ window.onload = function(e) {
|
||||||
handle('pages.quiz', /^quiz\/[^\/]+?$/);
|
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)) {
|
if (http.cache.expired('instance_emojis', 30*60)) {
|
||||||
http.cache.del('instance_emojis');
|
http.cache.del('instance_emojis');
|
||||||
http.get('/api/v1/instance/emojis', {}, function(data) {
|
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) {
|
function text2html(text) {
|
||||||
return text.replaceAll('>','>').replaceAll('<','<')
|
return text.replaceAll('>','>').replaceAll('<','<')
|
||||||
.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