chenos 883f1e6fd1
fix: eslint (#1759)
* fix: eslint

* fix: eslint --fix

* fix: changelog
2023-04-25 13:12:14 +08:00

371 lines
10 KiB
TypeScript

import React, { useState, useContext } from 'react';
import { useForm, ISchema, Schema, useFieldSchema } from '@formily/react';
import { get } from 'lodash';
import {
SchemaComponent,
SchemaComponentContext,
SchemaInitializer,
SchemaInitializerItemOptions,
InitializerWithSwitch,
SchemaInitializerProvider,
useSchemaComponentContext,
gridRowColWrap,
ActionContext,
GeneralSchemaDesigner,
SchemaSettings,
useCompile,
} from '@nocobase/client';
import { useTrigger } from '../../triggers';
import { instructions, useAvailableUpstreams, useNodeContext } from '..';
import { useFlowContext } from '../../FlowContext';
import { lang, NAMESPACE } from '../../locale';
import { JOB_STATUS } from '../../constants';
import customForm from './forms/customForm';
function useTriggerInitializers(): SchemaInitializerItemOptions | null {
const { workflow } = useFlowContext();
const trigger = useTrigger();
return trigger.useInitializers ? trigger.useInitializers(workflow.config) : null;
}
const blockTypeNames = {
customForm: customForm.title,
record: `{{t("Data record", { ns: "${NAMESPACE}" })}}`,
};
function SimpleDesigner() {
const schema = useFieldSchema();
const title = blockTypeNames[schema['x-designer-props']?.type] ?? '{{t("Block")}}';
const compile = useCompile();
return (
<GeneralSchemaDesigner title={compile(title)}>
<SchemaSettings.BlockTitleItem />
<SchemaSettings.Divider />
<SchemaSettings.Remove
removeParentsIfNoChildren
breakRemoveOn={{
'x-component': 'Grid',
}}
/>
</GeneralSchemaDesigner>
);
}
function AddBlockButton(props: any) {
const current = useNodeContext();
const nodes = useAvailableUpstreams(current);
const triggerInitializers = [useTriggerInitializers()].filter(Boolean);
const nodeBlockInitializers = nodes
.map((node) => {
const instruction = instructions.get(node.type);
return instruction?.useInitializers?.(node);
})
.filter(Boolean);
const dataBlockInitializers = [
...triggerInitializers,
...(nodeBlockInitializers.length
? [
{
key: 'nodes',
type: 'subMenu',
title: `{{t("Node result", { ns: "${NAMESPACE}" })}}`,
children: nodeBlockInitializers,
},
]
: []),
].filter(Boolean);
const items = [
...(dataBlockInitializers.length
? [
{
type: 'itemGroup',
title: '{{t("Data blocks")}}',
children: dataBlockInitializers,
},
]
: []),
{
type: 'itemGroup',
title: '{{t("Form")}}',
children: [
customForm.config.initializer,
// {
// key: 'createForm',
// type: 'item',
// title: '{{t("Create record form")}}',
// component: CustomFormBlockInitializer,
// },
// {
// key: 'updateForm',
// type: 'item',
// title: '{{t("Update record form")}}',
// component: CustomFormBlockInitializer,
// }
],
},
{
type: 'itemGroup',
title: '{{t("Other blocks")}}',
children: [
{
type: 'item',
title: '{{t("Markdown")}}',
component: 'MarkdownBlockInitializer',
},
],
},
] as SchemaInitializerItemOptions[];
return <SchemaInitializer.Button {...props} wrap={gridRowColWrap} items={items} title="{{t('Add block')}}" />;
}
function findSchema(schema, filter, onlyLeaf = false) {
const result = [];
if (!schema) {
return result;
}
if (filter(schema) && (!onlyLeaf || !schema.properties)) {
result.push(schema);
return result;
}
if (schema.properties) {
Object.keys(schema.properties).forEach((key) => {
result.push(...findSchema(schema.properties[key], filter));
});
}
return result;
}
function SchemaComponentRefreshProvider(props) {
const ctx = useSchemaComponentContext();
return (
<SchemaComponentContext.Provider
value={{
...ctx,
refresh() {
ctx?.refresh?.();
props?.onRefresh?.();
},
}}
>
{props.children}
</SchemaComponentContext.Provider>
);
}
function ActionInitializer({ action, actionProps, ...props }) {
return (
<InitializerWithSwitch
{...props}
schema={{
type: 'void',
title: props.title,
'x-decorator': 'ManualActionStatusProvider',
'x-decorator-props': {
value: action,
},
'x-component': 'Action',
'x-component-props': {
...actionProps,
useAction: '{{ useSubmit }}',
},
'x-designer': 'Action.Designer',
'x-action': `${action}`,
}}
type="x-action"
/>
);
}
function AddActionButton(props) {
return (
<SchemaInitializer.Button
{...props}
items={[
{
key: JOB_STATUS.RESOLVED,
type: 'item',
title: `{{t("Continue the process", { ns: "${NAMESPACE}" })}}`,
component: ActionInitializer,
action: JOB_STATUS.RESOLVED,
actionProps: {
type: 'primary',
},
},
{
key: JOB_STATUS.REJECTED,
type: 'item',
title: `{{t("Terminate the process", { ns: "${NAMESPACE}" })}}`,
component: ActionInitializer,
action: JOB_STATUS.REJECTED,
actionProps: {
type: 'danger',
},
},
{
key: JOB_STATUS.PENDING,
type: 'item',
title: `{{t("Save temporarily", { ns: "${NAMESPACE}" })}}`,
component: ActionInitializer,
action: JOB_STATUS.PENDING,
},
]}
title="{{t('Configure actions')}}"
/>
);
}
// NOTE: fake useAction for ui configuration
function useSubmit() {
// const { values, submit, id: formId } = useForm();
// const formSchema = useFieldSchema();
return {
run() {},
};
}
function useFlowRecordFromBlock() {
return {};
}
export function SchemaConfig({ value, onChange }) {
const ctx = useContext(SchemaComponentContext);
const trigger = useTrigger();
const node = useNodeContext();
const nodes = useAvailableUpstreams(node);
const form = useForm();
const { workflow } = useFlowContext();
const nodeInitializers = {};
const nodeComponents = {};
nodes.forEach((item) => {
const instruction = instructions.get(item.type);
Object.assign(nodeInitializers, instruction.initializers);
Object.assign(nodeComponents, instruction.components);
});
const schema = new Schema({
properties: {
drawer: {
type: 'void',
title: '{{t("Configure form")}}',
'x-decorator': 'Form',
'x-component': 'Action.Drawer',
'x-component-props': {
className: 'nb-action-popup',
},
properties: {
tabs: {
type: 'void',
'x-component': 'Tabs',
'x-component-props': {},
'x-initializer': 'TabPaneInitializers',
'x-initializer-props': {
gridInitializer: 'AddBlockButton',
},
properties: value ?? {
tab1: {
type: 'void',
title: `{{t("Manual", { ns: "${NAMESPACE}" })}}`,
'x-component': 'Tabs.TabPane',
'x-designer': 'Tabs.Designer',
properties: {
grid: {
type: 'void',
'x-component': 'Grid',
'x-initializer': 'AddBlockButton',
properties: {},
},
},
},
},
},
},
},
},
});
return (
<SchemaComponentContext.Provider value={{ ...ctx, designable: !workflow.executed }}>
<SchemaInitializerProvider
initializers={{
AddBlockButton,
AddActionButton,
...trigger.initializers,
...nodeInitializers,
...customForm.config.initializers,
}}
>
<SchemaComponentRefreshProvider
onRefresh={() => {
const forms = {};
const { tabs } = get(schema.toJSON(), 'properties.drawer.properties') as { tabs: ISchema };
const formBlocks: any[] = findSchema(tabs, (item) => item['x-decorator'] === 'FormCollectionProvider');
formBlocks.forEach((formBlock) => {
const [formKey] = Object.keys(formBlock.properties);
const formSchema = formBlock.properties[formKey];
const fields = findSchema(
formSchema.properties.grid,
(item) => item['x-component'] === 'CollectionField',
true,
);
formBlock['x-decorator-props'].collection.fields = fields.map((field) => field['x-interface-options']);
forms[formKey] = {
type: 'custom',
title: formBlock['x-component-props']?.title || formKey,
actions: findSchema(formSchema.properties.actions, (item) => item['x-component'] === 'Action').map(
(item) => item['x-decorator-props'].value,
),
collection: formBlock['x-decorator-props'].collection,
};
});
form.setValuesIn('forms', forms);
onChange(tabs.properties);
}}
>
<SchemaComponent
schema={schema}
components={{
...nodeComponents,
...customForm.config.components,
// NOTE: fake provider component
ManualActionStatusProvider(props) {
return props.children;
},
ActionBarProvider(props) {
return props.children;
},
SimpleDesigner,
}}
scope={{
useSubmit,
useFlowRecordFromBlock,
}}
/>
</SchemaComponentRefreshProvider>
</SchemaInitializerProvider>
</SchemaComponentContext.Provider>
);
}
export function SchemaConfigButton(props) {
const { workflow } = useFlowContext();
const [visible, setVisible] = useState(false);
return (
<>
<div className="ant-btn ant-btn-primary" onClick={() => setVisible(true)}>
{workflow.executed ? lang('View user interface') : lang('Configure user interface')}
</div>
<ActionContext.Provider value={{ visible, setVisible }}>{props.children}</ActionContext.Provider>
</>
);
}