feat(client): add form disabled context (#1374)

* feat(client): add form disabled context

* fix: improve code

* fix: improve code

* fix: form-v2 disabled

Co-authored-by: chenos <chenlinxh@gmail.com>
This commit is contained in:
Junyi 2023-01-16 23:01:28 +08:00 committed by GitHub
parent 246ee04154
commit 00efb38438
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 147 additions and 115 deletions

View File

@ -1,7 +1,7 @@
import { CloseCircleOutlined } from '@ant-design/icons';
import { ObjectField as ObjectFieldModel } from '@formily/core';
import { ArrayField, connect, useField } from '@formily/react';
import { Select, Space } from 'antd';
import { ConfigProvider, Select, Space } from 'antd';
import React, { useContext } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { FilterLogicContext, RemoveConditionContext } from './context';
@ -20,84 +20,89 @@ export const FilterGroup = connect((props) => {
[value]: [...(obj[logic] || [])],
};
};
const mergedDisabled = field.disabled;
return (
<FilterLogicContext.Provider value={logic}>
<div
style={
bordered
? {
position: 'relative',
border: '1px dashed #dedede',
padding: 14,
marginBottom: 8,
}
: {
position: 'relative',
marginBottom: 8,
}
}
>
{remove && (
<a>
<CloseCircleOutlined
style={{
position: 'absolute',
right: 10,
top: 10,
color: '#bfbfbf',
}}
onClick={() => remove()}
/>
</a>
)}
<div style={{ marginBottom: 8 }}>
<Trans>
{'Meet '}
<Select
style={{ width: 'auto' }}
value={logic}
onChange={(value) => {
setLogic(value);
}}
>
<Select.Option value={'$and'}>All</Select.Option>
<Select.Option value={'$or'}>Any</Select.Option>
</Select>
{' conditions in the group'}
</Trans>
<ConfigProvider componentDisabled={mergedDisabled}>
<FilterLogicContext.Provider value={logic}>
<div
style={
bordered
? {
position: 'relative',
border: '1px dashed #dedede',
padding: 14,
marginBottom: 8,
}
: {
position: 'relative',
marginBottom: 8,
}
}
>
{remove && !mergedDisabled && (
<a>
<CloseCircleOutlined
style={{
position: 'absolute',
right: 10,
top: 10,
color: '#bfbfbf',
}}
onClick={() => remove()}
/>
</a>
)}
<div style={{ marginBottom: 8 }}>
<Trans>
{'Meet '}
<Select
style={{ width: 'auto' }}
value={logic}
onChange={(value) => {
setLogic(value);
}}
>
<Select.Option value={'$and'}>All</Select.Option>
<Select.Option value={'$or'}>Any</Select.Option>
</Select>
{' conditions in the group'}
</Trans>
</div>
<div>
<ArrayField name={logic} component={[FilterItems]} disabled={mergedDisabled} />
</div>
{!mergedDisabled && (
<Space size={16} style={{ marginTop: 8, marginBottom: 8 }}>
<a
onClick={() => {
const value = field.value || {};
const items = value[logic] || [];
items.push({});
field.value = {
[logic]: items,
};
}}
>
{t('Add condition')}
</a>
<a
onClick={() => {
const value = field.value || {};
const items = value[logic] || [];
items.push({
$and: [{}],
});
field.value = {
[logic]: items,
};
}}
>
{t('Add condition group')}
</a>
</Space>
)}
</div>
<div>
<ArrayField name={logic} component={[FilterItems]} />
</div>
<Space size={16} style={{ marginTop: 8, marginBottom: 8 }}>
<a
onClick={() => {
const value = field.value || {};
const items = value[logic] || [];
items.push({});
field.value = {
[logic]: items,
};
}}
>
{t('Add condition')}
</a>
<a
onClick={() => {
const value = field.value || {};
const items = value[logic] || [];
items.push({
$and: [{}],
});
field.value = {
[logic]: items,
};
}}
>
{t('Add condition group')}
</a>
</Space>
</div>
</FilterLogicContext.Provider>
</FilterLogicContext.Provider>
</ConfigProvider>
);
});

View File

@ -50,9 +50,11 @@ export const FilterItem = observer((props: any) => {
setValue(value);
},
})}
<a>
<CloseCircleOutlined onClick={() => remove()} style={{ color: '#bfbfbf' }} />
</a>
{!props.disabled && (
<a>
<CloseCircleOutlined onClick={() => remove()} style={{ color: '#bfbfbf' }} />
</a>
)}
</Space>
</div>
);

View File

@ -2,7 +2,7 @@ import { FormLayout } from '@formily/antd';
import { createForm, Field, onFormInputChange } from '@formily/core';
import { FieldContext, FormContext, observer, RecursionField, useField, useFieldSchema } from '@formily/react';
import { uid } from '@formily/shared';
import { Spin } from 'antd';
import { ConfigProvider, Spin } from 'antd';
import React, { useEffect, useMemo } from 'react';
import { useActionContext } from '..';
import { useAttach, useComponent } from '../..';
@ -32,7 +32,7 @@ const FormComponent: React.FC<FormProps> = (props) => {
const Def = (props: any) => props.children;
const FormDecorator: React.FC<FormProps> = (props) => {
const { form, children, ...others } = props;
const { form, children, disabled, ...others } = props;
const field = useField();
const fieldSchema = useFieldSchema();
// TODO: component 里 useField 会与当前 field 存在偏差
@ -65,6 +65,7 @@ const WithForm = (props) => {
setFormValueChanged?.(true);
});
});
form.disabled = props.disabled;
return () => {
form.removeEffects(id);
};
@ -78,6 +79,7 @@ const WithoutForm = (props) => {
const form = useMemo(
() =>
createForm({
disabled: props.disabled,
effects() {
onFormInputChange((form) => {
setFormValueChanged?.(true);
@ -95,12 +97,19 @@ const WithoutForm = (props) => {
export const Form: React.FC<FormProps> & { Designer?: any; ReadPrettyDesigner?: any } = observer((props) => {
const field = useField<Field>();
const { form, ...others } = useProps(props);
const { form, disabled, ...others } = useProps(props);
const formDisabled = disabled || field.disabled;
return (
<form>
<Spin spinning={field.loading || false}>
{form ? <WithForm form={form} {...others} /> : <WithoutForm {...others} />}
</Spin>
</form>
<ConfigProvider componentDisabled={formDisabled}>
<form>
<Spin spinning={field.loading || false}>
{form ? (
<WithForm form={form} {...others} disabled={formDisabled} />
) : (
<WithoutForm {...others} disabled={formDisabled} />
)}
</Spin>
</form>
</ConfigProvider>
);
});

View File

@ -2,8 +2,8 @@ import { FormLayout } from '@formily/antd';
import { createForm } from '@formily/core';
import { FieldContext, FormContext, observer, RecursionField, useField, useFieldSchema } from '@formily/react';
import { Options, Result } from 'ahooks/lib/useRequest/src/types';
import { Spin } from 'antd';
import React, { createContext, useContext, useMemo } from 'react';
import { ConfigProvider, Spin } from 'antd';
import React, { createContext, useContext, useEffect, useMemo } from 'react';
import { useAttach, useComponent } from '../..';
import { useRequest } from '../../../api-client';
import { useCollection } from '../../../collection-manager';
@ -38,25 +38,30 @@ const FormComponent: React.FC<FormProps> = (props) => {
const Def = (props: any) => props.children;
const FormDecorator: React.FC<FormProps> = (props) => {
const { form, children, ...others } = props;
const { form, children, disabled, ...others } = props;
const field = useField();
const fieldSchema = useFieldSchema();
// TODO: component 里 useField 会与当前 field 存在偏差
const f = useAttach(form.createVoidField({ ...field.props, basePath: '' }));
const Component = useComponent(fieldSchema['x-component'], Def);
useEffect(() => {
form.disabled = disabled || field.disabled;
}, [disabled, field.disabled]);
return (
<FieldContext.Provider value={undefined}>
<FormContext.Provider value={form}>
<FormLayout layout={'vertical'} {...others}>
<FieldContext.Provider value={f}>
<Component {...field.componentProps}>
<RecursionField basePath={f.address} schema={fieldSchema} onlyRenderProperties />
</Component>
</FieldContext.Provider>
{/* <FieldContext.Provider value={f}>{children}</FieldContext.Provider> */}
</FormLayout>
</FormContext.Provider>
</FieldContext.Provider>
<ConfigProvider componentDisabled={disabled}>
<FieldContext.Provider value={undefined}>
<FormContext.Provider value={form}>
<FormLayout layout={'vertical'} {...others}>
<FieldContext.Provider value={f}>
<Component {...field.componentProps}>
<RecursionField basePath={f.address} schema={fieldSchema} onlyRenderProperties />
</Component>
</FieldContext.Provider>
{/* <FieldContext.Provider value={f}>{children}</FieldContext.Provider> */}
</FormLayout>
</FormContext.Provider>
</FieldContext.Provider>
</ConfigProvider>
);
};

View File

@ -5,7 +5,7 @@ import {
} from '@ant-design/icons';
import { css, cx } from '@emotion/css';
import { ISchema, useForm } from '@formily/react';
import { Button, message, Modal, Tag } from 'antd';
import { Button, message, Modal, Tag, Alert } from 'antd';
import { useTranslation } from 'react-i18next';
import parse from 'json-templates';
@ -334,6 +334,7 @@ export function NodeDefaultView(props) {
'x-component': 'Action.Drawer',
'x-decorator': 'Form',
'x-decorator-props': {
disabled: workflow.executed,
useValues(options) {
const d = useNodeContext();
return useRequest(() => {
@ -354,7 +355,6 @@ export function NodeDefaultView(props) {
name: 'config',
'x-component': 'fieldset',
'x-component-props': {
disabled: workflow.executed,
className: css`
.ant-select,
.ant-cascader-picker,
@ -373,11 +373,16 @@ export function NodeDefaultView(props) {
'x-component': 'Action.Drawer.Footer',
properties: workflow.executed
? {
close: {
title: '{{t("Close")}}',
'x-component': 'Action',
alert: {
'x-component': Alert,
'x-component-props': {
useAction: '{{ cm.useCancelAction }}',
type: 'warning',
showIcon: true,
message: `{{t("Node in executed workflow cannot be modified", { ns: "${NAMESPACE}" })}}`,
className: css`
width: 100%;
font-size: 85%;
`
},
}
}

View File

@ -1,7 +1,7 @@
import { css, cx } from "@emotion/css";
import { ISchema, useForm } from "@formily/react";
import { Registry } from "@nocobase/utils/client";
import { message, Tag } from "antd";
import { message, Tag, Alert } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { InfoOutlined } from '@ant-design/icons';
@ -159,6 +159,7 @@ export const TriggerConfig = () => {
'x-component': 'Action.Drawer',
'x-decorator': 'Form',
'x-decorator-props': {
disabled: workflow.executed,
useValues(options) {
return useRequest(() => Promise.resolve({
data: { config },
@ -185,11 +186,16 @@ export const TriggerConfig = () => {
'x-component': 'Action.Drawer.Footer',
properties: executed
? {
close: {
title: '{{t("Close")}}',
'x-component': 'Action',
alert: {
'x-component': Alert,
'x-component-props': {
useAction: '{{ cm.useCancelAction }}',
type: 'warning',
showIcon: true,
message: `{{t("Trigger in executed workflow cannot be modified", { ns: "${NAMESPACE}" })}}`,
className: css`
width: 100%;
font-size: 85%;
`
},
}
}