feat(ui): float the reaction emoji picker as an anchored popover (LC-384) #394
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/lc-384-reaction-popover"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes LC-384. The headline fix of the LC-383 room-polish-2 epic (from
data/room1.png/data/room2.png).Problem
The reaction picker swapped inline in place of the add-reaction button (
hx-target="this" hx-swap="outerHTML"), so the search box + Recent row + grid rendered as a block in the message flow: it reflowed the conversation, pushed later messages down, overlapped the next message, and could be open on several messages at once. It was also unstyled.Fix: float it into a fixed host
#lc-reaction-popoverhost inlayout.html. The add-reaction button (data-lc-reaction-open) now loads the picker there (hx-target="#lc-reaction-popover",innerHTML) instead of swapping inline.layout.htmlscript positions the host next to the trigger. Fixed positioning escapes the#messagesoverflow clip, so the picker floats above the conversation and never reflows it. It flips above the trigger when there's no room below.Redesigned card
A compact card: full-width search input + close
xin a bordered header, a labelled Recent section, and a clean emoji grid of consistent.lc-emoji-cellsquares with a sunken hover.Preserved
The root keeps
id="picker-{id}"(the LC-288 recent-row JS derives the message id from it; the LC-274 filter keys on[id^="picker-"]) and thedata-lc-emoji-filter/-recent/-gridhooks - so filtering, the recent MRU, custom emojis, and the react/toggle wiring (#reactions-{id}) are unchanged. The oldhx-get .../cancelinline restore is replaced by a client-side close (cancel_pickeris now unused).Tests
./dev/cargo check,just build-css,just test,just test-saas- all green.🤖 Generated with Claude Code
The reaction picker swapped inline in place of the add-reaction button (hx-target="this" hx-swap="outerHTML"), so the search box + Recent row + grid rendered as a block in the message flow: it reflowed the conversation, pushed later messages down, overlapped the next message, and could be open on several messages at once. Render it into a shared, fixed-position host (#lc-reaction-popover, added once in layout.html) instead. The add-reaction button now loads the picker there (hx-target="#lc-reaction-popover", data-lc-reaction-open); a new layout.html script positions the host next to the trigger - fixed positioning escapes the #messages overflow clip, so the picker floats above the conversation and never reflows it. One host means it is inherently single-open; it dismisses on outside-click, Escape (focus returns to the trigger), a react, and a scroll of the message list (but not the picker's own grid scroll), and flips above the trigger when there is no room below. The picker markup is redesigned as a compact card: a full-width search input + a close affordance in a bordered header, a labelled Recent section, and a clean emoji grid of consistent .lc-emoji-cell squares with a sunken hover. The root keeps id="picker-{id}" (the LC-288 recent-row JS derives the message id from it; the LC-274 filter keys on [id^="picker-"]) and the data-lc-emoji-filter / -recent / -grid hooks, so filtering, the recent MRU, custom emojis, and the react/toggle wiring (#reactions-{id}) are all unchanged. The old hx-get .../cancel inline restore is replaced by a client-side close; cancel_picker is now unused. The four LC-77 golden fixtures are regenerated for the add-button retarget; diff verified to contain only that. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>