From 0588b3a52fcee1575270b7a286f1b0d31f09acb9 Mon Sep 17 00:00:00 2001 From: kseniyakuzina Date: Tue, 13 Aug 2024 13:18:51 +0300 Subject: [PATCH] feat(Table): attributes customization --- src/components/Cell/Cell.tsx | 7 ++++ src/components/DraggableRow/DraggableRow.tsx | 41 +++++++++++++------- src/components/HeaderCell/HeaderCell.tsx | 13 +++++++ src/components/HeaderRow/HeaderRow.tsx | 17 +++++++- src/components/Row/Row.tsx | 30 ++++++++++---- src/components/Table/Table.tsx | 28 +++++++++++-- 6 files changed, 109 insertions(+), 27 deletions(-) diff --git a/src/components/Cell/Cell.tsx b/src/components/Cell/Cell.tsx index 66f7d2f..d56059b 100644 --- a/src/components/Cell/Cell.tsx +++ b/src/components/Cell/Cell.tsx @@ -10,6 +10,9 @@ export interface CellProps extends Omit, 'className'> { cell?: CellType; className?: string | ((cell?: CellType) => string); + attributes?: + | React.TdHTMLAttributes + | ((cell?: CellType) => React.TdHTMLAttributes); } export const Cell = ({ @@ -17,17 +20,21 @@ export const Cell = ({ children, className: classNameProp, style, + attributes: attributesProp, ...restProps }: CellProps) => { const className = React.useMemo(() => { return typeof classNameProp === 'function' ? classNameProp(cell) : classNameProp; }, [cell, classNameProp]); + const attributes = typeof attributesProp === 'function' ? attributesProp(cell) : attributesProp; + return ( {cell ? flexRender(cell.column.columnDef.cell, cell.getContext()) : children} diff --git a/src/components/DraggableRow/DraggableRow.tsx b/src/components/DraggableRow/DraggableRow.tsx index c1872e0..c25f330 100644 --- a/src/components/DraggableRow/DraggableRow.tsx +++ b/src/components/DraggableRow/DraggableRow.tsx @@ -2,6 +2,7 @@ import React from 'react'; import {useSortable} from '@dnd-kit/sortable'; import {useForkRef} from '@gravity-ui/uikit'; +import type {Row as RowType} from '@tanstack/react-table'; import {useDraggableRowDepth, useDraggableRowStyle} from '../../hooks'; import type {RowProps} from '../Row'; @@ -14,7 +15,12 @@ export interface DraggableRowProps( - {getRowAttributes, row, style, ...restProps}: DraggableRowProps, + { + attributes: attributesProp, + row, + style, + ...restProps + }: DraggableRowProps, ref: React.Ref, ) => { const {setNodeRef, transform, transition, isDragging} = useSortable({ @@ -48,25 +54,30 @@ export const DraggableRow = React.forwardRef( nestingEnabled: enableNesting, }); - const getDraggableRowDataAttributes = React.useCallback< - NonNullable['getRowAttributes']> - >( - (draggableRow) => ({ - ...getRowAttributes?.(draggableRow), - 'data-key': draggableRow.id, - 'data-depth': depth, - 'data-draggable': true, - 'data-dragging': isDragging, - 'data-drag-active': isDragActive, - 'data-expanded': isDragActive && isParent, - }), - [getRowAttributes, depth, isDragging, isDragActive, isParent], + const getDraggableRowDataAttributes = React.useCallback( + (draggableRow: RowType) => { + const attributes = + typeof attributesProp === 'function' + ? attributesProp(draggableRow) + : attributesProp; + + return { + ...attributes, + 'data-key': draggableRow.id, + 'data-depth': depth, + 'data-draggable': true, + 'data-dragging': isDragging, + 'data-drag-active': isDragActive, + 'data-expanded': isDragActive && isParent, + }; + }, + [attributesProp, depth, isDragging, isDragActive, isParent], ); return ( { renderSortIndicator?: (props: SortIndicatorProps) => React.ReactNode; resizeHandleClassName?: string; sortIndicatorClassName?: string; + attributes?: + | React.TdHTMLAttributes + | (( + header: Header, + parentHeader?: Header, + ) => React.TdHTMLAttributes); } export const HeaderCell = ({ @@ -30,6 +36,7 @@ export const HeaderCell = ({ renderSortIndicator, resizeHandleClassName, sortIndicatorClassName, + attributes: attributesProp, }: HeaderCellProps) => { const className = React.useMemo(() => { return typeof classNameProp === 'function' @@ -53,6 +60,11 @@ export const HeaderCell = ({ const rowSpan = header.isPlaceholder ? header.getLeafHeaders().length : 1; + const attributes = + typeof attributesProp === 'function' + ? attributesProp(header, parentHeader) + : attributesProp; + return ( ({ rowSpan={rowSpan > 1 ? rowSpan : undefined} onClick={header.column.getToggleSortingHandler()} style={getCellStyles(header)} + {...attributes} > {flexRender(header.column.columnDef.header, header.getContext())}{' '} {header.column.getCanSort() && diff --git a/src/components/HeaderRow/HeaderRow.tsx b/src/components/HeaderRow/HeaderRow.tsx index 328c85f..c0d5c24 100644 --- a/src/components/HeaderRow/HeaderRow.tsx +++ b/src/components/HeaderRow/HeaderRow.tsx @@ -18,6 +18,13 @@ export interface HeaderRowProps { renderSortIndicator: HeaderCellProps['renderSortIndicator']; resizeHandleClassName?: HeaderCellProps['resizeHandleClassName']; sortIndicatorClassName: HeaderCellProps['sortIndicatorClassName']; + attributes?: + | React.HTMLAttributes + | (( + headerGroup: HeaderGroup, + parentHeaderGroup?: HeaderGroup, + ) => React.HTMLAttributes); + cellAttributes?: HeaderCellProps['attributes']; } export const HeaderRow = ({ @@ -29,6 +36,8 @@ export const HeaderRow = ({ renderSortIndicator, resizeHandleClassName, sortIndicatorClassName, + attributes: attributesProp, + cellAttributes, }: HeaderRowProps) => { const className = React.useMemo(() => { return typeof classNameProp === 'function' @@ -36,8 +45,13 @@ export const HeaderRow = ({ : classNameProp; }, [classNameProp, headerGroup, parentHeaderGroup]); + const attributes = + typeof attributesProp === 'function' + ? attributesProp(headerGroup, parentHeaderGroup) + : attributesProp; + return ( - + {headerGroup.headers.map((header) => ( ({ renderSortIndicator={renderSortIndicator} resizeHandleClassName={resizeHandleClassName} sortIndicatorClassName={sortIndicatorClassName} + attributes={cellAttributes} /> ))} diff --git a/src/components/Row/Row.tsx b/src/components/Row/Row.tsx index 2bf65d2..24a118d 100644 --- a/src/components/Row/Row.tsx +++ b/src/components/Row/Row.tsx @@ -16,9 +16,6 @@ export interface RowProps) => React.ReactNode; getIsCustomRow?: (row: RowType) => boolean; getIsGroupHeaderRow?: (row: RowType) => boolean; - getRowAttributes?: ( - row: RowType, - ) => React.TdHTMLAttributes | undefined; groupHeaderClassName?: string; onClick?: (row: RowType, event: React.MouseEvent) => void; renderCustomRowContent?: (props: { @@ -37,6 +34,10 @@ export interface RowProps; style?: React.CSSProperties; virtualItem?: VirtualItem; + attributes?: + | React.HTMLAttributes + | ((row: RowType) => React.HTMLAttributes); + cellAttributes?: CellProps['attributes']; } export const Row = React.forwardRef( @@ -47,7 +48,6 @@ export const Row = React.forwardRef( getGroupTitle, getIsCustomRow, getIsGroupHeaderRow, - getRowAttributes, groupHeaderClassName, onClick, renderCustomRowContent, @@ -57,6 +57,8 @@ export const Row = React.forwardRef( rowVirtualizer, style, virtualItem, + attributes: attributesProp, + cellAttributes, }: RowProps, ref: React.Ref, ) => { @@ -83,7 +85,11 @@ export const Row = React.forwardRef( row, }) ) : ( - + {renderGroupHeader ? ( renderGroupHeader({ className: b('group-header', groupHeaderClassName), @@ -107,9 +113,19 @@ export const Row = React.forwardRef( return row .getVisibleCells() - .map((cell) => ); + .map((cell) => ( + + )); }; + const attributes = + typeof attributesProp === 'function' ? attributesProp(row) : attributesProp; + return ( {renderRowContent()} diff --git a/src/components/Table/Table.tsx b/src/components/Table/Table.tsx index 7e1f81d..c30d0e8 100644 --- a/src/components/Table/Table.tsx +++ b/src/components/Table/Table.tsx @@ -28,7 +28,6 @@ export interface TableProps['getGroupTitle']; getIsCustomRow?: RowProps['getIsCustomRow']; getIsGroupHeaderRow?: RowProps['getIsGroupHeaderRow']; - getRowAttributes?: RowProps['getRowAttributes']; groupHeaderClassName?: RowProps['groupHeaderClassName']; headerCellClassName?: HeaderRowProps['cellClassName']; headerClassName?: string; @@ -48,6 +47,13 @@ export interface TableProps; withFooter?: boolean; withHeader?: boolean; + attributes?: React.TableHTMLAttributes; + headerAttributes?: React.HTMLAttributes; + headerRowAttributes?: HeaderRowProps['attributes']; + headerCellAttributes?: HeaderRowProps['cellAttributes']; + bodyAttributes?: React.HTMLAttributes; + rowAttributes?: RowProps['attributes']; + cellAttributes?: RowProps['cellAttributes']; } export const Table = React.forwardRef( @@ -62,7 +68,6 @@ export const Table = React.forwardRef( footerRowClassName, getGroupTitle, getIsGroupHeaderRow, - getRowAttributes, headerCellClassName, headerClassName, headerRowClassName, @@ -80,6 +85,13 @@ export const Table = React.forwardRef( table, withFooter, withHeader = true, + attributes, + headerAttributes, + headerRowAttributes, + headerCellAttributes, + bodyAttributes, + rowAttributes, + cellAttributes, }: TableProps, ref: React.Ref, ) => { @@ -104,9 +116,13 @@ export const Table = React.forwardRef( ref={ref} className={b({'with-row-virtualization': Boolean(rowVirtualizer)}, className)} data-dragging-row-index={draggingRowIndex > -1 ? draggingRowIndex : undefined} + {...attributes} > {headerGroups && ( - + {headerGroups.map((headerGroup, index) => ( ))} @@ -127,6 +145,7 @@ export const Table = React.forwardRef( style={{ height: rowVirtualizer?.getTotalSize(), }} + {...bodyAttributes} > {(rowVirtualizer?.getVirtualItems() || rows).map((virtualItemOrRow) => { const row = rowVirtualizer @@ -138,7 +157,8 @@ export const Table = React.forwardRef( className: rowClassName, getGroupTitle, getIsGroupHeaderRow, - getRowAttributes, + attributes: rowAttributes, + cellAttributes, onClick: onRowClick, renderGroupHeader, renderGroupHeaderRowContent,