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,226 @@
1
+ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
2
+ import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
3
+ import type * as React from "react";
4
+
5
+ import { cn } from "../lib/utils";
6
+
7
+ function DropdownMenu({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
8
+ return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
9
+ }
10
+
11
+ function DropdownMenuPortal({
12
+ ...props
13
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
14
+ return <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />;
15
+ }
16
+
17
+ function DropdownMenuTrigger({
18
+ ...props
19
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
20
+ return <DropdownMenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />;
21
+ }
22
+
23
+ function DropdownMenuContent({
24
+ className,
25
+ sideOffset = 4,
26
+ ...props
27
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
28
+ return (
29
+ <DropdownMenuPrimitive.Portal>
30
+ <DropdownMenuPrimitive.Content
31
+ data-slot="dropdown-menu-content"
32
+ sideOffset={sideOffset}
33
+ className={cn(
34
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
35
+ className
36
+ )}
37
+ {...props}
38
+ />
39
+ </DropdownMenuPrimitive.Portal>
40
+ );
41
+ }
42
+
43
+ function DropdownMenuGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
44
+ return <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />;
45
+ }
46
+
47
+ function DropdownMenuItem({
48
+ className,
49
+ inset,
50
+ variant = "default",
51
+ ...props
52
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
53
+ inset?: boolean;
54
+ variant?: "default" | "destructive";
55
+ }) {
56
+ return (
57
+ <DropdownMenuPrimitive.Item
58
+ data-slot="dropdown-menu-item"
59
+ data-inset={inset}
60
+ data-variant={variant}
61
+ className={cn(
62
+ "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
63
+ className
64
+ )}
65
+ {...props}
66
+ />
67
+ );
68
+ }
69
+
70
+ function DropdownMenuCheckboxItem({
71
+ className,
72
+ children,
73
+ checked,
74
+ ...props
75
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
76
+ return (
77
+ <DropdownMenuPrimitive.CheckboxItem
78
+ data-slot="dropdown-menu-checkbox-item"
79
+ className={cn(
80
+ "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
81
+ className
82
+ )}
83
+ checked={checked}
84
+ {...props}
85
+ >
86
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
87
+ <DropdownMenuPrimitive.ItemIndicator>
88
+ <CheckIcon className="size-4" />
89
+ </DropdownMenuPrimitive.ItemIndicator>
90
+ </span>
91
+ {children}
92
+ </DropdownMenuPrimitive.CheckboxItem>
93
+ );
94
+ }
95
+
96
+ function DropdownMenuRadioGroup({
97
+ ...props
98
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
99
+ return <DropdownMenuPrimitive.RadioGroup data-slot="dropdown-menu-radio-group" {...props} />;
100
+ }
101
+
102
+ function DropdownMenuRadioItem({
103
+ className,
104
+ children,
105
+ ...props
106
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
107
+ return (
108
+ <DropdownMenuPrimitive.RadioItem
109
+ data-slot="dropdown-menu-radio-item"
110
+ className={cn(
111
+ "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
112
+ className
113
+ )}
114
+ {...props}
115
+ >
116
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
117
+ <DropdownMenuPrimitive.ItemIndicator>
118
+ <CircleIcon className="size-2 fill-current" />
119
+ </DropdownMenuPrimitive.ItemIndicator>
120
+ </span>
121
+ {children}
122
+ </DropdownMenuPrimitive.RadioItem>
123
+ );
124
+ }
125
+
126
+ function DropdownMenuLabel({
127
+ className,
128
+ inset,
129
+ ...props
130
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
131
+ inset?: boolean;
132
+ }) {
133
+ return (
134
+ <DropdownMenuPrimitive.Label
135
+ data-slot="dropdown-menu-label"
136
+ data-inset={inset}
137
+ className={cn("px-2 py-1.5 text-sm font-medium data-[inset]:pl-8", className)}
138
+ {...props}
139
+ />
140
+ );
141
+ }
142
+
143
+ function DropdownMenuSeparator({
144
+ className,
145
+ ...props
146
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
147
+ return (
148
+ <DropdownMenuPrimitive.Separator
149
+ data-slot="dropdown-menu-separator"
150
+ className={cn("bg-border -mx-1 my-1 h-px", className)}
151
+ {...props}
152
+ />
153
+ );
154
+ }
155
+
156
+ function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<"span">) {
157
+ return (
158
+ <span
159
+ data-slot="dropdown-menu-shortcut"
160
+ className={cn("text-muted-foreground ml-auto text-xs tracking-widest", className)}
161
+ {...props}
162
+ />
163
+ );
164
+ }
165
+
166
+ function DropdownMenuSub({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
167
+ return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
168
+ }
169
+
170
+ function DropdownMenuSubTrigger({
171
+ className,
172
+ inset,
173
+ children,
174
+ ...props
175
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
176
+ inset?: boolean;
177
+ }) {
178
+ return (
179
+ <DropdownMenuPrimitive.SubTrigger
180
+ data-slot="dropdown-menu-sub-trigger"
181
+ data-inset={inset}
182
+ className={cn(
183
+ "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8",
184
+ className
185
+ )}
186
+ {...props}
187
+ >
188
+ {children}
189
+ <ChevronRightIcon className="ml-auto size-4" />
190
+ </DropdownMenuPrimitive.SubTrigger>
191
+ );
192
+ }
193
+
194
+ function DropdownMenuSubContent({
195
+ className,
196
+ ...props
197
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
198
+ return (
199
+ <DropdownMenuPrimitive.SubContent
200
+ data-slot="dropdown-menu-sub-content"
201
+ className={cn(
202
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
203
+ className
204
+ )}
205
+ {...props}
206
+ />
207
+ );
208
+ }
209
+
210
+ export {
211
+ DropdownMenu,
212
+ DropdownMenuPortal,
213
+ DropdownMenuTrigger,
214
+ DropdownMenuContent,
215
+ DropdownMenuGroup,
216
+ DropdownMenuLabel,
217
+ DropdownMenuItem,
218
+ DropdownMenuCheckboxItem,
219
+ DropdownMenuRadioGroup,
220
+ DropdownMenuRadioItem,
221
+ DropdownMenuSeparator,
222
+ DropdownMenuShortcut,
223
+ DropdownMenuSub,
224
+ DropdownMenuSubTrigger,
225
+ DropdownMenuSubContent,
226
+ };
@@ -0,0 +1,165 @@
1
+ import type * as LabelPrimitive from "@radix-ui/react-label";
2
+ import { Slot } from "@radix-ui/react-slot";
3
+ import { CircleAlert } from "lucide-react";
4
+ import * as React from "react";
5
+ import type { ControllerProps, FieldPath, FieldValues } from "react-hook-form";
6
+ import { Controller, FormProvider, useFormContext } from "react-hook-form";
7
+ import type { ClassNameValue } from "tailwind-merge";
8
+ import { cn } from "../lib/utils";
9
+ import { Label } from "./label";
10
+
11
+ const Form = FormProvider;
12
+
13
+ type FormFieldContextValue<
14
+ TFieldValues extends FieldValues = FieldValues,
15
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
16
+ > = {
17
+ name: TName;
18
+ };
19
+
20
+ const FormFieldContext = React.createContext<FormFieldContextValue>({} as FormFieldContextValue);
21
+
22
+ const FormField = <
23
+ TFieldValues extends FieldValues = FieldValues,
24
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
25
+ >({
26
+ ...props
27
+ }: ControllerProps<TFieldValues, TName>) => {
28
+ return (
29
+ <FormFieldContext.Provider value={{ name: props.name }}>
30
+ <Controller {...props} />
31
+ </FormFieldContext.Provider>
32
+ );
33
+ };
34
+
35
+ const useFormField = () => {
36
+ const fieldContext = React.useContext(FormFieldContext);
37
+ const itemContext = React.useContext(FormItemContext);
38
+ const { getFieldState, formState } = useFormContext();
39
+
40
+ const fieldState = getFieldState(fieldContext.name, formState);
41
+
42
+ if (!fieldContext) {
43
+ throw new Error("useFormField should be used within <FormField>");
44
+ }
45
+
46
+ const { id } = itemContext;
47
+
48
+ return {
49
+ id,
50
+ name: fieldContext.name,
51
+ formItemId: `${id}-form-item`,
52
+ formDescriptionId: `${id}-form-item-description`,
53
+ formMessageId: `${id}-form-item-message`,
54
+ ...fieldState,
55
+ };
56
+ };
57
+
58
+ type FormItemContextValue = {
59
+ id: string;
60
+ };
61
+
62
+ const FormItemContext = React.createContext<FormItemContextValue>({} as FormItemContextValue);
63
+
64
+ const FormItem = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
65
+ ({ className, ...props }, ref) => {
66
+ const id = React.useId();
67
+
68
+ return (
69
+ <FormItemContext.Provider value={{ id }}>
70
+ <div ref={ref} className={cn("space-y-2", className)} {...props} />
71
+ </FormItemContext.Provider>
72
+ );
73
+ }
74
+ );
75
+ FormItem.displayName = "FormItem";
76
+
77
+ const FormLabel = React.forwardRef<
78
+ React.ElementRef<typeof LabelPrimitive.Root>,
79
+ React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & {
80
+ className?: ClassNameValue;
81
+ }
82
+ >(({ className, ...props }, ref) => {
83
+ const { error, formItemId } = useFormField();
84
+
85
+ return (
86
+ <Label
87
+ ref={ref}
88
+ className={cn(error && "text-destructive", className)}
89
+ htmlFor={formItemId}
90
+ {...props}
91
+ />
92
+ );
93
+ });
94
+ FormLabel.displayName = "FormLabel";
95
+
96
+ const FormControl = React.forwardRef<
97
+ React.ElementRef<typeof Slot>,
98
+ React.ComponentPropsWithoutRef<typeof Slot>
99
+ >(({ ...props }, ref) => {
100
+ const { error, formItemId, formDescriptionId, formMessageId } = useFormField();
101
+
102
+ return (
103
+ <Slot
104
+ ref={ref}
105
+ id={formItemId}
106
+ aria-describedby={!error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`}
107
+ aria-invalid={!!error}
108
+ {...props}
109
+ />
110
+ );
111
+ });
112
+ FormControl.displayName = "FormControl";
113
+
114
+ const FormDescription = React.forwardRef<
115
+ HTMLParagraphElement,
116
+ React.HTMLAttributes<HTMLParagraphElement>
117
+ >(({ className, ...props }, ref) => {
118
+ const { formDescriptionId } = useFormField();
119
+
120
+ return (
121
+ <p
122
+ ref={ref}
123
+ id={formDescriptionId}
124
+ className={cn("text-sm text-muted-foreground", className)}
125
+ {...props}
126
+ />
127
+ );
128
+ });
129
+ FormDescription.displayName = "FormDescription";
130
+
131
+ const FormMessage = React.forwardRef<
132
+ HTMLParagraphElement,
133
+ React.HTMLAttributes<HTMLParagraphElement>
134
+ >(({ className, children, ...props }, ref) => {
135
+ const { error, formMessageId } = useFormField();
136
+ const body = error ? String(error?.message) : children;
137
+
138
+ if (!body) {
139
+ return null;
140
+ }
141
+
142
+ return (
143
+ <span
144
+ ref={ref}
145
+ id={formMessageId}
146
+ className={cn("text-sm flex gap-2 items-center font-medium text-destructive", className)}
147
+ {...props}
148
+ >
149
+ <CircleAlert size={18} strokeWidth={2} />
150
+ <p>{body}</p>
151
+ </span>
152
+ );
153
+ });
154
+ FormMessage.displayName = "FormMessage";
155
+
156
+ export {
157
+ useFormField,
158
+ Form,
159
+ FormItem,
160
+ FormLabel,
161
+ FormControl,
162
+ FormDescription,
163
+ FormMessage,
164
+ FormField,
165
+ };
@@ -0,0 +1,76 @@
1
+ "use client";
2
+ import { OTPInput, OTPInputContext } from "input-otp";
3
+ import { MinusIcon } from "lucide-react";
4
+ import * as React from "react";
5
+
6
+ import { cn } from "../lib/utils";
7
+
8
+ function InputOTP({
9
+ className,
10
+ containerClassName,
11
+ ...props
12
+ }: React.ComponentProps<typeof OTPInput> & {
13
+ containerClassName?: string;
14
+ }) {
15
+ return (
16
+ <OTPInput
17
+ data-slot="input-otp"
18
+ containerClassName={cn(
19
+ "flex items-center gap-2 has-disabled:opacity-50",
20
+ containerClassName
21
+ )}
22
+ className={cn("disabled:cursor-not-allowed", className)}
23
+ {...props}
24
+ />
25
+ );
26
+ }
27
+
28
+ function InputOTPGroup({ className, ...props }: React.ComponentProps<"div">) {
29
+ return (
30
+ <div
31
+ data-slot="input-otp-group"
32
+ className={cn("flex items-center", className)}
33
+ {...props}
34
+ />
35
+ );
36
+ }
37
+
38
+ function InputOTPSlot({
39
+ index,
40
+ className,
41
+ ...props
42
+ }: React.ComponentProps<"div"> & {
43
+ index: number;
44
+ }) {
45
+ const inputOTPContext = React.useContext(OTPInputContext);
46
+ const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {};
47
+
48
+ return (
49
+ <div
50
+ data-slot="input-otp-slot"
51
+ data-active={isActive}
52
+ className={cn(
53
+ "data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive dark:bg-input/30 border-input relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]",
54
+ className
55
+ )}
56
+ {...props}
57
+ >
58
+ {char}
59
+ {hasFakeCaret && (
60
+ <div className="pointer-events-none absolute inset-0 flex items-center justify-center">
61
+ <div className="animate-caret-blink bg-foreground h-4 w-px duration-1000" />
62
+ </div>
63
+ )}
64
+ </div>
65
+ );
66
+ }
67
+
68
+ function InputOTPSeparator({ ...props }: React.ComponentProps<"div">) {
69
+ return (
70
+ <div data-slot="input-otp-separator" role="separator" {...props}>
71
+ <MinusIcon />
72
+ </div>
73
+ );
74
+ }
75
+
76
+ export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator };
@@ -0,0 +1,21 @@
1
+ import * as React from "react";
2
+
3
+ import { cn } from "../lib/utils";
4
+ export type InputProps = React.InputHTMLAttributes<HTMLInputElement>;
5
+ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
6
+ return (
7
+ <input
8
+ type={type}
9
+ data-slot="input"
10
+ className={cn(
11
+ "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-background border-b-1 border-b-primary/40 shadow px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
12
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
13
+ "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
14
+ className
15
+ )}
16
+ {...props}
17
+ />
18
+ );
19
+ }
20
+
21
+ export { Input };
@@ -0,0 +1,24 @@
1
+ "use client";
2
+
3
+ import * as LabelPrimitive from "@radix-ui/react-label";
4
+ import type * as React from "react";
5
+
6
+ import { cn } from "../lib/utils";
7
+
8
+ function Label({
9
+ className,
10
+ ...props
11
+ }: React.ComponentProps<typeof LabelPrimitive.Root>) {
12
+ return (
13
+ <LabelPrimitive.Root
14
+ data-slot="label"
15
+ className={cn(
16
+ "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
17
+ className
18
+ )}
19
+ {...props}
20
+ />
21
+ );
22
+ }
23
+
24
+ export { Label };