Enhance OTS Signage Theme with Icon Dashboard and UI Improvements
- Updated theme.js to refine dropdown initialization, excluding user menu handling. - Modified authed.twig to allow CSS variable theming for background and text colors, and adjusted content wrapper classes based on navigation state. - Adjusted override-styles.twig to improve layout responsiveness and ensure proper styling for horizontal navigation mode. - Enhanced theme-scripts.twig to improve user menu functionality, including repositioning and click handling. - Introduced a new dashboard-icon-page.twig for a stylized icon dashboard, featuring card-based buttons for quick access to various functionalities, along with custom styles for better user experience.
This commit is contained in:
@@ -31,6 +31,28 @@
|
|||||||
--ots-radius-lg: 14px;
|
--ots-radius-lg: 14px;
|
||||||
|
|
||||||
--ots-transition: 160ms ease;
|
--ots-transition: 160ms ease;
|
||||||
|
|
||||||
|
/* Modal pop-ups */
|
||||||
|
--modal-backdrop-bg: rgba(2, 6, 23, 0.55);
|
||||||
|
--modal-backdrop-blur: 6px;
|
||||||
|
--modal-bg: #141c2b;
|
||||||
|
--modal-border: #2c3a54;
|
||||||
|
--modal-radius: 16px;
|
||||||
|
--modal-shadow: 0 24px 64px rgba(0, 0, 0, 0.45), 0 0 0 1px rgba(255, 255, 255, 0.04);
|
||||||
|
--modal-header-bg: #0f172a;
|
||||||
|
--modal-header-border: #2c3a54;
|
||||||
|
--modal-header-text: #f1f5f9;
|
||||||
|
--modal-body-bg: #141c2b;
|
||||||
|
--modal-body-text: #e2e8f0;
|
||||||
|
--modal-footer-bg: #0f172a;
|
||||||
|
--modal-footer-border: #2c3a54;
|
||||||
|
--modal-close-color: #64748b;
|
||||||
|
--modal-close-hover: #f1f5f9;
|
||||||
|
--modal-input-bg: #0b111a;
|
||||||
|
--modal-input-border: #2c3a54;
|
||||||
|
--modal-input-text: #e6eefb;
|
||||||
|
--modal-input-focus-border: #4f8cff;
|
||||||
|
--modal-input-focus-ring: rgba(79, 140, 255, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* =============================================================================
|
/* =============================================================================
|
||||||
@@ -107,6 +129,41 @@ hr {
|
|||||||
box-shadow: var(--ots-shadow-sm);
|
box-shadow: var(--ots-shadow-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Horizontal nav mode: remove sidebar margins from #content-wrapper */
|
||||||
|
.navbar.navbar-expand-lg ~ #content-wrapper {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
padding-left: 20px !important;
|
||||||
|
padding-right: 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar.navbar-expand-lg ~ #content-wrapper .page-content {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar.navbar-expand-lg ~ #content-wrapper .page-content .row {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
margin-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar.navbar-expand-lg ~ #content-wrapper .page-content [class*="col-"] {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar.navbar-expand-lg ~ #content-wrapper .page-content > .row > .col-sm-12 {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar.navbar-expand-lg ~ #content-wrapper,
|
||||||
|
.navbar.navbar-expand-lg ~ #content-wrapper .page-content {
|
||||||
|
background: var(--ots-bg) !important;
|
||||||
|
color: var(--ots-text) !important;
|
||||||
|
}
|
||||||
|
|
||||||
.navbar-brand,
|
.navbar-brand,
|
||||||
.navbar-brand .xibo-logo {
|
.navbar-brand .xibo-logo {
|
||||||
color: var(--ots-text);
|
color: var(--ots-text);
|
||||||
@@ -792,54 +849,138 @@ textarea:focus {
|
|||||||
MODALS
|
MODALS
|
||||||
============================================================================= */
|
============================================================================= */
|
||||||
|
|
||||||
|
/* --- Content wrapper --- */
|
||||||
.modal-content {
|
.modal-content {
|
||||||
border-radius: var(--ots-radius-lg);
|
border-radius: var(--ots-radius-lg);
|
||||||
background-color: var(--color-surface) !important;
|
background-color: var(--modal-bg, var(--ots-surface)) !important;
|
||||||
color: var(--color-text-primary) !important;
|
color: var(--modal-body-text, var(--ots-text)) !important;
|
||||||
border: 1px solid var(--color-border) !important;
|
border: 1px solid var(--modal-border, var(--ots-border)) !important;
|
||||||
|
box-shadow: var(--modal-shadow, var(--ots-shadow-lg)) !important;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- Transparent parents --- */
|
||||||
.modal,
|
.modal,
|
||||||
.modal-header,
|
.modal-header,
|
||||||
.modal-body,
|
.modal-body,
|
||||||
.modal-footer {
|
.modal-footer {
|
||||||
background-color: transparent !important;
|
color: var(--modal-body-text, var(--ots-text)) !important;
|
||||||
color: var(--color-text-primary) !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- Header --- */
|
||||||
|
.modal-header {
|
||||||
|
background-color: var(--modal-header-bg, var(--ots-surface)) !important;
|
||||||
|
border-bottom: 1px solid var(--modal-header-border, var(--ots-border)) !important;
|
||||||
|
padding: 16px 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-title,
|
||||||
|
.modal-header h4,
|
||||||
|
.modal-header h5 {
|
||||||
|
color: var(--modal-header-text, var(--ots-text)) !important;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Body --- */
|
||||||
|
.modal-body {
|
||||||
|
background-color: var(--modal-body-bg, var(--ots-surface)) !important;
|
||||||
|
color: var(--modal-body-text, var(--ots-text)) !important;
|
||||||
|
padding: 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Backdrop --- */
|
||||||
.modal-backdrop,
|
.modal-backdrop,
|
||||||
.modal-backdrop.show,
|
.modal-backdrop.show,
|
||||||
.modal-backdrop.in {
|
.modal-backdrop.in {
|
||||||
background-color: rgba(0, 0, 0, 0.3) !important;
|
background-color: var(--modal-backdrop-bg, rgba(0, 0, 0, 0.3)) !important;
|
||||||
backdrop-filter: blur(4px) !important;
|
backdrop-filter: blur(var(--modal-backdrop-blur, 4px)) !important;
|
||||||
|
-webkit-backdrop-filter: blur(var(--modal-backdrop-blur, 4px)) !important;
|
||||||
opacity: 1 !important;
|
opacity: 1 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- Footer --- */
|
||||||
.modal-footer {
|
.modal-footer {
|
||||||
border-top: 1px solid var(--color-border) !important;
|
background-color: var(--modal-footer-bg, transparent) !important;
|
||||||
|
border-top: 1px solid var(--modal-footer-border, var(--ots-border)) !important;
|
||||||
|
padding: 12px 20px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ensure modal footer buttons (cancel/secondary) are readable on dark surfaces */
|
/* Footer buttons — secondary / cancel */
|
||||||
.modal-footer .btn,
|
.modal-footer .btn,
|
||||||
.modal-footer button {
|
.modal-footer button {
|
||||||
background: var(--ots-surface-3);
|
background: var(--modal-body-bg, var(--ots-surface-3)) !important;
|
||||||
color: var(--ots-text) !important;
|
color: var(--modal-body-text, var(--ots-text)) !important;
|
||||||
border: 1px solid var(--ots-border) !important;
|
border: 1px solid var(--modal-border, var(--ots-border)) !important;
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
|
border-radius: var(--ots-radius-sm) !important;
|
||||||
|
transition: background var(--ots-transition),
|
||||||
|
color var(--ots-transition),
|
||||||
|
border-color var(--ots-transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-footer .btn:hover,
|
.modal-footer .btn:hover,
|
||||||
.modal-footer button:hover {
|
.modal-footer button:hover {
|
||||||
background: var(--ots-surface-2);
|
background: rgba(79, 140, 255, 0.08) !important;
|
||||||
color: var(--ots-primary) !important;
|
color: var(--ots-primary) !important;
|
||||||
border-color: var(--ots-primary) !important;
|
border-color: var(--ots-primary) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Footer buttons — primary / submit */
|
||||||
.modal-footer .btn.btn-primary,
|
.modal-footer .btn.btn-primary,
|
||||||
.modal-footer button.btn-primary {
|
.modal-footer button.btn-primary {
|
||||||
background: var(--ots-primary) !important;
|
background: var(--ots-primary) !important;
|
||||||
color: #0b1020 !important;
|
color: #0b1020 !important;
|
||||||
border-color: var(--ots-primary-2) !important;
|
border-color: var(--ots-primary-2) !important;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer .btn.btn-primary:hover,
|
||||||
|
.modal-footer button.btn-primary:hover {
|
||||||
|
background: var(--ots-primary-2) !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer buttons — danger */
|
||||||
|
.modal-footer .btn.btn-danger {
|
||||||
|
background: var(--ots-danger) !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
border-color: var(--ots-danger) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Form controls inside modals --- */
|
||||||
|
.modal-body .form-control,
|
||||||
|
.modal-body input[type="text"],
|
||||||
|
.modal-body input[type="number"],
|
||||||
|
.modal-body input[type="email"],
|
||||||
|
.modal-body input[type="password"],
|
||||||
|
.modal-body textarea,
|
||||||
|
.modal-body select {
|
||||||
|
background-color: var(--modal-input-bg, var(--ots-bg)) !important;
|
||||||
|
border: 1px solid var(--modal-input-border, var(--ots-border)) !important;
|
||||||
|
border-radius: var(--ots-radius-sm) !important;
|
||||||
|
color: var(--modal-input-text, var(--ots-text)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body .form-control:focus,
|
||||||
|
.modal-body input:focus,
|
||||||
|
.modal-body textarea:focus,
|
||||||
|
.modal-body select:focus {
|
||||||
|
border-color: var(--modal-input-focus-border, var(--ots-primary)) !important;
|
||||||
|
box-shadow: 0 0 0 3px var(--modal-input-focus-ring, rgba(79, 140, 255, 0.2)) !important;
|
||||||
|
outline: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Close button --- */
|
||||||
|
.modal-header .close,
|
||||||
|
.modal-header [data-dismiss="modal"] {
|
||||||
|
color: var(--modal-close-color, var(--ots-text-muted)) !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
text-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header .close:hover,
|
||||||
|
.modal-header [data-dismiss="modal"]:hover {
|
||||||
|
color: var(--modal-close-hover, var(--ots-text)) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* =============================================================================
|
/* =============================================================================
|
||||||
|
|||||||
@@ -59,6 +59,29 @@
|
|||||||
--editor-modal-content-bg: #1e293b;
|
--editor-modal-content-bg: #1e293b;
|
||||||
--editor-modal-header-bg: #0f172a;
|
--editor-modal-header-bg: #0f172a;
|
||||||
--editor-modal-header-text: #f1f5f9;
|
--editor-modal-header-text: #f1f5f9;
|
||||||
|
|
||||||
|
/* Modal pop-ups (Bootstrap / Bootbox) */
|
||||||
|
--modal-backdrop-bg: rgba(2, 6, 23, 0.55);
|
||||||
|
--modal-backdrop-blur: 6px;
|
||||||
|
--modal-bg: #141c2b;
|
||||||
|
--modal-border: #2c3a54;
|
||||||
|
--modal-radius: 16px;
|
||||||
|
--modal-shadow: 0 24px 64px rgba(0, 0, 0, 0.45), 0 0 0 1px rgba(255, 255, 255, 0.04);
|
||||||
|
--modal-header-bg: #0f172a;
|
||||||
|
--modal-header-border: #2c3a54;
|
||||||
|
--modal-header-text: #f1f5f9;
|
||||||
|
--modal-body-bg: #141c2b;
|
||||||
|
--modal-body-text: #e2e8f0;
|
||||||
|
--modal-footer-bg: #0f172a;
|
||||||
|
--modal-footer-border: #2c3a54;
|
||||||
|
--modal-close-color: #64748b;
|
||||||
|
--modal-close-hover: #f1f5f9;
|
||||||
|
--modal-input-bg: #0b111a;
|
||||||
|
--modal-input-border: #2c3a54;
|
||||||
|
--modal-input-text: #e6eefb;
|
||||||
|
--modal-input-focus-border: #4f8cff;
|
||||||
|
--modal-input-focus-ring: rgba(79, 140, 255, 0.2);
|
||||||
|
|
||||||
--editor-body-bg: #1e293b;
|
--editor-body-bg: #1e293b;
|
||||||
--editor-border: #334155;
|
--editor-border: #334155;
|
||||||
--editor-text: #e2e8f0;
|
--editor-text: #e2e8f0;
|
||||||
@@ -140,7 +163,6 @@ body {
|
|||||||
Avoid forcing `.dropdown-menu` to be `display:block`/`visibility:visible` so
|
Avoid forcing `.dropdown-menu` to be `display:block`/`visibility:visible` so
|
||||||
native dropdown behaviour (open/close) is preserved. */
|
native dropdown behaviour (open/close) is preserved. */
|
||||||
.ots-notif-menu,
|
.ots-notif-menu,
|
||||||
.ots-user-menu,
|
|
||||||
.context-menu,
|
.context-menu,
|
||||||
.row-menu,
|
.row-menu,
|
||||||
.rowMenu,
|
.rowMenu,
|
||||||
@@ -154,6 +176,16 @@ body {
|
|||||||
pointer-events: auto !important;
|
pointer-events: auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* User menu positioning is handled by floatMenu() which adds .ots-floating-menu.
|
||||||
|
Only apply fixed positioning when the menu is actively floating. */
|
||||||
|
.ots-user-menu.ots-floating-menu {
|
||||||
|
position: fixed !important;
|
||||||
|
z-index: 2147483647 !important;
|
||||||
|
transform: none !important;
|
||||||
|
will-change: auto !important;
|
||||||
|
pointer-events: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* Hide the rowMenu column header completely */
|
/* Hide the rowMenu column header completely */
|
||||||
th.rowMenu,
|
th.rowMenu,
|
||||||
td.rowMenu {
|
td.rowMenu {
|
||||||
@@ -191,7 +223,8 @@ td.rowMenu {
|
|||||||
background-color: rgba(59, 130, 246, 0.1) !important;
|
background-color: rgba(59, 130, 246, 0.1) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Light mode styles (applied when .ots-light-mode is on body) */
|
/* Light mode styles (applied when .ots-light-mode is on html or body) */
|
||||||
|
html.ots-light-mode,
|
||||||
body.ots-light-mode {
|
body.ots-light-mode {
|
||||||
--color-primary: #2563eb;
|
--color-primary: #2563eb;
|
||||||
--color-primary-dark: #1d4ed8;
|
--color-primary-dark: #1d4ed8;
|
||||||
@@ -246,6 +279,29 @@ body.ots-light-mode {
|
|||||||
--editor-modal-content-bg: #ffffff;
|
--editor-modal-content-bg: #ffffff;
|
||||||
--editor-modal-header-bg: #f1f5f9;
|
--editor-modal-header-bg: #f1f5f9;
|
||||||
--editor-modal-header-text: #0f172a;
|
--editor-modal-header-text: #0f172a;
|
||||||
|
|
||||||
|
/* Modal pop-ups (Bootstrap / Bootbox) — light mode */
|
||||||
|
--modal-backdrop-bg: rgba(15, 23, 42, 0.25);
|
||||||
|
--modal-backdrop-blur: 6px;
|
||||||
|
--modal-bg: #ffffff;
|
||||||
|
--modal-border: #e2e8f0;
|
||||||
|
--modal-radius: 16px;
|
||||||
|
--modal-shadow: 0 24px 64px rgba(15, 23, 42, 0.14), 0 0 0 1px rgba(0, 0, 0, 0.04);
|
||||||
|
--modal-header-bg: #f8fafc;
|
||||||
|
--modal-header-border: #e2e8f0;
|
||||||
|
--modal-header-text: #0f172a;
|
||||||
|
--modal-body-bg: #ffffff;
|
||||||
|
--modal-body-text: #334155;
|
||||||
|
--modal-footer-bg: #f8fafc;
|
||||||
|
--modal-footer-border: #e2e8f0;
|
||||||
|
--modal-close-color: #94a3b8;
|
||||||
|
--modal-close-hover: #0f172a;
|
||||||
|
--modal-input-bg: #f8fafc;
|
||||||
|
--modal-input-border: #e2e8f0;
|
||||||
|
--modal-input-text: #0f172a;
|
||||||
|
--modal-input-focus-border: #2563eb;
|
||||||
|
--modal-input-focus-ring: rgba(37, 99, 235, 0.18);
|
||||||
|
|
||||||
--editor-body-bg: #ffffff;
|
--editor-body-bg: #ffffff;
|
||||||
--editor-border: #e2e8f0;
|
--editor-border: #e2e8f0;
|
||||||
--editor-text: #0f172a;
|
--editor-text: #0f172a;
|
||||||
@@ -517,7 +573,8 @@ html, body, #page-wrapper, .ots-main, .ots-content, .page-content {
|
|||||||
margin-left: 0 !important;
|
margin-left: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#content-wrapper {
|
/* Sidebar mode only — #page-wrapper is absent in horizontal nav mode */
|
||||||
|
#page-wrapper #content-wrapper {
|
||||||
margin-left: var(--ots-sidebar-actual-width, var(--ots-sidebar-width)) !important;
|
margin-left: var(--ots-sidebar-actual-width, var(--ots-sidebar-width)) !important;
|
||||||
width: calc(100vw - var(--ots-sidebar-actual-width, var(--ots-sidebar-width))) !important;
|
width: calc(100vw - var(--ots-sidebar-actual-width, var(--ots-sidebar-width))) !important;
|
||||||
max-width: calc(100vw - var(--ots-sidebar-actual-width, var(--ots-sidebar-width))) !important;
|
max-width: calc(100vw - var(--ots-sidebar-actual-width, var(--ots-sidebar-width))) !important;
|
||||||
@@ -531,6 +588,15 @@ html, body, #page-wrapper, .ots-main, .ots-content, .page-content {
|
|||||||
box-sizing: border-box !important;
|
box-sizing: border-box !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Base styles for #content-wrapper in all modes */
|
||||||
|
#content-wrapper {
|
||||||
|
border-left: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
outline: none !important;
|
||||||
|
overflow-x: hidden !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
PLAYLIST / LAYOUT EDITOR – themed for dark/light mode
|
PLAYLIST / LAYOUT EDITOR – themed for dark/light mode
|
||||||
The .editor-modal is AJAX-injected into #editor-container (inside
|
The .editor-modal is AJAX-injected into #editor-container (inside
|
||||||
@@ -1720,7 +1786,7 @@ html.ots-sidebar-collapsed #content-wrapper {
|
|||||||
============================================================================ */
|
============================================================================ */
|
||||||
.ots-page-actions {
|
.ots-page-actions {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 12px;
|
top: 14px;
|
||||||
right: 20px;
|
right: 20px;
|
||||||
z-index: 1100;
|
z-index: 1100;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1779,8 +1845,8 @@ html.ots-sidebar-collapsed #content-wrapper {
|
|||||||
|
|
||||||
.ots-page-actions .ots-topbar-action img.nav-avatar {
|
.ots-page-actions .ots-topbar-action img.nav-avatar {
|
||||||
display: block !important;
|
display: block !important;
|
||||||
width: 24px !important;
|
width: 20px !important;
|
||||||
height: 24px !important;
|
height: 20px !important;
|
||||||
border-radius: 50% !important;
|
border-radius: 50% !important;
|
||||||
object-fit: cover !important;
|
object-fit: cover !important;
|
||||||
border: 1px solid transparent !important;
|
border: 1px solid transparent !important;
|
||||||
@@ -1848,6 +1914,164 @@ body.ots-light-mode .ots-page-actions {
|
|||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
USER MENU — standalone styles that apply whether the menu is inside
|
||||||
|
.ots-page-actions or floated to <body> by floatMenu().
|
||||||
|
============================================================================ */
|
||||||
|
|
||||||
|
/* When the menu lives in <body> (permanently moved there by initDropdowns),
|
||||||
|
it's hidden by default and shown only via .ots-user-menu-open. */
|
||||||
|
.ots-user-menu.ots-user-menu-body {
|
||||||
|
position: fixed !important;
|
||||||
|
display: none !important;
|
||||||
|
z-index: 2147483647 !important;
|
||||||
|
transform: none !important;
|
||||||
|
pointer-events: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ots-user-menu.ots-user-menu-body.ots-user-menu-open {
|
||||||
|
display: block !important;
|
||||||
|
visibility: visible !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
animation: otsUserMenuIn 120ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes otsUserMenuIn {
|
||||||
|
from { opacity: 0; transform: translateY(-6px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Visual style — applies regardless of where the element is in the DOM */
|
||||||
|
.ots-user-menu {
|
||||||
|
min-width: 200px !important;
|
||||||
|
background-color: var(--color-surface-elevated) !important;
|
||||||
|
border: 1px solid var(--color-border) !important;
|
||||||
|
border-radius: 10px !important;
|
||||||
|
box-shadow: 0 12px 36px rgba(0, 0, 0, 0.25) !important;
|
||||||
|
padding: 6px 0 !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ots-user-menu .dropdown-header {
|
||||||
|
color: var(--color-text-primary) !important;
|
||||||
|
font-size: 13px !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
padding: 8px 14px !important;
|
||||||
|
white-space: nowrap !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ots-user-menu .dropdown-divider {
|
||||||
|
border-top: 1px solid var(--color-border) !important;
|
||||||
|
margin: 4px 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ots-user-menu .dropdown-item,
|
||||||
|
.ots-user-menu a {
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
gap: 8px !important;
|
||||||
|
padding: 8px 14px !important;
|
||||||
|
margin: 1px 6px !important;
|
||||||
|
border-radius: 6px !important;
|
||||||
|
color: var(--color-text-secondary) !important;
|
||||||
|
font-size: 13px !important;
|
||||||
|
font-weight: 500 !important;
|
||||||
|
transition: all 150ms ease !important;
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ots-user-menu .dropdown-item:hover,
|
||||||
|
.ots-user-menu a:hover {
|
||||||
|
background-color: rgba(59, 130, 246, 0.08) !important;
|
||||||
|
color: var(--color-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.ots-light-mode .ots-user-menu {
|
||||||
|
background-color: #ffffff !important;
|
||||||
|
border-color: #e2e8f0 !important;
|
||||||
|
box-shadow: 0 12px 36px rgba(0, 0, 0, 0.12) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.ots-light-mode .ots-user-menu .dropdown-item,
|
||||||
|
body.ots-light-mode .ots-user-menu a {
|
||||||
|
color: #334155 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.ots-light-mode .ots-user-menu .dropdown-item:hover,
|
||||||
|
body.ots-light-mode .ots-user-menu a:hover {
|
||||||
|
background-color: rgba(59, 130, 246, 0.1) !important;
|
||||||
|
color: #2563eb !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
HORIZONTAL NAV MODE — when .navbar.navbar-expand-lg is present there is no
|
||||||
|
sidebar, so #content-wrapper must not have the sidebar margin/width offsets.
|
||||||
|
The page background follows the current light/dark theme variables.
|
||||||
|
============================================================================ */
|
||||||
|
.navbar.navbar-expand-lg ~ #content-wrapper {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
padding-left: 20px !important;
|
||||||
|
padding-right: 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar.navbar-expand-lg ~ #content-wrapper .page-content {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar.navbar-expand-lg ~ #content-wrapper .page-content .row {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
margin-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar.navbar-expand-lg ~ #content-wrapper .page-content [class*="col-"] {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar.navbar-expand-lg ~ #content-wrapper .page-content > .row > .col-sm-12 {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar.navbar-expand-lg ~ #content-wrapper .page-header {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
padding-left: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure page background follows the theme when horizontal nav is active */
|
||||||
|
.navbar.navbar-expand-lg ~ #content-wrapper,
|
||||||
|
.navbar.navbar-expand-lg ~ #content-wrapper .page-content {
|
||||||
|
background-color: var(--color-background) !important;
|
||||||
|
color: var(--color-text-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light-mode overrides for horizontal nav page area */
|
||||||
|
body.ots-light-mode .navbar.navbar-expand-lg ~ #content-wrapper,
|
||||||
|
body.ots-light-mode .navbar.navbar-expand-lg ~ #content-wrapper .page-content {
|
||||||
|
background-color: var(--color-background) !important;
|
||||||
|
color: var(--color-text-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Horizontal navbar should also follow the theme */
|
||||||
|
body.ots-light-mode .navbar.navbar-expand-lg {
|
||||||
|
background-color: var(--color-surface) !important;
|
||||||
|
border-color: var(--color-border) !important;
|
||||||
|
color: var(--color-text-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.ots-light-mode .navbar.navbar-expand-lg .nav-link,
|
||||||
|
body.ots-light-mode .navbar.navbar-expand-lg .navbar-nav > li > a {
|
||||||
|
color: var(--color-text-secondary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.ots-light-mode .navbar.navbar-expand-lg .nav-link:hover,
|
||||||
|
body.ots-light-mode .navbar.navbar-expand-lg .navbar-nav > li > a:hover {
|
||||||
|
color: var(--color-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
@@ -3018,7 +3242,7 @@ body.ots-sidebar-collapsed .ots-sidebar-nav {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
height: 56px;
|
height: 67px;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
position: relative;
|
position: relative;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@@ -3505,6 +3729,22 @@ body.ots-sidebar-open .ots-topbar {
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Make DataTables column visibility dropdown independent of table scrolling
|
||||||
|
Position it fixed so it isn't clipped by table containers and allow
|
||||||
|
internal scrolling when there are many columns. */
|
||||||
|
.dt-button-collection {
|
||||||
|
position: fixed !important;
|
||||||
|
z-index: 1400 !important;
|
||||||
|
max-height: 60vh !important;
|
||||||
|
overflow-y: auto !important;
|
||||||
|
overflow-x: hidden !important;
|
||||||
|
box-shadow: 0 8px 24px rgba(2,6,23,0.08) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dt-button-collection .dropdown-menu {
|
||||||
|
max-height: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* OTS topbar badge removed */
|
/* OTS topbar badge removed */
|
||||||
|
|
||||||
.dropdown-menu li a {
|
.dropdown-menu li a {
|
||||||
@@ -6427,7 +6667,7 @@ a.text-muted:hover {
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
height: 56px;
|
height: 67px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.xibo-logo-text {
|
.xibo-logo-text {
|
||||||
@@ -6607,11 +6847,12 @@ body,
|
|||||||
color: var(--color-text-primary) !important;
|
color: var(--color-text-primary) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Content area tracks the sidebar width.
|
/* Content area tracks the sidebar width — SIDEBAR MODE ONLY.
|
||||||
The expanded value comes from --ots-sidebar-actual-width (measured).
|
The expanded value comes from --ots-sidebar-actual-width (measured).
|
||||||
The collapsed value comes from body.ots-sidebar-collapsed overrides. */
|
The collapsed value comes from body.ots-sidebar-collapsed overrides.
|
||||||
#content-wrapper,
|
Scoped to #page-wrapper which only exists in sidebar mode. */
|
||||||
.ots-main {
|
#page-wrapper #content-wrapper,
|
||||||
|
#page-wrapper .ots-main {
|
||||||
margin-left: var(--ots-sidebar-actual-width, var(--ots-sidebar-width)) !important;
|
margin-left: var(--ots-sidebar-actual-width, var(--ots-sidebar-width)) !important;
|
||||||
width: calc(100vw - var(--ots-sidebar-actual-width, var(--ots-sidebar-width))) !important;
|
width: calc(100vw - var(--ots-sidebar-actual-width, var(--ots-sidebar-width))) !important;
|
||||||
max-width: calc(100vw - var(--ots-sidebar-actual-width, var(--ots-sidebar-width))) !important;
|
max-width: calc(100vw - var(--ots-sidebar-actual-width, var(--ots-sidebar-width))) !important;
|
||||||
@@ -6620,6 +6861,10 @@ body,
|
|||||||
box-sizing: border-box !important;
|
box-sizing: border-box !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.ots-sidebar-collapsed #page-wrapper #content-wrapper,
|
||||||
|
body.ots-sidebar-collapsed #page-wrapper .ots-main,
|
||||||
|
html.ots-sidebar-collapsed #page-wrapper #content-wrapper,
|
||||||
|
html.ots-sidebar-collapsed #page-wrapper .ots-main,
|
||||||
body.ots-sidebar-collapsed #content-wrapper,
|
body.ots-sidebar-collapsed #content-wrapper,
|
||||||
body.ots-sidebar-collapsed .ots-main,
|
body.ots-sidebar-collapsed .ots-main,
|
||||||
html.ots-sidebar-collapsed #content-wrapper,
|
html.ots-sidebar-collapsed #content-wrapper,
|
||||||
@@ -6840,3 +7085,554 @@ body.ots-light-mode .dataTables_wrapper .dataTables_length select {
|
|||||||
float: none !important;
|
float: none !important;
|
||||||
box-sizing: border-box !important;
|
box-sizing: border-box !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* =============================================================================
|
||||||
|
HORIZONTAL NAV — FINAL OVERRIDE (must be last in file to win cascade)
|
||||||
|
Applied when the top navbar is active and there is no sidebar.
|
||||||
|
============================================================================= */
|
||||||
|
.navbar.navbar-expand-lg ~ #content-wrapper,
|
||||||
|
nav.navbar + #content-wrapper {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
padding-left: 20px !important;
|
||||||
|
padding-right: 20px !important;
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar.navbar-expand-lg ~ #content-wrapper .page-content,
|
||||||
|
nav.navbar + #content-wrapper .page-content {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar.navbar-expand-lg ~ #content-wrapper .page-content .row,
|
||||||
|
nav.navbar + #content-wrapper .page-content .row {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
margin-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar.navbar-expand-lg ~ #content-wrapper .page-content [class*="col-"],
|
||||||
|
.navbar.navbar-expand-lg ~ #content-wrapper .page-content > .row > .col-sm-12,
|
||||||
|
nav.navbar + #content-wrapper .page-content [class*="col-"],
|
||||||
|
nav.navbar + #content-wrapper .page-content > .row > .col-sm-12 {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =============================================================================
|
||||||
|
HORIZONTAL NAV — CLASS-BASED OVERRIDE
|
||||||
|
.ots-horizontal-nav is added directly to #content-wrapper in authed.twig
|
||||||
|
when horizontal nav mode is active. No sibling combinators needed.
|
||||||
|
============================================================================= */
|
||||||
|
#content-wrapper.ots-horizontal-nav {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
padding-left: 30px !important;
|
||||||
|
padding-right: 30px !important;
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content-wrapper.ots-horizontal-nav .page-content {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content-wrapper.ots-horizontal-nav .page-content .row {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
margin-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content-wrapper.ots-horizontal-nav .page-content [class*="col-"],
|
||||||
|
#content-wrapper.ots-horizontal-nav .page-content > .row > .col-sm-12 {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* =============================================================================
|
||||||
|
MODAL POP-UPS — Comprehensive Styles
|
||||||
|
Covers Bootstrap modals, Bootbox dialogs, confirmation prompts, and
|
||||||
|
AJAX-loaded form modals. All colours use --modal-* CSS variables so they
|
||||||
|
automatically follow dark / light mode.
|
||||||
|
============================================================================= */
|
||||||
|
|
||||||
|
/* ---------- Backdrop -------------------------------------------------------- */
|
||||||
|
.modal-backdrop,
|
||||||
|
.modal-backdrop.show,
|
||||||
|
.modal-backdrop.in,
|
||||||
|
.modal-backdrop.fade.in {
|
||||||
|
background-color: var(--modal-backdrop-bg) !important;
|
||||||
|
backdrop-filter: blur(var(--modal-backdrop-blur)) !important;
|
||||||
|
-webkit-backdrop-filter: blur(var(--modal-backdrop-blur)) !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Dialog container ----------------------------------------------- */
|
||||||
|
.modal-dialog {
|
||||||
|
margin: 2rem auto;
|
||||||
|
padding: 0 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sizes */
|
||||||
|
.modal-dialog.modal-sm { max-width: 420px; }
|
||||||
|
.modal-dialog.modal-md { max-width: 600px; }
|
||||||
|
.modal-dialog:not(.modal-sm):not(.modal-lg):not(.modal-xl) { max-width: 600px; }
|
||||||
|
.modal-dialog.modal-lg { max-width: 820px; }
|
||||||
|
.modal-dialog.modal-xl { max-width: 1080px; }
|
||||||
|
|
||||||
|
/* ---------- Content wrapper ------------------------------------------------ */
|
||||||
|
.modal-content {
|
||||||
|
background-color: var(--modal-bg) !important;
|
||||||
|
border: 1px solid var(--modal-border) !important;
|
||||||
|
border-radius: var(--modal-radius) !important;
|
||||||
|
box-shadow: var(--modal-shadow) !important;
|
||||||
|
overflow: hidden;
|
||||||
|
color: var(--modal-body-text) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Subtle entrance animation */
|
||||||
|
.modal.fade .modal-dialog {
|
||||||
|
transform: translateY(16px) scale(0.98);
|
||||||
|
transition: transform 200ms cubic-bezier(0.16, 1, 0.3, 1),
|
||||||
|
opacity 200ms ease;
|
||||||
|
}
|
||||||
|
.modal.fade.show .modal-dialog,
|
||||||
|
.modal.fade.in .modal-dialog {
|
||||||
|
transform: translateY(0) scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Header --------------------------------------------------------- */
|
||||||
|
.modal-header {
|
||||||
|
background-color: var(--modal-header-bg) !important;
|
||||||
|
border-bottom: 1px solid var(--modal-header-border) !important;
|
||||||
|
padding: 16px 20px !important;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
min-height: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-title,
|
||||||
|
.modal-header h4,
|
||||||
|
.modal-header h5,
|
||||||
|
.modal-header .modal-title {
|
||||||
|
font-family: "Inter", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--modal-header-text) !important;
|
||||||
|
line-height: 1.4;
|
||||||
|
margin: 0;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close button */
|
||||||
|
.modal-header .close,
|
||||||
|
.modal-header [data-dismiss="modal"],
|
||||||
|
.modal-header .btn-close {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 !important;
|
||||||
|
margin: 0 -4px 0 auto !important;
|
||||||
|
border: none !important;
|
||||||
|
border-radius: var(--ots-radius-sm) !important;
|
||||||
|
background: transparent !important;
|
||||||
|
color: var(--modal-close-color) !important;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
line-height: 1;
|
||||||
|
opacity: 1 !important;
|
||||||
|
transition: background var(--ots-transition),
|
||||||
|
color var(--ots-transition);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header .close:hover,
|
||||||
|
.modal-header [data-dismiss="modal"]:hover,
|
||||||
|
.modal-header .btn-close:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.06) !important;
|
||||||
|
color: var(--modal-close-hover) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.ots-light-mode .modal-header .close:hover,
|
||||||
|
body.ots-light-mode .modal-header [data-dismiss="modal"]:hover,
|
||||||
|
body.ots-light-mode .modal-header .btn-close:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.06) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close button "×" text reset */
|
||||||
|
.modal-header .close span,
|
||||||
|
.modal-header .close::before {
|
||||||
|
color: inherit !important;
|
||||||
|
text-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Body ----------------------------------------------------------- */
|
||||||
|
.modal-body {
|
||||||
|
background-color: var(--modal-body-bg) !important;
|
||||||
|
color: var(--modal-body-text) !important;
|
||||||
|
padding: 20px !important;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: calc(100vh - 220px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body p {
|
||||||
|
color: var(--modal-body-text);
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body p:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Help / description text inside modals */
|
||||||
|
.modal-body .form-text,
|
||||||
|
.modal-body .help-block,
|
||||||
|
.modal-body small.text-muted {
|
||||||
|
color: var(--ots-text-muted) !important;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Footer --------------------------------------------------------- */
|
||||||
|
.modal-footer {
|
||||||
|
background-color: var(--modal-footer-bg) !important;
|
||||||
|
border-top: 1px solid var(--modal-footer-border) !important;
|
||||||
|
padding: 12px 20px !important;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer buttons — secondary / cancel */
|
||||||
|
.modal-footer .btn,
|
||||||
|
.modal-footer button {
|
||||||
|
font-family: "Inter", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 8px 18px;
|
||||||
|
border-radius: var(--ots-radius-sm) !important;
|
||||||
|
border: 1px solid var(--modal-border) !important;
|
||||||
|
background: var(--modal-body-bg) !important;
|
||||||
|
color: var(--modal-body-text) !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
transition: background var(--ots-transition),
|
||||||
|
color var(--ots-transition),
|
||||||
|
border-color var(--ots-transition);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer .btn:hover,
|
||||||
|
.modal-footer button:hover {
|
||||||
|
background: rgba(79, 140, 255, 0.08) !important;
|
||||||
|
border-color: var(--ots-primary) !important;
|
||||||
|
color: var(--ots-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer buttons — primary / submit */
|
||||||
|
.modal-footer .btn-primary,
|
||||||
|
.modal-footer .btn.btn-primary,
|
||||||
|
.modal-footer button.btn-primary {
|
||||||
|
background: var(--ots-primary) !important;
|
||||||
|
color: #0b1020 !important;
|
||||||
|
border-color: var(--ots-primary) !important;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer .btn-primary:hover,
|
||||||
|
.modal-footer .btn.btn-primary:hover {
|
||||||
|
background: var(--color-primary-dark, #1d4ed8) !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
border-color: var(--color-primary-dark, #1d4ed8) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer buttons — success */
|
||||||
|
.modal-footer .btn-success {
|
||||||
|
background: var(--ots-success, #10b981) !important;
|
||||||
|
color: #0b1020 !important;
|
||||||
|
border-color: var(--ots-success, #10b981) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer .btn-success:hover {
|
||||||
|
filter: brightness(0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer buttons — danger / delete */
|
||||||
|
.modal-footer .btn-danger {
|
||||||
|
background: var(--ots-danger, #ef4444) !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
border-color: var(--ots-danger, #ef4444) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer .btn-danger:hover {
|
||||||
|
filter: brightness(0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer buttons — warning */
|
||||||
|
.modal-footer .btn-warning {
|
||||||
|
background: var(--ots-warning, #f59e0b) !important;
|
||||||
|
color: #0b1020 !important;
|
||||||
|
border-color: var(--ots-warning, #f59e0b) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer .btn-warning:hover {
|
||||||
|
filter: brightness(0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Form controls inside modals ------------------------------------ */
|
||||||
|
.modal-body .form-control,
|
||||||
|
.modal-body input[type="text"],
|
||||||
|
.modal-body input[type="number"],
|
||||||
|
.modal-body input[type="email"],
|
||||||
|
.modal-body input[type="password"],
|
||||||
|
.modal-body input[type="search"],
|
||||||
|
.modal-body input[type="url"],
|
||||||
|
.modal-body textarea,
|
||||||
|
.modal-body select {
|
||||||
|
background-color: var(--modal-input-bg) !important;
|
||||||
|
border: 1px solid var(--modal-input-border) !important;
|
||||||
|
border-radius: var(--ots-radius-sm) !important;
|
||||||
|
color: var(--modal-input-text) !important;
|
||||||
|
padding: 8px 12px;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
transition: border-color var(--ots-transition),
|
||||||
|
box-shadow var(--ots-transition),
|
||||||
|
background-color var(--ots-transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body .form-control:focus,
|
||||||
|
.modal-body input:focus,
|
||||||
|
.modal-body textarea:focus,
|
||||||
|
.modal-body select:focus {
|
||||||
|
border-color: var(--modal-input-focus-border) !important;
|
||||||
|
box-shadow: 0 0 0 3px var(--modal-input-focus-ring) !important;
|
||||||
|
outline: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body .form-control::placeholder,
|
||||||
|
.modal-body input::placeholder,
|
||||||
|
.modal-body textarea::placeholder {
|
||||||
|
color: var(--ots-text-faint, #64748b) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Labels inside modal forms */
|
||||||
|
.modal-body label,
|
||||||
|
.modal-body .control-label {
|
||||||
|
color: var(--modal-header-text) !important;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Form groups spacing */
|
||||||
|
.modal-body .form-group {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select2 inside modals */
|
||||||
|
.modal-body .select2-container--default .select2-selection--single,
|
||||||
|
.modal-body .select2-container--default .select2-selection--multiple {
|
||||||
|
background-color: var(--modal-input-bg) !important;
|
||||||
|
border: 1px solid var(--modal-input-border) !important;
|
||||||
|
border-radius: var(--ots-radius-sm) !important;
|
||||||
|
color: var(--modal-input-text) !important;
|
||||||
|
min-height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body .select2-container--default .select2-selection--single .select2-selection__rendered {
|
||||||
|
color: var(--modal-input-text) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body .select2-container--default .select2-selection--single:focus,
|
||||||
|
.modal-body .select2-container--default.select2-container--focus .select2-selection--multiple {
|
||||||
|
border-color: var(--modal-input-focus-border) !important;
|
||||||
|
box-shadow: 0 0 0 3px var(--modal-input-focus-ring) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Tabs inside modals --------------------------------------------- */
|
||||||
|
.modal-body .nav-tabs {
|
||||||
|
border-bottom: 1px solid var(--modal-border) !important;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body .nav-tabs .nav-link,
|
||||||
|
.modal-body .nav-tabs > li > a {
|
||||||
|
color: var(--ots-text-muted) !important;
|
||||||
|
border: none !important;
|
||||||
|
border-bottom: 2px solid transparent !important;
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: color var(--ots-transition),
|
||||||
|
border-color var(--ots-transition);
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body .nav-tabs .nav-link:hover,
|
||||||
|
.modal-body .nav-tabs > li > a:hover {
|
||||||
|
color: var(--modal-header-text) !important;
|
||||||
|
border-bottom-color: var(--ots-text-muted) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body .nav-tabs .nav-link.active,
|
||||||
|
.modal-body .nav-tabs > li.active > a {
|
||||||
|
color: var(--ots-primary) !important;
|
||||||
|
border-bottom-color: var(--ots-primary) !important;
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body .tab-content {
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Alerts inside modals ------------------------------------------- */
|
||||||
|
.modal-body .alert {
|
||||||
|
border-radius: var(--ots-radius-sm);
|
||||||
|
padding: 12px 16px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
border: 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body .alert-info {
|
||||||
|
background: rgba(79, 140, 255, 0.1);
|
||||||
|
border-color: rgba(79, 140, 255, 0.25);
|
||||||
|
color: var(--ots-info, #5ec0ff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body .alert-warning {
|
||||||
|
background: rgba(244, 184, 96, 0.1);
|
||||||
|
border-color: rgba(244, 184, 96, 0.25);
|
||||||
|
color: var(--ots-warning, #f4b860);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body .alert-danger {
|
||||||
|
background: rgba(255, 107, 107, 0.1);
|
||||||
|
border-color: rgba(255, 107, 107, 0.25);
|
||||||
|
color: var(--ots-danger, #ff6b6b);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body .alert-success {
|
||||||
|
background: rgba(42, 212, 164, 0.1);
|
||||||
|
border-color: rgba(42, 212, 164, 0.25);
|
||||||
|
color: var(--ots-success, #2ad4a4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Tables inside modals ------------------------------------------- */
|
||||||
|
.modal-body table,
|
||||||
|
.modal-body .table {
|
||||||
|
color: var(--modal-body-text) !important;
|
||||||
|
border-color: var(--modal-border) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body table th {
|
||||||
|
background: var(--modal-header-bg) !important;
|
||||||
|
color: var(--modal-header-text) !important;
|
||||||
|
border-color: var(--modal-border) !important;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body table td {
|
||||||
|
border-color: var(--modal-border) !important;
|
||||||
|
color: var(--modal-body-text) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body table tbody tr:hover {
|
||||||
|
background-color: rgba(79, 140, 255, 0.04) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Checkbox / radio inside modals --------------------------------- */
|
||||||
|
.modal-body input[type="checkbox"],
|
||||||
|
.modal-body input[type="radio"] {
|
||||||
|
accent-color: var(--ots-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Bootbox-specific overrides ------------------------------------- */
|
||||||
|
.bootbox .modal-content {
|
||||||
|
background-color: var(--modal-bg) !important;
|
||||||
|
border-color: var(--modal-border) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bootbox .bootbox-body {
|
||||||
|
color: var(--modal-body-text) !important;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bootbox confirm icon area */
|
||||||
|
.bootbox .modal-body .bootbox-body p {
|
||||||
|
color: var(--modal-body-text) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Folder tree modal ---------------------------------------------- */
|
||||||
|
#folder-tree-form-modal .modal-content {
|
||||||
|
background-color: var(--modal-bg) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#folder-tree-form-modal .modal-body {
|
||||||
|
background-color: var(--modal-body-bg) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Scrollbar inside modal bodies ---------------------------------- */
|
||||||
|
.modal-body::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--ots-border, #2c3a54);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--ots-text-faint, #64748b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Stacked modals (z-index management) ---------------------------- */
|
||||||
|
.modal + .modal {
|
||||||
|
z-index: 1062;
|
||||||
|
}
|
||||||
|
.modal-backdrop + .modal-backdrop {
|
||||||
|
z-index: 1061;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Responsive ----------------------------------------------------- */
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.modal-dialog {
|
||||||
|
margin: 0.75rem auto;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-dialog.modal-lg,
|
||||||
|
.modal-dialog.modal-xl {
|
||||||
|
max-width: calc(100vw - 16px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
padding: 12px 16px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
padding: 16px !important;
|
||||||
|
max-height: calc(100vh - 180px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
padding: 10px 16px !important;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer .btn,
|
||||||
|
.modal-footer button {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -349,10 +349,17 @@
|
|||||||
* Initialize dropdown menus
|
* Initialize dropdown menus
|
||||||
*/
|
*/
|
||||||
function initDropdowns() {
|
function initDropdowns() {
|
||||||
// Only handle OTS-specific dropdowns (user menu, notifications).
|
// Only handle OTS-specific dropdowns (notifications, etc.).
|
||||||
|
// The user menu (#navbarUserMenu) is handled by theme-scripts.twig's
|
||||||
|
// initDropdowns() which uses floatMenu() for proper positioning.
|
||||||
// Let Bootstrap handle topbar nav dropdowns (Schedule, Design, etc.) natively
|
// Let Bootstrap handle topbar nav dropdowns (Schedule, Design, etc.) natively
|
||||||
// so that links like Dayparting can navigate normally.
|
// so that links like Dayparting can navigate normally.
|
||||||
const otsDropdowns = document.querySelectorAll('.ots-topbar-action .dropdown, .ots-page-actions .dropdown');
|
const otsDropdowns = Array.from(
|
||||||
|
document.querySelectorAll('.ots-topbar-action .dropdown, .ots-page-actions .dropdown')
|
||||||
|
).filter(function(el) {
|
||||||
|
// Exclude the user menu — it has its own dedicated handler in theme-scripts.twig
|
||||||
|
return !el.querySelector('#navbarUserMenu');
|
||||||
|
});
|
||||||
|
|
||||||
otsDropdowns.forEach(dropdown => {
|
otsDropdowns.forEach(dropdown => {
|
||||||
const toggle = dropdown.querySelector('.dropdown-toggle, [data-toggle="dropdown"]');
|
const toggle = dropdown.querySelector('.dropdown-toggle, [data-toggle="dropdown"]');
|
||||||
|
|||||||
@@ -52,7 +52,9 @@
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style nonce="{{ cspNonce }}">html,body{background:#ffffff!important;color:#111111!important}
|
<style nonce="{{ cspNonce }}">
|
||||||
|
/* Let the CSS variable theming (light/dark) control page background */
|
||||||
|
html,body{background-color:var(--color-background,#0f172a)!important;color:var(--color-text-primary,#ffffff)!important}
|
||||||
/* Hide the old topbar strip entirely — actions are now in .ots-page-actions */
|
/* Hide the old topbar strip entirely — actions are now in .ots-page-actions */
|
||||||
.row.header.header-side,
|
.row.header.header-side,
|
||||||
.ots-topbar-strip { display: none !important; height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; }
|
.ots-topbar-strip { display: none !important; height: 0 !important; margin: 0 !important; padding: 0 !important; overflow: hidden !important; }
|
||||||
@@ -104,9 +106,10 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div id="content-wrapper"{% if hideNavigation == "1" %} class="no-nav"{% endif %}>
|
<div id="content-wrapper" class="{% if hideNavigation == "1" %}no-nav{% endif %}{% if horizontalNav %} ots-horizontal-nav{% endif %}">
|
||||||
{# Floating top-right actions: notification bell + user menu #}
|
{# Floating top-right actions: notification bell + user menu #}
|
||||||
{% if not forceHide %}
|
{# Hidden when horizontal nav is active — the navbar already has these controls #}
|
||||||
|
{% if not forceHide and not horizontalNav %}
|
||||||
<div class="ots-page-actions"{% if hideNavigation == "1" %} style="display:none!important"{% endif %}>
|
<div class="ots-page-actions"{% if hideNavigation == "1" %} style="display:none!important"{% endif %}>
|
||||||
{% include "authed-theme-topbar.twig" ignore missing %}
|
{% include "authed-theme-topbar.twig" ignore missing %}
|
||||||
{% if currentUser.featureEnabled("drawer") %}
|
{% if currentUser.featureEnabled("drawer") %}
|
||||||
|
|||||||
461
custom/otssignange/views/dashboard-icon-page.twig
Normal file
461
custom/otssignange/views/dashboard-icon-page.twig
Normal file
@@ -0,0 +1,461 @@
|
|||||||
|
{#
|
||||||
|
/**
|
||||||
|
* OTS Signage Theme - Icon Dashboard Override
|
||||||
|
*
|
||||||
|
* Custom stylized icon dashboard that uses card-based buttons
|
||||||
|
* matching the OTS dashboard design system.
|
||||||
|
*
|
||||||
|
* Based on Xibo CMS dashboard-icon-page.twig
|
||||||
|
*/
|
||||||
|
#}
|
||||||
|
{% extends "authed.twig" %}
|
||||||
|
{% import "inline.twig" as inline %}
|
||||||
|
|
||||||
|
{% block pageContent %}
|
||||||
|
{% include "theme-dashboard-message.twig" ignore missing %}
|
||||||
|
|
||||||
|
<div class="dashboard-page">
|
||||||
|
<div class="page-header">
|
||||||
|
<h1>{% trans "Dashboard" %}</h1>
|
||||||
|
<p class="text-muted">{% trans "Quick access to all areas of your signage network" %}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{# ── Scheduling ────────────────────────────────────────────── #}
|
||||||
|
{% set scheduleCount = currentUser.featureEnabledCount(["schedule.view", "daypart.view"]) %}
|
||||||
|
{% if scheduleCount > 0 %}
|
||||||
|
<div class="icon-dash-section">
|
||||||
|
<h3 class="section-title"><i class="fa fa-calendar"></i> {% trans "Scheduling" %}</h3>
|
||||||
|
<div class="icon-dash-grid">
|
||||||
|
{% if currentUser.featureEnabled("schedule.view") %}
|
||||||
|
<a class="icon-dash-card dashboard-card" href="{{ url_for("schedule.view") }}">
|
||||||
|
<div class="icon-dash-card-icon icon-dash-card-icon--blue">
|
||||||
|
<i class="fa fa-calendar-check-o"></i>
|
||||||
|
</div>
|
||||||
|
<div class="icon-dash-card-body">
|
||||||
|
<span class="icon-dash-card-title">{% trans "Schedule" %}</span>
|
||||||
|
<span class="icon-dash-card-desc">{% trans "Manage event schedules" %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if currentUser.featureEnabled("daypart.view") %}
|
||||||
|
<a class="icon-dash-card dashboard-card" href="{{ url_for("daypart.view") }}">
|
||||||
|
<div class="icon-dash-card-icon icon-dash-card-icon--indigo">
|
||||||
|
<i class="fa fa-clock-o"></i>
|
||||||
|
</div>
|
||||||
|
<div class="icon-dash-card-body">
|
||||||
|
<span class="icon-dash-card-title">{% trans "Dayparting" %}</span>
|
||||||
|
<span class="icon-dash-card-desc">{% trans "Define time slots" %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{# ── Design ────────────────────────────────────────────────── #}
|
||||||
|
{% set countViewable = currentUser.featureEnabledCount(["campaign.view", "layout.view", "template.view", "resolution.view"]) %}
|
||||||
|
{% if countViewable > 0 %}
|
||||||
|
<div class="icon-dash-section">
|
||||||
|
<h3 class="section-title"><i class="fa fa-paint-brush"></i> {% trans "Design" %}</h3>
|
||||||
|
<div class="icon-dash-grid">
|
||||||
|
{% if currentUser.featureEnabled("campaign.view") %}
|
||||||
|
<a class="icon-dash-card dashboard-card" href="{{ url_for("campaign.view") }}">
|
||||||
|
<div class="icon-dash-card-icon icon-dash-card-icon--green">
|
||||||
|
<i class="fa fa-bullhorn"></i>
|
||||||
|
</div>
|
||||||
|
<div class="icon-dash-card-body">
|
||||||
|
<span class="icon-dash-card-title">{% trans "Campaigns" %}</span>
|
||||||
|
<span class="icon-dash-card-desc">{% trans "Organise layout playlists" %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if currentUser.featureEnabled("layout.view") %}
|
||||||
|
<a class="icon-dash-card dashboard-card" href="{{ url_for("layout.view") }}">
|
||||||
|
<div class="icon-dash-card-icon icon-dash-card-icon--blue">
|
||||||
|
<i class="fa fa-columns"></i>
|
||||||
|
</div>
|
||||||
|
<div class="icon-dash-card-body">
|
||||||
|
<span class="icon-dash-card-title">{% trans "Layouts" %}</span>
|
||||||
|
<span class="icon-dash-card-desc">{% trans "Design screen layouts" %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if currentUser.featureEnabled("template.view") %}
|
||||||
|
<a class="icon-dash-card dashboard-card" href="{{ url_for("template.view") }}">
|
||||||
|
<div class="icon-dash-card-icon icon-dash-card-icon--purple">
|
||||||
|
<i class="fa fa-clone"></i>
|
||||||
|
</div>
|
||||||
|
<div class="icon-dash-card-body">
|
||||||
|
<span class="icon-dash-card-title">{% trans "Templates" %}</span>
|
||||||
|
<span class="icon-dash-card-desc">{% trans "Reusable layout templates" %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if currentUser.featureEnabled("resolution.view") %}
|
||||||
|
<a class="icon-dash-card dashboard-card" href="{{ url_for("resolution.view") }}">
|
||||||
|
<div class="icon-dash-card-icon icon-dash-card-icon--teal">
|
||||||
|
<i class="fa fa-expand"></i>
|
||||||
|
</div>
|
||||||
|
<div class="icon-dash-card-body">
|
||||||
|
<span class="icon-dash-card-title">{% trans "Resolutions" %}</span>
|
||||||
|
<span class="icon-dash-card-desc">{% trans "Screen resolution presets" %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{# ── Library ───────────────────────────────────────────────── #}
|
||||||
|
{% set countViewable = currentUser.featureEnabledCount(["library.view", "playlist.view", "dataset.view", "menuBoard.view"]) %}
|
||||||
|
{% if countViewable > 0 %}
|
||||||
|
<div class="icon-dash-section">
|
||||||
|
<h3 class="section-title"><i class="fa fa-picture-o"></i> {% trans "Library" %}</h3>
|
||||||
|
<div class="icon-dash-grid">
|
||||||
|
{% if currentUser.featureEnabled("library.view") %}
|
||||||
|
<a class="icon-dash-card dashboard-card" href="{{ url_for("library.view") }}">
|
||||||
|
<div class="icon-dash-card-icon icon-dash-card-icon--orange">
|
||||||
|
<i class="fa fa-image"></i>
|
||||||
|
</div>
|
||||||
|
<div class="icon-dash-card-body">
|
||||||
|
<span class="icon-dash-card-title">{% trans "Library" %}</span>
|
||||||
|
<span class="icon-dash-card-desc">{% trans "Upload & manage media" %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if currentUser.featureEnabled("playlist.view") %}
|
||||||
|
<a class="icon-dash-card dashboard-card" href="{{ url_for("playlist.view") }}">
|
||||||
|
<div class="icon-dash-card-icon icon-dash-card-icon--blue">
|
||||||
|
<i class="fa fa-list"></i>
|
||||||
|
</div>
|
||||||
|
<div class="icon-dash-card-body">
|
||||||
|
<span class="icon-dash-card-title">{% trans "Playlists" %}</span>
|
||||||
|
<span class="icon-dash-card-desc">{% trans "Content playlists" %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if currentUser.featureEnabled("dataset.view") %}
|
||||||
|
<a class="icon-dash-card dashboard-card" href="{{ url_for("dataset.view") }}">
|
||||||
|
<div class="icon-dash-card-icon icon-dash-card-icon--indigo">
|
||||||
|
<i class="fa fa-database"></i>
|
||||||
|
</div>
|
||||||
|
<div class="icon-dash-card-body">
|
||||||
|
<span class="icon-dash-card-title">{% trans "DataSets" %}</span>
|
||||||
|
<span class="icon-dash-card-desc">{% trans "Tabular data sources" %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if currentUser.featureEnabled("menuBoard.view") %}
|
||||||
|
<a class="icon-dash-card dashboard-card" href="{{ url_for("menuBoard.view") }}">
|
||||||
|
<div class="icon-dash-card-icon icon-dash-card-icon--red">
|
||||||
|
<i class="fa fa-cutlery"></i>
|
||||||
|
</div>
|
||||||
|
<div class="icon-dash-card-body">
|
||||||
|
<span class="icon-dash-card-title">{% trans "Menu Boards" %}</span>
|
||||||
|
<span class="icon-dash-card-desc">{% trans "Digital menu management" %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{# ── Displays ──────────────────────────────────────────────── #}
|
||||||
|
{% set countViewable = currentUser.featureEnabledCount(["displays.view", "displaygroup.view", "displayprofile.view"]) %}
|
||||||
|
{% if countViewable > 0 %}
|
||||||
|
<div class="icon-dash-section">
|
||||||
|
<h3 class="section-title"><i class="fa fa-desktop"></i> {% trans "Displays" %}</h3>
|
||||||
|
<div class="icon-dash-grid">
|
||||||
|
{% if currentUser.featureEnabled("displays.view") %}
|
||||||
|
<a class="icon-dash-card dashboard-card" href="{{ url_for("display.view") }}">
|
||||||
|
<div class="icon-dash-card-icon icon-dash-card-icon--green">
|
||||||
|
<i class="fa fa-desktop"></i>
|
||||||
|
</div>
|
||||||
|
<div class="icon-dash-card-body">
|
||||||
|
<span class="icon-dash-card-title">{% trans "Displays" %}</span>
|
||||||
|
<span class="icon-dash-card-desc">{% trans "Manage all screens" %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if currentUser.featureEnabled("displaygroup.view") %}
|
||||||
|
<a class="icon-dash-card dashboard-card" href="{{ url_for("displaygroup.view") }}">
|
||||||
|
<div class="icon-dash-card-icon icon-dash-card-icon--blue">
|
||||||
|
<i class="fa fa-object-group"></i>
|
||||||
|
</div>
|
||||||
|
<div class="icon-dash-card-body">
|
||||||
|
<span class="icon-dash-card-title">{% trans "Display Groups" %}</span>
|
||||||
|
<span class="icon-dash-card-desc">{% trans "Organise screen groups" %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if currentUser.featureEnabled("displayprofile.view") %}
|
||||||
|
<a class="icon-dash-card dashboard-card" href="{{ url_for("displayprofile.view") }}">
|
||||||
|
<div class="icon-dash-card-icon icon-dash-card-icon--purple">
|
||||||
|
<i class="fa fa-cog"></i>
|
||||||
|
</div>
|
||||||
|
<div class="icon-dash-card-body">
|
||||||
|
<span class="icon-dash-card-title">{% trans "Display Settings" %}</span>
|
||||||
|
<span class="icon-dash-card-desc">{% trans "Player configuration profiles" %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{# ── Administration ────────────────────────────────────────── #}
|
||||||
|
{% set showAdmin = false %}
|
||||||
|
{% if currentUser.featureEnabled("users.view") and (currentUser.isGroupAdmin() or currentUser.isSuperAdmin()) %}
|
||||||
|
{% set showAdmin = true %}
|
||||||
|
{% endif %}
|
||||||
|
{% if currentUser.isSuperUser() %}
|
||||||
|
{% set showAdmin = true %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if showAdmin %}
|
||||||
|
<div class="icon-dash-section">
|
||||||
|
<h3 class="section-title"><i class="fa fa-cogs"></i> {% trans "Administration" %}</h3>
|
||||||
|
<div class="icon-dash-grid">
|
||||||
|
{% if currentUser.featureEnabled("users.view") and (currentUser.isGroupAdmin() or currentUser.isSuperAdmin()) %}
|
||||||
|
<a class="icon-dash-card dashboard-card" href="{{ url_for("user.view") }}">
|
||||||
|
<div class="icon-dash-card-icon icon-dash-card-icon--indigo">
|
||||||
|
<i class="fa fa-users"></i>
|
||||||
|
</div>
|
||||||
|
<div class="icon-dash-card-body">
|
||||||
|
<span class="icon-dash-card-title">{% trans "Users" %}</span>
|
||||||
|
<span class="icon-dash-card-desc">{% trans "User accounts & permissions" %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if currentUser.isSuperUser() %}
|
||||||
|
<a class="icon-dash-card dashboard-card" href="{{ url_for("admin.view") }}">
|
||||||
|
<div class="icon-dash-card-icon icon-dash-card-icon--orange">
|
||||||
|
<i class="fa fa-cogs"></i>
|
||||||
|
</div>
|
||||||
|
<div class="icon-dash-card-body">
|
||||||
|
<span class="icon-dash-card-title">{% trans "Settings" %}</span>
|
||||||
|
<span class="icon-dash-card-desc">{% trans "CMS system configuration" %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* ===================================================================
|
||||||
|
ICON DASHBOARD – Card Button Styles
|
||||||
|
Matches the OTS dashboard-card design system
|
||||||
|
=================================================================== */
|
||||||
|
|
||||||
|
/* Section spacing */
|
||||||
|
.icon-dash-section {
|
||||||
|
margin-top: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-dash-section:first-of-type {
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grid layout – responsive card grid */
|
||||||
|
.icon-dash-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||||||
|
gap: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Individual card – inherits .dashboard-card base from override.css */
|
||||||
|
.icon-dash-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 18px;
|
||||||
|
padding: 22px 24px;
|
||||||
|
text-decoration: none !important;
|
||||||
|
color: var(--color-text-primary) !important;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
/* Override rigid dashboard-card flex-direction:column if set */
|
||||||
|
flex-direction: row !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Subtle radial glow matching kpi-card--modern */
|
||||||
|
.icon-dash-card::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: radial-gradient(circle at top right, rgba(59, 130, 246, 0.10), transparent 60%);
|
||||||
|
pointer-events: none;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-dash-card:hover::after {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Icon container */
|
||||||
|
.icon-dash-card-icon {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 52px;
|
||||||
|
height: 52px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 14px;
|
||||||
|
font-size: 22px;
|
||||||
|
transition: transform 0.25s ease, box-shadow 0.25s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-dash-card:hover .icon-dash-card-icon {
|
||||||
|
transform: scale(1.08);
|
||||||
|
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Icon colour variants */
|
||||||
|
.icon-dash-card-icon--blue {
|
||||||
|
background: linear-gradient(135deg, rgba(59, 130, 246, 0.28), rgba(59, 130, 246, 0.12));
|
||||||
|
color: #60a5fa;
|
||||||
|
}
|
||||||
|
.icon-dash-card-icon--green {
|
||||||
|
background: linear-gradient(135deg, rgba(16, 185, 129, 0.28), rgba(16, 185, 129, 0.12));
|
||||||
|
color: #34d399;
|
||||||
|
}
|
||||||
|
.icon-dash-card-icon--orange {
|
||||||
|
background: linear-gradient(135deg, rgba(245, 158, 11, 0.28), rgba(245, 158, 11, 0.12));
|
||||||
|
color: #fbbf24;
|
||||||
|
}
|
||||||
|
.icon-dash-card-icon--red {
|
||||||
|
background: linear-gradient(135deg, rgba(239, 68, 68, 0.28), rgba(239, 68, 68, 0.12));
|
||||||
|
color: #f87171;
|
||||||
|
}
|
||||||
|
.icon-dash-card-icon--purple {
|
||||||
|
background: linear-gradient(135deg, rgba(124, 58, 237, 0.28), rgba(124, 58, 237, 0.12));
|
||||||
|
color: #a78bfa;
|
||||||
|
}
|
||||||
|
.icon-dash-card-icon--indigo {
|
||||||
|
background: linear-gradient(135deg, rgba(99, 102, 241, 0.28), rgba(99, 102, 241, 0.12));
|
||||||
|
color: #818cf8;
|
||||||
|
}
|
||||||
|
.icon-dash-card-icon--teal {
|
||||||
|
background: linear-gradient(135deg, rgba(20, 184, 166, 0.28), rgba(20, 184, 166, 0.12));
|
||||||
|
color: #2dd4bf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text area */
|
||||||
|
.icon-dash-card-body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
min-width: 0;
|
||||||
|
/* Reset inherited dashboard-card body padding */
|
||||||
|
padding: 0 !important;
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-dash-card-title {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
line-height: 1.3;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-dash-card-desc {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: var(--color-text-tertiary);
|
||||||
|
line-height: 1.4;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hover effects matching action-card--modern */
|
||||||
|
.icon-dash-card:hover {
|
||||||
|
border-color: rgba(59, 130, 246, 0.45) !important;
|
||||||
|
transform: translateY(-3px);
|
||||||
|
box-shadow: 0 20px 40px rgba(8, 15, 30, 0.45) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-dash-card:active {
|
||||||
|
transform: translateY(0px);
|
||||||
|
box-shadow: 0 10px 20px rgba(8, 15, 30, 0.35) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Section title with icon */
|
||||||
|
.icon-dash-section .section-title i {
|
||||||
|
margin-right: 8px;
|
||||||
|
opacity: 0.65;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Light mode overrides ─────────────────────────────────────── */
|
||||||
|
body.ots-light-mode .icon-dash-card {
|
||||||
|
background: linear-gradient(180deg, #ffffff, #f8fafc) !important;
|
||||||
|
border-color: rgba(148, 163, 184, 0.25) !important;
|
||||||
|
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.06) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.ots-light-mode .icon-dash-card:hover {
|
||||||
|
background: linear-gradient(180deg, #ffffff, #f1f5f9) !important;
|
||||||
|
border-color: rgba(59, 130, 246, 0.4) !important;
|
||||||
|
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.ots-light-mode .icon-dash-card-desc {
|
||||||
|
color: #64748b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Responsive adjustments ───────────────────────────────────── */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.icon-dash-grid {
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-dash-card {
|
||||||
|
padding: 16px 18px;
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-dash-card-icon {
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
font-size: 18px;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-dash-card-title {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-dash-card-desc {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.icon-dash-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-dash-card-desc {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
@@ -468,7 +468,7 @@ body {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
height: 56px;
|
height: 67px;
|
||||||
z-index: 1100;
|
z-index: 1100;
|
||||||
position: relative;
|
position: relative;
|
||||||
width: auto;
|
width: auto;
|
||||||
@@ -555,6 +555,47 @@ nav.navbar + #content-wrapper .page-content {
|
|||||||
padding-top: 56px;
|
padding-top: 56px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Horizontal nav mode: no sidebar, so remove sidebar margin from content */
|
||||||
|
nav.navbar + #content-wrapper {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
padding-left: 20px !important;
|
||||||
|
padding-right: 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.navbar + #content-wrapper .page-content {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.navbar + #content-wrapper .page-content .row {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
margin-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.navbar + #content-wrapper .page-content [class*="col-"] {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.navbar + #content-wrapper .page-content > .row > .col-sm-12 {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.navbar + #content-wrapper .page-header {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
padding-left: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Page background follows the light/dark theme when horizontal nav is active */
|
||||||
|
nav.navbar + #content-wrapper,
|
||||||
|
nav.navbar + #content-wrapper .page-content {
|
||||||
|
background-color: var(--color-background) !important;
|
||||||
|
color: var(--color-text-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* Right-side controls: notification bell + account menu */
|
/* Right-side controls: notification bell + account menu */
|
||||||
.navbar-collapse .navbar-nav.navbar-right {
|
.navbar-collapse .navbar-nav.navbar-right {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
@@ -580,9 +621,9 @@ nav.navbar + #content-wrapper .page-content {
|
|||||||
z-index: 1100 !important;
|
z-index: 1100 !important;
|
||||||
background-color: var(--color-surface-elevated) !important;
|
background-color: var(--color-surface-elevated) !important;
|
||||||
border-bottom: 1px solid var(--color-border) !important;
|
border-bottom: 1px solid var(--color-border) !important;
|
||||||
height: 56px !important;
|
height: 67px !important;
|
||||||
min-height: 56px !important;
|
min-height: 67px !important;
|
||||||
max-height: 56px !important;
|
max-height: 67px !important;
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
overflow: visible !important;
|
overflow: visible !important;
|
||||||
@@ -601,7 +642,7 @@ body:not(.ots-sidebar-collapsed) .ots-topbar-strip {
|
|||||||
display: flex !important;
|
display: flex !important;
|
||||||
align-items: center !important;
|
align-items: center !important;
|
||||||
justify-content: space-between !important;
|
justify-content: space-between !important;
|
||||||
height: 56px !important;
|
height: 67px !important;
|
||||||
padding: 0 16px !important;
|
padding: 0 16px !important;
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
gap: 12px !important;
|
gap: 12px !important;
|
||||||
@@ -3008,28 +3049,50 @@ legend {
|
|||||||
.modal,
|
.modal,
|
||||||
.modal-body,
|
.modal-body,
|
||||||
.modal-footer {
|
.modal-footer {
|
||||||
background-color: transparent !important;
|
color: var(--modal-body-text, var(--color-text-primary)) !important;
|
||||||
color: var(--color-text-primary) !important;
|
|
||||||
border: 1px solid var(--color-border) !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-content {
|
.modal-content {
|
||||||
background-color: var(--color-surface) !important;
|
background-color: var(--modal-bg, var(--color-surface)) !important;
|
||||||
color: var(--color-text-primary) !important;
|
color: var(--modal-body-text, var(--color-text-primary)) !important;
|
||||||
border: 1px solid var(--color-border) !important;
|
border: 1px solid var(--modal-border, var(--color-border)) !important;
|
||||||
|
border-radius: var(--modal-radius, var(--ots-radius-lg)) !important;
|
||||||
|
box-shadow: var(--modal-shadow) !important;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-backdrop,
|
.modal-backdrop,
|
||||||
.modal-backdrop.show,
|
.modal-backdrop.show,
|
||||||
.modal-backdrop.in {
|
.modal-backdrop.in {
|
||||||
background-color: rgba(0, 0, 0, 0.3) !important;
|
background-color: var(--modal-backdrop-bg, rgba(0, 0, 0, 0.3)) !important;
|
||||||
backdrop-filter: blur(4px) !important;
|
backdrop-filter: blur(var(--modal-backdrop-blur, 4px)) !important;
|
||||||
|
-webkit-backdrop-filter: blur(var(--modal-backdrop-blur, 4px)) !important;
|
||||||
opacity: 1 !important;
|
opacity: 1 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-header {
|
.modal-header {
|
||||||
background-color: var(--color-surface-elevated) !important;
|
background-color: var(--modal-header-bg, var(--color-surface-elevated)) !important;
|
||||||
border-bottom: 1px solid var(--color-border) !important;
|
border-bottom: 1px solid var(--modal-header-border, var(--color-border)) !important;
|
||||||
|
padding: 16px 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-title,
|
||||||
|
.modal-header h4,
|
||||||
|
.modal-header h5 {
|
||||||
|
color: var(--modal-header-text, var(--color-text-primary)) !important;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
background-color: var(--modal-body-bg, transparent) !important;
|
||||||
|
color: var(--modal-body-text, var(--color-text-primary)) !important;
|
||||||
|
padding: 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
background-color: var(--modal-footer-bg, transparent) !important;
|
||||||
|
border-top: 1px solid var(--modal-footer-border, var(--color-border)) !important;
|
||||||
|
padding: 12px 20px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-menu a,
|
.dropdown-menu a,
|
||||||
@@ -3373,6 +3436,7 @@ a.text-muted:hover {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Light mode token overrides so layout backgrounds follow theme */
|
/* Light mode token overrides so layout backgrounds follow theme */
|
||||||
|
html.ots-light-mode,
|
||||||
body.ots-light-mode {
|
body.ots-light-mode {
|
||||||
--ots-bg: var(--color-background);
|
--ots-bg: var(--color-background);
|
||||||
--ots-surface: var(--color-surface);
|
--ots-surface: var(--color-surface);
|
||||||
@@ -3450,7 +3514,8 @@ hr {
|
|||||||
background: var(--ots-bg);
|
background: var(--ots-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#content-wrapper {
|
/* Only apply sidebar content gap when in sidebar mode (inside #page-wrapper) */
|
||||||
|
#page-wrapper #content-wrapper {
|
||||||
padding-left: var(--ots-sidebar-content-gap);
|
padding-left: var(--ots-sidebar-content-gap);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
@@ -3459,12 +3524,23 @@ hr {
|
|||||||
padding-top: 24px;
|
padding-top: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Override Bootstrap col padding inside page-content */
|
/* Override Bootstrap col padding inside page-content (sidebar mode only) */
|
||||||
.page-content > .row > .col-sm-12 {
|
#page-wrapper .page-content > .row > .col-sm-12 {
|
||||||
padding-left: 16px;
|
padding-left: 16px;
|
||||||
padding-right: 16px;
|
padding-right: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Horizontal nav: ensure no extra left padding leaks through */
|
||||||
|
nav.navbar + #content-wrapper {
|
||||||
|
padding-left: 20px !important;
|
||||||
|
padding-right: 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.navbar + #content-wrapper .page-content > .row > .col-sm-12 {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* =============================================================================
|
/* =============================================================================
|
||||||
NAVBAR / TOPBAR
|
NAVBAR / TOPBAR
|
||||||
============================================================================= */
|
============================================================================= */
|
||||||
@@ -3475,8 +3551,8 @@ hr {
|
|||||||
border: none;
|
border: none;
|
||||||
border-bottom: 1px solid var(--ots-border);
|
border-bottom: 1px solid var(--ots-border);
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
||||||
height: 56px;
|
height: 67px;
|
||||||
min-height: 56px;
|
min-height: 67px;
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -3649,30 +3725,44 @@ hr {
|
|||||||
|
|
||||||
.widget,
|
.widget,
|
||||||
.card,
|
.card,
|
||||||
.panel,
|
.panel {
|
||||||
.modal-content {
|
|
||||||
background: var(--color-surface) !important;
|
background: var(--color-surface) !important;
|
||||||
border: 1px solid var(--color-border) !important;
|
border: 1px solid var(--color-border) !important;
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
box-shadow: var(--shadow-base);
|
box-shadow: var(--shadow-base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
background: var(--modal-bg, var(--color-surface)) !important;
|
||||||
|
border: 1px solid var(--modal-border, var(--color-border)) !important;
|
||||||
|
border-radius: var(--modal-radius, var(--ots-radius-lg)) !important;
|
||||||
|
box-shadow: var(--modal-shadow) !important;
|
||||||
|
}
|
||||||
|
|
||||||
.widget-title,
|
.widget-title,
|
||||||
.panel-heading,
|
.panel-heading,
|
||||||
.card-header,
|
.card-header {
|
||||||
.modal-header {
|
|
||||||
background: var(--color-surface-elevated) !important;
|
background: var(--color-surface-elevated) !important;
|
||||||
border-bottom: 1px solid var(--color-border) !important;
|
border-bottom: 1px solid var(--color-border) !important;
|
||||||
color: var(--color-text-primary) !important;
|
color: var(--color-text-primary) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
background: var(--modal-header-bg, var(--color-surface-elevated)) !important;
|
||||||
|
border-bottom: 1px solid var(--modal-header-border, var(--color-border)) !important;
|
||||||
|
color: var(--modal-header-text, var(--color-text-primary)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
.widget-body,
|
.widget-body,
|
||||||
.panel-body,
|
.panel-body,
|
||||||
.card-body,
|
.card-body {
|
||||||
.modal-body {
|
|
||||||
color: var(--ots-text);
|
color: var(--ots-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
color: var(--modal-body-text, var(--ots-text));
|
||||||
|
}
|
||||||
|
|
||||||
/* =============================================================================
|
/* =============================================================================
|
||||||
BUTTONS
|
BUTTONS
|
||||||
============================================================================= */
|
============================================================================= */
|
||||||
@@ -3913,30 +4003,121 @@ textarea:focus {
|
|||||||
============================================================================= */
|
============================================================================= */
|
||||||
|
|
||||||
.modal-content {
|
.modal-content {
|
||||||
border-radius: var(--radius-lg);
|
border-radius: var(--modal-radius, var(--ots-radius-lg));
|
||||||
background-color: var(--color-surface) !important;
|
background-color: var(--modal-bg, var(--color-surface)) !important;
|
||||||
color: var(--color-text-primary) !important;
|
color: var(--modal-body-text, var(--color-text-primary)) !important;
|
||||||
border: 1px solid var(--color-border) !important;
|
border: 1px solid var(--modal-border, var(--color-border)) !important;
|
||||||
|
box-shadow: var(--modal-shadow) !important;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal,
|
.modal,
|
||||||
.modal-header,
|
.modal-header,
|
||||||
.modal-body,
|
.modal-body,
|
||||||
.modal-footer {
|
.modal-footer {
|
||||||
background-color: transparent !important;
|
color: var(--modal-body-text, var(--color-text-primary)) !important;
|
||||||
color: var(--color-text-primary) !important;
|
}
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
background-color: var(--modal-header-bg, var(--color-surface-elevated)) !important;
|
||||||
|
border-bottom: 1px solid var(--modal-header-border, var(--ots-border)) !important;
|
||||||
|
padding: 16px 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
background-color: var(--modal-body-bg, transparent) !important;
|
||||||
|
padding: 20px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-backdrop,
|
.modal-backdrop,
|
||||||
.modal-backdrop.show,
|
.modal-backdrop.show,
|
||||||
.modal-backdrop.in {
|
.modal-backdrop.in {
|
||||||
background-color: rgba(0, 0, 0, 0.3) !important;
|
background-color: var(--modal-backdrop-bg, rgba(0, 0, 0, 0.3)) !important;
|
||||||
backdrop-filter: blur(4px) !important;
|
backdrop-filter: blur(var(--modal-backdrop-blur, 4px)) !important;
|
||||||
|
-webkit-backdrop-filter: blur(var(--modal-backdrop-blur, 4px)) !important;
|
||||||
opacity: 1 !important;
|
opacity: 1 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-footer {
|
.modal-footer {
|
||||||
border-top: 1px solid var(--ots-border);
|
background-color: var(--modal-footer-bg, transparent) !important;
|
||||||
|
border-top: 1px solid var(--modal-footer-border, var(--ots-border)) !important;
|
||||||
|
padding: 12px 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal footer buttons — secondary */
|
||||||
|
.modal-footer .btn,
|
||||||
|
.modal-footer button {
|
||||||
|
border-radius: var(--ots-radius-sm) !important;
|
||||||
|
border: 1px solid var(--modal-border, var(--ots-border)) !important;
|
||||||
|
background: var(--modal-body-bg, var(--ots-surface-3)) !important;
|
||||||
|
color: var(--modal-body-text, var(--ots-text)) !important;
|
||||||
|
transition: background var(--ots-transition),
|
||||||
|
color var(--ots-transition),
|
||||||
|
border-color var(--ots-transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer .btn:hover,
|
||||||
|
.modal-footer button:hover {
|
||||||
|
background: rgba(79, 140, 255, 0.08) !important;
|
||||||
|
border-color: var(--ots-primary) !important;
|
||||||
|
color: var(--ots-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal footer buttons — primary */
|
||||||
|
.modal-footer .btn-primary,
|
||||||
|
.modal-footer .btn.btn-primary {
|
||||||
|
background: var(--ots-primary) !important;
|
||||||
|
color: #0b1020 !important;
|
||||||
|
border-color: var(--ots-primary-2, var(--ots-primary)) !important;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer .btn-primary:hover {
|
||||||
|
background: var(--ots-primary-2, var(--color-primary-dark)) !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal footer buttons — danger */
|
||||||
|
.modal-footer .btn-danger {
|
||||||
|
background: var(--ots-danger) !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
border-color: var(--ots-danger) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal close button */
|
||||||
|
.modal-header .close,
|
||||||
|
.modal-header [data-dismiss="modal"] {
|
||||||
|
color: var(--modal-close-color, var(--ots-text-muted)) !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
text-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header .close:hover,
|
||||||
|
.modal-header [data-dismiss="modal"]:hover {
|
||||||
|
color: var(--modal-close-hover, var(--ots-text)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal form controls */
|
||||||
|
.modal-body .form-control,
|
||||||
|
.modal-body input[type="text"],
|
||||||
|
.modal-body input[type="number"],
|
||||||
|
.modal-body input[type="email"],
|
||||||
|
.modal-body input[type="password"],
|
||||||
|
.modal-body textarea,
|
||||||
|
.modal-body select {
|
||||||
|
background-color: var(--modal-input-bg, var(--ots-bg)) !important;
|
||||||
|
border: 1px solid var(--modal-input-border, var(--ots-border)) !important;
|
||||||
|
border-radius: var(--ots-radius-sm) !important;
|
||||||
|
color: var(--modal-input-text, var(--ots-text)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body .form-control:focus,
|
||||||
|
.modal-body input:focus,
|
||||||
|
.modal-body textarea:focus,
|
||||||
|
.modal-body select:focus {
|
||||||
|
border-color: var(--modal-input-focus-border, var(--ots-primary)) !important;
|
||||||
|
box-shadow: 0 0 0 3px var(--modal-input-focus-ring, rgba(79, 140, 255, 0.2)) !important;
|
||||||
|
outline: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* =============================================================================
|
/* =============================================================================
|
||||||
@@ -4076,3 +4257,65 @@ textarea:focus {
|
|||||||
text-indent: 0 !important;
|
text-indent: 0 !important;
|
||||||
list-style: none !important;
|
list-style: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* =============================================================================
|
||||||
|
HORIZONTAL NAV — FINAL OVERRIDE (must be last in file to win cascade)
|
||||||
|
Applied when the top navbar is active and there is no sidebar.
|
||||||
|
============================================================================= */
|
||||||
|
nav.navbar + #content-wrapper,
|
||||||
|
.navbar.navbar-expand-lg ~ #content-wrapper,
|
||||||
|
.navbar-default.navbar-expand-lg ~ #content-wrapper {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
padding-left: 20px !important;
|
||||||
|
padding-right: 20px !important;
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.navbar + #content-wrapper .page-content,
|
||||||
|
.navbar.navbar-expand-lg ~ #content-wrapper .page-content {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.navbar + #content-wrapper .page-content .row,
|
||||||
|
.navbar.navbar-expand-lg ~ #content-wrapper .page-content .row {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
margin-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.navbar + #content-wrapper .page-content [class*="col-"],
|
||||||
|
nav.navbar + #content-wrapper .page-content > .row > .col-sm-12,
|
||||||
|
.navbar.navbar-expand-lg ~ #content-wrapper .page-content [class*="col-"],
|
||||||
|
.navbar.navbar-expand-lg ~ #content-wrapper .page-content > .row > .col-sm-12 {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =============================================================================
|
||||||
|
HORIZONTAL NAV — CLASS-BASED OVERRIDE (highest cascade priority)
|
||||||
|
.ots-horizontal-nav is added directly to #content-wrapper in authed.twig
|
||||||
|
============================================================================= */
|
||||||
|
#content-wrapper.ots-horizontal-nav {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
padding-left: 30px !important;
|
||||||
|
padding-right: 30px !important;
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content-wrapper.ots-horizontal-nav .page-content {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content-wrapper.ots-horizontal-nav .page-content .row {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
margin-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content-wrapper.ots-horizontal-nav .page-content [class*="col-"],
|
||||||
|
#content-wrapper.ots-horizontal-nav .page-content > .row > .col-sm-12 {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
}
|
||||||
|
|||||||
@@ -379,53 +379,107 @@
|
|||||||
// Only handle the user-menu dropdown.
|
// Only handle the user-menu dropdown.
|
||||||
// Let Bootstrap handle topbar nav dropdowns (Schedule, Design, etc.) natively
|
// Let Bootstrap handle topbar nav dropdowns (Schedule, Design, etc.) natively
|
||||||
// so that links like Dayparting navigate normally.
|
// so that links like Dayparting navigate normally.
|
||||||
const userDropdown = document.querySelector('#navbarUserMenu') && document.querySelector('#navbarUserMenu').closest('.dropdown');
|
var userToggle = document.querySelector('#navbarUserMenu');
|
||||||
|
if (!userToggle) return;
|
||||||
|
|
||||||
|
var userDropdown = userToggle.closest('.dropdown');
|
||||||
if (!userDropdown) return;
|
if (!userDropdown) return;
|
||||||
|
|
||||||
const userMenu = userDropdown.querySelector('.dropdown-menu');
|
var userMenu = userDropdown.querySelector('.dropdown-menu');
|
||||||
if (!userMenu) return;
|
if (!userMenu) return;
|
||||||
|
|
||||||
userDropdown.addEventListener('click', function(e) {
|
// ── Neutralize Bootstrap ──────────────────────────────────────────
|
||||||
if (e.target.closest('.user-btn') || e.target.closest('[aria-label="User menu"]') || e.target.closest('#navbarUserMenu')) {
|
// Remove data-toggle so Bootstrap's delegated handler never fires.
|
||||||
e.preventDefault();
|
userToggle.removeAttribute('data-toggle');
|
||||||
const nowActive = !userDropdown.classList.contains('active');
|
try {
|
||||||
userDropdown.classList.toggle('active');
|
var jq = window.jQuery || window.$;
|
||||||
|
if (jq) {
|
||||||
|
jq(userToggle).off('.bs.dropdown');
|
||||||
|
jq(userDropdown).off('.bs.dropdown');
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
// Float / unfloat the user menu
|
// ── Move menu to <body> ONCE and leave it there ───────────────────
|
||||||
try {
|
// This escapes any overflow:hidden ancestors permanently.
|
||||||
if (nowActive) {
|
// We toggle visibility via the .ots-user-menu-open class (no DOM moves).
|
||||||
floatMenu(userMenu, userDropdown);
|
document.body.appendChild(userMenu);
|
||||||
} else {
|
// Mark it so the MutationObserver in observeAndFloatMenus() skips it
|
||||||
unfloatMenu(userMenu);
|
userMenu.setAttribute('data-ots-floating', 'permanent');
|
||||||
}
|
userMenu.setAttribute('data-ots-floating-obs', '1');
|
||||||
} catch (err) { /* ignore */ }
|
// Start hidden
|
||||||
|
userMenu.classList.add('ots-user-menu-body');
|
||||||
|
userMenu.classList.remove('show', 'ots-floating-menu');
|
||||||
|
|
||||||
// Compute placement to avoid going off-screen
|
function positionMenu() {
|
||||||
const trigger = userDropdown.querySelector('#navbarUserMenu');
|
var rect = userToggle.getBoundingClientRect();
|
||||||
if (trigger) {
|
var menuWidth = userMenu.offsetWidth || 220;
|
||||||
userMenu.classList.remove('dropdown-menu-left', 'dropdown-menu-right');
|
var spaceRight = window.innerWidth - rect.right;
|
||||||
const trigRect = trigger.getBoundingClientRect();
|
|
||||||
const menuWidth = userMenu.offsetWidth || 220;
|
// Vertically: below the avatar
|
||||||
const spaceRight = window.innerWidth - trigRect.right;
|
userMenu.style.top = Math.round(rect.bottom + 6) + 'px';
|
||||||
const spaceLeft = trigRect.left;
|
|
||||||
if (spaceRight < menuWidth && spaceLeft > menuWidth) {
|
// Horizontally: align right edge to avatar right edge,
|
||||||
userMenu.classList.add('dropdown-menu-left');
|
// but fall back to left-aligned if not enough space
|
||||||
} else {
|
if (spaceRight >= menuWidth) {
|
||||||
userMenu.classList.add('dropdown-menu-right');
|
userMenu.style.left = Math.round(rect.left) + 'px';
|
||||||
}
|
userMenu.style.right = 'auto';
|
||||||
}
|
} else {
|
||||||
|
userMenu.style.left = 'auto';
|
||||||
|
userMenu.style.right = Math.round(window.innerWidth - rect.right) + 'px';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openUserMenu() {
|
||||||
|
userDropdown.classList.remove('show');
|
||||||
|
userMenu.classList.remove('show');
|
||||||
|
positionMenu();
|
||||||
|
userMenu.classList.add('ots-user-menu-open');
|
||||||
|
userDropdown.classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeUserMenu() {
|
||||||
|
userMenu.classList.remove('ots-user-menu-open');
|
||||||
|
userDropdown.classList.remove('active', 'show');
|
||||||
|
userMenu.classList.remove('show');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Click handler on the toggle element itself ─────────────────────
|
||||||
|
userToggle.addEventListener('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
if (userMenu.classList.contains('ots-user-menu-open')) {
|
||||||
|
closeUserMenu();
|
||||||
|
} else {
|
||||||
|
openUserMenu();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Close user menu when clicking outside
|
// ── Close when clicking outside ───────────────────────────────────
|
||||||
document.addEventListener('click', function(e) {
|
document.addEventListener('click', function(e) {
|
||||||
if (!userDropdown.contains(e.target) && !userMenu.contains(e.target)) {
|
if (!userMenu.classList.contains('ots-user-menu-open')) return;
|
||||||
const hadActive = userDropdown.classList.contains('active');
|
if (userToggle.contains(e.target)) return;
|
||||||
userDropdown.classList.remove('active');
|
if (userMenu.contains(e.target)) return;
|
||||||
if (hadActive) {
|
closeUserMenu();
|
||||||
try { unfloatMenu(userMenu); } catch (err) {}
|
});
|
||||||
}
|
|
||||||
|
// ── Close when a modal opens ─────────────────────────────────────
|
||||||
|
// Menu items like Preferences / Edit Profile trigger Bootstrap modals
|
||||||
|
// via .XiboFormButton — close the dropdown as soon as any modal shows.
|
||||||
|
document.addEventListener('show.bs.modal', function() { closeUserMenu(); }, true);
|
||||||
|
try {
|
||||||
|
var jq = window.jQuery || window.$;
|
||||||
|
if (jq) {
|
||||||
|
jq(document).on('show.bs.modal', function() { closeUserMenu(); });
|
||||||
}
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// ── Reposition on scroll/resize ───────────────────────────────────
|
||||||
|
window.addEventListener('scroll', function() {
|
||||||
|
if (userMenu.classList.contains('ots-user-menu-open')) positionMenu();
|
||||||
|
}, true);
|
||||||
|
window.addEventListener('resize', function() {
|
||||||
|
if (userMenu.classList.contains('ots-user-menu-open')) positionMenu();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -434,7 +488,10 @@
|
|||||||
* Adds `.ots-floating-menu` and positions absolutely based on the trigger rect.
|
* Adds `.ots-floating-menu` and positions absolutely based on the trigger rect.
|
||||||
*/
|
*/
|
||||||
function floatMenu(menuEl, triggerEl) {
|
function floatMenu(menuEl, triggerEl) {
|
||||||
if (!menuEl || !triggerEl || menuEl.getAttribute('data-ots-floating') === '1') return;
|
if (!menuEl || !triggerEl) return;
|
||||||
|
// Skip if already floating or permanently managed by initDropdowns
|
||||||
|
var floatAttr = menuEl.getAttribute('data-ots-floating');
|
||||||
|
if (floatAttr === '1' || floatAttr === 'permanent') return;
|
||||||
try {
|
try {
|
||||||
// Remember original parent and next sibling so we can restore later
|
// Remember original parent and next sibling so we can restore later
|
||||||
menuEl._otsOriginalParent = menuEl.parentNode || null;
|
menuEl._otsOriginalParent = menuEl.parentNode || null;
|
||||||
@@ -511,16 +568,25 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function unfloatMenu(menuEl) {
|
function unfloatMenu(menuEl) {
|
||||||
if (!menuEl || menuEl.getAttribute('data-ots-floating') !== '1') return;
|
if (!menuEl) return;
|
||||||
|
var floatAttr = menuEl.getAttribute('data-ots-floating');
|
||||||
|
// Skip permanently managed menus and menus that aren't floating
|
||||||
|
if (floatAttr === 'permanent' || floatAttr !== '1') return;
|
||||||
try {
|
try {
|
||||||
menuEl.removeAttribute('data-ots-floating');
|
menuEl.removeAttribute('data-ots-floating');
|
||||||
menuEl.classList.remove('ots-floating-menu');
|
menuEl.classList.remove('ots-floating-menu');
|
||||||
|
// Clear ALL inline styles that floatMenu() may have set (including
|
||||||
|
// transform which was previously missed, causing stale styles).
|
||||||
menuEl.style.position = '';
|
menuEl.style.position = '';
|
||||||
menuEl.style.top = '';
|
menuEl.style.top = '';
|
||||||
menuEl.style.left = '';
|
menuEl.style.left = '';
|
||||||
menuEl.style.zIndex = '';
|
menuEl.style.zIndex = '';
|
||||||
menuEl.style.minWidth = '';
|
menuEl.style.minWidth = '';
|
||||||
menuEl.style.pointerEvents = '';
|
menuEl.style.pointerEvents = '';
|
||||||
|
menuEl.style.transform = '';
|
||||||
|
menuEl.style.visibility = '';
|
||||||
|
menuEl.style.display = '';
|
||||||
|
menuEl.style.opacity = '';
|
||||||
// Remove reposition listeners
|
// Remove reposition listeners
|
||||||
if (menuEl._otsReposition) {
|
if (menuEl._otsReposition) {
|
||||||
window.removeEventListener('scroll', menuEl._otsReposition, true);
|
window.removeEventListener('scroll', menuEl._otsReposition, true);
|
||||||
|
|||||||
Reference in New Issue
Block a user