UNPKG

vui-design

Version:

A high quality UI Toolkit based on Vue.js

659 lines (561 loc) 19.6 kB
import VuiEmpty from "../../empty"; import VuiCheckbox from "../../checkbox"; import VuiRadio from "../../radio"; import PropTypes from "../../../utils/prop-types"; import Locale from "../../../mixins/locale"; import is from "../../../utils/is"; import clone from "../../../utils/clone"; import flatten from "../../../utils/flatten"; import getTargetByPath from "../../../utils/getTargetByPath"; import getScrollbarSize from "../../../utils/getScrollbarSize"; import utils from "./utils"; const VuiTableTbody = { name: "vui-table-tbody", inject: { vuiTable: { default: undefined } }, provide() { return { vuiTableTbody: this }; }, components: { VuiEmpty, VuiCheckbox, VuiRadio }, mixins: [ Locale ], props: { classNamePrefix: PropTypes.string.def("vui-table"), columns: PropTypes.array.def([]), data: PropTypes.array.def([]), colgroup: PropTypes.array.def([]), thead: PropTypes.array.def([]), tbody: PropTypes.array.def([]), rowKey: PropTypes.oneOfType([PropTypes.string, PropTypes.func]).def("key"), rowClassName: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), rowTreeview: PropTypes.object, rowExpansion: PropTypes.object, rowSelection: PropTypes.object, openedRowKeys: PropTypes.array.def([]), expandedRowKeys: PropTypes.array.def([]), selectedRowKeys: PropTypes.oneOfType([PropTypes.array, PropTypes.string, PropTypes.number]).def([]), striped: PropTypes.bool.def(false), scroll: PropTypes.object, locale: PropTypes.object }, methods: { isRowOpened(rowKey) { const { $props: props } = this; if (!props.rowTreeview) { return false; } return props.openedRowKeys.indexOf(rowKey) > -1; }, isRowExpanded(rowKey) { const { $props: props } = this; if (!props.rowExpansion) { return false; } return props.expandedRowKeys.indexOf(rowKey) > -1; }, isRowSelected(rowKey) { const { $props: props } = this; if (!props.rowSelection) { return false; } const isMultiple = utils.getSelectionMultiple(props.rowSelection); if (isMultiple) { return props.selectedRowKeys.indexOf(rowKey) > -1; } else { return props.selectedRowKeys === rowKey; } }, getRowClassName(type, row, rowIndex, rowKey) { const { $props: props } = this; if (type === "placeholder") { return { [`${props.classNamePrefix}-row`]: true, [`${props.classNamePrefix}-row-placeholder`]: true }; } else if (type === "expansion") { return { [`${props.classNamePrefix}-row`]: true, [`${props.classNamePrefix}-row-expanded`]: true }; } else { const stripe = rowIndex % 2 === 0 ? "even" : "odd"; const isSelected = this.isRowSelected(rowKey); let className; if (is.string(props.rowClassName)) { className = row[props.rowClassName]; } else if (is.function(props.rowClassName)) { className = props.rowClassName(row, rowIndex, rowKey); } return { [`${props.classNamePrefix}-row`]: true, [`${props.classNamePrefix}-row-${stripe}`]: props.striped, [`${props.classNamePrefix}-row-selected`]: isSelected, [`${className}`]: className }; } }, getRowStyle(type) { const { $props: props } = this; if (type === "placeholder") { return { height: props.scroll && props.scroll.y > 0 ? (props.scroll.x > 0 ? (`${props.scroll.y - getScrollbarSize()}px`) : `${props.scroll.y}px`) : "auto" }; } }, getColumnClassName(type, column, columnKey, row) { const { $props: props } = this; const { fixedFirst, fixedLast, align = "center", ellipsis, className } = column; let fixed = column.fixed; let cellClassName; if (type === "expansion" || type === "selection") { fixed = props.colgroup.findIndex(col => col.fixed === "left") > -1 ? "left" : undefined; } if (row && row.cellClassName && columnKey) { cellClassName = row.cellClassName[columnKey]; } return { [`${props.classNamePrefix}-column`]: true, [`${props.classNamePrefix}-column-fixed-${fixed}`]: fixed, [`${props.classNamePrefix}-column-fixed-left-last`]: fixed === "left" && fixedLast, [`${props.classNamePrefix}-column-fixed-right-first`]: fixed === "right" && fixedFirst, [`${props.classNamePrefix}-column-align-${align}`]: align, [`${props.classNamePrefix}-column-ellipsis`]: ellipsis, [`${props.classNamePrefix}-column-with-${type}`]: type, [`${props.classNamePrefix}-column-with-sorter`]: column.sorter, [`${props.classNamePrefix}-column-with-filter`]: column.filter, [`${className}`]: className, [`${cellClassName}`]: cellClassName }; }, getColumnStyle(type, column) { const { $props: props } = this; if (type === "expansion" || type === "selection") { const isFixed = props.colgroup.findIndex(col => col.fixed === "left") > -1; if (isFixed) { let offset = 0; if (type === "selection" && props.rowExpansion) { offset = offset + utils.getExpansionWidth(props.rowExpansion); } return { left: offset + "px" }; } } else if (column.fixed === "left") { let offset = column.offset; if (props.rowExpansion) { offset = offset + utils.getExpansionWidth(props.rowExpansion); } if (props.rowSelection) { offset = offset + utils.getSelectionWidth(props.rowSelection); } return { left: offset + "px" }; } else if (column.fixed === "right") { return { right: column.offset + "px" }; } }, getColumnBodyClassName() { const { $props: props } = this; return { [`${props.classNamePrefix}-column-body`]: true }; }, getColumnSwitchClassName(opened) { const { $props: props } = this; return { [`${props.classNamePrefix}-column-switch`]: true, [`active`]: opened }; }, getColumnExpansionClassName(expanded) { const { $props: props } = this; return { [`${props.classNamePrefix}-column-expansion`]: true, [`active`]: expanded }; }, getColumnSelectionClassName(selected) { const { $props: props } = this; return { [`${props.classNamePrefix}-column-selection`]: true, [`active`]: selected }; }, getColumnContentClassName() { const { $props: props } = this; return { [`${props.classNamePrefix}-column-content`]: true }; }, handleRowMouseenter(event, row, rowIndex, rowKey) { this.vuiTable.handleRowMouseenter(row, rowIndex, rowKey); }, handleRowMouseleave(event, row, rowIndex, rowKey) { this.vuiTable.handleRowMouseleave(row, rowIndex, rowKey); }, handleRowClick(event, row, rowIndex, rowKey) { const { $props: props } = this; const { rowTreeview, rowExpansion } = props; this.vuiTable.handleRowClick(row, rowIndex, rowKey); if (rowTreeview && rowTreeview.clickRowToToggle) { const isTogglable = utils.getRowTogglable(row, rowTreeview); if (!isTogglable) { return; } const isIgnoreElements = utils.isIgnoreElements(event, rowTreeview.ignoreElements); if (isIgnoreElements) { return; } this.vuiTable.handleRowToggle(row, rowIndex, rowKey); } if (rowExpansion && rowExpansion.clickRowToExpand) { const isExpandable = utils.getRowExpandable(row, rowKey, rowExpansion); if (!isExpandable) { return; } const isIgnoreElements = utils.isIgnoreElements(event, rowExpansion.ignoreElements); if (isIgnoreElements) { return; } this.vuiTable.handleRowExpand(row, rowIndex, rowKey); } }, handleRowDblclick(event, row, rowIndex, rowKey) { this.vuiTable.handleRowDblclick(row, rowIndex, rowKey); }, handleRowToggle(event, row, rowIndex, rowKey) { const { $props: props } = this; const { rowTreeview } = props; if (!rowTreeview) { return; } if (rowTreeview.clickRowToToggle) { return; } const isTogglable = utils.getRowTogglable(row, rowTreeview); if (!isTogglable) { return; } this.vuiTable.handleRowToggle(row, rowIndex, rowKey); }, handleRowExpand(event, row, rowIndex, rowKey) { const { $props: props } = this; const { rowExpansion } = props; if (!rowExpansion) { return; } if (rowExpansion.clickRowToExpand) { return; } const isExpandable = utils.getRowExpandable(row, rowKey, rowExpansion); if (!isExpandable) { return; } this.vuiTable.handleRowExpand(row, rowIndex, rowKey); }, handleRowSelect(checked, row, rowIndex, rowKey) { this.vuiTable.handleRowSelect(checked, row, rowIndex, rowKey); }, getColgroup(h) { const { $props: props } = this; let cols = []; this.getColgroupChildren(h, cols, props.colgroup); return ( <colgroup>{cols}</colgroup> ); }, getColgroupChildren(h, cols, columns) { const { $props: props } = this; if (props.rowExpansion) { cols.push( <col key="expansion" width={utils.getExpansionWidth(props.rowExpansion)} /> ); } if (props.rowSelection) { cols.push( <col key="selection" width={utils.getExpansionWidth(props.rowSelection)} /> ); } columns.forEach((column, columnIndex) => { cols.push( <col key={column.key} width={column.width} /> ); }); }, getTbody(h) { const { $props: props } = this; const { $slots: vuiTableSlots } = this.vuiTable; let trs = []; if (props.tbody.length === 0) { let colspan = 0; if (props.rowExpansion) { colspan++; } if (props.rowSelection) { colspan++; } colspan += props.colgroup.length; let empty; if (vuiTableSlots.empty) { empty = vuiTableSlots.empty; } else { const { locale } = props; const description = locale && locale.empty ? locale.empty : this.t("vui.table.empty"); empty = ( <VuiEmpty description={description} style="padding: 48px 0;" /> ); } trs.push( <tr class={this.getRowClassName("placeholder")} style={this.getRowStyle("placeholder")}> <td colspan={colspan}> {empty} </td> </tr> ); } else { this.rowIndex = 0; this.getTbodyChildren(h, trs, 1, props.tbody); } return ( <tbody>{trs}</tbody> ); }, getTbodyChildren(h, trs, level, rows) { const { $props: props } = this; const { $scopedSlots: vuiTableScopedSlots } = this.vuiTable; rows.forEach(row => { const rowKey = utils.getRowKey(row, props.rowKey); const rowIndex = this.rowIndex; let tds = []; if (props.rowExpansion) { const isExpandable = utils.getRowExpandable(row, rowKey, props.rowExpansion); let btnExpansion; if (isExpandable) { const btnExpansionAttributes = { class: this.getColumnExpansionClassName(this.isRowExpanded(rowKey)), on: { click: e => this.handleRowExpand(e, row, rowIndex, rowKey) } }; btnExpansion = ( <button {...btnExpansionAttributes}></button> ); } tds.push( <td key="expansion" class={this.getColumnClassName("expansion", props.rowExpansion)} style={this.getColumnStyle("expansion", props.rowExpansion)}> <div class={this.getColumnBodyClassName()}> {btnExpansion} </div> </td> ); } if (props.rowSelection) { const isMultiple = utils.getSelectionMultiple(props.rowSelection); const isRowSelected = this.isRowSelected(rowKey); const componentProps = utils.getSelectionComponentProps(row, rowKey, props.rowSelection); let btnSelection; let btnSelectionAttributes = { class: this.getColumnSelectionClassName(isRowSelected), props: { validator: false, checked: isRowSelected }, on: { change: checked => this.handleRowSelect(checked, row, rowIndex, rowKey) } }; if (props.rowTreeview && isMultiple && !props.rowSelection.strictly) { const childrenKey = utils.getTreeviewChildrenKey(props.rowTreeview); const children = utils.getRowChildren(row, childrenKey); if (is.array(children) && children.length > 0) { const status = utils.getSelectionComponentStatus(flatten(children, childrenKey, true), { rowKey: props.rowKey, rowSelection: props.rowSelection, selectedRowKeys: props.selectedRowKeys }); btnSelectionAttributes.props.indeterminate = status.indeterminate; } } if (componentProps) { btnSelectionAttributes.props = { ...componentProps, ...btnSelectionAttributes.props }; } if (isMultiple) { btnSelection = ( <VuiCheckbox {...btnSelectionAttributes} /> ); } else { btnSelection = ( <VuiRadio {...btnSelectionAttributes} /> ); } tds.push( <td key="selection" class={this.getColumnClassName("selection", props.rowSelection)} style={this.getColumnStyle("selection", props.rowExpansion)}> <div class={this.getColumnBodyClassName()}> {btnSelection} </div> </td> ); } props.colgroup.forEach((column, columnIndex) => { const cellProps = column.cellProps; let columnCellProps = {}; if (is.json(cellProps)) { columnCellProps.attrs = cellProps; } else if (is.function(cellProps)) { columnCellProps.attrs = cellProps({ row: clone(row), rowIndex: rowIndex, column: clone(column), columnIndex: columnIndex }); } if (columnCellProps.attrs && (columnCellProps.attrs.rowSpan === 0 || columnCellProps.attrs.colSpan === 0)) { return; } let btnSwitches = []; if (props.rowTreeview && columnIndex === 0) { const lastLevelIndex = level - 1; for (let i = 0; i < level; i++) { const childrenKey = utils.getTreeviewChildrenKey(props.rowTreeview); const children = utils.getRowChildren(row, childrenKey); let btnSwitchAttributes; if (i === lastLevelIndex && is.array(children) && children.length > 0) { const isRowOpened = this.isRowOpened(rowKey); btnSwitchAttributes = { class: this.getColumnSwitchClassName(isRowOpened), on: { click: e => this.handleRowToggle(e, row, rowIndex, rowKey) } }; } else { btnSwitchAttributes = { class: this.getColumnSwitchClassName(false), style: { visibility: "hidden" } }; } btnSwitches.push( <button {...btnSwitchAttributes}></button> ); } } let content; if (column.slot) { const scopedSlot = vuiTableScopedSlots[column.slot]; content = scopedSlot && scopedSlot({ row: clone(row), rowIndex: rowIndex, column: clone(column), columnIndex: columnIndex }); } else if (column.render) { content = column.render(h, { row: clone(row), rowIndex: rowIndex, column: clone(column), columnIndex: columnIndex }); } else { const target = getTargetByPath(row, column.dataIndex); content = target.value; } tds.push( <td key={column.key} class={this.getColumnClassName(undefined, column, column.key, row)} style={this.getColumnStyle(undefined, column)} {...columnCellProps}> <div class={this.getColumnBodyClassName()}> {btnSwitches} <div class={this.getColumnContentClassName()}>{content}</div> </div> </td> ); }); trs.push( <tr key={rowKey} class={this.getRowClassName("", row, rowIndex, rowKey)} onMouseenter={e => this.handleRowMouseenter(e, row, rowIndex, rowKey)} onMouseleave={e => this.handleRowMouseleave(e, row, rowIndex, rowKey)} onClick={e => this.handleRowClick(e, row, rowIndex, rowKey)} onDblclick={e => this.handleRowDblclick(e, row, rowIndex, rowKey)} > {tds} </tr> ); if (props.rowExpansion && this.isRowExpanded(rowKey)) { let content; if (props.rowExpansion.slot) { const scopedSlot = vuiTableScopedSlots[props.rowExpansion.slot]; content = scopedSlot && scopedSlot({ row: clone(row), rowIndex: rowIndex }); } else if (props.rowExpansion.render) { content = props.rowExpansion.render(h, { row: clone(row), rowIndex: rowIndex }); } trs.push( <tr key={rowKey + "-expansion"} class={this.getRowClassName("expansion", row, rowIndex, rowKey)}> <td></td> <td colspan={props.colgroup.length}>{content}</td> </tr> ); } this.rowIndex++; if (props.rowTreeview && this.isRowOpened(rowKey)) { const childrenKey = utils.getTreeviewChildrenKey(props.rowTreeview); const children = utils.getRowChildren(row, childrenKey); if (is.array(children) && children.length > 0) { this.getTbodyChildren(h, trs, level + 1, children); } } }); } }, render(h) { const { $props: props } = this; const styles = { el: { width: props.scroll && props.scroll.x > 0 ? `${props.scroll.x}px` : `100%` } }; return ( <table border="0" cellpadding="0" cellspacing="0" style={styles.el}> {this.getColgroup(h)} {this.getTbody(h)} </table> ); } }; export default VuiTableTbody;