@oceanbase-odc/ob-react-data-grid
Version:
Excel-like grid component built with React, with editors, keyboard navigation, copy & paste, and the like
396 lines • 14.6 kB
TypeScript
import type { ReactElement } from 'react';
import React from 'react';
import { ConnectDropTarget, ConnectDragSource, ConnectDragPreview } from 'react-dnd';
export interface ISelectorEvent {
rowIdx: number;
columnIdx: number;
isShiftClick?: boolean;
isCtrlClick?: boolean;
isUpdateIndex?: boolean;
enableEditor?: boolean;
mode?: RangeMode;
}
export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
export interface Column<TRow = any, TSummaryRow = unknown> {
/** The name of the column. By default it will be displayed in the header cell */
name: string | ReactElement;
/** A unique key to distinguish each column */
key: string;
/** Column width. If not specified, it will be determined automatically based on grid width and specified widths of other columns */
width?: number | string;
/** Minimum column width in px. */
minWidth?: number;
/** Maximum column width in px. */
maxWidth?: number;
dataType?: 'number' | 'string';
cellClass?: string | ((row: TRow, isRowCellSelected?: boolean) => string | undefined);
headerCellClass?: string;
summaryCellClass?: string | ((row: TSummaryRow) => string);
/** Formatter to be used to render the cell content */
formatter?: React.ComponentType<FormatterProps<TRow, TSummaryRow>>;
/** Formatter to be used to render the summary cell content */
summaryFormatter?: React.ComponentType<SummaryFormatterProps<TSummaryRow, TRow>>;
/** Formatter to be used to render the group cell content */
groupFormatter?: React.ComponentType<GroupFormatterProps<TRow, TSummaryRow>>;
/** Enables cell editing. If set and no editor property specified, then a textinput will be used as the cell editor */
editable?: boolean | ((row: TRow) => boolean);
/** Determines whether column is frozen or not */
frozen?: boolean;
/** Enable resizing of a column */
resizable?: boolean;
/** Enable sorting of a column */
sortable?: boolean;
/** Enable filter of a column */
filterable?: boolean;
clickable?: boolean;
/** Sets the column sort order to be descending instead of ascending the first time the column is sorted */
sortDescendingFirst?: boolean;
/** Editor to be rendered when cell of column is being edited. If set, then the column is automatically set to be editable */
editor?: React.ComponentType<EditorProps<TRow, TSummaryRow>>;
editorOptions?: {
/** @default false */
createPortal?: boolean;
/** @default false */
editOnClick?: boolean;
/** Prevent default to cancel editing */
onCellKeyDown?: (event: React.KeyboardEvent<HTMLDivElement>) => void;
/** Control the default cell navigation behavior while the editor is open */
onNavigation?: (event: React.KeyboardEvent<HTMLDivElement>) => boolean;
};
/** Header renderer for each header cell */
headerRenderer?: React.ComponentType<HeaderRendererProps<TRow, TSummaryRow>>;
/** Component to be used to filter the data of the column */
filterRenderer?: React.ComponentType<FilterRendererProps<TRow, any, TSummaryRow>>;
}
export interface CalculatedColumn<TRow, TSummaryRow = unknown> extends Column<TRow, TSummaryRow> {
idx: number;
resizable: boolean;
sortable: boolean;
frozen: boolean;
isLastFrozenColumn: boolean;
rowGroup: boolean;
formatter: React.ComponentType<FormatterProps<TRow, TSummaryRow>>;
}
export interface ColumnMetric {
width: number;
left: number;
}
export interface Position {
rowIdx: number;
columnIdx: number;
}
export interface IRange {
rowIdx: number;
columnIdx: number;
endRowIdx: number;
endColumnIdx: number;
}
export declare enum RangeMode {
SELECT = "SELECT",
EDIT = "EDIT"
}
export interface FormatterProps<TRow = any, TSummaryRow = any> {
rowIdx: number;
column: CalculatedColumn<TRow, TSummaryRow>;
row: TRow;
isCellSelected: boolean;
isRowSelected: boolean;
onRowSelectionChange: (checked: boolean, isShiftClick: boolean, isCtrlClick: boolean) => void;
onRowChange: (row: Readonly<TRow>) => void;
onRowReorder?: (sourceRowIndex: number, targetRowIndex: number) => void;
enableRowRecord?: boolean;
}
export interface SummaryFormatterProps<TSummaryRow, TRow = any> {
column: CalculatedColumn<TRow, TSummaryRow>;
row: TSummaryRow;
}
export interface GroupFormatterProps<TRow, TSummaryRow = unknown> {
groupKey: unknown;
column: CalculatedColumn<TRow, TSummaryRow>;
childRows: readonly TRow[];
isExpanded: boolean;
isCellSelected: boolean;
isRowSelected: boolean;
onRowSelectionChange: (checked: boolean) => void;
toggleGroup: () => void;
}
export interface SharedEditorProps<TRow> {
row?: Readonly<TRow>;
rowHeight: number;
editorPortalTarget: Element;
onRowChange: (row: TRow, commitChanges?: boolean) => void;
onClose: (commitChanges?: boolean) => void;
}
export interface EditorProps<TRow, TSummaryRow = unknown> extends SharedEditorProps<TRow> {
rowIdx: number;
column: Readonly<CalculatedColumn<TRow, TSummaryRow>>;
top: number;
left: number;
width: number | undefined;
}
export interface HeaderRendererProps<TRow, TSummaryRow = unknown> {
column: CalculatedColumn<TRow, TSummaryRow>;
sortInfo: SortInfo;
onSort?: (columnKey: string, direction: SortDirection) => void;
allRowsSelected: boolean;
}
interface SelectedCellPropsBase {
idx: number;
onKeyDown: (event: React.KeyboardEvent<HTMLDivElement>) => void;
}
export interface EditCellProps<TRow> extends SelectedCellPropsBase {
mode: 'EDIT';
editorProps: SharedEditorProps<TRow>;
}
export interface SelectedCellProps extends SelectedCellPropsBase {
mode: 'SELECT';
onFocus: () => void;
dragHandleProps?: Pick<React.HTMLAttributes<HTMLDivElement>, 'onMouseDown' | 'onDoubleClick'>;
}
export interface ISelectRange extends IRange {
mode: RangeMode;
key?: string | null;
}
export interface ContextMenuRenderProps<R> {
row: R;
rowKeyName: string;
rowIdx: number;
columnKey: string;
selectedRange: ISelectRange;
contextMenuVisible: boolean;
setContextMenuVisible: React.Dispatch<React.SetStateAction<boolean>>;
config?: ContextMenuConfig<R>[];
isRowSelected: boolean;
isColumnSelected: boolean;
}
export type ContextMenuRender<R = any> = React.ComponentType<ContextMenuRenderProps<R>>;
export interface ContextMenuConfig<TRow> {
key: string;
text: React.ReactNode;
isShowRowSelected?: boolean;
disabled?: boolean;
children?: ContextMenuConfig<TRow>[];
onClick?: (row: TRow) => void;
}
export interface getContextMenuConfig<R> {
(row: R, column: CalculatedColumn<R, R>): ContextMenuConfig<R>[];
}
export interface CellRendererProps<TRow, TSummaryRow = unknown> extends Omit<React.HTMLAttributes<HTMLDivElement>, 'style' | 'children'> {
rowIdx: number;
column: CalculatedColumn<TRow, TSummaryRow>;
columnIdx: number;
row: TRow;
isRowCellSelected: boolean;
lazyStore: Map<string, any>;
isModified: boolean;
setMouseDown: (isDown: boolean) => void;
isDraggedOver: boolean;
isCellSelected: boolean;
isCellFirstSelected: boolean;
isRowSelected: boolean;
/**
* 所处的 column 是否被选中
*/
isColumnSelected: boolean;
drop: [
{
isOver: boolean;
canDrop: boolean;
},
ConnectDropTarget
];
drag: [
{
isDragging: boolean;
},
ConnectDragSource,
ConnectDragPreview
];
width: number | undefined;
dragHandleProps?: Pick<React.HTMLAttributes<HTMLDivElement>, 'onMouseDown' | 'onDoubleClick'>;
onRowChange: (newRow: TRow) => void;
onRowReorder?: (sourceRowIndex: number, targetRowIndex: number) => void;
onRowClick?: (rowIdx: number, row: TRow, column: CalculatedColumn<TRow, TSummaryRow>) => void;
onGridSelect: (data: ISelectorEvent) => void;
enableRowRecord?: boolean;
selectedRange: ISelectRange;
}
export interface RowRendererProps<TRow, TSummaryRow = unknown> extends Omit<React.HTMLAttributes<HTMLDivElement>, 'style' | 'children'> {
viewportColumns: readonly CalculatedColumn<TRow, TSummaryRow>[];
row: TRow;
enableRowRecord?: boolean;
getModifiedColumns: (row: TRow) => string[];
isAdd: boolean;
isDeleted: boolean;
cellRenderer?: React.ComponentType<CellRendererProps<TRow, TSummaryRow>>;
rowIdx: number;
draggedOverCellIdx?: number;
lastFrozenColumnIndex: number;
isRowSelected: boolean;
isMainSelectedRow: boolean;
selectedColumns: Set<string>;
totalColumnWidth: number;
isLastRow: boolean;
isShowRowMenu?: boolean;
top: number;
selectedCellProps?: EditCellProps<TRow> | SelectedCellProps;
lazyStore: Map<string, any>;
selectedRange: ISelectRange;
setMouseDown: (isDown: boolean) => void;
onRowChange: (row: TRow) => void;
onRowReorder?: (sourceRowIndex: number, targetRowIndex: number) => void;
onRowClick?: (rowIdx: number, row: TRow, column: CalculatedColumn<TRow, TSummaryRow>) => void;
rowClass?: (row: TRow) => string | undefined;
setDraggedOverRowIdx?: (overRowIdx: number) => void;
onGridSelect: (data: ISelectorEvent) => void;
columnMetrics: Map<CalculatedColumn<TRow, TSummaryRow>, ColumnMetric>;
}
export interface FilterRendererProps<TRow, TFilterValue = unknown, TSummaryRow = unknown> {
column: CalculatedColumn<TRow, TSummaryRow>;
value: TFilterValue;
onChange: (value: TFilterValue) => void;
}
export type Filters = Record<string, Set<any> | undefined>;
export type FilterOptions = Record<string, Set<any>> | null;
export interface RowsChangeData<R = any, SR = any> {
modifiedRows?: R[];
newRows?: R[];
deletedRows?: R[];
indexes?: number[];
column?: CalculatedColumn<R, SR>;
}
export interface RowChangeType<R = any> {
_created?: boolean;
_deleted?: boolean;
modified?: boolean;
_originRow?: R;
}
export interface SelectRowEvent {
rowIdx: number;
isShiftClick: boolean;
isCtrlClick: boolean;
checked?: boolean;
}
export interface FillEvent<TRow> {
columnKey: string;
sourceRow: TRow;
targetRows: TRow[];
}
export interface PasteEvent<TRow> {
sourceColumnKey: string;
sourceRow: TRow;
targetColumnKey: string;
targetRow: TRow;
}
export type CellNavigationMode = 'NONE' | 'CHANGE_ROW' | 'LOOP_OVER_ROW';
export type SortDirection = 'ASC' | 'DESC' | 'NONE';
export type SortInfo = {
columnKey: string;
direction: SortDirection;
};
export type ColSpanArgs<R, SR> = {
type: 'HEADER' | 'FILTER';
} | {
type: 'ROW';
row: R;
} | {
type: 'SUMMARY';
row: SR;
};
type DefaultColumnOptions<R, SR> = Pick<Column<R, SR>, 'formatter' | 'minWidth' | 'resizable' | 'sortable'>;
type SharedDivProps = Pick<React.HTMLAttributes<HTMLDivElement>, 'className' | 'style'>;
export interface DataGridProps<R = any, SR = any> extends SharedDivProps, RowsChangeData<R> {
/**
* Grid and data Props
*/
/** An array of objects representing each column on the grid */
initialColumns: Column<R, SR>[];
/** A function called for each rendered row that should return a plain key/value pair object */
initialRows: R[];
/** The getter should return a unique key for each row */
rowKeyName?: string;
onRowsChange?: (rows: R[]) => void;
onColumnChange?: (columns: Column<R, SR>[]) => void;
onSelectChange?: (selectedRowsKey: React.Key[], cellColumnsKey?: React.Key[]) => void;
/**
* Feature props
*/
defaultColumnOptions?: DefaultColumnOptions<R, SR>;
onFill?: (event: FillEvent<R>) => R[];
onPaste?: (event: PasteEvent<R>) => R;
onSort?: (columnKey: string, direction: SortDirection) => void;
/**
* Custom renderers
*/
rowRenderer?: React.ComponentType<RowRendererProps<R, SR>>;
emptyRowsRenderer?: React.ComponentType;
/**
* Event props
*/
onCopy?: (ref: DataGridRef<R>) => void;
contextMenuRender?: ContextMenuRender<R>;
getContextMenuConfig?: getContextMenuConfig<R>;
options?: {
theme?: 'white' | 'dark';
readonly?: boolean;
/** The height of each row in pixels */
rowHeight?: number;
/** The height of the header row in pixels */
headerRowHeight?: number;
/** The height of the header filter row in pixels */
headerFiltersHeight?: number;
/** The height of each summary row in pixels */
summaryRowHeight?: number;
/** 行移动 */
enableRowRecord?: boolean;
enableColumnRecord?: boolean;
/** Toggles whether filters row is displayed or not */
enableFilterRow?: boolean;
/** Toggles whether sorters row is displayed or not */
enableSortRow?: boolean;
enableFrozenRow?: boolean;
enableSelect?: boolean;
enableVirtualization?: boolean;
enableRowTools?: boolean;
enableAddRow?: boolean;
enableFlushDelete?: boolean;
searchKey?: string;
aria?: React.AriaAttributes;
cellNavigationMode?: CellNavigationMode;
};
/**
* Miscellaneous
*/
/** The node where the editor portal should mount. */
editorPortalTarget?: Element;
rowClass?: (row: R) => string | undefined;
/** format 粘贴数据,再应用到单元格 */
pasteFormatter?: (row: R, column: Column<R, SR>, value: any) => any;
}
export interface DataGridRef<R = any> {
element: HTMLDivElement | null;
columns: Column<R, any>[];
rows: R[];
selectedColumns: Set<string>;
selectedRows: Set<string>;
selectedRange: ISelectRange;
enableFrozenRow: boolean;
rowKeyName: string;
scrollToColumn: (colIdx: number) => void;
scrollToRow: (rowIdx: number, force?: boolean) => void;
scrollToNextPage: () => void;
scrollToPrevPage: () => void;
addRows: (rows: R[]) => void;
deleteRows: () => void;
rowKeyGetter: (row: {
[key: string]: any;
}) => string;
onRowReorder: (sourceRowIndex: number, targetRowIndex: number) => void;
setRows: (rows: R[]) => void;
selectCell: (position: Position, isShiftClick?: boolean, enableEditor?: boolean) => void;
setColumns: (columns: Column<R, any>[]) => void;
setCellsByRowIndex: (rowIdx: number, cells: Record<string, any>) => void;
getCellsByRowIndex: (rowIdx: number) => Record<string, any>;
}
export {};
//# sourceMappingURL=types.d.ts.map