feat: improve schema designer

This commit is contained in:
chenos 2022-02-20 01:22:10 +08:00
parent 991345d988
commit 7be30b5b95
16 changed files with 283 additions and 111 deletions

View File

@ -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';

View File

@ -0,0 +1,10 @@
import React from 'react';
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
export const FormDesigner = () => {
return (
<GeneralSchemaDesigner>
<SchemaSettings.Remove />
</GeneralSchemaDesigner>
);
};

View File

@ -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>
);
};

View File

@ -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>
);
};

View File

@ -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;

View File

@ -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;

View File

@ -0,0 +1,8 @@
import { marked } from 'marked';
export function markdown(text) {
if (!text) {
return '';
}
return marked.parse(text);
}

View File

@ -0,0 +1,10 @@
import React from 'react';
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
export const VoidTableDesigner = () => {
return (
<GeneralSchemaDesigner>
<SchemaSettings.Remove />
</GeneralSchemaDesigner>
);
};

View File

@ -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);

View File

@ -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':

View File

@ -18,7 +18,7 @@ const createSchema = (collectionName) => {
params: {},
},
},
'x-designer': 'TestDesigner',
'x-designer': 'Form.Designer',
'x-component': 'CardItem',
properties: {
form: {

View File

@ -23,7 +23,7 @@ const createSchema = (collectionName) => {
},
},
},
'x-designer': 'TestDesigner',
'x-designer': 'VoidTable.Designer',
'x-component': 'CardItem',
properties: {
actions: {

View File

@ -34,7 +34,7 @@ const InitializeAction = SchemaInitializer.itemWrap((props) => {
}
insert({
type: 'void',
'x-designer': 'TestDesigner',
'x-designer': 'Action.Designer',
'x-component': 'Action',
...item.schema,
});

View File

@ -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>
);
};

View 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;
};

View File

@ -0,0 +1,3 @@
export * from './GeneralSchemaDesigner';
export * from './SchemaSettings';