diff --git a/packages/client/src/acl/ACLShortcut.tsx b/packages/client/src/acl/ACLShortcut.tsx index cb2f4397d..126826d48 100644 --- a/packages/client/src/acl/ACLShortcut.tsx +++ b/packages/client/src/acl/ACLShortcut.tsx @@ -3,11 +3,11 @@ import { ISchema, useForm } from '@formily/react'; import { uid } from '@formily/shared'; import React, { useState } from 'react'; import { PluginManager } from '../plugin-manager'; -import { SchemaComponent, useActionVisible, VisibleContext } from '../schema-component'; +import { ActionContext, SchemaComponent, useActionContext } from '../schema-component'; import { RoleTable } from './RolePermissionManager'; const useCloseAction = () => { - const { setVisible } = useActionVisible(); + const { setVisible } = useActionContext(); const form = useForm(); return { async run() { @@ -39,7 +39,7 @@ const schema: ISchema = { export const ACLShortcut = () => { const [visible, setVisible] = useState(false); return ( - + } title={'角色和权限'} @@ -48,6 +48,6 @@ export const ACLShortcut = () => { }} /> - + ); }; diff --git a/packages/client/src/collection-manager/CollectionManagerShortcut.tsx b/packages/client/src/collection-manager/CollectionManagerShortcut.tsx index 46de0c19f..2604d2aa2 100644 --- a/packages/client/src/collection-manager/CollectionManagerShortcut.tsx +++ b/packages/client/src/collection-manager/CollectionManagerShortcut.tsx @@ -3,11 +3,11 @@ import { ISchema, useForm } from '@formily/react'; import { uid } from '@formily/shared'; import React, { useState } from 'react'; import { PluginManager } from '../plugin-manager'; -import { SchemaComponent, useActionVisible, VisibleContext } from '../schema-component'; +import { ActionContext, SchemaComponent, useActionContext } from '../schema-component'; import { ConfigurationTable } from './Configuration'; const useCloseAction = () => { - const { setVisible } = useActionVisible(); + const { setVisible } = useActionContext(); const form = useForm(); return { async run() { @@ -38,7 +38,7 @@ const schema: ISchema = { export const CollectionManagerShortcut = () => { const [visible, setVisible] = useState(false); return ( - + } title={'数据表配置'} @@ -47,6 +47,6 @@ export const CollectionManagerShortcut = () => { }} /> - + ); }; diff --git a/packages/client/src/plugin-manager/demos/plugins/plugin2.tsx b/packages/client/src/plugin-manager/demos/plugins/plugin2.tsx index eb809bc4a..62fa627a8 100644 --- a/packages/client/src/plugin-manager/demos/plugins/plugin2.tsx +++ b/packages/client/src/plugin-manager/demos/plugins/plugin2.tsx @@ -1,10 +1,10 @@ -import React, { useState } from 'react'; import { VerifiedOutlined } from '@ant-design/icons'; import { ISchema, useForm } from '@formily/react'; -import { PluginManager, SchemaComponent, useActionVisible, VisibleContext } from '@nocobase/client'; +import { ActionContext, PluginManager, SchemaComponent, useActionContext } from '@nocobase/client'; +import React, { useState } from 'react'; const useCloseAction = () => { - const { setVisible } = useActionVisible(); + const { setVisible } = useActionContext(); const form = useForm(); return { async run() { @@ -51,7 +51,7 @@ export const Plugin2 = () => null; Plugin2.ToolbarItem = () => { const [visible, setVisible] = useState(false); return ( - + } title={'Plugin2'} @@ -60,6 +60,6 @@ Plugin2.ToolbarItem = () => { }} /> - + ); }; diff --git a/packages/client/src/plugin-manager/demos/plugins/plugin4.tsx b/packages/client/src/plugin-manager/demos/plugins/plugin4.tsx index 637317688..7ff6d014e 100644 --- a/packages/client/src/plugin-manager/demos/plugins/plugin4.tsx +++ b/packages/client/src/plugin-manager/demos/plugins/plugin4.tsx @@ -1,10 +1,10 @@ -import React, { useState } from 'react'; import { VerifiedOutlined } from '@ant-design/icons'; import { ISchema, useForm } from '@formily/react'; -import { PluginManager, SchemaComponent, useActionVisible, VisibleContext } from '@nocobase/client'; +import { ActionContext, PluginManager, SchemaComponent, useActionContext } from '@nocobase/client'; +import React, { useState } from 'react'; const useCloseAction = () => { - const { setVisible } = useActionVisible(); + const { setVisible } = useActionContext(); const form = useForm(); return { async run() { @@ -51,7 +51,7 @@ export const Plugin4 = () => null; Plugin4.ToolbarItem = () => { const [visible, setVisible] = useState(false); return ( - + } title={'Plugin4'} @@ -60,6 +60,6 @@ Plugin4.ToolbarItem = () => { }} /> - + ); }; diff --git a/packages/client/src/plugin-manager/index.md b/packages/client/src/plugin-manager/index.md index 2aada8867..1923e7ab8 100644 --- a/packages/client/src/plugin-manager/index.md +++ b/packages/client/src/plugin-manager/index.md @@ -44,7 +44,7 @@ Plugin1.ToolbarItem = () => { Plugin2.ToolbarItem = () => { const [visible, setVisible] = useState(false); return ( - + } title={'Plugin2'} @@ -53,7 +53,7 @@ Plugin2.ToolbarItem = () => { }} /> - + ); }; ``` diff --git a/packages/client/src/schema-component/antd/action/Action.Container.tsx b/packages/client/src/schema-component/antd/action/Action.Container.tsx new file mode 100644 index 000000000..ef09c4c9f --- /dev/null +++ b/packages/client/src/schema-component/antd/action/Action.Container.tsx @@ -0,0 +1,26 @@ +import { observer, RecursionField, useField, useFieldSchema } from '@formily/react'; +import React from 'react'; +import { useActionContext } from '.'; +import { ActionDrawer } from './Action.Drawer'; +import { ActionModal } from './Action.Modal'; +import { ActionPage } from './Action.Page'; +import { ComposedActionDrawer } from './types'; + +export const ActionContainer: ComposedActionDrawer = observer((props: any) => { + const { openMode } = useActionContext(); + if (openMode === 'drawer') { + return ; + } + if (openMode === 'modal') { + return ; + } + return ; +}); + +ActionContainer.Footer = observer(() => { + const field = useField(); + const schema = useFieldSchema(); + return ; +}); + +export default ActionContainer; diff --git a/packages/client/src/schema-component/antd/action/Action.Drawer.tsx b/packages/client/src/schema-component/antd/action/Action.Drawer.tsx index 254179e8d..4cd34d4e8 100644 --- a/packages/client/src/schema-component/antd/action/Action.Drawer.tsx +++ b/packages/client/src/schema-component/antd/action/Action.Drawer.tsx @@ -1,17 +1,18 @@ import { css } from '@emotion/css'; import { observer, RecursionField, useField, useFieldSchema } from '@formily/react'; import { Drawer } from 'antd'; -import React, { useContext } from 'react'; +import React from 'react'; import { createPortal } from 'react-dom'; -import { VisibleContext } from './context'; +import { useActionContext } from './hooks'; import { ComposedActionDrawer } from './types'; export const ActionDrawer: ComposedActionDrawer = observer((props) => { - const [visible, setVisible] = useContext(VisibleContext); + const { footerNodeName = 'Action.Drawer.Footer', ...others } = props; + const { visible, setVisible } = useActionContext(); const schema = useFieldSchema(); const field = useField(); const footerSchema = schema.reduceProperties((buf, s) => { - if (s['x-component'] === 'Action.Drawer.Footer') { + if (s['x-component'] === footerNodeName) { return s; } return buf; @@ -22,7 +23,7 @@ export const ActionDrawer: ComposedActionDrawer = observer((props) => { setVisible(false)} @@ -43,7 +44,7 @@ export const ActionDrawer: ComposedActionDrawer = observer((props) => { schema={schema} onlyRenderProperties filterProperties={(s) => { - return s['x-component'] === 'Action.Drawer.Footer'; + return s['x-component'] === footerNodeName; }} /> @@ -55,7 +56,7 @@ export const ActionDrawer: ComposedActionDrawer = observer((props) => { schema={schema} onlyRenderProperties filterProperties={(s) => { - return s['x-component'] !== 'Action.Drawer.Footer'; + return s['x-component'] !== footerNodeName; }} /> , diff --git a/packages/client/src/schema-component/antd/action/Action.Modal.tsx b/packages/client/src/schema-component/antd/action/Action.Modal.tsx new file mode 100644 index 000000000..42a2819b1 --- /dev/null +++ b/packages/client/src/schema-component/antd/action/Action.Modal.tsx @@ -0,0 +1,75 @@ +import { css } from '@emotion/css'; +import { observer, RecursionField, useField, useFieldSchema } from '@formily/react'; +import { Modal } from 'antd'; +import React from 'react'; +import { createPortal } from 'react-dom'; +import { useActionContext } from '.'; +import { ComposedActionDrawer } from './types'; + +export const ActionModal: ComposedActionDrawer = observer((props) => { + const { footerNodeName = 'Action.Modal.Footer', ...others } = props; + const { visible, setVisible } = useActionContext(); + const schema = useFieldSchema(); + const field = useField(); + const footerSchema = schema.reduceProperties((buf, s) => { + if (s['x-component'] === footerNodeName) { + return s; + } + return buf; + }); + return ( + <> + {createPortal( + setVisible(false)} + footer={ + footerSchema && ( +
+ { + return s['x-component'] === footerNodeName; + }} + /> +
+ ) + } + > + { + return s['x-component'] !== footerNodeName; + }} + /> +
, + document.body, + )} + + ); +}); + +ActionModal.Footer = observer(() => { + const field = useField(); + const schema = useFieldSchema(); + return ; +}); + +export default ActionModal; diff --git a/packages/client/src/schema-component/antd/action/Action.Page.tsx b/packages/client/src/schema-component/antd/action/Action.Page.tsx new file mode 100644 index 000000000..c4c3182c1 --- /dev/null +++ b/packages/client/src/schema-component/antd/action/Action.Page.tsx @@ -0,0 +1,74 @@ +import { css } from '@emotion/css'; +import { observer, RecursionField, SchemaExpressionScopeContext, useField, useFieldSchema } from '@formily/react'; +import React, { useContext } from 'react'; +import { createPortal } from 'react-dom'; +import { useActionContext } from '.'; +import { ComposedActionDrawer } from './types'; + +const useScope = (key: string) => { + const scope = useContext(SchemaExpressionScopeContext); + return scope[key]; +}; + +export const ActionPage: ComposedActionDrawer = observer((props: any) => { + const { footerNodeName = 'Action.Page.Footer', ...others } = props; + const { containerRefKey, visible, setVisible } = useActionContext(); + const containerRef = useScope(containerRefKey); + const schema = useFieldSchema(); + const field = useField(); + const footerSchema = schema.reduceProperties((buf, s) => { + if (s['x-component'] === footerNodeName) { + return s; + } + return buf; + }); + return ( + <> + {containerRef?.current && + visible && + createPortal( +
+ { + return s['x-component'] !== footerNodeName; + }} + /> + {footerSchema && ( +
+ { + return s['x-component'] === footerNodeName; + }} + /> +
+ )} +
, + containerRef?.current, + )} + + ); +}); + +ActionPage.Footer = observer(() => { + const field = useField(); + const schema = useFieldSchema(); + return ; +}); + +export default ActionPage; diff --git a/packages/client/src/schema-component/antd/action/Action.tsx b/packages/client/src/schema-component/antd/action/Action.tsx index 180678f05..a461a6f8a 100644 --- a/packages/client/src/schema-component/antd/action/Action.tsx +++ b/packages/client/src/schema-component/antd/action/Action.tsx @@ -1,19 +1,22 @@ import { observer, RecursionField, useField, useFieldSchema } from '@formily/react'; import { Button } from 'antd'; import React, { useState } from 'react'; +import ActionContainer from './Action.Container'; import { ActionDrawer } from './Action.Drawer'; -import { VisibleContext } from './context'; +import { ActionModal } from './Action.Modal'; +import { ActionPage } from './Action.Page'; +import { ActionContext } from './context'; import { useA } from './hooks'; import { ComposedAction } from './types'; -export const Action: ComposedAction = observer((props) => { - const { useAction = useA, onClick, ...others } = props; +export const Action: ComposedAction = observer((props: any) => { + const { openMode, containerRefKey, useAction = useA, onClick, ...others } = props; const [visible, setVisible] = useState(false); const schema = useFieldSchema(); const field = useField(); const { run } = useAction(); return ( - + - + ); }); Action.Drawer = ActionDrawer; +Action.Modal = ActionModal; +Action.Container = ActionContainer; +Action.Page = ActionPage; export default Action; diff --git a/packages/client/src/schema-component/antd/action/context.tsx b/packages/client/src/schema-component/antd/action/context.tsx index a703edcfa..39ff9818d 100644 --- a/packages/client/src/schema-component/antd/action/context.tsx +++ b/packages/client/src/schema-component/antd/action/context.tsx @@ -1,3 +1,10 @@ import { createContext } from 'react'; -export const VisibleContext = createContext<[boolean, any]>([false, () => {}]); +export const ActionContext = createContext({}); + +export interface ActionContextProps { + visible?: boolean; + setVisible?: (v: boolean) => void; + openMode?: 'drawer' | 'modal' | 'page'; + containerRefKey?: string; +} diff --git a/packages/client/src/schema-component/antd/action/demos/demo1.tsx b/packages/client/src/schema-component/antd/action/demos/demo1.tsx index ad3aa1f81..7d2e312e7 100644 --- a/packages/client/src/schema-component/antd/action/demos/demo1.tsx +++ b/packages/client/src/schema-component/antd/action/demos/demo1.tsx @@ -1,10 +1,10 @@ -import React from 'react'; -import { observer, ISchema, useForm } from '@formily/react'; -import { SchemaComponent, SchemaComponentProvider, Form, Action, useActionVisible } from '@nocobase/client'; import { FormItem, Input } from '@formily/antd'; +import { ISchema, observer, useForm } from '@formily/react'; +import { Action, Form, SchemaComponent, SchemaComponentProvider, useActionContext } from '@nocobase/client'; +import React from 'react'; const useCloseAction = () => { - const { setVisible } = useActionVisible(); + const { setVisible } = useActionContext(); const form = useForm(); return { async run() { diff --git a/packages/client/src/schema-component/antd/action/demos/demo2.tsx b/packages/client/src/schema-component/antd/action/demos/demo2.tsx index 249c267c9..44703bcb7 100644 --- a/packages/client/src/schema-component/antd/action/demos/demo2.tsx +++ b/packages/client/src/schema-component/antd/action/demos/demo2.tsx @@ -1,18 +1,17 @@ -import React, { useState } from 'react'; -import { observer, ISchema, useForm } from '@formily/react'; +import { FormItem, Input } from '@formily/antd'; +import { ISchema, observer, useForm } from '@formily/react'; import { + Action, + ActionContext, + Form, SchemaComponent, SchemaComponentProvider, - Form, - Action, - useActionVisible, - VisibleContext, + useActionContext } from '@nocobase/client'; -import { FormItem, Input } from '@formily/antd'; -import { Button } from 'antd'; +import React, { useState } from 'react'; const useCloseAction = () => { - const { setVisible } = useActionVisible(); + const { setVisible } = useActionContext(); const form = useForm(); return { async run() { @@ -58,10 +57,10 @@ export default observer(() => { const [visible, setVisible] = useState(false); return ( - + setVisible(true)}>Open - + ); }); diff --git a/packages/client/src/schema-component/antd/action/demos/demo3.tsx b/packages/client/src/schema-component/antd/action/demos/demo3.tsx new file mode 100644 index 000000000..d4f376ab6 --- /dev/null +++ b/packages/client/src/schema-component/antd/action/demos/demo3.tsx @@ -0,0 +1,107 @@ +import { FormItem, Input } from '@formily/antd'; +import { Field } from '@formily/core'; +import { ISchema, observer, useField, useForm } from '@formily/react'; +import { Action, Form, SchemaComponent, SchemaComponentProvider, useActionContext } from '@nocobase/client'; +import { Select } from 'antd'; +import React, { useRef } from 'react'; + +const useCloseAction = () => { + const { setVisible } = useActionContext(); + const form = useForm(); + return { + async run() { + setVisible(false); + form.submit((values) => { + console.log(values); + }); + }, + }; +}; + +const Editable = observer((props) => { + const field = useField(); + return ( +
+
+ 打开方式: + +
+ {props.children} +
+ ); +}); + +const schema: ISchema = { + type: 'object', + properties: { + action1: { + 'x-decorator': 'Editable', + 'x-component': 'Action', + 'x-component-props': { + type: 'primary', + openMode: 'modal', + containerRefKey: 'containerRef', + }, + type: 'void', + title: 'Open', + properties: { + drawer1: { + 'x-component': 'Action.Container', + 'x-component-props': {}, + type: 'void', + title: 'Modal Title', + properties: { + hello1: { + title: 'T1', + 'x-content': 'Hello', + }, + footer1: { + 'x-component': 'Action.Container.Footer', + type: 'void', + properties: { + close1: { + title: 'Close', + 'x-component': 'Action', + 'x-component-props': { + useAction: '{{ useCloseAction }}', + }, + }, + submit: { + title: 'Submit', + 'x-component': 'Action', + 'x-component-props': { + type: 'primary', + useAction: '{{ useCloseAction }}', + }, + }, + }, + }, + }, + }, + }, + }, + }, +}; + +export default observer(() => { + const containerRef = useRef(); + return ( + + +
+
+ ); +}); diff --git a/packages/client/src/schema-component/antd/action/hooks.ts b/packages/client/src/schema-component/antd/action/hooks.ts index 4dc749972..cf37a589c 100644 --- a/packages/client/src/schema-component/antd/action/hooks.ts +++ b/packages/client/src/schema-component/antd/action/hooks.ts @@ -1,6 +1,6 @@ import { useForm } from '@formily/react'; import { useContext } from 'react'; -import { VisibleContext } from './context'; +import { ActionContext } from './context'; export const useA = () => { return { @@ -8,13 +8,21 @@ export const useA = () => { }; }; -export const useActionVisible = () => { - const [visible, setVisible] = useContext(VisibleContext); - return { visible, setVisible }; +export const useActionContext = () => { + const ctx = useContext(ActionContext); + + return { + ...ctx, + setVisible(visible: boolean) { + if (ctx.openMode !== 'page') { + ctx.setVisible(visible); + } + }, + }; }; export const useCloseAction = () => { - const [, setVisible] = useContext(VisibleContext); + const { setVisible } = useContext(ActionContext); const form = useForm(); return { async run() { diff --git a/packages/client/src/schema-component/antd/action/index.md b/packages/client/src/schema-component/antd/action/index.md index 7189d1483..8cf455bc2 100644 --- a/packages/client/src/schema-component/antd/action/index.md +++ b/packages/client/src/schema-component/antd/action/index.md @@ -10,22 +10,15 @@ group: ## Nodes -已确定的节点: - - Action -- Action.URL -- Action.Link - Action.Drawer - Action.Drawer.Footer - Action.Modal - Action.Modal.Footer -- Action.Popover - -不确定的节点: - -- Action.Group -- Action.Dropdown -- Action.Window +- Action.Drawer +- Action.Drawer.Footer +- Action.Container +- Action.Container.Footer - ActionBar ## Examples @@ -34,8 +27,12 @@ group: -### VisibleContext + Action.Drawer +### ActionContext + Action.Drawer -只配置 Action.Drawer,而不需要 Action,通过 VisibleContext 自定义按钮。 +只配置 Action.Drawer,而不需要 Action,结合 ActionContext 自定义按钮。 + +### 不同的打开方式 + + diff --git a/packages/client/src/schema-component/antd/action/types.ts b/packages/client/src/schema-component/antd/action/types.ts index 4c4121a58..d40c68c25 100644 --- a/packages/client/src/schema-component/antd/action/types.ts +++ b/packages/client/src/schema-component/antd/action/types.ts @@ -11,6 +11,6 @@ export type ComposedAction = React.FC & { [key: string]: any; }; -export type ComposedActionDrawer = React.FC & { +export type ComposedActionDrawer = React.FC & { Footer?: React.FC; }; diff --git a/packages/client/src/schema-component/antd/record-picker/RecordPicker.tsx b/packages/client/src/schema-component/antd/record-picker/RecordPicker.tsx index bcb393088..d5c396faf 100644 --- a/packages/client/src/schema-component/antd/record-picker/RecordPicker.tsx +++ b/packages/client/src/schema-component/antd/record-picker/RecordPicker.tsx @@ -15,7 +15,7 @@ import { toArr } from '@formily/shared'; import { Button, Drawer, Select, Space, Tag } from 'antd'; import React, { createContext, useContext, useMemo, useState } from 'react'; import { useAttach } from '../../hooks/useAttach'; -import { VisibleContext } from '../action'; +import { ActionContext } from '../action'; const InputRecordPicker: React.FC = (props) => { const [visible, setVisible] = useState(false); @@ -130,13 +130,11 @@ RecordPicker.SelectedItem = () => { const fieldSchema = useFieldSchema(); const [visible, setVisible] = useState(false); return ( - <> - - setVisible(true)}> - {ctx.record?.name} - - - - + + setVisible(true)}> + {ctx.record?.name} + + + ); }; diff --git a/packages/client/src/system-settings/SystemSettingsShortcut.tsx b/packages/client/src/system-settings/SystemSettingsShortcut.tsx index 4f541d57e..1ec7f7e19 100644 --- a/packages/client/src/system-settings/SystemSettingsShortcut.tsx +++ b/packages/client/src/system-settings/SystemSettingsShortcut.tsx @@ -4,10 +4,10 @@ import { uid } from '@formily/shared'; import React, { useState } from 'react'; import { useSystemSettings } from '.'; import { PluginManager, useRequest } from '..'; -import { SchemaComponent, useActionVisible, VisibleContext } from '../schema-component'; +import { ActionContext, SchemaComponent, useActionContext } from '../schema-component'; const useCloseAction = () => { - const { setVisible } = useActionVisible(); + const { setVisible } = useActionContext(); // const form = useForm(); return { async run() { @@ -24,7 +24,7 @@ const useSystemSettingsValues = (props, options) => { }; const useSaveSystemSettingsValues = () => { - const { setVisible } = useActionVisible(); + const { setVisible } = useActionContext(); const form = useForm(); const { mutate } = useSystemSettings(); return { @@ -112,7 +112,7 @@ const schema: ISchema = { export const SystemSettingsShortcut = () => { const [visible, setVisible] = useState(false); return ( - + { @@ -125,6 +125,6 @@ export const SystemSettingsShortcut = () => { scope={{ useSaveSystemSettingsValues, useSystemSettingsValues, useCloseAction }} schema={schema} /> - + ); }; diff --git a/packages/client/src/user/ProfileAction.tsx b/packages/client/src/user/ProfileAction.tsx index dd7f6c559..05b9d5d34 100644 --- a/packages/client/src/user/ProfileAction.tsx +++ b/packages/client/src/user/ProfileAction.tsx @@ -2,10 +2,10 @@ import { ISchema, useForm } from '@formily/react'; import { uid } from '@formily/shared'; import { Menu } from 'antd'; import React, { useState } from 'react'; -import { SchemaComponent, useActionVisible, useRequest, VisibleContext } from '../'; +import { ActionContext, SchemaComponent, useActionContext, useRequest } from '../'; const useCloseAction = () => { - const { setVisible } = useActionVisible(); + const { setVisible } = useActionContext(); const form = useForm(); return { async run() { @@ -30,7 +30,7 @@ const useCurrentUserValues = (props, options) => { }; const useSaveCurrentUserValues = () => { - const { setVisible } = useActionVisible(); + const { setVisible } = useActionContext(); const form = useForm(); return { async run() { @@ -90,7 +90,7 @@ const schema: ISchema = { export const ProfileAction = () => { const [visible, setVisible] = useState(false); return ( - + { @@ -100,6 +100,6 @@ export const ProfileAction = () => { 个人资料 - + ); };