Floating UI 使用经验分享 - Dialog

news2024/11/17 4:57:17

上文:Floating UI 使用经验分享 - Popover

在本文中,我将分享如何使用 Floating UI 来创建另一种常见的浮动 UI 组件——Dialog(对话框)。Dialog 是一个浮动元素,显示需要立即关注的信息,他会出现在页面内容上并阻止与页面的交互,直到它被关闭。

它与弹出框有类似的交互,但有两个主要区别:

  • 它是模态的,并在对话框后面呈现一个背景,使后面的内容变暗,使页面的其余部分无法访问。
  • 它在视口中居中,不锚定到任何特定的参考元素。

一个可访问的对话框组件具有以下要点:

  • Dismissal:当用户按下 esc 键或在打开的对话框外按下时,它会关闭。
  • Role:元素被赋予相关的角色和 ARIA 属性,以便屏幕阅读器可以访问。
  • Focus management: 焦点完全被困在对话框中,必须由用户解除。

目标组件

目标:实现一个这样的 Dialog Demo 👇
在这里插入图片描述
接下来我们需要创建一个名为 Dialog 的 React 组件,它使用了 @floating-ui/react 库来创建一个可交互的浮动对话框。以下是对该组件的设想:

组件参数

Dialog 组件需要接受以下参数:

  • rootId:浮动元素的根元素,可选。
  • open:控制对话框是否打开的布尔值。
  • initialOpen:对话框初始是否打开的布尔值,默认为 false
  • onOpenChange:当对话框打开状态改变时的回调函数,接受一个布尔值参数。
  • render:一个函数,接受一个对象参数,该对象包含一个 close 方法,用于关闭对话框。该函数返回要在对话框中渲染的 React 节点。
  • className:应用于对话框的 CSS 类名。
  • overlayClass:应用于浮动覆盖层的 CSS 类名。
  • containerClass:应用于对话框容器的 CSS 类名。
  • isDismiss:一个布尔值,决定是否启用点击外部区域关闭对话框的功能,默认为 true
  • children:React 子元素,可以是一个按钮,点击后打开该弹窗。
  • showCloseButton:一个布尔值,决定是否显示关闭按钮,默认为 true

组件功能

Dialog 组件的主要功能是创建一个可交互的浮动对话框,它可以通过点击关闭按钮或点击对话框外部区域来关闭。对话框的打开和关闭状态可以通过 openonOpenChange 参数进行控制(受控),也可以通过内部状态进行自动管理(非受控)。

Dialog 组件使用了 @floating-ui/react 库的多个 Hook:

  • useFloating:用于管理对话框的打开和关闭状态。
  • useClickuseRoleuseDismiss:用于处理对话框的交互,如点击和角色管理。
  • useInteractions:用于获取和设置交互属性。

此外,Dialog 组件还使用了 FloatingPortalFloatingOverlayFloatingFocusManager 组件来创建浮动对话框的 UI。

完整代码

结合实际可以写出这样一个功能较为完整的 Dialog 案例,可以自定义遮罩层、内部元素的样式,也可以控制点击遮罩层是否关闭弹窗等,还可以结合 Framer-motion 制作弹窗动画等(以后有机会也写一篇)

import {  
  FloatingFocusManager,  
  FloatingOverlay,  
  FloatingPortal,  
  useClick,  
  useDismiss,  
  useFloating,  
  useInteractions,  
  useRole,  
} from '@floating-ui/react';  
import clsx from 'clsx';  
import React, { cloneElement, useState } from 'react';  
import { CgClose } from 'react-icons![](file:///C:\Users\34504\AppData\Roaming\Tencent\QQTempSys\3)6GH))S[9A2G57O0%MM45V.gif)';  
  
type DialogProps = {  
  rootId?: string;  
  open?: boolean;  
  initialOpen?: boolean;  
  onOpenChange?: (open: boolean) => void;  
  children?: JSX.Element;  
  render: (props: { close: () => void }) => React.ReactNode;  
  className?: string;  
  overlayClass?: string;  
  containerClass?: string;  
  isDismiss?: boolean;  
  showCloseButton?: boolean;  
};  
  
export default function Dialog({  
  initialOpen = false,  
  open: controlledOpen,  
  onOpenChange: setControlledOpen,  
  children,  
  className,  
  render,  
  rootId: customRootId,  
  overlayClass,  
  containerClass,  
  showCloseButton = true,  
  isDismiss = true,  
}: DialogProps) {  
  const [uncontrolledOpen, setUncontrolledOpen] = useState(initialOpen);  
  const open = controlledOpen ?? uncontrolledOpen;  
  const setOpen = setControlledOpen ?? setUncontrolledOpen;  
  
  const { reference, floating, context } = useFloating({  
    open,  
    onOpenChange: setOpen,  
  });  
  
  const click = useClick(context);  
  const role = useRole(context);  
  const dismiss = useDismiss(context, { enabled: isDismiss, outsidePressEvent: 'mousedown' });  
  
  const { getReferenceProps, getFloatingProps } = useInteractions([click, role, dismiss]);  
  
  const onClose = () => setOpen(false);  
  
  return (  
    <>  
      {children && cloneElement(children, getReferenceProps({ ref: reference, ...children.props }))}  
      <FloatingPortal id={customRootId}>  
        {open && (  
          <FloatingOverlay  
            className={clsx('absolute inset-0 z-10 flex h-full w-full items-center', overlayClass ?? 'bg-black/60')}  
            lockScroll  
          >  
            <div className={clsx('m-auto grid place-items-center', containerClass)}>  
              <FloatingFocusManager context={context}>  
                <div  
                  className={clsx('relative overflow-hidden rounded-md bg-white', className ?? 'mx-24')}  
                  ref={floating}  
                  {...getFloatingProps()}  
                >  
                  {showCloseButton && <CgClose className="absolute right-2 top-2 h-6 w-6 cursor-pointer" onClick={onClose} />}  
                  {render({ close: onClose })}  
                </div>  
              </FloatingFocusManager>  
            </div>  
          </FloatingOverlay>  
        )}  
      </FloatingPortal>  
    </>  
  );  
}

Basic Dialog Hooks

官方示例 👉 CodeSandbox demo

此示例演示如何创建用于单个实例的对话框以熟悉基础知识。

让我们看一下这个例子:

Open state

import {useState} from 'react';
 
function Dialog() {
  const [isOpen, setIsOpen] = useState(false);
}

isOpen 确定对话框当前是否在屏幕上打开。它用于条件渲染。

useFloating hook

useFloating hook

useFloating() hook为我们的对话提供上下文。我们需要传递一些信息:

  • open :来自我们上面的 useState() 挂钩的打开状态。
  • onOpenChange: 对话框打开或关闭时调用的回调函数。我们将使用它来更新我们的 isOpen 状态。
import {useFloating} from '@floating-ui/react';
 
function Dialog() {
  const [isOpen, setIsOpen] = useState(false);
 
  const {refs, context} = useFloating({
    open: isOpen,
    onOpenChange: setIsOpen,
  });
}

Interaction hooks

Interaction hooks

  • useClick() 添加了在单击引用元素打开或关闭对话框的功能。但是,对话框可能不会附加到引用元素,因此这是可选的。(一般对话框都是独立出来Portal的,也就是上下文是body)
  • useDismiss() 添加了当用户按下 esc 键或在对话框外按下时关闭对话框的功能。可以将其的 outsidePressEvent 选项设置为 'mousedown' 以便触摸事件变得懒惰并且不会穿过背景,因为默认行为是急切的。(不太好理解,大概是)
  • useRole()dialog 的正确 ARIA 属性添加到对话框和引用元素。

最后, useInteractions() 将他们所有的 props 合并到 prop getters 中,然后就可以用于渲染。

import {
  // ...
  useClick,
  useDismiss,
  useRole,
  useInteractions,
  useId,
} from '@floating-ui/react';
 
function Dialog() {
  const [isOpen, setIsOpen] = useState(false);
 
  const {refs, context} = useFloating({
    open: isOpen,
    onOpenChange: setIsOpen,
  });
 
  const click = useClick(context);
  const dismiss = useDismiss(context, {
    outsidePressEvent: 'mousedown',
  });
  const role = useRole(context);
 
  // Merge all the interactions into prop getters
  const {getReferenceProps, getFloatingProps} = useInteractions([
    click,
    dismiss,
    role,
  ]);
 
  // Set up label and description ids
  const labelId = useId();
  const descriptionId = useId();
}

Rendering

Rendering

现在我们已经设置了所有的变量和hook,可以渲染我们的元素了。

function Dialog() {
  // ...
  return (
    <>
      <button ref={refs.setReference} {...getReferenceProps()}>
        Reference element
      </button>
      {isOpen && (
        <FloatingOverlay
          lockScroll
          style={{background: 'rgba(0, 0, 0, 0.8)'}}
        >
          <FloatingFocusManager context={context}>
            <div
              ref={refs.setFloating}
              aria-labelledby={labelId}
              aria-describedby={descriptionId}
              {...getFloatingProps()}
            >
              <h2 id={labelId}>Heading element</h2>
              <p id={descriptionId}>Description element</p>
              <button onClick={() => setIsOpen(false)}>
                Close
              </button>
            </div>
          </FloatingFocusManager>
        </FloatingOverlay>
      )}
    </>
  );
}
  • {...getReferenceProps()} / {...getFloatingProps()} 上一篇说过,将 props 从交互挂钩传播到相关元素上。它们包含诸如 onClickaria-expanded 等道具。

FloatingPortal & FloatingOverlay & FloatingFocusManager

  • <FloatingOverlay /> 是一个在浮动元素后面渲染背景覆盖元素的组件,具有锁定主体滚动的能力。 FloatingOverlay docs
    • 提供了一个固定的基本样式,使背景内容变暗并阻止浮动元素后面的指针事件。
    • 它是一个常规的 <div/>,因此可以通过任何CSS解决方案进行样式设置。
  • <FloatingFocusManager />
    • FloatingPortal docs 一个管理和控制页面中浮动元素焦点的组件
    • 自动检测焦点变化,调整页面上的浮动元素的位置和状态,确保页面上所有元素的可访问性和可用性。
    • 默认情况通常将焦点捕获在内部
    • 它应该直接包裹浮动元素,并且只在对话框也被渲染时才被渲染。 FloatingFocusManager docs
      • <FloatingPortal />将浮动元素传送到给定的容器元素中——默认情况下,在应用程序根之外并进入 body。
      • 可以自定义 root,也就是可选择具有 id 的节点,或者创建它并将其附加到指定的根(body)
import {FloatingPortal} from '@floating-ui/react';
 
function Tooltip() {
  return (
    isOpen && (
      <FloatingPortal>
        <div>Floating element</div>
      </FloatingPortal>
    )
  );
}

本文Hexo博客链接🔗 https://ysx.cosine.ren/floating-ui-experience-dialog
xLog链接🔗 https://x.cosine.ren/floating-ui-experience-dialog
RSS订阅 📢 https://x.cosine.ren/feed/xml

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

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

相关文章

5G NR基于码本的上行传输

上行传输受基站DCI调度&#xff0c;UE收到DCI信息后&#xff0c;根据PMI信息选择相应的码本。 在3GPP TS 38.211 6.3.1.5节中&#xff0c;定义了不同天线端口数和不同传输层数情况下的可选码本。下面截取了单层2天线端口码本和双层两天线端口码本。 gNB在什么情况下为UE选择什…

Python--序列

Python--序列 <font colorblue>一、定义<font colorblue>二、索引<font colorblue>1.从左往右的索引&#xff1a;索引值从0开始递增<font colorblue>2.从右往左的索引&#xff1a;从-1开始递减 <font colorblue>三、切片<font colorblue>四…

Unity3d_Cut\Clipping sphere\CSG(boolean)(裁剪模型重合部分)总结

1、https://liu-if-else.github.io/stencil-buffers-uses-in-unity3d/ 下载&#xff1a;https://github.com/liu-if-else/UnityStencilBufferUses 2、手动切割 Unity 模型切割工具,CSG,任意图案,任意切割_unity csg_唐沢的博客-CSDN博客 3、 Shader Unity Shader学习&#x…

【从删库到跑路】详细讲解MySQL的函数和约束作用

&#x1f38a;专栏【MySQL】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【如愿】 大一同学小吉&#xff0c;欢迎并且感谢大家指出我的问题&#x1f970; 文章目录 &#x1f354;函数⭐字符串函数&#x1f388;字符串拼接函数&…

Python基础知识 数据容器

id() 函数是python 内置函数 返回 id() 函数返回对象的唯一标识符&#xff0c;标识符是一个整数。 a, b, c 20, 30 , 40 print(a,b,c) ## a20 b 30 c40 ## 跟ES6系列中的析构函数原理一样Python中 字符串不能通过 &#xff0c;把 数字等非字符串&#xff0c;进行拼接…

Redis从入门到精通【高阶篇】之底层数据结构链表包(listpacks)详解

文章目录 0.前言2. listpacks&#xff08;紧凑列表&#xff09;2. 源码解析3. 总结 0.前言 上个篇章回顾&#xff0c;我们上个章节我们学习了《Redis从入门到精通【高阶篇】之底层数据结构字典(Dictionary)详解》 本文将Redis底层数据结构 listpacks&#xff08;链表包&#…

SynchronousQueue的基本介绍

SynchronousQueue介绍 SynchronousQueue作为阻塞队列&#xff0c;区别于其他的阻塞队列。因为他不存储元素&#xff0c;但是存储消费者或者生产者。要是SynchronousQueue队列中存储了一个生产者&#xff0c;再来一个生产者想存放到队列中&#xff0c;要是你使用的是put方法&…

chatgpt赋能python:Python自动编号教程:如何给数据添加自动编号

Python自动编号教程&#xff1a;如何给数据添加自动编号 在进行数据处理和整理过程中&#xff0c;有时候需要为数据添加自动编号才能更好地进行分析和展示。而使用Python编程语言可以快速而准确地实现自动编号的功能。在本篇教程中&#xff0c;我们将介绍如何使用Python处理数…

网络层:IPv4数据报的首部格式

1.IPv4数据报的首部格式 笔记来源&#xff1a; 湖科大教书匠&#xff1a;IPv4数据报的首部格式 声明&#xff1a;该学习笔记来自湖科大教书匠&#xff0c;笔记仅做学习参考 下图来源&#xff1a;以太网MAC帧格式 IP数据报属于MAC帧的数据部分 IPv4数据报的首部格式 1.1 IP数…

VS2019编译GSL

VS2019 编译GSL 下载GSL&#xff1a;https://github.com/BrianGladman/gsl&#xff0c;此仓库带有用于编译的VS解决方案。 解压后&#xff0c;在 build.vc 目录下有两个解决方案&#xff1a; gsl.dll.sln 用于编译生成动态库gsl.lib.sln 用于编译生成静态库 请先阅读 build…

haproxy服务器对nginx服务器web服务调度负载均衡、用nfs做共享目录(脚本部署)

目录 一、准备 二、在作为haproxy的服务器上导入以下shell执行haproxy安装 三、由于nginx服务需要用的nfs共享目录&#xff0c;先部署nfs 四、nginx服务器1部署 五、nginx服务器2部署同上 六、测试 一、准备 四台服务器 系统IP搭建服务器centos7192.168.1.12haproxycent…

深度学习经典trick汇总

深度学习经典trick汇总 trick这个词或许有投机取巧的意味&#xff0c;但深度学习论文中出现的很多这个trick确实对模型更方面性能有所提高&#xff0c;而且它们中的很多还具有普适性&#xff0c;那么这种“trick“或许应该被叫做“技术”。 1. 权重衰减 θ t 1 ( 1 − ω α…

DHCP服务器

文章目录 DHCP服务器DHCP的工作原理DHCP服务器的用途DHCP协议的工作方式DHCP服务器给予客户端固定或动态的IP参数关于租约所造成的问题与租约期限多台DHCP服务器在同一物理网段的情况 何时需要架设DHCP服务器使用DHCP的时机不建议使用DHCP主机的时机 DHCP服务器端的配置所需软件…

Quantum Utility!IBM开辟“量子计算的实用时代”

光子盒研究院 今天&#xff0c;IBM&#xff08;纽约证券交易所股票代码&#xff1a;IBM&#xff09;宣布了一项新的突破&#xff0c;并发表在科学杂志《自然》的封面上。 ——团队首次证明了量子计算机可以在100多个量子比特的规模上产生精确的结果&#xff1b;并且至少在一种计…

Redis入门 - 基础通用指令

原文首更地址&#xff0c;阅读效果更佳&#xff01; Redis入门 - 基础通用指令 | CoderMast编程桅杆https://www.codermast.com/database/redis/base-commind.html 在正式介绍Redis数据结构及其操作指令之前&#xff0c;我们需要先掌握一些最基础的通用指令。 这些都是Redis…

鸟类识别Python,基于TensorFlow卷积神经网络【实战项目】

一、介绍 鸟类识别系统&#xff0c;使用Python作为主要开发语言&#xff0c;基于深度学习TensorFlow框架&#xff0c;搭建卷积神经网络算法。并通过对数据集进行训练&#xff0c;最后得到一个识别精度较高的模型。并基于Django框架&#xff0c;开发网页端操作平台&#xff0c;…

chatgpt赋能python:Python自动获取图片数据的方法

Python自动获取图片数据的方法 随着信息时代的到来&#xff0c;图像数据已经越来越重要。我们如何从互联网上获取大量的图片数据呢&#xff1f;Python提供了简单而直接的方法。本文将介绍如何使用Python自动获取图片数据&#xff0c;充分利用Python的编程能力&#xff0c;开展…

异常的相关知识

&#x1f4e2;博客主页&#xff1a;盾山狂热粉的博客_CSDN博客-C、C语言,机器视觉领域博主&#x1f4e2;努力努力再努力嗷~~~✨ &#x1f4a1;大纲 ⭕总结了python中所有可能的异常情况&#xff0c;有异常不一定是坏事&#xff0c;有提醒作用 一、常见异常 &#x1f4a1;可以…

Midjourney命令列表Command List介绍

您可以通过键入命令与Discord上的Midjourney Bot进行交互。命令可以用来生成图像、更改默认设置、监看用户信息以及执行其他有用的任务。 Midjourney 命令可以在任何Bot Channel中使用&#xff0c;在允许 Midjourney Bot 运行的私有 Discord 服务器上使用&#xff0c;或者在与…

Java项目开发基本数据类型与封装数据类型的选择

问题 Java项目开发基本数据类型与封装数据类型的选择 详细问题 关于基本数据类型与封装数据类型的区别&#xff0c;作为面试经典题目已被熟知&#xff0c;但是&#xff0c;项目开发时&#xff0c;对于一个变量&#xff0c;是选择基本数据类型&#xff0c;还是封装数据类型&a…