Compare commits
1 Commits
8db9fde31b
...
62ffc14ced
Author | SHA1 | Date | |
---|---|---|---|
|
62ffc14ced |
@ -14,6 +14,7 @@
|
|||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@heroicons/react": "^2.1.5",
|
||||||
"@radix-ui/react-collapsible": "^1.1.1",
|
"@radix-ui/react-collapsible": "^1.1.1",
|
||||||
"@radix-ui/react-dialog": "^1.1.2",
|
"@radix-ui/react-dialog": "^1.1.2",
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
|
@ -3,10 +3,10 @@
|
|||||||
import { ReactElement, useEffect, useState } from "react";
|
import { ReactElement, useEffect, useState } from "react";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/24/outline";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { DateTime } from "luxon";
|
import { DateTime } from "luxon";
|
||||||
import SimpleTooltip from "@/components/simple-tooltip";
|
import SimpleTooltip from "@/components/simple-tooltip";
|
||||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
||||||
|
|
||||||
const DocsFooter = ({
|
const DocsFooter = ({
|
||||||
pages,
|
pages,
|
||||||
@ -53,7 +53,7 @@ const DocsFooter = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Pages */}
|
{/* Pages */}
|
||||||
<Separator className="my-4 bg-separator-gradient" />
|
<Separator className="my-4" />
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
{/* Previous */}
|
{/* Previous */}
|
||||||
{previous && (
|
{previous && (
|
||||||
@ -62,7 +62,7 @@ const DocsFooter = ({
|
|||||||
href={`/${previous.slug}` || "#"}
|
href={`/${previous.slug}` || "#"}
|
||||||
draggable={false}
|
draggable={false}
|
||||||
>
|
>
|
||||||
<ChevronLeft className="pb-1 w-4 h-4 group-hover:-translate-x-0.5 transition-all transform-gpu" />
|
<ChevronLeftIcon className="pb-1 w-4 h-4 group-hover:-translate-x-0.5 transition-all transform-gpu" />
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<h1 className="text-sm opacity-75">Previous</h1>
|
<h1 className="text-sm opacity-75">Previous</h1>
|
||||||
<p>{previous.title}</p>
|
<p>{previous.title}</p>
|
||||||
@ -81,7 +81,7 @@ const DocsFooter = ({
|
|||||||
<h1 className="text-sm opacity-75">Next</h1>
|
<h1 className="text-sm opacity-75">Next</h1>
|
||||||
<p>{next.title}</p>
|
<p>{next.title}</p>
|
||||||
</div>
|
</div>
|
||||||
<ChevronRight className="pb-1 w-4 h-4 group-hover:translate-x-0.5 transition-all transform-gpu" />
|
<ChevronRightIcon className="pb-1 w-4 h-4 group-hover:translate-x-0.5 transition-all transform-gpu" />
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,8 +4,11 @@ import { ReactElement, ReactNode } from "react";
|
|||||||
import AnimatedGridPattern from "@/components/ui/animated-grid-pattern";
|
import AnimatedGridPattern from "@/components/ui/animated-grid-pattern";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
import {
|
||||||
|
ArrowTopRightOnSquareIcon,
|
||||||
|
EnvelopeIcon,
|
||||||
|
} from "@heroicons/react/24/outline";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { ExternalLink, Mail } from "lucide-react";
|
|
||||||
|
|
||||||
const links = {
|
const links = {
|
||||||
Resources: [
|
Resources: [
|
||||||
@ -66,7 +69,9 @@ const Footer = (): ReactElement => (
|
|||||||
/>
|
/>
|
||||||
<SocialLink
|
<SocialLink
|
||||||
name="Email"
|
name="Email"
|
||||||
logo={<Mail className="opacity-95 w-6 h-6" />}
|
logo={
|
||||||
|
<EnvelopeIcon className="opacity-95 w-6 h-6" />
|
||||||
|
}
|
||||||
href="mailto:support@pulseapp.cc"
|
href="mailto:support@pulseapp.cc"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -182,7 +187,7 @@ const FooterLink = ({
|
|||||||
{name}
|
{name}
|
||||||
</span>
|
</span>
|
||||||
{shortName && <span className="flex sm:hidden">{shortName}</span>}
|
{shortName && <span className="flex sm:hidden">{shortName}</span>}
|
||||||
{external && <ExternalLink className="w-3.5 h-3.5" />}
|
{external && <ArrowTopRightOnSquareIcon className="w-3.5 h-3.5" />}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -9,10 +9,10 @@ import {
|
|||||||
CommandItem,
|
CommandItem,
|
||||||
CommandList,
|
CommandList,
|
||||||
} from "@/components/ui/command";
|
} from "@/components/ui/command";
|
||||||
|
import { MagnifyingGlassIcon } from "@heroicons/react/24/outline";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime";
|
import { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime";
|
||||||
import { Search } from "lucide-react";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The dialog for quickly searching the docs.
|
* The dialog for quickly searching the docs.
|
||||||
@ -53,7 +53,7 @@ const QuickSearchDialog = ({
|
|||||||
onClick={() => setOpen(true)}
|
onClick={() => setOpen(true)}
|
||||||
>
|
>
|
||||||
<div className="absolute top-2.5 left-3 z-10">
|
<div className="absolute top-2.5 left-3 z-10">
|
||||||
<Search className="w-[1.15rem] h-[1.15rem]" />
|
<MagnifyingGlassIcon className="w-[1.15rem] h-[1.15rem]" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { ReactElement, useEffect, useRef, useState } from "react";
|
import { ReactElement, useEffect, useRef, useState } from "react";
|
||||||
|
import {
|
||||||
|
ArrowLongRightIcon,
|
||||||
|
ArrowLongUpIcon,
|
||||||
|
Bars3CenterLeftIcon,
|
||||||
|
} from "@heroicons/react/24/outline";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { truncateText } from "@/lib/string";
|
import { truncateText } from "@/lib/string";
|
||||||
import { motion, useInView } from "framer-motion";
|
import { motion, useInView } from "framer-motion";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { AlignLeftIcon, ArrowUpFromDot, MoveRight } from "lucide-react";
|
|
||||||
|
|
||||||
type Header = {
|
type Header = {
|
||||||
id: string;
|
id: string;
|
||||||
@ -86,7 +90,7 @@ const OnThisPage = ({ page }: { page: DocsContentMetadata }): ReactElement => {
|
|||||||
>
|
>
|
||||||
{/* Title */}
|
{/* Title */}
|
||||||
<div className="flex gap-2.5 items-center">
|
<div className="flex gap-2.5 items-center">
|
||||||
<AlignLeftIcon className="w-5 h-5" />
|
<Bars3CenterLeftIcon className="w-5 h-5" />
|
||||||
<h1>On This Page</h1>
|
<h1>On This Page</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -153,7 +157,7 @@ const Footer = ({ page }: { page: DocsContentMetadata }): ReactElement => {
|
|||||||
draggable={false}
|
draggable={false}
|
||||||
>
|
>
|
||||||
<span>Edit this page on GitHub</span>
|
<span>Edit this page on GitHub</span>
|
||||||
<MoveRight className="w-4 h-4 group-hover:translate-x-px transition-all transform-gpu" />
|
<ArrowLongRightIcon className="w-4 h-4 group-hover:translate-x-0.5 transition-all transform-gpu" />
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{/* Scroll to Top */}
|
{/* Scroll to Top */}
|
||||||
@ -173,7 +177,7 @@ const Footer = ({ page }: { page: DocsContentMetadata }): ReactElement => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<span>Scroll to Top</span>
|
<span>Scroll to Top</span>
|
||||||
<ArrowUpFromDot className="w-4 h-4 group-hover:-translate-y-px transition-all transform-gpu" />
|
<ArrowLongUpIcon className="w-4 h-4 group-hover:translate-x-0.5 transition-all transform-gpu" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
@ -11,7 +11,7 @@ import Link from "next/link";
|
|||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { AnimatePresence, motion } from "framer-motion";
|
import { AnimatePresence, motion } from "framer-motion";
|
||||||
import { ChevronRight } from "lucide-react";
|
import { ChevronRightIcon } from "@heroicons/react/24/outline";
|
||||||
|
|
||||||
const SidebarLinks = ({
|
const SidebarLinks = ({
|
||||||
pages,
|
pages,
|
||||||
@ -51,15 +51,14 @@ const CategoryItem = ({
|
|||||||
const hasChildren = Object.keys(node.children).length > 0;
|
const hasChildren = Object.keys(node.children).length > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn(`relative select-none`, depth > 0 && "ml-2.5")}>
|
<div className={`relative ${depth > 0 ? "ml-2.5" : ""} select-none`}>
|
||||||
{/* Indentation */}
|
{/* Indentation */}
|
||||||
{depth > 0 && (
|
{depth > 0 && (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={`absolute left-0 top-1 bottom-0 border-l-2 border-muted`}
|
||||||
"absolute left-0 bottom-0 border-l border-muted",
|
style={{
|
||||||
isLast ? "h-[32px]" : "h-[100%]",
|
height: isLast ? "30px" : "100%",
|
||||||
active && "border-primary"
|
}}
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -72,10 +71,9 @@ const CategoryItem = ({
|
|||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
className={cn(
|
className={cn(
|
||||||
`relative w-full px-1.5 h-8 text-base justify-between hover:bg-accent/20`,
|
`relative px-1.5 ${depth > 0 ? "pl-4" : ""} w-full justify-between`,
|
||||||
depth > 0 && "pl-4",
|
|
||||||
active &&
|
active &&
|
||||||
"text-primary/95 font-bold hover:text-primary"
|
"bg-primary/15 hover:bg-primary/20 text-primary/95 hover:text-primary"
|
||||||
)}
|
)}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
>
|
>
|
||||||
@ -86,7 +84,7 @@ const CategoryItem = ({
|
|||||||
animate={{ rotate: isOpen ? 90 : 180 }}
|
animate={{ rotate: isOpen ? 90 : 180 }}
|
||||||
transition={{ duration: 0.2 }}
|
transition={{ duration: 0.2 }}
|
||||||
>
|
>
|
||||||
<ChevronRight className="w-4 h-4" />
|
<ChevronRightIcon className="w-4 h-4" />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
@ -136,7 +134,7 @@ const CategoryItem = ({
|
|||||||
const buildTree = (pages: DocsContentMetadata[]): Record<string, TreeNode> => {
|
const buildTree = (pages: DocsContentMetadata[]): Record<string, TreeNode> => {
|
||||||
const tree: Record<string, TreeNode> = {};
|
const tree: Record<string, TreeNode> = {};
|
||||||
|
|
||||||
pages.forEach((page: DocsContentMetadata) => {
|
pages.forEach((page) => {
|
||||||
const parts: string[] | undefined = page.slug?.split("/");
|
const parts: string[] | undefined = page.slug?.split("/");
|
||||||
let currentLevel = tree;
|
let currentLevel = tree;
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import SidebarLinks from "@/components/sidebar/sidebar-links";
|
|||||||
import ThemeSwitcher from "@/components/theme-switcher";
|
import ThemeSwitcher from "@/components/theme-switcher";
|
||||||
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
|
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
|
||||||
import QuickSearchDialog from "@/components/navbar/search-dialog";
|
import QuickSearchDialog from "@/components/navbar/search-dialog";
|
||||||
import { AlignRightIcon } from "lucide-react";
|
import { Bars3BottomRightIcon } from "@heroicons/react/24/outline";
|
||||||
|
|
||||||
const Sidebar = (): ReactElement => (
|
const Sidebar = (): ReactElement => (
|
||||||
<>
|
<>
|
||||||
@ -13,7 +13,7 @@ const Sidebar = (): ReactElement => (
|
|||||||
<div className="xs:hidden">
|
<div className="xs:hidden">
|
||||||
<Sheet>
|
<Sheet>
|
||||||
<SheetTrigger className="flex items-center">
|
<SheetTrigger className="flex items-center">
|
||||||
<AlignRightIcon className="w-6 h-6" />
|
<Bars3BottomRightIcon className="w-6 h-6" />
|
||||||
</SheetTrigger>
|
</SheetTrigger>
|
||||||
<SheetContent className="h-full px-5 pt-11" side="right">
|
<SheetContent className="h-full px-5 pt-11" side="right">
|
||||||
<SidebarContent />
|
<SidebarContent />
|
||||||
@ -41,8 +41,8 @@ const SidebarContent = (): ReactElement => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Theme Switcher */}
|
{/* Theme Switcher */}
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col">
|
||||||
<Separator className="mb-3 bg-separator-gradient" />
|
<Separator className="mb-3" />
|
||||||
<ThemeSwitcher />
|
<ThemeSwitcher />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,17 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { ReactElement, useEffect, useState } from "react";
|
import { ReactElement, useEffect, useState } from "react";
|
||||||
|
import { MoonStar, Sun } from "lucide-react";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
import { UseThemeProps } from "next-themes/dist/types";
|
|
||||||
import { Monitor, MoonStar, Sun } from "lucide-react";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
const themes = {
|
import { UseThemeProps } from "next-themes/dist/types";
|
||||||
dark: <MoonStar className="w-4 h-4" />,
|
import { capitalizeWords } from "@/lib/string";
|
||||||
light: <Sun className="w-4 h-4" />,
|
|
||||||
system: <Monitor className="w-4 h-4" />,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The theme switcher component.
|
* The theme switcher component.
|
||||||
@ -20,32 +15,47 @@ const themes = {
|
|||||||
*/
|
*/
|
||||||
const ThemeSwitcher = (): ReactElement => {
|
const ThemeSwitcher = (): ReactElement => {
|
||||||
const [mounted, setMounted] = useState(false);
|
const [mounted, setMounted] = useState(false);
|
||||||
const { theme: activeTheme, setTheme }: UseThemeProps = useTheme();
|
const { theme, setTheme }: UseThemeProps = useTheme();
|
||||||
|
const isLight = theme === "light";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMounted(true);
|
setMounted(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return mounted ? (
|
||||||
<div className="w-fit p-1 flex gap-1.5 bg-black/30 ring-1 ring-white/5 rounded-full">
|
|
||||||
{Object.entries(themes).map(([theme, icon]) => {
|
|
||||||
const active: boolean = mounted && theme === activeTheme;
|
|
||||||
return (
|
|
||||||
<Button
|
<Button
|
||||||
key={theme}
|
className="p-1.5 flex gap-7 justify-start items-center hover:opacity-85 select-none"
|
||||||
className={cn(
|
|
||||||
"p-1 h-6 opacity-80 rounded-full",
|
|
||||||
active &&
|
|
||||||
"ring-1 bg-zinc-900 ring-white/15 opacity-100"
|
|
||||||
)}
|
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={() => setTheme(theme)}
|
onClick={() => setTheme(isLight ? "dark" : "light")}
|
||||||
>
|
>
|
||||||
{icon}
|
<div className="relative flex items-center">
|
||||||
</Button>
|
<motion.div
|
||||||
);
|
className="absolute"
|
||||||
})}
|
initial={{ rotate: 0, scale: 1 }}
|
||||||
|
animate={{
|
||||||
|
rotate: isLight ? 0 : -90,
|
||||||
|
scale: isLight ? 1 : 0,
|
||||||
|
}}
|
||||||
|
transition={{ duration: 0.5 }}
|
||||||
|
>
|
||||||
|
<Sun className="w-[1.1rem] h-[1.1rem]" />
|
||||||
|
</motion.div>
|
||||||
|
<motion.div
|
||||||
|
className="absolute"
|
||||||
|
initial={{ rotate: 90, scale: 0 }}
|
||||||
|
animate={{
|
||||||
|
rotate: isLight ? 90 : 0,
|
||||||
|
scale: isLight ? 0 : 1,
|
||||||
|
}}
|
||||||
|
transition={{ duration: 0.5 }}
|
||||||
|
>
|
||||||
|
<MoonStar className="w-[1.1rem] h-[1.1rem]" />
|
||||||
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
|
<span>{capitalizeWords(theme)}</span>
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -63,10 +63,6 @@ const config: Config = {
|
|||||||
md: "calc(var(--radius) - 2px)",
|
md: "calc(var(--radius) - 2px)",
|
||||||
sm: "calc(var(--radius) - 4px)",
|
sm: "calc(var(--radius) - 4px)",
|
||||||
},
|
},
|
||||||
backgroundImage: {
|
|
||||||
"separator-gradient":
|
|
||||||
"linear-gradient(90deg, #161619, hsl(var(--muted)), #161619)",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [require("tailwindcss-animate")],
|
plugins: [require("tailwindcss-animate")],
|
||||||
|
Reference in New Issue
Block a user