Search by

luinuxscl / lnx-ui

luinuxscl

Componentes UI compartidos para aplicaciones derivadas del kit lnx (Blade anónimos, override-friendly).

Package info

github.com/luinuxscl/lnx-ui

Language:Blade

pkg:composer/luinuxscl/lnx-ui

Statistics

Installs: 31

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.31.0 2026-11-10 03:09 UTC

README

Licencia: MIT

Componentes UI compartidos para las aplicaciones derivadas del kit lnx. Es un paquete Composer de Blade anónimos, pensado para tener una sola fuente de componentes entre proyectos y permitir que cada app los sobrescriba sin tocar el paquete.

Licencia MIT: parte del kit. Úsalo y modifícalo libremente.

Instalación

En una app lnx, añade el repositorio VCS público y requiere el paquete:

composer config repositories.lnx-ui vcs https://github.com/luinuxscl/lnx-ui.git
composer require luinuxscl/lnx-ui

En desarrollo local del propio paquete puedes usar un repositorio path en lugar del VCS.

El LnxUiServiceProvider se autodescubre gracias a extra.laravel.providers: no hay que registrarlo a mano.

Tailwind 4

Las clases dentro de vendor/ no se detectan solas. Añade una línea a resources/css/app.css:

@source '../../vendor/luinuxscl/lnx-ui/resources/views';

Uso

Los componentes viven bajo el namespace lnx-ui:

<x-lnx-ui::trend-card label="Sales" value="2.968" change="+3.2%" :series="[10, 20, 30]" color="emerald" />

Sobrescribir un componente

loadViewsFrom registra primero resources/views/vendor/lnx-ui, así que una app puede reemplazar cualquier componente creando el mismo archivo dentro de resources/views/vendor/lnx-ui/components/:

resources/views/vendor/lnx-ui/components/trend-card.blade.php

Para publicar todas las vistas del paquete y editarlas:

php artisan vendor:publish --tag=lnx-ui-views

Galería de diseño

El paquete incluye la vista lnx-ui::design-gallery: el catálogo de componentes agrupados por tipo, cada uno con un nombre y un ancla estable (#<nombre>) para referenciarlo con la IA. Es contenido sin layout; la app lo envuelve en su propio layout y decide la ruta y el gating:

{{-- resources/views/design-gallery.blade.php --}}
<x-layouts::app :title="__('Design gallery')" :breadcrumbs="[['label' => __('Design gallery')]]">
    @include('lnx-ui::design-gallery')
</x-layouts::app>

Las cadenas de la galería viven en el namespace lnx-ui::design-gallery (lang/{en,es}/design-gallery.php) y se pueden sobrescribir en lang/vendor/lnx-ui/{locale}/design-gallery.php.

Componentes disponibles

Componente Props
x-lnx-ui::stat label, value, icon, color
x-lnx-ui::stat-card label, value, icon, color (envuelve stat en flux:card)
x-lnx-ui::statistics-panel title, updated-label y updated-icon (opcionales); slots de métricas, actions, description y empty
x-lnx-ui::widget title, description, href, href-label, span (1–12), empty; slots actions y contenido
x-lnx-ui::trend-card label, value, change, series (array), color
x-lnx-ui::chart-card label, value; description, help, change, trend, change-style (text por defecto), series (array), chart (line/bar), labels (array), color
x-lnx-ui::metric-card label, value, icon; description, help, change, trend, change-style (badge por defecto), color
x-lnx-ui::tool-card title, description, icon, href, color, external
x-lnx-ui::resource-card title, description, icon, eyebrow, href, external, color; slots badges, tags, meta, actions
x-lnx-ui::properties-card title, description, items (array de label, value, href, icon), columns (1 o 2), empty; slots actions y contenido libre
x-lnx-ui::timeline title, description, items (array de title, description, meta, time, datetime, icon, color), empty; slots actions, empty y contenido libre
x-lnx-ui::placeholder-pattern id (opcional)
x-lnx-ui::auth-header title, description
x-lnx-ui::auth-session-status status
x-lnx-ui::chip label, icon, color; el slot es el fallback
x-lnx-ui::media-cell title, description, icon, image, href, color
x-lnx-ui::summary-card label, value, caption, change, icon, color
x-lnx-ui::page-header title, subtitle, eyebrow, icon, color; slots eyebrow y actions
x-lnx-ui::data-table title (opcional), description, filters-label, paginate, table-class, container-class; slots filters, toolbar, actions y el contenido de la tabla
x-lnx-ui::data-table-inline title (opcional), description, paginate, table-class, container-class; slots filters, filtersActions, toolbar, actions y el contenido de la tabla
x-lnx-ui::mail-list-item sender, email, avatar, subject, preview, time, unread, starred, selected, badge (['label' => …, 'color' => …]), href, select-label, star-label; slots select, star
x-lnx-ui::attachment-card name, size, icon, color, href; slot action
x-lnx-ui::presence-list title (opcional), users (array de name, avatar, line, online)
x-lnx-ui::mail-viewer sender, email, avatar, subject, date; slots actions, attachments, reply, footer y el cuerpo por defecto
x-lnx-ui::rich-text-editor value, label, placeholder, disabled; slot actions; usa wire:model/x-model
x-lnx-ui::sam-trigger label, shortcut, count (inicial); escucha sam-badge-count; emite sam-open
x-lnx-ui::sam-ambient-alerts alerts (bloque del panel Sam: revisar, descartar, silenciar, ocultar)
x-lnx-ui::sam-panel open y close (expresiones Alpine), title, subtitle, label, dock, preview; slots actions, context, footer y contenido
x-lnx-ui::sam-message role (assistant/user), name, copy, citations, sources, time, markdown (por defecto true para el asistente, false para el usuario)
x-lnx-ui::sam-signals observations, actions, observationLimit, actionLimit; bloque compartido por sam-brief y sam-insight
x-lnx-ui::sam-context-chip label, source, icon
x-lnx-ui::sam-suggestions items (array), label; emite sam-suggestion
x-lnx-ui::sam-empty-state title, body, icon; slot para acciones o sugerencias
x-lnx-ui::sam-notice variant (secondary, success, warning, danger), icon
x-lnx-ui::sam-typing label
x-lnx-ui::sam-proposal-card source, id, title, summary, changes, status, expiresAt, recordUrl, canConfirm, risk (reversible/irreversible/bulk, por defecto irreversible), requiresPhrase (por defecto, solo reversible la omite), canUndo, undoExpiresAt; emite sam-confirm y sam-undo

Los componentes de Sam toman el color del tema del anfitrión a través de los tokens accent (--color-accent, --color-accent-content, --color-accent-foreground). Una app derivada debe definirlos —el kit lo hace en resources/css/app.css— o los acentos caerán al valor por defecto de Tailwind. Los colores semánticos de la tarjeta de propuesta (green, amber, zinc, red) no dependen del tema.

Los componentes de Sam son presentacionales y no dependen de Livewire. El host conecta el estado y los turnos escuchando eventos DOM: sam-open (abrir el panel), sam-confirm ({ source, id, phrase } para confirmar un borrador; phrase viaja vacía cuando la propuesta es reversible y el módulo declaró el undo) y sam-suggestion ({ text } para rellenar la entrada). Tras confirmar, el host emite sam-phrase-consumed para limpiar el campo de frase. La frase nunca forma parte del HTML persistido.

La tarjeta muestra el riesgo de la propuesta y lo usa para decidir la confirmación: reversible se confirma con un clic, mientras irreversible y bulk piden la frase de un solo uso. Cuando el borrador ya se confirmó y su módulo conserva un undo vigente (canUndo y, si existe, undoExpiresAt), la tarjeta pinta un botón «Deshacer» de un solo uso que emite sam-undo ({ source, id }); el host vuelve a validar propiedad, ventana y frescura al ejecutarlo.

color acepta accent (predeterminado), violet, cyan, rose, emerald, blue y amber.

Los componentes de correo (mail-list-item, attachment-card, presence-list, mail-viewer) son presentacionales: el consumidor aporta los textos ya traducidos y los datos ya formateados. Sus etiquetas accesibles viven en el namespace lnx-ui::components (lang/{en,es}/components.php) y se pueden sobrescribir con select-label/star-label.

page-header es el encabezado estándar de una página: un solo <h1> con el título, un subtítulo opcional y un badge de icono opcional. El eyebrow opcional añade una línea pequeña y semibold sobre el título —útil para una fecha o un contexto breve— y acepta texto o el slot eyebrow cuando hace falta HTML, por ejemplo <time datetime="…">. Las acciones de la página van en el slot actions, alineadas a la derecha:

<x-lnx-ui::page-header
    :title="__('Users')"
    :subtitle="__('Manage accounts, roles and invitations.')"
    icon="users"
    color="blue"
>
    <x-slot:actions>
        <flux:button variant="primary" icon="plus">{{ __('Invite user') }}</flux:button>
    </x-slot:actions>
</x-lnx-ui::page-header>

El eyebrow se puede pasar como texto o como slot cuando el contenido necesita semántica propia:

<x-lnx-ui::page-header :title="__('Dashboard')">
    <x-slot:eyebrow>
        <time datetime="2026-11-02">{{ __('Monday, November 2, 2026') }}</time>
    </x-slot:eyebrow>
</x-lnx-ui::page-header>

data-table-inline es la variante compacta de data-table para barras de filtros: no muestra el rótulo de filtros y coloca el slot filtersActions (exportar, limpiar, etc.) alineado a la derecha dentro de la misma fila que filters, ajustándose a ancho completo en pantallas estrechas.

data-table renderiza el title solo si se entrega: sin título ni slot filters, desaparece la fila superior. container-class se reenvía al contenedor de flux:table (el que envuelve la tabla, el pie y la paginación), útil para acotar la altura y fijar el encabezado (por ejemplo container-class="max-h-[70vh] overflow-auto" junto con sticky en flux:table.columns). No lo uses para el ancho.

En pantallas estrechas no fuerces un ancho mínimo con table-class="min-w-[…]": crea un scroll horizontal sin barra visible que la tarjeta recorta y deja columnas inalcanzables. En su lugar, oculta columnas por breakpoint (max-sm:hidden, max-md:hidden, max-lg:hidden, max-xl:hidden) en la columna de flux:table.columns y en su celda, de modo que la tabla siempre quepa sin scroll horizontal. La galería (#data-table) es el ejemplo de referencia. Si de verdad necesitas un suelo de ancho, que sea responsivo y coherente con las columnas que ocultas.

En chart-card y metric-card, la variación (change) se colorea en verde o rojo según su signo; trend (up/down) lo sobrescribe. El contenedor asigna el ancho, normalmente col-span-12 md:col-span-6 xl:col-span-3.

En chart-card y metric-card, help añade un texto de ayuda inline bajo la descripción para explicar conceptos poco obvios. Cuando se entrega, la tarjeta se expone como grupo accesible (role="group") asociado a la etiqueta y a la ayuda con aria-labelledby/aria-describedby; sin help, el render es idéntico al anterior:

<x-lnx-ui::metric-card :label="__('Total sales')" value="24.67k" icon="shopping-bag" :help="__('Includes confirmed and pending records.')" />

Editor de texto enriquecido

x-lnx-ui::rich-text-editor es un editor TipTap (ProseMirror) con toolbar accesible para negrita, cursiva, h2/h3, listas, cita, enlaces y deshacer/rehacer. El HTML resultante es responsabilidad de la app: siempre debe sanearse en el servidor antes de guardarlo.

<x-lnx-ui::rich-text-editor
    wire:model="bodyHtml"
    :value="$bodyHtml"
    :label="__('Body')"
    :placeholder="__('Start writing…')"
>
    <x-slot:actions>
        <button type="button" x-on:click="assist('improve')">{{ __('Improve') }}</button>
    </x-slot:actions>
</x-lnx-ui::rich-text-editor>

El componente usa x-modelable="html", así que funciona con wire:model en Livewire y con x-model en Alpine. Para uso standalone, pasa el contenido inicial en value.

Dependencias JavaScript

El paquete solo distribuye el módulo ESM; las dependencias npm las instala la app consumidora:

npm install @tiptap/core @tiptap/starter-kit @tiptap/pm

Importa el módulo una sola vez en el bundle de la app:

// resources/js/app.js
import '../../vendor/luinuxscl/lnx-ui/resources/js/rich-text-editor.js';

El módulo registra el componente Alpine lnxRichText en alpine:init. Las clases Tailwind de la vista ya se detectan con el @source del paquete.

Acciones de IA y eventos

El editor no implementa IA. La app dibuja sus acciones en el slot actions y llama a assist(action), que captura la selección y emite un evento Livewire:

  • lnx-rich-text:assist ({ action, html }) — la app recibe la selección.
  • lnx-rich-text:assist-complete ({ html }) — la app devuelve el HTML saneado; el editor reemplaza la selección. Con html: null solo se reactiva el editor.
  • lnx-rich-text:set-content ({ html }) — reemplaza el contenido completo (por ejemplo, tras una acción de IA o al normalizar tras guardar).

Mientras espera la respuesta, el editor se bloquea para conservar el rango de selección. La sustitución es una sola transacción, así que se puede deshacer.

Versionado

El paquete sigue SemVer. Las apps consumidoras requieren ^1.0, de modo que las adiciones de componentes (minor) se reciben con composer update luinuxscl/lnx-ui sin tocar composer.json.

Desarrollo

composer install
vendor/bin/pest
vendor/bin/pint

Convención de extracción

Un componente se mueve a este paquete cuando es presentacional puro: no consulta la base de datos, no lee modelos del host (App\Models\*) y no depende de rutas ni servicios de la app. Los componentes acoplados al host (app-logo, user-avatar, desktop-user-menu, etc.) se quedan en la aplicación.