@codeleap/mobile 4.3.9 → 5.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.
Files changed (209) hide show
  1. package/dist/components/Autocomplete/index.js.map +1 -1
  2. package/dist/components/Autocomplete/types.d.ts +9 -10
  3. package/dist/components/Backdrop/index.js.map +1 -1
  4. package/dist/components/Checkbox/index.js +13 -9
  5. package/dist/components/Checkbox/index.js.map +1 -1
  6. package/dist/components/Checkbox/types.d.ts +5 -2
  7. package/dist/components/DatePickerModal/index.js +18 -11
  8. package/dist/components/DatePickerModal/index.js.map +1 -1
  9. package/dist/components/DatePickerModal/types.d.ts +12 -9
  10. package/dist/components/FileInput/types.d.ts +3 -4
  11. package/dist/components/Grid/index.js.map +1 -1
  12. package/dist/components/Image/types.d.ts +2 -5
  13. package/dist/components/InputBase/index.d.ts +2 -6
  14. package/dist/components/InputBase/index.js +9 -5
  15. package/dist/components/InputBase/index.js.map +1 -1
  16. package/dist/components/InputBase/types.d.ts +2 -0
  17. package/dist/components/InputBase/useInputBase.d.ts +24 -0
  18. package/dist/components/InputBase/useInputBase.js +46 -0
  19. package/dist/components/InputBase/useInputBase.js.map +1 -0
  20. package/dist/components/InputBase/useInputBasePartialStyles.d.ts +2 -0
  21. package/dist/components/InputBase/useInputBasePartialStyles.js +30 -0
  22. package/dist/components/InputBase/useInputBasePartialStyles.js.map +1 -0
  23. package/dist/components/InputBase/utils.js +16 -16
  24. package/dist/components/InputBase/utils.js.map +1 -1
  25. package/dist/components/List/index.js +1 -1
  26. package/dist/components/List/index.js.map +1 -1
  27. package/dist/components/Modal/index.js +4 -14
  28. package/dist/components/Modal/index.js.map +1 -1
  29. package/dist/components/NumberIncrement/index.js +21 -132
  30. package/dist/components/NumberIncrement/index.js.map +1 -1
  31. package/dist/components/NumberIncrement/types.d.ts +7 -5
  32. package/dist/components/NumberIncrement/useNumberIncrement.d.ts +55 -0
  33. package/dist/components/NumberIncrement/useNumberIncrement.js +107 -0
  34. package/dist/components/NumberIncrement/useNumberIncrement.js.map +1 -0
  35. package/dist/components/RadioInput/index.d.ts +1 -1
  36. package/dist/components/RadioInput/index.js +20 -41
  37. package/dist/components/RadioInput/index.js.map +1 -1
  38. package/dist/components/RadioInput/types.d.ts +11 -9
  39. package/dist/components/Scroll/index.js +25 -28
  40. package/dist/components/Scroll/index.js.map +1 -1
  41. package/dist/components/Scroll/types.d.ts +4 -4
  42. package/dist/components/Sections/index.js +1 -1
  43. package/dist/components/Sections/index.js.map +1 -1
  44. package/dist/components/SegmentedControl/index.js +6 -4
  45. package/dist/components/SegmentedControl/index.js.map +1 -1
  46. package/dist/components/SegmentedControl/types.d.ts +4 -3
  47. package/dist/components/Select/index.js +10 -6
  48. package/dist/components/Select/index.js.map +1 -1
  49. package/dist/components/Select/types.d.ts +19 -18
  50. package/dist/components/Slider/index.js +18 -57
  51. package/dist/components/Slider/index.js.map +1 -1
  52. package/dist/components/Slider/types.d.ts +7 -5
  53. package/dist/components/SortablePhotos/index.js +17 -18
  54. package/dist/components/SortablePhotos/index.js.map +1 -1
  55. package/dist/components/SortablePhotos/types.d.ts +5 -15
  56. package/dist/components/SortablePhotos/useSortablePhotos.d.ts +11 -8
  57. package/dist/components/SortablePhotos/useSortablePhotos.js +18 -11
  58. package/dist/components/SortablePhotos/useSortablePhotos.js.map +1 -1
  59. package/dist/components/Switch/index.js +13 -9
  60. package/dist/components/Switch/index.js.map +1 -1
  61. package/dist/components/Switch/types.d.ts +5 -2
  62. package/dist/components/TextInput/index.js +26 -70
  63. package/dist/components/TextInput/index.js.map +1 -1
  64. package/dist/components/TextInput/types.d.ts +8 -6
  65. package/dist/components/TextInput/useTextInput.d.ts +54 -0
  66. package/dist/components/TextInput/useTextInput.js +59 -0
  67. package/dist/components/TextInput/useTextInput.js.map +1 -0
  68. package/dist/components/Touchable/index.js +4 -3
  69. package/dist/components/Touchable/index.js.map +1 -1
  70. package/dist/components/View/index.d.ts +7 -12
  71. package/dist/components/View/index.js +9 -7
  72. package/dist/components/View/index.js.map +1 -1
  73. package/dist/components/View/types.d.ts +6 -6
  74. package/dist/components/components.d.ts +0 -2
  75. package/dist/components/components.js +0 -2
  76. package/dist/components/components.js.map +1 -1
  77. package/dist/hooks/index.d.ts +3 -2
  78. package/dist/hooks/index.js +3 -10
  79. package/dist/hooks/index.js.map +1 -1
  80. package/dist/hooks/useKeyboardController.d.ts +6 -0
  81. package/dist/hooks/useKeyboardController.js +19 -0
  82. package/dist/hooks/useKeyboardController.js.map +1 -0
  83. package/dist/hooks/useStatusBar.d.ts +6 -0
  84. package/dist/hooks/useStatusBar.js +15 -0
  85. package/dist/hooks/useStatusBar.js.map +1 -0
  86. package/dist/hooks/useStylesFor.d.ts +2 -0
  87. package/dist/hooks/useStylesFor.js +11 -0
  88. package/dist/hooks/useStylesFor.js.map +1 -0
  89. package/dist/index.d.ts +2 -1
  90. package/dist/index.js +2 -1
  91. package/dist/index.js.map +1 -1
  92. package/dist/modules/backgroundTimer.d.ts +3 -0
  93. package/dist/modules/backgroundTimer.js +31 -0
  94. package/dist/modules/backgroundTimer.js.map +1 -0
  95. package/dist/modules/index.d.ts +3 -0
  96. package/dist/modules/index.js +3 -0
  97. package/dist/modules/index.js.map +1 -0
  98. package/dist/modules/reactNavigation.d.ts +8 -21
  99. package/dist/modules/reactNavigation.js +38 -12
  100. package/dist/modules/reactNavigation.js.map +1 -1
  101. package/dist/modules/scroll.d.ts +18 -0
  102. package/dist/modules/scroll.js +57 -0
  103. package/dist/modules/scroll.js.map +1 -0
  104. package/dist/modules/types/textInputMask.d.ts +6 -2
  105. package/dist/utils/KeyboardAware/context.js +2 -6
  106. package/dist/utils/KeyboardAware/context.js.map +1 -1
  107. package/dist/utils/KeyboardAware/index.d.ts +0 -1
  108. package/dist/utils/KeyboardAware/index.js +0 -1
  109. package/dist/utils/KeyboardAware/index.js.map +1 -1
  110. package/dist/utils/ModalManager/context.js +2 -2
  111. package/dist/utils/ModalManager/context.js.map +1 -1
  112. package/dist/utils/hooks.js +4 -4
  113. package/dist/utils/hooks.js.map +1 -1
  114. package/dist/utils/locale.d.ts +1 -1
  115. package/dist/utils/locale.js +10 -5
  116. package/dist/utils/locale.js.map +1 -1
  117. package/dist/utils/theme.d.ts +1 -0
  118. package/dist/utils/theme.js +4 -2
  119. package/dist/utils/theme.js.map +1 -1
  120. package/package.json +32 -36
  121. package/package.json.bak +16 -20
  122. package/src/components/Autocomplete/index.tsx +2 -3
  123. package/src/components/Autocomplete/types.ts +9 -10
  124. package/src/components/Backdrop/index.tsx +0 -1
  125. package/src/components/Checkbox/index.tsx +23 -9
  126. package/src/components/Checkbox/types.ts +5 -2
  127. package/src/components/DatePickerModal/index.tsx +27 -18
  128. package/src/components/DatePickerModal/types.ts +12 -9
  129. package/src/components/FileInput/types.ts +3 -4
  130. package/src/components/Grid/index.tsx +3 -3
  131. package/src/components/Image/types.ts +3 -6
  132. package/src/components/InputBase/index.tsx +13 -7
  133. package/src/components/InputBase/types.ts +2 -0
  134. package/src/components/InputBase/useInputBase.ts +60 -0
  135. package/src/components/InputBase/useInputBasePartialStyles.ts +38 -0
  136. package/src/components/InputBase/utils.ts +17 -17
  137. package/src/components/List/index.tsx +0 -1
  138. package/src/components/Modal/index.tsx +4 -15
  139. package/src/components/NumberIncrement/index.tsx +52 -160
  140. package/src/components/NumberIncrement/types.ts +7 -5
  141. package/src/components/NumberIncrement/useNumberIncrement.ts +152 -0
  142. package/src/components/RadioInput/index.tsx +37 -53
  143. package/src/components/RadioInput/types.ts +11 -9
  144. package/src/components/Scroll/index.tsx +44 -45
  145. package/src/components/Scroll/types.ts +4 -4
  146. package/src/components/Sections/index.tsx +0 -1
  147. package/src/components/SegmentedControl/index.tsx +8 -6
  148. package/src/components/SegmentedControl/types.ts +4 -3
  149. package/src/components/Select/index.tsx +32 -24
  150. package/src/components/Select/types.ts +19 -18
  151. package/src/components/Slider/index.tsx +34 -66
  152. package/src/components/Slider/types.ts +7 -5
  153. package/src/components/SortablePhotos/index.tsx +31 -47
  154. package/src/components/SortablePhotos/types.ts +6 -15
  155. package/src/components/SortablePhotos/useSortablePhotos.ts +28 -22
  156. package/src/components/Switch/index.tsx +23 -9
  157. package/src/components/Switch/types.ts +5 -2
  158. package/src/components/TextInput/index.tsx +55 -89
  159. package/src/components/TextInput/types.ts +9 -7
  160. package/src/components/TextInput/useTextInput.ts +88 -0
  161. package/src/components/Touchable/index.tsx +5 -1
  162. package/src/components/View/index.tsx +19 -12
  163. package/src/components/View/types.ts +7 -6
  164. package/src/components/components.ts +0 -2
  165. package/src/hooks/index.ts +3 -13
  166. package/src/hooks/useKeyboardController.ts +28 -0
  167. package/src/hooks/useStatusBar.ts +21 -0
  168. package/src/hooks/useStylesFor.ts +13 -0
  169. package/src/index.ts +3 -1
  170. package/src/modules/backgroundTimer.ts +39 -0
  171. package/src/modules/index.ts +3 -0
  172. package/src/modules/reactNavigation.ts +64 -14
  173. package/src/modules/scroll.tsx +89 -0
  174. package/src/modules/types/textInputMask.ts +8 -4
  175. package/src/types/index.ts +1 -0
  176. package/src/utils/KeyboardAware/context.tsx +2 -6
  177. package/src/utils/KeyboardAware/index.ts +1 -1
  178. package/src/utils/ModalManager/context.tsx +2 -2
  179. package/src/utils/hooks.ts +4 -4
  180. package/src/utils/locale.ts +13 -5
  181. package/src/utils/theme.ts +6 -2
  182. package/dist/components/Navigation/Navigation.d.ts +0 -55
  183. package/dist/components/Navigation/Navigation.js +0 -41
  184. package/dist/components/Navigation/Navigation.js.map +0 -1
  185. package/dist/components/Navigation/constants.d.ts +0 -9
  186. package/dist/components/Navigation/constants.js +0 -9
  187. package/dist/components/Navigation/constants.js.map +0 -1
  188. package/dist/components/Navigation/index.d.ts +0 -3
  189. package/dist/components/Navigation/index.js +0 -4
  190. package/dist/components/Navigation/index.js.map +0 -1
  191. package/dist/components/Navigation/types.d.ts +0 -26
  192. package/dist/components/Navigation/types.js +0 -2
  193. package/dist/components/Navigation/types.js.map +0 -1
  194. package/dist/components/Navigation/utils.d.ts +0 -3
  195. package/dist/components/Navigation/utils.js +0 -34
  196. package/dist/components/Navigation/utils.js.map +0 -1
  197. package/dist/components/NumberIncrement/utils.d.ts +0 -5
  198. package/dist/components/NumberIncrement/utils.js +0 -23
  199. package/dist/components/NumberIncrement/utils.js.map +0 -1
  200. package/dist/utils/KeyboardAware/types.d.ts +0 -1
  201. package/dist/utils/KeyboardAware/types.js +0 -6
  202. package/dist/utils/KeyboardAware/types.js.map +0 -1
  203. package/src/components/Navigation/Navigation.tsx +0 -55
  204. package/src/components/Navigation/constants.ts +0 -24
  205. package/src/components/Navigation/index.tsx +0 -3
  206. package/src/components/Navigation/types.ts +0 -28
  207. package/src/components/Navigation/utils.tsx +0 -57
  208. package/src/components/NumberIncrement/utils.ts +0 -27
  209. package/src/utils/KeyboardAware/types.ts +0 -159
@@ -1,7 +1,7 @@
1
1
  import { CreateOSAlert } from '@codeleap/modals'
2
2
  import { useEffect, useMemo, useState } from '@codeleap/hooks'
3
3
  import { FileInputImageSource, useFileInput } from '../FileInput'
4
- import { SortablePhoto, SortablePhotosProps } from './types'
4
+ import { SortablePhoto, SortablePhotosProps, WithId } from './types'
5
5
 
6
6
  const SortableAlert = CreateOSAlert()
7
7
 
@@ -22,9 +22,9 @@ export const useSortablePhotos = <T extends SortablePhoto>(props: SortablePhotos
22
22
 
23
23
  const input = useFileInput()
24
24
 
25
- const [data, setData] = useState<T[]>([])
25
+ const [data, setData] = useState<WithId<T>[]>([])
26
26
 
27
- const onChange = (photos: T[]) => {
27
+ const onChange = (photos: WithId<T>[]) => {
28
28
  const { newPhotos, sortedPhotos } = sortPhotos(photos)
29
29
 
30
30
  setData(sortedPhotos)
@@ -37,14 +37,14 @@ export const useSortablePhotos = <T extends SortablePhoto>(props: SortablePhotos
37
37
  const length = Math.abs(numPhotos - currentLength)
38
38
  const fillPhotos = Array(length).fill({ filename: null, file: null }) as T[]
39
39
 
40
- const newPhotos = currentPhotos.concat(fillPhotos)
40
+ const newPhotos = currentPhotos.concat(fillPhotos).map((photo, idx) => ({ ...photo, key: idx + '-photo' }))
41
41
 
42
42
  setData(newPhotos)
43
- onChangePhotos(currentPhotos)
43
+ onChangePhotos(assignOrder(currentPhotos))
44
44
  }
45
45
  }, [loading])
46
46
 
47
- const { emptyIndexes, numberPhotosMissing } = useMemo(() => {
47
+ const { emptyIndexes, numberPhotosMissing, enabledDragDrop } = useMemo(() => {
48
48
  const copyPhotos = [...data]
49
49
 
50
50
  const emptyIndexes = copyPhotos.reduce((indexes, photo, index) => {
@@ -56,28 +56,32 @@ export const useSortablePhotos = <T extends SortablePhoto>(props: SortablePhotos
56
56
 
57
57
  const numberPhotosMissing = emptyIndexes?.length
58
58
 
59
+ const enabledDragDrop = numberPhotosMissing < numPhotos - 1
60
+
59
61
  return {
60
62
  emptyIndexes,
61
63
  numberPhotosMissing,
64
+ enabledDragDrop,
62
65
  }
63
66
  }, [JSON.stringify(data)])
64
67
 
65
- const sortPhotos = (_unorderedPhotos: T[]) => {
68
+ const assignOrder = (photos: WithId<T>[] | T[]) => {
69
+ return photos.map((photo, idx) => ({ ...photo, order: idx }))
70
+ }
71
+
72
+ const sortPhotos = (_unorderedPhotos: WithId<T>[]) => {
66
73
  const unorderedPhotos = [..._unorderedPhotos]
67
74
 
68
- const [newPhotos, emptyPhotos] = unorderedPhotos.reduce(
69
- ([newPhotos, emptyPhotos], photo) => {
70
- !!photo?.filename ? newPhotos.push(photo) : emptyPhotos.push(photo)
71
- return [newPhotos, emptyPhotos]
72
- },
73
- [[], []] as [T[], T[]]
74
- )
75
+ const newPhotos = unorderedPhotos.filter(photo => !!photo?.filename)
75
76
 
76
- const sortedPhotos: T[] = newPhotos.concat(emptyPhotos)
77
+ const sortedPhotos = unorderedPhotos.map((photo, index) => ({
78
+ ...(newPhotos[index] ?? { file: null, filename: null } as T),
79
+ key: photo?.key
80
+ }))
77
81
 
78
82
  return {
79
83
  sortedPhotos,
80
- newPhotos,
84
+ newPhotos: assignOrder(newPhotos),
81
85
  }
82
86
  }
83
87
 
@@ -111,7 +115,7 @@ export const useSortablePhotos = <T extends SortablePhoto>(props: SortablePhotos
111
115
  ...newPhotos[order],
112
116
  filename,
113
117
  file: uri,
114
- } as T
118
+ } as WithId<T>
115
119
  }
116
120
  } else {
117
121
  const file = files?.[0]
@@ -122,7 +126,7 @@ export const useSortablePhotos = <T extends SortablePhoto>(props: SortablePhotos
122
126
  ...newPhotos[order],
123
127
  filename,
124
128
  file: uri,
125
- } as T
129
+ } as WithId<T>
126
130
  }
127
131
 
128
132
  onChange(newPhotos)
@@ -135,12 +139,12 @@ export const useSortablePhotos = <T extends SortablePhoto>(props: SortablePhotos
135
139
  ...newPhotos[order],
136
140
  filename: null,
137
141
  file: null,
138
- } as T
142
+ } as WithId<T>
139
143
 
140
144
  onChange(newPhotos)
141
145
  }
142
146
 
143
- const handlePressPhoto = (currentData: T[], photo: T, order: number) => {
147
+ const handlePressPhoto = (photo: T, order: number) => {
144
148
  SortableAlert.custom({
145
149
  title: modalTitle,
146
150
  body: modalBody,
@@ -150,13 +154,14 @@ export const useSortablePhotos = <T extends SortablePhoto>(props: SortablePhotos
150
154
  !!photo?.filename && { text: modalDeleteText, onPress: () => handleDeletePhoto(photo, order) },
151
155
  ],
152
156
  // @ts-expect-error
157
+ closable: true,
153
158
  isRow: false,
154
159
  })
155
160
 
156
- onPressPhoto?.(currentData, photo, order)
161
+ onPressPhoto?.(data, photo, order)
157
162
  }
158
163
 
159
- const onChangePhotosOrder = (newData: T[]) => {
164
+ const onChangePhotosOrder = (newData: WithId<T>[]) => {
160
165
  onChange(newData)
161
166
  }
162
167
 
@@ -170,5 +175,6 @@ export const useSortablePhotos = <T extends SortablePhoto>(props: SortablePhotos
170
175
  onChangePhotosOrder,
171
176
  emptyIndexes,
172
177
  data,
178
+ enabledDragDrop,
173
179
  }
174
180
  }
@@ -7,6 +7,8 @@ import { SwitchProps } from './types'
7
7
  import { AnyRecord, IJSX, StyledComponentProps } from '@codeleap/styles'
8
8
  import { MobileStyleRegistry } from '../../Registry'
9
9
  import { useStylesFor } from '../../hooks'
10
+ import { useInputBase } from '../InputBase/useInputBase'
11
+ import { fields } from '@codeleap/form'
10
12
 
11
13
  export * from './styles'
12
14
  export * from './types'
@@ -24,15 +26,23 @@ export const Switch = (props: SwitchProps) => {
24
26
 
25
27
  const {
26
28
  style,
27
- value,
28
29
  disabled,
29
30
  debugName,
30
- onValueChange,
31
31
  switchOnLeft,
32
+ field,
33
+ forceError,
34
+ value,
35
+ onValueChange,
32
36
  } = others
33
37
 
34
38
  const styles = useStylesFor(Switch.styleRegistryName, style)
35
39
 
40
+ const {
41
+ fieldHandle,
42
+ validation,
43
+ wrapperRef,
44
+ } = useInputBase<boolean>(field, fields.boolean, [value, onValueChange])
45
+
36
46
  const trackAnimation = useAnimatedVariantStyles({
37
47
  variantStyles: styles,
38
48
  animatedProperties: ['track:off', 'track:disabled', 'track:on', 'track:disabled-on', 'track:disabled-off'],
@@ -41,16 +51,16 @@ export const Switch = (props: SwitchProps) => {
41
51
  'worklet'
42
52
  let disabledStyle = {}
43
53
  if (disabled) {
44
- disabledStyle = value ? styles['track:disabled-on'] : styles['track:disabled-off']
54
+ disabledStyle = fieldHandle?.value ? styles['track:disabled-on'] : styles['track:disabled-off']
45
55
  }
46
- const style = value ? styles['track:on'] : styles['track:off']
56
+ const style = fieldHandle?.value ? styles['track:on'] : styles['track:off']
47
57
 
48
58
  return {
49
59
  ...style,
50
60
  ...disabledStyle,
51
61
  }
52
62
  },
53
- dependencies: [value, disabled],
63
+ dependencies: [fieldHandle?.value, disabled],
54
64
  })
55
65
 
56
66
  const thumbAnimation = useAnimatedVariantStyles({
@@ -61,29 +71,33 @@ export const Switch = (props: SwitchProps) => {
61
71
  'worklet'
62
72
  let disabledStyle = {}
63
73
  if (disabled) {
64
- disabledStyle = value ? styles['thumb:disabled-on'] : styles['thumb:disabled-off']
74
+ disabledStyle = fieldHandle?.value ? styles['thumb:disabled-on'] : styles['thumb:disabled-off']
65
75
  }
66
- const style = value ? styles['thumb:on'] : styles['thumb:off']
76
+ const style = fieldHandle?.value ? styles['thumb:on'] : styles['thumb:off']
67
77
  return {
68
78
  ...style,
69
79
  ...disabledStyle,
70
80
  }
71
81
 
72
82
  },
73
- dependencies: [value, disabled],
83
+ dependencies: [fieldHandle?.value, disabled],
74
84
  })
75
85
 
76
86
  // @ts-expect-error
77
87
  const _switchOnLeft = switchOnLeft ?? styles?.__props?.switchOnLeft
78
88
 
89
+ const hasError = validation.showError || forceError
90
+
79
91
  return <InputBase
80
92
  {...inputBaseProps}
93
+ ref={wrapperRef}
81
94
  debugName={debugName}
82
95
  wrapper={Touchable}
96
+ error={hasError ? validation.message || forceError : null}
83
97
  style={styles}
84
98
  wrapperProps={{
85
99
  onPress: () => {
86
- onValueChange(!value)
100
+ fieldHandle.setValue(!fieldHandle?.value)
87
101
  },
88
102
  disabled,
89
103
  rippleDisabled: true,
@@ -1,12 +1,15 @@
1
1
  import { StyledProp } from '@codeleap/styles'
2
2
  import { InputBaseProps } from '../InputBase'
3
3
  import { SwitchComposition } from './styles'
4
+ import { BooleanField } from '@codeleap/form'
4
5
 
5
6
  export type SwitchProps =
6
7
  Omit<InputBaseProps, 'style'> &
7
8
  {
8
- value: boolean
9
- onValueChange: (value: boolean) => void
9
+ field?: BooleanField<any>
10
10
  style?: StyledProp<SwitchComposition>
11
11
  switchOnLeft?: boolean
12
+ forceError?: boolean
13
+ value?: boolean
14
+ onValueChange?: (value: boolean) => void
12
15
  }
@@ -1,9 +1,7 @@
1
- import React, { useState } from 'react'
2
- import { useValidate } from '@codeleap/form'
3
- import { useBooleanToggle } from '@codeleap/hooks'
1
+ import React from 'react'
4
2
  import { TypeGuards } from '@codeleap/types'
5
- import { forwardRef, useImperativeHandle } from 'react'
6
- import { TextInput as NativeTextInput, NativeSyntheticEvent, TextInputFocusEventData } from 'react-native'
3
+ import { forwardRef } from 'react'
4
+ import { TextInput as NativeTextInput } from 'react-native'
7
5
  import { InputBase, selectInputBaseProps } from '../InputBase'
8
6
  import { Touchable } from '../Touchable'
9
7
  import { MaskedTextInput } from '../../modules/textInputMask'
@@ -11,31 +9,29 @@ import { AnyRecord, AppIcon, IJSX, StyledComponentProps, StyledComponentWithProp
11
9
  import { TextInputProps } from './types'
12
10
  import { MobileStyleRegistry } from '../../Registry'
13
11
  import { useStylesFor } from '../../hooks'
12
+ import { useTextInput } from './useTextInput'
13
+ import { useInputBasePartialStyles } from '../InputBase/useInputBasePartialStyles'
14
14
 
15
15
  export * from './styles'
16
16
  export * from './types'
17
17
 
18
18
  export const TextInput = forwardRef<NativeTextInput, TextInputProps>((props, inputRef) => {
19
- const innerInputRef = React.useRef<NativeTextInput>(null)
20
-
21
- const [isFocused, setIsFocused] = useState(false)
22
- const [currentSelection, setCurrentSelection] = useState({ start: 0 })
19
+ const allProps = {
20
+ ...TextInput.defaultProps,
21
+ ...props,
22
+ }
23
23
 
24
24
  const {
25
25
  inputBaseProps,
26
26
  others,
27
- } = selectInputBaseProps({
28
- ...TextInput.defaultProps,
29
- ...props,
30
- })
27
+ } = selectInputBaseProps(allProps)
31
28
 
32
29
  const {
33
- value,
34
- validate,
35
30
  debugName,
36
31
  visibilityToggle,
37
32
  masking,
38
- password,
33
+ secure,
34
+ field,
39
35
  onChangeMask,
40
36
  onPress,
41
37
  visibleIcon,
@@ -43,70 +39,42 @@ export const TextInput = forwardRef<NativeTextInput, TextInputProps>((props, inp
43
39
  style,
44
40
  autoAdjustSelection,
45
41
  selectionStart,
46
- _error = null,
42
+ forceError,
43
+ onChangeText,
44
+ multiline,
47
45
  ...textInputProps
48
46
  } = others
49
47
 
50
- const [secureTextEntry, toggleSecureTextEntry] = useBooleanToggle(true)
51
-
52
- const isMasked = !!masking
53
-
54
- const InputElement = isMasked ? MaskedTextInput : NativeTextInput
55
-
56
48
  const styles = useStylesFor(TextInput.styleRegistryName, style)
57
49
 
58
- // @ts-expect-error - React's ref type system is weird
59
- useImperativeHandle(inputRef, () => {
60
- return {
61
- ...innerInputRef.current,
62
- focus: () => {
63
- innerInputRef.current?.focus?.()
64
- },
65
- isTextInput: true,
66
- }
67
- }, [!!innerInputRef?.current?.focus])
50
+ const {
51
+ fieldHandle,
52
+ validation,
53
+ innerInputRef,
54
+ wrapperRef,
55
+ isFocused,
56
+ secureTextEntry,
57
+ currentSelection,
58
+ hasMultipleLines,
59
+ hasValue,
60
+ hasError,
61
+ toggleSecureTextEntry,
62
+ handleMaskChange,
63
+ handleBlur,
64
+ handleFocus,
65
+ } = useTextInput(allProps)
66
+
67
+ const InputElement = masking ? MaskedTextInput : NativeTextInput
68
68
 
69
69
  const isPressable = TypeGuards.isFunction(onPress)
70
70
 
71
- const validation = useValidate(value, validate)
72
-
73
- const handleBlur = React.useCallback((e: NativeSyntheticEvent<TextInputFocusEventData>) => {
74
- validation.onInputBlurred()
75
- setIsFocused(false)
76
- if (autoAdjustSelection) setCurrentSelection({ start: selectionStart })
77
- props.onBlur?.(e)
78
- }, [validation.onInputBlurred, props.onBlur])
79
-
80
- const handleFocus = React.useCallback((e: NativeSyntheticEvent<TextInputFocusEventData>) => {
81
- validation.onInputFocused()
82
- setIsFocused(true)
83
- if (autoAdjustSelection) setCurrentSelection(null)
84
- props.onFocus?.(e)
85
- }, [validation.onInputFocused, props.onFocus])
86
-
87
- const handleMaskChange = (masked, unmasked) => {
88
- if (textInputProps.onChangeText) textInputProps.onChangeText(masking?.saveFormatted ? masked : masked)
89
- if (onChangeMask) onChangeMask(masked, unmasked)
90
- }
91
-
92
- const isMultiline = textInputProps.multiline
93
71
  const isDisabled = !!inputBaseProps.disabled
94
72
 
95
- const placeholderTextColor = [
96
- [isDisabled, styles['placeholder:disabled']],
97
- [!validation.isValid, styles['placeholder:error']],
98
- [isFocused, styles['placeholder:focus']],
99
- [true, styles?.placeholder],
100
- // @ts-expect-error
101
- ].find(([x]) => x)?.[1]?.color
102
-
103
- const selectionColor = [
104
- [isDisabled, styles['selection:disabled']],
105
- [!validation.isValid, styles['selection:error']],
106
- [isFocused, styles['selection:focus']],
107
- [true, styles?.selection],
108
- // @ts-expect-error
109
- ].find(([x]) => x)?.[1]?.color
73
+ const partialStyles = useInputBasePartialStyles(styles, ['placeholder', 'selection'], {
74
+ disabled: isDisabled,
75
+ error: !!hasError,
76
+ focus: isFocused,
77
+ })
110
78
 
111
79
  const visibilityToggleProps = visibilityToggle ? {
112
80
  onPress: toggleSecureTextEntry,
@@ -116,36 +84,33 @@ export const TextInput = forwardRef<NativeTextInput, TextInputProps>((props, inp
116
84
 
117
85
  const rightIcon = inputBaseProps?.rightIcon ?? visibilityToggleProps
118
86
 
119
- const maskingExtraProps = isMasked ? {
87
+ const maskingExtraProps = masking ? {
120
88
  onChangeText: handleMaskChange,
121
89
  ref: null,
122
90
  refInput: (inputRef) => {
123
- if (!!inputRef) {
124
- innerInputRef.current = inputRef
125
- }
91
+ if (!!inputRef) innerInputRef.current = inputRef
126
92
  },
127
93
  ...masking,
128
- } : {}
94
+ } : {
95
+ onChangeText: fieldHandle.setValue
96
+ }
129
97
 
130
98
  const buttonModeProps = isPressable ? {
131
99
  editable: false,
132
100
  caretHidden: true,
133
101
  } : {}
134
102
 
135
- const hasMultipleLines = isMultiline && value?.includes('\n')
136
-
137
- const hasValue = value?.length > 0
138
-
139
103
  return <InputBase
140
104
  {...inputBaseProps}
105
+ ref={wrapperRef}
141
106
  innerWrapper={isPressable ? Touchable : undefined}
142
107
  debugName={debugName}
143
- error={(validation.isValid && !_error) ? null : _error || validation.message}
108
+ error={hasError ? validation.message || forceError : null}
144
109
  style={{
145
110
  ...styles,
146
111
  innerWrapper: [
147
112
  styles?.innerWrapper,
148
- isMultiline && styles['innerWrapper:multiline'],
113
+ multiline && styles['innerWrapper:multiline'],
149
114
  hasMultipleLines && styles['innerWrapper:hasMultipleLines'],
150
115
  ],
151
116
  }}
@@ -164,23 +129,23 @@ export const TextInput = forwardRef<NativeTextInput, TextInputProps>((props, inp
164
129
  editable={!isPressable && !isDisabled}
165
130
  {...buttonModeProps}
166
131
  selection={autoAdjustSelection ? currentSelection : undefined}
167
- placeholderTextColor={placeholderTextColor}
168
- value={value}
169
- selectionColor={selectionColor}
170
- secureTextEntry={password && secureTextEntry}
171
- textAlignVertical={isMultiline ? 'top' : undefined}
132
+ placeholderTextColor={partialStyles?.placeholder?.color}
133
+ value={fieldHandle?.value}
134
+ selectionColor={partialStyles?.selection?.color}
135
+ secureTextEntry={secure && secureTextEntry}
136
+ textAlignVertical={multiline ? 'top' : undefined}
137
+ multiline={multiline}
172
138
  {...textInputProps}
173
139
  onBlur={handleBlur}
174
140
  onFocus={handleFocus}
175
141
  style={[
176
142
  styles?.input,
177
- isMultiline && styles['input:multiline'],
143
+ multiline && styles['input:multiline'],
178
144
  isFocused && styles['input:focused'],
179
- !validation.isValid && styles['input:error'],
145
+ hasError && styles['input:error'],
180
146
  isDisabled && styles['input:disabled'],
181
147
  hasMultipleLines && styles['input:hasMultipleLines'],
182
148
  hasValue && styles['input:typed'],
183
-
184
149
  ]}
185
150
  ref={innerInputRef}
186
151
  pointerEvents={isPressable ? 'none' : undefined}
@@ -203,6 +168,7 @@ TextInput.defaultProps = {
203
168
  visibilityToggle: false,
204
169
  autoAdjustSelection: false,
205
170
  selectionStart: 0,
171
+ secure: false,
206
172
  } as Partial<TextInputProps>
207
173
 
208
174
  MobileStyleRegistry.registerComponent(TextInput)
@@ -1,27 +1,29 @@
1
1
  import { InputBaseProps } from '../InputBase'
2
2
  import { TextInputProps as RNTextInputProps, TextInput as RNTextInput } from 'react-native'
3
3
  import { AnyFunction } from '@codeleap/types'
4
- import { FormTypes, yup } from '@codeleap/form'
5
4
  import { AppIcon, StyledProp } from '@codeleap/styles'
6
5
  import { TextInputMaskProps } from '../../modules/textInputMask'
7
6
  import { TextInputComposition } from './styles'
7
+ import { Field } from '@codeleap/form'
8
8
 
9
9
  export type TextInputProps =
10
- Omit<InputBaseProps, 'style'> &
10
+ Omit<InputBaseProps, 'style' | 'ref'> &
11
11
  Omit<RNTextInputProps, 'style'> &
12
12
  {
13
- password?: boolean
14
- validate?: FormTypes.ValidatorFunctionWithoutForm | yup.SchemaOf<string>
13
+ secure?: boolean
15
14
  debugName: string
16
15
  autoAdjustSelection?: boolean
17
16
  selectionStart?: number
18
17
  visibilityToggle?: boolean
19
- masking?: FormTypes.TextField['masking']
20
18
  onChangeMask?: TextInputMaskProps['onChangeText']
19
+ masking?: TextInputMaskProps['masking']
21
20
  visibleIcon?: AppIcon
22
21
  hiddenIcon?: AppIcon
23
- _error?: string
22
+ forceError?: string
24
23
  onPress?: AnyFunction
25
24
  style?: StyledProp<TextInputComposition>
26
25
  ref?: React.Ref<RNTextInput>
27
- }
26
+ field?: Field<string, any, any>
27
+ value?: string
28
+ onValueChange?: (value: string) => void
29
+ }
@@ -0,0 +1,88 @@
1
+ import { useCallback, useState } from 'react'
2
+ import { TextInputProps } from './types'
3
+ import { NativeSyntheticEvent, TextInputFocusEventData } from 'react-native/types'
4
+ import { useInputBase } from '../InputBase/useInputBase'
5
+ import { fields } from '@codeleap/form'
6
+
7
+ export function useTextInput(props: Partial<TextInputProps>) {
8
+ const {
9
+ onFocus,
10
+ onBlur,
11
+ secure,
12
+ field,
13
+ autoAdjustSelection,
14
+ selectionStart,
15
+ masking,
16
+ onChangeMask,
17
+ multiline,
18
+ forceError,
19
+ value,
20
+ onValueChange,
21
+ } = props
22
+
23
+ const [isFocused, setIsFocused] = useState(false)
24
+
25
+ const [currentSelection, setCurrentSelection] = useState({ start: 0 })
26
+
27
+ const [secureTextEntry, setSecureTextEntry] = useState(secure)
28
+
29
+ const toggleSecureTextEntry = () => setSecureTextEntry(s => !s)
30
+
31
+ const {
32
+ fieldHandle,
33
+ validation,
34
+ innerInputRef,
35
+ wrapperRef,
36
+ } = useInputBase<string>(field, fields.text, [value, onValueChange], {
37
+ revealValue() {
38
+ setSecureTextEntry(false)
39
+ },
40
+ hideValue() {
41
+ setSecureTextEntry(true)
42
+ },
43
+ toggleValueVisibility() {
44
+ toggleSecureTextEntry()
45
+ },
46
+ }, [setSecureTextEntry])
47
+
48
+ const handleBlur = useCallback((e: NativeSyntheticEvent<TextInputFocusEventData>) => {
49
+ validation.onInputBlurred()
50
+ setIsFocused(false)
51
+ if (autoAdjustSelection) setCurrentSelection({ start: selectionStart })
52
+ onBlur?.(e)
53
+ }, [validation.onInputBlurred, onBlur])
54
+
55
+ const handleFocus = useCallback((e: NativeSyntheticEvent<TextInputFocusEventData>) => {
56
+ setIsFocused(true)
57
+ if (autoAdjustSelection) setCurrentSelection(null)
58
+ onFocus?.(e)
59
+ }, [onFocus])
60
+
61
+ const handleMaskChange = useCallback((masked, unmasked) => {
62
+ fieldHandle.setValue(masking?.saveFormatted ? masked : masked)
63
+ if (onChangeMask) onChangeMask(masked, unmasked)
64
+ }, [masking?.saveFormatted, onChangeMask])
65
+
66
+ const hasMultipleLines = multiline && fieldHandle?.value?.includes('\n')
67
+
68
+ const hasValue = fieldHandle?.value?.length > 0
69
+
70
+ const hasError = validation.showError || forceError
71
+
72
+ return {
73
+ isFocused,
74
+ currentSelection,
75
+ secureTextEntry,
76
+ handleBlur,
77
+ handleFocus,
78
+ handleMaskChange,
79
+ fieldHandle,
80
+ validation,
81
+ innerInputRef,
82
+ wrapperRef,
83
+ toggleSecureTextEntry,
84
+ hasMultipleLines,
85
+ hasValue,
86
+ hasError,
87
+ }
88
+ }
@@ -1,6 +1,6 @@
1
1
  import React, { forwardRef } from 'react'
2
2
  import { TypeGuards } from '@codeleap/types'
3
- import { onMount } from '@codeleap/hooks'
3
+ import { onMount, useComponentTestId } from '@codeleap/hooks'
4
4
  import { useGlobalContext } from '@codeleap/hooks'
5
5
  import { Pressable, StyleSheet, View as RNView, Insets, Platform } from 'react-native'
6
6
  import { View } from '../View'
@@ -51,6 +51,8 @@ export const Touchable = forwardRef<RNView, TouchableProps>((touchableProps, ref
51
51
 
52
52
  const { logger } = useGlobalContext()
53
53
 
54
+ const testId = useComponentTestId(Touchable, touchableProps, ['style', 'children', 'debounce'])
55
+
54
56
  const press = () => {
55
57
  if (!onPress) return
56
58
 
@@ -187,6 +189,7 @@ export const Touchable = forwardRef<RNView, TouchableProps>((touchableProps, ref
187
189
  {!disableRipple ? (
188
190
  <PressableRipple
189
191
  onPress={press}
192
+ testID={testId}
190
193
  {...props}
191
194
  style={[pressableStyle, styles?.pressable]}
192
195
  rippleFades={false}
@@ -207,6 +210,7 @@ export const Touchable = forwardRef<RNView, TouchableProps>((touchableProps, ref
207
210
  getFeedbackStyle(pressed),
208
211
  styles?.pressable,
209
212
  ])}
213
+ testID={testId}
210
214
  {...props}
211
215
  ref={ref}
212
216
  >