@tamagui/input 1.143.1 → 2.0.0-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/Input.cjs +57 -122
- package/dist/cjs/Input.js +46 -115
- package/dist/cjs/Input.js.map +1 -1
- package/dist/cjs/Input.native.js +133 -71
- package/dist/cjs/Input.native.js.map +1 -1
- package/dist/cjs/TextArea.cjs +1 -1
- package/dist/cjs/TextArea.js +1 -1
- package/dist/cjs/TextArea.js.map +1 -1
- package/dist/cjs/TextArea.native.js +1 -1
- package/dist/cjs/TextArea.native.js.map +1 -1
- package/dist/cjs/v1/Input.cjs +190 -0
- package/dist/cjs/v1/Input.js +171 -0
- package/dist/cjs/v1/Input.js.map +6 -0
- package/dist/cjs/v1/Input.native.js +138 -0
- package/dist/cjs/v1/Input.native.js.map +1 -0
- package/dist/cjs/v1/TextArea.cjs +50 -0
- package/dist/cjs/v1/TextArea.js +43 -0
- package/dist/cjs/v1/TextArea.js.map +6 -0
- package/dist/cjs/v1/TextArea.native.js +53 -0
- package/dist/cjs/v1/TextArea.native.js.map +1 -0
- package/dist/cjs/v1/index.cjs +19 -0
- package/dist/cjs/v1/index.js +16 -0
- package/dist/cjs/v1/index.js.map +6 -0
- package/dist/cjs/v1/index.native.js +22 -0
- package/dist/cjs/v1/index.native.js.map +1 -0
- package/dist/cjs/v1/types.cjs +16 -0
- package/dist/cjs/v1/types.js +14 -0
- package/dist/cjs/v1/types.js.map +6 -0
- package/dist/cjs/v1/types.native.js +19 -0
- package/dist/cjs/v1/types.native.js.map +1 -0
- package/dist/esm/Input.js +47 -116
- package/dist/esm/Input.js.map +1 -1
- package/dist/esm/Input.mjs +58 -123
- package/dist/esm/Input.mjs.map +1 -1
- package/dist/esm/Input.native.js +134 -72
- package/dist/esm/Input.native.js.map +1 -1
- package/dist/esm/TextArea.js +1 -1
- package/dist/esm/TextArea.js.map +1 -1
- package/dist/esm/TextArea.mjs +1 -1
- package/dist/esm/TextArea.mjs.map +1 -1
- package/dist/esm/TextArea.native.js +1 -1
- package/dist/esm/TextArea.native.js.map +1 -1
- package/dist/esm/v1/Input.js +151 -0
- package/dist/esm/v1/Input.js.map +6 -0
- package/dist/esm/v1/Input.mjs +156 -0
- package/dist/esm/v1/Input.mjs.map +1 -0
- package/dist/esm/v1/Input.native.js +101 -0
- package/dist/esm/v1/Input.native.js.map +1 -0
- package/dist/esm/v1/TextArea.js +29 -0
- package/dist/esm/v1/TextArea.js.map +6 -0
- package/dist/esm/v1/TextArea.mjs +27 -0
- package/dist/esm/v1/TextArea.mjs.map +1 -0
- package/dist/esm/v1/TextArea.native.js +27 -0
- package/dist/esm/v1/TextArea.native.js.map +1 -0
- package/dist/esm/v1/index.js +3 -0
- package/dist/esm/v1/index.js.map +6 -0
- package/dist/esm/v1/index.mjs +3 -0
- package/dist/esm/v1/index.mjs.map +1 -0
- package/dist/esm/v1/index.native.js +3 -0
- package/dist/esm/v1/index.native.js.map +1 -0
- package/dist/esm/v1/types.js +1 -0
- package/dist/esm/v1/types.js.map +6 -0
- package/dist/esm/v1/types.mjs +2 -0
- package/dist/esm/v1/types.mjs.map +1 -0
- package/dist/esm/v1/types.native.js +2 -0
- package/dist/esm/v1/types.native.js.map +1 -0
- package/dist/jsx/Input.js +47 -116
- package/dist/jsx/Input.js.map +1 -1
- package/dist/jsx/Input.mjs +58 -123
- package/dist/jsx/Input.mjs.map +1 -1
- package/dist/jsx/Input.native.js +133 -71
- package/dist/jsx/Input.native.js.map +1 -1
- package/dist/jsx/TextArea.js +1 -1
- package/dist/jsx/TextArea.js.map +1 -1
- package/dist/jsx/TextArea.mjs +1 -1
- package/dist/jsx/TextArea.mjs.map +1 -1
- package/dist/jsx/TextArea.native.js +1 -1
- package/dist/jsx/TextArea.native.js.map +1 -1
- package/dist/jsx/v1/Input.js +151 -0
- package/dist/jsx/v1/Input.js.map +6 -0
- package/dist/jsx/v1/Input.mjs +156 -0
- package/dist/jsx/v1/Input.mjs.map +1 -0
- package/dist/jsx/v1/Input.native.js +138 -0
- package/dist/jsx/v1/Input.native.js.map +1 -0
- package/dist/jsx/v1/TextArea.js +29 -0
- package/dist/jsx/v1/TextArea.js.map +6 -0
- package/dist/jsx/v1/TextArea.mjs +27 -0
- package/dist/jsx/v1/TextArea.mjs.map +1 -0
- package/dist/jsx/v1/TextArea.native.js +53 -0
- package/dist/jsx/v1/TextArea.native.js.map +1 -0
- package/dist/jsx/v1/index.js +3 -0
- package/dist/jsx/v1/index.js.map +6 -0
- package/dist/jsx/v1/index.mjs +3 -0
- package/dist/jsx/v1/index.mjs.map +1 -0
- package/dist/jsx/v1/index.native.js +22 -0
- package/dist/jsx/v1/index.native.js.map +1 -0
- package/dist/jsx/v1/types.js +1 -0
- package/dist/jsx/v1/types.js.map +6 -0
- package/dist/jsx/v1/types.mjs +2 -0
- package/dist/jsx/v1/types.mjs.map +1 -0
- package/dist/jsx/v1/types.native.js +19 -0
- package/dist/jsx/v1/types.native.js.map +1 -0
- package/package.json +13 -13
- package/src/Input.native.tsx +145 -110
- package/src/Input.tsx +67 -137
- package/src/TextArea.tsx +4 -2
- package/src/index.ts +0 -1
- package/src/types.ts +52 -41
- package/src/v1/Input.native.tsx +154 -0
- package/src/v1/Input.tsx +191 -0
- package/src/v1/TextArea.tsx +35 -0
- package/src/v1/index.ts +3 -0
- package/src/v1/types.ts +65 -0
- package/types/Input.d.ts +47 -21
- package/types/Input.d.ts.map +1 -1
- package/types/Input.native.d.ts +52 -22
- package/types/Input.native.d.ts.map +1 -1
- package/types/TextArea.d.ts +25 -11
- package/types/TextArea.d.ts.map +1 -1
- package/types/index.d.ts +0 -1
- package/types/index.d.ts.map +1 -1
- package/types/types.d.ts +47 -21
- package/types/types.d.ts.map +1 -1
- package/types/v1/Input.d.ts +115 -0
- package/types/v1/Input.d.ts.map +1 -0
- package/types/v1/Input.native.d.ts +114 -0
- package/types/v1/Input.native.d.ts.map +1 -0
- package/types/v1/TextArea.d.ts +98 -0
- package/types/v1/TextArea.d.ts.map +1 -0
- package/types/v1/index.d.ts +4 -0
- package/types/v1/index.d.ts.map +1 -0
- package/types/v1/types.d.ts +39 -0
- package/types/v1/types.d.ts.map +1 -0
package/src/Input.native.tsx
CHANGED
|
@@ -1,149 +1,184 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import {
|
|
2
|
+
import { TextInput, type TextInputProps as RNTextInputProps } from 'react-native'
|
|
3
|
+
import { styled, useComposedRefs, useTheme, getTokenValue } from '@tamagui/core'
|
|
3
4
|
import { registerFocusable } from '@tamagui/focusable'
|
|
4
|
-
|
|
5
|
-
import type { NativeSyntheticEvent, TextInputChangeEventData } from 'react-native'
|
|
6
|
-
import { TextInput } from 'react-native'
|
|
7
5
|
import { styledBody } from './shared'
|
|
8
6
|
import type { InputProps } from './types'
|
|
7
|
+
|
|
9
8
|
const StyledInput = styled(TextInput, styledBody[0], styledBody[1])
|
|
10
9
|
|
|
11
|
-
|
|
10
|
+
/**
|
|
11
|
+
* A web-aligned input component for React Native.
|
|
12
|
+
* @see — Docs https://tamagui.dev/ui/inputs#input
|
|
13
|
+
*/
|
|
14
|
+
export const Input = StyledInput.styleable<InputProps>((props, forwardedRef) => {
|
|
12
15
|
const {
|
|
13
|
-
//
|
|
16
|
+
// Web props we need to convert
|
|
14
17
|
type,
|
|
15
|
-
|
|
18
|
+
disabled,
|
|
19
|
+
readOnly,
|
|
20
|
+
id,
|
|
21
|
+
rows,
|
|
22
|
+
autoComplete,
|
|
23
|
+
enterKeyHint,
|
|
24
|
+
|
|
25
|
+
// Tamagui color props
|
|
26
|
+
placeholderTextColor,
|
|
27
|
+
selectionColor,
|
|
28
|
+
|
|
29
|
+
// Callbacks
|
|
30
|
+
onChange,
|
|
31
|
+
onInput,
|
|
32
|
+
onKeyDown,
|
|
33
|
+
onChangeText,
|
|
34
|
+
onSubmitEditing,
|
|
35
|
+
onSelectionChange,
|
|
36
|
+
selection,
|
|
37
|
+
|
|
38
|
+
// Web-only props to filter out
|
|
39
|
+
// @ts-ignore
|
|
16
40
|
dirname,
|
|
17
|
-
max,
|
|
18
41
|
min,
|
|
42
|
+
max,
|
|
19
43
|
minLength,
|
|
20
44
|
multiple,
|
|
21
45
|
name,
|
|
46
|
+
pattern,
|
|
22
47
|
required,
|
|
23
48
|
step,
|
|
24
|
-
disabled,
|
|
25
|
-
id,
|
|
26
|
-
caretColor,
|
|
27
|
-
onChange,
|
|
28
|
-
onInput,
|
|
29
|
-
rows,
|
|
30
|
-
enterKeyHint,
|
|
31
|
-
returnKeyType,
|
|
32
|
-
onKeyDown,
|
|
33
|
-
inputMode,
|
|
34
49
|
tag,
|
|
35
|
-
...rest
|
|
36
|
-
} = inProps
|
|
37
50
|
|
|
38
|
-
|
|
51
|
+
...rest
|
|
52
|
+
} = props
|
|
39
53
|
|
|
54
|
+
const ref = React.useRef<any>(null)
|
|
55
|
+
const theme = useTheme()
|
|
40
56
|
const composedRefs = useComposedRefs<any>(forwardedRef, ref)
|
|
41
57
|
|
|
42
|
-
//
|
|
43
|
-
|
|
58
|
+
// Convert web type to native props
|
|
44
59
|
let secureTextEntry = false
|
|
45
|
-
let
|
|
46
|
-
let
|
|
47
|
-
let _enterKeyHint = enterKeyHint
|
|
48
|
-
if (enterKeyHint === 'go') {
|
|
49
|
-
_returnKeyType = 'go'
|
|
50
|
-
_enterKeyHint = undefined
|
|
51
|
-
}
|
|
60
|
+
let keyboardType: RNTextInputProps['keyboardType'] = 'default'
|
|
61
|
+
let inputMode: RNTextInputProps['inputMode'] = undefined
|
|
52
62
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
switch (type) {
|
|
64
|
+
case 'password':
|
|
65
|
+
secureTextEntry = true
|
|
66
|
+
break
|
|
67
|
+
case 'email':
|
|
68
|
+
keyboardType = 'email-address'
|
|
69
|
+
inputMode = 'email'
|
|
70
|
+
break
|
|
71
|
+
case 'tel':
|
|
72
|
+
keyboardType = 'phone-pad'
|
|
73
|
+
inputMode = 'tel'
|
|
74
|
+
break
|
|
75
|
+
case 'number':
|
|
76
|
+
keyboardType = 'numeric'
|
|
77
|
+
inputMode = 'numeric'
|
|
78
|
+
break
|
|
79
|
+
case 'url':
|
|
80
|
+
keyboardType = 'url'
|
|
81
|
+
inputMode = 'url'
|
|
82
|
+
break
|
|
83
|
+
case 'search':
|
|
84
|
+
inputMode = 'search'
|
|
85
|
+
break
|
|
69
86
|
}
|
|
70
87
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
88
|
+
// Convert enterKeyHint to returnKeyType
|
|
89
|
+
let returnKeyType: RNTextInputProps['returnKeyType'] = undefined
|
|
90
|
+
switch (enterKeyHint) {
|
|
91
|
+
case 'done':
|
|
92
|
+
returnKeyType = 'done'
|
|
93
|
+
break
|
|
94
|
+
case 'go':
|
|
95
|
+
returnKeyType = 'go'
|
|
96
|
+
break
|
|
97
|
+
case 'next':
|
|
98
|
+
returnKeyType = 'next'
|
|
99
|
+
break
|
|
100
|
+
case 'search':
|
|
101
|
+
returnKeyType = 'search'
|
|
102
|
+
break
|
|
103
|
+
case 'send':
|
|
104
|
+
returnKeyType = 'send'
|
|
105
|
+
break
|
|
74
106
|
}
|
|
75
107
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
108
|
+
// Resolve color tokens
|
|
109
|
+
const resolvedPlaceholderColor = placeholderTextColor
|
|
110
|
+
? getTokenValue(
|
|
111
|
+
theme[placeholderTextColor as any]?.val ?? placeholderTextColor,
|
|
112
|
+
'color'
|
|
113
|
+
)
|
|
114
|
+
: undefined
|
|
115
|
+
|
|
116
|
+
const resolvedSelectionColor = selectionColor
|
|
117
|
+
? getTokenValue(theme[selectionColor as any]?.val ?? selectionColor, 'color')
|
|
118
|
+
: undefined
|
|
119
|
+
|
|
120
|
+
// Register focusable
|
|
121
|
+
React.useEffect(() => {
|
|
122
|
+
if (!id || disabled) return
|
|
123
|
+
return registerFocusable(id, {
|
|
124
|
+
focusAndSelect: () => ref.current?.focus(),
|
|
125
|
+
focus: () => ref.current?.focus(),
|
|
126
|
+
})
|
|
127
|
+
}, [id, disabled])
|
|
88
128
|
|
|
89
|
-
|
|
90
|
-
|
|
129
|
+
// Handle web onChange/onInput -> native onChangeText
|
|
130
|
+
const handleChangeText = (text: string) => {
|
|
131
|
+
onChangeText?.(text)
|
|
132
|
+
if (onChange) {
|
|
133
|
+
onChange({ target: { value: text }, type: 'change' } as any)
|
|
134
|
+
}
|
|
135
|
+
if (onInput) {
|
|
136
|
+
onInput({ target: { value: text }, type: 'input' } as any)
|
|
137
|
+
}
|
|
91
138
|
}
|
|
92
139
|
|
|
93
|
-
|
|
94
|
-
|
|
140
|
+
// Handle onKeyDown via onKeyPress + onSubmitEditing
|
|
141
|
+
const handleKeyPress = (e: any) => {
|
|
142
|
+
if (onKeyDown) {
|
|
95
143
|
const { key } = e.nativeEvent
|
|
96
|
-
if (
|
|
97
|
-
key
|
|
98
|
-
(tag === 'textarea' && key === 'Enter') ||
|
|
99
|
-
key.length === 1
|
|
100
|
-
) {
|
|
101
|
-
onKeyDown({
|
|
102
|
-
key,
|
|
103
|
-
type: 'keydown',
|
|
104
|
-
} as any)
|
|
144
|
+
if (key === 'Backspace' || key === 'Enter' || key.length === 1) {
|
|
145
|
+
onKeyDown({ key, type: 'keydown' } as any)
|
|
105
146
|
}
|
|
106
147
|
}
|
|
107
|
-
finalProps.onSubmitEditing = (e) => {
|
|
108
|
-
onKeyDown({
|
|
109
|
-
key: 'Enter',
|
|
110
|
-
type: 'keydown',
|
|
111
|
-
} as any)
|
|
112
|
-
}
|
|
113
148
|
}
|
|
114
149
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
value: text,
|
|
122
|
-
},
|
|
123
|
-
type: 'change',
|
|
124
|
-
} as any)
|
|
125
|
-
}
|
|
126
|
-
if (onInput != null) {
|
|
127
|
-
onInput({
|
|
128
|
-
target: {
|
|
129
|
-
value: text,
|
|
130
|
-
},
|
|
131
|
-
type: 'input',
|
|
132
|
-
} as any)
|
|
133
|
-
}
|
|
150
|
+
const handleSubmitEditing = (e: any) => {
|
|
151
|
+
if (onKeyDown) {
|
|
152
|
+
onKeyDown({ key: 'Enter', type: 'keydown' } as any)
|
|
153
|
+
}
|
|
154
|
+
if (onSubmitEditing) {
|
|
155
|
+
onSubmitEditing(e)
|
|
134
156
|
}
|
|
135
157
|
}
|
|
136
158
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
159
|
+
// Handle selection change
|
|
160
|
+
const handleSelectionChange = (e: any) => {
|
|
161
|
+
onSelectionChange?.(e)
|
|
162
|
+
}
|
|
140
163
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
164
|
+
const finalProps: any = {
|
|
165
|
+
...rest,
|
|
166
|
+
editable: !disabled && !readOnly,
|
|
167
|
+
secureTextEntry,
|
|
168
|
+
keyboardType,
|
|
169
|
+
inputMode,
|
|
170
|
+
returnKeyType,
|
|
171
|
+
multiline: tag === 'textarea' || (rows && rows > 1),
|
|
172
|
+
numberOfLines: rows,
|
|
173
|
+
selection,
|
|
174
|
+
placeholderTextColor: resolvedPlaceholderColor,
|
|
175
|
+
selectionColor: resolvedSelectionColor,
|
|
176
|
+
onChangeText: handleChangeText,
|
|
177
|
+
onKeyPress: onKeyDown ? handleKeyPress : undefined,
|
|
178
|
+
onSubmitEditing: onKeyDown || onSubmitEditing ? handleSubmitEditing : undefined,
|
|
179
|
+
onSelectionChange: onSelectionChange ? handleSelectionChange : undefined,
|
|
180
|
+
autoComplete: autoComplete as any,
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return <StyledInput ref={composedRefs} {...finalProps} />
|
|
149
184
|
})
|
package/src/Input.tsx
CHANGED
|
@@ -1,148 +1,94 @@
|
|
|
1
|
-
import { View, styled, useComposedRefs,
|
|
1
|
+
import { View, styled, useComposedRefs, useTheme } from '@tamagui/core'
|
|
2
2
|
import { registerFocusable } from '@tamagui/focusable'
|
|
3
|
-
import React
|
|
3
|
+
import React from 'react'
|
|
4
4
|
import { styledBody } from './shared'
|
|
5
5
|
import type { InputProps } from './types'
|
|
6
6
|
|
|
7
7
|
const StyledInput = styled(View, styledBody[0], styledBody[1])
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* A web-aligned input component.
|
|
11
11
|
* @see — Docs https://tamagui.dev/ui/inputs#input
|
|
12
12
|
*/
|
|
13
|
-
export const Input = StyledInput.styleable<InputProps>((
|
|
13
|
+
export const Input = StyledInput.styleable<InputProps>((props, forwardedRef) => {
|
|
14
14
|
const {
|
|
15
|
-
// some of destructed props are just to avoid passing them to ...rest because they are not in web.
|
|
16
|
-
allowFontScaling,
|
|
17
|
-
selectTextOnFocus,
|
|
18
|
-
showSoftInputOnFocus,
|
|
19
|
-
textContentType,
|
|
20
|
-
passwordRules,
|
|
21
|
-
textBreakStrategy,
|
|
22
|
-
underlineColorAndroid,
|
|
23
|
-
selection,
|
|
24
|
-
lineBreakStrategyIOS,
|
|
25
|
-
returnKeyLabel,
|
|
26
15
|
disabled,
|
|
27
|
-
onSubmitEditing,
|
|
28
|
-
caretHidden,
|
|
29
|
-
clearButtonMode,
|
|
30
|
-
clearTextOnFocus,
|
|
31
|
-
contextMenuHidden,
|
|
32
|
-
dataDetectorTypes,
|
|
33
16
|
id,
|
|
34
|
-
enablesReturnKeyAutomatically,
|
|
35
|
-
importantForAutofill,
|
|
36
|
-
inlineImageLeft,
|
|
37
|
-
inlineImagePadding,
|
|
38
|
-
inputAccessoryViewID,
|
|
39
|
-
keyboardAppearance,
|
|
40
|
-
keyboardType,
|
|
41
|
-
cursorColor,
|
|
42
|
-
disableFullscreenUI,
|
|
43
|
-
editable,
|
|
44
|
-
maxFontSizeMultiplier,
|
|
45
|
-
multiline,
|
|
46
|
-
numberOfLines,
|
|
47
17
|
onChangeText,
|
|
48
|
-
|
|
49
|
-
onEndEditing,
|
|
50
|
-
onScroll,
|
|
18
|
+
onSubmitEditing,
|
|
51
19
|
onSelectionChange,
|
|
52
|
-
|
|
20
|
+
selection,
|
|
53
21
|
placeholderTextColor,
|
|
54
|
-
blurOnSubmit,
|
|
55
|
-
enterKeyHint,
|
|
56
|
-
returnKeyType,
|
|
57
|
-
rejectResponderTermination,
|
|
58
|
-
scrollEnabled,
|
|
59
|
-
secureTextEntry,
|
|
60
22
|
selectionColor,
|
|
61
|
-
|
|
23
|
+
rows,
|
|
62
24
|
...rest
|
|
63
|
-
} =
|
|
25
|
+
} = props
|
|
64
26
|
|
|
65
27
|
const ref = React.useRef<HTMLInputElement>(null)
|
|
66
28
|
const theme = useTheme()
|
|
67
|
-
|
|
68
29
|
const composedRefs = useComposedRefs(forwardedRef, ref)
|
|
69
30
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
31
|
+
// Handle selection changes
|
|
32
|
+
React.useEffect(() => {
|
|
33
|
+
if (!onSelectionChange) return
|
|
34
|
+
|
|
35
|
+
const node = ref.current
|
|
36
|
+
if (!node) return
|
|
37
|
+
|
|
38
|
+
const handleSelectionChange = () => {
|
|
39
|
+
onSelectionChange({
|
|
40
|
+
nativeEvent: {
|
|
41
|
+
selection: {
|
|
42
|
+
start: node.selectionStart ?? 0,
|
|
43
|
+
end: node.selectionEnd ?? 0,
|
|
44
|
+
},
|
|
78
45
|
},
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
})
|
|
46
|
+
})
|
|
47
|
+
}
|
|
82
48
|
|
|
49
|
+
node.addEventListener('select', handleSelectionChange)
|
|
50
|
+
return () => node.removeEventListener('select', handleSelectionChange)
|
|
51
|
+
}, [onSelectionChange])
|
|
52
|
+
|
|
53
|
+
// Sync selection prop
|
|
83
54
|
React.useEffect(() => {
|
|
84
|
-
if (
|
|
85
|
-
ref.current
|
|
86
|
-
return () => {
|
|
87
|
-
ref.current?.removeEventListener('selectionchange', _onSelectionChange)
|
|
88
|
-
}
|
|
55
|
+
if (selection && ref.current) {
|
|
56
|
+
ref.current.setSelectionRange(selection.start, selection.end ?? selection.start)
|
|
89
57
|
}
|
|
90
|
-
}, [])
|
|
58
|
+
}, [selection?.start, selection?.end])
|
|
91
59
|
|
|
60
|
+
// Register focusable
|
|
92
61
|
React.useEffect(() => {
|
|
93
|
-
if (
|
|
94
|
-
|
|
62
|
+
if (!id || disabled) return
|
|
63
|
+
return registerFocusable(id, {
|
|
64
|
+
focusAndSelect: () => ref.current?.focus(),
|
|
65
|
+
focus: () => ref.current?.focus(),
|
|
66
|
+
})
|
|
67
|
+
}, [id, disabled])
|
|
68
|
+
|
|
69
|
+
// Handle keyboard submit
|
|
70
|
+
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
71
|
+
if (e.key === 'Enter' && onSubmitEditing) {
|
|
72
|
+
onSubmitEditing({
|
|
73
|
+
nativeEvent: { text: (e.target as HTMLInputElement).value },
|
|
74
|
+
})
|
|
95
75
|
}
|
|
96
|
-
|
|
76
|
+
rest.onKeyDown?.(e)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Handle change with onChangeText support
|
|
80
|
+
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
81
|
+
onChangeText?.(e.target.value)
|
|
82
|
+
rest.onChange?.(e)
|
|
83
|
+
}
|
|
97
84
|
|
|
98
85
|
const finalProps = {
|
|
99
86
|
...rest,
|
|
100
87
|
disabled,
|
|
101
|
-
caretColor,
|
|
102
88
|
id,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
type: (() => {
|
|
107
|
-
if (rest?.type) return rest.type
|
|
108
|
-
if (secureTextEntry) return 'password'
|
|
109
|
-
switch (keyboardType) {
|
|
110
|
-
case 'number-pad':
|
|
111
|
-
case 'numeric':
|
|
112
|
-
return 'number'
|
|
113
|
-
case 'email-address':
|
|
114
|
-
return 'email'
|
|
115
|
-
case 'phone-pad':
|
|
116
|
-
return 'tel'
|
|
117
|
-
case 'url':
|
|
118
|
-
return 'url'
|
|
119
|
-
default:
|
|
120
|
-
return 'text'
|
|
121
|
-
}
|
|
122
|
-
})() satisfies HTMLInputTypeAttribute,
|
|
123
|
-
inputMode: (() => {
|
|
124
|
-
switch (keyboardType) {
|
|
125
|
-
case 'number-pad':
|
|
126
|
-
case 'numeric':
|
|
127
|
-
return 'numeric'
|
|
128
|
-
case 'decimal-pad':
|
|
129
|
-
return 'decimal'
|
|
130
|
-
case 'email-address':
|
|
131
|
-
return 'email'
|
|
132
|
-
case 'phone-pad':
|
|
133
|
-
return 'tel'
|
|
134
|
-
case 'url':
|
|
135
|
-
return 'url'
|
|
136
|
-
default:
|
|
137
|
-
return undefined
|
|
138
|
-
}
|
|
139
|
-
})() satisfies HTMLAttributes<HTMLInputElement>['inputMode'],
|
|
140
|
-
}
|
|
141
|
-
: {
|
|
142
|
-
keyboardType,
|
|
143
|
-
secureTextEntry,
|
|
144
|
-
inputMode,
|
|
145
|
-
}),
|
|
89
|
+
rows,
|
|
90
|
+
onKeyDown: onSubmitEditing ? handleKeyDown : rest.onKeyDown,
|
|
91
|
+
onChange: onChangeText ? handleChange : rest.onChange,
|
|
146
92
|
style: {
|
|
147
93
|
...(rest.style as any),
|
|
148
94
|
...(placeholderTextColor && {
|
|
@@ -155,34 +101,18 @@ export const Input = StyledInput.styleable<InputProps>((inProps, forwardedRef) =
|
|
|
155
101
|
},
|
|
156
102
|
} as any
|
|
157
103
|
|
|
158
|
-
React.useEffect(() => {
|
|
159
|
-
if (!id) return
|
|
160
|
-
if (disabled) return
|
|
161
|
-
|
|
162
|
-
return registerFocusable(id, {
|
|
163
|
-
focusAndSelect: () => {
|
|
164
|
-
ref.current?.focus()
|
|
165
|
-
},
|
|
166
|
-
focus: () => {},
|
|
167
|
-
})
|
|
168
|
-
}, [id, disabled])
|
|
169
|
-
|
|
170
104
|
return (
|
|
171
105
|
<>
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
{
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
`}
|
|
183
|
-
</style>
|
|
184
|
-
)}
|
|
185
|
-
|
|
106
|
+
<style>
|
|
107
|
+
{`
|
|
108
|
+
input::selection, textarea::selection {
|
|
109
|
+
background-color: var(--selectionColor) !important;
|
|
110
|
+
}
|
|
111
|
+
input::placeholder, textarea::placeholder {
|
|
112
|
+
color: var(--placeholderColor) !important;
|
|
113
|
+
}
|
|
114
|
+
`}
|
|
115
|
+
</style>
|
|
186
116
|
<StyledInput ref={composedRefs} {...finalProps} />
|
|
187
117
|
</>
|
|
188
118
|
)
|
package/src/TextArea.tsx
CHANGED
|
@@ -2,8 +2,10 @@ import { styled } from '@tamagui/web'
|
|
|
2
2
|
import { Input } from './Input'
|
|
3
3
|
import { defaultStyles, textAreaSizeVariant } from './shared'
|
|
4
4
|
|
|
5
|
+
export type { InputProps as TextAreaProps } from './types'
|
|
6
|
+
|
|
5
7
|
/**
|
|
6
|
-
*
|
|
8
|
+
* A web-aligned textarea component (multi-line input).
|
|
7
9
|
* @see — Docs https://tamagui.dev/ui/inputs#textarea
|
|
8
10
|
*/
|
|
9
11
|
export const TextArea = styled(Input, {
|
|
@@ -19,7 +21,7 @@ export const TextArea = styled(Input, {
|
|
|
19
21
|
false: {
|
|
20
22
|
height: 'auto',
|
|
21
23
|
...defaultStyles,
|
|
22
|
-
|
|
24
|
+
rows: 3,
|
|
23
25
|
},
|
|
24
26
|
},
|
|
25
27
|
|
package/src/index.ts
CHANGED