@neko-os/ui 0.0.5 → 0.0.7

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 (337) hide show
  1. package/dist/NekoUI.js +1 -1
  2. package/dist/abstractions/ActivityIndicator.js +1 -0
  3. package/dist/abstractions/ActivityIndicator.native.js +1 -0
  4. package/dist/abstractions/AnimatedView.js +1 -0
  5. package/dist/abstractions/AnimatedView.native.js +1 -0
  6. package/dist/abstractions/DraggableSlideView.js +1 -0
  7. package/dist/abstractions/DraggableSlideView.native.js +1 -0
  8. package/dist/abstractions/Icon.js +1 -1
  9. package/dist/abstractions/Image.js +1 -0
  10. package/dist/abstractions/Image.native.js +1 -0
  11. package/dist/abstractions/Image.web.js +1 -0
  12. package/dist/abstractions/Platform.js +1 -0
  13. package/dist/abstractions/Platform.native.js +1 -0
  14. package/dist/abstractions/Platform.web.js +1 -0
  15. package/dist/abstractions/SafeAreaView.js +1 -0
  16. package/dist/abstractions/SafeAreaView.native.js +1 -0
  17. package/dist/abstractions/Table.js +1 -0
  18. package/dist/abstractions/Table.native.js +1 -0
  19. package/dist/abstractions/Text.js +1 -1
  20. package/dist/abstractions/TextInput.js +1 -0
  21. package/dist/abstractions/TextInput.native.js +1 -0
  22. package/dist/abstractions/TextInput.web.js +1 -0
  23. package/dist/abstractions/View.js +1 -1
  24. package/dist/abstractions/helpers/componentSize.js +1 -0
  25. package/dist/abstractions/helpers/componentSize.native.js +1 -0
  26. package/dist/abstractions/helpers/transformStyle.js +1 -0
  27. package/dist/abstractions/helpers/transformStyle.native.js +1 -0
  28. package/dist/components/actions/Breadcrumb.js +1 -0
  29. package/dist/components/actions/Button.js +1 -1
  30. package/dist/components/actions/Dropdown.js +1 -0
  31. package/dist/components/actions/Link.js +1 -1
  32. package/dist/components/actions/index.js +1 -1
  33. package/dist/components/actions/menu/HorizontalMenu.js +1 -0
  34. package/dist/components/actions/menu/Menu.js +1 -0
  35. package/dist/components/actions/menu/SubmenuWrapper.js +1 -0
  36. package/dist/components/actions/menu/VerticalMenu.js +1 -0
  37. package/dist/components/animations/AnimatedView.js +1 -0
  38. package/dist/components/animations/DraggableSlideView.js +1 -0
  39. package/dist/components/animations/index.js +1 -0
  40. package/dist/components/calendar/DayPicker.js +1 -0
  41. package/dist/components/calendar/_helpers/calendarDays.js +1 -0
  42. package/dist/components/calendar/index.js +1 -0
  43. package/dist/components/feedback/index.js +1 -0
  44. package/dist/components/feedback/notifications/Notification.js +1 -0
  45. package/dist/components/feedback/notifications/NotificationsHandler.js +1 -0
  46. package/dist/components/form/Form.js +1 -1
  47. package/dist/components/form/FormGroup.js +1 -1
  48. package/dist/components/form/FormItem.js +1 -1
  49. package/dist/components/form/FormList.js +1 -1
  50. package/dist/components/form/FormWrapperComponent.js +1 -1
  51. package/dist/components/form/FormWrapperComponent.native.js +1 -1
  52. package/dist/components/form/SubmitButton.js +1 -0
  53. package/dist/components/form/index.js +1 -1
  54. package/dist/components/form/useNewForm.js +1 -0
  55. package/dist/components/form/useWatch.js +1 -0
  56. package/dist/components/helpers/LazyRender.js +1 -0
  57. package/dist/components/helpers/LazyRender.native.js +1 -0
  58. package/dist/components/helpers/Portal.js +1 -0
  59. package/dist/components/helpers/PortalHandler.js +1 -0
  60. package/dist/components/helpers/Responsive.js +1 -1
  61. package/dist/components/helpers/Separator.js +1 -1
  62. package/dist/components/helpers/VerticalView.js +1 -0
  63. package/dist/components/helpers/index.js +1 -1
  64. package/dist/components/index.js +1 -1
  65. package/dist/components/inputs/Checkbox.js +1 -0
  66. package/dist/components/inputs/InputWrapper.js +1 -0
  67. package/dist/components/inputs/Picker.js +1 -0
  68. package/dist/components/inputs/Radio.js +1 -0
  69. package/dist/components/inputs/Switch.js +1 -0
  70. package/dist/components/inputs/TextInput.js +1 -0
  71. package/dist/components/inputs/index.js +1 -0
  72. package/dist/components/layout/Layout.js +1 -0
  73. package/dist/components/layout/LayoutContent.js +1 -0
  74. package/dist/components/layout/LayoutHeader.js +1 -0
  75. package/dist/components/layout/LayoutSider.js +1 -0
  76. package/dist/components/layout/index.js +1 -0
  77. package/dist/components/presentation/Avatar.js +1 -0
  78. package/dist/components/presentation/AvatarLabel.js +1 -0
  79. package/dist/components/presentation/Badge.js +1 -0
  80. package/dist/components/presentation/ContentLabel.js +1 -1
  81. package/dist/components/presentation/IconLabel.js +1 -1
  82. package/dist/components/presentation/Image.js +1 -0
  83. package/dist/components/presentation/LabelValue.js +1 -0
  84. package/dist/components/presentation/Result.js +1 -0
  85. package/dist/components/presentation/ResultBar.js +1 -0
  86. package/dist/components/presentation/Tag.js +1 -1
  87. package/dist/components/presentation/Tooltip.js +1 -0
  88. package/dist/components/presentation/index.js +1 -1
  89. package/dist/components/state/Loading.js +1 -0
  90. package/dist/components/state/LoadingView.js +1 -0
  91. package/dist/components/state/index.js +1 -0
  92. package/dist/components/structure/Accordion.js +1 -0
  93. package/dist/components/structure/AccordionGroup.js +1 -0
  94. package/dist/components/structure/Card.js +1 -1
  95. package/dist/components/structure/Col.js +1 -0
  96. package/dist/components/structure/Row.js +1 -0
  97. package/dist/components/structure/SafeAreaView.js +1 -0
  98. package/dist/components/structure/View.js +1 -1
  99. package/dist/components/structure/index.js +1 -1
  100. package/dist/components/structure/overlay/OverlayHandler.js +1 -0
  101. package/dist/components/structure/overlay/OverlayHandler.native.js +1 -0
  102. package/dist/components/structure/overlay/OverlayWrapper.js +1 -0
  103. package/dist/components/structure/overlay/calculatePosition.js +1 -0
  104. package/dist/components/structure/overlay/smartPlacement.js +1 -0
  105. package/dist/components/structure/popover/Popover.js +1 -0
  106. package/dist/components/structure/popover/Popover.native.js +1 -0
  107. package/dist/components/structure/popover/PopoverContent.js +1 -0
  108. package/dist/components/table/DataTable.js +1 -0
  109. package/dist/components/table/Pagination.js +1 -0
  110. package/dist/components/table/Table.js +1 -0
  111. package/dist/components/table/TableCol.js +1 -0
  112. package/dist/components/table/TableHeader.js +1 -0
  113. package/dist/components/table/TableHeaderRow.js +1 -0
  114. package/dist/components/table/TableRow.js +1 -0
  115. package/dist/components/table/index.js +1 -0
  116. package/dist/components/text/Text.js +1 -1
  117. package/dist/components/text/VerticalText.js +1 -0
  118. package/dist/components/text/index.js +1 -1
  119. package/dist/helpers/debounce.js +1 -0
  120. package/dist/helpers/index.js +1 -1
  121. package/dist/helpers/options.js +1 -0
  122. package/dist/helpers/random.js +1 -0
  123. package/dist/index.css +10 -0
  124. package/dist/index.js +1 -1
  125. package/dist/modifiers/alignConverter.js +1 -0
  126. package/dist/modifiers/animation.js +1 -0
  127. package/dist/modifiers/animations/animatedEffects.js +1 -0
  128. package/dist/modifiers/animations/animatedEffects.native.js +1 -0
  129. package/dist/modifiers/animations/animatedEffects.web.js +1 -0
  130. package/dist/modifiers/animations/fadeEffect.js +1 -0
  131. package/dist/modifiers/animations/fadeEffect.native.js +1 -0
  132. package/dist/modifiers/animations/slideEffect.js +1 -0
  133. package/dist/modifiers/animations/slideEffect.native.js +1 -0
  134. package/dist/modifiers/applyStyles.js +1 -0
  135. package/dist/modifiers/border.js +1 -1
  136. package/dist/modifiers/display.js +1 -1
  137. package/dist/modifiers/flex.js +1 -1
  138. package/dist/modifiers/flexWrapper.js +1 -1
  139. package/dist/modifiers/fullColor.js +1 -1
  140. package/dist/modifiers/grid.js +1 -0
  141. package/dist/modifiers/overflow.js +1 -0
  142. package/dist/modifiers/position.js +1 -1
  143. package/dist/modifiers/responsiveConverter.js +1 -0
  144. package/dist/modifiers/size.js +1 -1
  145. package/dist/modifiers/state.js +1 -0
  146. package/dist/responsive/ResponsiveHandler.js +1 -0
  147. package/dist/responsive/index.js +1 -0
  148. package/dist/responsive/responsiveHooks.js +1 -0
  149. package/dist/theme/ThemeHandler.js +1 -1
  150. package/dist/theme/default/base.js +1 -1
  151. package/dist/theme/default/darkTheme.js +1 -1
  152. package/dist/theme/default/hackerTheme.js +1 -1
  153. package/dist/theme/default/lightTheme.js +1 -1
  154. package/dist/theme/default/msdosTheme.js +1 -1
  155. package/dist/theme/helpers/contrastColor.js +1 -0
  156. package/dist/theme/helpers/dynamicColor.js +1 -0
  157. package/dist/theme/helpers/relatedScales.js +1 -1
  158. package/dist/theme/helpers/sizeScale.js +1 -1
  159. package/dist/theme/helpers/textScale.js +1 -1
  160. package/package.json +9 -4
  161. package/src/NekoUI.js +15 -1
  162. package/src/abstractions/ActivityIndicator.js +31 -0
  163. package/src/abstractions/ActivityIndicator.native.js +44 -0
  164. package/src/abstractions/AnimatedView.js +3 -0
  165. package/src/abstractions/AnimatedView.native.js +6 -0
  166. package/src/abstractions/DraggableSlideView.js +85 -0
  167. package/src/abstractions/DraggableSlideView.native.js +62 -0
  168. package/src/abstractions/Icon.js +4 -42
  169. package/src/abstractions/Image.js +12 -0
  170. package/src/abstractions/Image.native.js +7 -0
  171. package/src/abstractions/Image.web.js +7 -0
  172. package/src/abstractions/Platform.js +1 -0
  173. package/src/abstractions/Platform.native.js +3 -0
  174. package/src/abstractions/Platform.web.js +3 -0
  175. package/src/abstractions/SafeAreaView.js +3 -0
  176. package/src/abstractions/SafeAreaView.native.js +3 -0
  177. package/src/abstractions/Table.js +29 -0
  178. package/src/abstractions/Table.native.js +19 -0
  179. package/src/abstractions/Text.js +13 -2
  180. package/src/abstractions/TextInput.js +3 -0
  181. package/src/abstractions/TextInput.native.js +5 -0
  182. package/src/abstractions/TextInput.web.js +5 -0
  183. package/src/abstractions/View.js +2 -2
  184. package/src/abstractions/helpers/componentSize.js +13 -0
  185. package/src/abstractions/helpers/componentSize.native.js +12 -0
  186. package/src/abstractions/helpers/transformStyle.js +8 -0
  187. package/src/abstractions/helpers/transformStyle.native.js +3 -0
  188. package/src/components/actions/Breadcrumb.js +47 -0
  189. package/src/components/actions/Button.js +8 -3
  190. package/src/components/actions/Dropdown.js +68 -0
  191. package/src/components/actions/Link.js +21 -8
  192. package/src/components/actions/index.js +3 -0
  193. package/src/components/actions/menu/HorizontalMenu.js +96 -0
  194. package/src/components/actions/menu/Menu.js +7 -0
  195. package/src/components/actions/menu/SubmenuWrapper.js +16 -0
  196. package/src/components/actions/menu/VerticalMenu.js +107 -0
  197. package/src/components/animations/AnimatedView.js +45 -0
  198. package/src/components/animations/DraggableSlideView.js +42 -0
  199. package/src/components/animations/index.js +2 -0
  200. package/src/components/calendar/DayPicker.js +94 -0
  201. package/src/components/calendar/_helpers/calendarDays.js +16 -0
  202. package/src/components/calendar/index.js +1 -0
  203. package/src/components/feedback/index.js +1 -0
  204. package/src/components/feedback/notifications/Notification.js +37 -0
  205. package/src/components/feedback/notifications/NotificationsHandler.js +65 -0
  206. package/src/components/form/Form.js +15 -4
  207. package/src/components/form/FormGroup.js +4 -4
  208. package/src/components/form/FormItem.js +30 -8
  209. package/src/components/form/FormList.js +45 -9
  210. package/src/components/form/FormWrapperComponent.js +37 -2
  211. package/src/components/form/FormWrapperComponent.native.js +2 -2
  212. package/src/components/form/SubmitButton.js +20 -0
  213. package/src/components/form/index.js +3 -2
  214. package/src/components/form/useNewForm.js +67 -0
  215. package/src/components/form/useWatch.js +70 -0
  216. package/src/components/helpers/LazyRender.js +55 -0
  217. package/src/components/helpers/LazyRender.native.js +58 -0
  218. package/src/components/helpers/Portal.js +21 -0
  219. package/src/components/helpers/PortalHandler.js +32 -0
  220. package/src/components/helpers/Responsive.js +1 -1
  221. package/src/components/helpers/Separator.js +10 -14
  222. package/src/components/helpers/VerticalView.js +34 -0
  223. package/src/components/helpers/index.js +4 -0
  224. package/src/components/index.js +7 -0
  225. package/src/components/inputs/Checkbox.js +56 -0
  226. package/src/components/inputs/InputWrapper.js +79 -0
  227. package/src/components/inputs/Picker.js +116 -0
  228. package/src/components/inputs/Radio.js +55 -0
  229. package/src/components/inputs/Switch.js +60 -0
  230. package/src/components/inputs/TextInput.js +22 -0
  231. package/src/components/inputs/index.js +6 -0
  232. package/src/components/layout/Layout.js +40 -0
  233. package/src/components/layout/LayoutContent.js +42 -0
  234. package/src/components/layout/LayoutHeader.js +69 -0
  235. package/src/components/layout/LayoutSider.js +64 -0
  236. package/src/components/layout/index.js +4 -0
  237. package/src/components/presentation/Avatar.js +79 -0
  238. package/src/components/presentation/AvatarLabel.js +58 -0
  239. package/src/components/presentation/Badge.js +90 -0
  240. package/src/components/presentation/ContentLabel.js +24 -6
  241. package/src/components/presentation/IconLabel.js +12 -2
  242. package/src/components/presentation/Image.js +33 -0
  243. package/src/components/presentation/LabelValue.js +49 -0
  244. package/src/components/presentation/Result.js +60 -0
  245. package/src/components/presentation/ResultBar.js +56 -0
  246. package/src/components/presentation/Tag.js +8 -11
  247. package/src/components/presentation/Tooltip.js +43 -0
  248. package/src/components/presentation/index.js +8 -0
  249. package/src/components/state/Loading.js +20 -0
  250. package/src/components/state/LoadingView.js +28 -0
  251. package/src/components/state/index.js +2 -0
  252. package/src/components/structure/Accordion.js +69 -0
  253. package/src/components/structure/AccordionGroup.js +35 -0
  254. package/src/components/structure/Card.js +4 -1
  255. package/src/components/structure/Col.js +22 -0
  256. package/src/components/structure/Row.js +42 -0
  257. package/src/components/structure/SafeAreaView.js +42 -0
  258. package/src/components/structure/View.js +9 -3
  259. package/src/components/structure/index.js +6 -0
  260. package/src/components/structure/overlay/OverlayHandler.js +70 -0
  261. package/src/components/structure/overlay/OverlayHandler.native.js +6 -0
  262. package/src/components/structure/overlay/OverlayWrapper.js +52 -0
  263. package/src/components/structure/overlay/calculatePosition.js +29 -0
  264. package/src/components/structure/overlay/smartPlacement.js +32 -0
  265. package/src/components/structure/popover/Popover.js +69 -0
  266. package/src/components/structure/popover/Popover.native.js +75 -0
  267. package/src/components/structure/popover/PopoverContent.js +18 -0
  268. package/src/components/table/DataTable.js +57 -0
  269. package/src/components/table/Pagination.js +128 -0
  270. package/src/components/table/Table.js +65 -0
  271. package/src/components/table/TableCol.js +67 -0
  272. package/src/components/table/TableHeader.js +69 -0
  273. package/src/components/table/TableHeaderRow.js +31 -0
  274. package/src/components/table/TableRow.js +30 -0
  275. package/src/components/table/index.js +7 -0
  276. package/src/components/text/Text.js +4 -0
  277. package/src/components/text/VerticalText.js +29 -0
  278. package/src/components/text/index.js +1 -0
  279. package/src/helpers/debounce.js +9 -0
  280. package/src/helpers/index.js +2 -1
  281. package/src/helpers/options.js +65 -0
  282. package/src/helpers/random.js +5 -0
  283. package/src/index.css +10 -0
  284. package/src/index.js +1 -0
  285. package/src/modifiers/alignConverter.js +11 -0
  286. package/src/modifiers/animation.js +18 -0
  287. package/src/modifiers/animations/animatedEffects.js +63 -0
  288. package/src/modifiers/animations/animatedEffects.native.js +53 -0
  289. package/src/modifiers/animations/animatedEffects.web.js +3 -0
  290. package/src/modifiers/animations/fadeEffect.js +43 -0
  291. package/src/modifiers/animations/fadeEffect.native.js +33 -0
  292. package/src/modifiers/animations/slideEffect.js +61 -0
  293. package/src/modifiers/animations/slideEffect.native.js +53 -0
  294. package/src/modifiers/applyStyles.js +7 -0
  295. package/src/modifiers/border.js +30 -6
  296. package/src/modifiers/display.js +3 -5
  297. package/src/modifiers/flex.js +1 -1
  298. package/src/modifiers/flexWrapper.js +48 -7
  299. package/src/modifiers/fullColor.js +5 -5
  300. package/src/modifiers/grid.js +27 -0
  301. package/src/modifiers/overflow.js +23 -0
  302. package/src/modifiers/position.js +10 -2
  303. package/src/modifiers/responsiveConverter.js +19 -0
  304. package/src/modifiers/size.js +10 -4
  305. package/src/modifiers/state.js +33 -0
  306. package/src/responsive/ResponsiveHandler.js +28 -0
  307. package/src/responsive/index.js +2 -0
  308. package/src/responsive/responsiveHooks.js +54 -0
  309. package/src/theme/ThemeHandler.js +1 -1
  310. package/src/theme/default/base.js +22 -22
  311. package/src/theme/default/darkTheme.js +2 -2
  312. package/src/theme/default/hackerTheme.js +8 -0
  313. package/src/theme/default/lightTheme.js +1 -1
  314. package/src/theme/default/msdosTheme.js +1 -1
  315. package/src/theme/helpers/contrastColor.js +20 -0
  316. package/src/theme/helpers/dynamicColor.js +32 -0
  317. package/src/theme/helpers/relatedScales.js +6 -6
  318. package/src/theme/helpers/sizeScale.js +7 -2
  319. package/src/theme/helpers/textScale.js +1 -1
  320. package/dist/components/form/inputs/Checkbox.js +0 -1
  321. package/dist/components/form/inputs/Radio.js +0 -1
  322. package/dist/components/form/inputs/Switch.js +0 -1
  323. package/dist/components/form/inputs/index.js +0 -1
  324. package/dist/components/form/useForm.js +0 -1
  325. package/dist/helpers/responsive.js +0 -1
  326. package/src/components/form/inputs/Checkbox.js +0 -42
  327. package/src/components/form/inputs/Radio.js +0 -42
  328. package/src/components/form/inputs/Switch.js +0 -44
  329. package/src/components/form/inputs/index.js +0 -3
  330. package/src/components/form/useForm.js +0 -65
  331. package/src/helpers/responsive.js +0 -54
  332. /package/dist/abstractions/{windowWidth.js → helpers/windowWidth.js} +0 -0
  333. /package/dist/abstractions/{windowWidth.native.js → helpers/windowWidth.native.js} +0 -0
  334. /package/dist/abstractions/{windowWidth.web.js → helpers/windowWidth.web.js} +0 -0
  335. /package/src/abstractions/{windowWidth.js → helpers/windowWidth.js} +0 -0
  336. /package/src/abstractions/{windowWidth.native.js → helpers/windowWidth.native.js} +0 -0
  337. /package/src/abstractions/{windowWidth.web.js → helpers/windowWidth.web.js} +0 -0
@@ -0,0 +1,37 @@
1
+ import React from 'react'
2
+
3
+ import { AnimatedView } from '../../animations/AnimatedView'
4
+ import { Icon } from '../../presentation/Icon'
5
+ import { Link } from '../../actions/Link'
6
+ import { ResultBar } from '../../presentation/ResultBar'
7
+
8
+ export function Notification({ time, ...props }) {
9
+ const [open, setOpen] = React.useState(true)
10
+
11
+ React.useEffect(() => {
12
+ setTimeout(() => setOpen(false), time - 500)
13
+ }, [])
14
+
15
+ return (
16
+ <AnimatedView
17
+ open={open}
18
+ className="neko-notification"
19
+ slide={{ from: 'right' }}
20
+ fade
21
+ zIndex={120}
22
+ maxWidth="100%"
23
+ bg="overlayBG"
24
+ br="sm"
25
+ unmountOnClose
26
+ >
27
+ <ResultBar
28
+ {...props}
29
+ rightContent={
30
+ <Link onPress={() => setOpen(false)}>
31
+ <Icon name="close-line" text4 />
32
+ </Link>
33
+ }
34
+ />
35
+ </AnimatedView>
36
+ )
37
+ }
@@ -0,0 +1,65 @@
1
+ import { is } from 'ramda'
2
+ import React from 'react'
3
+
4
+ import { Notification } from './Notification'
5
+ import { SafeAreaView } from '../../structure/SafeAreaView'
6
+ import { View } from '../../structure/View'
7
+ import { useResponsiveValue } from '../../../responsive/responsiveHooks'
8
+
9
+ const NotificationsContext = React.createContext(null)
10
+
11
+ export const useNotifications = () => React.useContext(NotificationsContext) || {}
12
+
13
+ let idCounter = 0
14
+
15
+ export function useNotifier() {
16
+ const { add, remove } = useNotifications()
17
+ const notify = (data, opts, type) => {
18
+ const key = ++idCounter
19
+ const time = opts?.time || 6000
20
+
21
+ if (is(String, data)) data = { title: data }
22
+
23
+ const timerId = setTimeout(() => remove(key), time)
24
+ add(key, { ...data, type, opts, timerId, time })
25
+ }
26
+
27
+ return {
28
+ notify,
29
+ info: (data, opts) => notify(data, opts, 'info'),
30
+ error: (data, opts) => notify(data, opts, 'error'),
31
+ warning: (data, opts) => notify(data, opts, 'warning'),
32
+ success: (data, opts) => notify(data, opts, 'success'),
33
+ }
34
+ }
35
+
36
+ export function NotificationsHandler({ children }) {
37
+ const width = useResponsiveValue({ sm: '100%', df: 400 })
38
+ const [messages, setMessages] = React.useState([])
39
+
40
+ const add = React.useCallback((key, data) => {
41
+ setMessages((prev) => [{ key, ...data }, ...prev])
42
+ }, [])
43
+
44
+ const remove = React.useCallback((key) => {
45
+ setMessages((prev) => prev.filter((p) => p.key !== key))
46
+ }, [])
47
+
48
+ const value = React.useMemo(() => ({ add, remove }), [add, remove])
49
+
50
+ return (
51
+ <NotificationsContext.Provider value={value}>
52
+ {children}
53
+
54
+ {!!messages.length && (
55
+ <View fixed top={0} right={0} padding="md" width={width} maxWidth="100%" pointerEvents="box-none">
56
+ <SafeAreaView gap="xs">
57
+ {messages.map(({ key, ...item }) => (
58
+ <Notification key={key} {...item} />
59
+ ))}
60
+ </SafeAreaView>
61
+ </View>
62
+ )}
63
+ </NotificationsContext.Provider>
64
+ )
65
+ }
@@ -1,14 +1,25 @@
1
1
  import React from 'react'
2
2
 
3
3
  import { FormWrapperComponent } from './FormWrapperComponent'
4
+ import { LoadingView } from '../state/LoadingView'
5
+ import { useNewForm } from './useNewForm'
4
6
 
5
7
  const FormContext = React.createContext(null)
6
- export const useFormInstance = () => React.useContext(FormContext)
8
+ export const useFormState = () => React.useContext(FormContext)
9
+ export const useFormInstance = () => useFormState()?.form
10
+ export const useForm = useFormInstance
11
+
12
+ export function Form({ form, onSubmit, initialValues, children, loading, disabled, ...props }) {
13
+ const defaultForm = useNewForm({ onSubmit, initialValues })
14
+ form = form || defaultForm
7
15
 
8
- export function Form({ form, children }) {
9
16
  return (
10
- <FormContext.Provider value={form}>
11
- <FormWrapperComponent form={form}>{children}</FormWrapperComponent>
17
+ <FormContext.Provider value={{ loading, disabled, form }}>
18
+ <LoadingView active={loading} noWrapper>
19
+ <FormWrapperComponent form={form} gap="md" {...props}>
20
+ {children}
21
+ </FormWrapperComponent>
22
+ </LoadingView>
12
23
  </FormContext.Provider>
13
24
  )
14
25
  }
@@ -4,17 +4,17 @@ const FormGroupContext = React.createContext(null)
4
4
  const useGroupPath = () => React.useContext(FormGroupContext)?.path || []
5
5
 
6
6
  export function useRelativePath(name, opts) {
7
- const { relative } = opts
7
+ const { isAbsolutePath } = opts
8
8
  const listPath = !!name ? (Array.isArray(name) ? name : [name]) : []
9
9
  const parentPath = useGroupPath()
10
10
 
11
- if (!relative) return listPath
11
+ if (!!isAbsolutePath) return listPath
12
12
 
13
13
  return [...parentPath, ...listPath]
14
14
  }
15
15
 
16
- export function FormGroup({ name }) {
17
- const path = useRelativePath(name, { relative: true })
16
+ export function FormGroup({ name, children }) {
17
+ const path = useRelativePath(name, { isAbsolutePath: false })
18
18
  const value = { path }
19
19
 
20
20
  return <FormGroupContext.Provider value={value}>{children}</FormGroupContext.Provider>
@@ -2,11 +2,14 @@ import React from 'react'
2
2
 
3
3
  import { FormGroup, useRelativePath } from './FormGroup'
4
4
  import { Text } from '../text/Text'
5
- import { useFormInstance } from './Form'
5
+ import { View } from '../structure/View'
6
+ import { clearProps } from '../../modifiers/_helpers'
7
+ import { useFormInstance, useFormState } from './Form'
6
8
 
7
- export function FormItem({ name, relative, children }) {
9
+ export function FormItem({ name, label, isAbsolutePath, children, useDefaultValue, ...props }) {
8
10
  const form = useFormInstance()
9
- const listPath = useRelativePath(name, { relative })
11
+ const formState = useFormState()
12
+ const listPath = useRelativePath(name, { isAbsolutePath })
10
13
  const [value, setValue] = React.useState(form.getFieldValue(listPath))
11
14
  const error = form.getError(listPath)
12
15
 
@@ -19,16 +22,35 @@ export function FormItem({ name, relative, children }) {
19
22
  form.setFieldValue(listPath, val)
20
23
  }
21
24
 
22
- const child = React.Children.only(children)
23
- const childWithProps = React.cloneElement(child, {
24
- value,
25
+ let valueKey = 'value'
26
+ if (!!useDefaultValue) valueKey = 'defaultValue'
27
+
28
+ const childProps = clearProps({
29
+ [valueKey]: value === undefined ? '' : value,
25
30
  onChange: handleChange,
31
+ // loading: formState?.loading === true || undefined,
32
+ disabled: formState?.disabled === true || undefined,
26
33
  })
27
34
 
35
+ let content
36
+ if (typeof children === 'function') {
37
+ content = children(childProps)
38
+ } else {
39
+ const child = React.Children.only(children)
40
+ content = React.cloneElement(child, { ...child.props, ...childProps })
41
+ }
42
+
28
43
  return (
29
44
  <FormGroup name={listPath}>
30
- {childWithProps}
31
- {error && <Text color="red">{error}</Text>}
45
+ <View {...props}>
46
+ {label && (
47
+ <Text sm marginB="xxs">
48
+ {label}
49
+ </Text>
50
+ )}
51
+ {content}
52
+ {error && <Text color="red">{error}</Text>}
53
+ </View>
32
54
  </FormGroup>
33
55
  )
34
56
  }
@@ -7,23 +7,43 @@ import { useFormInstance } from './Form'
7
7
  const FormListContext = React.createContext(null)
8
8
  const useFormList = () => React.useContext(FormListContext)
9
9
 
10
- export function FormList({ name, relative, children }) {
10
+ export function FormList({ name, isAbsolutePath, children }) {
11
11
  const form = useFormInstance()
12
- const listPath = useRelativePath(name, { relative })
12
+ const listPath = useRelativePath(name, { isAbsolutePath })
13
13
  // To avoid watch being recalled
14
14
  const listPathStr = listPath.join('$NEKOJOIN$')
15
15
  const error = form.getError(listPath)
16
16
 
17
+ // Counter to generate unique keys
18
+ const keyCounter = React.useRef(0)
19
+ // Map to track keys by value reference
20
+ const keysMap = React.useRef(new WeakMap())
21
+
22
+ const generateFields = (items) => {
23
+ if (!Array.isArray(items)) return []
24
+ return items.map((item, index) => {
25
+ let key
26
+ if (typeof item === 'object' && item !== null) {
27
+ key = keysMap.current.get(item)
28
+ if (!key) {
29
+ key = `field_${keyCounter.current++}`
30
+ keysMap.current.set(item, key)
31
+ }
32
+ } else {
33
+ key = `field_${keyCounter.current++}`
34
+ }
35
+ return { key, name: index }
36
+ })
37
+ }
38
+
17
39
  const [fields, setFields] = React.useState(() => {
18
40
  const initial = form.getFieldValue(listPath) || []
19
- return initial.map((_, index) => ({ key: index, name: index }))
41
+ return generateFields(initial)
20
42
  })
21
43
 
22
44
  React.useEffect(() => {
23
45
  return form.registerListener(listPath, (val) => {
24
- if (Array.isArray(val)) {
25
- setFields(val.map((_, index) => ({ key: index, name: index })))
26
- }
46
+ setFields(generateFields(val))
27
47
  })
28
48
  }, [listPathStr])
29
49
 
@@ -45,6 +65,15 @@ export function FormList({ name, relative, children }) {
45
65
  )
46
66
  }
47
67
 
68
+ const duplicate = (index) => {
69
+ const current = form.getFieldValue(listPath) || []
70
+ const value = current[index]
71
+ if (!value) return
72
+ // Deep clone the value to avoid reference issues
73
+ const clonedValue = typeof value === 'object' ? { ...value } : value
74
+ addOn(index + 1, clonedValue)
75
+ }
76
+
48
77
  const move = (fromIndex, toIndex) => {
49
78
  const current = form.getFieldValue(listPath) || []
50
79
  if (fromIndex < 0 || fromIndex >= current.length) return
@@ -71,16 +100,23 @@ export function FormList({ name, relative, children }) {
71
100
  replace,
72
101
  remove,
73
102
  move,
103
+ duplicate,
74
104
  }),
75
105
  [listPathStr]
76
106
  )
77
107
 
108
+ let content
109
+ if (typeof children === 'function') {
110
+ content = children(fields, actions)
111
+ } else {
112
+ const child = React.Children.only(children)
113
+ content = React.cloneElement(child, { ...child.props, fields, ...actions })
114
+ }
115
+
78
116
  return (
79
117
  <FormGroup name={listPath}>
80
118
  <FormListContext.Provider value={actions}>
81
- {typeof children === 'function'
82
- ? children(fields, actions)
83
- : React.cloneElement(React.Children.only(children), { fields, ...actions })}
119
+ {content}
84
120
  {error && <Text color="red">{error}</Text>}
85
121
  </FormListContext.Provider>
86
122
  </FormGroup>
@@ -1,8 +1,43 @@
1
- export function FormWrapperComponent({ children, form, ...props }) {
1
+ import { pipe } from 'ramda'
2
+
3
+ import { useBackgroundModifier } from '../../modifiers/background'
4
+ import { useBorderModifier } from '../../modifiers/border'
5
+ import { useDisplayModifier } from '../../modifiers/display'
6
+ import { useFlexModifier } from '../../modifiers/flex'
7
+ import { useFlexWrapperModifier } from '../../modifiers/flexWrapper'
8
+ import { useFormState } from './Form'
9
+ import { useMarginModifier } from '../../modifiers/margin'
10
+ import { usePaddingModifier } from '../../modifiers/padding'
11
+ import { usePositionModifier } from '../../modifiers/position'
12
+ import { useShadowModifier } from '../../modifiers/shadow'
13
+ import { useSizeModifier } from '../../modifiers/size'
14
+
15
+ export function FormWrapperComponent({ children, form, ...rootProps }) {
16
+ const formState = useFormState()
17
+ const [_, props] = pipe(
18
+ useDisplayModifier, //
19
+ // useStateModifier,
20
+ useSizeModifier, //
21
+ usePositionModifier,
22
+ usePaddingModifier,
23
+ useMarginModifier,
24
+ useFlexWrapperModifier,
25
+ useFlexModifier,
26
+ useBackgroundModifier,
27
+ useBorderModifier,
28
+ useShadowModifier
29
+ )([{}, rootProps])
30
+
2
31
  const handleSubmit = (e) => {
3
32
  e.preventDefault()
33
+ if (formState?.loading || formState?.disabled) return
4
34
  form.handleSubmit()
5
35
  }
6
36
 
7
- return <form onSubmit={handleSubmit}>{children}</form>
37
+ return (
38
+ <form onSubmit={handleSubmit} {...props}>
39
+ {children}
40
+ <input type="submit" style={{ display: 'none' }} />
41
+ </form>
42
+ )
8
43
  }
@@ -1,5 +1,5 @@
1
- import React from 'react'
1
+ import { View } from '../structure/View'
2
2
 
3
3
  export function FormWrapperComponent({ children, ...props }) {
4
- return children
4
+ return <View {...props}>{children}</View>
5
5
  }
@@ -0,0 +1,20 @@
1
+ import { Button } from '../actions/Button'
2
+ import { useFormInstance, useFormState } from './Form'
3
+
4
+ export function SubmitButton({ form, disabled, ...props }) {
5
+ const formState = useFormState()
6
+ const contextForm = useFormInstance()
7
+ form = form || contextForm
8
+ disabled = formState?.disabled || disabled
9
+
10
+ const handleSubmit = () => {
11
+ if (!form) {
12
+ console.error('No form provided to useWatch. Pass it as params or wrap it inside a <Form> component.')
13
+ return
14
+ }
15
+
16
+ form.handleSubmit()
17
+ }
18
+
19
+ return <Button {...props} disabled={disabled} onPress={handleSubmit} />
20
+ }
@@ -3,5 +3,6 @@ export * from './FormItem'
3
3
  export * from './FormList'
4
4
  export * from './FormWrapperComponent'
5
5
  export * from './FormGroup'
6
- export * from './useForm'
7
- export * from './inputs'
6
+ export * from './useNewForm'
7
+ export * from './useWatch'
8
+ export * from './SubmitButton'
@@ -0,0 +1,67 @@
1
+ import { assocPath, path } from 'ramda'
2
+ import React from 'react'
3
+
4
+ export function useNewForm({ initialValues = {}, validate, onSubmit } = {}) {
5
+ const valuesRef = React.useRef({ ...initialValues })
6
+ const errorsRef = React.useRef({})
7
+ const listenersRef = React.useRef({})
8
+
9
+ const formApi = React.useMemo(() => {
10
+ const notify = (name) => {
11
+ const key = Array.isArray(name) ? name.join('.') : name
12
+ if (listenersRef.current[key]) {
13
+ listenersRef.current[key].forEach((cb) => cb(path(name, valuesRef.current)))
14
+ }
15
+ }
16
+
17
+ const setFieldValue = (name, value) => {
18
+ valuesRef.current = assocPath(name, value, valuesRef.current)
19
+ notify(name)
20
+ }
21
+
22
+ const getFieldValue = (name) => path(name, valuesRef.current)
23
+
24
+ const getError = (name) => path(name, errorsRef.current)
25
+
26
+ const setError = (name, error) => {
27
+ errorsRef.current = assocPath(name, error, errorsRef.current)
28
+ }
29
+
30
+ const registerListener = (name, cb) => {
31
+ const key = Array.isArray(name) ? name.join('.') : name
32
+ if (!listenersRef.current[key]) {
33
+ listenersRef.current[key] = []
34
+ }
35
+ listenersRef.current[key].push(cb)
36
+ return () => {
37
+ listenersRef.current[key] = listenersRef.current[key].filter((fn) => fn !== cb)
38
+ }
39
+ }
40
+
41
+ const validateForm = () => {
42
+ if (!validate) return true
43
+ const newErrors = validate(valuesRef.current) || {}
44
+ errorsRef.current = newErrors
45
+ return Object.keys(newErrors).length === 0
46
+ }
47
+
48
+ const handleSubmit = () => {
49
+ const isValid = validateForm()
50
+ if (!isValid) return
51
+ console.log('SUBMIT')
52
+ onSubmit(valuesRef.current)
53
+ }
54
+
55
+ return {
56
+ setFieldValue,
57
+ getFieldValue,
58
+ getError,
59
+ setError,
60
+ registerListener,
61
+ handleSubmit,
62
+ valuesRef,
63
+ }
64
+ }, [validate, onSubmit])
65
+
66
+ return formApi
67
+ }
@@ -0,0 +1,70 @@
1
+ import React from 'react'
2
+ import { useFormInstance } from './Form'
3
+
4
+ export function useWatch(name, { form } = {}) {
5
+ const contextForm = useFormInstance()
6
+ form = form || contextForm
7
+ const [value, setValue] = React.useState(() => form?.getFieldValue(name))
8
+
9
+ React.useEffect(() => {
10
+ if (!form) {
11
+ console.error('No form provided to useWatch. Pass it as params or wrap it inside a <Form> component.')
12
+ return
13
+ }
14
+
15
+ setValue(form.getFieldValue(name))
16
+
17
+ const unsubscribe = form.registerListener(name, (newValue) => {
18
+ setValue(newValue)
19
+ })
20
+
21
+ return unsubscribe
22
+ }, [form, name])
23
+
24
+ return value
25
+ }
26
+
27
+ export function useWatchAll(form) {
28
+ const contextForm = useFormInstance()
29
+ form = form || contextForm
30
+ const [values, setValues] = React.useState(() => form?.valuesRef.current || {})
31
+ const watchedFieldsRef = React.useRef(new Set())
32
+
33
+ React.useEffect(() => {
34
+ if (!form) return
35
+
36
+ setValues({ ...form.valuesRef.current })
37
+
38
+ const checkForNewFields = () => {
39
+ const currentFields = Object.keys(form.valuesRef.current)
40
+ const unsubscribers = []
41
+
42
+ currentFields.forEach((field) => {
43
+ if (!watchedFieldsRef.current.has(field)) {
44
+ watchedFieldsRef.current.add(field)
45
+ const unsub = form.registerListener(field, () => {
46
+ setValues({ ...form.valuesRef.current })
47
+ })
48
+ unsubscribers.push(unsub)
49
+ }
50
+ })
51
+
52
+ return unsubscribers
53
+ }
54
+
55
+ const unsubscribers = checkForNewFields()
56
+
57
+ const interval = setInterval(() => {
58
+ const newUnsubs = checkForNewFields()
59
+ unsubscribers.push(...newUnsubs)
60
+ }, 100)
61
+
62
+ return () => {
63
+ clearInterval(interval)
64
+ unsubscribers.forEach((unsub) => unsub())
65
+ watchedFieldsRef.current.clear()
66
+ }
67
+ }, [form])
68
+
69
+ return values
70
+ }
@@ -0,0 +1,55 @@
1
+ import React from 'react'
2
+
3
+ import { View } from '../structure/View'
4
+
5
+ export function LazyRender({
6
+ children,
7
+ delay = 0,
8
+ whenVisible = false,
9
+ destroyOffScreen = false,
10
+ minHeight: initMinHeight,
11
+ ...props
12
+ }) {
13
+ const ref = React.useRef(null)
14
+ const [open, setOpen] = React.useState(!whenVisible && !delay)
15
+ const [minHeight, setMinHeight] = React.useState(initMinHeight)
16
+
17
+ React.useEffect(() => {
18
+ if (!whenVisible) return
19
+
20
+ const observer = new IntersectionObserver(
21
+ ([entry]) => {
22
+ setOpen((open) => {
23
+ if (entry.isIntersecting) return true
24
+ return destroyOffScreen ? false : open
25
+ })
26
+ },
27
+ { threshold: 0 }
28
+ )
29
+
30
+ if (ref.current) observer.observe(ref.current)
31
+
32
+ return () => {
33
+ if (ref.current) observer.unobserve(ref.current)
34
+ }
35
+ }, [whenVisible, destroyOffScreen])
36
+
37
+ React.useEffect(() => {
38
+ if (delay > 0 && !whenVisible) {
39
+ const timer = setTimeout(() => setOpen(true), delay)
40
+ return () => clearTimeout(timer)
41
+ }
42
+ }, [])
43
+
44
+ React.useEffect(() => {
45
+ if (ref.current && open) {
46
+ setMinHeight(ref.current.offsetHeight)
47
+ }
48
+ }, [open])
49
+
50
+ return (
51
+ <View className="neko-lazy-render" {...props} minHeight={minHeight} ref={ref}>
52
+ {open ? children : null}
53
+ </View>
54
+ )
55
+ }
@@ -0,0 +1,58 @@
1
+ import { Dimensions } from 'react-native'
2
+ import React from 'react'
3
+
4
+ import { View } from '../structure/View'
5
+
6
+ export function LazyRender({
7
+ children,
8
+ delay = 0,
9
+ whenVisible = false,
10
+ destroyOffScreen = false,
11
+ minHeight: initMinHeight,
12
+ ...props
13
+ }) {
14
+ const ref = React.useRef(null)
15
+ const [open, setOpen] = React.useState(!whenVisible && !delay)
16
+ const [minHeight, setMinHeight] = React.useState(initMinHeight)
17
+ const windowHeight = Dimensions.get('window').height
18
+
19
+ const checkVisibility = React.useCallback(() => {
20
+ if (!ref.current) return
21
+ ref.current.measureInWindow((x, y, width, height) => {
22
+ const isVisible = y + height > 0 && y < windowHeight
23
+ setOpen((prev) => {
24
+ if (isVisible) return true
25
+ return destroyOffScreen ? false : prev
26
+ })
27
+ })
28
+ }, [windowHeight, destroyOffScreen])
29
+
30
+ React.useEffect(() => {
31
+ if (whenVisible) {
32
+ checkVisibility()
33
+ const interval = setInterval(checkVisibility, 100)
34
+ return () => clearInterval(interval)
35
+ }
36
+ }, [whenVisible, checkVisibility])
37
+
38
+ React.useEffect(() => {
39
+ if (delay > 0 && !whenVisible) {
40
+ const timer = setTimeout(() => setOpen(true), delay)
41
+ return () => clearTimeout(timer)
42
+ }
43
+ }, [])
44
+
45
+ React.useEffect(() => {
46
+ if (ref.current && open) {
47
+ ref.current.measure((x, y, width, height) => {
48
+ setMinHeight(height)
49
+ })
50
+ }
51
+ }, [open])
52
+
53
+ return (
54
+ <View className="neko-lazy-render" {...props} minHeight={minHeight} ref={ref}>
55
+ {open ? children : null}
56
+ </View>
57
+ )
58
+ }
@@ -0,0 +1,21 @@
1
+ import React from 'react'
2
+
3
+ import { usePortal } from './PortalHandler'
4
+
5
+ let idCounter = 0
6
+
7
+ export function Portal({ children }) {
8
+ const keyRef = React.useRef(++idCounter)
9
+ const { mount, update, unmount } = usePortal()
10
+
11
+ React.useEffect(() => {
12
+ mount(keyRef.current, children)
13
+ return () => unmount(keyRef.current)
14
+ }, [])
15
+
16
+ React.useEffect(() => {
17
+ update(keyRef.current, children)
18
+ }, [children])
19
+
20
+ return null
21
+ }