Skip to content

Commit

Permalink
feat(BaseTable): additional properties for classNames and attributes …
Browse files Browse the repository at this point in the history
…customization (#36)
  • Loading branch information
beliarh authored Sep 10, 2024
1 parent 9c25359 commit 33db774
Show file tree
Hide file tree
Showing 33 changed files with 331 additions and 322 deletions.
15 changes: 6 additions & 9 deletions src/components/BaseCell/BaseCell.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import React from 'react';

import type {Cell as CellType} from '@tanstack/react-table';
import type {Cell} from '@tanstack/react-table';
import {flexRender} from '@tanstack/react-table';

import {getCellClassModes, getCellStyles} from '../../utils';
import {b} from '../BaseTable/BaseTable.classname';

export interface BaseCellProps<TData>
extends Omit<React.TdHTMLAttributes<HTMLTableCellElement>, 'className'> {
cell?: CellType<TData, unknown>;
className?: string | ((cell?: CellType<TData, unknown>) => string);
cell?: Cell<TData, unknown>;
className?: string | ((cell?: Cell<TData, unknown>) => string);
attributes?:
| React.TdHTMLAttributes<HTMLTableCellElement>
| ((cell?: CellType<TData, unknown>) => React.TdHTMLAttributes<HTMLTableCellElement>);
| ((cell?: Cell<TData, unknown>) => React.TdHTMLAttributes<HTMLTableCellElement>);
}

export const BaseCell = <TData,>({
Expand All @@ -23,18 +23,15 @@ export const BaseCell = <TData,>({
attributes: attributesProp,
...restProps
}: BaseCellProps<TData>) => {
const className = React.useMemo(() => {
return typeof classNameProp === 'function' ? classNameProp(cell) : classNameProp;
}, [cell, classNameProp]);

const attributes = typeof attributesProp === 'function' ? attributesProp(cell) : attributesProp;
const className = typeof classNameProp === 'function' ? classNameProp(cell) : classNameProp;

return (
<td
className={b('cell', getCellClassModes(cell), className)}
style={getCellStyles(cell, style)}
{...restProps}
{...attributes}
style={getCellStyles(cell, {...style, ...attributes?.style})}
>
{cell ? flexRender(cell.column.columnDef.cell, cell.getContext()) : children}
</td>
Expand Down
11 changes: 6 additions & 5 deletions src/components/BaseDragHandle/BaseDragHandle.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
import React from 'react';

import {useSortable} from '@dnd-kit/sortable';
import type {Row} from '@tanstack/react-table';
import type {Row, Table} from '@tanstack/react-table';

import {useDraggableRowDepth} from '../../hooks';
import {BaseDraggableRowMarker} from '../BaseDraggableRowMarker';
import {TableContext} from '../TableContext';
import {SortableListContext} from '../SortableListContext';

import {b} from './BaseDragHandle.classname';

import './BaseDragHandle.scss';

export interface BaseDragHandleProps<TData> {
row: Row<TData>;
table: Table<TData>;
}

export const BaseDragHandle = <TData,>({row}: BaseDragHandleProps<TData>) => {
export const BaseDragHandle = <TData,>({row, table}: BaseDragHandleProps<TData>) => {
const {attributes, listeners, isDragging} = useSortable({
id: row.id,
});

const {enableNesting} = React.useContext(TableContext);
const {enableNesting} = React.useContext(SortableListContext) || {};

const {depth} = useDraggableRowDepth({row, isDragging});
const {depth} = useDraggableRowDepth({row, table, isDragging});

return (
<React.Fragment>
Expand Down
17 changes: 9 additions & 8 deletions src/components/BaseDraggableRow/BaseDraggableRow.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import React from 'react';

import {useForkRef} from '@gravity-ui/uikit';
import type {Row as RowType} from '@tanstack/react-table';
import type {Row} from '@tanstack/react-table';

import {useDraggableRowDepth, useDraggableRowStyle} from '../../hooks';
import type {BaseRowProps} from '../BaseRow';
import {BaseRow} from '../BaseRow';
import {SortableListContext} from '../SortableListContext';
import {TableContext} from '../TableContext';

export interface BaseDraggableRowProps<
TData,
Expand All @@ -20,6 +19,7 @@ export const BaseDraggableRow = React.forwardRef(
attributes: attributesProp,
row,
style,
table,
...restProps
}: BaseDraggableRowProps<TData, TScrollElement>,
ref: React.Ref<HTMLTableRowElement>,
Expand All @@ -28,6 +28,7 @@ export const BaseDraggableRow = React.forwardRef(
isChildMode,
activeItemKey,
targetItemIndex = -1,
enableNesting,
// The `useSortable` hook is provided by `@dnd-kit/sortable` library and imported via `SortableListContext`.
// This is a temporary solution to prevent importing the entire `@dnd-kit` library
// when the user doesn't use the reordering feature.
Expand All @@ -43,15 +44,14 @@ export const BaseDraggableRow = React.forwardRef(
id: row.id,
}) || {};

const {enableNesting} = React.useContext(TableContext);

const isDragActive = Boolean(activeItemKey);
const isParent = isChildMode && targetItemIndex === row.index;

const handleRowRef = useForkRef(setNodeRef, ref);

const {isFirstChild, depth} = useDraggableRowDepth<TData>({
row,
table,
isDragging,
});

Expand All @@ -62,11 +62,11 @@ export const BaseDraggableRow = React.forwardRef(
isDragging,
isDragActive,
isFirstChild,
nestingEnabled: enableNesting,
enableNesting,
});

const getDraggableRowDataAttributes = React.useCallback(
(draggableRow: RowType<TData>) => {
const getDraggableRowAttributes = React.useCallback(
(draggableRow: Row<TData>) => {
const attributes =
typeof attributesProp === 'function'
? attributesProp(draggableRow)
Expand All @@ -88,9 +88,10 @@ export const BaseDraggableRow = React.forwardRef(
return (
<BaseRow
ref={handleRowRef}
attributes={getDraggableRowDataAttributes}
attributes={getDraggableRowAttributes}
row={row}
style={draggableStyle}
table={table}
{...restProps}
/>
);
Expand Down
18 changes: 12 additions & 6 deletions src/components/BaseFooterCell/BaseFooterCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,22 @@ import {getCellStyles, getHeaderCellClassModes} from '../../utils';
import {b} from '../BaseTable/BaseTable.classname';

export interface BaseFooterCellProps<TData, TValue> {
className?: string;
header: Header<TData, TValue>;
attributes?:
| React.ThHTMLAttributes<HTMLTableCellElement>
| ((header: Header<TData, TValue>) => React.ThHTMLAttributes<HTMLTableCellElement>);
className?: string | ((header: Header<TData, TValue>) => string);
}

export const BaseFooterCell = <TData, TValue>({
className,
header,
attributes: attributesProp,
className: classNameProp,
}: BaseFooterCellProps<TData, TValue>) => {
if (header.isPlaceholder) {
return null;
}
const attributes =
typeof attributesProp === 'function' ? attributesProp(header) : attributesProp;

const className = typeof classNameProp === 'function' ? classNameProp(header) : classNameProp;

const rowSpan = header.depth - header.column.depth;

Expand All @@ -26,7 +31,8 @@ export const BaseFooterCell = <TData, TValue>({
className={b('footer-cell', getHeaderCellClassModes(header), className)}
colSpan={header.colSpan > 1 ? header.colSpan : undefined}
rowSpan={rowSpan > 1 ? rowSpan : undefined}
style={getCellStyles(header)}
{...attributes}
style={getCellStyles(header, attributes?.style)}
>
{flexRender(header.column.columnDef.footer, header.getContext())}
</th>
Expand Down
46 changes: 30 additions & 16 deletions src/components/BaseFooterRow/BaseFooterRow.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,49 @@
import React from 'react';

import type {HeaderGroup} from '@tanstack/react-table';
import type {Header, HeaderGroup} from '@tanstack/react-table';

import {shouldRenderFooterCell} from '../../utils';
import type {BaseFooterCellProps} from '../BaseFooterCell';
import {BaseFooterCell} from '../BaseFooterCell';
import {b} from '../BaseTable/BaseTable.classname';

export interface BaseFooterRowProps<TData, TValue>
extends React.TdHTMLAttributes<HTMLTableRowElement> {
cellClassName: BaseFooterCellProps<TData, TValue>['className'];
className?: string;
export interface BaseFooterRowProps<TData, TValue = unknown>
extends Omit<React.HTMLAttributes<HTMLTableRowElement>, 'className'> {
footerGroup: HeaderGroup<TData>;
attributes?:
| React.HTMLAttributes<HTMLTableRowElement>
| ((footerGroup: HeaderGroup<TData>) => React.HTMLAttributes<HTMLTableRowElement>);
cellAttributes?: BaseFooterCellProps<TData, TValue>['attributes'];
cellClassName?: BaseFooterCellProps<TData, TValue>['className'];
className?: string | ((footerGroup: HeaderGroup<TData>) => string);
}

export const BaseFooterRow = <TData, TValue>({
cellClassName,
className,
export const BaseFooterRow = <TData, TValue = unknown>({
footerGroup,
attributes: attributesProp,
cellAttributes,
cellClassName,
className: classNameProp,
...restProps
}: BaseFooterRowProps<TData, TValue>) => {
const isEmptyRow = footerGroup.headers.every((header) => !header.column.columnDef.footer);
const attributes =
typeof attributesProp === 'function' ? attributesProp(footerGroup) : attributesProp;

if (isEmptyRow) {
return null;
}
const className =
typeof classNameProp === 'function' ? classNameProp(footerGroup) : classNameProp;

return (
<tr className={b('footer-row', className)} {...restProps}>
{footerGroup.headers.map((header) => (
<BaseFooterCell key={header.column.id} className={cellClassName} header={header} />
))}
<tr className={b('footer-row', className)} {...restProps} {...attributes}>
{footerGroup.headers.map((header) =>
shouldRenderFooterCell(header) ? (
<BaseFooterCell
key={header.column.id}
header={header as Header<TData, TValue>}
attributes={cellAttributes}
className={cellClassName}
/>
) : null,
)}
</tr>
);
};
4 changes: 2 additions & 2 deletions src/components/BaseGroupHeader/BaseGroupHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import {b} from './BaseGroupHeader.classname';
import './BaseGroupHeader.scss';

export interface BaseGroupHeaderProps<TData> {
row: Row<TData>;
className?: string;
getGroupTitle?: (row: Row<TData>) => React.ReactNode;
row: Row<TData>;
}

export const BaseGroupHeader = <TData,>({
row,
className,
getGroupTitle,
row,
}: BaseGroupHeaderProps<TData>) => {
return (
<h2 className={b(null, className)}>
Expand Down
33 changes: 8 additions & 25 deletions src/components/BaseHeaderCell/BaseHeaderCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ export interface BaseHeaderCellProps<TData, TValue> {
resizeHandleClassName?: string;
sortIndicatorClassName?: string;
attributes?:
| React.TdHTMLAttributes<HTMLTableCellElement>
| React.ThHTMLAttributes<HTMLTableCellElement>
| ((
header: Header<TData, TValue>,
parentHeader?: Header<TData, unknown>,
) => React.TdHTMLAttributes<HTMLTableCellElement>);
) => React.ThHTMLAttributes<HTMLTableCellElement>);
}

export const BaseHeaderCell = <TData, TValue>({
Expand All @@ -43,43 +43,26 @@ export const BaseHeaderCell = <TData, TValue>({
sortIndicatorClassName,
attributes: attributesProp,
}: BaseHeaderCellProps<TData, TValue>) => {
const className = React.useMemo(() => {
return typeof classNameProp === 'function'
? classNameProp(header, parentHeader)
: classNameProp;
}, [classNameProp, header, parentHeader]);

const isPlaceholderRowSpannedCell =
header.isPlaceholder &&
parentHeader?.isPlaceholder &&
parentHeader.placeholderId === header.placeholderId;

const isLeafRowSpannedCell =
!header.isPlaceholder &&
header.id === header.column.id &&
header.depth - header.column.depth > 1;

if (isPlaceholderRowSpannedCell || isLeafRowSpannedCell) {
return null;
}

const rowSpan = header.isPlaceholder ? header.getLeafHeaders().length : 1;

const attributes =
typeof attributesProp === 'function'
? attributesProp(header, parentHeader)
: attributesProp;

const className =
typeof classNameProp === 'function' ? classNameProp(header, parentHeader) : classNameProp;

const rowSpan = header.isPlaceholder ? header.getLeafHeaders().length : 1;

return (
<th
className={b('header-cell', getHeaderCellClassModes(header), className)}
colSpan={header.colSpan > 1 ? header.colSpan : undefined}
rowSpan={rowSpan > 1 ? rowSpan : undefined}
onClick={header.column.getToggleSortingHandler()}
style={getCellStyles(header)}
aria-sort={getAriaSort(header.column.getIsSorted())}
aria-colindex={getHeaderCellAriaColIndex(header)}
{...attributes}
style={getCellStyles(header, attributes?.style)}
>
{flexRender(header.column.columnDef.header, header.getContext())}{' '}
{header.column.getCanSort() &&
Expand Down
Loading

0 comments on commit 33db774

Please sign in to comment.