Chroma Panel

搜索文档

查找页面或章节

EN

模态框中的选择器

查看 Markdown

使用内联面板,让焦点和 Escape 都由同一个对话框管理。

在模态框、底部面板或抽屉里使用 React 颜色选择器时,请用 ChromaPanel。内联面板始终留在对话框内部,焦点、Escape 和外部点击都由同一个组件负责。

"use client";
 
import { ChromaPanel } from "chroma-panel";
import "chroma-panel/style.css";
import type { CSSProperties } from "react";
import { useRef, useState } from "react";
 
export function ColorDialog() {
  const dialogRef = useRef<HTMLDialogElement>(null);
  const [color, setColor] = useState("#3366cc");
 
  return (
    <>
      <button type="button" onClick={() => dialogRef.current?.showModal()}>
        Edit color
      </button>
 
      <dialog ref={dialogRef} aria-label="Edit color">
        <form method="dialog">
          <ChromaPanel
            defaultValue={color}
            showTitleBar={false}
            onChangeComplete={(next) => setColor(next.hex)}
            style={{ "--cp-width": "100%" } as CSSProperties}
          />
          <button type="submit">Done</button>
        </form>
      </dialog>
    </>
  );
}

为什么内联面板适合模态框

ChromaPanel 没有触发器,也没有弹出层。它像其他普通区块一样留在对话框的布局中,遮罩和焦点陷阱仍由你的对话框控制。

如果对话框已经有了自己的标题栏和内边距,下面两个 prop 能让面板放得更妥帖。showTitleBar={false} 会去掉面板自带的标题行和窗口按钮。把 --cp-width 设为 100%,面板就会撑满对话框,而不是保持默认的 320px。这两项以及面板其他控制尺寸的变量,都在主题页面中。

模态对话框位于顶层

showModal() 打开的对话框绘制在浏览器的顶层(top layer)中。通过 portal 渲染到 document.body 的内容则不在顶层,所以无论 z-index 设得多高,弹出层都可能被挡在对话框后面。在这类对话框里,请使用内联面板。

弹出层在对话框中的表现

如果你确实想在非模态对话框中使用 ColorInput,下面是它会带来的行为。

弹出层通过 createPortal 渲染到 document.body 中,所以它是对话框的兄弟节点,而不是后代节点。正因为如此,带 overflow: hiddenoverflow: auto 的对话框不会把面板裁切掉,打开选择器时,较矮的对话框也不会出现滚动条。弹出层自身的 z-index 设得很高,普通的层叠上下文不会让它落到对话框后面。

也正是因为 portal,有两种行为需要你结合自己的对话框测试一下:

  • 外部点击。 如果对话框在指针按在其元素之外时关闭,它可能会把在选择器内的点击也当成外部点击,因为选择器并不在对话框里面。打开选择器,点一下色轮:如果对话框在你眼前关掉了,就是这个原因。
  • 焦点。 如果对话框通过检查 DOM 包含关系来锁定焦点,那么弹出层刚把焦点移进来,对话框就可能把它拉回去,原因相同。

这些行为取决于对话框自身的规则。如果其中任何一项出了问题,就配置对话框,让它把选择器弹出层当作自己内容的一部分。

Escape 和焦点的具体细节

了解选择器弹出层自己会做什么,有助于你判断到底是谁的处理函数被触发了。

Escape 在 document 上的捕获阶段处理。弹出层会阻止事件继续传播,然后关闭自己,并把焦点还给触发器。因此,在传播路径中更靠后监听 Escape 的对话框收不到第一次按键,第二次按键才会正常传到它那里。如果对话框也在 document 的捕获阶段监听,就需要专门测试这种情况。

弹出层打开时,焦点会移到其中第一个可聚焦的控件上,Tab 只在弹出层内循环。关闭时如果焦点还在弹出层内,焦点会回到打开之前拥有焦点的元素。

这些都不是对话框特有的,弹出层一直都是这样工作的,完整说明见 Popover 参考文档。上面示例中的内联面板完全没有这些行为,这正是用它的意义所在。它的 prop 见 ChromaPanel 参考文档

在模态框中使用 React 颜色选择器 | Chroma Panel