Chroma Panel

搜索文档

查找页面或章节

EN

自定义触发器

查看 Markdown

用你自己的按钮打开选择器。

ColorInput 自带一个方形的色块按钮。如果你需要的是工具栏图标、设置项或文字按钮,就把 PopoverChromaPanel 组合起来使用。这样可以做出自定义的 React 颜色选择器弹出层,而不必嵌套两层浮层。

"use client";
 
import { ChromaPanel, Popover } from "chroma-panel";
import "chroma-panel/style.css";
import { useState } from "react";
 
export function BrandColorButton() {
  const [anchor, setAnchor] = useState<HTMLButtonElement | null>(null);
  const [open, setOpen] = useState(false);
  const [color, setColor] = useState("#3366cc");
 
  return (
    <>
      <button
        ref={setAnchor}
        type="button"
        aria-haspopup="dialog"
        aria-expanded={open}
        onClick={() => setOpen((current) => !current)}
      >
        <span
          aria-hidden="true"
          style={{
            display: "inline-block",
            width: 14,
            height: 14,
            borderRadius: 4,
            background: color,
          }}
        />
        Brand color
      </button>
 
      <Popover anchor={anchor} open={open} onClose={() => setOpen(false)}>
        <div role="dialog" aria-label="Brand color" aria-modal="false">
          <ChromaPanel
            value={color}
            onChange={(next) => setColor(next.hex)}
            title="Brand color"
            onClose={() => {
              setOpen(false);
              anchor?.focus();
            }}
          />
        </div>
      </Popover>
    </>
  );
}

用 state 而不是 ref 保存锚点

anchor 是弹出层定位时参照的元素。按钮渲染出来之前,它的值是 null。把它存在 state 里,元素可用时 Popover 才会重新渲染。

需要补上的属性

设置 aria-haspopup="dialog",并让 aria-expandedopen 保持同步。给触发器一个可访问名称。弹出层本身需要 role="dialog" 和自己的标签,写法见上面的示例。

给面板传入 onClose 后,点击它的红色窗口按钮就会关闭弹出层,而不是只变暗留在那里,示例中的处理函数做的就是这件事。关闭后把焦点还给触发器,则需要你自己处理。面板本身负责哪些事情,请参阅无障碍功能

Popover 已经做好的事

你不需要自己处理点击外部或按 Escape 关闭,弹出层自带这些行为。

  • 它通过 portal 渲染到 document.body 中,所以任何带 overflow: hidden 的祖先元素都裁切不到它。
  • 下方空间足够时,它显示在锚点下方,否则显示在上方;同时与视口边缘保持 8px 的距离。与锚点的间距由 offset 控制,默认 8px。
  • 页面滚动或窗口大小变化时,它会重新定位,并把可用空间告诉面板,面板据此限制自己的高度。
  • 按 Escape 会关闭它,并把焦点移回锚点。
  • 在弹出层和锚点之外按下指针,也会关闭它。
  • 打开时,焦点移到其中第一个可聚焦的控件上。Tab 和 Shift+Tab 只在弹出层内循环,不会跑到后面的页面上去。

宽度小于 640px 时

宽度小于 640px 时,弹出层默认变成底部面板(bottom sheet),同时加上遮罩并锁定页面滚动。设置 sheetOnMobile={false},就能在小屏幕上继续使用锚定在按钮旁的颜色选择器下拉层。

<Popover
  anchor={anchor}
  open={open}
  onClose={() => setOpen(false)}
  sheetOnMobile={false}
>
  <div role="dialog" aria-label="Brand color">
    <ChromaPanel value={color} onChange={(next) => setColor(next.hex)} />
  </div>
</Popover>

sheetOnMobile 是 Popover 的 prop

只有 Popover 有这个 prop。ColorInput 既不接收它,也不会传递它,所以 ColorInput 在宽度小于 640px 时总是变成底部面板。想避开这一行为,就得自己实现触发器。完整的 prop 列表见 Popover 参考文档,被你替换掉的那个触发器见 ColorInput 参考文档