---
title: "Custom Table"
slug: "custom-table"
category: "Widgets"
reactComponent: "ArcWidgetTable"
reactTier: "premium"
reactSince: "0.1.0-alpha.5"
reactExample: "table.tsx"
locale: "es"
hosts: "react"
---

# Table en React

Listas con columnas ordenables, acciones de fila y componentes incrustados. Adecuado para pedidos, inventario y listas de selección.

Referencia para `@arcrider/arcwidgets-react@0.1.0-alpha.5`. [Instalación](/docs/react/intro/setup-react) · [Todos los componentes](/docs/react/intro/react-components).

## Ejemplo completo

```tsx
import {
  ArcWidgetBadge,
  ArcWidgetTable,
  type ArcWidgetTableRow,
} from '@arcrider/arcwidgets-react';
import { useState } from 'react';

const lang = document.documentElement.lang.startsWith('en')
  ? 'en'
  : document.documentElement.lang.startsWith('es')
    ? 'es'
    : 'de';
const requested = new URLSearchParams(location.search).get('data');
const pack = requested === 'empty' || requested === 'busy' ? requested : 'demo';
const t = (de: string, en: string, es: string) =>
  lang === 'en' ? en : lang === 'es' ? es : de;

const statusTone = {
  open: { color: '#d97706', label: t('Offen', 'Open', 'Abierto') },
  work: { color: '#0f766e', label: t('In Arbeit', 'In progress', 'En curso') },
  done: { color: '#15803d', label: t('Erledigt', 'Done', 'Hecho') },
  wait: { color: '#be123c', label: t('Wartet', 'Waiting', 'En espera') },
};

type Status = keyof typeof statusTone;
type Order = {
  id: string;
  title: string;
  amount: number;
  status: Status;
  items?: Order[];
};

const demoOrders: Order[] = [
  {
    id: 'a',
    title: t('Auftrag Nordhafen', 'North Harbor order', 'Pedido Puerto Norte'),
    amount: 240,
    status: 'work',
    items: [
      {
        id: 'a-1',
        title: t('Vorbereitung', 'Preparation', 'Preparación'),
        amount: 80,
        status: 'done',
      },
      {
        id: 'a-2',
        title: t('Umsetzung', 'Delivery', 'Entrega'),
        amount: 160,
        status: 'work',
      },
    ],
  },
  {
    id: 'b',
    title: t('Auftrag Westpark', 'Westpark order', 'Pedido Westpark'),
    amount: 80,
    status: 'open',
  },
  {
    id: 'c',
    title: t('Auftrag Innenstadt', 'Downtown order', 'Pedido centro'),
    amount: 420,
    status: 'wait',
  },
  {
    id: 'd',
    title: t('Auftrag Werkstatt', 'Workshop order', 'Pedido taller'),
    amount: 150,
    status: 'done',
  },
];

const SITES = [
  'Nordhafen',
  'Westpark',
  'Innenstadt',
  'Werkstatt',
  'Messe',
  'Logistik',
  'Campus',
  'Hafen Süd',
];
const STATUSES: Status[] = ['open', 'work', 'done', 'wait'];

const busyOrders: Order[] = Array.from({ length: 800 }, (_, index) => ({
  id: `busy-${index + 1}`,
  title: `${t('Auftrag', 'Order', 'Pedido')} ${SITES[index % SITES.length]} ${String(index + 1).padStart(3, '0')}`,
  amount: 50 + ((index * 47) % 9950),
  status: STATUSES[index % STATUSES.length],
}));

const orders =
  pack === 'empty' ? [] : pack === 'busy' ? busyOrders : demoOrders;

export default function Example() {
  const [selected, setSelected] = useState(
    t('Keine Auswahl', 'Nothing selected', 'Sin selección'),
  );
  const statusMark = (status: Status) =>
    pack === 'busy' ? (
      <span
        style={{
          display: 'inline-block',
          borderRadius: 6,
          padding: '3px 8px',
          fontSize: 12,
          fontWeight: 600,
          color: statusTone[status].color,
          backgroundColor: '#f4f4f5',
        }}
      >
        {statusTone[status].label}
      </span>
    ) : (
      <ArcWidgetBadge
        data={{
          value: statusTone[status].label,
          singleColor: statusTone[status].color,
          borderRadius: '6px',
          paddingX: '8px',
          paddingY: '3px',
        }}
      />
    );
  const row = (order: Order): ArcWidgetTableRow => ({
    id: order.id,
    clickable: true,
    columns: [
      { value: order.title, width: 'fraction' },
      {
        value: statusMark(order.status),
        width: '130px',
      },
      {
        value: order.amount,
        width: '90px',
        sort: { value: order.amount },
      },
    ],
    items: (order.items ?? []).map(row),
  });
  return (
    <>
      <ArcWidgetTable
        id="orders"
        data={{
          height: pack === 'busy' ? '520px' : '320px',
          styles: {
            borders: {
              outer: true,
              rows: true,
              columns: false,
              color: '#e4e4e7',
            },
          },
          rowHoverAction: { hoverActive: true, backgroundColor: '#fafafa' },
          header: {
            columns: [
              {
                title: t('Auftrag', 'Order', 'Pedido'),
                width: 'fraction',
                sort: { enabled: true, type: 'text' },
              },
              { title: t('Status', 'Status', 'Estado'), width: '130px' },
              {
                title: t('Betrag', 'Amount', 'Importe'),
                width: '90px',
                sort: { enabled: true, type: 'number' },
              },
            ],
          },
          table: orders.map(row),
          emptyTable: {
            title: t('Keine Aufträge', 'No orders', 'Sin pedidos'),
            value: (
              <div style={{ padding: 24, textAlign: 'center' }}>
                <strong>
                  {t('Keine Aufträge', 'No orders', 'Sin pedidos')}
                </strong>
                <p>
                  {t(
                    'Leerer Zustand — über Daten auf Voll oder Demo wechseln.',
                    'Empty state — switch Data to Full or Demo.',
                    'Estado vacío — cambia Datos a Completo o Demo.',
                  )}
                </p>
              </div>
            ),
          },
        }}
        onCellClick={(_event, { row_index }) => {
          setSelected(
            row_index == null
              ? t('Keine Auswahl', 'Nothing selected', 'Sin selección')
              : `${t('Zeile', 'Row', 'Fila')} ${row_index + 1}`,
          );
        }}
      />
      <p aria-live="polite">{selected}</p>
    </>
  );
}
```

## Props del componente

### id

ID opcional y estable de la instancia del widget. Sin especificarlo, React genera un ID.

### data

Configuración de tipo `ArcWidgetTableData`. Pasa los cambios como objetos nuevos; gestiona los datos de negocio y la selección en el estado de React.

### onAction

Fallback opcional `(type: string, ...args: unknown[]) => unknown`. El callback concreto tiene prioridad. Detalles: [Estado y eventos](/docs/react/patterns/react-state-events).

### onCellClick

Evento del navegador y posición de la celda. La capacidad de clic debe estar activada en la fila o la celda.

```typescript
onCellClick?: (event: Event, payload: ArcWidgetTableCellPayload) => void;
```

### onCellHoverEnter

Posición de la celda sobre la que se entra; requiere `hover_enterable`.

```typescript
onCellHoverEnter?: (payload: ArcWidgetTableCellPayload) => void;
```

### onCellHoverLeave

Posición de la celda que se abandona; requiere `hover_leaveable`.

```typescript
onCellHoverLeave?: (payload: ArcWidgetTableCellPayload) => void;
```

## Configuración

Los siguientes campos pertenecen a `data`. Los tamaños se indican como strings, los números y valores de tiempo según su tipo respectivo.

### height

Alto del widget, p. ej. `"320px"`. Los valores en porcentaje necesitan un contenedor padre con altura fija.

Tipo: `string`.

### minHeight

Límite inferior de altura.

Tipo: `string`.

### minWidth

Límite inferior de ancho.

Tipo: `string`.

### maxHeight

Límite superior de altura.

Tipo: `string`.

### groupedBy

Clave de agrupación; omítela para tablas planas.

Tipo: `string`.

### groupsCollapsed

Estado inicial de los grupos.

Tipo: `boolean`.

### collapsible

Expandir o contraer; el formato concreto está en el tipo.

Tipo: `{ enabled?: boolean; expandColumn?: number; indentColumn?: number }`.

### theme

Configuración de tema específica del widget. En React puedes establecer los colores directamente en `styles` y en las distintas áreas.

Tipo: `ArcWidgetTableTheme | ArcNestedWidgetDescriptor | string`.

### rowHoverAction

Representación al pasar el cursor sobre una fila.

Tipo: `ArcWidgetTableHoverAction`.

### header

Encabezados de columna, anchos y opciones de ordenación.

Tipo: `ArcWidgetTableHeader`.

### emptyTable

`title` es el texto de fallback simple. Si `value` está establecido, ese contenido sustituye por completo al fallback. Para mostrar título y descripción juntos, usa un elemento JSX en `value`, como en el ejemplo en vivo.

Representación cuando la lista de filas está vacía.

Tipo: `ArcWidgetTableEmpty`.

### scrollBar

Representación de la barra de desplazamiento.

Tipo: `ArcWidgetTableScrollBar`.

### table

Filas de datos con `columns`; las subfilas opcionales están en `items`.

Tipo: `ArcWidgetTableRow[]`.

### footer

Contenidos y visibilidad del pie de tabla.

Tipo: `ArcWidgetTableFooter`.

### styles

Colores, espaciados y bordes comunes; los campos disponibles son específicos del widget.

Tipo: `ArcWidgetTableStyles | string`.

### borders

Configuración de bordes histórica. Prefiere `styles.borders` si el widget la ofrece.

Tipo: `ArcWidgetTableBorders`.

### borderRadius

Redondeo de las esquinas.

Tipo: `string`.

## Comportamiento y límites

- Cada columna necesita un ancho. Usa al menos una vez `width: "fraction"`; el encabezado y las celdas deben tener el mismo orden de columnas.

- Establece `clickable: true` en la fila o la celda para que se dispare `onCellClick`. `row_index` y `col_index` son índices que admiten nulo; compruébalos antes de usarlos.

- Con contenidos de celda JSX, la ordenación necesita un `sort.value` escalar. Para ordenar por fecha, usa milisegundos en lugar de strings de fecha localizados.

- Las filas anidadas se describen mediante `items`; `collapsible` controla la sangría y el despliegue. Los ejemplos planos pueden mapear `row_index` directamente a la lista de datos.

Los campos `ArcWidgetSlot` admitidos permiten elementos JSX individuales, texto o descriptores de widget. Pasa texto externo como texto JSX; los strings HTML se interpretan como HTML. [Combinar contenidos](/docs/react/patterns/nested-widget-descriptors).

## Tipos de datos

Estos tipos relacionados con el widget se exportan desde el paquete. También están disponibles tipos comunes como `ArcWidgetSlot`, `ArcWidgetStyles` y `ArcReactWidgetProps`. Las firmas de índice permiten extensiones; no garantizan que el widget evalúe campos arbitrarios.

```typescript
export interface ArcWidgetTableSort {
	enabled?: boolean;
	type?: 'text' | 'number' | 'date' | string;
	value?: string | number;
}

export interface ArcWidgetTableHeaderColumn {
	title?: ArcWidgetSlot;
	width?: string;
	sort?: ArcWidgetTableSort;
	styles?: ArcWidgetStyles | string;
	[key: string]: unknown;
}

export interface ArcWidgetTableHeader {
	showHeader?: boolean;
	height?: string;
	fontColor?: string;
	backgroundColor?: string;
	fontSize?: string;
	styles?: ArcWidgetStyles | string;
	columns?: ArcWidgetTableHeaderColumn[];
	sortIcons?: {
		color?: string;
		activeColor?: string;
		hoverColor?: string;
	};
	[key: string]: unknown;
}

export interface ArcWidgetTableCellPayload {
	row_index?: number | null;
	col_index?: number | null;
	flat_index?: number | null;
	group_value?: string | null;
}

export interface ArcWidgetTableColumn {
	title?: ArcWidgetSlot;
	value?: ArcWidgetSlot;
	color?: string;
	backgroundColor?: string;
	width?: string;
	align?: 'left' | 'center' | 'right' | string;
	paddingX?: string;
	paddingY?: string;
	groupExpandAction?: boolean;
	groupValue?: ArcWidgetSlot;
	groupByValue?: ArcWidgetSlot;
	clickable?: boolean;
	hover_enterable?: boolean;
	hover_leaveable?: boolean;
	popup_button?: { content?: ArcWidgetSlot };
	sort?: ArcWidgetTableSort;
	styles?: ArcWidgetStyles | string;
	[key: string]: unknown;
}

export interface ArcWidgetTableRow {
	id?: string | number;
	rowColor?: string;
	rowHeight?: string;
	rowPaddingY?: string;
	groupRowColor?: string;
	groupRowSettings?: { height?: string; [key: string]: unknown };
	columns?: ArcWidgetTableColumn[];
	items?: ArcWidgetTableRow[];
	clickable?: boolean;
	styles?: ArcWidgetStyles | string;
	[key: string]: unknown;
}

export interface ArcWidgetTableEmpty {
	title?: ArcWidgetSlot;
	value?: ArcWidgetSlot;
	backgroundColor?: string;
	[key: string]: unknown;
}

export interface ArcWidgetTableTheme {
	[key: string]: unknown;
}

export interface ArcWidgetTableFooter {
	showFooter?: boolean;
	showActionButton?: boolean;
	backgroundColor?: string;
	actionButtonTitle?: ArcWidgetSlot;
	leftSideContent?: ArcWidgetSlot;
	rightSideContent?: ArcWidgetSlot;
	[key: string]: unknown;
}

export interface ArcWidgetTableScrollBar {
	showScrollBar?: boolean;
	height?: string;
	backgroundColor?: string;
	handle?: {
		height?: string;
		borderRadius?: string;
		backgroundColor?: string;
		[key: string]: unknown;
	};
	[key: string]: unknown;
}

export type ArcWidgetTableBorderLine = boolean | {
	color?: string;
	width?: string;
	borderColor?: string;
	borderWidth?: string;
	borderSize?: string;
	enabled?: boolean;
};

export interface ArcWidgetTableBorders {
	color?: string;
	width?: string;
	outer?: ArcWidgetTableBorderLine;
	columns?: ArcWidgetTableBorderLine;
	rows?: ArcWidgetTableBorderLine;
}

export interface ArcWidgetTableStyles extends ArcWidgetStyles {
	borders?: ArcWidgetTableBorders;
}

export interface ArcWidgetTableHoverAction {
	hoverActive?: boolean;
	backgroundColor?: string;
	fontColor?: string;
	[key: string]: unknown;
}

export interface ArcWidgetTableData extends ArcWidgetBaseData {
	height?: string;
	minHeight?: string;
	minWidth?: string;
	maxHeight?: string;
	groupedBy?: string;
	groupsCollapsed?: boolean;
	collapsible?: { enabled?: boolean; expandColumn?: number; indentColumn?: number };
	theme?: ArcWidgetTableTheme | ArcNestedWidgetDescriptor | string;
	rowHoverAction?: ArcWidgetTableHoverAction;
	header?: ArcWidgetTableHeader;
	emptyTable?: ArcWidgetTableEmpty;
	scrollBar?: ArcWidgetTableScrollBar;
	table?: ArcWidgetTableRow[];
	footer?: ArcWidgetTableFooter;
	styles?: ArcWidgetTableStyles | string;
	borders?: ArcWidgetTableBorders;
	borderRadius?: string;
}

export interface ArcWidgetTableProps extends ArcReactWidgetProps<ArcWidgetTableData> {
	onCellClick?: (event: Event, payload: ArcWidgetTableCellPayload) => void;
	onCellHoverEnter?: (payload: ArcWidgetTableCellPayload) => void;
	onCellHoverLeave?: (payload: ArcWidgetTableCellPayload) => void;
}
```
