feat: improve schema designer
This commit is contained in:
parent
991345d988
commit
7be30b5b95
@ -16,6 +16,7 @@ export * from './record-provider';
|
||||
export * from './route-switch';
|
||||
export * from './schema-component';
|
||||
export * from './schema-initializer';
|
||||
export * from './schema-settings';
|
||||
export * from './settings-form';
|
||||
export * from './system-settings';
|
||||
export * from './user';
|
||||
|
@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
|
||||
|
||||
export const FormDesigner = () => {
|
||||
return (
|
||||
<GeneralSchemaDesigner>
|
||||
<SchemaSettings.Remove />
|
||||
</GeneralSchemaDesigner>
|
||||
);
|
||||
};
|
@ -6,6 +6,7 @@ import { Spin } from 'antd';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useAttach, useComponent } from '../..';
|
||||
import { useRequest } from '../../../api-client';
|
||||
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
|
||||
|
||||
type Opts = Options<any, any> & { uid?: string };
|
||||
|
||||
@ -73,7 +74,7 @@ const useDef = (opts: any = {}, props: FormProps = {}) => {
|
||||
return useRequest(useRequestProps(props), opts);
|
||||
};
|
||||
|
||||
export const Form: React.FC<FormProps> = observer((props) => {
|
||||
export const Form: React.FC<FormProps> & { Designer?: any } = observer((props) => {
|
||||
const { request, effects, initialValue, useValues = useDef, ...others } = props;
|
||||
const fieldSchema = useFieldSchema();
|
||||
const form = useMemo(() => createForm({ effects }), []);
|
||||
@ -97,3 +98,11 @@ export const Form: React.FC<FormProps> = observer((props) => {
|
||||
</Spin>
|
||||
);
|
||||
});
|
||||
|
||||
Form.Designer = () => {
|
||||
return (
|
||||
<GeneralSchemaDesigner>
|
||||
<SchemaSettings.Remove />
|
||||
</GeneralSchemaDesigner>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,19 @@
|
||||
import { useField } from '@formily/react';
|
||||
import React from 'react';
|
||||
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
|
||||
|
||||
export const MarkdownVoidDesigner = () => {
|
||||
const field = useField();
|
||||
return (
|
||||
<GeneralSchemaDesigner>
|
||||
<SchemaSettings.Item
|
||||
title={'编辑'}
|
||||
onClick={() => {
|
||||
field.editable = true;
|
||||
}}
|
||||
/>
|
||||
<SchemaSettings.Divider />
|
||||
<SchemaSettings.Remove />
|
||||
</GeneralSchemaDesigner>
|
||||
);
|
||||
};
|
@ -0,0 +1,78 @@
|
||||
import { observer, useField, useFieldSchema } from '@formily/react';
|
||||
import { Button, Input as AntdInput, Space } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDesignable } from '../../hooks/useDesignable';
|
||||
import { MarkdownVoidDesigner } from './Markdown.Void.Designer';
|
||||
import { markdown } from './util';
|
||||
|
||||
const MarkdownEditor = (props: any) => {
|
||||
const { t } = useTranslation();
|
||||
const [value, setValue] = useState(props.defaultValue);
|
||||
return (
|
||||
<div className={'mb-markdown'} style={{ position: 'relative' }}>
|
||||
<AntdInput.TextArea
|
||||
autoSize={{ minRows: 3 }}
|
||||
{...props}
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
setValue(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<Space style={{ position: 'absolute', bottom: 5, right: 5 }}>
|
||||
<Button
|
||||
onClick={(e) => {
|
||||
props.onCancel && props.onCancel(e);
|
||||
}}
|
||||
>
|
||||
{t('Cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
type={'primary'}
|
||||
onClick={() => {
|
||||
props.onSubmit && props.onSubmit(value);
|
||||
}}
|
||||
>
|
||||
{t('Save')}
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const MarkdownVoid: any = observer((props: any) => {
|
||||
const { content } = props;
|
||||
const field = useField();
|
||||
const schema = useFieldSchema();
|
||||
const { dn } = useDesignable();
|
||||
const { onSave, onCancel } = props;
|
||||
return field.editable ? (
|
||||
<MarkdownEditor
|
||||
{...props}
|
||||
defaultValue={content}
|
||||
onCancel={() => {
|
||||
field.editable = false;
|
||||
onCancel?.();
|
||||
}}
|
||||
onSubmit={async (value) => {
|
||||
field.editable = false;
|
||||
schema['x-component-props'] ?? (schema['x-component-props'] = {});
|
||||
schema['x-component-props']['content'] = value;
|
||||
field.componentProps.content = value;
|
||||
onSave?.(schema);
|
||||
dn.emit('patch', {
|
||||
schema: {
|
||||
'x-uid': schema['x-uid'],
|
||||
'x-component-props': {
|
||||
content: value,
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div className={'nb-markdown'} dangerouslySetInnerHTML={{ __html: markdown(content) }} />
|
||||
);
|
||||
});
|
||||
|
||||
MarkdownVoid.Designer = MarkdownVoidDesigner;
|
@ -1,19 +1,10 @@
|
||||
import { LoadingOutlined } from '@ant-design/icons';
|
||||
import { connect, mapProps, mapReadPretty, observer, useField, useFieldSchema } from '@formily/react';
|
||||
import { Button, Input as AntdInput, Space } from 'antd';
|
||||
import { marked } from 'marked';
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { DragHandler } from '../../common';
|
||||
import { useDesignable } from '../../hooks/useDesignable';
|
||||
import { connect, mapProps, mapReadPretty } from '@formily/react';
|
||||
import { Input as AntdInput } from 'antd';
|
||||
import React from 'react';
|
||||
import { ReadPretty as InputReadPretty } from '../input';
|
||||
|
||||
export function markdown(text) {
|
||||
if (!text) {
|
||||
return '';
|
||||
}
|
||||
return marked.parse(text);
|
||||
}
|
||||
import { MarkdownVoid } from './Markdown.Void';
|
||||
import { markdown } from './util';
|
||||
|
||||
export const Markdown: any = connect(
|
||||
AntdInput.TextArea,
|
||||
@ -30,97 +21,6 @@ export const Markdown: any = connect(
|
||||
}),
|
||||
);
|
||||
|
||||
function MarkdownEditor(props: any) {
|
||||
const { t } = useTranslation();
|
||||
const [value, setValue] = useState(props.defaultValue);
|
||||
return (
|
||||
<div className={'mb-markdown'} style={{ position: 'relative' }}>
|
||||
<AntdInput.TextArea
|
||||
autoSize={{ minRows: 3 }}
|
||||
{...props}
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
setValue(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<Space style={{ position: 'absolute', bottom: 5, right: 5 }}>
|
||||
<Button
|
||||
onClick={(e) => {
|
||||
props.onCancel && props.onCancel(e);
|
||||
}}
|
||||
>
|
||||
{t('Cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
type={'primary'}
|
||||
onClick={() => {
|
||||
props.onSubmit && props.onSubmit(value);
|
||||
}}
|
||||
>
|
||||
{t('Save')}
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Markdown.Void = observer((props: any) => {
|
||||
const { content } = props;
|
||||
const field = useField();
|
||||
const schema = useFieldSchema();
|
||||
const { dn } = useDesignable();
|
||||
const { onSave, onCancel } = props;
|
||||
return field.editable ? (
|
||||
<MarkdownEditor
|
||||
{...props}
|
||||
defaultValue={content}
|
||||
onCancel={() => {
|
||||
field.editable = false;
|
||||
onCancel?.();
|
||||
}}
|
||||
onSubmit={async (value) => {
|
||||
field.editable = false;
|
||||
schema['x-component-props'] ?? (schema['x-component-props'] = {});
|
||||
schema['x-component-props']['content'] = value;
|
||||
field.componentProps.content = value;
|
||||
onSave?.(schema);
|
||||
dn.emit('patch', {
|
||||
schema: {
|
||||
'x-uid': schema['x-uid'],
|
||||
'x-component-props': {
|
||||
content: value,
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div className={'nb-markdown'} dangerouslySetInnerHTML={{ __html: markdown(content) }} />
|
||||
);
|
||||
});
|
||||
|
||||
Markdown.Void.Designer = () => {
|
||||
const { remove } = useDesignable();
|
||||
const field = useField();
|
||||
return (
|
||||
<div>
|
||||
<a
|
||||
onClick={() => {
|
||||
remove();
|
||||
}}
|
||||
>
|
||||
删除
|
||||
</a>
|
||||
<a
|
||||
onClick={() => {
|
||||
field.editable = true;
|
||||
}}
|
||||
>
|
||||
Edit
|
||||
</a>
|
||||
<DragHandler />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Markdown.Void = MarkdownVoid;
|
||||
|
||||
export default Markdown;
|
||||
|
@ -0,0 +1,8 @@
|
||||
import { marked } from 'marked';
|
||||
|
||||
export function markdown(text) {
|
||||
if (!text) {
|
||||
return '';
|
||||
}
|
||||
return marked.parse(text);
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
|
||||
|
||||
export const VoidTableDesigner = () => {
|
||||
return (
|
||||
<GeneralSchemaDesigner>
|
||||
<SchemaSettings.Remove />
|
||||
</GeneralSchemaDesigner>
|
||||
);
|
||||
};
|
@ -7,6 +7,7 @@ import React, { useMemo } from 'react';
|
||||
import { AsyncDataProvider, useRequest } from '../../../';
|
||||
import { useAttach } from '../../hooks';
|
||||
import { ArrayTable } from '../array-table';
|
||||
import { VoidTableDesigner } from './VoidTable.Designer';
|
||||
|
||||
type VoidTableProps = TableProps<any> & {
|
||||
request?: any;
|
||||
@ -15,6 +16,7 @@ type VoidTableProps = TableProps<any> & {
|
||||
|
||||
type VoidTableType = React.FC<VoidTableProps> & {
|
||||
Column?: React.FC<any>;
|
||||
Designer?: any;
|
||||
mixin?: (T: any) => void;
|
||||
};
|
||||
|
||||
@ -107,6 +109,6 @@ export const VoidTable: VoidTableType = observer((props) => {
|
||||
);
|
||||
});
|
||||
|
||||
VoidTable.Designer = VoidTableDesigner;
|
||||
VoidTable.mixin = ArrayTable.mixin;
|
||||
|
||||
ArrayTable.mixin(VoidTable);
|
||||
|
@ -165,6 +165,11 @@ export class Designable {
|
||||
return false;
|
||||
}
|
||||
|
||||
refresh() {
|
||||
const { refresh } = this.options;
|
||||
return refresh?.();
|
||||
}
|
||||
|
||||
insertAdjacent(position: Position, schema: ISchema, options: InsertAdjacentOptions = {}) {
|
||||
switch (position) {
|
||||
case 'beforeBegin':
|
||||
|
@ -18,7 +18,7 @@ const createSchema = (collectionName) => {
|
||||
params: {},
|
||||
},
|
||||
},
|
||||
'x-designer': 'TestDesigner',
|
||||
'x-designer': 'Form.Designer',
|
||||
'x-component': 'CardItem',
|
||||
properties: {
|
||||
form: {
|
||||
|
@ -23,7 +23,7 @@ const createSchema = (collectionName) => {
|
||||
},
|
||||
},
|
||||
},
|
||||
'x-designer': 'TestDesigner',
|
||||
'x-designer': 'VoidTable.Designer',
|
||||
'x-component': 'CardItem',
|
||||
properties: {
|
||||
actions: {
|
||||
|
@ -34,7 +34,7 @@ const InitializeAction = SchemaInitializer.itemWrap((props) => {
|
||||
}
|
||||
insert({
|
||||
type: 'void',
|
||||
'x-designer': 'TestDesigner',
|
||||
'x-designer': 'Action.Designer',
|
||||
'x-component': 'Action',
|
||||
...item.schema,
|
||||
});
|
||||
|
@ -0,0 +1,23 @@
|
||||
import { useField, useFieldSchema } from '@formily/react';
|
||||
import React from 'react';
|
||||
import { DragHandler, useDesignable } from '../schema-component';
|
||||
import { SchemaSettings } from './SchemaSettings';
|
||||
|
||||
export const GeneralSchemaDesigner = (props: any) => {
|
||||
const { dn } = useDesignable();
|
||||
const field = useField();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const schemaSettingsProps = {
|
||||
dn,
|
||||
field,
|
||||
fieldSchema,
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<SchemaSettings title={'配置'} {...schemaSettingsProps}>
|
||||
{props.children}
|
||||
</SchemaSettings>
|
||||
<DragHandler />
|
||||
</div>
|
||||
);
|
||||
};
|
104
packages/client/src/schema-settings/SchemaSettings.tsx
Normal file
104
packages/client/src/schema-settings/SchemaSettings.tsx
Normal file
@ -0,0 +1,104 @@
|
||||
import { GeneralField } from '@formily/core';
|
||||
import { Schema } from '@formily/react';
|
||||
import { Dropdown, Menu, MenuItemProps } from 'antd';
|
||||
import React, { createContext, useContext } from 'react';
|
||||
import { Designable } from '..';
|
||||
|
||||
interface SchemaSettingsProps {
|
||||
title?: any;
|
||||
dn?: Designable;
|
||||
field?: GeneralField;
|
||||
fieldSchema?: Schema;
|
||||
}
|
||||
|
||||
interface SchemaSettingsContextProps {
|
||||
dn?: Designable;
|
||||
field?: GeneralField;
|
||||
fieldSchema?: Schema;
|
||||
}
|
||||
|
||||
const SchemaSettingsContext = createContext<SchemaSettingsContextProps>(null);
|
||||
|
||||
export const useSchemaSettings = () => {
|
||||
return useContext(SchemaSettingsContext);
|
||||
};
|
||||
|
||||
type SchemaSettingsNested = {
|
||||
Remove?: React.FC;
|
||||
Item?: React.FC<MenuItemProps>;
|
||||
Divider?: React.FC;
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
interface SchemaSettingsProviderProps {
|
||||
dn?: Designable;
|
||||
field?: GeneralField;
|
||||
fieldSchema?: Schema;
|
||||
}
|
||||
|
||||
export const SchemaSettingsProvider: React.FC<SchemaSettingsProviderProps> = (props) => {
|
||||
const { dn, field, fieldSchema, children } = props;
|
||||
return <SchemaSettingsContext.Provider value={{ dn, field, fieldSchema }}>{children}</SchemaSettingsContext.Provider>;
|
||||
};
|
||||
|
||||
export const SchemaSettings: React.FC<SchemaSettingsProps> & SchemaSettingsNested = (props) => {
|
||||
const { title, dn, field, fieldSchema } = props;
|
||||
const DropdownMenu = (
|
||||
<Dropdown overlay={<Menu>{props.children}</Menu>}>
|
||||
{typeof title === 'string' ? <span>{title}</span> : title}
|
||||
</Dropdown>
|
||||
);
|
||||
if (dn) {
|
||||
return (
|
||||
<SchemaSettingsProvider dn={dn} field={field} fieldSchema={fieldSchema}>
|
||||
{DropdownMenu}
|
||||
</SchemaSettingsProvider>
|
||||
);
|
||||
}
|
||||
return DropdownMenu;
|
||||
};
|
||||
|
||||
SchemaSettings.Item = (props) => {
|
||||
return <Menu.Item {...props}>{props.children || props.title}</Menu.Item>;
|
||||
};
|
||||
|
||||
SchemaSettings.ItemGroup = (props) => {
|
||||
return <Menu.ItemGroup {...props}>{props.children || props.title}</Menu.ItemGroup>;
|
||||
};
|
||||
|
||||
SchemaSettings.SubMenu = (props) => {
|
||||
return <Menu.SubMenu {...props}>{props.children || props.title}</Menu.SubMenu>;
|
||||
};
|
||||
|
||||
SchemaSettings.Divider = (props) => {
|
||||
return <Menu.Divider {...props} />;
|
||||
};
|
||||
|
||||
SchemaSettings.Remove = () => {
|
||||
const { dn } = useSchemaSettings();
|
||||
return (
|
||||
<Menu.Item
|
||||
onClick={() => {
|
||||
dn.remove();
|
||||
}}
|
||||
>
|
||||
移除
|
||||
</Menu.Item>
|
||||
);
|
||||
};
|
||||
|
||||
SchemaSettings.SelectItem = (props) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
SchemaSettings.SwitchItem = (props) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
SchemaSettings.ModalItem = (props) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
SchemaSettings.DrawerItem = (props) => {
|
||||
return null;
|
||||
};
|
3
packages/client/src/schema-settings/index.ts
Normal file
3
packages/client/src/schema-settings/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './GeneralSchemaDesigner';
|
||||
export * from './SchemaSettings';
|
||||
|
Loading…
Reference in New Issue
Block a user