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/alert-dialog/+page.svelte

400 lines
15 KiB

<script lang="ts">
import { AlertDialog } from '$uix/acel';
let controlledOpen = $state(false);
let lastAction = $state<string | null>(null);
function setAction(label: string) {
lastAction = label;
setTimeout(() => lastAction = null, 2500);
}
</script>
<div class="page">
<header class="ph">
<div class="ph-inner">
<div class="ph-brand">
<span class="ph-dot"></span>
<span class="ph-title">alert<em>dialog</em></span>
</div>
<span class="ph-sub">acel · primitivo de confirmación</span>
</div>
</header>
<main>
<!-- ── STATUS BAR ─────────────────────────────────────────────────── -->
<div class="status-bar" class:status-bar--active={lastAction}>
{#if lastAction}
<span class="status-dot status-dot--on"></span>
<span>acción ejecutada: <strong>{lastAction}</strong></span>
{:else}
<span class="status-dot"></span>
<span class="muted">esperando interacción…</span>
{/if}
</div>
<div class="grid">
<!-- ── 1. DESTRUCTIVE ──────────────────────────────────────────── -->
<section class="card" data-color="red">
<header class="card-hd">
<span class="tag">01</span>
<span class="card-title">Acción destructiva</span>
</header>
<p class="card-desc">Caso clásico: eliminar cuenta. No se puede cerrar con Escape.</p>
<AlertDialog.Root>
<AlertDialog.Trigger class="btn btn--red">
Eliminar cuenta
</AlertDialog.Trigger>
<AlertDialog.Portal>
<AlertDialog.Overlay class="overlay" />
<AlertDialog.Content class="dialog">
<AlertDialog.Title class="dialog-title">
¿Eliminar cuenta permanentemente?
</AlertDialog.Title>
<AlertDialog.Description class="dialog-desc">
Esta acción no se puede deshacer. Se eliminarán todos tus datos,
configuraciones e historial de forma permanente.
</AlertDialog.Description>
<div class="dialog-footer">
<AlertDialog.Cancel class="btn btn--ghost" onclick={() => setAction('cancel — delete account')}>
Cancelar
</AlertDialog.Cancel>
<AlertDialog.Action class="btn btn--red" onclick={() => setAction('confirm — delete account')}>
Sí, eliminar
</AlertDialog.Action>
</div>
</AlertDialog.Content>
</AlertDialog.Portal>
</AlertDialog.Root>
</section>
<!-- ── 2. CONFIRMACIÓN NEUTRAL ────────────────────────────────── -->
<section class="card" data-color="amber">
<header class="card-hd">
<span class="tag">02</span>
<span class="card-title">Confirmación neutral</span>
</header>
<p class="card-desc">Confirmar una acción significativa sin ser destructiva.</p>
<AlertDialog.Root>
<AlertDialog.Trigger class="btn btn--amber">
Publicar cambios
</AlertDialog.Trigger>
<AlertDialog.Portal>
<AlertDialog.Overlay class="overlay" />
<AlertDialog.Content class="dialog">
<AlertDialog.Title class="dialog-title">
¿Publicar estos cambios?
</AlertDialog.Title>
<AlertDialog.Description class="dialog-desc">
Los cambios serán visibles para todos los usuarios en tiempo real.
Asegúrate de haberlos revisado antes de continuar.
</AlertDialog.Description>
<div class="dialog-footer">
<AlertDialog.Cancel class="btn btn--ghost" onclick={() => setAction('cancel — publish')}>
Revisar antes
</AlertDialog.Cancel>
<AlertDialog.Action class="btn btn--amber" onclick={() => setAction('confirm — publish')}>
Publicar ahora
</AlertDialog.Action>
</div>
</AlertDialog.Content>
</AlertDialog.Portal>
</AlertDialog.Root>
</section>
<!-- ── 3. SOLO CANCEL ─────────────────────────────────────────── -->
<section class="card" data-color="blue">
<header class="card-hd">
<span class="tag">03</span>
<span class="card-title">Solo cancelar (aviso)</span>
</header>
<p class="card-desc">Diálogo informativo que solo permite confirmar lectura.</p>
<AlertDialog.Root>
<AlertDialog.Trigger class="btn btn--blue">
Ver aviso legal
</AlertDialog.Trigger>
<AlertDialog.Portal>
<AlertDialog.Overlay class="overlay" />
<AlertDialog.Content class="dialog">
<AlertDialog.Title class="dialog-title">
Aviso importante
</AlertDialog.Title>
<AlertDialog.Description class="dialog-desc">
Este sistema está en fase beta. Algunos datos pueden no estar
disponibles hasta la versión final. Las funcionalidades pueden
cambiar sin previo aviso.
</AlertDialog.Description>
<div class="dialog-footer dialog-footer--center">
<AlertDialog.Cancel class="btn btn--blue" onclick={() => setAction('acknowledged — notice')}>
Entendido
</AlertDialog.Cancel>
</div>
</AlertDialog.Content>
</AlertDialog.Portal>
</AlertDialog.Root>
</section>
<!-- ── 4. CONTROLLED ─────────────────────────────────────────── -->
<section class="card" data-color="violet">
<header class="card-hd">
<span class="tag">04</span>
<span class="card-title">Controlado externamente</span>
</header>
<p class="card-desc">El estado <code>open</code> es controlado desde fuera del componente.</p>
<div class="state-row">
<span class="state-label">open:</span>
<span class="state-val" class:state-val--on={controlledOpen}>{controlledOpen}</span>
</div>
<div class="btn-row">
<button class="btn btn--violet" onclick={() => controlledOpen = true}>
Abrir
</button>
<button class="btn btn--ghost" onclick={() => controlledOpen = false}>
Cerrar
</button>
</div>
<AlertDialog.Root bind:open={controlledOpen}>
<AlertDialog.Portal>
<AlertDialog.Overlay class="overlay" />
<AlertDialog.Content class="dialog">
<AlertDialog.Title class="dialog-title">
Diálogo controlado
</AlertDialog.Title>
<AlertDialog.Description class="dialog-desc">
Este diálogo fue abierto programáticamente. Su estado
<code>open</code> es gestionado por la variable reactiva
del componente padre.
</AlertDialog.Description>
<div class="dialog-footer">
<AlertDialog.Cancel class="btn btn--ghost" onclick={() => setAction('cancel — controlled')}>
Cerrar
</AlertDialog.Cancel>
<AlertDialog.Action class="btn btn--violet" onclick={() => setAction('confirm — controlled')}>
Confirmar
</AlertDialog.Action>
</div>
</AlertDialog.Content>
</AlertDialog.Portal>
</AlertDialog.Root>
</section>
<!-- ── 5. SIN PORTAL ─────────────────────────────────────────── -->
<section class="card" data-color="green">
<header class="card-hd">
<span class="tag">05</span>
<span class="card-title">Sin Portal</span>
</header>
<p class="card-desc">Content renderizado inline (sin teleport), útil para pruebas.</p>
<AlertDialog.Root>
<AlertDialog.Trigger class="btn btn--green">
Abrir inline
</AlertDialog.Trigger>
<AlertDialog.Overlay class="overlay" />
<AlertDialog.Content class="dialog">
<AlertDialog.Title class="dialog-title">
Diálogo inline
</AlertDialog.Title>
<AlertDialog.Description class="dialog-desc">
Este diálogo no usa Portal, el DOM se inserta en el árbol del
componente actual en lugar de en el body.
</AlertDialog.Description>
<div class="dialog-footer">
<AlertDialog.Cancel class="btn btn--ghost" onclick={() => setAction('cancel — inline')}>
Cancelar
</AlertDialog.Cancel>
<AlertDialog.Action class="btn btn--green" onclick={() => setAction('confirm — inline')}>
OK
</AlertDialog.Action>
</div>
</AlertDialog.Content>
</AlertDialog.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 #e5a100;
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: #e5a100; }
.ph-title {
font-size: .85rem; font-weight: 700;
letter-spacing: .15em; text-transform: uppercase; color: #f4f3ef;
}
.ph-title em { color: #e5a100; font-style: normal; }
.ph-sub { font-size: .65rem; color: #555; letter-spacing: .1em; }
/* ── MAIN ────────────────────────────────────────────────────────────── */
main {
max-width: 1200px; margin: 0 auto;
padding : 1.5rem;
display : flex; flex-direction: column; gap: 1rem;
}
/* ── STATUS BAR ──────────────────────────────────────────────────────── */
.status-bar {
display : flex; align-items: center; gap: .6rem;
padding : .5rem .85rem;
background : #16161f;
border : 1px solid #26262f;
border-radius: 4px;
font-size : .72rem;
transition : border-color .2s;
}
.status-bar--active { border-color: #e5a100; }
.status-bar strong { color: #e5a100; }
.status-dot {
width: 6px; height: 6px; border-radius: 50%;
background: #333; flex-shrink: 0;
transition: background .2s;
}
.status-dot--on { background: #e5a100; box-shadow: 0 0 6px #e5a100; }
.muted { color: #444; }
/* ── GRID ────────────────────────────────────────────────────────────── */
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 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="red"] { border-top-color: #f87171; }
.card[data-color="amber"] { border-top-color: #e5a100; }
.card[data-color="blue"] { border-top-color: #60a5fa; }
.card[data-color="violet"] { border-top-color: #a78bfa; }
.card[data-color="green"] { border-top-color: #4ade80; }
.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 ───────────────────────────────────────────────────────── */
.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: #a78bfa; }
.btn-row { display: flex; gap: .4rem; }
/* ── BUTTONS ─────────────────────────────────────────────────────────── */
:global(.btn) {
padding : .35rem .8rem;
border : none; border-radius: 3px;
font-family: 'DM Mono', 'Fira Code', monospace;
font-size : .72rem; font-weight: 700;
letter-spacing: .05em; cursor: pointer;
transition : opacity .12s;
}
:global(.btn:hover) { opacity: .8; }
:global(.btn--red) { background: #7f1d1d; color: #fca5a5; }
:global(.btn--amber) { background: #78350f; color: #fcd34d; }
:global(.btn--blue) { background: #1e3a5f; color: #93c5fd; }
:global(.btn--violet) { background: #2e1065; color: #c4b5fd; }
:global(.btn--green) { background: #14532d; color: #86efac; }
:global(.btn--ghost) {
background: transparent;
border : 1px solid #333; color: #666;
font-weight: 500;
}
:global(.btn--ghost:hover) { border-color: #555; color: #999; opacity: 1; }
/* ── OVERLAY ─────────────────────────────────────────────────────────── */
:global(.overlay) {
position : fixed; inset: 0;
background: rgba(0, 0, 0, .65);
backdrop-filter: blur(2px);
z-index : 50;
}
/* ── DIALOG ──────────────────────────────────────────────────────────── */
:global(.dialog) {
position : fixed;
top : 50%; left: 50%;
transform : translate(-50%, -50%);
z-index : 51;
width : min(480px, calc(100vw - 2rem));
background: #1a1a24;
border : 1px solid #2e2e3e;
border-radius: 8px;
padding : 1.75rem;
display : flex; flex-direction: column; gap: 1rem;
box-shadow: 0 24px 60px rgba(0,0,0,.6);
}
:global(.dialog-title) {
font-size : .95rem; font-weight: 700;
color : #f0ede6;
letter-spacing: .02em;
}
:global(.dialog-desc) {
font-size : .78rem; color: #777;
line-height: 1.65;
}
:global(.dialog-footer) {
display : flex; justify-content: flex-end; gap: .5rem;
padding-top: .5rem;
border-top : 1px solid #26262f;
}
:global(.dialog-footer--center) { justify-content: center; }
code {
background : #26262f;
padding : .1rem .35rem;
border-radius: 2px;
font-size : .7rem;
color : #a78bfa;
font-family : inherit;
}
</style>

Powered by TurnKey Linux.