feat: iconPicker support more icon (#5996)

* refactor: iconpicker support more icon

* fix: test

* fix: test

* fix: test
This commit is contained in:
Katherine 2025-01-08 09:32:16 +08:00 committed by GitHub
parent 7abe820950
commit cbdd5ffe8c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 66 additions and 27 deletions

View File

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

View File

@ -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": "双色风格"
}

View File

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

View File

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

View File

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