Saltar al contenido

Catálogo de componentes ​

Los componentes de admin/src/components/ui son el kit de interfaz del panel. Un plugin los importa con @/components/ui/<Nombre>.vue. Esta página se genera del código: las props, los modelos, los eventos y los slots se leen de cada .vue en cada compilación del portal, y cada ejemplo se comprueba contra ellos (si un ejemplo usa una prop que el componente no declara, la compilación falla).

ts
import AppPage from "@/components/ui/AppPage.vue";
import UiButton from "@/components/ui/UiButton.vue";
import { t } from "@/i18n";

Además de los componentes, components/ui ofrece types.ts (Column, Tone, Option, TabItem, MenuItem, FIELD_KEY), icons.ts (ICONS, IconName), focusTrap.ts (useFocusTrap) y dirtyClose.ts (useDirtyClose).

Tipos auxiliares:

ts
type Tone = "neutral" | "accent" | "success" | "danger" | "warning" | "info";
type Option<V> = { value: V; label: string; disabled?: boolean };
type Column = { key: string; label: string; align?: "left" | "right" | "center"; width?: string; mobile?: "title" | "hide" | "show"; nowrap?: boolean };
type TabItem = { id: string; label: string; count?: number | null };
type MenuItem = { label: string; icon?: IconName; tone?: "danger"; onSelect: () => void };

Estructura de página ​

AppPage ​

Marco de toda pantalla: título, descripción, enlace de vuelta, acciones y cuerpo. Una página de plugin empieza siempre por él.

  • width es default (1280 px), narrow (880 px, formularios de ajustes) o wide.
  • Los slots actions y toolbar colocan botones en la cabecera y filtros sobre el cuerpo.

Importación: import AppPage from "@/components/ui/AppPage.vue".

Props (con *, obligatorias)

PropTipoDefectoDescripción
title *string
descriptionstring
back{ label: string; to: RouteLocationRaw }
width"default" | "narrow" | "wide"Ancho: default (1280) o narrow (formularios de ajustes, 880).

Slots

SlotDatos que recibe
actions—
toolbar—
default—

Ejemplo

vue
<AppPage :title="t('Notas')" :description="t('Notas internas del local.')" width="narrow">
  <template #actions><UiButton variant="primary" icon="plus">{{ t("Nueva nota") }}</UiButton></template>
  <UiCard :title="t('Últimas notas')">…</UiCard>
</AppPage>

Fuente: admin/src/components/ui/AppPage.vue.

UiCard ​

Panel con degradado suave y borde teñido con el acento. Contiene secciones, tablas o formularios.

  • flush quita el relleno (para tablas y listas que ocupan todo el ancho).
  • to añade una flecha de enlace en la cabecera.

Importación: import UiCard from "@/components/ui/UiCard.vue".

Props (con *, obligatorias)

PropTipoDefecto
titlestring
subtitlestring
paddedbooleantrue
flushboolean
toRouteLocationRaw | nullnull
linkLabelstring

Slots

SlotDatos que recibe
header—
actions—
default—
footer—

Ejemplo

vue
<UiCard :title="t('Resumen')" :subtitle="t('Últimos 7 días')">
  <UiDescList :columns="2">
    <UiDesc :label="t('Notas')" :value="12" />
  </UiDescList>
  <template #footer><UiButton size="sm">{{ t("Ver todas") }}</UiButton></template>
</UiCard>

Fuente: admin/src/components/ui/UiCard.vue.

UiSection ​

Subtítulo con descripción y acciones para dividir una página o una tarjeta en bloques.

Importación: import UiSection from "@/components/ui/UiSection.vue".

Props (con *, obligatorias)

PropTipoDefecto
title *string
descriptionstring

Slots

SlotDatos que recibe
actions—
default—

Ejemplo

vue
<UiSection :title="t('Avisos')" :description="t('Quién recibe cada aviso.')">
  <template #actions><UiButton size="sm" icon="plus">{{ t("Añadir") }}</UiButton></template>
  …
</UiSection>

Fuente: admin/src/components/ui/UiSection.vue.

UiToolbar ​

Fila única de filtros sobre el contenido que filtran: búsqueda y selectores a la izquierda, acciones en #side.

Importación: import UiToolbar from "@/components/ui/UiToolbar.vue".

Slots

SlotDatos que recibe
default—
side—

Ejemplo

vue
<UiToolbar>
  <UiSearch v-model="q" />
  <UiSelect v-model="state" :options="stateOptions" />
  <template #side><UiButton icon="download">{{ t("Exportar") }}</UiButton></template>
</UiToolbar>

Fuente: admin/src/components/ui/UiToolbar.vue.

Datos y listas ​

UiDesc ​

Par etiqueta-valor de una UiDescList. Sin valor pinta una raya; el slot por defecto admite contenido propio.

Importación: import UiDesc from "@/components/ui/UiDesc.vue".

Props (con *, obligatorias)

PropTipoDefecto
label *string
valuestring | number | null
monoboolean

Slots

SlotDatos que recibe
default—

Ejemplo

vue
<UiDesc :label="t('Estado')"><UiBadge tone="success">{{ t("Activo") }}</UiBadge></UiDesc>

Fuente: admin/src/components/ui/UiDesc.vue.

UiDescList ​

Lista de descripción en 1, 2 o 3 columnas para fichas de solo lectura. Sus hijos son UiDesc.

Importación: import UiDescList from "@/components/ui/UiDescList.vue".

Props (con *, obligatorias)

PropTipoDefecto
columns1 | 2 | 32

Slots

SlotDatos que recibe
default—

Ejemplo

vue
<UiDescList :columns="3">
  <UiDesc :label="t('Autor')" :value="n.author" />
  <UiDesc :label="t('Clave')" :value="n.id" mono />
</UiDescList>

Fuente: admin/src/components/ui/UiDescList.vue.

UiIcon ​

Icono del panel (trazo 1,75 sobre rejilla 24x24). El nombre es uno de los de icons.ts; ver el catálogo en la guía de diseño.

Importación: import UiIcon from "@/components/ui/UiIcon.vue".

Props (con *, obligatorias)

PropTipoDefecto
name *IconName
sizenumber | string18

Ejemplo

vue
<UiIcon name="thermometer" :size="18" />

Fuente: admin/src/components/ui/UiIcon.vue.

Lista de filas con icono, texto, valor, insignia y enlace: índices de ajustes y resúmenes. Va dentro de una UiCard flush.

Importación: import UiLinkList from "@/components/ui/UiLinkList.vue".

Props (con *, obligatorias)

PropTipoDefecto
items *Array<{ key: string; icon: IconName; label: string; description?: string; value?: string; badge?: { text: string; tone: Tone } | null; to?: RouteLocationRaw; }>

Ejemplo

vue
<UiCard flush>
  <UiLinkList :items="[{ key: 'a', icon: 'settings', label: t('Ajustes'), description: t('Opciones del módulo'), to: '/notas/ajustes' }]" />
</UiCard>

Fuente: admin/src/components/ui/UiLinkList.vue.

UiList ​

Contenedor de filas separadas por líneas finas dentro de un panel. Su contenido son UiListItem.

Importación: import UiList from "@/components/ui/UiList.vue".

Props (con *, obligatorias)

PropTipoDefecto
bareboolean
tag"ul" | "ol" | "div""ul"

Slots

SlotDatos que recibe
default—

Ejemplo

vue
<UiList>
  <UiListItem v-for="n in notes" :key="n.id" :title="n.title" :description="n.body" icon="message" />
</UiList>

Fuente: admin/src/components/ui/UiList.vue.

UiListItem ​

Fila de UiList: asa opcional, icono o muestra de color, título y texto secundario, meta a la derecha y acciones.

  • to convierte la zona principal en enlace; clickable en botón (emite open).
  • Las acciones se ven al pasar por encima o al enfocar la fila; en táctil, siempre.

Importación: import UiListItem from "@/components/ui/UiListItem.vue".

Props (con *, obligatorias)

PropTipoDefectoDescripción
titlestring
descriptionstring | null
iconIconName
swatchstring | nullColor de la muestra (categorías, etiquetas) en lugar del icono.
metastring | null
toRouteLocationRaw
clickableboolean
handleboolean
chevronboolean
activeboolean
mutedboolean
actionsVisiblebooleanAcciones siempre visibles (no solo al pasar por encima).

Eventos

EventoArgumentos
open

Slots

SlotDatos que recibe
leading—
title—
description—
body—
meta—
actions—

Ejemplo

vue
<UiListItem :title="n.title" :description="n.body" icon="message" :meta="date(n.createdAt)" clickable @open="open(n)">
  <template #actions><UiButton variant="ghost" size="sm" icon="trash" :label="t('Borrar')" /></template>
</UiListItem>

Fuente: admin/src/components/ui/UiListItem.vue.

UiSortHandle ​

Asa y botones de orden que usan UiTable y UiSortableList por dentro. Solo se usa directamente al construir una lista ordenable propia.

Importación: import UiSortHandle from "@/components/ui/UiSortHandle.vue".

Props (con *, obligatorias)

PropTipoDefectoDescripción
gripbooleantrueMuestra el asa (clase ui-sortable__grip, la que agarra Sortable).
moveButtonsbooleantrue
disabledbooleanfalse
canUpbooleantrue
canDownbooleantrue
describedbystringDescripción compartida («Arrastra el asa o pulsa Alt + ↑/↓…»).

Eventos

EventoArgumentos
movedir: "up" | "down"

Ejemplo

vue
<UiSortHandle :can-up="index > 0" :can-down="index < last" @move="(dir) => move(index, dir)" />

Fuente: admin/src/components/ui/UiSortHandle.vue.

UiSortableList ​

Lista ordenable por arrastre (SortableJS) con teclado (Alt + flechas) y botones subir/bajar. reorder es el momento de guardar.

Importación: import UiSortableList from "@/components/ui/UiSortableList.vue". Genérico: T.

Props (con *, obligatorias)

PropTipoDefectoDescripción
modelValue *T[]
itemKey *string | ((item: T) => string | number)Campo (o función) que da la clave única de cada elemento.
handlestring | falseundefinedSelector del asa. Sin valor: asa ⋮⋮ integrada. false: toda la fila arrastra.
disabledbooleanfalse
groupstringMismo nombre en varias listas = se puede arrastrar entre ellas.
moveButtonsbooleantrueBotones subir/bajar accesibles en cada fila.
forceFallbackbooleanfalsePara pantallas táctiles problemáticas: arrastre simulado en lugar del nativo.
itemLabel(item: T) => stringNombre del elemento para los avisos de lectores de pantalla («Mesa 3 movido…»).
ariaLabelstring
tagstring"ul"
itemTagstring"li"

Eventos

EventoArgumentos
update:modelValuevalue: T[]
reorderids: string[], ctx: { previous: T[]; next: T[] }
changechange: SortableChange<T>

Slots

SlotDatos que recibe
itemitem, index, move

Ejemplo

vue
<UiSortableList v-model="items" item-key="id" @reorder="(ids) => save(ids)">
  <template #item="{ item }">{{ item.name }}</template>
</UiSortableList>

Fuente: admin/src/components/ui/UiSortableList.vue.

UiStat ​

Cifra con etiqueta y variación frente a un periodo con nombre. delta es la variación porcentual; lowerIsBetter invierte el color.

Importación: import UiStat from "@/components/ui/UiStat.vue".

Props (con *, obligatorias)

PropTipoDefecto
label *string
value *string
deltanumber | null
deltaLabelstring
hintstring
toRouteLocationRaw
loadingboolean
lowerIsBetterboolean
heroboolean

Slots

SlotDatos que recibe
info—

Ejemplo

vue
<UiStat :label="t('Ventas')" value="1.240 €" :delta="8.5" :delta-label="t('vs. semana anterior')" />

Fuente: admin/src/components/ui/UiStat.vue.

UiStats ​

Rejilla adaptable para varias UiStat. min fija el ancho mínimo de cada una.

Importación: import UiStats from "@/components/ui/UiStats.vue".

Props (con *, obligatorias)

PropTipoDefecto
minstring"190px"

Slots

SlotDatos que recibe
default—

Ejemplo

vue
<UiStats min="200px">
  <UiStat :label="t('Pedidos')" value="48" />
  <UiStat :label="t('Ticket medio')" value="12,40 €" :hint="t('Hoy')" />
</UiStats>

Fuente: admin/src/components/ui/UiStats.vue.

UiTable ​

Tabla única del panel: cabecera, filas, esqueleto de carga, estado vacío y «Cargar más». En móvil se convierte en tarjetas.

  • Celdas personalizadas con el slot #cell-<clave>="{ row }".
  • selectable con v-model:selected añade casillas y la barra #bulk; sortable añade asa de arrastre y emite reorder.
  • Columnas: { key, label, align?, width?, mobile?: 'title' | 'hide' | 'show', nowrap? }.

Importación: import UiTable from "@/components/ui/UiTable.vue". Genérico: T extends Record<string, any>.

Props (con *, obligatorias)

PropTipoDefectoDescripción
columns *Column[]
rows *T[]
rowKey *string | ((row: T) => string | number)
loadingbooleanfalse
clickablebooleanfalse
activeKeystring | number | nullnull
hasMorebooleanfalse
loadingMorebooleanfalse
densebooleanfalse
barebooleanfalseSin marco (dentro de una tarjeta o un panel).
selectablebooleanfalseCasilla por fila y barra de acciones masivas (v-model:selected).
sortablebooleanfalseAsa de arrastre por fila + reordenar con teclado.
sortDisabledbooleanfalse
moveButtonsbooleanfalseBotones subir/bajar junto al asa (útiles en tablet).
rowLabel(row: T) => stringNombre de la fila para los avisos de lectores de pantalla.

v-model

ModeloTipo
v-model:selectedArray<string | number>

Eventos

EventoArgumentos
rowClickrow: T
loadMore
reorderids: string[], ctx: { previous: T[]; next: T[] }
changechange: SortableChange<T>

Slots

SlotDatos que recibe
bulkcount, clear
cell-<clave>row
empty—
footer—

Ejemplo

vue
<UiTable :columns="[{ key: 'title', label: t('Título'), mobile: 'title' }, { key: 'status', label: t('Estado') }]" :rows="rows" row-key="id" :loading="loading" clickable @row-click="open">
  <template #cell-status="{ row }"><StatusBadge :of="STATUS" :value="row.status" /></template>
  <template #empty><UiEmpty :title="t('Sin notas')" /></template>
</UiTable>

Fuente: admin/src/components/ui/UiTable.vue.

UiThumb ​

Miniatura única (productos, portadas): si la imagen falta o no carga, muestra un bloque neutro.

Importación: import UiThumb from "@/components/ui/UiThumb.vue".

Props (con *, obligatorias)

PropTipoDefecto
src *string | null | undefined
sizenumber36
rationumber1
fallbackColorstring | null

Ejemplo

vue
<UiThumb :src="product.imageUrl" :size="40" />

Fuente: admin/src/components/ui/UiThumb.vue.

Formularios ​

UiCheckbox ​

Casilla con estado intermedio opcional (indeterminate). Sin texto visible, exige aria-label.

Importación: import UiCheckbox from "@/components/ui/UiCheckbox.vue".

Props (con *, obligatorias)

PropTipoDefecto
indeterminateboolean
ariaLabelstring
disabledboolean

v-model

ModeloTipo
v-modelboolean

Ejemplo

vue
<UiCheckbox v-model="form.accepted" :aria-label="t('Acepto')" />

Fuente: admin/src/components/ui/UiCheckbox.vue.

UiChoiceChips ​

Selección múltiple con fichas (días, canales, alérgenos). El modelo conserva el orden de options, no el de los clics.

Importación: import UiChoiceChips from "@/components/ui/UiChoiceChips.vue". Genérico: V extends string | number.

Props (con *, obligatorias)

PropTipoDefecto
options *Option<V>[]
ariaLabelstring
disabledboolean
invalidboolean

v-model

ModeloTipo
v-modelV[]

Ejemplo

vue
<UiChoiceChips v-model="form.days" :options="[{ value: 1, label: t('Lun') }, { value: 2, label: t('Mar') }]" />

Fuente: admin/src/components/ui/UiChoiceChips.vue.

UiField ​

Envoltorio de un control de formulario: etiqueta enlazada al control, ayuda, error y marca de obligatorio u opcional.

  • Los controles del kit (UiInput, UiSelect, UiTextarea, UiChoiceChips) leen su id y su estado de error de aquí.

Importación: import UiField from "@/components/ui/UiField.vue".

Props (con *, obligatorias)

PropTipoDefecto
labelstring
hintstring
errorstring | null
requiredboolean
optionalboolean

Slots

SlotDatos que recibe
default—

Ejemplo

vue
<UiField :label="t('Título')" :error="errors.title" required>
  <UiInput v-model="form.title" :maxlength="80" />
</UiField>

Fuente: admin/src/components/ui/UiField.vue.

UiFileInput ​

Selector de un fichero. El modelo es File | null; accept limita los tipos.

Importación: import UiFileInput from "@/components/ui/UiFileInput.vue".

Props (con *, obligatorias)

PropTipoDefecto
acceptstring
placeholderstring
disabledboolean

v-model

ModeloTipo
v-modelFile | null

Ejemplo

vue
<UiFileInput v-model="file" accept="image/png" :placeholder="t('Elige una imagen')" />

Fuente: admin/src/components/ui/UiFileInput.vue.

UiInput ​

Campo de texto. Tipos habituales de HTML, con prefijo, sufijo, icono y slot end.

Importación: import UiInput from "@/components/ui/UiInput.vue".

Props (con *, obligatorias)

PropTipoDefecto
type"text" | "email" | "password" | "number" | "date" | "time" | "datetime-local" | "tel" | "url" | "search" | "color""text"
placeholderstring
prefixstring
suffixstring
iconIconName
invalidboolean
disabledboolean
readonlyboolean
minnumber | string
maxnumber | string
stepnumber | string
maxlengthnumber
autocompletestring
inputmode"text" | "numeric" | "decimal" | "email" | "tel" | "search"
monoboolean
autofocusboolean
ariaLabelstring

v-model

ModeloTipo
v-modelstring | number | null

Slots

SlotDatos que recibe
end—

Ejemplo

vue
<UiInput v-model="form.email" type="email" icon="mail" :placeholder="t('nombre@ejemplo.com')" />

Fuente: admin/src/components/ui/UiInput.vue.

UiMoneyInput ​

Campo de importe cuyo modelo son céntimos enteros (number | null); muestra y entiende el formato local.

Importación: import UiMoneyInput from "@/components/ui/UiMoneyInput.vue".

Props (con *, obligatorias)

PropTipoDefecto
placeholderstring
invalidboolean
disabledboolean
ariaLabelstring

v-model

ModeloTipo
v-modelnumber | null

Ejemplo

vue
<UiMoneyInput v-model="form.priceCents" :aria-label="t('Precio')" />

Fuente: admin/src/components/ui/UiMoneyInput.vue.

UiSearch ​

Campo de búsqueda con retardo (debounce, 250 ms por defecto) que actualiza el modelo cuando el usuario deja de escribir.

Importación: import UiSearch from "@/components/ui/UiSearch.vue".

Props (con *, obligatorias)

PropTipoDefecto
placeholderstring
debouncenumber250

v-model

ModeloTipo
v-modelstring

Ejemplo

vue
<UiSearch v-model="q" :placeholder="t('Buscar notas')" />

Fuente: admin/src/components/ui/UiSearch.vue.

UiSelect ​

Selector de opción única. Las opciones son { value, label, disabled? }.

Importación: import UiSelect from "@/components/ui/UiSelect.vue". Genérico: V extends string | number | null.

Props (con *, obligatorias)

PropTipoDefecto
options *Option<V>[]
iconIconName
invalidboolean
disabledboolean
ariaLabelstring

v-model

ModeloTipo
v-modelV

Ejemplo

vue
<UiSelect v-model="form.category" :options="[{ value: 'ops', label: t('Operación') }, { value: 'mkt', label: t('Marketing') }]" />

Fuente: admin/src/components/ui/UiSelect.vue.

UiSwitch ​

Interruptor de encendido y apagado con etiqueta y descripción opcionales.

Importación: import UiSwitch from "@/components/ui/UiSwitch.vue".

Props (con *, obligatorias)

PropTipoDefectoDescripción
labelstring
descriptionstring
disabledboolean
ariaLabelstringNombre accesible cuando el interruptor va sin texto visible.

v-model

ModeloTipo
v-modelboolean

Ejemplo

vue
<UiSwitch v-model="form.notify" :label="t('Avisar al equipo')" :description="t('Se envía una notificación al guardar.')" />

Fuente: admin/src/components/ui/UiSwitch.vue.

UiTextarea ​

Área de texto de varias líneas; mono usa la tipografía de código.

Importación: import UiTextarea from "@/components/ui/UiTextarea.vue".

Props (con *, obligatorias)

PropTipoDefecto
rowsnumber3
placeholderstring
invalidboolean
maxlengthnumber
monoboolean

v-model

ModeloTipo
v-modelstring | null

Ejemplo

vue
<UiTextarea v-model="form.body" :rows="4" :maxlength="500" />

Fuente: admin/src/components/ui/UiTextarea.vue.

Acciones y navegación ​

UiButton ​

Botón único del panel. Variantes primary, secondary (por defecto), ghost, danger y danger-ghost.

  • Con to se pinta como enlace del router; con href, como enlace externo.
  • Solo icono: pasa icon y label (el texto accesible) y no pongas contenido.

Importación: import UiButton from "@/components/ui/UiButton.vue".

Props (con *, obligatorias)

PropTipoDefectoDescripción
variant"primary" | "secondary" | "ghost" | "danger" | "danger-ghost""secondary"
size"sm" | "md""md"
iconIconName
iconRightIconName
loadingboolean
disabledboolean
type"button" | "submit""button"
toRouteLocationRaw
hrefstring
blockboolean
labelstringSolo icono: exige label para accesibilidad.

Slots

SlotDatos que recibe
default—

Ejemplo

vue
<UiButton variant="primary" icon="check" :loading="saving" @click="save">{{ t("Guardar") }}</UiButton>
<UiButton variant="ghost" size="sm" icon="trash" :label="t('Borrar')" />

Fuente: admin/src/components/ui/UiButton.vue.

UiMenu ​

Menú desplegable de acciones. Cada elemento lleva label, onSelect y, opcionalmente, icon y tone: 'danger'.

Importación: import UiMenu from "@/components/ui/UiMenu.vue".

Props (con *, obligatorias)

PropTipoDefecto
items *MenuItem[]
align"left" | "right""right"

Slots

SlotDatos que recibe
triggeropen, toggle
header—

Ejemplo

vue
<UiMenu :items="[{ label: t('Editar'), icon: 'edit', onSelect: edit }, { label: t('Borrar'), icon: 'trash', tone: 'danger', onSelect: remove }]" />

Fuente: admin/src/components/ui/UiMenu.vue.

UiSegmented ​

Control segmentado de opción única (un radiogroup). Para pocas opciones visibles a la vez.

Importación: import UiSegmented from "@/components/ui/UiSegmented.vue". Genérico: V extends string | number | null.

Props (con *, obligatorias)

PropTipoDefecto
options *Option<V>[]
ariaLabelstring

v-model

ModeloTipo
v-modelV

Ejemplo

vue
<UiSegmented v-model="range" :options="[{ value: 'day', label: t('Día') }, { value: 'week', label: t('Semana') }]" :aria-label="t('Periodo')" />

Fuente: admin/src/components/ui/UiSegmented.vue.

UiTabs ​

Pestañas de una misma pantalla. El modelo es el id de la pestaña activa; count muestra un contador.

Importación: import UiTabs from "@/components/ui/UiTabs.vue".

Props (con *, obligatorias)

PropTipoDefecto
tabs *TabItem[]

v-model

ModeloTipo
v-modelstring

Ejemplo

vue
<UiTabs v-model="tab" :tabs="[{ id: 'open', label: t('Abiertas'), count: 3 }, { id: 'done', label: t('Cerradas') }]" />

Fuente: admin/src/components/ui/UiTabs.vue.

Avisos y estados ​

StatusBadge ​

UiBadge a partir de un diccionario de estados: of mapea cada valor a { label, tone }. Un valor ausente pinta una raya.

Importación: import StatusBadge from "@/components/ui/StatusBadge.vue".

Props (con *, obligatorias)

PropTipoDefecto
of *Record<string, { label: string; tone: Tone }>
value *string | boolean | null | undefined
dotboolean

Ejemplo

vue
<StatusBadge :of="{ open: { label: t('Abierta'), tone: 'info' }, done: { label: t('Cerrada'), tone: 'success' } }" :value="note.status" dot />

Fuente: admin/src/components/ui/StatusBadge.vue.

UiBadge ​

Etiqueta corta con tono (neutral, accent, success, danger, warning, info). El color solo transmite significado.

Importación: import UiBadge from "@/components/ui/UiBadge.vue".

Props (con *, obligatorias)

PropTipoDefecto
toneTone"neutral"
dotboolean
size"sm" | "md""md"

Slots

SlotDatos que recibe
default—

Ejemplo

vue
<UiBadge tone="success" dot>{{ t("Activo") }}</UiBadge>
<UiBadge tone="warning" size="sm">{{ t("Pendiente") }}</UiBadge>

Fuente: admin/src/components/ui/UiBadge.vue.

UiCallout ​

Aviso en línea dentro de la página (información, éxito, atención, error). Lleva título, texto opcional y acciones.

Importación: import UiCallout from "@/components/ui/UiCallout.vue".

Props (con *, obligatorias)

PropTipoDefecto
toneExclude<Tone, "accent">"info"
title *string
iconIconName

Slots

SlotDatos que recibe
default—
actions—

Ejemplo

vue
<UiCallout tone="warning" :title="t('Sin permisos')">{{ t("Pide acceso a un administrador.") }}</UiCallout>

Fuente: admin/src/components/ui/UiCallout.vue.

UiEmpty ​

Estado vacío: explica qué es la pantalla y ofrece el primer paso en el slot por defecto.

Importación: import UiEmpty from "@/components/ui/UiEmpty.vue".

Props (con *, obligatorias)

PropTipoDefecto
iconIconName"sparkles"
title *string
textstring
compactboolean

Slots

SlotDatos que recibe
default—

Ejemplo

vue
<UiEmpty icon="message" :title="t('Aún no hay notas')" :text="t('Crea la primera para tu equipo.')">
  <UiButton variant="primary" icon="plus">{{ t("Nueva nota") }}</UiButton>
</UiEmpty>

Fuente: admin/src/components/ui/UiEmpty.vue.

UiLoadError ​

Estado de error de carga con botón de reintento. Recibe el ApiError de useResource y emite retry.

Importación: import UiLoadError from "@/components/ui/UiLoadError.vue".

Props (con *, obligatorias)

PropTipoDefecto
error *ApiError | null
compactboolean

Eventos

EventoArgumentos
retry

Ejemplo

vue
<UiLoadError v-if="notes.error.value" :error="notes.error.value" @retry="notes.reload" />

Fuente: admin/src/components/ui/UiLoadError.vue.

UiSkeleton ​

Marcador animado mientras carga un dato. Tamaño y radio configurables.

Importación: import UiSkeleton from "@/components/ui/UiSkeleton.vue".

Props (con *, obligatorias)

PropTipoDefecto
widthstring"100%"
heightstring"14px"
radiusstring"6px"

Ejemplo

vue
<UiSkeleton width="60%" height="28px" />

Fuente: admin/src/components/ui/UiSkeleton.vue.

Ventanas y paneles ​

UiDrawer ​

Panel lateral para ver y editar sin perder la lista: cabecera (título, subtítulo, metadatos, acciones), pestañas, cuerpo y pie.

Importación: import UiDrawer from "@/components/ui/UiDrawer.vue".

Props (con *, obligatorias)

PropTipoDefecto
open *boolean
titlestring
subtitlestring
size"md" | "lg""md"

Eventos

EventoArgumentos
close

Slots

SlotDatos que recibe
title—
meta—
actions—
tabs—
default—
footer—

Ejemplo

vue
<UiDrawer :open="!!current" :title="current?.title" size="lg" @close="current = null">
  …
  <template #footer><UiButton variant="primary" @click="save">{{ t("Guardar") }}</UiButton></template>
</UiDrawer>

Fuente: admin/src/components/ui/UiDrawer.vue.

UiModal ​

Ventana modal con foco atrapado. Con cambios sin guardar, un clic fuera o Escape piden confirmación (salvo plain-close). Emite close.

Importación: import UiModal from "@/components/ui/UiModal.vue".

Props (con *, obligatorias)

PropTipoDefectoDescripción
open *boolean
titlestring
size"sm" | "md" | "lg""md"
elevatedbooleanPor encima de cualquier otra ventana (confirmaciones que se abren desde dentro de otra).
plainClosebooleanCierra sin preguntar aunque se haya escrito algo (cobros: nada que perder).

Eventos

EventoArgumentos
close

Slots

SlotDatos que recibe
default—
footer—

Ejemplo

vue
<UiModal :open="open" :title="t('Nueva nota')" size="sm" @close="open = false">
  <UiField :label="t('Título')"><UiInput v-model="form.title" /></UiField>
  <template #footer><UiButton variant="primary" @click="save">{{ t("Guardar") }}</UiButton></template>
</UiModal>

Fuente: admin/src/components/ui/UiModal.vue.

Montados por el panel ​

ConfirmHost ​

Ventana de confirmación única del panel. NO se monta en un plugin: lo hace el AppShell. Se usa con useFeedback().confirm().

Importación: import ConfirmHost from "@/components/ui/ConfirmHost.vue".

Ejemplo

ts
const fb = useFeedback();
if (await fb.confirm({ title: t("¿Borrar la nota?"), tone: "danger", confirmLabel: t("Borrar") })) await remove();

Fuente: admin/src/components/ui/ConfirmHost.vue.

UiToaster ​

Avisos efímeros (toasts). NO se monta en un plugin: lo hace el AppShell. Se usan con useFeedback().toast() y useFeedback().error().

Importación: import UiToaster from "@/components/ui/UiToaster.vue".

Ejemplo

ts
const fb = useFeedback();
fb.toast(t("Nota guardada"));
fb.error(err); // mensaje traducido por el código del ApiError

Fuente: admin/src/components/ui/UiToaster.vue.

Contrato con el host: apiVersion 2. Sitio estático: no recoge datos ni usa servicios de terceros.