521 lines
24 KiB
TypeScript
521 lines
24 KiB
TypeScript
import { Dialog, Disclosure, Popover, Transition } from '@headlessui/react';
|
|
import {
|
|
ArrowUpRightIcon,
|
|
Bars4Icon,
|
|
ChevronDownIcon,
|
|
XMarkIcon,
|
|
} from '@heroicons/react/24/outline';
|
|
import cx from 'classnames';
|
|
import Link from 'next/link';
|
|
import { useRouter } from 'next/router';
|
|
import { Fragment, useEffect, useState } from 'react';
|
|
import { ButtonLink } from '../button';
|
|
import {
|
|
eventItems,
|
|
featuresItems,
|
|
learnItems,
|
|
plans,
|
|
resourceMenuItems,
|
|
solutionsMenuItems,
|
|
} from './menu-items';
|
|
import { NxIcon } from '../nx-icon';
|
|
import { GitHubIcon } from '../github-icon';
|
|
import { MobileMenuItem } from './mobile-menu-item';
|
|
import { SectionsMenu } from './sections-menu';
|
|
import { TwoColumnsMenu } from './two-columns-menu';
|
|
import { AlgoliaSearch } from '@nx/nx-dev/feature-search';
|
|
import { NxCloudIcon } from '../nx-cloud-icon';
|
|
|
|
export function Header(): JSX.Element {
|
|
let [isOpen, setIsOpen] = useState(false);
|
|
const router = useRouter();
|
|
|
|
// We need to close the popover if the route changes or the window is resized to prevent the popover from being stuck open.
|
|
const checkSizeAndClosePopover = () => {
|
|
const breakpoint = 1024; // This is the standard Tailwind lg breakpoint value
|
|
if (window.innerWidth < breakpoint) {
|
|
setIsOpen(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
window.addEventListener('resize', checkSizeAndClosePopover);
|
|
return () => {
|
|
window.removeEventListener('resize', checkSizeAndClosePopover);
|
|
};
|
|
}, []);
|
|
|
|
return (
|
|
<div className="z-5 relative inset-x-0 top-0 flex print:hidden">
|
|
{/*DESKTOP*/}
|
|
<div className="mx-auto hidden w-full max-w-7xl items-center justify-between space-x-10 p-4 px-8 lg:flex">
|
|
{/*PRIMARY NAVIGATION*/}
|
|
<div className="flex flex-shrink-0 text-sm">
|
|
{/*LOGO*/}
|
|
<Link
|
|
href="/"
|
|
className="mr-4 flex items-center text-slate-900 dark:text-white"
|
|
>
|
|
<span className="sr-only">Nx</span>
|
|
<NxIcon aria-hidden="true" className="h-8 w-8" />
|
|
</Link>
|
|
<nav
|
|
role="menu"
|
|
className="items-justified flex items-center justify-center space-x-2 py-0.5"
|
|
>
|
|
<h2 className="sr-only">Main navigation</h2>
|
|
{/*FEATURES*/}
|
|
<Popover className="relative">
|
|
{({ open }) => (
|
|
<>
|
|
<Popover.Button
|
|
className={cx(
|
|
open ? 'text-blue-500 dark:text-sky-500' : '',
|
|
'group inline-flex items-center gap-2 px-3 py-2 font-medium leading-tight outline-0 dark:text-slate-200'
|
|
)}
|
|
>
|
|
<span
|
|
className={cx(
|
|
open ? 'text-blue-500 dark:text-sky-500' : '',
|
|
'transition duration-150 ease-in-out group-hover:text-blue-500 dark:group-hover:text-sky-500'
|
|
)}
|
|
>
|
|
Features
|
|
</span>
|
|
<ChevronDownIcon
|
|
aria-hidden="true"
|
|
className={cx(
|
|
open
|
|
? 'rotate-180 transform text-blue-500 dark:text-sky-500'
|
|
: '',
|
|
'h-3 w-3 transition duration-150 ease-in-out group-hover:text-blue-500 dark:group-hover:text-sky-500'
|
|
)}
|
|
/>
|
|
</Popover.Button>
|
|
<Transition
|
|
as={Fragment}
|
|
enter="transition ease-out duration-200"
|
|
enterFrom="opacity-0 translate-y-1"
|
|
enterTo="opacity-100 translate-y-0"
|
|
leave="transition ease-in duration-150"
|
|
leaveFrom="opacity-100 translate-y-0"
|
|
leaveTo="opacity-0 translate-y-1"
|
|
>
|
|
<Popover.Panel className="absolute z-10 mt-3 w-max max-w-3xl xl:max-w-3xl">
|
|
<TwoColumnsMenu items={featuresItems} />
|
|
</Popover.Panel>
|
|
</Transition>
|
|
</>
|
|
)}
|
|
</Popover>
|
|
{/*SOLUTIONS*/}
|
|
<Popover className="relative">
|
|
{({ open }) => (
|
|
<>
|
|
<Popover.Button
|
|
className={cx(
|
|
open ? 'text-blue-500 dark:text-sky-500' : '',
|
|
'group inline-flex items-center px-3 py-2 font-medium leading-tight outline-0 dark:text-slate-200'
|
|
)}
|
|
>
|
|
<span
|
|
className={cx(
|
|
open ? 'text-blue-500 dark:text-sky-500' : '',
|
|
'transition duration-150 ease-in-out group-hover:text-blue-500 dark:group-hover:text-sky-500'
|
|
)}
|
|
>
|
|
Solutions
|
|
</span>
|
|
<ChevronDownIcon
|
|
className={cx(
|
|
open
|
|
? 'rotate-180 transform text-blue-500 dark:text-sky-500'
|
|
: '',
|
|
'ml-2 h-3 w-3 transition duration-150 ease-in-out group-hover:text-blue-500 dark:group-hover:text-sky-500'
|
|
)}
|
|
aria-hidden="true"
|
|
/>
|
|
</Popover.Button>
|
|
|
|
<Transition
|
|
as={Fragment}
|
|
enter="transition ease-out duration-200"
|
|
enterFrom="opacity-0 translate-y-1"
|
|
enterTo="opacity-100 translate-y-0"
|
|
leave="transition ease-in duration-150"
|
|
leaveFrom="opacity-100 translate-y-0"
|
|
leaveTo="opacity-0 translate-y-1"
|
|
>
|
|
<Popover.Panel className="absolute z-10 mt-3 w-max max-w-2xl">
|
|
<SectionsMenu sections={solutionsMenuItems} />
|
|
</Popover.Panel>
|
|
</Transition>
|
|
</>
|
|
)}
|
|
</Popover>
|
|
<Link
|
|
href="/getting-started/intro"
|
|
title="Documentation"
|
|
className="hidden px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500"
|
|
>
|
|
Documentation
|
|
</Link>
|
|
<Link
|
|
href="/blog"
|
|
title="Blog"
|
|
className="hidden px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500"
|
|
>
|
|
Blog
|
|
</Link>
|
|
<a
|
|
href="https://nx.app/pricing"
|
|
title="Nx Cloud"
|
|
target="_blank"
|
|
className="hidden gap-2 px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500"
|
|
>
|
|
CI Pricing
|
|
<ArrowUpRightIcon className="h-2 w-2 align-super" />
|
|
</a>
|
|
{/*RESOURCES*/}
|
|
<Popover className="relative">
|
|
{({ open }) => (
|
|
<>
|
|
<Popover.Button
|
|
className={cx(
|
|
open ? 'text-blue-500 dark:text-sky-500' : '',
|
|
'group inline-flex items-center px-3 py-2 font-medium leading-tight outline-0 dark:text-slate-200'
|
|
)}
|
|
>
|
|
<span className="transition duration-150 ease-in-out group-hover:text-blue-500 dark:group-hover:text-sky-500">
|
|
Resources
|
|
</span>
|
|
<ChevronDownIcon
|
|
className={cx(
|
|
open
|
|
? 'rotate-180 transform text-blue-500 dark:text-sky-500'
|
|
: '',
|
|
'ml-2 h-3 w-3 transition duration-150 ease-in-out group-hover:text-blue-500 dark:group-hover:text-sky-500'
|
|
)}
|
|
aria-hidden="true"
|
|
/>
|
|
</Popover.Button>
|
|
|
|
<Transition
|
|
as={Fragment}
|
|
enter="transition ease-out duration-200"
|
|
enterFrom="opacity-0 translate-y-1"
|
|
enterTo="opacity-100 translate-y-0"
|
|
leave="transition ease-in duration-150"
|
|
leaveFrom="opacity-100 translate-y-0"
|
|
leaveTo="opacity-0 translate-y-1"
|
|
>
|
|
<Popover.Panel className="absolute left-60 z-10 mt-3 w-max max-w-2xl -translate-x-1/2 transform lg:left-20">
|
|
<SectionsMenu sections={resourceMenuItems} />
|
|
</Popover.Panel>
|
|
</Transition>
|
|
</>
|
|
)}
|
|
</Popover>
|
|
<div className="opacity-50 hover:opacity-100">
|
|
<AlgoliaSearch tiny={true} />
|
|
</div>
|
|
</nav>
|
|
</div>
|
|
{/*SECONDARY NAVIGATION*/}
|
|
<div className="flex-shrink-0 text-sm">
|
|
<nav className="flex items-center justify-center space-x-1">
|
|
<Link
|
|
className="hidden cursor-pointer px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500"
|
|
title="Contact Us"
|
|
href="/contact"
|
|
>
|
|
Contact
|
|
</Link>
|
|
<ButtonLink
|
|
href="https://cloud.nx.app"
|
|
variant="secondary"
|
|
size="small"
|
|
target="_blank"
|
|
title="Log in to your Nx Cloud Account"
|
|
>
|
|
<NxCloudIcon className="h-4 w-4" aria-hidden="true" />
|
|
<span>Go to App</span>
|
|
</ButtonLink>
|
|
<a
|
|
title="Nx is open source, check the code on GitHub"
|
|
href="https://github.com/nrwl/nx"
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
className="inline-flex items-center px-3 py-2 opacity-60 hover:opacity-90"
|
|
>
|
|
<span className="sr-only">Nx on GitHub</span>
|
|
<GitHubIcon aria-hidden="true" className="h-5 w-5" />
|
|
</a>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
{/*MOBILE*/}
|
|
<div className="relative mx-auto flex w-full items-center justify-between p-4 lg:hidden">
|
|
<div className="flex w-full items-center justify-between">
|
|
{/*LOGO*/}
|
|
<Link
|
|
href="/"
|
|
className="flex items-center text-slate-900 dark:text-white"
|
|
>
|
|
<span className="sr-only">Nx</span>
|
|
<NxIcon aria-hidden="true" className="h-8 w-8" />
|
|
</Link>
|
|
<div className="flex items-center gap-6">
|
|
<a
|
|
title="Nx is open source, check the code on GitHub"
|
|
href="https://github.com/nrwl/nx"
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
className="inline-flex items-center px-3 py-2 opacity-60 hover:opacity-90"
|
|
>
|
|
<span className="sr-only">Nx on GitHub</span>
|
|
<GitHubIcon aria-hidden="true" className="h-5 w-5" />
|
|
</a>
|
|
{/*MENU*/}
|
|
<button onClick={() => setIsOpen(!isOpen)} className="shrink-0 p-2">
|
|
<Bars4Icon
|
|
aria-hidden="true"
|
|
title="Open navigation menu"
|
|
strokeWidth="2"
|
|
className="h-6 w-6"
|
|
/>
|
|
<span className="sr-only">Open navigation panel</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<Transition.Root show={isOpen} as={Fragment}>
|
|
<Dialog
|
|
as="div"
|
|
className="relative z-10"
|
|
onClose={() => setIsOpen(!isOpen)}
|
|
>
|
|
<div className="fixed inset-0" />
|
|
<div className="fixed inset-0 overflow-hidden">
|
|
<div className="absolute inset-0 overflow-hidden">
|
|
<div className="pointer-events-none fixed inset-y-0 right-0 flex max-w-full">
|
|
<Transition.Child
|
|
as={Fragment}
|
|
enter="transform transition ease-in-out duration-250 sm:duration-500"
|
|
enterFrom="translate-x-full"
|
|
enterTo="translate-x-0"
|
|
leave="transform transition ease-in-out duration-250 sm:duration-500"
|
|
leaveFrom="translate-x-0"
|
|
leaveTo="translate-x-full"
|
|
>
|
|
<Dialog.Panel className="pointer-events-auto w-screen">
|
|
<div className="flex h-full flex-col overflow-y-scroll bg-white py-6 shadow-xl dark:bg-slate-900">
|
|
<div className="px-4 sm:px-6">
|
|
<div className="flex items-start justify-between">
|
|
<Dialog.Title>
|
|
<Link
|
|
href="/"
|
|
className="flex items-center text-slate-900 dark:text-white"
|
|
>
|
|
<svg
|
|
role="img"
|
|
viewBox="0 0 24 24"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
className="h-8 w-8"
|
|
fill="currentColor"
|
|
>
|
|
<title>Nx</title>
|
|
<path d="M11.987 14.138l-3.132 4.923-5.193-8.427-.012 8.822H0V4.544h3.691l5.247 8.833.005-3.998 3.044 4.759zm.601-5.761c.024-.048 0-3.784.008-3.833h-3.65c.002.059-.005 3.776-.003 3.833h3.645zm5.634 4.134a2.061 2.061 0 0 0-1.969 1.336 1.963 1.963 0 0 1 2.343-.739c.396.161.917.422 1.33.283a2.1 2.1 0 0 0-1.704-.88zm3.39 1.061c-.375-.13-.8-.277-1.109-.681-.06-.08-.116-.17-.176-.265a2.143 2.143 0 0 0-.533-.642c-.294-.216-.68-.322-1.18-.322a2.482 2.482 0 0 0-2.294 1.536 2.325 2.325 0 0 1 4.002.388.75.75 0 0 0 .836.334c.493-.105.46.36 1.203.518v-.133c-.003-.446-.246-.55-.75-.733zm2.024 1.266a.723.723 0 0 0 .347-.638c-.01-2.957-2.41-5.487-5.37-5.487a5.364 5.364 0 0 0-4.487 2.418c-.01-.026-1.522-2.39-1.538-2.418H8.943l3.463 5.423-3.379 5.32h3.54l1.54-2.366 1.568 2.366h3.541l-3.21-5.052a.7.7 0 0 1-.084-.32 2.69 2.69 0 0 1 2.69-2.691h.001c1.488 0 1.736.89 2.057 1.308.634.826 1.9.464 1.9 1.541a.707.707 0 0 0 1.066.596zm.35.133c-.173.372-.56.338-.755.639-.176.271.114.412.114.412s.337.156.538-.311c.104-.231.14-.488.103-.74z" />
|
|
</svg>
|
|
<span className="sr-only">Nx</span>
|
|
</Link>
|
|
</Dialog.Title>
|
|
<div className="ml-3 flex h-7 items-center">
|
|
<button
|
|
type="button"
|
|
className="dark:hovers:text-sky-500 relative rounded-md text-slate-600 hover:text-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:text-slate-400 dark:focus:ring-sky-500"
|
|
onClick={() => setIsOpen(!isOpen)}
|
|
>
|
|
<span className="absolute -inset-2.5" />
|
|
<span className="sr-only">
|
|
Close navigation panel
|
|
</span>
|
|
<XMarkIcon
|
|
aria-hidden="true"
|
|
className="h-6 w-6"
|
|
/>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="relative mt-6 flex-1 px-4 sm:px-6">
|
|
<ButtonLink
|
|
href="https://cloud.nx.app"
|
|
variant="primary"
|
|
size="small"
|
|
target="_blank"
|
|
title="Log in to your Nx Cloud Account"
|
|
className="w-full"
|
|
>
|
|
Go to App
|
|
</ButtonLink>
|
|
|
|
<div className="mt-4 divide-y divide-slate-200 border-b border-slate-200 dark:divide-slate-800 dark:border-slate-800">
|
|
{/*FEATURES*/}
|
|
<Disclosure as="div">
|
|
{({ open }) => (
|
|
<>
|
|
<Disclosure.Button
|
|
className={cx(
|
|
open
|
|
? 'text-blue-500 dark:text-sky-500'
|
|
: 'tex-slate-800 dark:text-slate-200',
|
|
'flex w-full items-center justify-between py-4 text-left text-base font-medium focus:outline-none'
|
|
)}
|
|
>
|
|
<span>Features</span>
|
|
<ChevronDownIcon
|
|
aria-hidden="true"
|
|
className={cx(
|
|
open
|
|
? 'rotate-180 transform text-blue-500 dark:text-sky-500'
|
|
: 'tex-slate-800 dark:text-slate-200',
|
|
'h-3 w-3 transition duration-150 ease-in-out group-hover:text-blue-500 dark:group-hover:text-sky-500'
|
|
)}
|
|
/>
|
|
</Disclosure.Button>
|
|
<Disclosure.Panel
|
|
as="ul"
|
|
className="space-y-1 pb-2"
|
|
>
|
|
{featuresItems.map((item) => (
|
|
<MobileMenuItem
|
|
key={item.name}
|
|
item={item}
|
|
/>
|
|
))}
|
|
</Disclosure.Panel>
|
|
</>
|
|
)}
|
|
</Disclosure>
|
|
{/*SOLUTIONS*/}
|
|
<Disclosure as="div">
|
|
{({ open }) => (
|
|
<>
|
|
<Disclosure.Button
|
|
className={cx(
|
|
open
|
|
? 'text-blue-500 dark:text-sky-500'
|
|
: 'tex-slate-800 dark:text-slate-200',
|
|
'flex w-full items-center justify-between py-4 text-left text-base font-medium focus:outline-none'
|
|
)}
|
|
>
|
|
<span>Solutions</span>
|
|
<ChevronDownIcon
|
|
aria-hidden="true"
|
|
className={cx(
|
|
open
|
|
? 'rotate-180 transform text-blue-500 dark:text-sky-500'
|
|
: 'tex-slate-800 dark:text-slate-200',
|
|
'h-3 w-3 transition duration-150 ease-in-out group-hover:text-blue-500 dark:group-hover:text-sky-500'
|
|
)}
|
|
/>
|
|
</Disclosure.Button>
|
|
<Disclosure.Panel as="ul" className="space-y-1">
|
|
{plans.map((item) => (
|
|
<MobileMenuItem
|
|
key={item.name}
|
|
item={item}
|
|
/>
|
|
))}
|
|
</Disclosure.Panel>
|
|
</>
|
|
)}
|
|
</Disclosure>
|
|
<Link
|
|
href="/getting-started/intro"
|
|
title="Documentation"
|
|
className="block py-4 font-medium leading-tight hover:text-blue-500 dark:text-slate-200 dark:hover:text-sky-500"
|
|
>
|
|
Documentation
|
|
</Link>
|
|
<Link
|
|
href="/blog"
|
|
title="Blog"
|
|
className="block py-4 font-medium leading-tight hover:text-blue-500 dark:text-slate-200 dark:hover:text-sky-500"
|
|
>
|
|
Blog
|
|
</Link>
|
|
<a
|
|
href="https://nx.app/pricing"
|
|
title="Nx Cloud"
|
|
target="_blank"
|
|
className="flex w-full gap-2 py-4 font-medium leading-tight hover:text-blue-500 dark:text-slate-200 dark:hover:text-sky-500"
|
|
>
|
|
CI Pricing
|
|
<ArrowUpRightIcon className="h-2 w-2 align-super" />
|
|
</a>
|
|
{/*RESOURCES*/}
|
|
<Disclosure as="div">
|
|
{({ open }) => (
|
|
<>
|
|
<Disclosure.Button
|
|
className={cx(
|
|
'flex w-full items-center justify-between py-4 text-left text-base font-medium focus:outline-none',
|
|
open
|
|
? 'text-blue-500 dark:text-sky-500'
|
|
: 'tex-slate-800 dark:text-slate-200'
|
|
)}
|
|
>
|
|
<span>Resources</span>
|
|
<ChevronDownIcon
|
|
aria-hidden="true"
|
|
className={cx(
|
|
open
|
|
? 'rotate-180 transform text-blue-500 dark:text-sky-500'
|
|
: 'tex-slate-800 dark:text-slate-200',
|
|
'h-3 w-3 transition duration-150 ease-in-out group-hover:text-blue-500 dark:group-hover:text-sky-500'
|
|
)}
|
|
/>
|
|
</Disclosure.Button>
|
|
<Disclosure.Panel
|
|
as="ul"
|
|
className="space-y-1 pb-2"
|
|
>
|
|
{learnItems.map((item) => (
|
|
<MobileMenuItem
|
|
key={item.name}
|
|
item={item}
|
|
/>
|
|
))}
|
|
{eventItems.map((item) => (
|
|
<MobileMenuItem
|
|
key={item.name}
|
|
item={item}
|
|
/>
|
|
))}
|
|
</Disclosure.Panel>
|
|
</>
|
|
)}
|
|
</Disclosure>
|
|
<Link
|
|
href="/contact"
|
|
title="Contact"
|
|
className="block py-4 font-medium leading-tight hover:text-blue-500 dark:text-slate-200 dark:hover:text-sky-500"
|
|
>
|
|
Contact
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Dialog.Panel>
|
|
</Transition.Child>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Dialog>
|
|
</Transition.Root>
|
|
</div>
|
|
);
|
|
}
|