@neko-os/ui 0.0.7 → 0.0.8

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 (217) hide show
  1. package/dist/NekoUI.js +1 -1
  2. package/dist/abstractions/AnimatedView.js +1 -1
  3. package/dist/abstractions/BlurView.js +1 -0
  4. package/dist/abstractions/BlurView.native.js +1 -0
  5. package/dist/abstractions/GradientView.js +1 -0
  6. package/dist/abstractions/GradientView.native.js +1 -0
  7. package/dist/abstractions/Icon.native.js +1 -1
  8. package/dist/abstractions/ImageBackground.js +1 -0
  9. package/dist/abstractions/ImageBackground.native.js +1 -0
  10. package/dist/abstractions/ImageBackground.web.js +1 -0
  11. package/dist/abstractions/Pressable.js +1 -0
  12. package/dist/abstractions/Pressable.native.js +1 -0
  13. package/dist/abstractions/Pressable.web.js +1 -0
  14. package/dist/abstractions/ScrollView.js +1 -0
  15. package/dist/abstractions/ScrollView.native.js +1 -0
  16. package/dist/abstractions/TextInput.js +1 -1
  17. package/dist/components/actions/Button.js +1 -1
  18. package/dist/components/actions/Dropdown.js +1 -1
  19. package/dist/components/actions/Link.js +1 -1
  20. package/dist/components/actions/Pressable.js +1 -0
  21. package/dist/components/actions/index.js +1 -1
  22. package/dist/components/actions/menu/HorizontalMenu.js +1 -1
  23. package/dist/components/actions/menu/VerticalMenu.js +1 -1
  24. package/dist/components/animations/ReanimatedView.js +1 -0
  25. package/dist/components/calendar/CalendarNav.js +1 -0
  26. package/dist/components/calendar/WeekDaysBar.js +1 -0
  27. package/dist/components/calendar/_helpers/calendarDays.js +1 -1
  28. package/dist/components/calendar/_helpers/dateDisabled.js +1 -0
  29. package/dist/components/calendar/index.js +0 -1
  30. package/dist/components/feedback/alerter.js +1 -0
  31. package/dist/components/feedback/confirmer.js +1 -0
  32. package/dist/components/feedback/index.js +1 -1
  33. package/dist/components/feedback/notifications/Notification.js +1 -1
  34. package/dist/components/feedback/notifications/NotificationsHandler.js +1 -1
  35. package/dist/components/index.js +1 -1
  36. package/dist/components/inputs/DateInput.js +1 -0
  37. package/dist/components/inputs/InputWrapper.js +1 -1
  38. package/dist/components/inputs/LinkInput.js +1 -0
  39. package/dist/components/inputs/MaskInput.js +1 -0
  40. package/dist/components/inputs/TextInput.js +1 -1
  41. package/dist/components/inputs/_DateInput.native.js +1 -0
  42. package/dist/components/inputs/datePicker/DatePicker.js +1 -0
  43. package/dist/components/inputs/datePicker/DayPicker.js +1 -0
  44. package/dist/components/inputs/datePicker/MonthPicker.js +1 -0
  45. package/dist/components/inputs/datePicker/QuarterPicker.js +1 -0
  46. package/dist/components/inputs/datePicker/WeekPicker.js +1 -0
  47. package/dist/components/inputs/datePicker/YearPicker.js +1 -0
  48. package/dist/components/inputs/index.js +1 -1
  49. package/dist/components/list/ScrollView.js +1 -0
  50. package/dist/components/list/index.js +1 -0
  51. package/dist/components/presentation/ImageBackground.js +1 -0
  52. package/dist/components/presentation/Result.js +1 -1
  53. package/dist/components/presentation/index.js +1 -1
  54. package/dist/components/structure/Accordion.js +1 -1
  55. package/dist/components/structure/BlurView.js +1 -0
  56. package/dist/components/structure/GradientView.js +1 -0
  57. package/dist/components/structure/bottomDrawer/index.js +1 -0
  58. package/dist/components/structure/bottomDrawer/index.native.js +1 -0
  59. package/dist/components/structure/bottomDrawer/index.web.js +1 -0
  60. package/dist/components/structure/bottomDrawer/native/BottomDrawer.js +1 -0
  61. package/dist/components/structure/bottomDrawer/native/DrawerContext.js +1 -0
  62. package/dist/components/structure/bottomDrawer/native/DrawerHandle.js +1 -0
  63. package/dist/components/structure/bottomDrawer/native/DrawerScrollView.js +1 -0
  64. package/dist/components/structure/bottomDrawer/native/utils.js +1 -0
  65. package/dist/components/structure/bottomDrawer/web/BottomDrawer.js +1 -0
  66. package/dist/components/structure/drawer/Drawer.js +1 -0
  67. package/dist/components/structure/drawer/Drawer.native.js +1 -0
  68. package/dist/components/structure/drawer/index.js +1 -0
  69. package/dist/components/structure/index.js +1 -1
  70. package/dist/components/structure/modal/Modal.js +1 -0
  71. package/dist/components/structure/modal/Modal.native.js +1 -0
  72. package/dist/components/structure/modal/ModalBackdrop.js +1 -0
  73. package/dist/components/structure/modal/ModalContent.js +1 -0
  74. package/dist/components/structure/modal/ModalFooter.js +1 -0
  75. package/dist/components/structure/modal/ModalHeader.js +1 -0
  76. package/dist/components/structure/modal/handler/ModalsHandler.js +1 -0
  77. package/dist/components/structure/modal/index.js +1 -0
  78. package/dist/components/structure/overlay/OverlayWrapper.js +1 -1
  79. package/dist/components/structure/popover/Popover.js +1 -1
  80. package/dist/components/structure/popover/Popover.native.js +1 -1
  81. package/dist/modifiers/animations/animatedEffects.js +1 -1
  82. package/dist/modifiers/animations/animatedEffects.native.js +1 -1
  83. package/dist/modifiers/animations/fadeEffect.js +1 -1
  84. package/dist/modifiers/animations/scaleEffect.js +1 -0
  85. package/dist/modifiers/animations/scaleEffect.native.js +1 -0
  86. package/dist/modifiers/animations/slideEffect.js +1 -1
  87. package/dist/modifiers/background.js +1 -1
  88. package/dist/modifiers/cursor.js +1 -0
  89. package/dist/modifiers/display.js +1 -1
  90. package/dist/modifiers/hover.js +1 -0
  91. package/dist/modifiers/position.js +1 -1
  92. package/dist/modifiers/size.js +1 -1
  93. package/dist/modifiers/state.js +1 -1
  94. package/dist/responsive/responsiveHooks.js +1 -1
  95. package/dist/theme/default/blackTheme.js +1 -0
  96. package/dist/theme/default/cyberpunkTheme.js +1 -1
  97. package/dist/theme/default/darkTheme.js +1 -1
  98. package/dist/theme/default/deepWoodsTheme.js +1 -1
  99. package/dist/theme/default/forestTheme.js +1 -1
  100. package/dist/theme/default/hackerTheme.js +1 -1
  101. package/dist/theme/default/lightTheme.js +1 -1
  102. package/dist/theme/default/midnightTheme.js +1 -1
  103. package/dist/theme/default/msdosTheme.js +1 -1
  104. package/dist/theme/default/oceanTheme.js +1 -1
  105. package/dist/theme/default/pastelTheme.js +1 -1
  106. package/dist/theme/default/sunsetTheme.js +1 -1
  107. package/dist/theme/default/themes.js +1 -1
  108. package/package.json +8 -3
  109. package/src/NekoUI.js +6 -3
  110. package/src/abstractions/AnimatedView.js +3 -3
  111. package/src/abstractions/BlurView.js +43 -0
  112. package/src/abstractions/BlurView.native.js +39 -0
  113. package/src/abstractions/GradientView.js +5 -0
  114. package/src/abstractions/GradientView.native.js +32 -0
  115. package/src/abstractions/Icon.native.js +4 -2
  116. package/src/abstractions/ImageBackground.js +17 -0
  117. package/src/abstractions/ImageBackground.native.js +27 -0
  118. package/src/abstractions/ImageBackground.web.js +7 -0
  119. package/src/abstractions/Pressable.js +4 -0
  120. package/src/abstractions/Pressable.native.js +3 -0
  121. package/src/abstractions/Pressable.web.js +3 -0
  122. package/src/abstractions/ScrollView.js +3 -0
  123. package/src/abstractions/ScrollView.native.js +5 -0
  124. package/src/abstractions/TextInput.js +2 -2
  125. package/src/components/actions/Button.js +7 -0
  126. package/src/components/actions/Dropdown.js +2 -1
  127. package/src/components/actions/Link.js +16 -2
  128. package/src/components/actions/Pressable.js +38 -0
  129. package/src/components/actions/index.js +1 -0
  130. package/src/components/actions/menu/HorizontalMenu.js +2 -1
  131. package/src/components/actions/menu/VerticalMenu.js +4 -3
  132. package/src/components/animations/ReanimatedView.js +40 -0
  133. package/src/components/calendar/CalendarNav.js +67 -0
  134. package/src/components/calendar/WeekDaysBar.js +18 -0
  135. package/src/components/calendar/_helpers/calendarDays.js +1 -3
  136. package/src/components/calendar/_helpers/dateDisabled.js +24 -0
  137. package/src/components/calendar/index.js +1 -1
  138. package/src/components/feedback/alerter.js +31 -0
  139. package/src/components/feedback/confirmer.js +70 -0
  140. package/src/components/feedback/index.js +2 -0
  141. package/src/components/feedback/notifications/Notification.js +1 -1
  142. package/src/components/feedback/notifications/NotificationsHandler.js +3 -3
  143. package/src/components/index.js +1 -0
  144. package/src/components/inputs/DateInput.js +111 -0
  145. package/src/components/inputs/InputWrapper.js +18 -6
  146. package/src/components/inputs/LinkInput.js +17 -0
  147. package/src/components/inputs/MaskInput.js +67 -0
  148. package/src/components/inputs/TextInput.js +2 -2
  149. package/src/components/inputs/_DateInput.native.js +89 -0
  150. package/src/components/inputs/datePicker/DatePicker.js +24 -0
  151. package/src/components/inputs/datePicker/DayPicker.js +65 -0
  152. package/src/components/inputs/datePicker/MonthPicker.js +62 -0
  153. package/src/components/inputs/datePicker/QuarterPicker.js +65 -0
  154. package/src/components/inputs/datePicker/WeekPicker.js +74 -0
  155. package/src/components/inputs/datePicker/YearPicker.js +67 -0
  156. package/src/components/inputs/index.js +4 -0
  157. package/src/components/list/ScrollView.js +58 -0
  158. package/src/components/list/index.js +1 -0
  159. package/src/components/presentation/ImageBackground.js +38 -0
  160. package/src/components/presentation/Result.js +2 -2
  161. package/src/components/presentation/index.js +1 -0
  162. package/src/components/structure/Accordion.js +1 -1
  163. package/src/components/structure/BlurView.js +58 -0
  164. package/src/components/structure/GradientView.js +42 -0
  165. package/src/components/structure/bottomDrawer/index.js +1 -0
  166. package/src/components/structure/bottomDrawer/index.native.js +4 -0
  167. package/src/components/structure/bottomDrawer/index.web.js +4 -0
  168. package/src/components/structure/bottomDrawer/native/BottomDrawer.js +229 -0
  169. package/src/components/structure/bottomDrawer/native/DrawerContext.js +21 -0
  170. package/src/components/structure/bottomDrawer/native/DrawerHandle.js +12 -0
  171. package/src/components/structure/bottomDrawer/native/DrawerScrollView.js +83 -0
  172. package/src/components/structure/bottomDrawer/native/utils.js +58 -0
  173. package/src/components/structure/bottomDrawer/web/BottomDrawer.js +3 -0
  174. package/src/components/structure/drawer/Drawer.js +5 -0
  175. package/src/components/structure/drawer/Drawer.native.js +3 -0
  176. package/src/components/structure/drawer/index.js +1 -0
  177. package/src/components/structure/index.js +5 -0
  178. package/src/components/structure/modal/Modal.js +84 -0
  179. package/src/components/structure/modal/Modal.native.js +83 -0
  180. package/src/components/structure/modal/ModalBackdrop.js +58 -0
  181. package/src/components/structure/modal/ModalContent.js +28 -0
  182. package/src/components/structure/modal/ModalFooter.js +31 -0
  183. package/src/components/structure/modal/ModalHeader.js +50 -0
  184. package/src/components/structure/modal/handler/ModalsHandler.js +61 -0
  185. package/src/components/structure/modal/index.js +6 -0
  186. package/src/components/structure/overlay/OverlayWrapper.js +1 -1
  187. package/src/components/structure/popover/Popover.js +28 -2
  188. package/src/components/structure/popover/Popover.native.js +44 -15
  189. package/src/modifiers/animations/animatedEffects.js +2 -0
  190. package/src/modifiers/animations/animatedEffects.native.js +3 -1
  191. package/src/modifiers/animations/fadeEffect.js +4 -2
  192. package/src/modifiers/animations/scaleEffect.js +45 -0
  193. package/src/modifiers/animations/scaleEffect.native.js +33 -0
  194. package/src/modifiers/animations/slideEffect.js +3 -1
  195. package/src/modifiers/background.js +16 -4
  196. package/src/modifiers/cursor.js +21 -0
  197. package/src/modifiers/display.js +2 -2
  198. package/src/modifiers/hover.js +28 -0
  199. package/src/modifiers/position.js +24 -3
  200. package/src/modifiers/size.js +2 -1
  201. package/src/modifiers/state.js +1 -1
  202. package/src/responsive/responsiveHooks.js +7 -0
  203. package/src/theme/default/blackTheme.js +32 -0
  204. package/src/theme/default/cyberpunkTheme.js +1 -0
  205. package/src/theme/default/darkTheme.js +1 -0
  206. package/src/theme/default/deepWoodsTheme.js +1 -0
  207. package/src/theme/default/forestTheme.js +1 -0
  208. package/src/theme/default/hackerTheme.js +1 -0
  209. package/src/theme/default/lightTheme.js +1 -0
  210. package/src/theme/default/midnightTheme.js +1 -0
  211. package/src/theme/default/msdosTheme.js +1 -0
  212. package/src/theme/default/oceanTheme.js +1 -0
  213. package/src/theme/default/pastelTheme.js +2 -1
  214. package/src/theme/default/sunsetTheme.js +2 -1
  215. package/src/theme/default/themes.js +2 -0
  216. package/dist/components/calendar/DayPicker.js +0 -1
  217. package/src/components/calendar/DayPicker.js +0 -94
@@ -0,0 +1,58 @@
1
+ export function normalizeSnapPoints(snapPoints, screenHeight) {
2
+ return snapPoints.map((point) => {
3
+ if (typeof point === 'string' && point.endsWith('%')) {
4
+ const percentage = parseFloat(point) / 100;
5
+ return screenHeight * percentage;
6
+ }
7
+ return point;
8
+ });
9
+ }
10
+
11
+ export function findClosestSnapPoint(currentPosition, snapPoints, velocity = 0) {
12
+ 'worklet';
13
+ // Reduced velocity impact for more stable snapping
14
+ const velocityImpact = velocity * 0.03;
15
+ const adjustedPosition = currentPosition + velocityImpact;
16
+
17
+ let closestIndex = 0;
18
+ let minDistance = Math.abs(snapPoints[0] - adjustedPosition);
19
+
20
+ for (let i = 1; i < snapPoints.length; i++) {
21
+ const distance = Math.abs(snapPoints[i] - adjustedPosition);
22
+ if (distance < minDistance) {
23
+ minDistance = distance;
24
+ closestIndex = i;
25
+ }
26
+ }
27
+
28
+ // Increased velocity thresholds for more resistance
29
+ // Only change snap points with strong swipe gestures
30
+
31
+ // Bias towards opening more when swiping up very fast
32
+ if (velocity < -1500 && closestIndex < snapPoints.length - 1) {
33
+ closestIndex++;
34
+ }
35
+ // Bias towards closing when swiping down very fast
36
+ else if (velocity > 1500 && closestIndex > 0) {
37
+ closestIndex--;
38
+ }
39
+
40
+ // Add hysteresis: prefer staying at current position unless moved significantly
41
+ // This prevents accidental snap point changes
42
+ if (snapPoints.length > 1) {
43
+ const currentSnapDistance = minDistance;
44
+ // Require at least 20% of the distance between snap points to change
45
+ const snapPointSpacing = Math.abs(snapPoints[Math.min(closestIndex + 1, snapPoints.length - 1)] - snapPoints[closestIndex]);
46
+ if (currentSnapDistance < snapPointSpacing * 0.2) {
47
+ // Stay at current snap point unless moved significantly
48
+ return closestIndex;
49
+ }
50
+ }
51
+
52
+ return closestIndex;
53
+ }
54
+
55
+ export function clamp(value, min, max) {
56
+ 'worklet';
57
+ return Math.min(Math.max(value, min), max);
58
+ }
@@ -0,0 +1,3 @@
1
+ import { Drawer } from '../../drawer'
2
+
3
+ export const BottomDrawer = (props) => <Drawer height={400} {...props} />
@@ -0,0 +1,5 @@
1
+ import { Modal } from '../modal/Modal'
2
+
3
+ export function Drawer(props) {
4
+ return <Modal position="bottom" {...props} />
5
+ }
@@ -0,0 +1,3 @@
1
+ import { BottomDrawer } from '../bottomDrawer/native/BottomDrawer'
2
+
3
+ export const Drawer = BottomDrawer
@@ -0,0 +1 @@
1
+ export * from './Drawer'
@@ -1,8 +1,13 @@
1
1
  export * from './Accordion'
2
2
  export * from './AccordionGroup'
3
3
  export * from './View'
4
+ export * from './GradientView'
5
+ export * from './BlurView'
4
6
  export * from './SafeAreaView'
5
7
  export * from './Card'
6
8
  export * from './Row'
7
9
  export * from './Col'
10
+ export * from './modal'
11
+ export * from './drawer'
12
+ export * from './bottomDrawer'
8
13
  export * from './popover/Popover'
@@ -0,0 +1,84 @@
1
+ import { pipe } from 'ramda'
2
+
3
+ import { AnimatedView } from '../../animations/AnimatedView'
4
+ import { ModalBackdrop } from './ModalBackdrop'
5
+ import { ModalContent } from './ModalContent'
6
+ import { ModalFooter } from './ModalFooter'
7
+ import { ModalHeader } from './ModalHeader'
8
+ import { Portal } from '../../helpers/Portal'
9
+ import { useDefaultModifier } from '../../../modifiers/default'
10
+ import { useThemeComponentModifier } from '../../../modifiers/themeComponent'
11
+
12
+ const DEFAULT_PROPS = ([{}, { position }]) => {
13
+ let radiusKey = 'br'
14
+ let height = undefined
15
+ if (position === 'bottom') {
16
+ radiusKey = 'borderRadiusT'
17
+ height = '95%'
18
+ } else if (position === 'top') {
19
+ radiusKey = 'borderRadiusB'
20
+ height = '95%'
21
+ } else if (position === 'right') {
22
+ radiusKey = 'borderRadiusL'
23
+ height = '100%'
24
+ } else if (position === 'left') {
25
+ radiusKey = 'borderRadiusR'
26
+ height = '100%'
27
+ }
28
+
29
+ return {
30
+ maxWidth: '100%',
31
+ maxHeight: !position && '95%',
32
+ height,
33
+ scale: !position,
34
+ fade: !!position,
35
+ slide: position && { from: position },
36
+ [radiusKey]: 'xl',
37
+ bg: 'overlayBG',
38
+ overflow: 'hidden',
39
+ shadow: true,
40
+ }
41
+ }
42
+
43
+ export function Modal({
44
+ open,
45
+ onClose,
46
+ title,
47
+ width = 500,
48
+ children,
49
+ header,
50
+ footer,
51
+ noLayout,
52
+ disableOutsideClick,
53
+ ...rootProps
54
+ }) {
55
+ const [{}, formattedProps] = pipe(
56
+ useThemeComponentModifier('Modal'),
57
+ useDefaultModifier(DEFAULT_PROPS)
58
+ )([{}, rootProps])
59
+
60
+ const { contentProps, headerProps, footerProps, position, ...props } = formattedProps
61
+
62
+ let content = children
63
+ if (!noLayout) {
64
+ content = (
65
+ <>
66
+ <ModalHeader title={title} onClose={onClose} {...headerProps}>
67
+ {header}
68
+ </ModalHeader>
69
+ <ModalContent {...contentProps}>{children}</ModalContent>
70
+ <ModalFooter {...footerProps}>{footer}</ModalFooter>
71
+ </>
72
+ )
73
+ }
74
+
75
+ return (
76
+ <Portal>
77
+ <ModalBackdrop open={open} onClose={!disableOutsideClick && onClose} position={position}>
78
+ <AnimatedView className="neko-modal" open={open} width={width} {...props} onPress={(e) => e.stopPropagation()}>
79
+ {content}
80
+ </AnimatedView>
81
+ </ModalBackdrop>
82
+ </Portal>
83
+ )
84
+ }
@@ -0,0 +1,83 @@
1
+ import { Modal as RNModal, ScrollView } from 'react-native'
2
+ import { pipe } from 'ramda'
3
+
4
+ import { ModalBackdrop } from './ModalBackdrop'
5
+ import { ModalContent } from './ModalContent'
6
+ import { ModalFooter } from './ModalFooter'
7
+ import { ModalHeader } from './ModalHeader'
8
+ import { Pressable } from '../../actions/Pressable'
9
+ import { View } from '../View'
10
+ import { useDefaultModifier } from '../../../modifiers/default'
11
+ import { useThemeComponentModifier } from '../../../modifiers/themeComponent'
12
+
13
+ const DEFAULT_PROPS = {
14
+ maxWidth: '90%',
15
+ maxHeight: '90%',
16
+ scale: true,
17
+ br: 'xl',
18
+ bg: 'overlayBG',
19
+ overflow: 'hidden',
20
+ shadow: true,
21
+ }
22
+
23
+ export function Modal({
24
+ open,
25
+ onClose,
26
+ title,
27
+ width = 500,
28
+ children,
29
+ header,
30
+ footer,
31
+ noLayout,
32
+ disableOutsideClick,
33
+ ...rootProps
34
+ }) {
35
+ const [{}, formattedProps] = pipe(
36
+ useThemeComponentModifier('Modal'),
37
+ useDefaultModifier(DEFAULT_PROPS)
38
+ )([{}, rootProps])
39
+
40
+ const { contentProps, headerProps, footerProps, ...props } = formattedProps
41
+
42
+ let content = children
43
+ if (!noLayout) {
44
+ content = (
45
+ <>
46
+ <ModalHeader title={title} onClose={onClose} {...headerProps}>
47
+ {header}
48
+ </ModalHeader>
49
+ <ScrollView>
50
+ <ModalContent {...contentProps}>{children}</ModalContent>
51
+ </ScrollView>
52
+ <ModalFooter {...footerProps}>{footer}</ModalFooter>
53
+ </>
54
+ )
55
+ }
56
+
57
+ return (
58
+ <RNModal
59
+ animationType="fade"
60
+ transparent
61
+ visible={open}
62
+ onRequestClose={onClose}
63
+ allowSwipeDismissal
64
+ navigationBarTranslucent
65
+ statusBarTranslucent
66
+ >
67
+ <ModalBackdrop open={open} useSimpleView>
68
+ <Pressable
69
+ onPress={!disableOutsideClick ? onClose : undefined}
70
+ absolute
71
+ top={0}
72
+ left={0}
73
+ right={0}
74
+ bottom={0}
75
+ pointerEvents="box-only"
76
+ />
77
+ <View width={width} {...props} zIndex={10}>
78
+ {content}
79
+ </View>
80
+ </ModalBackdrop>
81
+ </RNModal>
82
+ )
83
+ }
@@ -0,0 +1,58 @@
1
+ import { pipe } from 'ramda'
2
+ import React from 'react'
3
+
4
+ import { AnimatedView } from '../../animations/AnimatedView'
5
+ import { SafeAreaView } from '../SafeAreaView'
6
+ import { useDefaultModifier } from '../../../modifiers/default'
7
+ import { useThemeComponentModifier } from '../../../modifiers/themeComponent'
8
+
9
+ const DEFAULT_PROPS = ([{}, { position }]) => {
10
+ let justify = 'center'
11
+ let align = 'center'
12
+ if (position === 'bottom') {
13
+ justify = 'flex-end'
14
+ } else if (position === 'top') {
15
+ justify = 'flex-top'
16
+ } else if (position === 'left') {
17
+ align = 'flex-start'
18
+ } else if (position === 'right') {
19
+ align = 'flex-end'
20
+ }
21
+
22
+ return {
23
+ fixed: true,
24
+ top: 0,
25
+ left: 0,
26
+ right: 0,
27
+ bottom: 0,
28
+ justify,
29
+ align,
30
+ lazy: true,
31
+ unmountOnClose: true,
32
+ fade: true,
33
+ zIndex: 500,
34
+ bg: 'backdrop_op70',
35
+ }
36
+ }
37
+
38
+ export function ModalBackdrop({ open, onClose, children, useSimpleView, ...rootProps }) {
39
+ const modalRef = React.useRef()
40
+ const [{}, props] = pipe(
41
+ useThemeComponentModifier('ModalBackdrop'),
42
+ useDefaultModifier(DEFAULT_PROPS)
43
+ )([{}, rootProps])
44
+
45
+ if (useSimpleView) {
46
+ return (
47
+ <SafeAreaView className="neko-modal-backdrop" {...props}>
48
+ {children}
49
+ </SafeAreaView>
50
+ )
51
+ }
52
+
53
+ return (
54
+ <AnimatedView open={open} className="neko-modal-backdrop" onPress={onClose} {...props}>
55
+ {children}
56
+ </AnimatedView>
57
+ )
58
+ }
@@ -0,0 +1,28 @@
1
+ import { pipe } from 'ramda'
2
+
3
+ import { View } from '../View'
4
+ import { useDefaultModifier } from '../../../modifiers/default'
5
+ import { useThemeComponentModifier } from '../../../modifiers/themeComponent'
6
+
7
+ const DEFAULT_PROPS = {
8
+ fullW: true,
9
+ flex: true,
10
+ // fullH: true,
11
+ scrollY: true,
12
+ padding: 'md',
13
+ }
14
+
15
+ export function ModalContent({ children, ...rootProps }) {
16
+ const [{}, props] = pipe(
17
+ useThemeComponentModifier('ModalContent'), //
18
+ useDefaultModifier(DEFAULT_PROPS)
19
+ )([{}, rootProps])
20
+
21
+ if (!children) return false
22
+
23
+ return (
24
+ <View className="neko-modal-content" {...props}>
25
+ {children}
26
+ </View>
27
+ )
28
+ }
@@ -0,0 +1,31 @@
1
+ import { pipe } from 'ramda'
2
+
3
+ import { View } from '../View'
4
+ import { moveScale } from '../../../theme/helpers/sizeScale'
5
+ import { useDefaultModifier } from '../../../modifiers/default'
6
+ import { useSizeConverter } from '../../../modifiers/sizeConverter'
7
+ import { useThemeComponentModifier } from '../../../modifiers/themeComponent'
8
+
9
+ const DEFAULT_PROPS = ([{ sizeCode }, _]) => ({
10
+ borderT: true,
11
+ paddingH: sizeCode,
12
+ paddingV: moveScale(sizeCode, -2),
13
+ minHeight: moveScale(sizeCode, 1),
14
+ centerV: true,
15
+ })
16
+
17
+ export function ModalFooter({ children, ...rootProps }) {
18
+ const [{}, props] = pipe(
19
+ useSizeConverter('elementHeights', 'md'),
20
+ useThemeComponentModifier('ModalFooter'), //
21
+ useDefaultModifier(DEFAULT_PROPS)
22
+ )([{}, rootProps])
23
+
24
+ if (!children) return false
25
+
26
+ return (
27
+ <View className="neko-modal-footer" {...props}>
28
+ {children}
29
+ </View>
30
+ )
31
+ }
@@ -0,0 +1,50 @@
1
+ import { pipe } from 'ramda'
2
+
3
+ import { Icon } from '../../presentation/Icon'
4
+ import { Link } from '../../actions/Link'
5
+ import { Text } from '../../text/Text'
6
+ import { View } from '../View'
7
+ import { moveScale } from '../../../theme/helpers/sizeScale'
8
+ import { useDefaultModifier } from '../../../modifiers/default'
9
+ import { useSizeConverter } from '../../../modifiers/sizeConverter'
10
+ import { useThemeComponentModifier } from '../../../modifiers/themeComponent'
11
+
12
+ const DEFAULT_PROPS = ([{ sizeCode }, _]) => ({
13
+ centerV: true,
14
+ paddingH: sizeCode,
15
+ paddingV: moveScale(sizeCode, -2),
16
+ minHeight: moveScale(sizeCode, 1),
17
+ gap: 'md',
18
+ borderB: true,
19
+ justify: 'space-between',
20
+
21
+ titleProps: {
22
+ strong: true,
23
+ },
24
+ })
25
+
26
+ export function ModalHeader({ onClose, title, children, ...rootProps }) {
27
+ const [{}, formattedProps] = pipe(
28
+ useSizeConverter('elementHeights', 'md'),
29
+ useThemeComponentModifier('ModalHeader'),
30
+ useDefaultModifier(DEFAULT_PROPS)
31
+ )([{}, rootProps])
32
+
33
+ const { titleProps, ...props } = formattedProps
34
+
35
+ if (!title && !children) return false
36
+
37
+ return (
38
+ <View className="neko-modal-header" {...props} row>
39
+ {title && <Text {...titleProps}>{title}</Text>}
40
+
41
+ {children}
42
+
43
+ {!!onClose && (
44
+ <Link onPress={onClose} padding="sm" marginR={-10}>
45
+ <Icon name="close-line" />
46
+ </Link>
47
+ )}
48
+ </View>
49
+ )
50
+ }
@@ -0,0 +1,61 @@
1
+ import React from 'react'
2
+
3
+ import { Modal } from '../Modal'
4
+ import { useResponsiveValue } from '../../../../responsive/responsiveHooks'
5
+
6
+ const ModalsContext = React.createContext(null)
7
+
8
+ export const useModals = () => React.useContext(ModalsContext) || {}
9
+
10
+ let idCounter = 0
11
+
12
+ export function useModalOpener() {
13
+ const { add, remove, close } = useModals()
14
+ const open = (init) => {
15
+ const key = ++idCounter
16
+
17
+ const onClose = () => close(key)
18
+ const params = { onClose, key }
19
+
20
+ const data = init(params)
21
+
22
+ add(key, { onClose, ...data, open: true })
23
+ return params
24
+ }
25
+
26
+ return {
27
+ open,
28
+ }
29
+ }
30
+
31
+ export function ModalsHandler({ children }) {
32
+ const width = useResponsiveValue({ sm: '100%', df: 400 })
33
+ const [modals, setModals] = React.useState([])
34
+
35
+ const add = React.useCallback((key, data) => {
36
+ setModals((prev) => [...prev, { key, ...data }])
37
+ }, [])
38
+
39
+ const remove = React.useCallback((key) => {
40
+ setModals((prev) => prev.filter((p) => p.key !== key))
41
+ }, [])
42
+
43
+ const close = React.useCallback((key) => {
44
+ setModals((items) => items.map((i) => (i.key === key ? { ...i, open: false } : i)))
45
+ setTimeout(() => remove(key), 600)
46
+ }, [])
47
+
48
+ const value = React.useMemo(() => ({ add, remove, close }), [add, remove, close])
49
+
50
+ return (
51
+ <ModalsContext.Provider value={value}>
52
+ {children}
53
+
54
+ {modals?.map?.(({ key, content, ...item }) => (
55
+ <Modal key={key} {...item}>
56
+ {content}
57
+ </Modal>
58
+ ))}
59
+ </ModalsContext.Provider>
60
+ )
61
+ }
@@ -0,0 +1,6 @@
1
+ export * from './handler/ModalsHandler'
2
+ export * from './Modal'
3
+ export * from './ModalContent'
4
+ export * from './ModalHeader'
5
+ export * from './ModalFooter'
6
+ export * from './ModalBackdrop'
@@ -43,7 +43,7 @@ export function OverlayWrapper({ children, triggerRect, placement, onClickOutsid
43
43
  top={pos.y}
44
44
  left={pos.x}
45
45
  zIndex={9999}
46
- pointerEvents="none"
46
+ ointerEvents="none"
47
47
  {...props}
48
48
  >
49
49
  {children}
@@ -1,7 +1,9 @@
1
1
  import React from 'react'
2
2
 
3
+ import { BottomDrawer } from '../bottomDrawer'
3
4
  import { PopoverContent } from './PopoverContent'
4
5
  import { useRegisterOverlay } from '../overlay/OverlayHandler'
6
+ import { useResponsiveValue } from '../../../responsive'
5
7
 
6
8
  export function Popover({
7
9
  renderContent,
@@ -12,8 +14,10 @@ export function Popover({
12
14
  children,
13
15
  parentWidth,
14
16
  parentMinWidth,
17
+ useBottomDrawer = {},
15
18
  ...props
16
19
  }) {
20
+ const shouldUseDrawer = useResponsiveValue(useBottomDrawer)
17
21
  const ref = React.useRef(null)
18
22
  const { onOpen, onClose, onFastClose, stopDelayedClosing } = useRegisterOverlay({ unmountOnClose })
19
23
 
@@ -53,17 +57,39 @@ export function Popover({
53
57
  ),
54
58
  triggerRect,
55
59
  placement,
56
- options: { dismissOnClickOutside: !!click },
60
+ options: { dismissOnClickOutside: click || focus },
57
61
  })
58
62
  }
59
63
 
60
64
  React.useEffect(() => () => onClose(), [])
61
65
 
66
+ if (shouldUseDrawer) {
67
+ return <DrawerPopover content={content} renderContent={renderContent} children={children} {...props} />
68
+ }
69
+
62
70
  const child = React.Children.only(children)
63
71
  let childProps = { ref, onClick: show }
64
72
 
65
73
  if (hover) childProps = { ref, onMouseEnter: show, onMouseLeave: onClose }
66
- if (focus) childProps = { ref, onFocus: show, onBlur: onClose }
74
+ if (focus) childProps = { ref, onFocus: show }
67
75
 
68
76
  return React.cloneElement(child, childProps)
69
77
  }
78
+
79
+ function DrawerPopover({ children, content, renderContent, snapPoints, ...props }) {
80
+ const [open, setOpen] = React.useState(false)
81
+ const onClose = () => setOpen(false)
82
+
83
+ const child = React.Children.only(children)
84
+ let childProps = { onClick: () => setOpen(true) }
85
+
86
+ return (
87
+ <>
88
+ {React.cloneElement(child, childProps)}
89
+
90
+ <BottomDrawer open={open} onClose={onClose} snapPoints={snapPoints}>
91
+ {renderContent({ onClose })}
92
+ </BottomDrawer>
93
+ </>
94
+ )
95
+ }
@@ -1,17 +1,34 @@
1
1
  import { Modal, TouchableWithoutFeedback } from 'react-native'
2
2
  import React from 'react'
3
3
 
4
+ import { BottomDrawer } from '../bottomDrawer'
4
5
  import { PopoverContent } from './PopoverContent'
5
6
  import { View } from '../View'
6
7
  import { calculatePosition } from '../overlay/calculatePosition'
8
+ import { useResponsiveValue } from '../../../responsive'
7
9
 
8
- export function Popover({ content, placement = 'bottom', children, ...props }) {
10
+ export function Popover({
11
+ content,
12
+ renderContent,
13
+ placement = 'bottom',
14
+ children,
15
+ useBottomDrawer = {},
16
+ snapPoints,
17
+ ...props
18
+ }) {
19
+ const shouldUseDrawer = useResponsiveValue(useBottomDrawer)
9
20
  const ref = React.useRef(null)
10
- const [visible, setVisible] = React.useState(false)
21
+ const [open, setOpen] = React.useState(false)
11
22
  const [triggerRect, setTriggerRect] = React.useState(null)
12
23
  const [position, setPosition] = React.useState(null)
13
24
 
14
- const show = () => {
25
+ renderContent = renderContent || (() => content)
26
+
27
+ const onOpen = () => {
28
+ if (shouldUseDrawer) {
29
+ setOpen(true)
30
+ return
31
+ }
15
32
  if (ref.current) {
16
33
  ref.current.measureInWindow((x, y, width, height) => {
17
34
  setTriggerRect({
@@ -22,29 +39,41 @@ export function Popover({ content, placement = 'bottom', children, ...props }) {
22
39
  width,
23
40
  height,
24
41
  })
25
- setVisible(true)
42
+ setOpen(true)
26
43
  })
27
44
  }
28
45
  }
29
46
 
30
- const hide = () => {
31
- setVisible(false)
47
+ const onClose = () => {
48
+ setOpen(false)
32
49
  setTriggerRect(null)
33
50
  setPosition(null)
34
51
  }
35
52
 
36
- return (
37
- <>
53
+ children = React.cloneElement(React.Children.only(children), {
54
+ onPress: onOpen,
55
+ })
56
+
57
+ if (shouldUseDrawer) {
58
+ return (
38
59
  <View ref={ref}>
39
- {React.cloneElement(React.Children.only(children), {
40
- onPress: show,
41
- })}
60
+ {children}
61
+
62
+ <BottomDrawer open={open} onClose={onClose} snapPoints={snapPoints}>
63
+ {renderContent({ onClose: onClose })}
64
+ </BottomDrawer>
42
65
  </View>
66
+ )
67
+ }
68
+
69
+ return (
70
+ <>
71
+ <View ref={ref}>{children}</View>
43
72
 
44
- {visible && (
45
- <Modal transparent visible={visible} animationType="fade" onRequestClose={hide}>
73
+ {open && (
74
+ <Modal transparent visible={open} animationType="fade" onRequestClose={onClose}>
46
75
  <View fullW flex fullH bg="bg_op50">
47
- <TouchableWithoutFeedback onPress={hide}>
76
+ <TouchableWithoutFeedback onPress={onClose}>
48
77
  <View style={{ flex: 1 }}>
49
78
  {triggerRect && (
50
79
  <View
@@ -61,7 +90,7 @@ export function Popover({ content, placement = 'bottom', children, ...props }) {
61
90
  }}
62
91
  >
63
92
  <PopoverContent placement={placement} {...props}>
64
- {content}
93
+ {renderContent({ onClose: onClose })}
65
94
  </PopoverContent>
66
95
  </View>
67
96
  )}
@@ -3,6 +3,7 @@ import React from 'react'
3
3
 
4
4
  import { useApplyStyles } from '../applyStyles'
5
5
  import { useFadeEffect } from './fadeEffect'
6
+ import { useScaleEffect } from './scaleEffect'
6
7
  import { useSlideEffect } from './slideEffect'
7
8
 
8
9
  export function useAnimatedEffects([values, { open, onClose, lazy = false, unmountOnClose = false, ...props }]) {
@@ -41,6 +42,7 @@ export function useAnimatedEffects([values, { open, onClose, lazy = false, unmou
41
42
  return pipe(
42
43
  useFadeEffect, //
43
44
  useSlideEffect,
45
+ useScaleEffect,
44
46
  useApplyStyles({ transition: transitions.join(',') })
45
47
  )([
46
48
  {