Tabla y layout
Encabezados fijos, densidad, reordenar y redimensionar columnas, tamaño y truncado por columna, indicadores de scroll y la barra de paginación.
Defaults de tabla y layout
Estos comportamientos pasaron de opt-in a default, porque cada uno se estaba reimplementando en todas las apps que consumen listkit. La columna de versión es el release que lo cambió:
| Comportamiento | Default | Cómo salir | Desde |
|---|---|---|---|
| Las columnas recortan el texto que desborda | on | truncate: false por columna (wrap/grow lo implican) | 4.0 |
| Layout de paginación | 'sticky' | paginationVariant='fixed' | 'inline' | 4.0 |
| Selector de filas por página | [20, 50, 100, 200] | pageSizeOptions: false | 4.0 |
| Scroll al tope al cambiar de página | on | scrollToTopOnPageChange: false | 4.0 |
Ancho mínimo por columna sin width | 140px | table.minColumnWidth (0 desactiva el piso) | 4.1 |
El piso de columna. Un layout 'fixed' reparte el contenedor en partes
iguales entre las columnas que no declaran width, así que la cantidad de
columnas sola decide cuánto le toca a cada una: doce columnas en un shell de
1280px reciben 106px — insuficiente para una fecha, ya no digamos para un par
de botones de acción. La tabla por eso lleva un min-width de
columnas sin width x minColumnWidth (más los width declarados, sumados con
calc porque son longitudes CSS), y scrollea en horizontal cuando el
contenedor baja de ahí en vez de seguir apretando.
Es un min-width, no un breakpoint medido: el browser lo reevalúa en cada
resize sin re-render, sin lectura de layout y con la misma respuesta en SSR.
Combínalo con sticky: 'right' en la columna de acciones para que los botones
se queden quietos mientras lo demás scrollea — esa combinación es la que hace
usable una tabla ancha.
Una celda fijada pinta con bg-inherit, así que el fondo de la fila tiene que
ser opaco. Los estados propios de listkit lo son; si rowClassName devuelve
algo como bg-amber-50/60, el contenido que scrollea se transparenta a través
de la columna fijada.
El fijado arranca en md. Un checkbox más una columna de acciones son
~150px, un quinto de un teléfono, y la razón por la que vale la pena gastarlos
en una pantalla ancha es exactamente la razón por la que no en una angosta. El
checkbox de selección se fija solo cuando selection está activo — una
selección que no ves es una selección que pierdes de vista a media tabla.
Acciones de borde. overlay: true renderiza una columna como el espejo del
checkbox de selección en el otro extremo: siempre visible, padding px-2
delgado, un divisor border-l nítido en vez de una etiqueta de header, y
fijada a la derecha desde md:
{ key: 'actions', header: 'Acciones', width: '7rem', overlay: true,
render: (item, i) => <RowActions item={item} index={i} variant='inline' actions={…} /> }overlay también implica exportable: false — la columna contiene botones y su
key no nombra ningún valor de la fila, así que de otro modo escribiría una
columna vacía y marcada en cada CSV. Pasa exportable: true si de verdad la
quieres.
Dale un width del tamaño de sus botones más el padding — 28px por botón de
icono, 4px por gap, 16px de padding, así que tres botones ≈ '7rem'. No se
revela en hover a propósito: las acciones que nadie ve son acciones que nadie
usa, y una pantalla táctil nunca hace hover.
UX de tabla: encabezado fijo, densidad, reordenar, redimensionar
Vienen activadas por defecto. Cualquier config con table obtiene el gestor de columnas, el toggle de densidad, reordenar arrastrando el encabezado, redimensionar desde el borde y el menú de opciones — sin banderas — y cada elección se persiste en localStorage. Escribe false para quitar alguna:
table: {
columns,
// Todo lo de abajo es opcional; el valor por defecto ya es `true`.
columnControl: false, // fija las columnas (sin panel de ocultar/mostrar)
reorderable: false, // sin reordenar arrastrando el encabezado
resizable: false, // sin redimensionar desde el borde
density: false, // sin toggle cómoda/compacta
optionsMenu: false, // quita por completo el menú de opciones
defaultDensity: 'comfortable',
stickyHeader: true, // el encabezado permanece visible mientras la tabla hace scroll
maxBodyHeight: '70vh', // altura del área de scroll del encabezado fijo (por defecto '70vh')
}stickyHeaderle da a la tabla un área de scroll acotada (limitada pormaxBodyHeight, por defecto'70vh') para que el encabezado quede fijo arriba y la barra de paginación abajo — ambos visibles mientras haces scroll. El scroll horizontal queda contenido en la misma caja, así una tabla ancha nunca se desborda fuera de la página en pantallas pequeñas. Solo en vista de tabla.density+defaultDensityexponen el toggle cómoda ↔ compacta (sobrescribe elcompactestático).reorderable/resizableagregan reordenar arrastrando encabezados y redimensionar por el borde; los anchos redimensionados persisten por columna.
Tamaño de columnas y truncado
Por defecto la tabla usa layout: 'auto' — las columnas se ajustan a su contenido, así una celda larga ensancha su columna y empuja a las demás. Para mantener las columnas estables y recortar el desborde, activa truncate por columna:
table: {
columns: [
// Elipsis de una línea. Cambia la tabla a layout: 'fixed' para que el recorte
// siga el ancho real de la columna — ensánchala/redimensiónala y se ve más texto.
{ key: 'name', header: 'Nombre', truncate: true, width: '14rem' },
// Recorta a N líneas.
{ key: 'notes', header: 'Notas', truncate: 2 },
// Vuelve a permitir el salto de línea en una columna.
{ key: 'address', header: 'Dirección', wrap: true },
// Acota el rango del redimensionado.
{ key: 'sku', header: 'SKU', minWidth: 96, maxWidth: 240 },
],
}truncate: truerecorta a una línea con elipsis;truncate: Nrecorta a N líneas. Es dinámico — el recorte sigue el ancho visible de la columna, así que ensancharla o redimensionarla revela más texto en vivo (sin config extra por columna). Para celdas de texto plano se agrega automáticamente un tooltiptitlecon el texto completo; para unrendercon JSX, pasatooltip: item => '…'para mostrar el valor completo al hover.truncatepone la tabla enlayout: 'fixed'para que el recorte quede atado al ancho real de la columna — esta es la solución cuando el texto sigue cortado aunque ensanches o redimensiones la columna (en layoutautogana el contenido). Para unrenderpersonalizado con líneas apiladas (p. ej. un nombre sobre un id), manténtruncateen tus propios elementos internos y pontable.layout: 'fixed'directamente — no envuelvas anchos fijos comomax-w-[180px]dentro de la celda, porque ignoran el redimensionado.grow: truemarca la columna prioritaria: absorbe el espacio sobrante y nunca se trunca, así el valor más importante siempre se ve completo mientras las vecinas recortan.- Auto-ajuste: con
resizable, doble clic en el handle de redimensionado de una columna la ajusta a su celda visible más ancha (acotado pormaxWidth). No necesitas adivinar un ancho fijo. widthes una pista en layoutautoy autoritativo enfixed; es solo el tamaño inicial y nunca bloquea el redimensionado.minWidth/maxWidth(px) son topes opcionales para la celda y el handle (piso por defecto 48px, sin techo) — ojo:maxWidthtambién limita hasta dónde arrastra el handle, así que omítelo para resize sin tope.
El toolbar se mantiene limpio. Densidad, columnas y exportar no agregan un botón cada uno —
<ListView>los pliega en un único menú de opciones (⚙), dejando inline solo lo esencial (toggle de vista, conteo de resultados). Es responsivo (disponible también en móvil) y en vista de tarjetas muestra solo exportar. Los componentesDensityToggle,ColumnManager,ExportButtonyTableOptionsMenuse exportan por si construyes tu propio toolbar.
Indicadores de scroll
Todo contenedor con scroll acotado en listkit — el sidebar de filtros, el menú
de opciones, el gestor de columnas, las opciones de un select, el diálogo de
export y el scroll horizontal de la tabla — difumina sus bordes recortados,
para que el contenido más allá del corte se anuncie en vez de leerse como el
final de la lista. ScrollArea y useScrollFade se exportan para tus paneles.
Todo fade se disuelve contra la superficie por defecto (el tono 'surface',
blanco en modo claro). Donde quieras que el borde oscurezca en su lugar,
ScrollArea acepta fadeTone='shadow' | 'shadow-strong'.
Donde la tabla tiene columnas pinned, el fade no desaparece: se recorre hacia
adentro hasta la costura entre el stack pinned y el contenido que scrollea,
así la señal queda visible en cualquier device (un fade dejado en el borde del
contenedor pintaría debajo de las celdas pinned opacas). Además, los fades de
la tabla empiezan debajo del header: lavan datos que scrollean, nunca los
títulos de columna. La celda pinned más
externa agrega un divisor hairline en esa misma frontera. ScrollArea expone
las piezas para tus propios scrollers: fadeLeft / fadeRight apagan un lado,
fadeInsetLeft / fadeInsetRight (una longitud CSS) recorren un fade desde el
borde del contenedor a partir de md, y el wrapper es un group/scroll con
data-scroll-left / data-scroll-right para que los descendientes estilen
según el scroll.
Los diálogos toman un height fijo, así su contenido scrollea en lugar de que
el diálogo cambie de tamaño bajo el cursor mientras el usuario filtra.
Desplazar la barra de paginación
La barra de paginación usa position: fixed. Pasa paginationClassName para despejar elementos de la app como una sidebar (mergeado vía tailwind-merge, así que un left-* sobrescribe el left-0 por defecto):
<ListView config={config} adapter={adapter} paginationClassName='lg:left-64' />Para una sidebar cuyo ancho cambia (colapsable), manéjalo con una variable CSS que la sidebar establezca y una clase que la lea, ej. left-[var(--sidebar-w)].
Exportación
Exportación CSV lista para usar, y un contrato de exportación configurable para alcance, campos y orden de columnas.
Acciones y selección
Menús y barras rápidas de acciones por fila, selección de filas, acciones masivas y seleccionar todos los resultados que coinciden con la consulta actual.