diff options
Diffstat (limited to 'src/view/com/composer/text-input/web/EmojiPicker.web.tsx')
-rw-r--r-- | src/view/com/composer/text-input/web/EmojiPicker.web.tsx | 140 |
1 files changed, 83 insertions, 57 deletions
diff --git a/src/view/com/composer/text-input/web/EmojiPicker.web.tsx b/src/view/com/composer/text-input/web/EmojiPicker.web.tsx index f4b2d99b0..6d16403ff 100644 --- a/src/view/com/composer/text-input/web/EmojiPicker.web.tsx +++ b/src/view/com/composer/text-input/web/EmojiPicker.web.tsx @@ -1,11 +1,17 @@ import React from 'react' import Picker from '@emoji-mart/react' -import {StyleSheet, TouchableWithoutFeedback, View} from 'react-native' -import * as DropdownMenu from '@radix-ui/react-dropdown-menu' +import { + StyleSheet, + TouchableWithoutFeedback, + useWindowDimensions, + View, +} from 'react-native' import {textInputWebEmitter} from '../TextInput.web' -import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' -import {usePalette} from 'lib/hooks/usePalette' -import {useMediaQuery} from 'react-responsive' + +const HEIGHT_OFFSET = 40 +const WIDTH_OFFSET = 100 +const PICKER_HEIGHT = 435 + HEIGHT_OFFSET +const PICKER_WIDTH = 350 + WIDTH_OFFSET export type Emoji = { aliases?: string[] @@ -18,59 +24,87 @@ export type Emoji = { unified: string } -export function EmojiPickerButton() { - const pal = usePalette('default') - const [open, setOpen] = React.useState(false) - const onOpenChange = (o: boolean) => { - setOpen(o) - } - const close = () => { - setOpen(false) - } +export interface EmojiPickerState { + isOpen: boolean + pos: {top: number; left: number; right: number; bottom: number} +} - return ( - <DropdownMenu.Root open={open} onOpenChange={onOpenChange}> - <DropdownMenu.Trigger style={styles.trigger}> - <FontAwesomeIcon - icon={['far', 'face-smile']} - color={pal.colors.link} - size={22} - /> - </DropdownMenu.Trigger> - - <DropdownMenu.Portal> - <EmojiPicker close={close} /> - </DropdownMenu.Portal> - </DropdownMenu.Root> - ) +interface IProps { + state: EmojiPickerState + close: () => void } -export function EmojiPicker({close}: {close: () => void}) { +export function EmojiPicker({state, close}: IProps) { + const {height, width} = useWindowDimensions() + + const isShiftDown = React.useRef(false) + + const position = React.useMemo(() => { + const fitsBelow = state.pos.top + PICKER_HEIGHT < height + const fitsAbove = PICKER_HEIGHT < state.pos.top + const placeOnLeft = PICKER_WIDTH < state.pos.left + const screenYMiddle = height / 2 - PICKER_HEIGHT / 2 + + if (fitsBelow) { + return { + top: state.pos.top + HEIGHT_OFFSET, + } + } else if (fitsAbove) { + return { + bottom: height - state.pos.bottom + HEIGHT_OFFSET, + } + } else { + return { + top: screenYMiddle, + left: placeOnLeft ? state.pos.left - PICKER_WIDTH : undefined, + right: !placeOnLeft + ? width - state.pos.right - PICKER_WIDTH + : undefined, + } + } + }, [state.pos, height, width]) + + React.useEffect(() => { + if (!state.isOpen) return + + const onKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Shift') { + isShiftDown.current = true + } + } + const onKeyUp = (e: KeyboardEvent) => { + if (e.key === 'Shift') { + isShiftDown.current = false + } + } + window.addEventListener('keydown', onKeyDown, true) + window.addEventListener('keyup', onKeyUp, true) + + return () => { + window.removeEventListener('keydown', onKeyDown, true) + window.removeEventListener('keyup', onKeyUp, true) + } + }, [state.isOpen]) + const onInsert = (emoji: Emoji) => { textInputWebEmitter.emit('emoji-inserted', emoji) - close() + + if (!isShiftDown.current) { + close() + } } - const reducedPadding = useMediaQuery({query: '(max-height: 750px)'}) - const noPadding = useMediaQuery({query: '(max-height: 550px)'}) - const noPicker = useMediaQuery({query: '(max-height: 350px)'}) + + if (!state.isOpen) return null return ( - // eslint-disable-next-line react-native-a11y/has-valid-accessibility-descriptors - <TouchableWithoutFeedback onPress={close} accessibilityViewIsModal> + <TouchableWithoutFeedback + accessibilityRole="button" + onPress={close} + accessibilityViewIsModal> <View style={styles.mask}> {/* eslint-disable-next-line react-native-a11y/has-valid-accessibility-descriptors */} - <TouchableWithoutFeedback - onPress={e => { - e.stopPropagation() // prevent event from bubbling up to the mask - }}> - <View - style={[ - styles.picker, - { - paddingTop: noPadding ? 0 : reducedPadding ? 150 : 325, - display: noPicker ? 'none' : 'flex', - }, - ]}> + <TouchableWithoutFeedback onPress={e => e.stopPropagation()}> + <View style={[{position: 'absolute'}, position]}> <Picker data={async () => { return (await import('./EmojiPickerData.json')).default @@ -93,15 +127,7 @@ const styles = StyleSheet.create({ right: 0, width: '100%', height: '100%', - }, - trigger: { - backgroundColor: 'transparent', - // @ts-ignore web only -prf - border: 'none', - paddingTop: 4, - paddingLeft: 12, - paddingRight: 12, - cursor: 'pointer', + alignItems: 'center', }, picker: { marginHorizontal: 'auto', |