Redesign user dropdown menu with new icons and UI

Replaces the old top menu and UI selection controls with a modern user dropdown menu featuring new icon assets, improved styling, and a UI settings submenu. Updates CSS for layout and responsiveness, adds new PNG icon files, and refactors related JavaScript logic for menu toggling and user image handling.
This commit is contained in:
TheDevRyan 2025-10-17 22:41:28 +01:00
parent e5205f285b
commit f6fec27a92
19 changed files with 355 additions and 88 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -2341,8 +2341,8 @@ nav .lbbuttonsel2 {
.notifiyBox {
position: absolute;
z-index: 1000;
top: 50px;
right: 26px;
top: 80px;
right: 187px;
width: 300px;
text-align: left;
background-color: #F0ECCD;
@ -2400,6 +2400,9 @@ nav .lbbuttonsel2 {
margin: 8px;
cursor: pointer;
padding: 4px;
position: absolute;
right: 10rem;
top: 0.2em;
}
.night #notificationCount {
@ -3344,6 +3347,12 @@ nav .lbbuttonsel2 {
font-size: 36px;
}
#masthead {
background-size: 18rem 2.5rem !important;
background-position: center left;
background-repeat: no-repeat !important;
padding-right: 20px !important;
}
#MainMeshImage /*, #p2AccountImageFrame */ {
/* text-align: center !important; */
float: none !important;
@ -3355,17 +3364,40 @@ nav .lbbuttonsel2 {
float: none !important;
text-align: left;
}
#userDropdown {
display: inline-block !important;
position: absolute !important;
right: 10px !important;
}
#logoutControl {
#userDropdownName {
display: none !important;
}
#topMenuIcon {
.userDropdownMobileOnly {
display: flex !important;
}
#userDropdownMenuDivider.userDropdownMobileOnly {
display: block !important;
}
.userDropdownSubmenu {
right: 100%;
left: auto;
top: 13rem !important;
margin-left: 0;
margin-right: 4px;
}
.userDropdownUISettings {
position: relative;
}
#notificationCount {
margin-right: 45px;
right: 7rem;
}
.notifiyBox {
right: 97px;
}
.notifiyBox:after {
@ -3404,4 +3436,141 @@ nav .lbbuttonsel2 {
.select2-container--bootstrap-5 .select2-selection--multiple .select2-search {
display: inline !important;
} */
} */
/* User Dropdown Menu Styles */
#userDropdown {
position: absolute;
display: inline-block;
z-index: 1000;
right: 10px;
}
#userDropdownButton {
display: flex;
align-items: center;
cursor: pointer;
padding: 6px 8px;
border-radius: 4px;
height: 40px;
}
#userDropdownButton:hover {
background-color: rgba(255, 255, 255, 0.1);
}
#userDropdownImage {
width: 28px;
height: 28px;
border-radius: 50%;
margin-right: 8px;
border: 2px solid rgba(255, 255, 255, 0.3);
object-fit: cover;
}
#userDropdownName {
margin-right: 6px;
white-space: nowrap;
}
#userDropdownArrow {
font-size: 12px;
}
#userDropdownMenu {
display: none;
position: absolute;
top: 100%;
right: 0;
background-color: #ffffff;
box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.3);
border-radius: 4px;
z-index: 10000;
min-width: 200px;
margin-top: 4px;
transform: translateX(0);
font-family: sans-serif;
}
#userDropdownMenuContainer {
padding: 6px 0;
}
.userDropdownMenuItem {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
cursor: pointer;
color: #333;
white-space: nowrap;
transition: background-color 0.15s ease;
}
.userDropdownMenuItem:hover {
background-color: #f5f5f5;
border-radius: 4px;
}
.userDropdownMenuIcon {
width: 18px;
height: 18px;
object-fit: contain;
flex-shrink: 0;
}
.userDropdownMenuItem span {
flex-grow: 1;
text-align: left;
color: #333;
font-size: 14px;
line-height: 1.2;
}
#userDropdownMenuDivider {
border-top: 1px solid #eee;
margin: 4px 0;
}
.userDropdownSubmenu {
position: absolute;
right: 100%;
top: 0;
background-color: #ffffff;
box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.3);
border-radius: 4px;
margin-right: 4px;
min-width: 200px;
z-index: 10001;
padding: 6px 0;
}
.userDropdownSubmenu .userDropdownMenuItem {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
color: #333;
}
.userDropdownSubmenu .userDropdownMenuItem:hover {
background-color: #f5f5f5;
border-radius: 4px;
}
.userDropdownSubmenu .userDropdownMenuIcon {
width: 18px;
height: 18px;
object-fit: contain;
flex-shrink: 0;
}
.userDropdownMobileOnly {
display: none;
}
.userDropdownUISettings img:last-child {
margin-left: auto;
width: 14px;
height: 14px;
opacity: 0.7;
}

View File

@ -150,9 +150,6 @@
</div>
<p id="logoutControl"><span id=logoutControlSpan class="logoncontrolspan"></span><span id=idleTimeoutNotify
style="color:yellow"></span></p>
<img id="topMenuIcon" class=noselect
style="position:absolute;right:0;top:10px;color:#c8c8c8;font-size:44px;margin-right:8px;cursor:pointer;display:none"
onclick=topMenu() src="/images/3bars-30.png" width=30 height=30 />
<div class=textnewui id=textnewui onmouseup=toggleBootstrapUIMode() onkeypress="if (event.key=='Enter') { toggleBootstrapUIMode(); }">
<b>Try the new MeshCentral UI</b>
</div>
@ -172,55 +169,6 @@
<div>
<div style="position:relative">
<span id=logoutControlSpan2></span>
<div tabindex=0 id=uiMenuButton title="User interface selection" onclick="showUserInterfaceSelectMenu()" onkeypress="if (event.key == 'Enter') showUserInterfaceSelectMenu()">
&diams;
<div id=uiMenu style="display:none">
<table>
<tr>
<td>
<div tabindex=0 id=uiViewButton1 class=uiSelector
onclick=userInterfaceSelectMenu(1) title="Left bar interface"
onkeypress="if (event.key == 'Enter') userInterfaceSelectMenu(1)">
<div class="uiSelector1"></div>
</div>
<div tabindex=0 id=uiViewButton2 class=uiSelector
onclick=userInterfaceSelectMenu(2) title="Top bar interface"
onkeypress="if (event.key == 'Enter') userInterfaceSelectMenu(2)">
<div class="uiSelector2"></div>
</div>
<div tabindex=0 id=uiViewButton3 class=uiSelector
onclick=userInterfaceSelectMenu(3) title="Fixed width interface"
onkeypress="if (event.key == 'Enter') userInterfaceSelectMenu(3)">
<div class="uiSelector3"></div>
</div>
<div tabindex=0 id=uiViewButton7 class=uiSelector
onclick=toggleBootstrapUIMode() title="Toggle Modern UI"
onkeypress="if (event.key == 'Enter') toggleBootstrapUIMode()">
<div class="uiSelector7"></div>
</div>
</td>
<td>
<div tabindex=0 id=uiViewButton6 class=uiSelector onclick="showNotes(false)"
title="Personal Notes"
onkeypress="if (event.key == 'Enter') showNotes(false)">
<div class="uiSelector6"></div>
</div>
<div tabindex=0 id=uiViewButton4 class=uiSelector onclick=toggleNightMode()
title="Toggle night mode"
onkeypress="if (event.key == 'Enter') toggleNightMode()">
<div class="uiSelector4"></div>
</div>
<div tabindex=0 id=uiViewButton5 class=uiSelector onclick=toggleFooterBarMode()
title="Toggle footer bar"
onkeypress="if (event.key == 'Enter') toggleFooterBarMode()">
<div class="uiSelector5"></div>
</div>
<div class=uiSelector_end>&nbsp;</div>
</td>
</tr>
</table>
</div>
</div>
<table id=MainMenuSpan cellpadding=0 cellspacing=0 class=style1>
<tr>
<td tabindex=0 id=MainMenuMyDevices class="topbar_td style3x" onmouseup=go(1,event)
@ -1975,17 +1923,6 @@
class="btn btn-outline-success" /><input type=submit id=p13loginSubmit2 style="display:none"
class="btn btn-outline-success" /></form>
<div id=topMenu style="z-index:1000;background-color:#ffffff;box-shadow:0px 0px 15px #666;font-family:Arial,Helvetica,sans-serif;border-radius:0px 0px 5px 5px;position:fixed;top:50px;right:5px;width:170px;display:none">
<div style="padding:12px;border-top:1px solid gray;color:black;cursor:pointer" onclick=topMenu(1)>My Devices</div>
<div style="padding:12px;border-top:1px solid gray;color:black;cursor:pointer" onclick=topMenu(2)>My Account</div>
<div style="padding:12px;border-top:1px solid gray;color:black;cursor:pointer" onclick=topMenu(3)>My Events</div>
<div style="padding:12px;border-top:1px solid gray;color:black;cursor:pointer" onclick=topMenu(5)>My Files</div>
<div style="padding:12px;border-top:1px solid gray;color:black;cursor:pointer" onclick=topMenu(4)>My Users</div>
<div style="padding:12px;border-top:1px solid gray;color:black;cursor:pointer" onclick=topMenu(6)>My Server</div>
<div id="logoutMenuOption">
<a id="logoutMenuOptionRef" href=/logout><div style="padding:12px;border-top:1px solid gray;color:black;cursor:pointer">Logout</div></a>
</div>
</div>
<audio id="chimes"><source src="sounds/chimes.mp3" type="audio/mp3" /></audio>
<iframe style=display:none name="fileDownloadFrame"></iframe>
@ -2180,11 +2117,108 @@
// Setup logout control
var logoutControl = '';
if (logoutControls) {
if (logoutControls.name != null) { logoutControl = ('<span onmouseup=go(2) CLASS="LogoffLinkColor" style="cursor:pointer">' + format("Welcome {0}.", logoutControls.name) + '</span>'); }
if (logoutControls.logoutUrl != null) { logoutControl += format(' <a href="' + logoutControls.logoutUrl + '" CLASS="LogoffLinkColor">' + "Logout" + '</a>'); }
if (logoutControls.name != null) {
var userImageSrc = 'images/user-256.png';
if (userinfo && userinfo.flags && (userinfo.flags & 1)) {
if (userinfo.accountImageRnd == null) { userinfo.accountImageRnd = Math.floor(Math.random() * 9999999999); }
userImageSrc = 'userimage.ashx?rnd=' + userinfo.accountImageRnd;
}
logoutControl = '<div id="userDropdown">' +
'<div id="userDropdownButton">' +
'<img id="userDropdownImage" src="' + userImageSrc + '" />' +
'<span id="userDropdownName" class="LogoffLinkColor">' + logoutControls.name + '</span>' +
'<img id="userDropdownArrow" class="LogoffLinkColor" src="images/icons/chevron-down.svg" onerror="this.src=\'images/icons/chevron-down.png\'; this.onerror=null;" />' +
'</div>' +
'<div id="userDropdownMenu">' +
'<div id="userDropdownMenuContainer">' +
'<div class="userDropdownMenuItem userDropdownMobileOnly" onclick="goForward(\'devices\'); QV(\'userDropdownMenu\', false);">' +
'<img id="userDropdownMyDevices" class="userDropdownMenuIcon" src="images/icons/MyDevices.svg" onerror="this.src=\'images/icons/MyDevices.png\'; this.onerror=null;" />' +
'<span>My Devices</span>' +
'</div>' +
'<div class="userDropdownMenuItem userDropdownMobileOnly" onclick="goForward(\'events\'); QV(\'userDropdownMenu\', false);">' +
'<img id="userDropdownMyEvents" class="userDropdownMenuIcon" src="images/icons/MyEvents.svg" onerror="this.src=\'images/icons/MyEvents.png\'; this.onerror=null;" />' +
'<span>My Events</span>' +
'</div>' +
'<div class="userDropdownMenuItem userDropdownMobileOnly" onclick="goForward(\'users\'); QV(\'userDropdownMenu\', false);">' +
'<img id="userDropdownMyUsers" class="userDropdownMenuIcon" src="images/icons/MyUsers.svg" onerror="this.src=\'images/icons/MyUsers.png\'; this.onerror=null;" />' +
'<span>My Users</span>' +
'</div>' +
'<div class="userDropdownMenuItem userDropdownMobileOnly" onclick="goForward(\'files\'); QV(\'userDropdownMenu\', false);">' +
'<img id="userDropdownMyFiles" class="userDropdownMenuIcon" src="images/icons/MyFiles.svg" onerror="this.src=\'images/icons/MyFiles.png\'; this.onerror=null;" />' +
'<span>My Files</span>' +
'</div>' +
'<div class="userDropdownMenuItem userDropdownMobileOnly" onclick="goForward(\'server\'); QV(\'userDropdownMenu\', false);">' +
'<img id="userDropdownMyServer" class="userDropdownMenuIcon" src="images/icons/MyServer.svg" onerror="this.src=\'images/icons/MyServer.png\'; this.onerror=null;" />' +
'<span>My Server</span>' +
'</div>' +
'<div id="userDropdownMenuDivider" class="userDropdownMobileOnly"></div>' +
'<div class="userDropdownMenuItem userDropdownUISettings" onclick="toggleUISubmenu(event);">' +
'<img id="userDropdownUISettings" class="userDropdownMenuIcon" src="images/icons/UISettings.svg" onerror="this.src=\'images/icons/UISettings.png\'; this.onerror=null;" />' +
'<span>UI Settings</span>' +
'<img id="userDropdownChevronRight" class="userDropdownMenuIcon" src="images/icons/chevron-right.svg" onerror="this.src=\'images/icons/chevron-right.png\'; this.onerror=null;" style="margin-left: auto; margin-right: 0;" />' +
'</div>' +
'<div id="uiSubmenu" class="userDropdownSubmenu" style="display:none;" onclick="event.stopPropagation();">' +
'<div class="userDropdownMenuItem" onclick="toggleBootstrapUIMode(); QV(\'userDropdownMenu\', false); QV(\'uiSubmenu\', false); document.removeEventListener(\'click\', closeUISubmenu);">' +
'<img id="userDropdownToggleModernUI" class="userDropdownMenuIcon" src="images/icons/ToggleModernUI.svg" onerror="this.src=\'images/icons/ToggleModernUI.png\'; this.onerror=null;" />' +
'<span>Toggle Modern UI</span>' +
'</div>' +
'<div class="userDropdownMenuItem" onclick="userInterfaceSelectMenu(1); QV(\'userDropdownMenu\', false); QV(\'uiSubmenu\', false); document.removeEventListener(\'click\', closeUISubmenu);">' +
'<img id="userDropdownLeftbarinterface" class="userDropdownMenuIcon" src="images/icons/Leftbarinterface.svg" onerror="this.src=\'images/icons/Leftbarinterface.png\'; this.onerror=null;" />' +
'<span>Left bar interface</span>' +
'</div>' +
'<div class="userDropdownMenuItem" onclick="userInterfaceSelectMenu(2); QV(\'userDropdownMenu\', false); QV(\'uiSubmenu\', false); document.removeEventListener(\'click\', closeUISubmenu);">' +
'<img id="userDropdownTopbarinterface" class="userDropdownMenuIcon" src="images/icons/Topbarinterface.svg" onerror="this.src=\'images/icons/Topbarinterface.png\'; this.onerror=null;" />' +
'<span>Top bar interface</span>' +
'</div>' +
'<div class="userDropdownMenuItem" onclick="userInterfaceSelectMenu(3); QV(\'userDropdownMenu\', false); QV(\'uiSubmenu\', false); document.removeEventListener(\'click\', closeUISubmenu);">' +
'<img id="userDropdownFixedwidthinterface" class="userDropdownMenuIcon" src="images/icons/Fixedwidthinterface.svg" onerror="this.src=\'images/icons/Fixedwidthinterface.png\'; this.onerror=null;" />' +
'<span>Fixed width interface</span>' +
'</div>' +
'<div class="userDropdownMenuItem" onclick="toggleFooterBarMode(); QV(\'userDropdownMenu\', false); QV(\'uiSubmenu\', false); document.removeEventListener(\'click\', closeUISubmenu);">' +
'<img id="userDropdownTogglefooterbar" class="userDropdownMenuIcon" src="images/icons/Togglefooterbar.svg" onerror="this.src=\'images/icons/Togglefooterbar.png\'; this.onerror=null;" />' +
'<span>Toggle footer bar</span>' +
'</div>' +
'</div>' +
'<div class="userDropdownMenuItem" onclick="toggleNightMode(); QV(\'userDropdownMenu\', false);">' +
'<img id="userDropdownTogglenightmode" class="userDropdownMenuIcon" src="images/icons/Togglenightmode.svg" onerror="this.src=\'images/icons/Togglenightmode.png\'; this.onerror=null;" />' +
'<span>Toggle night mode</span>' +
'</div>' +
'<div class="userDropdownMenuItem" onclick="showNotes(false); QV(\'userDropdownMenu\', false);">' +
'<img id="userDropdownPersonalNotes" class="userDropdownMenuIcon" src="images/icons/PersonalNotes.svg" onerror="this.src=\'images/icons/PersonalNotes.png\'; this.onerror=null;" />' +
'<span>Personal Notes</span>' +
'</div>' +
'<div class="userDropdownMenuItem" onclick="go(2); QV(\'userDropdownMenu\', false);">' +
'<img id="userDropdownMyAccount" class="userDropdownMenuIcon" src="images/icons/MyAccount.svg" onerror="this.src=\'images/icons/MyAccount.png\'; this.onerror=null;" />' +
'<span>My Account</span>' +
'</div>' +
'<div id="userDropdownMenuDivider"></div>';
if (logoutControls.logoutUrl != null) {
logoutControl += '<div class="userDropdownMenuItem" onclick="window.location.href=\'' + logoutControls.logoutUrl + '\'">' +
'<img id="userDropdownLogout" class="userDropdownMenuIcon" src="images/icons/Logout.svg" onerror="this.src=\'images/icons/Logout.png\'; this.onerror=null;" />' +
'<span>Logout</span>' +
'</div>';
}
logoutControl += '</div></div></div>';
}
}
if (args.hide & 1) { QH('logoutControlSpan2', logoutControl); } else { QH('logoutControlSpan', logoutControl); }
if (logoutControls && logoutControls.logoutUrl) { Q('logoutMenuOptionRef').href = logoutControls.logoutUrl; }
setTimeout(updateUserDropdown, 100);
// Setup the context menu
document.onclick = function (e) { hideContextMenu(); }
@ -2612,6 +2646,83 @@
if (footerBar) { Q('uiViewButton5').classList.add('uiSelectorSel'); }
}
// Toggle user dropdown menu
function toggleUserDropdown() {
if (xxdialogMode) return;
var userDropdownMenu = Q('userDropdownMenu');
if (userDropdownMenu) {
var isVisible = (userDropdownMenu.style.display == 'none' || userDropdownMenu.style.display == '');
if (isVisible) {
userDropdownMenu.style.display = 'block';
QV('uiMenu', false);
QV('topMenu', false);
} else {
userDropdownMenu.style.display = 'none';
var uiSubmenu = Q('uiSubmenu');
if (uiSubmenu) {
uiSubmenu.style.display = 'none';
}
}
}
}
function updateUserDropdown() {
if (logoutControls && logoutControls.name) {
var userImageSrc = 'images/user-256.png';
if (userinfo && userinfo.flags && (userinfo.flags & 1)) {
if (userinfo.accountImageRnd == null) { userinfo.accountImageRnd = Math.floor(Math.random() * 9999999999); }
userImageSrc = 'userimage.ashx?rnd=' + userinfo.accountImageRnd;
}
var userImage = Q('userDropdownImage');
if (userImage) {
userImage.src = userImageSrc;
}
var userDropdownButton = Q('userDropdownButton');
if (userDropdownButton) {
userDropdownButton.onclick = function(e) {
e.preventDefault();
e.stopPropagation();
toggleUserDropdown();
return false;
};
}
var userDropdownMenu = Q('userDropdownMenu');
if (userDropdownMenu) {
userDropdownMenu.style.display = 'none';
}
}
}
function toggleUISubmenu(event) {
if (event) {
event.stopPropagation();
}
var uiSubmenu = Q('uiSubmenu');
if (uiSubmenu) {
var isVisible = (uiSubmenu.style.display == 'block');
uiSubmenu.style.display = isVisible ? 'none' : 'block';
if (!isVisible) {
setTimeout(function() {
document.addEventListener('click', closeUISubmenu);
}, 10);
}
}
}
function closeUISubmenu(event) {
var uiSubmenu = Q('uiSubmenu');
var userDropdown = Q('userDropdown');
if (uiSubmenu && userDropdown) {
if (!uiSubmenu.contains(event.target) && !userDropdown.contains(event.target)) {
uiSubmenu.style.display = 'none';
document.removeEventListener('click', closeUISubmenu);
}
}
}
function userInterfaceSelectMenu(s) {
if (s) { uiMode = s; putstore('uiMode', uiMode); }
webPageFullScreen = (uiMode < 3);
@ -2843,6 +2954,7 @@
// If we are site administrator, register to get server statistics
if ((siteRights & 21) != 0) { meshserver.send({ action: 'serverstats', interval: 10000 }); }
updateUserDropdown();
}
// To boost the speed of the web page when even floods occur, this method perform a delayed update on the web page.
@ -3109,6 +3221,7 @@
userinfo = message.userinfo;
updateSiteAdmin();
updateSelf();
updateUserDropdown();
break;
}
case 'users': {
@ -4638,22 +4751,6 @@
// MY DEVICES
//
function topMenu(select) {
if ((xxdialogMode != null) && (xxdialogMode != 0) && (xxdialogMode != 999)) return;
if (select === undefined) {
var x = (QS('topMenu').display == 'none');
if (x == true) { if ((xxdialogMode == 0) || (xxdialogMode == null)) { QV('topMenu', true); xxdialogMode = 999; } } else { QV('topMenu', false); xxdialogMode = 0; }
} else {
QV('topMenu', false);
xxdialogMode = 0;
if ((select == 1)) { goForward('devices'); }
if ((select == 2)) { goForward('account'); }
if ((select == 3)) { goForward('events'); }
if ((select == 4)) { goForward('users'); }
if ((select == 5)) { goForward('files'); }
if ((select == 6)) { goForward('server'); }
}
}
function onRealNameCheckBox() {
showRealNames = Q('RealNameCheckBox').checked;
@ -7587,6 +7684,7 @@
QV('filesContextMenu', false);
QV('deskPlayerContextMenu', false);
QV('deskKeyShortcutContextMenu', false);
QV('userDropdownMenu', false);
QV('deskPreConfigShortcutContextMenu', false);
QV('deskPreConfigScriptContextMenu', false);
QV('expandAllContextMenu', false);