integrasoft-shared-ui

Librería de componentes UI en Vue 3 + TypeScript para estandarizar estilos entre los proyectos de Integrasoft.
2,758
Install
npm install integrasoft-shared-ui
Latest Version:0.3.9
Vue:3
License:MIT
Last Updated:Oct 7, 2026
Links: npm
Maintainer: integrasoftsas

integrasoft-shared-ui

Librería de componentes UI en Vue 3 + TypeScript, pensada para estandarizar los estilos y componentes repetidos entre los proyectos frontend de Integrasoft (formularios, modales, tablas de búsqueda, paginación, etc.), en vez de reimplementarlos en cada proyecto.

Publicada en npm: integrasoft-shared-ui

Índice

Instalación

npm install integrasoft-shared-ui

Dependencias necesarias en el proyecto consumidor

La librería no incluye Vue ni FontAwesome dentro de su bundle (son peerDependencies): cada proyecto usa su propia versión ya instalada.

npm install vue@^3.5.0 @fortawesome/vue-fontawesome@^3.2.0 @fortawesome/fontawesome-svg-core

Si tu proyecto no usa iconos de FontAwesome en ningún componente, puedes omitir esas dos dependencias — están marcadas como opcionales (peerDependenciesMeta).

Configuración inicial

1. Importa los estilos globales una sola vez, normalmente en tu main.ts:

import { createApp } from "vue";
import "integrasoft-shared-ui/style.css";
import App from "./App.vue";

createApp(App).mount("#app");

2. Importa los componentes que necesites directamente donde los uses (recomendado, mejor tree-shaking):

<script setup lang="ts">
import { IsuButton } from "integrasoft-shared-ui";
</script>

<template>
  <IsuButton label="Guardar" variant="primary" />
</template>

Alternativa: registrar todos los componentes de golpe como plugin global (menos recomendado, pero disponible):

import IntegrasoftSharedUI from "integrasoft-shared-ui";

createApp(App).use(IntegrasoftSharedUI).mount("#app");

Diseño y tokens (theming)

Todos los componentes usan CSS variables (--isu-*) en vez de colores fijos, para que cada proyecto pueda ajustar la marca sin tocar el código de los componentes. Sobreescribe las que necesites en tu CSS global, después de importar integrasoft-shared-ui/style.css:

:root {
  --isu-color-primary: #135995;
  --isu-color-primary-hover: #1a6bb0;
  --isu-color-danger: #dc2626;
  --isu-font-family: "Poppins", Arial, Helvetica, sans-serif;
  /* ...ver la lista completa en src/styles/tokens.css del repositorio */
}

Temas de color

La librería trae cinco temas. El tema por defecto es :root; los demás se activan con data-theme="<nombre>" en <html> o <body>, y cada uno sobreescribe las mismas variables --isu-*. Las paletas viven en src/styles/tokens.css, bajo el encabezado THEMES - EDIT PALETTES HERE.

<html data-theme="pink">
Tema (data-theme) Primary Secondary Fondo de página Superficie (--isu-color-bg) Borde Texto
defecto (sin atributo) #135995 #03A9F4 #f0eff5 #ffffff #bec7d3 #0f172a
oscuro (alias dark) #3b82f6 #0ea5e9 #000000 #0a0a0a #334155 #f1f5f9
pink #f8c0d9 #fbe9f7 #f1e3e9 #fff8fb #f1d7e0 #3b1f2b
azul #1d4ed8 #60a5fa #e8f0fb #ffffff #b6c8e4 #0f1f3d
verde #15803d #34d399 #e6f4ea #ffffff #b7d6c1 #0f2a1a

Notas:

  • Los valores de pink (fondo, primary, secondary y borde) vienen de diseño. El resto de pink, y casi todo azul y verde, son valores asumidos y se pueden ajustar libremente en tokens.css.
  • Cada tema sobreescribe, además de los de la tabla, el hover de primary/secondary, --isu-color-bg-muted, el texto atenuado (--isu-color-text-muted), --isu-label-color, los tokens de inputs (--isu-input-*) y los de filas de tabla (--isu-table-row-*).
  • --isu-color-page-bg es el color de fondo de la página. La librería no lo pinta sola: el proyecto consumidor lo usa donde corresponda (por ejemplo, --color-backgroup: var(--isu-color-page-bg) en el puente a Tailwind).
  • --isu-color-text-inverse es el color del texto sobre botones primary/secondary/danger. En pink es rosa oscuro (#3b1f2b) porque el blanco no se lee sobre #f8c0d9.
  • Los colores pasados directo por props (iconColor, borderColor, textColor) y los hex fijos en estilos del proyecto consumidor no siguen el tema. Usa var(--isu-color-*) para que lo hagan.

Ejemplo de proyecto consumidor que aplica el tema que le llega de un host:

const TEMAS = ["oscuro", "pink", "azul", "verde"];

function aplicarTema(modo: string): void {
  const tema = modo.trim().toLowerCase();
  if (TEMAS.includes(tema)) document.documentElement.dataset.theme = tema;
  else document.documentElement.removeAttribute("data-theme"); // "defecto" o valor desconocido
}

Sobre la tipografía (Poppins)

--isu-font-family solo le dice a los componentes qué fuente usar — no la carga por sí sola. Si Poppins no está disponible en el navegador, cae al siguiente de la lista (Arial/Helvetica/sans-serif del sistema), que se ve distinto en cada máquina/SO. Cada proyecto consumidor debe cargar la fuente él mismo, por ejemplo:

  • Google Fonts (requiere acceso a internet desde el navegador del usuario final):
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet" />
    
  • Autohospedada (recomendado si tus apps corren en una red interna sin salida a internet garantizada): descarga los archivos .woff2 de Poppins y decláralos con @font-face en el CSS global de tu proyecto.

Componentes

IsuButton

<IsuButton label="Guardar" variant="primary" size="md" @click="onGuardar" />
<IsuButton label="Eliminar" variant="danger" :icon="faTrash" icon-position="left" />
<IsuButton label="Continuar" :icon="faArrowRight" icon-position="right" />

<!-- Botón cuadrado de solo ícono, con tooltip (toolbars, columna de acciones de una rejilla) -->
<IsuButton square variant="secondary" :icon="faRotate" tooltip="Consultar" @click="consultar" />
<IsuButton square variant="danger" :icon="faTrash" tooltip="Eliminar" @click="eliminar" />

<!-- Variante "outline": transparente + borde gris, fondo gris claro al hacer hover -->
<IsuButton variant="outline" label="Cancelar" @click="cancelar" />
<IsuButton variant="outline" square :icon="faTrash" icon-color="#dc2626" tooltip="Eliminar" @click="eliminar" />

<!-- Variante "default": gris neutro con hover, es la variante usada cuando no se especifica `variant` -->
<IsuButton label="Ver detalle" @click="verDetalle" />
<IsuButton variant="default" label="Ver detalle" @click="verDetalle" />

<!-- Colores custom: borderColor/textColor, independientes de la variante -->
<IsuButton label="Pendiente" variant="outline" border-color="#f59e0b" text-color="#f59e0b" />
<!-- Si hay ícono y no se pasa iconColor, el ícono hereda textColor -->
<IsuButton label="Pendiente" variant="outline" :icon="faTriangleExclamation" text-color="#f59e0b" border-color="#f59e0b" />

<!-- Botones predeterminados de la empresa: label/ícono/variante/colores ya definidos -->
<IsuButton preset="guardar" @click="guardar" />
<IsuButton preset="atras" @click="volver" />
<IsuButton preset="nuevo" @click="crear" />
<IsuButton preset="eliminar" @click="eliminar" />
<IsuButton preset="editar" @click="editar" />
<IsuButton preset="descargar" @click="descargar" />
<IsuButton preset="buscar" @click="buscar" />
<IsuButton preset="cancelar" @click="cancelar" />

<!-- Cualquier prop pasada explícitamente tiene prioridad sobre el preset -->
<IsuButton preset="guardar" label="Guardar cambios" variant="secondary" />
Prop Tipo Default Descripción
label string — Texto del botón. Si no se pasa, se usa el slot por defecto (o el label del preset, si hay uno).
variant "primary" | "secondary" | "danger" | "disabled" | "outline" | "default" "default" Estilo visual. "outline": fondo transparente, borde gris, fondo gris claro al hacer hover. "default": gris neutro con hover, mismo set de props que el resto de variantes.
size "sm" | "md" | "lg" "md" Tamaño (padding + tipografía). En modo square, define el lado del cuadrado (28px / igual al alto de los inputs / 40px).
disabled boolean false Deshabilita el botón (atributo nativo: bloquea clicks y aplica el estilo atenuado).
type "button" | "submit" | "reset" "button" Atributo type nativo.
icon IconDefinition (FontAwesome) — Ícono opcional.
iconPosition "left" | "right" "left" Posición del ícono respecto al texto (ignorado si square es true).
iconColor string — Color CSS del ícono (ej. "#dc2626" o "var(--isu-color-primary)"), sin importar la variante. Si no se pasa, usa textColor (si está definida), o el color de texto normal de la variante actual.
borderColor string — Color CSS del borde del botón (ej. "#f59e0b" o "var(--isu-color-warning)"), sin importar la variante.
textColor string — Color CSS del texto del botón, sin importar la variante. También se usa como color del ícono si iconColor no está definida.
square boolean false Botón cuadrado de solo ícono (ignora label y el slot por defecto). El tamaño md queda exactamente del alto de un input, para alinearse con IsuInput/IsuInputWithAddon.
tooltip string — Texto mostrado en un tooltip propio (estilizado con los tokens de la librería) al pasar el mouse o enfocar el botón. Textos largos hacen wrap automáticamente en varias líneas (máximo ~220px de ancho) en vez de desbordarse en una sola línea.
tooltipPosition "top" | "bottom" | "left" | "right" "top" Lado del botón donde aparece el tooltip.
preset "guardar" | "atras" | "nuevo" | "eliminar" | "editar" | "descargar" | "buscar" | "cancelar" — Botón predeterminado de la empresa: aplica automáticamente label, icon, iconPosition, variant y colores/tooltip por defecto (ver tabla abajo). Cualquier otra prop pasada explícitamente (label, variant, icon, iconColor, tooltip, etc.) tiene prioridad sobre el valor del preset.

Presets disponibles:

preset Variante Ícono Label Tooltip Notas
guardar primary disquete, izquierda "Guardar" "Guardar" —
atras default flecha izquierda "Atrás" "Volver" —
nuevo default más, izquierda "Nuevo" "Nuevo" —
eliminar outline, square basura (solo ícono) — "Eliminar" Ícono y borde en rojo (--isu-color-danger)
editar outline, square lápiz (solo ícono) — "Editar" Ícono y borde en azul (--isu-color-primary)
descargar secondary descarga, izquierda "Descargar" "Descargar" —
buscar secondary lupa, izquierda "Buscar" "Buscar" —
cancelar default equis, izquierda "Cancelar" "Cancelar" —

⚠️ variant="disabled" solo aplica el estilo visual gris de deshabilitado — el botón sigue siendo clickeable. Si además quieres que no reaccione a clicks, agrega también la prop disabled.

Emits: click(event: MouseEvent)

IsuInput

Input de texto estandarizado con label, validación y filtros de escritura.

<IsuInput v-model="fecha" type="date" label="Fecha del documento" required min="2026-01-01" />
<IsuInput v-model="correo" type="email" label="Correo" placeholder="nombre@dominio.com" />
<IsuInput v-model="nombre" filter="text" label="Solo texto (sin números)" />
<IsuInput v-model="telefono" filter="number" label="Solo número (sin letras)" />
<IsuInput v-model="valorUnitario" filter="money" label="Valor unitario" align="right" />
<IsuInput v-model="unidad" label="Unidad funcional" readonly required />

<!-- Tooltip propio: aparece tras mantener el cursor encima del input durante tooltipDelay ms -->
<IsuInput
  v-model="unidad"
  label="Unidad funcional"
  readonly
  tooltip="Este campo se completa automáticamente desde el módulo de presupuesto"
  :tooltip-delay="800"
  tooltip-position="right"
/>
Prop Tipo Default Descripción
modelValue string — Valor del input (v-model).
type "text" | "number" | "date" | "email" | "password" | "tel" "text" Tipo nativo de HTML.
filter "none" | "text" | "number" | "money" "none" Restringe lo que se puede escribir, independiente del type. "text": solo letras/espacios. "number": solo dígitos. "money": solo dígitos, formateados en vivo con separador de miles (es-CO) y el signo $ al inicio (ej. "$ 1.000.000"). Se aplica al escribir, al pegar, y también al valor inicial recibido por v-model (aunque llegue como dígitos sin formatear).
label string — Etiqueta encima del input.
required boolean false Muestra asterisco rojo junto al label.
disabled / readonly boolean false Estados nativos.
error string — Mensaje de error externo (prioridad sobre la validación automática de email).
emailErrorMessage string "Correo electrónico no válido" Mensaje mostrado cuando type="email" y el valor no tiene estructura de correo válida (se valida en tiempo real, mientras se escribe).
reserveErrorSpace boolean true Reserva el espacio del mensaje de error para evitar saltos de layout.
align "left" | "right" "left" Alineación del texto (usar "right" para campos monetarios).
min / max — — Atributos nativos (rangos de fecha/número).
maxlength number — Límite de caracteres. En type="text", "email", "password", "tel": atributo nativo del navegador. En type="number": el navegador ignora maxlength (no es un tipo soportado por el spec de HTML), así que el componente lo refuerza a mano truncando lo que exceda el límite al escribir o pegar.
tooltip string — Texto de un tooltip propio (mismo estilo visual que el de IsuButton), mostrado tras mantener el cursor sobre el input durante tooltipDelay ms. Si el cursor sale antes de que se cumpla el delay, no llega a mostrarse.
tooltipDelay number 500 Milisegundos de espera con el cursor encima antes de mostrar el tooltip.
tooltipPosition "top" | "bottom" | "left" | "right" "top" Lado del input donde aparece el tooltip.

Slots: addon — contenido renderizado al lado del input (mismo alto), sin afectar la posición del label ni del mensaje de error. Es lo que usa IsuInputWithAddon por dentro para sus botones de acción; también se puede usar directamente en IsuInput. Emits: update:modelValue(value: string), focus(event), blur(event)

IsuTextarea

Área de texto multilínea con el mismo label/asterisco/validación que IsuInput. El ancho lo define el contenedor y no cambia; solo el alto es ajustable (arrastrando la esquina inferior derecha), gracias a resize: vertical por defecto.

<IsuTextarea v-model="observaciones" label="Observaciones" placeholder="Escribe aquí..." />

<!-- Con contador de caracteres y límites -->
<IsuTextarea
  v-model="justificacion"
  label="Justificación"
  required
  :minlength="20"
  :maxlength="300"
  show-char-count
/>

<!-- Tamaño fijo, sin permitir redimensionar -->
<IsuTextarea v-model="descripcion" label="Descripción" resize="none" :rows="4" />

<!-- Alternativa a resize: crece solo según el contenido, sin tirador ni scroll interno -->
<IsuTextarea v-model="descripcion" label="Descripción" auto-grow />
Prop Tipo Default Descripción
modelValue string — Valor del textarea (v-model).
label string — Etiqueta encima del campo.
required boolean false Muestra asterisco rojo junto al label.
disabled / readonly boolean false Estados nativos.
error string — Mensaje de error externo (prioridad sobre la validación automática de minlength).
reserveErrorSpace boolean true Igual que en IsuInput.
minlength number — Si se define, valida en vivo mientras se escribe y muestra "Mínimo N caracteres" cuando el campo tiene contenido pero no alcanza el mínimo.
maxlength number — Atributo nativo: el navegador bloquea escribir/pegar más caracteres. También se usa como total del contador (actual/maxlength) si showCharCount está activo.
rows number 3 Alto inicial en líneas visibles.
resize "vertical" | "none" "vertical" "vertical": el usuario puede arrastrar para cambiar el alto (el ancho siempre queda fijo por el contenedor). "none": tamaño fijo, sin permitir redimensionar. Se ignora si autoGrow está activo.
autoGrow boolean false Alternativa a resize: el alto se ajusta solo al contenido a medida que se escribe, sin scroll interno. Al activarlo, el usuario ya no puede arrastrar el alto a mano (equivale a forzar resize: "none").
showCharCount boolean false Muestra el contador de caracteres debajo, a la derecha del mensaje de error.

Emits: update:modelValue(value: string), focus(event), blur(event)

IsuModal

<IsuButton label="Abrir" @click="visible = true" />

<IsuModal v-model="visible" title="Editar registro">
  Contenido del modal.
  <template #footer="{ close }">
    <IsuButton variant="secondary" label="Cancelar" @click="close" />
    <IsuButton variant="primary" label="Guardar" @click="close" />
  </template>
</IsuModal>
Prop Tipo Default Descripción
modelValue boolean — Visibilidad (v-model).
title string — Título del header.
icon IconDefinition — Ícono junto al título.
variant "default" | "danger" "default" Variante visual del header (danger para confirmaciones destructivas).
size "sm" | "md" | "lg" | "xl" "md" Ancho máximo predefinido.
maxWidth string — Ancho máximo custom (ej. "820px"), tiene prioridad sobre size.
closeOnOverlayClick boolean true Cerrar al hacer click fuera del modal.
closeOnEsc boolean true Cerrar con la tecla Esc.
showCloseButton boolean true Muestra el botón de cerrar.
closeIcon IconDefinition — Ícono del botón de cerrar (ej. faXmark). Si no se pasa, se muestra "✕" como texto.
teleportTarget string "body" Destino del <Teleport>.

Slots: default (body), header (reemplaza título+ícono, recibe { close }), footer (recibe { close }). Emits: update:modelValue(value: boolean), close()

IsuLoading

Loading estándar del proyecto (logo animado con órbita de puntos + revelado del wordmark). No es cerrable por el usuario — su visibilidad la controla el consumidor 100% vía v-model, típicamente atado a un estado de "cargando" (boot de la app, transición de ruta, una petición larga).

<!-- Pantalla completa (default): tapa toda la vista con Teleport a body -->
<IsuLoading v-model="cargandoApp" />

<!-- Inline: se renderiza donde lo pongas, sin overlay ni Teleport -->
<IsuLoading v-model="cargandoSeccion" :fullscreen="false" :size="150" />

<!-- Con texto debajo (slot libre) -->
<IsuLoading v-model="cargandoApp">
  <p style="margin:0; color:var(--isu-color-text-muted); font-size:13px">Cargando información...</p>
</IsuLoading>
Prop Tipo Default Descripción
modelValue boolean — Visibilidad (v-model). No hay forma de cerrarlo desde el componente mismo.
size number 300 Tamaño en px del loader. Todo el diseño (puntos, wordmark, radio de la órbita) escala proporcionalmente al valor original de 300px.
fullscreen boolean true true: cubre toda la pantalla (<Teleport> a teleportTarget, fondo sólido, bloquea el scroll del body mientras está visible). false: se renderiza inline donde se use, sin overlay.
teleportTarget string "body" Destino del <Teleport>. Solo aplica cuando fullscreen es true.

Slots: default — contenido libre debajo del loader (ej. un texto de estado).

IsuPagination

<!-- Selector fijo: aplica al instante -->
<IsuPagination v-model="page" v-model:page-size="pageSize" :total-items="230" :page-size-options="[8, 16, 32]" />

<!-- Input libre + botón "Mostrar" -->
<IsuPagination v-model="page" v-model:page-size="pageSize" :total-items="500" :sibling-count="4" />
Prop Tipo Default Descripción
modelValue number — Página actual, 1-indexed (v-model).
pageSize number — Registros por página (v-model:page-size).
totalItems number — Total de registros.
pageSizeOptions number[] — Si se pasa, renderiza un <select> que aplica al instante. Si se omite, renderiza un input numérico libre + botón "Mostrar".
minPageSize / maxPageSize number 5 / 50 Límites del input libre.
siblingCount number 2 Páginas visibles a cada lado de la actual.
showPageSize boolean true Oculta el control de tamaño de página.

El input numérico libre aplica el cambio al presionar Enter (además del botón "Mostrar"), y no muestra las flechas de incremento/decremento nativas del navegador.

Emits: update:modelValue(page: number), update:pageSize(size: number)

IsuBadge

<IsuBadge label="Provisional" variant="warning" />
<IsuBadge label="Firme" variant="success" />
<IsuBadge label="Anulado" variant="danger" size="md" :dot="false" />
Prop Tipo Default Descripción
label string — Texto del badge.
variant "success" | "warning" | "info" | "danger" | "neutral" "neutral" Color (fondo pastel + texto contrastante).
size "sm" | "md" "sm" Tamaño.
dot boolean true Muestra el punto de color junto al texto.

IsuInputWithAddon

IsuInput con uno o varios botones de acción adjuntos (buscar, refrescar, etc.) — internamente envuelve IsuInput y expone exactamente las mismas props (label, filtros, validación de email, alineación, error, etc.), así que el campo de búsqueda tiene las mismas opciones que cualquier IsuInput. Los botones son IsuButton en modo square, con soporte de tooltip.

<IsuInputWithAddon
  v-model="nit"
  label="CC / NIT"
  required
  placeholder="Buscar por NIT o nombre..."
  filter="number"
  :actions="[
    { icon: faRotate, onClick: consultar, tooltip: 'Consultar' },
    { icon: faMagnifyingGlass, onClick: abrirModal, tooltip: 'Buscar' },
  ]"
/>
Prop Tipo Default Descripción
modelValue string — Valor del input (v-model).
type "text" | "number" | "date" | "email" | "password" | "tel" "text" Igual que en IsuInput.
filter "none" | "text" | "number" | "money" "none" Igual que en IsuInput (incluye "money" para valores monetarios).
label string — Etiqueta encima del input (igual estilo que IsuInput).
placeholder string — Igual que un input nativo.
required boolean false Muestra asterisco rojo junto al label.
disabled / readonly boolean false Estados nativos.
error string — Mensaje de error externo (igual que en IsuInput): borde rojo + mensaje debajo del input y los botones.
emailErrorMessage string "Correo electrónico no válido" Igual que en IsuInput (solo aplica con type="email").
reserveErrorSpace boolean true Igual que en IsuInput.
align "left" | "right" "left" Igual que en IsuInput (usar "right" para campos monetarios).
min / max / maxlength / name — — Igual que en IsuInput.
tooltip / tooltipDelay / tooltipPosition — — / 500 / "top" Igual que en IsuInput: tooltip propio del campo, mostrado tras mantener el cursor encima.
actions InputAddonAction[] [] Botones adjuntos: { icon, onClick, disabled?, variant?: ButtonVariant, tooltip?: string, ariaLabel? }. variant usa las mismas opciones que IsuButton ("primary" | "secondary" | "danger" | "disabled" | "outline" | "default", default "primary").

Emits: update:modelValue(value: string), focus(event), blur(event)

IsuAutocomplete

Lista flotante de sugerencias, genérica sobre cualquier tipo de dato (usar junto a un input propio o a IsuInputWithAddon, dentro de un contenedor con position: relative).

El contenedor position: relative debe envolver solo el input y el IsuAutocomplete. Si agregás ahí otro contenido que aparece condicionalmente (p. ej. un mensaje de "seleccionado"), le suma altura al contenedor y el dropdown se desplaza hacia abajo la próxima vez que se abra.

<div style="position: relative">
  <input v-model="query" />
  <IsuAutocomplete :items="sugerencias" @select="seleccionar">
    <template #default="{ item }">
      <span>{{ item.nit }}</span>
      <span>{{ item.nombre }}</span>
    </template>
  </IsuAutocomplete>
</div>
Prop Tipo Default Descripción
items T[] — Lista de sugerencias a mostrar (ya filtrada por quien la use). No se renderiza nada si está vacía.

Al seleccionar un ítem (@select), el componente se oculta automáticamente aunque items no quede vacío, y solo vuelve a mostrarse cuando items cambia de nuevo (p. ej. el usuario sigue escribiendo).

Slots: default({ item, index }) — contenido de cada sugerencia, 100% libre. Emits: select(item: T)

IsuSearchPickerModal

Combina IsuModal + tabla + IsuPagination para modales de búsqueda con selección de fila (reemplaza los típicos "buscar tercero / buscar producto / buscar unidad funcional").

<IsuSearchPickerModal
  v-model="visible"
  title="Buscar tercero"
  :icon="faSearch"
  :items="resultados"
  :loading="cargando"
  search-placeholder="Buscar por NIT o nombre..."
  @search="onBuscar"
  @select="onSeleccionar"
>
  <template #columns>
    <th>NIT</th>
    <th>Nombre</th>
  </template>
  <template #row="{ item }">
    <td>{{ item.nit }}</td>
    <td>{{ item.nombre }}</td>
  </template>
</IsuSearchPickerModal>

El componente no filtra los datos por sí mismo: solo maneja el input de búsqueda y la paginación. Escucha el evento search y actualiza tú la prop items (filtrando en memoria o llamando a tu API).

Prop Tipo Default Descripción
modelValue boolean — Visibilidad (v-model).
title / icon / size — size: "xl" Igual que en IsuModal.
items T[] — Resultados actuales a mostrar (ya buscados/filtrados por ti).
loading boolean false Muestra un spinner en vez de la tabla.
searchPlaceholder string "Buscar..." Placeholder del input de búsqueda.
emptyMessage string — Mensaje cuando no hay resultados (por defecto varía si hay o no una búsqueda escrita).
pageSize number 8 Tamaño de página inicial.
pageSizeOptions number[] — Igual que en IsuPagination.
siblingCount number 4 Igual que en IsuPagination.

Slots: columns (<th> del header), row({ item }) (<td> de cada fila), empty (mensaje vacío custom), footer({ close }) (por defecto muestra un botón "Cancelar"). Emits: update:modelValue(value: boolean), search(query: string), select(item: T)

IsuDataGrid

Tabla estandarizada con paginación integrada, para usar directamente en una vista (listados, grillas de detalle) — mismo estándar visual que IsuSearchPickerModal, pero sin el modal. Las columnas se definen como datos (título, alineación, formato) y el componente las renderiza automáticamente; para columnas especiales (badges, botones de acción, combinaciones de campos) hay un slot de escape por columna. Opcionalmente puede incluir su propio buscador interno (showSearch) que filtra en memoria los items ya cargados.

<script setup lang="ts">
import { ref } from "vue";
import type { DataGridColumn } from "integrasoft-shared-ui";

const seleccionado = ref<number | null>(null);

const columnas: DataGridColumn[] = [
  { key: "consecutivo", label: "Consecutivo" },
  { key: "tercero", label: "Tercero" },
  { key: "estado", label: "Estado", align: "center" }, // se llena con el slot cell-estado
  {
    key: "valor",
    label: "Valor",
    align: "right", // el contenido queda a la derecha...
    alignTitle: "center", // ...pero el título queda centrado
    format: "currency",
    cellStyle: (item) => (item.valor > 5000000 ? { color: "var(--isu-color-danger)", fontWeight: "700" } : {}),
  },
];
</script>

<template>
  <!-- Listado con fila clickeable, estado resaltado, una columna con badge y buscador interno -->
  <IsuDataGrid
    :items="documentosFiltrados"
    :columns="columnas"
    show-search
    search-placeholder="Buscar por consecutivo o tercero..."
    :row-class="(doc) => ({ 'isu-data-grid__row--selected': doc.id === seleccionado })"
    @row-click="(doc) => (seleccionado = doc.id)"
  >
    <template #cell-estado="{ item }">
      <IsuBadge :label="item.estadoLabel" :variant="item.estadoVariant" />
    </template>
  </IsuDataGrid>
</template>
<!-- Grilla de detalle sin paginación, con columnas combinadas (accessor) y una columna de acciones -->
<script setup lang="ts">
const columnasDetalle: DataGridColumn[] = [
  { key: "combo", label: "Línea - Producto", accessor: (d) => `${d.linea} - ${d.producto}` },
  { key: "cantidad", label: "Cantidad", align: "right", format: "number" },
  { key: "valorUnitario", label: "Valor unitario", align: "right", format: "currency" },
  { key: "iva", label: "% Iva", align: "center", format: "percentage" },
  { key: "acciones", label: "Acciones", align: "center", format: "button" },
];
</script>

<template>
  <IsuDataGrid :items="detalles" :columns="columnasDetalle" :show-pagination="false" :clickable-rows="false">
    <template #cell-acciones="{ index }">
      <IsuButton size="sm" variant="secondary" :icon="faPen" @click="editar(index)" />
      <IsuButton size="sm" variant="danger" :icon="faTrash" @click="eliminar(index)" />
    </template>
  </IsuDataGrid>
</template>

El componente pagina internamente: recibe la lista completa (ya filtrada/buscada por ti) y solo muestra la porción de la página actual — igual que IsuSearchPickerModal, no reimplementes el "slice" en cada vista.

Columnas de botones (format: "button"): marca la columna como una columna de acciones. El contenido que renderices en el slot cell-<key> (típicamente uno o más IsuButton) queda centrado horizontalmente con un gap entre los botones, sin que tengas que escribir estilos propios. Además, igual que las columnas con field, queda no-ordenable por default (no tendría sentido ordenar por una columna de botones).

<template #cell-acciones="{ item }">
  <IsuButton size="sm" variant="secondary" :icon="faPen" @click="editar(item)" />
  <IsuButton size="sm" variant="danger" :icon="faTrash" @click="eliminar(item)" />
</template>

Definición de columna (DataGridColumn):

Campo Tipo Default Descripción
key string — Se usa para leer item[key] (salvo que definas accessor) y para el slot de escape cell-<key>.
label string — Título de la columna (header).
align "left" | "center" | "right" "left" Alineación horizontal del contenido de la celda.
alignTitle "left" | "center" | "right" usa align Alineación horizontal del título (header), independiente de align. Útil para, por ejemplo, centrar el título mientras el contenido queda a la derecha.
valign "top" | "middle" | "bottom" "middle" Alineación vertical del contenido de la celda.
format "text" | "number" | "currency" | "percentage" | "date" | "button" "text" "currency": antepone $ y formatea con separador de miles y decimales ($ 1.000.000,00) — configurable con currency, ver abajo. "percentage": agrega % al final. "date": espera un valor parseable por Date (recomendado: string ISO "YYYY-MM-DD") y lo muestra como DD/MM/AAAA. "number"/"text": el valor tal cual. "button": marca la columna como columna de acciones — ver "Columnas de botones (format: "button")" abajo.
currency DataGridCurrencyOptions — Solo aplica con format: "currency". Ver "Formato de moneda" abajo.
width string — Ancho fijo de la columna, en cualquier unidad CSS (ej. "160px"). Tiene prioridad sobre widthPercent.
widthPercent number — Ancho como porcentaje del total de la tabla (ej. 20 = 20%). Útil para repartir el ancho entre columnas sin calcular píxeles. Se ignora si defines width.
accessor (item) => unknown — Función para combinar/transformar varios campos en una sola columna. Si no se define, se usa item[key] directamente.
cellStyle Record<string, string | number> | (item) => Record<string, string | number> — Estilos CSS extra para el contenido de la celda (no el título). Acepta un objeto fijo o una función por item — por ejemplo, para resaltar en rojo un valor que excede un límite.
field DataGridColumnField — Si se define, la celda renderiza un IsuInput editable (bindeado a item[key]) en vez de texto/slot — ver abajo.
sortable boolean ver abajo Habilita ordenar al hacer click en el título — ver "Ordenar por columna".

Formato de moneda (currency): usa Intl.NumberFormat con style: "currency" (locale es-CO), configurable por columna. Por defecto es peso colombiano (COP) con 2 decimales — igual que antes, sin tener que declarar nada.

<script setup lang="ts">
const columnas: DataGridColumn[] = [
  { key: "valor", label: "Valor", format: "currency" }, // $ 1.000.000,00 (default)
  { key: "total", label: "Total", format: "currency", currency: { maximumFractionDigits: 0 } }, // $ 1.000.000 (sin decimales)
  {
    key: "tasa",
    label: "Tasa",
    format: "currency",
    currency: { minimumFractionDigits: 0, maximumFractionDigits: 2 }, // $ 1.000.000 o $ 1.000.000,5 (decimales solo si el valor los tiene)
  },
  { key: "montoUsd", label: "Monto (USD)", format: "currency", currency: { currencyCode: "USD" } }, // US$ 1.000,00
];
</script>
Campo de DataGridCurrencyOptions Tipo Default Descripción
currencyCode string "COP" Código ISO 4217. Define el símbolo ($, US$, etc.) y las reglas de redondeo de la moneda.
minimumFractionDigits number sigue a maximumFractionDigits Decimales mínimos a mostrar.
maximumFractionDigits number 2 Decimales máximos a mostrar.

Si minimumFractionDigits queda mayor que maximumFractionDigits (por un typo), el componente lo ajusta solo hacia abajo en vez de dejar que Intl.NumberFormat tire una excepción.

Ordenar por columna: al hacer click en el título, la columna ordena ascendente → descendente → orden original (3 estados), con un ícono que indica la dirección activa. Por defecto, una columna es ordenable solo si tiene format (distinto de "button") y no es editable (field) — así las columnas de acciones (format: "button") quedan afuera automáticamente, sin que tengas que declarar nada. Podés forzar el comportamiento con sortable: true (ej. para habilitar una columna con slot que sí representa un valor ordenable) o sortable: false (para apagarla en una columna con format que no quieras que se pueda ordenar). El orden se aplica sobre la lista ya filtrada, antes de paginar, y las filas con valor vacío siempre quedan al final sin importar la dirección.

<script setup lang="ts">
const columnas: DataGridColumn[] = [
  { key: "consecutivo", label: "Consecutivo" }, // sin format: no ordenable
  { key: "fecha", label: "Fecha", format: "date" }, // ordenable, compara como fecha (no alfabético)
  { key: "valor", label: "Valor", align: "right", format: "currency" }, // ordenable
  { key: "acciones", label: "Acciones", align: "center", format: "button" }, // no ordenable
];
</script>

<template>
  <IsuDataGrid :items="documentos" :columns="columnas">
    <template #cell-acciones="{ item }">
      <IsuButton size="sm" variant="secondary" :icon="faPen" @click="editar(item)" />
    </template>
  </IsuDataGrid>
</template>

Columnas editables (field), para grillas de detalle tipo "cantidad autorizada" o valores que el usuario corrige en línea:

<script setup lang="ts">
const columnas: DataGridColumn[] = [
  { key: "producto", label: "Producto" },
  {
    key: "cantAutorizada",
    label: "Cant. autorizada",
    align: "right",
    field: { type: "number", filter: "number", placeholder: "0" },
  },
  {
    key: "valorUnitario",
    label: "Valor unitario",
    align: "right",
    field: { filter: "money" }, // "$ 1.000.000" formateado en vivo
  },
];
</script>

<template>
  <IsuDataGrid :items="detalles" :columns="columnas" @cell-change="(item, column, value) => recalcular(item)" />
</template>

El componente muta item[key] directamente (los objetos de items deben ser reactivos, ej. definidos con ref/reactive) y además emite cell-change para que puedas reaccionar (recalcular totales, validar, etc.).

Campo de DataGridColumnField Tipo Descripción
type InputType de IsuInput Tipo nativo del input ("text", "number", "date", etc.).
filter InputFilter de IsuInput Incluye "money" para valores monetarios editables en línea.
placeholder string Placeholder del input.
min / max string | number Igual que en IsuInput.
disabled boolean | (item) => boolean Deshabilita el input, fijo o condicionado por fila.

Props del componente:

Prop Tipo Default Descripción
items T[] — Lista completa ya filtrada (el grid pagina internamente). Al reemplazar el array (ej. tras filtrar) vuelve automáticamente a la página 1.
columns DataGridColumn[] — Definición de columnas (ver arriba).
bordered boolean true Muestra bordes verticales entre columnas.
rowClass (item: T, index: number) => string | string[] | Record<string, boolean> — Clase(s) extra por fila. Usa isu-data-grid__row--selected o isu-data-grid__row--editing para los estados ya estilizados, o cualquier clase propia.
clickableRows boolean true Si es true, las filas muestran cursor pointer y emiten row-click.
loading boolean false Muestra un spinner en vez de la tabla.
emptyMessage string "No hay registros para mostrar." Mensaje cuando items está vacío (o usa el slot empty).
showPagination boolean true Oculta la paginación y muestra todos los items sin recortar (útil para grillas de detalle pequeñas).
pageSize number 8 Tamaño de página inicial.
pageSizeOptions number[] — Igual que en IsuPagination (selector fijo vs. input libre + "Mostrar").
minPageSize number 1 Límite inferior del input libre de tamaño de página (se reenvía a IsuPagination; no modifica los defaults propios de IsuPagination cuando se usa fuera de IsuDataGrid).
maxPageSize number 100 Límite superior del input libre de tamaño de página (se reenvía a IsuPagination).
siblingCount number 2 Igual que en IsuPagination.
showSearch boolean false Muestra un IsuInput de búsqueda (con label "Buscar") arriba de la rejilla, que filtra en memoria los items ya cargados (no dispara ninguna petición externa).
searchPlaceholder string "Buscar..." Placeholder del input de búsqueda (solo aplica si showSearch es true).

El componente muestra una barra superior de color azul (mismo estilo que IsuSearchPickerModal, con un borde inferior que la separa de la tabla) con el formato "Contar: {vistos}/{total}": {vistos} es el acumulado de registros recorridos hasta la página actual (ej. en la página 5 con 8 por página, {vistos} es 40, no 8) y {total} es la cantidad de items que coinciden con la búsqueda activa (si showSearch está desactivado, {total} es simplemente el total de items). Con showPagination: false, {vistos} es igual a {total}, ya que se muestran todos los registros de una vez. Se oculta automáticamente cuando no hay resultados que mostrar.

Cuando showSearch está activo, la búsqueda compara el texto visible de todas las columnas (incluyendo columnas con accessor y formatos como currency/percentage, ya formateados), sin distinguir mayúsculas/minúsculas, y vuelve a la página 1 en cada cambio. Si la búsqueda no encuentra coincidencias se muestra "No se encontraron resultados.", distinto del emptyMessage que se usa cuando items está vacío desde el inicio. Limitación: el contenido que solo se renderiza a través de un slot cell-<key> (por ejemplo un IsuBadge custom) no participa en la búsqueda, porque esta se basa en el valor/formato de la columna, no en el HTML del slot.

Slots: cell-<key>({ item, index, value }) (uno por cada key de columna sin field — sustituye el valor formateado por contenido custom), empty (mensaje vacío custom). Emits: row-click(item: T, index: number), cell-change(item: T, column: DataGridColumn, value: string) (columnas editables).

IsuSwitch

Interruptor on/off, con label y texto de estado ("Sí"/"No" por defecto) junto al switch.

<IsuSwitch v-model="tieneMedidor" label="Tiene medidor" />
Prop Tipo Default Descripción
modelValue boolean — Estado del switch (v-model).
label string — Etiqueta encima del switch.
required boolean false Muestra asterisco rojo junto al label.
disabled boolean false Deshabilita el switch.
onLabel string "Sí" Texto mostrado junto al switch cuando está activado.
offLabel string "No" Texto mostrado junto al switch cuando está desactivado.
showStateLabel boolean true Oculta el texto de estado si es false.

Emits: update:modelValue(value: boolean)

IsuDropdown

Selector con el mismo estilo visual que IsuInput (label, borde, foco, error), con modo de selección única o múltiple. En modo múltiple, cada opción muestra un checkbox y el panel permanece abierto entre selecciones.

<script setup lang="ts">
import { ref } from "vue";
import type { DropdownOption } from "integrasoft-shared-ui";

const estado = ref<string | number | null>(null);
const estados = ref<(string | number)[]>([]);

const opciones: DropdownOption[] = [
  { value: "prov", label: "Provisional" },
  { value: "firme", label: "Firme" },
  { value: "anulado", label: "Anulado", disabled: true },
];
</script>

<template>
  <!-- Selección única -->
  <IsuDropdown v-model="estado" :options="opciones" label="Estado" required />

  <!-- Selección múltiple, con checkbox por opción -->
  <IsuDropdown v-model="estados" :options="opciones" multiple label="Estados" />
</template>
Prop Tipo Default Descripción
modelValue string | number | (string | number)[] | null — Valor seleccionado (v-model). En modo multiple es un arreglo; en modo simple, un valor único o null.
options DropdownOption[] — { value: string | number, label: string, disabled?: boolean }.
multiple boolean false Activa selección múltiple con checkbox por opción; el panel no se cierra al seleccionar.
label string — Etiqueta encima del control.
placeholder string "Seleccione..." Texto mostrado cuando no hay nada seleccionado.
required boolean false Muestra asterisco rojo junto al label.
disabled boolean false Deshabilita el control (no abre el panel).
error string — Mensaje de error, con borde rojo en el control.
reserveErrorSpace boolean true Reserva el espacio del mensaje de error para evitar saltos de layout.

En modo multiple, si hay 3 o más opciones seleccionadas el control muestra "N seleccionados" en vez de listar todas las etiquetas (con 1 o 2 seleccionadas, las junta con ", "). El panel se cierra automáticamente al hacer clic fuera del componente.

Emits: update:modelValue(value: string | number | (string | number)[] | null)

IsuPageHeader

Encabezado de página estándar: ícono + título, con una ruta de navegación (breadcrumb) debajo. Todos los segmentos de la ruta son clickeables excepto el último, que se muestra como la página activa (en color primario, sin interacción). El componente no navega por sí mismo — emite breadcrumb-click con el ítem clickeado y su índice, y cada proyecto decide cómo navegar (vue-router, window.location, etc.), igual que search en IsuSearchPickerModal o row-click en IsuDataGrid.

<script setup lang="ts">
import { faClipboard } from "@fortawesome/free-solid-svg-icons";
import type { PageHeaderBreadcrumbItem } from "integrasoft-shared-ui";
import { useRouter } from "vue-router";

const router = useRouter();

const ruta: PageHeaderBreadcrumbItem[] = [
  { label: "ERP.go", to: "/" },
  { label: "Servicios públicos", to: "/servicios-publicos" },
  { label: "Procesos", to: "/servicios-publicos/procesos" },
  { label: "Confirmar orden de trabajo" }, // último = página activa, no lleva "to"
];

function onBreadcrumbClick(item: PageHeaderBreadcrumbItem) {
  if (item.to) router.push(item.to as string);
}
</script>

<template>
  <IsuPageHeader :icon="faClipboard" title="Órdenes de trabajo" :items="ruta" @breadcrumb-click="onBreadcrumbClick" />
</template>
Prop Tipo Default Descripción
icon IconDefinition (FontAwesome) — Ícono junto al título.
title string — Título de la página.
items PageHeaderBreadcrumbItem[] — Segmentos de la ruta, en orden: { label: string, to?: unknown }. to es un valor libre (string, objeto de ruta, etc.) que se devuelve tal cual en breadcrumb-click — el componente no lo interpreta. El último ítem del arreglo siempre se renderiza como la página activa, sin importar si tiene to.

Emits: breadcrumb-click(item: PageHeaderBreadcrumbItem, index: number) — solo se emite al hacer clic en un segmento que no sea el último.

IsuTabs

Barra de pestañas para navegar entre secciones de una página. La pestaña activa se resalta (texto y borde inferior en color primario) y no es clickeable; el resto son clickeables. Igual que IsuPageHeader, el componente no navega por sí mismo — emite tab-click con el ítem y su índice, y cada proyecto decide cómo cambiar de página.

<script setup lang="ts">
import { faFileEdit, faList } from "@fortawesome/free-solid-svg-icons";
import type { TabItem } from "integrasoft-shared-ui";
import { useRouter } from "vue-router";

const router = useRouter();

const pestanias: TabItem[] = [
  { label: "Lista de documentos", icon: faList, active: true, to: "/documentos" },
  { label: "Elaboración de documentos", icon: faFileEdit, to: "/documentos/nuevo" },
];

function onTabClick(item: TabItem) {
  if (item.to) router.push(item.to as string);
}
</script>

<template>
  <IsuTabs :items="pestanias" @tab-click="onTabClick" />
</template>
Prop Tipo Default Descripción
items TabItem[] — Pestañas a mostrar: { label: string, icon?: IconDefinition, active?: boolean, to?: unknown }. active: true resalta la pestaña y la vuelve no clickeable. to es un valor libre (string, objeto de ruta, etc.) que se devuelve tal cual en tab-click — el componente no lo interpreta.

Emits: tab-click(item: TabItem, index: number) — solo se emite al hacer clic en una pestaña que no tenga active: true.

Catálogo visual (Storybook)

El repositorio incluye Storybook con ejemplos interactivos de cada componente:

npm run storybook

Abre http://localhost:6006.

Desarrollo local

npm install
npm run dev          # playground local (src/App.vue) para probar componentes mientras se desarrollan
npm run test         # tests unitarios (Vitest)
npm run build        # genera dist/ (ESM + CJS + tipos + CSS)
npm run storybook    # catálogo visual interactivo

Publicar una nueva versión

npm run build
npm version patch   # o minor / major según el cambio
npm publish

Publicar requiere autenticación con npm login o un token en la variable de entorno NPM_TOKEN (ver .npmrc). Si tu cuenta tiene 2FA, necesitas un Granular Access Token con "Bypass 2FA" habilitado, o pasar --otp=123456 a npm publish.

Related Packages

@mtktech/ui

Thư viện UI component dùng chung cho Vue 3 (MButton, MDataTable, MSidebar, ...)

1,240
@banxa-official/banxa-ui

Banxa's Vue 3 design system — accessible, themeable components built on a CSS cu...

926
@nubisco/ui

Vue 3 UI component library

35,080 1
@quidgest/ui

Quidgest's UI framework

22,574
@cocoar/vue-ui

Cocoar Design System — a touch-first Vue 3 component library with 30+ accessible...

17,724 0

Version History

Version Released Vue License
0.3.9 ^3.5.0 MIT
0.3.8 ^3.5.0 MIT
0.3.7 ^3.5.0 MIT
0.3.6 ^3.5.0 MIT
0.3.5 ^3.5.0 MIT