AppBar.jsx 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import { h } from 'preact';
  2. import Button from './Button';
  3. import LinkedLogo from './LinkedLogo';
  4. import Menu, { MenuItem, MenuSeparator } from './Menu';
  5. import MenuIcon from '../icons/Menu';
  6. import MoreIcon from '../icons/More';
  7. import AutoAwesomeIcon from '../icons/AutoAwesome';
  8. import LightModeIcon from '../icons/LightMode';
  9. import DarkModeIcon from '../icons/DarkMode';
  10. import { useDarkMode, useDrawer } from '../context';
  11. import { useLayoutEffect, useCallback, useRef, useState } from 'preact/hooks';
  12. // We would typically preserve these in component state
  13. // But need to avoid too many re-renders
  14. let lastScrollY = window.scrollY;
  15. export default function AppBar({ title }) {
  16. const [show, setShow] = useState(true);
  17. const [atZero, setAtZero] = useState(window.scrollY === 0);
  18. const [showMoreMenu, setShowMoreMenu] = useState(false);
  19. const { setDarkMode } = useDarkMode();
  20. const { setShowDrawer } = useDrawer();
  21. const handleSelectDarkMode = useCallback(
  22. (value, label) => {
  23. setDarkMode(value);
  24. setShowMoreMenu(false);
  25. },
  26. [setDarkMode, setShowMoreMenu]
  27. );
  28. const moreRef = useRef(null);
  29. const scrollListener = useCallback(
  30. (event) => {
  31. const scrollY = window.scrollY;
  32. window.requestAnimationFrame(() => {
  33. setShow(scrollY <= 0 || lastScrollY > scrollY);
  34. setAtZero(scrollY === 0);
  35. lastScrollY = scrollY;
  36. });
  37. },
  38. [setShow]
  39. );
  40. useLayoutEffect(() => {
  41. document.addEventListener('scroll', scrollListener);
  42. return () => {
  43. document.removeEventListener('scroll', scrollListener);
  44. };
  45. }, [scrollListener]);
  46. const handleShowMenu = useCallback(() => {
  47. setShowMoreMenu(true);
  48. }, [setShowMoreMenu]);
  49. const handleDismissMoreMenu = useCallback(() => {
  50. setShowMoreMenu(false);
  51. }, [setShowMoreMenu]);
  52. const handleShowDrawer = useCallback(() => {
  53. setShowDrawer(true);
  54. }, [setShowDrawer]);
  55. return (
  56. <div
  57. className={`w-full border-b border-gray-200 dark:border-gray-700 flex items-center align-middle p-4 space-x-2 fixed left-0 right-0 z-10 bg-white dark:bg-gray-900 transform transition-all duration-200 translate-y-0 ${
  58. !show ? '-translate-y-full' : ''
  59. } ${!atZero ? 'shadow-sm' : ''}`}
  60. >
  61. <div className="lg:hidden">
  62. <Button color="black" className="rounded-full w-12 h-12" onClick={handleShowDrawer} type="text">
  63. <MenuIcon className="w-10 h-10" />
  64. </Button>
  65. </div>
  66. <LinkedLogo />
  67. <div className="flex-grow-1 flex justify-end w-full">
  68. <div className="w-auto" ref={moreRef}>
  69. <Button color="black" className="rounded-full w-12 h-12" onClick={handleShowMenu} type="text">
  70. <MoreIcon className="w-10 h-10" />
  71. </Button>
  72. </div>
  73. </div>
  74. {showMoreMenu ? (
  75. <Menu onDismiss={handleDismissMoreMenu} relativeTo={moreRef}>
  76. <MenuItem icon={AutoAwesomeIcon} label="Auto dark mode" value="media" onSelect={handleSelectDarkMode} />
  77. <MenuSeparator />
  78. <MenuItem icon={LightModeIcon} label="Light" value="light" onSelect={handleSelectDarkMode} />
  79. <MenuItem icon={DarkModeIcon} label="Dark" value="dark" onSelect={handleSelectDarkMode} />
  80. </Menu>
  81. ) : null}
  82. </div>
  83. );
  84. }