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