From 484954c5baa636b4eebb3ce7db9624fe3f397ec7 Mon Sep 17 00:00:00 2001 From: Dmitry Artemov Date: Wed, 24 Jul 2024 11:16:42 +0200 Subject: [PATCH] feat(Table): footer (#22) --- src/components/FooterCell/FooterCell.tsx | 31 ++++++++++++++++++++++ src/components/FooterCell/index.ts | 1 + src/components/FooterRow/FooterRow.tsx | 33 ++++++++++++++++++++++++ src/components/FooterRow/index.ts | 1 + src/components/Table/Table.scss | 29 ++++++++++++++------- src/components/Table/Table.tsx | 30 ++++++++++++++++++--- src/components/index.ts | 1 + 7 files changed, 114 insertions(+), 12 deletions(-) create mode 100644 src/components/FooterCell/FooterCell.tsx create mode 100644 src/components/FooterCell/index.ts create mode 100644 src/components/FooterRow/FooterRow.tsx create mode 100644 src/components/FooterRow/index.ts diff --git a/src/components/FooterCell/FooterCell.tsx b/src/components/FooterCell/FooterCell.tsx new file mode 100644 index 0000000..18c5af9 --- /dev/null +++ b/src/components/FooterCell/FooterCell.tsx @@ -0,0 +1,31 @@ +import React from 'react'; + +import type {Header} from '@tanstack/react-table'; +import {flexRender} from '@tanstack/react-table'; + +import {getCellStyles, getHeaderCellClassModes} from '../../utils'; +import {b} from '../Table/Table.classname'; + +export interface FooterCellProps { + className?: string; + header: Header; +} + +export const FooterCell = ({className, header}: FooterCellProps) => { + if (header.isPlaceholder) { + return null; + } + + const rowSpan = header.depth - header.column.depth; + + return ( + 1 ? header.colSpan : undefined} + rowSpan={rowSpan > 1 ? rowSpan : undefined} + style={getCellStyles(header)} + > + {flexRender(header.column.columnDef.footer, header.getContext())} + + ); +}; diff --git a/src/components/FooterCell/index.ts b/src/components/FooterCell/index.ts new file mode 100644 index 0000000..5b3a526 --- /dev/null +++ b/src/components/FooterCell/index.ts @@ -0,0 +1 @@ +export * from './FooterCell'; diff --git a/src/components/FooterRow/FooterRow.tsx b/src/components/FooterRow/FooterRow.tsx new file mode 100644 index 0000000..59d8d07 --- /dev/null +++ b/src/components/FooterRow/FooterRow.tsx @@ -0,0 +1,33 @@ +import React from 'react'; + +import type {HeaderGroup} from '@tanstack/react-table'; + +import type {FooterCellProps} from '../FooterCell'; +import {FooterCell} from '../FooterCell'; +import {b} from '../Table/Table.classname'; + +export interface FooterRowProps { + cellClassName: FooterCellProps['className']; + className?: string; + footerGroup: HeaderGroup; +} + +export const FooterRow = ({ + cellClassName, + className, + footerGroup, +}: FooterRowProps) => { + const isEmptyRow = footerGroup.headers.every((header) => !header.column.columnDef.footer); + + if (isEmptyRow) { + return null; + } + + return ( + + {footerGroup.headers.map((header) => ( + + ))} + + ); +}; diff --git a/src/components/FooterRow/index.ts b/src/components/FooterRow/index.ts new file mode 100644 index 0000000..ffb2cbf --- /dev/null +++ b/src/components/FooterRow/index.ts @@ -0,0 +1 @@ +export * from './FooterRow'; diff --git a/src/components/Table/Table.scss b/src/components/Table/Table.scss index e98527e..beb4e60 100644 --- a/src/components/Table/Table.scss +++ b/src/components/Table/Table.scss @@ -25,8 +25,17 @@ $block: '.#{variables.$ns}table'; } } + &__footer { + &_sticky { + position: sticky; + inset-block-end: 0; + z-index: 1; + } + } + &__cell, - &__header-cell { + &__header-cell, + &__footer-cell { height: inherit; text-align: start; @@ -36,11 +45,14 @@ $block: '.#{variables.$ns}table'; font-weight: normal; } - &__header-cell { + &__header-cell, + &__footer-cell { position: relative; font-weight: 500; + } + &__header-cell { &_sortable { cursor: pointer; user-select: none; @@ -60,15 +72,13 @@ $block: '.#{variables.$ns}table'; position: relative; } - &__header { + &__header, + &__footer { display: grid; - - inset-block-start: 0; - - z-index: 1; } - &__header-row { + &__header-row, + &__footer-row { display: flex; width: 100%; @@ -85,7 +95,8 @@ $block: '.#{variables.$ns}table'; } &__cell, - &__header-cell { + &__header-cell, + &__footer-cell { display: flex; } } diff --git a/src/components/Table/Table.tsx b/src/components/Table/Table.tsx index da7b1f6..c8992cb 100644 --- a/src/components/Table/Table.tsx +++ b/src/components/Table/Table.tsx @@ -4,6 +4,7 @@ import type {Row as RowType, Table as TableType} from '@tanstack/react-table'; import type {VirtualItem, Virtualizer} from '@tanstack/react-virtual'; import {DraggableRow} from '../DraggableRow'; +import {FooterRow} from '../FooterRow'; import type {HeaderRowProps} from '../HeaderRow'; import {HeaderRow} from '../HeaderRow'; import {Row} from '../Row'; @@ -21,6 +22,9 @@ export interface TableProps['getGroupTitle']; getIsCustomRow?: RowProps['getIsCustomRow']; getIsGroupHeaderRow?: RowProps['getIsGroupHeaderRow']; @@ -39,8 +43,10 @@ export interface TableProps; sortIndicatorClassName?: HeaderRowProps['sortIndicatorClassName']; + stickyFooter?: boolean; stickyHeader?: boolean; table: TableType; + withFooter?: boolean; withHeader?: boolean; } @@ -51,6 +57,9 @@ export const Table = React.forwardRef( cellClassName, className, enableNesting, + footerCellClassName, + footerClassName, + footerRowClassName, getGroupTitle, getIsGroupHeaderRow, getRowAttributes, @@ -66,8 +75,10 @@ export const Table = React.forwardRef( rowClassName, rowVirtualizer, sortIndicatorClassName, - stickyHeader = false, + stickyFooter, + stickyHeader, table, + withFooter, withHeader = true, }: TableProps, ref: React.Ref, @@ -84,7 +95,8 @@ export const Table = React.forwardRef( const {rows} = table.getRowModel(); - const headerGroups = table.getHeaderGroups(); + const headerGroups = withHeader && table.getHeaderGroups(); + const footerGroups = withFooter && table.getFooterGroups(); return ( @@ -93,7 +105,7 @@ export const Table = React.forwardRef( className={b({'with-row-virtualization': Boolean(rowVirtualizer)}, className)} data-dragging-row-index={draggingRowIndex > -1 ? draggingRowIndex : undefined} > - {withHeader && ( + {headerGroups && ( {headerGroups.map((headerGroup, index) => ( ; })} + {footerGroups && ( + + {footerGroups.map((footerGroup) => ( + + ))} + + )} ); diff --git a/src/components/index.ts b/src/components/index.ts index 054394f..81efc03 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -2,6 +2,7 @@ export * from './Cell'; export * from './DragHandle'; export * from './DraggableRow'; export * from './DraggableRowMarker'; +export * from './FooterCell'; export * from './GroupHeader'; export * from './HeaderCell'; export * from './HeaderRow';