import React, { forwardRef } from "react"; import classNames from "classnames"; import { Omit } from "../../_type"; import { TableAddon, TableProps, RowRenderContext } from "../TableProps"; import { ControlledProps } from "../../form/controlled"; import { Radio } from "../../radio"; import { injectPropsIfTargetNotExisted } from "../util/inject-props-if-target-not-existed"; /** * `radioable` 插件用于支持表格可单选行的样式及操作。 */ export interface RadioableOptions extends ControlledProps< string, React.SyntheticEvent, { event: React.SyntheticEvent; record: Record } > { /** * 不支持非受控模式 */ defaultValue?: never; /** * 提供一个列的 `key`,将选择组件插入到一个目标列 * * 默认在最前新建一列插入 */ targetColumnKey?: string; /** * 是否整行可选 * @default false */ rowSelect?: boolean; /** * 列宽度,可以指定 CSS 属性或数字 (单位:px) * @default 26 */ width?: string | number; /** * **高级用法** * 更改该插件的在每行的渲染内容,`element` 为默认渲染内容,`context` 中包含该行数据相关信息 * @default x => x */ render?: ( element: JSX.Element, context: RowRenderContext ) => React.ReactNode; } const fallbackColumnKey = "__radioable_addon__"; let rowDisabled: TableProps["rowDisabled"] = null; export function radioable({ value, onChange, targetColumnKey, rowSelect, width = 26, render = x => x, }: RadioableOptions): TableAddon { return { getInfo: () => ({ name: "radioable" }), onInjectProps: props => { rowDisabled = props.rowDisabled || (() => false); const columns = injectPropsIfTargetNotExisted( props.columns, targetColumnKey, { key: fallbackColumnKey, width, header: null, render: () => null, } ); return { ...props, columns }; }, onInjectColumn: previous => ( record, rowKey, recordIndex, column, columnIndex ) => { // 不是目标列 if (column.key !== targetColumnKey && column.key !== fallbackColumnKey) { return previous(record, rowKey, recordIndex, column, columnIndex); } const { children: preChildren, props, ...result } = previous( record, rowKey, recordIndex, column, columnIndex ); let children = preChildren; // 非表头 if (recordIndex !== -1) { const element = ( onChange(rowKey, { event, record })} > {children || <> } ); children = render(element, { children: preChildren, record, rowKey, recordIndex, disabled: rowDisabled(record), }); } return { ...result, props, children }; }, onInjectRow: renderRow => (record, rowKey, recordIndex, columns) => { const { prepends, appends, row: preRow } = renderRow( record, rowKey, recordIndex, columns ); let row = preRow; // 支持整行选择 row = ( onChange(value, { ...context, record })} rowSelect={rowSelect && !rowDisabled(record)} > {row} ); return { prepends, row, appends }; }, }; } const SelectWrapper = forwardRef(function SelectWrapper( { value, onChange, name, rowSelect, children, ...props }: { name: string; rowSelect: boolean; value: string; onChange: (name: string, context: { event: React.SyntheticEvent }) => void; children: React.ReactElement>; } & Omit, "onChange">, ref ): JSX.Element { const rowSelectProps = { onClick: (event: React.MouseEvent) => { // 事件合并 if (typeof props.onClick === "function") { props.onClick(event); } if (typeof children.props.onClick === "function") { children.props.onClick(event); } return onChange(name, { event }); }, }; return React.cloneElement(children, { ...props, ref, className: classNames(props.className, children.props.className, { "is-selected": value === name, }), ...(rowSelect ? rowSelectProps : {}), }); });