Private
Public Access
Flip reaction emoji picker upward when it won't fit below
Reacting to a message near the bottom of the scrolled list opened a picker that ran off-screen and couldn't be used -- placement was hardcoded to "below" regardless of the trigger's actual position. Now computed per-click from the trigger's bounding rect against available viewport space, matching the composer's own picker (which was already positioned dynamically, just always "above" since it's pinned to the bottom of the screen). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,11 @@ interface EmojiPickerProps {
|
|||||||
align?: 'left' | 'right'
|
align?: 'left' | 'right'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Kept in sync with .emoji-picker's max-height in EmojiPicker.css -- callers
|
||||||
|
// that compute placement dynamically (flipping above/below based on
|
||||||
|
// available viewport space) need this to know how much room to check for.
|
||||||
|
export const EMOJI_PICKER_MAX_HEIGHT = 380
|
||||||
|
|
||||||
function searchEmoji(query: string): string[] {
|
function searchEmoji(query: string): string[] {
|
||||||
const q = query.trim().toLowerCase()
|
const q = query.trim().toLowerCase()
|
||||||
if (!q) return []
|
if (!q) return []
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { getRoomFileUrl, getRoomImageUrl } from '../api/rooms'
|
|||||||
import { useAuth } from '../context/AuthContext'
|
import { useAuth } from '../context/AuthContext'
|
||||||
import { avatarUrlFor, displayNameFor, senderColorIndex } from '../lib/messageGrouping'
|
import { avatarUrlFor, displayNameFor, senderColorIndex } from '../lib/messageGrouping'
|
||||||
import type { ChatMessageEnvelope, Message, MessageFileInfo, RoomMember } from '../types'
|
import type { ChatMessageEnvelope, Message, MessageFileInfo, RoomMember } from '../types'
|
||||||
import { EmojiPicker } from './EmojiPicker'
|
import { EMOJI_PICKER_MAX_HEIGHT, EmojiPicker } from './EmojiPicker'
|
||||||
import { FilePreviewModal, getPreviewKind } from './FilePreviewModal'
|
import { FilePreviewModal, getPreviewKind } from './FilePreviewModal'
|
||||||
import { ImageLightbox } from './ImageLightbox'
|
import { ImageLightbox } from './ImageLightbox'
|
||||||
import { MessageContent } from './MessageContent'
|
import { MessageContent } from './MessageContent'
|
||||||
@@ -79,6 +79,7 @@ export function MessageList({ roomId, messages, members, onEdit, onReact }: Mess
|
|||||||
const [draft, setDraft] = useState('')
|
const [draft, setDraft] = useState('')
|
||||||
const [lightboxSrc, setLightboxSrc] = useState<string | null>(null)
|
const [lightboxSrc, setLightboxSrc] = useState<string | null>(null)
|
||||||
const [reactingId, setReactingId] = useState<string | null>(null)
|
const [reactingId, setReactingId] = useState<string | null>(null)
|
||||||
|
const [reactionPlacement, setReactionPlacement] = useState<'above' | 'below'>('below')
|
||||||
const [previewFile, setPreviewFile] = useState<MessageFileInfo | null>(null)
|
const [previewFile, setPreviewFile] = useState<MessageFileInfo | null>(null)
|
||||||
|
|
||||||
function displayNameForUserId(userId: string): string {
|
function displayNameForUserId(userId: string): string {
|
||||||
@@ -200,7 +201,20 @@ export function MessageList({ roomId, messages, members, onEdit, onReact }: Mess
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="message-reaction-trigger"
|
className="message-reaction-trigger"
|
||||||
onClick={() => setReactingId(reactingId === msg.id ? null : msg.id)}
|
onClick={(e) => {
|
||||||
|
if (reactingId === msg.id) {
|
||||||
|
setReactingId(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Flip upward when the picker wouldn't fit below the
|
||||||
|
// trigger -- a message near the bottom of the
|
||||||
|
// scrolled list otherwise opens a picker that runs
|
||||||
|
// off-screen and can't be used.
|
||||||
|
const rect = e.currentTarget.getBoundingClientRect()
|
||||||
|
const spaceBelow = window.innerHeight - rect.bottom
|
||||||
|
setReactionPlacement(spaceBelow < EMOJI_PICKER_MAX_HEIGHT ? 'above' : 'below')
|
||||||
|
setReactingId(msg.id)
|
||||||
|
}}
|
||||||
aria-label="Add reaction"
|
aria-label="Add reaction"
|
||||||
>
|
>
|
||||||
🙂
|
🙂
|
||||||
@@ -212,7 +226,7 @@ export function MessageList({ roomId, messages, members, onEdit, onReact }: Mess
|
|||||||
setReactingId(null)
|
setReactingId(null)
|
||||||
}}
|
}}
|
||||||
onClose={() => setReactingId(null)}
|
onClose={() => setReactingId(null)}
|
||||||
placement="below"
|
placement={reactionPlacement}
|
||||||
align="right"
|
align="right"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user