[Clipclops] Dynamic input height (#3778)
* input max height/scrollability * remove unused imports * add a web-specific version * enter and shift enter for web * missing onSubmit for native * missing attributes * improve layout of input on web * use the correct text color in the input * trim messages * remove `onSubmit` * move prop up * trim message on web * remove extra function call --------- Co-authored-by: Samuel Newman <mozzius@protonmail.com>
This commit is contained in:
parent
6f9993ca55
commit
333ccdad39
4 changed files with 165 additions and 10 deletions
|
@ -1,5 +1,16 @@
|
|||
import React from 'react'
|
||||
import {Pressable, TextInput, View} from 'react-native'
|
||||
import {
|
||||
Dimensions,
|
||||
Keyboard,
|
||||
NativeSyntheticEvent,
|
||||
Pressable,
|
||||
TextInput,
|
||||
TextInputContentSizeChangeEventData,
|
||||
View,
|
||||
} from 'react-native'
|
||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
@ -13,41 +24,67 @@ export function MessageInput({
|
|||
onFocus: () => void
|
||||
onBlur: () => void
|
||||
}) {
|
||||
const {_} = useLingui()
|
||||
const t = useTheme()
|
||||
const [message, setMessage] = React.useState('')
|
||||
const [maxHeight, setMaxHeight] = React.useState<number | undefined>()
|
||||
const [isInputScrollable, setIsInputScrollable] = React.useState(false)
|
||||
|
||||
const {top: topInset} = useSafeAreaInsets()
|
||||
|
||||
const inputRef = React.useRef<TextInput>(null)
|
||||
|
||||
const onSubmit = React.useCallback(() => {
|
||||
onSendMessage(message)
|
||||
if (message.trim() === '') {
|
||||
return
|
||||
}
|
||||
onSendMessage(message.trimEnd())
|
||||
setMessage('')
|
||||
setTimeout(() => {
|
||||
inputRef.current?.focus()
|
||||
}, 100)
|
||||
}, [message, onSendMessage])
|
||||
|
||||
const onInputLayout = React.useCallback(
|
||||
(e: NativeSyntheticEvent<TextInputContentSizeChangeEventData>) => {
|
||||
const keyboardHeight = Keyboard.metrics()?.height ?? 0
|
||||
const windowHeight = Dimensions.get('window').height
|
||||
|
||||
const max = windowHeight - keyboardHeight - topInset - 100
|
||||
const availableSpace = max - e.nativeEvent.contentSize.height
|
||||
|
||||
setMaxHeight(max)
|
||||
setIsInputScrollable(availableSpace < 30)
|
||||
},
|
||||
[topInset],
|
||||
)
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
a.flex_row,
|
||||
a.py_sm,
|
||||
a.px_sm,
|
||||
a.rounded_full,
|
||||
a.pl_md,
|
||||
a.mt_sm,
|
||||
t.atoms.bg_contrast_25,
|
||||
{borderRadius: 23},
|
||||
]}>
|
||||
<TextInput
|
||||
accessibilityLabel="Text input field"
|
||||
accessibilityHint="Write a message"
|
||||
accessibilityLabel={_(msg`Message input field`)}
|
||||
accessibilityHint={_(msg`Type your message here`)}
|
||||
placeholder={_(msg`Write a message`)}
|
||||
placeholderTextColor={t.palette.contrast_500}
|
||||
value={message}
|
||||
multiline={true}
|
||||
onChangeText={setMessage}
|
||||
placeholder="Write a message"
|
||||
style={[a.flex_1, a.text_sm, a.px_sm, t.atoms.text]}
|
||||
onSubmitEditing={onSubmit}
|
||||
style={[a.flex_1, a.text_md, a.px_sm, t.atoms.text, {maxHeight}]}
|
||||
keyboardAppearance={t.name === 'light' ? 'light' : 'dark'}
|
||||
scrollEnabled={isInputScrollable}
|
||||
blurOnSubmit={false}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
placeholderTextColor={t.palette.contrast_500}
|
||||
keyboardAppearance={t.name === 'light' ? 'light' : 'dark'}
|
||||
onContentSizeChange={onInputLayout}
|
||||
ref={inputRef}
|
||||
/>
|
||||
<Pressable
|
||||
|
|
91
src/screens/Messages/Conversation/MessageInput.web.tsx
Normal file
91
src/screens/Messages/Conversation/MessageInput.web.tsx
Normal file
|
@ -0,0 +1,91 @@
|
|||
import React from 'react'
|
||||
import {Pressable, StyleSheet, View} from 'react-native'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import TextareaAutosize from 'react-textarea-autosize'
|
||||
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
||||
export function MessageInput({
|
||||
onSendMessage,
|
||||
}: {
|
||||
onSendMessage: (message: string) => void
|
||||
onFocus: () => void
|
||||
onBlur: () => void
|
||||
}) {
|
||||
const {_} = useLingui()
|
||||
const t = useTheme()
|
||||
const [message, setMessage] = React.useState('')
|
||||
|
||||
const onSubmit = React.useCallback(() => {
|
||||
if (message.trim() === '') {
|
||||
return
|
||||
}
|
||||
onSendMessage(message.trimEnd())
|
||||
setMessage('')
|
||||
}, [message, onSendMessage])
|
||||
|
||||
const onKeyDown = React.useCallback(
|
||||
(e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
if (e.key === 'Enter') {
|
||||
if (e.shiftKey) return
|
||||
e.preventDefault()
|
||||
onSubmit()
|
||||
}
|
||||
},
|
||||
[onSubmit],
|
||||
)
|
||||
|
||||
const onChange = React.useCallback(
|
||||
(e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
setMessage(e.target.value)
|
||||
},
|
||||
[],
|
||||
)
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
a.flex_row,
|
||||
a.py_sm,
|
||||
a.px_sm,
|
||||
a.pl_md,
|
||||
a.mt_sm,
|
||||
t.atoms.bg_contrast_25,
|
||||
{borderRadius: 23},
|
||||
]}>
|
||||
<TextareaAutosize
|
||||
style={StyleSheet.flatten([
|
||||
a.flex_1,
|
||||
a.px_sm,
|
||||
a.border_0,
|
||||
t.atoms.text,
|
||||
{
|
||||
backgroundColor: 'transparent',
|
||||
resize: 'none',
|
||||
paddingTop: 6,
|
||||
},
|
||||
])}
|
||||
maxRows={12}
|
||||
placeholder={_(msg`Write a message`)}
|
||||
defaultValue=""
|
||||
value={message}
|
||||
dirName="ltr"
|
||||
autoFocus={true}
|
||||
onChange={onChange}
|
||||
onKeyDown={onKeyDown}
|
||||
/>
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
style={[
|
||||
a.rounded_full,
|
||||
a.align_center,
|
||||
a.justify_center,
|
||||
{height: 30, width: 30, backgroundColor: t.palette.primary_500},
|
||||
]}>
|
||||
<Text style={a.text_md}>🐴</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue