Popover
An overlay beside its trigger, such as a menu of settings.
<script>
import Button from '@brumate/ui/Button.brm';
import Dialog from '@brumate/ui/Dialog.brm';
import DialogTrigger from '@brumate/ui/DialogTrigger.brm';
import Heading from '@brumate/ui/Heading.brm';
import Input from '@brumate/ui/Input.brm';
import Label from '@brumate/ui/Label.brm';
import OverlayArrow from '@brumate/ui/OverlayArrow.brm';
import Popover from '@brumate/ui/Popover.brm';
import TextField from '@brumate/ui/TextField.brm';
</script>
<DialogTrigger>
<Button class="ui-button">Share</Button>
<Popover class="ui-popover" transition>
<OverlayArrow class="ui-arrow" />
<Dialog class="ui-dialog">
<Heading slot="title" level=(4)>Share the page</Heading>
<TextField class="ui-field" value="https://brumate.dev/ui">
<Label>Address</Label>
<Input class="ui-input" readonly />
</TextField>
<DialogTrigger>
<Button class="ui-button">Who can see it</Button>
<Popover class="ui-popover" placement="end" transition>
<Dialog class="ui-dialog" aria-label="Who can see it">
<p>Anyone with the address.</p>
</Dialog>
</Popover>
</DialogTrigger>
<Button slot="close" class="ui-button">Done</Button>
</Dialog>
</Popover>
</DialogTrigger>
- A DialogTrigger holds a
Button, which opens the popover, and aPopover, which often holds a Dialog. - It renders in a layer at the end of the page, or at the end of the layer of the overlay around it.
- Escape and a press outside close it. A press is a pointerdown and a pointerup of the first button, so a touch that scrolls the page closes nothing. Only the topmost of nested overlays closes.
transitionlets CSS animate it, withdata-closed,data-enteranddata-leave, as a Transition does.
Placement
placementputs it on a side of its trigger, such asbottom,bottom start,endortop end.startandendfollow the direction of the locale. It flips to the other side when it does not fit, unlessshouldFlip=(false).offset,crossOffsetandcontainerPaddingset the space to the trigger, along it, and to the edges of the window.- It is
position: fixed, placed withtopandleft. It follows its trigger as the page scrolls and resizes. - It shows
data-placement, the side that it took, and sets--trigger-widthand--available-height.
Focus
- It is modal: the rest of the page is
inertand does not scroll, and focus stays inside. Tab and Shift+Tab wrap at its ends, and focus that lands outside or on nothing, such as when the focused element is removed, goes back to the last element that had it. isNonModalleaves the page and focus as they are. A press outside does not close it, and focus that leaves it does, as in React Aria.- Focus moves into it once it has entered. When it closes, focus goes back:
- to the element that had focus before it opened;
- or, when that element is gone, to the trigger with the same ID, such as a trigger that rendered again;
- or, when nothing had focus, such as when a shortcut opened it, Tab moves on from where the selection was.
- Focus goes back only when it is still in the popover, or on nothing. A close that comes after the user moved focus elsewhere leaves it there.
- When the popover leaves the page while it is open, such as with an
#ifaround it, it gives back what it held: the page is no longerinert, and focus goes back.
OverlayArrow
An arrow from the popover to its trigger. The popover places it along its side, with left or top, and gives it data-placement, so CSS points it at the trigger:
.arrow[data-placement='bottom'] {
top: -6px;
rotate: 45deg;
}