From 87a444b8de8e01745b4348218421a5d830b84ed9 Mon Sep 17 00:00:00 2001 From: matt Date: Fri, 6 Feb 2026 23:54:21 -0500 Subject: [PATCH] Add new pages for managing tags, tasks, transitions, users, user groups, and their respective JavaScript functionalities - Implemented tag management page with filtering, data table, and AJAX functionality. - Created task management page with task listing, filtering, and AJAX data loading. - Developed transition management page with a data table for transitions. - Added user management page with comprehensive user details, filtering options, and AJAX support. - Introduced user group management page with filtering and data table for user groups. - Enhanced JavaScript for data tables, including state saving, filtering, and AJAX data fetching for all new pages. --- SIDEBAR_DESIGN_REVIEW.md | 320 +++++ custom/otssignange/css/client.css | 16 + custom/otssignange/css/override.css | 706 ++++++++-- custom/otssignange/js/theme.js | 80 +- .../otssignange/views/applications-page.twig | 268 ++++ custom/otssignange/views/authed-sidebar.twig | 2 +- custom/otssignange/views/authed.twig | 52 +- custom/otssignange/views/campaign-page.twig | 21 +- custom/otssignange/views/command-page.twig | 15 +- .../views/dashboard-status-page.twig | 2 +- custom/otssignange/views/dataset-page.twig | 23 +- custom/otssignange/views/daypart-page.twig | 15 +- custom/otssignange/views/display-page.twig | 29 +- .../otssignange/views/displaygroup-page.twig | 17 +- .../views/displayprofile-page.twig | 15 +- custom/otssignange/views/fonts-page.twig | 159 +++ custom/otssignange/views/layout-page.twig | 33 +- custom/otssignange/views/library-page.twig | 46 +- custom/otssignange/views/menuboard-page.twig | 25 +- custom/otssignange/views/module-page.twig | 121 ++ custom/otssignange/views/override-styles.twig | 554 +++++--- .../views/playersoftware-page.twig | 15 +- custom/otssignange/views/playlist-page.twig | 19 +- custom/otssignange/views/resolution-page.twig | 15 +- custom/otssignange/views/schedule-page.twig | 21 +- custom/otssignange/views/settings-page.twig | 1253 +++++++++++++++++ custom/otssignange/views/syncgroup-page.twig | 15 +- custom/otssignange/views/tag-page.twig | 171 +++ custom/otssignange/views/task-page.twig | 177 +++ custom/otssignange/views/template-page.twig | 21 +- custom/otssignange/views/theme-scripts.twig | 305 ++-- custom/otssignange/views/transition-page.twig | 98 ++ custom/otssignange/views/user-page.twig | 444 ++++++ custom/otssignange/views/usergroup-page.twig | 194 +++ 34 files changed, 4579 insertions(+), 688 deletions(-) create mode 100644 SIDEBAR_DESIGN_REVIEW.md create mode 100644 custom/otssignange/views/applications-page.twig create mode 100644 custom/otssignange/views/fonts-page.twig create mode 100644 custom/otssignange/views/module-page.twig create mode 100644 custom/otssignange/views/settings-page.twig create mode 100644 custom/otssignange/views/tag-page.twig create mode 100644 custom/otssignange/views/task-page.twig create mode 100644 custom/otssignange/views/transition-page.twig create mode 100644 custom/otssignange/views/user-page.twig create mode 100644 custom/otssignange/views/usergroup-page.twig diff --git a/SIDEBAR_DESIGN_REVIEW.md b/SIDEBAR_DESIGN_REVIEW.md new file mode 100644 index 0000000..d477892 --- /dev/null +++ b/SIDEBAR_DESIGN_REVIEW.md @@ -0,0 +1,320 @@ +# Comprehensive Sidebar Design Review - OTS Signage Theme + +## Executive Summary +The sidebar is well-structured with a modern dark-mode design, but has several areas for refinement: +- **Padding**: Generally good, but can be more consistent +- **Icon Arrangement**: Icons are properly centered and sized, but scaling between states could be improved +- **Overall Style**: Cohesive and modern, with good dark mode aesthetics + +--- + +## 1. SIDEBAR DIMENSIONS & STRUCTURE + +### CSS Variables (in `override.css`): +```css +--ots-sidebar-width: 256px /* Full width */ +--ots-sidebar-collapsed-width: 64px /* Collapsed width */ +--ots-sidebar-header-height: 64px /* Header section */ +--ots-sidebar-item-height: 44px /* Nav item height */ +--ots-sidebar-item-radius: 10px /* Border radius */ +--ots-sidebar-item-padding-x: 12px /* Horizontal padding */ +``` + +### Layout Analysis: +- **Full Width**: 256px (reasonable for desktop, matches common patterns) +- **Collapsed Width**: 64px (good for icon-only display) +- **Responsive Break**: Changes to overlay at 768px (mobile-first, good) + +**Issue #1**: When collapsed, icons still have adequate space (20px icon + padding), but text labels are 100% hidden. Consider adding a tooltip system for discoverability. + +--- + +## 2. PADDING ANALYSIS + +### Sidebar Header (`.sidebar-header`) +```css +padding: 20px 16px; /* Vertical: 20px | Horizontal: 16px */ +``` +- **Assessment**: ✅ Good - balanced and symmetrical +- **Component**: Contains logo (32px icon) + brand text + toggle button +- **Vertical Alignment**: Properly centered with `align-items: center` + +### Sidebar Content (`.sidebar-content`) +```css +padding: 12px 0; /* Vertical: 12px | Horizontal: 0 */ +``` +- **Assessment**: ⚠️ Inconsistent - no horizontal padding here +- **Issue**: Padding is applied at the list item level instead (see nav items) + +### Sidebar Navigation (`.sidebar-nav`) +```css +padding: 72px 0 120px; /* Top: 72px | Bottom: 120px | Sides: 0 */ +``` +- **Assessment**: ⚠️ **PROBLEM AREA** - padding is excessive for top/bottom + - 72px top padding assumes a fixed header height, could be dynamic + - 120px bottom padding creates large blank space (mobile unfriendly) + - No horizontal padding means nav items extend to edge + +### Navigation Items (`.ots-sidebar li.sidebar-list > a`) +```css +padding: 6px 10px; /* Item content padding */ +margin: 3px 10px; /* Outer margin/spacing */ +border-radius: 12px; +min-height: 40px; +``` +- **Assessment**: ⚠️ Mixed padding/margin approach + - **Inner padding (6px 10px)**: Good for text breathing room + - **Outer margin (3px 10px)**: Creates 10px left/right space from sidebar edge + - **Min-height (40px)**: Good touch target size + +**Recommendation**: The left margin (10px) combined with border-radius (12px) creates a nice inset look. Could be intentional and good. + +--- + +## 3. ICON ARRANGEMENT + +### Icon Container (`.ots-nav-icon`) +```css +width: 24px; +height: 24px; +display: flex; +align-items: center; +justify-content: center; +font-size: 16px; +color: currentColor; +justify-self: center; +``` +- **Assessment**: ✅ Excellent + - Square container with centered content + - `justify-self: center` ensures centering in CSS Grid layout + - `color: currentColor` inherits link color for active/hover states + +### Nav Item Grid Layout +```css +display: grid; +grid-template-columns: 20px 1fr; /* Icon: 20px fixed | Text: flex */ +align-items: center; +column-gap: 12px; +``` +- **Assessment**: ✅ Good grid-based approach + - Consistent 12px gap between icon and text + - Icon column is tight (20px), text is flexible + - **Issue**: 20px icon container is narrower than 24px icon element - could cause slight misalignment + +**Recommendation**: Change to `grid-template-columns: 24px 1fr` to match the icon container size. + +### Active Item Styling +```css +.ots-sidebar li.sidebar-list.active > a { + color: #0b1221; /* Dark text on light bg */ + background-color: #ffffff; + font-weight: 600; + box-shadow: 0 8px 18px rgba(15, 23, 42, 0.25); +} +``` +- **Assessment**: ✅ Strong visual feedback + - High contrast (white background + dark text) + - Shadow adds depth + - Icon inherits dark color via `currentColor` + +--- + +## 4. OVERALL STYLE & DESIGN CONSISTENCY + +### Color Palette (Dark Mode) +```css +--ots-sidebar-bg: #08132a; /* Very dark blue */ +--ots-sidebar-link: #f9fbff; /* Nearly white text */ +--ots-sidebar-link-hover-bg: rgba(255,255,255,0.08); +--ots-sidebar-active-bg: rgba(255,255,255,0.06); +--ots-sidebar-active-text: #ffffff; +--ots-sidebar-muted-text: #8ea4c7; /* Muted section headers */ +``` +- **Assessment**: ✅ Excellent contrast and hierarchy + - Background: Deep navy (#08132a) + - Primary text: Nearly white (#f9fbff) + - Hover: 8% white overlay (subtle) + - Active: 6% white overlay (subtle) + - Section headers: Medium blue (#8ea4c7) + - **Accessibility**: WCAG AA compliant (>4.5:1 contrast) + +### Section Headers (`.sidebar-title`) +```css +display: block; +font-size: 10px; +font-weight: 700; +text-transform: uppercase; +color: #8ea4c7; +letter-spacing: 0.12em; +padding: 12px 14px 4px; +``` +- **Assessment**: ✅ Good hierarchy + - Small, uppercase, muted color clearly distinguishes from regular nav items + - Letter spacing adds visual interest + - Adequate padding separates sections + +### Sidebar Footer (`.sidebar-footer`) +```css +border-top: 1px solid var(--color-border); +padding: 16px; +background-color: rgba(59, 130, 246, 0.05); /* Slight blue tint */ +``` +- **Assessment**: ✅ Good + - Separated with border + - Subtle background color differentiates from main nav + - 16px padding consistent with other components + +--- + +## 5. RESPONSIVE BEHAVIOR + +### Mobile Override (@media max-width: 768px) +```css +.ots-sidebar { + transform: translateX(-100%); + transition: transform var(--transition-base); + width: 280px; +} +.ots-sidebar.active { + transform: translateX(0); + box-shadow: 2px 0 8px rgba(0, 0, 0, 0.3); +} +.ots-main { + margin-left: 0; +} +``` +- **Assessment**: ✅ Good mobile experience + - Slides in from left (drawer pattern) + - Shadow adds depth when open + - Content isn't pushed aside (overlay instead) + - **Issue**: Sidebar width changes from 256px → 280px on mobile (should be 280px, maybe even narrower for mobile) + +--- + +## 6. COLLAPSED STATE STYLING + +### Collapsed Class +```css +.ots-sidebar.collapsed { + /* No explicit width override - uses CSS variable */ +} +``` +- **Assessment**: ⚠️ Lacks dedicated styling + - When `.collapsed` is added, only width changes (via CSS variable) + - No visual indicator (badge, animation) that it's collapsed + - Icon-only display works but lacks affordance + +### Collapsed Item Styling (`.sidebar-collapsed-item-bg`) +```css +--ots-sidebar-collapsed-item-bg: rgba(255, 255, 255, 0.08); +--ots-sidebar-collapsed-item-hover-bg: rgba(255, 255, 255, 0.16); +``` +- **Assessment**: ⚠️ Variables defined but not actively used in CSS rules + - These variables exist but I don't see them applied to `.collapsed` state styling + - **Action Item**: Verify if collapsed items have proper styling + +--- + +## 7. KEY ISSUES & RECOMMENDATIONS + +### 🔴 HIGH PRIORITY + +1. **Icon/Grid Mismatch** + - Icon container: 24px, but grid column: 20px + - **Fix**: Change `grid-template-columns: 20px 1fr` → `grid-template-columns: 24px 1fr` + +2. **Excessive Nav Padding** + - `.sidebar-nav { padding: 72px 0 120px }` is too much + - **Fix**: Use dynamic sizing based on header height (JavaScript or CSS custom property) + - **Suggestion**: `padding: var(--ots-sidebar-header-height) 0 60px` (60px is enough for footer breathing room) + +3. **Collapsed State Styling** + - Collapsed items have CSS variables defined but not used + - **Fix**: Add active rules like: + ```css + .ots-sidebar.collapsed li.sidebar-list.active > a { + background-color: rgba(255, 255, 255, 0.12); + } + ``` + +### 🟡 MEDIUM PRIORITY + +4. **Icon Discoverability When Collapsed** + - No tooltips or labels appear when sidebar is collapsed + - **Suggestion**: Add `title` attributes to nav items or implement CSS tooltips + +5. **Sidebar Header Button Spacing** + - Header has "toggle" button but spacing could be tighter on mobile + - **Suggestion**: When collapsed, header could be more compact + +6. **Mobile Width Inconsistency** + - Sidebar is 256px full-width but 280px on mobile (why wider?) + - **Suggestion**: Keep consistent at 256px or make it responsive (e.g., 90vw max 280px on small phones) + +### 🟢 LOW PRIORITY + +7. **Brand Icon & Text** + - Logo + text look good but could use more breathing room + - **Current**: `gap: 12px` - consider `gap: 16px` for more visual separation + +--- + +## 8. VISUAL SPACING SUMMARY + +| Component | Padding | Margin | Assessment | +|-----------|---------|--------|------------| +| Header | 20px V, 16px H | — | ✅ Good | +| Content Wrapper | 12px V, 0 H | — | ⚠️ Inconsistent | +| Nav List | 72px T, 120px B | — | 🔴 Excessive | +| Nav Items | 6px V, 10px H | 3px V, 10px H | ✅ Good | +| Section Headers | 12px T, 14px H | — | ✅ Good | +| Footer | 16px | — | ✅ Consistent | + +--- + +## 9. ICON SIZING CONSISTENCY + +| Element | Width | Height | Font Size | Usage | +|---------|-------|--------|-----------|-------| +| Nav Icon | 24px | 24px | 16px | Primary nav items | +| Brand Icon | 32px | 32px | 24px | Logo in header | +| Topbar Icon | 20px | 20px | 16px | Topbar controls | + +**Assessment**: ✅ Good hierarchy and clarity + +--- + +## 10. RECOMMENDATIONS FOR NEXT PHASE + +1. ✅ Fix grid column width mismatch (20px → 24px) +2. ✅ Refactor `.sidebar-nav` padding (use CSS variables or dynamic) +3. ✅ Add collapsed state active item styling +4. ✅ Add `title` attributes to nav items for tooltip support +5. ⚠️ Consider adding a visual "collapse indicator" (e.g., chevron or light pulsing border) +6. ⚠️ Standardize mobile sidebar width +7. ⚠️ Add more breathing room in header (gap: 16px instead of 12px for brand icon) + +--- + +## Summary + +**Overall Grade: B+ (85/100)** + +### Strengths: +- ✅ Modern, cohesive dark-mode aesthetic +- ✅ Good color contrast and accessibility +- ✅ Proper icon sizing and centering +- ✅ Responsive mobile overlay pattern +- ✅ Clear visual hierarchy (headers, active states, hover) + +### Weaknesses: +- ⚠️ Excessive bottom padding in nav list +- ⚠️ Grid icon column width mismatch +- ⚠️ Lacking visual affordance when collapsed +- ⚠️ Icon discoverability issues + +### Quick Wins (implement first): +1. Change `grid-template-columns: 20px 1fr` → `24px 1fr` +2. Change `.sidebar-nav padding: 72px 0 120px` → `64px 0 60px` +3. Add collapsed state styling for active items + diff --git a/custom/otssignange/css/client.css b/custom/otssignange/css/client.css index a669ac6..8ea04af 100644 --- a/custom/otssignange/css/client.css +++ b/custom/otssignange/css/client.css @@ -283,3 +283,19 @@ img { outline: 2px solid var(--color-primary); outline-offset: 2px; } + +/* Table search input sizing moved to CSS for responsive control */ +.table-search-input { + min-width: 11.25rem; /* 180px */ + padding: 0.5rem 0.75rem; + border-radius: 0.375rem; + border: 1px solid var(--color-border); +} + +@media (max-width: 600px) { + .table-search-input { + min-width: auto; + width: 100%; + box-sizing: border-box; + } +} diff --git a/custom/otssignange/css/override.css b/custom/otssignange/css/override.css index 4ad369a..146aa54 100644 --- a/custom/otssignange/css/override.css +++ b/custom/otssignange/css/override.css @@ -60,6 +60,8 @@ html, body { background-color: var(--color-background); color: var(--color-text-primary); + overflow-x: hidden !important; + max-width: 100vw !important; } /* Light/dark mode toggle */ @@ -193,10 +195,12 @@ body.ots-light-mode .sidebar-header { } body.ots-light-mode .ots-topbar { - background-color: #ffffff; - border-bottom: 1px solid #e2e8f0; + background-color: transparent; + border-bottom: none; } +/* (topbar strip is now hidden — light-mode overrides removed) */ + body.ots-light-mode .ots-topbar .nav-link { color: #334155; } @@ -297,37 +301,86 @@ html, body, #page-wrapper, .ots-main, .ots-content, .page-content { flex-direction: column; overflow: hidden; z-index: 1200; + transition: width 200ms ease; } .ots-main { flex: 1; display: flex; flex-direction: column; - margin-left: var(--ots-sidebar-width) !important; -} - -.ots-main, -#page-wrapper { position: relative; margin-left: var(--ots-sidebar-width) !important; - transition: margin-left 150ms ease-out; + width: calc(100vw - var(--ots-sidebar-width)) !important; + max-width: calc(100vw - var(--ots-sidebar-width)) !important; + transition: margin-left 200ms ease, width 200ms ease, max-width 200ms ease; + overflow-x: hidden !important; + box-sizing: border-box !important; +} + +#page-wrapper { + position: relative; + margin-left: 0 !important; + padding-left: 0 !important; + border: none !important; + box-shadow: none !important; + background-color: var(--color-background) !important; + max-width: 100vw !important; + overflow-x: hidden !important; + box-sizing: border-box !important; +} + +#page-wrapper.active { + padding-left: 0 !important; + margin-left: 0 !important; +} + +#content-wrapper { + margin-left: var(--ots-sidebar-width) !important; + width: calc(100vw - var(--ots-sidebar-width)) !important; + max-width: calc(100vw - var(--ots-sidebar-width)) !important; + transition: margin-left 200ms ease, width 200ms ease, max-width 200ms ease; + padding-left: 16px !important; + padding-right: 16px !important; + border-left: none !important; + box-shadow: none !important; + outline: none !important; + overflow-x: hidden !important; + box-sizing: border-box !important; +} + +/* Collapsed: shift content to match the narrow sidebar */ +body.ots-sidebar-collapsed .ots-main, +body.ots-sidebar-collapsed #content-wrapper, +html.ots-sidebar-collapsed .ots-main, +html.ots-sidebar-collapsed #content-wrapper { + margin-left: var(--ots-sidebar-collapsed-width) !important; + width: calc(100vw - var(--ots-sidebar-collapsed-width)) !important; + max-width: calc(100vw - var(--ots-sidebar-collapsed-width)) !important; } .ots-content { flex: 1; - padding: 32px 32px 32px 0; + padding: 32px 16px 32px 0; overflow-y: auto; + overflow-x: hidden !important; + max-width: 100% !important; + box-sizing: border-box !important; } /* Remove left padding between sidebar and page content */ .page-content { padding-left: 0 !important; + max-width: 100% !important; + overflow-x: hidden !important; + box-sizing: border-box !important; } /* Remove bootstrap gutters for main content so it sits flush against sidebar */ .ots-main .page-content .row { margin-left: 0 !important; margin-right: 0 !important; + max-width: 100% !important; + overflow-x: hidden !important; } .ots-main .page-content [class*="col-"] { @@ -341,8 +394,7 @@ html, body, #page-wrapper, .ots-main, .ots-content, .page-content { padding-left: 0 !important; } -/* Strongly enforce no left gap between sidebar and content for all layouts */ -#content-wrapper, +/* Strongly enforce no left gap for content children (not #content-wrapper itself) */ #content-wrapper .page-content, #content-wrapper .page-content .row, #content-wrapper .page-content [class*="col-"] { @@ -351,35 +403,173 @@ html, body, #page-wrapper, .ots-main, .ots-content, .page-content { } /* Ensure page header and meta area align flush */ -.page .meta, -.page-header, -.header-side { +.page-header { margin-left: 0 !important; padding-left: 0 !important; } -/* When the sidebar is expanded (not collapsed) make the page header clear and hide the header logo */ -.ots-sidebar:not(.collapsed) ~ .ots-main .page .meta, -.ots-sidebar:not(.collapsed) ~ .ots-main .page-header, -.ots-sidebar:not(.collapsed) ~ .ots-main .header-side, -.ots-sidebar:not(.collapsed) + .ots-main .page .meta, -.ots-sidebar:not(.collapsed) + .ots-main .page-header, -body.ots-sidebar-open .page .meta, -body.ots-sidebar-open .page-header, -body.ots-sidebar-open .header-side { - background: transparent !important; +/* Prevent XiboGrid, folder grids, and data containers from causing page overflow */ +.XiboGrid, +.grid-with-folders-container, +.XiboData, +#datatable-container { + max-width: 100% !important; + overflow-x: auto !important; + box-sizing: border-box !important; +} + +/* ============================================================================ + TOPBAR STRIP — HIDDEN + The old full-width topbar is no longer shown. The notification bell and + user menu now live in .ots-page-actions (fixed top-right on every page). + ============================================================================ */ +.row.header.header-side, +.ots-topbar-strip { + display: none !important; + height: 0 !important; + margin: 0 !important; + padding: 0 !important; + overflow: hidden !important; + border: none !important; box-shadow: none !important; } -.ots-sidebar:not(.collapsed) ~ .ots-main .page .xibo-logo, -.ots-sidebar:not(.collapsed) ~ .ots-main .xibo-logo, -.ots-sidebar:not(.collapsed) + .ots-main .page .xibo-logo, -.ots-sidebar:not(.collapsed) + .ots-main .xibo-logo, -.ots-sidebar:not(.collapsed) ~ .ots-main .xibo-logo-container, -.ots-sidebar:not(.collapsed) + .ots-main .xibo-logo-container, -body.ots-sidebar-open .xibo-logo, -body.ots-sidebar-open .xibo-logo-container { - display: none !important; +/* ============================================================================ + PAGE ACTIONS — fixed top-right cluster (bell + user icon) + ============================================================================ */ +.ots-page-actions { + position: fixed; + top: 12px; + right: 20px; + z-index: 1100; + display: flex; + align-items: center; + gap: 4px; + background: var(--color-surface-elevated); + border: 1px solid var(--color-border); + border-radius: 12px; + padding: 4px 6px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.10); +} + +/* Each action inside the cluster — NO position:relative so dropdowns + anchor to .ots-page-actions (position:fixed) instead */ +.ots-page-actions .ots-topbar-action { + display: inline-flex !important; + align-items: center !important; + position: static !important; +} + +.ots-page-actions .ots-topbar-action .nav-item, +.ots-page-actions .ots-topbar-action .dropdown, +.ots-page-actions .ots-topbar-action > div { + display: inline-flex !important; + align-items: center !important; + position: static !important; +} + +.ots-page-actions .ots-topbar-action .nav-link { + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + width: 36px !important; + height: 36px !important; + padding: 0 !important; + border-radius: 8px !important; + color: var(--color-text-secondary) !important; + transition: all 150ms ease !important; + border: none !important; + background: transparent !important; +} + +.ots-page-actions .ots-topbar-action .nav-link:hover { + background-color: rgba(59, 130, 246, 0.08) !important; + color: var(--color-primary) !important; +} + +.ots-page-actions .ots-topbar-action .ots-topbar-icon { + display: flex !important; + align-items: center !important; + justify-content: center !important; + width: 18px !important; + height: 18px !important; + font-size: 16px !important; +} + +.ots-page-actions .ots-topbar-action img.nav-avatar { + display: block !important; + width: 28px !important; + height: 28px !important; + border-radius: 50% !important; + object-fit: cover !important; + border: 2px solid transparent !important; + transition: border-color 150ms ease !important; +} + +.ots-page-actions .ots-topbar-action .nav-link:hover img.nav-avatar { + border-color: var(--color-primary) !important; +} + +/* Dropdown menus from the page actions cluster */ +.ots-page-actions .dropdown-menu { + position: absolute !important; + top: 100% !important; + right: 0 !important; + left: auto !important; + margin-top: 0 !important; + 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; + z-index: 3000 !important; + overflow: visible !important; +} + +/* Bridge the gap between the pill and dropdown so it looks connected */ +.ots-page-actions .dropdown-menu::before { + content: '' !important; + display: block !important; + position: absolute !important; + top: -4px !important; + right: 8px !important; + width: 48px !important; + height: 4px !important; + background: transparent !important; +} + +.ots-page-actions .dropdown-item, +.ots-page-actions .dropdown-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-page-actions .dropdown-item:hover, +.ots-page-actions .dropdown-menu a:hover { + background-color: rgba(59, 130, 246, 0.08) !important; + color: var(--color-primary) !important; +} + +/* Light mode adjustments for the actions cluster */ +body.ots-light-mode .ots-page-actions { + background: #ffffff; + border-color: #e2e8f0; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); +} + +.page-header { + background: transparent !important; + box-shadow: none !important; } .ots-footer { @@ -403,7 +593,7 @@ body.ots-sidebar-open .xibo-logo-container { } .ots-main { - margin-left: 0; + margin-left: 0 !important; } .ots-content { @@ -411,6 +601,19 @@ body.ots-sidebar-open .xibo-logo-container { } } +/* Responsive helpers for paged selects and other inline-width controls */ +.pagedSelect { + min-width: 12.5rem; /* 200px */ +} + +@media (max-width: 600px) { + .pagedSelect { + min-width: auto; + width: 100%; + box-sizing: border-box; + } +} + /* ============================================================================ SIDEBAR STYLES ============================================================================ */ @@ -677,12 +880,34 @@ body.ots-sidebar-open .xibo-logo-container { .ots-sidebar.collapsed { width: var(--ots-sidebar-collapsed-width) !important; + overflow: visible !important; /* allow popout menus to escape */ +} + +/* Ensure parent wrapper doesn't clip flyout menus and doesn't create a visible bar. + The sidebar is position:fixed so this wrapper should be invisible in the flow. + Using display:contents so the sidebar inside still renders but the wrapper + itself produces no box at all — eliminating the black bar. */ +.navbar-collapse-side, +.navbar-collapse-side.collapse, +#navbar-collapse-1 { + display: contents !important; + overflow: visible !important; + width: 0 !important; + min-width: 0 !important; + max-width: 0 !important; + height: 0 !important; + max-height: 0 !important; + padding: 0 !important; + margin: 0 !important; + border: none !important; + background: transparent !important; + box-shadow: none !important; + outline: none !important; } .ots-sidebar.collapsed .brand-text, .ots-sidebar.collapsed .sidebar-group-toggle .ots-nav-text, .ots-sidebar.collapsed .sidebar-list .ots-nav-text, -.ots-sidebar.collapsed .sidebar-submenu, .ots-sidebar.collapsed .sidebar-group-caret, .ots-sidebar.collapsed .user-details, .ots-sidebar.collapsed .sidebar-header .sidebar-collapse-btn, @@ -690,6 +915,127 @@ body.ots-sidebar-open .xibo-logo-container { display: none !important; } +/* Collapsed: hide submenus by default, show on hover as flyout */ +.ots-sidebar.collapsed .sidebar-submenu { + display: none !important; + position: absolute !important; + left: 100% !important; + top: 0 !important; + min-width: 200px !important; + background: var(--color-surface-elevated, #334155) !important; + border: 1px solid var(--color-border, #475569) !important; + border-radius: 8px !important; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3) !important; + padding: 8px 0 !important; + margin-left: 4px !important; + z-index: 9999 !important; + list-style: none !important; +} + +/* Flyout: show submenu on hover over the group */ +.ots-sidebar.collapsed .sidebar-group:hover > .sidebar-submenu, +.ots-sidebar.collapsed .sidebar-group.flyout-open > .sidebar-submenu { + display: block !important; +} + +/* Flyout submenu items */ +.ots-sidebar.collapsed .sidebar-submenu .sidebar-list > a { + display: flex !important; + align-items: center !important; + justify-content: flex-start !important; + gap: 10px !important; + padding: 8px 16px !important; + margin: 2px 6px !important; + height: auto !important; + width: auto !important; + background: transparent !important; + color: var(--color-text-secondary, #f1f5f9) !important; + border-radius: 6px !important; + font-size: 13px !important; + white-space: nowrap !important; +} + +.ots-sidebar.collapsed .sidebar-submenu .sidebar-list > a:hover { + background: rgba(59, 130, 246, 0.12) !important; + color: #ffffff !important; +} + +/* Show text labels inside flyout submenu */ +.ots-sidebar.collapsed .sidebar-submenu .ots-nav-text { + display: inline !important; + visibility: visible !important; + width: auto !important; + overflow: visible !important; +} + +/* Flyout icon sizing */ +.ots-sidebar.collapsed .sidebar-submenu .ots-nav-icon { + width: 16px !important; + height: 16px !important; + font-size: 14px !important; +} + +/* Make sidebar-group position relative so flyout anchors correctly */ +.ots-sidebar.collapsed .sidebar-group { + position: relative !important; +} + +/* Also show a label tooltip on hover for top-level items */ +.ots-sidebar.collapsed .sidebar-group-toggle::after { + content: attr(data-group); + position: absolute; + left: 100%; + top: 50%; + transform: translateY(-50%); + background: var(--color-surface-elevated, #334155); + color: var(--color-text-primary, #fff); + padding: 4px 12px; + border-radius: 6px; + font-size: 13px; + font-weight: 500; + white-space: nowrap; + margin-left: 8px; + pointer-events: none; + opacity: 0; + transition: opacity 0.15s; + z-index: 9999; + box-shadow: 0 4px 12px rgba(0,0,0,0.2); +} + +.ots-sidebar.collapsed .sidebar-group:hover > .sidebar-group-toggle::after { + display: none; /* hide tooltip when submenu flyout is showing instead */ +} + +/* Single items (Dashboard etc) - show tooltip on hover */ +.ots-sidebar.collapsed > .sidebar-content .sidebar-nav > .sidebar-list > a { + position: relative !important; +} + +.ots-sidebar.collapsed > .sidebar-content .sidebar-nav > .sidebar-list > a::after { + content: attr(data-tooltip); + position: absolute; + left: 100%; + top: 50%; + transform: translateY(-50%); + background: var(--color-surface-elevated, #334155); + color: var(--color-text-primary, #fff); + padding: 4px 12px; + border-radius: 6px; + font-size: 13px; + font-weight: 500; + white-space: nowrap; + margin-left: 8px; + pointer-events: none; + opacity: 0; + transition: opacity 0.15s; + z-index: 9999; + box-shadow: 0 4px 12px rgba(0,0,0,0.2); +} + +.ots-sidebar.collapsed > .sidebar-content .sidebar-nav > .sidebar-list:hover > a::after { + opacity: 1; +} + .ots-sidebar.collapsed .sidebar-header { height: auto; padding: 8px; @@ -742,7 +1088,11 @@ body.ots-sidebar-open .xibo-logo-container { display: none !important; } -/* Center icons when collapsed */ +/* Center icons when collapsed — triple-class + ID selector to beat expanded-state + rules like #sidebar-wrapper .sidebar-list .ots-nav-icon { margin-right:8px } + Specificity: (1,3,0) beats (1,2,0) */ +#sidebar-wrapper.ots-sidebar.collapsed .ots-nav-icon, +#navbar-collapse-1 .ots-sidebar.collapsed .ots-nav-icon, .ots-sidebar.collapsed .ots-nav-icon { display: flex !important; width: 20px !important; @@ -751,7 +1101,9 @@ body.ots-sidebar-open .xibo-logo-container { justify-content: center !important; flex-shrink: 0 !important; font-size: 20px !important; - margin: 0 !important; + margin: 0 auto !important; + margin-left: auto !important; + margin-right: auto !important; padding: 0 !important; color: var(--ots-sidebar-link) !important; visibility: visible !important; @@ -866,9 +1218,12 @@ body.ots-light-mode .ots-sidebar.collapsed .ots-nav-icon { display: none !important; } -.ots-sidebar-collapsed #page-wrapper, -.ots-sidebar-collapsed .ots-main { +body.ots-sidebar-collapsed #content-wrapper, +body.ots-sidebar-collapsed .ots-main, +html.ots-sidebar-collapsed #content-wrapper, +html.ots-sidebar-collapsed .ots-main { margin-left: var(--ots-sidebar-collapsed-width) !important; + transition: margin-left 200ms ease; } .ots-sidebar-nav { @@ -878,14 +1233,11 @@ body.ots-light-mode .ots-sidebar.collapsed .ots-nav-icon { /* When collapsed, add extra top padding so menu items sit below the header (logo + buttons) */ .ots-sidebar.collapsed .ots-sidebar-nav, -.ots-sidebar.collapsed .ots-sidebar-nav, -.ots-sidebar-collapsed .ots-sidebar-nav { +body.ots-sidebar-collapsed .ots-sidebar-nav { padding-top: 72px !important; } -/* Also ensure expanded sidebar has sufficient top padding below header/logo */ -.ots-sidebar:not(.collapsed) .ots-sidebar-nav, -.ots-sidebar-collapsed:not(.collapsed) .ots-sidebar-nav { +.ots-sidebar:not(.collapsed) .ots-sidebar-nav { padding-top: 72px !important; } @@ -1206,28 +1558,28 @@ body.ots-light-mode .ots-sidebar.collapsed .ots-nav-icon { } /* ============================================================================ - TOPBAR STYLES - DEEP ANALYSIS & MODERN OVERHAUL + TOPBAR STYLES - CLEAN MODERN SINGLE LINE MENU BAR ============================================================================ Design decisions: - - 64px total height (44px content + 10px top/bottom padding) = consistent baseline - - All interactive elements are 40px (buttons, search height) - - Icon sizing: 20px container, 16px icon (scale-friendly) - - Consistent 16px gap spacing throughout - - Remove conflicting .navbar-nav rules + - 56px total height — compact, modern feel + - All interactive elements are 36px (buttons, avatar, icons) + - Consistent 4px gap between nav items + - Clean border-bottom separator, no heavy shadows - Proper z-index layering for dropdowns ============================================================================ */ .ots-topbar { - background-color: var(--color-surface-elevated); - padding: 10px 32px; + background-color: transparent; + padding: 0; display: flex; align-items: center; - justify-content: space-between; - gap: 16px; - height: 64px; + justify-content: flex-start; + gap: 4px; + height: 56px; z-index: 1000; - position: sticky; - top: 0; + position: relative; + flex: 1; + min-width: 0; } body.ots-sidebar-open .ots-topbar { @@ -1241,9 +1593,10 @@ body.ots-sidebar-open .ots-topbar { border: 0 !important; padding: 0 !important; margin: 0 !important; - gap: 4px; + gap: 2px; height: 100%; align-items: center; + flex-wrap: nowrap; } .ots-topbar .nav-item { @@ -1257,15 +1610,15 @@ body.ots-sidebar-open .ots-topbar { .ots-topbar .nav-link { display: inline-flex; align-items: center; - height: 100%; + height: 36px; gap: 6px; - padding: 0 12px; + padding: 0 10px; border-radius: 6px; color: var(--color-text-secondary); font-weight: 500; - font-size: 14px; + font-size: 13px; white-space: nowrap; - transition: all var(--transition-fast); + transition: all 150ms ease; position: relative; border: 0 !important; background: transparent !important; @@ -1292,7 +1645,7 @@ body.ots-sidebar-open .ots-topbar { .ots-topbar .nav-item.open .nav-link, .ots-topbar .nav-item.active .nav-link { - background-color: rgba(59, 130, 246, 0.12); + background-color: rgba(59, 130, 246, 0.1); color: var(--color-primary); font-weight: 600; } @@ -1300,14 +1653,14 @@ body.ots-sidebar-open .ots-topbar { .ots-topbar .dropdown-toggle::after { content: ''; display: inline-block; - margin-left: 6px; + margin-left: 4px; width: 0; height: 0; - border-left: 4px solid transparent; - border-right: 4px solid transparent; - border-top: 4px solid currentColor; - opacity: 0.6; - transition: transform var(--transition-fast); + border-left: 3.5px solid transparent; + border-right: 3.5px solid transparent; + border-top: 3.5px solid currentColor; + opacity: 0.5; + transition: transform 150ms ease; } .ots-topbar .nav-item.open .dropdown-toggle::after { @@ -1315,14 +1668,14 @@ body.ots-sidebar-open .ots-topbar { } .ots-topbar .dropdown-menu { - border-radius: 8px; + border-radius: 10px; padding: 6px 0; margin-top: 4px; - box-shadow: none; - border: 0 !important; - background-color: var(--color-surface); + box-shadow: 0 12px 36px rgba(0, 0, 0, 0.25); + border: 1px solid var(--color-border) !important; + background-color: var(--color-surface-elevated); min-width: 180px; - z-index: 1100; + z-index: 3000; } .ots-topbar .dropdown-item, @@ -1330,35 +1683,35 @@ body.ots-sidebar-open .ots-topbar { display: flex; align-items: center; gap: 8px; - border-radius: 4px; - padding: 8px 14px; + border-radius: 6px; + padding: 8px 12px; color: var(--color-text-secondary); - font-size: 14px; + font-size: 13px; font-weight: 500; - margin: 2px 6px; - transition: all var(--transition-fast); + margin: 1px 6px; + transition: all 150ms ease; } .ots-topbar .dropdown-item:hover, .ots-topbar .dropdown-menu a:hover { - background-color: rgba(59, 130, 246, 0.1); + background-color: rgba(59, 130, 246, 0.08); color: var(--color-primary); } /* Icon sizing for topbar and nav */ .ots-topbar-icon { - width: 20px; - height: 20px; + width: 16px; + height: 16px; display: flex; align-items: center; justify-content: center; - font-size: 14px; - opacity: 0.9; + font-size: 13px; + opacity: 0.85; flex-shrink: 0; } .ots-topbar .dropdown-menu .ots-topbar-icon { - font-size: 13px; + font-size: 12px; } /* Left section: toggle, title, nav */ @@ -1372,8 +1725,8 @@ body.ots-sidebar-open .ots-topbar { .topbar-toggle { display: none; - width: 40px; - height: 40px; + width: 36px; + height: 36px; padding: 0; border: none; background-color: transparent; @@ -1440,11 +1793,11 @@ body.ots-sidebar-open .ots-topbar { flex-shrink: 0; } -/* Search box - 40px height to match button grid */ +/* Search box - 36px height to match button grid */ .topbar-search { position: relative; - height: 40px; - width: 280px; + height: 36px; + width: 240px; display: flex; align-items: center; background-color: var(--color-surface); @@ -1494,12 +1847,12 @@ body.ots-sidebar-open .ots-topbar { gap: 4px; } -/* Consistent 40px button sizing */ +/* Consistent 36px button sizing */ .topbar-btn { - width: 40px; - height: 40px; - min-width: 40px; - min-height: 40px; + width: 36px; + height: 36px; + min-width: 36px; + min-height: 36px; padding: 0; border: none; background-color: transparent; @@ -1761,6 +2114,7 @@ body.ots-sidebar-open .ots-topbar { width: 100%; min-width: 0; flex: 1; + overflow: visible; } .kpi-card--modern { @@ -1835,6 +2189,7 @@ body.ots-sidebar-open .ots-topbar { .page-header { margin-bottom: 16px; + margin-left: 0 !important; } .page-header h1 { @@ -2412,6 +2767,7 @@ body .panel .panel-heading, display: flex; flex-direction: column; min-width: 0; + overflow: visible !important; } .ots-displays-body { @@ -2419,6 +2775,7 @@ body .panel .panel-heading, min-width: 0; display: flex; flex-direction: column; + overflow: visible !important; } .ots-displays-body .XiboGrid { @@ -2426,6 +2783,7 @@ body .panel .panel-heading, min-width: 0; display: flex; flex-direction: column; + overflow: visible !important; } .ots-displays-title { @@ -2804,6 +3162,92 @@ body .panel .panel-heading, color: #ffffff !important; } +/* === Row dropdown menus (context menus on table rows) === + When opened, these get moved to by JS so they escape all + overflow:hidden containers. Styled here for both in-situ and + body-appended states. */ +body > .dropdown-menu.ots-row-dropdown { + position: fixed !important; + z-index: 1199 !important; + display: block !important; + background-color: var(--color-surface-elevated) !important; + border: 1px solid var(--color-border) !important; + border-radius: 8px !important; + box-shadow: 0 12px 32px rgba(0, 0, 0, 0.4) !important; + padding: 6px 0 !important; + min-width: 180px !important; + max-height: 80vh; + overflow-y: auto; +} + +body > .dropdown-menu.ots-row-dropdown a, +body > .dropdown-menu.ots-row-dropdown .dropdown-item { + color: var(--color-text-primary) !important; + padding: 8px 16px !important; + font-size: 13px !important; + display: block !important; + white-space: nowrap !important; + text-decoration: none !important; + transition: background 120ms ease !important; +} + +body > .dropdown-menu.ots-row-dropdown a:hover, +body > .dropdown-menu.ots-row-dropdown .dropdown-item:hover { + background-color: rgba(59, 130, 246, 0.12) !important; + color: var(--color-primary-light) !important; +} + +body > .dropdown-menu.ots-row-dropdown .divider, +body > .dropdown-menu.ots-row-dropdown .dropdown-divider { + border-top: 1px solid var(--color-border) !important; + margin: 4px 0 !important; +} + +/* Table toolbar — action buttons row inside the table card */ +.ots-table-toolbar { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 16px 12px; + flex-wrap: wrap; +} + +.ots-table-toolbar .btn { + display: inline-flex !important; + align-items: center !important; + gap: 6px !important; + font-size: 13px !important; + font-weight: 600 !important; + padding: 6px 14px !important; + border-radius: 8px !important; + white-space: nowrap !important; + line-height: 1.4 !important; +} + +.ots-table-toolbar .btn i, +.ots-table-toolbar .btn .fa { + font-size: 14px !important; +} + +/* Light mode toolbar buttons */ +body.ots-light-mode .ots-table-toolbar .btn-success { + background-color: #10b981 !important; + border-color: #10b981 !important; + color: #ffffff !important; +} + +body.ots-light-mode .ots-table-toolbar .btn-warning { + background-color: #f59e0b !important; + border-color: #f59e0b !important; + color: #ffffff !important; +} + +body.ots-light-mode .ots-table-toolbar .btn-primary { + background-color: #3b82f6 !important; + border-color: #3b82f6 !important; + color: #ffffff !important; +} + .ots-table-card .dataTables_wrapper, .ots-table-card .dataTables_wrapper * { color: #e2e8f0 !important; @@ -3635,6 +4079,18 @@ canvas { } .modern-table-card { padding: 12px; + + /* Backdrop shown when sidebar is active on small screens */ + .ots-backdrop { + display: none; + position: fixed; + inset: 0; + background: rgba(0,0,0,0.45); + z-index: 1040; + } + .ots-backdrop.show { + display: block; + } } } @@ -3697,6 +4153,20 @@ textarea { .dataTables_wrapper * { color: var(--color-text-primary) !important; } + +/* Ensure tables scroll within their container, not the page */ +.dataTables_wrapper { + max-width: 100% !important; + overflow-x: auto !important; + box-sizing: border-box !important; +} + +table.dataTable, +.XiboData table, +.ots-table-card table { + max-width: 100% !important; + table-layout: auto !important; +} .dataTables_wrapper .dataTables_info, .dataTables_wrapper .dataTables_paginate { color: var(--color-text-primary) !important; @@ -3796,6 +4266,19 @@ h1, h2, h3, h4, h5, h6, vertical-align: middle !important; } +/* Override: when collapsed, center icons — must follow expanded rules above */ +#sidebar-wrapper.ots-sidebar.collapsed .ots-nav-icon, +#sidebar-wrapper.collapsed .sidebar-list .ots-nav-icon, +#sidebar-wrapper.collapsed .sidebar-main .ots-nav-icon { + display: flex !important; + width: 20px !important; + height: 20px !important; + margin: 0 auto !important; + margin-left: auto !important; + margin-right: auto !important; + text-align: center !important; +} + .ots-sidebar .ots-nav-text, #sidebar-wrapper .sidebar-list .ots-nav-text { display: inline-block !important; @@ -4219,9 +4702,10 @@ a.text-muted:hover { .navbar-brand.xibo-logo-container { display: flex; align-items: center; - gap: 12px; + gap: 10px; padding: 0; - margin-right: 16px; + margin-right: 12px; + height: 56px; } .xibo-logo-text { @@ -4401,24 +4885,26 @@ body, color: var(--color-text-primary) !important; } -/* Reduce horizontal gap between sidebar and page content. - Keep content flush with the sidebar by matching the sidebar width. */ +/* Content area tracks the sidebar width. + The expanded value comes from --ots-sidebar-width (256px) set in :root. + The collapsed value comes from body.ots-sidebar-collapsed overrides. */ +#content-wrapper, .ots-main { margin-left: var(--ots-sidebar-width) !important; + width: calc(100vw - var(--ots-sidebar-width)) !important; + max-width: calc(100vw - var(--ots-sidebar-width)) !important; + transition: margin-left 200ms ease, width 200ms ease, max-width 200ms ease; + overflow-x: hidden !important; + box-sizing: border-box !important; } -.ots-main, -#page-wrapper, -#content-wrapper, -.ots-main #content-wrapper { - margin-left: var(--ots-sidebar-width) !important; -} - -/* When sidebar is collapsed, align content to the collapsed width */ -.ots-sidebar.collapsed ~ .ots-main, -.ots-sidebar.collapsed + .ots-main, -.ots-sidebar-collapsed .ots-main { +body.ots-sidebar-collapsed #content-wrapper, +body.ots-sidebar-collapsed .ots-main, +html.ots-sidebar-collapsed #content-wrapper, +html.ots-sidebar-collapsed .ots-main { margin-left: var(--ots-sidebar-collapsed-width) !important; + width: calc(100vw - var(--ots-sidebar-collapsed-width)) !important; + max-width: calc(100vw - var(--ots-sidebar-collapsed-width)) !important; } diff --git a/custom/otssignange/js/theme.js b/custom/otssignange/js/theme.js index 207cf06..b814092 100644 --- a/custom/otssignange/js/theme.js +++ b/custom/otssignange/js/theme.js @@ -31,11 +31,66 @@ }); } + // Mobile-aware toggle: add backdrop, aria-expanded, and focus management if (toggleBtn) { + let lastFocus = null; + function ensureBackdrop() { + let bd = document.querySelector('.ots-backdrop'); + if (!bd) { + bd = document.createElement('div'); + bd.className = 'ots-backdrop'; + bd.addEventListener('click', function() { + sidebar.classList.remove('active'); + bd.classList.remove('show'); + toggleBtn.setAttribute('aria-expanded', 'false'); + if (lastFocus) lastFocus.focus(); + }); + document.body.appendChild(bd); + } + return bd; + } + + toggleBtn.setAttribute('role', 'button'); + toggleBtn.setAttribute('aria-controls', 'ots-sidebar'); + toggleBtn.setAttribute('aria-expanded', 'false'); + toggleBtn.addEventListener('click', function(e) { e.preventDefault(); + const isNowActive = !sidebar.classList.contains('active'); sidebar.classList.toggle('active'); + // On small screens show backdrop and manage focus + if (window.innerWidth <= 768) { + const bd = ensureBackdrop(); + if (isNowActive) { + bd.classList.add('show'); + toggleBtn.setAttribute('aria-expanded', 'true'); + lastFocus = document.activeElement; + // move focus into the sidebar + const firstFocusable = sidebar.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'); + if (firstFocusable) firstFocusable.focus(); else sidebar.setAttribute('tabindex', '-1'), sidebar.focus(); + // add escape handler + document.addEventListener('keydown', escHandler); + } else { + bd.classList.remove('show'); + toggleBtn.setAttribute('aria-expanded', 'false'); + if (lastFocus) lastFocus.focus(); + document.removeEventListener('keydown', escHandler); + } + } }); + + function escHandler(e) { + if (e.key === 'Escape' || e.key === 'Esc') { + const bd = document.querySelector('.ots-backdrop'); + if (sidebar.classList.contains('active')) { + sidebar.classList.remove('active'); + if (bd) bd.classList.remove('show'); + toggleBtn.setAttribute('aria-expanded', 'false'); + if (lastFocus) lastFocus.focus(); + document.removeEventListener('keydown', escHandler); + } + } + } } if (collapseBtn) { @@ -43,6 +98,7 @@ if (isCollapsed) { sidebar.classList.add('collapsed'); body.classList.add('ots-sidebar-collapsed'); + document.documentElement.classList.add('ots-sidebar-collapsed'); updateSidebarStateClass(); } @@ -51,10 +107,8 @@ const nowCollapsed = !sidebar.classList.contains('collapsed'); sidebar.classList.toggle('collapsed'); body.classList.toggle('ots-sidebar-collapsed', nowCollapsed); + document.documentElement.classList.toggle('ots-sidebar-collapsed', nowCollapsed); localStorage.setItem(STORAGE_KEYS.sidebarCollapsed, nowCollapsed ? 'true' : 'false'); - // Update measured sidebar width when collapsed state changes - updateSidebarWidth(); - // Recalculate nav offset so items remain below header after collapse updateSidebarNavOffset(); updateSidebarStateClass(); }); @@ -65,9 +119,8 @@ e.preventDefault(); sidebar.classList.remove('collapsed'); body.classList.remove('ots-sidebar-collapsed'); + document.documentElement.classList.remove('ots-sidebar-collapsed'); localStorage.setItem(STORAGE_KEYS.sidebarCollapsed, 'false'); - updateSidebarWidth(); - // Recalculate nav offset after expanding updateSidebarNavOffset(); updateSidebarStateClass(); }); @@ -90,17 +143,16 @@ } /** - * Measure sidebar width and set CSS variable for layout + * Sidebar width is now handled purely by CSS classes (.ots-sidebar-collapsed). + * This function is kept as a no-op for backward compatibility. */ function updateSidebarWidth() { - const sidebar = document.querySelector('.ots-sidebar'); - if (!sidebar) return; - // If collapsed, use the known collapsed width; otherwise use measured width - const collapsed = sidebar.classList.contains('collapsed'); - const base = collapsed ? 64 : sidebar.offsetWidth || sidebar.getBoundingClientRect().width || 256; - const padding = 0; - const value = Math.max(64, Math.round(base + padding)); - document.documentElement.style.setProperty('--ots-sidebar-width', `${value}px`); + // No-op: CSS handles layout via body.ots-sidebar-collapsed class + if (window.__otsDebug) { + const sidebar = document.querySelector('.ots-sidebar'); + const collapsed = sidebar ? sidebar.classList.contains('collapsed') : false; + console.log('[OTS] updateSidebarWidth (no-op, CSS-driven)', { collapsed }); + } } /** diff --git a/custom/otssignange/views/applications-page.twig b/custom/otssignange/views/applications-page.twig new file mode 100644 index 0000000..efc5d8d --- /dev/null +++ b/custom/otssignange/views/applications-page.twig @@ -0,0 +1,268 @@ +{# +/* + * OTS Signs Theme - Applications Page + * Based on Xibo CMS applications-page.twig with OTS styling + */ +#} +{% extends "authed.twig" %} +{% import "inline.twig" as inline %} + +{% block title %}{{ "Applications"|trans }} | {% endblock %} + +{% block actionMenu %}{% endblock %} + +{% block pageContent %} +
+ + +
+
+
+
+
+

{% trans "Filter Applications" %}

+ +
+ +
+
+
+ + +
+ + + + + + + + + + + +
{% trans "Name" %}{% trans "Owner" %}
+
+
+
+
+
+
+
+ +
+ {% if theme.getThemeConfig("app_name") == "Xibo" %} +
+ Canva +
+
Canva
+

+ Publish your designs from Canva to Xibo at the push of a button. +
+
+ This connector is configured in Canva using the "Publish menu". +

+
+ +
+ {% endif %} +
+
+
+{% endblock %} + +{% block javaScript %} + + + {% for js in connectorJavaScript %} + {% include js ~ ".twig" %} + {% endfor %} +{% endblock %} + +{% block javaScriptTemplates %} + {{ parent() }} + + {% verbatim %} + + {% endverbatim %} +{% endblock %} diff --git a/custom/otssignange/views/authed-sidebar.twig b/custom/otssignange/views/authed-sidebar.twig index d249840..ab4cb87 100644 --- a/custom/otssignange/views/authed-sidebar.twig +++ b/custom/otssignange/views/authed-sidebar.twig @@ -22,7 +22,7 @@