feat: add IconPicker component into schema components
This commit is contained in:
parent
d94874d345
commit
d04d3d0639
51
packages/client/src/components/icon/Icon.tsx
Normal file
51
packages/client/src/components/icon/Icon.tsx
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import * as antIcons from '@ant-design/icons';
|
||||||
|
import { createFromIconfontCN } from '@ant-design/icons';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export const IconFont = createFromIconfontCN({
|
||||||
|
scriptUrl: ['//at.alicdn.com/t/font_2261954_u9jzwc44ug.js'],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const icons = new Map<string, any>();
|
||||||
|
|
||||||
|
export function registerIcon(type: string, icon: any = IconFont) {
|
||||||
|
icons.set(type.toLowerCase(), icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasIcon(type: string) {
|
||||||
|
if (!type) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return icons.has(type.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerIcons(components) {
|
||||||
|
Object.keys(components).forEach((type) => {
|
||||||
|
registerIcon(type, components[type]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(antIcons).forEach((name) => {
|
||||||
|
if (name.endsWith('Outlined')) {
|
||||||
|
registerIcon(name, antIcons[name]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
interface IconProps {
|
||||||
|
type: string;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Icon(props: IconProps) {
|
||||||
|
const { type = '', ...restProps } = props;
|
||||||
|
if (type && icons.has(type.toLowerCase())) {
|
||||||
|
const IconComponent = icons.get(type.toLowerCase());
|
||||||
|
if (IconComponent === IconFont) {
|
||||||
|
return <IconFont type={type} />;
|
||||||
|
}
|
||||||
|
return <IconComponent {...restProps} />;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Icon;
|
1
packages/client/src/components/icon/index.ts
Normal file
1
packages/client/src/components/icon/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './Icon';
|
1
packages/client/src/components/index.ts
Normal file
1
packages/client/src/components/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './icon';
|
@ -0,0 +1,71 @@
|
|||||||
|
import { CloseOutlined, LoadingOutlined } from '@ant-design/icons';
|
||||||
|
import { connect, mapProps, mapReadPretty } from '@formily/react';
|
||||||
|
import { isValid } from '@formily/shared';
|
||||||
|
import { Button, Input, Popover } from 'antd';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { hasIcon, Icon, icons } from '../../../components/icon';
|
||||||
|
|
||||||
|
function IconField(props: any) {
|
||||||
|
const { value, onChange } = props;
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Input.Group compact>
|
||||||
|
<Popover
|
||||||
|
placement={'bottom'}
|
||||||
|
visible={visible}
|
||||||
|
onVisibleChange={(val) => {
|
||||||
|
setVisible(val);
|
||||||
|
}}
|
||||||
|
content={
|
||||||
|
<div style={{ width: '26em', maxHeight: '20em', overflowY: 'auto' }}>
|
||||||
|
{[...icons.keys()].map((key) => (
|
||||||
|
<span
|
||||||
|
style={{ fontSize: 18, marginRight: 10, cursor: 'pointer' }}
|
||||||
|
onClick={() => {
|
||||||
|
onChange(key);
|
||||||
|
setVisible(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon type={key} />
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
title={t('Icon')}
|
||||||
|
trigger="click"
|
||||||
|
>
|
||||||
|
<Button>{hasIcon(value) ? <Icon type={value} /> : t('Select icon')}</Button>
|
||||||
|
</Popover>
|
||||||
|
{value && (
|
||||||
|
<Button
|
||||||
|
icon={<CloseOutlined />}
|
||||||
|
onClick={(e) => {
|
||||||
|
onChange(null);
|
||||||
|
}}
|
||||||
|
></Button>
|
||||||
|
)}
|
||||||
|
</Input.Group>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const IconPicker = connect(
|
||||||
|
IconField,
|
||||||
|
mapProps((props, field) => {
|
||||||
|
return {
|
||||||
|
...props,
|
||||||
|
suffix: <span>{field?.['loading'] || field?.['validating'] ? <LoadingOutlined /> : props.suffix}</span>,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
mapReadPretty((props) => {
|
||||||
|
if (!isValid(props.value)) {
|
||||||
|
return <div></div>;
|
||||||
|
}
|
||||||
|
return <Icon type={props.value} />;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
export default IconPicker;
|
@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* title: IconPicker
|
||||||
|
*/
|
||||||
|
import { FormItem } from '@formily/antd';
|
||||||
|
import { IconPicker, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
input: {
|
||||||
|
type: 'boolean',
|
||||||
|
title: `Editable`,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'IconPicker',
|
||||||
|
'x-reactions': {
|
||||||
|
target: 'read',
|
||||||
|
fulfill: {
|
||||||
|
state: {
|
||||||
|
value: '{{$self.value}}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
read: {
|
||||||
|
type: 'boolean',
|
||||||
|
title: `Read pretty`,
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'IconPicker',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
return (
|
||||||
|
<SchemaComponentProvider components={{ IconPicker, FormItem }}>
|
||||||
|
<SchemaComponent schema={schema} />
|
||||||
|
</SchemaComponentProvider>
|
||||||
|
);
|
||||||
|
};
|
@ -6,3 +6,9 @@ group:
|
|||||||
---
|
---
|
||||||
|
|
||||||
# IconPicker
|
# IconPicker
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### IconPicker usage
|
||||||
|
|
||||||
|
<code src="./demos/demo1.tsx" />
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
export * from './IconPicker';
|
@ -7,9 +7,10 @@ export * from './dnd-context';
|
|||||||
export * from './form';
|
export * from './form';
|
||||||
export * from './form-item';
|
export * from './form-item';
|
||||||
export * from './grid';
|
export * from './grid';
|
||||||
|
export * from './icon-picker';
|
||||||
export * from './input';
|
export * from './input';
|
||||||
export * from './input-number';
|
export * from './input-number';
|
||||||
export * from './password';
|
|
||||||
export * from './radio';
|
|
||||||
export * from './menu';
|
export * from './menu';
|
||||||
export * from './page';
|
export * from './page';
|
||||||
|
export * from './password';
|
||||||
|
export * from './radio';
|
||||||
|
Loading…
Reference in New Issue
Block a user