React_自定义组件_下拉框

news2024/11/14 3:22:45

目录

一、效果图

二、代码

1.直接使用_不和父组件传参

2.作为通用组件使用_和父组件传参


一、效果图

1.未选择任何选项时

2.悬浮效果

3.点击效果

4.选中选项的样式

5.选项太多时效果,(设置最大高度200,根据需要自行更改.popover-box样式的max-height即可)

二、代码

使用技术 react+scss+tsx ,如果你用 react+scss+jsx , 只需要将所有的类型注解(num:number 和 person:string)后面的类型去掉即可。只有函数的参数使用了类型注解,去掉即可。

新建一个文件夹 selecteOption,下面建两个文件 index.tsx,index.scss。

1.直接使用_不和父组件传参

index.tsx:

import './index.scss';
// 使用svg,后期更改图标颜色比较方便,还可以做悬浮变色。记得换成你自己路径下的图片
import { ReactComponent as Up } from '../../assets/images/icon-up.svg';
import { useState, useEffect } from 'react'

function SelectOption() {

  // 选项body的滚动条
  const [scrollWidth, setScrollWidth] = useState(0)

  // 选项弹窗是否显示,为了优化用户体验,控制它的opacity透明度隐藏,为了使选项点击事件能够生效,选项弹窗立即隐藏
  const [showOption, setShowOption] = useState(false)
  // 选项弹窗是否显示,控制它的div是否存在,(弹窗隐藏后元素存在,点击事件生效)需要将弹窗移除
  const [showOptionSetTimeout, setShowOptionSetTimeout] = useState(false)

  // 当前选中的选项
  const [selectedItem, setSelectedItem] = useState({
    id: '',
    item: ''
  })

  //挂载后,计算选项弹窗是否有滚动条,从而改变选项弹窗body宽度
  function changeWidth() {
    var myElement: any = document.getElementById('mySelecte');
    var widthWithBorder = myElement.offsetWidth;
    var widthWithoutBorder = myElement.clientWidth;
    setScrollWidth(widthWithBorder - widthWithoutBorder);
  }
  useEffect(() => {
    changeWidth();
  }, []);

  /**
   * input元素获得焦点以及失去焦点的事件
   */
  function showOptionBox() {
    setShowOption(true)
    setShowOptionSetTimeout(true)
  }

  /**
   * 选择框失去焦点事件的处理逻辑  
   */
  function inputBlur() {
    setShowOption(false)
    console.log(showOption, "showOption")
    setTimeout(() => {
      setShowOptionSetTimeout(false)
    }, 300);
  }

  /**
   * 用户选中了某个选项
   * @param id 区分选项的唯一标志
   * @param item 选中的名称
   */
  function selectedOption(id: number, item: string) {
    setSelectedItem({
      id: String(id), item
    });
  }

  return (
    <div className='so-all-box' >
      <input type="button" onBlur={inputBlur} className={showOption ? 'so-box borderB' : 'so-box borderA'} id="mySelecte" onClick={() => { showOptionBox() }}>
      </input>
      <div className={selectedItem.id === '' ? "lr-selected-manage-around-text textA" : "lr-selected-manage-around-text textB"}>
        {selectedItem.id === '' ? '请选择' : selectedItem.item}
      </div>

      {/* 箭头 */}
      <Up className={showOption === true ? 'lr-selected-manage-around-icon-select arrow-down' : 'lr-selected-manage-around-icon-select arrow-up'}></Up>

      {/* 选择元素的下拉框*/}
      {showOptionSetTimeout ?
        <div className={showOption ? 'popover-box popover-box-show' : 'popover-box popover-box-hidden'} id="mySelecte" style={{ width: `calc(100% + ${scrollWidth}px)` }}>
          {['牛肉馅饼', '北京烤鸭', '蛋糕', '铁锅炖大鹅', '可乐鸡翅'].map((item, index) => (
            <div key={index} className={selectedItem.item === item ? "popover-single popover-single-selected" : "popover-single"} onClick={() => { selectedOption(index, item) }}>{item}</div>
          ))}
        </div>
        : ''}

    </div>

  );
}

export default SelectOption;

index.scss

.so-all-box {
    width: 360px;
    position: relative;

    .textA {
        color: #A7AAA8;
    }

    .textB {
        color: #1E201F;
    }

    .lr-selected-manage-around-text {
        position: absolute;
        top: 6px;
        left: 12px;
    }

    .popover-box-show {
        opacity: 1;
    }

    .popover-box-hidden {
        opacity: 0;
    }

    .popover-box {
        padding: 4px 0;
        position: absolute;
        top: 34px;
        left: 0;
        z-index: 10;
        border-radius: 4px;
        background: #FFF;
        box-shadow: 0px 4px 15px 0px rgba(0, 0, 0, 0.12);
        max-height: 200px; //更改选项卡最大高度
        overflow: auto;
        width: 100%;

        .popover-single {
            width: 100%;
            height: 32px;
            line-height: 32px;
            cursor: pointer;
            color: #505553;
            font-size: 14px;
            text-align: left;
            padding-left: 10px;
            box-sizing: border-box;

            &.popover-single-selected {
                background: #E8FAF8;
                color: #00b498;
            }

            &:hover {
                background: #F2F5F4;
            }
        }
    }

    .borderA {
        border: 1px solid #E8ECEB;
    }

    .borderB {
        border: 1px solid #00b498;
    }

    .so-box {
        width: 360px;
        height: 34px;
        line-height: 34px;
        font-size: 14px;
        padding: 2px 10px 0px 10px;
        margin: 0;
        border-radius: 6px;
        box-sizing: border-box;
        position: relative;
        cursor: pointer;
        background-color: #fff;
        outline: none;

        &:hover {
            border: 1px solid #00b498;
        }
    }

    .lr-selected-manage-around-icon-select {
        width: 14px;
        height: 14px;
        position: absolute;
        top: 10px;
        right: 12px;
        stroke: #A8ABB2;
        font-size: 14px;
        z-index: 9;
    }

    .arrow-up {
        transform: rotateZ(-90deg); //根据箭头方向,自行调整角度,关闭时状态
    }

    .arrow-down {
        transform: rotateZ(90deg); //根据箭头方向,自行调整角度,选项可出现时状态
    }

}

使用方法:

//在父元素中引入
import SelectOption from '../selecteOption'

function Home() {
  return (
    <div>
      <SelectOption></SelectOption>
    </div>
  );
}

export default Home;

2.作为通用组件使用_和父组件传参

封装为通用组件使用。scss样式文件和上面的一样。

index.tsx

import './index.scss';
// 使用svg,后期更改图标颜色比较方便,还可以做悬浮变色
import { ReactComponent as Up } from '../../assets/images/icon-up.svg';
import { useState, useEffect } from 'react'

function SelectOption(props: any) {
  // 父组件传过来的选项组
  let optionList = props.selectList

  // 选项body的滚动条
  const [scrollWidth, setScrollWidth] = useState(0)

  // 选项弹窗是否显示,为了优化用户体验,控制它的opacity透明度隐藏,为了使选项点击事件能够生效,选项弹窗立即隐藏
  const [showOption, setShowOption] = useState(false)
  // 选项弹窗是否显示,控制它的div是否存在,(弹窗隐藏后元素存在,点击事件生效)需要将弹窗移除
  const [showOptionSetTimeout, setShowOptionSetTimeout] = useState(false)

  // 当前选中的选项,从父元素获得初始值
  const [selectedItem, setSelectedItem] = useState(props.currentOption)

  //挂载后,计算选项弹窗是否有滚动条,从而改变选项弹窗body宽度
  function changeWidth() {
    var myElement: any = document.getElementById('mySelecte');
    var widthWithBorder = myElement.offsetWidth;
    var widthWithoutBorder = myElement.clientWidth;
    setScrollWidth(widthWithBorder - widthWithoutBorder);
  }
  useEffect(() => {
    changeWidth();
  }, []);

  /**
   * input元素获得焦点以及失去焦点的事件
   */
  function showOptionBox() {
    setShowOption(true)
    setShowOptionSetTimeout(true)
  }

  /**
   * 选择框失去焦点事件的处理逻辑  
   */
  function inputBlur() {
    setShowOption(false)
    console.log(showOption, "showOption")
    setTimeout(() => {
      setShowOptionSetTimeout(false)
    }, 300);
  }

  /**
   * 用户选中了某个选项
   * @param id 区分选项的唯一标志
   * @param item 选中的名称
   */
  function selectedOption(id: number, item: string) {
    setSelectedItem({
      id: String(id), item
    });
    // 将用户点击结果返回给父元素
    props.getOption({ id: String(id), item });
  }

  return (
    <div className='so-all-box' >
      <input type="button" onBlur={inputBlur} className={showOption ? 'so-box borderB' : 'so-box borderA'} id="mySelecte" onClick={() => { showOptionBox() }}>
      </input>
      <div className={selectedItem.id === '' ? "lr-selected-manage-around-text textA" : "lr-selected-manage-around-text textB"}>
        {selectedItem.id === '' ? '请选择' : selectedItem.item}
      </div>

      {/* 箭头 */}
      <Up className={showOption === true ? 'lr-selected-manage-around-icon-select arrow-down' : 'lr-selected-manage-around-icon-select arrow-up'}></Up>

      {/* 选择元素的下拉框*/}
      {showOptionSetTimeout ?
        <div className={showOption ? 'popover-box popover-box-show' : 'popover-box popover-box-hidden'} id="mySelecte" style={{ width: `calc(100% + ${scrollWidth}px)` }}>
          {optionList.map((item: any, index: any) => (
            <div key={index} className={selectedItem.item === item ? "popover-single popover-single-selected" : "popover-single"} onClick={() => { selectedOption(index, item) }}>{item}</div>
          ))}
        </div>
        : ''}

    </div>

  );
}

export default SelectOption;

父组件调用:

//在父元素中引入
import SelectOption from '../selecteOption'

function Home() {

  /**
   * 下拉框子元素返回的用户点击的结果
   * @param obj 返回的结果,是个对象,id唯一标志+item选中的name
   */
  function getOptionFromSon(obj: Object) {
    console.log(obj)
    // 将结果传给后端
  }

  return (
    <div>

    <SelectOption selectList={['牛肉馅饼', '北京烤鸭', '蛋糕', '铁锅炖大鹅', '可乐鸡翅']} currentOption={{ id: '1', item: '北京烤鸭' }} getOption={getOptionFromSon}></SelectOption>
  
   </div>
  );
}

export default Home;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1927129.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

807.力扣每日一题7/14 Java(执行用时分布击败100%)

博客主页&#xff1a;音符犹如代码系列专栏&#xff1a;算法练习关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 目录 解题思路 解题过程 时间复杂度 空间复杂度 Code 解题思路 首先…

188家国产大模型:挑战与机遇,未来杀手级AI应用究竟该长什么样子?

未来的杀手级AI应用究竟该长什么样子&#xff1f;这篇文章里&#xff0c;作者梳理了国内外LLMs基础大模型的特征&#xff0c;并于最后发表了自己关于杀手级AI应用的看法和见解&#xff0c;一起来看一下。 摘要&#xff1a; 本文详细列表展示国外18家&#xff0c;国内188家大模…

<数据集>UA-DETRAC车辆识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;20500张 标注数量(xml文件个数)&#xff1a;20500 标注数量(txt文件个数)&#xff1a;20500 标注类别数&#xff1a;4 标注类别名称&#xff1a;[car, van, others, bus] 序号类别名称图片数框数1car201871259342…

深度学习基础:Numpy 数组包

数组基础 在使用导入 Numpy 时&#xff0c;通常给其一个别名 “np”&#xff0c;即 import numpy as np 。 数据类型 整数类型数组与浮点类型数组 为了克服列表的缺点&#xff0c;一个 Numpy 数组只容纳一种数据类型&#xff0c;以节约内存。为方便起见&#xff0c;可将 Nu…

Jira学习

1.Dev OPS DevOps简介 DEV OPS 流程 DEV OPS流程对应工具 最重要的就是持续集成–Jenkins 2.Jira 新建项目

嵌入式系统中的GPIO控制与应用

GPIO是嵌入式系统中最常见且功能最强大的接口之一。它允许硬件工程师通过编程来配置和控制芯片上的数字引脚&#xff0c;实现输入和输出的功能。在本文中&#xff0c;我们将从理论和实践两个方面探讨GPIO的工作原理&#xff0c;并通过一个简单的示例项目来演示如何利用GPIO控制…

whereis命令是 Linux 和类 Unix 系统中的一个命令行工具,用于定位二进制程序、源代码和手册页(man pages)的位置

文章目录 1、whereis2、实例 1、whereis whereis 命令是 Linux 和类 Unix 系统中的一个命令行工具&#xff0c;用于定位二进制程序、源代码和手册页&#xff08;man pages&#xff09;的位置。当你想要快速找到某个程序或命令的安装位置时&#xff0c;whereis 命令会非常有用。…

三相PWM整流器PI双闭环控制Simulink

1.模型简介 本仿真模型基于MATLAB/Simulink&#xff08;版本MATLAB 2017Rb&#xff09;软件。建议采用matlab2017 Rb及以上版本打开。&#xff08;若需要其他版本可联系代为转换&#xff09; 2.拓扑结构&#xff1a; 3.模型算法架构&#xff1a; 4.仿真算法&#xff1a; &am…

[misc]-流量包-wireshark-icmp

wireshark打开&#xff0c;大部分都是icmp,查看data部分 提取data长度&#xff1a; tshark.exe -r 1.pcapng -T fields -e data.len > length.txt 使用python解析这个文件&#xff0c;剔除异常值&#xff0c;每8个取一个值&#xff0c;得到flag ds [] with open(length.tx…

JVM和类加载机制-01[JVM底层]

JVM底层 Java虚拟机内存模型JVM组成部分五大内存区域各自的作用虚拟机栈(线程栈)本地方法栈程序计数器为什么jvm要设计程序计数器&#xff1f; 堆方法区 Java虚拟机内存模型 JVM跨平台原因 就是在JVM层面对各个操作系统的指令做了不同处理 JVM组成部分 五大内存区域各自的作用…

STM32第十九课:FreeRTOS移植和使用

目录 需求一、FreeRtos概要二、移植FreeRtos1.复制源码2.内存空间分配和内核相关接口3.FreeRTOSConfig.h4.在工程中添加.c.h 三、任务块操作1.创建任务2.任务挂起&#xff0c;恢复&#xff0c;删除 四、需求实现代码 需求 1.将FreeRtos&#xff08;嵌入式实时操作系统&#xf…

Visual Studio 2022 + Qt 编写 VTK 程序

Visual Studio 2022 Qt 编写 VTK 程序 Visual Studio 2022 Qt 编写 VTK 程序前期准备创建一个 Qt 程序引入 VTK 库运行项目 Visual Studio 2022 Qt 编写 VTK 程序 前期准备 你需要一个编译好的 VTK 库&#xff1a;使用 Cmake 对 VTK-9.3.0 进行编译 你需要安装 Qt&#x…

<数据集>光伏板缺陷检测数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;2400张 标注数量(xml文件个数)&#xff1a;2400 标注数量(txt文件个数)&#xff1a;2400 标注类别数&#xff1a;4 标注类别名称&#xff1a;[Crack,Grid,Spot] 序号类别名称图片数框数1Crack8688922Grid8248843S…

CTF之easyupload

拿到题目发现是文件上传的漏洞&#xff0c;但是这个黑名单过滤的有点严格&#xff0c;无论是文件里还是文件后缀都不能出现php 那我们就用<?eval($_POST[a]);?>来进行绕过&#xff08;注意这里要加个GIF89a或者GIP87a进行欺骗&#xff09; 但是后缀依然不能绕过怎么办&…

我的六天C++外出学习记

第一天 7月7日 星期日 早晨&#xff0c;我早早起来了&#xff0c;穿好衣服吃完饭就出发了。 我从家到学校用了1H&#xff0c;迟到了&#xff01;我急急忙忙去报到。 我们中午和晚上的饭菜虽说有点贵&#xff0c;但实在太美味了&#xff0c;和我们原本初中的饭菜相比&#…

C语言指针超详解——强化篇

C语言指针系列文章目录 入门篇 强化篇 文章目录 C语言指针系列文章目录1. assert 断言2. 指针的使用和传址调用2. 1 strlen的模拟实现2. 2 传值调用和传址调用 3. 数组名的理解4. 使用指针访问数组5. 一维数组传参的本质6. 冒泡排序7. 二级指针8. 指针数组9. 指针数组模拟实现…

【LeetCode力扣】005.最长回文子串(Python)

最直观的做法&#xff0c;时间&#xff0c;空间复杂度都是O(2^n) class Solution:def longestPalindrome(self, s: str) -> str:dp [[0 for i in range(len(s))] for j in range(len(s))]longestSubStr "" # 存储最长回文子串longestLen 0 # 最长回文子串的长…

第一部分:C++入门

目录 前言 1、C关键字(C98) 2、命名空间 2.1、命名空间定义 2.2、命名空间的使用 3、C输入&输出 4、缺省参数 4.1、缺省参数的概念 4.2、缺省参数的分类 5、函数重载 5.1、函数重载的概念 5.2、C支持函数重载的原理 6、引用 6.1、引用的概念 6.2、引用特性 …

职业PDF标准 Python 下载器-CSDN

目的 下载技能人才评价网 - 职业技能标准查询系统 - PDF 打包下载 使用文件 a.json 代码解析 import base64 import requests import json import os import time# 读取JSON文件 with open(a.json, r, encodingutf-8) as f:data json.load(f) # 从名为 a.json 的文件中读…

数据库作业d8

要求&#xff1a; 一备份 1 mysqldump -u root -p booksDB > booksDB_all_tables.sql 2 mysqldump -u root -p booksDB books > booksDB_books_table.sql 3 mysqldump -u root -p --databases booksDB test > booksDB_and_test_databases.sql 4 mysql -u roo…