feat: Enhance layout designer with skeleton loading screen and keyboard shortcut hints
This commit is contained in:
@@ -109,8 +109,52 @@
|
||||
<!-- Editor structure -->
|
||||
<div id="layout-editor" data-published-layout-id="{{ publishedLayoutId }}" data-layout-id="{{ layout.layoutId }}" data-layout-help={{ help }}></div>
|
||||
|
||||
<div class="loading-overlay">
|
||||
<i class="fa fa-spinner fa-spin loading-icon"></i>
|
||||
{# Skeleton loading screen — replaced by editor once editor-opened class fires on body #}
|
||||
<div id="ots-editor-skeleton" aria-hidden="true">
|
||||
<div class="ots-skeleton-topbar">
|
||||
<div class="ots-skeleton-topbar-left">
|
||||
<div class="ots-skeleton-chip"></div>
|
||||
<div class="ots-skeleton-chip" style="width:80px"></div>
|
||||
</div>
|
||||
<div class="ots-skeleton-topbar-right">
|
||||
<div class="ots-skeleton-chip" style="width:150px"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ots-skeleton-body">
|
||||
<div class="ots-skeleton-left-rail">
|
||||
<div class="ots-skeleton-icon-dot"></div>
|
||||
<div class="ots-skeleton-icon-dot"></div>
|
||||
<div class="ots-skeleton-icon-dot"></div>
|
||||
<div class="ots-skeleton-icon-dot"></div>
|
||||
</div>
|
||||
<div class="ots-skeleton-canvas-area">
|
||||
<div class="ots-skeleton-canvas-box"></div>
|
||||
</div>
|
||||
<div class="ots-skeleton-props">
|
||||
<div class="ots-skeleton-prop-group">
|
||||
<div class="ots-skeleton-prop-label"></div>
|
||||
<div class="ots-skeleton-prop-swatch-row">
|
||||
<div class="ots-skeleton-prop-swatch"></div>
|
||||
<div class="ots-skeleton-prop-field"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ots-skeleton-prop-group">
|
||||
<div class="ots-skeleton-prop-label"></div>
|
||||
<div class="ots-skeleton-prop-field" style="height:70px"></div>
|
||||
</div>
|
||||
<div class="ots-skeleton-prop-group">
|
||||
<div class="ots-skeleton-prop-label"></div>
|
||||
<div class="ots-skeleton-prop-field"></div>
|
||||
</div>
|
||||
<div class="ots-skeleton-prop-group">
|
||||
<div class="ots-skeleton-prop-label" style="width:35%"></div>
|
||||
<div class="ots-skeleton-prop-field"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ots-skeleton-bottombar">
|
||||
<div class="ots-skeleton-chip" style="width:120px; opacity:.5"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -458,6 +502,57 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
{# ── Skeleton dismiss + keyboard shortcut hints ──────────── #}
|
||||
<script type="text/javascript" nonce="{{ cspNonce }}">
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// ── Skeleton dismiss ─────────────────────────────────────
|
||||
var skeleton = document.getElementById('ots-editor-skeleton');
|
||||
if (skeleton) {
|
||||
if (document.body.classList.contains('editor-opened')) {
|
||||
skeleton.parentNode.removeChild(skeleton);
|
||||
} else {
|
||||
var skeletonObs = new MutationObserver(function() {
|
||||
if (document.body.classList.contains('editor-opened')) {
|
||||
skeleton.classList.add('ots-skeleton-done');
|
||||
setTimeout(function() {
|
||||
if (skeleton.parentNode) skeleton.parentNode.removeChild(skeleton);
|
||||
}, 380);
|
||||
skeletonObs.disconnect();
|
||||
}
|
||||
});
|
||||
skeletonObs.observe(document.body, { attributes: true, attributeFilter: ['class'] });
|
||||
}
|
||||
}
|
||||
|
||||
// ── Keyboard shortcut hints (title attrs on toolbar buttons) ──
|
||||
var hints = [
|
||||
{ sel: '#undoBtn', hint: 'Undo (Ctrl+Z)' },
|
||||
{ sel: '#fullscreenBtn', hint: 'Toggle Fullscreen (F)' },
|
||||
{ sel: '#layerManagerBtn', hint: 'Layer Manager (L)' }
|
||||
];
|
||||
|
||||
function applyKbHints() {
|
||||
hints.forEach(function(h) {
|
||||
try {
|
||||
document.querySelectorAll(h.sel).forEach(function(el) {
|
||||
if (!el.title) el.title = h.hint;
|
||||
});
|
||||
} catch(ignore) {}
|
||||
});
|
||||
}
|
||||
|
||||
var kbObs = new MutationObserver(function() {
|
||||
if (document.body.classList.contains('editor-opened')) {
|
||||
setTimeout(applyKbHints, 1500);
|
||||
kbObs.disconnect();
|
||||
}
|
||||
});
|
||||
kbObs.observe(document.body, { attributes: true, attributeFilter: ['class'] });
|
||||
})();
|
||||
</script>
|
||||
|
||||
{# ── Embed mode: postMessage bridge ──────────────────────── #}
|
||||
<script type="text/javascript" nonce="{{ cspNonce }}">
|
||||
(function() {
|
||||
@@ -557,21 +652,45 @@
|
||||
lD.showPublishScreen();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'xibo:editor:setTheme': {
|
||||
var newMode = msg.mode;
|
||||
if (newMode === 'light') {
|
||||
document.documentElement.classList.add('ots-light-mode');
|
||||
document.body.classList.add('ots-light-mode');
|
||||
} else {
|
||||
document.documentElement.classList.remove('ots-light-mode');
|
||||
document.body.classList.remove('ots-light-mode');
|
||||
}
|
||||
try { localStorage.setItem('ots-theme-mode', newMode); } catch(ignore) {}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// ── Notify parent when editor is ready ──────────────────
|
||||
// Wait for the layout editor to initialize (it adds .editor-opened to body)
|
||||
// Fire xibo:editor:error if editor hasn't opened within 15 s
|
||||
var initErrTimeout = setTimeout(function() {
|
||||
if (!document.body.classList.contains('editor-opened')) {
|
||||
sendToParent('xibo:editor:error', { reason: 'timeout' });
|
||||
}
|
||||
}, 15000);
|
||||
|
||||
var readyObserver = new MutationObserver(function(mutations) {
|
||||
if (document.body.classList.contains('editor-opened')) {
|
||||
sendToParent('xibo:editor:ready', {});
|
||||
clearTimeout(initErrTimeout);
|
||||
sendToParent('xibo:editor:ready', {
|
||||
theme: document.documentElement.classList.contains('ots-light-mode') ? 'light' : 'dark'
|
||||
});
|
||||
readyObserver.disconnect();
|
||||
}
|
||||
});
|
||||
|
||||
if (document.body.classList.contains('editor-opened')) {
|
||||
// Already ready (unlikely but safe)
|
||||
sendToParent('xibo:editor:ready', {});
|
||||
clearTimeout(initErrTimeout);
|
||||
sendToParent('xibo:editor:ready', {
|
||||
theme: document.documentElement.classList.contains('ots-light-mode') ? 'light' : 'dark'
|
||||
});
|
||||
} else {
|
||||
readyObserver.observe(document.body, { attributes: true, attributeFilter: ['class'] });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user