Close web mention suggestions popup on Escape (#8605)
* alf web typeahead * fix type error * fix escape behaviour * change selection on hover * rm React. * undo random change
This commit is contained in:
@@ -1,4 +1,11 @@
|
||||
import React, {useRef} from 'react'
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import {StyleSheet, View} from 'react-native'
|
||||
import Animated, {FadeIn, FadeOut} from 'react-native-reanimated'
|
||||
import {AppBskyRichtextFacet, RichText} from '@atproto/api'
|
||||
@@ -16,7 +23,6 @@ import {EditorContent, type JSONContent, useEditor} from '@tiptap/react'
|
||||
import Graphemer from 'graphemer'
|
||||
|
||||
import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle'
|
||||
import {usePalette} from '#/lib/hooks/usePalette'
|
||||
import {blobToDataUri, isUriImage} from '#/lib/media/util'
|
||||
import {useActorAutocompleteFn} from '#/state/queries/actor-autocomplete'
|
||||
import {
|
||||
@@ -27,57 +33,34 @@ import {textInputWebEmitter} from '#/view/com/composer/text-input/textInputWebEm
|
||||
import {atoms as a, useAlf} from '#/alf'
|
||||
import {normalizeTextStyles} from '#/alf/typography'
|
||||
import {Portal} from '#/components/Portal'
|
||||
import {Text} from '../../util/text/Text'
|
||||
import {createSuggestion} from './web/Autocomplete'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {type TextInputProps} from './TextInput.types'
|
||||
import {type AutocompleteRef, createSuggestion} from './web/Autocomplete'
|
||||
import {type Emoji} from './web/EmojiPicker'
|
||||
import {LinkDecorator} from './web/LinkDecorator'
|
||||
import {TagDecorator} from './web/TagDecorator'
|
||||
|
||||
export interface TextInputRef {
|
||||
focus: () => void
|
||||
blur: () => void
|
||||
getCursorPosition: () => DOMRect | undefined
|
||||
}
|
||||
|
||||
interface TextInputProps {
|
||||
richtext: RichText
|
||||
placeholder: string
|
||||
suggestedLinks: Set<string>
|
||||
webForceMinHeight: boolean
|
||||
hasRightPadding: boolean
|
||||
isActive: boolean
|
||||
setRichText: (v: RichText | ((v: RichText) => RichText)) => void
|
||||
onPhotoPasted: (uri: string) => void
|
||||
onPressPublish: (richtext: RichText) => void
|
||||
onNewLink: (uri: string) => void
|
||||
onError: (err: string) => void
|
||||
onFocus: () => void
|
||||
}
|
||||
|
||||
export const TextInput = React.forwardRef(function TextInputImpl(
|
||||
{
|
||||
richtext,
|
||||
placeholder,
|
||||
webForceMinHeight,
|
||||
hasRightPadding,
|
||||
isActive,
|
||||
setRichText,
|
||||
onPhotoPasted,
|
||||
onPressPublish,
|
||||
onNewLink,
|
||||
onFocus,
|
||||
}: // onError, TODO
|
||||
TextInputProps,
|
||||
export function TextInput({
|
||||
ref,
|
||||
) {
|
||||
richtext,
|
||||
placeholder,
|
||||
webForceMinHeight,
|
||||
hasRightPadding,
|
||||
isActive,
|
||||
setRichText,
|
||||
onPhotoPasted,
|
||||
onPressPublish,
|
||||
onNewLink,
|
||||
onFocus,
|
||||
}: TextInputProps) {
|
||||
const {theme: t, fonts} = useAlf()
|
||||
const autocomplete = useActorAutocompleteFn()
|
||||
const pal = usePalette('default')
|
||||
const modeClass = useColorSchemeStyle('ProseMirror-light', 'ProseMirror-dark')
|
||||
|
||||
const [isDropping, setIsDropping] = React.useState(false)
|
||||
const [isDropping, setIsDropping] = useState(false)
|
||||
const autocompleteRef = useRef<AutocompleteRef>(null)
|
||||
|
||||
const extensions = React.useMemo(
|
||||
const extensions = useMemo(
|
||||
() => [
|
||||
Document,
|
||||
LinkDecorator,
|
||||
@@ -86,7 +69,7 @@ export const TextInput = React.forwardRef(function TextInputImpl(
|
||||
HTMLAttributes: {
|
||||
class: 'mention',
|
||||
},
|
||||
suggestion: createSuggestion({autocomplete}),
|
||||
suggestion: createSuggestion({autocomplete, autocompleteRef}),
|
||||
}),
|
||||
Paragraph,
|
||||
Placeholder.configure({
|
||||
@@ -99,7 +82,7 @@ export const TextInput = React.forwardRef(function TextInputImpl(
|
||||
[autocomplete, placeholder],
|
||||
)
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (!isActive) {
|
||||
return
|
||||
}
|
||||
@@ -109,7 +92,7 @@ export const TextInput = React.forwardRef(function TextInputImpl(
|
||||
}
|
||||
}, [onPressPublish, isActive])
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (!isActive) {
|
||||
return
|
||||
}
|
||||
@@ -119,7 +102,7 @@ export const TextInput = React.forwardRef(function TextInputImpl(
|
||||
}
|
||||
}, [isActive, onPhotoPasted])
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (!isActive) {
|
||||
return
|
||||
}
|
||||
@@ -296,13 +279,13 @@ export const TextInput = React.forwardRef(function TextInputImpl(
|
||||
[modeClass],
|
||||
)
|
||||
|
||||
const onEmojiInserted = React.useCallback(
|
||||
const onEmojiInserted = useCallback(
|
||||
(emoji: Emoji) => {
|
||||
editor?.chain().focus().insertContent(emoji.native).run()
|
||||
},
|
||||
[editor],
|
||||
)
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (!isActive) {
|
||||
return
|
||||
}
|
||||
@@ -312,7 +295,7 @@ export const TextInput = React.forwardRef(function TextInputImpl(
|
||||
}
|
||||
}, [onEmojiInserted, isActive])
|
||||
|
||||
React.useImperativeHandle(ref, () => ({
|
||||
useImperativeHandle(ref, () => ({
|
||||
focus: () => {
|
||||
editor?.chain().focus()
|
||||
},
|
||||
@@ -323,9 +306,10 @@ export const TextInput = React.forwardRef(function TextInputImpl(
|
||||
const pos = editor?.state.selection.$anchor.pos
|
||||
return pos ? editor?.view.coordsAtPos(pos) : undefined
|
||||
},
|
||||
maybeClosePopup: () => autocompleteRef.current?.maybeClose() ?? false,
|
||||
}))
|
||||
|
||||
const inputStyle = React.useMemo(() => {
|
||||
const inputStyle = useMemo(() => {
|
||||
const style = normalizeTextStyles(
|
||||
[a.text_lg, a.leading_snug, t.atoms.text],
|
||||
{
|
||||
@@ -360,10 +344,20 @@ export const TextInput = React.forwardRef(function TextInputImpl(
|
||||
style={styles.dropContainer}
|
||||
entering={FadeIn.duration(80)}
|
||||
exiting={FadeOut.duration(80)}>
|
||||
<View style={[pal.view, pal.border, styles.dropModal]}>
|
||||
<View
|
||||
style={[
|
||||
t.atoms.bg,
|
||||
t.atoms.border_contrast_low,
|
||||
styles.dropModal,
|
||||
]}>
|
||||
<Text
|
||||
type="lg"
|
||||
style={[pal.text, pal.borderDark, styles.dropText]}>
|
||||
style={[
|
||||
a.text_lg,
|
||||
a.font_bold,
|
||||
t.atoms.text_contrast_medium,
|
||||
t.atoms.border_contrast_high,
|
||||
styles.dropText,
|
||||
]}>
|
||||
<Trans>Drop to add images</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
@@ -372,7 +366,7 @@ export const TextInput = React.forwardRef(function TextInputImpl(
|
||||
)}
|
||||
</>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
function editorJsonToText(
|
||||
json: JSONContent,
|
||||
|
||||
Reference in New Issue
Block a user