跳到内容

弹出层

一个锚定于触发器的浮动面板,用于日期选择器、筛选器和内联表单——采用新粗野主义风格的边框与阴影处理。

import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"

弹出层在锚定到触发器的浮层面板中展示富内容——点击打开,点击外部或按 Esc 关闭。它基于 Base UI Popover 组件构建,并按新粗野主义的配方设计:粗边框、硬阴影、加粗字体。

适合以下场景:

  • 行内表单——重命名、设置尺寸或改一项配置,无需打开完整对话框。
  • 日期与颜色选择器——把日历或色板网格直接锚定到对应输入。
  • 工具栏与表格筛选——把复选框与范围控件收在紧凑触发器后面。

安装

pnpm dlx shadcn@latest add https://neobrutalism.com/r/base/popover.json
npx shadcn@latest add https://neobrutalism.com/r/base/popover.json
yarn dlx shadcn@latest add https://neobrutalism.com/r/base/popover.json
bunx --bun shadcn@latest add https://neobrutalism.com/r/base/popover.json

安装以下依赖:

pnpm add @base-ui/react
npm install @base-ui/react
yarn add @base-ui/react
bun add @base-ui/react

将以下代码复制并粘贴到您的项目中。

components/ui/popover.tsx
"use client"

import * as React from "react"
import { Popover as PopoverPrimitive } from "radix-ui"

import { cn } from "@/lib/utils"

function Popover({
  ...props
}: React.ComponentProps<typeof PopoverPrimitive.Root>) {
  return <PopoverPrimitive.Root data-slot="popover" {...props} />
}

function PopoverTrigger({
  ...props
}: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
  return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />
}

function PopoverContent({
  className,
  align = "center",
  sideOffset = 4,
  ...props
}: React.ComponentProps<typeof PopoverPrimitive.Content>) {
  return (
    <PopoverPrimitive.Portal>
      <PopoverPrimitive.Content
        data-slot="popover-content"
        align={align}
        sideOffset={sideOffset}
        className={cn(
          "z-50 flex w-72 origin-(--radix-popover-content-transform-origin) flex-col gap-2.5 rounded border-2 bg-popover p-2.5 text-sm text-popover-foreground shadow-md outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
          className
        )}
        {...props}
      />
    </PopoverPrimitive.Portal>
  )
}

function PopoverAnchor({
  ...props
}: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
  return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />
}

function PopoverHeader({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="popover-header"
      className={cn("flex flex-col gap-0.5 text-sm", className)}
      {...props}
    />
  )
}

function PopoverTitle({ className, ...props }: React.ComponentProps<"h2">) {
  return (
    <div
      data-slot="popover-title"
      className={cn("font-medium", className)}
      {...props}
    />
  )
}

function PopoverDescription({
  className,
  ...props
}: React.ComponentProps<"p">) {
  return (
    <p
      data-slot="popover-description"
      className={cn("text-muted-foreground", className)}
      {...props}
    />
  )
}

export {
  Popover,
  PopoverAnchor,
  PopoverContent,
  PopoverDescription,
  PopoverHeader,
  PopoverTitle,
  PopoverTrigger,
}

更新导入路径以匹配您的项目结构。

用法

import {
  Popover,
  PopoverContent,
  PopoverDescription,
  PopoverHeader,
  PopoverTitle,
  PopoverTrigger,
} from "@/components/ui/popover"
<Popover>
  <PopoverTrigger render={<Button variant="outline" />}>
    Open Popover
  </PopoverTrigger>
  <PopoverContent>
    <PopoverHeader>
      <PopoverTitle>Title</PopoverTitle>
      <PopoverDescription>Description text here.</PopoverDescription>
    </PopoverHeader>
  </PopoverContent>
</Popover>

组合

使用以下组合来构建 Popover

Popover
├── PopoverTrigger
└── PopoverContent

示例

基础

一个带页眉、标题和描述的简单弹出框。

import { Button } from "@/components/ui/button"
import {
  Popover,

对齐

使用 PopoverContent 上的 align 属性控制水平对齐方式。

import { Button } from "@/components/ui/button"
import {
  Popover,

带表单

一个内部放置了表单字段的弹出框。

import { Button } from "@/components/ui/button"
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field"
import { Input } from "@/components/ui/input"

RTL

若要在 Neobrutalism 中启用 RTL 支持,请参阅 RTL 配置指南

"use client"

import {

无障碍

弹出层遵循 WAI-ARIA Dialog 模式 的非模态形态:触发器带有 aria-haspopup="dialog"aria-expandedaria-controls,面板渲染为 role="dialog"——打开时焦点移入,关闭时焦点交还给触发器。

键盘交互:

按键操作
Space / Enter从触发器打开或关闭弹出层
Tab / Shift + Tab在面板内可聚焦元素之间移动焦点
Esc关闭弹出层,并把焦点交还给触发器

API 参考

更多信息请参阅 Base UI 文档。