Skip to content
This repository has been archived by the owner on Jun 13, 2023. It is now read-only.

feat(#13): 增加 itemCount 属性的布局方式 #17

Merged
merged 1 commit into from
Jul 11, 2021
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
17 changes: 17 additions & 0 deletions docs/components/form/__demos__/form-item/itemSpan.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';

import { SuperCheckboxGroup, SuperEmail, SuperForm, SuperInput, SuperNumber } from 'super-antd';

const Demo = () => {
return (
<SuperForm itemCount={3} layout="vertical">
<SuperInput name="name" label="姓名"></SuperInput>
<SuperNumber name="age" label="年龄"></SuperNumber>
<SuperCheckboxGroup name="sex" label="性别" options={['男', '女']}></SuperCheckboxGroup>
<SuperInput name="like" label="爱好"></SuperInput>
<SuperEmail name="address" label="地址" itemSpan={16}></SuperEmail>
</SuperForm>
);
};

export default Demo;
2 changes: 2 additions & 0 deletions docs/components/form/__demos__/form-item/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export interface FormItemProps {
* | number | string
*/
wrapperCol?: ColType;
/** 表单项占用的栅格数。 */
itemSpan?: number;
/** 只读。 */
readonly?: boolean;
/** 禁用。 */
Expand Down
35 changes: 35 additions & 0 deletions docs/components/form/__demos__/form/itemCount.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Radio } from 'antd';
import React, { useState } from 'react';

import { SuperCheckboxGroup, SuperEmail, SuperForm, SuperInput, SuperNumber } from 'super-antd';

const Demo = () => {
const [itemCount, setItemCount] = useState<number>(3);
return (
<>
<div style={{ margin: 20 }}>
<Radio.Group
optionType="button"
buttonStyle="solid"
value={itemCount}
onChange={(e) => setItemCount(Number(e.target.value))}
options={[
{ value: 1, label: '一列' },
{ value: 2, label: '两列' },
{ value: 3, label: '三列' },
]}
></Radio.Group>
</div>

<SuperForm itemCount={itemCount} layout="vertical">
<SuperInput name="name" label="姓名"></SuperInput>
<SuperNumber name="age" label="年龄"></SuperNumber>
<SuperCheckboxGroup name="sex" label="性别" options={['男', '女']}></SuperCheckboxGroup>
<SuperInput name="like" label="爱好"></SuperInput>
<SuperEmail name="address" label="地址" itemSpan={16}></SuperEmail>
</SuperForm>
</>
);
};

export default Demo;
2 changes: 2 additions & 0 deletions docs/components/form/__demos__/form/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ export interface IFormDemoProps {
btns?: BtnsProps;
/** 对齐方式 */
align?: 'left' | 'right' | 'center';
/** 一列展示表单项的个数 */
itemCount?: number;
}

const Demo: FC<IFormDemoProps> = () => <>Demo!</>;
Expand Down
4 changes: 4 additions & 0 deletions docs/components/form/form-item.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ const SuperInput = (props) => {

<code src="./__demos__/form-item/col.tsx" />

## itemSpan 占用栅格数

<code src="./__demos__/form-item/itemSpan.tsx" />

## 隐藏 label

<code src="./__demos__/form-item/hideLabel.tsx" />
Expand Down
6 changes: 6 additions & 0 deletions docs/components/form/form.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ antd Form 当 `Form.Item` 无 `label` 时,还需要指定其 labelCol 的 `off

<code src="./__demos__/form/layout.tsx" />

## 表单项布局

我们可以通过 `itemCount` 设置一列显示多少表单项,并且表单项可以通过 `itemSpan` 自定义显示的占用的栅格数。

<code src="./__demos__/form/itemCount.tsx" />

## debug 功能

SuperForm 增加 `debug` 功能,通过 `debug` 属性即可开启。
Expand Down
15 changes: 13 additions & 2 deletions src/form-item/src/hoc/withFormItem.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { compilerStr } from '@dream2023/data-mapping';
import { useCreation } from 'ahooks';
import { Col } from 'antd';
import set from 'lodash.set';
import type { ComponentType, FC } from 'react';
import React, { useContext, useEffect } from 'react';
Expand Down Expand Up @@ -51,6 +52,7 @@ export function withFormItem<P extends object = any>(
disabledOn,
readonlyOn,
requiredOn,
itemSpan,
wrapperCol,
placeholder,
validateStatus,
Expand All @@ -64,7 +66,7 @@ export function withFormItem<P extends object = any>(
const { delimiters } = useContext(SuperAntdContext);
// 表单 context
const formContext = useContext<SuperFormContextProps>(SuperFormContext);
const { form, initialValues } = formContext;
const { form, initialValues, itemCount: formItemCount } = formContext;
const data = Object.assign({}, initialValues, form?.getFieldsValue());
const { layout, autoPlaceholder, hideLabel: formHideLabel, remoteErrors } = formContext;

Expand Down Expand Up @@ -162,6 +164,13 @@ export function withFormItem<P extends object = any>(
return needData ? { data, form } : {};
}, [data]);

// 合并 formItemCount 和 本身 itemSpan
const computedItemLayout = useCreation(() => {
if (itemSpan) return itemSpan;
if (formItemCount) return 24 / formItemCount;
return 24;
}, [itemSpan, formItemCount]);

// 动态必填,参考:https://ant.design/components/form-cn/#components-form-demo-dynamic-rule
useEffect(() => {
if (computedName) {
Expand Down Expand Up @@ -207,7 +216,7 @@ export function withFormItem<P extends object = any>(
return { help, validateStatus };
}, [help, validateStatus, remoteErrors, computedName]);

return (
const FormItemContent = (
<FormItemComponent
{...(componentProps as P)}
{...dataProps}
Expand All @@ -226,6 +235,8 @@ export function withFormItem<P extends object = any>(
messageVariables={computedMessageVariables}
/>
);

return computedItemLayout !== 24 ? <Col span={computedItemLayout}>{FormItemContent}</Col> : FormItemContent;
};

return EnhancedFormComponent;
Expand Down
3 changes: 3 additions & 0 deletions src/form-item/src/hoc/withFormItemTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ export interface NewWithFormItemProps {
clearValueAfterReadonly?: boolean;
/** 是否隐藏 label。当我们想要保留 label 作为校检的名称,又不想显示 label 时,可以将其设置为 true。 */
hideLabel?: boolean;
/** 表单项所占栅格数量 */
itemSpan?: number;
}

// 更改原表单项属性
Expand Down Expand Up @@ -76,6 +78,7 @@ export const omitWithFormItemKeys: Record<keyof OmitWithFormItemProps, any> = {
readonlyOn: '',
wrapperCol: '',
requiredOn: '',
itemSpan: '',
linkageFields: '',
clearValueAfterHidden: '',
clearValueAfterDisabled: '',
Expand Down
12 changes: 9 additions & 3 deletions src/form/src/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import './form.less';
import ProForm from '@ant-design/pro-form';
import type { ProFormProps } from '@ant-design/pro-form';
import { useCreation, useLocalStorageState, usePersistFn, useThrottleFn } from 'ahooks';
import { Form, Spin } from 'antd';
import { Form, Row, Spin } from 'antd';
import type { Key } from 'react';
import { useContext } from 'react';
import React, { useState } from 'react';
Expand Down Expand Up @@ -60,6 +60,7 @@ export function SuperForm<Values extends Record<Key, any> = any>(props: SuperFor

// 响应式和标签
labelCol,
itemCount,
wrapperCol,
isResponsive,

Expand Down Expand Up @@ -236,12 +237,13 @@ export function SuperForm<Values extends Record<Key, any> = any>(props: SuperFor
readonly,
disabled,
hideLabel,
itemCount,
form: formInstance,
autoPlaceholder,
remoteErrors,
initialValues: initialValuesWithStorage,
};
}, [remoteErrors, layout, formInstance, readonly, disabled, hideLabel, autoPlaceholder]);
}, [remoteErrors, itemCount, layout, formInstance, readonly, disabled, hideLabel, autoPlaceholder]);

// loading 效果(初始化和提交数据时)
const loading = useCreation(() => {
Expand All @@ -251,6 +253,7 @@ export function SuperForm<Values extends Record<Key, any> = any>(props: SuperFor
// 响应式
const { responsiveRef, responsiveLabelCol, responsiveWrapperCol } = useResponsiveCol({
layout,
itemCount,
align,
labelCol,
hideLabel,
Expand Down Expand Up @@ -331,7 +334,9 @@ export function SuperForm<Values extends Record<Key, any> = any>(props: SuperFor
{debug && <SuperFormDebugger />}

{/* children 数据 */}
<SuperFormContext.Provider value={formContextValue}>{children}</SuperFormContext.Provider>
<SuperFormContext.Provider value={formContextValue}>
{Number(itemCount) > 1 ? <Row gutter={16}>{children}</Row> : children}
</SuperFormContext.Provider>
{btnDoms && (
<Form.Item
wrapperCol={{
Expand All @@ -349,6 +354,7 @@ export function SuperForm<Values extends Record<Key, any> = any>(props: SuperFor
}

SuperForm.defaultProps = {
itemCount: 1,
autoPlaceholder: true,
clearPersistDataAfterSubmit: true,
btns: {
Expand Down
1 change: 1 addition & 0 deletions src/form/src/context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface SuperFormContextProps<Values extends Record<Key, any> = any> ex
layout?: 'horizontal' | 'vertical' | 'inline';
/** 服务端返回的错误 */
remoteErrors: Record<string, any>;
itemCount?: number;
initialValues?: Record<string, any>;
}

Expand Down
4 changes: 4 additions & 0 deletions src/form/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,8 @@ export interface FormEnhancedProps {
align?: 'left' | 'right' | 'center';
/** 是否保留远程获取的非表单项里的数据,例如 {id: 1, name: 'Jerry'} id 未设置表单项,antd 默认会删除,如果开启,则保留 */
preserveRemoteData?: boolean;
/**
* 每行所占的表单项个数
*/
itemCount?: number;
}
11 changes: 8 additions & 3 deletions src/shared/src/hooks/useResponsiveCol/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ export interface ResponsiveColOptions {
layout?: FormLayout;
// 是否隐藏标签
hideLabel?: boolean;
// 表单项布局
itemCount?: number;
// 是否开启响应式
isResponsive?: boolean;
// 对齐方式
Expand All @@ -54,6 +56,7 @@ export interface ResponsiveColOptions {

export const useResponsiveCol = ({
layout = 'horizontal',
itemCount = 1,
isResponsive = true,
labelCol,
align = 'left',
Expand Down Expand Up @@ -91,9 +94,11 @@ export const useResponsiveCol = ({

// 判断是否已经开启响应式
const shouldResponsive = useCreation(() => {
// isResponsive 为 true 并且没有指定 labelCol 和 wrapperCol
return layout === 'horizontal' && align === 'left' && isResponsive && !labelCol && !wrapperCol;
}, [layout, isResponsive, labelCol, wrapperCol, align]);
// itemCount < 1 且 isResponsive 为 true 并且没有指定 labelCol 和 wrapperCol
return (
Number(itemCount) <= 1 && layout === 'horizontal' && align === 'left' && isResponsive && !labelCol && !wrapperCol
);
}, [layout, itemCount, isResponsive, labelCol, wrapperCol, align]);

// 通过 ref 绑定 DOM
const responsiveRef = useCreation(() => {
Expand Down
44 changes: 44 additions & 0 deletions tests/form/Form.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -652,4 +652,48 @@ describe('SuperForm 表单', () => {
expect(wrapper.container.querySelector('super-antd-left')).not.toBeInTheDocument();
});
});

describe('itemCount 表单项个数', () => {
test('当未设置 itemCount 或者 itemCount = 1 时,不起作用', () => {
// 第一次渲染
const { container, rerender } = render(<SuperForm isResponsive={false}></SuperForm>);
const count = container.querySelectorAll('.ant-form > .ant-form-item').length;

// 第二次渲染,查看子元素数量
rerender(
<SuperForm isResponsive={false}>
<SuperInput name="foo" label="bar" />
</SuperForm>,
);
const count2 = container.querySelectorAll('.ant-form > .ant-form-item').length;
expect(count + 1).toBe(count2);

// 第三次渲染,当设置了 1,查看子元素数量
rerender(
<SuperForm itemCount={1} isResponsive={false}>
<SuperInput name="foo" label="bar" />
</SuperForm>,
);
const count3 = container.querySelectorAll('.ant-form > .ant-form-item').length;
expect(count2).toBe(count3);
});

test('当设置 itemCount 为 3 时,表单项栅格数应为 8', () => {
const { container } = render(
<SuperForm itemCount={3} isResponsive={false}>
<SuperInput name="foo" label="bar" />
</SuperForm>,
);
expect(container.querySelector('.ant-row > .ant-col-8')).toBeInTheDocument();
});

test('当表单项设置 itemSpan 为值时,起到覆盖作用', () => {
const { container } = render(
<SuperForm itemCount={3} isResponsive={false}>
<SuperInput name="foo" label="bar" itemSpan={16} />
</SuperForm>,
);
expect(container.querySelector('.ant-row > .ant-col-16')).toBeInTheDocument();
});
});
});
6 changes: 6 additions & 0 deletions tests/shared/hooks/useResponsiveCol.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ describe('useResponsiveCol', () => {
const { result } = setup({ isResponsive: false });
expect(result.current.shouldResponsive).toBeFalsy();
});

test('itemCount > 1,则为 false', () => {
const { result } = setup({ itemCount: 2 });
expect(result.current.shouldResponsive).toBeFalsy();
});

test('labelCol 为存在,则为 false', () => {
const { result } = setup({ labelCol: 9 });
expect(result.current.shouldResponsive).toBeFalsy();
Expand Down
4 changes: 2 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5556,8 +5556,8 @@ caniuse-api@^3.0.0:
lodash.uniq "^4.5.0"

caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000971, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001166, caniuse-lite@^1.0.30001179, caniuse-lite@^1.0.30001181, caniuse-lite@^1.0.30001219:
version "1.0.30001241"
resolved "https://registry.npm.taobao.org/caniuse-lite/download/caniuse-lite-1.0.30001241.tgz"
version "1.0.30001243"
resolved "https://registry.nlark.com/caniuse-lite/download/caniuse-lite-1.0.30001243.tgz"

capture-exit@^2.0.0:
version "2.0.0"
Expand Down