Refactor filter panels and enhance sidebar functionality

- Updated filter panel toggle icons from chevron-up to chevron-down across multiple pages for consistency.
- Added 'collapsed' class to filter content divs to manage visibility state.
- Enhanced library page button for tidying up media items, replacing the trash icon with a custom SVG broom icon.
- Improved CSS styles for sidebar and page header to ensure visibility and proper layout when the sidebar is collapsed.
- Introduced JavaScript functionality to manage sidebar width and state, including theme toggle for light/dark mode.
- Created a new notification drawer template that adapts based on the compact view state.
This commit is contained in:
Matt Batchelder
2026-02-05 09:04:06 -05:00
parent d8f8c0f916
commit 122d098be4
23 changed files with 2447 additions and 190 deletions

View File

@@ -29,6 +29,78 @@
color-scheme: dark;
}
/* Ensure the page title/description remain visible when the sidebar is collapsed */
html.ots-sidebar-collapsed .page-header,
body.ots-sidebar-collapsed .page-header,
.ots-sidebar.collapsed ~ .ots-main .page-header,
.ots-main .page-header {
display: block !important;
visibility: visible !important;
height: auto !important;
padding-top: 16px !important;
padding-bottom: 16px !important;
}
html.ots-sidebar-collapsed .page-header h1,
body.ots-sidebar-collapsed .page-header h1,
.ots-sidebar.collapsed ~ .ots-main .page-header h1,
.ots-main .page-header h1 {
color: var(--color-text-primary) !important;
}
html.ots-sidebar-collapsed .page-header p.text-muted,
body.ots-sidebar-collapsed .page-header p.text-muted,
.ots-sidebar.collapsed ~ .ots-main .page-header p.text-muted,
.ots-main .page-header p.text-muted {
color: var(--color-text-tertiary) !important;
}
/* Ensure page header is not obscured by panels when sidebar is collapsed */
.ots-main .page-header {
position: relative !important;
z-index: 2500 !important;
margin-top: 8px !important;
margin-bottom: 12px !important;
}
/* Prevent immediate container clipping near the top */
.ots-content,
.ots-main,
.page-content {
overflow: visible !important;
}
/* Fixed-position fallback: keep the page header visible and readable when sidebar is collapsed */
@media (min-width: 992px) {
html.ots-sidebar-collapsed .page-header,
body.ots-sidebar-collapsed .page-header,
.ots-sidebar.collapsed ~ .ots-main .page-header {
position: fixed !important;
top: 16px !important;
left: calc(var(--ots-sidebar-width,64px) + 24px) !important;
/* fallback positions in case the CSS variable isn't set early */
left: 80px !important;
left: 260px !important;
right: 24px !important;
z-index: 3200 !important;
background: transparent !important;
padding-top: 8px !important;
padding-bottom: 8px !important;
margin: 0 !important;
}
/* If sidebar is collapsed, prefer a smaller left offset */
html.ots-sidebar-collapsed .page-header,
body.ots-sidebar-collapsed .page-header {
left: 80px !important;
}
html.ots-sidebar-collapsed .page-header .ots-filter-header,
body.ots-sidebar-collapsed .page-header .ots-filter-header {
margin-top: 0 !important;
}
}
html,
body {
background-color: var(--color-background);
@@ -144,7 +216,15 @@ body {
.sidebar-nav {
list-style: none;
margin: 0;
padding: 12px 0 120px;
padding: 72px 0 120px;
}
/* Extra top padding when sidebar is collapsed or expanded so items clear header */
.ots-sidebar.collapsed .sidebar-nav,
.ots-sidebar-collapsed .sidebar-nav,
.ots-sidebar:not(.collapsed) .sidebar-nav,
.ots-sidebar-collapsed:not(.collapsed) .sidebar-nav {
padding-top: 72px !important;
}
.sidebar-nav li {
@@ -354,13 +434,16 @@ body {
.ots-topbar {
background-color: var(--color-surface-elevated);
border-bottom: 2px solid var(--color-border);
padding: 10px 32px;
padding: 8px 24px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
justify-content: flex-start;
gap: 8px;
height: 64px;
z-index: 1000;
z-index: 1100;
position: sticky;
top: 0;
width: 100%;
}
/* Topbar nav container - override .navbar-nav defaults */
@@ -369,9 +452,10 @@ body {
border: 0 !important;
padding: 0 !important;
margin: 0 !important;
gap: 4px;
gap: 6px;
height: 100%;
align-items: center;
justify-content: flex-start;
}
.ots-topbar .nav-item {
@@ -382,12 +466,44 @@ body {
padding: 0 !important;
}
/* Brand stacking for 'OTS Signs' */
.xibo-logo-container {
display: flex;
align-items: center;
gap: 12px;
}
.xibo-logo-text {
display: inline-flex;
flex-direction: column;
line-height: 1;
}
.brand-line {
display: block;
margin: 0;
padding: 0;
}
.brand-line-top {
font-weight: 700;
font-size: 18px;
letter-spacing: 0.01em;
}
.brand-line-bottom {
font-weight: 600;
font-size: 13px;
opacity: 0.95;
margin-top: -2px;
}
.ots-topbar .nav-link {
display: inline-flex;
align-items: center;
height: 100%;
gap: 6px;
padding: 0 12px;
padding: 0 8px;
border-radius: 6px;
color: var(--color-text-secondary);
font-weight: 500;
@@ -402,6 +518,162 @@ body {
color: var(--color-primary);
}
/* Ensure content is offset below the sticky topbar when horizontal nav present */
nav.navbar + #content-wrapper,
nav.navbar + #content-wrapper .page-content {
padding-top: 64px;
}
/* Right-side controls: notification bell + account menu */
.navbar-collapse .navbar-nav.navbar-right {
margin-left: auto;
display: flex;
align-items: center;
gap: 8px;
}
.navbar-collapse .navbar-nav.navbar-right > li {
display: flex;
align-items: center;
}
.header-side .user,
.header-side .user-notif,
.header-side .user-actions {
display: inline-flex;
align-items: center;
gap: 8px;
margin: 0;
}
.header-side .user-actions {
float: right;
display: flex;
align-items: center;
gap: 12px;
}
.header-side .user-actions > * {
display: inline-flex !important;
align-items: center;
margin: 0 !important;
padding: 0 !important;
}
.header-side .user-actions li,
.header-side .user-actions .nav-item,
.header-side .user-actions .dropdown,
.header-side .user-actions .item,
.header-side .user-actions .nav-link {
display: inline-flex !important;
align-items: center;
width: auto !important;
}
.header-side .user-actions img.nav-avatar {
display: block;
width: 36px;
height: 36px;
}
.header-side .user-actions .dropdown-menu {
right: 0;
left: auto;
}
/* Ensure header area does not clip absolutely positioned dropdowns */
.row.header.header-side,
.row.header.header-side .col-sm-12,
.row.header.header-side .user-actions {
overflow: visible !important;
}
/* Ensure user dropdown renders above other content */
.header-side .user-actions .dropdown-menu,
.ots-user-menu {
z-index: 3000 !important;
}
/* When JS decides to open to the left (avoid viewport overflow) */
.dropdown-menu-left {
left: auto !important;
right: 0 !important;
}
/* When JS wants explicit left-aligned menu (menu's left edge aligned to trigger's left) */
.dropdown-menu-left-align {
left: 0 !important;
right: auto !important;
}
/* Force header row into a flex container so right-side controls align horizontally */
.row.header.header-side {
position: relative;
z-index: 10;
}
.row.header.header-side .col-sm-12 {
display: flex !important;
align-items: center !important;
justify-content: flex-start !important;
gap: 12px;
}
/* Ensure notification and user li elements render inline in header */
.header-side li.dropdown.nav-item.item,
.header-side .dropdown.nav-item.item,
.header-side .user-actions > li,
.header-side .user-actions > .dropdown,
.header-side .user-actions > .nav-item {
display: inline-flex !important;
float: none !important;
vertical-align: middle !important;
margin: 0 8px !important;
}
.header-side .nav-link {
display: inline-flex !important;
align-items: center !important;
padding: 4px !important;
}
.header-side .ots-topbar-icon,
.header-side .nav-avatar,
.header-side img.nav-avatar {
display: inline-block !important;
vertical-align: middle !important;
}
/* Push user actions to the right and maintain flex layout */
.row.header.header-side .meta {
flex: 0 0 auto;
}
.row.header.header-side .user-actions {
margin-left: auto;
display: flex !important;
align-items: center !important;
gap: 12px !important;
flex-shrink: 0;
}
/* Ensure sidebar items are visible and above header when sidebar is collapsed */
.ots-sidebar.collapsed {
z-index: 20;
}
.ots-sidebar.collapsed .ots-nav-icon {
position: relative;
z-index: 100;
}
@media (max-width: 991px) {
/* When collapsed on small screens, allow the right nav to flow normally */
.navbar-collapse .navbar-nav.navbar-right {
margin-left: 0;
}
}
.ots-topbar .nav-item.open .nav-link,
.ots-topbar .nav-item.active .nav-link {
background-color: rgba(59, 130, 246, 0.12);
@@ -709,19 +981,8 @@ body {
display: block;
}
/* OTS theme badge in topbar (authed-theme-topbar.twig) */
.ots-theme-badge .nav-link {
display: inline-flex;
align-items: center;
padding: 6px 10px;
border-radius: 999px;
background: rgba(59, 130, 246, 0.12);
color: var(--color-text-primary);
font-size: 12px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
}
/* OTS topbar badge removed */
.dropdown-menu li a {
display: flex;
@@ -781,6 +1042,22 @@ body {
margin-bottom: 16px;
}
/* When the sidebar is collapsed, hide the page header and meta area to
provide a compact layout consistent with the collapsed navigation state. */
.ots-sidebar.collapsed ~ .ots-main .page .meta,
.ots-sidebar.collapsed ~ .ots-main .page-header,
.ots-sidebar.collapsed ~ .ots-main .header-side,
.ots-sidebar.collapsed + .ots-main .page .meta,
.ots-sidebar.collapsed + .ots-main .page-header,
.ots-sidebar-collapsed .ots-main .page .meta,
.ots-sidebar-collapsed .ots-main .page-header,
body.ots-sidebar-collapsed .page .meta,
body.ots-sidebar-collapsed .page-header {
display: none !important;
margin: 0 !important;
padding: 0 !important;
}
.page-header h1 {
margin: 0 0 8px;
font-size: 32px;
@@ -1258,6 +1535,46 @@ body .panel .panel-heading,
display: none;
}
/* When the whole filter card is collapsed, reduce to a small floating button
positioned top-left so the page has minimal clutter but the user can
reopen the filter. This keeps the header and toggle usable. */
.ots-filter-card.collapsed {
position: fixed !important;
top: 12px !important;
left: 12px !important;
width: 48px !important;
height: 48px !important;
border-radius: 10px !important;
padding: 6px !important;
background: var(--color-surface, #ffffff) !important;
box-shadow: 0 6px 20px rgba(6,10,20,0.18) !important;
z-index: 1400 !important;
overflow: visible !important;
}
.ots-filter-card.collapsed .ots-filter-header {
display: flex !important;
align-items: center !important;
justify-content: center !important;
padding: 0 !important;
height: 100% !important;
}
.ots-filter-card.collapsed .ots-filter-title {
display: none !important;
}
.ots-filter-card.collapsed .ots-filter-toggle {
width: 36px !important;
height: 36px !important;
border-radius: 8px !important;
background: transparent !important;
}
.ots-filter-card.collapsed .ots-filter-content {
display: none !important;
}
.ots-filter-card .nav-tabs {
display: none;
}
@@ -1299,8 +1616,9 @@ body .panel .panel-heading,
.ots-filter-card .select2-selection,
.ots-filter-card .input-group-addon,
.ots-filter-card .input-group-text {
background: linear-gradient(180deg, rgba(15, 23, 42, 0.85), rgba(15, 23, 42, 0.65)) !important;
border: 1px solid rgba(148, 163, 184, 0.25) !important;
background-color: var(--color-surface) !important;
background-image: none !important;
border: 1px solid var(--color-border) !important;
color: var(--color-text-primary) !important;
border-radius: 10px !important;
padding: 12px 14px !important;
@@ -1309,7 +1627,7 @@ body .panel .panel-heading,
transition: border 150ms ease, box-shadow 150ms ease !important;
height: 48px !important;
line-height: 1.4 !important;
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.02), 0 6px 16px rgba(6, 10, 20, 0.18) !important;
box-shadow: none !important;
box-sizing: border-box !important;
}
@@ -1351,11 +1669,12 @@ body .panel .panel-heading,
.ots-filter-card .select2-container--default .select2-selection--single,
.ots-filter-card .select2-container--default .select2-selection--multiple {
background: linear-gradient(180deg, rgba(15, 23, 42, 0.85), rgba(15, 23, 42, 0.65)) !important;
border: 1px solid rgba(148, 163, 184, 0.25) !important;
background-color: var(--color-surface) !important;
background-image: none !important;
border: 1px solid var(--color-border) !important;
border-radius: 10px !important;
min-height: 44px !important;
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.02), 0 6px 16px rgba(6, 10, 20, 0.18) !important;
box-shadow: none !important;
}
.ots-filter-card .select2-container--default .select2-selection--multiple .select2-search__field {
@@ -1372,13 +1691,14 @@ body .panel .panel-heading,
.ots-filter-card .bootstrap-tagsinput,
.ots-filter-card .tagsinput {
background: linear-gradient(180deg, rgba(15, 23, 42, 0.85), rgba(15, 23, 42, 0.65)) !important;
border: 1px solid rgba(148, 163, 184, 0.25) !important;
background-color: var(--color-surface) !important;
background-image: none !important;
border: 1px solid var(--color-border) !important;
border-radius: 10px !important;
color: var(--color-text-primary) !important;
min-height: 44px !important;
padding: 6px 10px !important;
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.02), 0 6px 16px rgba(6, 10, 20, 0.18) !important;
box-shadow: none !important;
}
.ots-filter-card .bootstrap-tagsinput input,
@@ -2976,19 +3296,19 @@ hr {
.card,
.panel,
.modal-content {
background: var(--ots-surface-2);
border: 1px solid var(--ots-border);
border-radius: var(--ots-radius-md);
box-shadow: var(--ots-shadow-sm);
background: var(--color-surface) !important;
border: 1px solid var(--color-border) !important;
border-radius: var(--radius-md);
box-shadow: var(--shadow-base);
}
.widget-title,
.panel-heading,
.card-header,
.modal-header {
background: var(--ots-surface-3);
border-bottom: 1px solid var(--ots-border);
color: var(--ots-text);
background: var(--color-surface-elevated) !important;
border-bottom: 1px solid var(--color-border) !important;
color: var(--color-text-primary) !important;
}
.widget-body,
@@ -3238,8 +3558,10 @@ textarea:focus {
============================================================================= */
.modal-content {
border-radius: var(--ots-radius-lg);
background-color: var(--ots-surface-2) !important;
border-radius: var(--radius-lg);
background-color: var(--color-surface) !important;
color: var(--color-text-primary) !important;
border: 1px solid var(--color-border) !important;
}
.modal,
@@ -3247,6 +3569,7 @@ textarea:focus {
.modal-body,
.modal-footer {
background-color: transparent !important;
color: var(--color-text-primary) !important;
}
.modal-backdrop,