react+antd Input回车输入生成tag组件

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

参考链接:

https://blog.csdn.net/qq_49464155/article/details/123247090

相关推荐
Csvn7 分钟前
📌 position: sticky 失效的 6 个经典坑:明明写了 sticky,为什么就是不吸顶?
前端
恋猫de小郭9 分钟前
Flutter 的另外一种形态?社区 DartNative 要来了。
android·前端·flutter
秋天的一阵风16 分钟前
🔥 Network 里那坨 "data:" 我真看吐了,自制开源 Chrome 插件,AI 流式调试直接开挂
前端·人工智能·后端
看到我请叫我铁锤22 分钟前
vue编写web端在线预览文档
前端·javascript·vue.js
Ratten27 分钟前
【Vben 解决】---- 跨页复选获取选择数据
前端
ssshooter31 分钟前
为什么明明只有一个 12px 的小元素,父容器却有 63px 高?
前端·javascript·面试
IT_陈寒38 分钟前
Python的GIL让我深夜加班,这破锁到底怎么折腾的
前端·人工智能·后端
乘风gg1 小时前
9 张 AI 生成的图,吃透任何一个前端项目
前端·ai编程·claude
moonsims1 小时前
低空量子无人机
前端·数据结构
breeze jiang1 小时前
React + WebGPU 在浏览器运行 DeepSeek:从 Worker 通信到流式生成
前端·javascript·react.js