Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
337 changes: 337 additions & 0 deletions apps/test-app/public/parent-app.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,337 @@
<!DOCTYPE html>
<!--
Parent App Example — Condensed Layout Demo
===========================================
Demonstrates a parent portal with its own header + left nav that iframes an
AppUI child app. A "Full Screen" button hides the side nav and tells the
child to switch to CondensedLayout. "Exit Full Screen" restores the side
nav and switches back to StandardLayout.

Usage (local dev):
Serve the test-app normally, then open:
http://localhost:3000/parent-app.html
-->
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Parent App – Condensed Layout Demo</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
height: 100%;
font-family: system-ui, -apple-system, sans-serif;
background: #1e1e1e;
color: #e0e0e0;
}

/* ================================================================== */
/* Normal layout: header + body (left nav + content area with iframe) */
/* ================================================================== */
#app-shell {
display: flex;
flex-direction: column;
height: 100%;
}

/* ---------- Header ---------- */
#app-header {
display: flex;
align-items: center;
height: 48px;
padding: 0 16px;
background: #2b2b2b;
border-bottom: 1px solid #3a3a3a;
flex-shrink: 0;
gap: 16px;
z-index: 100;
}
#app-header .logo {
font-size: 15px;
font-weight: 700;
color: #4a90d9;
white-space: nowrap;
}
#app-header .header-center {
flex: 1;
display: flex;
align-items: center;
gap: 12px;
}
#app-header select {
background: #3a3a3a;
color: #e0e0e0;
border: 1px solid #555;
border-radius: 4px;
padding: 4px 8px;
font-size: 13px;
min-width: 180px;
}
#app-header .header-right {
display: flex;
align-items: center;
gap: 12px;
}
.user-avatar {
width: 30px;
height: 30px;
border-radius: 50%;
background: #4a90d9;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: 600;
cursor: pointer;
}
.fullscreen-btn {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
padding: 0;
background: transparent;
color: #a0a0a0;
border: none;
border-radius: 4px;
cursor: pointer;
}
.fullscreen-btn:hover {
background: #3a3a3a;
color: #fff;
}
.fullscreen-btn svg {
width: 18px;
height: 18px;
fill: currentColor;
}

/* ---------- Body (nav + main) ---------- */
#app-body {
display: flex;
flex: 1;
overflow: hidden;
}

/* ---------- Left nav ---------- */
#left-nav {
display: flex;
flex-direction: column;
width: 56px;
background: #252525;
border-right: 1px solid #3a3a3a;
padding: 8px 0;
align-items: center;
gap: 4px;
flex-shrink: 0;
z-index: 100;
transition: width 0.2s ease-out, opacity 0.2s ease-out,
padding 0.2s ease-out;
overflow: hidden;
}
.nav-item {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border-radius: 6px;
cursor: pointer;
color: #a0a0a0;
transition: background 0.15s, color 0.15s;
}
.nav-item:hover {
background: #3a3a3a;
color: #fff;
}
.nav-item.active {
background: #4a90d9;
color: #fff;
}
.nav-item svg {
width: 20px;
height: 20px;
fill: currentColor;
}
.nav-spacer {
flex: 1;
}

/* ---------- Main content area ---------- */
#main-content {
flex: 1;
position: relative;
overflow: hidden;
}

/* ---------- iframe ---------- */
#child-frame {
width: 100%;
height: 100%;
border: none;
}

/* ================================================================== */
/* Condensed mode — hide the left nav */
/* ================================================================== */
body.condensed #left-nav {
width: 0;
opacity: 0;
padding: 0;
border-right: none;
}
</style>
</head>
<body>
<div id="app-shell">
<!-- ============ HEADER ============ -->
<header id="app-header">
<div class="logo">iTwin Platform</div>
<div class="header-center">
<select id="project-select">
<option>Highway Bridge</option>
<option>Campus Building</option>
<option>Stadium Renovation</option>
<option>Rail Corridor</option>
</select>
</div>
<div class="header-right">
<button
class="fullscreen-btn"
id="toggle-fullscreen-btn"
title="Enter Full Screen"
>
<!-- Expand icon -->
<svg id="expand-icon" viewBox="0 0 16 16">
<path
d="M2 2h5v1.5H3.5V6H2V2zm7 0h5v4h-1.5V3.5H9V2zM2 10h1.5v2.5H6V14H2v-4zm10.5 2.5V10H14v4h-4v-1.5h2.5z"
/>
</svg>
<!-- Collapse icon (hidden by default) -->
<svg id="collapse-icon" viewBox="0 0 16 16" style="display: none">
<path
d="M4 2v2.5H1.5V6H6V1.5H4.5V2H4zm8 0v.5H10V6h4.5V4.5H12V2h-.5zM4 14v-.5H1.5V12H6v4.5H4.5V14H4zm8 0v.5H10V10h4.5v1.5H12V14h.5z"
/>
</svg>
</button>
<div class="user-avatar" id="user-btn" title="Jane Doe">JD</div>
</div>
</header>

<!-- ============ BODY ============ -->
<div id="app-body">
<!-- Left navigation -->
<nav id="left-nav">
<!-- Home -->
<div class="nav-item active" title="Home">
<svg viewBox="0 0 16 16">
<path
d="M8 1L1 7h2v7h4v-4h2v4h4V7h2L8 1zm0 1.7L13 7.6V13h-2V9H5v4H3V7.6L8 2.7z"
/>
</svg>
</div>
<!-- Projects -->
<div class="nav-item" title="Projects">
<svg viewBox="0 0 16 16">
<path d="M1 2h5l2 2h7v10H1V2zm1 1v10h12V5H7.586L5.586 3H2z" />
</svg>
</div>
<!-- Models -->
<div class="nav-item" title="Models">
<svg viewBox="0 0 16 16">
<path
d="M8 1l7 4v6l-7 4-7-4V5l7-4zm0 1.2L2.5 5.5v5l5.5 3.2 5.5-3.2v-5L8 2.2z"
/>
</svg>
</div>
<!-- Issues -->
<div class="nav-item" title="Issues">
<svg viewBox="0 0 16 16">
<path
d="M8 1a7 7 0 100 14A7 7 0 008 1zm0 1.2a5.8 5.8 0 110 11.6A5.8 5.8 0 018 2.2zM7.25 5h1.5v4h-1.5V5zm0 5h1.5v1.5h-1.5V10z"
/>
</svg>
</div>
<!-- Analytics -->
<div class="nav-item" title="Analytics">
<svg viewBox="0 0 16 16">
<path
d="M2 13h12v1H2v-1zm1-5h2v4H3V8zm3-3h2v7H6V5zm3-2h2v9H9V3zm3 4h2v6h-2V7z"
/>
</svg>
</div>
<div class="nav-spacer"></div>
<!-- Settings -->
<div class="nav-item" title="Settings">
<svg viewBox="0 0 16 16">
<path
d="M8 5.5a2.5 2.5 0 100 5 2.5 2.5 0 000-5zM6.7 8a1.3 1.3 0 112.6 0 1.3 1.3 0 01-2.6 0zm.2-6.5l-.3 1.6a4.8 4.8 0 00-1.3.7L3.8 3 2.5 5.2l1.2 1.1a5 5 0 000 1.4l-1.2 1.1L3.8 11l1.5-.8c.4.3.8.5 1.3.7l.3 1.6h2.6l.3-1.6c.5-.2.9-.4 1.3-.7l1.5.8 1.3-2.2-1.2-1.1a5 5 0 000-1.4l1.2-1.1L11.5 3 10 3.8a4.8 4.8 0 00-1.3-.7L8.4 1.5H6.9z"
/>
</svg>
</div>
</nav>

<!-- Main content with iframe -->
<div id="main-content">
<iframe
id="child-frame"
src="/blank?menu=0&frontstageId=content-overlay"
></iframe>
</div>
</div>
</div>

<script>
const iframe = document.getElementById("child-frame");
const toggleBtn = document.getElementById("toggle-fullscreen-btn");
const expandIcon = document.getElementById("expand-icon");
const collapseIcon = document.getElementById("collapse-icon");

let isCondensed = false;

toggleBtn.addEventListener("click", () => {
isCondensed = !isCondensed;

if (isCondensed) {
document.body.classList.add("condensed");
toggleBtn.title = "Exit Full Screen";
expandIcon.style.display = "none";
collapseIcon.style.display = "";
} else {
document.body.classList.remove("condensed");
toggleBtn.title = "Enter Full Screen";
expandIcon.style.display = "";
collapseIcon.style.display = "none";
}

// Tell the child iframe to switch layouts
iframe.contentWindow.postMessage(
{ type: "appui-set-overlay-mode", enabled: isCondensed },
"*"
);
});

// Nav item active state
document.querySelectorAll(".nav-item").forEach((item) => {
item.addEventListener("click", () => {
document
.querySelectorAll(".nav-item")
.forEach((i) => i.classList.remove("active"));
item.classList.add("active");
});
});

// User button
document.getElementById("user-btn").addEventListener("click", () => {
alert("User menu – a real app would open a dropdown here.");
});
</script>
</body>
</html>
Loading
Loading