Header.tsx 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. 'use client';
  2. import { Fragment, MutableRefObject, PropsWithChildren, RefObject, useCallback, useEffect, useRef, useState } from 'react';
  3. import { Dialog, Popover } from '@headlessui/react';
  4. import clsx from 'clsx';
  5. import { banner, componentsRounded, iconsCountRounded, sponsorsUrl, uiGithubUrl } from '@/config/site';
  6. import Icon from '@/components/Icon';
  7. import GoToTop from '@/components/layout/GoToTop';
  8. import Link from '@/components/Link';
  9. import NavLink from '@/components/NavLink';
  10. import Shape from '@/components/Shape';
  11. // import { useRouter } from 'next/router'
  12. const NavDropdown = ({ title, children, active, footer = false }) => {
  13. return (
  14. <Popover className="navbar-dropdown">
  15. {({ open }) => (
  16. <>
  17. <Popover.Button className={clsx('navbar-link', active && 'active')}>{title}</Popover.Button>
  18. <Popover.Panel className="navbar-dropdown-menu">
  19. <div className="navbar-dropdown-menu-content">{children}</div>
  20. {footer && <div className="navbar-dropdown-menu-footer">{footer}</div>}
  21. </Popover.Panel>
  22. </>
  23. )}
  24. </Popover>
  25. );
  26. };
  27. const menuLinks = [
  28. {
  29. title: 'UI Kit',
  30. menu: 'ui',
  31. children: [
  32. {
  33. icon: 'home',
  34. href: '/',
  35. title: 'About',
  36. description: 'Develop beautiful web apps with Tabler',
  37. },
  38. {
  39. icon: 'layout-dashboard',
  40. href: '/preview',
  41. title: 'Preview template',
  42. description: 'See what Tabler looks like and offers',
  43. },
  44. {
  45. icon: 'script',
  46. href: '/docs',
  47. title: 'Documentation',
  48. description: 'Read how to develop apps with Tabler',
  49. },
  50. {
  51. icon: 'lego',
  52. href: '/features',
  53. title: 'Features',
  54. description: 'See what kind of features you can find here',
  55. },
  56. {
  57. icon: 'lifebuoy',
  58. href: '/support',
  59. title: 'Support',
  60. description: 'Write to us if you need anything!',
  61. },
  62. {
  63. icon: 'brand-github',
  64. href: uiGithubUrl,
  65. title: 'Source code',
  66. description: 'View Tabler\'s source code ',
  67. props: {
  68. target: '_blank',
  69. rel: 'nofollow',
  70. },
  71. },
  72. ],
  73. },
  74. {
  75. href: '/emails',
  76. menu: 'emails',
  77. title: 'Email templates',
  78. },
  79. {
  80. href: '/icons',
  81. menu: 'icons',
  82. title: (
  83. <>
  84. <span className="d-none lg:d-inline">Over {iconsCountRounded} </span>
  85. Icons
  86. </>
  87. ),
  88. },
  89. {
  90. href: '/blog',
  91. menu: 'blog',
  92. title: <>Blog</>,
  93. },
  94. {
  95. href: '/docs',
  96. menu: 'docs',
  97. title: 'Documentation',
  98. },
  99. // {
  100. // href: '/guides',
  101. // menu: 'guides',
  102. // title: 'Guides',
  103. // },
  104. {
  105. menu: 'sponsors',
  106. href: sponsorsUrl,
  107. type: 'button',
  108. title: (
  109. <span>
  110. Sponsor<span className="d-none lg:d-inline"> project</span>
  111. </span>
  112. ),
  113. icon: <Icon name="heart" filled color="red" />,
  114. },
  115. ];
  116. const NavbarLink = (link, menu) => {
  117. // const router = useRouter()
  118. if (link.type === 'button') {
  119. return (
  120. <div className="navbar-item">
  121. <a href={link.href} className="btn" target="_blank" rel="noopener noreferrer">
  122. {link.icon}
  123. {link.title}
  124. </a>
  125. </div>
  126. );
  127. } else if (link.children) {
  128. return (
  129. <NavDropdown title={link.title} active={menu === link.menu}>
  130. {link.children.map((link) => (
  131. <Popover.Button as={Link} href={link.href || ''} className="navbar-dropdown-menu-link" key={link.title} onClick={() => true} {...link.props}>
  132. <div className="row g-3">
  133. <div className="col-auto">
  134. <Shape icon={link.icon} />
  135. </div>
  136. <div className="col">
  137. <h5 className="mb-1">{link.title}</h5>
  138. <p className="font-h6 m-0 text-muted">{link.description}</p>
  139. </div>
  140. </div>
  141. </Popover.Button>
  142. ))}
  143. </NavDropdown>
  144. );
  145. }
  146. return (
  147. // router.pathname.replace(/^\//, '').startsWith(link.menu)
  148. <NavLink href={link.href} className="navbar-link" active={false}>
  149. {link.title}
  150. </NavLink>
  151. );
  152. };
  153. const SidebarLink = (link, menu, onClick) => {
  154. if (link.type === 'button') {
  155. return (
  156. <div className="aside-menu-item mt-4">
  157. <a href={link.href} className="btn btn-block" target="_blank" rel="noopener noreferrer" onClick={onClick}>
  158. {link.icon}
  159. {link.title}
  160. </a>
  161. </div>
  162. );
  163. } else if (link.children) {
  164. return (
  165. <div className="aside-menu-item">
  166. <div className={clsx('aside-menu-title', { active: menu === link.menu })}>{link.title}</div>
  167. <div className="aside-menu-children">
  168. {link.children.map((link) => (
  169. <Link href={link.href || ''} key={link.title} className="aside-menu-link" onClick={onClick} {...link.props}>
  170. {link.title}
  171. </Link>
  172. ))}
  173. </div>
  174. </div>
  175. );
  176. }
  177. return (
  178. <Link href={link.href} className={clsx('aside-menu-link', { active: menu === link.menu })} onClick={onClick}>
  179. {link.title}
  180. </Link>
  181. );
  182. };
  183. const Navbar = ({ menu, opened, onClick, ...props }: { menu?: string; opened?: boolean; onClick?: (event: React.MouseEvent) => void; className?: string }) => {
  184. return (
  185. <div className={clsx('navbar', opened && 'opened', props.className)}>
  186. {menuLinks.map((link) => (
  187. <Fragment key={link.menu}>{NavbarLink(link, menu)}</Fragment>
  188. ))}
  189. </div>
  190. );
  191. };
  192. const Banner = () => {
  193. const [showBanner, setShowBanner] = useState(false);
  194. useEffect(() => {
  195. if (window.localStorage.getItem(`banner-${banner.id}`) !== '1') {
  196. setShowBanner(true);
  197. }
  198. }, []);
  199. function closeBanner() {
  200. localStorage.setItem(`banner-${banner.id}`, '1');
  201. setShowBanner(false);
  202. }
  203. return (
  204. banner.show &&
  205. showBanner && (
  206. <div className="banner">
  207. <div className="container">
  208. <div className="text-truncate">{banner.text}</div>
  209. <a href={banner.link.href} className="ml-5 banner-link" target="_blank">
  210. {banner.link.text}
  211. </a>
  212. </div>
  213. <a onClick={closeBanner} className="banner-close">
  214. <Icon name="x" />
  215. </a>
  216. </div>
  217. )
  218. );
  219. };
  220. export default function Header({ headerStatic, className, pageProps, ...props }: { headerStatic?: boolean; className?: string; pageProps?: any }) {
  221. const [sticky, setSticky] = useState(false);
  222. const [isOpen, setIsOpen] = useState(false);
  223. const pop = () => {
  224. setSticky(window.pageYOffset > 0);
  225. };
  226. function closeModal() {
  227. setIsOpen(false);
  228. }
  229. function toggleModal() {
  230. setIsOpen(!isOpen);
  231. }
  232. return (
  233. <>
  234. <Banner />
  235. <header
  236. className={clsx(
  237. 'header',
  238. // router.pathname.startsWith('/docs') && 'header-bordered',
  239. // headerStatic ? 'header-static' : '',
  240. // isVisible && 'header-sticky',
  241. className,
  242. )}
  243. >
  244. <div className="container" data-aos="fade-down">
  245. <nav className="row items-center">
  246. <div className="col-auto">
  247. <Link href="/" className={clsx('logo' /*, pageProps.brand ? `logo-${pageProps.brand}` : ''*/)} aria-label="Tabler" />
  248. </div>
  249. <div className="col-auto ml-auto">
  250. <div className="d-none md:d-block">
  251. {/* <Navbar menu={pageProps.menu} /> */}
  252. <Navbar />
  253. </div>
  254. <div className="md:d-none">
  255. <button
  256. className={clsx('navbar-toggle', {
  257. active: isOpen,
  258. })}
  259. onClick={toggleModal}
  260. >
  261. <span />
  262. <span />
  263. <span />
  264. <span />
  265. </button>
  266. </div>
  267. </div>
  268. </nav>
  269. </div>
  270. </header>
  271. <Dialog open={isOpen} onClose={closeModal} className="modal-backdrop">
  272. <Dialog.Panel className="modal modal-side">
  273. <div className={clsx('aside-menu mt-4')}>
  274. {/* {menuLinks.map((link) => (
  275. // <Fragment key={link.menu}>{SidebarLink(link, pageProps.menu, closeModal)}</Fragment>
  276. ))} */}
  277. </div>
  278. </Dialog.Panel>
  279. </Dialog>
  280. <GoToTop />
  281. </>
  282. );
  283. }