import React, { useState, useContext, useMemo } from 'react'; import { useForm, ISchema, Schema, useFieldSchema } from '@formily/react'; import { get } from 'lodash'; import { SchemaComponent, SchemaComponentContext, SchemaInitializer, SchemaInitializerItemOptions, InitializerWithSwitch, SchemaInitializerProvider, gridRowColWrap, ActionContext, GeneralSchemaDesigner, SchemaSettings, useCompile, } from '@nocobase/client'; import { Registry } from '@nocobase/utils/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/custom'; import createForm from './forms/create'; import updateForm from './forms/update'; import { FormBlockProvider } from './FormBlockProvider'; type ValueOf = T[keyof T]; export type FormType = { type: 'create' | 'update' | 'custom'; title: string; actions: ValueOf[]; collection: | string | { name: string; fields: any[]; [key: string]: any; }; }; export type ManualFormType = { title: string; config: { useInitializer: () => SchemaInitializerItemOptions; initializers?: { [key: string]: React.FC; }; components?: { [key: string]: React.FC; }; parseFormOptions(root: ISchema): { [key: string]: FormType }; }; block: { scope?: { [key: string]: () => any; }; components?: { [key: string]: React.FC; }; }; }; export const manualFormTypes = new Registry(); manualFormTypes.register('customForm', customForm); manualFormTypes.register('createForm', createForm); manualFormTypes.register('updateForm', updateForm); 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 ( ); } 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: Array.from(manualFormTypes.getValues()).map((item) => { const { useInitializer: getInitializer } = item.config; return getInitializer(); }), }, { type: 'itemGroup', title: '{{t("Other blocks")}}', children: [ { type: 'item', title: '{{t("Markdown")}}', component: 'MarkdownBlockInitializer', }, ], }, ] as SchemaInitializerItemOptions[]; return ; } function ActionInitializer({ action, actionProps, ...props }) { return ( ); } function AddActionButton(props) { return ( ); } // 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 = useMemo( () => 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 ( Object.assign(result, item.config.parseFormOptions(tabs)), {}, ); form.setValuesIn('forms', forms); onChange(tabs.properties); }, }} > Object.assign(result, item.config.initializers), {}, ), }} > Object.assign(result, item.config.components), {}, ), FormBlockProvider, // NOTE: fake provider component ManualActionStatusProvider(props) { return props.children; }, ActionBarProvider(props) { return props.children; }, SimpleDesigner, }} scope={{ useSubmit, useFlowRecordFromBlock, }} /> ); } export function SchemaConfigButton(props) { const { workflow } = useFlowContext(); const [visible, setVisible] = useState(false); return ( <>
setVisible(true)}> {workflow.executed ? lang('View user interface') : lang('Configure user interface')}
{props.children} ); }