diff --git a/ots-signs/css/override.css b/ots-signs/css/override.css index 48df6d6..8ef251c 100644 --- a/ots-signs/css/override.css +++ b/ots-signs/css/override.css @@ -8624,4 +8624,594 @@ select.form-control, .ots-stat-bar { grid-template-columns: 1fr; } -} \ No newline at end of file +} +/* ============================================================================ + LAYOUT DESIGNER — UX ENHANCEMENTS + Phase 4: Skeleton loading screen + canvas dot-grid + ============================================================================ */ + +/* --- Skeleton loading screen --------------------------------------------- */ + +#ots-editor-skeleton { + position: fixed; + inset: 0; + z-index: 9998; + display: flex; + flex-direction: column; + background: var(--editor-body-bg); + pointer-events: none; + transition: opacity 0.35s ease; +} + +#ots-editor-skeleton.ots-skeleton-done { + opacity: 0; +} + +/* Top bar strip */ +.ots-skeleton-topbar { + height: 46px; + background: var(--editor-modal-header-bg); + border-bottom: 1px solid var(--editor-border); + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 16px; +} + +.ots-skeleton-topbar-left, +.ots-skeleton-topbar-right { + display: flex; + align-items: center; + gap: 10px; +} + +/* Body row */ +.ots-skeleton-body { + flex: 1; + display: flex; + overflow: hidden; + min-height: 0; +} + +/* Left icon rail */ +.ots-skeleton-left-rail { + width: 52px; + background: var(--editor-toolbar-bg); + border-right: 1px solid var(--editor-border); + flex-shrink: 0; + display: flex; + flex-direction: column; + align-items: center; + padding: 16px 0; + gap: 14px; +} + +.ots-skeleton-icon-dot { + width: 28px; + height: 28px; + border-radius: 6px; + background: rgba(255,255,255,0.08); + animation: ots-shimmer 1.7s ease-in-out infinite; +} + +/* Canvas area with dot-grid */ +.ots-skeleton-canvas-area { + flex: 1; + background: var(--editor-body-bg); + background-image: radial-gradient(circle, var(--editor-border) 1px, transparent 1px); + background-size: 24px 24px; + display: flex; + align-items: center; + justify-content: center; +} + +/* The layout preview placeholder box */ +.ots-skeleton-canvas-box { + width: 62%; + max-width: 680px; + aspect-ratio: 16 / 9; + background: var(--editor-widget-bg); + border: 1px solid var(--editor-border); + border-radius: 4px; + animation: ots-shimmer 1.7s ease-in-out infinite; +} + +/* Properties panel */ +.ots-skeleton-props { + width: 272px; + min-width: 272px; + background: var(--editor-properties-bg); + border-left: 1px solid var(--editor-border); + padding: 20px 16px; + display: flex; + flex-direction: column; + gap: 20px; + flex-shrink: 0; + overflow: hidden; +} + +.ots-skeleton-prop-group { + display: flex; + flex-direction: column; + gap: 7px; +} + +.ots-skeleton-prop-label { + height: 10px; + width: 55%; + background: var(--editor-border); + border-radius: 3px; + animation: ots-shimmer 1.7s ease-in-out infinite; +} + +.ots-skeleton-prop-field { + height: 34px; + background: var(--editor-border); + border-radius: 5px; + animation: ots-shimmer 1.7s ease-in-out infinite 0.08s; +} + +.ots-skeleton-prop-swatch-row { + display: flex; + align-items: center; + gap: 8px; +} + +.ots-skeleton-prop-swatch { + width: 34px; + height: 34px; + min-width: 34px; + background: var(--editor-border); + border-radius: 5px; + animation: ots-shimmer 1.7s ease-in-out infinite; +} + +/* Bottom bar */ +.ots-skeleton-bottombar { + height: 44px; + background: var(--editor-footer-controls-bg); + border-top: 1px solid var(--editor-border); + flex-shrink: 0; + display: flex; + align-items: center; + padding: 0 16px; +} + +/* Inline chip (for topbar breadcrumb placeholders) */ +.ots-skeleton-chip { + height: 16px; + width: 100px; + background: rgba(255,255,255,0.10); + border-radius: 4px; + animation: ots-shimmer 1.7s ease-in-out infinite 0.12s; +} + +/* Shimmer keyframe */ +@keyframes ots-shimmer { + 0%, 100% { opacity: 0.35; } + 50% { opacity: 0.70; } +} + +/* --- Canvas dead-space dot-grid pattern ---------------------------------- */ + +/* Applied to the area that surrounds the actual layout preview viewport */ +#layout-editor #layout-viewer, +#layout-editor .main-panel-wrapper { + background-image: radial-gradient(circle, var(--editor-border) 1px, transparent 1px) !important; + background-size: 24px 24px !important; +} + + +/* ============================================================================ + LAYOUT DESIGNER — UX ENHANCEMENTS + Phase 5: Resize handles, add-region button, micro-transitions + ============================================================================ */ + +/* --- Region resize handles (jQuery UI) ----------------------------------- */ + +#layout-editor .designer-region .ui-resizable-handle, +#layout-editor .selected .ui-resizable-handle { + background-color: var(--color-primary) !important; + border-radius: 2px !important; + opacity: 0.45; + transition: opacity 0.15s ease !important; + z-index: 10 !important; +} + +#layout-editor .designer-region:hover .ui-resizable-handle, +#layout-editor .designer-region.selected .ui-resizable-handle { + opacity: 0.9 !important; +} + +#layout-editor .ui-resizable-se { cursor: se-resize !important; } +#layout-editor .ui-resizable-sw { cursor: sw-resize !important; } +#layout-editor .ui-resizable-ne { cursor: ne-resize !important; } +#layout-editor .ui-resizable-nw { cursor: nw-resize !important; } +#layout-editor .ui-resizable-n, +#layout-editor .ui-resizable-s { cursor: n-resize !important; } +#layout-editor .ui-resizable-e, +#layout-editor .ui-resizable-w { cursor: e-resize !important; } + +/* Corner handles: slightly larger for easier grab */ +#layout-editor .ui-resizable-se, +#layout-editor .ui-resizable-sw, +#layout-editor .ui-resizable-ne, +#layout-editor .ui-resizable-nw { + width: 10px !important; + height: 10px !important; + border-radius: 3px !important; +} + +/* --- Canvas "+" add-region button ---------------------------------------- */ + +/* Xibo renders a prominent add button at the top of the viewer panel */ +#layout-editor .add-btn, +#layout-editor .viewer-add-btn, +#layout-editor .button-add-new, +#layout-editor [class*="add-region-btn"] { + background-color: var(--color-primary) !important; + color: #ffffff !important; + border-radius: 50% !important; + width: 36px !important; + height: 36px !important; + min-width: 36px !important; + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + border: none !important; + box-shadow: 0 3px 10px rgba(var(--ots-primary-rgb), 0.45) !important; + transition: transform 0.15s ease, box-shadow 0.15s ease !important; + padding: 0 !important; +} + +#layout-editor .add-btn:hover, +#layout-editor .viewer-add-btn:hover, +#layout-editor .button-add-new:hover, +#layout-editor [class*="add-region-btn"]:hover { + transform: scale(1.12) !important; + box-shadow: 0 5px 16px rgba(var(--ots-primary-rgb), 0.65) !important; +} + +/* --- Micro-transitions for key interactive elements ---------------------- */ + +/* Timeline widget cards */ +#playlist-timeline .playlist-widget { + transition: background-color 0.14s ease, outline-color 0.14s ease !important; +} + +/* Toolbar card rows */ +.editor-toolbar .toolbar-card, +.editor-toolbar nav tr.toolbar-card { + transition: background-color 0.14s ease, color 0.14s ease !important; +} + +/* Toolbar nav buttons */ +.editor-toolbar nav .toolbar-menu-btn { + transition: background-color 0.14s ease, color 0.14s ease !important; +} + +/* Properties panel nav tabs */ +.properties-panel-container .nav-link, +#properties-panel .nav .nav-link { + transition: color 0.14s ease, border-bottom-color 0.14s ease, + background-color 0.14s ease !important; +} + +/* Bottom-bar buttons */ +.editor-bottom-bar nav .btn, +.editor-bottom-bar .viewer-navbar-info .btn { + transition: color 0.14s ease, background-color 0.14s ease !important; +} + +/* Context menu items */ +.context-menu-btn { + transition: background-color 0.12s ease, color 0.12s ease !important; +} + +/* Properties panel action buttons */ +#properties-panel .btn { + transition: background-color 0.14s ease, color 0.14s ease, + border-color 0.14s ease, box-shadow 0.14s ease !important; +} + + +/* ============================================================================ + LAYOUT DESIGNER — UX ENHANCEMENTS + Phase 6: Custom scrollbars, focus rings, empty timeline state + ============================================================================ */ + +/* --- Custom scrollbars (all editor scrollable areas) --------------------- */ + +/* Firefox */ +#properties-panel, +#properties-panel-form-container, +.editor-toolbar .toolbar-pane, +.toolbar-menu-content, +#playlist-editor, +#layout-editor { + scrollbar-width: thin; + scrollbar-color: var(--editor-scrollbar-thumb) var(--editor-scrollbar-track); +} + +/* WebKit */ +#properties-panel ::-webkit-scrollbar, +#properties-panel-form-container ::-webkit-scrollbar, +.editor-toolbar .toolbar-pane ::-webkit-scrollbar, +.toolbar-menu-content ::-webkit-scrollbar { + width: 5px; + height: 5px; +} + +#properties-panel ::-webkit-scrollbar-track, +#properties-panel-form-container ::-webkit-scrollbar-track, +.editor-toolbar .toolbar-pane ::-webkit-scrollbar-track, +.toolbar-menu-content ::-webkit-scrollbar-track { + background: var(--editor-scrollbar-track); + border-radius: 3px; +} + +#properties-panel ::-webkit-scrollbar-thumb, +#properties-panel-form-container ::-webkit-scrollbar-thumb, +.editor-toolbar .toolbar-pane ::-webkit-scrollbar-thumb, +.toolbar-menu-content ::-webkit-scrollbar-thumb { + background: var(--editor-scrollbar-thumb); + border-radius: 3px; +} + +#properties-panel ::-webkit-scrollbar-thumb:hover, +#properties-panel-form-container ::-webkit-scrollbar-thumb:hover, +.editor-toolbar .toolbar-pane ::-webkit-scrollbar-thumb:hover, +.toolbar-menu-content ::-webkit-scrollbar-thumb:hover { + background: var(--editor-scrollbar-thumb-hover); +} + +/* --- Accessible focus ring indicators ------------------------------------ */ + +#layout-editor *:focus-visible, +#playlist-editor *:focus-visible, +.editor-modal *:focus-visible, +#properties-panel *:focus-visible { + outline: 2px solid var(--color-primary) !important; + outline-offset: 2px !important; + box-shadow: 0 0 0 4px rgba(var(--ots-primary-rgb), 0.15) !important; +} + +/* --- Empty timeline state (no regions yet) -------------------------------- */ + +/* Xibo renders an empty-state message inside the timeline container */ +#playlist-timeline .empty-timeline, +#playlist-timeline .timeline-empty-message, +#playlist-timeline .empty-message, +#layout-editor .timeline-no-content, +#layout-editor .empty-canvas-hint { + display: flex !important; + flex-direction: column !important; + align-items: center !important; + justify-content: center !important; + min-height: 72px !important; + color: var(--editor-text-secondary) !important; + font-size: 13px !important; + text-align: center !important; + padding: 12px 20px !important; + border: 2px dashed var(--editor-border) !important; + border-radius: 8px !important; + margin: 10px !important; + cursor: pointer !important; + transition: border-color 0.15s ease, color 0.15s ease !important; +} + +#playlist-timeline .empty-timeline:hover, +#playlist-timeline .timeline-empty-message:hover, +#playlist-timeline .empty-message:hover, +#layout-editor .timeline-no-content:hover, +#layout-editor .empty-canvas-hint:hover { + border-color: var(--color-primary) !important; + color: var(--color-primary) !important; +} + + +/* ============================================================================ + LAYOUT DESIGNER — UX ENHANCEMENTS + Phase 7: Interactive Mode toggle, bottom bar refinement + ============================================================================ */ + +/* --- Interactive Mode toggle button -------------------------------------- */ + +/* Xibo renders this as a button in .editor-top-bar; it shows + "Interactive Mode OFF/ON". We can't know the exact dynamic class but we can + style all top-bar outline/light buttons that aren't the back-button. */ + +body.editor-opened .editor-top-bar .btn:not(.back-button):not(.btn-primary) { + border-radius: 6px !important; + font-size: 12px !important; + transition: background-color 0.14s ease, border-color 0.14s ease, + color 0.14s ease !important; +} + +/* When toggled "ON" — Xibo adds .active or .btn-success */ +body.editor-opened .editor-top-bar .btn.active:not(.back-button), +body.editor-opened .editor-top-bar .btn-success:not(.back-button) { + background-color: var(--color-primary) !important; + border-color: var(--color-primary) !important; + color: #ffffff !important; +} + +/* The layout name / info text */ +body.editor-opened .editor-top-bar .layout-info, +body.editor-opened .editor-top-bar .navbar-text { + font-size: 13px !important; + font-weight: 500 !important; + color: var(--editor-text) !important; + letter-spacing: 0.01em !important; +} + +/* --- Bottom bar refinement ----------------------------------------------- */ + +/* Slim down the orange info strip slightly and add a top border accent */ +.editor-bottom-bar .viewer-navbar-info { + font-size: 12px !important; + letter-spacing: 0.015em !important; + border-top: 2px solid rgba(255,255,255,0.15) !important; +} + +/* Slightly tighter padding on bottom-bar nav */ +.editor-bottom-bar nav { + min-height: 40px !important; +} + +.editor-bottom-bar nav .btn { + font-size: 12px !important; + padding: 4px 10px !important; + border-radius: 4px !important; +} + +/* Active/selected button in the bottom bar */ +.editor-bottom-bar nav .btn.active, +.editor-bottom-bar nav .btn:focus { + background-color: rgba(var(--ots-primary-rgb), 0.12) !important; + color: var(--color-primary) !important; +} + +/* Preview "play" button accent */ +.editor-bottom-bar .btn-play-preview, +.editor-bottom-bar [class*="play-preview"] { + background-color: var(--color-primary) !important; + color: #ffffff !important; + border-radius: 50% !important; + width: 30px !important; + height: 30px !important; + padding: 0 !important; + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + transition: transform 0.14s ease, box-shadow 0.14s ease !important; +} + +.editor-bottom-bar .btn-play-preview:hover, +.editor-bottom-bar [class*="play-preview"]:hover { + transform: scale(1.1) !important; + box-shadow: 0 3px 10px rgba(var(--ots-primary-rgb), 0.5) !important; +} + + +/* ============================================================================ + LAYOUT DESIGNER — UX ENHANCEMENTS + Phase 8: Compact embed mode + light-mode propagation fix + ============================================================================ */ + +/* --- Compact mode for narrow embed viewports ----------------------------- */ + +/* At 1280px the properties panel starts crowding the canvas */ +@media (max-width: 1280px) { + .ots-embed-mode #properties-panel, + .ots-embed-mode .properties-panel-container { + min-width: 230px !important; + max-width: 230px !important; + } +} + +@media (max-width: 1100px) { + .ots-embed-mode #properties-panel, + .ots-embed-mode .properties-panel-container { + min-width: 200px !important; + max-width: 200px !important; + font-size: 12px !important; + } + + .ots-embed-mode #properties-panel .form-control, + .ots-embed-mode .properties-panel-container .form-control { + height: 30px !important; + font-size: 12px !important; + } + + .ots-embed-mode #properties-panel label, + .ots-embed-mode .properties-panel-container label { + font-size: 11px !important; + margin-bottom: 3px !important; + } +} + +/* --- Light-mode propagation: body.ots-light-mode editor tokens ----------- */ + +/* The theme applies ots-light-mode to both and
. + CSS custom properties cascade from html.ots-light-mode already, but + we reinforce key structural backgrounds here to beat Xibo's bundled CSS. */ + +html.ots-light-mode #layout-editor, +html.ots-light-mode #playlist-editor, +html.ots-light-mode .layout-editor, +html.ots-light-mode .playlist-editor { + background-color: var(--editor-body-bg) !important; + color: var(--editor-text) !important; +} + +html.ots-light-mode #layout-editor #layout-viewer, +html.ots-light-mode #layout-editor .main-panel-wrapper { + background-color: var(--editor-left-margin-bg) !important; +} + +html.ots-light-mode .editor-toolbar, +html.ots-light-mode .editor-toolbar nav { + background-color: var(--editor-toolbar-bg) !important; + color: var(--editor-toolbar-text) !important; +} + +html.ots-light-mode .editor-top-bar nav, +html.ots-light-mode body.editor-opened .header-side { + background-color: var(--editor-modal-header-bg) !important; + color: var(--editor-text) !important; + border-bottom: 1px solid var(--editor-border) !important; +} + +html.ots-light-mode .editor-bottom-bar nav { + background-color: var(--editor-modal-content-bg) !important; + color: var(--editor-text) !important; + border-top: 1px solid var(--editor-border) !important; +} + +html.ots-light-mode #playlist-editor .left-margin { + background-color: var(--editor-left-margin-bg) !important; +} + +html.ots-light-mode #playlist-editor .time-grid { + color: var(--editor-timegrid-text) !important; +} + +html.ots-light-mode #playlist-editor .time-grid .time-grid-step { + background-color: var(--editor-timegrid-line) !important; +} + +html.ots-light-mode #playlist-timeline { + background-color: var(--editor-timeline-bg) !important; +} + +html.ots-light-mode #playlist-timeline .playlist-widget { + background-color: var(--editor-widget-bg) !important; + color: var(--editor-widget-text) !important; + outline-color: var(--editor-widget-border) !important; +} + +html.ots-light-mode .properties-panel-container, +html.ots-light-mode #properties-panel, +html.ots-light-mode #properties-panel-form-container { + background-color: var(--editor-properties-bg) !important; + color: var(--editor-text) !important; +} + +/* Panel header tabs stay distinguishable in light mode */ +html.ots-light-mode #properties-panel .nav, +html.ots-light-mode #properties-panel .nav-tabs { + background-color: var(--editor-header-bg) !important; + border-bottom-color: var(--editor-border) !important; +} + +/* Skeleton adapts to light mode too (variables are already resolved) */ +html.ots-light-mode .ots-skeleton-icon-dot { + background: rgba(0,0,0,0.06); +} + +html.ots-light-mode .ots-skeleton-chip { + background: rgba(0,0,0,0.07); +} + diff --git a/ots-signs/views/layout-designer-page.twig b/ots-signs/views/layout-designer-page.twig index 3e2f74b..28d03cc 100644 --- a/ots-signs/views/layout-designer-page.twig +++ b/ots-signs/views/layout-designer-page.twig @@ -109,8 +109,52 @@ -