Mini-Map (Embedded Floor Plan Preview)
A floor plan embedded in a container instead of fullscreen — the web counterpart to the iOS/Android mini-map pattern. Scope the SDK to a target element with element, hide the default chrome inside onInit, then drive it from the host page with selectBooth, selectExhibitor, or zoomTo.
Code
html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<title>ExpoFP Zoom Demo</title>
<style>
* {
box-sizing: border-box;
}
html,
body {
margin: 0;
padding: 0;
min-height: 100%;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #f5f5f7;
color: #1d1d1f;
}
.screen {
min-height: 100vh;
padding: 24px 16px;
padding-top: max(24px, env(safe-area-inset-top));
padding-bottom: max(24px, env(safe-area-inset-bottom));
display: flex;
flex-direction: column;
visibility: hidden;
opacity: 0;
pointer-events: none;
position: absolute;
inset: 0;
transition: opacity 0.3s ease;
}
.screen.active {
visibility: visible;
opacity: 1;
pointer-events: auto;
}
.welcome-title {
font-size: 28px;
font-weight: 700;
text-align: center;
margin: 0 0 8px 0;
}
.welcome-subtitle {
font-size: 15px;
color: #86868b;
text-align: center;
margin: 0 0 32px 0;
}
.toggle-container {
display: flex;
justify-content: center;
margin-bottom: 24px;
}
.toggle {
display: flex;
background: #e8e8ed;
border-radius: 20px;
padding: 4px;
gap: 4px;
}
.toggle-option {
padding: 10px 20px;
border-radius: 16px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
border: none;
background: transparent;
color: #86868b;
}
.toggle-option.active {
background: #fff;
color: #1d1d1f;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.cards-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12px;
margin-bottom: 24px;
}
.card {
background: #fff;
border-radius: 12px;
padding: 20px 16px;
text-align: center;
cursor: pointer;
border: 2px solid transparent;
transition: all 0.2s ease;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
.card:active {
transform: scale(0.98);
}
.card.selected {
border-color: #0071e3;
background: #f0f7ff;
}
.card-label {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
color: #86868b;
margin-bottom: 6px;
}
.card-name {
font-size: 16px;
font-weight: 600;
}
.spacer {
flex: 1;
}
.btn {
width: 100%;
padding: 16px 24px;
border-radius: 12px;
font-size: 17px;
font-weight: 600;
cursor: pointer;
border: none;
transition: all 0.2s ease;
min-height: 50px;
}
.btn-primary {
background: #0071e3;
color: #fff;
}
.btn-primary:active {
background: #005bb5;
}
.demo-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 16px;
flex-wrap: wrap;
}
.btn-back {
background: #e8e8ed;
color: #1d1d1f;
padding: 10px 16px;
border-radius: 10px;
font-size: 15px;
font-weight: 500;
border: none;
cursor: pointer;
transition: background 0.2s ease;
}
.btn-back:active {
background: #d1d1d6;
}
.demo-title {
font-size: 20px;
font-weight: 700;
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.mode-badge {
font-size: 12px;
font-weight: 600;
padding: 6px 12px;
border-radius: 20px;
background: #e8e8ed;
color: #86868b;
}
#floorplan {
flex: 1;
min-height: 300px;
background: #fff;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
/* opt out of SDK fullscreen defaults so this fits in the flex container */
position: relative;
inset: auto;
width: auto;
height: auto;
}
.demo-description {
margin-top: 16px;
padding: 16px;
background: #fff;
border-radius: 12px;
font-size: 14px;
line-height: 1.5;
color: #515154;
}
@media (min-width: 480px) {
.screen {
padding: 32px 24px;
max-width: 500px;
margin: 0 auto;
}
.welcome-title {
font-size: 34px;
}
}
</style>
</head>
<body>
<div id="screen-select" class="screen active">
<h1 class="welcome-title">Zoom Demo</h1>
<p class="welcome-subtitle">Choose a mode and select a booth or exhibitor to explore</p>
<div class="toggle-container">
<div class="toggle">
<button class="toggle-option active" data-mode="selectBooth">selectBooth</button>
<button class="toggle-option" data-mode="zoomTo">zoomTo</button>
</div>
</div>
<div class="cards-grid">
<div class="card selected" data-type="booth" data-value="1">
<div class="card-label">Booth</div>
<div class="card-name">Booth 1</div>
</div>
<div class="card" data-type="booth" data-value="11.1-25">
<div class="card-label">Booth</div>
<div class="card-name">Booth 11.1-25</div>
</div>
<div class="card" data-type="exhibitor" data-value="Sustainable Horizons Inc.">
<div class="card-label">Exhibitor</div>
<div class="card-name">Sustainable Horizons</div>
</div>
<div class="card" data-type="exhibitor" data-value="TKNL">
<div class="card-label">Exhibitor</div>
<div class="card-name">TKNL</div>
</div>
</div>
<div class="spacer"></div>
<button id="btn-next" class="btn btn-primary">Next</button>
</div>
<div id="screen-demo" class="screen">
<div class="demo-header">
<button id="btn-back" class="btn-back">Back</button>
<div class="demo-title" id="demo-title"></div>
<span class="mode-badge" id="mode-badge"></span>
</div>
<div id="floorplan">Loading...</div>
<div class="demo-description">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
laboris
</div>
</div>
<script type="module">
import { load } from 'https://unpkg.com/@expofp/floorplan';
let state = {
mode: 'selectBooth',
selectedType: 'booth',
selectedValue: '1',
selectedName: 'Booth 1',
};
let fpPromise = null;
const screenSelect = document.getElementById('screen-select');
const screenDemo = document.getElementById('screen-demo');
const btnNext = document.getElementById('btn-next');
const btnBack = document.getElementById('btn-back');
const demoTitle = document.getElementById('demo-title');
const modeBadge = document.getElementById('mode-badge');
const toggleOptions = document.querySelectorAll('.toggle-option');
const cards = document.querySelectorAll('.card');
toggleOptions.forEach((option) => {
option.addEventListener('click', () => {
state.mode = option.dataset.mode;
toggleOptions.forEach((opt) => {
opt.classList.toggle('active', opt === option);
});
});
});
cards.forEach((card) => {
card.addEventListener('click', () => {
cards.forEach((c) => c.classList.remove('selected'));
card.classList.add('selected');
state.selectedType = card.dataset.type;
state.selectedValue = card.dataset.value;
state.selectedName = card.querySelector('.card-name').textContent;
});
});
btnNext.addEventListener('click', () => {
demoTitle.textContent = state.selectedName;
modeBadge.textContent = state.mode;
screenSelect.classList.remove('active');
screenDemo.classList.add('active');
applyAction();
});
btnBack.addEventListener('click', async () => {
screenDemo.classList.remove('active');
screenSelect.classList.add('active');
if (fpPromise) {
const fp = await fpPromise;
fp.selectBooth('');
fp.fitBounds();
}
});
function initFloorPlan() {
if (fpPromise) return fpPromise;
fpPromise = load(
{ $ref: 'https://demo.expofp.com/manifest.json' },
{
element: document.getElementById('floorplan'),
noOverlay: true,
onInit: (fp) => {
fp.setVisibility({
controls: false,
levels: false,
header: false,
overlay: false,
});
},
},
);
return fpPromise;
}
async function applyAction() {
const fp = await initFloorPlan();
if (state.mode === 'selectBooth') {
if (state.selectedType === 'booth') {
fp.selectBooth(state.selectedValue);
} else {
fp.selectExhibitor(state.selectedValue);
}
} else if (state.mode === 'zoomTo') {
if (state.selectedType === 'booth') {
fp.zoomTo({ booths: [{ name: state.selectedValue }] });
} else {
fp.zoomTo({ exhibitors: [{ name: state.selectedValue }] });
}
}
}
initFloorPlan();
</script>
</body>
</html>