carbon-components-svelte
Version:
Svelte implementation of the Carbon Design System
474 lines (418 loc) • 15.9 kB
TypeScript
import { SvelteComponentTyped } from "svelte";
import type { SvelteHTMLElements } from "svelte/elements";
export type DataTableValue = any;
export interface DataTableRow<Id = any> {
id: Id;
[key: string]: DataTableValue;
}
export type DataTableKey<Row = DataTableRow> = (
[keyof import('./data-table-utils.d.ts').KeysWithoutIndexSignature<Row>] extends [never]
? import('./data-table-utils.d.ts').PropertyPath<Row>
: keyof import('./data-table-utils.d.ts').KeysWithoutIndexSignature<Row> extends "id"
? import('./data-table-utils.d.ts').PropertyPath<Row>
: Row extends DataTableRow
? import('./data-table-utils.d.ts').PropertyPathIgnoringIndexSignatures<Row>
: import('./data-table-utils.d.ts').PropertyPath<Row>
);
/**
* Path keys for sort, headers, and cells; mirrors PropertyPath / PropertyPathIgnoringIndexSignatures in ./data-table-utils.d.ts.
*/
export type DataTableSortValue<Row = DataTableRow> = import('./data-table-utils.d.ts').DataTableSortValue<Row>;
export type DataTableEmptyHeader<Row = DataTableRow> = {
key: DataTableKey<Row> | (string & {});
/** Whether the header is empty */
empty: true;
display?: (item: DataTableValue, row: Row) => DataTableValue;
sort?: false | ((a: DataTableSortValue<Row>, b: DataTableSortValue<Row>) => number);
/** Override table-level sortAlways for this column */
sortAlways?: boolean;
/** Whether the column menu is enabled */
columnMenu?: boolean;
width?: string;
minWidth?: string;
};
export type DataTableNonEmptyHeader<Row = DataTableRow> = {
key: DataTableKey<Row>;
empty?: false;
value: DataTableValue;
display?: (item: DataTableValue, row: Row) => DataTableValue;
sort?: false | ((a: DataTableSortValue<Row>, b: DataTableSortValue<Row>) => number);
/** Override table-level sortAlways for this column */
sortAlways?: boolean;
/** Whether the column menu is enabled */
columnMenu?: boolean;
width?: string;
minWidth?: string;
};
export type DataTableHeader<Row = DataTableRow> = DataTableNonEmptyHeader<Row> | DataTableEmptyHeader<Row>;
export type DataTableCell<Row = DataTableRow> = {
key: DataTableKey<Row> | (string & {});
value: DataTableValue;
display?: (item: DataTableValue, row: DataTableRow) => DataTableValue;
};
export interface DataTableRowClassArgs<Row = DataTableRow> {
row: Row, rowIndex: number, selected: boolean, expanded: boolean
}
export type DataTableRowClass<Row = DataTableRow> = string | ((row: DataTableRowClassArgs<Row>) => string | undefined);
export type CarbonDataTableContext<Row extends DataTableRow = DataTableRow> = {
batchSelectedIds: import("svelte/store").Writable<ReadonlyArray<Row["id"]>>;
tableRows: import("svelte/store").Writable<ReadonlyArray<Row>>;
/** Exposes the table size to slotted content (e.g. `Toolbar`)
so it can derive a matching size unless explicitly overridden. */
tableSize: import("svelte/store").Writable<"compact" | "short" | "medium" | "tall" | undefined>;
resetSelectedRowIds: () => void;
filterRows: (searchValue: string, customFilter?: (row: Row, value: string) => boolean) => ReadonlyArray<Row["id"]>;
/** Rebuild cells for a single row after an in-place edit to `rows`. */
refreshRow: (id: Row["id"]) => void;
/** Rebuild cells for every row after batch in-place edits to `rows`. */
refreshCells: () => void;
};
type $RestProps = SvelteHTMLElements["div"];
type $Props<Row extends DataTableRow = DataTableRow> = {
/**
* Specify the data table headers.
* @default []
*/
headers?: ReadonlyArray<DataTableHeader<Row>>;
/**
* Specify the rows the data table should render.
* Keys defined in `headers` are used for the row ids.
* @default []
*/
rows?: ReadonlyArray<Row>;
/**
* Set the size of the data table.
* @default undefined
*/
size?: "compact" | "short" | "medium" | "tall";
/**
* Specify the title of the data table
* @default ""
*/
title?: string;
/**
* Specify the description of the data table
* @default ""
*/
description?: string;
/**
* Specify a custom class name for each row.
* Provide a function to return a class name based
* on row properties, allowing conditional classes
* based on selected/expanded state.
* @example
* ```svelte
* <DataTable rowClass={({ row, rowIndex, selected, expanded }) => {
* return `row-${rowIndex} ${selected ? 'selected' : ''} ${expanded ? 'expanded' : ''}`;
* }} />
* ```
* @default undefined
*/
rowClass?: DataTableRowClass<Row>;
/**
* Specify a name attribute for the input elements
* in a selectable data table (radio or checkbox).
* When the table is inside a form, this name will
* be included in the form data on submit.
* @default `ccs-${Math.random().toString(36)}`
*/
inputName?: string;
/**
* Set to `true` to use zebra styles
* @default false
*/
zebra?: boolean;
/**
* Set to `true` for the sortable variant
* @default false
*/
sortable?: boolean;
/**
* Specify the header key to sort by.
* @default null
*/
sortKey?: DataTableKey<Row>;
/**
* Specify the sort direction.
* @default "none"
*/
sortDirection?: "none" | "ascending" | "descending";
/**
* Set to `true` to only toggle between "ascending" and
* "descending" sort directions, skipping "none".
* @default false
*/
sortAlways?: boolean;
/**
* Specify a default sort comparator for all sortable columns.
* Per-header `sort` functions take precedence over this prop.
*
* With a typed row generic, `a` and `b` are {@link DataTableSortValue} (the union of cell value types over every {@link DataTableKey} on `Row`). Narrow using `context.key` (typed as {@link DataTableKey}) or runtime checks.
* @example
* ```svelte
* <DataTable
* sort={(a, b, { key }) => {
* switch (key) {
* case "expireDate":
* return new Date(a) - new Date(b);
* case "port":
* return a - b;
* default:
* return String(a).localeCompare(String(b));
* }
* }}
* />
* ```
* @default undefined
*/
sort?: (a: DataTableSortValue<Row>, b: DataTableSortValue<Row>, context: {
key: DataTableKey<Row>;
ascending: boolean;
row_a: Row;
row_b: Row
}) => number;
/**
* Set to `true` for the expandable variant.
* Automatically set to `true` if `batchExpansion` is `true`.
* @default false
*/
expandable?: boolean;
/**
* Set to `true` to enable batch expansion.
* @default false
*/
batchExpansion?: boolean;
/**
* Specify the row ids to be expanded.
* @default []
*/
expandedRowIds?: ReadonlyArray<Row["id"]>;
/**
* Specify the ids for rows that should not be expandable.
* @default []
*/
nonExpandableRowIds?: ReadonlyArray<Row["id"]>;
/**
* Set to `true` for the radio selection variant
* @default false
*/
radio?: boolean;
/**
* Set to `true` for the selectable variant.
* Automatically set to `true` if `radio` or `batchSelection` are `true`.
* @default false
*/
selectable?: boolean;
/**
* Set to `true` to enable batch selection
* @default false
*/
batchSelection?: boolean;
/**
* Specify the row ids to be selected.
* @default []
*/
selectedRowIds?: ReadonlyArray<Row["id"]>;
/**
* Specify the ids of rows that should not be selectable.
* @default []
*/
nonSelectableRowIds?: ReadonlyArray<Row["id"]>;
/**
* Specify the row ids to highlight.
* Adds `bx--data-table--highlighted-row`. The highlighted row class is themed by default.
* @default []
*/
highlightedRowIds?: ReadonlyArray<Row["id"]>;
/**
* Set to `true` to enable a sticky header
* @default false
*/
stickyHeader?: boolean;
/**
* Override the maximum height of the sticky header table, replacing the
* default `300px`. Only applies when `stickyHeader` is `true`. Pass a number
* (interpreted as `px`) or a CSS length string (e.g. `"100%"`, `"24rem"`).
* @default undefined
*/
stickyHeaderMaxHeight?: number | string;
/**
* Set to `true` to use static width
* @default false
*/
useStaticWidth?: boolean;
/**
* Set the filtering strategy used by `ToolbarSearch`.
* - `"remove"`: remove non-matching rows from the DOM and recreate them when the
* filter clears.
* - `"hide"`: keep all rows mounted and hide non-matching rows with the `hidden`
* attribute, preserving focus, inputs, and open menus.
*
* `"hide"` falls back to `"remove"` when `pageSize` is set or `virtualize` is enabled.
* @default "remove"
*/
filterMode?: "remove" | "hide";
/**
* Specify the number of items to display in a page
* @default 0
*/
pageSize?: number;
/**
* Set to `number` to set current page
* @default 0
*/
page?: number;
/**
* Enable virtualization for large row lists. Virtualization renders only the rows currently visible in the viewport, improving performance for large datasets.
*
* Virtualization is opt-in. Set `virtualize={true}` to enable with default settings, or pass a configuration object to customize.
* Virtualized tables are intended for use with `stickyHeader={true}` so the header stays visible while scrolling. Pagination is ignored when virtualization is enabled.
* Virtualization assumes a uniform row height; combining it with `expandable` rows is not supported and may cause incorrect scroll-spacer sizing when rows are expanded mid-list.
*
* Provide an object to customize virtualization behavior:
* - `itemHeight` (default: 48 for medium size, adjusted for size variant): The height in pixels of each row. Specify a custom value when using custom slots with multi-line content or different heights.
* - `maxVisibleRows` (default: 10): The maximum number of rows to display in the viewport. The container height will be calculated as `itemHeight * maxVisibleRows`. Overridden by `containerHeight` if explicitly provided.
* - `containerHeight` (default: calculated from maxVisibleRows): The maximum height in pixels of the table body container. If not provided, calculated from `itemHeight * maxVisibleRows`.
* - `overscan` (default: 3): The number of extra rows to render above and below the viewport for smoother scrolling. Higher values may cause more flickering during very fast scrolling.
* - `threshold` (default: 100): The minimum number of rows required before virtualization activates. Tables with fewer rows will render all rows normally without virtualization.
* - `maxItems` (default: undefined): The maximum number of rows to render. When undefined, all visible rows are rendered.
* @default undefined
*/
virtualize?: undefined | boolean | {
itemHeight?: number, maxVisibleRows?: number, containerHeight?: number, overscan?: number, threshold?: number, maxItems?: number
};
/**
* Obtain a reference to the table wrapper element. When virtualization is enabled and `stickyHeader` is false, this element is the scroll container—use `bind:scrollContainerRef` to programmatically control scroll position (e.g. `scrollContainerRef.scrollTop = 0`).
* @default null
*/
scrollContainerRef?: null | HTMLDivElement;
/**
* Override the default table header translation ids.
* @default undefined
*/
tableHeaderTranslateWithId?: (id: import("./TableHeader.svelte").TableHeaderTranslationId) => string;
cell?: (this: void, ...args: [{
row: Row;
cell: DataTableCell<Row>;
rowIndex: number;
cellIndex: number;
rowSelected: boolean;
rowExpanded: boolean;
}]) => void;
cellHeader?: (this: void, ...args: [{ header: DataTableNonEmptyHeader }]) => void;
descriptionChildren?: (this: void, ...args: [{ props: { class: "bx--data-table-header__description" } }]) => void;
expandIcon?: (this: void, ...args: [{
expanded: boolean;
row: Row | undefined;
props: {
"aria-hidden": "true" | "false";
class: string;
};
}]) => void;
expandedRow?: (this: void, ...args: [{
row: Row;
rowSelected: boolean;
}]) => void;
titleChildren?: (this: void, ...args: [{ props: { class: "bx--data-table-header__title" } }]) => void;
children?: (this: void) => void;
[key: `data-${string}`]: unknown;
};
export type DataTableProps<Row extends DataTableRow = DataTableRow> = Omit<$RestProps, keyof $Props<Row>> & $Props<Row>;
export default class DataTable<Row extends DataTableRow = DataTableRow> extends SvelteComponentTyped<
DataTableProps<Row>,
{
click: CustomEvent<{
header?: DataTableHeader<Row>;
row?: Row;
cell?: DataTableCell<Row>;
}>;
"click:cell": CustomEvent<{
cell: DataTableCell<Row>;
target: EventTarget;
currentTarget: EventTarget;
rowClass?: DataTableRowClass<Row>;
}>;
"click:header": CustomEvent<{
header: DataTableHeader<Row>;
/** The intended next sort direction for this click, reported regardless of whether the `sort` event was cancelled. */
sortDirection?: "ascending" | "descending" | "none";
target: EventTarget;
currentTarget: EventTarget;
}>;
"click:header--expand": CustomEvent<{
expanded: boolean;
}>;
"click:header--select": CustomEvent<{
indeterminate: boolean;
selected: boolean;
}>;
"click:row": CustomEvent<{
row: Row;
target: EventTarget;
currentTarget: EventTarget;
}>;
"click:row--expand": CustomEvent<{
expanded: boolean;
row: Row;
}>;
"click:row--select": CustomEvent<{
selected: boolean;
row: Row;
}>;
"mouseenter:row": CustomEvent<Row>;
"mouseleave:row": CustomEvent<Row>;
/** Dispatched when a sortable column header would change the active sort. The event is cancelable: call `preventDefault()` to skip updating `sortKey` / `sortDirection` and skip client side sorting for that click (for example full server side sorting while still reading `detail.key` / `detail.direction` for your API). If not cancelled, the table applies the new sort and sorts the current `rows` client side. Typical uses: server side sorting, URL or query string sync, analytics, and persisting sort preferences. */
sort: CustomEvent<{
key: null;
direction: "none"
} | {
key: DataTableKey<Row>;
direction: "ascending" | "descending"
}>;
},
{
default: Record<string, never>;
cell: {
row: Row;
cell: DataTableCell<Row>;
rowIndex: number;
cellIndex: number;
rowSelected: boolean;
rowExpanded: boolean;
};
cellHeader: { header: DataTableNonEmptyHeader };
descriptionChildren: { props: { class: "bx--data-table-header__description" } };
expandIcon: {
expanded: boolean;
row: Row | undefined;
props: {
"aria-hidden": "true" | "false";
class: string;
};
};
expandedRow: {
row: Row;
rowSelected: boolean;
};
titleChildren: { props: { class: "bx--data-table-header__title" } };
}
> {
/**
* Rebuild cells for a single row after an in-place edit to `rows`.
* @example
* ```svelte
* <DataTable bind:this={dataTable} {headers} {rows} />
* <NumberInput min={0} bind:value={row.qty} on:input={() => dataTable.refreshRow(row.id)} />
* ```
*/
refreshRow: (id: Row["id"]) => void;
/**
* Rebuild cells for every row after batch in-place edits to `rows`.
* @example
* ```svelte
* <DataTable bind:this={dataTable} {headers} {rows} />
* <button on:click={() => dataTable.refreshCells()}>Refresh table</button>
* ```
*/
refreshCells: () => void;
}