UNPKG

lightview

Version:

A reactive UI library with features of Bau, Juris, and HTMX plus safe LLM UI generation

556 lines (517 loc) 29.4 kB
<!-- SEO-friendly SPA Shim --> <script src="/lightview-router.js"></script> <script> if (globalThis.LightviewRouter) { LightviewRouter.base('/index.html'); } </script> <!-- Load the page-specific stylesheet --> <link rel="stylesheet" href="./index.css"> <!-- Load DaisyUI and Avatar Component --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/daisyui@3.9.4/dist/full.min.css"> <script type="module" src="/components/data-display/avatar.js"></script> <!-- Gallery Structure --> <div class="gallery-page"> <div class="gallery-layout"> <!-- Sidebar Overlay --> <div id="sidebar-overlay" class="sidebar-overlay"></div> <!-- Sidebar --> <div id="gallery-sidebar" class="gallery-sidebar" style="visibility: hidden" src="./component-nav.html"></div> <!-- Main Content --> <div id="gallery-main" class="gallery-main"> <!-- Header Container --> <div style="position: sticky; top: 0; z-index: 30; background: var(--gallery-surface); border-bottom: 1px solid var(--gallery-border); backdrop-filter: blur(8px);"> <!-- Breadcrumbs Row --> <div style="padding: 0.75rem 1.5rem 0;"> <script> (() => { const { Breadcrumbs } = Lightview.tags; const breadcrumbs = Breadcrumbs({ id: 'page-breadcrumbs', items: [ { label: 'Components', href: '/docs/components' }, { label: 'Avatar' } ] }); document.currentScript.replaceWith(breadcrumbs.domEl); })(); </script> </div> <!-- Title Row --> <div class="gallery-header" style="border-bottom: none; height: auto; padding-top: 0.5rem; padding-bottom: 0.75rem;"> <button id="toggle-btn" class="toggle-btn" aria-label="Toggle Sidebar"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="toggle-icon" style="stroke: currentColor; stroke-width: 2;"> <path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7" /> <path stroke-linecap="round" stroke-linejoin="round" d="M11 19l-7-7 7-7" /> </svg> </button> <h1 class="gallery-title">Avatar</h1> </div> </div> <!-- Content --> <div class="gallery-content"> <div class="section-content" style="max-width: 1000px;"> <p class="text-lg" style="opacity: 0.7; margin-bottom: 1.5rem;"> Avatar is used to show a thumbnail representation of an individual or business. Supports images, placeholders, status indicators, and grouping. </p> <!-- Basic Usage --> <div class="card bg-base-200" style="margin-bottom: 2rem;"> <div class="card-body"> <h2 class="card-title">Basic Usage</h2> <p class="text-sm" style="opacity: 0.7; margin-bottom: 1rem;">Avatars with different shapes and features.</p> <!-- Tabs --> <script> globalThis.switchSyntaxTab = (tabId) => { const tabs = ['tagged', 'vdom', 'object', 'html']; tabs.forEach(t => { const tabEl = document.getElementById(`tab-btn-${t}`); const contentEl = document.getElementById(`syntax-${t}`); if (t === tabId) { tabEl.classList.add('syntax-tab-active'); contentEl.style.display = 'block'; } else { tabEl.classList.remove('syntax-tab-active'); contentEl.style.display = 'none'; } }); }; </script> <div role="tablist" class="syntax-tabs" style="margin-bottom: 1rem;"> <button id="tab-btn-tagged" role="tab" class="syntax-tab syntax-tab-active" onclick="switchSyntaxTab('tagged')">Tagged</button> <button id="tab-btn-vdom" role="tab" class="syntax-tab" onclick="switchSyntaxTab('vdom')">vDOM</button> <button id="tab-btn-object" role="tab" class="syntax-tab" onclick="switchSyntaxTab('object')">Object DOM</button> <button id="tab-btn-html" role="tab" class="syntax-tab" onclick="switchSyntaxTab('html')">HTML</button> </div> <!-- Tagged Syntax --> <div id="syntax-tagged"> <pre><script> examplify(document.currentScript.nextElementSibling, { at: document.currentScript.parentElement, scripts: ['/lightview.js', '/lightview-x.js'], styles: ['https://cdn.jsdelivr.net/npm/daisyui@3.9.4/dist/full.min.css'], type: 'module', minHeight: 75, autoRun: true }); </script><code contenteditable="true">await import('/components/data-display/avatar.js'); const { tags, $ } = Lightview; const { div, Avatar } = tags; const avatars = div({ style: 'display: flex; gap: 1rem; align-items: center; flex-wrap: wrap;' }, // Image avatar - default circle Avatar({ src: 'https://i.pravatar.cc/100?img=1', alt: 'User 1', size: 'w-16' }), // Squircle shape Avatar({ src: 'https://i.pravatar.cc/100?img=2', size: 'w-16', shape: 'squircle' }), // Hexagon shape Avatar({ src: 'https://i.pravatar.cc/100?img=3', size: 'w-16', shape: 'hexagon' }), // Rounded shape Avatar({ src: 'https://i.pravatar.cc/100?img=4', size: 'w-16', shape: 'rounded' }), // With ring Avatar({ src: 'https://i.pravatar.cc/100?img=5', size: 'w-16', ring: true, ringColor: 'ring-primary' }), // Online status Avatar({ src: 'https://i.pravatar.cc/100?img=6', size: 'w-16', online: true }) ); $('#example').content(avatars);</code></pre> </div> <!-- vDOM Syntax --> <div id="syntax-vdom" style="display: none;"> <pre><script> examplify(document.currentScript.nextElementSibling, { at: document.currentScript.parentElement, scripts: ['/lightview.js', '/lightview-x.js'], styles: ['https://cdn.jsdelivr.net/npm/daisyui@3.9.4/dist/full.min.css'], type: 'module', minHeight: 75 }); </script><code contenteditable="true">await import('/components/data-display/avatar.js'); const { $, tags } = Lightview; const { div, Avatar } = tags; const avatars = { tag: div, attributes: { style: 'display: flex; gap: 1rem; align-items: center; flex-wrap: wrap;' }, children: [ { tag: Avatar, attributes: { src: 'https://i.pravatar.cc/100?img=1', alt: 'User 1', size: 'w-16' } }, { tag: Avatar, attributes: { src: 'https://i.pravatar.cc/100?img=2', size: 'w-16', shape: 'squircle' } }, { tag: Avatar, attributes: { src: 'https://i.pravatar.cc/100?img=3', size: 'w-16', shape: 'hexagon' } }, { tag: Avatar, attributes: { src: 'https://i.pravatar.cc/100?img=5', size: 'w-16', ring: true, ringColor: 'ring-primary' } }, { tag: Avatar, attributes: { placeholder: 'JD', size: 'w-16' } }, { tag: Avatar, attributes: { src: 'https://i.pravatar.cc/100?img=4', size: 'w-16', online: true } } ] }; $('#example').content(avatars);</code></pre> </div> <!-- Object DOM Syntax --> <div id="syntax-object" style="display: none;"> <pre><script> examplify(document.currentScript.nextElementSibling, { at: document.currentScript.parentElement, scripts: ['/lightview.js', '/lightview-x.js'], styles: ['https://cdn.jsdelivr.net/npm/daisyui@3.9.4/dist/full.min.css'], type: 'module', minHeight: 75 }); </script><code contenteditable="true">await import('/components/data-display/avatar.js'); const { $ } = Lightview; const avatars = { div: { style: 'display: flex; gap: 1rem; align-items: center; flex-wrap: wrap;', children: [ { Avatar: { src: 'https://i.pravatar.cc/100?img=1', alt: 'User 1', size: 'w-16' } }, { Avatar: { src: 'https://i.pravatar.cc/100?img=2', size: 'w-16', shape: 'squircle' } }, { Avatar: { src: 'https://i.pravatar.cc/100?img=3', size: 'w-16', shape: 'hexagon' } }, { Avatar: { src: 'https://i.pravatar.cc/100?img=5', size: 'w-16', ring: true, ringColor: 'ring-primary' } }, { Avatar: { placeholder: 'JD', size: 'w-16' } }, { Avatar: { src: 'https://i.pravatar.cc/100?img=4', size: 'w-16', online: true } } ] } }; $('#example').content(avatars);</code></pre> </div> <!-- HTML Syntax --> <div id="syntax-html" style="display: none;"> <pre><script> examplify(document.currentScript.nextElementSibling, { at: document.currentScript.parentElement, scripts: ['/lightview.js', '/lightview-x.js'], styles: ['https://cdn.jsdelivr.net/npm/daisyui@3.9.4/dist/full.min.css'], type: 'module', language: 'html', minHeight: 75 }); </script><code contenteditable="true" class="language-html">&lt;!-- Import the component (registers lv-avatar) --&gt; &lt;script type="module" src="/components/data-display/avatar.js"&gt;&lt;/script&gt; &lt;div style="display: flex; gap: 1rem; align-items: center; flex-wrap: wrap;"&gt; &lt;lv-avatar src="https://i.pravatar.cc/100?img=1" size="w-16"&gt;&lt;/lv-avatar&gt; &lt;lv-avatar src="https://i.pravatar.cc/100?img=2" size="w-16" shape="squircle"&gt;&lt;/lv-avatar&gt; &lt;lv-avatar src="https://i.pravatar.cc/100?img=3" size="w-16" shape="hexagon"&gt;&lt;/lv-avatar&gt; &lt;lv-avatar src="https://i.pravatar.cc/100?img=5" size="w-16" ring ring-color="ring-primary"&gt;&lt;/lv-avatar&gt; &lt;lv-avatar placeholder="JD" size="w-16"&gt;&lt;/lv-avatar&gt; &lt;lv-avatar src="https://i.pravatar.cc/100?img=4" size="w-16" online&gt;&lt;/lv-avatar&gt; &lt;/div&gt;</code></pre> </div> </div> </div> <!-- Reactive Example --> <div class="card bg-base-200" style="margin-bottom: 2rem;"> <div class="card-body"> <h2 class="card-title">User Status Toggle</h2> <p class="text-sm" style="opacity: 0.7; margin-bottom: 1rem;">Avatar with reactive online/offline status </p> <!-- Tabs --> <script> globalThis.switchReactiveSyntaxTab = (tabId) => { const tabs = ['tagged', 'vdom', 'object']; tabs.forEach(t => { const tabEl = document.getElementById(`reactive-tab-btn-${t}`); const contentEl = document.getElementById(`reactive-syntax-${t}`); if (t === tabId) { tabEl.classList.add('syntax-tab-active'); contentEl.style.display = 'block'; } else { tabEl.classList.remove('syntax-tab-active'); contentEl.style.display = 'none'; } }); }; </script> <div role="tablist" class="syntax-tabs" style="margin-bottom: 1rem;"> <button id="reactive-tab-btn-tagged" role="tab" class="syntax-tab syntax-tab-active" onclick="switchReactiveSyntaxTab('tagged')">Tagged</button> <button id="reactive-tab-btn-vdom" role="tab" class="syntax-tab" onclick="switchReactiveSyntaxTab('vdom')">vDOM</button> <button id="reactive-tab-btn-object" role="tab" class="syntax-tab" onclick="switchReactiveSyntaxTab('object')">Object DOM</button> </div> <!-- Tagged Syntax --> <div id="reactive-syntax-tagged"> <pre><script> examplify(document.currentScript.nextElementSibling, { at: document.currentScript.parentElement, scripts: ['/lightview.js', '/lightview-x.js'], styles: ['https://cdn.jsdelivr.net/npm/daisyui@3.9.4/dist/full.min.css'], autoRun: true, type: 'module', minHeight: 120 }); </script><code contenteditable="true">await import('/components/data-display/avatar.js'); await import('/components/actions/button.js'); const { signal, tags, $ } = Lightview; const { div, span, Avatar, Button } = tags; const isOnline = signal(true); const demo = div({ style: 'display: flex; align-items: center; gap: 1.5rem;' }, () => Avatar({ src: 'https://i.pravatar.cc/100?img=5', size: 'w-20', ring: true, ringColor: isOnline.value ? 'ring-success' : 'ring-error', online: isOnline.value, offline: !isOnline.value }), div({ style: 'display: flex; flex-direction: column; gap: 0.5rem;' }, span({ style: 'font-weight: 700;' }, 'Sarah Johnson'), span({ style: 'font-size: 0.875rem;' }, () => isOnline.value ? '🟢 Online' : '⚫ Offline' ), Button({ size: 'sm', onclick: () => { isOnline.value = !isOnline.value; } }, () => isOnline.value ? 'Go Offline' : 'Go Online') ) ); $('#example').content(demo);</code></pre> </div> <!-- vDOM Syntax --> <div id="reactive-syntax-vdom" style="display: none;"> <pre><script> examplify(document.currentScript.nextElementSibling, { at: document.currentScript.parentElement, scripts: ['/lightview.js', '/lightview-x.js'], styles: ['https://cdn.jsdelivr.net/npm/daisyui@3.9.4/dist/full.min.css'], type: 'module', minHeight: 120 }); </script><code contenteditable="true">await import('/components/data-display/avatar.js'); await import('/components/actions/button.js'); const { signal, $, tags } = Lightview; const { div, span, Avatar, Button } = tags; const isOnline = signal(true); const demo = { tag: div, attributes: { style: 'display: flex; align-items: center; gap: 1.5rem;' }, children: [ () => ({ tag: Avatar, attributes: { src: 'https://i.pravatar.cc/100?img=5', size: 'w-20', ring: true, ringColor: isOnline.value ? 'ring-success' : 'ring-error', online: isOnline.value, offline: !isOnline.value } }), { tag: div, attributes: { style: 'display: flex; flex-direction: column; gap: 0.5rem;' }, children: [ { tag: span, attributes: { style: 'font-weight: 700;' }, children: ['Sarah Johnson'] }, { tag: span, attributes: { style: 'font-size: 0.875rem;' }, children: [() => isOnline.value ? '🟢 Online' : '⚫ Offline'] }, { tag: Button, attributes: { size: 'sm', onclick: () => { isOnline.value = !isOnline.value; } }, children: [() => isOnline.value ? 'Go Offline' : 'Go Online'] } ] } ] }; $('#example').content(demo);</code></pre> </div> <!-- Object DOM Syntax --> <div id="reactive-syntax-object" style="display: none;"> <pre><script> examplify(document.currentScript.nextElementSibling, { at: document.currentScript.parentElement, scripts: ['/lightview.js', '/lightview-x.js'], styles: ['https://cdn.jsdelivr.net/npm/daisyui@3.9.4/dist/full.min.css'], type: 'module', minHeight: 120 }); </script><code contenteditable="true">await import('/components/data-display/avatar.js'); await import('/components/actions/button.js'); const { signal, $ } = Lightview; const isOnline = signal(true); const demo = { div: { style: 'display: flex; align-items: center; gap: 1.5rem;', children: [ () => ({ Avatar: { src: 'https://i.pravatar.cc/100?img=5', size: 'w-20', ring: true, ringColor: isOnline.value ? 'ring-success' : 'ring-error', online: isOnline.value, offline: !isOnline.value } }), { div: { style: 'display: flex; flex-direction: column; gap: 0.5rem;', children: [ { span: { style: 'font-weight: 700;', children: ['Sarah Johnson'] } }, { span: { style: 'font-size: 0.875rem;', children: [() => isOnline.value ? '🟢 Online' : '⚫ Offline'] } }, { Button: { size: 'sm', onclick: () => { isOnline.value = !isOnline.value; }, children: [() => isOnline.value ? 'Go Offline' : 'Go Online'] } } ] } } ] } }; $('#example').content(demo);</code></pre> </div> </div> </div> <!-- Props Table --> <h2 class="text-xl font-bold" style="margin-bottom: 1rem;">Props</h2> <div style="overflow-x: auto; margin-bottom: 2rem;"> <table class="table table-zebra"> <thead> <tr> <th>Prop</th> <th>Type</th> <th>Default</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td><code>src</code></td> <td>string</td> <td>-</td> <td>Image source URL</td> </tr> <tr> <td><code>alt</code></td> <td>string</td> <td>'Avatar'</td> <td>Alt text for image</td> </tr> <tr> <td><code>placeholder</code></td> <td>string</td> <td>-</td> <td>Text to show when no image (e.g., initials)</td> </tr> <tr> <td><code>size</code></td> <td>string</td> <td>'w-12'</td> <td>Width class (w-8, w-12, w-16, w-24, etc.)</td> </tr> <tr> <td><code>shape</code></td> <td>string</td> <td>'circle'</td> <td>'circle' | 'rounded' | 'squircle' | 'hexagon' | 'triangle'</td> </tr> <tr> <td><code>ring</code></td> <td>boolean</td> <td>false</td> <td>Add ring border</td> </tr> <tr> <td><code>ringColor</code></td> <td>string</td> <td>'ring-primary'</td> <td>Ring color class</td> </tr> <tr> <td><code>online</code></td> <td>boolean</td> <td>false</td> <td>Show online indicator</td> </tr> <tr> <td><code>offline</code></td> <td>boolean</td> <td>false</td> <td>Show offline indicator</td> </tr> </tbody> </table> </div> <!-- Custom Element Gallery --> <h2 class="text-xl font-bold" style="margin-top: 2rem; margin-bottom: 1rem;">Avatar Gallery</h2> <p class="text-sm" style="opacity: 0.7; margin-bottom: 1.5rem;"> Live examples using <code>&lt;lv-avatar&gt;</code> custom elements. </p> <script type="module" src="/components/data-display/avatar.js"></script> <!-- Shapes --> <h3 class="text-lg font-semibold" style="margin-bottom: 0.75rem;">Shapes</h3> <div style="display: flex; gap: 1rem; flex-wrap: wrap; margin-bottom: 2rem;"> <lv-avatar src="https://i.pravatar.cc/100?img=1" size="w-16" shape="circle"></lv-avatar> <lv-avatar src="https://i.pravatar.cc/100?img=2" size="w-16" shape="rounded"></lv-avatar> <lv-avatar src="https://i.pravatar.cc/100?img=3" size="w-16" shape="squircle"></lv-avatar> <lv-avatar src="https://i.pravatar.cc/100?img=4" size="w-16" shape="hexagon"></lv-avatar> <lv-avatar src="https://i.pravatar.cc/100?img=5" size="w-16" shape="triangle"></lv-avatar> </div> <!-- Online/Offline Status --> <h3 class="text-lg font-semibold" style="margin-bottom: 0.75rem;">Status Indicators</h3> <div style="display: flex; gap: 1rem; margin-bottom: 2rem;"> <lv-avatar src="https://i.pravatar.cc/100?img=7" size="w-16" online></lv-avatar> <lv-avatar src="https://i.pravatar.cc/100?img=8" size="w-16" offline></lv-avatar> <lv-avatar src="https://i.pravatar.cc/100?img=9" size="w-16" ring ring-color="ring-primary"></lv-avatar> </div> <!-- Placeholders --> <h3 class="text-lg font-semibold" style="margin-bottom: 0.75rem;">Placeholders</h3> <div style="display: flex; gap: 1rem; align-items: center; margin-bottom: 2rem;"> <lv-avatar placeholder="AI" size="w-12" style="--lv-avatar-bg: oklch(var(--n)); --lv-avatar-text: oklch(var(--nc));"></lv-avatar> <lv-avatar placeholder="JD" size="w-16" shape="squircle" style="--lv-avatar-bg: oklch(var(--p)); --lv-avatar-text: oklch(var(--pc));"></lv-avatar> <lv-avatar placeholder="LR" size="w-20" shape="hexagon" style="--lv-avatar-bg: oklch(var(--s)); --lv-avatar-text: oklch(var(--sc));"></lv-avatar> </div> <!-- Grouping --> <h3 class="text-lg font-semibold" style="margin-bottom: 0.75rem;">Avatar Group</h3> <div style="margin-bottom: 2rem;"> <lv-avatar-group> <lv-avatar src="https://i.pravatar.cc/100?img=10" size="w-12"></lv-avatar> <lv-avatar src="https://i.pravatar.cc/100?img=11" size="w-12"></lv-avatar> <lv-avatar src="https://i.pravatar.cc/100?img=12" size="w-12"></lv-avatar> <lv-avatar placeholder="+99" size="w-12" style="--lv-avatar-bg: oklch(var(--n)); --lv-avatar-text: oklch(var(--nc));"></lv-avatar> </lv-avatar-group> </div> </div> </div> </div> </div>