TableHeaderRow.tsx 7.7 KB

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