ComponentsPopover

Popover

Display arbitrary rich content inside a non-modal dialog triggered by a button.

Documentation

Usage

import {
  Heading,
  Popover,
  PopoverContent,
  PopoverTrigger,
  Text,
} from "@optiaxiom/react";
 
export function App() {
  return (
    <Popover>
      <PopoverTrigger>Open popover</PopoverTrigger>
      <PopoverContent>
        <Heading level="6">Popover content</Heading>
        <Text>This is the popover content</Text>
      </PopoverContent>
    </Popover>
  );
}

Controlled

Use the open and defaultOpen props to toggle between controlled and uncontrolled usage. And combine it with onOpenChange to listen for changes to the state.

import {
  Flex,
  Heading,
  Popover,
  PopoverContent,
  PopoverTrigger,
  Switch,
  Text,
} from "@optiaxiom/react";
import { useState } from "react";
 
export function App() {
  const [keepOpen, setKeepOpen] = useState(false);
  const [open, setOpen] = useState(false);
 
  return (
    <Flex flexDirection="row">
      <Switch onCheckedChange={setKeepOpen}>Keep popover open</Switch>
 
      <Popover onOpenChange={(flag) => setOpen(flag || keepOpen)} open={open}>
        <PopoverTrigger>Open popover</PopoverTrigger>
        <PopoverContent>
          <Heading level="6">Popover content</Heading>
          <Text>This is the popover content</Text>
        </PopoverContent>
      </Popover>
    </Flex>
  );
}

Example

import {
  Field,
  Popover,
  PopoverContent,
  PopoverTrigger,
  SegmentedControl,
  SegmentedControlItem,
  Separator,
  Tooltip,
} from "@optiaxiom/react";
import {
  Select,
  SelectContent,
  SelectRadioItem,
  SelectTrigger,
  SelectValue,
} from "@optiaxiom/react/unstable";
import {
  IconAdjustmentsHorizontal,
  IconLayoutGrid,
  IconList,
} from "@tabler/icons-react";
 
export function App() {
  return (
    <Popover>
      <PopoverTrigger icon={<IconAdjustmentsHorizontal />}>
        Display
      </PopoverTrigger>
 
      <PopoverContent align="end" gap="12" px="0">
        <SegmentedControl defaultValue="list" gap="12" px="16">
          <Tooltip content="List">
            <SegmentedControlItem
              aria-label="List"
              icon={<IconList />}
              size="lg"
              value="list"
            />
          </Tooltip>
 
          <Tooltip content="Grid">
            <SegmentedControlItem
              aria-label="Grid"
              icon={<IconLayoutGrid />}
              size="lg"
              value="grid"
            />
          </Tooltip>
        </SegmentedControl>
 
        <Field
          flexDirection="row"
          justifyContent="space-between"
          label="Columns"
          px="16"
        >
          <Select defaultValue="Status" items={["Status", "Due Dates"]}>
            <SelectTrigger ml="12" size="sm">
              <SelectValue />
            </SelectTrigger>
 
            <SelectContent align="end">
              {["Status", "Due Dates"].map((item) => (
                <SelectRadioItem item={item} key={item}>
                  {item}
                </SelectRadioItem>
              ))}
            </SelectContent>
          </Select>
        </Field>
 
        <Field
          flexDirection="row"
          justifyContent="space-between"
          label="Grouping"
          px="16"
        >
          <Select
            defaultValue="No grouping"
            items={["No grouping", "Campaign"]}
          >
            <SelectTrigger ml="12" size="sm">
              <SelectValue />
            </SelectTrigger>
 
            <SelectContent align="end">
              {["No grouping", "Campaign"].map((item) => (
                <SelectRadioItem item={item} key={item}>
                  {item}
                </SelectRadioItem>
              ))}
            </SelectContent>
          </Select>
        </Field>
 
        <Separator orientation="horizontal" />
 
        <Field
          flexDirection="row"
          justifyContent="space-between"
          label="View"
          px="16"
        >
          <Select defaultValue="Tasks" items={["Tasks", "Steps"]}>
            <SelectTrigger ml="12" size="sm">
              <SelectValue />
            </SelectTrigger>
 
            <SelectContent align="end">
              {["Tasks", "Steps"].map((item) => (
                <SelectRadioItem item={item} key={item}>
                  {item}
                </SelectRadioItem>
              ))}
            </SelectContent>
          </Select>
        </Field>
      </PopoverContent>
    </Popover>
  );
}

Customize trigger

By default we use the Button component for the dialog trigger which accepts all of the existing button props.

We can also completely change the trigger by using asChild and passing our own component.

4
import {
  Indicator,
  Popover,
  PopoverContent,
  PopoverTrigger,
  Text,
} from "@optiaxiom/react";
import { IconFilter } from "@tabler/icons-react";
 
export function App() {
  return (
    <Popover>
      <Indicator content="4" intent="information" variant="solid">
        <PopoverTrigger
          appearance="subtle"
          aria-label="Filters"
          icon={<IconFilter />}
        />
      </Indicator>
 
      <PopoverContent>
        <Text>Notification filters</Text>
      </PopoverContent>
    </Popover>
  );
}

Props

Popover

Doesn't render its own HTML element.

Prop

defaultOpen

The initial open state in uncontrolled mode.

false | true

modal

When enabled interaction with outside elements will be disabled and only popover content will be visible to screen readers.

false | true

onOpenChange

Handler that is called when the open state changes.

(open: boolean) => void

open

The open state in controlled mode.

false | true

PopoverTrigger

Supports all Button props in addition to its own. Renders a <button> element.

Prop

addonAfter

Display content inside the button after children.

ReactNode

addonBefore

Display content inside the button before children.

ReactNode

appearance

Control the appearance by selecting between the different button types.

"default" | "danger" | "primary" | "subtle" | "danger-outline" | "inverse"

asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

false | true

className

string

disabled

Whether the button is disabled.

false | true

icon

Display an icon before or after the button content or omit children to only show the icon.

ReactNode

iconOnly

Whether button should have square shape.

false | true

iconPosition

Control whether to show the icon before or after the button content.

"end" | "start"

loading

Whether to show loading spinner inside the button.

false | true

size

Control the size of the button.

"sm" | "md" | "lg"

PopoverContent

Supports all Box props in addition to its own. Renders a <div> element.

Prop

align

"center" | "end" | "start"

Default: start

asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read the Composition guide for more details.

false | true

className

string

maxH

Whether to restrict the max-height of the content.

Content is also restricted by the available height in the screen relative to the trigger.

"xs" | "sm" | "md" | "lg" | "full"

minW

Whether to set the min-width to the width of the trigger.

"0" | "trigger"

onCloseAutoFocus

Event handler called when auto-focusing on close. Can be prevented.

(event: Event) => void

onEscapeKeyDown

Event handler called when the escape key is down. Can be prevented.

(event: KeyboardEvent) => void

onFocusOutside

Event handler called when the focus moves outside of the DismissableLayer. Can be prevented.

(event: FocusOutsideEvent) => void

onInteractOutside

Event handler called when an interaction happens outside the DismissableLayer. Specifically, when a pointerdown event happens outside or focus moves outside of it. Can be prevented.

(event: FocusOutsideEvent | PointerDownOutsideEvent) => void

onOpenAutoFocus

Event handler called when auto-focusing on open. Can be prevented.

(event: Event) => void

onPointerDownOutside

Event handler called when the a pointerdown event happens outside of the DismissableLayer. Can be prevented.

(event: PointerDownOutsideEvent) => void

side

"bottom" | "left" | "right" | "top"

sideOffset

number

Default: 2

withArrow

Whether to show an arrow.

false | true

Changelog

0.1.0

  • Added component