首页 > 其他分享 >下拉筛选隐藏式遮罩

下拉筛选隐藏式遮罩

时间:2024-10-28 15:49:15浏览次数:3  
标签:遮罩 const isOpen title item globalState 隐藏式 筛选

0. 缘起

本项目为taro+react的微信小程序,下拉筛选组件有许多改动。记录下有一个需求,当按钮或者输入框在筛选遮罩上方,希望点击按钮或者输入框遮罩自动关闭。

1. 思路

如果是web,直接document.addEventListener监听当前页面点击事件,如果在筛选器外,自动关闭筛选器。
但本项目是微信小程序,无法监听页面点击事件,且tarocreateSelectorQuery只能获取到当前项长宽高定位信息。
看社区发现这里有个巧妙的思路,即上方增加透明遮罩,点击遮罩部分关闭。

createSelectorQuery

const q = Taro.createSelectorQuery();
q.select(`#${uniqueId}`)
  .boundingClientRect((res: NodesRef.BoundingClientRectCallbackResult) => {
    $title.current = res;
  })
  .exec();

注意细节点

如果有多个下拉筛选,透明遮罩层不会覆盖在上面,靠closeOverlay解决。这里用到了globalState用于存储全局变量
如果不是筛选,则靠透明遮罩。

2. 组件

代码如下

import React, {
  useState,
  useEffect,
  useRef,
  useImperativeHandle,
  forwardRef,
} from 'react';
import { RootPortal, View } from '@tarojs/components';
import Taro, { NodesRef, useDidShow } from '@tarojs/taro';
import { globalState } from '@/global';
import styles from './index.module.less';

interface DropdownMenuProps {
  title: React.ReactNode | string;
  children: React.ReactNode;
  // 是否在点击遮罩层后关闭
  closeOnClickOverlay?: boolean;
  onClose: () => void;
  onOpen?: () => void;
}

export type DropdownInstance = {
  isOpen: boolean;
  setIsOpen: (isOpen: boolean) => void;
};

const DropdownMenu: React.ForwardRefRenderFunction<
  DropdownInstance,
  DropdownMenuProps
> = ({ title, closeOnClickOverlay = true, children, onClose, onOpen }, ref) => {
  const [isOpen, setIsOpen] = useState(false);
  const [screenWidth, setScreenWidth] = useState(0);

  const $title = useRef<NodesRef.BoundingClientRectCallbackResult>();
  const indexRef = useRef<any>(`${+new Date()}${Math.ceil(Math.random() * 10000)}`);

  useEffect(() => {
    const systemInfo = Taro.getSystemInfoSync();
    setScreenWidth(systemInfo.screenWidth);
  }, []);

  const uniqueId = `DROP_ROOT_TITLE_${indexRef.current}`;

  useDidShow(() => {
    Taro.nextTick(() => {
      const q = Taro.createSelectorQuery();
      q.select(`#${uniqueId}`)
        .boundingClientRect((res: NodesRef.BoundingClientRectCallbackResult) => {
          $title.current = res;
        })
        .exec();
    });
  });

  useImperativeHandle(ref, () => {
    return {
      isOpen,
      setIsOpen,
    };
  });

  const closeOverlay = () => {
    if (isOpen) {
      onClose();
      globalState.dropdownBus = [];
    } else if (ref) {
      // When open the dropdown, remember it.
      globalState.dropdownBus.push({ id: uniqueId, open: true, ref: ref });
      const isExistDuplicateOpen =
        globalState.dropdownBus.filter(item => item.open)?.length > 1;

      // When the dropdown opened, the previous dropdown closed.
      if (isExistDuplicateOpen) {
        const previousOpen = globalState.dropdownBus.find(
          item => item.open && !uniqueId.includes(item.id)
        );
        if (previousOpen) {
          previousOpen.ref?.current?.setIsOpen(false);
          globalState.dropdownBus = globalState.dropdownBus.map(item => {
            if (item.open && !uniqueId.includes(item.id)) {
              return { ...item, open: false };
            }
            return item;
          });
        }
      }
    }
  };

  const handleToggle = () => {
    closeOverlay();
    setIsOpen(!isOpen);
  };

  const getNowTop = () => {
    return ($title.current?.top || 0) + ($title.current?.height || 0);
  };

  const getNowBottom = () => {
    const height = getNowTop();
    const bottom = `calc(100vh - ${height}px)`;
    return bottom;
  };

  const closeLayer = () => {
    if (closeOnClickOverlay) {
      closeOverlay();
      setIsOpen(false);
    }
  };

  return (
    <View>
      <View className={styles['drop-wrapper']}>
        <View id={uniqueId} onClick={handleToggle}>
          {title}
        </View>
        {isOpen && (
          <RootPortal enable>
            <View
              catchMove
              className={styles['drop-box']}
              style={{
                width: `${screenWidth}px`,
                top: getNowTop(),
              }}
              onClick={closeLayer}>
              <View
                onClick={e => {
                  e.preventDefault();
                  e.stopPropagation();
                }}
                style={{ background: '#fff' }}>
                {children}
              </View>
            </View>
          </RootPortal>
        )}
      </View>
      {isOpen && (
        <View
          onClick={closeLayer}
          className={styles['hide-layer']}
          style={{ bottom: getNowBottom() }}></View>
      )}
    </View>
  );
};

export default forwardRef(DropdownMenu);

标签:遮罩,const,isOpen,title,item,globalState,隐藏式,筛选
From: https://www.cnblogs.com/lepanyou/p/18510820

相关文章

  • 基于hive的歌曲筛选推荐系统
    开发语言:Java框架:springbootJDK版本:JDK1.8服务器:tomcat7数据库:mysql5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包:Maven3.3.9浏览器:谷歌浏览器后台路径地址:localhost:8080/项目名称/admin/dist/index.html前台路径地址:localhost:80......
  • C06.L01.筛选法求质数.质数个数(筛选法优化)
    题目描述输入n,输出1~n以内的质数的个数。(n<=1000)输入格式一个整数n。输出格式一个整数,代表1~n以内的质数的个数。样例输入数据110Copy输出数据14代码:#include<bits/stdc++.h>usingnamespacestd;boolflag[1001];intmain(){     ......
  • 原生js实现复选框筛选功能,jquery实现复选框筛选功能
    <div><label><inputtype="checkbox"id="not-effective"onclick="filterItems()">未生效</label><label><inputtype="checkbox"......
  • 并发请求太多,服务器崩溃了?试试使用 ASP.NET Core Web API 操作筛选器对请求进行限流
    前言请求限流(RateLimiting)主要是一种用于控制客户端对服务器的请求频率的机制。其目的是限制客户端在一定时间内可以发送的请求数量,保护服务器免受过多请求的影响,确保系统的稳定性和可靠性。请求限流通常会基于以下几个因素来进行限制:时间窗口:规定了在多长时间内允许的请求......
  • kibana筛选数据
    q:kibana如何编写语句,过滤es日志晚上9点到第二个早上8点的数据,并且筛选出用户名,是已经去重的用户名 a:在Kibana中使用Elasticsearch查询语言(如Painless脚本或Kibana的Kuery查询语言)来过滤特定时间段的数据并去重用户名,可以通过以下步骤实现。假设你的索引模式为`m......
  • 操作筛选器的 1 个应用实例:自动启用事务
    前言在数据库操作过程中,有一个概念是绕不开的,那就是事务。事务能够确保一系列数据库操作要么全部成功提交,要么全部失败回滚,保证数据的一致性和完整性。在Asp.NetCoreWebAPI中,我们可以使用操作筛选器给所有的数据库操作API加上事务控制,省心又省力,效果还很好。看看Step......
  • java 按行读取文件,并筛选包含指定字符行数据
    `importjava.io.BufferedReader;importjava.io.FileNotFoundException;importjava.io.FileReader;importjava.util.Collections;importjava.util.List;importjava.util.stream.Collectors;publicclassTestController{publicstaticvoidmain(String[]......
  • Django路由和视图(筛选)
    温习温故知新1.路由分发方式一:includefromdjango.urlsimportpath,includeurlpatterns=[path('app/',include('app.urls'))]方式二:手工分发urlpatterns=[#path('user/login/',login,name='login'),#path('......
  • IIS 配置referer 请求筛选_请求拒绝
    一、IIS配置Referer拒绝   解析:访问静态内容,拒绝指定的referer,例如:拒绝后,对应的网站引用当前网站的静态资源会被拒绝。更多:iis怎么限制http下载速度_IIS限制网站带宽使用?IIS执行此操作时出错。详细信息:web.config错误,.netcore项目 IIS10隐藏httpserve......
  • 妙用编辑器:如何使用编辑器的筛选功能更高效的阅读日志
    妙用编辑器:如何使用编辑器的筛选功能更高效的阅读日志应用场景在日常工作中,经常需要查看日志以便定位问题,有时通篇的阅读日志文件往往非常消耗时间,往往找到一个关键字,希望一次性把该关键字在日志中所有出现的地方都查阅一遍,使用搜索功能当然可行,但是还是有些麻烦,需要前后跳转,打......