diff --git a/custom/otssignange/css/override-dark.css b/custom/otssignange/css/override-dark.css index 48edfb7..22e4ae5 100644 --- a/custom/otssignange/css/override-dark.css +++ b/custom/otssignange/css/override-dark.css @@ -31,6 +31,28 @@ --ots-radius-lg: 14px; --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); } +/* 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 .xibo-logo { color: var(--ots-text); @@ -792,54 +849,138 @@ textarea:focus { MODALS ============================================================================= */ +/* --- Content wrapper --- */ .modal-content { border-radius: var(--ots-radius-lg); - background-color: var(--color-surface) !important; - color: var(--color-text-primary) !important; - border: 1px solid var(--color-border) !important; + background-color: var(--modal-bg, var(--ots-surface)) !important; + color: var(--modal-body-text, var(--ots-text)) !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-header, .modal-body, .modal-footer { - background-color: transparent !important; - color: var(--color-text-primary) !important; + color: var(--modal-body-text, var(--ots-text)) !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.show, .modal-backdrop.in { - background-color: rgba(0, 0, 0, 0.3) !important; - backdrop-filter: blur(4px) !important; + background-color: var(--modal-backdrop-bg, rgba(0, 0, 0, 0.3)) !important; + backdrop-filter: blur(var(--modal-backdrop-blur, 4px)) !important; + -webkit-backdrop-filter: blur(var(--modal-backdrop-blur, 4px)) !important; opacity: 1 !important; } +/* --- 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 button { - background: var(--ots-surface-3); - color: var(--ots-text) !important; - border: 1px solid var(--ots-border) !important; + background: var(--modal-body-bg, var(--ots-surface-3)) !important; + color: var(--modal-body-text, var(--ots-text)) !important; + border: 1px solid var(--modal-border, var(--ots-border)) !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 button:hover { - background: var(--ots-surface-2); + background: rgba(79, 140, 255, 0.08) !important; color: var(--ots-primary) !important; border-color: var(--ots-primary) !important; } +/* Footer buttons — primary / submit */ .modal-footer .btn.btn-primary, .modal-footer button.btn-primary { background: var(--ots-primary) !important; color: #0b1020 !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; } /* ============================================================================= diff --git a/custom/otssignange/css/override.css b/custom/otssignange/css/override.css index 330e541..2bbda53 100644 --- a/custom/otssignange/css/override.css +++ b/custom/otssignange/css/override.css @@ -59,6 +59,29 @@ --editor-modal-content-bg: #1e293b; --editor-modal-header-bg: #0f172a; --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-border: #334155; --editor-text: #e2e8f0; @@ -140,7 +163,6 @@ body { Avoid forcing `.dropdown-menu` to be `display:block`/`visibility:visible` so native dropdown behaviour (open/close) is preserved. */ .ots-notif-menu, -.ots-user-menu, .context-menu, .row-menu, .rowMenu, @@ -154,6 +176,16 @@ body { 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 */ th.rowMenu, td.rowMenu { @@ -191,7 +223,8 @@ td.rowMenu { 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 { --color-primary: #2563eb; --color-primary-dark: #1d4ed8; @@ -246,6 +279,29 @@ body.ots-light-mode { --editor-modal-content-bg: #ffffff; --editor-modal-header-bg: #f1f5f9; --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-border: #e2e8f0; --editor-text: #0f172a; @@ -517,7 +573,8 @@ html, body, #page-wrapper, .ots-main, .ots-content, .page-content { 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; 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; } +/* 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 The .editor-modal is AJAX-injected into #editor-container (inside @@ -1720,7 +1786,7 @@ html.ots-sidebar-collapsed #content-wrapper { ============================================================================ */ .ots-page-actions { position: absolute; - top: 12px; + top: 14px; right: 20px; z-index: 1100; display: flex; @@ -1779,8 +1845,8 @@ html.ots-sidebar-collapsed #content-wrapper { .ots-page-actions .ots-topbar-action img.nav-avatar { display: block !important; - width: 24px !important; - height: 24px !important; + width: 20px !important; + height: 20px !important; border-radius: 50% !important; object-fit: cover !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); } +/* ============================================================================ + USER MENU — standalone styles that apply whether the menu is inside + .ots-page-actions or floated to
by floatMenu(). + ============================================================================ */ + +/* When the menu lives in (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 { background: transparent !important; box-shadow: none !important; @@ -3018,7 +3242,7 @@ body.ots-sidebar-collapsed .ots-sidebar-nav { align-items: center; justify-content: flex-start; gap: 4px; - height: 56px; + height: 67px; z-index: 1000; position: relative; flex: 1; @@ -3505,6 +3729,22 @@ body.ots-sidebar-open .ots-topbar { 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 */ .dropdown-menu li a { @@ -6427,7 +6667,7 @@ a.text-muted:hover { gap: 10px; padding: 0; margin-right: 12px; - height: 56px; + height: 67px; } .xibo-logo-text { @@ -6607,11 +6847,12 @@ body, 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 collapsed value comes from body.ots-sidebar-collapsed overrides. */ -#content-wrapper, -.ots-main { + The collapsed value comes from body.ots-sidebar-collapsed overrides. + Scoped to #page-wrapper which only exists in sidebar mode. */ +#page-wrapper #content-wrapper, +#page-wrapper .ots-main { 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; max-width: calc(100vw - var(--ots-sidebar-actual-width, var(--ots-sidebar-width))) !important; @@ -6620,6 +6861,10 @@ body, 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 .ots-main, html.ots-sidebar-collapsed #content-wrapper, @@ -6839,4 +7084,555 @@ body.ots-light-mode .dataTables_wrapper .dataTables_length select { text-indent: 0 !important; float: none !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; + } } \ No newline at end of file diff --git a/custom/otssignange/js/theme.js b/custom/otssignange/js/theme.js index 0de33f5..ac09245 100644 --- a/custom/otssignange/js/theme.js +++ b/custom/otssignange/js/theme.js @@ -349,10 +349,17 @@ * Initialize dropdown menus */ 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 // 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 => { const toggle = dropdown.querySelector('.dropdown-toggle, [data-toggle="dropdown"]'); diff --git a/custom/otssignange/views/authed.twig b/custom/otssignange/views/authed.twig index 42bd868..6056498 100644 --- a/custom/otssignange/views/authed.twig +++ b/custom/otssignange/views/authed.twig @@ -52,7 +52,9 @@ })(); - +{% endblock %} diff --git a/custom/otssignange/views/override-styles.twig b/custom/otssignange/views/override-styles.twig index fef232e..ef4362b 100644 --- a/custom/otssignange/views/override-styles.twig +++ b/custom/otssignange/views/override-styles.twig @@ -468,7 +468,7 @@ body { align-items: center; justify-content: flex-start; gap: 4px; - height: 56px; + height: 67px; z-index: 1100; position: relative; width: auto; @@ -555,6 +555,47 @@ nav.navbar + #content-wrapper .page-content { 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 */ .navbar-collapse .navbar-nav.navbar-right { margin-left: auto; @@ -580,9 +621,9 @@ nav.navbar + #content-wrapper .page-content { z-index: 1100 !important; background-color: var(--color-surface-elevated) !important; border-bottom: 1px solid var(--color-border) !important; - height: 56px !important; - min-height: 56px !important; - max-height: 56px !important; + height: 67px !important; + min-height: 67px !important; + max-height: 67px !important; margin: 0 !important; padding: 0 !important; overflow: visible !important; @@ -601,7 +642,7 @@ body:not(.ots-sidebar-collapsed) .ots-topbar-strip { display: flex !important; align-items: center !important; justify-content: space-between !important; - height: 56px !important; + height: 67px !important; padding: 0 16px !important; margin: 0 !important; gap: 12px !important; @@ -3008,28 +3049,50 @@ legend { .modal, .modal-body, .modal-footer { - background-color: transparent !important; - color: var(--color-text-primary) !important; - border: 1px solid var(--color-border) !important; + color: var(--modal-body-text, var(--color-text-primary)) !important; } .modal-content { - background-color: var(--color-surface) !important; - color: var(--color-text-primary) !important; - border: 1px solid var(--color-border) !important; + background-color: var(--modal-bg, var(--color-surface)) !important; + color: var(--modal-body-text, var(--color-text-primary)) !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.show, .modal-backdrop.in { - background-color: rgba(0, 0, 0, 0.3) !important; - backdrop-filter: blur(4px) !important; + background-color: var(--modal-backdrop-bg, rgba(0, 0, 0, 0.3)) !important; + backdrop-filter: blur(var(--modal-backdrop-blur, 4px)) !important; + -webkit-backdrop-filter: blur(var(--modal-backdrop-blur, 4px)) !important; opacity: 1 !important; } .modal-header { - background-color: var(--color-surface-elevated) !important; - border-bottom: 1px solid var(--color-border) !important; + background-color: var(--modal-header-bg, var(--color-surface-elevated)) !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, @@ -3373,6 +3436,7 @@ a.text-muted:hover { } /* Light mode token overrides so layout backgrounds follow theme */ +html.ots-light-mode, body.ots-light-mode { --ots-bg: var(--color-background); --ots-surface: var(--color-surface); @@ -3450,7 +3514,8 @@ hr { 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); box-sizing: border-box; } @@ -3459,12 +3524,23 @@ hr { padding-top: 24px; } -/* Override Bootstrap col padding inside page-content */ -.page-content > .row > .col-sm-12 { +/* Override Bootstrap col padding inside page-content (sidebar mode only) */ +#page-wrapper .page-content > .row > .col-sm-12 { padding-left: 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 ============================================================================= */ @@ -3475,8 +3551,8 @@ hr { border: none; border-bottom: 1px solid var(--ots-border); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); - height: 56px; - min-height: 56px; + height: 67px; + min-height: 67px; padding: 0 16px; display: flex; align-items: center; @@ -3649,30 +3725,44 @@ hr { .widget, .card, -.panel, -.modal-content { +.panel { background: var(--color-surface) !important; border: 1px solid var(--color-border) !important; border-radius: var(--radius-md); 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, .panel-heading, -.card-header, -.modal-header { +.card-header { background: var(--color-surface-elevated) !important; border-bottom: 1px solid var(--color-border) !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, .panel-body, -.card-body, -.modal-body { +.card-body { color: var(--ots-text); } +.modal-body { + color: var(--modal-body-text, var(--ots-text)); +} + /* ============================================================================= BUTTONS ============================================================================= */ @@ -3913,30 +4003,121 @@ textarea:focus { ============================================================================= */ .modal-content { - border-radius: var(--radius-lg); - background-color: var(--color-surface) !important; - color: var(--color-text-primary) !important; - border: 1px solid var(--color-border) !important; + border-radius: var(--modal-radius, var(--ots-radius-lg)); + background-color: var(--modal-bg, var(--color-surface)) !important; + color: var(--modal-body-text, var(--color-text-primary)) !important; + border: 1px solid var(--modal-border, var(--color-border)) !important; + box-shadow: var(--modal-shadow) !important; + overflow: hidden; } .modal, .modal-header, .modal-body, .modal-footer { - background-color: transparent !important; - color: var(--color-text-primary) !important; + color: var(--modal-body-text, 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.show, .modal-backdrop.in { - background-color: rgba(0, 0, 0, 0.3) !important; - backdrop-filter: blur(4px) !important; + background-color: var(--modal-backdrop-bg, rgba(0, 0, 0, 0.3)) !important; + backdrop-filter: blur(var(--modal-backdrop-blur, 4px)) !important; + -webkit-backdrop-filter: blur(var(--modal-backdrop-blur, 4px)) !important; opacity: 1 !important; } .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; 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; +} diff --git a/custom/otssignange/views/theme-scripts.twig b/custom/otssignange/views/theme-scripts.twig index 7f031f8..5f25be0 100644 --- a/custom/otssignange/views/theme-scripts.twig +++ b/custom/otssignange/views/theme-scripts.twig @@ -379,53 +379,107 @@ // Only handle the user-menu dropdown. // Let Bootstrap handle topbar nav dropdowns (Schedule, Design, etc.) natively // 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; - const userMenu = userDropdown.querySelector('.dropdown-menu'); + var userMenu = userDropdown.querySelector('.dropdown-menu'); if (!userMenu) return; - userDropdown.addEventListener('click', function(e) { - if (e.target.closest('.user-btn') || e.target.closest('[aria-label="User menu"]') || e.target.closest('#navbarUserMenu')) { - e.preventDefault(); - const nowActive = !userDropdown.classList.contains('active'); - userDropdown.classList.toggle('active'); + // ── Neutralize Bootstrap ────────────────────────────────────────── + // Remove data-toggle so Bootstrap's delegated handler never fires. + userToggle.removeAttribute('data-toggle'); + try { + var jq = window.jQuery || window.$; + if (jq) { + jq(userToggle).off('.bs.dropdown'); + jq(userDropdown).off('.bs.dropdown'); + } + } catch (e) {} - // Float / unfloat the user menu - try { - if (nowActive) { - floatMenu(userMenu, userDropdown); - } else { - unfloatMenu(userMenu); - } - } catch (err) { /* ignore */ } + // ── Move menu to ONCE and leave it there ─────────────────── + // This escapes any overflow:hidden ancestors permanently. + // We toggle visibility via the .ots-user-menu-open class (no DOM moves). + document.body.appendChild(userMenu); + // Mark it so the MutationObserver in observeAndFloatMenus() skips it + userMenu.setAttribute('data-ots-floating', 'permanent'); + userMenu.setAttribute('data-ots-floating-obs', '1'); + // Start hidden + userMenu.classList.add('ots-user-menu-body'); + userMenu.classList.remove('show', 'ots-floating-menu'); - // Compute placement to avoid going off-screen - const trigger = userDropdown.querySelector('#navbarUserMenu'); - if (trigger) { - userMenu.classList.remove('dropdown-menu-left', 'dropdown-menu-right'); - const trigRect = trigger.getBoundingClientRect(); - const menuWidth = userMenu.offsetWidth || 220; - const spaceRight = window.innerWidth - trigRect.right; - const spaceLeft = trigRect.left; - if (spaceRight < menuWidth && spaceLeft > menuWidth) { - userMenu.classList.add('dropdown-menu-left'); - } else { - userMenu.classList.add('dropdown-menu-right'); - } - } + function positionMenu() { + var rect = userToggle.getBoundingClientRect(); + var menuWidth = userMenu.offsetWidth || 220; + var spaceRight = window.innerWidth - rect.right; + + // Vertically: below the avatar + userMenu.style.top = Math.round(rect.bottom + 6) + 'px'; + + // Horizontally: align right edge to avatar right edge, + // but fall back to left-aligned if not enough space + if (spaceRight >= menuWidth) { + 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) { - if (!userDropdown.contains(e.target) && !userMenu.contains(e.target)) { - const hadActive = userDropdown.classList.contains('active'); - userDropdown.classList.remove('active'); - if (hadActive) { - try { unfloatMenu(userMenu); } catch (err) {} - } + if (!userMenu.classList.contains('ots-user-menu-open')) return; + if (userToggle.contains(e.target)) return; + if (userMenu.contains(e.target)) return; + closeUserMenu(); + }); + + // ── 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. */ 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 { // Remember original parent and next sibling so we can restore later menuEl._otsOriginalParent = menuEl.parentNode || null; @@ -511,16 +568,25 @@ } 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 { menuEl.removeAttribute('data-ots-floating'); 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.top = ''; menuEl.style.left = ''; menuEl.style.zIndex = ''; menuEl.style.minWidth = ''; menuEl.style.pointerEvents = ''; + menuEl.style.transform = ''; + menuEl.style.visibility = ''; + menuEl.style.display = ''; + menuEl.style.opacity = ''; // Remove reposition listeners if (menuEl._otsReposition) { window.removeEventListener('scroll', menuEl._otsReposition, true);