diff --git a/web/src/_templates/app/header.php b/web/src/_templates/app/header.php
index 3845d64..d58f66b 100644
--- a/web/src/_templates/app/header.php
+++ b/web/src/_templates/app/header.php
@@ -17,7 +17,7 @@
-
+
@@ -49,7 +49,7 @@
-
+
diff --git a/web/src/_templates/app/js.php b/web/src/_templates/app/js.php
index e73655c..614bca1 100644
--- a/web/src/_templates/app/js.php
+++ b/web/src/_templates/app/js.php
@@ -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 },
+ ],
+ },
+};
diff --git a/web/src/app/css/app.css b/web/src/app/css/app.css
index 6363b9b..5ef8f2f 100644
--- a/web/src/app/css/app.css
+++ b/web/src/app/css/app.css
@@ -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;
}
diff --git a/web/src/app/js/app.js b/web/src/app/js/app.js
index 504c35b..8dab961 100644
--- a/web/src/app/js/app.js
+++ b/web/src/app/js/app.js
@@ -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) {
diff --git a/web/src/app/js/base.js b/web/src/app/js/base.js
index e43d54f..f6e0cd5 100644
--- a/web/src/app/js/base.js
+++ b/web/src/app/js/base.js
@@ -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('&', '&');
diff --git a/web/src/app/js/navmenu.js b/web/src/app/js/navmenu.js
deleted file mode 100644
index 89b2c02..0000000
--- a/web/src/app/js/navmenu.js
+++ /dev/null
@@ -1,5 +0,0 @@
-app.navmenu = {
- show: function() {
- console.log('TODO: show menu for profile');
- },
-};
diff --git a/web/src/app/js/templates/overlay/menu.html b/web/src/app/js/templates/overlay/menu.html
new file mode 100644
index 0000000..ee9b36f
--- /dev/null
+++ b/web/src/app/js/templates/overlay/menu.html
@@ -0,0 +1,35 @@
+