diff --git a/app/soapbox/features/emoji/components/emoji-picker-dropdown.tsx b/app/soapbox/features/emoji/components/emoji-picker-dropdown.tsx index 7e6bd2414..3f486502e 100644 --- a/app/soapbox/features/emoji/components/emoji-picker-dropdown.tsx +++ b/app/soapbox/features/emoji/components/emoji-picker-dropdown.tsx @@ -11,7 +11,6 @@ import { RootState } from 'soapbox/store'; import { buildCustomEmojis } from '../../emoji'; import { EmojiPicker as EmojiPickerAsync } from '../../ui/util/async-components'; -import type { State as PopperState } from '@popperjs/core'; import type { Emoji, CustomEmoji, NativeEmoji } from 'soapbox/features/emoji'; let EmojiPicker: any; // load asynchronously @@ -49,7 +48,7 @@ export interface IEmojiPickerDropdown { withCustom?: boolean visible: boolean setVisible: (value: boolean) => void - update: (() => Promise>) | null + update: (() => any) | null } const perLine = 8; diff --git a/app/soapbox/features/emoji/containers/emoji-picker-dropdown-container.tsx b/app/soapbox/features/emoji/containers/emoji-picker-dropdown-container.tsx index 47dea8d6f..5929d1711 100644 --- a/app/soapbox/features/emoji/containers/emoji-picker-dropdown-container.tsx +++ b/app/soapbox/features/emoji/containers/emoji-picker-dropdown-container.tsx @@ -1,17 +1,14 @@ +import { useFloating, shift } from '@floating-ui/react'; import clsx from 'clsx'; -import { supportsPassiveEvents } from 'detect-passive-events'; -import React, { KeyboardEvent, useEffect, useState } from 'react'; +import React, { KeyboardEvent, useState } from 'react'; import { createPortal } from 'react-dom'; import { defineMessages, useIntl } from 'react-intl'; -import { usePopper } from 'react-popper'; import { IconButton } from 'soapbox/components/ui'; -import { isMobile } from 'soapbox/is-mobile'; +import { useClickOutside } from 'soapbox/hooks'; import EmojiPickerDropdown, { IEmojiPickerDropdown } from '../components/emoji-picker-dropdown'; -const listenerOptions = supportsPassiveEvents ? { passive: true } : false; - export const messages = defineMessages({ emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' }, }); @@ -22,51 +19,28 @@ const EmojiPickerDropdownContainer = ( const intl = useIntl(); const title = intl.formatMessage(messages.emoji); - const [popperElement, setPopperElement] = useState(null); - const [popperReference, setPopperReference] = useState(null); - const [containerElement, setContainerElement] = useState(null); - const [visible, setVisible] = useState(false); - const placement = props.condensed ? 'bottom-start' : 'top-start'; - const { styles, attributes, update } = usePopper(popperReference, popperElement, { - placement: isMobile(window.innerWidth) ? 'auto' : placement, + const { x, y, strategy, refs, update } = useFloating({ + middleware: [shift()], }); - const handleDocClick = (e: any) => { - if (!containerElement?.contains(e.target) && !popperElement?.contains(e.target)) { - setVisible(false); - } - }; + useClickOutside(refs, () => { + setVisible(false); + }); const handleToggle = (e: MouseEvent | KeyboardEvent) => { e.stopPropagation(); setVisible(!visible); }; - // TODO: move to class - const style: React.CSSProperties = !isMobile(window.innerWidth) ? styles.popper : { - ...styles.popper, width: '100%', - }; - - useEffect(() => { - document.addEventListener('click', handleDocClick, false); - document.addEventListener('touchend', handleDocClick, listenerOptions); - - return () => { - document.removeEventListener('click', handleDocClick, false); - // @ts-ignore - document.removeEventListener('touchend', handleDocClick, listenerOptions); - }; - }); - return ( -
+
- +
, document.body, )}