@m5kdev/web-ui 0.1.0
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/LICENSE +621 -0
- package/README.md +17 -0
- package/package.json +169 -0
- package/src/animations/card.motion.ts +9 -0
- package/src/components/AvatarUpload.tsx +133 -0
- package/src/components/Button.tsx +14 -0
- package/src/components/Calendar.css +684 -0
- package/src/components/Calendar.tsx +32 -0
- package/src/components/CardsSelect.tsx +155 -0
- package/src/components/CollapsibleSidebarMenuItem.tsx +57 -0
- package/src/components/ColorPicker.tsx +56 -0
- package/src/components/CopyButton.tsx +45 -0
- package/src/components/CropDialog.tsx +154 -0
- package/src/components/DialogProvider.tsx +105 -0
- package/src/components/ErrorFallback.tsx +17 -0
- package/src/components/FileDropzone.tsx +120 -0
- package/src/components/MultiSelectDropdown.tsx +233 -0
- package/src/components/Orb.tsx +288 -0
- package/src/components/PageAlert.tsx +121 -0
- package/src/components/SelectChips.tsx +40 -0
- package/src/components/SidebarItem.tsx +26 -0
- package/src/components/Steps.tsx +340 -0
- package/src/components/TablerIconPicker.tsx +4260 -0
- package/src/components/app-header.tsx +40 -0
- package/src/components/blur-card.tsx +132 -0
- package/src/components/features-section-demo-1.tsx +127 -0
- package/src/components/features-section-demo-2.tsx +102 -0
- package/src/components/features-section-demo-3.tsx +272 -0
- package/src/components/mode-toggle.tsx +31 -0
- package/src/components/nav-main.tsx +69 -0
- package/src/components/pricing-cards.tsx +133 -0
- package/src/components/shared/ButtonCopy.tsx +50 -0
- package/src/components/team-switcher.tsx +83 -0
- package/src/components/theme-provider.tsx +74 -0
- package/src/components/typewriter.tsx +90 -0
- package/src/components/ui/alert-dialog.tsx +133 -0
- package/src/components/ui/alert.tsx +60 -0
- package/src/components/ui/avatar.tsx +47 -0
- package/src/components/ui/badge.tsx +33 -0
- package/src/components/ui/bento-grid.tsx +54 -0
- package/src/components/ui/bento-grid2.tsx +66 -0
- package/src/components/ui/breadcrumb.tsx +101 -0
- package/src/components/ui/button.tsx +50 -0
- package/src/components/ui/card.tsx +55 -0
- package/src/components/ui/checkbox.tsx +26 -0
- package/src/components/ui/collapsible.tsx +9 -0
- package/src/components/ui/dialog.tsx +119 -0
- package/src/components/ui/dropdown-menu.tsx +186 -0
- package/src/components/ui/floating-navbar.tsx +78 -0
- package/src/components/ui/form.tsx +167 -0
- package/src/components/ui/image.tsx +55 -0
- package/src/components/ui/input.tsx +22 -0
- package/src/components/ui/label.tsx +19 -0
- package/src/components/ui/pagination.tsx +105 -0
- package/src/components/ui/progress.tsx +23 -0
- package/src/components/ui/resizable-navbar.tsx +260 -0
- package/src/components/ui/segment-control.tsx +143 -0
- package/src/components/ui/select.tsx +153 -0
- package/src/components/ui/separator.tsx +24 -0
- package/src/components/ui/sheet.tsx +121 -0
- package/src/components/ui/sidebar.tsx +736 -0
- package/src/components/ui/skeleton.tsx +7 -0
- package/src/components/ui/slider.tsx +23 -0
- package/src/components/ui/sonner.tsx +27 -0
- package/src/components/ui/spinner.tsx +45 -0
- package/src/components/ui/switch.tsx +27 -0
- package/src/components/ui/table.tsx +90 -0
- package/src/components/ui/tabs.tsx +52 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/timeline.tsx +95 -0
- package/src/components/ui/toast.tsx +126 -0
- package/src/components/ui/tooltip.tsx +55 -0
- package/src/components/ui/typewriter-effect.tsx +181 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/hooks/useDialog.ts +25 -0
- package/src/icons/GoogleIcon.tsx +32 -0
- package/src/icons/LinkedInIcon.tsx +30 -0
- package/src/icons/MicrosoftIcon.tsx +21 -0
- package/src/lib/chatwoot.ts +51 -0
- package/src/lib/utils.ts +6 -0
- package/src/modules/app/components/AppLoader.tsx +9 -0
- package/src/modules/app/components/AppShell.tsx +21 -0
- package/src/modules/app/components/AppSidebar.tsx +26 -0
- package/src/modules/app/components/AppSidebarContent.tsx +73 -0
- package/src/modules/app/components/AppSidebarHeader.tsx +57 -0
- package/src/modules/app/components/AppSidebarInvites.tsx +32 -0
- package/src/modules/app/components/AppSidebarUser.tsx +128 -0
- package/src/modules/auth/components/AdminUserManagement.tsx +1136 -0
- package/src/modules/auth/components/AdminWaitlist.tsx +358 -0
- package/src/modules/auth/components/AuthLayout.tsx +13 -0
- package/src/modules/auth/components/AuthProviders.tsx +105 -0
- package/src/modules/auth/components/AuthRouter.tsx +29 -0
- package/src/modules/auth/components/ClaimAccountRoute.tsx +242 -0
- package/src/modules/auth/components/ErrorAuthRoute.tsx +121 -0
- package/src/modules/auth/components/ForgotPasswordForm.tsx +58 -0
- package/src/modules/auth/components/ForgotPasswordRoute.tsx +27 -0
- package/src/modules/auth/components/InviteFriends.tsx +273 -0
- package/src/modules/auth/components/LastUsedBadge.tsx +22 -0
- package/src/modules/auth/components/LoginForm.tsx +104 -0
- package/src/modules/auth/components/LoginRoute.tsx +31 -0
- package/src/modules/auth/components/LogoutRoute.tsx +21 -0
- package/src/modules/auth/components/OrganizationAcceptInvitationRoute.tsx +161 -0
- package/src/modules/auth/components/OrganizationMembersRoute.tsx +730 -0
- package/src/modules/auth/components/OrganizationSettingsRoute.tsx +280 -0
- package/src/modules/auth/components/OrganizationSwitcher.tsx +148 -0
- package/src/modules/auth/components/ProfileRoute.tsx +104 -0
- package/src/modules/auth/components/RangeNuqsDatePicker.tsx +365 -0
- package/src/modules/auth/components/ResetPasswordForm.tsx +103 -0
- package/src/modules/auth/components/ResetPasswordRoute.tsx +27 -0
- package/src/modules/auth/components/SignupFormRoute.tsx +189 -0
- package/src/modules/auth/components/SignupRoute.tsx +53 -0
- package/src/modules/auth/components/UserPreferences.tsx +144 -0
- package/src/modules/auth/components/WaitlistCard.tsx +78 -0
- package/src/modules/auth/components/WaitlistCodeValidation.tsx +79 -0
- package/src/modules/billing/components/BillingBetaPage.tsx +124 -0
- package/src/modules/billing/components/BillingInvoicePage.tsx +180 -0
- package/src/modules/billing/components/BillingPlanSelect.tsx +14 -0
- package/src/modules/billing/components/BillingRouter.tsx +20 -0
- package/src/modules/billing/components/BillingSinglePlanSelect.tsx +172 -0
- package/src/modules/table/components/ColumnOrderAndVisibility.tsx +127 -0
- package/src/modules/table/components/NuqsTable.tsx +396 -0
- package/src/modules/table/components/TableFiltering.tsx +520 -0
- package/src/modules/table/components/TablePagination.tsx +59 -0
- package/src/modules/table/components/table.types.ts +11 -0
- package/src/modules/table/filterTransformers.ts +323 -0
- package/src/types.ts +4 -0
- package/src/vite-env.d.ts +1 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
2
|
+
import { Check, ChevronRight, Circle } from "lucide-react";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "#utils";
|
|
6
|
+
|
|
7
|
+
const DropdownMenu = DropdownMenuPrimitive.Root;
|
|
8
|
+
|
|
9
|
+
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
10
|
+
|
|
11
|
+
const DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
|
12
|
+
|
|
13
|
+
const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
|
14
|
+
|
|
15
|
+
const DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
|
16
|
+
|
|
17
|
+
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
18
|
+
|
|
19
|
+
const DropdownMenuSubTrigger = React.forwardRef<
|
|
20
|
+
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
|
|
21
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
|
|
22
|
+
inset?: boolean;
|
|
23
|
+
}
|
|
24
|
+
>(({ className, inset, children, ...props }, ref) => (
|
|
25
|
+
<DropdownMenuPrimitive.SubTrigger
|
|
26
|
+
ref={ref}
|
|
27
|
+
className={cn(
|
|
28
|
+
"flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
29
|
+
inset && "pl-8",
|
|
30
|
+
className
|
|
31
|
+
)}
|
|
32
|
+
{...props}
|
|
33
|
+
>
|
|
34
|
+
{children}
|
|
35
|
+
<ChevronRight className="ml-auto" />
|
|
36
|
+
</DropdownMenuPrimitive.SubTrigger>
|
|
37
|
+
));
|
|
38
|
+
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
|
|
39
|
+
|
|
40
|
+
const DropdownMenuSubContent = React.forwardRef<
|
|
41
|
+
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
|
|
42
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
|
|
43
|
+
>(({ className, ...props }, ref) => (
|
|
44
|
+
<DropdownMenuPrimitive.SubContent
|
|
45
|
+
ref={ref}
|
|
46
|
+
className={cn(
|
|
47
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg 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",
|
|
48
|
+
className
|
|
49
|
+
)}
|
|
50
|
+
{...props}
|
|
51
|
+
/>
|
|
52
|
+
));
|
|
53
|
+
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
|
|
54
|
+
|
|
55
|
+
const DropdownMenuContent = React.forwardRef<
|
|
56
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
|
57
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
|
|
58
|
+
>(({ className, sideOffset = 4, ...props }, ref) => (
|
|
59
|
+
<DropdownMenuPrimitive.Portal>
|
|
60
|
+
<DropdownMenuPrimitive.Content
|
|
61
|
+
ref={ref}
|
|
62
|
+
sideOffset={sideOffset}
|
|
63
|
+
className={cn(
|
|
64
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
|
|
65
|
+
"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",
|
|
66
|
+
className
|
|
67
|
+
)}
|
|
68
|
+
{...props}
|
|
69
|
+
/>
|
|
70
|
+
</DropdownMenuPrimitive.Portal>
|
|
71
|
+
));
|
|
72
|
+
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
|
73
|
+
|
|
74
|
+
const DropdownMenuItem = React.forwardRef<
|
|
75
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
|
|
76
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
|
|
77
|
+
inset?: boolean;
|
|
78
|
+
}
|
|
79
|
+
>(({ className, inset, ...props }, ref) => (
|
|
80
|
+
<DropdownMenuPrimitive.Item
|
|
81
|
+
ref={ref}
|
|
82
|
+
className={cn(
|
|
83
|
+
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
|
|
84
|
+
inset && "pl-8",
|
|
85
|
+
className
|
|
86
|
+
)}
|
|
87
|
+
{...props}
|
|
88
|
+
/>
|
|
89
|
+
));
|
|
90
|
+
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
|
91
|
+
|
|
92
|
+
const DropdownMenuCheckboxItem = React.forwardRef<
|
|
93
|
+
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
|
|
94
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
|
|
95
|
+
>(({ className, children, checked, ...props }, ref) => (
|
|
96
|
+
<DropdownMenuPrimitive.CheckboxItem
|
|
97
|
+
ref={ref}
|
|
98
|
+
className={cn(
|
|
99
|
+
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
100
|
+
className
|
|
101
|
+
)}
|
|
102
|
+
checked={checked}
|
|
103
|
+
{...props}
|
|
104
|
+
>
|
|
105
|
+
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
106
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
107
|
+
<Check className="h-4 w-4" />
|
|
108
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
109
|
+
</span>
|
|
110
|
+
{children}
|
|
111
|
+
</DropdownMenuPrimitive.CheckboxItem>
|
|
112
|
+
));
|
|
113
|
+
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
|
|
114
|
+
|
|
115
|
+
const DropdownMenuRadioItem = React.forwardRef<
|
|
116
|
+
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
|
|
117
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
|
|
118
|
+
>(({ className, children, ...props }, ref) => (
|
|
119
|
+
<DropdownMenuPrimitive.RadioItem
|
|
120
|
+
ref={ref}
|
|
121
|
+
className={cn(
|
|
122
|
+
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
123
|
+
className
|
|
124
|
+
)}
|
|
125
|
+
{...props}
|
|
126
|
+
>
|
|
127
|
+
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
128
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
129
|
+
<Circle className="h-2 w-2 fill-current" />
|
|
130
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
131
|
+
</span>
|
|
132
|
+
{children}
|
|
133
|
+
</DropdownMenuPrimitive.RadioItem>
|
|
134
|
+
));
|
|
135
|
+
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
|
136
|
+
|
|
137
|
+
const DropdownMenuLabel = React.forwardRef<
|
|
138
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
|
|
139
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
|
|
140
|
+
inset?: boolean;
|
|
141
|
+
}
|
|
142
|
+
>(({ className, inset, ...props }, ref) => (
|
|
143
|
+
<DropdownMenuPrimitive.Label
|
|
144
|
+
ref={ref}
|
|
145
|
+
className={cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className)}
|
|
146
|
+
{...props}
|
|
147
|
+
/>
|
|
148
|
+
));
|
|
149
|
+
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
|
150
|
+
|
|
151
|
+
const DropdownMenuSeparator = React.forwardRef<
|
|
152
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
|
|
153
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
|
|
154
|
+
>(({ className, ...props }, ref) => (
|
|
155
|
+
<DropdownMenuPrimitive.Separator
|
|
156
|
+
ref={ref}
|
|
157
|
+
className={cn("-mx-1 my-1 h-px bg-muted", className)}
|
|
158
|
+
{...props}
|
|
159
|
+
/>
|
|
160
|
+
));
|
|
161
|
+
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
|
162
|
+
|
|
163
|
+
const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
|
|
164
|
+
return (
|
|
165
|
+
<span className={cn("ml-auto text-xs tracking-widest opacity-60", className)} {...props} />
|
|
166
|
+
);
|
|
167
|
+
};
|
|
168
|
+
DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
|
|
169
|
+
|
|
170
|
+
export {
|
|
171
|
+
DropdownMenu,
|
|
172
|
+
DropdownMenuTrigger,
|
|
173
|
+
DropdownMenuContent,
|
|
174
|
+
DropdownMenuItem,
|
|
175
|
+
DropdownMenuCheckboxItem,
|
|
176
|
+
DropdownMenuRadioItem,
|
|
177
|
+
DropdownMenuLabel,
|
|
178
|
+
DropdownMenuSeparator,
|
|
179
|
+
DropdownMenuShortcut,
|
|
180
|
+
DropdownMenuGroup,
|
|
181
|
+
DropdownMenuPortal,
|
|
182
|
+
DropdownMenuSub,
|
|
183
|
+
DropdownMenuSubContent,
|
|
184
|
+
DropdownMenuSubTrigger,
|
|
185
|
+
DropdownMenuRadioGroup,
|
|
186
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { AnimatePresence, motion, useMotionValueEvent, useScroll } from "motion/react";
|
|
3
|
+
import type React from "react";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { cn } from "#utils";
|
|
6
|
+
|
|
7
|
+
export const FloatingNav = ({
|
|
8
|
+
navItems,
|
|
9
|
+
className,
|
|
10
|
+
}: {
|
|
11
|
+
navItems: {
|
|
12
|
+
name: string;
|
|
13
|
+
link: string;
|
|
14
|
+
icon?: React.ReactNode;
|
|
15
|
+
}[];
|
|
16
|
+
className?: string;
|
|
17
|
+
}) => {
|
|
18
|
+
const { scrollYProgress } = useScroll();
|
|
19
|
+
|
|
20
|
+
const [visible, setVisible] = useState(true);
|
|
21
|
+
|
|
22
|
+
useMotionValueEvent(scrollYProgress, "change", (current) => {
|
|
23
|
+
// Check if current is not undefined and is a number
|
|
24
|
+
if (typeof current === "number") {
|
|
25
|
+
const direction = current! - scrollYProgress.getPrevious()!;
|
|
26
|
+
|
|
27
|
+
if (scrollYProgress.get() < 0.05) {
|
|
28
|
+
setVisible(false);
|
|
29
|
+
} else {
|
|
30
|
+
if (direction < 0) {
|
|
31
|
+
setVisible(true);
|
|
32
|
+
} else if (current === 1) {
|
|
33
|
+
setVisible(true);
|
|
34
|
+
} else {
|
|
35
|
+
setVisible(false);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<AnimatePresence mode="wait">
|
|
43
|
+
<motion.div
|
|
44
|
+
initial={{
|
|
45
|
+
opacity: 0,
|
|
46
|
+
y: 0,
|
|
47
|
+
}}
|
|
48
|
+
animate={{
|
|
49
|
+
y: visible ? 0 : -100,
|
|
50
|
+
opacity: visible ? 1 : 0,
|
|
51
|
+
}}
|
|
52
|
+
transition={{
|
|
53
|
+
duration: 0.2,
|
|
54
|
+
}}
|
|
55
|
+
className={cn(
|
|
56
|
+
"flex max-w-fit fixed top-6 inset-x-0 mx-auto border border-transparent dark:border-white/[0.2] rounded-full dark:bg-black bg-white shadow-[0px_2px_3px_-1px_rgba(0,0,0,0.1),0px_1px_0px_0px_rgba(25,28,33,0.02),0px_0px_0px_1px_rgba(25,28,33,0.08)] z-[5000] pr-2 pl-8 py-2 items-center justify-center space-x-4",
|
|
57
|
+
className
|
|
58
|
+
)}
|
|
59
|
+
>
|
|
60
|
+
{navItems.map((navItem: any, idx: number) => (
|
|
61
|
+
<a
|
|
62
|
+
key={`link=${idx}`}
|
|
63
|
+
href={navItem.link}
|
|
64
|
+
className={cn(
|
|
65
|
+
"relative dark:text-neutral-50 items-center flex space-x-1 text-neutral-600 dark:hover:text-neutral-300 hover:text-neutral-500"
|
|
66
|
+
)}
|
|
67
|
+
>
|
|
68
|
+
<span className="block sm:hidden">{navItem.icon}</span>
|
|
69
|
+
<span className="hidden sm:block text-sm">{navItem.name}</span>
|
|
70
|
+
</a>
|
|
71
|
+
))}
|
|
72
|
+
<button className="text-sm font-medium relative text-black bg-white px-4 py-2 rounded-full">
|
|
73
|
+
<span>Get in touch</span>
|
|
74
|
+
</button>
|
|
75
|
+
</motion.div>
|
|
76
|
+
</AnimatePresence>
|
|
77
|
+
);
|
|
78
|
+
};
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import type * as LabelPrimitive from "@radix-ui/react-label";
|
|
2
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import {
|
|
5
|
+
Controller,
|
|
6
|
+
type ControllerProps,
|
|
7
|
+
type FieldPath,
|
|
8
|
+
type FieldValues,
|
|
9
|
+
FormProvider,
|
|
10
|
+
useFormContext,
|
|
11
|
+
} from "react-hook-form";
|
|
12
|
+
|
|
13
|
+
import { Label } from "#components/ui/label";
|
|
14
|
+
import { cn } from "#utils";
|
|
15
|
+
|
|
16
|
+
const Form = FormProvider;
|
|
17
|
+
|
|
18
|
+
type FormFieldContextValue<
|
|
19
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
20
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
21
|
+
> = {
|
|
22
|
+
name: TName;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const FormFieldContext = React.createContext<FormFieldContextValue>({} as FormFieldContextValue);
|
|
26
|
+
|
|
27
|
+
const FormField = <
|
|
28
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
29
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
30
|
+
>({
|
|
31
|
+
...props
|
|
32
|
+
}: ControllerProps<TFieldValues, TName>) => {
|
|
33
|
+
return (
|
|
34
|
+
<FormFieldContext.Provider value={{ name: props.name }}>
|
|
35
|
+
<Controller {...props} />
|
|
36
|
+
</FormFieldContext.Provider>
|
|
37
|
+
);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const useFormField = () => {
|
|
41
|
+
const fieldContext = React.useContext(FormFieldContext);
|
|
42
|
+
const itemContext = React.useContext(FormItemContext);
|
|
43
|
+
const { getFieldState, formState } = useFormContext();
|
|
44
|
+
|
|
45
|
+
const fieldState = getFieldState(fieldContext.name, formState);
|
|
46
|
+
|
|
47
|
+
if (!fieldContext) {
|
|
48
|
+
throw new Error("useFormField should be used within <FormField>");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const { id } = itemContext;
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
id,
|
|
55
|
+
name: fieldContext.name,
|
|
56
|
+
formItemId: `${id}-form-item`,
|
|
57
|
+
formDescriptionId: `${id}-form-item-description`,
|
|
58
|
+
formMessageId: `${id}-form-item-message`,
|
|
59
|
+
...fieldState,
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
type FormItemContextValue = {
|
|
64
|
+
id: string;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const FormItemContext = React.createContext<FormItemContextValue>({} as FormItemContextValue);
|
|
68
|
+
|
|
69
|
+
const FormItem = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
70
|
+
({ className, ...props }, ref) => {
|
|
71
|
+
const id = React.useId();
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<FormItemContext.Provider value={{ id }}>
|
|
75
|
+
<div ref={ref} className={cn("space-y-2", className)} {...props} />
|
|
76
|
+
</FormItemContext.Provider>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
FormItem.displayName = "FormItem";
|
|
81
|
+
|
|
82
|
+
const FormLabel = React.forwardRef<
|
|
83
|
+
React.ElementRef<typeof LabelPrimitive.Root>,
|
|
84
|
+
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
|
|
85
|
+
>(({ className, ...props }, ref) => {
|
|
86
|
+
const { error, formItemId } = useFormField();
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<Label
|
|
90
|
+
ref={ref}
|
|
91
|
+
className={cn(error && "text-destructive", className)}
|
|
92
|
+
htmlFor={formItemId}
|
|
93
|
+
{...props}
|
|
94
|
+
/>
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
FormLabel.displayName = "FormLabel";
|
|
98
|
+
|
|
99
|
+
const FormControl = React.forwardRef<
|
|
100
|
+
React.ElementRef<typeof Slot>,
|
|
101
|
+
React.ComponentPropsWithoutRef<typeof Slot>
|
|
102
|
+
>(({ ...props }, ref) => {
|
|
103
|
+
const { error, formItemId, formDescriptionId, formMessageId } = useFormField();
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<Slot
|
|
107
|
+
ref={ref}
|
|
108
|
+
id={formItemId}
|
|
109
|
+
aria-describedby={!error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`}
|
|
110
|
+
aria-invalid={!!error}
|
|
111
|
+
{...props}
|
|
112
|
+
/>
|
|
113
|
+
);
|
|
114
|
+
});
|
|
115
|
+
FormControl.displayName = "FormControl";
|
|
116
|
+
|
|
117
|
+
const FormDescription = React.forwardRef<
|
|
118
|
+
HTMLParagraphElement,
|
|
119
|
+
React.HTMLAttributes<HTMLParagraphElement>
|
|
120
|
+
>(({ className, ...props }, ref) => {
|
|
121
|
+
const { formDescriptionId } = useFormField();
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
<p
|
|
125
|
+
ref={ref}
|
|
126
|
+
id={formDescriptionId}
|
|
127
|
+
className={cn("text-[0.8rem] text-muted-foreground", className)}
|
|
128
|
+
{...props}
|
|
129
|
+
/>
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
|
+
FormDescription.displayName = "FormDescription";
|
|
133
|
+
|
|
134
|
+
const FormMessage = React.forwardRef<
|
|
135
|
+
HTMLParagraphElement,
|
|
136
|
+
React.HTMLAttributes<HTMLParagraphElement>
|
|
137
|
+
>(({ className, children, ...props }, ref) => {
|
|
138
|
+
const { error, formMessageId } = useFormField();
|
|
139
|
+
const body = error ? String(error?.message) : children;
|
|
140
|
+
|
|
141
|
+
if (!body) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<p
|
|
147
|
+
ref={ref}
|
|
148
|
+
id={formMessageId}
|
|
149
|
+
className={cn("text-[0.8rem] font-medium text-destructive", className)}
|
|
150
|
+
{...props}
|
|
151
|
+
>
|
|
152
|
+
{body}
|
|
153
|
+
</p>
|
|
154
|
+
);
|
|
155
|
+
});
|
|
156
|
+
FormMessage.displayName = "FormMessage";
|
|
157
|
+
|
|
158
|
+
export {
|
|
159
|
+
useFormField,
|
|
160
|
+
Form,
|
|
161
|
+
FormItem,
|
|
162
|
+
FormLabel,
|
|
163
|
+
FormControl,
|
|
164
|
+
FormDescription,
|
|
165
|
+
FormMessage,
|
|
166
|
+
FormField,
|
|
167
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { cn } from "#utils";
|
|
3
|
+
|
|
4
|
+
interface ImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {
|
|
5
|
+
fallback?: React.ReactNode;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const Image = React.forwardRef<HTMLImageElement, ImageProps>(
|
|
9
|
+
({ className, src, alt, fallback, ...props }, ref) => {
|
|
10
|
+
const [error, setError] = React.useState(false);
|
|
11
|
+
|
|
12
|
+
const handleError = () => {
|
|
13
|
+
setError(true);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
if (error || !src) {
|
|
17
|
+
return (
|
|
18
|
+
<div
|
|
19
|
+
className={cn(
|
|
20
|
+
"flex items-center justify-center bg-muted text-muted-foreground",
|
|
21
|
+
className
|
|
22
|
+
)}
|
|
23
|
+
{...props}
|
|
24
|
+
>
|
|
25
|
+
{fallback || (
|
|
26
|
+
<svg
|
|
27
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
28
|
+
width="24"
|
|
29
|
+
height="24"
|
|
30
|
+
viewBox="0 0 24 24"
|
|
31
|
+
fill="none"
|
|
32
|
+
stroke="currentColor"
|
|
33
|
+
strokeWidth="2"
|
|
34
|
+
strokeLinecap="round"
|
|
35
|
+
strokeLinejoin="round"
|
|
36
|
+
className="h-8 w-8"
|
|
37
|
+
>
|
|
38
|
+
<rect width="18" height="18" x="3" y="3" rx="2" ry="2" />
|
|
39
|
+
<circle cx="9" cy="9" r="2" />
|
|
40
|
+
<path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" />
|
|
41
|
+
</svg>
|
|
42
|
+
)}
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<img ref={ref} src={src} alt={alt} onError={handleError} className={className} {...props} />
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
Image.displayName = "Image";
|
|
54
|
+
|
|
55
|
+
export default Image;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "#utils";
|
|
4
|
+
|
|
5
|
+
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
|
|
6
|
+
({ className, type, ...props }, ref) => {
|
|
7
|
+
return (
|
|
8
|
+
<input
|
|
9
|
+
type={type}
|
|
10
|
+
className={cn(
|
|
11
|
+
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
12
|
+
className
|
|
13
|
+
)}
|
|
14
|
+
ref={ref}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
Input.displayName = "Input";
|
|
21
|
+
|
|
22
|
+
export { Input };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as LabelPrimitive from "@radix-ui/react-label";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "#utils";
|
|
6
|
+
|
|
7
|
+
const labelVariants = cva(
|
|
8
|
+
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
const Label = React.forwardRef<
|
|
12
|
+
React.ElementRef<typeof LabelPrimitive.Root>,
|
|
13
|
+
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants>
|
|
14
|
+
>(({ className, ...props }, ref) => (
|
|
15
|
+
<LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} />
|
|
16
|
+
));
|
|
17
|
+
Label.displayName = LabelPrimitive.Root.displayName;
|
|
18
|
+
|
|
19
|
+
export { Label };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { ChevronLeftIcon, ChevronRightIcon, MoreHorizontalIcon } from "lucide-react";
|
|
2
|
+
import type * as React from "react";
|
|
3
|
+
import { type Button, buttonVariants } from "#components/ui/button";
|
|
4
|
+
import { cn } from "#utils";
|
|
5
|
+
|
|
6
|
+
function Pagination({ className, ...props }: React.ComponentProps<"nav">) {
|
|
7
|
+
return (
|
|
8
|
+
<nav
|
|
9
|
+
role="navigation"
|
|
10
|
+
aria-label="pagination"
|
|
11
|
+
data-slot="pagination"
|
|
12
|
+
className={cn("mx-auto flex w-full justify-center", className)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function PaginationContent({ className, ...props }: React.ComponentProps<"ul">) {
|
|
19
|
+
return (
|
|
20
|
+
<ul
|
|
21
|
+
data-slot="pagination-content"
|
|
22
|
+
className={cn("flex flex-row items-center gap-1", className)}
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function PaginationItem({ ...props }: React.ComponentProps<"li">) {
|
|
29
|
+
return <li data-slot="pagination-item" {...props} />;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type PaginationLinkProps = {
|
|
33
|
+
isActive?: boolean;
|
|
34
|
+
} & Pick<React.ComponentProps<typeof Button>, "size"> &
|
|
35
|
+
React.ComponentProps<"a">;
|
|
36
|
+
|
|
37
|
+
function PaginationLink({ className, isActive, size = "icon", ...props }: PaginationLinkProps) {
|
|
38
|
+
return (
|
|
39
|
+
<a
|
|
40
|
+
aria-current={isActive ? "page" : undefined}
|
|
41
|
+
data-slot="pagination-link"
|
|
42
|
+
data-active={isActive}
|
|
43
|
+
className={cn(
|
|
44
|
+
buttonVariants({
|
|
45
|
+
variant: isActive ? "outline" : "ghost",
|
|
46
|
+
size,
|
|
47
|
+
}),
|
|
48
|
+
className
|
|
49
|
+
)}
|
|
50
|
+
{...props}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function PaginationPrevious({ className, ...props }: React.ComponentProps<typeof PaginationLink>) {
|
|
56
|
+
return (
|
|
57
|
+
<PaginationLink
|
|
58
|
+
aria-label="Go to previous page"
|
|
59
|
+
size="default"
|
|
60
|
+
className={cn("gap-1 px-2.5 sm:pl-2.5", className)}
|
|
61
|
+
{...props}
|
|
62
|
+
>
|
|
63
|
+
<ChevronLeftIcon />
|
|
64
|
+
<span className="hidden sm:block">Previous</span>
|
|
65
|
+
</PaginationLink>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function PaginationNext({ className, ...props }: React.ComponentProps<typeof PaginationLink>) {
|
|
70
|
+
return (
|
|
71
|
+
<PaginationLink
|
|
72
|
+
aria-label="Go to next page"
|
|
73
|
+
size="default"
|
|
74
|
+
className={cn("gap-1 px-2.5 sm:pr-2.5", className)}
|
|
75
|
+
{...props}
|
|
76
|
+
>
|
|
77
|
+
<span className="hidden sm:block">Next</span>
|
|
78
|
+
<ChevronRightIcon />
|
|
79
|
+
</PaginationLink>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function PaginationEllipsis({ className, ...props }: React.ComponentProps<"span">) {
|
|
84
|
+
return (
|
|
85
|
+
<span
|
|
86
|
+
aria-hidden
|
|
87
|
+
data-slot="pagination-ellipsis"
|
|
88
|
+
className={cn("flex size-9 items-center justify-center", className)}
|
|
89
|
+
{...props}
|
|
90
|
+
>
|
|
91
|
+
<MoreHorizontalIcon className="size-4" />
|
|
92
|
+
<span className="sr-only">More pages</span>
|
|
93
|
+
</span>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export {
|
|
98
|
+
Pagination,
|
|
99
|
+
PaginationContent,
|
|
100
|
+
PaginationLink,
|
|
101
|
+
PaginationItem,
|
|
102
|
+
PaginationPrevious,
|
|
103
|
+
PaginationNext,
|
|
104
|
+
PaginationEllipsis,
|
|
105
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as ProgressPrimitive from "@radix-ui/react-progress";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import { cn } from "#utils";
|
|
5
|
+
|
|
6
|
+
const Progress = React.forwardRef<
|
|
7
|
+
React.ElementRef<typeof ProgressPrimitive.Root>,
|
|
8
|
+
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
|
|
9
|
+
>(({ className, value, ...props }, ref) => (
|
|
10
|
+
<ProgressPrimitive.Root
|
|
11
|
+
ref={ref}
|
|
12
|
+
className={cn("relative h-2 w-full overflow-hidden rounded-full bg-primary/20", className)}
|
|
13
|
+
{...props}
|
|
14
|
+
>
|
|
15
|
+
<ProgressPrimitive.Indicator
|
|
16
|
+
className="h-full w-full flex-1 bg-primary transition-all"
|
|
17
|
+
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
|
18
|
+
/>
|
|
19
|
+
</ProgressPrimitive.Root>
|
|
20
|
+
));
|
|
21
|
+
Progress.displayName = ProgressPrimitive.Root.displayName;
|
|
22
|
+
|
|
23
|
+
export { Progress };
|