feat: 图标支持快捷搜索, 悬浮提示 (#743)
![image](/attachments/24bd3a25-990d-431c-8682-a7e034d7826d) Co-authored-by: sealday <zhanglin@daoyoucloud.com> Reviewed-on: daoyoucloud/tachycode#743 Co-authored-by: bai.zixv <bai.zixv@foxmail.com> Co-committed-by: bai.zixv <bai.zixv@foxmail.com>
This commit is contained in:
parent
8d9fc95977
commit
e3f23fa7b1
@ -57,7 +57,7 @@ CACHE_MEMORY_MAX=2000
|
|||||||
################# STORAGE (Initialization only) #################
|
################# STORAGE (Initialization only) #################
|
||||||
|
|
||||||
INIT_LANG=en-US
|
INIT_LANG=en-US
|
||||||
INIT_ROOT_EMAIL=admin@nocobase.com
|
INIT_ROOT_EMAIL=admin@tachybase.com
|
||||||
INIT_ROOT_PASSWORD=admin123
|
INIT_ROOT_PASSWORD=!Admin123.
|
||||||
INIT_ROOT_NICKNAME=Super Admin
|
INIT_ROOT_NICKNAME=Super Admin
|
||||||
INIT_ROOT_USERNAME=nocobase
|
INIT_ROOT_USERNAME=tachybase
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
import { Input } from 'antd';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export const IconFilter = (props) => {
|
||||||
|
const { changeFilterKey } = props;
|
||||||
|
const onChange = _.debounce((e) => {
|
||||||
|
const inputValue = e.target.value;
|
||||||
|
changeFilterKey(inputValue);
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
return <Input allowClear placeholder={`antd的key,例如"ApiOutlined",忽略大小写`} onChange={onChange} />;
|
||||||
|
};
|
@ -0,0 +1,47 @@
|
|||||||
|
import React, { useMemo, useState } from 'react';
|
||||||
|
import { Icon, icons } from '../../../icon';
|
||||||
|
import { Tooltip } from 'antd';
|
||||||
|
|
||||||
|
export const IconList = (props) => {
|
||||||
|
const { filterKey, onChange, changePop } = props;
|
||||||
|
|
||||||
|
const iconKeysByFilter = useMemo(() => {
|
||||||
|
if (!filterKey) {
|
||||||
|
return icons;
|
||||||
|
}
|
||||||
|
const filterIcons = new Map();
|
||||||
|
for (const key of icons.keys()) {
|
||||||
|
if (key.toLowerCase().includes(filterKey.toLowerCase())) {
|
||||||
|
filterIcons.set(key, icons.get(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filterIcons;
|
||||||
|
}, [filterKey]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ width: '26em', maxHeight: '20em', overflowY: 'auto' }}>
|
||||||
|
{[...iconKeysByFilter.keys()].map((key) => (
|
||||||
|
<span key={key} style={{ fontSize: 18, marginRight: 10, cursor: 'pointer' }}>
|
||||||
|
<Tooltip
|
||||||
|
destroyTooltipOnHide
|
||||||
|
mouseEnterDelay={0.3}
|
||||||
|
mouseLeaveDelay={0.3}
|
||||||
|
placement="top"
|
||||||
|
title={key}
|
||||||
|
arrow={{
|
||||||
|
pointAtCenter: true,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
onClick={() => {
|
||||||
|
onChange(key);
|
||||||
|
changePop(false);
|
||||||
|
}}
|
||||||
|
type={key}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -3,16 +3,20 @@ import { useFormLayout } from '@formily/antd-v5';
|
|||||||
import { connect, mapProps, mapReadPretty } from '@nocobase/schema';
|
import { connect, mapProps, mapReadPretty } from '@nocobase/schema';
|
||||||
import { isValid } from '@nocobase/schema';
|
import { isValid } from '@nocobase/schema';
|
||||||
import { Button, Space } from 'antd';
|
import { Button, Space } from 'antd';
|
||||||
import React, { useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Icon, hasIcon, icons } from '../../../icon';
|
import { Icon, hasIcon, icons } from '../../../icon';
|
||||||
import { StablePopover } from '../popover';
|
import { StablePopover } from '../popover';
|
||||||
|
import { IconFilter } from './IconFilter';
|
||||||
|
import { IconList } from './IconList';
|
||||||
|
|
||||||
function IconField(props: any) {
|
function IconField(props: any) {
|
||||||
const layout = useFormLayout();
|
|
||||||
const { value, onChange, disabled } = props;
|
const { value, onChange, disabled } = props;
|
||||||
const [visible, setVisible] = useState(false);
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const layout = useFormLayout();
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [filterKey, setFilterKey] = useState('');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Space.Compact>
|
<Space.Compact>
|
||||||
@ -26,20 +30,10 @@ function IconField(props: any) {
|
|||||||
setVisible(val);
|
setVisible(val);
|
||||||
}}
|
}}
|
||||||
content={
|
content={
|
||||||
<div style={{ width: '26em', maxHeight: '20em', overflowY: 'auto' }}>
|
<>
|
||||||
{[...icons.keys()].map((key) => (
|
<IconFilter changeFilterKey={setFilterKey} />
|
||||||
<span
|
<IconList filterKey={filterKey} onChange={onChange} changePop={setVisible} />
|
||||||
key={key}
|
</>
|
||||||
style={{ fontSize: 18, marginRight: 10, cursor: 'pointer' }}
|
|
||||||
onClick={() => {
|
|
||||||
onChange(key);
|
|
||||||
setVisible(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon type={key} />
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
title={t('Icon')}
|
title={t('Icon')}
|
||||||
trigger="click"
|
trigger="click"
|
||||||
|
Loading…
Reference in New Issue
Block a user