TableHeaderRow.tsx 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  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 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. constructor(props: TableHeaderRowProps) {
  61. super(props);
  62. this.headerNode = null;
  63. }
  64. cacheRef = (node: HTMLElement) => {
  65. this.headerNode = node;
  66. if (node && this.context.setHeadWidths) {
  67. const { prefixCls, row, index } = this.props;
  68. const cellSelector = `.${prefixCls}-row-head`;
  69. const heads = node && node.querySelectorAll && node.querySelectorAll(cellSelector);
  70. this.context.setHeadWidths(
  71. map(heads, (head, headIndex) => {
  72. let configWidth = get(row, [headIndex, 'column', 'width']);
  73. const key = get(row, [headIndex, 'column', 'key']);
  74. if (typeof configWidth !== 'number') {
  75. configWidth = (head && head.getBoundingClientRect().width) || 0;
  76. }
  77. return { width: configWidth, key };
  78. }),
  79. index
  80. );
  81. }
  82. };
  83. componentDidUpdate(prevProps: TableHeaderRowProps) {
  84. if (prevProps.columns !== this.props.columns && this.headerNode) {
  85. this.cacheRef(this.headerNode);
  86. }
  87. }
  88. render() {
  89. const { components, row, prefixCls, onHeaderRow, index, style, columns } = this.props;
  90. const { getCellWidths } = this.context;
  91. const slicedColumns = sliceColumnsByLevel(columns, index);
  92. const headWidths = getCellWidths(slicedColumns);
  93. const HeaderRow = get(components, 'header.row', 'tr');
  94. const HeaderCell = get(components, 'header.cell', 'th');
  95. const rowProps = onHeaderRow(columns, index) || {};
  96. set(rowProps, 'className', classnames(get(rowProps, 'className'), `${prefixCls}-row`));
  97. const cells = map(row, (cell, cellIndex) => {
  98. const { column, ...cellProps } = cell;
  99. const customProps =
  100. typeof column.onHeaderCell === 'function' ? column.onHeaderCell(column, cellIndex, index) : {};
  101. let cellStyle = { ...customProps.style };
  102. if (column.align) {
  103. cellStyle = { ...cellStyle, textAlign: column.align };
  104. customProps.className = classnames(customProps.className, column.className, {
  105. [`${prefixCls}-align-${column.align}`]: Boolean(column.align),
  106. });
  107. }
  108. customProps.className = classnames(
  109. `${prefixCls}-row-head`,
  110. column.className,
  111. customProps.className,
  112. // `${prefixCls}-fixed-columns`,
  113. {
  114. [`${prefixCls}-cell-fixed-left`]: isFixedLeft(column),
  115. [`${prefixCls}-cell-fixed-left-last`]: isLastLeftFixed(slicedColumns, column),
  116. [`${prefixCls}-cell-fixed-right`]: isFixedRight(column),
  117. [`${prefixCls}-cell-fixed-right-first`]: isFirstFixedRight(slicedColumns, column),
  118. }
  119. );
  120. if (headWidths.length && slicedColumns.length) {
  121. const indexOfSlicedColumns = findIndex(
  122. slicedColumns,
  123. item => item && item.key != null && item.key === column.key
  124. );
  125. if (indexOfSlicedColumns > -1) {
  126. if (isFixedLeft(column)) {
  127. cellStyle = {
  128. ...cellStyle,
  129. position: 'sticky',
  130. left: arrayAdd(headWidths, 0, indexOfSlicedColumns),
  131. };
  132. } else if (isFixedRight(column)) {
  133. cellStyle = {
  134. ...cellStyle,
  135. position: 'sticky',
  136. right: arrayAdd(headWidths, indexOfSlicedColumns + 1),
  137. };
  138. }
  139. }
  140. }
  141. const props = omit({ ...cellProps, ...customProps }, [
  142. 'colStart',
  143. 'colEnd',
  144. 'hasSubColumns',
  145. 'parents',
  146. 'level',
  147. ]);
  148. const { rowSpan, colSpan } = props;
  149. if (rowSpan === 0 || colSpan === 0) {
  150. return null;
  151. }
  152. return (
  153. <HeaderCell
  154. role="columnheader"
  155. aria-colindex={cellIndex + 1}
  156. {...props}
  157. style={cellStyle}
  158. key={column.key || column.dataIndex || cellIndex}
  159. />
  160. );
  161. });
  162. return (
  163. <HeaderRow
  164. role="row"
  165. aria-rowindex={index + 1}
  166. {...rowProps}
  167. style={style}
  168. ref={this.cacheRef}
  169. >
  170. {cells}
  171. </HeaderRow>
  172. );
  173. }
  174. }