fix(layout): viewport-filling rApp layout — eliminate body scroll
Make #app a flex column with height:100vh so all rApps fill the viewport exactly. Wrap module body in .rapp-content flex child. Replace all hardcoded calc(100vh - Npx) with height:100% across 20+ components. Remove sticky positioning from subnav/tabbar (now flex items). Generalize mobile body-flex to all pages (not just canvas-layout). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
0a21caa5e5
commit
2cbff8925d
|
|
@ -1,5 +1,4 @@
|
||||||
/* CrowdSurf module layout */
|
/* CrowdSurf module layout */
|
||||||
main {
|
main {
|
||||||
min-height: calc(100vh - 56px);
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -856,7 +856,6 @@ class FolkCrowdSurfDashboard extends HTMLElement {
|
||||||
|
|
||||||
.cs-app {
|
.cs-app {
|
||||||
display: flex; flex-direction: column; height: 100%;
|
display: flex; flex-direction: column; height: 100%;
|
||||||
min-height: calc(100vh - 56px);
|
|
||||||
background: var(--rs-bg-page);
|
background: var(--rs-bg-page);
|
||||||
color: var(--rs-text-primary);
|
color: var(--rs-text-primary);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1 @@
|
||||||
/* Books module layout */
|
/* Books module layout */
|
||||||
main {
|
|
||||||
min-height: calc(100vh - 56px);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -365,7 +365,6 @@ export class FolkBookReader extends HTMLElement {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
min-height: calc(100vh - 52px);
|
|
||||||
background: var(--rs-bg-page);
|
background: var(--rs-bg-page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -418,7 +417,7 @@ export class FolkBookReader extends HTMLElement {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
height: calc(100vh - 52px);
|
height: 100%;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -457,7 +456,7 @@ export class FolkBookReader extends HTMLElement {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
height: calc(100vh - 52px);
|
height: 100%;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
.error h3 { color: var(--rs-error); margin: 0; }
|
.error h3 { color: var(--rs-error); margin: 0; }
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
/* Cart module layout */
|
/* Cart module layout */
|
||||||
main {
|
main {
|
||||||
min-height: calc(100vh - 56px);
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -20,9 +19,3 @@ main:has(folk-payment-page) .rapp-subnav,
|
||||||
main:has(folk-group-buy-page) .rapp-subnav {
|
main:has(folk-group-buy-page) .rapp-subnav {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
main {
|
|
||||||
min-height: calc(100vh - 44px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
/* Choices module layout */
|
/* Choices module layout */
|
||||||
main {
|
main {
|
||||||
min-height: calc(100vh - 56px);
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -72,9 +72,8 @@ routes.get("/", (c) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const RDESIGN_CSS = `
|
const RDESIGN_CSS = `
|
||||||
/* Layout — prevent page scroll */
|
/* Layout — fills viewport via shell flex layout */
|
||||||
html:has(.rd-app), html:has(.rd-app) body { overflow:hidden; height:100vh; }
|
.rd-app { display:flex; flex-direction:column; height:100%; overflow:hidden; }
|
||||||
.rd-app { display:flex; flex-direction:column; height:calc(100vh - 56px); overflow:hidden; }
|
|
||||||
.rd-toolbar { display:flex; align-items:center; gap:8px; padding:6px 12px; border-bottom:1px solid var(--rs-border); background:var(--rs-bg-surface); flex-shrink:0; }
|
.rd-toolbar { display:flex; align-items:center; gap:8px; padding:6px 12px; border-bottom:1px solid var(--rs-border); background:var(--rs-bg-surface); flex-shrink:0; }
|
||||||
.rd-split { display:flex; flex:1; overflow:hidden; }
|
.rd-split { display:flex; flex:1; overflow:hidden; }
|
||||||
.rd-chat { flex:1; display:flex; flex-direction:column; min-width:300px; border-right:1px solid var(--rs-border); background:var(--rs-bg-page); }
|
.rd-chat { flex:1; display:flex; flex-direction:column; min-width:300px; border-right:1px solid var(--rs-border); background:var(--rs-bg-page); }
|
||||||
|
|
|
||||||
|
|
@ -2206,7 +2206,7 @@ class FolkMapViewer extends HTMLElement {
|
||||||
.room-name { font-size: 15px; font-weight: 600; }
|
.room-name { font-size: 15px; font-weight: 600; }
|
||||||
|
|
||||||
.map-container {
|
.map-container {
|
||||||
width: 100%; height: calc(100vh - 220px); min-height: 300px;
|
width: 100%; height: 100%; min-height: 300px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background: var(--rs-bg-surface-sunken); border: 1px solid var(--rs-border);
|
background: var(--rs-bg-surface-sunken); border: 1px solid var(--rs-border);
|
||||||
position: relative; overflow: hidden;
|
position: relative; overflow: hidden;
|
||||||
|
|
@ -2219,7 +2219,7 @@ class FolkMapViewer extends HTMLElement {
|
||||||
.map-sidebar {
|
.map-sidebar {
|
||||||
width: 220px; flex-shrink: 0;
|
width: 220px; flex-shrink: 0;
|
||||||
background: var(--rs-glass-bg); border: 1px solid var(--rs-border); border-radius: 10px;
|
background: var(--rs-glass-bg); border: 1px solid var(--rs-border); border-radius: 10px;
|
||||||
padding: 12px; max-height: calc(100vh - 280px); overflow-y: auto;
|
padding: 12px; max-height: 100%; overflow-y: auto;
|
||||||
}
|
}
|
||||||
.sidebar-title {
|
.sidebar-title {
|
||||||
font-size: 11px; font-weight: 600; color: var(--rs-text-secondary);
|
font-size: 11px; font-weight: 600; color: var(--rs-text-secondary);
|
||||||
|
|
@ -2279,7 +2279,7 @@ class FolkMapViewer extends HTMLElement {
|
||||||
.mobile-bottom-sheet { display: none; }
|
.mobile-bottom-sheet { display: none; }
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.map-container { height: calc(100vh - 48px); min-height: 250px; max-height: none; border-radius: 0; border: none; }
|
.map-container { height: 100%; min-height: 250px; max-height: none; border-radius: 0; border: none; }
|
||||||
.map-layout { flex-direction: column; }
|
.map-layout { flex-direction: column; }
|
||||||
.map-sidebar { display: none; }
|
.map-sidebar { display: none; }
|
||||||
.controls { display: none; }
|
.controls { display: none; }
|
||||||
|
|
|
||||||
|
|
@ -61,10 +61,10 @@ class FolkJitsiRoom extends HTMLElement {
|
||||||
private render() {
|
private render() {
|
||||||
this.shadow.innerHTML = `
|
this.shadow.innerHTML = `
|
||||||
<style>
|
<style>
|
||||||
:host { display: block; width: 100%; min-height: 70vh; }
|
:host { display: flex; flex-direction: column; width: 100%; height: 100%; }
|
||||||
.jitsi-container { width: 100%; height: 70vh; border-radius: 12px; overflow: hidden; background: #000; }
|
.jitsi-container { width: 100%; flex: 1; min-height: 0; border-radius: 12px; overflow: hidden; background: #000; }
|
||||||
.jitsi-container iframe { border: none !important; }
|
.jitsi-container iframe { border: none !important; }
|
||||||
.loading { display: flex; align-items: center; justify-content: center; height: 70vh; color: var(--rs-text-muted, #888); font-family: system-ui, sans-serif; }
|
.loading { display: flex; align-items: center; justify-content: center; flex: 1; min-height: 0; color: var(--rs-text-muted, #888); font-family: system-ui, sans-serif; }
|
||||||
.director-strip { display: flex; gap: 6px; padding: 10px; background: var(--rs-bg-surface, #1a1a2e); border: 1px solid var(--rs-border, #333); border-radius: 0 0 12px 12px; overflow-x: auto; align-items: center; }
|
.director-strip { display: flex; gap: 6px; padding: 10px; background: var(--rs-bg-surface, #1a1a2e); border: 1px solid var(--rs-border, #333); border-radius: 0 0 12px 12px; overflow-x: auto; align-items: center; }
|
||||||
.director-thumb { position: relative; width: 160px; min-width: 160px; aspect-ratio: 16/9; border-radius: 6px; overflow: hidden; cursor: pointer; border: 2px solid transparent; background: #111; }
|
.director-thumb { position: relative; width: 160px; min-width: 160px; aspect-ratio: 16/9; border-radius: 6px; overflow: hidden; cursor: pointer; border: 2px solid transparent; background: #111; }
|
||||||
.director-thumb.active { border-color: #ef4444; }
|
.director-thumb.active { border-color: #ef4444; }
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ folk-graph-viewer {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 400px;
|
min-height: 400px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
height: calc(100vh - 60px);
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Canvas cursor states */
|
/* Canvas cursor states */
|
||||||
|
|
|
||||||
|
|
@ -3543,7 +3543,7 @@ Gear: EUR 400 (10%)</code></pre><p><em>Maya is tracking expenses in rF
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 260px 1fr;
|
grid-template-columns: 260px 1fr;
|
||||||
min-height: 400px;
|
min-height: 400px;
|
||||||
height: calc(100vh - 120px);
|
height: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
transition: grid-template-columns 0.2s ease;
|
transition: grid-template-columns 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -820,9 +820,10 @@ export class FolkPubsEditor extends HTMLElement {
|
||||||
return `<style>
|
return `<style>
|
||||||
:host {
|
:host {
|
||||||
display: block;
|
display: block;
|
||||||
height: calc(100vh - 140px);
|
height: 100%;
|
||||||
background: var(--rs-bg-page);
|
background: var(--rs-bg-page);
|
||||||
color: var(--rs-text-primary);
|
color: var(--rs-text-primary);
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wizard-layout {
|
.wizard-layout {
|
||||||
|
|
@ -1267,7 +1268,7 @@ export class FolkPubsEditor extends HTMLElement {
|
||||||
/* ── Responsive ── */
|
/* ── Responsive ── */
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
:host { height: auto; min-height: calc(100vh - 56px); }
|
:host { height: auto; min-height: 100%; }
|
||||||
.editor-toolbar { gap: 0.5rem; }
|
.editor-toolbar { gap: 0.5rem; }
|
||||||
.toolbar-left { flex-direction: column; gap: 0.375rem; }
|
.toolbar-left { flex-direction: column; gap: 0.375rem; }
|
||||||
.title-input, .author-input { max-width: 100%; flex: 1; }
|
.title-input, .author-input { max-width: 100%; flex: 1; }
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
/* Pubs module layout */
|
/* Pubs module layout */
|
||||||
main {
|
main {
|
||||||
min-height: calc(100vh - 56px);
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/* rSchedule Automation Canvas — n8n-style workflow builder */
|
/* rSchedule Automation Canvas — n8n-style workflow builder */
|
||||||
folk-automation-canvas {
|
folk-automation-canvas {
|
||||||
display: block;
|
display: block;
|
||||||
height: calc(100vh - 60px);
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ac-root {
|
.ac-root {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/* rSocials Campaign Planner — n8n-style flow canvas */
|
/* rSocials Campaign Planner — n8n-style flow canvas */
|
||||||
folk-campaign-planner {
|
folk-campaign-planner {
|
||||||
display: block;
|
display: block;
|
||||||
height: calc(100vh - 60px);
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cp-root {
|
.cp-root {
|
||||||
|
|
@ -675,7 +675,7 @@ folk-campaign-planner {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
max-height: calc(100vh - 180px);
|
max-height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cp-pv-card {
|
.cp-pv-card {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/* rSocials Campaign Workflow — n8n-style workflow builder */
|
/* rSocials Campaign Workflow — n8n-style workflow builder */
|
||||||
folk-campaign-workflow {
|
folk-campaign-workflow {
|
||||||
display: block;
|
display: block;
|
||||||
height: calc(100vh - 92px);
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cw-root {
|
.cw-root {
|
||||||
|
|
@ -578,7 +578,7 @@ folk-campaign-workflow {
|
||||||
/* ── Mobile ── */
|
/* ── Mobile ── */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
folk-campaign-workflow {
|
folk-campaign-workflow {
|
||||||
height: calc(100vh - 56px);
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cw-palette {
|
.cw-palette {
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
max-width: 1200px;
|
max-width: 1200px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
color: var(--splat-text);
|
color: var(--splat-text);
|
||||||
min-height: calc(100vh - 56px);
|
min-height: 100%;
|
||||||
background: var(--splat-bg);
|
background: var(--splat-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -427,7 +427,7 @@ button.splat-viewer__save {
|
||||||
.splat-viewer {
|
.splat-viewer {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(100vh - 56px);
|
height: 100%;
|
||||||
background: var(--splat-bg);
|
background: var(--splat-bg);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
/* Swag module layout */
|
/* Swag module layout */
|
||||||
main {
|
main {
|
||||||
min-height: calc(100vh - 56px);
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,6 @@
|
||||||
folk-timebank-app {
|
folk-timebank-app {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: calc(100vh - var(--rs-header-height, 56px));
|
height: 100%;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -429,7 +429,7 @@ class FolkTripsPlanner extends HTMLElement {
|
||||||
if (this._mapInstance) { try { this._mapInstance.remove(); } catch {} this._mapInstance = null; this._mapMarkers = []; }
|
if (this._mapInstance) { try { this._mapInstance.remove(); } catch {} this._mapInstance = null; this._mapMarkers = []; }
|
||||||
this.shadow.innerHTML = `
|
this.shadow.innerHTML = `
|
||||||
<style>
|
<style>
|
||||||
:host { display: flex; flex-direction: column; height: calc(100vh - 112px); font-family: system-ui, -apple-system, sans-serif; color: var(--rs-text-primary); overflow: hidden; }
|
:host { display: flex; flex-direction: column; height: 100%; font-family: system-ui, -apple-system, sans-serif; color: var(--rs-text-primary); overflow: hidden; }
|
||||||
* { box-sizing: border-box; }
|
* { box-sizing: border-box; }
|
||||||
.rtrips-shell { display: flex; flex-direction: column; height: 100%; min-height: 0; overflow: hidden; }
|
.rtrips-shell { display: flex; flex-direction: column; height: 100%; min-height: 0; overflow: hidden; }
|
||||||
.rtrips-scroll { flex: 1; overflow-y: auto; min-height: 0; padding-bottom: 8px; }
|
.rtrips-scroll { flex: 1; overflow-y: auto; min-height: 0; padding-bottom: 8px; }
|
||||||
|
|
|
||||||
|
|
@ -309,10 +309,7 @@ export function renderShell(opts: ShellOptions): string {
|
||||||
<button class="rstack-header__history-btn" id="history-btn" title="History"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg></button>
|
<button class="rstack-header__history-btn" id="history-btn" title="History"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg></button>
|
||||||
<rstack-history-panel></rstack-history-panel>
|
<rstack-history-panel></rstack-history-panel>
|
||||||
</div>
|
</div>
|
||||||
<div class="rstack-header__dropdown-wrap">
|
<button class="rstack-header__settings-btn" id="settings-btn" title="Space Settings"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg></button>
|
||||||
<button class="rstack-header__settings-btn" id="settings-btn" title="Space Settings"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg></button>
|
|
||||||
<rstack-space-settings space="${escapeAttr(spaceSlug)}" module-id="${escapeAttr(moduleId)}"></rstack-space-settings>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="rstack-header__center">
|
<div class="rstack-header__center">
|
||||||
<rstack-mi></rstack-mi>
|
<rstack-mi></rstack-mi>
|
||||||
|
|
@ -346,7 +343,7 @@ export function renderShell(opts: ShellOptions): string {
|
||||||
<main id="app"${moduleId === "rspace" ? ' class="canvas-layout"' : ''}>
|
<main id="app"${moduleId === "rspace" ? ' class="canvas-layout"' : ''}>
|
||||||
${renderModuleSubNav(moduleId, spaceSlug, visibleModules, opts.isSubdomain ?? IS_PRODUCTION)}
|
${renderModuleSubNav(moduleId, spaceSlug, visibleModules, opts.isSubdomain ?? IS_PRODUCTION)}
|
||||||
${opts.tabs ? renderTabBar(opts.tabs, opts.activeTab, opts.tabBasePath || ((opts.isSubdomain ?? IS_PRODUCTION) ? `/${escapeAttr(moduleId)}` : `/${escapeAttr(spaceSlug)}/${escapeAttr(moduleId)}`)) : ''}
|
${opts.tabs ? renderTabBar(opts.tabs, opts.activeTab, opts.tabBasePath || ((opts.isSubdomain ?? IS_PRODUCTION) ? `/${escapeAttr(moduleId)}` : `/${escapeAttr(spaceSlug)}/${escapeAttr(moduleId)}`)) : ''}
|
||||||
${body}
|
<div class="rapp-content">${body}</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
${renderWelcomeOverlay()}
|
${renderWelcomeOverlay()}
|
||||||
|
|
@ -422,15 +419,15 @@ export function renderShell(opts: ShellOptions): string {
|
||||||
const minimized = document.body.classList.toggle('rspace-headers-minimized');
|
const minimized = document.body.classList.toggle('rspace-headers-minimized');
|
||||||
localStorage.setItem('rspace_headers_minimized', minimized ? '1' : '0');
|
localStorage.setItem('rspace_headers_minimized', minimized ? '1' : '0');
|
||||||
});
|
});
|
||||||
// ── Settings panel toggle (close history first) ──
|
// ── Settings modal (close history first) ──
|
||||||
document.getElementById('settings-btn')?.addEventListener('click', () => {
|
document.getElementById('settings-btn')?.addEventListener('click', () => {
|
||||||
document.querySelector('rstack-history-panel')?.close();
|
document.querySelector('rstack-history-panel')?.close();
|
||||||
document.querySelector('rstack-space-settings')?.toggle();
|
const sw = document.querySelector('rstack-space-switcher');
|
||||||
|
if (sw) sw.openSettingsModal('${spaceSlug}', '${spaceName || spaceSlug}');
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── History panel toggle (close settings first) ──
|
// ── History panel toggle ──
|
||||||
document.getElementById('history-btn')?.addEventListener('click', () => {
|
document.getElementById('history-btn')?.addEventListener('click', () => {
|
||||||
document.querySelector('rstack-space-settings')?.close();
|
|
||||||
document.querySelector('rstack-history-panel')?.toggle();
|
document.querySelector('rstack-history-panel')?.toggle();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1105,9 +1102,6 @@ export function renderShell(opts: ShellOptions): string {
|
||||||
console.log('[shell] layer-switch:', moduleId, 'layerId:', layerId, 'tabCache:', !!tabCache, 'layers:', layers.length);
|
console.log('[shell] layer-switch:', moduleId, 'layerId:', layerId, 'tabCache:', !!tabCache, 'layers:', layers.length);
|
||||||
currentModuleId = moduleId;
|
currentModuleId = moduleId;
|
||||||
saveTabs();
|
saveTabs();
|
||||||
// Update settings panel to show config for the newly active module
|
|
||||||
const sp = document.querySelector('rstack-space-settings');
|
|
||||||
if (sp) sp.setAttribute('module-id', moduleId);
|
|
||||||
if (tabCache) {
|
if (tabCache) {
|
||||||
const switchId = moduleId; // capture for staleness check
|
const switchId = moduleId; // capture for staleness check
|
||||||
tabCache.switchTo(moduleId).then(ok => {
|
tabCache.switchTo(moduleId).then(ok => {
|
||||||
|
|
@ -2039,15 +2033,12 @@ const SUBNAV_CSS = `
|
||||||
border-bottom: 1px solid var(--rs-border);
|
border-bottom: 1px solid var(--rs-border);
|
||||||
background: var(--rs-bg-surface);
|
background: var(--rs-bg-surface);
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
position: sticky;
|
flex-shrink: 0;
|
||||||
top: 93px;
|
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
}
|
}
|
||||||
.rapp-subnav::-webkit-scrollbar { display: none; }
|
.rapp-subnav::-webkit-scrollbar { display: none; }
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
.rapp-subnav {
|
.rapp-subnav {
|
||||||
position: relative;
|
|
||||||
top: auto;
|
|
||||||
padding: 0.375rem 0.75rem;
|
padding: 0.375rem 0.75rem;
|
||||||
gap: 0.25rem;
|
gap: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
@ -2134,20 +2125,15 @@ const TABBAR_CSS = `
|
||||||
border-bottom: 1px solid var(--rs-border);
|
border-bottom: 1px solid var(--rs-border);
|
||||||
background: var(--rs-bg-surface);
|
background: var(--rs-bg-surface);
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
position: sticky;
|
flex-shrink: 0;
|
||||||
top: 92px;
|
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
}
|
}
|
||||||
.rapp-tabbar::-webkit-scrollbar { display: none; }
|
.rapp-tabbar::-webkit-scrollbar { display: none; }
|
||||||
.rapp-subnav + .rapp-tabbar { top: 130px; }
|
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
.rapp-tabbar {
|
.rapp-tabbar {
|
||||||
position: relative;
|
|
||||||
top: auto;
|
|
||||||
padding: 0.25rem 0.75rem;
|
padding: 0.25rem 0.75rem;
|
||||||
gap: 0.25rem;
|
gap: 0.25rem;
|
||||||
}
|
}
|
||||||
.rapp-subnav + .rapp-tabbar { top: auto; }
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -78,9 +78,6 @@ body.rspace-banner-visible .rstack-tab-row {
|
||||||
body.rspace-banner-visible #app {
|
body.rspace-banner-visible #app {
|
||||||
padding-top: 128px; /* 36px banner + 92px normal */
|
padding-top: 128px; /* 36px banner + 92px normal */
|
||||||
}
|
}
|
||||||
body.rspace-banner-visible .rapp-subnav {
|
|
||||||
top: 129px;
|
|
||||||
}
|
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
.rspace-banner {
|
.rspace-banner {
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
|
|
@ -92,7 +89,6 @@ body.rspace-banner-visible .rapp-subnav {
|
||||||
body.rspace-banner-visible .rstack-header { top: 0; }
|
body.rspace-banner-visible .rstack-header { top: 0; }
|
||||||
body.rspace-banner-visible .rstack-tab-row { top: 0; }
|
body.rspace-banner-visible .rstack-tab-row { top: 0; }
|
||||||
body.rspace-banner-visible #app { padding-top: 0; }
|
body.rspace-banner-visible #app { padding-top: 0; }
|
||||||
body.rspace-banner-visible .rapp-subnav { top: auto; }
|
|
||||||
.rspace-banner { position: sticky; }
|
.rspace-banner { position: sticky; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -229,14 +225,30 @@ body.rspace-banner-visible .rapp-subnav {
|
||||||
|
|
||||||
#app {
|
#app {
|
||||||
padding-top: 92px; /* Below fixed header (56px) + tab row (36px) */
|
padding-top: 92px; /* Below fixed header (56px) + tab row (36px) */
|
||||||
min-height: 100vh;
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* When canvas module is active, make it fill the viewport */
|
/* .rapp-content fills below subnav/tabbar — scrollable for landing pages, app views fill exactly */
|
||||||
#app.canvas-layout {
|
.rapp-content {
|
||||||
padding-top: 92px;
|
flex: 1;
|
||||||
height: 100vh;
|
min-height: 0;
|
||||||
overflow: hidden;
|
overflow: auto;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
/* When a single child (app view component), fill the viewport */
|
||||||
|
.rapp-content > :only-child {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
/* Tab panes: active pane fills, hidden ones don't participate in flex */
|
||||||
|
.rapp-content > .rspace-tab-pane--active {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Standalone mode (no app/space switcher) ── */
|
/* ── Standalone mode (no app/space switcher) ── */
|
||||||
|
|
@ -481,28 +493,23 @@ body.rstack-sidebar-open #toolbar {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
#app {
|
/* Mobile: body becomes flex column so #app fills below sticky header/tabs */
|
||||||
padding-top: 0;
|
html, body {
|
||||||
}
|
|
||||||
/* Canvas on mobile: header/tab-row are sticky (in-flow), so body must
|
|
||||||
become a flex column to prevent the 100vh #app from overflowing. */
|
|
||||||
html:has(#app.canvas-layout),
|
|
||||||
body:has(#app.canvas-layout) {
|
|
||||||
height: 100dvh;
|
height: 100dvh;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
overscroll-behavior: none;
|
overscroll-behavior: none;
|
||||||
}
|
}
|
||||||
body:has(#app.canvas-layout) {
|
body {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
body:has(#app.canvas-layout) .rstack-header {
|
.rstack-header {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
body:has(#app.canvas-layout) .rstack-tab-row {
|
.rstack-tab-row {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
#app.canvas-layout {
|
#app {
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
height: auto;
|
height: auto;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
@ -512,7 +519,7 @@ body.rstack-sidebar-open #toolbar {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(100vh - 80px);
|
flex: 1;
|
||||||
min-height: 400px;
|
min-height: 400px;
|
||||||
}
|
}
|
||||||
.rapp-nav {
|
.rapp-nav {
|
||||||
|
|
@ -548,16 +555,12 @@ body.rstack-sidebar-open #toolbar {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* When canvas is active, the tab pane must fill the viewport so #canvas height:100% works */
|
|
||||||
#app.canvas-layout > .rspace-tab-pane--active {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rspace-tab-loading {
|
.rspace-tab-loading {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
min-height: calc(100vh - 92px);
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rspace-tab-spinner {
|
.rspace-tab-spinner {
|
||||||
|
|
@ -612,9 +615,6 @@ body.rspace-headers-minimized #app.canvas-layout {
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
}
|
}
|
||||||
body.rspace-headers-minimized .rapp-subnav {
|
body.rspace-headers-minimized .rapp-subnav {
|
||||||
position: fixed;
|
|
||||||
top: 0; left: 0; right: 0;
|
|
||||||
z-index: 9997;
|
|
||||||
height: 28px;
|
height: 28px;
|
||||||
padding: 2px 8px;
|
padding: 2px 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
@ -645,8 +645,4 @@ body.rspace-headers-minimized .rapp-subnav {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transform: none;
|
transform: none;
|
||||||
}
|
}
|
||||||
body.rspace-headers-minimized .rapp-subnav {
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue