@neko-os/ui 0.0.13 → 0.2.0

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 (239) hide show
  1. package/dist/NekoUI.js +1 -1
  2. package/dist/abstractions/FlatList.native.js +1 -1
  3. package/dist/abstractions/KeyboardAvoidingView.js +1 -0
  4. package/dist/abstractions/KeyboardAvoidingView.native.js +1 -0
  5. package/dist/abstractions/ScrollView.native.js +1 -1
  6. package/dist/components/actions/ActionsDrawer.js +1 -0
  7. package/dist/components/actions/Button.js +1 -1
  8. package/dist/components/actions/FloatingMenu.js +1 -0
  9. package/dist/components/actions/index.js +1 -1
  10. package/dist/components/animations/AnimatedTopBar.js +1 -0
  11. package/dist/components/animations/AnimatedTopBar.native.js +1 -0
  12. package/dist/components/animations/AnimatedTopBar.web.js +1 -0
  13. package/dist/components/animations/ParallaxHeader.js +1 -0
  14. package/dist/components/animations/ParallaxHeader.native.js +1 -0
  15. package/dist/components/animations/ParallaxHeader.web.js +1 -0
  16. package/dist/components/animations/ReanimatedScrollHandler.js +1 -0
  17. package/dist/components/animations/ReanimatedScrollHandler.native.js +1 -0
  18. package/dist/components/animations/ReanimatedScrollHandler.web.js +1 -0
  19. package/dist/components/animations/index.js +1 -1
  20. package/dist/components/feedback/alerter.js +1 -1
  21. package/dist/components/feedback/confirmer.js +1 -1
  22. package/dist/components/form/FormItem.js +1 -1
  23. package/dist/components/form/FormList.js +1 -1
  24. package/dist/components/form/SubmitButton.js +1 -1
  25. package/dist/components/form/index.js +1 -1
  26. package/dist/components/form/useNewForm.js +1 -1
  27. package/dist/components/form/validation/defaultMessages.js +1 -0
  28. package/dist/components/form/validation/index.js +1 -0
  29. package/dist/components/form/validation/normalizeRules.js +1 -0
  30. package/dist/components/form/validation/shouldValidateOn.js +1 -0
  31. package/dist/components/form/validation/validateRules.js +1 -0
  32. package/dist/components/form/validation/validators.js +1 -0
  33. package/dist/components/index.js +1 -1
  34. package/dist/components/inputs/InputWrapper.js +1 -1
  35. package/dist/components/inputs/NumberInput.js +1 -1
  36. package/dist/components/inputs/Picker.js +1 -1
  37. package/dist/components/inputs/Select.js +1 -1
  38. package/dist/components/modals/bottomDrawer/index.js +1 -0
  39. package/dist/components/modals/bottomDrawer/index.native.js +1 -0
  40. package/dist/components/modals/bottomDrawer/index.web.js +1 -0
  41. package/dist/components/modals/bottomDrawer/native/BottomDrawer.js +1 -0
  42. package/dist/components/modals/bottomDrawer/native/DrawerContext.js +1 -0
  43. package/dist/components/modals/bottomDrawer/native/DrawerHandle.js +1 -0
  44. package/dist/components/modals/bottomDrawer/native/DrawerScrollView.js +1 -0
  45. package/dist/components/modals/bottomDrawer/native/createDrawerScrollComponent.js +1 -0
  46. package/dist/components/modals/bottomDrawer/web/BottomDrawer.js +1 -0
  47. package/dist/components/modals/drawer/Drawer.js +1 -0
  48. package/dist/components/modals/index.js +1 -0
  49. package/dist/components/modals/modal/Modal.js +1 -0
  50. package/dist/components/modals/modal/Modal.native.js +1 -0
  51. package/dist/components/modals/modal/ModalBackdrop.js +1 -0
  52. package/dist/components/modals/modal/ModalContent.js +1 -0
  53. package/dist/components/modals/modal/ModalFooter.js +1 -0
  54. package/dist/components/modals/modal/ModalHeader.js +1 -0
  55. package/dist/components/modals/modal/handler/ModalsHandler.js +1 -0
  56. package/dist/components/modals/router/ModalRoute.js +1 -0
  57. package/dist/components/modals/router/ModalsRouter.js +1 -0
  58. package/dist/components/modals/router/ModalsRouterContext.js +1 -0
  59. package/dist/components/modals/router/index.js +1 -0
  60. package/dist/components/modals/router/useAllModalsParams.js +1 -0
  61. package/dist/components/modals/router/useModalParams.js +1 -0
  62. package/dist/components/modals/router/useModalsNavigation.js +1 -0
  63. package/dist/components/modals/router/useUpdateModalContainer.js +1 -0
  64. package/dist/components/presentation/Avatar.js +1 -1
  65. package/dist/components/presentation/AvatarLabel.js +1 -1
  66. package/dist/components/presentation/LabelValue.js +1 -1
  67. package/dist/components/presentation/Result.js +1 -1
  68. package/dist/components/presentation/Tooltip.js +1 -1
  69. package/dist/components/sections/Section.js +1 -0
  70. package/dist/components/sections/SectionItem.js +1 -0
  71. package/dist/components/sections/SectionItemDropdown.js +1 -0
  72. package/dist/components/sections/SectionItemLink.js +1 -0
  73. package/dist/components/sections/index.js +1 -0
  74. package/dist/components/state/StatePresenter.js +1 -0
  75. package/dist/components/state/index.js +1 -1
  76. package/dist/components/structure/BlurView.js +1 -1
  77. package/dist/components/structure/KeyboardAvoidingView.js +1 -0
  78. package/dist/components/structure/TopBar.js +1 -0
  79. package/dist/components/structure/index.js +1 -1
  80. package/dist/components/structure/popover/Popover.js +1 -1
  81. package/dist/components/structure/popover/Popover.native.js +1 -1
  82. package/dist/components/structure/popover/Popover_BU.js +1 -1
  83. package/dist/components/text/DateText.js +1 -0
  84. package/dist/components/text/index.js +1 -1
  85. package/dist/components/theme/ThemePicker.js +1 -1
  86. package/dist/components/theme/ThemePickerDrawer.js +1 -1
  87. package/dist/helpers/index.js +1 -1
  88. package/dist/helpers/storage.js +1 -1
  89. package/dist/responsive/responsiveHooks.js +1 -1
  90. package/dist/theme/ThemeHandler.js +1 -1
  91. package/dist/theme/default/base.js +1 -1
  92. package/dist/theme/default/blackTheme.js +1 -1
  93. package/dist/theme/default/cyberpunkTheme.js +1 -1
  94. package/dist/theme/default/darkTheme.js +1 -1
  95. package/dist/theme/default/hackerTheme.js +1 -1
  96. package/dist/theme/default/lightTheme.js +1 -1
  97. package/dist/theme/default/paperTheme.js +1 -1
  98. package/dist/theme/default/themes.js +1 -1
  99. package/package.json +1 -1
  100. package/src/NekoUI.js +1 -1
  101. package/src/abstractions/FlatList.native.js +2 -1
  102. package/src/abstractions/KeyboardAvoidingView.js +3 -0
  103. package/src/abstractions/KeyboardAvoidingView.native.js +3 -0
  104. package/src/abstractions/ScrollView.native.js +2 -2
  105. package/src/components/actions/ActionsDrawer.js +68 -0
  106. package/src/components/actions/Button.js +2 -1
  107. package/src/components/actions/FloatingMenu.js +39 -0
  108. package/src/components/actions/index.js +2 -0
  109. package/src/components/animations/AnimatedTopBar.js +10 -0
  110. package/src/components/animations/AnimatedTopBar.native.js +34 -0
  111. package/src/components/animations/AnimatedTopBar.web.js +1 -0
  112. package/src/components/animations/ParallaxHeader.js +9 -0
  113. package/src/components/animations/ParallaxHeader.native.js +32 -0
  114. package/src/components/animations/ParallaxHeader.web.js +32 -0
  115. package/src/components/animations/ReanimatedScrollHandler.js +8 -0
  116. package/src/components/animations/ReanimatedScrollHandler.native.js +24 -0
  117. package/src/components/animations/ReanimatedScrollHandler.web.js +1 -0
  118. package/src/components/animations/index.js +3 -0
  119. package/src/components/feedback/alerter.js +1 -1
  120. package/src/components/feedback/confirmer.js +1 -1
  121. package/src/components/form/FormItem.js +42 -5
  122. package/src/components/form/FormList.js +23 -4
  123. package/src/components/form/SubmitButton.js +4 -2
  124. package/src/components/form/index.js +1 -0
  125. package/src/components/form/useNewForm.js +108 -15
  126. package/src/components/form/validation/defaultMessages.js +20 -0
  127. package/src/components/form/validation/index.js +5 -0
  128. package/src/components/form/validation/normalizeRules.js +22 -0
  129. package/src/components/form/validation/shouldValidateOn.js +21 -0
  130. package/src/components/form/validation/validateRules.js +83 -0
  131. package/src/components/form/validation/validators.js +82 -0
  132. package/src/components/index.js +2 -0
  133. package/src/components/inputs/InputWrapper.js +1 -1
  134. package/src/components/inputs/NumberInput.js +6 -5
  135. package/src/components/inputs/Picker.js +3 -2
  136. package/src/components/inputs/Select.js +31 -15
  137. package/src/components/modals/bottomDrawer/index.js +3 -0
  138. package/src/components/{structure → modals}/bottomDrawer/index.native.js +2 -1
  139. package/src/components/{structure → modals}/bottomDrawer/index.web.js +2 -1
  140. package/src/components/{structure → modals}/bottomDrawer/native/BottomDrawer.js +15 -21
  141. package/src/components/{structure → modals}/bottomDrawer/native/DrawerHandle.js +1 -1
  142. package/src/components/modals/bottomDrawer/native/DrawerScrollView.js +5 -0
  143. package/src/components/modals/bottomDrawer/native/createDrawerScrollComponent.js +131 -0
  144. package/src/components/modals/index.js +4 -0
  145. package/src/components/{structure → modals}/modal/Modal.native.js +1 -1
  146. package/src/components/{structure → modals}/modal/ModalBackdrop.js +1 -1
  147. package/src/components/{structure → modals}/modal/ModalContent.js +1 -1
  148. package/src/components/{structure → modals}/modal/ModalFooter.js +1 -1
  149. package/src/components/{structure → modals}/modal/ModalHeader.js +1 -1
  150. package/src/components/modals/router/ModalRoute.js +15 -0
  151. package/src/components/modals/router/ModalsRouter.js +120 -0
  152. package/src/components/modals/router/ModalsRouterContext.js +16 -0
  153. package/src/components/modals/router/index.js +6 -0
  154. package/src/components/modals/router/useAllModalsParams.js +6 -0
  155. package/src/components/modals/router/useModalParams.js +6 -0
  156. package/src/components/modals/router/useModalsNavigation.js +6 -0
  157. package/src/components/modals/router/useUpdateModalContainer.js +6 -0
  158. package/src/components/presentation/Avatar.js +2 -2
  159. package/src/components/presentation/AvatarLabel.js +2 -0
  160. package/src/components/presentation/LabelValue.js +7 -5
  161. package/src/components/presentation/Result.js +2 -2
  162. package/src/components/presentation/Tooltip.js +1 -1
  163. package/src/components/sections/Section.js +50 -0
  164. package/src/components/sections/SectionItem.js +24 -0
  165. package/src/components/sections/SectionItemDropdown.js +68 -0
  166. package/src/components/sections/SectionItemLink.js +33 -0
  167. package/src/components/sections/index.js +4 -0
  168. package/src/components/state/StatePresenter.js +41 -0
  169. package/src/components/state/index.js +1 -0
  170. package/src/components/structure/BlurView.js +1 -0
  171. package/src/components/structure/KeyboardAvoidingView.js +52 -0
  172. package/src/components/structure/TopBar.js +45 -0
  173. package/src/components/structure/index.js +2 -3
  174. package/src/components/structure/popover/Popover.js +1 -1
  175. package/src/components/structure/popover/Popover.native.js +1 -1
  176. package/src/components/structure/popover/Popover_BU.js +1 -1
  177. package/src/components/text/DateText.js +11 -0
  178. package/src/components/text/index.js +1 -0
  179. package/src/components/theme/ThemePicker.js +1 -2
  180. package/src/components/theme/ThemePickerDrawer.js +3 -4
  181. package/src/helpers/index.js +1 -0
  182. package/src/helpers/storage.js +32 -9
  183. package/src/responsive/responsiveHooks.js +6 -0
  184. package/src/theme/ThemeHandler.js +6 -3
  185. package/src/theme/default/base.js +16 -4
  186. package/src/theme/default/blackTheme.js +33 -21
  187. package/src/theme/default/cyberpunkTheme.js +24 -22
  188. package/src/theme/default/darkTheme.js +1 -0
  189. package/src/theme/default/hackerTheme.js +40 -19
  190. package/src/theme/default/lightTheme.js +1 -0
  191. package/src/theme/default/paperTheme.js +14 -0
  192. package/src/theme/default/themes.js +0 -9
  193. package/dist/components/structure/bottomDrawer/index.js +0 -1
  194. package/dist/components/structure/bottomDrawer/index.native.js +0 -1
  195. package/dist/components/structure/bottomDrawer/index.web.js +0 -1
  196. package/dist/components/structure/bottomDrawer/native/BottomDrawer.js +0 -1
  197. package/dist/components/structure/bottomDrawer/native/DrawerContext.js +0 -1
  198. package/dist/components/structure/bottomDrawer/native/DrawerHandle.js +0 -1
  199. package/dist/components/structure/bottomDrawer/native/DrawerScrollView.js +0 -1
  200. package/dist/components/structure/bottomDrawer/web/BottomDrawer.js +0 -1
  201. package/dist/components/structure/drawer/Drawer.js +0 -1
  202. package/dist/components/structure/modal/Modal.js +0 -1
  203. package/dist/components/structure/modal/Modal.native.js +0 -1
  204. package/dist/components/structure/modal/ModalBackdrop.js +0 -1
  205. package/dist/components/structure/modal/ModalContent.js +0 -1
  206. package/dist/components/structure/modal/ModalFooter.js +0 -1
  207. package/dist/components/structure/modal/ModalHeader.js +0 -1
  208. package/dist/components/structure/modal/handler/ModalsHandler.js +0 -1
  209. package/dist/theme/default/deepWoodsTheme.js +0 -1
  210. package/dist/theme/default/forestTheme.js +0 -1
  211. package/dist/theme/default/midnightTheme.js +0 -1
  212. package/dist/theme/default/msdosTheme.js +0 -1
  213. package/dist/theme/default/oceanTheme.js +0 -1
  214. package/dist/theme/default/pastelTheme.js +0 -1
  215. package/dist/theme/default/sunsetTheme.js +0 -1
  216. package/src/components/structure/bottomDrawer/index.js +0 -1
  217. package/src/components/structure/bottomDrawer/native/DrawerScrollView.js +0 -83
  218. package/src/theme/default/deepWoodsTheme.js +0 -34
  219. package/src/theme/default/forestTheme.js +0 -34
  220. package/src/theme/default/midnightTheme.js +0 -34
  221. package/src/theme/default/msdosTheme.js +0 -55
  222. package/src/theme/default/oceanTheme.js +0 -34
  223. package/src/theme/default/pastelTheme.js +0 -34
  224. package/src/theme/default/sunsetTheme.js +0 -35
  225. /package/dist/components/{structure → modals}/bottomDrawer/native/utils.js +0 -0
  226. /package/dist/components/{structure → modals}/drawer/Drawer.native.js +0 -0
  227. /package/dist/components/{structure → modals}/drawer/Drawer.web.js +0 -0
  228. /package/dist/components/{structure → modals}/drawer/index.js +0 -0
  229. /package/dist/components/{structure → modals}/modal/index.js +0 -0
  230. /package/src/components/{structure → modals}/bottomDrawer/native/DrawerContext.js +0 -0
  231. /package/src/components/{structure → modals}/bottomDrawer/native/utils.js +0 -0
  232. /package/src/components/{structure → modals}/bottomDrawer/web/BottomDrawer.js +0 -0
  233. /package/src/components/{structure → modals}/drawer/Drawer.js +0 -0
  234. /package/src/components/{structure → modals}/drawer/Drawer.native.js +0 -0
  235. /package/src/components/{structure → modals}/drawer/Drawer.web.js +0 -0
  236. /package/src/components/{structure → modals}/drawer/index.js +0 -0
  237. /package/src/components/{structure → modals}/modal/Modal.js +0 -0
  238. /package/src/components/{structure → modals}/modal/handler/ModalsHandler.js +0 -0
  239. /package/src/components/{structure → modals}/modal/index.js +0 -0
@@ -0,0 +1,83 @@
1
+ import { path as getPath } from 'ramda'
2
+ import { validators } from './validators'
3
+
4
+ /**
5
+ * Validates a value against an array of rules
6
+ * @param {any} value - The value to validate
7
+ * @param {Array} rules - Array of rule objects
8
+ * @param {string} trigger - Current validation trigger ('onSubmit', 'onBlur', 'onChange')
9
+ * @returns {Promise<string|null>} - First error message or null if valid
10
+ */
11
+ export async function validateRules(value, rules, trigger = 'onSubmit') {
12
+ if (!rules || rules.length === 0) return null
13
+
14
+ for (const rule of rules) {
15
+ const ruleTrigger = rule.trigger || 'onSubmit'
16
+ const triggers = Array.isArray(ruleTrigger) ? ruleTrigger : [ruleTrigger]
17
+
18
+ // Always run on submit, otherwise check trigger match
19
+ if (trigger !== 'onSubmit' && !triggers.includes(trigger)) {
20
+ continue
21
+ }
22
+
23
+ let error = null
24
+
25
+ // Custom validator takes precedence
26
+ if (rule.validator) {
27
+ try {
28
+ await rule.validator(rule, value)
29
+ } catch (e) {
30
+ error = e.message || rule.message || 'Validation failed'
31
+ }
32
+ } else {
33
+ // Run built-in validators
34
+ if (rule.required) {
35
+ error = validators.required(value, rule)
36
+ }
37
+ if (!error && rule.type) {
38
+ error = validators.type(value, rule)
39
+ }
40
+ if (!error && rule.min !== undefined) {
41
+ error = validators.min(value, rule)
42
+ }
43
+ if (!error && rule.max !== undefined) {
44
+ error = validators.max(value, rule)
45
+ }
46
+ if (!error && rule.pattern) {
47
+ error = validators.pattern(value, rule)
48
+ }
49
+ }
50
+
51
+ if (error) {
52
+ return error
53
+ }
54
+ }
55
+
56
+ return null
57
+ }
58
+
59
+ /**
60
+ * Validates multiple fields at once (for form-level validation)
61
+ * @param {Object} values - All form values
62
+ * @param {Map} rulesRegistry - Map of field path keys to { path: array, rules: array }
63
+ * @returns {Promise<Object>} - Object of path key -> error message
64
+ */
65
+ export async function validateAllFields(values, rulesRegistry) {
66
+ const errors = {}
67
+ const validationPromises = []
68
+
69
+ rulesRegistry.forEach(({ path, rules }, pathKey) => {
70
+ const value = getPath(path, values)
71
+
72
+ validationPromises.push(
73
+ validateRules(value, rules, 'onSubmit').then((error) => {
74
+ if (error) {
75
+ errors[pathKey] = error
76
+ }
77
+ })
78
+ )
79
+ })
80
+
81
+ await Promise.all(validationPromises)
82
+ return errors
83
+ }
@@ -0,0 +1,82 @@
1
+ import { defaultMessages } from './defaultMessages'
2
+
3
+ const isEmpty = (value) => {
4
+ if (value === undefined || value === null) return true
5
+ if (typeof value === 'string' && value.trim() === '') return true
6
+ if (Array.isArray(value) && value.length === 0) return true
7
+ return false
8
+ }
9
+
10
+ export const validators = {
11
+ required: (value, rule) => {
12
+ if (isEmpty(value)) {
13
+ return rule.message || defaultMessages.required
14
+ }
15
+ return null
16
+ },
17
+
18
+ type: (value, rule) => {
19
+ if (isEmpty(value)) return null
20
+
21
+ const typeValidators = {
22
+ email: (v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v),
23
+ url: (v) => {
24
+ try {
25
+ new URL(v)
26
+ return true
27
+ } catch {
28
+ return false
29
+ }
30
+ },
31
+ number: (v) => !isNaN(Number(v)),
32
+ integer: (v) => Number.isInteger(Number(v)) && !isNaN(Number(v)),
33
+ }
34
+
35
+ const validator = typeValidators[rule.type]
36
+ if (validator && !validator(value)) {
37
+ return rule.message || defaultMessages.type[rule.type] || `Invalid ${rule.type}`
38
+ }
39
+ return null
40
+ },
41
+
42
+ min: (value, rule) => {
43
+ if (isEmpty(value)) return null
44
+
45
+ if (typeof value === 'string' && value.length < rule.min) {
46
+ return rule.message || defaultMessages.min.string(rule.min)
47
+ }
48
+ if (typeof value === 'number' && value < rule.min) {
49
+ return rule.message || defaultMessages.min.number(rule.min)
50
+ }
51
+ if (Array.isArray(value) && value.length < rule.min) {
52
+ return rule.message || defaultMessages.min.array(rule.min)
53
+ }
54
+ return null
55
+ },
56
+
57
+ max: (value, rule) => {
58
+ if (isEmpty(value)) return null
59
+
60
+ if (typeof value === 'string' && value.length > rule.max) {
61
+ return rule.message || defaultMessages.max.string(rule.max)
62
+ }
63
+ if (typeof value === 'number' && value > rule.max) {
64
+ return rule.message || defaultMessages.max.number(rule.max)
65
+ }
66
+ if (Array.isArray(value) && value.length > rule.max) {
67
+ return rule.message || defaultMessages.max.array(rule.max)
68
+ }
69
+ return null
70
+ },
71
+
72
+ pattern: (value, rule) => {
73
+ if (isEmpty(value)) return null
74
+
75
+ const regex = rule.pattern instanceof RegExp ? rule.pattern : new RegExp(rule.pattern)
76
+
77
+ if (!regex.test(String(value))) {
78
+ return rule.message || defaultMessages.pattern
79
+ }
80
+ return null
81
+ },
82
+ }
@@ -3,6 +3,7 @@ export * from './animations'
3
3
  export * from './form'
4
4
  export * from './presentation'
5
5
  export * from './structure'
6
+ export * from './modals'
6
7
  export * from './text'
7
8
  export * from './helpers'
8
9
  export * from './inputs'
@@ -14,3 +15,4 @@ export * from './calendar'
14
15
  export * from './list'
15
16
  export * from './tabs'
16
17
  export * from './theme'
18
+ export * from './sections'
@@ -48,7 +48,7 @@ export function InputWrapper({
48
48
  if (!!suffix && is(String, suffix)) suffix = <Text>{suffix}</Text>
49
49
  if (!prefix && !!prefixIcon) prefix = <Icon name={prefixIcon} size={sizeCode} color={prefixIconColor} />
50
50
  if (!suffix && !!suffixIcon) suffix = <Icon name={suffixIcon} size={sizeCode} color={suffixIconColor} />
51
- if (!prefix && !!error) suffix = <Icon name="close-circle-fill" size={sizeCode} red />
51
+ if (!prefix && !!error) suffix = <Icon name="alert-fill" size={sizeCode} red />
52
52
  if (!!loading) suffix = <Loading size={sizeCode} />
53
53
 
54
54
  let borderColor = !!hover ? 'primary_op40' : 'divider'
@@ -73,11 +73,11 @@ export function formatNumericValue(newValue, prevValue, options = {}) {
73
73
  return numericValue
74
74
  }
75
75
 
76
- export function NumberInput({ onChange, value, useInt, precision, min, max, error, ...props }) {
76
+ export function NumberInput({ onChange, onBlur, value, useInt, precision, min, max, error, ...props }) {
77
77
  const [hasError, setHasError] = React.useState(false)
78
78
  const [inputValue, setInputValue] = React.useState(value)
79
79
  const [localValue, setLocalValue] = React.useState(value)
80
- React.useEffect(() => setInputValue(value), [value])
80
+ React.useEffect(() => setInputValue(value?.toString() || ''), [value])
81
81
 
82
82
  if (useInt) precision = 0
83
83
  if (!useInt && precision === 0) useInt = true
@@ -85,6 +85,7 @@ export function NumberInput({ onChange, value, useInt, precision, min, max, erro
85
85
 
86
86
  return (
87
87
  <TextInput
88
+ {...props}
88
89
  onChange={(newValue) => {
89
90
  const numericValue = formatNumericValue(newValue, localValue, opts)
90
91
  setInputValue(newValue?.toString() || '')
@@ -92,14 +93,14 @@ export function NumberInput({ onChange, value, useInt, precision, min, max, erro
92
93
  onChange?.(numericValue)
93
94
  setHasError(!isValidNumber(newValue, opts))
94
95
  }}
95
- onBlur={() => {
96
- setInputValue(localValue)
96
+ onBlur={(e) => {
97
+ setInputValue(localValue?.toString() || '')
97
98
  setHasError(!isValidNumber(localValue, opts))
99
+ onBlur?.(e)
98
100
  }}
99
101
  value={inputValue}
100
102
  keyboardType={useInt ? 'number-pad' : 'decimal-pad'}
101
103
  error={error || hasError}
102
- {...props}
103
104
  />
104
105
  )
105
106
  }
@@ -2,9 +2,10 @@ import { is } from 'ramda'
2
2
  import React from 'react'
3
3
 
4
4
  import { Col } from '../structure/Col'
5
- import { FlatList } from '../list/FlatList'
5
+ import { DrawerFlatList } from '../modals/bottomDrawer'
6
6
  import { LoadingView } from '../state/LoadingView'
7
7
  import { Row } from '../structure/Row'
8
+ import { View } from '../structure'
8
9
  import { normalizeString } from '../../helpers/string'
9
10
  import { useOptions } from '../../helpers/options'
10
11
 
@@ -94,7 +95,7 @@ function DefaultPickerWrapper({ renderItem, options, ...props }) {
94
95
 
95
96
  function FlatListPickerWrapper({ renderItem, options, valueKey, ...props }) {
96
97
  return (
97
- <FlatList
98
+ <DrawerFlatList
98
99
  keyExtractor={(i) => i[valueKey]}
99
100
  data={options}
100
101
  divider
@@ -1,3 +1,4 @@
1
+ import { dissoc } from 'ramda'
1
2
  import React from 'react'
2
3
 
3
4
  import { Icon, IconLabel } from '../presentation'
@@ -38,6 +39,7 @@ export function Select({
38
39
  pickerProps,
39
40
  popoverProps,
40
41
  popoverMaxHeight,
42
+ snapPoints,
41
43
  ...props
42
44
  }) {
43
45
  const [focus, setFocus] = React.useState(false)
@@ -52,7 +54,17 @@ export function Select({
52
54
  onEndReached = onEndReached || pickerProps?.onEndReached
53
55
  renderFooter = renderFooter || pickerProps?.renderFooter
54
56
  renderHeader = renderHeader || pickerProps?.renderHeader
55
- pickerProps = { ...pickerProps, labelKey, valueKey, useRawOption, multiple, onEndReached, renderFooter, renderHeader }
57
+ pickerProps = {
58
+ ...pickerProps,
59
+
60
+ labelKey,
61
+ valueKey,
62
+ useRawOption,
63
+ multiple,
64
+ onEndReached,
65
+ renderFooter,
66
+ renderHeader,
67
+ }
56
68
 
57
69
  popoverMaxHeight = popoverMaxHeight || 300
58
70
 
@@ -93,7 +105,10 @@ export function Select({
93
105
  const finalRenderOption = React.useCallback(
94
106
  (params) => {
95
107
  if (!!renderOption) return renderOption(params)
96
- const { option, labelKey, selected } = params
108
+ let { option, labelKey, selected } = params
109
+ if (option?.id) option = dissoc('id', option)
110
+ if (option?.color) option = { ...option, color: undefined, iconColor: option.color }
111
+
97
112
  return <IconLabel {...option} label={option?.[labelKey]} flex strong={selected} />
98
113
  },
99
114
  [renderOption]
@@ -103,7 +118,7 @@ export function Select({
103
118
  <Popover
104
119
  trigger="click"
105
120
  placement={placement || 'bottomLeft'}
106
- snapPoints={[450]}
121
+ snapPoints={snapPoints || [450]}
107
122
  useBottomDrawer={useBottomDrawer}
108
123
  parentWidth
109
124
  padding={0}
@@ -112,17 +127,6 @@ export function Select({
112
127
  maxHeight={popoverMaxHeight}
113
128
  {...popoverProps}
114
129
  renderContent={({ onClose }) => (
115
- <>
116
- {useBottomDrawer && useSearch && (
117
- <View padding="md" paddingB="xs">
118
- <TextInput
119
- prefixIcon="search-line"
120
- prefixIconColor="text4"
121
- value={search}
122
- onChange={handleChangeSearch}
123
- />
124
- </View>
125
- )}
126
130
  <Picker
127
131
  row={false}
128
132
  options={searchOptions(options, search, { labelKey })}
@@ -137,6 +141,19 @@ export function Select({
137
141
  if (!multiple) onClose()
138
142
  }}
139
143
  {...pickerProps}
144
+ renderHeader={useBottomDrawer && useSearch ? () => (
145
+ <>
146
+ <View padding="md" paddingB="xs">
147
+ <TextInput
148
+ prefixIcon="search-line"
149
+ prefixIconColor="text4"
150
+ value={search}
151
+ onChange={handleChangeSearch}
152
+ />
153
+ </View>
154
+ {renderHeader?.()}
155
+ </>
156
+ ) : renderHeader}
140
157
  renderOption={({ option, selected, onChange }) => (
141
158
  <Link
142
159
  row
@@ -156,7 +173,6 @@ export function Select({
156
173
  </Link>
157
174
  )}
158
175
  />
159
- </>
160
176
  )}
161
177
  >
162
178
  <Input
@@ -0,0 +1,3 @@
1
+ export { BottomDrawer } from './web/BottomDrawer'
2
+ export { ScrollView as DrawerScrollView } from '../../list/ScrollView'
3
+ export { FlatList as DrawerFlatList } from '../../list/FlatList'
@@ -1,4 +1,5 @@
1
1
  export { BottomDrawer } from './native/BottomDrawer'
2
- export { DrawerScrollView } from './native/DrawerScrollView'
2
+ export { DrawerScrollView, DrawerFlatList } from './native/DrawerScrollView'
3
+ export { createDrawerScrollComponent } from './native/createDrawerScrollComponent'
3
4
  export { DrawerHandle } from './native/DrawerHandle'
4
5
  export { DrawerProvider, useDrawerContext } from './native/DrawerContext'
@@ -1,4 +1,5 @@
1
1
  export { BottomDrawer } from './native/BottomDrawer'
2
- export { DrawerScrollView } from './native/DrawerScrollView'
2
+ export { DrawerScrollView, DrawerFlatList } from './native/DrawerScrollView'
3
+ export { createDrawerScrollComponent } from './native/createDrawerScrollComponent'
3
4
  export { DrawerHandle } from './native/DrawerHandle'
4
5
  export { DrawerProvider, useDrawerContext } from './native/DrawerContext'
@@ -6,14 +6,13 @@ import Animated, {
6
6
  useAnimatedStyle,
7
7
  withSpring,
8
8
  runOnJS,
9
- useAnimatedReaction,
10
9
  } from 'react-native-reanimated'
11
10
  import React from 'react'
12
11
 
13
12
  import { DrawerHandle } from './DrawerHandle'
14
13
  import { DrawerProvider } from './DrawerContext'
15
14
  import { Pressable } from '../../../actions/Pressable'
16
- import { View } from '../../View'
15
+ import { View } from '../../../structure/View'
17
16
  import { normalizeSnapPoints, findClosestSnapPoint } from './utils'
18
17
  import { useColors } from '../../../../theme/ThemeHandler'
19
18
 
@@ -47,9 +46,6 @@ function InnerContent({
47
46
  const colors = useColors()
48
47
 
49
48
  const translateY = useSharedValue(SCREEN_HEIGHT)
50
- const scrollOffset = useSharedValue(0)
51
- const scrollEnabled = useSharedValue(false)
52
- const isScrolling = useSharedValue(false)
53
49
  const snapIndex = useSharedValue(0)
54
50
  const velocityY = useSharedValue(0)
55
51
 
@@ -67,8 +63,6 @@ function InnerContent({
67
63
  snapIndex.value = 0
68
64
  } else {
69
65
  translateY.value = withSpring(SCREEN_HEIGHT, animationConfig, () => {
70
- scrollOffset.value = 0
71
- scrollEnabled.value = false
72
66
  runOnJS(setRender)(false)
73
67
  })
74
68
  snapIndex.value = -1
@@ -84,15 +78,6 @@ function InnerContent({
84
78
  return () => backHandler.remove()
85
79
  }, [open, onClose])
86
80
 
87
- useAnimatedReaction(
88
- () => translateY.value,
89
- (currentY) => {
90
- const atMaxSnapPoint = currentY <= SCREEN_HEIGHT - maxSnapPoint
91
- scrollEnabled.value = atMaxSnapPoint
92
- },
93
- []
94
- )
95
-
96
81
  let handleClose = React.useCallback(() => {
97
82
  onClose?.()
98
83
  }, [onClose])
@@ -112,9 +97,11 @@ function InnerContent({
112
97
 
113
98
  // contexto manual para gesto
114
99
  const gestureStartTranslateY = useSharedValue(0)
100
+ const panRef = React.useRef()
115
101
 
116
102
  const panGesture = React.useMemo(() => {
117
103
  return Gesture.Pan()
104
+ .withRef(panRef)
118
105
  .enabled(enableHandlePanningGesture || enableContentPanningGesture)
119
106
  .onStart(() => {
120
107
  gestureStartTranslateY.value = translateY.value
@@ -135,7 +122,11 @@ function InnerContent({
135
122
  const currentPosition = SCREEN_HEIGHT - translateY.value
136
123
  const shouldClose =
137
124
  !!handleClose &&
138
- ((velocityY.value > 2000 && currentPosition < minSnapPoint * 0.75) || currentPosition < minSnapPoint * 0.25)
125
+ (
126
+ velocityY.value > 1500 ||
127
+ (velocityY.value > 800 && currentPosition < minSnapPoint) ||
128
+ currentPosition < minSnapPoint * 0.35
129
+ )
139
130
 
140
131
  if (shouldClose) {
141
132
  runOnJS(handleClose)()
@@ -168,15 +159,18 @@ function InnerContent({
168
159
  const contextValue = React.useMemo(
169
160
  () => ({
170
161
  translateY,
171
- scrollOffset,
172
- scrollEnabled,
173
- isScrolling,
174
162
  snapIndex,
175
163
  maxSnapPoint,
176
164
  snapTo,
177
165
  animationConfig,
166
+ panRef,
167
+ normalizedSnapPoints,
168
+ SCREEN_HEIGHT,
169
+ minSnapPoint,
170
+ handleClose,
171
+ velocityY,
178
172
  }),
179
- [maxSnapPoint]
173
+ [maxSnapPoint, normalizedSnapPoints, SCREEN_HEIGHT, minSnapPoint, handleClose]
180
174
  )
181
175
 
182
176
  return (
@@ -1,5 +1,5 @@
1
1
  import { Divider } from '../../../helpers/Separator'
2
- import { View } from '../../View'
2
+ import { View } from '../../../structure/View'
3
3
 
4
4
  export function DrawerHandle({ hide }) {
5
5
  if (!!hide) return false
@@ -0,0 +1,5 @@
1
+ import { FlatList, ScrollView } from '../../../list'
2
+ import { createDrawerScrollComponent } from './createDrawerScrollComponent'
3
+
4
+ export const DrawerScrollView = createDrawerScrollComponent(ScrollView)
5
+ export const DrawerFlatList = createDrawerScrollComponent(FlatList)
@@ -0,0 +1,131 @@
1
+ import { Gesture, GestureDetector } from 'react-native-gesture-handler'
2
+ import Animated, {
3
+ useSharedValue,
4
+ useAnimatedScrollHandler,
5
+ useAnimatedRef,
6
+ scrollTo,
7
+ withSpring,
8
+ runOnJS,
9
+ } from 'react-native-reanimated'
10
+ import React from 'react'
11
+
12
+ import { findClosestSnapPoint, clamp } from './utils'
13
+ import { useDrawerContext } from './DrawerContext'
14
+
15
+ export function createDrawerScrollComponent(Component) {
16
+ const AnimatedComponent = Animated.createAnimatedComponent(Component)
17
+
18
+ function DrawerScrollComponent({ ref, onScroll, ...props }) {
19
+ const {
20
+ translateY,
21
+ panRef,
22
+ normalizedSnapPoints,
23
+ SCREEN_HEIGHT,
24
+ maxSnapPoint,
25
+ minSnapPoint,
26
+ handleClose,
27
+ animationConfig,
28
+ snapIndex,
29
+ } = useDrawerContext()
30
+
31
+ const scrollRef = useAnimatedRef()
32
+ const scrollOffset = useSharedValue(0)
33
+ const prevTranslationY = useSharedValue(0)
34
+ const drawerMoved = useSharedValue(false)
35
+
36
+ React.useImperativeHandle(ref, () => scrollRef.current)
37
+
38
+ const maxPosition = SCREEN_HEIGHT - maxSnapPoint
39
+
40
+ const panGesture = React.useMemo(() => {
41
+ return Gesture.Pan()
42
+ .activeOffsetY([-10, 10])
43
+ .blocksExternalGesture(panRef)
44
+ .onStart(() => {
45
+ prevTranslationY.value = 0
46
+ drawerMoved.value = false
47
+ })
48
+ .onUpdate((event) => {
49
+ const delta = event.translationY - prevTranslationY.value
50
+ prevTranslationY.value = event.translationY
51
+
52
+ const currentY = translateY.value
53
+ const atMaxSnap = currentY <= maxPosition + 1
54
+ const atScrollTop = scrollOffset.value <= 0
55
+
56
+ if (drawerMoved.value) {
57
+ const newY = clamp(currentY + delta, maxPosition, SCREEN_HEIGHT)
58
+ translateY.value = newY
59
+ scrollTo(scrollRef, 0, 0, false)
60
+
61
+ if (newY <= maxPosition + 1 && delta < 0) {
62
+ drawerMoved.value = false
63
+ }
64
+ } else if (!atMaxSnap) {
65
+ drawerMoved.value = true
66
+ const newY = clamp(currentY + delta, maxPosition, SCREEN_HEIGHT)
67
+ translateY.value = newY
68
+ scrollTo(scrollRef, 0, 0, false)
69
+ } else if (atScrollTop && delta > 0) {
70
+ drawerMoved.value = true
71
+ const newY = clamp(currentY + delta, maxPosition, SCREEN_HEIGHT)
72
+ translateY.value = newY
73
+ scrollTo(scrollRef, 0, 0, false)
74
+ }
75
+ })
76
+ .onEnd((event) => {
77
+ if (!drawerMoved.value) return
78
+
79
+ const currentPosition = SCREEN_HEIGHT - translateY.value
80
+ const velocity = event.velocityY
81
+
82
+ const shouldClose =
83
+ !!handleClose &&
84
+ (velocity > 1500 ||
85
+ (velocity > 800 && currentPosition < minSnapPoint) ||
86
+ currentPosition < minSnapPoint * 0.35)
87
+
88
+ if (shouldClose) {
89
+ runOnJS(handleClose)()
90
+ } else {
91
+ const closestSnapIndex = findClosestSnapPoint(currentPosition, normalizedSnapPoints, velocity)
92
+ const targetSnapPoint = normalizedSnapPoints[closestSnapIndex]
93
+ translateY.value = withSpring(SCREEN_HEIGHT - targetSnapPoint, animationConfig)
94
+ snapIndex.value = closestSnapIndex
95
+ }
96
+ })
97
+ }, [panRef, maxPosition, normalizedSnapPoints, minSnapPoint, handleClose, animationConfig])
98
+
99
+ const nativeGesture = React.useMemo(() => Gesture.Native(), [])
100
+
101
+ const composedGesture = React.useMemo(
102
+ () => Gesture.Simultaneous(panGesture, nativeGesture),
103
+ [panGesture, nativeGesture]
104
+ )
105
+
106
+ const animatedScrollHandler = useAnimatedScrollHandler({
107
+ onScroll: (event) => {
108
+ scrollOffset.value = event.contentOffset.y
109
+ if (onScroll) {
110
+ runOnJS(onScroll)(event)
111
+ }
112
+ },
113
+ })
114
+
115
+ return (
116
+ <GestureDetector gesture={composedGesture}>
117
+ <AnimatedComponent
118
+ ref={scrollRef}
119
+ style={{ flex: 1 }}
120
+ onScroll={animatedScrollHandler}
121
+ scrollEventThrottle={16}
122
+ bounces={false}
123
+ overScrollMode="never"
124
+ {...props}
125
+ />
126
+ </GestureDetector>
127
+ )
128
+ }
129
+
130
+ return DrawerScrollComponent
131
+ }
@@ -0,0 +1,4 @@
1
+ export * from './modal'
2
+ export * from './drawer'
3
+ export * from './bottomDrawer'
4
+ export * from './router'
@@ -6,7 +6,7 @@ import { ModalContent } from './ModalContent'
6
6
  import { ModalFooter } from './ModalFooter'
7
7
  import { ModalHeader } from './ModalHeader'
8
8
  import { Pressable } from '../../actions/Pressable'
9
- import { View } from '../View'
9
+ import { View } from '../../structure/View'
10
10
  import { useDefaultModifier } from '../../../modifiers/default'
11
11
  import { useThemeComponentModifier } from '../../../modifiers/themeComponent'
12
12
 
@@ -2,7 +2,7 @@ import { pipe } from 'ramda'
2
2
  import React from 'react'
3
3
 
4
4
  import { AnimatedView } from '../../animations/AnimatedView'
5
- import { SafeAreaView } from '../SafeAreaView'
5
+ import { SafeAreaView } from '../../structure/SafeAreaView'
6
6
  import { useDefaultModifier } from '../../../modifiers/default'
7
7
  import { useThemeComponentModifier } from '../../../modifiers/themeComponent'
8
8
 
@@ -1,6 +1,6 @@
1
1
  import { pipe } from 'ramda'
2
2
 
3
- import { View } from '../View'
3
+ import { View } from '../../structure/View'
4
4
  import { useDefaultModifier } from '../../../modifiers/default'
5
5
  import { useThemeComponentModifier } from '../../../modifiers/themeComponent'
6
6
 
@@ -1,6 +1,6 @@
1
1
  import { pipe } from 'ramda'
2
2
 
3
- import { View } from '../View'
3
+ import { View } from '../../structure/View'
4
4
  import { moveScale } from '../../../theme/helpers/sizeScale'
5
5
  import { useDefaultModifier } from '../../../modifiers/default'
6
6
  import { useSizeConverter } from '../../../modifiers/sizeConverter'
@@ -3,7 +3,7 @@ import { pipe } from 'ramda'
3
3
  import { Icon } from '../../presentation/Icon'
4
4
  import { Link } from '../../actions/Link'
5
5
  import { Text } from '../../text/Text'
6
- import { View } from '../View'
6
+ import { View } from '../../structure/View'
7
7
  import { moveScale } from '../../../theme/helpers/sizeScale'
8
8
  import { useDefaultModifier } from '../../../modifiers/default'
9
9
  import { useSizeConverter } from '../../../modifiers/sizeConverter'