@douyinfe/semi-ui
Version:
A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.
331 lines • 11.5 kB
JavaScript
import _pick from "lodash/pick";
import _isEqual from "lodash/isEqual";
import _omit from "lodash/omit";
import _stubTrue from "lodash/stubTrue";
import _get from "lodash/get";
import _noop from "lodash/noop";
import _each from "lodash/each";
var __rest = this && this.__rest || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
}
return t;
};
import React from 'react';
import classnames from 'classnames';
import PropTypes from 'prop-types';
import { strings, cssClasses } from '@douyinfe/semi-foundation/lib/es/table/constants';
import shallowEqualObjects from '@douyinfe/semi-foundation/lib/es/utils/shallowEqualObjects';
import TableRowFoundation from '@douyinfe/semi-foundation/lib/es/table/tableRowFoundation';
import { isLastLeftFixed, arrayAdd, isFixedLeft, isFixedRight, isScrollbarColumn, isFirstFixedRight, isInnerColumnKey, isExpandedColumn } from '@douyinfe/semi-foundation/lib/es/table/utils';
import BaseComponent from '../../_base/baseComponent';
import TableCell from '../TableCell';
/**
* avoid affected by https://www.npmjs.com/package/babel-plugin-transform-react-remove-prop-types
*/
export const baseRowPropTypes = {
anyColumnFixed: PropTypes.bool,
cellWidths: PropTypes.array.isRequired,
className: PropTypes.string,
columns: PropTypes.array.isRequired,
components: PropTypes.object.isRequired,
disabled: PropTypes.bool,
expandIcon: PropTypes.oneOfType([PropTypes.bool, PropTypes.func, PropTypes.node]),
expandableRow: PropTypes.bool,
expanded: PropTypes.bool,
displayNone: PropTypes.bool,
expandedRow: PropTypes.bool,
fixed: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
hideExpandedColumn: PropTypes.bool,
hovered: PropTypes.bool.isRequired,
indent: PropTypes.number,
indentSize: PropTypes.number,
index: PropTypes.number,
isSection: PropTypes.bool,
level: PropTypes.number,
onDidUpdate: PropTypes.func,
onHover: PropTypes.func,
onRow: PropTypes.func,
onRowClick: PropTypes.func,
onRowContextMenu: PropTypes.func,
onRowDoubleClick: PropTypes.func,
onRowMouseEnter: PropTypes.func,
onRowMouseLeave: PropTypes.func,
prefixCls: PropTypes.string,
record: PropTypes.object,
renderExpandIcon: PropTypes.func,
replaceClassName: PropTypes.string,
rowExpandable: PropTypes.func,
rowKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
selected: PropTypes.bool,
store: PropTypes.object,
style: PropTypes.object,
virtualized: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]),
visible: PropTypes.bool.isRequired
};
export default class TableRow extends BaseComponent {
get adapter() {
var _this = this;
return Object.assign(Object.assign({}, super.adapter), {
notifyClick: function () {
return _this.props.onRowClick(...arguments);
},
notifyDoubleClick: function () {
return _this.props.onRowDoubleClick(...arguments);
},
notifyMouseLeave: function () {
_this.props.onHover(false, _this.props.rowKey);
_this.props.onRowMouseEnter(...arguments);
},
notifyMouseEnter: function () {
_this.props.onHover(true, _this.props.rowKey);
_this.props.onRowMouseEnter(...arguments);
}
});
}
constructor(props) {
super(props);
// Pass true to render the tree-shaped expand button
this.renderExpandIcon = record => {
const {
renderExpandIcon
} = this.props;
return renderExpandIcon(record, true);
};
this.handleMouseEnter = e => {
this.foundation.handleMouseEnter(e);
const customRowProps = this.adapter.getCache('customRowProps');
if (typeof (customRowProps === null || customRowProps === void 0 ? void 0 : customRowProps.onMouseEnter) === 'function') {
customRowProps.onMouseEnter(e);
}
};
this.handleMouseLeave = e => {
this.foundation.handleMouseLeave(e);
const customRowProps = this.adapter.getCache('customRowProps');
if (typeof (customRowProps === null || customRowProps === void 0 ? void 0 : customRowProps.onMouseLeave) === 'function') {
customRowProps.onMouseLeave(e);
}
};
this.handleClick = e => {
this.foundation.handleClick(e);
const customRowProps = this.adapter.getCache('customRowProps');
if (customRowProps && typeof customRowProps.onClick === 'function') {
customRowProps.onClick(e);
}
};
this.foundation = new TableRowFoundation(this.adapter);
}
componentDidMount() {
// fix #745
// didmount/willUnmount may be called twice when React.StrictMode is true in React 18, we need to ensure that this.cache.customRowProps is correct
const {
onRow,
index,
record
} = this.props;
const customRowProps = this.adapter.getCache('customRowProps');
if (typeof customRowProps === 'undefined') {
const _a = onRow(record, index) || {},
{
className: customClassName,
style: customStyle
} = _a,
rowProps = __rest(_a, ["className", "style"]);
this.adapter.setCache('customRowProps', Object.assign({}, rowProps));
}
}
shouldComponentUpdate(nextProps) {
/**
* Shallow comparison of incoming props to simulate PureComponent
* Deep comparison cellWidths
*
* 浅层对比传入的 props,模拟 PureComponent
* 深比较 cellWidths
*/
const omitProps = ['cellWidths'];
const isPropsShallowEqual = shallowEqualObjects(_omit(nextProps, omitProps), _omit(this.props, omitProps));
if (!isPropsShallowEqual || !_isEqual(_pick(nextProps, omitProps), _pick(this.props, omitProps))) {
return true;
}
return false;
}
renderCells() {
const {
columns,
record,
index,
prefixCls,
fixed,
components,
expandableRow,
level,
expandIcon,
rowExpandable,
isSection,
expandedRow,
virtualized,
indentSize,
hideExpandedColumn,
cellWidths,
selected,
expanded,
disabled,
onDidUpdate
} = this.props;
const BodyCell = _get(components, 'body.cell', strings.DEFAULT_COMPONENTS.body.cell);
const cells = [];
const displayExpandedColumn = rowExpandable(record);
let firstIndex = 0;
// const dataColumns = getDataColumns(columns);
_each(columns, (column, columnIndex) => {
const columnKey = _get(column, 'key');
const expandableProps = {};
if (fixed !== 'right') {
if (isInnerColumnKey(columnKey)) {
firstIndex++;
}
if (expandableRow && columnIndex === firstIndex) {
expandableProps.renderExpandIcon = this.renderExpandIcon;
if (hideExpandedColumn || isSection) {
expandableProps.expandIcon = expandIcon != null ? expandIcon : true;
}
}
// Only the first data row will be indented
if (level != null && columnIndex === firstIndex) {
expandableProps.indent = level;
const isBool = typeof expandIcon === 'boolean';
const hasExpandIcon = expandIcon !== false || !isBool && expandIcon !== null;
// 如果 expandIcon 为空,不需要 indent
if (!expandableRow && hideExpandedColumn && hasExpandIcon) {
expandableProps.indent = level + 1;
}
}
}
if (isExpandedColumn(column) && !displayExpandedColumn) {
cells.push(/*#__PURE__*/React.createElement(TableCell, {
key: columnIndex,
colIndex: columnIndex,
isSection: isSection
}));
} else if (!isScrollbarColumn(column)) {
const diyProps = {};
if (BodyCell !== strings.DEFAULT_COMPONENTS.body.cell && virtualized && !expandedRow) {
diyProps.width = _get(cellWidths, columnIndex);
}
cells.push(/*#__PURE__*/React.createElement(TableCell, Object.assign({
colIndex: columnIndex
}, expandableProps, diyProps, {
hideExpandedColumn: hideExpandedColumn,
indentSize: indentSize,
isSection: isSection,
prefixCls: `${prefixCls}`,
column: column,
key: columnIndex,
index: index,
record: record,
component: BodyCell,
fixedLeft: isFixedLeft(column) && arrayAdd(cellWidths, 0, columnIndex),
lastFixedLeft: isLastLeftFixed(columns, column),
fixedRight: isFixedRight(column) && arrayAdd(cellWidths, columnIndex + 1),
firstFixedRight: isFirstFixedRight(columns, column),
selected: selected,
expanded: expanded,
disabled: disabled,
onDidUpdate: onDidUpdate
})));
}
});
return cells;
}
render() {
const {
style
} = this.props;
const {
components,
prefixCls,
selected,
onRow,
index,
className,
replaceClassName,
record,
hovered,
expanded,
displayNone,
expandableRow,
level,
expandedRow,
isSection,
rowKey
} = this.props;
const BodyRow = components.body.row;
const _a = onRow(record, index) || {},
{
className: customClassName,
style: customStyle
} = _a,
rowProps = __rest(_a, ["className", "style"]);
this.adapter.setCache('customRowProps', Object.assign({}, rowProps));
const baseRowStyle = Object.assign(Object.assign({}, style), customStyle);
const rowCls = typeof replaceClassName === 'string' && replaceClassName.length ? classnames(replaceClassName, customClassName) : classnames(className, `${prefixCls}-row`, {
[`${prefixCls}-row-selected`]: selected,
[`${prefixCls}-row-expanded`]: expanded,
[`${prefixCls}-row-hovered`]: hovered,
[`${prefixCls}-row-hidden`]: displayNone
}, customClassName);
const ariaProps = {};
if (typeof index === 'number') {
ariaProps['aria-rowindex'] = index + 1;
}
if (expandableRow) {
ariaProps['aria-expanded'] = expanded;
}
// if row is expandedRow, set it's level to 2
if (expanded || expandedRow) {
ariaProps['aria-level'] = 2;
}
if (typeof level === 'number') {
ariaProps['aria-level'] = level + 1;
}
if (isSection) {
ariaProps['aria-level'] = 1;
}
return /*#__PURE__*/React.createElement(BodyRow, Object.assign({
role: "row"
}, ariaProps, rowProps, {
style: baseRowStyle,
className: rowCls,
"data-row-key": rowKey,
onMouseEnter: this.handleMouseEnter,
onMouseLeave: this.handleMouseLeave,
onClick: this.handleClick
}), this.renderCells());
}
}
TableRow.propTypes = baseRowPropTypes;
TableRow.defaultProps = {
columns: [],
rowExpandable: _stubTrue,
components: {
body: {
row: 'tr',
cell: 'td'
}
},
prefixCls: cssClasses.PREFIX,
onRow: _noop,
onRowClick: _noop,
onRowDoubleClick: _noop,
onRowMouseEnter: _noop,
onRowMouseLeave: _noop,
onHover: _noop,
onDidUpdate: _noop,
visible: true,
hovered: false,
selected: false,
disabled: false
};