Svelte Integration: Complete Example
A Svelte component for the Wedding Band Builder.
Component
svelte
<!-- WeddingBandBuilder.svelte -->
<script>
import { onMount, onDestroy, createEventDispatcher } from 'svelte';
export let basePath = 'https://your-cdn.com/wbb-assets/';
export let manifestUrl = 'wedding-band-project.json';
export let showUI = true;
export let theme = null;
const dispatch = createEventDispatcher();
let container;
let api = null;
let price = null;
const SCRIPT_URL = 'https://releases.ijewel3d.com/libs/mini-viewer/latest/bundle.iife.js';
function loadScript() {
if (window.ijewelViewer) return Promise.resolve();
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = SCRIPT_URL;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
function handleReady(e) {
const viewer = e.detail.viewer;
api = viewer.getPluginByType('WeddingBandBuilder')?.controller;
if (!api) return;
if (theme) api.setTheme(theme);
api.events.on('price:updated', (data) => {
if (data.pricing) {
price = data.pricing;
dispatch('pricechange', data.pricing);
}
});
dispatch('ready', api);
}
onMount(async () => {
await loadScript();
window.addEventListener('ijewel-viewer-ready', handleReady);
new window.ijewelViewer.Viewer(container, {
name: 'Wedding Band Builder',
version: 'v5',
basePath,
plugins: {
WeddingBandBuilder: { manifestUrl, showUI },
},
}, {
showCard: false,
showSwitchNode: false,
showUiButtons: showUI,
showConfigurator: false,
showZoomButtons: showUI,
enableZoom: true,
});
});
onDestroy(() => {
window.removeEventListener('ijewel-viewer-ready', handleReady);
});
export function getApi() { return api; }
</script>
<div class="wbb-container">
<div bind:this={container} class="wbb-viewer" />
{#if price}
<slot name="price" {price}>
<div class="wbb-price">${price.totalUsd.toFixed(2)}</div>
</slot>
{/if}
</div>
<style>
.wbb-container { width: 100%; height: 100%; position: relative; }
.wbb-viewer { width: 100%; height: 100%; }
.wbb-price {
position: absolute;
bottom: 16px;
right: 16px;
font-size: 24px;
font-weight: 600;
background: rgba(0,0,0,0.7);
color: #fff;
padding: 8px 16px;
border-radius: 8px;
}
</style>Usage
svelte
<!-- App.svelte -->
<script>
import WeddingBandBuilder from './WeddingBandBuilder.svelte';
let api;
function onReady(e) {
api = e.detail;
console.log('Profiles:', api.getAvailableProfiles());
}
function onPrice(e) {
console.log(`Total: $${e.detail.totalUsd.toFixed(2)}`);
}
</script>
<div style="width: 100vw; height: 100vh;">
<WeddingBandBuilder
theme="luxury-gold"
on:ready={onReady}
on:pricechange={onPrice}
/>
</div>Headless with Custom Controls
svelte
<script>
import WeddingBandBuilder from './WeddingBandBuilder.svelte';
let api;
let price = null;
let width = 4.0;
let activeProfile = 0;
$: profiles = api?.getAvailableProfiles() || [];
$: metals = api?.getAvailableMetals() || [];
async function selectProfile(index) {
await api.setProfile(index);
activeProfile = index;
}
function onWidthChange() {
api?.setWidth(width);
}
</script>
<div class="app">
<div class="viewer">
<WeddingBandBuilder
showUI={false}
on:ready={(e) => { api = e.detail; }}
on:pricechange={(e) => { price = e.detail; }}
/>
</div>
{#if api}
<div class="controls">
<div class="row">
{#each profiles as p}
<button
class:active={activeProfile === p.index}
on:click={() => selectProfile(p.index)}
>
{p.name}
</button>
{/each}
</div>
<div class="row">
{#each metals as m}
<button on:click={() => api.setMaterial(1, m.id, 'Polished')}>
{m.name}
</button>
{/each}
</div>
<div class="row">
<input type="range" min="2" max="10" step="0.1" bind:value={width} on:input={onWidthChange} />
<span>{width.toFixed(1)} mm</span>
</div>
{#if price}
<div class="price">${price.totalUsd.toFixed(2)}</div>
{/if}
</div>
{/if}
</div>
<style>
.app { display: flex; flex-direction: column; height: 100vh; }
.viewer { flex: 1; }
.controls { padding: 16px; background: #fff; border-top: 1px solid #eee; }
.row { display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; }
.price { font-size: 24px; font-weight: 600; margin-top: 8px; }
button { padding: 8px 16px; border: 1px solid #ddd; border-radius: 6px; background: #fff; cursor: pointer; }
button.active { background: #333; color: #fff; border-color: #333; }
</style>