import { ReactNode } from 'react'; import { IconType, ButtonColors, VisualSlotType, AnyIconDefinition, IconFontColor } from './types'; import { InnerButtonProps } from './button'; import { ButtonTooltipConfig } from './buttonConfig'; import { DateFormat } from '../../modules/Table/components/atoms/DateCell.types'; import { TextColor } from './text'; export type CellType = 'default' | 'primary' | 'status' | 'actions' | 'link' | 'avatars' | 'textfield' | 'dropdown' | 'button' | 'progressbar' | 'skeleton' | 'tags' | 'checkbox' | 'icon' | 'stars' | 'number' | 'numeric' | 'date' | 'boolean' | 'select' | 'gantt' /** * Miniatura 16:9, contenedor `rounded-control-xs` (img sin radio), borde brand al hover; máx. * altura fila **menos 16 px**; * `Popup` con la misma imagen (tamaño/proporción vía `previewAspectInPopup`, ver `ImagePreviewCellValue`) sin * lightbox. */ | 'imagePreview'; export type CellState = 'default' | 'hover' | 'selected' | 'disabled' | 'focused'; /** * Configuración de una acción individual en la tabla * * @example * ```tsx * const actions: TableActionConfig[] = [ * { * icon: 'Edit', * label: 'Editar', * tooltip: 'Editar usuario', * onClick: (row) => console.log('Editar', row) * }, * { * icon: 'Trash', * color: 'destructive', * tooltip: 'Eliminar usuario', * onClick: (row) => console.log('Eliminar', row), * disabled: false * } * ] * ``` */ export interface TableActionConfig { /** Nombre del icono. Usa nombres sin sufijo 'Outlined' (ej: 'Edit', 'Trash', 'Eye', 'Share', 'Download') o iconos Font Awesome */ icon: IconType | AnyIconDefinition; /** * Texto visible en el botón junto al icono. * Si se omite, el botón es solo icono (compacto); el `tooltip` sigue siendo el accesible al hover. */ label?: string; /** * Con `label`, posición del icono respecto al texto. * @default 'left' */ iconPosition?: 'left' | 'right'; /** Texto del tooltip que aparece al pasar el mouse */ tooltip: string; /** Función que se ejecuta al hacer clic en la acción */ onClick: (row: TRow) => void; /** Si la acción está deshabilitada (opcional) */ disabled?: boolean; /** * Variante de color del botón (`InnerButton`). * Aplica tanto a acciones solo icono como a las que llevan `label`. * @default 'tertiary' */ color?: ButtonColors; /** * Tono del icono (glifo) respecto al color del botón. * Útil con `label` cuando quieres alinear el icono a tokens concretos. */ colorIcon?: IconFontColor; } export type SortDirection = 'asc' | 'desc' | null; /** * Tipo para el valor de PrimaryCell. * Puede ser un string (nombre) o un objeto con avatar, name y subtitle. */ export interface PrimaryCellValue { avatar?: string; name: string | number; subtitle?: string; } /** * Valor de celda con `type: 'imagePreview'`: URL de imagen y metadatos opcionales. * * @example * ```tsx * { id: 'thumb', type: 'imagePreview', accessor: (row) => ({ src: row.coverUrl, title: row.name }) } * ``` */ export interface ImagePreviewCellValue { /** URL de la imagen (miniatura en la tabla y vista ampliada en el popup). */ src: string; /** Pie de foto opcional bajo la imagen en el panel. */ title?: string; /** `alt` de las imágenes; por defecto se usa `title` o cadena vacía. */ alt?: string; /** * Proporción del recuadro de imagen **en el panel Popup** (hover). No altera la miniatura 16:9 * de la grilla. * @default '1:1' (ancho 300 con `aspect-square`, equivalente a 300×300) */ previewAspectInPopup?: '1:1' | '16:9' | '4:3'; } /** * Tipo para el valor de LinkCell. * Puede ser un string (label) o un objeto con href y label. */ export interface LinkCellValue { href: string; label: string; } /** * Valor de celda con `type: 'button'`: objeto pasado a {@link InnerButton} dentro de la tabla. * * También puede usarse un `string` o `number` como atajo (solo etiqueta). * * @example * ```tsx * { * label: 'Descargar', * leftSlot: 'DownloadOutlined', * onClick: (row) => exportRow(row), * } * ``` */ export interface TableButtonCellValue { label?: string | number; theme?: InnerButtonProps['theme']; color?: InnerButtonProps['color']; /** Variante visual del `Button` (`button`, `text`, `outlined`). */ variant?: 'button' | 'text' | 'outlined'; onClick?: (row: unknown) => void; disabled?: boolean; tooltip?: string | ButtonTooltipConfig; /** Icono principal (p. ej. botón solo icono si omites `label`). */ icon?: VisualSlotType; leftSlot?: VisualSlotType; rightSlot?: VisualSlotType; /** Tono del glifo; equivale a `colorIcon` del `Button`. */ colorIcon?: IconFontColor; } export interface CellProps { type?: CellType; state?: CellState; value?: unknown; onChange?: (value: unknown) => void; render?: (value: unknown, row: TRow) => ReactNode; className?: string; onClick?: (e: React.MouseEvent) => void; } /** * Tipo para configuraciones especiales de celdas (modo legacy). * Objetos con type y value que el CellRenderer interpreta. */ export interface CellConfigValue { type: string; value: unknown; onChange?: (value: unknown) => void; options?: Array<{ value: string | number; label: string; }>; } /** * Tipo condicional para el retorno de la función render en ColumnConfig. * Cuando type es 'primary', puede retornar PrimaryCellValue. * Cuando type es 'link', puede retornar LinkCellValue. * También puede retornar CellConfigValue para configuraciones especiales. */ export type ColumnRenderReturn = TType extends 'primary' ? ReactNode | TableActionConfig[] | PrimaryCellValue | CellConfigValue : TType extends 'link' ? ReactNode | TableActionConfig[] | LinkCellValue | CellConfigValue : TType extends 'gantt' ? ReactNode | TableActionConfig[] | GhanttRowValue | CellConfigValue : ReactNode | TableActionConfig[] | CellConfigValue; export interface ColumnConfig { id: string; header: string | ReactNode; /** Icono opcional que se muestra al lado izquierdo del nombre del header */ headerIcon?: IconType | AnyIconDefinition; accessor?: keyof T | ((row: T) => unknown); cell?: CellProps; width?: number | string; /** Ancho original de la columna (definido al montar). Se usa en resize para poder volver al ancho inicial. */ originalWidth?: number; minWidth?: number; /** Máximo ancho en px, o 'auto' para que la columna use el ancho disponible de la tabla */ maxWidth?: number | 'auto'; sortable?: boolean; filterable?: boolean; /** * Opciones para el filtro avanzado tipo lista (`avatars`, `tags`, `checkbox`). * Si la columna es de texto (p. ej. sin `type` o `primary`/`status`) y defines al menos una opción, * el popup de filtros la trata como lista usando estas opciones. */ filterOptions?: Array<{ value: string | number; label: string; }>; /** * Si es `false`, esa columna no muestra el control de fijar/bloquear. * Si es `true`, la columna es bloqueable aunque la tabla tenga `columnsLockable={false}`. * Si se omite, aplica el valor por defecto de la tabla (`TableProps.columnsLockable`, por defecto bloqueable). */ lockable?: boolean; locked?: boolean; visible?: boolean; resizable?: boolean; type?: CellType; editable?: 'none' | 'textfield' | 'dropdown'; columnOptions?: Array<{ value: string | number; label: string; }>; /** Si true, en modo edición se muestra un icono de carga (p. ej. opciones cargando por API) en lugar del dropdown */ columnOptionsLoading?: boolean; tagLabelAccessor?: string; tagColorAccessor?: string; randomColors?: boolean; dateFormat?: DateFormat; locale?: string; dateFallback?: string; render?: (value: unknown, row: T, column: ColumnConfig) => ColumnRenderReturn['type']>; actions?: TableActionConfig[]; contentAlign?: 'left' | 'right' | 'center'; /** * Color del texto de la celda (tipos `default` / sin tipo). * Usa los mismos tokens que el componente `Text` (`color`). */ textColor?: TextColor; className?: string; headerClassName?: string; cellClassName?: string; } export type { TextColor }; /** Todas las variantes de color para StatusCell (alineadas con Badge) */ export type StatusCellType = 'brand' | 'success' | 'warning' | 'danger' | 'info' | 'gray'; /** Valor de celda de estado: string o objeto con text y type */ export type StatusCellValue = string | { text: string; type: StatusCellType; }; /** Paleta de relleno para barras Gantt (tokens de relleno del DS). */ export type GhanttBarColorToken = 'brand' | 'success' | 'warning' | 'danger' | 'info' | 'gray'; /** * Segmento de barra en la columna Gantt (por fila). * `start` / `end` pueden ser `Date` o string parseable por `Date`. */ export interface GhanttBarSegment { id: string; label?: string; start: Date | string; end: Date | string; /** Color de la barra; por defecto `brand`. */ colorToken?: GhanttBarColorToken; /** Progreso 0–100 opcional (barra interna). */ progress?: number; } /** * Valor de celda para `type: 'gantt'`: conjunto de barras y dependencias opcionales. * Las dependencias se reservan para evoluciones (render de flechas); el MVP dibuja solo barras. */ export interface GhanttRowValue { bars: GhanttBarSegment[]; /** Identificadores de barra `from` → `to` (opcional). */ dependencies?: Array<{ from: string; to: string; }>; } /** Filas del showcase "Todas las celdas": tipado para RowConfig en stories */ export interface CeldaShowcaseRow { id: number; tipo: string; descripcion: string; ejemplo: unknown; estado: StatusCellValue; } export type RowConfig = T & { id: string | number; selected?: boolean; disabled?: boolean; expanded?: boolean; isParent?: boolean; children?: RowConfig[]; className?: string; onClick?: (row: RowConfig) => void; _isEditing?: boolean; _toggleEdit?: () => void; }; export interface FilterConfig { columnId: string; operator: 'equals' | 'notEquals' | 'contains' | 'notContains' | 'startsWith' | 'endsWith' | 'gt' | 'gte' | 'lt' | 'lte' | 'between' | 'isEmpty' | 'isNotEmpty' | 'in' | 'afterDate' | 'beforeDate' | 'thisYear' | 'lastYear' | 'thisMonth' | 'lastMonth'; value: string | number | boolean | Date | (string | number)[] | null; value2?: string | number | boolean | Date | null; } export interface SortConfig { columnId: string; direction: SortDirection; } export interface SelectionAction { /** Etiqueta del botón (opcional si hay icono) */ label?: string; /** Icono del botón */ icon?: IconType | AnyIconDefinition; /** Handler al hacer click */ onClick: (selectedIds: (string | number)[]) => void; /** Color del botón */ color?: ButtonColors; /** Deshabilitado */ disabled?: boolean; } export interface SelectionConfig { enabled: boolean; multiple?: boolean; selectedIds?: (string | number)[]; onSelectionChange?: (selectedIds: (string | number)[]) => void; /** Mostrar barra de opciones flotante cuando hay selección */ showSelectionBar?: boolean; /** * Ubicación de la barra de selección respecto al cuerpo de la tabla. * - `inline` (por defecto): en flujo normal, justo debajo del área de filas y encima de la paginación. * - `overlay`: flotante, superpuesta en la parte inferior del contenedor. */ selectionBarPlacement?: 'overlay' | 'inline'; /** Acciones disponibles en la barra de selección */ actions?: SelectionAction[]; /** Texto personalizado para el contador (por defecto: "Seleccionados") */ selectionText?: string; /** Mostrar botón para limpiar selección */ showClearSelection?: boolean; } export interface PaginationConfig { enabled: boolean; currentPage?: number; pageSize?: number; total?: number; pageSizes?: number[]; onPageChange?: (page: number) => void; onPageSizeChange?: (pageSize: number) => void; } /** Contexto pasado a los callbacks del toolbar con las filas seleccionadas (aunque la acción no requiera selección). */ export interface ToolbarSelectionContext { selectedIds: (string | number)[]; selectedRows: RowConfig[]; } /** Configuración del botón principal del toolbar. Sustituye a showCustomAction + customActionLabel + customActionIcon + onCustomAction. */ export interface TableToolbarPrimaryActionConfig { label: ReactNode; leftSlot?: VisualSlotType; /** Recibe el contexto de selección (ids y filas seleccionadas) aunque requiresSelection sea false. */ onClick: (context: ToolbarSelectionContext) => void; /** Si true, el botón se deshabilita cuando no hay filas seleccionadas. Por defecto true. */ requiresSelection?: boolean; theme?: InnerButtonProps['theme']; color?: InnerButtonProps['color']; size?: InnerButtonProps['size']; } /** Configuración del botón secundario del toolbar. Sustituye a showSecondCustomAction + secondCustomActionLabel + secondCustomActionIcon + onSecondCustomAction. */ export interface TableToolbarSecondaryActionConfig { label: ReactNode; leftSlot?: VisualSlotType; /** Recibe el contexto de selección (ids y filas seleccionadas) aunque requiresSelection sea false. */ onClick: (context: ToolbarSelectionContext) => void; /** Si true, el botón se deshabilita cuando no hay filas seleccionadas. Por defecto true. */ requiresSelection?: boolean; theme?: InnerButtonProps['theme']; color?: InnerButtonProps['color']; size?: InnerButtonProps['size']; } /** Botón adicional del toolbar; tipado con props compatibles con InnerButton. Se renderizan antes del secondary. onClick recibe el contexto de selección. */ export type TableToolbarCustomActionButton = Pick & { label: ReactNode; /** Recibe el contexto de selección (ids y filas seleccionadas). */ onClick: (context: ToolbarSelectionContext) => void; }; export interface TableToolbarConfig { showSearch?: boolean; searchPlaceholder?: string; showExport?: boolean; showColumnConfig?: boolean; /** @deprecated Usa `primaryCustomAction` en su lugar */ showCustomAction?: boolean; /** @deprecated Usa `primaryCustomAction.label` en su lugar */ customActionLabel?: string; /** @deprecated Usa `primaryCustomAction.leftSlot` en su lugar */ customActionIcon?: VisualSlotType; /** @deprecated Usa `secondaryCustomAction` en su lugar */ showSecondCustomAction?: boolean; /** @deprecated Usa `secondaryCustomAction.label` en su lugar */ secondCustomActionLabel?: string; /** @deprecated Usa `secondaryCustomAction.leftSlot` en su lugar */ secondCustomActionIcon?: VisualSlotType; showFilters?: boolean; internalFilters?: boolean; internalSearch?: boolean; onSearch?: (query: string) => void; onFilter?: () => void; onExport?: (format: 'csv' | 'pdf' | 'xlsx') => void; /** @deprecated Usa `primaryCustomAction.onClick` en su lugar. Si se define, se llama con el mismo contexto de selección (selectedIds, selectedRows) para compatibilidad. */ onCustomAction?: (context?: ToolbarSelectionContext) => void; /** @deprecated Usa `secondaryCustomAction.onClick` en su lugar. Si se define, se llama con el mismo contexto de selección para compatibilidad. */ onSecondCustomAction?: (context?: ToolbarSelectionContext) => void; /** Botón principal (estilo primary). Si se define, tiene prioridad sobre las props legacy de customAction. */ primaryCustomAction?: TableToolbarPrimaryActionConfig | null; /** Botón secundario (estilo secondary). Si se define, tiene prioridad sobre las props legacy de secondCustomAction. */ secondaryCustomAction?: TableToolbarSecondaryActionConfig | null; /** Botones adicionales; se renderizan siempre antes del secondary. Cada item se tipa con props de botón. */ customActions?: TableToolbarCustomActionButton[]; } /** * Filas cuyo campo `excludeKey` coincide con algún valor de `rows` no podrán seleccionarse * (checkbox en `selection` deshabilitado). */ export interface ExcludedRowsConfig { /** Clave del objeto fila (campo) a comparar */ excludeKey: keyof T & string; /** Valores de ese campo para los que el checkbox queda deshabilitado */ rows: (string | number)[]; } export interface TableProps { columns: ColumnConfig[]; rows: RowConfig[]; loading?: boolean; empty?: EmptyState; selection?: SelectionConfig; /** * Excluye filas de la selección por valor de un campo (ej. estado `archived`). * Solo aplica si `selection.enabled` es true. */ excludedRows?: ExcludedRowsConfig; pagination?: PaginationConfig; toolbar?: TableToolbarConfig; sort?: SortConfig; filters?: FilterConfig[]; expandable?: boolean; resizable?: boolean; /** * Permite fijar/bloquear columnas desde la cabecera por defecto. * Si es `false`, ninguna columna es bloqueable salvo que definas `lockable: true` en esa `ColumnConfig`. * Si se omite, el comportamiento es el mismo que `true`. */ columnsLockable?: boolean; striped?: boolean; bordered?: boolean; /** * Muestra el borde exterior del contenedor de la tabla (borde, sombra y border-radius). * @default false */ borderable?: boolean; hoverable?: boolean; dense?: boolean; stickyHeader?: boolean; stickyCheckbox?: boolean; stickyActions?: boolean; showEditActions?: boolean; /** * Clave única para guardar la configuración de la tabla en localStorage. * Si se proporciona, la configuración se persistirá automáticamente. * @example configKey="users-table" */ configKey?: string; /** * Habilitar persistencia de configuración en localStorage. * Requiere que se proporcione `configKey`. * @default false */ persistConfig?: boolean; /** * Persistir también filtros activos y búsqueda en la misma configKey. * Solo aplica si `persistConfig` es true. Si es false, no se guardan ni cargan filtros ni búsqueda. * @default true */ queryPersist?: boolean; /** * Callback cuando cambia la configuración de la tabla. * Se llama con la configuración completa y el tipo de cambio. */ onConfigChange?: (config: TableConfigState, changeType: TableConfigChangeType) => void; className?: string; onSort?: (sort: SortConfig | undefined) => void; onFilter?: (filters: FilterConfig[]) => void; onColumnResize?: (columnId: string, width: number) => void; onColumnReorder?: (columns: ColumnConfig[]) => void; onColumnVisibilityChange?: (columnId: string, visible: boolean) => void; onRowClick?: (row: RowConfig) => void; onRowExpand?: (row: RowConfig) => void; onHandleChange?: (rowId: string | number, columnId: string, value: unknown, row?: T) => void; /** * IDs de filas que deben estar en modo edición. * Permite controlar desde fuera qué filas están editando. * Útil para activar múltiples filas en modo edición simultáneamente. * @example editingRowIds={[1, 2, 3]} */ editingRowIds?: (string | number)[]; /** * Callback cuando cambian los IDs de filas en modo edición. * Se llama cuando el usuario hace clic en el botón de editar/guardar de una fila. * Permite sincronizar el estado externo con las acciones internas de la tabla. * @example onEditingRowIdsChange={(ids) => setEditingRowIds(ids)} */ onEditingRowIdsChange?: (editingRowIds: (string | number)[]) => void; /** * Callback cuando se habilita o se cierra el modo de edición de una fila. * Se ejecuta tanto cuando se habilita como cuando se cierra el modo de edición. * @param rowId - ID de la fila que cambió su estado de edición * @param isEditing - true si se está habilitando el modo de edición, false si se está cerrando * @example onEditModeChange={(rowId, isEditing) => console.log(`Fila ${rowId} ${isEditing ? 'editando' : 'cerrada'}`)} */ onEditModeChange?: (rowId: string | number, isEditing: boolean) => void; /** * Callback cuando se cierra una o múltiples filas que fueron editadas. * Se ejecuta un solo evento al cerrar, mostrando un array con los IDs de todas las filas editadas que se están cerrando. * Solo se ejecuta si las filas fueron realmente editadas (se modificó alguna celda). * @param editedRowIds - Array con los IDs de las filas que fueron editadas y se están cerrando * @example onSuccessComplete={(editedRowIds) => console.log('Filas editadas cerradas:', editedRowIds)} */ onSuccessComplete?: (editedRowIds: (string | number)[]) => void; } export interface EmptyState { type?: 'empty' | 'no-results' | 'no-filters'; icon?: IconType; /** Título del estado vacío. Si no se proporciona, se usa el texto por defecto según el tipo. */ title?: string; /** Descripción del estado vacío. Si no se proporciona, se usa el texto por defecto según el tipo. */ description?: string; action?: { label: string; onClick: () => void; }; } export interface HeaderCellProps { column: ColumnConfig; sortable?: boolean; filterable?: boolean; lockable?: boolean; resizable?: boolean; sortDirection?: SortDirection; filterActive?: boolean; locked?: boolean; /** Es la última columna bloqueada (para mostrar borde separador) */ isLastLocked?: boolean; /** Posición left para columnas sticky (en píxeles) */ stickyLeft?: number; /** Posición right para columnas de acciones sticky (en píxeles) */ stickyRight?: number; /** Si es una columna de acciones */ isActionsColumn?: boolean; /** Hacer el header sticky al hacer scroll vertical */ stickyHeader?: boolean; onSort?: () => void; onFilter?: () => void; onLock?: () => void; /** defaultWidthAtStart = ancho al inicio del resize; se persiste como mínimo para poder volver al ancho inicial */ onResize?: (width: number, defaultWidthAtStart?: number) => void; onResizeDoubleClick?: () => void; onResizeHover?: (isHovering: boolean) => void; /** Clave que cambia cuando cambian las filas mostradas (p. ej. paginación). Si cambia, se vuelven a calcular los anchos auto. */ rowsDataKey?: string; className?: string; /** Si hay una columna con maxWidth: 'auto'; con table-layout:fixed las demás tienen ancho explícito para que el resize funcione. */ hasColumnMaxWidthAuto?: boolean; } export interface TableCellComponentProps { value: unknown; /** * Valor devuelto por el accessor, antes de `column.render`. * Obligatorio para que `render` reciba el dato crudo: `value` puede ser ya el nodo renderizado en la fila. */ rawValue?: unknown; row: TRow; column: ColumnConfig; state?: CellState; onChange?: (value: unknown) => void; className?: string; onCellFocus?: () => void; /** Posición left para columnas sticky (en píxeles) */ stickyLeft?: number; /** Posición right para columnas de acciones sticky (en píxeles) */ stickyRight?: number; /** Es la última columna bloqueada (para mostrar borde separador) */ isLastLocked?: boolean; /** Si es una columna de acciones */ isActionsColumn?: boolean; /** Si la fila completa está en modo edición (para aplicar bordes brand) */ isRowEditing?: boolean; /** Si hay una columna con maxWidth: 'auto'; columnas sin width reciben ancho por defecto para que el resize funcione. */ hasColumnMaxWidthAuto?: boolean; } /** * Estado de configuración de una columna individual * Se usa para guardar/restaurar la configuración de la tabla */ export interface TableColumnState { /** ID de la columna */ id: string; /** Posición/orden de la columna (0-indexed) */ order: number; /** Si la columna es visible */ visible: boolean; /** Si la columna está bloqueada (sticky) */ locked: boolean; /** Ancho de la columna (si fue redimensionada) */ width?: number | string; /** Ancho inicial/por defecto: definido en columna o el que tenía en auto al primer resize. Permite volver al ancho inicial al reducir. */ defaultWidth?: number; } /** * Estado completo de configuración de la tabla * Se puede serializar a JSON para guardar en localStorage, API, etc. */ export interface TableConfigState { /** Versión del esquema de configuración (para migraciones futuras) */ version: number; /** Timestamp de la última modificación */ updatedAt: string; /** Estado de las columnas */ columns: TableColumnState[]; /** Configuración de ordenamiento actual */ sort?: SortConfig; /** Filtros aplicados */ filters?: FilterConfig[]; /** Texto de búsqueda del toolbar (persistido cuando queryPersist es true) */ searchQuery?: string; } /** * Tipo de cambio que disparó onConfigChange */ export type TableConfigChangeType = 'column-reorder' | 'column-visibility' | 'column-lock' | 'column-resize' | 'sort' | 'filter' | 'search'; //# sourceMappingURL=table.d.ts.map