
EditableTagGroup.tsx:
javascript
import React, { useState, useRef, useEffect } from 'react'
import { Input, Tag, Tooltip, message } from 'antd'
import { CloseCircleFilled } from '@ant-design/icons'
import isEqual from 'lodash/isEqual'
import { useTranslation } from 'react-i18next'
import './index.scss'
interface ITagProps {
value?: string[]
onChange?: (value: string[]) => void
}
const EditableTagGroup: React.FC<ITagProps> = ({ value = [], onChange }: any) => {
const [tags, setTags] = useState<any>(value)
const [inputValue, setInputValue] = useState<string>('')
const inputRef = useRef<any>(null)
const { t } = useTranslation()
// Sync props.value to state when it changes
useEffect(() => {
if (!isEqual(value, tags)) {
setTags(value)
}
}, [value])
// Notify parent component when tags change
// useEffect(() => {
// if (onChange && !isEqual(value, tags)) {
// console.log(value, tags)
// onChange(tags)
// }
// }, [tags])
const handleClose = (removedTag: any) => {
const newTags = tags.filter((tag: any) => tag.id !== removedTag.id)
setTags(newTags)
onChange(newTags)
}
const inputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value)
}
const isValidEntityToken = (t: string): boolean => {
const re = /^(?:\p{Script=Han}|[A-Za-z])+$/u
return t.length >= 1 && t.length <= 64 && re.test(t)
}
const addTag = (e: React.KeyboardEvent<HTMLInputElement>) => {
const value = (e.target as HTMLInputElement).value
if (value) {
if (isValidEntityToken(value)) {
let now = Date.now()
const newTags = [...tags, { id: now, text: value }]
setTags(newTags)
setInputValue('')
onChange(newTags)
} else {
message.info(t('models.entityTypesWarning'))
}
}
}
const clickEdit = () => {
inputRef.current?.focus()
}
const renderTags = () => {
return (
<>
{tags?.map((tag: any, index: any) => {
const isLongTag = tag.text.length > 20
const tagElem = (
<Tag key={tag.id} onClose={() => handleClose(tag)} closeIcon={<CloseCircleFilled className="close-icon" />}>
{isLongTag ? `${tag.text.slice(0, 20)}...` : tag.text}
</Tag>
)
return isLongTag ? (
<Tooltip title={tag.text} key={tag.id}>
{tagElem}
</Tooltip>
) : (
tagElem
)
})}
</>
)
}
return (
<div className="editable" onClick={clickEdit}>
<div className="show">{renderTags()}</div>
<Input
className="input"
ref={inputRef}
placeholder={t('models.entityTypesPlaceholder')}
onPressEnter={addTag}
value={inputValue}
onChange={inputChange}
/>
</div>
)
}
export default EditableTagGroup
index.scss:
css
.editable {
height: 100px;
border-radius: 2px;
background-color: rgba(255, 255, 255, 1);
border: 1px solid #d9d9d9;
display: flex;
flex-direction: column;
justify-content: space-between;
&:focus-within,
&:hover {
border-color: rgba(22, 119, 254, 1);
box-shadow: 0 0 4px 0 rgba(10, 42, 97, 0.2);
}
.ant-input {
border: none;
height: 32px;
&:hover,
&:focus {
box-shadow: none;
border: none;
}
}
.show {
flex: 1;
min-height: '47px';
overflow: auto;
padding: 5px 5px;
.ant-tag {
color: #5a5e66;
padding: 2px 8px;
margin: 0 4px 4px 0;
font-size: 12px;
border: none;
border-radius: 3px;
background-color: rgba(240, 242, 245, 1);
.close-icon {
color: rgba(192, 196, 204, 1);
margin-left: 2px;
font-size: 12px;
}
}
}
}
参考链接: