diff --git a/packages/client/src/schema-component/antd/action/ActionBar.tsx b/packages/client/src/schema-component/antd/action/ActionBar.tsx index 41e540263..fc3bb0f1f 100644 --- a/packages/client/src/schema-component/antd/action/ActionBar.tsx +++ b/packages/client/src/schema-component/antd/action/ActionBar.tsx @@ -5,7 +5,7 @@ import { useComponent } from '../../hooks'; export const ActionBar = () => { const fieldSchema = useFieldSchema(); - const Initializer = useComponent(fieldSchema['x-initializer']); + const ActionInitializer = useComponent(fieldSchema['x-action-initializer']); return (
@@ -26,7 +26,7 @@ export const ActionBar = () => { })}
- {Initializer && } + {ActionInitializer && }
); }; diff --git a/packages/client/src/schema-component/antd/array-table/ArrayTable.tsx b/packages/client/src/schema-component/antd/array-table/ArrayTable.tsx index 4d1653940..438214ea8 100644 --- a/packages/client/src/schema-component/antd/array-table/ArrayTable.tsx +++ b/packages/client/src/schema-component/antd/array-table/ArrayTable.tsx @@ -11,7 +11,7 @@ const isColumnComponent = (schema: Schema) => { const useTableColumns = () => { const field = useField(); const schema = useFieldSchema(); - const Initializer = useComponent(schema['x-initializer']); + const Initializer = useComponent(schema['x-column-initializer']); const columns = schema .reduceProperties((buf, s) => { if (isColumnComponent(s)) { diff --git a/packages/client/src/schema-component/hooks/useDesignable.tsx b/packages/client/src/schema-component/hooks/useDesignable.tsx index 76756e890..ff5659070 100644 --- a/packages/client/src/schema-component/hooks/useDesignable.tsx +++ b/packages/client/src/schema-component/hooks/useDesignable.tsx @@ -332,8 +332,8 @@ export function useDesignable() { update(key); refresh(); }, - remove() { - dn.remove(); + remove(schema: any, options?: any) { + dn.remove(schema, options); }, insertAdjacent(position: Position, schema: ISchema, options: InsertAdjacentOptions = {}) { dn.insertAdjacent(position, schema, options); diff --git a/packages/client/src/schema-initializer/SchemaInitializer.tsx b/packages/client/src/schema-initializer/SchemaInitializer.tsx index faf002f90..a1cddfc98 100644 --- a/packages/client/src/schema-initializer/SchemaInitializer.tsx +++ b/packages/client/src/schema-initializer/SchemaInitializer.tsx @@ -12,6 +12,7 @@ export const SchemaInitializer = () => null; SchemaInitializer.Button = observer((props: any) => { const { wrap = defaultWrap, items = [], insertPosition, dropdown, ...others } = props; const { insertAdjacent, findComponent } = useDesignable(); + const [visible, setVisible] = useState(false); const menu = ( @@ -81,7 +82,6 @@ SchemaInitializer.Button = observer((props: any) => { })} ); - const [visible, setVisible] = useState(false); return ( { children: [ { title: 'Table', - component: 'TableBlockInitializer', + component: TableBlockInitializerItem, }, { title: 'Form', - component: 'FormBlockInitializer', + component: FormBlockInitializerItem, }, ], }, @@ -39,7 +39,7 @@ const InitializerButton = observer((props: any) => { ); }); -const TableBlockInitializer = (props) => { +const TableBlockInitializerItem = (props) => { const { insert } = props; return ( { ); }; -const FormBlockInitializer = (props) => { +const FormBlockInitializerItem = (props) => { const { insert } = props; return ( { export default function App() { return ( - + { +const ActionInitializerButton = observer((props: any) => { return ( schema} insertPosition={'beforeEnd'} style={{ marginLeft: 8 }} items={[ @@ -15,15 +22,21 @@ const InitializerButton = observer((props: any) => { { title: 'Create', key: 'create', - component: 'ActionInitializer', + component: 'ActionInitializerItem', schema: { + title: 'Create', + 'x-action': 'posts:create', 'x-align': 'left', }, }, { title: 'Update', key: 'update', - component: 'ActionInitializer', + schema: { + title: 'Update', + 'x-action': 'posts:update', + }, + component: 'ActionInitializerItem', }, ], }, @@ -34,44 +47,70 @@ const InitializerButton = observer((props: any) => { ); }); -const ActionInitializer = (props) => { +const useCurrentActionSchema = (action: string) => { + const fieldSchema = useFieldSchema(); + const { remove } = useDesignable(); + const schema = fieldSchema.reduceProperties((buf, s) => { + if (s['x-action'] === action) { + return s; + } + return buf; + }); + return { + schema, + exists: !!schema, + remove() { + schema && remove(schema); + }, + }; +}; + +const ActionInitializerItem = (props) => { const { title, schema, insert } = props; + const { exists, remove } = useCurrentActionSchema(schema['x-action']); return ( { + if (exists) { + return remove(); + } insert({ type: 'void', - title: info.key, 'x-component': 'Action', ...schema, }); }} > - {title} +
+ {title} +
); }; export default function App() { return ( - + diff --git a/packages/client/src/schema-initializer/demos/demo3.tsx b/packages/client/src/schema-initializer/demos/demo3.tsx index c55e6c6b9..bff1f9497 100644 --- a/packages/client/src/schema-initializer/demos/demo3.tsx +++ b/packages/client/src/schema-initializer/demos/demo3.tsx @@ -1,33 +1,46 @@ -import { Field } from '@formily/core'; -import { observer, useField } from '@formily/react'; -import { SchemaComponent, SchemaComponentProvider, SchemaInitializer } from '@nocobase/client'; +import { observer, Schema, useFieldSchema } from '@formily/react'; +import { + Form, + FormItem, + Markdown, + SchemaComponent, + SchemaComponentProvider, + SchemaInitializer, + useDesignable +} from '@nocobase/client'; +import { Input, Switch } from 'antd'; import React from 'react'; -const Hello = observer((props) => { - const field = useField(); - return ( -
- {field.title} -
- ); -}); - const useFormItemInitializerFields = () => { return [ { - key: 'field1', - title: 'Field1', + key: 'name', + title: 'Name', component: 'FormItemInitializer', + schema: { + type: 'string', + title: 'Name', + 'x-component': 'Input', + 'x-decorator': 'FormItem', + 'x-collection-field': 'posts.name', + }, }, { - key: 'field2', - title: 'Field2', + key: 'title', + title: 'Title', component: 'FormItemInitializer', + schema: { + type: 'string', + title: 'Title', + 'x-component': 'Input', + 'x-decorator': 'FormItem', + 'x-collection-field': 'posts.title', + }, }, ]; }; -const InitializerButton = observer((props: any) => { +const FormItemInitializerButton = observer((props: any) => { return ( schema} @@ -51,19 +64,48 @@ const InitializerButton = observer((props: any) => { ); }); +const useCurrentFieldSchema = (path: string) => { + const fieldSchema = useFieldSchema(); + const { remove } = useDesignable(); + const findFieldSchema = (schema: Schema) => { + return schema.reduceProperties((buf, s) => { + if (s['x-collection-field'] === path) { + return s; + } + const child = findFieldSchema(s); + if (child) { + return child; + } + return buf; + }); + }; + const schema = findFieldSchema(fieldSchema); + return { + schema, + exists: !!schema, + remove() { + schema && remove(schema); + }, + }; +}; + const FormItemInitializer = (props) => { - const { title, insert } = props; + const { title, schema, insert } = props; + const { exists, remove } = useCurrentFieldSchema(schema['x-collection-field']); return ( { + onClick={() => { + if (exists) { + return remove(); + } insert({ - type: 'void', - title: info.key, - 'x-component': 'Hello', + ...schema, }); }} > - {title} +
+ {title} +
); }; @@ -75,8 +117,9 @@ const AddTextFormItemInitializer = (props) => { onClick={(info) => { insert({ type: 'void', - title: 'Text', - 'x-component': 'Hello', + 'x-component': 'Markdown.Void', + 'x-decorator': 'FormItem', + // 'x-editable': false, }); }} > @@ -89,7 +132,7 @@ const Page = (props) => { return (
{props.children} - +
); }; @@ -97,23 +140,28 @@ const Page = (props) => { export default function App() { return ( { const fields = [ { - key: 'field1', - title: 'Field1', + key: 'name', + title: 'Name', + schema: { + type: 'string', + name: 'name', + 'x-collection-field': 'posts.name', + 'x-component': 'Input', + }, component: ColumnInitializer, }, { - key: 'field2', - title: 'Field2', - component: 'ColumnInitializer', + key: 'title', + title: 'Title', + schema: { + type: 'string', + name: 'title', + 'x-collection-field': 'posts.title', + 'x-component': 'Input', + }, + component: ColumnInitializer, }, ]; return fields; @@ -35,26 +55,56 @@ export const InitializerButton = observer((props: any) => { ); }); +const useCurrentColumnSchema = (path: string) => { + const fieldSchema = useFieldSchema(); + const { remove } = useDesignable(); + const findFieldSchema = (schema: Schema) => { + return schema.reduceProperties((buf, s) => { + if (s['x-collection-field'] === path) { + return s; + } + const child = findFieldSchema(s); + if (child) { + return child; + } + return buf; + }); + }; + const schema = findFieldSchema(fieldSchema); + return { + schema, + exists: !!schema, + remove() { + schema && remove(schema.parent); + }, + }; +}; + export const ColumnInitializer = (props) => { - const { title, insert } = props; + const { title, schema, insert } = props; + const { exists, remove } = useCurrentColumnSchema(schema['x-collection-field']); return ( { + onClick={() => { + if (exists) { + return remove(); + } insert({ type: 'void', - title: 'Name', + title: schema.name, 'x-component': 'ArrayTable.Column', properties: { - name: { - type: 'string', - 'x-component': 'Input', + [schema.name]: { 'x-read-pretty': true, + ...schema, }, }, }); }} > - {title} +
+ {title} +
); }; @@ -70,7 +120,7 @@ const schema = { { id: 3, name: 'Name3' }, ], 'x-component': 'ArrayTable', - 'x-initializer': 'InitializerButton', + 'x-column-initializer': 'InitializerButton', 'x-component-props': { rowKey: 'id', }, @@ -83,6 +133,7 @@ const schema = { name: { type: 'string', 'x-component': 'Input', + 'x-collection-field': 'posts.name', 'x-read-pretty': true, }, }, @@ -94,7 +145,7 @@ const schema = { export default function App() { return ( - + ); diff --git a/packages/client/src/schema-initializer/index.md b/packages/client/src/schema-initializer/index.md index 957db7acc..aca365ba2 100644 --- a/packages/client/src/schema-initializer/index.md +++ b/packages/client/src/schema-initializer/index.md @@ -9,87 +9,39 @@ group: 用于配置各种 schema 的初始化 -## SchemaInitializerProvider - -```tsx | pure - -``` - -目前可能需要配置 shortcuts 的有: - -```ts -const shortcuts = { - // 表单可选操作 - FormAction: [], - // 抽屉表单可选操作 - DrawerFormAction: [], - // 对话框表单可选操作 - ModalFormAction: [], - // 详情区块可选操作 - DetailsAction: [], - // 表格区块可选操作 - TableAction: [], - // 表格行可选操作 - TableRowAction: [], - // 日历可选操作 - CalendarAction: [], - // 普通页面可添加区块 - Block: [], - // 当前数据页面可添加区块 - RecordBlock: [], - // 可添加的表单项 - FormItem: [], - // 可添加的表格列 - TableColumn: [], -}; -``` - ## SchemaInitializer.Button 常规区块的初始化按钮 ```tsx | pure - schema} - // 插入位置 - insertPosition={'beforeBegin'} - // 从上下文获取 shortcuts 里配置的 items - shortcut={'Block'} - items={[ - { - title: 'Data blocks', - children: [ - { - title: 'Table', - component: 'TableBlockInitializer', - }, - { - title: 'Form', - component: 'FormBlockInitializer', - }, - ], - }, - ]} ->Create block +const items = [ + { + title: 'Data blocks', + children: [ + { + title: 'Table', + component: 'TableBlockInitializerItem', + }, + { + title: 'Form', + component: 'FormBlockInitializerItem', + }, + ], + }, +]; + +export const AddBlockButton = () => { + return ( + schema} + // 插入位置 + insertPosition={'beforeBegin'} + // 菜单项 + items={items} + >Create block + ); +} ``` 动态字段的配置 @@ -100,25 +52,27 @@ const useFormItemInitializerFields = () => { return fields.map(field => { return { key: field.name, - component: 'FormItemInitializer' + component: 'FormItemInitializerItem' } }); } - schema} - // 插入位置 - insertPosition={'beforeBegin'} - // 从上下文获取 shortcuts 里配置的 items - shortcut={'Block'} - items={[ - { - title: 'Display fields', - children: useFormItemInitializerFields(), - }, - ]} ->Configure fields +export const AddFieldButton = () => { + return ( + schema} + // 插入位置 + insertPosition={'beforeBegin'} + items={[ + { + title: 'Display fields', + children: useFormItemInitializerFields(), + }, + ]} + >Configure fields + ) +} ``` ## SchemaInitializer.Item @@ -126,7 +80,7 @@ const useFormItemInitializerFields = () => { 扩展项 ```tsx | pure -const TableBlockInitializer = (props) => { +const TableBlockInitializerItem = (props) => { const { insert } = props; return (