@sonamu-kit/react-components 0.1.0 → 0.1.2

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 (146) hide show
  1. package/dist/components/index.d.ts +65 -0
  2. package/dist/components/ui/accordion.d.ts +7 -0
  3. package/dist/components/ui/alert-dialog.d.ts +20 -0
  4. package/dist/components/ui/alert.d.ts +8 -0
  5. package/dist/components/ui/aspect-ratio.d.ts +3 -0
  6. package/dist/components/ui/async-select.d.ts +36 -0
  7. package/dist/components/ui/avatar.d.ts +6 -0
  8. package/dist/components/ui/badge.d.ts +9 -0
  9. package/dist/components/ui/breadcrumb.d.ts +19 -0
  10. package/dist/components/ui/button.d.ts +13 -0
  11. package/dist/components/ui/calendar.d.ts +5 -0
  12. package/dist/components/ui/card.d.ts +9 -0
  13. package/dist/components/ui/carousel.d.ts +18 -0
  14. package/dist/components/ui/checkbox.d.ts +8 -0
  15. package/dist/components/ui/collapsible.d.ts +5 -0
  16. package/dist/components/ui/combobox.d.ts +20 -0
  17. package/dist/components/ui/command.d.ts +80 -0
  18. package/dist/components/ui/common-modal.d.ts +28 -0
  19. package/dist/components/ui/context-menu.d.ts +27 -0
  20. package/dist/components/ui/date-input.d.ts +7 -0
  21. package/dist/components/ui/date-picker.d.ts +26 -0
  22. package/dist/components/ui/date-selector-multiple.d.ts +38 -0
  23. package/dist/components/ui/dialog.d.ts +19 -0
  24. package/dist/components/ui/drawer.d.ts +22 -0
  25. package/dist/components/ui/dropdown-menu.d.ts +27 -0
  26. package/dist/components/ui/form.d.ts +23 -0
  27. package/dist/components/ui/hover-card.d.ts +6 -0
  28. package/dist/components/ui/image-uploader.d.ts +14 -0
  29. package/dist/components/ui/input-otp.d.ts +34 -0
  30. package/dist/components/ui/input.d.ts +7 -0
  31. package/dist/components/ui/label.d.ts +5 -0
  32. package/dist/components/ui/menubar.d.ts +28 -0
  33. package/dist/components/ui/month-picker-multiple.d.ts +41 -0
  34. package/dist/components/ui/multi-image-uploader.d.ts +15 -0
  35. package/dist/components/ui/multi-select.d.ts +229 -0
  36. package/dist/components/ui/navigation-menu.d.ts +12 -0
  37. package/dist/components/ui/pagination.d.ts +10 -0
  38. package/dist/components/ui/popover.d.ts +7 -0
  39. package/dist/components/ui/progress.d.ts +4 -0
  40. package/dist/components/ui/radio-group.d.ts +5 -0
  41. package/dist/components/ui/resizable.d.ts +23 -0
  42. package/dist/components/ui/scroll-area.d.ts +5 -0
  43. package/dist/components/ui/select.d.ts +20 -0
  44. package/dist/components/ui/separator.d.ts +4 -0
  45. package/dist/components/ui/sheet.d.ts +25 -0
  46. package/dist/components/ui/sidebar.d.ts +69 -0
  47. package/dist/components/ui/skeleton.d.ts +2 -0
  48. package/dist/components/ui/slider.d.ts +8 -0
  49. package/dist/components/ui/sonner.d.ts +4 -0
  50. package/dist/components/ui/switch.d.ts +8 -0
  51. package/dist/components/ui/table.d.ts +24 -0
  52. package/dist/components/ui/tabs.d.ts +7 -0
  53. package/dist/components/ui/textarea.d.ts +7 -0
  54. package/dist/components/ui/toast.d.ts +15 -0
  55. package/dist/components/ui/toaster.d.ts +1 -0
  56. package/dist/components/ui/toggle-group.d.ts +12 -0
  57. package/dist/components/ui/toggle.d.ts +12 -0
  58. package/dist/components/ui/tooltip.d.ts +7 -0
  59. package/dist/hooks/index.d.ts +1 -0
  60. package/dist/hooks/use-toast.d.ts +44 -0
  61. package/dist/index.d.ts +3 -0
  62. package/dist/lib/caster.d.ts +3 -0
  63. package/dist/lib/helpers.d.ts +72 -0
  64. package/dist/lib/index.d.ts +6 -0
  65. package/{src/lib/lazy-upload.ts → dist/lib/lazy-upload.d.ts} +1 -12
  66. package/dist/lib/use-mobile.d.ts +1 -0
  67. package/dist/lib/utils.d.ts +2 -0
  68. package/dist/react-components.es.js +28375 -0
  69. package/package.json +105 -76
  70. package/COMPONENTS_LIST.md +0 -106
  71. package/COMPONENTS_STATUS.md +0 -114
  72. package/HELPERS_GUIDE.md +0 -489
  73. package/MIGRATION_PLAN.md +0 -404
  74. package/SETUP_GUIDE.md +0 -125
  75. package/components.json +0 -21
  76. package/postcss.config.js +0 -6
  77. package/src/components/index.ts +0 -315
  78. package/src/components/ui/accordion.tsx +0 -54
  79. package/src/components/ui/alert-dialog.tsx +0 -115
  80. package/src/components/ui/alert.tsx +0 -49
  81. package/src/components/ui/aspect-ratio.tsx +0 -5
  82. package/src/components/ui/async-select.tsx +0 -186
  83. package/src/components/ui/avatar.tsx +0 -45
  84. package/src/components/ui/badge.tsx +0 -38
  85. package/src/components/ui/breadcrumb.tsx +0 -102
  86. package/src/components/ui/button.tsx +0 -54
  87. package/src/components/ui/calendar.tsx +0 -193
  88. package/src/components/ui/card.tsx +0 -65
  89. package/src/components/ui/carousel.tsx +0 -243
  90. package/src/components/ui/checkbox.tsx +0 -67
  91. package/src/components/ui/collapsible.tsx +0 -9
  92. package/src/components/ui/combobox.tsx +0 -135
  93. package/src/components/ui/command.tsx +0 -143
  94. package/src/components/ui/common-modal.tsx +0 -95
  95. package/src/components/ui/context-menu.tsx +0 -189
  96. package/src/components/ui/date-picker.tsx +0 -112
  97. package/src/components/ui/date-selector-multiple.tsx +0 -197
  98. package/src/components/ui/dialog.tsx +0 -104
  99. package/src/components/ui/drawer.tsx +0 -100
  100. package/src/components/ui/dropdown-menu.tsx +0 -189
  101. package/src/components/ui/form.tsx +0 -171
  102. package/src/components/ui/hover-card.tsx +0 -27
  103. package/src/components/ui/image-uploader.tsx +0 -251
  104. package/src/components/ui/input-otp.tsx +0 -69
  105. package/src/components/ui/input.tsx +0 -38
  106. package/src/components/ui/label.tsx +0 -19
  107. package/src/components/ui/menubar.tsx +0 -231
  108. package/src/components/ui/month-picker-multiple.tsx +0 -351
  109. package/src/components/ui/multi-image-uploader.tsx +0 -283
  110. package/src/components/ui/multi-select.tsx +0 -1143
  111. package/src/components/ui/navigation-menu.tsx +0 -120
  112. package/src/components/ui/pagination.tsx +0 -72
  113. package/src/components/ui/popover.tsx +0 -42
  114. package/src/components/ui/progress.tsx +0 -25
  115. package/src/components/ui/radio-group.tsx +0 -38
  116. package/src/components/ui/resizable.tsx +0 -42
  117. package/src/components/ui/scroll-area.tsx +0 -46
  118. package/src/components/ui/select.tsx +0 -235
  119. package/src/components/ui/separator.tsx +0 -24
  120. package/src/components/ui/sheet.tsx +0 -119
  121. package/src/components/ui/sidebar.tsx +0 -683
  122. package/src/components/ui/skeleton.tsx +0 -7
  123. package/src/components/ui/slider.tsx +0 -57
  124. package/src/components/ui/sonner.tsx +0 -39
  125. package/src/components/ui/switch.tsx +0 -63
  126. package/src/components/ui/table.tsx +0 -94
  127. package/src/components/ui/tabs.tsx +0 -53
  128. package/src/components/ui/textarea.tsx +0 -34
  129. package/src/components/ui/toast.tsx +0 -122
  130. package/src/components/ui/toaster.tsx +0 -29
  131. package/src/components/ui/toggle-group.tsx +0 -55
  132. package/src/components/ui/toggle.tsx +0 -41
  133. package/src/components/ui/tooltip.tsx +0 -28
  134. package/src/hooks/index.ts +0 -2
  135. package/src/hooks/use-toast.ts +0 -189
  136. package/src/icons.d.ts +0 -1
  137. package/src/index.ts +0 -4
  138. package/src/lib/caster.ts +0 -66
  139. package/src/lib/helpers.ts +0 -394
  140. package/src/lib/index.ts +0 -31
  141. package/src/lib/use-mobile.ts +0 -19
  142. package/src/lib/utils.ts +0 -6
  143. package/src/styles/globals.css +0 -658
  144. package/tailwind.config.ts +0 -8
  145. package/tsconfig.json +0 -31
  146. package/tsconfig.node.json +0 -11
@@ -1,186 +0,0 @@
1
- "use client";
2
-
3
- import * as React from "react";
4
- import CheckIcon from "~icons/lucide/check";
5
- import ChevronsUpDownIcon from "~icons/lucide/chevrons-up-down";
6
- import Loader2Icon from "~icons/lucide/loader2";
7
- import XCircleIcon from "~icons/lucide/x-circle";
8
-
9
- import { cn } from "../../lib/utils";
10
- import { Button } from "./button";
11
- import {
12
- Command,
13
- CommandEmpty,
14
- CommandGroup,
15
- CommandInput,
16
- CommandItem,
17
- CommandList,
18
- } from "./command";
19
- import { Popover, PopoverContent, PopoverTrigger } from "./popover";
20
-
21
- export interface AsyncSelectOption<T = unknown> {
22
- value: T;
23
- label: string;
24
- }
25
-
26
- export interface AsyncSelectProps<T = number> {
27
- /** 선택 옵션 배열 */
28
- options: AsyncSelectOption<T>[];
29
- /** 현재 선택된 값 */
30
- value?: T | null;
31
- /** 값 변경 핸들러 */
32
- onValueChange?: (value: T | undefined) => void;
33
- /** 로딩 상태 */
34
- isLoading?: boolean;
35
- /** placeholder 텍스트 */
36
- placeholder?: string;
37
- /** 검색 입력 placeholder */
38
- searchPlaceholder?: string;
39
- /** 결과 없음 텍스트 */
40
- emptyText?: string;
41
- /** 로딩 중 텍스트 */
42
- loadingText?: string;
43
- /** clearable 옵션 */
44
- clearable?: boolean;
45
- /** 비활성화 */
46
- disabled?: boolean;
47
- /** 추가 className */
48
- className?: string;
49
- /** 검색어 변경 핸들러 (서버 검색용) */
50
- onSearch?: (keyword: string) => void;
51
- /** 검색 디바운스 시간 (ms) */
52
- searchDebounce?: number;
53
- }
54
-
55
- export function AsyncSelect<T = number>({
56
- options,
57
- value,
58
- onValueChange,
59
- isLoading = false,
60
- placeholder = "Select...",
61
- searchPlaceholder = "Search...",
62
- emptyText = "No results found.",
63
- loadingText = "Loading...",
64
- clearable = false,
65
- disabled = false,
66
- className,
67
- onSearch,
68
- searchDebounce = 300,
69
- }: AsyncSelectProps<T>) {
70
- const [open, setOpen] = React.useState(false);
71
- const [keyword, setKeyword] = React.useState("");
72
-
73
- // 검색어 디바운스
74
- React.useEffect(() => {
75
- if (!onSearch) return;
76
-
77
- const timer = setTimeout(() => {
78
- onSearch(keyword);
79
- }, searchDebounce);
80
-
81
- return () => clearTimeout(timer);
82
- }, [keyword, onSearch, searchDebounce]);
83
-
84
- const selectedOption = React.useMemo(
85
- () => options.find((opt) => opt.value === value),
86
- [options, value],
87
- );
88
-
89
- const handleSelect = (selectedValue: T | undefined) => {
90
- onValueChange?.(selectedValue);
91
- setOpen(false);
92
- };
93
-
94
- const handleClear = (e: React.MouseEvent) => {
95
- e.preventDefault();
96
- e.stopPropagation();
97
- onValueChange?.(undefined);
98
- setOpen(false);
99
- };
100
-
101
- // value가 실제로 유효한 값인지 확인 (null, undefined, 빈 문자열 제외)
102
- const hasValue = value !== null && value !== undefined && value !== "";
103
-
104
- return (
105
- <Popover open={open} onOpenChange={setOpen}>
106
- <PopoverTrigger asChild>
107
- <Button
108
- variant="outline"
109
- role="combobox"
110
- aria-expanded={open}
111
- disabled={disabled || isLoading}
112
- className={cn(
113
- "w-full justify-between hover:bg-background! hover:text-foreground! dark:hover:bg-input/30!",
114
- className,
115
- )}
116
- >
117
- <span className="flex-1 truncate text-left">
118
- {isLoading ? (
119
- <Loader2Icon className="h-4 w-4 animate-spin" />
120
- ) : selectedOption ? (
121
- selectedOption.label
122
- ) : (
123
- <span className="text-muted-foreground">{placeholder}</span>
124
- )}
125
- </span>
126
- <div className="flex items-center gap-1 shrink-0 pl-2">
127
- {clearable && hasValue && (
128
- <span
129
- className="flex items-center justify-center cursor-pointer"
130
- onMouseDown={(e) => e.stopPropagation()}
131
- onClick={handleClear}
132
- >
133
- <XCircleIcon className="h-4 w-4 opacity-50 hover:opacity-100 transition-opacity" />
134
- </span>
135
- )}
136
- <ChevronsUpDownIcon className="h-4 w-4 shrink-0 opacity-50" />
137
- </div>
138
- </Button>
139
- </PopoverTrigger>
140
- <PopoverContent className="w-full p-0" align="start">
141
- <Command shouldFilter={false}>
142
- <CommandInput
143
- placeholder={searchPlaceholder}
144
- value={keyword}
145
- onValueChange={setKeyword}
146
- />
147
- <CommandList>
148
- <CommandEmpty>{isLoading ? loadingText : emptyText}</CommandEmpty>
149
- <CommandGroup>
150
- {clearable && (
151
- <CommandItem
152
- value="__all__"
153
- onSelect={() => handleSelect(undefined)}
154
- className="cursor-pointer hover:bg-accent"
155
- >
156
- <CheckIcon
157
- className={cn("mr-2 h-4 w-4", !hasValue ? "opacity-100" : "opacity-0")}
158
- />
159
- ALL
160
- </CommandItem>
161
- )}
162
- {options.map((option) => (
163
- <CommandItem
164
- key={String(option.value)}
165
- value={String(option.value)}
166
- onSelect={() => handleSelect(option.value)}
167
- className="cursor-pointer"
168
- >
169
- <CheckIcon
170
- className={cn(
171
- "mr-2 h-4 w-4",
172
- value === option.value ? "opacity-100" : "opacity-0",
173
- )}
174
- />
175
- {option.label}
176
- </CommandItem>
177
- ))}
178
- </CommandGroup>
179
- </CommandList>
180
- </Command>
181
- </PopoverContent>
182
- </Popover>
183
- );
184
- }
185
-
186
- AsyncSelect.displayName = "AsyncSelect";
@@ -1,45 +0,0 @@
1
- import * as AvatarPrimitive from "@radix-ui/react-avatar";
2
- import * as React from "react";
3
-
4
- import { cn } from "../../lib/utils";
5
-
6
- const Avatar = React.forwardRef<
7
- React.ElementRef<typeof AvatarPrimitive.Root>,
8
- React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
9
- >(({ className, ...props }, ref) => (
10
- <AvatarPrimitive.Root
11
- ref={ref}
12
- className={cn("relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full", className)}
13
- {...props}
14
- />
15
- ));
16
- Avatar.displayName = AvatarPrimitive.Root.displayName;
17
-
18
- const AvatarImage = React.forwardRef<
19
- React.ElementRef<typeof AvatarPrimitive.Image>,
20
- React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
21
- >(({ className, ...props }, ref) => (
22
- <AvatarPrimitive.Image
23
- ref={ref}
24
- className={cn("aspect-square h-full w-full", className)}
25
- {...props}
26
- />
27
- ));
28
- AvatarImage.displayName = AvatarPrimitive.Image.displayName;
29
-
30
- const AvatarFallback = React.forwardRef<
31
- React.ElementRef<typeof AvatarPrimitive.Fallback>,
32
- React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
33
- >(({ className, ...props }, ref) => (
34
- <AvatarPrimitive.Fallback
35
- ref={ref}
36
- className={cn(
37
- "flex h-full w-full items-center justify-center rounded-full bg-muted",
38
- className,
39
- )}
40
- {...props}
41
- />
42
- ));
43
- AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
44
-
45
- export { Avatar, AvatarImage, AvatarFallback };
@@ -1,38 +0,0 @@
1
- import { Slot } from "@radix-ui/react-slot";
2
- import { cva, type VariantProps } from "class-variance-authority";
3
- import type * as React from "react";
4
-
5
- import { cn } from "../../lib/utils";
6
-
7
- const badgeVariants = cva(
8
- "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
9
- {
10
- variants: {
11
- variant: {
12
- default: "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
13
- secondary:
14
- "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
15
- destructive:
16
- "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
17
- outline: "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
18
- },
19
- },
20
- defaultVariants: {
21
- variant: "default",
22
- },
23
- },
24
- );
25
-
26
- function Badge({
27
- className,
28
- variant,
29
- asChild = false,
30
- ...props
31
- }: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
32
- const Comp = asChild ? Slot : "span";
33
- return (
34
- <Comp data-slot="badge" className={cn(badgeVariants({ variant }), className)} {...props} />
35
- );
36
- }
37
-
38
- export { Badge, badgeVariants };
@@ -1,102 +0,0 @@
1
- import { Slot } from "@radix-ui/react-slot";
2
- import * as React from "react";
3
- import ChevronRightIcon from "~icons/lucide/chevron-right";
4
- import MoreHorizontalIcon from "~icons/lucide/more-horizontal";
5
-
6
- import { cn } from "../../lib/utils";
7
-
8
- const Breadcrumb = React.forwardRef<
9
- HTMLElement,
10
- React.ComponentPropsWithoutRef<"nav"> & {
11
- separator?: React.ReactNode;
12
- }
13
- >(({ ...props }, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />);
14
- Breadcrumb.displayName = "Breadcrumb";
15
-
16
- const BreadcrumbList = React.forwardRef<HTMLOListElement, React.ComponentPropsWithoutRef<"ol">>(
17
- ({ className, ...props }, ref) => (
18
- <ol
19
- ref={ref}
20
- className={cn(
21
- "flex flex-wrap items-center gap-1.5 overflow-wrap-anywhere text-sm text-muted-foreground sm:gap-2.5",
22
- className,
23
- )}
24
- {...props}
25
- />
26
- ),
27
- );
28
- BreadcrumbList.displayName = "BreadcrumbList";
29
-
30
- const BreadcrumbItem = React.forwardRef<HTMLLIElement, React.ComponentPropsWithoutRef<"li">>(
31
- ({ className, ...props }, ref) => (
32
- <li ref={ref} className={cn("inline-flex items-center gap-1.5", className)} {...props} />
33
- ),
34
- );
35
- BreadcrumbItem.displayName = "BreadcrumbItem";
36
-
37
- const BreadcrumbLink = React.forwardRef<
38
- HTMLAnchorElement,
39
- React.ComponentPropsWithoutRef<"a"> & {
40
- asChild?: boolean;
41
- }
42
- >(({ asChild, className, ...props }, ref) => {
43
- const Comp = asChild ? Slot : "a";
44
-
45
- return (
46
- <Comp
47
- ref={ref}
48
- className={cn("transition-colors hover:text-foreground", className)}
49
- {...props}
50
- />
51
- );
52
- });
53
- BreadcrumbLink.displayName = "BreadcrumbLink";
54
-
55
- const BreadcrumbPage = React.forwardRef<HTMLSpanElement, React.ComponentPropsWithoutRef<"span">>(
56
- ({ className, ...props }, ref) => (
57
- <span
58
- ref={ref}
59
- role="link"
60
- aria-disabled="true"
61
- aria-current="page"
62
- className={cn("font-normal text-foreground", className)}
63
- {...props}
64
- />
65
- ),
66
- );
67
- BreadcrumbPage.displayName = "BreadcrumbPage";
68
-
69
- const BreadcrumbSeparator = ({ children, className, ...props }: React.ComponentProps<"li">) => (
70
- <li
71
- role="presentation"
72
- aria-hidden="true"
73
- className={cn("[&>svg]:w-3.5 [&>svg]:h-3.5", className)}
74
- {...props}
75
- >
76
- {children ?? <ChevronRightIcon />}
77
- </li>
78
- );
79
- BreadcrumbSeparator.displayName = "BreadcrumbSeparator";
80
-
81
- const BreadcrumbEllipsis = ({ className, ...props }: React.ComponentProps<"span">) => (
82
- <span
83
- role="presentation"
84
- aria-hidden="true"
85
- className={cn("flex h-9 w-9 items-center justify-center", className)}
86
- {...props}
87
- >
88
- <MoreHorizontalIcon className="h-4 w-4" />
89
- <span className="sr-only">More</span>
90
- </span>
91
- );
92
- BreadcrumbEllipsis.displayName = "BreadcrumbElipssis";
93
-
94
- export {
95
- Breadcrumb,
96
- BreadcrumbList,
97
- BreadcrumbItem,
98
- BreadcrumbLink,
99
- BreadcrumbPage,
100
- BreadcrumbSeparator,
101
- BreadcrumbEllipsis,
102
- };
@@ -1,54 +0,0 @@
1
- import { Slot } from "@radix-ui/react-slot";
2
- import { cva, type VariantProps } from "class-variance-authority";
3
- import * as React from "react";
4
-
5
- import { cn } from "../../lib/utils";
6
-
7
- const buttonVariants = cva(
8
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-button text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
9
- {
10
- variants: {
11
- variant: {
12
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
13
- destructive:
14
- "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
15
- outline:
16
- "border bg-background text-foreground hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
17
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
18
- ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
19
- link: "text-primary underline-offset-4 hover:underline",
20
- },
21
- size: {
22
- default: "h-9 px-4 py-2 has-[>svg]:px-3",
23
- sm: "h-8 rounded-button gap-1.5 px-3 has-[>svg]:px-2.5",
24
- lg: "h-10 rounded-button px-6 has-[>svg]:px-4",
25
- icon: "size-9 rounded-button",
26
- },
27
- },
28
- defaultVariants: {
29
- variant: "default",
30
- size: "default",
31
- },
32
- },
33
- );
34
-
35
- const Button = React.forwardRef<
36
- HTMLButtonElement,
37
- React.ComponentProps<"button"> &
38
- VariantProps<typeof buttonVariants> & {
39
- asChild?: boolean;
40
- }
41
- >(({ className, variant, size, asChild = false, ...props }, ref) => {
42
- const Comp = asChild ? Slot : "button";
43
- return (
44
- <Comp
45
- data-slot="button"
46
- className={cn(buttonVariants({ variant, size, className }))}
47
- ref={ref}
48
- {...props}
49
- />
50
- );
51
- });
52
- Button.displayName = "Button";
53
-
54
- export { Button, buttonVariants };
@@ -1,193 +0,0 @@
1
- "use client";
2
-
3
- import * as React from "react";
4
- import { type DateRange, DayPicker } from "react-day-picker";
5
- import ChevronLeftIcon from "~icons/lucide/chevron-left";
6
- import ChevronRightIcon from "~icons/lucide/chevron-right";
7
-
8
- import { cn } from "../../lib/utils";
9
- import { buttonVariants } from "./button";
10
- import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./select";
11
-
12
- function Calendar({
13
- className,
14
- classNames,
15
- showOutsideDays = true,
16
- ...props
17
- }: React.ComponentProps<typeof DayPicker>) {
18
- const getInitialMonth = () => {
19
- if (props.mode === "range") {
20
- const rangeValue = props.selected as DateRange | undefined;
21
- return rangeValue?.from || new Date();
22
- }
23
- return (props.selected as Date | undefined) || new Date();
24
- };
25
-
26
- const getInitialSecondMonth = () => {
27
- const firstMonth = getInitialMonth();
28
- const secondMonth = new Date(firstMonth);
29
- secondMonth.setMonth(secondMonth.getMonth() + 1);
30
- return secondMonth;
31
- };
32
-
33
- const [firstMonth, setFirstMonth] = React.useState<Date>(getInitialMonth());
34
- const [secondMonth, setSecondMonth] = React.useState<Date>(getInitialSecondMonth());
35
-
36
- React.useEffect(() => {
37
- if (props.selected) {
38
- if (props.mode === "range") {
39
- const rangeValue = props.selected as DateRange | undefined;
40
- if (rangeValue?.from) {
41
- setFirstMonth(rangeValue.from);
42
- const newSecondMonth = new Date(rangeValue.from);
43
- newSecondMonth.setMonth(newSecondMonth.getMonth() + 1);
44
- setSecondMonth(newSecondMonth);
45
- }
46
- } else {
47
- const dateValue = props.selected as Date | undefined;
48
- if (dateValue) {
49
- setFirstMonth(dateValue);
50
- }
51
- }
52
- }
53
- }, [props.selected, props.mode]);
54
-
55
- // Generate years from 1900 to current year + 10
56
- const years = Array.from({ length: new Date().getFullYear() - 1900 + 11 }, (_, i) => 1900 + i);
57
- const months = [
58
- "January",
59
- "February",
60
- "March",
61
- "April",
62
- "May",
63
- "June",
64
- "July",
65
- "August",
66
- "September",
67
- "October",
68
- "November",
69
- "December",
70
- ];
71
-
72
- const isRangeMode = props.mode === "range" && props.numberOfMonths === 2;
73
-
74
- return (
75
- <DayPicker
76
- showOutsideDays={showOutsideDays}
77
- className={cn("p-3", className)}
78
- month={firstMonth}
79
- onMonthChange={setFirstMonth}
80
- classNames={{
81
- months: "flex flex-col sm:flex-row gap-2",
82
- month: "flex flex-col gap-4",
83
- caption: "flex justify-center pt-1 relative items-center w-full",
84
- caption_label: "text-sm font-medium hidden",
85
- nav: "flex items-center gap-1",
86
- nav_button: cn(
87
- buttonVariants({ variant: "outline" }),
88
- "size-7 bg-transparent p-0 opacity-50 hover:opacity-100",
89
- ),
90
- nav_button_previous: "absolute left-1",
91
- nav_button_next: "absolute right-1",
92
- table: "w-full border-collapse mx-auto",
93
- head_row: "flex justify-center",
94
- head_cell: "text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]",
95
- row: "flex w-full mt-2 justify-center",
96
- cell: cn(
97
- "relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-range-end)]:rounded-r-md",
98
- props.mode === "range"
99
- ? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
100
- : "[&:has([aria-selected])]:rounded-md",
101
- ),
102
- day: cn(
103
- buttonVariants({ variant: "ghost" }),
104
- "size-8 p-0 font-normal aria-selected:opacity-100",
105
- ),
106
- day_range_start:
107
- "day-range-start aria-selected:bg-primary aria-selected:text-primary-foreground",
108
- day_range_end:
109
- "day-range-end aria-selected:bg-primary aria-selected:text-primary-foreground",
110
- day_selected:
111
- "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
112
- day_today: "bg-accent text-accent-foreground",
113
- day_outside: "day-outside text-muted-foreground aria-selected:text-muted-foreground",
114
- day_disabled: "text-muted-foreground opacity-50",
115
- day_range_middle: "aria-selected:bg-accent aria-selected:text-accent-foreground",
116
- day_hidden: "invisible",
117
- ...classNames,
118
- }}
119
- components={{
120
- IconLeft: ({ className, ...props }) => (
121
- <ChevronLeftIcon className={cn("size-4", className)} {...props} />
122
- ),
123
- IconRight: ({ className, ...props }) => (
124
- <ChevronRightIcon className={cn("size-4", className)} {...props} />
125
- ),
126
- Caption: ({ displayMonth, displayIndex }) => {
127
- // Determine which month state to use based on displayIndex
128
- const currentDisplayMonth =
129
- isRangeMode && displayIndex === 1 ? secondMonth : displayMonth;
130
- const displayYear = currentDisplayMonth.getFullYear();
131
- const displayMonthIndex = currentDisplayMonth.getMonth();
132
-
133
- const handleMonthChange = (value: string | undefined | null) => {
134
- if (!value) return;
135
- const newDate = new Date(currentDisplayMonth);
136
- newDate.setMonth(parseInt(value));
137
-
138
- if (isRangeMode && displayIndex === 1) {
139
- setSecondMonth(newDate);
140
- } else {
141
- setFirstMonth(newDate);
142
- }
143
- };
144
-
145
- const handleYearChange = (value: string | undefined | null) => {
146
- if (!value) return;
147
- const newDate = new Date(currentDisplayMonth);
148
- newDate.setFullYear(parseInt(value));
149
-
150
- if (isRangeMode && displayIndex === 1) {
151
- setSecondMonth(newDate);
152
- } else {
153
- setFirstMonth(newDate);
154
- }
155
- };
156
-
157
- return (
158
- <div className="flex justify-center pt-1 relative items-center w-full gap-2 px-10">
159
- <Select value={displayMonthIndex.toString()} onValueChange={handleMonthChange}>
160
- <SelectTrigger className="h-7 text-xs w-[110px]">
161
- <SelectValue />
162
- </SelectTrigger>
163
- <SelectContent>
164
- {months.map((month, index) => (
165
- <SelectItem key={month} value={index.toString()}>
166
- {month}
167
- </SelectItem>
168
- ))}
169
- </SelectContent>
170
- </Select>
171
- <Select value={displayYear.toString()} onValueChange={handleYearChange}>
172
- <SelectTrigger className="h-7 text-xs w-[90px]">
173
- <SelectValue />
174
- </SelectTrigger>
175
- <SelectContent className="max-h-[200px]">
176
- {years.reverse().map((year) => (
177
- <SelectItem key={year} value={year.toString()}>
178
- {year}
179
- </SelectItem>
180
- ))}
181
- </SelectContent>
182
- </Select>
183
- </div>
184
- );
185
- },
186
- }}
187
- {...props}
188
- />
189
- );
190
- }
191
-
192
- export { Calendar };
193
- export type { DateRange };
@@ -1,65 +0,0 @@
1
- import type * as React from "react";
2
-
3
- import { cn } from "../../lib/utils";
4
-
5
- function Card({ className, ...props }: React.ComponentProps<"div">) {
6
- return (
7
- <div
8
- data-slot="card"
9
- className={cn(
10
- "bg-card text-card-foreground flex flex-col gap-6 rounded-card border",
11
- className,
12
- )}
13
- {...props}
14
- />
15
- );
16
- }
17
-
18
- function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
19
- return (
20
- <div
21
- data-slot="card-header"
22
- className={cn(
23
- "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 pt-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
24
- className,
25
- )}
26
- {...props}
27
- />
28
- );
29
- }
30
-
31
- function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
32
- return <h4 data-slot="card-title" className={cn("leading-none", className)} {...props} />;
33
- }
34
-
35
- function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
36
- return (
37
- <p data-slot="card-description" className={cn("text-muted-foreground", className)} {...props} />
38
- );
39
- }
40
-
41
- function CardAction({ className, ...props }: React.ComponentProps<"div">) {
42
- return (
43
- <div
44
- data-slot="card-action"
45
- className={cn("col-start-2 row-span-2 row-start-1 self-start justify-self-end", className)}
46
- {...props}
47
- />
48
- );
49
- }
50
-
51
- function CardContent({ className, ...props }: React.ComponentProps<"div">) {
52
- return <div data-slot="card-content" className={cn("px-6 last:pb-6", className)} {...props} />;
53
- }
54
-
55
- function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
56
- return (
57
- <div
58
- data-slot="card-footer"
59
- className={cn("flex items-center px-6 pb-6 [.border-t]:pt-6", className)}
60
- {...props}
61
- />
62
- );
63
- }
64
-
65
- export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent };