重构-标签(多元)

动画:
	删除图标的移入移出颜色渐变
	添加标签后,添加的标签伸缩变化(实际是旋转实现)
		设置索引来指向添加的标签,为其添加一个类,类使用帧动画(才能一开始就加载动画)
		
添加标签的实现:
	动态切换添加按钮和输入框
	输入内容后,光标消失时,将不为空的内容放进数组,重新渲染
	
参数:
	  tags: PropTypes.array,		标签数组,格式为[{name:'标签名',type{color:'背景色',borderColor:'边框色',fontColor:'字体颜色'}}]
	  delete: PropTypes.bool,		标签是否可删除
	  add:PropTypes.bool,			标签是否可添加
	  onDelete: PropTypes.func,		删除回调,参数为删除后的数组
	  onTagClick: PropTypes.func,	点击标签回调,第一个参数为标签索引,第二个参数为标签内容
	  onAdd:PropTypes.func,         添加回调,参数为添加标签后的数组
	  width: PropTypes.string,      标签总体容器的宽度,用于换行,默认不设置不换行
	  height: PropTypes.string,		标签高度
	  borderRadius: PropTypes.string,	标签圆角
	  fontSize:PropTypes.string,		标签字体大小
	  marginRight: PropTypes.string,	标签距离右间距
	  marginBottom: PropTypes.string,   标签底间距
	  iconColor: PropTypes.string,		图标背景颜色
	  hoverIconColor: PropTypes.string, 鼠标hover图标背景颜色

效果图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码示例:
使用:

import React,{useState} from 'react'
import Tag from './tag/tag'

// type:{color:'red',borderColor:'orange',fontColor:'green'}

const tags = [{ name: '标签一'},
  { name: '标签二'},
  { name: '标签三'},
  {name:'标签四'}

]

function App(){

  const onDelete = function (data)
  {
    console.log(data)
  }

  const onTagClick = function (index, name)
  {
    console.log(name);
    console.log(index);
  }

  const onAdd = function (data)
  {
    console.log(data);
  }

  return(

    <div>
      <Tag width='200px'  onAdd={onAdd} onDelete={onDelete} onTagClick={onTagClick} tags={tags} delete add></Tag>
    </div>
  )
}

export default App

tag.jsx:

import React, { useState,useCallback,useEffect,useRef } from 'react'
import PropTypes from 'prop-types'
import './tag.css'

function App(props) {
  
  const [tags, setTags] = useState([]);
  const [newTag, setNewTag] = useState(false);
  const [addIndex, setAddIndex] = useState(-1);
  const tagf = useRef(null);
  const inputf = useRef(null);


  useEffect(() => {
    setTags(props.tags);

    _styleHandler('width', props.width);
    _styleHandler('height', props.height);
    _styleHandler('borderRadius', props.borderRadius);
    _styleHandler('fontSize', props.fontSize);
    _styleHandler('marginRight', props.marginRight);
    _styleHandler('marginBottom', props.marginBottom);
    _styleHandler('iconColor', props.iconColor);
    _styleHandler('hoverIconColor', props.hoverIconColor);

  }, [props.tags])
  
  //统一初始化处理
  const _styleHandler = useCallback((key, value) => {
    // console.log(props[keys]);
    props[key] && tagf.current.style.setProperty('--' + key, value)
    
  },[])

  // 删除
  const _delete = useCallback((index) => {
    
    tags.splice(index, 1);
    setTags([...tags]);

    props.onDelete(tags);


  },[tags])

  // 点击
  const _clickhandler = useCallback((index,name) => {
    props.onTagClick(index, name);
  }, [])
  

  // 添加
  const _addTagItem =useCallback(() => {
    setNewTag(true);


  },[inputf])

  //光标消失,统一处理数据
  const _onBlur = useCallback(() => {
    let value=inputf.current.value
    if (value !== '')
    {
      tags.push({ name: value })
      setTags([...tags]);
      setAddIndex(tags.length - 1);
      
      props.onAdd(tags);
    }
    setNewTag(false);
    inputf.current.value = '';

  }, [tags])
  
  //回车事件
  const _onKeyUp = useCallback((e) => {
    if (e.keyCode === 13)
    {
      inputf.current.blur();
      setNewTag(false);
    }
    
  }, [tags])
  
  return(
    
    <div ref={tagf} className='jf-tag-container'>
      <div className='jf-tag-layout'>
        {
              
          tags.map((item, index) => {
            
            return (
              <div className={`jf-tag-item ${(addIndex==index)&&'jf-tag-item-add'} `} key={item + index}
                style={{
                  backgroundColor:item.type?(item.type.color ? item.type.color:props.color):props.color,
                  borderColor: item.type?(item.type.borderColor?item.type.borderColor:props.borderColor):props.borderColor,
                  color:item.type?(item.type.fontColor?item.type.fontColor:props.fontColor):props.fontColor,
                }}
              
              >
                <span onClick={() => { _clickhandler(index,item.name) }} className='jf-tag-item-name'>{item.name}</span>
                {props.delete && <i onClick={() => { _delete(index) }} className='jf-tag-delete iconfont icon-shanchu'></i>}
              </div>   
            )
          })
        }
        {props.add && <input className='jf-tag-input' style={{ display: newTag ? 'block' : 'none' }} ref={inputf} onBlur={_onBlur} onKeyUp={_onKeyUp} autoFocus type="text" />}
        {props.add && <div
          className='jf-tag-item jf-tag-item-new'
          style={{ display: newTag ? 'none' : 'block' }}
          onClick={(e) => { _addTagItem(e) }}>
          <span className='jf-tag-item-name'>+新增</span>
        </div>}

      </div>
    </div>
  )
}

App.propTypes = {
  tags: PropTypes.array,
  delete: PropTypes.bool,
  add:PropTypes.bool,
  onDelete: PropTypes.func,
  onTagClick: PropTypes.func,
  onAdd:PropTypes.func,
  width: PropTypes.string,
  height: PropTypes.string,
  borderRadius: PropTypes.string,
  fontSize:PropTypes.string,
  marginRight: PropTypes.string,
  marginBottom: PropTypes.string,
  iconColor: PropTypes.string,
  hoverIconColor: PropTypes.string,
}

App.defaultProps = {
  tags: [],
  onDelete: () => {},
  onTagClick: () => { },
  onAdd:()=>{},
  color: '#ecf5ff',
  borderColor: '#d9ecff',
  fontColor: '#409eff',
}

export default React.memo(App)

tag.css:

.jf-tag-container {
  display: inline-block;
  box-sizing: border-box;

  --width: 100%;
  --height: 38px;
  --color: #ecf5ff;
  --borderColor: #d9ecff;
  --borderRadius: 5px;
  --fontColor: #409eff;
  --fontSize: 12px;
  --marginRight: 20px;
  --marginBottom: 5px;
  --iconColor: #ddd;
  --hoverIconColor: #ccc;
}

.jf-tag-layout {
  display: flex;
  flex-wrap: wrap;
  width: var(--width);
}

.jf-tag-item {
  position: relative;
  box-sizing: border-box;
  height: var(--height);
  color: var(--fontColor);
  line-height: var(--height);
  background-color: var(--color);
  border: solid 1px var(--borderColor);
  border-radius: var(--borderRadius);
  display: flex;
  justify-content: space-between;
  font-size: var(--fontSize);
  margin: 0 var(--marginRight) var(--marginBottom) 0;
}

.jf-tag-item-add {
  animation: scale 0.5s;
}

.jf-tag-input {
  outline: none;
  border: solid 1px #409eff;
  height: var(--height);
  width: 89px;
  border-radius: var(--borderRadius);
}

.jf-tag-item-new {
  cursor: pointer;
}

@keyframes scale {
  from {
    transform: rotateY(90deg);
  }
  to {
    transform: rotateY(0deg);
  }
}
.jf-tag-item-name {
  display: inline-block;
  flex: 1;
  text-align: center;
  padding: 0 5px;
}

.jf-tag-delete {
  margin: 0;
  padding-right: 2px;
  color: var(--iconColor);
  /* position: absolute; */
  /* right: 2px; */
}

.jf-tag-delete:hover {
  color: var(--hoverIconColor);
  cursor: pointer;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值