This version adds explicit scroll element control for collection renderers, custom typeahead labels for composite items, and selected-state rendering for select items. It also improves disabled radio groups, hidden popover performance, nested Esc handling, dialogs across portals and shadow roots, multi-select combobox form values, typeahead with unmounted options, and composite virtual focus.
Custom typeahead text for composite items
The new typeaheadText prop lets CompositeItem use an explicit label for typeahead matching when its rendered content starts with an emoji or other decoration.
<SelectItem typeaheadText="Canada" value="Canada">
<span aria-hidden>🇨🇦</span> Canada
</SelectItem>Set typeaheadText to an empty string to exclude an item from typeahead matching. The prop is also available on these components exported by @ariakit/react and built on CompositeItem: ComboboxItem, FormRadio, MenuItem, MenuItemCheckbox, MenuItemRadio, Radio, SelectItem, Tab, ToolbarContainer, ToolbarInput, and ToolbarItem.
Thanks to @Dremora for reporting the issue and providing the reproduction, and @georgekaran for the investigation and implementation work that informed this solution.
Skip position updates on hidden popovers
Popovers that stay mounted while closed, such as Popover, Tooltip, Hovercard, Menu, SelectPopover, and ComboboxPopover, no longer set up position auto-updates while hidden, unless a custom updatePosition callback is provided. Closed popovers no longer keep standing scroll and resize listeners and observers around, and hiding a popover skips a full positioning setup and teardown cycle. This reduces aggregate CPU and rendering work when rapidly showing and hiding popovers, such as when quickly moving across toolbar items with tooltips.
Thanks to @aledecicco for reporting the issue.
RadioGroup disables descendant radios
The RadioGroup disabled prop now marks the group as disabled and disables descendant Radio components, including radios rendered as custom elements.
<RadioGroup disabled>
<Radio value="Apple" />
<Radio value="Orange" />
</RadioGroup>Thanks to @kripod for reporting the issue.
New SelectItemSelected component
The new SelectItemSelected value component exposes whether the closest SelectItem is selected through a required function child.
<SelectItem value="Apple">
<SelectItemSelected>
{(selected) => (selected ? <CheckIcon /> : null)}
</SelectItemSelected>
Apple
</SelectItem>Thanks to @jonrimmer for proposing the feature, and @georgekaran for the investigation and implementation work that informed this solution.
Explicit scroll elements for collection renderers
Collection renderers, including CompositeRenderer and SelectRenderer, now accept a scrollElement prop. It accepts an element, a React ref, a resolver function, or null to disable viewport-driven rendering.
Use this prop when the scrolling ancestor cannot be detected automatically, such as when an overflow: auto element does not overflow until asynchronous items are loaded:
const scrollElementRef = useRef<HTMLDivElement>(null);
<div ref={scrollElementRef} className="scroller">
<SelectRenderer scrollElement={scrollElementRef} items={items}>
{(item) => <SelectItem key={item.id} {...item} />}
</SelectRenderer>
</div>;Nested renderers using the same store inherit an explicitly provided scroll element unless they provide their own. If neither a renderer nor a same-store ancestor provides a value, the renderer detects its closest scrolling ancestor.
Thanks to @ItaiYosephi for reporting the issue, providing the video and StackBlitz reproduction, and proposing an explicit scroller prop.
Handling Esc in nested widgets
The Dialog component and components that inherit its default Esc handling, including Popover, ComboboxPopover, and SelectPopover, now let descendants call event.stopPropagation() on Esc without hiding the enclosing component. This allows a nested widget to dismiss itself first.
<Dialog>
<input
onKeyDown={(event) => {
if (event.key !== "Escape") return;
if (!suggestionsOpen) return;
event.stopPropagation();
closeSuggestions();
}}
/>
</Dialog>When the component handles an Esc event from its React subtree, it also stops the event at its boundary. This keeps an enclosing third-party React dialog with a bubble handler open while the Ariakit component closes.
When it handles Esc through the document fallback, such as when focus is on its disclosure, it stops the event at document before it reaches window bubble listeners.
An ancestor capture handler that stops Esc before it reaches the component owns the event. If hideOnEscape runs before such a handler, it can call event.stopPropagation() to keep the event from reaching it.
Thanks to @boaz-wiz for reporting the issue.
Other updates
- Fixed multi-selectable
Comboboxcomponents to submit selected values to forms when anameis provided. Thanks to @cloud-walker and @georgekaran. - Fixed
Compositeand derived widgets such asSelectListto clear stale focus-visible state and warn in development when virtual focus is used with a non-focusable composite element. Thanks to @ItaiYosephi. - Fixed
Dialogand components built on it, such asPopoverandComboboxPopover, so interacting with elements returned bygetPersistentElementsacross open shadow roots no longer closes the component before it receives focus. - Fixed sibling modal
Dialogcomponents and modal components built on them, such asPopoverandComboboxPopover, rendered in their default portals so opening them in the same render no longer made each other inert. Thanks to @yishayhaz and @gonzoblasco. - Fixed collection store
itemlookups to resolve controlled items added after store creation when no live item is registered. This allowsSelecttypeahead to update its value while options are unmounted. Thanks to @georgekaran. - Updated dependencies:
@ariakit/components@0.1.8,@ariakit/utils@0.1.5,@ariakit/react-store@0.1.8,@ariakit/react-utils@0.2.3,@ariakit/store@0.1.7