@imksh/create 1.0.30 → 1.0.32

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 (247) hide show
  1. package/README.md +2 -0
  2. package/package.json +1 -1
  3. package/src/commands/create.js +2 -0
  4. package/src/index.js +1 -1
  5. package/src/utils/askTemplate.js +2 -1
  6. package/templates/layers/electron/base/electron/main.js +262 -0
  7. package/templates/layers/electron/base/electron/preload.js +37 -0
  8. package/templates/layers/electron/base/electron-builder.json5 +68 -0
  9. package/templates/layers/electron/javascript/_gitignore +38 -0
  10. package/templates/layers/electron/javascript/package.json +49 -0
  11. package/templates/layers/electron/javascript/src/components/layout/SidebarLayout.jsx +43 -0
  12. package/templates/layers/electron/javascript/src/components/ui/TitleBar.jsx +130 -0
  13. package/templates/layers/electron/javascript/src/config/appConfig.js +51 -0
  14. package/templates/layers/electron/javascript/src/hooks/useElectron.js +68 -0
  15. package/templates/layers/electron/javascript/src/main.jsx +17 -0
  16. package/templates/layers/electron/javascript/src/store/useUiStore.js +59 -0
  17. package/templates/layers/electron/javascript/vite.config.js +29 -0
  18. package/templates/layers/electron/typescript/_gitignore +38 -0
  19. package/templates/layers/electron/typescript/electron/main.ts +213 -0
  20. package/templates/layers/electron/typescript/electron/preload.ts +52 -0
  21. package/templates/layers/electron/typescript/package.json +52 -0
  22. package/templates/layers/electron/typescript/src/components/layout/SidebarLayout.tsx +42 -0
  23. package/templates/layers/electron/typescript/src/components/ui/TitleBar.tsx +120 -0
  24. package/templates/layers/electron/typescript/src/config/appConfig.ts +52 -0
  25. package/templates/layers/electron/typescript/src/hooks/useElectron.ts +68 -0
  26. package/templates/layers/electron/typescript/src/main.tsx +17 -0
  27. package/templates/layers/electron/typescript/src/store/useUiStore.ts +97 -0
  28. package/templates/layers/electron/typescript/src/vite-env.d.ts +13 -0
  29. package/templates/layers/electron/typescript/tsconfig.json +25 -0
  30. package/templates/layers/electron/typescript/tsconfig.node.json +11 -0
  31. package/templates/layers/electron/typescript/vite.config.ts +29 -0
  32. package/templates/layers/next/javascript/package.json +8 -2
  33. package/templates/layers/next/javascript/src/components/common/Breadcrumbs.jsx +44 -6
  34. package/templates/layers/next/javascript/src/components/common/Loading.jsx +1 -0
  35. package/templates/layers/next/javascript/src/components/common/LottieIcon.jsx +1 -0
  36. package/templates/layers/next/javascript/src/components/common/Pagination.jsx +170 -0
  37. package/templates/layers/next/javascript/src/components/common/SidebarModal.jsx +180 -0
  38. package/templates/layers/next/javascript/src/components/common/StatsCard.jsx +168 -0
  39. package/templates/layers/next/javascript/src/components/common/Table.jsx +191 -41
  40. package/templates/layers/next/javascript/src/components/common/Tabs.jsx +187 -0
  41. package/templates/layers/next/javascript/src/components/common/ToggleSwitch.jsx +4 -3
  42. package/templates/layers/next/javascript/src/components/layout/ProtectedRoute.jsx +2 -1
  43. package/templates/layers/next/javascript/src/components/layout/PublicLayout.jsx +3 -6
  44. package/templates/layers/next/javascript/src/components/layout/SidebarLayout.jsx +4 -17
  45. package/templates/layers/next/javascript/src/components/modals/ConfirmModal.jsx +4 -12
  46. package/templates/layers/next/javascript/src/components/modals/Modal.jsx +50 -19
  47. package/templates/layers/next/javascript/src/components/ui/Input.jsx +50 -0
  48. package/templates/layers/next/{typescript/src/components/ui/sidebar.tsx → javascript/src/components/ui/Sidebar.jsx} +65 -53
  49. package/templates/layers/next/javascript/src/components/ui/SidebarHeader.jsx +11 -6
  50. package/templates/layers/next/javascript/src/config/appConfig.js +4 -4
  51. package/templates/layers/next/javascript/src/hooks/useWindowSize.js +2 -3
  52. package/templates/layers/next/javascript/src/store/useUiStore.js +9 -4
  53. package/templates/layers/next/typescript/.eslintrc.json +12 -0
  54. package/templates/layers/next/typescript/package.json +14 -3
  55. package/templates/layers/next/typescript/src/components/common/Breadcrumbs.tsx +57 -8
  56. package/templates/layers/next/typescript/src/components/common/Loading.tsx +2 -1
  57. package/templates/layers/next/typescript/src/components/common/LottieIcon.tsx +2 -2
  58. package/templates/layers/next/typescript/src/components/common/Pagination.tsx +186 -0
  59. package/templates/layers/next/typescript/src/components/common/SidebarModal.tsx +200 -0
  60. package/templates/layers/next/typescript/src/components/common/StatsCard.tsx +232 -0
  61. package/templates/layers/next/typescript/src/components/common/Table.tsx +205 -39
  62. package/templates/layers/next/typescript/src/components/common/Tabs.tsx +190 -0
  63. package/templates/layers/next/typescript/src/components/common/ToggleSwitch.tsx +12 -8
  64. package/templates/layers/next/typescript/src/components/layout/PublicLayout.tsx +8 -6
  65. package/templates/layers/next/typescript/src/components/layout/SidebarLayout.tsx +8 -17
  66. package/templates/layers/next/typescript/src/components/modals/ConfirmModal.tsx +3 -1
  67. package/templates/layers/next/typescript/src/components/modals/Modal.tsx +67 -21
  68. package/templates/layers/next/typescript/src/components/ui/Button.tsx +1 -0
  69. package/templates/layers/next/typescript/src/components/ui/Header.tsx +2 -2
  70. package/templates/layers/next/typescript/src/components/ui/Input.tsx +40 -60
  71. package/templates/layers/next/{javascript/src/components/ui/sidebar.jsx → typescript/src/components/ui/Sidebar.tsx} +77 -44
  72. package/templates/layers/next/typescript/src/components/ui/SidebarHeader.tsx +16 -10
  73. package/templates/layers/next/typescript/src/config/appConfig.ts +4 -4
  74. package/templates/layers/next/typescript/src/hooks/usePwaInstall.ts +10 -4
  75. package/templates/layers/next/typescript/src/hooks/useWindowSize.ts +2 -3
  76. package/templates/layers/next/typescript/src/services/authService.ts +1 -1
  77. package/templates/layers/next/typescript/src/store/useAuthStore.ts +24 -9
  78. package/templates/layers/next/typescript/src/store/useUiStore.ts +14 -4
  79. package/templates/layers/node/javascript/.env.example +3 -1
  80. package/templates/layers/node/javascript/package.json +12 -3
  81. package/templates/layers/node/javascript/src/app.js +2 -1
  82. package/templates/layers/node/javascript/src/controllers/auth.controller.js +106 -168
  83. package/templates/layers/node/javascript/src/controllers/push.controller.js +51 -58
  84. package/templates/layers/node/javascript/src/controllers/upload.controller.js +12 -19
  85. package/templates/layers/node/javascript/src/middleware/auth.middleware.js +5 -11
  86. package/templates/layers/node/javascript/src/middleware/errorHandler.js +6 -4
  87. package/templates/layers/node/javascript/src/routes/auth.route.js +2 -0
  88. package/templates/layers/node/javascript/src/services/auth.service.js +159 -164
  89. package/templates/layers/node/javascript/src/utils/ApiError.js +20 -0
  90. package/templates/layers/node/javascript/src/utils/SuccessResponse.js +15 -0
  91. package/templates/layers/node/javascript/src/utils/apiResponse.js +4 -0
  92. package/templates/layers/node/javascript/src/utils/asyncHandler.js +5 -0
  93. package/templates/layers/node/typescript/.env.example +3 -1
  94. package/templates/layers/node/typescript/.eslintrc.cjs +28 -0
  95. package/templates/layers/node/typescript/package-lock.json +2 -2
  96. package/templates/layers/node/typescript/package.json +17 -3
  97. package/templates/layers/node/typescript/src/app.ts +3 -2
  98. package/templates/layers/node/typescript/src/config/db.ts +3 -2
  99. package/templates/layers/node/typescript/src/config/socket.ts +5 -4
  100. package/templates/layers/node/typescript/src/controllers/auth.controller.ts +104 -132
  101. package/templates/layers/node/typescript/src/controllers/push.controller.ts +52 -59
  102. package/templates/layers/node/typescript/src/controllers/upload.controller.ts +12 -19
  103. package/templates/layers/node/typescript/src/middleware/auth.middleware.ts +13 -7
  104. package/templates/layers/node/typescript/src/middleware/errorHandler.ts +18 -5
  105. package/templates/layers/node/typescript/src/middleware/rateLimiter.ts +2 -2
  106. package/templates/layers/node/typescript/src/routes/auth.route.ts +2 -0
  107. package/templates/layers/node/typescript/src/services/auth.service.ts +86 -43
  108. package/templates/layers/node/typescript/src/types/express.d.ts +5 -3
  109. package/templates/layers/node/typescript/src/utils/ApiError.ts +25 -0
  110. package/templates/layers/node/typescript/src/utils/SuccessResponse.ts +17 -0
  111. package/templates/layers/node/typescript/src/utils/apiResponse.ts +4 -0
  112. package/templates/layers/node/typescript/src/utils/asyncHandler.ts +13 -0
  113. package/templates/layers/node/typescript/src/utils/cloudinaryUpload.ts +18 -7
  114. package/templates/layers/node/typescript/src/utils/expoNotification.ts +1 -1
  115. package/templates/layers/node/typescript/src/utils/genOtpToken.ts +7 -1
  116. package/templates/layers/node/typescript/src/utils/jwt.ts +8 -2
  117. package/templates/layers/node/typescript/src/utils/logger.ts +2 -1
  118. package/templates/layers/node/typescript/src/utils/pushHelper.ts +13 -9
  119. package/templates/layers/node-prisma/javascript/.env.example +3 -1
  120. package/templates/layers/node-prisma/javascript/package.json +11 -2
  121. package/templates/layers/node-prisma/javascript/src/app.js +2 -1
  122. package/templates/layers/node-prisma/javascript/src/controllers/auth.controller.js +90 -135
  123. package/templates/layers/node-prisma/javascript/src/controllers/push.controller.js +64 -76
  124. package/templates/layers/node-prisma/javascript/src/controllers/upload.controller.js +12 -19
  125. package/templates/layers/node-prisma/javascript/src/middleware/auth.middleware.js +5 -4
  126. package/templates/layers/node-prisma/javascript/src/middleware/errorHandler.js +6 -4
  127. package/templates/layers/node-prisma/javascript/src/routes/auth.route.js +2 -0
  128. package/templates/layers/node-prisma/javascript/src/services/auth.service.js +145 -122
  129. package/templates/layers/node-prisma/javascript/src/utils/ApiError.js +20 -0
  130. package/templates/layers/node-prisma/javascript/src/utils/SuccessResponse.js +15 -0
  131. package/templates/layers/node-prisma/javascript/src/utils/apiResponse.js +4 -0
  132. package/templates/layers/node-prisma/javascript/src/utils/asyncHandler.js +5 -0
  133. package/templates/layers/node-prisma/typescript/.env.example +3 -1
  134. package/templates/layers/node-prisma/typescript/.eslintrc.cjs +28 -0
  135. package/templates/layers/node-prisma/typescript/package.json +19 -5
  136. package/templates/layers/node-prisma/typescript/src/app.ts +3 -2
  137. package/templates/layers/node-prisma/typescript/src/config/prisma.ts +3 -2
  138. package/templates/layers/node-prisma/typescript/src/config/socket.ts +5 -4
  139. package/templates/layers/node-prisma/typescript/src/controllers/auth.controller.ts +89 -105
  140. package/templates/layers/node-prisma/typescript/src/controllers/push.controller.ts +67 -83
  141. package/templates/layers/node-prisma/typescript/src/controllers/upload.controller.ts +12 -19
  142. package/templates/layers/node-prisma/typescript/src/middleware/auth.middleware.ts +9 -13
  143. package/templates/layers/node-prisma/typescript/src/middleware/errorHandler.ts +18 -5
  144. package/templates/layers/node-prisma/typescript/src/middleware/rateLimiter.ts +2 -2
  145. package/templates/layers/node-prisma/typescript/src/routes/auth.route.ts +2 -0
  146. package/templates/layers/node-prisma/typescript/src/services/auth.service.ts +83 -31
  147. package/templates/layers/node-prisma/typescript/src/types/express.d.ts +5 -3
  148. package/templates/layers/node-prisma/typescript/src/utils/ApiError.ts +25 -0
  149. package/templates/layers/node-prisma/typescript/src/utils/SuccessResponse.ts +17 -0
  150. package/templates/layers/node-prisma/typescript/src/utils/apiResponse.ts +4 -0
  151. package/templates/layers/node-prisma/typescript/src/utils/asyncHandler.ts +13 -0
  152. package/templates/layers/node-prisma/typescript/src/utils/cloudinaryUpload.ts +18 -7
  153. package/templates/layers/node-prisma/typescript/src/utils/genOtpToken.ts +7 -1
  154. package/templates/layers/node-prisma/typescript/src/utils/jwt.ts +8 -2
  155. package/templates/layers/node-prisma/typescript/src/utils/logger.ts +2 -1
  156. package/templates/layers/node-prisma/typescript/src/utils/pushHelper.ts +10 -6
  157. package/templates/layers/prisma/javascript/package.json +8 -0
  158. package/templates/layers/prisma/typescript/package.json +14 -1
  159. package/templates/layers/prisma/typescript/prisma/schema.prisma +35 -10
  160. package/templates/layers/prisma/typescript/src/config/db.ts +3 -2
  161. package/templates/layers/react/javascript/package.json +6 -0
  162. package/templates/layers/react/javascript/src/components/common/Breadcrumbs.jsx +62 -58
  163. package/templates/layers/react/javascript/src/components/common/Pagination.jsx +164 -76
  164. package/templates/layers/react/javascript/src/components/common/SidebarModal.jsx +179 -0
  165. package/templates/layers/react/javascript/src/components/common/StatsCard.jsx +167 -0
  166. package/templates/layers/react/javascript/src/components/common/Table.jsx +248 -132
  167. package/templates/layers/react/javascript/src/components/common/Tabs.jsx +164 -112
  168. package/templates/layers/react/javascript/src/components/common/ToggleSwitch.jsx +3 -3
  169. package/templates/layers/react/javascript/src/components/layout/SidebarLayout.jsx +4 -12
  170. package/templates/layers/react/javascript/src/components/modals/Modal.jsx +53 -18
  171. package/templates/layers/react/javascript/src/components/ui/Input.jsx +49 -0
  172. package/templates/layers/react/javascript/src/components/ui/Sidebar.jsx +215 -0
  173. package/templates/layers/react/javascript/src/components/ui/SidebarHeader.jsx +25 -30
  174. package/templates/layers/react/javascript/src/config/appConfig.js +3 -3
  175. package/templates/layers/react/javascript/src/hooks/useWindowSize.js +2 -2
  176. package/templates/layers/react/typescript/.eslintrc.cjs +20 -0
  177. package/templates/layers/react/typescript/package.json +11 -2
  178. package/templates/layers/react/typescript/src/components/common/Breadcrumbs.tsx +32 -6
  179. package/templates/layers/react/typescript/src/components/common/Pagination.tsx +151 -57
  180. package/templates/layers/react/typescript/src/components/common/SidebarModal.tsx +199 -0
  181. package/templates/layers/react/typescript/src/components/common/StatsCard.tsx +231 -0
  182. package/templates/layers/react/typescript/src/components/common/Table.tsx +201 -38
  183. package/templates/layers/react/typescript/src/components/common/Tabs.tsx +56 -12
  184. package/templates/layers/react/typescript/src/components/common/ToggleSwitch.tsx +15 -3
  185. package/templates/layers/react/typescript/src/components/layout/SidebarLayout.tsx +3 -6
  186. package/templates/layers/react/typescript/src/components/modals/Modal.tsx +151 -0
  187. package/templates/layers/react/typescript/src/components/ui/Input.tsx +58 -0
  188. package/templates/layers/react/typescript/src/components/ui/Sidebar.tsx +116 -53
  189. package/templates/layers/react/typescript/src/components/ui/SidebarHeader.tsx +5 -4
  190. package/templates/layers/react/typescript/src/config/appConfig.ts +3 -3
  191. package/templates/layers/react/typescript/src/hooks/useWindowSize.ts +2 -3
  192. package/templates/layers/react/typescript/src/main.tsx +11 -11
  193. package/templates/layers/react/typescript/src/services/authService.ts +1 -1
  194. package/templates/layers/react/typescript/src/store/useAuthStore.ts +24 -9
  195. package/templates/layers/react-native/base/app.json +9 -22
  196. package/templates/layers/react-native/base/eslint.config.js +5 -1
  197. package/templates/layers/react-native/base/package.json +11 -1
  198. package/templates/layers/react-native/base/src/app/(auth)/edit-profile.tsx +7 -7
  199. package/templates/layers/react-native/base/src/app/(auth)/login.tsx +31 -7
  200. package/templates/layers/react-native/base/src/app/(tabs)/_layout.tsx +3 -3
  201. package/templates/layers/react-native/base/src/app/(tabs)/index.tsx +6 -5
  202. package/templates/layers/react-native/base/src/app/_layout.tsx +3 -3
  203. package/templates/layers/react-native/base/src/app/doc-viewer.tsx +3 -3
  204. package/templates/layers/react-native/base/src/components/common/ConfirmationToast.tsx +58 -40
  205. package/templates/layers/react-native/base/src/components/common/GoogleAuthButton.tsx +144 -0
  206. package/templates/layers/react-native/base/src/components/common/LocationLoader.tsx +64 -9
  207. package/templates/layers/react-native/base/src/components/common/Typography.tsx +54 -19
  208. package/templates/layers/react-native/base/src/components/ui/ClassicTabBar.tsx +15 -12
  209. package/templates/layers/react-native/base/src/components/ui/CustomTabBar.tsx +84 -90
  210. package/templates/layers/react-native/base/src/components/ui/Drawer.tsx +344 -0
  211. package/templates/layers/react-native/base/src/components/ui/Sidebar.tsx +4 -4
  212. package/templates/layers/react-native/base/src/components/ui/buttons/Button.tsx +29 -6
  213. package/templates/layers/react-native/base/src/components/ui/data-display/Accordion.tsx +2 -2
  214. package/templates/layers/react-native/base/src/components/ui/data-display/Badge.tsx +1 -2
  215. package/templates/layers/react-native/base/src/components/ui/data-display/Image.tsx +3 -3
  216. package/templates/layers/react-native/base/src/components/ui/data-display/SwipeableRow.tsx +28 -7
  217. package/templates/layers/react-native/base/src/components/ui/feedback/GestureWrapper.tsx +11 -4
  218. package/templates/layers/react-native/base/src/components/ui/inputs/DatePicker.tsx +3 -3
  219. package/templates/layers/react-native/base/src/components/ui/inputs/ImagePickerModal.tsx +45 -19
  220. package/templates/layers/react-native/base/src/components/ui/inputs/Input.tsx +28 -9
  221. package/templates/layers/react-native/base/src/components/ui/inputs/SearchablePicker.tsx +199 -0
  222. package/templates/layers/react-native/base/src/components/ui/inputs/Switch.tsx +7 -7
  223. package/templates/layers/react-native/base/src/components/ui/inputs/WebLocationPicker.tsx +146 -72
  224. package/templates/layers/react-native/base/src/config/api.ts +5 -5
  225. package/templates/layers/react-native/base/src/config/appConfig.ts +1 -1
  226. package/templates/layers/react-native/base/src/services/auth.service.ts +3 -1
  227. package/templates/layers/react-native/base/src/services/permission.service.ts +0 -2
  228. package/templates/layers/react-native/base/src/services/socket.service.ts +0 -7
  229. package/templates/layers/react-native/base/src/store/useAuthStore.ts +61 -16
  230. package/templates/layers/react-native/base/src/store/useNotificationStore.ts +36 -42
  231. package/templates/layers/react-native/base/src/types/images.d.ts +7 -5
  232. package/templates/layers/react-native/base/src/utils/fileDownload.ts +206 -0
  233. package/templates/layers/react-native/base/src/utils/storage.ts +1 -1
  234. package/templates/presets/electron/preset.json +9 -0
  235. package/templates/presets/electron-ts/preset.json +9 -0
  236. package/templates/presets/production-fullstack/overrides/client/package.json +9 -0
  237. package/templates/presets/production-fullstack/overrides/mobile/app.json +2 -15
  238. package/templates/presets/production-fullstack/overrides/mobile/package.json +11 -1
  239. package/templates/presets/production-fullstack/overrides/mobile/src/app/_layout.tsx +3 -3
  240. package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/Drawer.tsx +344 -0
  241. package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/inputs/DatePicker.tsx +3 -3
  242. package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/inputs/ImagePickerModal.tsx +3 -3
  243. package/templates/presets/production-fullstack/overrides/server/package.json +11 -2
  244. package/templates/layers/react/javascript/src/components/ui/sidebar.jsx +0 -169
  245. package/templates/layers/react-native/base/src/components/ui/BottomModal.tsx +0 -176
  246. package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/BottomModal.tsx +0 -159
  247. /package/templates/layers/react-native/base/assets/images/logo/{logoWithBg.png → icon.png} +0 -0
@@ -5,8 +5,17 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context';
5
5
  import { useColorScheme } from 'nativewind';
6
6
  import { Colors } from '../../constants/Colors';
7
7
  import { getTabConfig } from '../../config/tabNavigation';
8
+ import type { BottomTabBarProps } from "@react-navigation/bottom-tabs";
8
9
 
9
- const TabItem = ({ isFocused, onPress, onLongPress, iconName, colors }: any) => {
10
+ interface TabItemProps {
11
+ isFocused: boolean;
12
+ onPress: () => void;
13
+ onLongPress: () => void;
14
+ iconName: keyof typeof Ionicons.glyphMap;
15
+ colors: (typeof Colors)[keyof typeof Colors];
16
+ }
17
+
18
+ const TabItem = ({ isFocused, onPress, onLongPress, iconName, colors }: TabItemProps) => {
10
19
  return (
11
20
  <TouchableOpacity
12
21
  accessibilityRole="button"
@@ -18,7 +27,7 @@ const TabItem = ({ isFocused, onPress, onLongPress, iconName, colors }: any) =>
18
27
  >
19
28
  <View style={styles.tabItem}>
20
29
  <Ionicons
21
- name={iconName as any}
30
+ name={iconName}
22
31
  size={28}
23
32
  color={isFocused ? colors.primary : colors.baseContent + '60'}
24
33
  />
@@ -27,7 +36,7 @@ const TabItem = ({ isFocused, onPress, onLongPress, iconName, colors }: any) =>
27
36
  );
28
37
  };
29
38
 
30
- export default function ClassicTabBar({ state, descriptors, navigation }: any) {
39
+ export default function ClassicTabBar({ state, descriptors, navigation }: BottomTabBarProps) {
31
40
  const insets = useSafeAreaInsets();
32
41
  const { colorScheme } = useColorScheme();
33
42
  const colors = Colors[colorScheme ?? 'light'];
@@ -43,15 +52,9 @@ export default function ClassicTabBar({ state, descriptors, navigation }: any) {
43
52
  elevation:1
44
53
  }
45
54
  ]}>
46
- {state.routes.map((route: any, index: number) => {
55
+ {state.routes.map((route, index: number) => {
47
56
  const { options } = descriptors[route.key];
48
57
  const tabConfig = getTabConfig(route.name);
49
- const label = options.tabBarLabel !== undefined
50
- ? options.tabBarLabel
51
- : options.title !== undefined
52
- ? options.title
53
- : tabConfig?.title || route.name;
54
-
55
58
  const isFocused = state.index === index;
56
59
 
57
60
  const onPress = () => {
@@ -73,8 +76,8 @@ export default function ClassicTabBar({ state, descriptors, navigation }: any) {
73
76
  });
74
77
  };
75
78
 
76
- let iconName: any = tabConfig?.baseIcon || "home";
77
- iconName = isFocused ? iconName : `${iconName}-outline`;
79
+ const baseIcon = tabConfig?.baseIcon || "home";
80
+ const iconName = (isFocused ? baseIcon : `${baseIcon}-outline`) as keyof typeof Ionicons.glyphMap;
78
81
 
79
82
  return (
80
83
  <TabItem
@@ -6,15 +6,23 @@ import { useColorScheme } from "nativewind";
6
6
  import { Colors } from "../../constants/Colors";
7
7
  import { getTabConfig } from "../../config/tabNavigation";
8
8
  import { LinearGradient } from "expo-linear-gradient";
9
+ import type { BottomTabBarProps } from "@react-navigation/bottom-tabs";
10
+
11
+ interface TabItemProps {
12
+ isFocused: boolean;
13
+ onPress: () => void;
14
+ onLongPress: () => void;
15
+ iconName: keyof typeof Ionicons.glyphMap;
16
+ colors: (typeof Colors)[keyof typeof Colors];
17
+ }
9
18
 
10
19
  const TabItem = ({
11
20
  isFocused,
12
21
  onPress,
13
22
  onLongPress,
14
23
  iconName,
15
- label,
16
24
  colors,
17
- }: any) => {
25
+ }: TabItemProps) => {
18
26
  return (
19
27
  <TouchableOpacity
20
28
  accessibilityRole="button"
@@ -39,7 +47,7 @@ const TabItem = ({
39
47
  ]}
40
48
  >
41
49
  <Ionicons
42
- name={iconName as any}
50
+ name={iconName}
43
51
  size={isFocused ? 20 : 24}
44
52
  color={isFocused ? colors.primaryContent : colors.secondary}
45
53
  />
@@ -49,25 +57,10 @@ const TabItem = ({
49
57
  );
50
58
  };
51
59
 
52
- const CenterButton = ({ colors, onPress }: any) => {
53
- return (
54
- <View style={styles.centerButtonWrapper}>
55
- <TouchableOpacity
56
- activeOpacity={0.9}
57
- onPress={onPress}
58
- style={[
59
- styles.centerButton,
60
- {
61
- backgroundColor: colors.primary,
62
- shadowColor: colors.primary,
63
- },
64
- ]}
65
- >
66
- <Ionicons name="add" size={32} color={colors.primaryContent} />
67
- </TouchableOpacity>
68
- </View>
69
- );
70
- };
60
+ export interface CustomTabBarProps extends BottomTabBarProps {
61
+ showCenterButton?: boolean;
62
+ onCenterButtonPress?: () => void;
63
+ }
71
64
 
72
65
  export default function CustomTabBar({
73
66
  state,
@@ -75,7 +68,7 @@ export default function CustomTabBar({
75
68
  navigation,
76
69
  showCenterButton = true,
77
70
  onCenterButtonPress,
78
- }: any) {
71
+ }: CustomTabBarProps) {
79
72
  const insets = useSafeAreaInsets();
80
73
  const { colorScheme } = useColorScheme();
81
74
  const colors = Colors[colorScheme ?? "light"];
@@ -86,7 +79,8 @@ export default function CustomTabBar({
86
79
  return null;
87
80
  }
88
81
 
89
- const bottomOffset = insets.bottom > 0 ? insets.bottom : 20;
82
+ // Since we are using a global SafeAreaView, we don't need to manually add bottom safe area
83
+ const bottomOffset = insets.bottom > 0 ? 5 : 20; // Just a small spacing above the global safe area
90
84
  const totalTabBarHeight = bottomOffset + 70; // 70 is the height of the tab bar container
91
85
 
92
86
  return (
@@ -113,63 +107,80 @@ export default function CustomTabBar({
113
107
  },
114
108
  ]}
115
109
  >
116
- {state.routes.map((route: any, index: number) => {
117
- const { options } = descriptors[route.key];
118
- if (
119
- options.href === null ||
120
- options.tabBarItemStyle?.display === "none"
121
- )
122
- return null;
123
-
124
- const tabConfig = getTabConfig(route.name);
125
- const label =
126
- options.tabBarLabel !== undefined
127
- ? options.tabBarLabel
128
- : options.title !== undefined
129
- ? options.title
130
- : tabConfig?.title || route.name;
131
-
132
- const isFocused = state.index === index;
133
-
134
- const onPress = () => {
135
- const event = navigation.emit({
136
- type: "tabPress",
137
- target: route.key,
138
- canPreventDefault: true,
139
- });
140
-
141
- if (!isFocused && !event.defaultPrevented) {
142
- navigation.navigate(route.name, route.params);
110
+ {(() => {
111
+ const children: React.ReactNode[] = [];
112
+
113
+ state.routes.forEach((route, index: number) => {
114
+ const { options } = descriptors[route.key];
115
+ if (
116
+ options.href === null ||
117
+ options.tabBarItemStyle?.display === "none"
118
+ ) {
119
+ return;
120
+ }
121
+
122
+ const tabConfig = getTabConfig(route.name);
123
+ const label =
124
+ options.tabBarLabel !== undefined
125
+ ? options.tabBarLabel
126
+ : options.title !== undefined
127
+ ? options.title
128
+ : tabConfig?.title || route.name;
129
+
130
+ const isFocused = state.index === index;
131
+
132
+ const onPress = () => {
133
+ const event = navigation.emit({
134
+ type: "tabPress",
135
+ target: route.key,
136
+ canPreventDefault: true,
137
+ });
138
+
139
+ if (!isFocused && !event.defaultPrevented) {
140
+ navigation.navigate(route.name, route.params);
141
+ }
142
+ };
143
+
144
+ const onLongPress = () => {
145
+ navigation.emit({
146
+ type: "tabLongPress",
147
+ target: route.key,
148
+ });
149
+ };
150
+
151
+ const baseIcon = tabConfig?.baseIcon || "home";
152
+ const iconName = (isFocused ? baseIcon : `${baseIcon}-outline`) as keyof typeof Ionicons.glyphMap;
153
+
154
+ if (
155
+ showCenterButton &&
156
+ index === Math.floor(state.routes.length / 2)
157
+ ) {
158
+ children.push(
159
+ <TabItem
160
+ key="center-btn"
161
+ isFocused={false}
162
+ onPress={onCenterButtonPress}
163
+ iconName="add"
164
+ colors={colors}
165
+ />,
166
+ );
143
167
  }
144
- };
145
-
146
- const onLongPress = () => {
147
- navigation.emit({
148
- type: "tabLongPress",
149
- target: route.key,
150
- });
151
- };
152
-
153
- let iconName: any = tabConfig?.baseIcon || "home";
154
- iconName = isFocused ? iconName : `${iconName}-outline`;
155
-
156
- return (
157
- <React.Fragment key={route.key}>
158
- {showCenterButton &&
159
- index === Math.floor(state.routes.length / 2) && (
160
- <CenterButton colors={colors} onPress={onCenterButtonPress} />
161
- )}
168
+
169
+ children.push(
162
170
  <TabItem
171
+ key={route.key}
163
172
  isFocused={isFocused}
164
173
  onPress={onPress}
165
174
  onLongPress={onLongPress}
166
175
  iconName={iconName}
167
176
  label={label as string}
168
177
  colors={colors}
169
- />
170
- </React.Fragment>
171
- );
172
- })}
178
+ />,
179
+ );
180
+ });
181
+
182
+ return children;
183
+ })()}
173
184
  </View>
174
185
  </View>
175
186
  );
@@ -218,21 +229,4 @@ const styles = StyleSheet.create({
218
229
  position: "absolute",
219
230
  bottom: -6,
220
231
  },
221
- centerButtonWrapper: {
222
- flex: 1,
223
- alignItems: "center",
224
- justifyContent: "center",
225
- },
226
- centerButton: {
227
- width: 56,
228
- height: 56,
229
- borderRadius: 28,
230
- alignItems: "center",
231
- justifyContent: "center",
232
- marginTop: -40,
233
- shadowOffset: { width: 0, height: 8 },
234
- shadowOpacity: 0.4,
235
- shadowRadius: 15,
236
- elevation: 15,
237
- },
238
232
  });
@@ -0,0 +1,344 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import {
3
+ View,
4
+ TouchableWithoutFeedback,
5
+ Dimensions,
6
+ StyleSheet,
7
+ KeyboardAvoidingView,
8
+ Platform,
9
+ BackHandler,
10
+ ViewStyle,
11
+ } from "react-native";
12
+ import Animated, {
13
+ useSharedValue,
14
+ useAnimatedStyle,
15
+ withTiming,
16
+ Easing,
17
+ runOnJS,
18
+ } from "react-native-reanimated";
19
+ import { Gesture, GestureDetector } from "react-native-gesture-handler";
20
+ import { Portal } from "react-native-paper";
21
+ import { useColorScheme } from "nativewind";
22
+ import { Colors } from "../../constants/Colors";
23
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
24
+
25
+ const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get("window");
26
+
27
+ export type DrawerPosition = "top" | "bottom" | "left" | "right";
28
+
29
+ export interface DrawerProps {
30
+ isOpen: boolean;
31
+ onClose: () => void;
32
+ children: React.ReactNode;
33
+ position?: DrawerPosition;
34
+ sizePercent?: number; // e.g. 0.5 for 50% of the screen height (top/bottom) or width (left/right)
35
+ heightPercent?: number; // backwards compatibility alias for top/bottom
36
+ widthPercent?: number; // backwards compatibility alias for left/right
37
+ showHandle?: boolean;
38
+ zIndex?: number;
39
+ }
40
+
41
+ export default function Drawer({
42
+ isOpen,
43
+ onClose,
44
+ children,
45
+ position = "bottom",
46
+ sizePercent,
47
+ heightPercent,
48
+ widthPercent,
49
+ showHandle = true,
50
+ zIndex = 50,
51
+ }: DrawerProps) {
52
+ const { colorScheme } = useColorScheme();
53
+ const colors = Colors[colorScheme ?? "light"];
54
+ const insets = useSafeAreaInsets();
55
+
56
+ const isVertical = position === "top" || position === "bottom";
57
+ const defaultPercent = isVertical ? 0.5 : 0.75;
58
+ const resolvedPercent =
59
+ sizePercent ??
60
+ (isVertical ? heightPercent : widthPercent) ??
61
+ defaultPercent;
62
+
63
+ const DRAWER_SIZE = isVertical
64
+ ? SCREEN_HEIGHT * resolvedPercent
65
+ : SCREEN_WIDTH * resolvedPercent;
66
+
67
+ const getClosedOffset = () => {
68
+ switch (position) {
69
+ case "top":
70
+ case "left":
71
+ return -DRAWER_SIZE;
72
+ case "bottom":
73
+ case "right":
74
+ return DRAWER_SIZE;
75
+ }
76
+ };
77
+
78
+ const translate = useSharedValue(getClosedOffset());
79
+ const backdropOpacity = useSharedValue(0);
80
+ const [isRendered, setIsRendered] = useState(false);
81
+
82
+ if (isOpen && !isRendered) {
83
+ setIsRendered(true);
84
+ }
85
+
86
+ useEffect(() => {
87
+ const closedOffset = getClosedOffset();
88
+ if (isOpen) {
89
+ translate.value = withTiming(0, {
90
+ duration: 300,
91
+ easing: Easing.out(Easing.ease),
92
+ });
93
+ backdropOpacity.value = withTiming(0.4, { duration: 300 });
94
+ } else {
95
+ translate.value = withTiming(closedOffset, {
96
+ duration: 300,
97
+ easing: Easing.in(Easing.ease),
98
+ });
99
+ backdropOpacity.value = withTiming(0, { duration: 300 }, (finished) => {
100
+ if (finished) {
101
+ runOnJS(setIsRendered)(false);
102
+ }
103
+ });
104
+ }
105
+ }, [isOpen, DRAWER_SIZE, position]);
106
+
107
+ useEffect(() => {
108
+ if (!isOpen) return;
109
+
110
+ const backAction = () => {
111
+ onClose();
112
+ return true;
113
+ };
114
+
115
+ const backHandler = BackHandler.addEventListener(
116
+ "hardwareBackPress",
117
+ backAction
118
+ );
119
+
120
+ return () => backHandler.remove();
121
+ }, [isOpen, onClose]);
122
+
123
+ const panGesture = Gesture.Pan()
124
+ .onUpdate((event) => {
125
+ if (position === "bottom") {
126
+ if (event.translationY > 0) {
127
+ translate.value = event.translationY;
128
+ }
129
+ } else if (position === "top") {
130
+ if (event.translationY < 0) {
131
+ translate.value = event.translationY;
132
+ }
133
+ } else if (position === "left") {
134
+ if (event.translationX < 0) {
135
+ translate.value = event.translationX;
136
+ }
137
+ } else if (position === "right") {
138
+ if (event.translationX > 0) {
139
+ translate.value = event.translationX;
140
+ }
141
+ }
142
+ })
143
+ .onEnd((event) => {
144
+ let shouldClose = false;
145
+ if (position === "bottom") {
146
+ shouldClose =
147
+ event.translationY > DRAWER_SIZE * 0.3 || event.velocityY > 500;
148
+ } else if (position === "top") {
149
+ shouldClose =
150
+ event.translationY < -DRAWER_SIZE * 0.3 || event.velocityY < -500;
151
+ } else if (position === "left") {
152
+ shouldClose =
153
+ event.translationX < -DRAWER_SIZE * 0.3 || event.velocityX < -500;
154
+ } else if (position === "right") {
155
+ shouldClose =
156
+ event.translationX > DRAWER_SIZE * 0.3 || event.velocityX > 500;
157
+ }
158
+
159
+ if (shouldClose) {
160
+ runOnJS(onClose)();
161
+ } else {
162
+ translate.value = withTiming(0, {
163
+ duration: 300,
164
+ easing: Easing.out(Easing.ease),
165
+ });
166
+ }
167
+ });
168
+
169
+ const drawerAnimatedStyle = useAnimatedStyle(() => {
170
+ if (position === "left" || position === "right") {
171
+ return {
172
+ transform: [{ translateX: translate.value }],
173
+ };
174
+ }
175
+ return {
176
+ transform: [{ translateY: translate.value }],
177
+ };
178
+ });
179
+
180
+ const backdropAnimatedStyle = useAnimatedStyle(() => ({
181
+ opacity: backdropOpacity.value,
182
+ }));
183
+
184
+ const getContainerStyle = (): ViewStyle => {
185
+ const baseShadow: ViewStyle = {
186
+ backgroundColor: colors.base100,
187
+ elevation: 24,
188
+ shadowColor: "#000",
189
+ shadowOpacity: 0.15,
190
+ shadowRadius: 15,
191
+ };
192
+
193
+ switch (position) {
194
+ case "top":
195
+ return {
196
+ ...baseShadow,
197
+ position: "absolute",
198
+ top: 0,
199
+ left: 0,
200
+ right: 0,
201
+ height: DRAWER_SIZE,
202
+ borderBottomLeftRadius: 30,
203
+ borderBottomRightRadius: 30,
204
+ shadowOffset: { width: 0, height: 5 },
205
+ paddingTop: insets.top > 0 ? insets.top : 20,
206
+ };
207
+ case "bottom":
208
+ return {
209
+ ...baseShadow,
210
+ position: "absolute",
211
+ bottom: 0,
212
+ left: 0,
213
+ right: 0,
214
+ height: DRAWER_SIZE,
215
+ borderTopLeftRadius: 30,
216
+ borderTopRightRadius: 30,
217
+ shadowOffset: { width: 0, height: -5 },
218
+ paddingBottom: insets.bottom > 0 ? insets.bottom : 20,
219
+ };
220
+ case "left":
221
+ return {
222
+ ...baseShadow,
223
+ position: "absolute",
224
+ top: 0,
225
+ bottom: 0,
226
+ left: 0,
227
+ width: DRAWER_SIZE,
228
+ borderTopRightRadius: 30,
229
+ borderBottomRightRadius: 30,
230
+ shadowOffset: { width: 5, height: 0 },
231
+ paddingTop: insets.top > 0 ? insets.top : 20,
232
+ paddingBottom: insets.bottom > 0 ? insets.bottom : 20,
233
+ paddingLeft: insets.left > 0 ? insets.left : 0,
234
+ };
235
+ case "right":
236
+ return {
237
+ ...baseShadow,
238
+ position: "absolute",
239
+ top: 0,
240
+ bottom: 0,
241
+ right: 0,
242
+ width: DRAWER_SIZE,
243
+ borderTopLeftRadius: 30,
244
+ borderBottomLeftRadius: 30,
245
+ shadowOffset: { width: -5, height: 0 },
246
+ paddingTop: insets.top > 0 ? insets.top : 20,
247
+ paddingBottom: insets.bottom > 0 ? insets.bottom : 20,
248
+ paddingRight: insets.right > 0 ? insets.right : 0,
249
+ };
250
+ }
251
+ };
252
+
253
+ if (!isRendered && !isOpen) return null;
254
+
255
+ return (
256
+ <Portal>
257
+ <KeyboardAvoidingView
258
+ behavior={Platform.OS === "ios" ? "padding" : undefined}
259
+ style={StyleSheet.absoluteFill}
260
+ pointerEvents="box-none"
261
+ >
262
+ <View style={[StyleSheet.absoluteFill, { zIndex }]}>
263
+ {/* Backdrop Overlay */}
264
+ <TouchableWithoutFeedback onPress={onClose}>
265
+ <Animated.View
266
+ style={[
267
+ StyleSheet.absoluteFill,
268
+ { backgroundColor: "#000" },
269
+ backdropAnimatedStyle,
270
+ ]}
271
+ />
272
+ </TouchableWithoutFeedback>
273
+
274
+ {/* Sliding Drawer Panel */}
275
+ <Animated.View style={[getContainerStyle(), drawerAnimatedStyle]}>
276
+ {position === "bottom" && (
277
+ <View className="flex-1">
278
+ {showHandle && (
279
+ <GestureDetector gesture={panGesture}>
280
+ <View
281
+ className="items-center pt-4 pb-6 bg-base-100 rounded-t-[30px]"
282
+ style={{ zIndex: 10 }}
283
+ >
284
+ <View className="w-16 h-1.5 rounded-full bg-base-300" />
285
+ </View>
286
+ </GestureDetector>
287
+ )}
288
+ <View className="flex-1 px-6">{children}</View>
289
+ </View>
290
+ )}
291
+
292
+ {position === "top" && (
293
+ <View className="flex-1">
294
+ <View className="flex-1 px-6">{children}</View>
295
+ {showHandle && (
296
+ <GestureDetector gesture={panGesture}>
297
+ <View
298
+ className="items-center pt-4 pb-6 bg-base-100 rounded-b-[30px]"
299
+ style={{ zIndex: 10 }}
300
+ >
301
+ <View className="w-16 h-1.5 rounded-full bg-base-300" />
302
+ </View>
303
+ </GestureDetector>
304
+ )}
305
+ </View>
306
+ )}
307
+
308
+ {position === "left" && (
309
+ <View className="flex-1 flex-row">
310
+ <View className="flex-1 px-6">{children}</View>
311
+ {showHandle && (
312
+ <GestureDetector gesture={panGesture}>
313
+ <View
314
+ className="justify-center items-center px-3 bg-base-100 rounded-r-[30px]"
315
+ style={{ zIndex: 10 }}
316
+ >
317
+ <View className="w-1.5 h-16 rounded-full bg-base-300" />
318
+ </View>
319
+ </GestureDetector>
320
+ )}
321
+ </View>
322
+ )}
323
+
324
+ {position === "right" && (
325
+ <View className="flex-1 flex-row">
326
+ {showHandle && (
327
+ <GestureDetector gesture={panGesture}>
328
+ <View
329
+ className="justify-center items-center px-3 bg-base-100 rounded-l-[30px]"
330
+ style={{ zIndex: 10 }}
331
+ >
332
+ <View className="w-1.5 h-16 rounded-full bg-base-300" />
333
+ </View>
334
+ </GestureDetector>
335
+ )}
336
+ <View className="flex-1 px-6">{children}</View>
337
+ </View>
338
+ )}
339
+ </Animated.View>
340
+ </View>
341
+ </KeyboardAvoidingView>
342
+ </Portal>
343
+ );
344
+ }
@@ -21,7 +21,7 @@ import { Ionicons } from "@expo/vector-icons";
21
21
  import { useSafeAreaInsets } from "react-native-safe-area-context";
22
22
  import { useAuthStore } from "../../store/useAuthStore";
23
23
  import { sidebarConfig } from "../../config/sidebar.config";
24
- import { router } from "expo-router";
24
+ import { router, type Href } from "expo-router";
25
25
  import Image from "./data-display/Image";
26
26
 
27
27
  const { width } = Dimensions.get("window");
@@ -40,10 +40,10 @@ const MenuItem = ({
40
40
  onPress,
41
41
  colors,
42
42
  }: {
43
- icon: any;
43
+ icon: keyof typeof Ionicons.glyphMap;
44
44
  label: string;
45
45
  onPress?: () => void;
46
- colors: any;
46
+ colors: (typeof Colors)[keyof typeof Colors];
47
47
  }) => (
48
48
  <TouchableOpacity
49
49
  activeOpacity={0.7}
@@ -217,7 +217,7 @@ export default function Sidebar({
217
217
  label={item.label}
218
218
  onPress={() => {
219
219
  onClose();
220
- router.push(item.route as any);
220
+ router.push(item.route as Href);
221
221
  }}
222
222
  colors={colors}
223
223
  />