组件 · 布局
Panel
A column of the desktop main area with a 56px panel header and its own scrolling content; its header is also the top bar of a phone's pushed pages.
import { Panel } from "@botiverse/raft-ui";A panel is one column of the desktop main area: a panel header, then content that scrolls inside the column. Every column uses the same header, whether it holds a conversation, a thread, a side panel or a drawer's content. On a phone, PanelHeader is the top bar of a pushed page.
When to use
| Situation | Use |
|---|---|
| A column of the desktop main area, or a side panel beside it (tasks, files, a profile) | Panel |
| A conversation: header, tabs, messages and composer | ConversationPanel, built on Panel |
| A thread beside a conversation | ThreadPanel |
| The top bar of a phone's pushed page | PanelHeader |
| The list of what a destination contains, next to the rail | Sidebar |
| Details or settings that slide in over the page | Drawer |
How the regions of a screen are arranged, and when a side panel sits beside the main area or covers it, is in Page structure and Responsive layout.
Anatomy
Panel: the column.edge="attached"draws aline-seamon its left edge, for a side panel next to a conversation;edge="inset"draws none.PanelHeader: the 56px header with aline-seamunder it. Required.PanelHeaderIcon: the location tile of a place, or the avatar of a person. Optional.PanelHeaderContent: holdsPanelHeadingandPanelMeta.PanelHeading: thePanelTitle, followed by anyPanelStatus(a tag such as Archived) orPanelActivity(a live status, wrapping a Status).PanelMeta: one line of description under the title.PanelActions: the trailingPanelActionandPanelToggleActionbuttons.
PanelSectionHeader: the same bar for a section-level view. On a phone it sits on the frame.PanelHeaderRoot: the bare 56px bar both headers are built on, for a header with its own layout.PanelNotice, withPanelNoticeContentandPanelNoticeActions: a full-width strip under the header for a problem in this panel. When to use it instead of a banner is in Banner. A status pixel or an icon placed beforePanelNoticeContentleads its first line (A mark that leads text).PanelBody,PanelScrollViewportandPanelScrollContent: the content and its single scroll container.PanelSection: a block of content with a hairline above it.PanelFooter: the bottom action area, padded for the safe area.
Variants
| Header | Use for |
|---|---|
PanelHeader | Every desktop panel header; the white top bar of a phone's pushed page |
PanelSectionHeader | A section-level view. On a phone it sits on layer-frame with foreground-on-frame text, and hides PanelHeaderIcon |
A phone's pushed page has a white top bar, and its location tile stays a yellow sticker (Open questions).
States
- A pressed
PanelToggleAction, or aPanelActionwitharia-pressed, is a yellow sticker: it marks the side panel that is open. - Other states follow States.
Behavior
- The header stays put; only
PanelScrollViewportscrolls. - The title and the description truncate with an ellipsis on one line each.
- On a phone the header sticks to the top of the page.
- When the window is shorter than 600px, the header shrinks with every other header (Spacing).
Phone top bars
The structure of a phone's top bars (the navigation row of a top-level page, Back and up to 2 icon buttons on a pushed page) is in Page structure. The top bar also follows these rules:
- On a top-level page, put the page name in the 56px row in
text-headline, with no large title and no icon tile before it (Page structure). - Don't put New or Search buttons in the top bar of a top-level page: search is the Search tab (Navigation).
- Make the bell the entry to the notification center, with a pink pixel when something is new. On a phone it is only in the top bar of the first top-level page (Page structure).
- Use the
arrow-lefticon for Back, not a chevron: its ink fills the icon box like other icons, so it mirrors the button at the other end without being shifted (Alignment).
Content
- Name the place, the person or the view in
PanelTitle. - Put a location tile (IconTile) in
PanelHeaderIconfor a place, and the person's avatar for a person. Other pages have no icon: theirPanelMetasays where they belong, such as the channel a thread is in. - Start
PanelMetawith what is live, then the counts: "2 agents working · 5 members" (Working agents). Set numbers and IDs in Space Mono (Typography). - Put a tag on the name (Experimental, Archived, Deleted) in
PanelHeadingas aPanelStatus, never inPanelMeta: the description truncates, and truncation cuts off the tag's edge.
Accessibility
Panelrenders asection. Name it witharia-labeloraria-labelledbywhen several panels sit side by side.PanelTitlerenders adiv. Render it as the page's heading (render={<h1 />}, orh2inside a page that has one).PanelActionwraps the Base UI Button and always renderstype="button".PanelToggleActionwraps the Base UI Toggle, which setsaria-pressed.- Give every icon-only
PanelActionandPanelToggleActionanaria-label: "Back", "Channel Details", "Close Tasks". - Give an icon-only
PanelStatus(a muted bell) an accessible name.
Rules
- Use this header in every panel: conversation, thread, side panel and drawer. Don't give one panel its own header or button sizes.
- Use
PanelActionfor the header's icon buttons: 32px with an 18px icon and a 44px hit area on desktop (from 768px with a mouse or trackpad), 4px apart; 44px with a 22px icon, 2px apart, on a phone and on a touch screen at any width. They follow the default control height (Spacing). A 44px button next to a 17px title looks heavy on desktop; a finger needs 44px. - Put Back first in the header and the actions last, in
PanelActions: the header pulls its first and last button out so their icon boxes, not the buttons, sit on the content margin, 20px on a desktop and on a touch screen in the desktop layout, 16px on a phone, at every button size (Alignment). Don't add margins of your own to move an edge button. Keep onlyPanelActions inPanelActions: the group declares their inset, so an action hidden at some width (a thread's Close below 1024px) leaves the next one on the edge. A framelessButtonaticon-mdoricon-lgas the header's first or last child is pulled the same way; a filled one ends its shape on the margin. - Give a
PanelStatusits own cell on the title's line, and center it by its shape. Don't nudge it: the title's letters already sit on the line's center, so a 1px shift makes it sit high. - Open a side panel from the header with a
PanelToggleAction: pressing it again closes the panel. - Give a side panel next to a conversation
edge="attached", so it draws the one line between the two (Page structure).
API
Panel
| Prop | 类型 | 默认值 |
|---|---|---|
| PanelEdge | ||
| Allows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement | ((props, state) => ReactElement) |
Also takes the attributes of its element.
PanelAction
| Prop | 类型 | 默认值 |
|---|---|---|
| Whether the button should be focusable when disabled. | boolean | false |
| Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). | boolean | true |
| Allows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement | ((props, state) => ReactElement) |
Also takes the attributes of its element.
PanelActions, PanelActivity, PanelBody, PanelFooter, PanelHeader, PanelHeaderContent, PanelHeaderIcon, PanelHeaderRoot, PanelHeading
| Prop | 类型 | 默认值 |
|---|---|---|
| Allows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement | ((props, state) => ReactElement) |
Also takes the attributes of its element.
PanelMeta
| Prop | 类型 | 默认值 |
|---|---|---|
| "sm" | "md" | "sm" | |
| Allows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement | ((props, state) => ReactElement) |
Also takes the attributes of its element.
PanelNotice
| Prop | 类型 | 默认值 |
|---|---|---|
| The tint: warning for something to fix (the default), destructive only for what cannot be undone. | PanelNoticeStatus | "warning" |
| Allows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement | ((props, state) => ReactElement) |
Also takes the attributes of its element.
PanelNoticeActions, PanelNoticeContent, PanelScrollContent, PanelScrollViewport, PanelSection, PanelSectionHeader, PanelStatus, PanelTitle
| Prop | 类型 | 默认值 |
|---|---|---|
| Allows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement | ((props, state) => ReactElement) |
Also takes the attributes of its element.
PanelToggleAction
| Prop | 类型 | 默认值 |
|---|---|---|
| Whether the toggle button is currently pressed. This is the uncontrolled counterpart of `pressed`. | boolean | false |
| Whether the component should ignore user interaction. | boolean | false |
| Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). | boolean | true |
| Callback fired when the pressed state is changed. | (pressed: boolean, eventDetails: BaseToggle.ChangeEventDetails) => void | |
| Whether the toggle button is currently pressed. This is the controlled counterpart of `defaultPressed`. | boolean | |
| Allows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement | ((props, state) => ReactElement) | |
| A unique string that identifies the toggle when used inside a toggle group. | string |
Also takes the attributes of its element.
相关组件
改动
- 0.10.2 · Fixed
PanelActionandPanelToggleActionfollow the same desktop condition as every other control ((width >= 48rem) and (pointer: fine)). On a touch tablet they were 32px while the rest stayed 44px; they now keep 44px with 22px icons there. Phone and desktop sizes are unchanged. - 0.5.40 · FixedElegant
PanelHeader's icon chip:fill-mutedmeasured 1.04:1 against the canvas-muted header (effectively invisible) and becomesfill-strong; avatar slots declaredata-icon-kind="avatar"and drop the chip entirely, so a round avatar no longer sits on a square. Brutal's black-bordered chip is unchanged. - 0.5.36 · ChangedPanel alignment: the elegant
PanelHeaderputs its icon box on the 20px line and the title on the 68px line (36px to 20px atmd+, gap 8 to 12);MessageItemrows in both themes carry the 8px inset (7px padding + 1px border, 6px + 2px in Brutal) that lands the avatar on 20 and the text on 68 next to the 12px the app shell keeps. Brutal also drops the row'smx-3, taking the mobile avatar inset from 32 to 20. The thread panel uses the chat panel's figures. - 0.5.0 · Breaking ChangesElegant
PanelHeaderIconnow owns its container treatment. It renders as a 36px rounded muted surface instead of a bare auto-sized slot. Remove redundant consumer wrappers and update layouts that relied on the previous intrinsic size. - 0.4.0 · Breaking Changes
PanelHeaderIconno longer sizes its icon. The slot dropped the[&_svg]sizing (brutal 18px / elegant 14px stroke 2.5); consumers must now size icons themselves, e.g. with asize-*class or thesizeattribute. ([0295c2e](https://github.com/botiverse/rui/commit/0295c2e)) - 0.3.0 · Breaking ChangesRenamed
PanelHeaderFrametoPanelHeaderRootand updated the corresponding type anddata-slotfrompanel-header-frametopanel-header-root. Panel header parts remain available from the package root and are now owned by the shared Panel vocabulary. - 0.3.0 · AddedAdded
AppShellPanelSlotandAppShellOverlaySlot, overlay-aware main slots, attached/inset Panel edges, and the sharedPanelActivityheader part for composable multi-panel layouts. - 0.2.0 · Breaking ChangesGeneralized panel chrome: replace
PanelHeaderwithPanelHeaderFrame, and replaceConversationPanelActions,ConversationPanelHeader*,ConversationPanelHeading,ConversationPanelMeta,ConversationPanelSectionHeader,ConversationPanelStatus, andConversationPanelTitlewith theirPanel*counterparts. Update selectors targeting the renameddata-slotvalues.