diff --git a/packages/core/client/src/schema-component/antd/variable/TextArea.tsx b/packages/core/client/src/schema-component/antd/variable/TextArea.tsx index 0952d742f..d888117a6 100644 --- a/packages/core/client/src/schema-component/antd/variable/TextArea.tsx +++ b/packages/core/client/src/schema-component/antd/variable/TextArea.tsx @@ -2,7 +2,7 @@ import { css, cx } from '@emotion/css'; import { useForm } from '@formily/react'; import { Input } from 'antd'; import { cloneDeep } from 'lodash'; -import React, { useEffect, useMemo, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import sanitizeHTML from 'sanitize-html'; import { error } from '@nocobase/utils/client'; @@ -228,26 +228,30 @@ export function TextArea(props) { } const sel = window.getSelection?.(); if (sel) { - const children = Array.from(current.childNodes) as HTMLElement[]; - if (children.length) { - if (range[0] === -1) { - if (range[1]) { - nextRange.setStartAfter(children[range[1] - 1]); + try { + const children = Array.from(current.childNodes) as HTMLElement[]; + if (children.length) { + if (range[0] === -1) { + if (range[1]) { + nextRange.setStartAfter(children[range[1] - 1]); + } + } else { + nextRange.setStart(children[range[0]], range[1]); } - } else { - nextRange.setStart(children[range[0]], range[1]); - } - if (range[2] === -1) { - if (range[3]) { - nextRange.setEndAfter(children[range[3] - 1]); + if (range[2] === -1) { + if (range[3]) { + nextRange.setEndAfter(children[range[3] - 1]); + } + } else { + nextRange.setEnd(children[range[2]], range[3]); } - } else { - nextRange.setEnd(children[range[2]], range[3]); } + nextRange.collapse(true); + sel.removeAllRanges(); + sel.addRange(nextRange); + } catch (ex) { + // console.error(ex); } - nextRange.collapse(true); - sel.removeAllRanges(); - sel.addRange(nextRange); } } else { const { lastChild } = current; @@ -262,39 +266,45 @@ export function TextArea(props) { } }, [html]); - function onInsert(paths: string[]) { - const variable: string[] = paths.filter((key) => Boolean(key.trim())); - const { current } = inputRef; - if (!current || !variable) { - return; - } + const onInsert = useCallback( + function (paths: string[]) { + const variable: string[] = paths.filter((key) => Boolean(key.trim())); + const { current } = inputRef; + if (!current || !variable) { + return; + } - current.focus(); + current.focus(); - const content = createVariableTagHTML(variable.join('.'), keyLabelMap); - pasteHTML(current, content, { - range, - }); + const content = createVariableTagHTML(variable.join('.'), keyLabelMap); + pasteHTML(current, content, { + range, + }); - setChanged(true); - setRange(getCurrentRange(current)); - onChange(getValue(current)); - } + setChanged(true); + setRange(getCurrentRange(current)); + onChange(getValue(current)); + }, + [keyLabelMap, onChange, range], + ); - function onInput({ currentTarget }) { - if (ime) { - return; - } - setChanged(true); + const onInput = useCallback( + function ({ currentTarget }) { + if (ime) { + return; + } + setChanged(true); + setRange(getCurrentRange(currentTarget)); + onChange(getValue(currentTarget)); + }, + [ime, onChange], + ); + + const onBlur = useCallback(function ({ currentTarget }) { setRange(getCurrentRange(currentTarget)); - onChange(getValue(currentTarget)); - } + }, []); - function onBlur({ currentTarget }) { - setRange(getCurrentRange(currentTarget)); - } - - function onKeyDown(ev) { + const onKeyDown = useCallback(function (ev) { if (ev.key === 'Enter') { ev.preventDefault(); } @@ -305,37 +315,40 @@ export function TextArea(props) { // if (ev.key === 'Alt') { // console.debug(getCurrentRange(ev.currentTarget)); // } - } + }, []); - function onPaste(ev) { - ev.preventDefault(); - const input = ev.clipboardData.getData('text/html') || ev.clipboardData.getData('text'); - const sanitizedHTML = sanitizeHTML(input, { - allowedTags: ['span'], - allowedAttributes: { - span: ['data-variable', 'contenteditable'], - }, - allowedClasses: { - span: ['ant-tag', 'ant-tag-*'], - }, - transformTags: { - span(tagName, attribs) { - return attribs['data-variable'] - ? { - tagName: tagName, - attribs, - } - : {}; + const onPaste = useCallback( + function (ev) { + ev.preventDefault(); + const input = ev.clipboardData.getData('text/html') || ev.clipboardData.getData('text'); + const sanitizedHTML = sanitizeHTML(input, { + allowedTags: ['span'], + allowedAttributes: { + span: ['data-variable', 'contenteditable'], }, - }, - }).replace(/\n/g, ' '); - // ev.clipboardData.setData('text/html', sanitizedHTML); - // console.log(input, sanitizedHTML); - setChanged(true); - pasteHTML(ev.currentTarget, sanitizedHTML); - setRange(getCurrentRange(ev.currentTarget)); - onChange(getValue(ev.currentTarget)); - } + allowedClasses: { + span: ['ant-tag', 'ant-tag-*'], + }, + transformTags: { + span(tagName, attribs) { + return attribs['data-variable'] + ? { + tagName: tagName, + attribs, + } + : {}; + }, + }, + }).replace(/\n/g, ' '); + // ev.clipboardData.setData('text/html', sanitizedHTML); + // console.log(input, sanitizedHTML); + setChanged(true); + pasteHTML(ev.currentTarget, sanitizedHTML); + setRange(getCurrentRange(ev.currentTarget)); + onChange(getValue(ev.currentTarget)); + }, + [onChange], + ); const disabled = props.disabled || form.disabled; diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/index.tsx b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/index.tsx index cb9ebe945..9589baf2a 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/index.tsx +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useContext, useMemo, useState } from 'react'; +import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react'; import { DeleteOutlined } from '@ant-design/icons'; import { cloneDeep } from 'lodash'; import { createForm } from '@formily/core'; @@ -8,6 +8,7 @@ import { useTranslation } from 'react-i18next'; import { ActionContextProvider, + FormProvider, SchemaComponent, SchemaInitializerItemOptions, css, @@ -273,6 +274,10 @@ export function JobButton() { ); } +function useNodeFormProps() { + return { form: useForm() }; +} + export function NodeDefaultView(props) { const { data, children } = props; const compile = useCompile(); @@ -292,15 +297,14 @@ export function NodeDefaultView(props) { const values = cloneDeep(data.config); return createForm({ initialValues: values, - values, disabled: workflow.executed, }); }, [data, workflow]); const resetForm = useCallback( - (changed) => { - setFormValueChanged(changed); - if (!changed) { + (editing) => { + setEditingConfig(editing); + if (!editing) { form.reset(); } }, @@ -366,123 +370,129 @@ export function NodeDefaultView(props) { - - {data.title} - - - {data.key} - - - - ), - 'x-decorator': 'FormV2', - 'x-decorator-props': { - form, - }, - 'x-component': 'Action.Drawer', - properties: { - ...(instruction.description - ? { - description: { - type: 'void', - 'x-component': DrawerDescription, - 'x-component-props': { - label: lang('Node type'), - title: instruction.title, - description: instruction.description, - }, - }, - } - : {}), - fieldset: { - type: 'void', - 'x-component': 'fieldset', - 'x-component-props': { - className: css` - .ant-input, - .ant-select, - .ant-cascader-picker, - .ant-picker, - .ant-input-number, - .ant-input-affix-wrapper { - &.auto-width { - width: auto; - min-width: 6em; - } - } - `, - }, - properties: instruction.fieldset, + + + {data.title} + + + {data.key} + + + + ), + 'x-decorator': 'FormV2', + 'x-decorator-props': { + // form, + useProps: '{{ useNodeFormProps }}', + }, + 'x-component': 'Action.Drawer', + properties: { + ...(instruction.description + ? { + description: { + type: 'void', + 'x-component': DrawerDescription, 'x-component-props': { - useAction: '{{ cm.useCancelAction }}', + label: lang('Node type'), + title: instruction.title, + description: instruction.description, }, }, - submit: { - title: '{{t("Submit")}}', - 'x-component': 'Action', - 'x-component-props': { - type: 'primary', - useAction: useUpdateAction, + } + : {}), + fieldset: { + type: 'void', + 'x-component': 'fieldset', + 'x-component-props': { + className: css` + .ant-input, + .ant-select, + .ant-cascader-picker, + .ant-picker, + .ant-input-number, + .ant-input-affix-wrapper { + &.auto-width { + width: auto; + min-width: 6em; + } + } + `, + }, + properties: instruction.fieldset, + }, + actions: workflow.executed + ? null + : { + type: 'void', + 'x-component': 'Action.Drawer.Footer', + properties: { + cancel: { + title: '{{t("Cancel")}}', + 'x-component': 'Action', + 'x-component-props': { + useAction: '{{ cm.useCancelAction }}', + }, + }, + submit: { + title: '{{t("Submit")}}', + 'x-component': 'Action', + 'x-component-props': { + type: 'primary', + useAction: useUpdateAction, + }, }, }, }, - }, - }, - } as ISchema, - }, - }} - /> + }, + } as ISchema, + }, + }} + /> + {children} diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/manual/SchemaConfig.tsx b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/manual/SchemaConfig.tsx index b5aa34e1d..b9ea4e8ef 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/manual/SchemaConfig.tsx +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/manual/SchemaConfig.tsx @@ -1,8 +1,8 @@ +import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react'; import { FormLayout } from '@formily/antd-v5'; import { createForm } from '@formily/core'; import { FormProvider, ISchema, Schema, useFieldSchema, useForm } from '@formily/react'; import { Alert, Button, Modal, Space } from 'antd'; -import React, { useContext, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { @@ -450,22 +450,27 @@ export function SchemaConfig({ value, onChange }) { [], ); + const refresh = useCallback( + function refresh() { + // ctx.refresh?.(); + const { tabs } = lodash.get(schema.toJSON(), 'properties.drawer.properties') as { tabs: ISchema }; + const forms = Array.from(manualFormTypes.getValues()).reduce( + (result, item: ManualFormType) => Object.assign(result, item.config.parseFormOptions(tabs)), + {}, + ); + form.setValuesIn('forms', forms); + + onChange(tabs.properties); + }, + [form, onChange, schema], + ); + return ( Object.assign(result, item.config.parseFormOptions(tabs)), - {}, - ); - form.setValuesIn('forms', forms); - - onChange(tabs.properties); - }, + refresh, }} > setVisible(true)}> {workflow.executed ? lang('View user interface') : lang('Configure user interface')} - {props.children} + + {props.children} + ); }