vue-table-editor

Tabla genérica reutilizable y autocontenida para Vue 3: redimensionable, reordenable, preferencias de columnas, lazy loading server-side, edición inline, CSV export, paginación e infinite scroll. Sin dependencias de UI (CSS propio, iconos SVG embebidos).
915
Install
npm install vue-table-editor
Latest Version:1.2.0
Vue:3
License:MIT
Last Updated:Oct 23, 2026
Links: GitHub  ·  npm
Maintainer: 2012lucho

vue-table-editor

Tabla genérica reutilizable y autocontenida para Vue 3. Lista de funcionalidades:

  • Toolbar (refresh, CSV, CRUD, botones personalizados)
  • Selección de columnas visibles (dropdown)
  • Búsqueda global y filtro por columna
  • Ordenamiento client-side y server-side
  • Paginación client-side y server-side
  • Scroll infinito (IntersectionObserver)
  • Selección single / multiple + selección todo
  • Columnas redimensionables (pointer events) y reordenables (drag & drop)
  • Preferencias de columnas persistentes (orden, ancho, visibilidad)
  • Edición inline con debounce y validación
  • Grupos de columnas, valueFormatters, badges de color, formateo fecha/boolean/json
  • Exportar a CSV
  • CRUD (api.create / api.edit / api.delete) y acciones por fila

Sin dependencias de UI: no usa Bootstrap ni Bootstrap Icons. Incluye su propio CSS autocontenido (te-*) y un set de iconos SVG embebido. Solo requiere vue como peer dependency.


Instalación

npm install vue-table-editor

vue es peer dependency. La app consumidora debe tenerlo instalado.

Uso

1. Importar y registrar el componente

import { TableEditor } from 'vue-table-editor'
import 'vue-table-editor/style.css'

export default {
  components: { TableEditor },
}

Con <script setup>:

import { TableEditor } from 'vue-table-editor'
import 'vue-table-editor/style.css'

2. Template

<TableEditor
  ref="table"
  id="productos"
  :api="apiProductos"
  :config="tableConfig"
  @rowSelected="onRowSelected"
  @rowDoubleClick="onRowDblClick"
/>

3. API (server-side / lazy)

import { BtnConfig } from 'vue-table-editor'

export default {
  data() {
    return {
      selectedRow: null,
      apiProductos: {
        list: (params) => api.get('/productos/list', { params }).then(r => r.data),
        create: (data) => api.post('/productos', data).then(r => r.data),
        edit: (data) => api.put(`/productos/${data.id}`, data).then(r => r.data),
        delete: (data) => api.delete(`/productos/${data.id}`).then(r => r.data),
      },
    }
  },
  computed: {
    tableConfig() {
      return {
        lazy: true,
        selectionMode: 'single',
        elementName: { singular: 'Producto', gender: 'M' },
        buttons: {
          toolbar: [
            { key: 'create', icon: 'plus', severity: 'success', label: 'Nuevo',
              onClick: () => this.abrirModal() },
            { key: 'edit', icon: 'pencil', severity: 'warning', label: 'Editar',
              isDisabled: () => !this.selectedRow, onClick: () => this.abrirModal(this.selectedRow) },
            { key: 'delete', icon: 'trash', severity: 'danger', label: 'Eliminar',
              isDisabled: () => !this.selectedRow, onClick: () => this.eliminar(this.selectedRow) },
          ],
          rowActions: [
            new BtnConfig({
              key: 'ver', icon: 'eye', severity: 'info', label: 'Ver',
              onClick: (row) => alert('Detalle: ' + row.id),
            }),
          ],
        },
      }
    },
  },
}

api.list debe devolver:

{ status: true, data: { rows, totalRecords, fields_def } }

donde fields_def define las columnas: { field, headerName, type, sortable, form_type, css }.

Cliente-side (sin backend)

<TableEditor :data="{ rows, fields_def }" :config="{ selectionMode: 'multiple' }" />

Iconos

Los botones aceptan un icon por nombre. El componente incluye un set SVG embebido:

search, download, plus, pencil, trash, columns, eye, refresh.

También acepta nombres legacy de Bootstrap Icons (bi bi-plus-lg, bi bi-pencil, etc.) y los mapea automáticamente a los SVG incluidos.

Severity de botones

severity usa variantes propias (te-btn-*):

Severity Estilo
primary Relleno azul
secondary Relleno gris
success Relleno verde
info Relleno cian
warning Relleno ámbar
danger Relleno rojo
outline-primary / outline-secondary / outline-info / outline-success / outline-warning / outline-danger Contorno

También se aceptan valores legacy con prefijo btn- (p. ej. btn-primary) y se normalizan.


Props

Prop Tipo Default Descripción
api Object null { list, create, edit, delete }
permisos Object {} { ver, crear, editar, eliminar }
config Object {} Configuración (ver abajo)
data Object null { rows, fields_def } para modo cliente-side
id String null Clave de persistencia de preferencias

Config (config)

Campo Tipo Default Descripción
lazy Boolean false Carga server-side vía api.list
selectionMode String 'single' 'single', 'multiple' o null
infiniteScroll Boolean false Scroll infinito (por defecto true si lazy)
elementName Object — { singular, gender } para etiquetas
columnGroups Array — [{ headerName, fields }]
inlineEditing Object — { campos: { [field]: cfg }, api, debounce_ms, onSave }
valueFormatters Object — { [field]: (row) => html }
showFilterRow Boolean false Fila de filtros por columna
scrollHeight String null Altura de scroll
pageSize Number 25 Filas por página
pageSizeOptions Array [25,50,100,200] Opciones del selector
hideToolbar / hideRefresh / hideCsvExport Boolean false Oculta elementos
showPaginator Boolean true Muestra paginador
defaultColumnProps Object — Props por defecto a todas las columnas
columnOrder Array — Orden inicial de columnas
preferencesStore Object — Adaptador de preferencias (si no se pasa, usa localStorage)
buttons Object — { toolbar: BtnConfig[], rowActions: BtnConfig[] }

Opciones de alineación (v1.1)

Todas son opcionales y aditivas; sin ellas el comportamiento es idéntico al de la 1.0.

Campo Tipo Default Descripción
rowActionsMode String 'column' 'column' (columna "Acciones" por fila) o 'toolbar' (botones de fila en la toolbar, habilitados al seleccionar)
lazyProtocol String 'standard' 'standard' envía {page,pageSize,search} con filtros planos; 'legacy' envía {page,rows,globalSearch} y filtros {value,matchMode:'contains'}. La respuesta se normaliza aceptando tanto status como stat.
dataPipeline Object — { processRows(rows,fields_def), processFields(fields_def,firstRow), processColumns(cols) }. Usa greenbornDataPipeline(styling) para replicar el pipeline JSON/formatters/estilos del frontend.
styling Object — { rowClass, rowClassFn, rowStyleFn, fieldClasses, fieldStyleFns } para estilos dinámicos por fila/celda.
extraFields Object — { create, edit, list }: columnas adicionales para cada acción.
permissionsCheck Function — (permiso) => boolean. Si se define, los botones CRUD built-in se ocultan cuando la prop permisos.<accion> no pasa el chequeo.
crud.openModal Function — (payload) => void. Reemplaza los stubs de CRUD: recibe {action,title,campos,modelo,onSubmit,guardado,selectedRows}. Si no se define, se emiten createRequest/editRequest.
crud.confirmDelete Function — (ids) => Promise<boolean>|boolean. Si se define, ejecuta api.delete tras confirmar; si no, emite deleteRequest.
editOnDoubleClick Boolean false Al hacer doble click en una fila, abre la edición (requiere api.edit y permiso).
csvFetchAll Boolean false En modo lazy, exportar a CSV pidiendo todas las filas al servidor antes de descargar.

Actualización en vivo (v1.2)

Para datos que se reemplazan periódicamente (polling, sockets), al reasignar la prop data con forma { rows, fields_def }:

  • Si la firma de fields_def no cambió, no se reconstruyen las columnas: se preserva el estado de sesión (orden/ancho/ocultas) y no se re-leen preferencias persistidas.
  • config.trackBy estabiliza las row keys: nombre de campo (ej. 'pid') o función (row, index) => key. Sin trackBy se usa row.id, row.__uid o el índice.
const config = {
  trackBy: 'pid',
}

Pipeline legacy (paridad con el frontend):

import { greenbornDataPipeline } from 'vue-table-editor'

const config = {
  lazy: true,
  lazyProtocol: 'legacy',
  dataPipeline: greenbornDataPipeline(),   // opcional: pasar el objeto styling
  permissionsCheck: (p) => usuario?.permisos?.includes(p),
}

Inne edit: se añadieron type: 'currency', cfg.format(val) y rollback automático al valor original si el guardado falla (respuesta status === false o stat === false). El editor inline ahora restaura el valor al cancelar (Esc).

Eventos

Evento Payload Descripción
loaded Boolean Datos cargados
rowSelected Object | null | Array Fila(s) seleccionada(s)
rowDoubleClick Object Fila con doble click
createRequest Object (v1.1) Solicitud de alta cuando no hay crud.openModal
editRequest Object (v1.1) Solicitud de edición cuando no hay crud.openModal
deleteRequest { ids, action } (v1.1) Solicitud de borrado cuando no hay crud.confirmDelete

Dark mode

La librería incluye estilos para modo oscuro activados por el atributo [data-bs-theme="dark"] (mismo convenio que Bootstrap).

Métodos (vía ref)

Método Descripción
loadData(data?) Recarga datos
applyConfig() Re-aplica configuración
refresh() Recarga y resetea selección

Preferencias de columnas

Las preferencias (orden, ancho, visibilidad) se guardan automáticamente con debounce. Por defecto se usan localStorage (autónomo, sin servidor). Puedes inyectar tu propio adaptador — por ejemplo un store Pinia del host — vía config.preferencesStore, siempre que exponga:

{
  misValores: object,
  valor(key): any,
  guardarValores(data): Promise|void,
  fetchMisPreferencias(): Promise|void,
}

También puedes registrar un adaptador global:

import { setGlobalPreferencesAdapter } from 'vue-table-editor'
setGlobalPreferencesAdapter(miAdaptador)

Y crear adaptadores de localStorage con clave propia:

import { createLocalStoragePrefsAdapter } from 'vue-table-editor'
const adapter = createLocalStoragePrefsAdapter('mi_clave')

Demo local

cd vue-table-editor
npm install
npm run dev

Incluye dos ejemplos: server-side (lazy, con API simulada) y cliente-side con edición inline.

Build de la librería

npm run build

Genera dist/vue-table-editor.js, dist/vue-table-editor.umd.cjs y dist/vue-table-editor.css.

Licencia

MIT

Related Packages

vue2-datatable-component

The best Datatable for Vue.js 2.x which never sucks

37,016 870
@unicorns/data-table

Vue component for rendering data-tables

12,383 3
vue2-datatable-component-uikit

The best Datatable for Vue.js 2.x which never sucks... with ui-kit

4,771 870
@laraveldaily/vue2-datatable

Datatable for Vue.js 2.x

3,285 6
vue-datatable-component

Datatable encapsulation for Vue 1.x

646 23

Version History

Version Released Vue License
1.2.0 ^3.3.0 MIT
1.1.1 ^3.3.0 MIT
1.1.0 ^3.3.0 MIT
1.0.1 ^3.3.0 MIT
1.0.0 ^3.3.0 MIT