uui/components/tables/DataTableHeaderCell.tsx (230 lines of code) (raw):
import * as React from 'react';
import {
cx, DataColumnProps, DataTableHeaderCellProps, IDropdownTogglerProps, Overwrite, uuiDataTableHeaderCell, uuiMarkers,
} from '@epam/uui-core';
import {
DataTableCellContainer,
DataTableHeaderCell as UuiDataTableHeaderCell,
HeaderCellContentProps,
} from '@epam/uui-components';
import { ColumnHeaderDropdown } from './ColumnHeaderDropdown';
import { DataTableHeaderCellMods } from './types';
import { IconButton } from '../buttons';
import { Checkbox, CheckboxProps } from '../inputs';
import { Tooltip } from '../overlays';
import { Text, TextProps } from '../typography';
import { ReactComponent as DefaultSortIcon } from '@epam/assets/icons/table-swap-outline.svg';
import { ReactComponent as SortIcon } from '@epam/assets/icons/table-sort_asc-outline.svg';
import { ReactComponent as SortIconDesc } from '@epam/assets/icons/table-sort_desc-outline.svg';
import { ReactComponent as FilterIcon } from '@epam/assets/icons/content-filtration-fill.svg';
import { ReactComponent as DropdownIcon } from '@epam/assets/icons/navigation-chevron_down-outline.svg';
import { ReactComponent as OpenedDropdownIcon } from '@epam/assets/icons/navigation-chevron_up-outline.svg';
import { ReactComponent as FoldIcon } from '@epam/assets/icons/navigation-collapse_all-outline.svg';
import { ReactComponent as UnfoldIcon } from '@epam/assets/icons/navigation-expand_all-outline.svg';
import { i18n } from '../../i18n';
import { settings } from '../../settings';
import './variables.scss';
import css from './DataTableHeaderCell.module.scss';
interface DataTableHeaderCellState {
isDropdownOpen: boolean;
}
export interface DataTableHeaderCellModsOverride {
}
export class DataTableHeaderCell<TItem, TId> extends
React.Component<
DataTableHeaderCellProps<TItem, TId> & Overwrite<DataTableHeaderCellMods,
DataTableHeaderCellModsOverride>,
DataTableHeaderCellState
> {
state: DataTableHeaderCellState = {
isDropdownOpen: null,
};
getTooltipContent = (column: DataColumnProps<TItem, TId>) => (
<div className={ cx(css.cellTooltipWrapper, uuiDataTableHeaderCell.uuiTableHeaderCaptionTooltip) }>
<Text cx={ [css.cellTooltipText, css.tooltipCaption] }>
{ column.caption }
</Text>
{ column.info && (
<Text cx={ [css.cellTooltipText, css.tooltipInfo] }>
{ column.info }
</Text>
) }
</div>
);
getColumnCaption = () => {
const renderTooltip = this.props.column.renderTooltip || this.getTooltipContent;
const captionCx = [
css.caption,
this.props.textCase === 'upper' && css.upperCase,
uuiDataTableHeaderCell.uuiTableHeaderCaption,
settings.sizes.dataTable.header.row.cell.truncate.includes(this.props.size) && css.truncate,
];
return (
<div
className={ cx(css.captionWrapper, css['align-' + this.props.column.textAlign], uuiDataTableHeaderCell.uuiTableHeaderCaptionWrapper) }
>
<Tooltip
placement="top"
color="inverted"
content={ renderTooltip(this.props.column) }
cx={ css.cellTooltip }
openDelay={ 600 }
>
<Text
key="text"
fontSize={ settings.sizes.dataTable.header.row.cell.columnCaption[this.props.textCase === 'upper' ? 'uppercase' : 'fontSize'] as TextProps['fontSize'] }
size={ settings.sizes.dataTable.header.row.cell.columnCaption.size as TextProps['size'] }
cx={ captionCx }
>
{ this.props.column.caption }
</Text>
</Tooltip>
{ this.props.column.isSortable && (!this.props.column.renderFilter || this.props.sortDirection) && (
<IconButton
key="sort"
cx={ cx(css.icon, css.sortIcon, this.props.sortDirection && css.sortIconActive, uuiDataTableHeaderCell.uuiTableHeaderSortIcon) }
color={ this.props.sortDirection ? 'neutral' : 'secondary' }
icon={ this.props.sortDirection === 'desc' ? SortIconDesc : this.props.sortDirection === 'asc' ? SortIcon : DefaultSortIcon }
/>
) }
{ this.props.isFilterActive && (
<IconButton
key="filter"
cx={ cx(css.icon, !this.props.sortDirection && css.filterIcon, uuiDataTableHeaderCell.uuiTableHeaderFilterIcon) }
color="neutral"
icon={ FilterIcon }
/>
) }
{ this.props.column.renderFilter && (
<IconButton
key="dropdown"
cx={ cx(css.icon, css.dropdownIcon, uuiDataTableHeaderCell.uuiTableHeaderDropdownIcon) }
color="secondary"
icon={ this.state.isDropdownOpen ? OpenedDropdownIcon : DropdownIcon }
/>
) }
</div>
);
};
renderHeaderCheckbox = () => {
if (this.props.selectAll && this.props.isFirstColumn) {
return (
<Checkbox
size={ settings.sizes.dataTable.header.row.cell.checkbox[this.props.size] as CheckboxProps['size'] }
{ ...this.props.selectAll }
cx={ cx(css.checkbox, uuiDataTableHeaderCell.uuiTableHeaderCheckbox) }
/>
);
}
};
renderFoldAllIcon = () => {
if (this.props.isFirstColumn && this.props.showFoldAll) {
return (
<Tooltip content={
this.props.areAllFolded
? i18n.tables.columnHeader.expandAllTooltip
: i18n.tables.columnHeader.collapseAllTooltip
}
>
<IconButton
color="secondary"
cx={ cx(css.icon, css.foldAllIcon, uuiDataTableHeaderCell.uuiTableHeaderFoldAllIcon) }
icon={ this.props.areAllFolded ? UnfoldIcon : FoldIcon }
onClick={ this.props.onFoldAll }
rawProps={ {
'aria-label': this.props.areAllFolded ? 'Expand All' : 'Collapse All',
'aria-expanded': !!this.props.areAllFolded,
} }
/>
</Tooltip>
);
}
};
renderResizingMarker = (props: HeaderCellContentProps) => {
return (
<div
role="separator"
onMouseDown={ props.onResizeStart }
className={ cx(css.resizingMarker, uuiMarkers.draggable, uuiMarkers.clickable) }
/>
);
};
getLeftPadding = () => {
const { columnsGap, isFirstColumn } = this.props;
if (columnsGap) return isFirstColumn ? columnsGap : +columnsGap / 2;
return isFirstColumn ? settings.sizes.dataTable.header.row.cell.defaults.paddingEdge : settings.sizes.dataTable.header.row.cell.defaults.padding;
};
getRightPadding = () => {
const { columnsGap, isLastColumn } = this.props;
if (columnsGap) return isLastColumn ? columnsGap : +columnsGap / 2;
return isLastColumn ? settings.sizes.dataTable.header.row.cell.defaults.paddingEdge : settings.sizes.dataTable.header.row.cell.defaults.padding;
};
getResizingMarkerWidth = () => {
const { columnsGap } = this.props;
return columnsGap ? +columnsGap / 2 : settings.sizes.dataTable.header.row.cell.defaults.resizeMarker;
};
renderCellContent = (props: HeaderCellContentProps, dropdownProps?: IDropdownTogglerProps) => {
const isResizable = this.props.column.allowResizing ?? this.props.allowColumnsResizing;
const onClickEvent = !props.isResizing && (!this.props.column.renderFilter ? props.toggleSort : dropdownProps?.onClick);
const computeStyles = {
'--uui-dt-header-cell-icon-size': `${settings.sizes.dataTable.header.row.cell.iconSize[this.props.size || settings.sizes.dataTable.header.row.cell.defaults.size]}px`,
'--uui-dt-header-cell-padding-start': `${this.getLeftPadding()}px`,
'--uui-dt-header-cell-padding-end': `${this.getRightPadding()}px`,
'--uui-dt-header-cell-resizing-marker-width': `${this.getResizingMarkerWidth()}px`,
} as React.CSSProperties;
return (
<DataTableCellContainer
column={ this.props.column }
ref={ (ref) => {
(props.ref as React.RefCallback<HTMLElement>)(ref);
(dropdownProps?.ref as React.RefCallback<HTMLElement>)?.(ref);
} }
cx={ cx(
uuiDataTableHeaderCell.uuiTableHeaderCell,
(this.props.column.isSortable || this.props.isDropdown) && uuiMarkers.clickable,
css.root,
`uui-size-${this.props.size || settings.sizes.dataTable.header.row.cell.defaults.size}`,
this.props.isFirstColumn && 'uui-dt-header-first-column',
this.props.isLastColumn && 'uui-dt-header-last-column',
this.props.column.fix && css['pinned-' + this.props.column.fix],
isResizable && css.resizable,
props.isDraggable && css.draggable,
props.isDragGhost && css.ghost,
props.isDraggedOut && css.isDraggedOut,
props.isDndInProgress && css['dnd-marker-' + props.position],
) }
onClick={ onClickEvent }
rawProps={ {
role: 'columnheader',
'aria-sort': this.props.sortDirection === 'asc' ? 'ascending' : this.props.sortDirection ? 'descending' : 'none',
...props.eventHandlers,
} }
style={ computeStyles }
>
{ this.renderHeaderCheckbox() }
{ this.renderFoldAllIcon() }
{ this.getColumnCaption() }
{ isResizable && this.renderResizingMarker(props) }
</DataTableCellContainer>
);
};
renderCellWithFilter = (props: HeaderCellContentProps) => (
<ColumnHeaderDropdown
isOpen={ this.state.isDropdownOpen }
isSortable={ this.props.column.isSortable }
renderTarget={ (dropdownProps) => this.renderCellContent(props, dropdownProps) }
renderFilter={ this.props.renderFilter }
onSort={ this.props.onSort }
sortDirection={ this.props.sortDirection }
onOpenChange={ (isDropdownOpen) => this.setState({ isDropdownOpen }) }
title={ this.props.column.caption as string }
/>
);
render() {
if (this.props.column.renderHeaderCell) {
return this.props.column.renderHeaderCell(this.props);
}
return (
<UuiDataTableHeaderCell
{ ...this.props }
renderCellContent={ this.props.column.renderFilter ? this.renderCellWithFilter : this.renderCellContent }
/>
);
}
}