diff --git a/src/components/HeaderCell/HeaderCell.tsx b/src/components/HeaderCell/HeaderCell.tsx index 8675d91..7737548 100644 --- a/src/components/HeaderCell/HeaderCell.tsx +++ b/src/components/HeaderCell/HeaderCell.tsx @@ -10,6 +10,7 @@ export interface HeaderCellProps { className?: string; contentClassName?: string; header: Header; + parentHeader?: Header; renderSortIndicator?: (header: Header, className?: string) => React.ReactNode; selection?: boolean; sortIndicatorClassName?: string; @@ -19,6 +20,7 @@ export const HeaderCell = ({ className, contentClassName, header, + parentHeader, renderSortIndicator = renderDefaultSortIndicator, selection, sortIndicatorClassName, @@ -27,6 +29,23 @@ export const HeaderCell = ({ const {columnSizingInfo} = table.getState(); const {columnResizeDirection, columnResizeMode, enableColumnResizing} = table.options; + const columnRelativeDepth = header.depth - header.column.depth; + + if (!header.isPlaceholder && header.id === header.column.id && columnRelativeDepth > 1) { + return null; + } + + let rowSpan = 1; + + if (header.isPlaceholder) { + if (parentHeader?.isPlaceholder && parentHeader.placeholderId === header.placeholderId) { + return null; + } + + const leafs = header.getLeafHeaders(); + rowSpan = leafs.length ?? 1; + } + return ( ({ id: header.column.id, placeholder: header.isPlaceholder, sortable: header.column.getCanSort(), + wide: header.colSpan > 1, }, className, )} @@ -44,36 +64,34 @@ export const HeaderCell = ({ maxWidth: header.column.columnDef.maxSize, }} colSpan={header.colSpan} + rowSpan={rowSpan} onClick={header.column.getToggleSortingHandler()} > - {header.isPlaceholder ? null : ( -
- {flexRender(header.column.columnDef.header, header.getContext())}{' '} - {header.column.getCanSort() && - renderSortIndicator(header, sortIndicatorClassName)} - {enableColumnResizing && ( - // eslint-disable-next-line jsx-a11y/no-static-element-interactions -
header.column.resetSize()} - onMouseDown={header.getResizeHandler()} - onTouchStart={header.getResizeHandler()} - style={{ - transform: - columnResizeMode === 'onEnd' && header.column.getIsResizing() - ? `translateX(${ - (columnResizeDirection === 'rtl' ? -1 : 1) * - (columnSizingInfo.deltaOffset ?? 0) - }px)` - : '', - }} - /> - )} -
- )} +
+ {flexRender(header.column.columnDef.header, header.getContext())}{' '} + {header.column.getCanSort() && renderSortIndicator(header, sortIndicatorClassName)} + {enableColumnResizing && ( + // eslint-disable-next-line jsx-a11y/no-static-element-interactions +
header.column.resetSize()} + onMouseDown={header.getResizeHandler()} + onTouchStart={header.getResizeHandler()} + style={{ + transform: + columnResizeMode === 'onEnd' && header.column.getIsResizing() + ? `translateX(${ + (columnResizeDirection === 'rtl' ? -1 : 1) * + (columnSizingInfo.deltaOffset ?? 0) + }px)` + : '', + }} + /> + )} +
); }; diff --git a/src/components/HeaderRow/HeaderRow.tsx b/src/components/HeaderRow/HeaderRow.tsx index 48f75dc..a788adf 100644 --- a/src/components/HeaderRow/HeaderRow.tsx +++ b/src/components/HeaderRow/HeaderRow.tsx @@ -11,6 +11,7 @@ export interface HeaderRowProps { cellContentClassName: HeaderCellProps['contentClassName']; className?: string; headerGroup: HeaderGroup; + parentHeaderGroup?: HeaderGroup; renderSortIndicator: HeaderCellProps['renderSortIndicator']; selectionColumnId?: string; sortIndicatorClassName: HeaderCellProps['sortIndicatorClassName']; @@ -21,6 +22,7 @@ export const HeaderRow = ({ cellContentClassName, className, headerGroup, + parentHeaderGroup, renderSortIndicator, selectionColumnId, sortIndicatorClassName, @@ -33,6 +35,9 @@ export const HeaderRow = ({ className={cellClassName} contentClassName={cellContentClassName} header={header as Header} + parentHeader={parentHeaderGroup?.headers.find( + (item) => header.column.id === item.column.id, + )} renderSortIndicator={renderSortIndicator} selection={header.column.id === selectionColumnId} sortIndicatorClassName={sortIndicatorClassName} diff --git a/src/components/Table/Table.scss b/src/components/Table/Table.scss index ee64d8c..c233051 100644 --- a/src/components/Table/Table.scss +++ b/src/components/Table/Table.scss @@ -69,6 +69,12 @@ $block: '.#{variables.$ns}table'; opacity: 1; } } + + &_wide { + #{$block}__header-cell-content { + justify-content: center; + } + } } &__header-cell-content, diff --git a/src/components/Table/Table.tsx b/src/components/Table/Table.tsx index 07788c2..005a55a 100644 --- a/src/components/Table/Table.tsx +++ b/src/components/Table/Table.tsx @@ -258,6 +258,8 @@ export const Table = React.memo( const {rows} = table.getRowModel(); + const headerGroups = table.getHeaderGroups(); + return ( {withHeader && ( - {table.getHeaderGroups().map((headerGroup) => ( + {headerGroups.map((headerGroup, index) => ( > = Template.bind({}); + +HeaderGroups.args = { + data, + columns: [ + { + id: 'id', + header: 'ID', + accessorKey: 'id', + }, + { + id: 'columns-parent', + header: 'Columns header', + columns: [ + { + id: 'columns', + header: 'Columns', + columns, + }, + ], + }, + { + id: 'actions', + header: 'Actions', + accessorKey: 'id', + columns: [ + { + id: 'edit', + header: 'Edit', + accessorKey: 'id', + }, + { + id: 'delete', + header: 'Delete', + accessorKey: 'id', + }, + ], + }, + ], + getRowId: (item: Item) => item.id, + onSelectedChange: undefined, + onRowClick: undefined, + className: cnGridDemo('header-groups-grid'), +}; + const WithSelectionTemplate: StoryFn> = (args) => ( ); diff --git a/src/components/__stories__/GridDemo.classname.ts b/src/components/__stories__/GridDemo.classname.ts new file mode 100644 index 0000000..36ba8ec --- /dev/null +++ b/src/components/__stories__/GridDemo.classname.ts @@ -0,0 +1,3 @@ +import {cn} from '@bem-react/classname'; + +export const cnGridDemo = cn('grid-demo'); diff --git a/src/components/__stories__/GridDemo.scss b/src/components/__stories__/GridDemo.scss new file mode 100644 index 0000000..259c8c6 --- /dev/null +++ b/src/components/__stories__/GridDemo.scss @@ -0,0 +1,10 @@ +.grid-demo { + &-header-groups-grid { + border-collapse: collapse; + + th, + td { + border: 1px solid var(--g-color-line-generic); + } + } +}