perf: form item (#1171)

Co-authored-by: sealday <sealday@gmail.com>
Reviewed-on: daoyoucloud/tachybase#1171
This commit is contained in:
sealday 2024-06-13 03:46:45 +08:00
parent c1d1a70eaf
commit 943db605c8
6 changed files with 92 additions and 86 deletions

View File

@ -1,7 +1,9 @@
import { observer, RecursionField, useField, useFieldSchema } from '@tachybase/schema';
import { Input, Select } from 'antd';
import { differenceBy, unionBy } from 'lodash';
import React, { useContext, useMemo, useState } from 'react';
import { observer, RecursionField, useField, useFieldSchema } from '@tachybase/schema';
import { Select, Space } from 'antd';
import { differenceBy, unionBy } from 'lodash';
import {
FormProvider,
RecordPickerContext,
@ -9,7 +11,7 @@ import {
SchemaComponentOptions,
useActionContext,
} from '../..';
import { CollectionProvider_deprecated, RecordProvider, useFormBlockContext, useCollectionRecordData } from '../../../';
import { CollectionProvider_deprecated, RecordProvider, useCollectionRecordData, useFormBlockContext } from '../../../';
import {
TableSelectorParamsProvider,
useTableSelectorProps as useTsp,
@ -133,7 +135,7 @@ export const InternalPicker = observer(
};
return (
<>
<Input.Group compact style={{ display: 'flex', lineHeight: '32px' }}>
<Space.Compact style={{ display: 'flex', lineHeight: '32px' }}>
<div style={{ width: '100%' }}>
<Select
role="button"
@ -181,7 +183,7 @@ export const InternalPicker = observer(
/>
</RecordProvider>
)}
</Input.Group>
</Space.Compact>
<ActionContextProvider
value={{
openSize: fieldSchema['x-component-props']?.['openSize'] || openSize,

View File

@ -1,6 +1,6 @@
import React, { useEffect, useMemo } from 'react';
import { FormItem as Item } from '@tachybase/components';
import { Field, observer, useField, useFieldSchema } from '@tachybase/schema';
import { Field, useField, useFieldSchema } from '@tachybase/schema';
import { createStyles } from 'antd-style';
import cx from 'classnames';
@ -39,68 +39,67 @@ const useStyles = createStyles(({ css }) => {
};
});
export const FormItem: any = observer(
(props: any) => {
const { styles } = useStyles();
const { layoutDirection: selfLayoutDirection } = props;
const field = useField<Field>();
const schema = useFieldSchema();
const contextVariable = useContextVariable();
const variables = useVariables();
const { addActiveFieldName } = useFormActiveFields() || {};
const { layoutDirection } = useContextConfigSetting();
export const FormItem = (props: any) => {
const { styles } = useStyles();
const { layoutDirection: selfLayoutDirection } = props;
const field = useField<Field>();
const schema = useFieldSchema();
const contextVariable = useContextVariable();
const variables = useVariables();
const { addActiveFieldName } = useFormActiveFields() || {};
const { layoutDirection } = useContextConfigSetting();
const finishLayoutDirection = selfLayoutDirection ?? layoutDirection;
const finishLayoutDirection = selfLayoutDirection ?? layoutDirection;
useEffect(() => {
variables?.registerVariable(contextVariable);
}, [contextVariable]);
useEffect(() => {
variables?.registerVariable(contextVariable);
}, [contextVariable]);
// 需要放在注冊完变量之后
useParseDefaultValue();
useLazyLoadDisplayAssociationFieldsOfForm();
// 需要放在注冊完变量之后
useParseDefaultValue();
useLazyLoadDisplayAssociationFieldsOfForm();
useEffect(() => {
addActiveFieldName?.(schema.name as string);
}, [addActiveFieldName, schema.name]);
useEffect(() => {
addActiveFieldName?.(schema.name as string);
}, [addActiveFieldName, schema.name]);
const showTitle = schema['x-decorator-props']?.showTitle ?? true;
const showTitle = schema['x-decorator-props']?.showTitle ?? true;
const extra = useMemo(() => {
return typeof field.description === 'string' ? (
<div
dangerouslySetInnerHTML={{
__html: HTMLEncode(field.description).split('\n').join('<br/>'),
}}
/>
) : (
field.description
);
}, [field.description]);
const className = useMemo(() => {
return cx(
styles.space,
{
[styles.hide]: showTitle === false,
},
{
[styles.row]: finishLayoutDirection === 'row',
},
);
}, [showTitle]);
return (
<CollectionFieldProvider allowNull={true}>
<BlockItem className={'tb-form-item'}>
<ACLCollectionFieldProvider>
<Item className={className} {...props} extra={extra} />
</ACLCollectionFieldProvider>
</BlockItem>
</CollectionFieldProvider>
const extra = useMemo(() => {
return typeof field.description === 'string' ? (
<div
dangerouslySetInnerHTML={{
__html: HTMLEncode(field.description).split('\n').join('<br/>'),
}}
/>
) : (
field.description
);
},
{ displayName: 'FormItem' },
);
}, [field.description]);
const className = useMemo(() => {
return cx(
styles.space,
{
[styles.hide]: showTitle === false,
},
{
[styles.row]: finishLayoutDirection === 'row',
},
);
}, [showTitle]);
return (
<CollectionFieldProvider allowNull={true}>
<BlockItem className={'tb-form-item'}>
<ACLCollectionFieldProvider>
<Item className={className} {...props} extra={extra} />
</ACLCollectionFieldProvider>
</BlockItem>
</CollectionFieldProvider>
);
};
FormItem.displayName = 'FormItem';
FormItem.Designer = function Designer() {
const app = useApp();

View File

@ -1,11 +1,13 @@
import { Input, Select } from 'antd';
import React, { useCallback, useMemo, useState } from 'react';
import { Select, Space } from 'antd';
import { useTranslation } from 'react-i18next';
import { useFormBlockContext } from '../../block-provider';
import { useRecord } from '../../record-provider';
import { Variable } from '.././../schema-component';
import { useCompatOldVariables } from '../VariableInput/VariableInput';
import { useVariableOptions } from '../VariableInput/hooks/useVariableOptions';
import { useCompatOldVariables } from '../VariableInput/VariableInput';
import { DynamicComponent } from './DynamicComponent';
const { Option } = Select;
@ -104,7 +106,7 @@ export const ValueDynamicComponent = (props: ValueDynamicComponentProps) => {
};
return (
<Input.Group compact>
<Space.Compact>
<Select
// @ts-ignore
role="button"
@ -123,6 +125,6 @@ export const ValueDynamicComponent = (props: ValueDynamicComponentProps) => {
<Option value="empty">{t('Empty')}</Option>
</Select>
{modeMap[mode]}
</Input.Group>
</Space.Compact>
);
};

View File

@ -1293,7 +1293,7 @@ export const SchemaSettingsLinkageRules = function LinkageRules(props) {
'x-use-component-props': () => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const options = useLinkageCollectionFilterOptions(collectionName);
return {
const result = {
options,
defaultValues: gridSchema?.['x-linkage-rules'] || fieldSchema?.['x-linkage-rules'],
type,
@ -1306,6 +1306,7 @@ export const SchemaSettingsLinkageRules = function LinkageRules(props) {
record,
formBlockType,
};
return result;
},
},
},

View File

@ -1,11 +1,13 @@
import React, { useState } from 'react';
import { CheckCircleOutlined, CloseCircleOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
import { isVoidField } from '@formily/core';
import { connect, mapProps, ReactFC } from '@formily/react';
import { Popover, Tooltip } from 'antd';
import cls from 'classnames';
import React, { useState } from 'react';
import { FormLayoutShallowContext } from '../form-layout';
import cx from 'classnames';
import { pickDataProps, usePrefixCls } from '../__builtins__';
import { FormLayoutShallowContext } from '../form-layout';
import { useFormItemLayout, useOverflow } from './hooks';
import useStyle from './style';
import { IFormItemProps } from './types';
@ -88,7 +90,7 @@ export const BaseItem: React.FC<React.PropsWithChildren<IFormItemProps>> = ({ ch
placement="top"
content={
<div
className={cls({
className={cx({
[`${prefixCls}-${feedbackStatus}-help`]: !!feedbackStatus,
[`${prefixCls}-help`]: true,
})}
@ -155,7 +157,7 @@ export const BaseItem: React.FC<React.PropsWithChildren<IFormItemProps>> = ({ ch
if (!label) return null;
return (
<div
className={cls({
className={cx({
[`${prefixCls}-label`]: true,
[`${prefixCls}-label-tooltip`]: (tooltip && tooltipLayout === 'text') || overflow,
[`${prefixCls}-item-col-${labelCol}`]: enableCol && !!labelCol,
@ -177,7 +179,7 @@ export const BaseItem: React.FC<React.PropsWithChildren<IFormItemProps>> = ({ ch
...gridStyles,
}}
data-grid-span={props.gridSpan}
className={cls(
className={cx(
props.className,
{
[`${prefixCls}`]: true,
@ -211,28 +213,28 @@ export const BaseItem: React.FC<React.PropsWithChildren<IFormItemProps>> = ({ ch
>
{renderLabel()}
<div
className={cls({
className={cx({
[`${prefixCls}-control`]: true,
[`${prefixCls}-item-col-${wrapperCol}`]: enableCol && !!wrapperCol && label,
})}
>
<div className={cls(`${prefixCls}-control-content`)}>
{addonBefore && <div className={cls(`${prefixCls}-addon-before`)}>{addonBefore}</div>}
<div className={cx(`${prefixCls}-control-content`)}>
{addonBefore && <div className={cx(`${prefixCls}-addon-before`)}>{addonBefore}</div>}
<div
style={wrapperStyle}
className={cls({
className={cx({
[`${prefixCls}-control-content-component`]: true,
[`${prefixCls}-control-content-component-has-feedback-icon`]: !!feedbackIcon,
})}
>
<FormLayoutShallowContext.Provider value={{}}>{formatChildren}</FormLayoutShallowContext.Provider>
{feedbackIcon && <div className={cls(`${prefixCls}-feedback-icon`)}>{feedbackIcon}</div>}
{feedbackIcon && <div className={cx(`${prefixCls}-feedback-icon`)}>{feedbackIcon}</div>}
</div>
{addonAfter && <div className={cls(`${prefixCls}-addon-after`)}>{addonAfter}</div>}
{addonAfter && <div className={cx(`${prefixCls}-addon-after`)}>{addonAfter}</div>}
</div>
{!!feedbackText && feedbackLayout !== 'popover' && feedbackLayout !== 'none' && (
<div
className={cls({
className={cx({
[`${prefixCls}-${feedbackStatus}-help`]: !!feedbackStatus,
[`${prefixCls}-help`]: true,
[`${prefixCls}-help-enter`]: true,
@ -242,7 +244,7 @@ export const BaseItem: React.FC<React.PropsWithChildren<IFormItemProps>> = ({ ch
{feedbackText}
</div>
)}
{extra && <div className={cls(`${prefixCls}-extra`)}>{extra}</div>}
{extra && <div className={cx(`${prefixCls}-extra`)}>{extra}</div>}
</div>
</div>,
);

View File

@ -3,7 +3,7 @@ import { css, useCollectionManager_deprecated, useRecord, useRequest, useToken }
import { connect } from '@tachybase/schema';
import { CollectionsGraph, lodash } from '@tachybase/utils/client';
import { App, Col, Input, Row, Select, Spin, Table, Tag } from 'antd';
import { App, Col, Input, Row, Select, Space, Spin, Table, Tag } from 'antd';
import { useTranslation } from 'react-i18next';
const excludeCollections = ['users', 'roles', 'applications'];
@ -232,7 +232,7 @@ export const TableTransfer = connect((props) => {
`}
>
<strong style={{ fontSize: token.fontSizeLG, color: token.colorText }}>{t('Unshared collections')}</strong>
<Input.Group compact style={{ width: 360 }}>
<Space.Compact style={{ width: 360 }}>
<Select
popupMatchSelectWidth={false}
onChange={(value) => {
@ -251,7 +251,7 @@ export const TableTransfer = connect((props) => {
placeholder={t('Enter name or title...')}
allowClear
/>
</Input.Group>
</Space.Compact>
</div>
<Table
bordered
@ -317,7 +317,7 @@ export const TableTransfer = connect((props) => {
`}
>
<strong style={{ fontSize: token.fontSizeLG, color: token.colorText }}>{t('Shared collections')}</strong>
<Input.Group compact style={{ width: 360 }}>
<Space.Compact style={{ width: 360 }}>
<Select
popupMatchSelectWidth={false}
onChange={(value) => {
@ -336,7 +336,7 @@ export const TableTransfer = connect((props) => {
placeholder={t('Enter name or title...')}
allowClear
/>
</Input.Group>
</Space.Compact>
</div>
<Table
bordered