@startupjs-ui/text-input 0.1.3
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/CHANGELOG.md +26 -0
- package/README.mdx +165 -0
- package/index.cssx.styl +111 -0
- package/index.d.ts +58 -0
- package/index.mdx.cssx.styl +2 -0
- package/index.tsx +263 -0
- package/package.json +22 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
+
|
|
6
|
+
## [0.1.3](https://github.com/startupjs/startupjs-ui/compare/v0.1.2...v0.1.3) (2025-12-29)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @startupjs-ui/text-input
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## [0.1.2](https://github.com/startupjs/startupjs-ui/compare/v0.1.1...v0.1.2) (2025-12-29)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
* **text-input:** increase padding for 'm' size ([b3b6836](https://github.com/startupjs/startupjs-ui/commit/b3b6836ffea7f26d73851fbee3e6c6f0c53a81d3))
|
|
20
|
+
* **text-input:** set default font ([a77a27b](https://github.com/startupjs/startupjs-ui/commit/a77a27bbe570537d0f95784aad9e2b58627a6b0f))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Features
|
|
24
|
+
|
|
25
|
+
* add mdx and docs packages. Refactor docs to get rid of any @startupjs/ui usage and use startupjs-ui instead ([703c926](https://github.com/startupjs/startupjs-ui/commit/703c92636efb0421ffd11783f692fc892b74018f))
|
|
26
|
+
* **text-input:** refactor TextInput component ([d0c7d64](https://github.com/startupjs/startupjs-ui/commit/d0c7d64233e575227c1764454c336735e203a95b))
|
package/README.mdx
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import TextInput, { _PropsJsonSchema as TextInputPropsJsonSchema } from './index'
|
|
3
|
+
import Div from '@startupjs-ui/div'
|
|
4
|
+
import Br from '@startupjs-ui/br'
|
|
5
|
+
import { faSearch, faTimesCircle } from '@fortawesome/free-solid-svg-icons'
|
|
6
|
+
import { Sandbox } from '@startupjs-ui/docs'
|
|
7
|
+
import './index.mdx.cssx.styl'
|
|
8
|
+
|
|
9
|
+
# TextInput
|
|
10
|
+
|
|
11
|
+
TextInput lets user enter or edit text.
|
|
12
|
+
|
|
13
|
+
```jsx
|
|
14
|
+
import { TextInput } from 'startupjs-ui'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Simple example
|
|
18
|
+
|
|
19
|
+
```jsx example
|
|
20
|
+
const [value, setValue] = useState()
|
|
21
|
+
return (
|
|
22
|
+
<TextInput
|
|
23
|
+
placeholder='TextInput'
|
|
24
|
+
value={value}
|
|
25
|
+
onChangeText={setValue}
|
|
26
|
+
/>
|
|
27
|
+
)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Disabled
|
|
31
|
+
|
|
32
|
+
```jsx example
|
|
33
|
+
return (
|
|
34
|
+
<TextInput
|
|
35
|
+
disabled
|
|
36
|
+
value='disabled'
|
|
37
|
+
/>
|
|
38
|
+
)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Readonly
|
|
42
|
+
|
|
43
|
+
```jsx example
|
|
44
|
+
return (
|
|
45
|
+
<TextInput
|
|
46
|
+
value='readonly'
|
|
47
|
+
readonly
|
|
48
|
+
/>
|
|
49
|
+
)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Sizes
|
|
53
|
+
|
|
54
|
+
Size can be modified using the `size` prop. Default size is `m`.
|
|
55
|
+
|
|
56
|
+
```jsx example
|
|
57
|
+
const [valueL, setValueL] = useState()
|
|
58
|
+
const [valueM, setValueM] = useState()
|
|
59
|
+
const [valueS, setValueS] = useState()
|
|
60
|
+
return (
|
|
61
|
+
<Div>
|
|
62
|
+
<TextInput
|
|
63
|
+
size='s'
|
|
64
|
+
value={valueS}
|
|
65
|
+
onChangeText={setValueS}
|
|
66
|
+
/>
|
|
67
|
+
<Br />
|
|
68
|
+
<TextInput
|
|
69
|
+
size='m'
|
|
70
|
+
value={valueM}
|
|
71
|
+
onChangeText={setValueM}
|
|
72
|
+
/>
|
|
73
|
+
<Br />
|
|
74
|
+
<TextInput
|
|
75
|
+
size='l'
|
|
76
|
+
value={valueL}
|
|
77
|
+
onChangeText={setValueL}
|
|
78
|
+
/>
|
|
79
|
+
</Div>
|
|
80
|
+
)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Icons
|
|
84
|
+
|
|
85
|
+
Icon can be added using `icon` and `secondaryIcon` properties. Position of icon can be changed by passing `iconPosition` to component (`left` by default). The `secondaryIcon` uses opposite position of `iconPosition`. To handle clicks on the icon, use the property `onIconPress` and `onSecondaryIconPress`. To change icon color use the ʻiconStyleName` and `secondaryIconStyleName` properties.
|
|
86
|
+
|
|
87
|
+
In `.styl` file
|
|
88
|
+
```stylus
|
|
89
|
+
.icon
|
|
90
|
+
color var(--color-text-primary)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
```jsx example
|
|
94
|
+
const [valueLeft, setLeftValue] = useState()
|
|
95
|
+
const [valueRight, setRightValue] = useState()
|
|
96
|
+
return (
|
|
97
|
+
<Div>
|
|
98
|
+
<TextInput
|
|
99
|
+
icon={faSearch}
|
|
100
|
+
secondaryIcon={faTimesCircle}
|
|
101
|
+
secondaryIconStyleName='icon'
|
|
102
|
+
value={valueLeft}
|
|
103
|
+
onChangeText={setLeftValue}
|
|
104
|
+
onSecondaryIconPress={() => setLeftValue()}
|
|
105
|
+
/>
|
|
106
|
+
<Br />
|
|
107
|
+
<TextInput
|
|
108
|
+
icon={faTimesCircle}
|
|
109
|
+
iconPosition='right'
|
|
110
|
+
value={valueRight}
|
|
111
|
+
onChangeText={setRightValue}
|
|
112
|
+
onIconPress={() => setRightValue()}
|
|
113
|
+
/>
|
|
114
|
+
</Div>
|
|
115
|
+
)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Number of lines
|
|
119
|
+
|
|
120
|
+
Pass `numberOfLines={number}` to set the number of lines.
|
|
121
|
+
|
|
122
|
+
```jsx example
|
|
123
|
+
const [value, setValue] = useState()
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<TextInput
|
|
127
|
+
placeholder='write here'
|
|
128
|
+
value={value}
|
|
129
|
+
numberOfLines={4}
|
|
130
|
+
onChangeText={setValue}
|
|
131
|
+
/>
|
|
132
|
+
)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Dynamic number of lines
|
|
136
|
+
|
|
137
|
+
Pass `resize=true` to dynamically change number of lines according to content.
|
|
138
|
+
|
|
139
|
+
```jsx example
|
|
140
|
+
const [value, setValue] = useState()
|
|
141
|
+
|
|
142
|
+
return (
|
|
143
|
+
<TextInput
|
|
144
|
+
placeholder='write here'
|
|
145
|
+
value={value}
|
|
146
|
+
resize
|
|
147
|
+
onChangeText={setValue}
|
|
148
|
+
/>
|
|
149
|
+
)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Sandbox
|
|
153
|
+
|
|
154
|
+
<Sandbox
|
|
155
|
+
Component={TextInput}
|
|
156
|
+
propsJsonSchema={TextInputPropsJsonSchema}
|
|
157
|
+
extraParams={{
|
|
158
|
+
icon: { showIconSelect: true },
|
|
159
|
+
secondaryIcon: { showIconSelect: true }
|
|
160
|
+
}}
|
|
161
|
+
props={{
|
|
162
|
+
onIconPress: () => alert('"onIconPress" event on "TextInput" component'),
|
|
163
|
+
}}
|
|
164
|
+
block
|
|
165
|
+
/>
|
package/index.cssx.styl
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// ----- CONFIG: $UI.TextInput
|
|
2
|
+
$this = merge({
|
|
3
|
+
textColor: var(--TextInput-text-color),
|
|
4
|
+
borderWidth: 1,
|
|
5
|
+
heights: {
|
|
6
|
+
s: 2u,
|
|
7
|
+
m: 2.5u,
|
|
8
|
+
l: 3u
|
|
9
|
+
},
|
|
10
|
+
fontSizes: {
|
|
11
|
+
s: 1.75u,
|
|
12
|
+
m: 1.75u,
|
|
13
|
+
l: 2u
|
|
14
|
+
}
|
|
15
|
+
paddings: {
|
|
16
|
+
s: 0.5u,
|
|
17
|
+
m: 0.75u,
|
|
18
|
+
l: 1u
|
|
19
|
+
}
|
|
20
|
+
}, $UI.TextInput, true)
|
|
21
|
+
|
|
22
|
+
$this.caretColor = $this.textColor
|
|
23
|
+
|
|
24
|
+
// ----- COMPONENT
|
|
25
|
+
|
|
26
|
+
$inputBg = var(--color-bg-main-strong)
|
|
27
|
+
$inputBorderColor = var(--color-border-main)
|
|
28
|
+
$inputBgDisabled = var(--color-bg-main-subtle)
|
|
29
|
+
$inputBorderWidth = 1px
|
|
30
|
+
|
|
31
|
+
.input-input
|
|
32
|
+
margin 0 // important for safari
|
|
33
|
+
flex 1 // important for multiline
|
|
34
|
+
// padding-top, padding-bottom is important
|
|
35
|
+
// for android because it has invisible paddings
|
|
36
|
+
// on the web is important for textarea (override default paddings)
|
|
37
|
+
padding-top 0
|
|
38
|
+
padding-bottom 0
|
|
39
|
+
padding-left 1u
|
|
40
|
+
padding-right @padding-left
|
|
41
|
+
color: $this.textColor
|
|
42
|
+
background-color $inputBg
|
|
43
|
+
border-width $inputBorderWidth
|
|
44
|
+
border-style solid
|
|
45
|
+
border-color var(--color-border-main)
|
|
46
|
+
min-width 8u
|
|
47
|
+
radius()
|
|
48
|
+
fontFamily('normal')
|
|
49
|
+
|
|
50
|
+
+web()
|
|
51
|
+
outline 0
|
|
52
|
+
caret-color: $this.caretColor ? $this.caretColor : this.textColor
|
|
53
|
+
|
|
54
|
+
&.s
|
|
55
|
+
padding-top $this.paddings.s
|
|
56
|
+
padding-bottom $this.paddings.s
|
|
57
|
+
font-size $this.fontSizes.s
|
|
58
|
+
line-height $this.heights.s
|
|
59
|
+
|
|
60
|
+
&.m
|
|
61
|
+
padding-top $this.paddings.m
|
|
62
|
+
padding-bottom $this.paddings.m
|
|
63
|
+
font-size $this.fontSizes.m
|
|
64
|
+
line-height $this.heights.m
|
|
65
|
+
|
|
66
|
+
&.l
|
|
67
|
+
padding-top $this.paddings.l
|
|
68
|
+
padding-bottom $this.paddings.l
|
|
69
|
+
font-size $this.fontSizes.l
|
|
70
|
+
line-height $this.heights.l
|
|
71
|
+
|
|
72
|
+
&.disabled
|
|
73
|
+
background-color $inputBgDisabled
|
|
74
|
+
|
|
75
|
+
+web()
|
|
76
|
+
cursor default
|
|
77
|
+
|
|
78
|
+
&.focused
|
|
79
|
+
border-color var(--color-border-primary)
|
|
80
|
+
|
|
81
|
+
&.error
|
|
82
|
+
border-color var(--color-border-error)
|
|
83
|
+
|
|
84
|
+
for side in (left right)
|
|
85
|
+
&.icon-{side}
|
|
86
|
+
&.l
|
|
87
|
+
padding-{side} 5u
|
|
88
|
+
|
|
89
|
+
&.m
|
|
90
|
+
padding-{side} 4u
|
|
91
|
+
|
|
92
|
+
&.s
|
|
93
|
+
padding-{side} 4u
|
|
94
|
+
|
|
95
|
+
.input-icon
|
|
96
|
+
position absolute
|
|
97
|
+
top 0
|
|
98
|
+
bottom 0
|
|
99
|
+
justify-content center
|
|
100
|
+
z-index 1
|
|
101
|
+
|
|
102
|
+
&.left
|
|
103
|
+
left 1u
|
|
104
|
+
|
|
105
|
+
&.right
|
|
106
|
+
right 1u
|
|
107
|
+
|
|
108
|
+
// ----- JS EXPORTS
|
|
109
|
+
|
|
110
|
+
:export
|
|
111
|
+
config: $this
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// DO NOT MODIFY THIS FILE - IT IS AUTOMATICALLY GENERATED ON COMMITS.
|
|
3
|
+
|
|
4
|
+
import { type ReactNode, type RefObject } from 'react';
|
|
5
|
+
import { type StyleProp, type TextStyle, type ViewStyle, type TextInputProps } from 'react-native';
|
|
6
|
+
declare const _default: import("react").ComponentType<UITextInputProps>;
|
|
7
|
+
export default _default;
|
|
8
|
+
export declare const _PropsJsonSchema: {};
|
|
9
|
+
export interface UITextInputProps extends Omit<TextInputProps, 'placeholder' | 'style'> {
|
|
10
|
+
/** Ref to access the underlying input */
|
|
11
|
+
ref?: RefObject<any>;
|
|
12
|
+
/** Custom styles for the wrapper element */
|
|
13
|
+
style?: StyleProp<ViewStyle>;
|
|
14
|
+
/** Custom styles for the input element */
|
|
15
|
+
inputStyle?: StyleProp<TextStyle>;
|
|
16
|
+
/** Custom styles for the primary icon */
|
|
17
|
+
iconStyle?: StyleProp<TextStyle>;
|
|
18
|
+
/** Custom styles for the secondary icon */
|
|
19
|
+
secondaryIconStyle?: StyleProp<TextStyle>;
|
|
20
|
+
/** Placeholder text */
|
|
21
|
+
placeholder?: string | number;
|
|
22
|
+
/** Test identifier */
|
|
23
|
+
testID?: string;
|
|
24
|
+
/** Input value @default '' */
|
|
25
|
+
value?: string;
|
|
26
|
+
/** Size preset @default 'm' */
|
|
27
|
+
size?: 'l' | 'm' | 's';
|
|
28
|
+
/** Disable input interactions @default false */
|
|
29
|
+
disabled?: boolean;
|
|
30
|
+
/** Render a non-editable value @default false */
|
|
31
|
+
readonly?: boolean;
|
|
32
|
+
/** Enable dynamic height based on content @default false */
|
|
33
|
+
resize?: boolean;
|
|
34
|
+
/** Number of lines to display @default 1 */
|
|
35
|
+
numberOfLines?: number;
|
|
36
|
+
/** Primary icon component */
|
|
37
|
+
icon?: any;
|
|
38
|
+
/** Position of the primary icon @default 'left' */
|
|
39
|
+
iconPosition?: 'left' | 'right';
|
|
40
|
+
/** Secondary icon component */
|
|
41
|
+
secondaryIcon?: any;
|
|
42
|
+
/** Primary icon press handler */
|
|
43
|
+
onIconPress?: () => void;
|
|
44
|
+
/** Secondary icon press handler */
|
|
45
|
+
onSecondaryIconPress?: () => void;
|
|
46
|
+
/** Focus event handler */
|
|
47
|
+
onFocus?: (...args: any[]) => void;
|
|
48
|
+
/** Blur event handler */
|
|
49
|
+
onBlur?: (...args: any[]) => void;
|
|
50
|
+
/** Change text handler */
|
|
51
|
+
onChangeText?: (...args: any[]) => void;
|
|
52
|
+
/** Custom wrapper renderer @private */
|
|
53
|
+
_renderWrapper?: (options: {
|
|
54
|
+
style?: StyleProp<ViewStyle>;
|
|
55
|
+
}, children: ReactNode) => ReactNode;
|
|
56
|
+
/** Error state flag @private */
|
|
57
|
+
_hasError?: boolean;
|
|
58
|
+
}
|
package/index.tsx
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useState,
|
|
3
|
+
useMemo,
|
|
4
|
+
useRef,
|
|
5
|
+
type ReactNode,
|
|
6
|
+
type RefObject
|
|
7
|
+
} from 'react'
|
|
8
|
+
import {
|
|
9
|
+
TextInput as RNTextInput,
|
|
10
|
+
Platform,
|
|
11
|
+
type StyleProp,
|
|
12
|
+
type TextStyle,
|
|
13
|
+
type ViewStyle,
|
|
14
|
+
type TextInputProps
|
|
15
|
+
} from 'react-native'
|
|
16
|
+
import { pug, observer, useIsomorphicLayoutEffect } from 'startupjs'
|
|
17
|
+
import { themed, useColors } from '@startupjs-ui/core'
|
|
18
|
+
import Div from '@startupjs-ui/div'
|
|
19
|
+
import Icon from '@startupjs-ui/icon'
|
|
20
|
+
import Span from '@startupjs-ui/span'
|
|
21
|
+
import STYLES from './index.cssx.styl'
|
|
22
|
+
|
|
23
|
+
const {
|
|
24
|
+
config: {
|
|
25
|
+
caretColor,
|
|
26
|
+
heights,
|
|
27
|
+
paddings
|
|
28
|
+
}
|
|
29
|
+
} = STYLES
|
|
30
|
+
|
|
31
|
+
const IS_WEB = Platform.OS === 'web'
|
|
32
|
+
const IS_ANDROID = Platform.OS === 'android'
|
|
33
|
+
const ICON_SIZES = {
|
|
34
|
+
s: 'm',
|
|
35
|
+
m: 'm',
|
|
36
|
+
l: 'l'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default observer(themed('TextInput', TextInput))
|
|
40
|
+
|
|
41
|
+
export const _PropsJsonSchema = {/* TextInputProps */}
|
|
42
|
+
|
|
43
|
+
export interface UITextInputProps extends Omit<TextInputProps, 'placeholder' | 'style'> {
|
|
44
|
+
/** Ref to access the underlying input */
|
|
45
|
+
ref?: RefObject<any>
|
|
46
|
+
/** Custom styles for the wrapper element */
|
|
47
|
+
style?: StyleProp<ViewStyle>
|
|
48
|
+
/** Custom styles for the input element */
|
|
49
|
+
inputStyle?: StyleProp<TextStyle>
|
|
50
|
+
/** Custom styles for the primary icon */
|
|
51
|
+
iconStyle?: StyleProp<TextStyle>
|
|
52
|
+
/** Custom styles for the secondary icon */
|
|
53
|
+
secondaryIconStyle?: StyleProp<TextStyle>
|
|
54
|
+
/** Placeholder text */
|
|
55
|
+
placeholder?: string | number
|
|
56
|
+
/** Test identifier */
|
|
57
|
+
testID?: string
|
|
58
|
+
/** Input value @default '' */
|
|
59
|
+
value?: string
|
|
60
|
+
/** Size preset @default 'm' */
|
|
61
|
+
size?: 'l' | 'm' | 's'
|
|
62
|
+
/** Disable input interactions @default false */
|
|
63
|
+
disabled?: boolean
|
|
64
|
+
/** Render a non-editable value @default false */
|
|
65
|
+
readonly?: boolean
|
|
66
|
+
/** Enable dynamic height based on content @default false */
|
|
67
|
+
resize?: boolean
|
|
68
|
+
/** Number of lines to display @default 1 */
|
|
69
|
+
numberOfLines?: number
|
|
70
|
+
/** Primary icon component */
|
|
71
|
+
icon?: any
|
|
72
|
+
/** Position of the primary icon @default 'left' */
|
|
73
|
+
iconPosition?: 'left' | 'right'
|
|
74
|
+
/** Secondary icon component */
|
|
75
|
+
secondaryIcon?: any
|
|
76
|
+
/** Primary icon press handler */
|
|
77
|
+
onIconPress?: () => void
|
|
78
|
+
/** Secondary icon press handler */
|
|
79
|
+
onSecondaryIconPress?: () => void
|
|
80
|
+
/** Focus event handler */
|
|
81
|
+
onFocus?: (...args: any[]) => void
|
|
82
|
+
/** Blur event handler */
|
|
83
|
+
onBlur?: (...args: any[]) => void
|
|
84
|
+
/** Change text handler */
|
|
85
|
+
onChangeText?: (...args: any[]) => void
|
|
86
|
+
/** Custom wrapper renderer @private */
|
|
87
|
+
_renderWrapper?: (options: { style?: StyleProp<ViewStyle> }, children: ReactNode) => ReactNode
|
|
88
|
+
/** Error state flag @private */
|
|
89
|
+
_hasError?: boolean
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function TextInput ({
|
|
93
|
+
ref,
|
|
94
|
+
style,
|
|
95
|
+
placeholder,
|
|
96
|
+
value = '',
|
|
97
|
+
size = 'm',
|
|
98
|
+
disabled = false,
|
|
99
|
+
readonly = false,
|
|
100
|
+
resize = false,
|
|
101
|
+
numberOfLines = 1,
|
|
102
|
+
iconPosition = 'left',
|
|
103
|
+
icon,
|
|
104
|
+
secondaryIcon,
|
|
105
|
+
onFocus,
|
|
106
|
+
onBlur,
|
|
107
|
+
onIconPress,
|
|
108
|
+
onSecondaryIconPress,
|
|
109
|
+
_renderWrapper,
|
|
110
|
+
_hasError,
|
|
111
|
+
...props
|
|
112
|
+
}: UITextInputProps): ReactNode {
|
|
113
|
+
const [focused, setFocused] = useState(false)
|
|
114
|
+
const [currentNumberOfLines, setCurrentNumberOfLines] = useState(numberOfLines)
|
|
115
|
+
const fallbackRef = useRef<any>(null)
|
|
116
|
+
const inputRef = ref ?? fallbackRef
|
|
117
|
+
|
|
118
|
+
const getColor = useColors()
|
|
119
|
+
|
|
120
|
+
function handleFocus (...args: any[]) {
|
|
121
|
+
onFocus && onFocus(...args)
|
|
122
|
+
setFocused(true)
|
|
123
|
+
}
|
|
124
|
+
function handleBlur (...args: any[]) {
|
|
125
|
+
onBlur && onBlur(...args)
|
|
126
|
+
setFocused(false)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (!_renderWrapper) {
|
|
130
|
+
_renderWrapper = ({ style }: { style?: StyleProp<ViewStyle> }, children: ReactNode): ReactNode => pug`
|
|
131
|
+
Div(style=style)= children
|
|
132
|
+
`
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
useIsomorphicLayoutEffect(() => {
|
|
136
|
+
if (readonly || !resize) return
|
|
137
|
+
const numberOfLinesInValue = value.split('\n').length
|
|
138
|
+
if (numberOfLinesInValue >= numberOfLines) {
|
|
139
|
+
setCurrentNumberOfLines(numberOfLinesInValue)
|
|
140
|
+
}
|
|
141
|
+
}, [value, resize, numberOfLines, readonly])
|
|
142
|
+
|
|
143
|
+
if (IS_WEB) {
|
|
144
|
+
// repeat mobile behaviour on the web
|
|
145
|
+
// TODO
|
|
146
|
+
// test mobile device behaviour
|
|
147
|
+
|
|
148
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
149
|
+
useIsomorphicLayoutEffect(() => {
|
|
150
|
+
if (readonly) return
|
|
151
|
+
if (focused && disabled) {
|
|
152
|
+
inputRef.current?.blur()
|
|
153
|
+
setFocused(false)
|
|
154
|
+
}
|
|
155
|
+
}, [disabled, focused, readonly])
|
|
156
|
+
// fix minWidth on web
|
|
157
|
+
// ref: https://stackoverflow.com/a/29990524/1930491
|
|
158
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
159
|
+
useIsomorphicLayoutEffect(() => {
|
|
160
|
+
if (readonly) return
|
|
161
|
+
// TODO: looks like it's not available anymore on new versions of react-native-web
|
|
162
|
+
inputRef.current?.setNativeProps?.({ size: '1' })
|
|
163
|
+
}, [readonly])
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// useDidUpdate(() => {
|
|
167
|
+
// if (readonly) return
|
|
168
|
+
// if (numberOfLines !== currentNumberOfLines) {
|
|
169
|
+
// setCurrentNumberOfLines(numberOfLines)
|
|
170
|
+
// }
|
|
171
|
+
// }, [numberOfLines, currentNumberOfLines, readonly])
|
|
172
|
+
|
|
173
|
+
const multiline = useMemo(() => {
|
|
174
|
+
return resize || numberOfLines > 1
|
|
175
|
+
}, [resize, numberOfLines])
|
|
176
|
+
|
|
177
|
+
const fullHeight = useMemo(() => {
|
|
178
|
+
return currentNumberOfLines * (heights[size] as number) + (paddings[size] as number) * 2
|
|
179
|
+
}, [currentNumberOfLines, size])
|
|
180
|
+
|
|
181
|
+
function onLayoutIcon (e: any) {
|
|
182
|
+
if (IS_WEB) {
|
|
183
|
+
e.nativeEvent.target.childNodes[0].tabIndex = -1
|
|
184
|
+
e.nativeEvent.target.childNodes[0].childNodes[0].tabIndex = -1
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const inputExtraProps: Record<string, any> = {}
|
|
189
|
+
if (IS_ANDROID && multiline) inputExtraProps.textAlignVertical = 'top'
|
|
190
|
+
|
|
191
|
+
const inputStyleName = [
|
|
192
|
+
size,
|
|
193
|
+
{
|
|
194
|
+
disabled,
|
|
195
|
+
focused,
|
|
196
|
+
[`icon-${iconPosition}`]: !!icon,
|
|
197
|
+
[`icon-${getOppositePosition(iconPosition)}`]: !!secondaryIcon,
|
|
198
|
+
error: _hasError
|
|
199
|
+
}
|
|
200
|
+
]
|
|
201
|
+
|
|
202
|
+
if (readonly) {
|
|
203
|
+
return pug`
|
|
204
|
+
Span= value
|
|
205
|
+
`
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return _renderWrapper({
|
|
209
|
+
style: [style]
|
|
210
|
+
}, pug`
|
|
211
|
+
RNTextInput.input-input(
|
|
212
|
+
part=['input', {
|
|
213
|
+
inputIconLeft: icon && iconPosition === 'left',
|
|
214
|
+
inputIconRight: icon && iconPosition === 'right'
|
|
215
|
+
}]
|
|
216
|
+
ref=inputRef
|
|
217
|
+
style={ minHeight: fullHeight }
|
|
218
|
+
styleName=inputStyleName
|
|
219
|
+
selectionColor=caretColor
|
|
220
|
+
placeholder=placeholder
|
|
221
|
+
placeholderTextColor=getColor('text-placeholder')
|
|
222
|
+
value=value
|
|
223
|
+
disabled=IS_WEB ? disabled : undefined
|
|
224
|
+
editable=IS_WEB ? undefined : !disabled
|
|
225
|
+
multiline=multiline
|
|
226
|
+
selectTextOnFocus=false
|
|
227
|
+
onFocus=handleFocus
|
|
228
|
+
onBlur=handleBlur
|
|
229
|
+
...props
|
|
230
|
+
...inputExtraProps
|
|
231
|
+
)
|
|
232
|
+
if icon
|
|
233
|
+
Div.input-icon(
|
|
234
|
+
focusable=false
|
|
235
|
+
onLayout=onLayoutIcon
|
|
236
|
+
styleName=[size, iconPosition]
|
|
237
|
+
onPress=disabled ? undefined : onIconPress
|
|
238
|
+
pointerEvents=onIconPress ? undefined : 'none'
|
|
239
|
+
)
|
|
240
|
+
Icon(
|
|
241
|
+
part='icon'
|
|
242
|
+
icon=icon
|
|
243
|
+
size=ICON_SIZES[size]
|
|
244
|
+
)
|
|
245
|
+
if secondaryIcon
|
|
246
|
+
Div.input-icon(
|
|
247
|
+
focusable=false
|
|
248
|
+
onLayout=onLayoutIcon
|
|
249
|
+
styleName=[size, getOppositePosition(iconPosition)]
|
|
250
|
+
onPress=disabled ? undefined : onSecondaryIconPress
|
|
251
|
+
pointerEvents=onSecondaryIconPress ? undefined : 'none'
|
|
252
|
+
)
|
|
253
|
+
Icon(
|
|
254
|
+
part='secondaryIcon'
|
|
255
|
+
icon=secondaryIcon
|
|
256
|
+
size=ICON_SIZES[size]
|
|
257
|
+
)
|
|
258
|
+
`)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function getOppositePosition (position: 'left' | 'right') {
|
|
262
|
+
return position === 'left' ? 'right' : 'left'
|
|
263
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@startupjs-ui/text-input",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"main": "index.tsx",
|
|
8
|
+
"types": "index.d.ts",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@startupjs-ui/core": "^0.1.3",
|
|
12
|
+
"@startupjs-ui/div": "^0.1.3",
|
|
13
|
+
"@startupjs-ui/icon": "^0.1.3",
|
|
14
|
+
"@startupjs-ui/span": "^0.1.3"
|
|
15
|
+
},
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"react": "*",
|
|
18
|
+
"react-native": "*",
|
|
19
|
+
"startupjs": "*"
|
|
20
|
+
},
|
|
21
|
+
"gitHead": "fd964ebc3892d3dd0a6c85438c0af619cc50c3f0"
|
|
22
|
+
}
|