v3: stop cards and modal bodies from clipping the editor's popups
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 40s
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 40s
The emoji picker and the @-mention list were absolutely positioned inside .mention-textarea-wrap, so every ancestor with a clipping box cut them off — and TextEditor sits inside plenty: .dump-card-inner, .journal-card and .rich-content-card clip with overflow: hidden, while .modal-body and the chat scroller clip just as hard with overflow-y: auto. The picker also always opened upward, so a textarea near the top of the viewport pushed it off-screen. Both popups are now portaled to <body> and anchored to the textarea with position: fixed, which no ancestor can clip. The new useAnchoredFloat hook flips to whichever side has more room, clamps horizontally on narrow screens, and publishes the space left as --float-max-h so the list shrinks and scrolls instead of overflowing. It re-places on scroll, on resize, and through a ResizeObserver, since autoResize grows the textarea as you type. z-index goes to 1100 to clear the modal backdrop, staying under tooltips. Drops two point-fixes that only existed to work around the clipping: the hardcoded upward flip for the chat composer's mention list, whose leftover margin would now double the gap, and the claim that the chat modal's body needed overflow: visible for the popovers' sake. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
43
src/App.css
43
src/App.css
@@ -4277,21 +4277,14 @@ body.has-player .chat-fab {
|
||||
max-height: 92vh;
|
||||
}
|
||||
|
||||
/* The composer's popovers (mention list, emoji picker) anchor to the input,
|
||||
which sits at the very bottom of the modal. The modal body must not clip them
|
||||
— let them escape the body entirely so they're never cropped. The message
|
||||
list keeps its own scroll, so nothing else relies on the body clipping. */
|
||||
/* The chat sizes itself to the modal and the message list carries its own
|
||||
scroll, so the body never has anything to scroll — dropping its clip keeps a
|
||||
rounding overflow from raising a second, empty scrollbar beside the messages.
|
||||
(The composer's popovers no longer need this: they are portaled to <body>.) */
|
||||
.modal-card:has(.chat) .modal-body {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/* Open the mention list upward, into the roomy message area above the input. */
|
||||
.chat-composer .mention-dropdown {
|
||||
top: auto;
|
||||
bottom: 100%;
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
|
||||
/* Very narrow viewports: stack the input and Send button. */
|
||||
@media (max-width: 400px) {
|
||||
.chat-composer {
|
||||
@@ -5806,20 +5799,22 @@ body.has-player .chat-fab {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Portaled to <body> and positioned from JS (see useAnchoredFloat), for the
|
||||
same reason as the emoji picker below: the editor sits inside cards, modal
|
||||
bodies and scrollers whose overflow would clip an absolutely positioned
|
||||
popup. Above the modal backdrop (1000), below tooltips (9999). */
|
||||
.mention-dropdown {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 200;
|
||||
position: fixed;
|
||||
z-index: 1100;
|
||||
max-width: calc(100vw - 16px);
|
||||
list-style: none;
|
||||
margin: 2px 0 0;
|
||||
margin: 0;
|
||||
padding: 4px 0;
|
||||
background: var(--color-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
|
||||
max-height: 220px;
|
||||
max-height: var(--float-max-h, 220px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
.mention-dropdown-item {
|
||||
@@ -5840,12 +5835,14 @@ body.has-player .chat-fab {
|
||||
}
|
||||
|
||||
/* ── Emoji picker (frimousse) ── */
|
||||
/* Portaled to <body> and positioned from JS (see useAnchoredFloat): anchored
|
||||
with `position: fixed` so cards, modal bodies and scrollers can't clip it.
|
||||
Above the modal backdrop (1000), below tooltips (9999). */
|
||||
.emoji-picker-float {
|
||||
position: absolute;
|
||||
bottom: calc(100% + 4px);
|
||||
left: 0;
|
||||
z-index: 201;
|
||||
position: fixed;
|
||||
z-index: 1100;
|
||||
width: 320px;
|
||||
max-width: calc(100vw - 16px);
|
||||
background: var(--color-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 8px;
|
||||
@@ -5892,7 +5889,7 @@ body.has-player .chat-fab {
|
||||
}
|
||||
/* frimousse uses bare attributes (no data- prefix) */
|
||||
.emoji-picker-float [frimousse-viewport] {
|
||||
max-height: 220px;
|
||||
max-height: var(--float-max-h, 220px);
|
||||
/* frimousse already sets overflow-y: auto inline */
|
||||
}
|
||||
.emoji-picker-float [frimousse-category-header] {
|
||||
|
||||
Reference in New Issue
Block a user