React+TS前台项目实战(七)-- 全局常用组件Select封装

news2025/1/8 14:30:33

文章目录

  • 前言
  • Select组件
    • 1. 功能分析
    • 2. 代码+详细注释说明
    • 3. 使用方式
    • 4. 效果展示
    • (1)鼠标移入效果
    • (2)下拉框打开效果
    • (3)回调输出
  • 总结


前言

今天这篇主要讲全局select组件封装,可根据UI设计师要求自定义修改。


Select组件

1. 功能分析

(1)鼠标移入,选中时,选择框样式处理
(2)定义Option和props的类型,用于表示组件的相关属性
(3)添加onChange属性,用于定义选中选项时的回调函数
(4)添加defaultValue属性,用于定义默认选中的选项
(5)添加placeholder属性,用于定义选项为空时的占位符文本
(6)添加className属性,用于定义组件的自定义类名
(7)使用react-outside-click-handler插件,实现点击外部区域时收起下拉框的功能

2. 代码+详细注释说明

// @/components/Select/index.tsx
import { useState, FC } from "react";
import OutsideClickHandler from "react-outside-click-handler";
import classNames from "classnames";
import styles from "./index.module.scss";
import Arrow from "@/assets/arrowDown.png";
// 定义Option的类型,用于表示选项的属性
type Option = {
  label: string; // 选项的显示文本
  value: string; // 选项的值
};

// 定义Props的类型,用于表示组件的属性
type Props = {
  options: Option[]; // 选项数组
  onChange: (value: string) => void; // 选中选项时的回调函数
  defaultValue?: string; // 默认选中的选项值
  placeholder?: string; // 选项为空时的占位符文本
  className?: string; // 组件的自定义类名
};

// 定义Select的组件,用于实现下拉选择框的功能
const Select: FC<Props> = (props) => {
  // 解构组件的属性
  const { options, onChange, defaultValue, placeholder, className } = props;
  // 获取默认选中的选项的显示文本
  const defaultLabel = options.find((option) => option.value === defaultValue)?.label;
  // 定义状态变量
  const [isExpanded, setIsExpanded] = useState(false); // 是否展开下拉框
  const [value, setValue] = useState(defaultLabel); // 当前选中的选项的显示文本
  const [currentIndex, setCurrentIndex] = useState(1); // 当前选中的选项的索引
  // 切换下拉框的展开状态
  const toggleExpand = () => {
    setIsExpanded(!isExpanded);
  };
  // option选项点击事件
  const handlerOptionClick = (option: Option, index: number) => {
    setValue(option.label); // 更新当前选中的选项的显示文本
    onChange(option.value); // 调用回调函数,通知父组件选中的选项值
    setCurrentIndex(index); // 更新当前选中的选项的索引
    toggleExpand(); // 切换下拉框的展开状态
  };

  return (
    // 使用OutsideClickHandler组件包裹根元素,用于处理点击外部区域的事件
    <OutsideClickHandler onOutsideClick={() => setIsExpanded(false)}>
      <div className={classNames(styles.outsideContainer, className)}>
        <div className={classNames(styles.selectContainer, isExpanded && styles.isFocused)} onClick={toggleExpand}>
          <div className={classNames(styles.selectValue)}>
            <div className={classNames(styles.selectText)}>{value ?? placeholder}</div>
          </div>
          <img src={Arrow} alt="" data-is-flipped={isExpanded} />
        </div>
        {isExpanded && (
          // 如果下拉框展开,则渲染选项列表
          <ul className={classNames(styles.selectOption)}>
            {options.map((option, index) => (
              <li className={classNames(styles.selectOptionItem, index === currentIndex && styles.selected)} key={option.value} onClick={() => handlerOptionClick(option, index)}>
                <span className={classNames(styles.optionValue)}>{option.label}</span>
              </li>
            ))}
          </ul>
        )}
      </div>
    </OutsideClickHandler>
  );
};

export default Select;
------------------------------------------------------------------------------
// @/components/Button/index.module.scss
.outsideContainer {
  position: relative;
}

.selectContainer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 16px;
  min-height: 40px;
  border-radius: var(--cd-border-radius-base);
  background-color: var(--cd-fill-color-blank);
  transition: var(--cd-transition-duration);
  box-shadow: 0 0 0 1px var(--cd-border-color);
  cursor: pointer;
  user-select: none;
  position: relative;
  &.isFocused {
    box-shadow: 0 0 0 1px var(--cd-shadow-color) inset;
  }
  &:hover:not(.isFocused) {
    box-shadow: 0 0 0 1px var(--cd-border-color-hover) inset;
  }
  .selectValue {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    flex: 1;
    min-width: 0;
    position: relative;
    .selectText {
      position: absolute;
      top: 50%;
      transform: translateY(-50%);
      width: 100%;
      text-align: left;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
  }
  img {
    width: 10px;
    transform: rotate(0);
    transition: var(--cd-transition-duration);
    &[data-is-flipped="true"] {
      transform: rotateX(180deg);
    }
  }
}
.selectOption {
  min-width: fill-available;
  padding: 6px 0;
  margin-top: 5px;
  border-radius: 4px;
  list-style: none;
  background-color: #ffffff;
  border: 1px solid #e4e7ed;
  box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12);
  position: absolute;
  .selectOptionItem {
    font-size: 14px;
    padding: 0 20px;
    position: relative;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    color: #606266;
    height: 34px;
    line-height: 34px;
    box-sizing: border-box;
    cursor: pointer;
    &:hover {
      background-color: #f5f7fa;
    }
    &.selected {
      color: var(--cd-shadow-color);
    }
  }
}

3. 使用方式

import { useState } from "react";
// 引入组件
import Select from "@/components/Select";
// 使用方式
const [options] = useState([
  {
    label: "标签1",
    value: "123",
  },
  {
    label: "标签2",
    value: "456",
  },
]);
const defaltValue = options[1].value
<Select options={options} onChange={onChange} defaultValue={defaltValue} placeholder="请选择"></Select>

// 选择变化
const onChange = (value: string) => {
  console.log("onChange", value);
};

4. 效果展示

(1)鼠标移入效果

在这里插入图片描述

(2)下拉框打开效果

在这里插入图片描述

(3)回调输出

在这里插入图片描述


总结

下一篇讲【全局模态框Modal组件、公共弹窗Dialog组件封装】。关注本栏目,将实时更新。

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

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

相关文章

java1.8运行arthas-boot.jar运行报错解决

报错内容 输入java -jar arthas-boot.jar&#xff0c;后报错。 [INFO] JAVA_HOME: D:\developing\jdk\jre1.8 [INFO] arthas-boot version: 3.7.2 [INFO] Can not find java process. Try to run jps command lists the instrumented Java HotSpot VMs on the target system.…

资源付费系统小程序APP公众号h5源码

&#x1f510; 揭秘“资源付费系统”&#xff1a;知识、技能与价值的交汇点 &#x1f48e; &#x1f31f; 引言&#xff1a;为何资源需要付费&#xff1f; 在数字化时代&#xff0c;我们周围充斥着大量的信息。但并非所有信息都具有同等的价值。其中&#xff0c;那些经过精心…

项目五串行通信系统 任务5-3温度信息上传

任务描述&#xff1a;DS18B20测量温度&#xff0c;单片机采集温度数据转换显示代码&#xff0c;并通过串行口发送到上位机显示。 底层文件&#xff1a; /********************************************* ds18b20底层函数:能完成一次温度数据读取 ***************************…

算法课程笔记——线段树动态开点

算法课程笔记——线段树动态开点 、

大模型高考数学测评结果,国内AI大模型成绩超GPT-4o!

每年高考都是备受全社会关注的一件大事&#xff0c;而今年略有不同&#xff0c;因为除了鱼跃龙门的高三学子们&#xff0c;还多了许多陪他们一同参加考试的 AI 大模型。 在所有的考试科目中&#xff0c;数学显然一直都是最难的那一门&#xff0c;不论是对考生还是对大模型。因…

设备管理系统——设备台账管理

设备管理系统中的设备台账管理是一项关键的功能&#xff0c;它涵盖了设备从购入、使用、维护到报废的全生命周期管理。以下是设备台账管理在设备管理系统中的详细功能和重要性&#xff1a; 一、设备台账管理的基本功能 设备信息录入&#xff1a; 录入设备的基本信息&#xff0…

B端系统的颜值问题:成也框架,败也框架!

B端UI框架和前端框架的出现&#xff0c;让系统的搭建就像堆积木一样&#xff0c;十分的容易了。这也一下子把程序员的设计和审美水平拔高到了UI框架能够达到的高度。伴随而来的则是系统的堆砌、同质化、糟糕的体验&#xff0c;以及各种违和的组件被生搬硬套的绑定在一块&#x…

传统工厂该如何做数字化转型?

传统工厂实现数字化转型需多方面着手&#xff0c;包括树立战略意识、明确目标规划&#xff0c;加强信息化建设、提升数据能力&#xff0c;培养引进人才、推动技术创新&#xff0c;优化业务流程、提高生产效率与质量管控&#xff0c;加强协同合作、实现产业链整合&#xff0c;建…

男士内裤买便宜还是贵的?2024年高性价比男士内裤汇总分享

男生内裤&#xff0c;作为贴身衣物&#xff0c;承载着男性的私密与舒适。然而&#xff0c;许多男士的内裤状况却让人大跌眼镜&#xff1a;穿到变形、腰部松垮无弹性&#xff0c;屁股后面甚至出现破洞&#xff0c;这样的景象已然屡见不鲜。更有些男士的内裤&#xff0c;中间一个…

lnmp的介绍与源码部署以及 |什么是正向、反向、透明代理 | 常见的集群有哪些

lnmp 文章目录 lnmp1.LNMP是什么2. lnmp简介3.系统特点4.优点5.lnmp部署5.1 nginx安装5.2 mysql安装5.3 php安装5.4配置nginx服务处理php 6.扩展知识点1.什么是集群2.常见的集群有哪些集群的分类1、高可用集群2、负载均衡集群3、分布式计算集群4、高性能集群(High Performance …

你为什么不相信 LLM 模型评测:深入评测 LLM 接口

我相信你已经看过很多机构发布的 LLM&#xff08;大语言模型&#xff09; 的模型效果质量的评测文章了。 其实呢&#xff0c;大家看了很多自称权威&#xff0c;或者不怎么权威的评测文章&#xff0c;基本上也就看看就完了&#xff0c;很少有人真的相信这些测试结果。 为什么你…

基于Simufact Welding定向沉积增材仿真的几何变形补偿

在DED&#xff08;Directed energy deposition定向能量沉积&#xff0c;下述简称DED&#xff09;增材工艺过程中&#xff0c;由于零部件的重复加热&#xff0c;极易产生部件的变形问题。借助专业的金属定向能量沉积仿真软件Simufact Welding&#xff0c;能够对目标件进行瞬态数…

线稳源极跟随 线性电源前端降压

功率MOSFET线性电源涉及跟随.ms14 根本原理是Vgs对Id的控制&#xff0c;Vgs越大&#xff0c;Id越大&#xff0c;反之亦然。 观察转移特性曲线&#xff0c;结合接线图可知&#xff0c;电路稳定后&#xff0c;如果负载电阻增大&#xff0c;则Vsgnd增大&#xff0c;由于Vggnd有稳…

负压式水帘风机和一体式水帘风机的特点

负压式水帘风机和一体式水帘风机各有其独特的特点&#xff0c;以下是它们的特点归纳&#xff1a; 负压式水帘风机特点&#xff1a; 高效节能&#xff1a; 通过水帘与负压风机的配合&#xff0c;模拟自然界的蒸发降温过程&#xff0c;耗电量仅为传统空调的十分之一&#xff0c…

Liinux:进程程序替换

替换原理 用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用e…

Java常用的设计模式,如单例模式、工厂模式、观察者模式等

设计模式是软件工程中的一种解决方案&#xff0c;用于应对常见的设计问题和挑战。它们提供了一种标准化的方式来解决设计难题&#xff0c;使代码更加灵活、可扩展和易于维护。 单例模式&#xff08;Singleton Pattern&#xff09; 概述 单例模式确保一个类只有一个实例&…

Lua解释器裁剪

本文目录 1、引言2、文件功能3、选择需要初始化的库4、结论 文章对应视频教程&#xff1a; 已更新。见下方 点击图片或链接访问我的B站主页~~~ Lua解释器裁剪&#xff0c;很简单~ 1、引言 在嵌入式中使用lua解释器&#xff0c;很多时候会面临资源紧张的情况。 同时&#xff0c…

木头姐预测:2029年特斯拉股价将达2600美元,市值8.2万亿美元

ARK预计特斯拉将在未来两年内推出robotaxi服务&#xff0c;并估计到2029年特斯拉近90%的市值和盈利将归功于robotaxi业务。此外研究表明&#xff0c;FSD模式下的特斯拉比人类驾驶的特斯拉安全约5倍&#xff0c;比道路上的普通汽车安全约16倍。 北京时间12日晚&#xff0c;木头姐…

如何识别和管理软件测试风险?

TestComplete 是一款自动化UI测试工具&#xff0c;这款工具目前在全球范围内被广泛应用于进行桌面、移动和Web应用的自动化测试。 TestComplete 集成了一种精心设计的自动化引擎&#xff0c;可以自动记录和回放用户的操作&#xff0c;方便用户进行UI&#xff08;用户界面&…

通用大模型和垂直大模型,如何选择?

通用大模型和垂直大模型在多个方面存在显著的区别。就像生活中的全才能手和行业里的高精专家&#xff0c;各有千秋&#xff0c;各有魅力。从专业角度分析&#xff0c;他们有以下这些区别&#xff1a; 1.定义与特点&#xff1a; 通用大模型&#xff1a; 是一…