You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/src/routes/test/accordion/+page.svelte

319 lines
12 KiB

<script lang="ts">
import { Accordion } from '$uix/acel';
let singleValue = $state<string | undefined>(undefined);
let multipleValue = $state<string[]>([]);
</script>
<div class="page">
<header class="ph">
<div class="ph-inner">
<div class="ph-brand">
<span class="ph-dot"></span>
<span class="ph-title">ac<em>cordion</em></span>
</div>
<span class="ph-sub">acel · primitivo expandible</span>
</div>
</header>
<main>
<div class="grid">
<!-- ── 1. SINGLE ──────────────────────────────────────────────── -->
<section class="card" data-color="teal">
<header class="card-hd">
<span class="tag">01</span>
<span class="card-title">type="single"</span>
</header>
<p class="card-desc">Solo un ítem puede estar abierto a la vez.</p>
<div class="state-row">
<span class="state-label">value:</span>
<span class="state-val" class:state-val--on={!!singleValue}>
{singleValue ?? '—'}
</span>
</div>
<Accordion.Root type="single" bind:value={singleValue} class="accordion">
<Accordion.Item value="q1" class="acc-item">
<Accordion.Header class="acc-hd">
<Accordion.Trigger class="acc-trigger">
<span>¿Es accesible?</span>
<span class="chevron">▾</span>
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content class="acc-content">
Sí. Sigue el patrón de diseño WAI-ARIA para acordeones,
con roles y atributos ARIA correctos.
</Accordion.Content>
</Accordion.Item>
<Accordion.Item value="q2" class="acc-item">
<Accordion.Header class="acc-hd">
<Accordion.Trigger class="acc-trigger">
<span>¿Tiene estilos propios?</span>
<span class="chevron">▾</span>
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content class="acc-content">
No incluye estilos por defecto. Está pensado para que
tú definas el diseño completo desde el exterior.
</Accordion.Content>
</Accordion.Item>
<Accordion.Item value="q3" class="acc-item">
<Accordion.Header class="acc-hd">
<Accordion.Trigger class="acc-trigger">
<span>¿Está animado?</span>
<span class="chevron">▾</span>
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content class="acc-content">
El content expone <code>data-state="open|closed"</code> y
<code>--bits-accordion-content-height</code> para animar
con CSS sin JavaScript adicional.
</Accordion.Content>
</Accordion.Item>
</Accordion.Root>
</section>
<!-- ── 2. MULTIPLE ────────────────────────────────────────────── -->
<section class="card" data-color="indigo">
<header class="card-hd">
<span class="tag">02</span>
<span class="card-title">type="multiple"</span>
</header>
<p class="card-desc">Varios ítems pueden estar abiertos simultáneamente.</p>
<div class="state-row">
<span class="state-label">value:</span>
<span class="state-val" class:state-val--on={multipleValue.length > 0}>
{multipleValue.length ? multipleValue.join(', ') : '—'}
</span>
</div>
<Accordion.Root type="multiple" bind:value={multipleValue} class="accordion">
{#each ['Alpha', 'Beta', 'Gamma', 'Delta'] as name, i}
<Accordion.Item value="m{i+1}" class="acc-item">
<Accordion.Header class="acc-hd">
<Accordion.Trigger class="acc-trigger">
<span>{name}</span>
<span class="chevron">▾</span>
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content class="acc-content">
Contenido de <strong>{name}</strong>. Abre otros ítems y
este permanecerá visible.
</Accordion.Content>
</Accordion.Item>
{/each}
</Accordion.Root>
</section>
<!-- ── 3. DISABLED ROOT ───────────────────────────────────────── -->
<section class="card" data-color="rose">
<header class="card-hd">
<span class="tag">03</span>
<span class="card-title">Root deshabilitado</span>
</header>
<p class="card-desc">
Con <code>disabled</code> en el Root, todos los triggers quedan inactivos.
</p>
<Accordion.Root type="single" disabled class="accordion accordion--disabled">
<Accordion.Item value="d1" class="acc-item">
<Accordion.Header class="acc-hd">
<Accordion.Trigger class="acc-trigger">
<span>No se puede abrir</span>
<span class="chevron">▾</span>
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content class="acc-content">
Este contenido no debería ser visible.
</Accordion.Content>
</Accordion.Item>
<Accordion.Item value="d2" class="acc-item">
<Accordion.Header class="acc-hd">
<Accordion.Trigger class="acc-trigger">
<span>Tampoco este</span>
<span class="chevron">▾</span>
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content class="acc-content">
Bloqueado a nivel de raíz.
</Accordion.Content>
</Accordion.Item>
</Accordion.Root>
</section>
<!-- ── 4. ITEM DISABLED ───────────────────────────────────────── -->
<section class="card" data-color="amber">
<header class="card-hd">
<span class="tag">04</span>
<span class="card-title">Ítem individual deshabilitado</span>
</header>
<p class="card-desc">
<code>disabled</code> en un <code>Item</code> concreto, los demás siguen activos.
</p>
<Accordion.Root type="single" class="accordion">
<Accordion.Item value="e1" class="acc-item">
<Accordion.Header class="acc-hd">
<Accordion.Trigger class="acc-trigger">
<span>Ítem activo</span>
<span class="chevron">▾</span>
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content class="acc-content">
Este ítem funciona con normalidad.
</Accordion.Content>
</Accordion.Item>
<Accordion.Item value="e2" disabled class="acc-item acc-item--disabled">
<Accordion.Header class="acc-hd">
<Accordion.Trigger class="acc-trigger">
<span>Ítem deshabilitado</span>
<span class="chevron">▾</span>
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content class="acc-content">
No debería ser accesible.
</Accordion.Content>
</Accordion.Item>
<Accordion.Item value="e3" class="acc-item">
<Accordion.Header class="acc-hd">
<Accordion.Trigger class="acc-trigger">
<span>Ítem activo</span>
<span class="chevron">▾</span>
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content class="acc-content">
Este ítem también funciona con normalidad.
</Accordion.Content>
</Accordion.Item>
</Accordion.Root>
</section>
</div>
</main>
</div>
<style>
:global(*, *::before, *::after) { box-sizing: border-box; margin: 0; padding: 0; }
.page {
min-height : 100vh;
background : #0e0e14;
color : #d4d2cc;
font-family: 'DM Mono', 'Fira Code', monospace;
font-size : 13px;
}
/* ── PAGE HEADER ─────────────────────────────────────────────────────── */
.ph { border-bottom: 2px solid #2dd4bf; background: #080810; }
.ph-inner {
max-width: 1200px; margin: 0 auto;
padding : .75rem 1.5rem;
display : flex; align-items: center; justify-content: space-between;
}
.ph-brand { display: flex; align-items: center; gap: .5rem; }
.ph-dot { width: 8px; height: 8px; border-radius: 50%; background: #2dd4bf; }
.ph-title {
font-size: .85rem; font-weight: 700;
letter-spacing: .15em; text-transform: uppercase; color: #f4f3ef;
}
.ph-title em { color: #2dd4bf; font-style: normal; }
.ph-sub { font-size: .65rem; color: #555; letter-spacing: .1em; }
/* ── MAIN ────────────────────────────────────────────────────────────── */
main { max-width: 1200px; margin: 0 auto; padding: 1.5rem; }
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
gap: .75rem;
}
/* ── CARDS ───────────────────────────────────────────────────────────── */
.card {
background : #16161f;
border : 1px solid #26262f;
border-radius: 6px;
padding : 1.1rem;
display : flex; flex-direction: column; gap: .75rem;
border-top : 3px solid transparent;
}
.card[data-color="teal"] { border-top-color: #2dd4bf; }
.card[data-color="indigo"] { border-top-color: #818cf8; }
.card[data-color="rose"] { border-top-color: #fb7185; }
.card[data-color="amber"] { border-top-color: #e5a100; }
.card-hd {
display : flex; align-items: center; gap: .5rem;
padding-bottom: .6rem;
border-bottom : 1px solid #26262f;
}
.tag { font-size: .6rem; font-weight: 700; letter-spacing: .15em; color: #555; }
.card-title { font-size: .75rem; font-weight: 600; color: #aaa; }
.card-desc { font-size: .72rem; color: #555; line-height: 1.5; }
.state-row { display: flex; align-items: center; gap: .5rem; font-size: .72rem; }
.state-label { color: #555; }
.state-val { color: #444; font-weight: 700; transition: color .2s; }
.state-val--on { color: #2dd4bf; }
/* ── ACCORDION ───────────────────────────────────────────────────────── */
:global(.accordion) {
border : 1px solid #26262f;
border-radius: 4px;
overflow : hidden;
}
:global(.accordion--disabled) { opacity: .45; }
:global(.acc-item) {
border-bottom: 1px solid #26262f;
}
:global(.acc-item:last-child) { border-bottom: none; }
:global(.acc-item--disabled) { opacity: .4; }
:global(.acc-hd) { display: block; }
:global(.acc-trigger) {
width : 100%;
display : flex; align-items: center; justify-content: space-between;
padding : .65rem .85rem;
background : transparent; border: none;
color : #aaa;
font-family : 'DM Mono', 'Fira Code', monospace;
font-size : .75rem; font-weight: 600;
cursor : pointer; text-align: left;
transition : color .15s, background .15s;
}
:global(.acc-trigger:hover) { color: #f0ede6; background: #1e1e2a; }
:global([data-state="open"] > .acc-hd .acc-trigger) { color: #f0ede6; }
:global(.acc-content) {
padding : 0 .85rem;
font-size : .75rem; color: #666; line-height: 1.65;
overflow : hidden;
}
:global([data-state="open"] .acc-content) { padding: .65rem .85rem; }
:global([data-state="closed"] .acc-content) { padding: 0 .85rem; }
.chevron {
font-size : .8rem; color: #555;
transition: transform .2s;
flex-shrink: 0;
}
:global([data-state="open"] .chevron) { transform: rotate(180deg); color: #2dd4bf; }
:global([data-state="closed"] .chevron) { transform: rotate(0deg); }
code {
background : #26262f; padding: .1rem .35rem;
border-radius: 2px; font-size: .68rem;
color: #2dd4bf; font-family: inherit;
}
</style>

Powered by TurnKey Linux.