Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Form): support title, footer, summary on Form.Table #353

Merged
merged 1 commit into from
Jun 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions src/form/demos/footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React, { useMemo, useState } from 'react';
import { Form } from 'dt-react-component';
import { Button, Select } from 'antd';

export default () => {
const [form] = Form.useForm();
const data: { name?: number; [key: string]: any }[] = Form.useWatch('data', form) || [];

const [rawOptions] = useState(
new Array(10).fill(1).map((_, i) => ({ label: `test-${i}`, value: i }))
);

const options = useMemo(() => {
const names = data.filter(Boolean).map((i) => i.name);
return rawOptions.map((op) => ({
...op,
disabled: names.includes(op.value),
}));
}, [
data
.filter(Boolean)
.map((i) => i.name)
.toString(),
]);

return (
<Form form={form} layout="vertical" initialValues={{ data: [{}] }} style={{ padding: 18 }}>
<Form.Table
bordered
name="data"
scroll={{
y: 280,
}}
columns={(_, { remove }) => [
{
key: 'name',
title: 'Name',
dataIndex: 'name',
required: true,
rules: [
{
required: true,
message: 'Please Input Name!',
},
],
render: () => <Select placeholder="Name" options={options} />,
},
{
key: 'op',
title: 'Configuration',
render: ({ name }) => (
<Button type="link" onClick={() => remove(name)}>
delete
</Button>
),
},
]}
size="small"
footer={(_, { add }) => (
<Button type="link" onClick={() => add({})}>
add
</Button>
)}
/>
</Form>
);
};
2 changes: 2 additions & 0 deletions src/form/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ demo:

<code src="./demos/rules.tsx" title="联动规则校验" description="只有性别为男性时,才需要填写地址" compact="true"></code>

<code src="./demos/footer.tsx" title="支持设置 footer" description="下拉框联动禁用" compact="true"></code>

## FAQ

### 如何设置 title 上的必选标识?
Expand Down
195 changes: 116 additions & 79 deletions src/form/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ type NotNullRowSelection = NonNullable<TableProps<any>['rowSelection']>;
type OverrideParameters = (string | number)[];
type RcFormInstance = Parameters<RuleRender>[0];

type RawPanelRender = NonNullable<TableProps<any>['footer']>;
/**
* Override PanelRender type
*/
type PanelRenderFunc = (
...args: Parameters<FormListProps['children']>
) => ReturnType<RawPanelRender>;

/**
* Form.Table 组件类型
*/
Expand All @@ -28,15 +36,24 @@ export interface IFormTableProps
* - and pagination which is expect to be not supported in Form.Table
* - and className which is renamed to tableClassName
* - and rowKey, dataSource for which are defined and not allowed to be modified
* - and footer, title, summary for which are re-defined to pass form's operation
*/
Omit<
TableProps<any>,
'columns' | 'rowSelection' | 'pagination' | 'className' | 'rowKey' | 'dataSource'
| 'columns'
| 'rowSelection'
| 'pagination'
| 'className'
| 'rowKey'
| 'dataSource'
| 'footer'
| 'title'
| 'summary'
> {
/**
* 表格列的配置描述
*/
columns?: ColumnType[];
columns?: ColumnType[] | ((...args: Parameters<FormListProps['children']>) => ColumnType[]);
/**
* Table 的 className
*/
Expand All @@ -49,6 +66,9 @@ export interface IFormTableProps
field: FormListFieldData
) => ReturnType<NonNullable<NotNullRowSelection['getCheckboxProps']>>;
};
title?: PanelRenderFunc;
footer?: PanelRenderFunc;
summary?: PanelRenderFunc;
}

export interface ColumnType
Expand Down Expand Up @@ -89,11 +109,37 @@ export default function InternalTable({
initialValue,
...tableProps
}: IFormTableProps) {
const { tableClassName, columns: rawColumns, ...restProps } = tableProps;
const columns: ColumnsType<FormListFieldData> = useMemo(() => {
return (
rawColumns?.map(
({
const {
tableClassName,
columns: rawColumns,
// override title, footer, summary
title,
footer,
summary,
...restProps
} = tableProps;

const convertRawToTableCol = (raw?: ColumnType[]): ColumnsType<FormListFieldData> => {
if (!raw?.length) return [];
return raw.map(
({
noStyle,
style,
className,
id,
hasFeedback,
validateStatus,
required,
hidden,
initialValue,
messageVariables,
tooltip,
dependencies,
rules: rawRules,
render,
...cols
}) => {
const formItemProps = {
noStyle,
style,
className,
Expand All @@ -105,94 +151,85 @@ export default function InternalTable({
initialValue,
messageVariables,
tooltip,
dependencies,
rules: rawRules,
render,
...cols
}) => {
const formItemProps = {
noStyle,
style,
className,
id,
hasFeedback,
validateStatus,
required,
hidden,
initialValue,
messageVariables,
tooltip,
};
};

const isRequired =
required ||
rawRules?.some((rule) => typeof rule === 'object' && rule.required);
const isRequired =
required || rawRules?.some((rule) => typeof rule === 'object' && rule.required);

return {
...cols,
title: (
<>
{isRequired && (
<span className="dtc-form__table__column--required" />
)}
{cols.title}
</>
),
render(_, record) {
const currentNamePath = [record.name, cols.dataIndex]
.filter(utils.checkExist)
.flat() as OverrideParameters;
return {
...cols,
title: (
<>
{isRequired && <span className="dtc-form__table__column--required" />}
{cols.title}
</>
),
render(_, record) {
const currentNamePath = [record.name, cols.dataIndex]
.filter(utils.checkExist)
.flat() as OverrideParameters;

const rules: Rule[] | undefined = rawRules?.map((rule) =>
typeof rule === 'function'
? (form) => rule(form, currentNamePath)
: rule
);

if (dependencies) {
return (
<Form.Item
noStyle
dependencies={
typeof dependencies === 'function'
? dependencies(currentNamePath)
: dependencies
}
>
{(formInstance) => (
<Form.Item
name={currentNamePath}
rules={rules}
{...formItemProps}
>
{render?.(record, currentNamePath, formInstance)}
</Form.Item>
)}
</Form.Item>
);
}
const rules: Rule[] | undefined = rawRules?.map((rule) =>
typeof rule === 'function'
? (form) => rule(form, currentNamePath)
: rule
);

if (dependencies) {
return (
<Form.Item name={currentNamePath} rules={rules} {...formItemProps}>
{render?.(record, currentNamePath)}
<Form.Item
noStyle
dependencies={
typeof dependencies === 'function'
? dependencies(currentNamePath)
: dependencies
}
>
{(formInstance) => (
<Form.Item
name={currentNamePath}
rules={rules}
{...formItemProps}
>
{render?.(record, currentNamePath, formInstance)}
</Form.Item>
)}
</Form.Item>
);
},
};
}
) || []
}

return (
<Form.Item name={currentNamePath} rules={rules} {...formItemProps}>
{render?.(record, currentNamePath)}
</Form.Item>
);
},
};
}
);
};

const columns = useMemo(() => {
if (typeof rawColumns === 'function') return rawColumns;
return convertRawToTableCol(rawColumns);
}, [rawColumns]);

return (
<Form.List name={name} rules={rules} initialValue={initialValue}>
{(fields) => (
<Table
{(fields, ope, meta) => (
<Table<FormListFieldData>
className={classnames(className, tableClassName)}
rowKey="key"
dataSource={fields}
pagination={false}
columns={columns}
columns={
typeof columns === 'function'
? convertRawToTableCol(columns(fields, ope, meta))
: columns
}
footer={footer ? () => footer(fields, ope, meta) : undefined}
title={title ? () => title(fields, ope, meta) : undefined}
summary={summary ? () => summary(fields, ope, meta) : undefined}
{...restProps}
/>
)}
Expand Down