重构-下拉菜单

动画:
	点击标题伸缩
	点击菜单项伸缩
	
实现:
	子元素未知高度过渡:通过js获取子元素高度后,再将其隐藏,后续就可通过获取的高度进行动画过渡

参数:
	App.propTypes = {
	  click: PropTypes.func,				点击菜单项回调,第一个参数是索引,第二个参数是菜单项内容
	  title:PropTypes.string,				下拉框的标题
	  tipTriangleWidth:PropTypes.string,    下拉框的三角提示大小
	  tipTriangleLeft:PropTypes.string,	    下拉框的三角提示位置
	  tipBorderColor:PropTypes.string,		下拉框的边框色
	  titleColor:PropTypes.string,		    标题颜色
	  titleSize:PropTypes.string,           标题字体大小
	  itemSize:PropTypes.string,			菜单项字体大小
	  itemColor:PropTypes.string,           菜单项字体颜色
	  hoverColor:PropTypes.string,          菜单项hover背景色
	}

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

import React,{useState} from 'react'
import Dropdown from  './dropdown/dropdown'
import DropItem from './dropdown/dropItem/dropItem'

function App(){

  const click=function (index,text) {
    console.log(index);
    console.log(text);
  }

  return(

    <div>
      <Dropdown
        click={click}
        title={'下拉内容'}
      >
        <DropItem>哈哈哈哈哈哈哈哈哈</DropItem>
        <DropItem>哈哈哈哈哈哈哈哈哈</DropItem>
        <DropItem>哈哈哈哈哈哈哈哈哈</DropItem>
        <DropItem>哈哈哈哈哈哈哈哈哈</DropItem>
        <DropItem>哈哈哈哈哈哈哈哈哈</DropItem>
        <DropItem>哈哈哈哈哈哈哈哈哈</DropItem>
        <DropItem>哈哈哈哈哈哈哈哈哈</DropItem>
      </Dropdown>
    </div>
  )
}

export default App

dropdown.jsx:

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



function App(props) {
  const [panelHeight, setPanelHeight] = useState(0);
  const [pop, setPop] = useState(false);
  const dropf = useRef(null);
  const panelf = useRef(null);

  //样式处理
  useEffect(() => {
    
    _styleHandler('tipTriangleWidth');
    _styleHandler('tipTriangleLeft');
    _styleHandler('tipBorderColor');
    _styleHandler('titleColor');
    _styleHandler('titleSize');
    _styleHandler('itemSize');
    _styleHandler('itemColor');
    _styleHandler('hoverColor');
    
  }, [])

  // 统一样式处理
  const _styleHandler = useCallback((key) => {
    props[key] && dropf.current.style.setProperty('--' + key, props[key])
    
  }, [])

  //初始获取子组件高度
  useEffect(() => {
    setPanelHeight(panelf.current.scrollHeight);
    panelf.current.style.display = 'none'; 
    panelf.current.style.height = 0 + 'px';
  }, [])

  //点击标题收缩展开
  const _click = useCallback(function () {

    if (pop) {
      panelf.current.style.height = 0 + 'px';
      panelf.current.style.opacity = '0';
    } else {

      panelf.current.style.display = 'block';
      setTimeout(() => {
        panelf.current.style.opacity = '1';
        panelf.current.style.height = panelHeight + 'px';
    },10)
    }
    setPop(!pop); 

  },[pop,panelHeight])

  //展开后点击收缩

  const _hide=useCallback(function (index,text) {

    if (pop) {
      panelf.current.style.height = 0 + 'px';
      panelf.current.style.opacity = '0';
    }
    setPop(!pop);
    props.click(index, text);
  },[pop,panelHeight])
  
  //监听过渡结束,设置none
  const _transitioned = useCallback(function () {
    if (!pop) {
      panelf.current.style.display='none'; 
    }
    
  }, [pop])
  
  return(

    <div className='jf-dropdown-container' ref={dropf}>
      <div className='jf-dropdown-title' onClick={_click}>
        <span>{props.title }</span>
        <span className='iconfont icon-xiasanjiao'></span>
      </div>

      <div onTransitionEnd={_transitioned} className='jf-dropdown-panel' ref={panelf}>
        <div className='jf-dropdown-triangle'></div>
        <ul className='jf-dropdown-cont'>
          {
            props.children.map((item,index)=>{
              return <DropItem click={_hide} index={index} {...item.props} key={item+index}>{ item.props.children }</DropItem>
            })
          }
        </ul>
        
        
      </div>
    </div>
  )
}
// export const xxx=xx;
export default App;

App.propTypes = {
  click: PropTypes.func,
  title:PropTypes.string,
  tipTriangleWidth:PropTypes.string,
  tipTriangleLeft:PropTypes.string,
  tipBorderColor:PropTypes.string,
  titleColor:PropTypes.string,
  titleSize:PropTypes.string,
  itemSize:PropTypes.string,
  itemColor:PropTypes.string,
  hoverColor:PropTypes.string,
}

App.defaultProps = {
  click:()=>{}
}

dropdown.css:

.jf-dropdown-container {
  display: inline-block;
  position: relative;

  --tipTriangleWidth: 6px;
  --tipTriangleLeft: 5%;
  --tipBorderColor: #ebeef5;
  --titleColor: #409eff;
  --titleSize: 16px;
  --itemSize: 16px;
  --itemColor: black;
  --hoverColor: #ecf5ff;
}
.jf-dropdown-title {
  color: var(--titleColor);
  cursor: pointer;
  font-size: var(--titleSize);
}
.jf-dropdown-title > .iconfont {
  font-size: var(--titleSize);
}

.jf-dropdown-panel {
  position: absolute;
  overflow: hidden;
  opacity: 0;
  transition: height 0.5s, opacity 0.2s 0.1s;

  margin: 5px 0;
}

.jf-dropdown-cont {
  white-space: nowrap;
  z-index: 2000;
  border: 1px solid var(--tipBorderColor, #ebeef5);
  border-radius: 4px;
  padding-left: 0;
  color: var(--itemColor);
  margin: 0;
  font-size: var(--itemSize);

  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

.jf-dropdown-cont > li:hover {
  background-color: var(--hoverColor);
}

.jf-dropdown-triangle {
  position: relative;
  /* top: -6px; */
  width: 0;
  height: 0;
  left: var(--tipTriangleLeft);
  box-sizing: border-box;
  border-top-width: 0px;
  border-right-width: var(--tipTriangleWidth, 6px);
  border-bottom-width: var(--tipTriangleWidth, 6px);
  border-left-width: var(--tipTriangleWidth, 6px);

  border-top-color: transparent;
  border-right-color: transparent;
  border-bottom-color: var(--tipBorderColor, #ebeef5);
  border-left-color: transparent;

  border-style: solid;
}

.jf-dropdown-triangle::after {
  content: " ";
  box-sizing: border-box;
  position: absolute;
  top: 1px;
  left: 0;
  margin-left: calc(0px - var(--tipTriangleWidth, 6px));
  border-width: var(--tipTriangleWidth, 6px);
  border-right-width: var(--tipTriangleWidth, 6px);
  border-bottom-width: var(--tipTriangleWidth, 6px);
  border-left-width: var(--tipTriangleWidth, 6px);
  border-top-width: 0;
  border-bottom-color: white;

  width: 0;
  height: 0;
  border-top-color: transparent;
  border-right-color: transparent;
  border-left-color: transparent;
  border-style: solid;
}

dropdown/dropItem.jsx:

import React, { useState,useCallback } from 'react'
import './dropItem.css'

function App(props){

  return(
    
    <li onClick={()=>{props.click(props.index,props.children)}} className='jf-dropItem-container'>
      {props.children}
    </li>

  )
}

export default App

dropdown/dropItem.css:

.jf-dropItem-container {
  cursor: pointer;
  height: 20px;
  padding: 10px 5px;
  list-style: none;
}

.jf-dropItem-container:hover {
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值