TableHeaderRow.tsx 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. /* eslint-disable eqeqeq */
  2. import React from 'react';
  3. import classnames from 'classnames';
  4. import PropTypes from 'prop-types';
  5. import { get, noop, map, set, omit, findIndex } from 'lodash';
  6. import { cssClasses } from '@douyinfe/semi-foundation/table/constants';
  7. import {
  8. arrayAdd,
  9. isFirstFixedRight,
  10. isLastLeftFixed,
  11. isFixedLeft,
  12. isFixedRight,
  13. sliceColumnsByLevel
  14. } from '@douyinfe/semi-foundation/table/utils';
  15. import BaseComponent from '../_base/baseComponent';
  16. import TableContext, { TableContextProps } from './table-context';
  17. import { TableComponents, OnHeaderRow, Fixed } from './interface';
  18. export interface TableHeaderRowProps {
  19. components?: TableComponents;
  20. row?: any[];
  21. prefixCls?: string;
  22. onHeaderRow?: OnHeaderRow<any>;
  23. index?: number;
  24. style?: React.CSSProperties;
  25. columns?: any[];
  26. fixed?: Fixed;
  27. selectedRowKeysSet: Set<any>;
  28. }
  29. export default class TableHeaderRow extends BaseComponent<TableHeaderRowProps, Record<string, any>> {
  30. static contextType = TableContext;
  31. static propTypes = {
  32. components: PropTypes.object,
  33. row: PropTypes.array,
  34. prefixCls: PropTypes.string,
  35. onHeaderRow: PropTypes.func,
  36. index: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  37. style: PropTypes.object,
  38. columns: PropTypes.array,
  39. fixed: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
  40. selectedRowKeysSet: PropTypes.instanceOf(Set).isRequired,
  41. };
  42. static defaultProps = {
  43. onHeaderRow: noop,
  44. prefixCls: cssClasses.PREFIX,
  45. columns: [] as [],
  46. components: {
  47. header: {
  48. wrapper: 'thead',
  49. row: 'tr',
  50. cell: 'th',
  51. },
  52. },
  53. };
  54. get adapter() {
  55. return {
  56. ...super.adapter,
  57. };
  58. }
  59. headerNode: HTMLElement;
  60. context: TableContextProps;
  61. constructor(props: TableHeaderRowProps) {
  62. super(props);
  63. this.headerNode = null;
  64. }
  65. cacheRef = (node: HTMLElement) => {
  66. this.headerNode = node;
  67. if (node && this.context.setHeadWidths) {
  68. const { prefixCls, row, index } = this.props;
  69. const cellSelector = `.${prefixCls}-row-head`;
  70. const heads = node && node.querySelectorAll && node.querySelectorAll(cellSelector);
  71. this.context.setHeadWidths(
  72. map(heads, (head, headIndex) => {
  73. let configWidth = get(row, [headIndex, 'column', 'width']);
  74. const key = get(row, [headIndex, 'column', 'key']);
  75. if (typeof configWidth !== 'number') {
  76. configWidth = (head && head.getBoundingClientRect().width) || 0;
  77. }
  78. return { width: configWidth, key };
  79. }),
  80. index
  81. );
  82. }
  83. };
  84. componentDidUpdate(prevProps: TableHeaderRowProps) {
  85. if (prevProps.columns !== this.props.columns && this.headerNode) {
  86. this.cacheRef(this.headerNode);
  87. }
  88. }
  89. render() {
  90. const { components, row, prefixCls, onHeaderRow, index, style, columns } = this.props;
  91. const { getCellWidths } = this.context;
  92. const slicedColumns = sliceColumnsByLevel(columns, index);
  93. const headWidths = getCellWidths(slicedColumns);
  94. const HeaderRow = get(components, 'header.row', 'tr');
  95. const HeaderCell = get(components, 'header.cell', 'th');
  96. const rowProps = onHeaderRow(columns, index) || {};
  97. set(rowProps, 'className', classnames(get(rowProps, 'className'), `${prefixCls}-row`));
  98. const cells = map(row, (cell, cellIndex) => {
  99. const { column, ...cellProps } = cell;
  100. const customProps =
  101. typeof column.onHeaderCell === 'function' ? column.onHeaderCell(column, cellIndex, index) : {};
  102. let cellStyle = { ...customProps.style };
  103. if (column.align) {
  104. cellStyle = { ...cellStyle, textAlign: column.align };
  105. customProps.className = classnames(customProps.className, column.className, {
  106. [`${prefixCls}-align-${column.align}`]: Boolean(column.align),
  107. });
  108. }
  109. customProps.className = classnames(
  110. `${prefixCls}-row-head`,
  111. column.className,
  112. customProps.className,
  113. // `${prefixCls}-fixed-columns`,
  114. {
  115. [`${prefixCls}-cell-fixed-left`]: isFixedLeft(column),
  116. [`${prefixCls}-cell-fixed-left-last`]: isLastLeftFixed(slicedColumns, column),
  117. [`${prefixCls}-cell-fixed-right`]: isFixedRight(column),
  118. [`${prefixCls}-cell-fixed-right-first`]: isFirstFixedRight(slicedColumns, column),
  119. }
  120. );
  121. if (headWidths.length && slicedColumns.length) {
  122. const indexOfSlicedColumns = findIndex(
  123. slicedColumns,
  124. item => item && item.key != null && item.key === column.key
  125. );
  126. if (indexOfSlicedColumns > -1) {
  127. if (isFixedLeft(column)) {
  128. cellStyle = {
  129. ...cellStyle,
  130. position: 'sticky',
  131. left: arrayAdd(headWidths, 0, indexOfSlicedColumns),
  132. };
  133. } else if (isFixedRight(column)) {
  134. cellStyle = {
  135. ...cellStyle,
  136. position: 'sticky',
  137. right: arrayAdd(headWidths, indexOfSlicedColumns + 1),
  138. };
  139. }
  140. }
  141. }
  142. const props = omit({ ...cellProps, ...customProps }, [
  143. 'colStart',
  144. 'colEnd',
  145. 'hasSubColumns',
  146. 'parents',
  147. 'level',
  148. ]);
  149. const { rowSpan, colSpan } = props;
  150. if (rowSpan === 0 || colSpan === 0) {
  151. return null;
  152. }
  153. return (
  154. <HeaderCell
  155. role="columnheader"
  156. aria-colindex={cellIndex + 1}
  157. {...props}
  158. style={cellStyle}
  159. key={column.key || column.dataIndex || cellIndex}
  160. />
  161. );
  162. });
  163. return (
  164. <HeaderRow
  165. role="row"
  166. aria-rowindex={index + 1}
  167. {...rowProps}
  168. style={style}
  169. ref={this.cacheRef}
  170. >
  171. {cells}
  172. </HeaderRow>
  173. );
  174. }
  175. }