feat: add Markdown component into schema components (#173)
* feat: markdown migrate * feat: markdown migrate * feat: add Markdown component into schema components * refactor: modifty Markdown.Void component * refactor: modifty Markdown.Void component * refactor: modifty Markdown.Void component * refactor: modify Markdown.Void component * refactor: modify Markdown.Void component * fix: x-editable=false Co-authored-by: chenos <chenlinxh@gmail.com>
This commit is contained in:
parent
145940fc6f
commit
dfa28983d7
@ -27,6 +27,7 @@
|
|||||||
"axios": "^0.24.0",
|
"axios": "^0.24.0",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"i18next": "^21.6.0",
|
"i18next": "^21.6.0",
|
||||||
|
"marked": "^4.0.12",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-i18next": "^11.15.1",
|
"react-i18next": "^11.15.1",
|
||||||
"react-image-lightbox": "^5.1.4",
|
"react-image-lightbox": "^5.1.4",
|
||||||
@ -47,6 +48,7 @@
|
|||||||
"build:esm": "tsc --project tsconfig.build.json --module es2015 --outDir esm"
|
"build:esm": "tsc --project tsconfig.build.json --module es2015 --outDir esm"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/marked": "^4.0.1",
|
||||||
"axios-mock-adapter": "^1.20.0"
|
"axios-mock-adapter": "^1.20.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ export * from './grid';
|
|||||||
export * from './icon-picker';
|
export * from './icon-picker';
|
||||||
export * from './input';
|
export * from './input';
|
||||||
export * from './input-number';
|
export * from './input-number';
|
||||||
|
export * from './markdown';
|
||||||
export * from './menu';
|
export * from './menu';
|
||||||
export * from './page';
|
export * from './page';
|
||||||
export * from './password';
|
export * from './password';
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
import React from 'react';
|
import { LoadingOutlined } from '@ant-design/icons';
|
||||||
|
import { connect, mapProps, mapReadPretty } from '@formily/react';
|
||||||
import { Input as AntdInput } from 'antd';
|
import { Input as AntdInput } from 'antd';
|
||||||
import { InputProps, TextAreaProps } from 'antd/lib/input';
|
import { InputProps, TextAreaProps } from 'antd/lib/input';
|
||||||
import { connect, mapProps, mapReadPretty } from '@formily/react';
|
import React from 'react';
|
||||||
import { LoadingOutlined } from '@ant-design/icons';
|
|
||||||
import { ReadPretty } from './ReadPretty';
|
import { ReadPretty } from './ReadPretty';
|
||||||
|
|
||||||
type ComposedInput = React.FC<InputProps> & {
|
type ComposedInput = React.FC<InputProps> & {
|
||||||
TextArea?: React.FC<TextAreaProps>;
|
TextArea?: React.FC<TextAreaProps>;
|
||||||
URL?: React.FC<InputProps>;
|
URL?: React.FC<InputProps>;
|
||||||
DesignableBar?: React.FC<any>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Input: ComposedInput = connect(
|
export const Input: ComposedInput = connect(
|
||||||
|
@ -1 +1,2 @@
|
|||||||
export * from './Input';
|
export * from './Input';
|
||||||
|
export * from './ReadPretty';
|
||||||
|
@ -0,0 +1,91 @@
|
|||||||
|
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 { ReadPretty as InputReadPretty } from '../input';
|
||||||
|
|
||||||
|
export function markdown(text) {
|
||||||
|
if (!text) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return marked.parse(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Markdown: any = connect(
|
||||||
|
AntdInput.TextArea,
|
||||||
|
mapProps((props: any, field) => {
|
||||||
|
return {
|
||||||
|
...props,
|
||||||
|
suffix: <span>{field?.['loading'] || field?.['validating'] ? <LoadingOutlined /> : props.suffix}</span>,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
mapReadPretty((props) => {
|
||||||
|
let text = props.value;
|
||||||
|
let value = <div className={'nb-markdown'} dangerouslySetInnerHTML={{ __html: markdown(text) }} />;
|
||||||
|
return <InputReadPretty.TextArea {...props} text={text} value={value} />;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
function MarkdownTextArea(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 schema = useFieldSchema();
|
||||||
|
const field = useField<any>();
|
||||||
|
const text = schema['x-component-props']?.['content'] ?? schema['default'];
|
||||||
|
let value = <div className={'nb-markdown'} dangerouslySetInnerHTML={{ __html: markdown(text) }} />;
|
||||||
|
const { onSave, onCancel } = props;
|
||||||
|
return field.editable ? (
|
||||||
|
<MarkdownTextArea
|
||||||
|
{...props}
|
||||||
|
defaultValue={text}
|
||||||
|
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;
|
||||||
|
onSave?.(schema);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<InputReadPretty.TextArea {...props} text={text} value={value} />
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Markdown;
|
@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* title: Markdown
|
||||||
|
*/
|
||||||
|
import { FormItem } from '@formily/antd';
|
||||||
|
import { Markdown, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
input: {
|
||||||
|
type: 'string',
|
||||||
|
title: `Editable`,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Markdown',
|
||||||
|
'x-reactions': {
|
||||||
|
target: 'read',
|
||||||
|
fulfill: {
|
||||||
|
state: {
|
||||||
|
value: '{{$self.value}}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
read: {
|
||||||
|
type: 'string',
|
||||||
|
title: `Read pretty`,
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Markdown',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
return (
|
||||||
|
<SchemaComponentProvider components={{ Markdown, FormItem }}>
|
||||||
|
<SchemaComponent schema={schema} />
|
||||||
|
</SchemaComponentProvider>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* title: Markdown.Void
|
||||||
|
*/
|
||||||
|
import { FormItem } from '@formily/antd';
|
||||||
|
import { observer, useField } from '@formily/react';
|
||||||
|
import { Markdown, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
|
||||||
|
import { Button } from 'antd';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
markdown: {
|
||||||
|
type: 'void',
|
||||||
|
title: `Read pretty`,
|
||||||
|
'x-decorator': 'Editable',
|
||||||
|
'x-component': 'Markdown.Void',
|
||||||
|
'x-editable': false,
|
||||||
|
'x-component-props': {
|
||||||
|
content: '# Markdown content',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const Editable = observer((props: any) => {
|
||||||
|
const filed = useField<any>();
|
||||||
|
if (filed.editable) {
|
||||||
|
return props.children;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
filed.editable = true;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</Button>
|
||||||
|
<div>{props.children}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
return (
|
||||||
|
<SchemaComponentProvider components={{ Editable, Markdown, FormItem }}>
|
||||||
|
<SchemaComponent schema={schema} />
|
||||||
|
</SchemaComponentProvider>
|
||||||
|
);
|
||||||
|
};
|
@ -6,3 +6,13 @@ group:
|
|||||||
---
|
---
|
||||||
|
|
||||||
# Markdown
|
# Markdown
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Markdown
|
||||||
|
|
||||||
|
<code src="./demos/demo1.tsx" />
|
||||||
|
|
||||||
|
### Markdown. Void
|
||||||
|
|
||||||
|
<code src="./demos/demo2.tsx" />
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
export * from './Markdown';
|
@ -0,0 +1,12 @@
|
|||||||
|
.nb-markdown > *:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-description-textarea,
|
||||||
|
.ant-description-input {
|
||||||
|
line-height: 1.612;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-interface-datetime {
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
10
yarn.lock
10
yarn.lock
@ -2957,6 +2957,11 @@
|
|||||||
resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.177.tgz#f70c0d19c30fab101cad46b52be60363c43c4578"
|
resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.177.tgz#f70c0d19c30fab101cad46b52be60363c43c4578"
|
||||||
integrity sha512-0fDwydE2clKe9MNfvXHBHF9WEahRuj+msTuQqOmAApNORFvhMYZKNGGJdCzuhheVjMps/ti0Ak/iJPACMaevvw==
|
integrity sha512-0fDwydE2clKe9MNfvXHBHF9WEahRuj+msTuQqOmAApNORFvhMYZKNGGJdCzuhheVjMps/ti0Ak/iJPACMaevvw==
|
||||||
|
|
||||||
|
"@types/marked@^4.0.1":
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.npmmirror.com/@types/marked/download/@types/marked-4.0.1.tgz#d588a7bbc4d6551c5e75249bc106ffda96ae33c5"
|
||||||
|
integrity sha512-ZigEmCWdNUU7IjZEuQ/iaimYdDHWHfTe3kg8ORfKjyGYd9RWumPoOJRQXB0bO+XLkNwzCthW3wUIQtANaEZ1ag==
|
||||||
|
|
||||||
"@types/mathjax@^0.0.36":
|
"@types/mathjax@^0.0.36":
|
||||||
version "0.0.36"
|
version "0.0.36"
|
||||||
resolved "https://registry.npmjs.org/@types/mathjax/-/mathjax-0.0.36.tgz#18cf766f88ac0cd4e7ee8282b1286049bb6aa682"
|
resolved "https://registry.npmjs.org/@types/mathjax/-/mathjax-0.0.36.tgz#18cf766f88ac0cd4e7ee8282b1286049bb6aa682"
|
||||||
@ -9714,6 +9719,11 @@ markdown-table@^2.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
repeat-string "^1.0.0"
|
repeat-string "^1.0.0"
|
||||||
|
|
||||||
|
marked@^4.0.12:
|
||||||
|
version "4.0.12"
|
||||||
|
resolved "https://registry.npmmirror.com/marked/download/marked-4.0.12.tgz#2262a4e6fd1afd2f13557726238b69a48b982f7d"
|
||||||
|
integrity sha512-hgibXWrEDNBWgGiK18j/4lkS6ihTe9sxtV4Q1OQppb/0zzyPSzoFANBa5MfsG/zgsWklmNnhm0XACZOH/0HBiQ==
|
||||||
|
|
||||||
mathjax-full@^3.0.0:
|
mathjax-full@^3.0.0:
|
||||||
version "3.2.0"
|
version "3.2.0"
|
||||||
resolved "https://registry.npmjs.org/mathjax-full/-/mathjax-full-3.2.0.tgz#e53269842a943d4df10502937518991268996c5c"
|
resolved "https://registry.npmjs.org/mathjax-full/-/mathjax-full-3.2.0.tgz#e53269842a943d4df10502937518991268996c5c"
|
||||||
|
Loading…
Reference in New Issue
Block a user