Refactor: Rebuilt Masthead User Dropdown for Cleaner Structure and Modern Behavior (#7398)

* Update default3.handlebars

* Update default3.handlebars

* Update style-bootstrap.css

* Update translate.json

* Improve user dropdown menu visibility and control

Refactored user dropdown menu items in default3.handlebars to add unique IDs and a new 'mobile-menu-item' class for better visibility control. Added logic to conditionally show/hide menu items based on UI mode and feature flags, including forced Modern UI and night mode. Updated CSS to remove unnecessary border styling. updated webserver.js to set a feature flag when Modern UI is forced.

* Adjust desktop breakpoint and submenu animation logic

Changed desktop breakpoint from 1025px to 769px in both CSS and JS to better align with responsive design. Submenu animation now respects the user's reduced motion preference, disabling transitions when 'prefers-reduced-motion' is enabled.

* Improve masthead layout and text overflow handling

* Refine mobile menu item visibility logic

Updated the logic for showing/hiding mobile menu items based on uiMode. Now, only menu items without 'users-menu-item', 'files-menu-item', or 'server-menu-item' classes are shown when uiMode is not 2 or 3.

* Update style-bootstrap.css

* Comment out toggleModernUIMenuItem line

Comment out the toggleModernUIMenuItem visibility check.

* Update translate.json
This commit is contained in:
TheDevRyan
2025-11-17 11:46:46 +00:00
committed by GitHub
parent 6103d94e2c
commit 53f108fe7b
4 changed files with 3952 additions and 3610 deletions

View File

@@ -92,7 +92,7 @@ body {
grid-gap: 0px;
grid-template-areas: "header header" "sidebar nav" "sidebar content" "sidebar footer";
-ms-grid-columns: 90px 1fr;
grid-template-columns: 90px auto;
grid-template-columns: 70px auto;
-ms-grid-rows: 66px 24px 1fr 45px;
grid-template-rows: 66px 24px auto 45px;
}
@@ -128,20 +128,52 @@ body {
}
#masthead {
display: flex;
justify-content: space-between;
align-items: center;
width: auto;
margin: 0;
padding: 0;
overflow: hidden;
text-align: right;
padding: 0 1rem;
background-color: #036;
background-image: url(../logo.png);
background-position-x: 0px;
background-position-y: 0px;
background-attachment: initial;
background-position: 0 0;
background-repeat: no-repeat;
/*width: 960px;*/
height: 66px;
}
.masthead-container {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.5rem 1rem;
width: 100%;
}
.idle-timeout-notify {
color: yellow;
}
.masthead-left {
display: flex;
align-items: center;
gap: 1rem;
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
}
.masthead-right {
display: flex;
align-items: center;
gap: 1.5rem;
flex: 0 0 auto;
}
.notification-icon {
cursor: pointer;
}
.user-menu-container {
display: flex;
align-items: center;
}
.fullscreen #masthead {
width: 100%;
@@ -163,41 +195,164 @@ body {
display: none;
}
.masthead-left {
display: flex;
align-items: center;
gap: 1rem;
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
}
.masthead-title {
display: flex;
align-items: center;
flex-shrink: 0;
}
.masthead-titles-container {
display: flex;
align-items: center;
gap: 0.5rem;
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
}
#masthead .title {
float: left;
height: 66px;
color: #c8c8c8;
padding-left: 14px;
font-size: 46px;
font-family: Arial,Helvetica,sans-serif;
font-weight: bold;
text-shadow: 1px 1px 2px #000;
cursor: pointer;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex-shrink: 1;
min-width: 0;
}
#masthead .title2 {
float: left;
height: 66px;
color: #c8c8c8;
padding-left: 5px;
padding-top: 12px;
font-size: 14px;
font-family: Arial,Helvetica,sans-serif;
font-weight: bold;
text-shadow: 1px 1px 2px #000;
cursor: pointer;
padding-top: 12px;
padding-bottom: 30px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex-shrink: 1;
min-width: 0;
}
@media (max-width: 1500px) {
#masthead .title {
font-size: 40px;
}
}
@media (max-width: 1300px) {
#masthead .title {
font-size: 34px;
white-space: normal;
line-height: 1.1;
}
#masthead .title2 {
font-size: 13px;
padding-top: 10px;
padding-bottom: 20px;
white-space: normal;
line-height: 1.2;
}
}
@media (max-width: 1100px) {
.masthead-left {
gap: 0.75rem;
}
.masthead-titles-container {
max-width: 100%;
}
#masthead .title {
font-size: 30px;
}
}
.masthead-right {
display: flex;
align-items: center;
gap: 0.5rem;
margin-left: auto;
flex-direction: row; /* Ensure horizontal layout */
}
.notification-icon {
display: flex;
align-items: center;
cursor: pointer;
padding: 0.5rem;
border-radius: 4px;
}
.notification-icon:hover {
background-color: rgba(255, 255, 255, 0.1);
}
#notificationCount {
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
height: 40px;
padding: 6px 8px;
border-radius: 4px;
position: static;
flex: 0 0 auto;
min-width: unset;
margin: 0;
top: auto;
right: auto;
}
#notificationCount:hover {
background-color: rgba(255, 255, 255, 0.1);
}
#notificationBadge {
font-size: 0.65rem !important;
box-shadow: -0.1px 1px 2px #000000;
-webkit-box-shadow: -0.1px 1px 2px #000000;
-moz-box-shadow: -0.1px 1px 2px #000000;
}
.user-menu-container {
display: flex;
align-items: center;
flex: 0 0 auto; /* Prevent flex item from growing or shrinking */
}
#page_leftbar {
-ms-grid-column: 1;
-ms-grid-row: 2;
-ms-grid-row-span: 3;
height: 100%;
/* height: calc(100vh - 66px); */
width: 90px;
width: 70px;
z-index: 1000;
background: #113962;
background: linear-gradient(to bottom, #104893 0%,#113962 100%);
color: white;
overflow-y: hidden;
overflow-x: hidden;
display: none;
}
@@ -2352,10 +2507,10 @@ nav .lbbuttonsel2 {
}
.notifiyBox {
position: absolute;
position: fixed;
z-index: 1000;
top: 80px;
right: 10rem;
right: 8.3rem;
width: 300px;
text-align: left;
background-color: #F0ECCD;
@@ -2369,6 +2524,11 @@ nav .lbbuttonsel2 {
max-height: 200px;
}
body:not(.fullscreen) .notifiyBox {
position: absolute;
right: calc(50vw - 512px + 10rem);
}
.night .notifiyBox {
color: black;
}
@@ -2403,19 +2563,7 @@ nav .lbbuttonsel2 {
.notification:hover {
background-color: #EFE8B6;
}
#notificationCount {
min-width: 28px;
font-size: 20px;
border-radius: 5px;
text-align: center;
margin: 8px;
cursor: pointer;
padding: 4px;
position: absolute;
right: 9rem;
top: 0.2em;
}
#notificationBadge {
box-shadow: -0.1px 1px 2px #000000;
-webkit-box-shadow: -0.1px 1px 2px #000000;
@@ -3314,8 +3462,17 @@ nav .lbbuttonsel2 {
.sidebar .nav-link {
font-size: xx-large;
}
.nav-link {
width: unset !important;
}
.nav {
--bs-nav-link-padding-x: 19px !important;
}
.card:hover, #p2AccountImage:hover, #p2canvas:hover {
background: #f3f5f7 !important;
}
@@ -3365,7 +3522,31 @@ nav .lbbuttonsel2 {
}
#masthead .title {
font-size: 36px;
font-size: 22px;
padding-top: 4px;
}
#masthead .title2 {
font-size: 12px;
padding-top: 2px;
padding-bottom: 10px;
}
.masthead-left {
gap: 0.5rem;
max-width: calc(100% - 120px);
}
.masthead-titles-container {
flex-direction: column;
align-items: flex-start;
gap: 0;
}
.masthead-titles-container {
flex-direction: column;
align-items: flex-start;
gap: 0;
}
#masthead {
@@ -3386,9 +3567,7 @@ nav .lbbuttonsel2 {
text-align: left;
}
#userDropdown {
display: inline-block !important;
position: absolute !important;
right: 10px !important;
display: flex !important;
}
#userDropdownName {
@@ -3400,7 +3579,7 @@ nav .lbbuttonsel2 {
}
#userDropdownMenuDivider.userDropdownMobileOnly {
display: block !important;
display: block;
}
.userDropdownSubmenu {
@@ -3428,16 +3607,10 @@ nav .lbbuttonsel2 {
border-radius: 0 !important;
}
.userDropdownSubmenu .userDropdownMenuItem {
border-left: 3px solid #444444 !important;
}
.userDropdownMenuItem:hover {
border-radius: unset !important;
}
#notificationCount {
right: 4rem;
}
.notifiyBox {
right: 3.5rem;
}
@@ -3480,10 +3653,9 @@ nav .lbbuttonsel2 {
} */
/* User Dropdown Menu Styles */
#userDropdown {
position: absolute;
display: inline-block;
display: flex;
align-items: center;
z-index: 1000;
right: 10px;
background: #00000026;
border-radius: 4px;
}
@@ -3518,6 +3690,10 @@ nav .lbbuttonsel2 {
font-size: 12px;
}
#userDropdown {
position: relative;
}
#userDropdownMenu {
display: none;
position: absolute;
@@ -3534,6 +3710,8 @@ nav .lbbuttonsel2 {
font-size: small;
}
#userDropdownMenuContainer {
padding: 6px 0;
}
@@ -3579,6 +3757,10 @@ nav .lbbuttonsel2 {
border-top: var(--bs-border-width) solid;
}
.userDropdownUISettings {
position: relative;
}
.userDropdownSubmenu {
position: absolute;
right: 96%;
@@ -3591,6 +3773,7 @@ nav .lbbuttonsel2 {
z-index: 10001;
padding: 6px 0;
font-size: small;
display: none;
}
.userDropdownSubmenu .userDropdownMenuItem {
@@ -3626,30 +3809,27 @@ nav .lbbuttonsel2 {
background-color: #292c2c;
}
body:not(.fullscreen) #userDropdown {
right: calc(50vw - 470px);
.userDropdownUISettings .fa-chevron-right {
transition: transform 0.3s ease;
transform: rotate(0deg);
}
@media (min-width: 1025px) {
.userDropdownUISettings .fa-chevron-right.rotated {
transform: rotate(90deg);
}
@media (min-width: 769px) {
.userDropdownSubmenu {
display: block !important;
position: absolute;
display: none;
opacity: 0;
transform: translateX(12px);
transition: opacity 0.2s ease, transform 0.3s ease;
pointer-events: none;
}
.userDropdownUISettings:hover + .userDropdownSubmenu,
.userDropdownSubmenu:hover,
.userDropdownMenuItem:hover .userDropdownSubmenu {
display: block !important;
.userDropdownSubmenu.show {
display: block;
opacity: 1;
transform: translateX(0);
pointer-events: auto;
}
.userDropdownUISettings:hover .fa-chevron-right,
.userDropdownUISettings:has(+ .userDropdownSubmenu:hover) .fa-chevron-right {
transform: rotate(90deg) !important;
transition: transform 0.3s ease;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -140,26 +140,120 @@
<!-- main page -->
<div id=container>
<div id="notifiyBox" class="notifiyBox" style="display:none"></div>
<div id=masthead class=noselect>
<div style="float:left">{{{titlehtml}}}</div>
<div class="title" onclick="go(1,event)">{{{title1}}}</div>
<div class="title2">{{{title2}}}</div>
<div style="float:right">
<div id="masthead" class="noselect masthead-container">
<div class="masthead-left">
<div class="masthead-title" onclick="go(1,event)">{{{titlehtml}}}</div>
<div class="masthead-titles-container">
<div class="title" onclick="go(1,event)">{{{title1}}}</div>
<div class="title2" onclick="go(1,event)">{{{title2}}}</div>
</div>
</div>
<div class="masthead-right">
<div id="notificationCount"
class="position-relative"
class="notification-icon position-relative"
title="Click to view current notifications"
onclick="clickNotificationIcon()"
style="display: none;">
<i class="fa-solid fa-bell fa-lg text-white"></i>
<span id="notificationBadge"
class="position-absolute top-1 start-100 translate-middle badge rounded-pill bg-danger"
style="font-size: 0.65rem;">
0
</span>
<i class="fa-solid fa-bell fa-xl text-white"></i>
<span id="notificationBadge"
class="position-absolute top-1 start-100 translate-middle badge rounded-pill bg-danger">0</span>
</div>
<div class="user-menu-container">
<div id="userDropdown">
<div id="userDropdownButton">
<img id="userDropdownImage" src="images/user-256.png" alt="User" />
<span id="userDropdownName" class="LogoffLinkColor"></span>
<i class="fa fa-chevron-down LogoffLinkColor" style="font-size: 12px;"></i>
</div>
<div id="userDropdownMenu">
<div id="userDropdownMenuContainer">
<div class="userDropdownMenuItem userDropdownMobileOnly mobile-menu-item" data-action="devices" onclick="handleUserMenuItem('devices')">
<i class="fa fa-desktop userDropdownMenuIcon"></i>
<span>My Devices</span>
</div>
<div class="userDropdownMenuItem userDropdownMobileOnly mobile-menu-item" data-action="events" onclick="handleUserMenuItem('events')">
<i class="fa fa-calendar userDropdownMenuIcon"></i>
<span>My Events</span>
</div>
<div id="mobileUsersMenuItem" class="userDropdownMenuItem userDropdownMobileOnly users-menu-item mobile-menu-item d-none" data-action="users" onclick="handleUserMenuItem('users')">
<i class="fa fa-users userDropdownMenuIcon"></i>
<span>My Users</span>
</div>
<div id="mobileFilesMenuItem" class="userDropdownMenuItem userDropdownMobileOnly files-menu-item mobile-menu-item d-none" data-action="files" onclick="handleUserMenuItem('files')">
<i class="fa fa-folder userDropdownMenuIcon"></i>
<span>My Files</span>
</div>
<div id="mobileServerMenuItem" class="userDropdownMenuItem userDropdownMobileOnly server-menu-item mobile-menu-item d-none" data-action="server" onclick="handleUserMenuItem('server')">
<i class="fa fa-server userDropdownMenuIcon"></i>
<span>My Server</span>
</div>
<div id="userDropdownMenuDivider" class="userDropdownMobileOnly mobile-menu-item"></div>
<div class="userDropdownMenuItem userDropdownUISettings" onclick="toggleUISubmenu(); event.stopPropagation();">
<i class="fa fa-sliders userDropdownMenuIcon"></i>
<span>UI Settings</span>
<i class="fa fa-chevron-right userDropdownMenuIcon submenu-arrow"></i>
</div>
<div id="uiSubmenu" class="userDropdownSubmenu">
<div id="toggleModernUIMenuItem" class="userDropdownMenuItem" data-action="toggle-modern-ui" onclick="handleUserMenuItem('toggle-modern-ui')">
<i class="fa fa-toggle-on userDropdownMenuIcon"></i>
<span>Toggle Modern UI</span>
</div>
<div class="userDropdownMenuItem" data-action="left-bar" onclick="handleUserMenuItem('left-bar')">
<i class="fa fa-window-maximize fa-rotate-270 userDropdownMenuIcon"></i>
<span>Left Bar Interface</span>
</div>
<div class="userDropdownMenuItem" data-action="top-bar" onclick="handleUserMenuItem('top-bar')">
<i class="fa fa-window-maximize userDropdownMenuIcon"></i>
<span>Top Bar Interface</span>
</div>
<div class="userDropdownMenuItem" data-action="fixed-width" onclick="handleUserMenuItem('fixed-width')">
<i class="fa fa-columns userDropdownMenuIcon"></i>
<span>Fixed Width Interface</span>
</div>
<div id="toggleFooterMenuItem" class="userDropdownMenuItem" data-action="toggle-footer" onclick="handleUserMenuItem('toggle-footer')">
<i class="fa fa-window-minimize userDropdownMenuIcon"></i>
<span>Toggle Footer Bar</span>
</div>
<div id="userDropdownMenuDivider" class="userDropdownMobileOnly"></div>
</div>
<div id="toggleNightMenuItem" class="userDropdownMenuItem" data-action="toggle-night" onclick="toggleNightMode(); QV('userDropdownMenu', false); QV('uiSubmenu', false); resetChevronArrow();">
<i id="nightModeIcon" class="fa userDropdownMenuIcon night-mode-icon"></i>
<span id="nightModeText">Toggle night mode</span>
</div>
<div class="userDropdownMenuItem" data-action="notes" onclick="handleUserMenuItem('notes')">
<i class="fa fa-sticky-note userDropdownMenuIcon"></i>
<span>Personal Notes</span>
</div>
<div class="userDropdownMenuItem" data-action="account" onclick="handleUserMenuItem('account')">
<i class="fa fa-user userDropdownMenuIcon"></i>
<span>My Account</span>
</div>
<div id="userDropdownMenuDivider"></div>
<div class="userDropdownMenuItem" data-action="logout" onclick="handleUserMenuItem('logout')">
<i class="fa fa-sign-out userDropdownMenuIcon"></i>
<span>Logout</span>
</div>
</div>
</div>
</div>
<span id="idleTimeoutNotify" class="idle-timeout-notify"></span>
</div>
</div>
<p id="logoutControl"><span id=logoutControlSpan class="logoncontrolspan"></span><span id=idleTimeoutNotify
style="color:yellow"></span></p>
</div>
<div class="sidebar flex-column" id="page_leftbar">
<div style="height:24px"></div>
@@ -2112,120 +2206,13 @@
}
args.hide = hide;
QV('uiViewButton5', !(args.hide & 4)); // Hide the footer toggle button if footer is hidden anyway.
QV('toggleFooterMenuItem', !(args.hide & 4)); // Hide the footer toggle menu item if footer is hidden anyway.
QV('toggleNightMenuItem', ((features2 & 0x00100000) == 0) && ((features2 & 0x00200000) == 0)); // Hide night mode toggle if permanent night (1) or day (2) mode is set
//QV('toggleModernUIMenuItem', ((features2 & 0x80000000) == 0)); // Show only if allowed and not forced siteStyle=3
updateMobileTopBarVisibility();
adjustPanels();
// Setup logout control
var logoutControl = '';
if (logoutControls) {
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;
}
// Check permissions
var siteRights = userinfo ? userinfo.siteadmin : 0;
var serverFeatures = parseInt('{{{serverfeatures}}}');
var canViewUsers = ((users != null) && ((features & 4) == 0)) || (((userinfo && userinfo.siteadmin & 512) != 0) && ((features & 0x08000000) != 0));
var canViewFiles = (siteRights & 8) != 0;
var canViewServer = (siteRights & 21) && ((serverFeatures & 64) != 0);
logoutControl = '<div id="userDropdown">' +
'<div id="userDropdownButton">' +
'<img id="userDropdownImage" src="' + userImageSrc + '" />' +
'<span id="userDropdownName" class="LogoffLinkColor">' + logoutControls.name + '</span>' +
'<i class="fa fa-chevron-down LogoffLinkColor" style="font-size: 12px;"></i>' +
'</div>' +
'<div id="userDropdownMenu">' +
'<div id="userDropdownMenuContainer">' +
'<div class="userDropdownMenuItem userDropdownMobileOnly" onclick="goForward(\'devices\'); QV(\'userDropdownMenu\', false); QV(\'uiSubmenu\', false); resetChevronArrow(); document.removeEventListener(\'click\', closeUISubmenu);">' +
'<i class="fa fa-desktop userDropdownMenuIcon"></i>' +
'<span>' + "My Devices" + '</span>' +
'</div>' +
'<div class="userDropdownMenuItem userDropdownMobileOnly" onclick="goForward(\'events\'); QV(\'userDropdownMenu\', false); QV(\'uiSubmenu\', false); resetChevronArrow(); document.removeEventListener(\'click\', closeUISubmenu);">' +
'<i class="fa fa-calendar userDropdownMenuIcon"></i>' +
'<span>' + "My Events" + '</span>' +
'</div>' +
(canViewUsers ? '<div class="userDropdownMenuItem userDropdownMobileOnly" onclick="goForward(\'users\'); QV(\'userDropdownMenu\', false); QV(\'uiSubmenu\', false); resetChevronArrow(); document.removeEventListener(\'click\', closeUISubmenu);">' +
'<i class="fa fa-users userDropdownMenuIcon"></i>' +
'<span>' + "My Users" + '</span>' +
'</div>' : '') +
(canViewFiles ? '<div class="userDropdownMenuItem userDropdownMobileOnly" onclick="goForward(\'files\'); QV(\'userDropdownMenu\', false); QV(\'uiSubmenu\', false); resetChevronArrow(); document.removeEventListener(\'click\', closeUISubmenu);">' +
'<i class="fa fa-folder userDropdownMenuIcon"></i>' +
'<span>' + "My Files" + '</span>' +
'</div>' : '') +
(canViewServer ? '<div class="userDropdownMenuItem userDropdownMobileOnly" onclick="goForward(\'server\'); QV(\'userDropdownMenu\', false); QV(\'uiSubmenu\', false); resetChevronArrow(); document.removeEventListener(\'click\', closeUISubmenu);">' +
'<i class="fa fa-server userDropdownMenuIcon"></i>' +
'<span>' + "My Server" + '</span>' +
'</div>' : '') +
'<div id="userDropdownMenuDivider" class="userDropdownMobileOnly"></div>' +
'<div class="userDropdownMenuItem userDropdownUISettings" onclick="toggleUISubmenu(event);">' +
'<i class="fa fa-sliders userDropdownMenuIcon"></i>' +
'<span>' + "UI Settings" + '</span>' +
'<i class="fa fa-chevron-right userDropdownMenuIcon" style="margin-left: auto; margin-right: 0; transition: transform 0.3s ease;"></i>' +
'</div>' +
'<div id="uiSubmenu" class="userDropdownSubmenu" style="display:none;" onclick="event.stopPropagation();">' +
'<div class="userDropdownMenuItem" onclick="toggleBootstrapUIMode(); QV(\'userDropdownMenu\', false); QV(\'uiSubmenu\', false); resetChevronArrow(); document.removeEventListener(\'click\', closeUISubmenu);">' +
'<i class="fa fa-toggle-on userDropdownMenuIcon"></i>' +
'<span>' + "Toggle Modern UI" + '</span>' +
'</div>' +
'<div class="userDropdownMenuItem" onclick="userInterfaceSelectMenu(1); QV(\'userDropdownMenu\', false); QV(\'uiSubmenu\', false); resetChevronArrow(); document.removeEventListener(\'click\', closeUISubmenu);">' +
'<i class="fa fa-window-maximize fa-rotate-270 userDropdownMenuIcon"></i>' +
'<span>' + "Left Bar Interface" + '</span>' +
'</div>' +
'<div class="userDropdownMenuItem" onclick="userInterfaceSelectMenu(2); QV(\'userDropdownMenu\', false); QV(\'uiSubmenu\', false); resetChevronArrow(); document.removeEventListener(\'click\', closeUISubmenu);">' +
'<i class="fa fa-window-maximize userDropdownMenuIcon"></i>' +
'<span>' + "Top Bar Interface" + '</span>' +
'</div>' +
'<div class="userDropdownMenuItem" onclick="userInterfaceSelectMenu(3); QV(\'userDropdownMenu\', false); QV(\'uiSubmenu\', false); resetChevronArrow(); document.removeEventListener(\'click\', closeUISubmenu);">' +
'<i class="fa fa-columns userDropdownMenuIcon"></i>' +
'<span>' + "Fixed Width Interface" + '</span>' +
'</div>' +
'<div class="userDropdownMenuItem" onclick="toggleFooterBarMode(); QV(\'userDropdownMenu\', false); QV(\'uiSubmenu\', false); resetChevronArrow(); document.removeEventListener(\'click\', closeUISubmenu);">' +
'<i class="fa fa-window-minimize userDropdownMenuIcon"></i>' +
'<span>' + "Toggle Footer Bar" + '</span>' +
'</div>' +
'</div>' +
'<div class="userDropdownMenuItem" onclick="toggleNightMode(); QV(\'userDropdownMenu\', false); QV(\'uiSubmenu\', false); resetChevronArrow(); document.removeEventListener(\'click\', closeUISubmenu);">' +
'<i class="fa ' + (document.body.classList.contains('night') ? 'fa-sun' : 'fa-moon') + ' userDropdownMenuIcon"></i>' +
'<span>' + "Toggle night mode" + '</span>' +
'</div>' +
'<div class="userDropdownMenuItem" onclick="showNotes(false); QV(\'userDropdownMenu\', false); QV(\'uiSubmenu\', false); resetChevronArrow(); document.removeEventListener(\'click\', closeUISubmenu);">' +
'<i class="fa fa-sticky-note userDropdownMenuIcon"></i>' +
'<span>' + "Personal Notes" + '</span>' +
'</div>' +
'<div class="userDropdownMenuItem" onclick="go(2); QV(\'userDropdownMenu\', false); QV(\'uiSubmenu\', false); resetChevronArrow(); document.removeEventListener(\'click\', closeUISubmenu);">' +
'<i class="fa fa-user userDropdownMenuIcon"></i>' +
'<span>' + "My Account" + '</span>' +
'</div>' +
'<div id="userDropdownMenuDivider"></div>';
if (logoutControls.logoutUrl != null) {
logoutControl += '<div class="userDropdownMenuItem" onclick="QV(\'uiSubmenu\', false); resetChevronArrow(); document.removeEventListener(\'click\', closeUISubmenu); window.location.href=\'' + logoutControls.logoutUrl + '\'">' +
'<i class="fa fa-sign-out userDropdownMenuIcon"></i>' +
'<span>' + "Logout" + '</span>' +
'</div>';
}
logoutControl += '</div></div></div>';
}
}
if (args.hide & 1) { QH('logoutControlSpan2', logoutControl); } else { QH('logoutControlSpan', logoutControl); }
setTimeout(updateUserDropdown, 100);
setTimeout(updateUserDropdownVisibility, 100);
// Setup the context menu
document.onclick = function (e) { hideContextMenu(); }
@@ -2664,6 +2651,7 @@
QV('uiMenu', false);
QV('topMenu', false);
updateNightModeIcon();
updateMobileTopBarVisibility();
} else {
userDropdownMenu.style.display = 'none';
var uiSubmenu = Q('uiSubmenu');
@@ -2707,21 +2695,27 @@
function toggleUISubmenu(event) {
if (event) {
event.stopPropagation();
event.preventDefault();
}
var uiSubmenu = Q('uiSubmenu');
if (uiSubmenu) {
var isVisible = (uiSubmenu.style.display == 'block');
uiSubmenu.style.display = isVisible ? 'none' : 'block';
var chevronIcon = document.querySelector('.userDropdownMenuItem[onclick*="toggleUISubmenu"] .fa-chevron-right');
if (chevronIcon) {
if (isVisible) {
chevronIcon.style.transform = 'rotate(0deg)';
} else {
chevronIcon.style.transform = 'rotate(90deg)';
if (isVisible) {
uiSubmenu.style.display = 'none';
uiSubmenu.classList.remove('show');
var chevronIcon = document.querySelector('.userDropdownUISettings .fa-chevron-right');
if (chevronIcon) {
chevronIcon.classList.remove('rotated');
}
document.removeEventListener('click', closeUISubmenu);
} else {
uiSubmenu.style.display = 'block';
uiSubmenu.classList.add('show');
var chevronIcon = document.querySelector('.userDropdownUISettings .fa-chevron-right');
if (chevronIcon) {
chevronIcon.classList.add('rotated');
}
}
if (!isVisible) {
setTimeout(function() {
document.addEventListener('click', closeUISubmenu);
}, 10);
@@ -2735,7 +2729,11 @@
if (uiSubmenu && userDropdown) {
if (!uiSubmenu.contains(event.target) && !userDropdown.contains(event.target)) {
uiSubmenu.style.display = 'none';
resetChevronArrow();
uiSubmenu.classList.remove('show');
var chevronIcon = document.querySelector('.userDropdownUISettings .fa-chevron-right');
if (chevronIcon) {
chevronIcon.classList.remove('rotated');
}
document.removeEventListener('click', closeUISubmenu);
}
}
@@ -2751,6 +2749,21 @@
adjustPanels();
}
function updateMobileTopBarVisibility() {
try {
var items = document.querySelectorAll('.mobile-menu-item');
for (var i = 0; i < items.length; i++) {
if ((uiMode == 2) || (uiMode == 3)) {
items[i].classList.add('d-none');
} else {
if (!(items[i].classList.contains('users-menu-item') || items[i].classList.contains('files-menu-item') || items[i].classList.contains('server-menu-item'))) {
items[i].classList.remove('d-none');
}
}
}
} catch (ex) { }
}
function toggleNightMode() {
if (xxdialogMode) return;
var cNightMode = getstore('nightMode', '0');
@@ -2930,6 +2943,7 @@
QV('p2AccountImageFrame', !accountSettingsLocked)
QV('p2ServerActions', (siteRights & 21) && ((serverFeatures & 143) != 0));
QV('LeftMenuMyServer', (siteRights & 21) && ((serverFeatures & 64) != 0)); // 16 + 4 + 1
try { (QS('LeftMenuMyServer').display == 'none') ? QC('mobileServerMenuItem').add('d-none') : QC('mobileServerMenuItem').remove('d-none'); } catch (e) { }
QV('MainMenuMyServer', siteRights & 21);
QV('p2ServerActionsBackup', (siteRights & 1) && ((serverFeatures & 1) != 0));
QV('p2ServerActionsRestore', (siteRights & 4) && ((serverFeatures & 2) != 0));
@@ -2938,6 +2952,7 @@
QV('p2ServerActionsConfig', (siteRights & 16) && ((serverFeatures & 128) != 0));
QV('MainMenuMyFiles', siteRights & 8);
QV('LeftMenuMyFiles', siteRights & 8);
try { (QS('LeftMenuMyFiles').display == 'none') ? QC('mobileFilesMenuItem').add('d-none') : QC('mobileFilesMenuItem').remove('d-none'); } catch (e) { }
if (((siteRights & 8) == 0) && (xxcurrentView == 5)) { setDialogMode(0); go(1); }
if (currentNode != null) { gotoDevice(currentNode._id, xxcurrentView, true); }
@@ -3067,6 +3082,7 @@
// Adjust "My Users" tabs
QV('MainMenuMyUsers', ((users != null) && ((features & 4) == 0)) || (((userinfo.siteadmin & 512) != 0) && ((features & 0x08000000) != 0)));
QV('LeftMenuMyUsers', ((users != null) && ((features & 4) == 0)) || (((userinfo.siteadmin & 512) != 0) && ((features & 0x08000000) != 0)));
try { (QS('LeftMenuMyUsers').display == 'none') ? QC('mobileUsersMenuItem').add('d-none') : QC('mobileUsersMenuItem').remove('d-none'); } catch (e) { }
QV('UsersGeneral', ((users != null) && ((features & 4) == 0)));
QV('UsersGroups', ((users != null) && ((features & 4) == 0)));
QV('UsersRecordings', ((userinfo.siteadmin & 512) != 0) && ((features & 0x08000000) != 0));
@@ -18822,6 +18838,174 @@
meshserver.send({ action: 'removemeshuser', meshid: meshid, userid: currentUser._id });
}
//
// UserDropDown Menu
//
var userDropdownOpen = false;
var uiSubmenuOpen = false;
function initializeUserDropdown() {
var dropdownButton = Q('userDropdownButton');
var dropdownMenu = Q('userDropdownMenu');
var uiSettingsButton = document.querySelector('.userDropdownUISettings');
var uiSubmenu = Q('uiSubmenu');
if (!dropdownButton || !dropdownMenu) return;
QS('uiSubmenu').display = 'none';
QS('uiSubmenu').opacity = '0';
QS('uiSubmenu').transform = 'translateX(12px)';
dropdownMenu.classList.remove('show');
dropdownButton.onclick = function(e) {
e.preventDefault();
e.stopPropagation();
userDropdownOpen = !userDropdownOpen;
dropdownMenu.classList.toggle('show');
if (!userDropdownOpen) {
QS('uiSubmenu').display = 'none';
QS('uiSubmenu').opacity = '0';
QS('uiSubmenu').transform = 'translateX(12px)';
uiSubmenuOpen = false;
resetChevronArrow();
}
};
document.addEventListener('click', function(e) {
var uiSubmenu = Q('uiSubmenu');
if (!dropdownMenu.contains(e.target) && !dropdownButton.contains(e.target) && !uiSubmenu.contains(e.target)) {
dropdownMenu.classList.remove('show');
QS('uiSubmenu').display = 'none';
QS('uiSubmenu').opacity = '0';
QS('uiSubmenu').transform = 'translateX(12px)';
userDropdownOpen = false;
uiSubmenuOpen = false;
resetChevronArrow();
}
});
}
function toggleUISubmenu() {
uiSubmenuOpen = !uiSubmenuOpen;
var uiSubmenu = Q('uiSubmenu');
var isDesktop = window.innerWidth > 769;
var reduceMotion = (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches);
if (uiSubmenuOpen) {
QS('uiSubmenu').display = 'block';
if (isDesktop && (reduceMotion == false)) {
setTimeout(function() {
QS('uiSubmenu').opacity = '1';
QS('uiSubmenu').transform = 'translateX(0)';
}, 10);
} else {
QS('uiSubmenu').opacity = '1';
QS('uiSubmenu').transform = 'translateX(0)';
}
} else {
if (isDesktop && (reduceMotion == false)) {
QS('uiSubmenu').opacity = '0';
QS('uiSubmenu').transform = 'translateX(12px)';
setTimeout(function() {
QS('uiSubmenu').display = 'none';
}, 300);
} else {
QS('uiSubmenu').display = 'none';
QS('uiSubmenu').opacity = '0';
QS('uiSubmenu').transform = 'translateX(12px)';
}
}
var chevronIcon = document.querySelector('.userDropdownUISettings .fa-chevron-right');
if (chevronIcon) {
chevronIcon.style.transform = uiSubmenuOpen ? 'rotate(90deg)' : '';
}
}
function handleUserMenuItem(action) {
Q('userDropdownMenu').classList.remove('show');
QS('uiSubmenu').display = 'none';
QS('uiSubmenu').opacity = '0';
QS('uiSubmenu').transform = 'translateX(12px)';
userDropdownOpen = false;
uiSubmenuOpen = false;
resetChevronArrow();
switch(action) {
case 'devices': goForward('devices'); break;
case 'events': goForward('events'); break;
case 'users': goForward('users'); break;
case 'files': goForward('files'); break;
case 'server': goForward('server'); break;
case 'toggle-modern-ui': toggleBootstrapUIMode(); break;
case 'left-bar': userInterfaceSelectMenu(1); break;
case 'top-bar': userInterfaceSelectMenu(2); break;
case 'fixed-width': userInterfaceSelectMenu(3); break;
case 'toggle-footer': toggleFooterBarMode(); break;
case 'toggle-night': toggleNightMode(); break;
case 'notes': showNotes(false); break;
case 'account': go(2); break;
case 'logout':
if (logoutControls && logoutControls.logoutUrl) {
window.location.href = logoutControls.logoutUrl;
}
break;
}
}
function resetChevronArrow() {
var arrow = document.querySelector('.userDropdownUISettings .fa-chevron-right');
if (arrow) arrow.style.transform = '';
}
function updateUserDropdownVisibility() {
var userDropdown = Q('userDropdown');
var dropdownName = Q('userDropdownName');
var dropdownImage = Q('userDropdownImage');
if (logoutControls && logoutControls.name) {
QS('userDropdown').display = 'block';
dropdownName.textContent = logoutControls.name;
if (userinfo && userinfo.flags && (userinfo.flags & 1)) {
var rnd = userinfo.accountImageRnd || Math.floor(Math.random() * 9999999999);
dropdownImage.src = 'userimage.ashx?rnd=' + rnd;
}
} else {
QS('userDropdown').display = 'none';
}
}
function updateNightModeIcon() {
var nightModeItem = document.querySelector('.userDropdownMenuItem[data-action="toggle-night"]');
if (nightModeItem) {
var icon = nightModeItem.querySelector('#nightModeIcon');
var textSpan = nightModeItem.querySelector('#nightModeText');
if (icon && textSpan) {
var isNightMode = document.body.classList.contains('night');
var iconClass = isNightMode ? 'fa-sun' : 'fa-moon';
var text = isNightMode ? "Toggle Light Mode" : "Toggle Dark Mode";
var newIcon = document.createElement('i');
newIcon.id = 'nightModeIcon';
newIcon.className = 'fa ' + iconClass + ' userDropdownMenuIcon night-mode-icon';
icon.parentNode.replaceChild(newIcon, icon);
textSpan.textContent = text;
}
}
}
document.addEventListener('DOMContentLoaded', function() {
initializeUserDropdown();
updateUserDropdownVisibility();
updateNightModeIcon();
});
//
// MY USER EVENTS
//
@@ -19532,29 +19716,6 @@
}
setupNotificationClickOutside();
// Update night mode icon and text in dropdown menu
function updateNightModeIcon() {
var nightModeItem = document.querySelector('.userDropdownMenuItem[onclick*="toggleNightMode"]');
if (nightModeItem) {
var icon = nightModeItem.querySelector('.userDropdownMenuIcon');
var textSpan = nightModeItem.querySelector('span');
if (icon && textSpan) {
var isNightMode = document.body.classList.contains('night');
var iconClass = isNightMode ? 'fa-sun' : 'fa-moon';
var text = isNightMode ? "Toggle Light Mode" : "Toggle Dark Mode";
var newIcon = document.createElement('i');
newIcon.className = 'fa ' + iconClass + ' userDropdownMenuIcon';
icon.parentNode.replaceChild(newIcon, icon);
textSpan.textContent = text;
}
}
}
function resetChevronArrow() {
var chevronIcon = document.querySelector('.userDropdownMenuItem[onclick*="toggleUISubmenu"] .fa-chevron-right');
if (chevronIcon) {
chevronIcon.style.transform = 'rotate(0deg)';
}
}
// Refresh the notification box
function drawNotifications() {

View File

@@ -3393,6 +3393,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
if (domain.devicesearchbargroupname === true) { features2 += 0x10000000; } // Search bar will find by group name too
if (((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.duo2factor != false)) && (typeof domain.duo2factor == 'object') && (typeof domain.duo2factor.integrationkey == 'string') && (typeof domain.duo2factor.secretkey == 'string') && (typeof domain.duo2factor.apihostname == 'string')) { features2 += 0x20000000; } // using Duo for 2FA is allowed
if (domain.showmodernuitoggle == true) { features2 += 0x40000000; } // Indicates that the new UI should be shown
if (domain.sitestyle === 3) { features2 |= 0x80000000; } // Indicates that Modern UI is forced (siteStyle = 3)
return { features: features, features2: features2 };
}