mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-04 21:19:27 +08:00
feat: iconPicker support more icon (#5996)
* refactor: iconpicker support more icon * fix: test * fix: test * fix: test
This commit is contained in:
parent
7abe820950
commit
cbdd5ffe8c
@ -33,9 +33,7 @@ export function registerIcons(components) {
|
||||
}
|
||||
|
||||
Object.keys(antIcons).forEach((name) => {
|
||||
if (name.endsWith('Outlined')) {
|
||||
registerIcon(name, antIcons[name]);
|
||||
}
|
||||
registerIcon(name, antIcons[name]);
|
||||
});
|
||||
|
||||
interface IconProps {
|
||||
|
@ -1047,5 +1047,8 @@
|
||||
"Associate": "关联",
|
||||
"Please add or select record": "请添加或选择数据",
|
||||
"No data": "暂无数据",
|
||||
"Fields can only be used correctly if they are defined with an interface.": "只有字段设置了interface字段才能正常使用"
|
||||
}
|
||||
"Fields can only be used correctly if they are defined with an interface.": "只有字段设置了interface字段才能正常使用",
|
||||
"Outlined": "线框风格",
|
||||
"Filled": "实底风格",
|
||||
"Two tone": "双色风格"
|
||||
}
|
||||
|
@ -11,8 +11,8 @@ import { CloseOutlined, LoadingOutlined } from '@ant-design/icons';
|
||||
import { useFormLayout } from '@formily/antd-v5';
|
||||
import { connect, mapProps, mapReadPretty } from '@formily/react';
|
||||
import { isValid } from '@formily/shared';
|
||||
import { Button, Empty, Input, Space, theme } from 'antd';
|
||||
import { debounce } from 'lodash';
|
||||
import { Button, Empty, Input, Space, theme, Radio, Flex } from 'antd';
|
||||
import { debounce, groupBy } from 'lodash';
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Icon, hasIcon, icons } from '../../../icon';
|
||||
@ -33,6 +33,14 @@ interface IconPickerReadPrettyProps {
|
||||
value?: string;
|
||||
}
|
||||
|
||||
const groupByIconName = (data) => {
|
||||
return groupBy(data, (str) => {
|
||||
if (str.endsWith('outlined')) return 'Outlined';
|
||||
if (str.endsWith('filled')) return 'Filled';
|
||||
if (str.endsWith('twotone')) return 'TwoTone';
|
||||
});
|
||||
};
|
||||
|
||||
function IconField(props: IconPickerProps) {
|
||||
const { fontSizeXL } = theme.useToken().token;
|
||||
const availableIcons = [...icons.keys()];
|
||||
@ -40,7 +48,9 @@ function IconField(props: IconPickerProps) {
|
||||
const { value, onChange, disabled, iconSize = fontSizeXL, searchable = true } = props;
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [filteredIcons, setFilteredIcons] = useState(availableIcons);
|
||||
const [type, setType] = useState('Outlined');
|
||||
const { t } = useTranslation();
|
||||
const groupIconData = groupByIconName(availableIcons);
|
||||
|
||||
const style: any = {
|
||||
width: '26em',
|
||||
@ -57,6 +67,52 @@ function IconField(props: IconPickerProps) {
|
||||
);
|
||||
}, 250);
|
||||
|
||||
const IconContent = () => {
|
||||
return (
|
||||
<Flex vertical gap="middle">
|
||||
<Radio.Group
|
||||
options={[
|
||||
{
|
||||
label: t('Outlined'),
|
||||
value: 'Outlined',
|
||||
},
|
||||
{
|
||||
label: t('Filled'),
|
||||
value: 'Filled',
|
||||
},
|
||||
{
|
||||
label: t('Two tone'),
|
||||
value: 'TwoTone',
|
||||
},
|
||||
]}
|
||||
value={type}
|
||||
optionType="button"
|
||||
onChange={(e) => {
|
||||
setType(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<div>
|
||||
{groupIconData[type].map((key) => {
|
||||
if (filteredIcons.includes(key)) {
|
||||
return (
|
||||
<span
|
||||
key={key}
|
||||
title={key.replace(/outlined|filled|twotone$/i, '')}
|
||||
style={{ fontSize: iconSize, marginRight: 10, cursor: 'pointer' }}
|
||||
onClick={() => {
|
||||
onChange(key);
|
||||
setVisible(false);
|
||||
}}
|
||||
>
|
||||
<Icon type={key} />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<Space.Compact>
|
||||
@ -71,28 +127,11 @@ function IconField(props: IconPickerProps) {
|
||||
}}
|
||||
content={
|
||||
<div style={style}>
|
||||
{filteredIcons.length === 0 ? (
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
||||
) : (
|
||||
filteredIcons.map((key) => (
|
||||
<span
|
||||
key={key}
|
||||
title={key.replace(/outlined|filled|twotone$/i, '')}
|
||||
style={{ fontSize: iconSize, marginRight: 10, cursor: 'pointer' }}
|
||||
onClick={() => {
|
||||
onChange(key);
|
||||
setVisible(false);
|
||||
}}
|
||||
>
|
||||
<Icon type={key} />
|
||||
</span>
|
||||
))
|
||||
)}
|
||||
{filteredIcons.length === 0 ? <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /> : <IconContent />}
|
||||
</div>
|
||||
}
|
||||
title={
|
||||
<div>
|
||||
<div>{t('Icon')}</div>
|
||||
{searchable && (
|
||||
<Search
|
||||
style={{ marginTop: 8 }}
|
||||
|
@ -18,7 +18,6 @@ describe('IconPicker', () => {
|
||||
const button = container.querySelector('button') as HTMLButtonElement;
|
||||
await userEvent.click(button);
|
||||
|
||||
expect(screen.getByText('Icon')).toHaveTextContent(`Icon`);
|
||||
expect(screen.queryAllByRole('img').length).toBe(422);
|
||||
});
|
||||
|
||||
|
@ -27,7 +27,7 @@ export async function iconChecker(options: IconCheckOptions) {
|
||||
|
||||
await waitFor(() => {
|
||||
expectNoTsError(screen.queryByRole('tooltip')).toBeInTheDocument();
|
||||
expectNoTsError(screen.getByRole('tooltip').querySelector('.ant-popover-title')).toHaveTextContent('Icon');
|
||||
// expectNoTsError(screen.getByRole('tooltip').querySelector('.ant-popover-title')).toHaveTextContent('Icon');
|
||||
});
|
||||
|
||||
await userEvent.click(screen.getByRole('tooltip').querySelector(`span[aria-label="${options.newValue}"]`));
|
||||
|
Loading…
x
Reference in New Issue
Block a user