Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(BaseTable): additional properties for classNames and attributes customization #36

Merged
merged 2 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 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,20 @@ export const BaseCell = <TData,>({
attributes: attributesProp,
...restProps
}: BaseCellProps<TData>) => {
const attributes = React.useMemo(() => {
return typeof attributesProp === 'function' ? attributesProp(cell) : attributesProp;
}, [attributesProp, cell]);

const className = React.useMemo(() => {
return typeof classNameProp === 'function' ? classNameProp(cell) : classNameProp;
}, [cell, classNameProp]);

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

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
19 changes: 16 additions & 3 deletions src/components/BaseFooterCell/BaseFooterCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,26 @@ 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>) => {
const attributes = React.useMemo(() => {
beliarh marked this conversation as resolved.
Show resolved Hide resolved
return typeof attributesProp === 'function' ? attributesProp(header) : attributesProp;
}, [attributesProp, header]);

const className = React.useMemo(() => {
beliarh marked this conversation as resolved.
Show resolved Hide resolved
return typeof classNameProp === 'function' ? classNameProp(header) : classNameProp;
}, [classNameProp, header]);

if (header.isPlaceholder) {
return null;
}
Expand All @@ -26,7 +38,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
37 changes: 28 additions & 9 deletions src/components/BaseFooterRow/BaseFooterRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,48 @@ 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 attributes = React.useMemo(() => {
return typeof attributesProp === 'function' ? attributesProp(footerGroup) : attributesProp;
}, [attributesProp, footerGroup]);

const className = React.useMemo(() => {
return typeof classNameProp === 'function' ? classNameProp(footerGroup) : classNameProp;
}, [classNameProp, footerGroup]);

const isEmptyRow = footerGroup.headers.every((header) => !header.column.columnDef.footer);

if (isEmptyRow) {
return null;
}

return (
<tr className={b('footer-row', className)} {...restProps}>
<tr className={b('footer-row', className)} {...restProps} {...attributes}>
{footerGroup.headers.map((header) => (
<BaseFooterCell key={header.column.id} className={cellClassName} header={header} />
<BaseFooterCell
key={header.column.id}
header={header}
attributes={cellAttributes}
className={cellClassName}
/>
))}
</tr>
);
Expand Down
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
17 changes: 9 additions & 8 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,6 +43,12 @@ export const BaseHeaderCell = <TData, TValue>({
sortIndicatorClassName,
attributes: attributesProp,
}: BaseHeaderCellProps<TData, TValue>) => {
const attributes = React.useMemo(() => {
return typeof attributesProp === 'function'
? attributesProp(header, parentHeader)
: attributesProp;
}, [attributesProp, header, parentHeader]);

const className = React.useMemo(() => {
return typeof classNameProp === 'function'
? classNameProp(header, parentHeader)
Expand All @@ -65,21 +71,16 @@ export const BaseHeaderCell = <TData, TValue>({

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

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

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
17 changes: 9 additions & 8 deletions src/components/BaseHeaderRow/BaseHeaderRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import {BaseHeaderCell} from '../BaseHeaderCell';
import type {BaseResizeHandleProps} from '../BaseResizeHandle';
import {b} from '../BaseTable/BaseTable.classname';

export interface BaseHeaderRowProps<TData, TValue>
extends Omit<React.TdHTMLAttributes<HTMLTableRowElement>, 'className'> {
export interface BaseHeaderRowProps<TData, TValue = unknown>
extends Omit<React.HTMLAttributes<HTMLTableRowElement>, 'className'> {
cellClassName?: BaseHeaderCellProps<TData, TValue>['className'];
className?:
| string
Expand All @@ -28,7 +28,7 @@ export interface BaseHeaderRowProps<TData, TValue>
cellAttributes?: BaseHeaderCellProps<TData, TValue>['attributes'];
}

export const BaseHeaderRow = <TData, TValue>({
export const BaseHeaderRow = <TData, TValue = unknown>({
cellClassName,
className: classNameProp,
headerGroup,
Expand All @@ -41,17 +41,18 @@ export const BaseHeaderRow = <TData, TValue>({
cellAttributes,
...restProps
}: BaseHeaderRowProps<TData, TValue>) => {
const attributes = React.useMemo(() => {
return typeof attributesProp === 'function'
? attributesProp(headerGroup, parentHeaderGroup)
: attributesProp;
}, [attributesProp, headerGroup, parentHeaderGroup]);

const className = React.useMemo(() => {
return typeof classNameProp === 'function'
? classNameProp(headerGroup, parentHeaderGroup)
: classNameProp;
}, [classNameProp, headerGroup, parentHeaderGroup]);

const attributes =
typeof attributesProp === 'function'
? attributesProp(headerGroup, parentHeaderGroup)
: attributesProp;

return (
<tr className={b('header-row', className)} {...restProps} {...attributes}>
{headerGroup.headers.map((header) => (
Expand Down
Loading
Loading