@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.
- package/dist/components/index.d.ts +65 -0
- package/dist/components/ui/accordion.d.ts +7 -0
- package/dist/components/ui/alert-dialog.d.ts +20 -0
- package/dist/components/ui/alert.d.ts +8 -0
- package/dist/components/ui/aspect-ratio.d.ts +3 -0
- package/dist/components/ui/async-select.d.ts +36 -0
- package/dist/components/ui/avatar.d.ts +6 -0
- package/dist/components/ui/badge.d.ts +9 -0
- package/dist/components/ui/breadcrumb.d.ts +19 -0
- package/dist/components/ui/button.d.ts +13 -0
- package/dist/components/ui/calendar.d.ts +5 -0
- package/dist/components/ui/card.d.ts +9 -0
- package/dist/components/ui/carousel.d.ts +18 -0
- package/dist/components/ui/checkbox.d.ts +8 -0
- package/dist/components/ui/collapsible.d.ts +5 -0
- package/dist/components/ui/combobox.d.ts +20 -0
- package/dist/components/ui/command.d.ts +80 -0
- package/dist/components/ui/common-modal.d.ts +28 -0
- package/dist/components/ui/context-menu.d.ts +27 -0
- package/dist/components/ui/date-input.d.ts +7 -0
- package/dist/components/ui/date-picker.d.ts +26 -0
- package/dist/components/ui/date-selector-multiple.d.ts +38 -0
- package/dist/components/ui/dialog.d.ts +19 -0
- package/dist/components/ui/drawer.d.ts +22 -0
- package/dist/components/ui/dropdown-menu.d.ts +27 -0
- package/dist/components/ui/form.d.ts +23 -0
- package/dist/components/ui/hover-card.d.ts +6 -0
- package/dist/components/ui/image-uploader.d.ts +14 -0
- package/dist/components/ui/input-otp.d.ts +34 -0
- package/dist/components/ui/input.d.ts +7 -0
- package/dist/components/ui/label.d.ts +5 -0
- package/dist/components/ui/menubar.d.ts +28 -0
- package/dist/components/ui/month-picker-multiple.d.ts +41 -0
- package/dist/components/ui/multi-image-uploader.d.ts +15 -0
- package/dist/components/ui/multi-select.d.ts +229 -0
- package/dist/components/ui/navigation-menu.d.ts +12 -0
- package/dist/components/ui/pagination.d.ts +10 -0
- package/dist/components/ui/popover.d.ts +7 -0
- package/dist/components/ui/progress.d.ts +4 -0
- package/dist/components/ui/radio-group.d.ts +5 -0
- package/dist/components/ui/resizable.d.ts +23 -0
- package/dist/components/ui/scroll-area.d.ts +5 -0
- package/dist/components/ui/select.d.ts +20 -0
- package/dist/components/ui/separator.d.ts +4 -0
- package/dist/components/ui/sheet.d.ts +25 -0
- package/dist/components/ui/sidebar.d.ts +69 -0
- package/dist/components/ui/skeleton.d.ts +2 -0
- package/dist/components/ui/slider.d.ts +8 -0
- package/dist/components/ui/sonner.d.ts +4 -0
- package/dist/components/ui/switch.d.ts +8 -0
- package/dist/components/ui/table.d.ts +24 -0
- package/dist/components/ui/tabs.d.ts +7 -0
- package/dist/components/ui/textarea.d.ts +7 -0
- package/dist/components/ui/toast.d.ts +15 -0
- package/dist/components/ui/toaster.d.ts +1 -0
- package/dist/components/ui/toggle-group.d.ts +12 -0
- package/dist/components/ui/toggle.d.ts +12 -0
- package/dist/components/ui/tooltip.d.ts +7 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/use-toast.d.ts +44 -0
- package/dist/index.d.ts +3 -0
- package/dist/lib/caster.d.ts +3 -0
- package/dist/lib/helpers.d.ts +72 -0
- package/dist/lib/index.d.ts +6 -0
- package/{src/lib/lazy-upload.ts → dist/lib/lazy-upload.d.ts} +1 -12
- package/dist/lib/use-mobile.d.ts +1 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/react-components.es.js +28375 -0
- package/package.json +105 -76
- package/COMPONENTS_LIST.md +0 -106
- package/COMPONENTS_STATUS.md +0 -114
- package/HELPERS_GUIDE.md +0 -489
- package/MIGRATION_PLAN.md +0 -404
- package/SETUP_GUIDE.md +0 -125
- package/components.json +0 -21
- package/postcss.config.js +0 -6
- package/src/components/index.ts +0 -315
- package/src/components/ui/accordion.tsx +0 -54
- package/src/components/ui/alert-dialog.tsx +0 -115
- package/src/components/ui/alert.tsx +0 -49
- package/src/components/ui/aspect-ratio.tsx +0 -5
- package/src/components/ui/async-select.tsx +0 -186
- package/src/components/ui/avatar.tsx +0 -45
- package/src/components/ui/badge.tsx +0 -38
- package/src/components/ui/breadcrumb.tsx +0 -102
- package/src/components/ui/button.tsx +0 -54
- package/src/components/ui/calendar.tsx +0 -193
- package/src/components/ui/card.tsx +0 -65
- package/src/components/ui/carousel.tsx +0 -243
- package/src/components/ui/checkbox.tsx +0 -67
- package/src/components/ui/collapsible.tsx +0 -9
- package/src/components/ui/combobox.tsx +0 -135
- package/src/components/ui/command.tsx +0 -143
- package/src/components/ui/common-modal.tsx +0 -95
- package/src/components/ui/context-menu.tsx +0 -189
- package/src/components/ui/date-picker.tsx +0 -112
- package/src/components/ui/date-selector-multiple.tsx +0 -197
- package/src/components/ui/dialog.tsx +0 -104
- package/src/components/ui/drawer.tsx +0 -100
- package/src/components/ui/dropdown-menu.tsx +0 -189
- package/src/components/ui/form.tsx +0 -171
- package/src/components/ui/hover-card.tsx +0 -27
- package/src/components/ui/image-uploader.tsx +0 -251
- package/src/components/ui/input-otp.tsx +0 -69
- package/src/components/ui/input.tsx +0 -38
- package/src/components/ui/label.tsx +0 -19
- package/src/components/ui/menubar.tsx +0 -231
- package/src/components/ui/month-picker-multiple.tsx +0 -351
- package/src/components/ui/multi-image-uploader.tsx +0 -283
- package/src/components/ui/multi-select.tsx +0 -1143
- package/src/components/ui/navigation-menu.tsx +0 -120
- package/src/components/ui/pagination.tsx +0 -72
- package/src/components/ui/popover.tsx +0 -42
- package/src/components/ui/progress.tsx +0 -25
- package/src/components/ui/radio-group.tsx +0 -38
- package/src/components/ui/resizable.tsx +0 -42
- package/src/components/ui/scroll-area.tsx +0 -46
- package/src/components/ui/select.tsx +0 -235
- package/src/components/ui/separator.tsx +0 -24
- package/src/components/ui/sheet.tsx +0 -119
- package/src/components/ui/sidebar.tsx +0 -683
- package/src/components/ui/skeleton.tsx +0 -7
- package/src/components/ui/slider.tsx +0 -57
- package/src/components/ui/sonner.tsx +0 -39
- package/src/components/ui/switch.tsx +0 -63
- package/src/components/ui/table.tsx +0 -94
- package/src/components/ui/tabs.tsx +0 -53
- package/src/components/ui/textarea.tsx +0 -34
- package/src/components/ui/toast.tsx +0 -122
- package/src/components/ui/toaster.tsx +0 -29
- package/src/components/ui/toggle-group.tsx +0 -55
- package/src/components/ui/toggle.tsx +0 -41
- package/src/components/ui/tooltip.tsx +0 -28
- package/src/hooks/index.ts +0 -2
- package/src/hooks/use-toast.ts +0 -189
- package/src/icons.d.ts +0 -1
- package/src/index.ts +0 -4
- package/src/lib/caster.ts +0 -66
- package/src/lib/helpers.ts +0 -394
- package/src/lib/index.ts +0 -31
- package/src/lib/use-mobile.ts +0 -19
- package/src/lib/utils.ts +0 -6
- package/src/styles/globals.css +0 -658
- package/tailwind.config.ts +0 -8
- package/tsconfig.json +0 -31
- 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 };
|