Interactividad con Alpine.js
En esta página
Interactividad con Alpine.js
Cada página construida por BxSites ya carga Alpine.js
- es lo que impulsa el interruptor de modo oscuro y el desplegable de
idioma incorporados en todos los temas incluidos. Esa misma
instancia de Alpine también está disponible para el contenido de tus
propias páginas, gratis: sin ninguna opción de
bxsites.yamlque activar, sin ninguna entradaextraJsque añadir, sin ninguna etiqueta<script>adicional que escribir en tu markdown.
Dado que el HTML en bruto a nivel de bloque pasa sin cambios
en tu markdown, puedes colocar los atributos x-data/x-show/@click/
etc. de Alpine directamente en cualquier bloque HTML y simplemente
funciona.
Antes de recurrir a Alpine
La mayorÃa de las necesidades "interactivas" ya tienen un bloque de directiva construido para ese propósito que no requiere escribir nada de JS por tu cuenta - recurre primero a estos:
- Una sección colapsable → Expandible o una admonición colapsable
- Contenido alternativo agrupado detrás de pestañas clicables → Pestañas de Contenido
- Un recorrido numerado paso a paso → Stepper
- Un enlace estilizado de llamada a la acción →
Botones (el botón de copiar al portapapeles
de abajo es un caso diferente - no tiene ningún
hrefen absoluto, solo comportamiento del lado del cliente - que es exactamente para lo que sirve Alpine)
Alpine es para el contenido interactivo que esos no cubren - cualquier cosa con su propio estado del lado del cliente.
Un botón de copiar al portapapeles
::: button únicamente renderiza un enlace
real (o un marcador de posición inerte) - al igual que el propio botón de
GitBook, no tiene ninguna noción de ejecutar JS arbitrario al hacer clic.
Para un botón que sà hace algo en lugar de navegar a algún sitio, coloca
sus clases bxsites-button/bxsites-button--* en un <button> HTML
simple en su lugar - el mismo aspecto, con estilos en todos los temas
incorporados, solo que conectado con Alpine en lugar de un href. Uno
común: un botón junto a un comando de instalación que lo copia y confirma
la copia:
<div x-data="{ copied: false }">
<button type="button" class="bxsites-button bxsites-button--secondary bxsites-button--small"
@click="navigator.clipboard.writeText( 'box install bx-sites' ); copied = true; setTimeout( () => copied = false, 1500 )">
<span x-show="!copied">Copy install command</span>
<span x-show="copied" x-cloak>Copied!</span>
</button>
</div>
Un filtro en vivo
Filtrar una lista del lado del cliente, sin ida y vuelta al servidor:
<div x-data="{ query: '' }">
<input type="text" x-model="query" placeholder="Filter providers...">
<ul>
<li x-show="'local'.includes( query.toLowerCase() )">local (static index, no server)</li>
<li x-show="'algolia'.includes( query.toLowerCase() )">algolia (hosted DocSearch)</li>
<li x-show="'pagefind'.includes( query.toLowerCase() )">pagefind (indexed at build time)</li>
</ul>
</div>
x-model vincula el valor del campo al estado de Alpine; el x-show de
cada <li> se reevalúa en cada pulsación de tecla.
Una tabla ordenable y filtrable
Una tabla de pipes nativa es estática una vez
construida - para una que el lector realmente pueda ordenar y filtrar del
lado del cliente (lo más parecido que hay aquà a la búsqueda/orden de
tablas de GitBook), deja que Alpine sea el dueño de las filas en su
lugar: coloca los datos en x-data y renderÃzalos con x-for, en lugar
de escribir la sintaxis de pipes | Feature | Status |:
<div x-data="{
query: '',
sortKey: 'name',
sortAsc: true,
rows: [
{ name: 'Bootstrap', type: 'Components', stars: 4 },
{ name: 'GitBook', type: 'SaaS', stars: 5 },
{ name: 'Docusaurus', type: 'React', stars: 4 },
{ name: 'VuePress', type: 'Vue', stars: 3 }
],
sortBy(key) {
this.sortAsc = this.sortKey === key ? !this.sortAsc : true
this.sortKey = key
},
get sorted() {
return [...this.rows]
.filter(r => r.name.toLowerCase().includes(this.query.toLowerCase()))
.sort((a, b) => {
const dir = this.sortAsc ? 1 : -1
return a[this.sortKey] > b[this.sortKey] ? dir : a[this.sortKey] < b[this.sortKey] ? -dir : 0
})
}
}">
<input type="text" x-model="query" placeholder="Filter by name...">
<table class="table">
<thead>
<tr>
<th @click="sortBy('name')" style="cursor:pointer">Name</th>
<th @click="sortBy('type')" style="cursor:pointer">Type</th>
<th @click="sortBy('stars')" style="cursor:pointer">Stars</th>
</tr>
</thead>
<tbody>
<template x-for="row in sorted" :key="row.name">
<tr>
<td x-text="row.name"></td>
<td x-text="row.type"></td>
<td x-text="row.stars"></td>
</tr>
</template>
</tbody>
</table>
</div>
Lo que se renderiza como (escribe en el cuadro, haz clic en un encabezado de columna):
| Name | Type | Stars |
|---|---|---|
rows es un array JS plano incrustado directamente en la página - bien
para el tipo de tabla de referencia pequeña que suelen tener los docs.
sorted es un getter de Alpine, asà que vuelve a filtrar y a ordenar
en cada pulsación de tecla/clic sin cableado adicional; sortBy()
invierte la dirección al hacer un segundo clic en la misma columna. El
<table> de aquà es una etiqueta <table> real escrita a mano (no hay
sintaxis de tabla de pipes que entregue filas a Alpine directamente), asÃ
que de todas formas se envuelve en .bxsites-table-wrap y recibe
automáticamente el tratamiento de desplazamiento
responsivo/encabezado fijo,
igual que cualquier tabla que el propio bx-markdown renderiza.
Fundamentos de x-data, si eres nuevo en Alpine
x-data declara el propio estado reactivo de un ámbito como un objeto
JS simple; cualquier cosa dentro de ese elemento puede leerlo/escribirlo,
y x-show/x-text/x-model/@click (abreviatura de x-on:click)
reaccionan todos cuando cambia:
<div x-data="{ count: 0 }">
<button type="button" @click="count++">Clicked <span x-text="count"></span> times</button>
</div>
Consulta la propia documentación de Alpine
para la lista completa de directivas (x-if, x-for, x-transition, y
más).
Cosas que debes saber
- Es parte del núcleo, no opcional. El armazón del tema (modo oscuro,
selector de idioma) depende de Alpine, asà que no se puede desactivar
en
bxsites.yamlcomo sà puede hacerse conmermaid/math. - Versión. Actualmente
alpinejs@3.14.1, incluida con este módulo y servida desdesite/assets/vendor/alpine/- sin ningún CDN involucrado. Consulta el propiolayout.bxmde un tema para ver la etiqueta<script>exacta si necesitas saber precisamente qué se carga. - CSP estricta. La compilación por defecto de Alpine evalúa las
expresiones JS dentro de
x-data/@clicketc. directamente, lo cual necesitaunsafe-evalbajo una Content-Security-Policy estricta. Si tu despliegue no puede permitir eso, no dependas de Alpine en el contenido de tus páginas. - Mantenlo ligero. Una página de documentación debe seguir siendo rápida y sencilla - pequeños widgets autocontenidos (un botón de copiar, un filtro, un interruptor) encajan bien; una aplicación completa del lado del cliente no es para lo que sirve esto.