diff --git a/src/components/room/widgets/avatar-info/infostand/InfoStandBadgeSlotView.tsx b/src/components/room/widgets/avatar-info/infostand/InfoStandBadgeSlotView.tsx index 267f535..c66609d 100644 --- a/src/components/room/widgets/avatar-info/infostand/InfoStandBadgeSlotView.tsx +++ b/src/components/room/widgets/avatar-info/infostand/InfoStandBadgeSlotView.tsx @@ -1,4 +1,5 @@ import { FC, useCallback, useEffect, useRef, useState } from 'react'; +import { createPortal } from 'react-dom'; import { FaPlus } from 'react-icons/fa'; import { GetConfigurationValue, LocalizeText } from '../../../../../api'; import { LayoutBadgeImageView } from '../../../../../common'; @@ -15,7 +16,8 @@ const BadgeMiniPicker: FC<{ onSelect: (badgeCode: string) => void; onClose: () => void; activeBadgeCodes: (string | null)[]; -}> = ({ onSelect, onClose, activeBadgeCodes }) => + position: { top: number; left: number }; +}> = ({ onSelect, onClose, activeBadgeCodes, position }) => { const { badgeCodes = [], requestBadges = null } = useInventoryBadges(); const ref = useRef(null); @@ -43,10 +45,11 @@ const BadgeMiniPicker: FC<{ return () => document.removeEventListener('mousedown', handleClickOutside); }, [ onClose ]); - return ( + return createPortal(
e.stopPropagation() }> ) } -
+ , + document.body ); }; @@ -83,7 +87,9 @@ export const InfoStandBadgeSlotView: FC = ({ slotIndex, const [ isDragOver, setIsDragOver ] = useState(false); const [ isDragging, setIsDragging ] = useState(false); const [ justDropped, setJustDropped ] = useState(false); - const [ showPicker, setShowPicker ] = useState(false); + const [ pickerPosition, setPickerPosition ] = useState<{ top: number; left: number } | null>(null); + const slotRef = useRef(null); + const showPicker = pickerPosition !== null; const hookInitialized = activeBadgeCodes.length > 0; @@ -152,9 +158,17 @@ export const InfoStandBadgeSlotView: FC = ({ slotIndex, const handleSlotClick = useCallback(() => { - if(!isOwnUser || badgeCode) return; + if(!isOwnUser || badgeCode || !slotRef.current) return; - setShowPicker(true); + const rect = slotRef.current.getBoundingClientRect(); + const pickerWidth = 180; + const gap = 8; + let left = rect.right + gap; + + if((left + pickerWidth) > (window.innerWidth - gap)) left = rect.left - pickerWidth - gap; + if(left < gap) left = gap; + + setPickerPosition({ top: rect.top, left }); }, [ isOwnUser, badgeCode ]); const handleDoubleClick = useCallback(() => @@ -167,12 +181,13 @@ export const InfoStandBadgeSlotView: FC = ({ slotIndex, const handlePickerSelect = useCallback((code: string) => { setBadgeAtSlot(code, slotIndex); - setShowPicker(false); + setPickerPosition(null); }, [ setBadgeAtSlot, slotIndex ]); return (
= ({ slotIndex, { showPicker && ( setShowPicker(false) } + onClose={ () => setPickerPosition(null) } onSelect={ handlePickerSelect } + position={ pickerPosition } /> ) }