Keep open menus and rich tooltips open through a Livewire render
<x-menu>, <x-fab-menu> and <x-rich-tooltip> gave their popover an id that is new with every render, and Livewire's morph matches an element without a wire:key by its id. So every render of the component around them swapped the popover for a closed copy: an open menu closed, its listeners stayed behind on the old element (Escape or a press outside then left aria-expanded="true" on the trigger and focus unreturned), a keep-open item's wire:click closed its menu when the response came, and a rich tooltip went on showing the detached bubble, so it never opened again. <x-carousel>'s row had the same kind of id: after a render it scrolled without its listeners, and its items stopped re-masking. The popover, the bubble and the row now carry a wire:key, so the morph patches them in place and changes the id and anchor name together with the trigger's, as it already did for everything else. The key goes through an attribute bag: Livewire compiles a wire:key written in a template into the key of the loop iteration around it, which would have given every child component after the menu in a row the same key. A morph also removes the menu button's ARIA attributes, which only script writes. menu.js now writes them again after every morph, so the button of a menu that stays open, and a FAB menu's close look, still say it is open, and aria-controls names the popover's new id. A second press on an open menu's button opened it again, render or not: the popover closes on the press, and the guard against the click that follows was timed from the toggle event, which is queued and arrives after that click. It is timed from beforetoggle now. Browser tests with Livewire probes in Chromium, Firefox and WebKit, and a render test for the keys and the keys of the child components after. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RHoXZSHc8gGpZjFmA5fPc2
This commit is contained in:
co-authored by
Claude Opus 5
parent
ab7317faae
commit
8640d815c8
@@ -44,7 +44,9 @@
|
||||
longer slides inside its mask. RTL mirrors the keylines, keys and buttons.
|
||||
|
||||
Re-measures itself when resized, when a Livewire morph resets its styles and when items
|
||||
come and go. --}}
|
||||
come and go. The row's id, which the buttons control, is new with every render; the row
|
||||
carries a `wire:key` (see `<x-menu>`), so a morph patches it in place — its scroll position
|
||||
and listeners kept — rather than swapping in a copy. --}}
|
||||
|
||||
@props([
|
||||
'layout' => 'multi-browse',
|
||||
@@ -116,6 +118,7 @@
|
||||
|
||||
<div
|
||||
x-ref="scroller"
|
||||
{{ new \Illuminate\View\ComponentAttributeBag(['wire:key' => 'material-carousel']) }}
|
||||
id="{{ $scrollerId }}"
|
||||
role="region"
|
||||
aria-roledescription="{{ __('carousel') }}"
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
Two to six items. The FAB (`icon`, `add` by default, in `color`'s container) turns into a
|
||||
round close button in the colour itself while the list is open above it, end-aligned; the
|
||||
list is a `popover="auto"` menu with the menu keyboard of `<x-menu>`. `label` names the FAB
|
||||
for screen readers. Give the items the same `color`.
|
||||
for screen readers. Give the items the same `color`. Like `<x-menu>`'s, the list is keyed for
|
||||
Livewire, so it stays open through a render of the component around it.
|
||||
|
||||
FabMenuBaselineTokens (androidx Compose Material 3, Apache-2.0): 56px items, 4px apart, 8px
|
||||
above the close button. --}}
|
||||
@@ -56,6 +57,7 @@
|
||||
|
||||
<div
|
||||
x-ref="menu"
|
||||
{{ new \Illuminate\View\ComponentAttributeBag(['wire:key' => 'material-fab-menu']) }}
|
||||
id="material-fab-menu-{{ $key }}"
|
||||
popover="auto"
|
||||
role="menu"
|
||||
|
||||
@@ -24,6 +24,14 @@
|
||||
that is `position: fixed` (`<x-button fab>` on a phone) leaves the wrapper behind as an empty
|
||||
box where the page put it, and the menu opened there.
|
||||
|
||||
The id and the anchor name are new with every render. The popover carries a `wire:key`, which
|
||||
a Livewire morph matches it by before the id, so a render of the component around an open
|
||||
menu patches it in place — still open, focus and listeners kept — instead of swapping in a
|
||||
closed copy; menu.js then writes the menu button's ARIA attributes again. The key goes
|
||||
through an attribute bag: Livewire compiles a `wire:key` written in a template into the key
|
||||
of the loop iteration around it, which would give every child component after the menu the
|
||||
same key.
|
||||
|
||||
The container is Expressive's standard menu (surface-container-low, 16px corner, elevation
|
||||
2), or `vibrant` in tertiary-container — StandardMenuTokens and VibrantMenuTokens from
|
||||
androidx Compose Material 3 (Apache-2.0). --}}
|
||||
@@ -49,6 +57,7 @@
|
||||
|
||||
<div
|
||||
x-ref="menu"
|
||||
{{ new \Illuminate\View\ComponentAttributeBag(['wire:key' => 'material-menu']) }}
|
||||
id="material-menu-{{ $key }}"
|
||||
popover="auto"
|
||||
role="menu"
|
||||
|
||||
@@ -10,6 +10,11 @@
|
||||
form M3 asks for when it has actions. The bubble is a popover in surface-container with a
|
||||
medium corner and elevation 2, 312px at most, placed by anchor positioning on `side`.
|
||||
|
||||
The bubble's id and anchor name are new with every render; its `wire:key` (see `<x-menu>`)
|
||||
lets a Livewire morph patch it in place, so an open bubble stays open — through its own
|
||||
action's `wire:click` too — and resources/js/rich-tooltip.js keeps showing and hiding the
|
||||
element on the page rather than one the morph took away.
|
||||
|
||||
RichTooltipTokens (androidx Compose Material 3, Apache-2.0): title-small subhead and body-medium
|
||||
text in on-surface-variant, label-large actions in primary. --}}
|
||||
|
||||
@@ -35,6 +40,7 @@
|
||||
|
||||
<span
|
||||
x-ref="bubble"
|
||||
{{ new \Illuminate\View\ComponentAttributeBag(['wire:key' => 'material-rich-tooltip']) }}
|
||||
id="material-rich-tooltip-{{ $key }}"
|
||||
popover="{{ $persistent ? 'auto' : 'manual' }}"
|
||||
role="{{ $persistent ? 'dialog' : 'tooltip' }}"
|
||||
|
||||
Reference in New Issue
Block a user