mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 09:17:23 +00:00
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
|
||||
|
||||
## 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-item';
|
||||
export * from './grid';
|
||||
export * from './icon-picker';
|
||||
export * from './input';
|
||||
export * from './input-number';
|
||||
export * from './password';
|
||||
export * from './radio';
|
||||
export * from './menu';
|
||||
export * from './page';
|
||||
export * from './password';
|
||||
export * from './radio';
|
||||
|
Loading…
Reference in New Issue
Block a user