mirror of
https://github.com/Dokploy/website.git
synced 2026-06-15 20:25:25 +02:00
feat: update navigation and pricing components for improved user experience
- Enhanced the Header component with a new NavigationMenu for better organization of links. - Integrated a ContactFormModal in the EnterpriseLanding component for direct user engagement. - Added an FAQ section in the Pricing component using an Accordion for better information accessibility. - Updated dependencies in package.json and pnpm-lock.yaml for consistency and maintenance.
This commit is contained in:
@@ -11,8 +11,10 @@ import {
|
||||
Shield,
|
||||
Users,
|
||||
} from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { Container } from "./Container";
|
||||
import { ContactFormModal } from "./ContactFormModal";
|
||||
import AnimatedGradientText from "./ui/animated-gradient-text";
|
||||
import AnimatedGridPattern from "./ui/animated-grid-pattern";
|
||||
import { Button } from "./ui/button";
|
||||
@@ -74,8 +76,11 @@ const hostingOptions = [
|
||||
];
|
||||
|
||||
export function EnterpriseLanding() {
|
||||
const [contactOpen, setContactOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="bg-black">
|
||||
<ContactFormModal open={contactOpen} onOpenChange={setContactOpen} />
|
||||
{/* Hero Section */}
|
||||
<div className="relative overflow-hidden bg-background pt-20 pb-16 lg:pt-32">
|
||||
<div className="relative">
|
||||
@@ -147,8 +152,8 @@ export function EnterpriseLanding() {
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3, delay: 0.4 }}
|
||||
>
|
||||
<Button className="rounded-full" asChild>
|
||||
<Link href="/contact">Contact sales</Link>
|
||||
<Button className="rounded-full" onClick={() => setContactOpen(true)}>
|
||||
Contact sales
|
||||
</Button>
|
||||
<Button variant="outline" className="rounded-full" asChild>
|
||||
<Link href="https://docs.dokploy.com" target="_blank">
|
||||
@@ -257,8 +262,8 @@ export function EnterpriseLanding() {
|
||||
Talk to our team about your deployment needs and discover how
|
||||
Dokploy Enterprise can transform your infrastructure.
|
||||
</p>
|
||||
<Button className="mt-8 rounded-full" asChild>
|
||||
<Link href="/contact">Schedule a call with sales</Link>
|
||||
<Button className="mt-8 rounded-full" onClick={() => setContactOpen(true)}>
|
||||
Schedule a call with sales
|
||||
</Button>
|
||||
<p className="mt-6 text-sm text-gray-500">
|
||||
Questions? Email us at{" "}
|
||||
|
||||
@@ -2,16 +2,23 @@
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Popover, Transition } from "@headlessui/react";
|
||||
import { ChevronRight, HeartIcon } from "lucide-react";
|
||||
import { ChevronRight } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { Fragment, type JSX, type SVGProps } from "react";
|
||||
import { Container } from "./Container";
|
||||
import GithubStars from "./GithubStars";
|
||||
import { NavLink } from "./NavLink";
|
||||
import { trackGAEvent } from "./analitycs";
|
||||
import { Logo } from "./shared/Logo";
|
||||
import AnimatedGradientText from "./ui/animated-gradient-text";
|
||||
import { Button, buttonVariants } from "./ui/button";
|
||||
import { Button } from "./ui/button";
|
||||
import {
|
||||
NavigationMenu,
|
||||
NavigationMenuContent,
|
||||
NavigationMenuItem,
|
||||
NavigationMenuLink,
|
||||
NavigationMenuList,
|
||||
NavigationMenuTrigger,
|
||||
navigationMenuTriggerStyle,
|
||||
} from "./ui/navigation-menu";
|
||||
|
||||
function MobileNavLink({
|
||||
href,
|
||||
@@ -65,24 +72,6 @@ function MobileNavIcon({ open }: { open: boolean }) {
|
||||
);
|
||||
}
|
||||
|
||||
const I18nIcon = (props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={24}
|
||||
height={24}
|
||||
fill="currentColor"
|
||||
stroke="currentColor"
|
||||
strokeWidth={0}
|
||||
viewBox="0 0 512 512"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
stroke="none"
|
||||
d="m478.33 433.6-90-218a22 22 0 0 0-40.67 0l-90 218a22 22 0 1 0 40.67 16.79L316.66 406h102.67l18.33 44.39A22 22 0 0 0 458 464a22 22 0 0 0 20.32-30.4zM334.83 362 368 281.65 401.17 362zm-66.99-19.08a22 22 0 0 0-4.89-30.7c-.2-.15-15-11.13-36.49-34.73 39.65-53.68 62.11-114.75 71.27-143.49H330a22 22 0 0 0 0-44H214V70a22 22 0 0 0-44 0v20H54a22 22 0 0 0 0 44h197.25c-9.52 26.95-27.05 69.5-53.79 108.36-31.41-41.68-43.08-68.65-43.17-68.87a22 22 0 0 0-40.58 17c.58 1.38 14.55 34.23 52.86 83.93.92 1.19 1.83 2.35 2.74 3.51-39.24 44.35-77.74 71.86-93.85 80.74a22 22 0 1 0 21.07 38.63c2.16-1.18 48.6-26.89 101.63-85.59 22.52 24.08 38 35.44 38.93 36.1a22 22 0 0 0 30.75-4.9z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
function MobileNavigation() {
|
||||
return (
|
||||
<Popover>
|
||||
@@ -116,33 +105,41 @@ function MobileNavigation() {
|
||||
>
|
||||
<Popover.Panel
|
||||
as="div"
|
||||
className="absolute inset-x-0 top-full mt-4 flex origin-top flex-col rounded-2xl border border-border bg-background p-4 text-lg tracking-tight text-primary shadow-xl ring-1 ring-border/5"
|
||||
className="absolute inset-x-0 top-full mt-4 flex origin-top flex-col rounded-2xl border border-border bg-background p-4 text-lg tracking-tight text-primary shadow-xl ring-1 ring-border/5"
|
||||
>
|
||||
<MobileNavLink href="/#features">Features</MobileNavLink>
|
||||
<MobileNavLink href="/pricing">Pricing</MobileNavLink>
|
||||
<MobileNavLink href="/#faqs">FAQ</MobileNavLink>
|
||||
<hr className="m-2 border-border" />
|
||||
<p className="px-2 py-1 text-xs font-semibold uppercase text-muted-foreground">
|
||||
Solutions
|
||||
</p>
|
||||
<MobileNavLink href="/enterprise">Enterprise</MobileNavLink>
|
||||
<MobileNavLink href="/partners">Partners</MobileNavLink>
|
||||
<hr className="m-2 border-border" />
|
||||
<MobileNavLink
|
||||
href="https://docs.dokploy.com/docs/core"
|
||||
target="_blank"
|
||||
>
|
||||
Docs
|
||||
</MobileNavLink>
|
||||
<hr className="m-2 border-border" />
|
||||
<p className="px-2 py-1 text-xs font-semibold uppercase text-muted-foreground">
|
||||
Resources
|
||||
</p>
|
||||
<MobileNavLink href="https://templates.dokploy.com" target="_blank">Templates</MobileNavLink>
|
||||
<MobileNavLink href="/blog">Blog</MobileNavLink>
|
||||
<MobileNavLink href="/#faqs">FAQ</MobileNavLink>
|
||||
<hr className="m-2 border-border" />
|
||||
<MobileNavLink href="/contact">Contact</MobileNavLink>
|
||||
<MobileNavLink
|
||||
href="https://docs.dokploy.com/docs/core"
|
||||
href="https://app.dokploy.com/register"
|
||||
target="_blank"
|
||||
>
|
||||
<Button className=" w-full" asChild>
|
||||
<Link
|
||||
href="https://app.dokploy.com/register"
|
||||
aria-label="Sign In Dokploy Cloud"
|
||||
target="_blank"
|
||||
>
|
||||
<div className="group relative mx-auto flex w-full max-w-fit flex-row items-center justify-center rounded-2xl text-sm font-medium">
|
||||
<span>Sign In</span>
|
||||
<ChevronRight className="ml-1 size-3 transition-transform duration-300 ease-in-out group-hover:translate-x-0.5" />
|
||||
</div>
|
||||
</Link>
|
||||
<Button className="w-full" asChild>
|
||||
<div className="group relative mx-auto flex w-full max-w-fit flex-row items-center justify-center rounded-2xl text-sm font-medium">
|
||||
<span>Sign In</span>
|
||||
<ChevronRight className="ml-1 size-3 transition-transform duration-300 ease-in-out group-hover:translate-x-0.5" />
|
||||
</div>
|
||||
</Button>
|
||||
</MobileNavLink>
|
||||
</Popover.Panel>
|
||||
@@ -152,6 +149,49 @@ function MobileNavigation() {
|
||||
);
|
||||
}
|
||||
|
||||
function ListItem({
|
||||
className,
|
||||
title,
|
||||
href,
|
||||
target,
|
||||
children,
|
||||
}: {
|
||||
className?: string;
|
||||
title: string;
|
||||
href: string;
|
||||
target?: string;
|
||||
children?: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<li>
|
||||
<NavigationMenuLink asChild>
|
||||
<Link
|
||||
href={href}
|
||||
target={target}
|
||||
onClick={() =>
|
||||
trackGAEvent({
|
||||
action: "Nav Link Clicked",
|
||||
category: "Navigation",
|
||||
label: href,
|
||||
})
|
||||
}
|
||||
className={cn(
|
||||
"block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className="text-sm font-medium leading-none">{title}</div>
|
||||
{children && (
|
||||
<p className="line-clamp-2 text-sm leading-snug text-muted-foreground">
|
||||
{children}
|
||||
</p>
|
||||
)}
|
||||
</Link>
|
||||
</NavigationMenuLink>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
export function Header() {
|
||||
return (
|
||||
<header className="sticky top-0 z-50 border-b border-border/40 bg-background/95 py-5 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
||||
@@ -161,16 +201,102 @@ export function Header() {
|
||||
<Link href="/" aria-label="Home">
|
||||
<Logo className="h-10 w-auto" />
|
||||
</Link>
|
||||
<div className="hidden md:flex md:gap-x-6">
|
||||
<NavLink href="/pricing">Pricing</NavLink>
|
||||
<NavLink href="/#faqs">FAQ</NavLink>
|
||||
<NavLink
|
||||
href="https://docs.dokploy.com/docs/core"
|
||||
target="_blank"
|
||||
>
|
||||
Docs
|
||||
</NavLink>
|
||||
<NavLink href="/blog">Blog</NavLink>
|
||||
<div className="hidden md:flex">
|
||||
<NavigationMenu>
|
||||
<NavigationMenuList>
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuLink
|
||||
asChild
|
||||
className={navigationMenuTriggerStyle()}
|
||||
>
|
||||
<Link
|
||||
href="/#features"
|
||||
onClick={() =>
|
||||
trackGAEvent({
|
||||
action: "Nav Link Clicked",
|
||||
category: "Navigation",
|
||||
label: "/#features",
|
||||
})
|
||||
}
|
||||
>
|
||||
Features
|
||||
</Link>
|
||||
</NavigationMenuLink>
|
||||
</NavigationMenuItem>
|
||||
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuLink
|
||||
asChild
|
||||
className={navigationMenuTriggerStyle()}
|
||||
>
|
||||
<Link
|
||||
href="/pricing"
|
||||
onClick={() =>
|
||||
trackGAEvent({
|
||||
action: "Nav Link Clicked",
|
||||
category: "Navigation",
|
||||
label: "/pricing",
|
||||
})
|
||||
}
|
||||
>
|
||||
Pricing
|
||||
</Link>
|
||||
</NavigationMenuLink>
|
||||
</NavigationMenuItem>
|
||||
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuTrigger>Solutions</NavigationMenuTrigger>
|
||||
<NavigationMenuContent>
|
||||
<ul className="grid w-[200px] gap-1 p-2">
|
||||
<ListItem href="/enterprise" title="Enterprise">
|
||||
Enterprise-grade deployment platform
|
||||
</ListItem>
|
||||
<ListItem href="/partners" title="Partners">
|
||||
Partner program and integrations
|
||||
</ListItem>
|
||||
</ul>
|
||||
</NavigationMenuContent>
|
||||
</NavigationMenuItem>
|
||||
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuLink
|
||||
asChild
|
||||
className={navigationMenuTriggerStyle()}
|
||||
>
|
||||
<Link
|
||||
href="https://docs.dokploy.com/docs/core"
|
||||
target="_blank"
|
||||
onClick={() =>
|
||||
trackGAEvent({
|
||||
action: "Nav Link Clicked",
|
||||
category: "Navigation",
|
||||
label: "https://docs.dokploy.com/docs/core",
|
||||
})
|
||||
}
|
||||
>
|
||||
Docs
|
||||
</Link>
|
||||
</NavigationMenuLink>
|
||||
</NavigationMenuItem>
|
||||
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuTrigger>Resources</NavigationMenuTrigger>
|
||||
<NavigationMenuContent>
|
||||
<ul className="grid w-[200px] gap-1 p-2">
|
||||
<ListItem href="https://templates.dokploy.com" target="_blank" title="Templates">
|
||||
Ready-to-deploy templates
|
||||
</ListItem>
|
||||
<ListItem href="/blog" title="Blog">
|
||||
Latest news and updates
|
||||
</ListItem>
|
||||
<ListItem href="/#faqs" title="FAQ">
|
||||
Frequently asked questions
|
||||
</ListItem>
|
||||
</ul>
|
||||
</NavigationMenuContent>
|
||||
</NavigationMenuItem>
|
||||
</NavigationMenuList>
|
||||
</NavigationMenu>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-x-4 md:gap-x-5">
|
||||
@@ -208,20 +334,6 @@ export function Header() {
|
||||
</Link>
|
||||
</Button>
|
||||
|
||||
{/* <Link
|
||||
className={buttonVariants({
|
||||
variant: "outline",
|
||||
className: " flex items-center gap-2 !rounded-full",
|
||||
})}
|
||||
href="https://opencollective.com/dokploy"
|
||||
target="_blank"
|
||||
>
|
||||
<span className="text-sm font-semibold">
|
||||
{t("navigation.support")}{" "}
|
||||
</span>
|
||||
<HeartIcon className="animate-heartbeat size-4 fill-red-600 text-red-500 " />
|
||||
</Link> */}
|
||||
|
||||
<Button className="rounded-full max-md:hidden" asChild>
|
||||
<Link
|
||||
href="https://app.dokploy.com/register"
|
||||
|
||||
@@ -6,6 +6,12 @@ import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { Container } from "./Container";
|
||||
import { ContactFormModal } from "./ContactFormModal";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "./ui/accordion";
|
||||
import { Badge } from "./ui/badge";
|
||||
import { Button, buttonVariants } from "./ui/button";
|
||||
import { Tabs, TabsList, TabsTrigger } from "./ui/tabs";
|
||||
@@ -15,6 +21,49 @@ import { cn } from "@/lib/utils";
|
||||
|
||||
const CLOUD_APP_URL = "https://app.dokploy.com";
|
||||
|
||||
const pricingFaqs = [
|
||||
{
|
||||
question: "What happens if I need more than one server?",
|
||||
answer:
|
||||
"You can add as many servers as you need. Each additional server costs $4.50/month on the Hobby plan. On the Startup plan, 3 servers are included in the base price, and you can add more at $4.50/month each.",
|
||||
},
|
||||
{
|
||||
question: "How does the annual billing discount work?",
|
||||
answer:
|
||||
"When you choose annual billing, you get a 20% discount on all plans. For example, the Hobby plan goes from $4.50/month to $3.60/month per server, billed annually.",
|
||||
},
|
||||
{
|
||||
question: "Can I switch between plans?",
|
||||
answer:
|
||||
"Yes, you can upgrade or downgrade your plan at any time. When upgrading, you'll be prorated for the remainder of your billing cycle. When downgrading, the change takes effect at the start of your next billing cycle.",
|
||||
},
|
||||
{
|
||||
question: "Is there a limit on the number of deployments?",
|
||||
answer:
|
||||
"No, there is no limit on the number of deployments in any of the plans. You can deploy unlimited applications and databases.",
|
||||
},
|
||||
{
|
||||
question: "What's included in the Enterprise plan?",
|
||||
answer:
|
||||
"The Enterprise plan includes unlimited servers and organizations, fine-grained RBAC, SSO/SAML integration (Azure, OKTA, etc.), audit logs, MSA/SLA, white labeling, and priority support. It's available as both Cloud and Self-Hosted.",
|
||||
},
|
||||
{
|
||||
question: "Do you offer refunds?",
|
||||
answer:
|
||||
"We do not offer refunds. However, you can cancel your subscription at any time. Feel free to try our open-source version for free before making a purchase.",
|
||||
},
|
||||
{
|
||||
question: "What kind of support do I get with each plan?",
|
||||
answer:
|
||||
"The Hobby plan includes community support via Discord. The Startup plan adds email and chat support. The Enterprise plan includes priority support and dedicated services.",
|
||||
},
|
||||
{
|
||||
question: "Do I need to provide my own server?",
|
||||
answer:
|
||||
"Yes, you provide your own server (e.g., Hetzner, Hostinger, AWS, etc.) VPS, and we manage the Dokploy UI infrastructure for you.",
|
||||
},
|
||||
];
|
||||
|
||||
function SwirlyDoodle(props: React.ComponentPropsWithoutRef<"svg">) {
|
||||
return (
|
||||
<svg
|
||||
@@ -319,6 +368,30 @@ export function Pricing() {
|
||||
<PricingFeatureTable />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Pricing FAQ */}
|
||||
<div className="mx-auto mt-24 max-w-3xl">
|
||||
<h3 className="text-center text-2xl font-semibold text-white">
|
||||
Frequently asked questions
|
||||
</h3>
|
||||
<p className="mt-4 text-center text-sm text-muted-foreground">
|
||||
Have a different question? Contact us via Discord or email.
|
||||
</p>
|
||||
<Accordion
|
||||
type="single"
|
||||
collapsible
|
||||
className="mt-8 w-full"
|
||||
>
|
||||
{pricingFaqs.map((faq, index) => (
|
||||
<AccordionItem value={`${index}`} key={index}>
|
||||
<AccordionTrigger className="text-left">
|
||||
{faq.question}
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>{faq.answer}</AccordionContent>
|
||||
</AccordionItem>
|
||||
))}
|
||||
</Accordion>
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
<ContactFormModal
|
||||
|
||||
137
apps/website/components/ui/navigation-menu.tsx
Normal file
137
apps/website/components/ui/navigation-menu.tsx
Normal file
@@ -0,0 +1,137 @@
|
||||
import * as React from "react"
|
||||
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
|
||||
import { cva } from "class-variance-authority"
|
||||
import { ChevronDown } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const NavigationMenu = React.forwardRef<
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<NavigationMenuPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative z-10 flex max-w-max flex-1 items-center justify-center",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</NavigationMenuPrimitive.Root>
|
||||
))
|
||||
NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName
|
||||
|
||||
const NavigationMenuList = React.forwardRef<
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.List>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<NavigationMenuPrimitive.List
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"group flex flex-1 list-none items-center justify-center space-x-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName
|
||||
|
||||
const NavigationMenuItem = React.forwardRef<
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Item>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<NavigationMenuPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn("relative", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
NavigationMenuItem.displayName = NavigationMenuPrimitive.Item.displayName
|
||||
|
||||
const navigationMenuTriggerStyle = cva(
|
||||
"group inline-flex h-10 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[state=open]:text-accent-foreground data-[state=open]:bg-accent/50 data-[state=open]:hover:bg-accent data-[state=open]:focus:bg-accent"
|
||||
)
|
||||
|
||||
const NavigationMenuTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<NavigationMenuPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(navigationMenuTriggerStyle(), "group", className)}
|
||||
{...props}
|
||||
>
|
||||
{children}{" "}
|
||||
<ChevronDown
|
||||
className="relative top-[1px] ml-1 h-3 w-3 transition duration-200 group-data-[state=open]:rotate-180"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</NavigationMenuPrimitive.Trigger>
|
||||
))
|
||||
NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName
|
||||
|
||||
const NavigationMenuContent = React.forwardRef<
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<NavigationMenuPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"absolute left-0 top-full mt-1.5 w-auto overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0 data-[state=open]:zoom-in-95 data-[state=closed]:zoom-out-95",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName
|
||||
|
||||
const NavigationMenuLink = NavigationMenuPrimitive.Link
|
||||
|
||||
const NavigationMenuViewport = React.forwardRef<
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Viewport>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div className="absolute left-1/2 top-full -translate-x-1/2 flex justify-center">
|
||||
<NavigationMenuPrimitive.Viewport
|
||||
className={cn(
|
||||
"origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
NavigationMenuViewport.displayName =
|
||||
NavigationMenuPrimitive.Viewport.displayName
|
||||
|
||||
const NavigationMenuIndicator = React.forwardRef<
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Indicator>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Indicator>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<NavigationMenuPrimitive.Indicator
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />
|
||||
</NavigationMenuPrimitive.Indicator>
|
||||
))
|
||||
NavigationMenuIndicator.displayName =
|
||||
NavigationMenuPrimitive.Indicator.displayName
|
||||
|
||||
export {
|
||||
navigationMenuTriggerStyle,
|
||||
NavigationMenu,
|
||||
NavigationMenuList,
|
||||
NavigationMenuItem,
|
||||
NavigationMenuContent,
|
||||
NavigationMenuTrigger,
|
||||
NavigationMenuLink,
|
||||
NavigationMenuIndicator,
|
||||
NavigationMenuViewport,
|
||||
}
|
||||
@@ -12,12 +12,14 @@
|
||||
},
|
||||
"browserslist": "defaults, not ie <= 11",
|
||||
"dependencies": {
|
||||
"@next/third-parties": "16.0.7",
|
||||
"hast-util-to-jsx-runtime": "^2.3.5",
|
||||
"@headlessui/react": "^2.2.0",
|
||||
"@headlessui/tailwindcss": "^0.2.0",
|
||||
"@next/third-parties": "16.0.7",
|
||||
"@prettier/plugin-xml": "^3.4.1",
|
||||
"@radix-ui/react-accordion": "^1.2.1",
|
||||
"@radix-ui/react-avatar": "^1.1.1",
|
||||
"@radix-ui/react-dialog": "1.1.15",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.14",
|
||||
"@radix-ui/react-scroll-area": "^1.2.0",
|
||||
"@radix-ui/react-select": "^2.0.0",
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
@@ -26,17 +28,17 @@
|
||||
"@radix-ui/react-tooltip": "^1.1.3",
|
||||
"@tabler/icons-react": "3.21.0",
|
||||
"@tryghost/content-api": "^1.11.21",
|
||||
"@radix-ui/react-dialog":"1.1.15",
|
||||
"@types/node": "20.4.6",
|
||||
"@types/turndown": "^5.0.5",
|
||||
"autoprefixer": "^10.4.12",
|
||||
"axios": "^1.8.1",
|
||||
"resend": "6.5.2",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.0",
|
||||
"framer-motion": "^11.3.19",
|
||||
"hast-util-to-jsx-runtime": "^2.3.5",
|
||||
"lucide-react": "0.364.0",
|
||||
"next": "16.1.5",
|
||||
"prettier": "^3.3.3",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"react-ga4": "^2.1.0",
|
||||
@@ -45,6 +47,7 @@
|
||||
"rehype-raw": "^7.0.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"remark-toc": "^9.0.0",
|
||||
"resend": "6.5.2",
|
||||
"satori": "^0.12.1",
|
||||
"sharp": "^0.33.5",
|
||||
"shiki": "^3.20.0",
|
||||
@@ -54,9 +57,7 @@
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"turndown": "^7.2.0",
|
||||
"turndown-plugin-gfm": "^1.0.2",
|
||||
"typescript": "5.1.6",
|
||||
"prettier": "^3.3.3",
|
||||
"@prettier/plugin-xml": "^3.4.1"
|
||||
"typescript": "5.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.26.9",
|
||||
|
||||
11
pnpm-lock.yaml
generated
11
pnpm-lock.yaml
generated
@@ -32,7 +32,7 @@ importers:
|
||||
dependencies:
|
||||
'@next/third-parties':
|
||||
specifier: 16.0.7
|
||||
version: 16.0.7(next@16.1.5(@babel/core@7.26.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1)
|
||||
version: 16.0.7(next@16.1.5(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1)
|
||||
'@radix-ui/react-dropdown-menu':
|
||||
specifier: ^2.1.16
|
||||
version: 2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
|
||||
@@ -111,7 +111,7 @@ importers:
|
||||
version: 0.2.1(tailwindcss@3.4.7)
|
||||
'@next/third-parties':
|
||||
specifier: 16.0.7
|
||||
version: 16.0.7(next@16.1.5(@babel/core@7.26.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1)
|
||||
version: 16.0.7(next@16.1.5(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1)
|
||||
'@prettier/plugin-xml':
|
||||
specifier: ^3.4.1
|
||||
version: 3.4.1(prettier@3.3.3)
|
||||
@@ -124,6 +124,9 @@ importers:
|
||||
'@radix-ui/react-dialog':
|
||||
specifier: 1.1.15
|
||||
version: 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
|
||||
'@radix-ui/react-navigation-menu':
|
||||
specifier: ^1.2.14
|
||||
version: 1.2.14(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
|
||||
'@radix-ui/react-scroll-area':
|
||||
specifier: ^1.2.0
|
||||
version: 1.2.0(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
|
||||
@@ -2916,6 +2919,7 @@ packages:
|
||||
git-raw-commits@4.0.0:
|
||||
resolution: {integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==}
|
||||
engines: {node: '>=16'}
|
||||
deprecated: This package is no longer maintained. For the JavaScript API, please use @conventional-changelog/git-client instead.
|
||||
hasBin: true
|
||||
|
||||
github-slugger@2.0.0:
|
||||
@@ -2931,6 +2935,7 @@ packages:
|
||||
|
||||
glob@10.4.5:
|
||||
resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
|
||||
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
|
||||
hasBin: true
|
||||
|
||||
global-directory@4.0.1:
|
||||
@@ -5160,7 +5165,7 @@ snapshots:
|
||||
'@next/swc-win32-x64-msvc@16.1.5':
|
||||
optional: true
|
||||
|
||||
'@next/third-parties@16.0.7(next@16.1.5(@babel/core@7.26.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1)':
|
||||
'@next/third-parties@16.0.7(next@16.1.5(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1)':
|
||||
dependencies:
|
||||
next: 16.1.5(@babel/core@7.26.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
|
||||
react: 19.2.1
|
||||
|
||||
Reference in New Issue
Block a user