mirror of https://github.com/buster-so/buster.git
pass generics onto the components themselves
This commit is contained in:
parent
38e2bbafba
commit
21b3c22e5c
|
@ -29,9 +29,6 @@ function BusterInfiniteListComponent<T = any>({
|
|||
rowClassName = '',
|
||||
scrollEndThreshold = 48 // Default threshold of 200px
|
||||
}: BusterInfiniteListProps<T>) {
|
||||
const Header = BusterListHeader<T>();
|
||||
const RowSelector = BusterListRowComponentSelector<T>();
|
||||
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const scrollRef = useRef<HTMLDivElement | null>(null);
|
||||
const lastChildIndex = useMemo(() => {
|
||||
|
@ -139,7 +136,7 @@ function BusterInfiniteListComponent<T = any>({
|
|||
return (
|
||||
<div ref={containerRef} className="infinite-list-container relative">
|
||||
{showHeader && !showEmptyState && (
|
||||
<Header
|
||||
<BusterListHeader<T>
|
||||
columns={columns}
|
||||
onGlobalSelectChange={onSelectChange ? onGlobalSelectChange : undefined}
|
||||
globalCheckStatus={globalCheckStatus}
|
||||
|
@ -153,7 +150,7 @@ function BusterInfiniteListComponent<T = any>({
|
|||
rows
|
||||
.filter((row) => !row.hidden)
|
||||
.map((row, index) => (
|
||||
<RowSelector
|
||||
<BusterListRowComponentSelector<T>
|
||||
key={row.id}
|
||||
row={row}
|
||||
id={row.id}
|
||||
|
|
|
@ -5,70 +5,70 @@ import { CheckboxColumn } from './CheckboxColumn';
|
|||
import { HEIGHT_OF_HEADER } from './config';
|
||||
import type { BusterListColumn } from './interfaces';
|
||||
|
||||
export const BusterListHeader =
|
||||
<T = unknown,>(): React.FC<{
|
||||
columns: BusterListColumn<T>[];
|
||||
onGlobalSelectChange?: (v: boolean) => void;
|
||||
globalCheckStatus?: 'checked' | 'unchecked' | 'indeterminate';
|
||||
showSelectAll?: boolean;
|
||||
rowsLength: number;
|
||||
rowClassName: string;
|
||||
}> =>
|
||||
({
|
||||
columns,
|
||||
rowClassName,
|
||||
showSelectAll = true,
|
||||
onGlobalSelectChange,
|
||||
globalCheckStatus,
|
||||
rowsLength
|
||||
}) => {
|
||||
const showCheckboxColumn = !!onGlobalSelectChange;
|
||||
const showGlobalCheckbox =
|
||||
globalCheckStatus === 'indeterminate' || globalCheckStatus === 'checked';
|
||||
interface BusterListHeaderProps<T> {
|
||||
columns: BusterListColumn<T>[];
|
||||
onGlobalSelectChange?: (v: boolean) => void;
|
||||
globalCheckStatus?: 'checked' | 'unchecked' | 'indeterminate';
|
||||
showSelectAll?: boolean;
|
||||
rowsLength: number;
|
||||
rowClassName: string;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'group border-border flex items-center justify-start border-b pr-6',
|
||||
{
|
||||
'pl-3.5': !onGlobalSelectChange
|
||||
},
|
||||
rowClassName
|
||||
)}
|
||||
style={{
|
||||
height: `${HEIGHT_OF_HEADER}px`,
|
||||
minHeight: `${HEIGHT_OF_HEADER}px`
|
||||
}}>
|
||||
{showCheckboxColumn && (
|
||||
<CheckboxColumn
|
||||
checkStatus={globalCheckStatus}
|
||||
onChange={onGlobalSelectChange}
|
||||
className={cn({
|
||||
'opacity-100': showGlobalCheckbox,
|
||||
'invisible!': rowsLength === 0,
|
||||
'pointer-events-none invisible!': !showSelectAll
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
export const BusterListHeader = <T = unknown,>({
|
||||
columns,
|
||||
rowClassName,
|
||||
showSelectAll = true,
|
||||
onGlobalSelectChange,
|
||||
globalCheckStatus,
|
||||
rowsLength
|
||||
}: BusterListHeaderProps<T>) => {
|
||||
const showCheckboxColumn = !!onGlobalSelectChange;
|
||||
const showGlobalCheckbox =
|
||||
globalCheckStatus === 'indeterminate' || globalCheckStatus === 'checked';
|
||||
|
||||
{columns.map((column, index) => (
|
||||
<div
|
||||
className="header-cell flex h-full items-center p-0"
|
||||
key={String(column.dataIndex)}
|
||||
style={{
|
||||
width: column.width || '100%',
|
||||
flex: column.width ? 'none' : 1,
|
||||
paddingLeft: showCheckboxColumn ? undefined : '0px'
|
||||
}}>
|
||||
{column.headerRender ? (
|
||||
column.headerRender(column.title)
|
||||
) : (
|
||||
<Text size="sm" variant="secondary" truncate>
|
||||
{column.title}
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'group border-border flex items-center justify-start border-b pr-6',
|
||||
{
|
||||
'pl-3.5': !onGlobalSelectChange
|
||||
},
|
||||
rowClassName
|
||||
)}
|
||||
style={{
|
||||
height: `${HEIGHT_OF_HEADER}px`,
|
||||
minHeight: `${HEIGHT_OF_HEADER}px`
|
||||
}}>
|
||||
{showCheckboxColumn && (
|
||||
<CheckboxColumn
|
||||
checkStatus={globalCheckStatus}
|
||||
onChange={onGlobalSelectChange}
|
||||
className={cn({
|
||||
'opacity-100': showGlobalCheckbox,
|
||||
'invisible!': rowsLength === 0,
|
||||
'pointer-events-none invisible!': !showSelectAll
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
|
||||
{columns.map((column, index) => (
|
||||
<div
|
||||
className="header-cell flex h-full items-center p-0"
|
||||
key={String(column.dataIndex)}
|
||||
style={{
|
||||
width: column.width || '100%',
|
||||
flex: column.width ? 'none' : 1,
|
||||
paddingLeft: showCheckboxColumn ? undefined : '0px'
|
||||
}}>
|
||||
{column.headerRender ? (
|
||||
column.headerRender(column.title)
|
||||
) : (
|
||||
<Text size="sm" variant="secondary" truncate>
|
||||
{column.title}
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -12,104 +12,101 @@ import type {
|
|||
BusterListRowItem
|
||||
} from './interfaces';
|
||||
|
||||
export const BusterListRowComponent = <T = unknown,>() =>
|
||||
React.memo(
|
||||
React.forwardRef<
|
||||
HTMLDivElement,
|
||||
interface BusterListRowComponentProps<T> {
|
||||
row: BusterListRow;
|
||||
columns: BusterListColumn<T>[];
|
||||
checked: boolean;
|
||||
onSelectChange?: (v: boolean, id: string, e: React.MouseEvent) => void;
|
||||
onContextMenuClick?: (e: React.MouseEvent<HTMLDivElement>, id: string) => void;
|
||||
style?: React.CSSProperties;
|
||||
hideLastRowBorder: NonNullable<BusterListProps['hideLastRowBorder']>;
|
||||
useRowClickSelectChange: boolean;
|
||||
rowClassName?: string;
|
||||
isLastChild: boolean;
|
||||
}
|
||||
|
||||
export const BusterListRowComponent = React.memo(
|
||||
React.forwardRef(
|
||||
<T,>(
|
||||
{
|
||||
row: BusterListRow;
|
||||
columns: BusterListColumn<T>[];
|
||||
checked: boolean;
|
||||
onSelectChange?: (v: boolean, id: string, e: React.MouseEvent) => void;
|
||||
onContextMenuClick?: (e: React.MouseEvent<HTMLDivElement>, id: string) => void;
|
||||
style?: React.CSSProperties;
|
||||
hideLastRowBorder: NonNullable<BusterListProps['hideLastRowBorder']>;
|
||||
useRowClickSelectChange: boolean;
|
||||
rowClassName?: string;
|
||||
isLastChild: boolean;
|
||||
}
|
||||
>(
|
||||
(
|
||||
{
|
||||
style,
|
||||
hideLastRowBorder,
|
||||
row,
|
||||
columns,
|
||||
onSelectChange,
|
||||
checked,
|
||||
onContextMenuClick,
|
||||
rowClassName = '',
|
||||
isLastChild,
|
||||
useRowClickSelectChange
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const link = row.link;
|
||||
style,
|
||||
hideLastRowBorder,
|
||||
row,
|
||||
columns,
|
||||
onSelectChange,
|
||||
checked,
|
||||
onContextMenuClick,
|
||||
rowClassName = '',
|
||||
isLastChild,
|
||||
useRowClickSelectChange
|
||||
}: BusterListRowComponentProps<T>,
|
||||
ref: React.ForwardedRef<HTMLDivElement>
|
||||
) => {
|
||||
const link = row.link;
|
||||
|
||||
const onContextMenu = useMemoizedFn((e: React.MouseEvent<HTMLDivElement>) => {
|
||||
onContextMenuClick?.(e, row.id);
|
||||
});
|
||||
const onContextMenu = useMemoizedFn((e: React.MouseEvent<HTMLDivElement>) => {
|
||||
onContextMenuClick?.(e, row.id);
|
||||
});
|
||||
|
||||
const onChange = useMemoizedFn((newChecked: boolean, e: React.MouseEvent) => {
|
||||
onSelectChange?.(newChecked, row.id, e);
|
||||
});
|
||||
const onChange = useMemoizedFn((newChecked: boolean, e: React.MouseEvent) => {
|
||||
onSelectChange?.(newChecked, row.id, e);
|
||||
});
|
||||
|
||||
const onContainerClick = useMemoizedFn((e: React.MouseEvent) => {
|
||||
if (useRowClickSelectChange) {
|
||||
onChange(!checked, e);
|
||||
}
|
||||
row.onClick?.();
|
||||
});
|
||||
const onContainerClick = useMemoizedFn((e: React.MouseEvent) => {
|
||||
if (useRowClickSelectChange) {
|
||||
onChange(!checked, e);
|
||||
}
|
||||
row.onClick?.();
|
||||
});
|
||||
|
||||
const rowStyles = {
|
||||
height: `${HEIGHT_OF_ROW}px`,
|
||||
minHeight: `${HEIGHT_OF_ROW}px`,
|
||||
...style
|
||||
};
|
||||
const rowStyles = {
|
||||
height: `${HEIGHT_OF_ROW}px`,
|
||||
minHeight: `${HEIGHT_OF_ROW}px`,
|
||||
...style
|
||||
};
|
||||
|
||||
return (
|
||||
<LinkWrapper href={link}>
|
||||
<div
|
||||
onClick={onContainerClick}
|
||||
style={rowStyles}
|
||||
onContextMenu={onContextMenu}
|
||||
data-testid={row.dataTestId}
|
||||
className={cn(
|
||||
'border-border flex items-center border-b pr-6',
|
||||
checked ? 'bg-primary-background hover:bg-primary-background-hover' : '',
|
||||
isLastChild && hideLastRowBorder ? 'border-b-0!' : '',
|
||||
!onSelectChange ? 'pl-3.5' : '',
|
||||
link || row.onClick || (onSelectChange && useRowClickSelectChange)
|
||||
? 'hover:bg-item-hover cursor-pointer'
|
||||
: '',
|
||||
rowClassName,
|
||||
'group'
|
||||
)}
|
||||
ref={ref}>
|
||||
{onSelectChange ? (
|
||||
<CheckboxColumn
|
||||
checkStatus={checked ? 'checked' : 'unchecked'}
|
||||
onChange={onChange}
|
||||
/>
|
||||
) : null}
|
||||
{columns.map((column, columnIndex) => (
|
||||
<BusterListCellComponent
|
||||
key={String(column.dataIndex)}
|
||||
data={get(row.data, column.dataIndex)}
|
||||
row={row}
|
||||
render={column.render as any}
|
||||
isFirstCell={columnIndex === 0}
|
||||
isLastCell={columnIndex === columns.length - 1}
|
||||
width={column.width}
|
||||
onSelectChange={onSelectChange}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</LinkWrapper>
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
return (
|
||||
<LinkWrapper href={link}>
|
||||
<div
|
||||
onClick={onContainerClick}
|
||||
style={rowStyles}
|
||||
onContextMenu={onContextMenu}
|
||||
data-testid={row.dataTestId}
|
||||
className={cn(
|
||||
'border-border flex items-center border-b pr-6',
|
||||
checked ? 'bg-primary-background hover:bg-primary-background-hover' : '',
|
||||
isLastChild && hideLastRowBorder ? 'border-b-0!' : '',
|
||||
!onSelectChange ? 'pl-3.5' : '',
|
||||
link || row.onClick || (onSelectChange && useRowClickSelectChange)
|
||||
? 'hover:bg-item-hover cursor-pointer'
|
||||
: '',
|
||||
rowClassName,
|
||||
'group'
|
||||
)}
|
||||
ref={ref}>
|
||||
{onSelectChange ? (
|
||||
<CheckboxColumn checkStatus={checked ? 'checked' : 'unchecked'} onChange={onChange} />
|
||||
) : null}
|
||||
{columns.map((column, columnIndex) => (
|
||||
<BusterListCellComponent
|
||||
key={String(column.dataIndex)}
|
||||
data={get(row.data, column.dataIndex)}
|
||||
row={row}
|
||||
render={column.render as any}
|
||||
isFirstCell={columnIndex === 0}
|
||||
isLastCell={columnIndex === columns.length - 1}
|
||||
width={column.width}
|
||||
onSelectChange={onSelectChange}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</LinkWrapper>
|
||||
);
|
||||
}
|
||||
)
|
||||
) as any as <T = unknown>(
|
||||
props: BusterListRowComponentProps<T> & React.RefAttributes<HTMLDivElement>
|
||||
) => React.ReactElement | null;
|
||||
|
||||
const BusterListCellComponent: React.FC<{
|
||||
data: string | number | React.ReactNode;
|
||||
|
|
|
@ -3,80 +3,75 @@ import { BusterListRowComponent } from './BusterListRowComponent';
|
|||
import { BusterListSectionComponent } from './BusterListSectionComponent';
|
||||
import type { BusterListColumn, BusterListProps, BusterListRow } from './interfaces';
|
||||
|
||||
export const BusterListRowComponentSelector = <T = unknown,>() => {
|
||||
// const RowComponent = BusterListRowComponent<T>();
|
||||
interface BusterListRowComponentSelectorProps<T> {
|
||||
row: BusterListRow;
|
||||
columns: BusterListColumn<T>[];
|
||||
id: string;
|
||||
onSelectChange?: (v: boolean, id: string, e: React.MouseEvent) => void;
|
||||
onSelectSectionChange?: (v: boolean, id: string) => void;
|
||||
onContextMenuClick?: (e: React.MouseEvent<HTMLDivElement>, id: string) => void;
|
||||
selectedRowKeys?: string[];
|
||||
rows: BusterListRow[];
|
||||
style?: React.CSSProperties;
|
||||
hideLastRowBorder: NonNullable<BusterListProps['hideLastRowBorder']>;
|
||||
rowClassName?: string;
|
||||
isLastChild: boolean;
|
||||
useRowClickSelectChange?: boolean;
|
||||
}
|
||||
|
||||
return React.forwardRef<
|
||||
HTMLDivElement,
|
||||
export const BusterListRowComponentSelector = React.forwardRef(
|
||||
<T,>(
|
||||
{
|
||||
row: BusterListRow;
|
||||
columns: BusterListColumn<T>[];
|
||||
id: string;
|
||||
onSelectChange?: (v: boolean, id: string, e: React.MouseEvent) => void;
|
||||
onSelectSectionChange?: (v: boolean, id: string) => void;
|
||||
onContextMenuClick?: (e: React.MouseEvent<HTMLDivElement>, id: string) => void;
|
||||
selectedRowKeys?: string[];
|
||||
rows: BusterListRow[];
|
||||
style?: React.CSSProperties;
|
||||
hideLastRowBorder: NonNullable<BusterListProps['hideLastRowBorder']>;
|
||||
rowClassName?: string;
|
||||
isLastChild: boolean;
|
||||
useRowClickSelectChange?: boolean;
|
||||
}
|
||||
>(
|
||||
(
|
||||
{
|
||||
style,
|
||||
row,
|
||||
rows,
|
||||
columns,
|
||||
isLastChild,
|
||||
onSelectChange,
|
||||
onSelectSectionChange,
|
||||
selectedRowKeys,
|
||||
onContextMenuClick,
|
||||
hideLastRowBorder,
|
||||
rowClassName,
|
||||
useRowClickSelectChange = false
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
if (row.hidden) return null;
|
||||
|
||||
if (row.rowSection) {
|
||||
return (
|
||||
<BusterListSectionComponent
|
||||
style={style}
|
||||
rowSection={row.rowSection}
|
||||
ref={ref}
|
||||
id={row.id}
|
||||
key={row.id}
|
||||
rows={rows}
|
||||
selectedRowKeys={selectedRowKeys}
|
||||
rowClassName={rowClassName}
|
||||
onSelectSectionChange={onSelectSectionChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const RowComponent = BusterListRowComponent<T>();
|
||||
style,
|
||||
row,
|
||||
rows,
|
||||
columns,
|
||||
isLastChild,
|
||||
onSelectChange,
|
||||
onSelectSectionChange,
|
||||
selectedRowKeys,
|
||||
onContextMenuClick,
|
||||
hideLastRowBorder,
|
||||
rowClassName,
|
||||
useRowClickSelectChange = false
|
||||
}: BusterListRowComponentSelectorProps<T>,
|
||||
ref: React.ForwardedRef<HTMLDivElement>
|
||||
) => {
|
||||
if (row.hidden) return null;
|
||||
|
||||
if (row.rowSection) {
|
||||
return (
|
||||
<RowComponent
|
||||
<BusterListSectionComponent
|
||||
style={style}
|
||||
row={row}
|
||||
columns={columns}
|
||||
key={row.id}
|
||||
rowClassName={rowClassName}
|
||||
onSelectChange={onSelectChange}
|
||||
checked={!!selectedRowKeys?.includes(row.id)}
|
||||
rowSection={row.rowSection}
|
||||
ref={ref}
|
||||
onContextMenuClick={onContextMenuClick}
|
||||
hideLastRowBorder={hideLastRowBorder}
|
||||
useRowClickSelectChange={useRowClickSelectChange}
|
||||
isLastChild={isLastChild}
|
||||
id={row.id}
|
||||
key={row.id}
|
||||
rows={rows}
|
||||
selectedRowKeys={selectedRowKeys}
|
||||
rowClassName={rowClassName}
|
||||
onSelectSectionChange={onSelectSectionChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<BusterListRowComponent<T>
|
||||
style={style}
|
||||
row={row}
|
||||
columns={columns}
|
||||
key={row.id}
|
||||
rowClassName={rowClassName}
|
||||
onSelectChange={onSelectChange}
|
||||
checked={!!selectedRowKeys?.includes(row.id)}
|
||||
ref={ref}
|
||||
onContextMenuClick={onContextMenuClick}
|
||||
hideLastRowBorder={hideLastRowBorder}
|
||||
useRowClickSelectChange={useRowClickSelectChange}
|
||||
isLastChild={isLastChild}
|
||||
/>
|
||||
);
|
||||
}
|
||||
) as any as <T = unknown>(
|
||||
props: BusterListRowComponentSelectorProps<T> & React.RefAttributes<HTMLDivElement>
|
||||
) => React.ReactElement | null;
|
||||
|
|
|
@ -23,9 +23,6 @@ function BusterListVirtuaComponent<T = any>({
|
|||
rowClassName = '',
|
||||
hideLastRowBorder = false
|
||||
}: BusterListProps<T>) {
|
||||
const Header = BusterListHeader<T>();
|
||||
const RowSelector = BusterListRowComponentSelector<T>();
|
||||
|
||||
const showEmptyState = (!rows || rows.length === 0) && !!emptyState;
|
||||
const lastChildIndex = rows.length - 1;
|
||||
const lastSelectedIdRef = useRef<string | null>(null);
|
||||
|
@ -126,7 +123,7 @@ function BusterListVirtuaComponent<T = any>({
|
|||
<WrapperNode {...wrapperNodeProps}>
|
||||
<div className="list-container relative flex h-full w-full flex-col overflow-hidden">
|
||||
{showHeader && !showEmptyState && (
|
||||
<Header
|
||||
<BusterListHeader<T>
|
||||
columns={columns}
|
||||
onGlobalSelectChange={onSelectChange ? onGlobalSelectChange : undefined}
|
||||
globalCheckStatus={globalCheckStatus}
|
||||
|
@ -140,7 +137,7 @@ function BusterListVirtuaComponent<T = any>({
|
|||
<VList overscan={10}>
|
||||
{rows.map((row, index) => (
|
||||
<div key={row.id + index.toString()} style={{ height: itemSize(index) }}>
|
||||
<RowSelector
|
||||
<BusterListRowComponentSelector<T>
|
||||
row={row}
|
||||
id={row.id}
|
||||
isLastChild={index === lastChildIndex}
|
||||
|
|
Loading…
Reference in New Issue