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