export type DataTableKey = string | number; export type DataTableAlign = 'left' | 'center' | 'right'; export type DataTableLayout = 'auto' | 'fixed'; export type DataTableColumnFixed = boolean | 'left' | 'right'; export type DataTableSortOrder = 'asc' | 'desc' | null; export interface DataTableSortState { key: string; order: DataTableSortOrder; } export type DataTableColumnType = 'text' | 'number' | 'datetime' | 'date' | 'tag' | 'source' | 'actions'; export interface DataTableFilterOption { label: string; value: unknown; tone?: string; /** * Rows behind the option, shown beside its label. Options gathered from the * rows count themselves while filtering runs on the client; pass it for * server-side counts. */ count?: number; } export type DataTableFilterState = Record; /** * How repeated clicks on a sortable header cycle. * * - `tri`: ascending → descending → unsorted. * - `bi`: ascending → descending → ascending; the table is never unsorted. */ export type DataTableSortCycle = 'tri' | 'bi'; /** * What `loading` draws. * * - `overlay`: a blurred veil and a spinner over the visible table, rows or not. * - `skeleton`: placeholder rows shaped like the columns while there are no rows * yet; once rows exist they stay as they are, and a thin bar under the header * says a refresh is running. * - `shimmer`: a band of light sweeps across each row in turn — over placeholder * rows before the first rows land, over the rows themselves during a refresh. */ export type DataTableLoadingVariant = 'overlay' | 'skeleton' | 'shimmer'; /** * Every string the table renders around the data. TuffEx owns no message * catalog, so a host localizes by passing the slice it needs through `labels`. */ export interface DataTableLabels { /** Accessible name of a column's filter button; `{column}` becomes the column title. */ filter: string; /** Option that clears a tag filter. */ filterAll: string; /** Option that clears a date filter. */ filterAnyTime: string; filterToday: string; filterLast3Days: string; filterLast7Days: string; filterLast30Days: string; /** Placeholder of the text filter, and of the option search a long tag filter gets. */ filterSearch: string; filterMin: string; filterMax: string; /** Clears a number range. */ filterReset: string; /** Closes a number range. */ filterApply: string; } export declare const DATA_TABLE_DEFAULT_LABELS: DataTableLabels; /** * Class contribution for a row: anything Vue's `class` binding accepts. */ export type DataTableRowClass = string | string[] | Record; /** * Scope handed to a `header-` slot. */ export interface DataTableHeaderSlotProps { column: DataTableColumn; /** True while this column is the active sort. */ sorted: boolean; /** Active direction, or `null` when this column is not the active sort. */ order: DataTableSortOrder; /** Advances this column through the configured sort cycle. */ toggle: () => void; } /** * Scope handed to a `filter-` slot, which replaces the column's built-in * filter panel. */ export interface DataTableFilterSlotProps { column: DataTableColumn; /** This column's entry in the active filter state. */ filterValue: unknown; setFilter: (value: unknown) => void; clearFilter: () => void; /** Closes the panel. */ close: () => void; } export interface DataTableColumn { key: string; title: string; type?: DataTableColumnType; dataIndex?: string; width?: string | number; minWidth?: string | number; maxWidth?: string | number; auto?: boolean; fixed?: DataTableColumnFixed; nowrap?: boolean; align?: DataTableAlign; sortable?: boolean; sorter?: (a: T, b: T) => number; format?: (value: any, row: T, index: number) => string; headerClass?: string; cellClass?: string; /** * Enables column filtering in the header. * @default false */ filter?: boolean; /** * Custom options for tag/enum filtering. If omitted for a tag column, * options are automatically gathered from distinct row values. */ filterOptions?: DataTableFilterOption[]; } export type DataTableRowKey = keyof T | ((row: T, index: number) => DataTableKey); export interface DataTableProps { columns: DataTableColumn[]; data: T[]; rowKey?: DataTableRowKey; loading?: boolean; /** * How `loading` is drawn. `skeleton` and `shimmer` render placeholder rows only * while there is no row to show, and keep existing rows in place during a * refresh. * @default 'overlay' */ loadingVariant?: DataTableLoadingVariant; /** * Placeholder rows drawn by the `skeleton` and `shimmer` variants before the * first rows land. Match it to the page size so nothing moves when the data * arrives. A table whose height is set by its host adds rows until the * placeholders reach its bottom edge. * @default 5 */ skeletonRows?: number; emptyText?: string; striped?: boolean; bordered?: boolean; hover?: boolean; interactiveRows?: boolean; selectable?: boolean; selectedKeys?: DataTableKey[]; /** * Initial sort for the uncontrolled mode. The component then owns the sort. * * Mutually exclusive with {@link DataTableProps.sort}: pass one or the other, * never both. Passing `sort` puts the component in controlled mode and this * prop is ignored. */ defaultSort?: DataTableSortState; /** * Controlled sort. Supply it (including as `null` for "unsorted") and the * component stops holding its own state — it only reports what the user asked * for through `update:sort`, and renders whatever comes back. * * Leave it out entirely for the uncontrolled mode driven by `defaultSort`. */ sort?: DataTableSortState | null; sortOnClient?: boolean; /** * @default 'tri' */ sortCycle?: DataTableSortCycle; tableLayout?: DataTableLayout; nowrap?: boolean; /** * Caps the table height and turns the component into its own vertical scroll * container. Required for `stickyHeader` / `stickyFooter` to have anything to * stick to unless an ancestor already scrolls. */ maxHeight?: string | number; /** * Lets the table scroll horizontally inside the component. Needed by wide * tables with `fixed` columns, which otherwise rely on an outer scroller. * @default false */ scrollX?: boolean; /** * Pins the header row while the body scrolls. * @default false */ stickyHeader?: boolean; /** * Pins the footer row while the body scrolls. * @default false */ stickyFooter?: boolean; /** * Renders a leading toggle column and lets a row open a detail row of its * own. Off by default: existing tables render exactly as before. * @default false */ expandable?: boolean; /** * Initial expanded rows for the uncontrolled mode; the component owns the * state after that. * * Mutually exclusive with {@link DataTableProps.expandedKeys}: passing that * one puts the component in controlled mode and this prop is ignored. */ defaultExpandedKeys?: DataTableKey[]; /** * Controlled expanded rows. Supply it (including as `[]`) and the component * stops holding its own state — it reports what the reader toggled through * `update:expandedKeys` / `expand` and renders whatever comes back. */ expandedKeys?: DataTableKey[]; /** * Per-row gate for the toggle. Rows it rejects keep an empty leading cell so * the columns stay aligned, and their detail row cannot be opened. */ rowExpandable?: (row: T, index: number) => boolean; /** * Accessible name of a closed toggle. * @default 'Expand row' */ expandLabel?: string; /** * Accessible name of an open toggle. * @default 'Collapse row' */ collapseLabel?: string; /** Extra classes per row, e.g. to tint a row by its state. */ rowClass?: (row: T, index: number) => DataTableRowClass; /** * Tints selected rows. Off by default so existing tables keep rendering * selection through the checkbox alone. * @default false */ highlightSelected?: boolean; /** * Initial filter state for uncontrolled mode. */ defaultFilter?: DataTableFilterState; /** * Controlled filter state. When provided, the component reports changes * through `update:filter` / `filterChange`. */ filter?: DataTableFilterState; /** * Filters rows in memory on the client when `true`. * @default true */ filterOnClient?: boolean; /** * Automatically fixes actions columns (key `actions`/`action` or title containing '操作') * to the right edge unless `fixed` is explicitly configured. * @default true */ autoStickyActions?: boolean; /** * Text for the filter controls. Pass only the strings to replace; the rest * keep their English defaults (see `DATA_TABLE_DEFAULT_LABELS`). */ labels?: Partial; } export interface DataTableEmits { (e: 'update:selectedKeys', value: DataTableKey[]): void; (e: 'selectionChange', value: DataTableKey[]): void; (e: 'update:expandedKeys', value: DataTableKey[]): void; (e: 'expand', payload: { row: T; index: number; expanded: boolean; }): void; (e: 'sortChange', value: DataTableSortState | null): void; (e: 'update:sort', value: DataTableSortState | null): void; (e: 'rowClick', payload: { row: T; index: number; }): void; (e: 'update:filter', value: DataTableFilterState): void; (e: 'filterChange', value: DataTableFilterState): void; }