startx 0.0.1

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 (181) hide show
  1. package/.editorconfig +20 -0
  2. package/.prettierignore +24 -0
  3. package/.prettierrc.js +52 -0
  4. package/.vscode/settings.json +3 -0
  5. package/LICENSE +21 -0
  6. package/apps/core-server/.env.example +24 -0
  7. package/apps/core-server/Dockerfile +61 -0
  8. package/apps/core-server/eslint.config.mjs +47 -0
  9. package/apps/core-server/package.json +73 -0
  10. package/apps/core-server/src/config/custom-type.ts +54 -0
  11. package/apps/core-server/src/events/index.ts +37 -0
  12. package/apps/core-server/src/index.ts +19 -0
  13. package/apps/core-server/src/middlewares/auth-middleware.ts +50 -0
  14. package/apps/core-server/src/middlewares/cors-middleware.ts +6 -0
  15. package/apps/core-server/src/middlewares/error-middleware.ts +23 -0
  16. package/apps/core-server/src/middlewares/logger-middleware.ts +21 -0
  17. package/apps/core-server/src/middlewares/notfound-middleware.ts +14 -0
  18. package/apps/core-server/src/middlewares/serve-static.ts +24 -0
  19. package/apps/core-server/src/routes/files/router.ts +7 -0
  20. package/apps/core-server/src/routes/server.ts +36 -0
  21. package/apps/core-server/tsconfig.json +10 -0
  22. package/apps/core-server/tsdown.config.ts +14 -0
  23. package/biome.json +62 -0
  24. package/configs/eslint-config/package.json +60 -0
  25. package/configs/eslint-config/plugins.d.ts +1 -0
  26. package/configs/eslint-config/src/configs/base.ts +237 -0
  27. package/configs/eslint-config/src/configs/frontend.ts +62 -0
  28. package/configs/eslint-config/src/configs/node.ts +10 -0
  29. package/configs/eslint-config/src/plugin.ts +25 -0
  30. package/configs/eslint-config/src/rules/index.ts +30 -0
  31. package/configs/eslint-config/src/rules/no-argument-spread.test.ts +47 -0
  32. package/configs/eslint-config/src/rules/no-argument-spread.ts +96 -0
  33. package/configs/eslint-config/src/rules/no-dynamic-import-template.ts +32 -0
  34. package/configs/eslint-config/src/rules/no-internal-package-import.ts +40 -0
  35. package/configs/eslint-config/src/rules/no-interpolation-in-regular-string.ts +32 -0
  36. package/configs/eslint-config/src/rules/no-json-parse-json-stringify.test.ts +34 -0
  37. package/configs/eslint-config/src/rules/no-json-parse-json-stringify.ts +49 -0
  38. package/configs/eslint-config/src/rules/no-plain-errors.ts +50 -0
  39. package/configs/eslint-config/src/rules/no-skipped-tests.ts +61 -0
  40. package/configs/eslint-config/src/rules/no-top-level-relative-imports-in-backend-module.ts +27 -0
  41. package/configs/eslint-config/src/rules/no-type-unsafe-event-emitter.ts +33 -0
  42. package/configs/eslint-config/src/rules/no-uncaught-json-parse.test.ts +21 -0
  43. package/configs/eslint-config/src/rules/no-uncaught-json-parse.ts +45 -0
  44. package/configs/eslint-config/src/rules/no-untyped-config-class-field.ts +26 -0
  45. package/configs/eslint-config/src/rules/no-unused-param-catch-clause.ts +33 -0
  46. package/configs/eslint-config/src/rules/no-useless-catch-throw.test.ts +34 -0
  47. package/configs/eslint-config/src/rules/no-useless-catch-throw.ts +47 -0
  48. package/configs/eslint-config/src/utils/json.ts +21 -0
  49. package/configs/eslint-config/tsconfig.json +8 -0
  50. package/configs/eslint-config/tsdown.config.ts +11 -0
  51. package/configs/eslint-config/vitest.config.ts +3 -0
  52. package/configs/tsdown-config/package.json +14 -0
  53. package/configs/tsdown-config/src/config/tsdown.base.ts +13 -0
  54. package/configs/typescript-config/package.json +10 -0
  55. package/configs/typescript-config/tsconfig.common.json +32 -0
  56. package/configs/typescript-config/tsconfig.frontend.json +14 -0
  57. package/configs/typescript-config/tsconfig.node.json +9 -0
  58. package/configs/vitest-config/package.json +25 -0
  59. package/configs/vitest-config/src/base.ts +34 -0
  60. package/configs/vitest-config/src/frontend.ts +15 -0
  61. package/configs/vitest-config/src/node.ts +5 -0
  62. package/configs/vitest-config/tsconfig.json +7 -0
  63. package/package.json +47 -0
  64. package/packages/@repo/constants/eslint.config.mjs +21 -0
  65. package/packages/@repo/constants/package.json +19 -0
  66. package/packages/@repo/constants/src/api.ts +1 -0
  67. package/packages/@repo/constants/src/index.ts +8 -0
  68. package/packages/@repo/constants/src/time.ts +23 -0
  69. package/packages/@repo/constants/tsconfig.json +7 -0
  70. package/packages/@repo/db/eslint.config.mjs +21 -0
  71. package/packages/@repo/db/package.json +30 -0
  72. package/packages/@repo/db/src/functions.ts +122 -0
  73. package/packages/@repo/db/src/index.ts +20 -0
  74. package/packages/@repo/db/src/schema/common.ts +49 -0
  75. package/packages/@repo/db/src/schema/index.ts +1 -0
  76. package/packages/@repo/db/tsconfig.json +13 -0
  77. package/packages/@repo/lib/eslint.config.mjs +49 -0
  78. package/packages/@repo/lib/package.json +57 -0
  79. package/packages/@repo/lib/src/bucket-module/file-storage.ts +49 -0
  80. package/packages/@repo/lib/src/bucket-module/s3-storage.ts +114 -0
  81. package/packages/@repo/lib/src/bucket-module/utils.ts +11 -0
  82. package/packages/@repo/lib/src/command-module.ts +77 -0
  83. package/packages/@repo/lib/src/constants.ts +3 -0
  84. package/packages/@repo/lib/src/cookie-module.ts +42 -0
  85. package/packages/@repo/lib/src/custom-type.ts +54 -0
  86. package/packages/@repo/lib/src/env.ts +13 -0
  87. package/packages/@repo/lib/src/error-handlers-module/index.ts +11 -0
  88. package/packages/@repo/lib/src/file-system/index.ts +90 -0
  89. package/packages/@repo/lib/src/hashing-module.ts +9 -0
  90. package/packages/@repo/lib/src/index.ts +27 -0
  91. package/packages/@repo/lib/src/logger-module/log-config.ts +16 -0
  92. package/packages/@repo/lib/src/logger-module/logger.ts +78 -0
  93. package/packages/@repo/lib/src/logger-module/memory-profiler.ts +65 -0
  94. package/packages/@repo/lib/src/mail-module/api.ts +0 -0
  95. package/packages/@repo/lib/src/mail-module/mock.ts +8 -0
  96. package/packages/@repo/lib/src/mail-module/nodemailer.ts +45 -0
  97. package/packages/@repo/lib/src/notification-module/index.ts +172 -0
  98. package/packages/@repo/lib/src/notification-module/push-notification.ts +90 -0
  99. package/packages/@repo/lib/src/oauth2-client.ts +109 -0
  100. package/packages/@repo/lib/src/otp-module.ts +98 -0
  101. package/packages/@repo/lib/src/pagination-module.ts +49 -0
  102. package/packages/@repo/lib/src/token-module.ts +35 -0
  103. package/packages/@repo/lib/src/user-session.ts +117 -0
  104. package/packages/@repo/lib/src/utils.ts +42 -0
  105. package/packages/@repo/lib/src/validation-module.ts +187 -0
  106. package/packages/@repo/lib/tsconfig.json +7 -0
  107. package/packages/@repo/mail/package.json +29 -0
  108. package/packages/@repo/mail/src/emails/admin/OtpEmail.tsx +168 -0
  109. package/packages/@repo/mail/src/index.ts +13 -0
  110. package/packages/@repo/mail/tsconfig.build.json +14 -0
  111. package/packages/@repo/mail/tsconfig.json +13 -0
  112. package/packages/@repo/mail/tsdown.config.ts +9 -0
  113. package/packages/@repo/redis/eslint.config.mjs +8 -0
  114. package/packages/@repo/redis/package.json +31 -0
  115. package/packages/@repo/redis/src/index.ts +2 -0
  116. package/packages/@repo/redis/src/lib/redis-client.ts +23 -0
  117. package/packages/@repo/redis/src/lib/redis-module.ts +3 -0
  118. package/packages/@repo/redis/tsconfig.json +12 -0
  119. package/packages/ui/components.json +17 -0
  120. package/packages/ui/eslint.config.mjs +18 -0
  121. package/packages/ui/package.json +67 -0
  122. package/packages/ui/postcss.config.mjs +9 -0
  123. package/packages/ui/src/components/custom/form-wrapper.tsx +551 -0
  124. package/packages/ui/src/components/custom/grid-component.tsx +23 -0
  125. package/packages/ui/src/components/custom/hover-tool.tsx +38 -0
  126. package/packages/ui/src/components/custom/image-picker.tsx +109 -0
  127. package/packages/ui/src/components/custom/no-content.tsx +37 -0
  128. package/packages/ui/src/components/custom/page-container.tsx +24 -0
  129. package/packages/ui/src/components/custom/page-section.tsx +59 -0
  130. package/packages/ui/src/components/custom/simple-popover.tsx +29 -0
  131. package/packages/ui/src/components/custom/switch-component.tsx +20 -0
  132. package/packages/ui/src/components/custom/theme-provider.tsx +74 -0
  133. package/packages/ui/src/components/custom/typography.tsx +111 -0
  134. package/packages/ui/src/components/extensions/carousel.tsx +392 -0
  135. package/packages/ui/src/components/hooks/event/use-click.tsx +39 -0
  136. package/packages/ui/src/components/hooks/time/useDebounce.tsx +21 -0
  137. package/packages/ui/src/components/hooks/time/useInterval.tsx +35 -0
  138. package/packages/ui/src/components/hooks/time/useTimeout.tsx +19 -0
  139. package/packages/ui/src/components/hooks/time/useTimer.tsx +51 -0
  140. package/packages/ui/src/components/hooks/use-media-query.tsx +19 -0
  141. package/packages/ui/src/components/hooks/use-persistent-storage.tsx +52 -0
  142. package/packages/ui/src/components/hooks/use-update-effect.tsx +13 -0
  143. package/packages/ui/src/components/hooks/use-window-dimension.tsx +30 -0
  144. package/packages/ui/src/components/lib/utils.ts +242 -0
  145. package/packages/ui/src/components/lucide.tsx +3 -0
  146. package/packages/ui/src/components/sonner.tsx +1 -0
  147. package/packages/ui/src/components/ui/alert-dialog.tsx +116 -0
  148. package/packages/ui/src/components/ui/avatar.tsx +53 -0
  149. package/packages/ui/src/components/ui/badge.tsx +46 -0
  150. package/packages/ui/src/components/ui/breadcrumb.tsx +109 -0
  151. package/packages/ui/src/components/ui/button.tsx +96 -0
  152. package/packages/ui/src/components/ui/card.tsx +92 -0
  153. package/packages/ui/src/components/ui/carousel.tsx +243 -0
  154. package/packages/ui/src/components/ui/checkbox.tsx +32 -0
  155. package/packages/ui/src/components/ui/command.tsx +155 -0
  156. package/packages/ui/src/components/ui/dialog.tsx +127 -0
  157. package/packages/ui/src/components/ui/dropdown-menu.tsx +226 -0
  158. package/packages/ui/src/components/ui/form.tsx +165 -0
  159. package/packages/ui/src/components/ui/input-otp.tsx +76 -0
  160. package/packages/ui/src/components/ui/input.tsx +21 -0
  161. package/packages/ui/src/components/ui/label.tsx +24 -0
  162. package/packages/ui/src/components/ui/multiple-select.tsx +510 -0
  163. package/packages/ui/src/components/ui/popover.tsx +42 -0
  164. package/packages/ui/src/components/ui/select.tsx +170 -0
  165. package/packages/ui/src/components/ui/separator.tsx +28 -0
  166. package/packages/ui/src/components/ui/sheet.tsx +130 -0
  167. package/packages/ui/src/components/ui/skeleton.tsx +13 -0
  168. package/packages/ui/src/components/ui/spinner.tsx +16 -0
  169. package/packages/ui/src/components/ui/switch.tsx +28 -0
  170. package/packages/ui/src/components/ui/table.tsx +116 -0
  171. package/packages/ui/src/components/ui/tabs.tsx +54 -0
  172. package/packages/ui/src/components/ui/textarea.tsx +18 -0
  173. package/packages/ui/src/components/ui/timeline.tsx +118 -0
  174. package/packages/ui/src/components/ui/tooltip.tsx +30 -0
  175. package/packages/ui/src/components/util/n-formattor.ts +22 -0
  176. package/packages/ui/src/components/util/storage.ts +37 -0
  177. package/packages/ui/src/globals.css +87 -0
  178. package/packages/ui/tailwind.config.ts +94 -0
  179. package/packages/ui/tsconfig.json +12 -0
  180. package/pnpm-workspace.yaml +43 -0
  181. package/turbo.json +77 -0
@@ -0,0 +1,392 @@
1
+ 'use client';
2
+
3
+ import type { EmblaOptionsType } from 'embla-carousel';
4
+ import useEmblaCarousel from 'embla-carousel-react';
5
+ import { ChevronRightIcon, ChevronLeftIcon } from 'lucide-react';
6
+ import type React from 'react';
7
+ import { forwardRef, useCallback, useContext, useEffect, useState, createContext } from 'react';
8
+
9
+ import { Button } from '@/components/ui/button';
10
+
11
+ import { cn } from '../lib/utils';
12
+
13
+ type CarouselContextProps = {
14
+ carouselOptions?: EmblaOptionsType;
15
+ orientation?: 'vertical' | 'horizontal';
16
+ plugins?: Parameters<typeof useEmblaCarousel>[1];
17
+ };
18
+
19
+ type DirectionOption = 'ltr' | 'rtl' | undefined;
20
+
21
+ type CarouselContextType = {
22
+ emblaMainApi: ReturnType<typeof useEmblaCarousel>[1];
23
+ mainRef: ReturnType<typeof useEmblaCarousel>[0];
24
+ thumbsRef: ReturnType<typeof useEmblaCarousel>[0];
25
+ scrollNext: () => void;
26
+ scrollPrev: () => void;
27
+ canScrollNext: boolean;
28
+ canScrollPrev: boolean;
29
+ activeIndex: number;
30
+ onThumbClick: (index: number) => void;
31
+ handleKeyDown: (event: React.KeyboardEvent<HTMLDivElement>) => void;
32
+ orientation: 'vertical' | 'horizontal';
33
+ direction: DirectionOption;
34
+ } & CarouselContextProps;
35
+
36
+ const useCarousel = () => {
37
+ const context = useContext(CarouselContext);
38
+ if (!context) {
39
+ throw new Error('useCarousel must be used within a CarouselProvider');
40
+ }
41
+ return context;
42
+ };
43
+
44
+ const CarouselContext = createContext<CarouselContextType | null>(null);
45
+
46
+ /**
47
+ * Carousel Docs: {@link: https://shadcn-extension.vercel.app/docs/carousel}
48
+ */
49
+
50
+ const Carousel = forwardRef<
51
+ HTMLDivElement,
52
+ CarouselContextProps & React.HTMLAttributes<HTMLDivElement> & {dir: DirectionOption}
53
+ >(
54
+ (
55
+ { carouselOptions, orientation = 'horizontal', dir, plugins, children, className, ...props },
56
+ ref,
57
+ ) => {
58
+ const [emblaMainRef, emblaMainApi] = useEmblaCarousel(
59
+ {
60
+ ...carouselOptions,
61
+ axis: orientation === 'vertical' ? 'y' : 'x',
62
+ direction: carouselOptions?.direction ?? (dir as DirectionOption),
63
+ },
64
+ plugins,
65
+ );
66
+
67
+ const [emblaThumbsRef, emblaThumbsApi] = useEmblaCarousel(
68
+ {
69
+ ...carouselOptions,
70
+ axis: orientation === 'vertical' ? 'y' : 'x',
71
+ direction: carouselOptions?.direction ?? (dir as DirectionOption),
72
+ containScroll: 'keepSnaps',
73
+ dragFree: true,
74
+ },
75
+ plugins,
76
+ );
77
+
78
+ const [canScrollPrev, setCanScrollPrev] = useState<boolean>(false);
79
+ const [canScrollNext, setCanScrollNext] = useState<boolean>(false);
80
+ const [activeIndex, setActiveIndex] = useState<number>(0);
81
+
82
+ const ScrollNext = useCallback(() => {
83
+ if (!emblaMainApi) return;
84
+ emblaMainApi.scrollNext();
85
+ }, [emblaMainApi]);
86
+
87
+ const ScrollPrev = useCallback(() => {
88
+ if (!emblaMainApi) return;
89
+ emblaMainApi.scrollPrev();
90
+ }, [emblaMainApi]);
91
+
92
+ const direction = carouselOptions?.direction ?? (dir as DirectionOption);
93
+
94
+ const handleKeyDown = useCallback(
95
+ (event: React.KeyboardEvent<HTMLDivElement>) => {
96
+ if (!emblaMainApi) return;
97
+ switch (event.key) {
98
+ case 'ArrowLeft':
99
+ event.preventDefault();
100
+ if (orientation === 'horizontal') {
101
+ if (direction === 'rtl') {
102
+ ScrollNext();
103
+ return;
104
+ }
105
+ ScrollPrev();
106
+ }
107
+ break;
108
+ case 'ArrowRight':
109
+ event.preventDefault();
110
+ if (orientation === 'horizontal') {
111
+ if (direction === 'rtl') {
112
+ ScrollPrev();
113
+ return;
114
+ }
115
+ ScrollNext();
116
+ }
117
+ break;
118
+ case 'ArrowUp':
119
+ event.preventDefault();
120
+ if (orientation === 'vertical') {
121
+ ScrollPrev();
122
+ }
123
+ break;
124
+ case 'ArrowDown':
125
+ event.preventDefault();
126
+ if (orientation === 'vertical') {
127
+ ScrollNext();
128
+ }
129
+ break;
130
+ }
131
+ },
132
+ [emblaMainApi, orientation, direction],
133
+ );
134
+
135
+ const onThumbClick = useCallback(
136
+ (index: number) => {
137
+ if (!emblaMainApi || !emblaThumbsApi) return;
138
+ emblaMainApi.scrollTo(index);
139
+ },
140
+ [emblaMainApi, emblaThumbsApi],
141
+ );
142
+
143
+ const onSelect = useCallback(() => {
144
+ if (!emblaMainApi || !emblaThumbsApi) return;
145
+ const selected = emblaMainApi.selectedScrollSnap();
146
+ setActiveIndex(selected);
147
+ emblaThumbsApi.scrollTo(selected);
148
+ setCanScrollPrev(emblaMainApi.canScrollPrev());
149
+ setCanScrollNext(emblaMainApi.canScrollNext());
150
+ }, [emblaMainApi, emblaThumbsApi]);
151
+
152
+ useEffect(() => {
153
+ if (!emblaMainApi) return;
154
+ onSelect();
155
+ emblaMainApi.on('select', onSelect);
156
+ emblaMainApi.on('reInit', onSelect);
157
+
158
+ return () => {
159
+ emblaMainApi.off('select', onSelect);
160
+ };
161
+ }, [emblaMainApi, onSelect]);
162
+
163
+ return (
164
+ <CarouselContext.Provider
165
+ value={{
166
+ emblaMainApi,
167
+ mainRef: emblaMainRef,
168
+ thumbsRef: emblaThumbsRef,
169
+ scrollNext: ScrollNext,
170
+ scrollPrev: ScrollPrev,
171
+ canScrollNext,
172
+ canScrollPrev,
173
+ activeIndex,
174
+ onThumbClick,
175
+ handleKeyDown,
176
+ carouselOptions,
177
+ direction,
178
+ orientation: orientation || (carouselOptions?.axis === 'y' ? 'vertical' : 'horizontal'),
179
+ }}
180
+ >
181
+ <div
182
+ {...props}
183
+ // tabIndex={0}
184
+ ref={ref}
185
+ onKeyDownCapture={handleKeyDown}
186
+ className={cn('grid gap-2 w-full relative focus:outline-none', className)}
187
+ dir={direction}
188
+ >
189
+ {children}
190
+ </div>
191
+ </CarouselContext.Provider>
192
+ );
193
+ },
194
+ );
195
+
196
+ Carousel.displayName = 'Carousel';
197
+
198
+ const CarouselMainContainer = forwardRef<HTMLDivElement, {
199
+ } & React.HTMLAttributes<HTMLDivElement>>(
200
+ ({ className, children, ...props }, ref) => {
201
+ const { mainRef, orientation, direction } = useCarousel();
202
+
203
+ return (
204
+ <div {...props} ref={mainRef} className="overflow-hidden" dir={direction}>
205
+ <div
206
+ ref={ref}
207
+ className={cn('flex', `${orientation === 'vertical' ? 'flex-col' : ''}`, className)}
208
+ >
209
+ {children}
210
+ </div>
211
+ </div>
212
+ );
213
+ },
214
+ );
215
+
216
+ CarouselMainContainer.displayName = 'CarouselMainContainer';
217
+
218
+ const CarouselThumbsContainer = forwardRef<
219
+ HTMLDivElement,
220
+ {} & React.HTMLAttributes<HTMLDivElement>
221
+ >(({ className, children, ...props }, ref) => {
222
+ const { thumbsRef, orientation, direction } = useCarousel();
223
+
224
+ return (
225
+ <div {...props} ref={thumbsRef} className="overflow-hidden" dir={direction}>
226
+ <div
227
+ ref={ref}
228
+ className={cn('flex', `${orientation === 'vertical' ? 'flex-col' : ''}`, className)}
229
+ >
230
+ {children}
231
+ </div>
232
+ </div>
233
+ );
234
+ });
235
+
236
+ CarouselThumbsContainer.displayName = 'CarouselThumbsContainer';
237
+
238
+ const SliderMainItem = forwardRef<HTMLDivElement, {} & React.HTMLAttributes<HTMLDivElement>>(
239
+ ({ className, children, ...props }, ref) => {
240
+ const { orientation } = useCarousel();
241
+ return (
242
+ <div
243
+ {...props}
244
+ ref={ref}
245
+ className={cn(
246
+ `min-w-0 shrink-0 grow-0 basis-full bg-background p-1 ${
247
+ orientation === 'vertical' ? 'pb-1' : 'pr-1'
248
+ }`,
249
+ className,
250
+ )}
251
+ >
252
+ {children}
253
+ </div>
254
+ );
255
+ },
256
+ );
257
+
258
+ SliderMainItem.displayName = 'SliderMainItem';
259
+
260
+ const SliderThumbItem = forwardRef<
261
+ HTMLDivElement,
262
+ {
263
+ index: number;
264
+ } & React.HTMLAttributes<HTMLDivElement>
265
+ >(({ className, index, children, ...props }, ref) => {
266
+ const { activeIndex, onThumbClick, orientation } = useCarousel();
267
+ const isSlideActive = activeIndex === index;
268
+ return (
269
+ <div
270
+ {...props}
271
+ ref={ref}
272
+ onClick={() => onThumbClick(index)}
273
+ className={cn(
274
+ 'flex min-w-0 shrink-0 grow-0 basis-1/3 bg-background p-1',
275
+ `${orientation === 'vertical' ? 'pb-1' : 'pr-1'}`,
276
+ className,
277
+ )}
278
+ >
279
+ <div
280
+ className={`relative aspect-square h-20 w-full opacity-50 rounded-md transition-opacity ${
281
+ isSlideActive ? '!opacity-100' : ''
282
+ }`}
283
+ >
284
+ {children}
285
+ </div>
286
+ </div>
287
+ );
288
+ });
289
+
290
+ SliderThumbItem.displayName = 'SliderThumbItem';
291
+
292
+ const CarouselIndicator = forwardRef<
293
+ HTMLButtonElement,
294
+ { index: number } & React.ComponentProps<typeof Button>
295
+ >(({ className, index, children, ...props }, ref) => {
296
+ const { activeIndex, onThumbClick } = useCarousel();
297
+ const isSlideActive = activeIndex === index;
298
+ return (
299
+ <Button
300
+ ref={ref}
301
+ size="icon"
302
+ className={cn(
303
+ 'h-1 w-6 rounded-full',
304
+ "data-[active='false']:bg-primary/50 data-[active='true']:bg-primary",
305
+ className,
306
+ )}
307
+ data-active={isSlideActive}
308
+ onClick={() => onThumbClick(index)}
309
+ {...props}
310
+ >
311
+ <span className="sr-only">slide {index + 1} </span>
312
+ </Button>
313
+ );
314
+ });
315
+
316
+ CarouselIndicator.displayName = 'CarouselIndicator';
317
+
318
+ const CarouselPrevious = forwardRef<HTMLButtonElement, React.ComponentProps<typeof Button>>(
319
+ ({ className, dir, variant = 'outline', size = 'icon', ...props }, ref) => {
320
+ const { canScrollNext, canScrollPrev, scrollNext, scrollPrev, orientation, direction } =
321
+ useCarousel();
322
+
323
+ const scroll = direction === 'rtl' ? scrollNext : scrollPrev;
324
+ const canScroll = direction === 'rtl' ? canScrollNext : canScrollPrev;
325
+ return (
326
+ <Button
327
+ type="button"
328
+ ref={ref}
329
+ variant={variant}
330
+ size={size}
331
+ className={cn(
332
+ 'absolute h-6 w-6 rounded-full z-10',
333
+ orientation === 'vertical'
334
+ ? '-top-2 left-1/2 -translate-x-1/2 rotate-90'
335
+ : '-left-2 top-1/2 -translate-y-1/2',
336
+ className,
337
+ )}
338
+ onClick={scroll}
339
+ disabled={!canScroll}
340
+ {...props}
341
+ >
342
+ <ChevronLeftIcon className="h-4 w-4" />
343
+ <span className="sr-only">Previous slide</span>
344
+ </Button>
345
+ );
346
+ },
347
+ );
348
+ CarouselPrevious.displayName = 'CarouselPrevious';
349
+
350
+ const CarouselNext = forwardRef<HTMLButtonElement, React.ComponentProps<typeof Button>>(
351
+ ({ className, dir, variant = 'outline', size = 'icon', ...props }, ref) => {
352
+ const { canScrollNext, canScrollPrev, scrollNext, scrollPrev, orientation, direction } =
353
+ useCarousel();
354
+ const scroll = direction === 'rtl' ? scrollPrev : scrollNext;
355
+ const canScroll = direction === 'rtl' ? canScrollPrev : canScrollNext;
356
+ return (
357
+ <Button
358
+ type="button"
359
+ ref={ref}
360
+ variant={variant}
361
+ size={size}
362
+ className={cn(
363
+ 'absolute h-6 w-6 rounded-full z-10',
364
+ orientation === 'vertical'
365
+ ? '-bottom-2 left-1/2 -translate-x-1/2 rotate-90'
366
+ : '-right-2 top-1/2 -translate-y-1/2',
367
+ className,
368
+ )}
369
+ onClick={scroll}
370
+ disabled={!canScroll}
371
+ {...props}
372
+ >
373
+ <ChevronRightIcon className="h-4 w-4" />
374
+ <span className="sr-only">Next slide</span>
375
+ </Button>
376
+ );
377
+ },
378
+ );
379
+
380
+ CarouselNext.displayName = 'CarouselNext';
381
+
382
+ export {
383
+ Carousel,
384
+ CarouselMainContainer,
385
+ CarouselThumbsContainer,
386
+ SliderMainItem,
387
+ SliderThumbItem,
388
+ CarouselIndicator,
389
+ CarouselPrevious,
390
+ CarouselNext,
391
+ useCarousel,
392
+ };
@@ -0,0 +1,39 @@
1
+ /* eslint-disable id-denylist */
2
+ import { type RefObject, useEffect } from 'react';
3
+
4
+ function useOutsideClick<T extends HTMLElement>(
5
+ ref: RefObject<T | null>,
6
+ callback: () => void,
7
+ ): void {
8
+ useEffect(() => {
9
+ const handleClickOrTouch = (event: Event) => {
10
+ if (ref.current && !ref.current.contains(event.target as Node)) {
11
+ callback();
12
+ }
13
+ };
14
+
15
+ document.addEventListener('mousedown', handleClickOrTouch);
16
+
17
+ return () => {
18
+ document.removeEventListener('mousedown', handleClickOrTouch);
19
+ };
20
+ }, [ref, callback]);
21
+ }
22
+
23
+ function useInsideClick<T extends HTMLElement>(ref: RefObject<T>, callback: () => void): void {
24
+ useEffect(() => {
25
+ const handleClickOrTouch = (event: Event) => {
26
+ if (ref.current && !ref.current.contains(event.target as Node)) {
27
+ callback();
28
+ }
29
+ };
30
+
31
+ document.addEventListener('mousedown', handleClickOrTouch);
32
+
33
+ return () => {
34
+ document.removeEventListener('mousedown', handleClickOrTouch);
35
+ };
36
+ }, [ref, callback]);
37
+ }
38
+
39
+ export { useInsideClick, useOutsideClick };
@@ -0,0 +1,21 @@
1
+ import { useState, useEffect } from 'react';
2
+
3
+ function useDebounce<T>(value: T, delay: number): T {
4
+ const [debouncedValue, setDebouncedValue] = useState<T>(value);
5
+
6
+ useEffect(() => {
7
+ // Update debounced value after delay
8
+ const handler = setTimeout(() => {
9
+ setDebouncedValue(value);
10
+ }, delay);
11
+
12
+ // Clear timeout on cleanup to prevent memory leaks
13
+ return () => {
14
+ clearTimeout(handler);
15
+ };
16
+ }, [value, delay]);
17
+
18
+ return debouncedValue;
19
+ }
20
+
21
+ export { useDebounce };
@@ -0,0 +1,35 @@
1
+ /* eslint-disable id-denylist */
2
+ import { useRef, useCallback, useEffect } from 'react';
3
+
4
+ const useInterval = (callback: () => void, interval: number) => {
5
+ const intervalIdRef = useRef<number | null>(null);
6
+ const savedCallback = useRef<() => void>(null);
7
+
8
+ // Remember the latest callback.
9
+ useEffect(() => {
10
+ savedCallback.current = callback;
11
+ }, [callback]);
12
+
13
+ const start = useCallback(() => {
14
+ if (intervalIdRef.current !== null) return; // Prevent multiple intervals
15
+ intervalIdRef.current = window.setInterval(() => {
16
+ if (savedCallback.current) savedCallback.current();
17
+ }, interval);
18
+ }, [interval]);
19
+
20
+ const clear = useCallback(() => {
21
+ if (intervalIdRef.current !== null) {
22
+ clearInterval(intervalIdRef.current);
23
+ intervalIdRef.current = null;
24
+ }
25
+ }, []);
26
+
27
+ // Clear interval on component unmount
28
+ useEffect(() => {
29
+ return () => clear();
30
+ }, [clear]);
31
+
32
+ return { start, clear };
33
+ };
34
+
35
+ export { useInterval };
@@ -0,0 +1,19 @@
1
+ import * as React from 'react';
2
+
3
+ export function useMediaQuery(query: string) {
4
+ const [value, setValue] = React.useState(false);
5
+
6
+ React.useEffect(() => {
7
+ function onChange(event: MediaQueryListEvent) {
8
+ setValue(event.matches);
9
+ }
10
+
11
+ const result = matchMedia(query);
12
+ result.addEventListener('change', onChange);
13
+ setValue(result.matches);
14
+
15
+ return () => result.removeEventListener('change', onChange);
16
+ }, [query]);
17
+
18
+ return value;
19
+ }
@@ -0,0 +1,51 @@
1
+ import { useState, useRef, useEffect } from "react";
2
+
3
+ type TimerHook = {
4
+ counter: number;
5
+ start: () => void;
6
+ reset: () => void;
7
+ clear: () => void;
8
+ };
9
+
10
+ export function useTimer(initialSeconds: number): TimerHook {
11
+ const [counter, setCounter] = useState(initialSeconds);
12
+ const timerRef = useRef<NodeJS.Timeout | null>(null);
13
+
14
+ useEffect(() => {
15
+ // Cleanup on component unmount or when counter changes
16
+ return () => {
17
+ if (timerRef.current) {
18
+ clearTimeout(timerRef.current);
19
+ }
20
+ };
21
+ }, []);
22
+
23
+ const start = () => {
24
+ if (timerRef.current) {
25
+ clearTimeout(timerRef.current);
26
+ }
27
+
28
+ timerRef.current = setTimeout(() => {
29
+ setCounter((prev) => (prev > 0 ? prev - 1 : 0));
30
+ }, 1000);
31
+ };
32
+
33
+ const reset = () => {
34
+ setCounter(initialSeconds);
35
+ clear();
36
+ };
37
+
38
+ const clear = () => {
39
+ if (timerRef.current) {
40
+ clearTimeout(timerRef.current);
41
+ }
42
+ };
43
+
44
+ useEffect(() => {
45
+ if (counter > 0) {
46
+ start();
47
+ }
48
+ }, [counter]);
49
+
50
+ return { counter, start, reset, clear };
51
+ }
@@ -0,0 +1,19 @@
1
+ import * as React from "react";
2
+
3
+ export function useMediaQuery(query: string) {
4
+ const [value, setValue] = React.useState(false);
5
+
6
+ React.useEffect(() => {
7
+ function onChange(event: MediaQueryListEvent) {
8
+ setValue(event.matches);
9
+ }
10
+
11
+ const result = matchMedia(query);
12
+ result.addEventListener("change", onChange);
13
+ setValue(result.matches);
14
+
15
+ return () => result.removeEventListener("change", onChange);
16
+ }, [query]);
17
+
18
+ return value;
19
+ }
@@ -0,0 +1,52 @@
1
+ 'use client';
2
+ import { useState, useEffect, type Dispatch, type SetStateAction } from 'react';
3
+
4
+ export function useLocalStorage<T>(key: string, initialValue: T): [T, Dispatch<SetStateAction<T>>] {
5
+ // Retrieve stored value from localStorage, or use initialValue if none is found
6
+ const getStoredValue = (): T => {
7
+ // if (typeof window === "undefined") return initialValue;
8
+ const storedValue = localStorage.getItem(key);
9
+ if (storedValue !== null) {
10
+ try {
11
+ return JSON.parse(storedValue) as T;
12
+ } catch (error) {
13
+ console.error('Error parsing stored value:', error);
14
+ }
15
+ }
16
+ return initialValue;
17
+ };
18
+
19
+ const [value, setValue] = useState<T>(getStoredValue);
20
+
21
+ useEffect(() => {
22
+ localStorage.setItem(key, JSON.stringify(value));
23
+ }, [key, value]);
24
+
25
+ return [value, setValue];
26
+ }
27
+
28
+ export function useSessionStorage<T>(
29
+ key: string,
30
+ initialValue: T,
31
+ ): [T, Dispatch<SetStateAction<T>>] {
32
+ // Retrieve stored value from localStorage, or use initialValue if none is found
33
+ const getStoredValue = (): T => {
34
+ const storedValue = sessionStorage.getItem(key);
35
+ if (storedValue !== null) {
36
+ try {
37
+ return JSON.parse(storedValue) as T;
38
+ } catch (error) {
39
+ console.error('Error parsing stored value:', error);
40
+ }
41
+ }
42
+ return initialValue;
43
+ };
44
+
45
+ const [value, setValue] = useState<T>(getStoredValue);
46
+
47
+ useEffect(() => {
48
+ sessionStorage.setItem(key, JSON.stringify(value));
49
+ }, [key, value]);
50
+
51
+ return [value, setValue];
52
+ }
@@ -0,0 +1,13 @@
1
+ import type React from 'react';
2
+ import { useEffect, useRef } from 'react';
3
+
4
+ const useDidMountEffect = (func: React.EffectCallback, deps: React.DependencyList) => {
5
+ const didMount = useRef(false);
6
+
7
+ useEffect(() => {
8
+ if (didMount.current) func();
9
+ else didMount.current = true;
10
+ }, deps);
11
+ };
12
+
13
+ export { useDidMountEffect };
@@ -0,0 +1,30 @@
1
+ 'use client';
2
+ import { useEffect, useState } from 'react';
3
+
4
+ type WindowDimensions = {
5
+ width: number | undefined;
6
+ height: number | undefined;
7
+ };
8
+
9
+ const useWindowDimensions = (): WindowDimensions => {
10
+ const win = typeof window !== 'undefined' ? window : undefined;
11
+ const [windowDimensions, setWindowDimensions] = useState<WindowDimensions>({
12
+ width: win && window?.innerWidth,
13
+ height: win && window?.innerHeight,
14
+ });
15
+ useEffect(() => {
16
+ function handleResize(): void {
17
+ setWindowDimensions({
18
+ width: window?.innerWidth,
19
+ height: window?.innerHeight,
20
+ });
21
+ }
22
+ handleResize();
23
+ window.addEventListener('resize', handleResize);
24
+ return (): void => window.removeEventListener('resize', handleResize);
25
+ }, []);
26
+
27
+ return windowDimensions;
28
+ };
29
+
30
+ export { useWindowDimensions };