@zeix/ui-element
Version:
UIElement - a HTML-first library for reactive Web Components
436 lines (398 loc) • 11.1 kB
Markdown
---
title: 'Examples'
emoji: '🍽️'
description: 'Common use cases and demos'
---
<section-hero>
# 🍽️ Examples & Recipes
<div>
<p class="lead">Discover practical examples and patterns for building reactive, modular components with UIElement. Each example focuses on showcasing a specific feature or best practice, guiding you through real-world use cases.</p>
{{ toc }}
</div>
</section-hero>
<section>
## Counter
<module-demo>
<div class="preview">
<basic-counter>
<button type="button">💐 <span>5</span></button>
</basic-counter>
</div>
<details>
<summary>Source Code</summary>
<module-lazy src="./examples/basic-counter.html">
<card-callout>
<p class="loading" role="status">Loading...</p>
<p class="error" role="alert" aria-live="polite"></p>
</card-callout>
</module-lazy>
</details>
</module-demo>
</section>
<section>
## Carousel
<module-demo>
<div class="preview">
<module-carousel>
<h2 class="visually-hidden">Slides</h2>
<div class="slides">
<div id="slide1" role="tabpanel" aria-current="true" style="background: var(--color-blue-20);">
<h3>Slide 1</h3>
<hello-world>
<label>Your name<br>
<input type="text">
</label>
<p>Hello, <span>World</span>!</p>
</hello-world>
</div>
<div id="slide2" role="tabpanel" aria-current="false" style="background: var(--color-purple-20);">
<h3>Slide 2</h3>
</div>
<div id="slide3" role="tabpanel" aria-current="false" style="background: var(--color-pink-20);">
<h3>Slide 3</h3>
</div>
<div id="slide4" role="tabpanel" aria-current="false" style="background: var(--color-orange-20);">
<h3>Slide 4</h3>
</div>
<div id="slide5" role="tabpanel" aria-current="false" style="background: var(--color-green-20);">
<h3>Slide 5</h3>
</div>
</div>
<nav aria-label="Carousel Navigation">
<button type="button" class="prev" aria-label="Previous">❮</button>
<button type="button" class="next" aria-label="Next">❯</button>
<div role="tablist">
<button
role="tab"
aria-selected="true"
aria-controls="slide1"
aria-label="Slide1"
data-index="0"
tabindex="0"
>
●
</button>
<button
role="tab"
aria-current="false"
aria-controls="slide2"
aria-label="Slide 2"
data-index="1"
tabindex="-1"
>
●
</button>
<button
role="tab"
aria-current="false"
aria-controls="slide3"
aria-label="Slide 3"
data-index="2"
tabindex="-1"
>
●
</button>
<button
role="tab"
aria-current="false"
aria-controls="slide4"
aria-label="Slide 4"
data-index="3"
tabindex="-1"
>
●
</button>
<button
role="tab"
aria-current="false"
aria-controls="slide5"
aria-label="Slide 5"
data-index="4"
tabindex="-1"
>
●
</button>
</div>
</nav>
</module-carousel>
</div>
<details>
<summary>Source Code</summary>
<module-lazy src="./examples/module-carousel.html">
<card-callout>
<p class="loading" role="status">Loading...</p>
<p class="error" role="alert" aria-live="polite"></p>
</card-callout>
</module-lazy>
</details>
</module-demo>
</section>
<section>
## Combobox
<module-demo>
<div class="preview">
<form-combobox value="">
<label for="city-input">Choose a city</label>
<div class="input">
<input
id="city-input"
type="text"
role="combobox"
aria-expanded="false"
aria-controls="city-popup"
aria-autocomplete="list"
autocomplete="off"
required
/>
<ol id="city-popup" role="listbox" hidden>
<li role="option" tabindex="-1">Amsterdam</li>
<li role="option" tabindex="-1">Berlin</li>
<li role="option" tabindex="-1">Copenhagen</li>
<li role="option" tabindex="-1">Dublin</li>
<li role="option" tabindex="-1">Edinburgh</li>
<li role="option" tabindex="-1">Frankfurt</li>
<li role="option" tabindex="-1">Geneva</li>
<li role="option" tabindex="-1">Helsinki</li>
<li role="option" tabindex="-1">Istanbul</li>
<li role="option" tabindex="-1">Jakarta</li>
<li role="option" tabindex="-1">Kairo</li>
<li role="option" tabindex="-1">London</li>
<li role="option" tabindex="-1">Madrid</li>
<li role="option" tabindex="-1">New York</li>
<li role="option" tabindex="-1">Oslo</li>
<li role="option" tabindex="-1">Paris</li>
<li role="option" tabindex="-1">Qingdao</li>
<li role="option" tabindex="-1">Rome</li>
<li role="option" tabindex="-1">Stockholm</li>
<li role="option" tabindex="-1">Tokyo</li>
<li role="option" tabindex="-1">Ulan Bator</li>
<li role="option" tabindex="-1">Vienna</li>
<li role="option" tabindex="-1">Warsaw</li>
<li role="option" tabindex="-1">Xi'an</li>
<li role="option" tabindex="-1">Yokohama</li>
<li role="option" tabindex="-1">Zurich</li>
</ol>
<button type="button" class="clear" aria-label="Clear input" hidden>
✕
</button>
</div>
<p class="error" aria-live="assertive" id="city-error"></p>
<p class="description" aria-live="polite" id="city-description">Tell us where you live so we can set your timezone for our calendar and notification features.</p>
</form-combobox>
</div>
<details>
<summary>Source Code</summary>
<module-lazy src="./examples/form-combobox.html">
<card-callout>
<p class="loading" role="status">Loading...</p>
<p class="error" role="alert" aria-live="polite"></p>
</card-callout>
</module-lazy>
</details>
</module-demo>
</section>
<section>
## Todo App
<module-demo>
<div class="preview">
<module-todo>
<form action="#">
<form-textbox>
<label for="add-todo">What needs to be done?</label>
<div class="input">
<input id="add-todo" type="text" value="" />
</div>
</form-textbox>
<basic-button class="submit">
<button type="submit" class="constructive" disabled>
Add Todo
</button>
</basic-button>
</form>
<ol filter="all"></ol>
<template>
<li>
<form-checkbox class="todo">
<label>
<input type="checkbox" class="visually-hidden" />
<span class="label"><slot></slot></span>
</label>
</form-checkbox>
<basic-button class="delete">
<button type="button" class="destructive small">Delete</button>
</basic-button>
</li>
</template>
<footer>
<div class="todo-count">
<p class="all-done">Well done, all done!</p>
<p class="remaining">
<span class="count"></span>
<span class="singular">task</span>
<span class="plural">tasks</span>
remaining
</p>
</div>
<form-radiogroup value="all" class="split-button">
<fieldset>
<legend class="visually-hidden">Filter</legend>
<label class="selected">
<input
type="radio"
class="visually-hidden"
name="filter"
value="all"
checked
/>
<span>All</span>
</label>
<label>
<input
type="radio"
class="visually-hidden"
name="filter"
value="active"
/>
<span>Active</span>
</label>
<label>
<input
type="radio"
class="visually-hidden"
name="filter"
value="completed"
/>
<span>Completed</span>
</label>
</fieldset>
</form-radiogroup>
<basic-button class="clear-completed">
<button type="button" class="destructive">
<span class="label">Clear Completed</span>
<span class="badge"></span>
</button>
</basic-button>
</footer>
</module-todo>
</div>
<details>
<summary>ModuleTodo Source Code</summary>
<module-lazy src="./examples/module-todo.html">
<card-callout>
<p class="loading" role="status">Loading...</p>
<p class="error" role="alert" aria-live="polite"></p>
</card-callout>
</module-lazy>
</details>
<details>
<summary>InputTextbox Source Code</summary>
<module-lazy src="./examples/form-textbox.html">
<card-callout>
<p class="loading" role="status">Loading...</p>
<p class="error" role="alert" aria-live="polite"></p>
</card-callout>
</module-lazy>
</details>
<details>
<summary>InputButton Source Code</summary>
<module-lazy src="./examples/basic-button.html">
<card-callout>
<p class="loading" role="status">Loading...</p>
<p class="error" role="alert" aria-live="polite"></p>
</card-callout>
</module-lazy>
</details>
<details>
<summary>InputCheckbox Source Code</summary>
<module-lazy src="./examples/form-checkbox.html">
<card-callout>
<p class="loading" role="status">Loading...</p>
<p class="error" role="alert" aria-live="polite"></p>
</card-callout>
</module-lazy>
</details>
<details>
<summary>InputRadiogroup Source Code</summary>
<module-lazy src="./examples/form-radiogroup.html">
<card-callout>
<p class="loading" role="status">Loading...</p>
<p class="error" role="alert" aria-live="polite"></p>
</card-callout>
</module-lazy>
</details>
</module-demo>
</section>
<section>
## Lazy Loading
This example shows how to handle asynchronous data loading and error states.
**Features:**
- Lazy loading with intersection observer
- Loading states and error handling
- Content replacement patterns
```js
component(
'module-lazy',
{
src: asString(),
loaded: asBoolean(),
loading: asBoolean(),
error: asString(),
},
(el, { first }) => {
let observer
const loadContent = async () => {
if (el.loaded || el.loading || !el.src) return
el.loading = true
el.error = ''
try {
const response = await fetch(el.src)
if (!response.ok) throw new Error(`HTTP ${response.status}`)
const content = await response.text()
el.querySelector('.content').innerHTML = content
el.loaded = true
} catch (err) {
el.error = err.message
} finally {
el.loading = false
}
}
return [
// Loading state
first(
'.loading',
setStyle('display', () => (el.loading ? 'block' : 'none')),
),
// Error state
first(
'.error',
setText('error'),
setStyle('display', () => (el.error ? 'block' : 'none')),
),
// Content container
first(
'.content',
setStyle('display', () => (el.loaded ? 'block' : 'none')),
),
// Setup intersection observer
() => {
observer = new IntersectionObserver(entries => {
if (entries[0].isIntersecting) {
loadContent()
observer.disconnect()
}
})
observer.observe(el)
return () => observer?.disconnect()
},
]
},
)
```
```html
<module-lazy src="/api/user-profile">
<div class="loading">Loading user profile...</div>
<div class="error"></div>
<div class="content"></div>
</module-lazy>
```
</section>