@neko-os/ui 0.0.13 → 0.1.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 (137) hide show
  1. package/dist/abstractions/KeyboardAvoidingView.js +1 -0
  2. package/dist/abstractions/KeyboardAvoidingView.native.js +1 -0
  3. package/dist/components/actions/ActionsDrawer.js +1 -0
  4. package/dist/components/actions/Button.js +1 -1
  5. package/dist/components/actions/FloatingMenu.js +1 -0
  6. package/dist/components/actions/index.js +1 -1
  7. package/dist/components/animations/AnimatedTopBar.js +1 -0
  8. package/dist/components/animations/AnimatedTopBar.native.js +1 -0
  9. package/dist/components/animations/AnimatedTopBar.web.js +1 -0
  10. package/dist/components/animations/ParallaxHeader.js +1 -0
  11. package/dist/components/animations/ParallaxHeader.native.js +1 -0
  12. package/dist/components/animations/ParallaxHeader.web.js +1 -0
  13. package/dist/components/animations/ReanimatedScrollHandler.js +1 -0
  14. package/dist/components/animations/ReanimatedScrollHandler.native.js +1 -0
  15. package/dist/components/animations/ReanimatedScrollHandler.web.js +1 -0
  16. package/dist/components/animations/index.js +1 -1
  17. package/dist/components/form/FormItem.js +1 -1
  18. package/dist/components/form/FormList.js +1 -1
  19. package/dist/components/form/SubmitButton.js +1 -1
  20. package/dist/components/form/index.js +1 -1
  21. package/dist/components/form/useNewForm.js +1 -1
  22. package/dist/components/form/validation/defaultMessages.js +1 -0
  23. package/dist/components/form/validation/index.js +1 -0
  24. package/dist/components/form/validation/normalizeRules.js +1 -0
  25. package/dist/components/form/validation/shouldValidateOn.js +1 -0
  26. package/dist/components/form/validation/validateRules.js +1 -0
  27. package/dist/components/form/validation/validators.js +1 -0
  28. package/dist/components/index.js +1 -1
  29. package/dist/components/inputs/InputWrapper.js +1 -1
  30. package/dist/components/inputs/NumberInput.js +1 -1
  31. package/dist/components/inputs/Picker.js +1 -1
  32. package/dist/components/inputs/Select.js +1 -1
  33. package/dist/components/presentation/Avatar.js +1 -1
  34. package/dist/components/presentation/AvatarLabel.js +1 -1
  35. package/dist/components/presentation/LabelValue.js +1 -1
  36. package/dist/components/presentation/Result.js +1 -1
  37. package/dist/components/presentation/Tooltip.js +1 -1
  38. package/dist/components/sections/Section.js +1 -0
  39. package/dist/components/sections/SectionItem.js +1 -0
  40. package/dist/components/sections/SectionItemLink.js +1 -0
  41. package/dist/components/sections/index.js +1 -0
  42. package/dist/components/state/StatePresenter.js +1 -0
  43. package/dist/components/state/index.js +1 -1
  44. package/dist/components/structure/BlurView.js +1 -1
  45. package/dist/components/structure/KeyboardAvoidingView.js +1 -0
  46. package/dist/components/structure/TopBar.js +1 -0
  47. package/dist/components/structure/bottomDrawer/index.js +1 -1
  48. package/dist/components/structure/bottomDrawer/index.native.js +1 -1
  49. package/dist/components/structure/bottomDrawer/index.web.js +1 -1
  50. package/dist/components/structure/bottomDrawer/native/BottomDrawer.js +1 -1
  51. package/dist/components/structure/bottomDrawer/native/DrawerScrollView.js +1 -1
  52. package/dist/components/structure/bottomDrawer/native/createDrawerScrollComponent.js +1 -0
  53. package/dist/components/structure/index.js +1 -1
  54. package/dist/components/text/DateText.js +1 -0
  55. package/dist/components/text/index.js +1 -1
  56. package/dist/components/theme/ThemePicker.js +1 -1
  57. package/dist/helpers/index.js +1 -1
  58. package/dist/helpers/storage.js +1 -1
  59. package/dist/responsive/responsiveHooks.js +1 -1
  60. package/dist/theme/ThemeHandler.js +1 -1
  61. package/dist/theme/default/base.js +1 -1
  62. package/dist/theme/default/blackTheme.js +1 -1
  63. package/dist/theme/default/cyberpunkTheme.js +1 -1
  64. package/dist/theme/default/darkTheme.js +1 -1
  65. package/dist/theme/default/hackerTheme.js +1 -1
  66. package/dist/theme/default/lightTheme.js +1 -1
  67. package/dist/theme/default/msdosTheme.js +1 -1
  68. package/dist/theme/default/paperTheme.js +1 -1
  69. package/package.json +1 -1
  70. package/src/abstractions/KeyboardAvoidingView.js +3 -0
  71. package/src/abstractions/KeyboardAvoidingView.native.js +3 -0
  72. package/src/components/actions/ActionsDrawer.js +68 -0
  73. package/src/components/actions/Button.js +2 -1
  74. package/src/components/actions/FloatingMenu.js +39 -0
  75. package/src/components/actions/index.js +2 -0
  76. package/src/components/animations/AnimatedTopBar.js +10 -0
  77. package/src/components/animations/AnimatedTopBar.native.js +34 -0
  78. package/src/components/animations/AnimatedTopBar.web.js +1 -0
  79. package/src/components/animations/ParallaxHeader.js +9 -0
  80. package/src/components/animations/ParallaxHeader.native.js +32 -0
  81. package/src/components/animations/ParallaxHeader.web.js +32 -0
  82. package/src/components/animations/ReanimatedScrollHandler.js +8 -0
  83. package/src/components/animations/ReanimatedScrollHandler.native.js +24 -0
  84. package/src/components/animations/ReanimatedScrollHandler.web.js +1 -0
  85. package/src/components/animations/index.js +3 -0
  86. package/src/components/form/FormItem.js +42 -5
  87. package/src/components/form/FormList.js +23 -4
  88. package/src/components/form/SubmitButton.js +4 -2
  89. package/src/components/form/index.js +1 -0
  90. package/src/components/form/useNewForm.js +108 -15
  91. package/src/components/form/validation/defaultMessages.js +20 -0
  92. package/src/components/form/validation/index.js +5 -0
  93. package/src/components/form/validation/normalizeRules.js +22 -0
  94. package/src/components/form/validation/shouldValidateOn.js +21 -0
  95. package/src/components/form/validation/validateRules.js +83 -0
  96. package/src/components/form/validation/validators.js +82 -0
  97. package/src/components/index.js +1 -0
  98. package/src/components/inputs/InputWrapper.js +1 -1
  99. package/src/components/inputs/NumberInput.js +6 -5
  100. package/src/components/inputs/Picker.js +3 -2
  101. package/src/components/inputs/Select.js +31 -15
  102. package/src/components/presentation/Avatar.js +2 -2
  103. package/src/components/presentation/AvatarLabel.js +2 -0
  104. package/src/components/presentation/LabelValue.js +7 -5
  105. package/src/components/presentation/Result.js +2 -2
  106. package/src/components/presentation/Tooltip.js +1 -1
  107. package/src/components/sections/Section.js +50 -0
  108. package/src/components/sections/SectionItem.js +24 -0
  109. package/src/components/sections/SectionItemLink.js +33 -0
  110. package/src/components/sections/index.js +3 -0
  111. package/src/components/state/StatePresenter.js +41 -0
  112. package/src/components/state/index.js +1 -0
  113. package/src/components/structure/BlurView.js +1 -0
  114. package/src/components/structure/KeyboardAvoidingView.js +52 -0
  115. package/src/components/structure/TopBar.js +45 -0
  116. package/src/components/structure/bottomDrawer/index.js +2 -0
  117. package/src/components/structure/bottomDrawer/index.native.js +2 -1
  118. package/src/components/structure/bottomDrawer/index.web.js +2 -1
  119. package/src/components/structure/bottomDrawer/native/BottomDrawer.js +14 -20
  120. package/src/components/structure/bottomDrawer/native/DrawerScrollView.js +4 -82
  121. package/src/components/structure/bottomDrawer/native/createDrawerScrollComponent.js +131 -0
  122. package/src/components/structure/index.js +2 -0
  123. package/src/components/text/DateText.js +11 -0
  124. package/src/components/text/index.js +1 -0
  125. package/src/components/theme/ThemePicker.js +1 -2
  126. package/src/helpers/index.js +1 -0
  127. package/src/helpers/storage.js +32 -9
  128. package/src/responsive/responsiveHooks.js +6 -0
  129. package/src/theme/ThemeHandler.js +6 -3
  130. package/src/theme/default/base.js +16 -4
  131. package/src/theme/default/blackTheme.js +1 -0
  132. package/src/theme/default/cyberpunkTheme.js +10 -0
  133. package/src/theme/default/darkTheme.js +1 -0
  134. package/src/theme/default/hackerTheme.js +17 -3
  135. package/src/theme/default/lightTheme.js +1 -0
  136. package/src/theme/default/msdosTheme.js +9 -10
  137. package/src/theme/default/paperTheme.js +10 -0
@@ -10,7 +10,7 @@ import { useThemeComponentModifier } from '../../modifiers/themeComponent'
10
10
  export function Tooltip(rootProps) {
11
11
  const [{ loading, fontColor, sizeCode }, formattedProps] = pipe(
12
12
  useColorConverter('overlayBG'),
13
- useSizeConverter('text', 'md'),
13
+ useSizeConverter('text', 'sm'),
14
14
  useThemeComponentModifier('Tooltip'),
15
15
  useFullColorModifier
16
16
  )([{}, rootProps])
@@ -0,0 +1,50 @@
1
+ import { pipe } from 'ramda'
2
+ import React from 'react'
3
+
4
+ import { Divider } from '../helpers/Separator'
5
+ import { Text } from '../text'
6
+ import { View } from '../structure'
7
+ import { useDefaultModifier } from '../../modifiers/default'
8
+ import { useThemeComponentModifier } from '../../modifiers/themeComponent'
9
+
10
+ const DEFAULT_PROPS = {
11
+ marginH: 'md',
12
+ bg: 'overlayBG',
13
+ br: 'md',
14
+ titleProps: {
15
+ strong: true,
16
+ text4: true,
17
+ sm: true,
18
+ marginB: 'sm',
19
+ paddingH: 'md',
20
+ },
21
+ dividerProps: {
22
+ line: true,
23
+ height: 1,
24
+ },
25
+ }
26
+
27
+ export function Section({ children, title, ...rootProps }) {
28
+ const [{}, formattedProps] = pipe(
29
+ useThemeComponentModifier('Section'), //
30
+ useDefaultModifier(DEFAULT_PROPS)
31
+ )([{}, rootProps])
32
+
33
+ const { titleProps, dividerProps, ...props } = formattedProps
34
+
35
+ const childArray = React.Children.toArray(children)
36
+
37
+ return (
38
+ <View>
39
+ {!!title && <Text marginH={props.marginH} {...titleProps} label={title} />}
40
+ <View className="neko-section" {...props}>
41
+ {childArray.map((child, i) => (
42
+ <React.Fragment key={child.key}>
43
+ {child}
44
+ {i < childArray.length - 1 && <Divider {...dividerProps} />}
45
+ </React.Fragment>
46
+ ))}
47
+ </View>
48
+ </View>
49
+ )
50
+ }
@@ -0,0 +1,24 @@
1
+ import { pipe } from 'ramda'
2
+
3
+ import { LabelValue } from '../presentation'
4
+ import { useColorConverter } from '../../modifiers/colorConverter'
5
+ import { useDefaultModifier } from '../../modifiers/default'
6
+ import { useThemeComponentModifier } from '../../modifiers/themeComponent'
7
+
8
+ const DEFAULT_PROPS = {
9
+ paddingH: 'md',
10
+ paddingV: 'sm',
11
+ minH: 'md',
12
+ }
13
+
14
+ export function SectionItem({ ...rootProps }) {
15
+ const [{ color }, formattedProps] = pipe(
16
+ useColorConverter(),
17
+ useThemeComponentModifier('SectionItem'), //
18
+ useDefaultModifier(DEFAULT_PROPS)
19
+ )([{}, rootProps])
20
+
21
+ const { ...props } = formattedProps
22
+
23
+ return <LabelValue spread className="neko-section-item" color={color} {...props} />
24
+ }
@@ -0,0 +1,33 @@
1
+ import { pipe } from 'ramda'
2
+
3
+ import { IconLabel } from '../presentation'
4
+ import { Link } from '../actions'
5
+ import { SectionItem } from './SectionItem'
6
+ import { useColorConverter } from '../../modifiers/colorConverter'
7
+ import { useDefaultModifier } from '../../modifiers/default'
8
+ import { useThemeComponentModifier } from '../../modifiers/themeComponent'
9
+
10
+ const DEFAULT_PROPS = {
11
+ icon: 'md',
12
+ paddingV: 'sm',
13
+ minH: 'md',
14
+ icon: 'arrow-right-s-line',
15
+ }
16
+
17
+ export function SectionItemLink({ children, value, onPress, ...rootProps }) {
18
+ const [{ color }, formattedProps] = pipe(
19
+ useColorConverter(),
20
+ useThemeComponentModifier('SectionItemLink'), //
21
+ useDefaultModifier(DEFAULT_PROPS)
22
+ )([{}, rootProps])
23
+
24
+ const { icon, iconLabelProps, ...props } = formattedProps
25
+
26
+ return (
27
+ <Link className="neko-section-item-link" onPress={onPress}>
28
+ <SectionItem color={color} {...props}>
29
+ {children || <IconLabel gap={2} invert color={color} {...iconLabelProps} label={value} icon={icon} />}
30
+ </SectionItem>
31
+ </Link>
32
+ )
33
+ }
@@ -0,0 +1,3 @@
1
+ export * from './Section'
2
+ export * from './SectionItem'
3
+ export * from './SectionItemLink'
@@ -0,0 +1,41 @@
1
+ import { Loading } from './Loading'
2
+ import { Result } from '../presentation'
3
+ import { View } from '../structure'
4
+
5
+ export function StatePresenter({
6
+ loading,
7
+ error,
8
+ empty,
9
+ errorTitle = 'Something went wrong',
10
+ errorDescription,
11
+ emptyTitle = 'No results',
12
+ emptyDescription,
13
+ children,
14
+ ...props
15
+ }) {
16
+ if (loading) {
17
+ return (
18
+ <View flex center {...props}>
19
+ <Loading />
20
+ </View>
21
+ )
22
+ }
23
+
24
+ if (error) {
25
+ return (
26
+ <View flex center {...props}>
27
+ <Result type="error" title={errorTitle} description={errorDescription || error.message} />
28
+ </View>
29
+ )
30
+ }
31
+
32
+ if (empty) {
33
+ return (
34
+ <View flex center {...props}>
35
+ <Result type="empty" title={emptyTitle} description={emptyDescription} />
36
+ </View>
37
+ )
38
+ }
39
+
40
+ return children
41
+ }
@@ -1,2 +1,3 @@
1
1
  export * from './Loading'
2
2
  export * from './LoadingView'
3
+ export * from './StatePresenter'
@@ -25,6 +25,7 @@ const DEFAULT_PROPS =
25
25
  const isDark = tinycolor(colors?.overlayBG || colors?.bg).isDark()
26
26
 
27
27
  return {
28
+ width: 'auto',
28
29
  tint: isDark ? 'dark' : 'light',
29
30
  }
30
31
  }
@@ -0,0 +1,52 @@
1
+ import { pipe } from 'ramda'
2
+
3
+ import { AbsKeyboardAvoidingView } from '../../abstractions/KeyboardAvoidingView'
4
+ import { Platform } from '../../abstractions/Platform'
5
+ import { useSafeAreaInsets } from '../../abstractions/helpers/useSafeAreaInsets'
6
+ import { useAnimationModifier } from '../../modifiers/animation'
7
+ import { useBackgroundModifier } from '../../modifiers/background'
8
+ import { useBorderModifier } from '../../modifiers/border'
9
+ import { useCursorModifier } from '../../modifiers/cursor'
10
+ import { useDisplayModifier } from '../../modifiers/display'
11
+ import { useFlexModifier } from '../../modifiers/flex'
12
+ import { useFlexWrapperModifier } from '../../modifiers/flexWrapper'
13
+ import { useMarginModifier } from '../../modifiers/margin'
14
+ import { useOverflowModifier } from '../../modifiers/overflow'
15
+ import { usePaddingModifier } from '../../modifiers/padding'
16
+ import { usePositionModifier } from '../../modifiers/position'
17
+ import { useShadowModifier } from '../../modifiers/shadow'
18
+ import { useSizeModifier } from '../../modifiers/size'
19
+ import { useStateModifier } from '../../modifiers/state'
20
+ import { useThemeComponentModifier } from '../../modifiers/themeComponent'
21
+
22
+ export function KeyboardAvoidingView({ children, keyboardVerticalOffset = 0, ...rootProps }) {
23
+ const { bottom } = useSafeAreaInsets()
24
+
25
+ const [_, props] = pipe(
26
+ useThemeComponentModifier('KeyboardAvoidingView'),
27
+ useFlexWrapperModifier,
28
+ useDisplayModifier,
29
+ useCursorModifier,
30
+ useAnimationModifier,
31
+ useStateModifier,
32
+ useSizeModifier,
33
+ usePositionModifier,
34
+ useOverflowModifier,
35
+ usePaddingModifier,
36
+ useMarginModifier,
37
+ useFlexModifier,
38
+ useBackgroundModifier,
39
+ useBorderModifier,
40
+ useShadowModifier
41
+ )([{}, rootProps])
42
+
43
+ return (
44
+ <AbsKeyboardAvoidingView
45
+ behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
46
+ keyboardVerticalOffset={keyboardVerticalOffset + bottom}
47
+ {...props}
48
+ >
49
+ {children}
50
+ </AbsKeyboardAvoidingView>
51
+ )
52
+ }
@@ -0,0 +1,45 @@
1
+ import { Text } from '../text'
2
+ import { View } from './View'
3
+ import { useResponsiveConverter } from '../../modifiers/responsiveConverter'
4
+ import { useSafeAreaInsets } from '../../abstractions/helpers/useSafeAreaInsets'
5
+
6
+ export function TopBar({ right, left, WrapperView, children, ...rootProps }) {
7
+ const { top: safeTop } = useSafeAreaInsets()
8
+
9
+ const [_, props] = useResponsiveConverter([])([{}, rootProps])
10
+ let { useSafeArea = true, title, subtitle } = props
11
+
12
+ const hasContent = !!title || !!subtitle || !!children || !!right || !!left
13
+
14
+ WrapperView = WrapperView || View
15
+
16
+ return (
17
+ <WrapperView paddingT={useSafeArea && safeTop} {...props}>
18
+ {!!hasContent && (
19
+ <View minH="md" centerV row paddingH="md" paddingV="sm" gap="sm" fullW>
20
+ <View flex={1} toLeft>
21
+ {left}
22
+ </View>
23
+
24
+ <View center flex={3}>
25
+ {children || (
26
+ <Text center h6 numberOfLines={1}>
27
+ {title}
28
+ </Text>
29
+ )}
30
+
31
+ {subtitle && (
32
+ <Text center xs numberOfLines={1}>
33
+ {subtitle}
34
+ </Text>
35
+ )}
36
+ </View>
37
+
38
+ <View flex={1} toRight>
39
+ {right}
40
+ </View>
41
+ </View>
42
+ )}
43
+ </WrapperView>
44
+ )
45
+ }
@@ -1 +1,3 @@
1
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,7 +6,6 @@ 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
 
@@ -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,83 +1,5 @@
1
- import React from 'react'
2
- import { ScrollView } from 'react-native'
3
- import Animated, { useAnimatedScrollHandler, useAnimatedReaction, runOnJS } from 'react-native-reanimated'
4
- import { useDrawerContext } from './DrawerContext'
1
+ import { FlatList, ScrollView } from '../../../list'
2
+ import { createDrawerScrollComponent } from './createDrawerScrollComponent'
5
3
 
6
- const AnimatedScrollView = Animated.createAnimatedComponent(ScrollView)
7
-
8
- export const DrawerScrollView = React.forwardRef((props, ref) => {
9
- const {
10
- children,
11
- onScroll,
12
- scrollEventThrottle = 16,
13
- showsVerticalScrollIndicator = true,
14
- bounces = true,
15
- ...scrollViewProps
16
- } = props
17
-
18
- const { scrollOffset, scrollEnabled, isScrolling } = useDrawerContext()
19
-
20
- const scrollRef = React.useRef(null)
21
-
22
- // Convert shared value to React state for the component
23
- const [isScrollEnabled, setIsScrollEnabled] = React.useState(true)
24
-
25
- React.useImperativeHandle(ref, () => scrollRef.current)
26
-
27
- // Handle scroll lock/unlock based on drawer position
28
- useAnimatedReaction(
29
- () => scrollEnabled.value,
30
- (enabled) => {
31
- if (!enabled && scrollOffset.value > 0) {
32
- runOnJS(() => {
33
- scrollRef.current?.scrollTo({ y: 0, animated: false })
34
- })()
35
- scrollOffset.value = 0
36
- }
37
- // Update React state
38
- runOnJS(setIsScrollEnabled)(enabled)
39
- },
40
- []
41
- )
42
-
43
- const handleScrollBeginDrag = React.useCallback(() => {
44
- isScrolling.value = true
45
- }, [isScrolling])
46
-
47
- const handleScrollEndDrag = React.useCallback(() => {
48
- isScrolling.value = false
49
- }, [isScrolling])
50
-
51
- const animatedScrollHandler = useAnimatedScrollHandler({
52
- onScroll: (event) => {
53
- scrollOffset.value = event.contentOffset.y
54
-
55
- if (onScroll) {
56
- runOnJS(onScroll)(event)
57
- }
58
- },
59
- onBeginDrag: () => {
60
- runOnJS(handleScrollBeginDrag)()
61
- },
62
- onEndDrag: () => {
63
- runOnJS(handleScrollEndDrag)()
64
- },
65
- })
66
-
67
- return (
68
- <AnimatedScrollView
69
- ref={scrollRef}
70
- style={{ flex: 1 }}
71
- scrollEnabled={isScrollEnabled}
72
- onScroll={animatedScrollHandler}
73
- scrollEventThrottle={scrollEventThrottle}
74
- showsVerticalScrollIndicator={showsVerticalScrollIndicator}
75
- bounces={bounces}
76
- {...scrollViewProps}
77
- >
78
- {children}
79
- </AnimatedScrollView>
80
- )
81
- })
82
-
83
- DrawerScrollView.displayName = 'DrawerScrollView'
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
+ }
@@ -12,3 +12,5 @@ export * from './drawer'
12
12
  export * from './bottomDrawer'
13
13
  export * from './popover/Popover'
14
14
  export * from './Segment'
15
+ export * from './TopBar'
16
+ export * from './KeyboardAvoidingView'
@@ -0,0 +1,11 @@
1
+ import { is } from 'ramda'
2
+ import dayjs from 'dayjs'
3
+
4
+ import { Text } from '../text'
5
+
6
+ export function DateText({ format = 'DD MMM YYYY', value, children, ...props }) {
7
+ value = is(String, children) ? children : value
8
+ // TODO: Get format from i18n
9
+
10
+ return <Text {...props}>{dayjs(value).format(format)}</Text>
11
+ }
@@ -1,2 +1,3 @@
1
1
  export * from './Text'
2
2
  export * from './VerticalText'
3
+ export * from './DateText'
@@ -7,7 +7,7 @@ import { Picker } from '../inputs'
7
7
  import { ThemeThumb } from './ThemeThumb'
8
8
 
9
9
  export function ThemePicker({ onChange, onlyKeys, hideKeys }) {
10
- const { activeThemeKey, setActiveThemeKey, themes, onChangeTheme } = useThemeHandler()
10
+ const { activeThemeKey, themes, onChangeTheme } = useThemeHandler()
11
11
 
12
12
  let options = pipe(
13
13
  mergeDeepRight(DEFAULT_THEMES),
@@ -27,7 +27,6 @@ export function ThemePicker({ onChange, onlyKeys, hideKeys }) {
27
27
  gap="lg"
28
28
  value={activeThemeKey}
29
29
  onChange={(key) => {
30
- setActiveThemeKey(key)
31
30
  onChangeTheme?.(key)
32
31
  onChange?.(key)
33
32
  }}
@@ -2,3 +2,4 @@ export * from './debounce'
2
2
  export * from './string'
3
3
  export * from './random'
4
4
  export * from './storage'
5
+ export * from './../abstractions/helpers/useSafeAreaInsets'