Changeset 0.37.2 (#214)
Co-authored-by: gobha <jasafpro@gmail.com> Co-committed-by: gobha <jasafpro@gmail.com>
This commit is contained in:
81
src/js/sw/shell/app-shell.js
Normal file
81
src/js/sw/shell/app-shell.js
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* AppShell — Layer 1 layout scaffold
|
||||
*
|
||||
* ┌─────────────────────────────────┐ ← fixed banner (top, optional)
|
||||
* ├─────────────────────────────────┤
|
||||
* │ message (optional, dismissible)│
|
||||
* ├─────────────────────────────────┤
|
||||
* │ │
|
||||
* │ surface (fills remaining) │
|
||||
* │ │
|
||||
* ├─────────────────────────────────┤
|
||||
* │ footer (optional) │
|
||||
* ├─────────────────────────────────┤
|
||||
* └─────────────────────────────────┘ ← fixed banner (bottom, same as top)
|
||||
*
|
||||
* Props:
|
||||
* banner — { text, variant } or null (one config → top AND bottom)
|
||||
* message — { text, variant } or null (dismissible)
|
||||
* footer — VNode children or null
|
||||
* children — Surface content
|
||||
*/
|
||||
const { html } = window;
|
||||
const { useState, useEffect, useRef } = hooks;
|
||||
|
||||
function ShellBanner({ position, text, variant = 'info' }) {
|
||||
const ref = useRef(null);
|
||||
const prop = position === 'top' ? '--banner-top-height' : '--banner-bottom-height';
|
||||
|
||||
useEffect(() => {
|
||||
if (ref.current) {
|
||||
const h = ref.current.offsetHeight + 'px';
|
||||
document.documentElement.style.setProperty(prop, h);
|
||||
}
|
||||
return () => document.documentElement.style.setProperty(prop, '0px');
|
||||
}, [text, prop]);
|
||||
|
||||
return html`
|
||||
<div class="sw-shell__banner sw-shell__banner--${position} sw-shell__banner--${variant}" ref=${ref}>
|
||||
<span class="sw-shell__banner-text">${text}</span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
export function AppShell({ banner, message, footer, children }) {
|
||||
const [msgDismissed, setMsgDismissed] = useState(false);
|
||||
|
||||
const showMsg = message && !msgDismissed;
|
||||
|
||||
return html`
|
||||
<div class="sw-shell">
|
||||
${banner && html`
|
||||
<${ShellBanner} position="top" text=${banner.text} variant=${banner.variant} />
|
||||
`}
|
||||
|
||||
<div class="sw-shell__body">
|
||||
${showMsg && html`
|
||||
<div class="sw-shell__announcement">
|
||||
<div class="sw-shell__announcement-inner sw-shell__announcement--${message.variant || 'info'}">
|
||||
<span class="sw-shell__announcement-text">${message.text}</span>
|
||||
<button class="sw-shell__banner-close" onClick=${() => setMsgDismissed(true)} aria-label="Dismiss">×</button>
|
||||
</div>
|
||||
</div>
|
||||
`}
|
||||
|
||||
<main class="sw-shell__surface">
|
||||
${children}
|
||||
</main>
|
||||
|
||||
${footer && html`
|
||||
<footer class="sw-shell__footer">
|
||||
${footer}
|
||||
</footer>
|
||||
`}
|
||||
</div>
|
||||
|
||||
${banner && html`
|
||||
<${ShellBanner} position="bottom" text=${banner.text} variant=${banner.variant} />
|
||||
`}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
Reference in New Issue
Block a user