github编辑

Floating UI 使用经验分享 - Dialog

上文:Floating UI 使用经验分享 - Popoverarrow-up-right

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

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

  • 它是模态的,并在对话框后面呈现一个背景,使后面的内容变暗,使页面的其余部分无法访问。

  • 它在视口中居中,不锚定到任何特定的参考元素。

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

  • Dismissal:当用户按下 esc 键或在打开的对话框外按下时,它会关闭。

  • Role:元素被赋予相关的角色和 ARIA 属性,以便屏幕阅读器可以访问。

  • Focus management: 焦点完全被困在对话框中,必须由用户解除。

目标组件

目标:实现一个这样的 Dialog Demo 👇

Pasted image 20230616145507

接下来我们需要创建一个名为 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 制作弹窗动画等(以后有机会也写一篇)

Basic Dialog Hooks

官方示例 👉 CodeSandbox demoarrow-up-right

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

让我们看一下这个例子:

Open state

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

useFloating hook

useFloating hookarrow-up-right

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

  • open :来自我们上面的 useState() 挂钩的打开状态。

  • onOpenChange: 对话框打开或关闭时调用的回调函数。我们将使用它来更新我们的 isOpen 状态。

Interaction hooks

Interaction hooksarrow-up-right

  • useClick() 添加了在单击引用元素打开或关闭对话框的功能。但是,对话框可能不会附加到引用元素,因此这是可选的。(一般对话框都是独立出来Portal的,也就是上下文是body)

  • useDismiss() 添加了当用户按下 esc 键或在对话框外按下时关闭对话框的功能。可以将其的 outsidePressEvent 选项设置为 'mousedown' 以便触摸事件变得懒惰并且不会穿过背景,因为默认行为是急切的。(不太好理解,大概是)

  • useRole()dialog 的正确 ARIA 属性添加到对话框和引用元素。

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

Rendering

Renderingarrow-up-right

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

  • {...getReferenceProps()} / {...getFloatingProps()} 上一篇说过,将 props 从交互挂钩传播到相关元素上。它们包含诸如 onClickaria-expanded 等道具。

FloatingPortal & FloatingOverlay & FloatingFocusManager

  • <FloatingOverlay /> 是一个在浮动元素后面渲染背景覆盖元素的组件,具有锁定主体滚动的能力。 FloatingOverlay docsarrow-up-right

    • 提供了一个固定的基本样式,使背景内容变暗并阻止浮动元素后面的指针事件。

    • 它是一个常规的 <div/>,因此可以通过任何CSS解决方案进行样式设置。

  • <FloatingFocusManager />

    • FloatingPortal docsarrow-up-right 一个管理和控制页面中浮动元素焦点的组件

    • 自动检测焦点变化,调整页面上的浮动元素的位置和状态,确保页面上所有元素的可访问性和可用性。

    • 默认情况通常将焦点捕获在内部

    • 它应该直接包裹浮动元素,并且只在对话框也被渲染时才被渲染FloatingFocusManager docsarrow-up-right

      • <FloatingPortal />将浮动元素传送到给定的容器元素中——默认情况下,在应用程序根之外并进入 body。

      • 可以自定义 root,也就是可选择具有 id 的节点,或者创建它并将其附加到指定的根(body)

最后更新于

这有帮助吗?