npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vue-table-editor

v1.1.1

Published

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).

Downloads

354

Readme

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. |

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