From 4838df315931bb883f704ec3e1abe2685f296cdf Mon Sep 17 00:00:00 2001 From: HsiangNianian Date: Sat, 22 Apr 2023 19:52:26 +0800 Subject: 😀 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/components/LogoContext/icons.tsx | 228 ++++++++++++++++++++++++++++++++++ docs/components/LogoContext/index.tsx | 169 +++++++++++++++++++++++++ docs/components/LogoContext/items.tsx | 91 ++++++++++++++ docs/components/LogoContext/types.ts | 25 ++++ 4 files changed, 513 insertions(+) create mode 100644 docs/components/LogoContext/icons.tsx create mode 100644 docs/components/LogoContext/index.tsx create mode 100644 docs/components/LogoContext/items.tsx create mode 100644 docs/components/LogoContext/types.ts (limited to 'docs/components/LogoContext') diff --git a/docs/components/LogoContext/icons.tsx b/docs/components/LogoContext/icons.tsx new file mode 100644 index 0000000..dabefc2 --- /dev/null +++ b/docs/components/LogoContext/icons.tsx @@ -0,0 +1,228 @@ +import classNames from "classnames"; + +export const VercelLogo = ({ className }: { className?: string }) => ( + + + +); + +export const TurborepoLogo = ({ className }: { className?: string }) => ( + + + + + + + + + + +); + +export const TurbopackLogo = ({ className }: { className?: string }) => ( + + + + + + + + + + + + + + + + + + + + +); + +export const NextJSLogo = ({ className }: { className?: string }) => ( + + + + + + + + + + + + + + + + + + + + +); + +export const DesignSystemLogo = ({ className }: { className?: string }) => ( + + + + + + +); + +export const IconType = ({ className }: { className?: string }) => ( + + + + + +); diff --git a/docs/components/LogoContext/index.tsx b/docs/components/LogoContext/index.tsx new file mode 100644 index 0000000..3f03740 --- /dev/null +++ b/docs/components/LogoContext/index.tsx @@ -0,0 +1,169 @@ +import { useEffect, useCallback, useState, useRef } from "react"; +import { useTheme } from "nextra-theme-docs"; +import Link from "next/link"; +import classNames from "classnames"; +import { VercelLogo } from "./icons"; +import { PRODUCT_MENU_ITEMS, PLATFORM_MENU_ITEMS } from "./items"; +import type { MenuItemProps } from "./types"; +import { MouseEvent } from "react"; +import { useTurboSite } from "../SiteSwitcher"; + +function MenuDivider({ children, ...other }: { children: string }) { + return ( +

+ {children} +

+ ); +} + +function MenuItem({ + children, + prefix, + className, + type, + href, + onClick, + closeMenu, + disabled, + ...other +}: MenuItemProps) { + const [copied, setCopied] = useState(false); + + const handleClick = () => { + if (onClick) { + onClick(); + } + if (type === "copy") { + setCopied(true); + } else { + closeMenu(); + } + }; + + useEffect(() => { + if (copied) { + const timeout = setTimeout(() => { + setCopied(false); + closeMenu(); + }, 2000); + return () => clearTimeout(timeout); + } + }, [copied, closeMenu]); + + const classes = classNames( + className, + "group flex items-center px-4 py-2 text-sm dark:hover:bg-gray-800 hover:bg-gray-200 w-full rounded-md" + ); + if (type === "internal") { + return ( + + {prefix} + {children} + + ); + } + if (type === "external") { + return ( + + {prefix} + {children} + + ); + } + + if (type === "copy") { + return ( + + ); + } +} + +export function LogoContext() { + const [open, setOpen] = useState(false); + const site = useTurboSite(); + const menu = useRef(null); + const { theme = "dark" } = useTheme(); + + const toggleMenu = (e: MouseEvent) => { + e.preventDefault(); + if (e.type === "contextmenu") { + setOpen((prev) => !prev); + } else { + setOpen(false); + window.open(`https://vercel.com`, "_blank"); + } + }; + + const onClickOutside: EventListener = useCallback( + (e) => { + if (menu.current && open && !menu.current.contains(e.target)) { + setOpen(false); + } + }, + [open] + ); + + useEffect(() => { + document.addEventListener("click", onClickOutside, true); + return () => { + document.removeEventListener("click", onClickOutside, true); + }; + }, [onClickOutside]); + + return ( +
+ + {open && ( +
+
+ Platform + {PLATFORM_MENU_ITEMS({ theme, site }).map((item) => ( + setOpen(false)} + {...item} + > + {item.children} + + ))} + Products + {PRODUCT_MENU_ITEMS({ theme, site }).map((item) => ( + setOpen(false)} + {...item} + > + {item.children} + + ))} +
+
+ )} +
+ ); +} diff --git a/docs/components/LogoContext/items.tsx b/docs/components/LogoContext/items.tsx new file mode 100644 index 0000000..6ce1fe6 --- /dev/null +++ b/docs/components/LogoContext/items.tsx @@ -0,0 +1,91 @@ +import { + VercelLogo, + TurborepoLogo, + TurbopackLogo, + IconType, + NextJSLogo, + DesignSystemLogo, +} from "./icons"; +import type { ContextItem, ContextList } from "./types"; +import copy from "copy-to-clipboard"; + +export const PLATFORM_MENU_ITEMS = ({ + theme, +}: ContextList): Array => [ + { + name: "copy-logo", + "aria-label": "Copy Logo as SVG to Clipboard", + children: "Copy Logo as SVG", + prefix: , + type: "copy", + onClick: () => { + copy( + ` + + ` + ); + }, + }, + { + name: "copy-wordmark", + "aria-label": "Copy Wordmark as SVG to Clipboard", + children: "Copy Wordmark as SVG", + prefix: , + type: "copy", + onClick: () => { + copy( + // NOTE: We include `xmlns` as this is required when the SVG isn't inlined. + `` + ); + }, + }, + { + name: "brand-guidelines", + "aria-label": "Open Brand Guidelines in New Tab", + children: "Brand Guidelines", + prefix: , + type: "external", + href: "https://vercel.com/design/brands", + }, +]; + +export const PRODUCT_MENU_ITEMS = ({ + site, +}: ContextList): Array => [ + { + name: "next-js", + "aria-label": "Open Next.js Home in New Tab", + children: "Next.js", + prefix: , + type: "external", + href: "https://nextjs.org", + }, + { + name: "turborepo", + "aria-label": "Open Turborepo Home in New Tab", + disabled: site === "repo", + children: "Turborepo", + prefix: , + type: "internal", + href: "/repo", + }, + { + name: "turbopack", + "aria-label": "Open Turbopack Home in New Tab", + disabled: site === "pack", + children: "Turbopack", + prefix: , + type: "internal", + href: "/pack", + }, +]; diff --git a/docs/components/LogoContext/types.ts b/docs/components/LogoContext/types.ts new file mode 100644 index 0000000..ff7f644 --- /dev/null +++ b/docs/components/LogoContext/types.ts @@ -0,0 +1,25 @@ +import type { ReactNode } from "react"; +import { TurboSite } from "../SiteSwitcher"; + +type MenuItemType = "internal" | "external" | "copy"; + +export interface MenuItemProps extends ContextItem { + closeMenu?: () => void; + className?: string; +} + +export interface ContextList { + theme: string; + site: TurboSite; +} + +export interface ContextItem { + name: string; + "aria-label": string; + disabled?: boolean; + type: MenuItemType; + children: ReactNode; + prefix: ReactNode; + href?: string; + onClick?: () => void; +} -- cgit v1.2.3-70-g09d2