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

Commit

Permalink
feat(#13): 增加 itemCount 属性的布局方式 (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
dream2023 authored Jul 11, 2021
1 parent 8382bca commit c92ab97
Show file tree
Hide file tree
Showing 15 changed files with 156 additions and 10 deletions.
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

0 comments on commit c92ab97

Please sign in to comment.