import * as React from 'react'; import { createPortal } from 'react-dom'; import { SortableHandle } from 'react-sortable-hoc'; import { noop } from 'lodash'; import { Manager as PopperManager, Popper, Reference as PopperReference, } from 'react-popper'; import { AddEmoji } from '../elements/icons'; import { DropZone, Props as DropZoneProps } from '../elements/DropZone'; import { StickerPreview } from '../elements/StickerPreview'; import * as styles from './StickerFrame.scss'; import { EmojiPickDataType, EmojiPicker, Props as EmojiPickerProps, } from '../../ts/components/emoji/EmojiPicker'; import { Emoji } from '../../ts/components/emoji/Emoji'; import { PopperRootContext } from '../../ts/components/PopperRootContext'; import { useI18n } from '../util/i18n'; export type Mode = 'removable' | 'pick-emoji' | 'add'; export type Props = Partial< Pick > & Partial> & { readonly id?: string; readonly emojiData?: EmojiPickDataType; readonly image?: string; readonly mode?: Mode; readonly showGuide?: boolean; onPickEmoji?({ id, emoji, }: { id: string; emoji: EmojiPickDataType; }): unknown; onRemove?(id: string): unknown; }; const spinnerSvg = ( ); const closeSvg = ( ); const ImageHandle = SortableHandle((props: { src: string }) => ( Sticker )); export const StickerFrame = React.memo( // tslint:disable-next-line max-func-body-length ({ id, emojiData, image, showGuide, mode, onRemove, onPickEmoji, skinTone, onSetSkinTone, onDrop, }: Props) => { const i18n = useI18n(); const [emojiPickerOpen, setEmojiPickerOpen] = React.useState(false); const [ emojiPopperRoot, setEmojiPopperRoot, ] = React.useState(null); const [previewActive, setPreviewActive] = React.useState(false); const [ previewPopperRoot, setPreviewPopperRoot, ] = React.useState(null); const timerRef = React.useRef(); const handleToggleEmojiPicker = React.useCallback(() => { setEmojiPickerOpen(open => !open); }, [setEmojiPickerOpen]); const handlePickEmoji = React.useCallback( (emoji: EmojiPickDataType) => { onPickEmoji({ id, emoji }); setEmojiPickerOpen(false); }, [id, onPickEmoji, setEmojiPickerOpen] ); const handleRemove = React.useCallback(() => { onRemove(id); }, [onRemove, id]); const handleMouseEnter = React.useCallback(() => { window.clearTimeout(timerRef.current); timerRef.current = window.setTimeout(() => { setPreviewActive(true); }, 500); }, [timerRef, setPreviewActive]); const handleMouseLeave = React.useCallback(() => { clearTimeout(timerRef.current); setPreviewActive(false); }, [timerRef, setPreviewActive]); React.useEffect( () => () => { clearTimeout(timerRef.current); }, [timerRef] ); const { createRoot, removeRoot } = React.useContext(PopperRootContext); // Create popper root and handle outside clicks React.useEffect(() => { if (emojiPickerOpen) { const root = createRoot(); setEmojiPopperRoot(root); const handleOutsideClick = ({ target }: MouseEvent) => { if (!root.contains(target as Node)) { setEmojiPickerOpen(false); } }; document.addEventListener('click', handleOutsideClick); return () => { removeRoot(root); document.removeEventListener('click', handleOutsideClick); }; } return noop; }, [ createRoot, emojiPickerOpen, removeRoot, setEmojiPickerOpen, setEmojiPopperRoot, ]); React.useEffect(() => { if (mode !== 'pick-emoji' && image && previewActive) { const root = createRoot(); setPreviewPopperRoot(root); return () => { removeRoot(root); }; } return noop; }, [ createRoot, image, mode, previewActive, removeRoot, setPreviewPopperRoot, ]); const [dragActive, setDragActive] = React.useState(false); const containerClass = dragActive ? styles.dragActive : styles.container; return ( {({ ref: rootRef }) => (
{// eslint-disable-next-line no-nested-ternary mode !== 'add' ? ( image ? ( ) : (
{spinnerSvg}
) ) : null} {showGuide && mode !== 'add' ? (
) : null} {mode === 'add' && onDrop ? ( ) : null} {mode === 'removable' ? ( ) : null} {mode === 'pick-emoji' ? ( {({ ref }) => ( )} {emojiPickerOpen && emojiPopperRoot ? createPortal( {({ ref, style }) => ( )} , emojiPopperRoot ) : null} ) : null} {mode !== 'pick-emoji' && image && previewActive && previewPopperRoot ? createPortal( {({ ref, style, arrowProps, placement }) => ( )} , previewPopperRoot ) : null}
)} ); } );