on master help sign in
swipe to close menu on mobile
e84ad81Gavin Atkinson
components/Header.js+2−52
@@ -23,7 +23,7 @@ function ActiveLink({ children, href, className, currentPath }) {
23}
23}
24
24
25export default function Header(props) {
25export default function Header(props) {
26− const [isOpen, toggleTray] = useState(false);
26+ const [isOpen, setTray] = useState(false);
27
27
28 const currentPath = useRouter().asPath;
28 const currentPath = useRouter().asPath;
29
29
@@ -91,7 +91,7 @@ export default function Header(props) {
91 {
91 {
92 // Small screen header
92 // Small screen header
93 }
93 }
94− <MenuTray isOpen={isOpen} toggleTray={toggleTray}>
94+ <MenuTray isOpen={isOpen} setTray={setTray}>
95 <Link href="/">
95 <Link href="/">
96 <a className="type-ui mb-12">Urbit</a>
96 <a className="type-ui mb-12">Urbit</a>
97 </Link>
97 </Link>
@@ -124,56 +124,6 @@ export default function Header(props) {
124 Grants
124 Grants
125 </ActiveLink>
125 </ActiveLink>
126 </MenuTray>
126 </MenuTray>
127−
128− {
129− // Mobile menu button and search button
130− }
131− <div className="block md:hidden">
132− <button
133− onClick={(e) => {
134− e.stopPropagation();
135− props.search.toggleSearch(e);
136− }}
137− className={`z-10 fixed px-4 items-center justify-center type-ui rounded-xl h-16 bg-white text-gray left-4 right-4 bottom-4 mobile-search-button-width ${
138− isOpen ? "flex" : "hidden"
139− }`}
140− >
141− Search
142− </button>
143− <button
144− onClick={() => toggleTray(!isOpen)}
145− className="z-10 fixed bottom-4 right-4 w-16 h-16 bg-ultraDeepWall flex items-center justify-center rounded-full"
146− >
147− {isOpen ? (
148− <svg
149− width="16"
150− height="16"
151− viewBox="0 0 16 16"
152− fill="none"
153− xmlns="http://www.w3.org/2000/svg"
154− >
155− <path
156− fill-rule="evenodd"
157− clip-rule="evenodd"
158− d="M0.39382 13.7045C-0.131273 14.2296 -0.131274 15.081 0.39382 15.6061C0.918913 16.1312 1.77026 16.1312 2.29535 15.6061L7.99999 9.90142L13.7047 15.6061C14.2297 16.1312 15.0811 16.1312 15.6062 15.6061C16.1313 15.081 16.1313 14.2296 15.6062 13.7046L9.90152 7.99989L15.6061 2.29535C16.1312 1.77026 16.1312 0.918913 15.6061 0.39382C15.081 -0.131273 14.2296 -0.131273 13.7045 0.39382L7.99999 6.09836L2.29548 0.393844C1.77038 -0.131249 0.919038 -0.13125 0.393945 0.393844C-0.131148 0.918937 -0.131148 1.77028 0.393945 2.29537L6.09846 7.99989L0.39382 13.7045Z"
159− fill="white"
160− />
161− </svg>
162− ) : (
163− <svg
164− width="16"
165− height="16"
166− viewBox="0 0 16 17"
167− fill="none"
168− xmlns="http://www.w3.org/2000/svg"
169− >
170− <rect width="16" height="3" rx="1.5" fill="white" />
171− <rect y="7" width="16" height="3" rx="1.5" fill="white" />
172− <rect y="14" width="16" height="3" rx="1.5" fill="white" />
173− </svg>
174− )}
175− </button>
176− </div>
177 </header>
127 </header>
178 );
128 );
179}
129}
components/MenuTray.js+77−20
@@ -1,47 +1,104 @@
1import classnames from "classnames";
1import classnames from "classnames";
2+import { useSwipeable } from "react-swipeable";
2
3
3−export default function MenuTray(props) {
4+export default function MenuTray({ isOpen, setTray, search, children }) {
4 // Locks document scrolling when menu is open
5 // Locks document scrolling when menu is open
5−
6 if (typeof document !== "undefined") {
6 if (typeof document !== "undefined") {
7− if (props.isOpen) {
7+ if (isOpen) {
8 document.body.style.overflow = "hidden";
8 document.body.style.overflow = "hidden";
9 } else {
9 } else {
10 document.body.style.overflow = "visible";
10 document.body.style.overflow = "visible";
11 }
11 }
12 }
12 }
13
13
14+ const handlers = useSwipeable({
15+ onSwipedLeft: (e) => setTray(false),
16+ });
17+
14 // Slides the tray in or out from the left
18 // Slides the tray in or out from the left
15 const trayClasses = classnames({
19 const trayClasses = classnames({
16− "tray-menu-open": props.isOpen,
20+ "tray-menu-open": isOpen,
17− "tray-menu-closed": !props.isOpen,
21+ "tray-menu-closed": !isOpen,
18 });
22 });
19
23
20 // Fades the background overlay in or out
24 // Fades the background overlay in or out
21 const overlayClasses = classnames({
25 const overlayClasses = classnames({
22− "tray-overlay-open": props.isOpen,
26+ "tray-overlay-open": isOpen,
23− "tray-overlay-closed": !props.isOpen,
27+ "tray-overlay-closed": !isOpen,
24 });
28 });
25
29
26 // Hides or shows the menu
30 // Hides or shows the menu
27 const menuClasses = classnames({
31 const menuClasses = classnames({
28− "menu-open": props.isOpen,
32+ "menu-open": isOpen,
29− "menu-closed": !props.isOpen,
33+ "menu-closed": !isOpen,
30 });
34 });
31
35
32 return (
36 return (
37+ <>
38+ <nav
39+ {...handlers}
33− <nav className={`z-10 w-screen h-screen top-0 left-0 fixed ${menuClasses}`}>
40+ className={`z-10 w-screen h-screen top-0 left-0 fixed block md:hidden ${menuClasses}`}
34− <div
35− onClick={() => props.toggleTray(!props.isOpen)}
36− className={`bg-washedWhite w-screen h-screen ${overlayClasses}`}
37− />
38− <div
39− className={`absolute bg-wall h-screen top-0 left-0 tray-menu-width overflow-y-scroll mb-24 ${trayClasses}`}
40 >
41 >
42+ <div
43+ onClick={() => setTray(!isOpen)}
44+ className={`bg-washedWhite w-screen h-screen ${overlayClasses}`}
45+ />
46+ <div
47+ className={`absolute bg-wall h-screen top-0 left-0 tray-menu-width overflow-y-scroll mb-24 ${trayClasses}`}
48+ >
49+ <div
50+ {...handlers}
41− <div className="flex flex-col px-4 md:px-8 pt-8 md:pt-10 lg:pt-12">
51+ className="flex flex-col px-4 md:px-8 pt-8 md:pt-10 lg:pt-12"
52+ >
42− {props.children}
53+ {children}
54+ </div>
43 </div>
55 </div>
44− </div>
56+
45− </nav>
57+ <button
58+ onClick={(e) => {
59+ e.stopPropagation();
60+ search.toggleSearch(e);
61+ }}
62+ className={`z-10 fixed px-4 items-center justify-center type-ui rounded-xl h-16 bg-white text-gray left-4 right-4 bottom-4 mobile-search-button-width ${
63+ isOpen ? "flex" : "hidden"
64+ }`}
65+ >
66+ Search
67+ </button>
68+ </nav>
69+ <button
70+ onClick={() => setTray(!isOpen)}
71+ className="z-10 fixed bottom-4 right-4 w-16 h-16 bg-ultraDeepWall flex items-center justify-center rounded-full"
72+ >
73+ {isOpen ? (
74+ <svg
75+ width="16"
76+ height="16"
77+ viewBox="0 0 16 16"
78+ fill="none"
79+ xmlns="http://www.w3.org/2000/svg"
80+ >
81+ <path
82+ fillRule="evenodd"
83+ clipRule="evenodd"
84+ d="M0.39382 13.7045C-0.131273 14.2296 -0.131274 15.081 0.39382 15.6061C0.918913 16.1312 1.77026 16.1312 2.29535 15.6061L7.99999 9.90142L13.7047 15.6061C14.2297 16.1312 15.0811 16.1312 15.6062 15.6061C16.1313 15.081 16.1313 14.2296 15.6062 13.7046L9.90152 7.99989L15.6061 2.29535C16.1312 1.77026 16.1312 0.918913 15.6061 0.39382C15.081 -0.131273 14.2296 -0.131273 13.7045 0.39382L7.99999 6.09836L2.29548 0.393844C1.77038 -0.131249 0.919038 -0.13125 0.393945 0.393844C-0.131148 0.918937 -0.131148 1.77028 0.393945 2.29537L6.09846 7.99989L0.39382 13.7045Z"
85+ fill="white"
86+ />
87+ </svg>
88+ ) : (
89+ <svg
90+ width="16"
91+ height="16"
92+ viewBox="0 0 16 17"
93+ fill="none"
94+ xmlns="http://www.w3.org/2000/svg"
95+ >
96+ <rect width="16" height="3" rx="1.5" fill="white" />
97+ <rect y="7" width="16" height="3" rx="1.5" fill="white" />
98+ <rect y="14" width="16" height="3" rx="1.5" fill="white" />
99+ </svg>
100+ )}
101+ </button>
102+ </>
46 );
103 );
47}
104}
components/Sidebar.js+10−56
@@ -3,7 +3,7 @@ import { useState } from "react";
3import MenuTray from "./MenuTray";
3import MenuTray from "./MenuTray";
4
4
5export default function Sidebar(props) {
5export default function Sidebar(props) {
6− const [isOpen, toggleTray] = useState(false);
6+ const [isOpen, setTray] = useState(false);
7 return (
7 return (
8 <>
8 <>
9 <div className="hidden md:flex flex-col w-96 bg-wall max-h-screen h-screen">
9 <div className="hidden md:flex flex-col w-96 bg-wall max-h-screen h-screen">
@@ -18,61 +18,15 @@ export default function Sidebar(props) {
18 </div>
18 </div>
19 </div>
19 </div>
20
20
21− <div className="block md:hidden">
22− <MenuTray isOpen={isOpen} toggleTray={toggleTray}>
21+ <MenuTray isOpen={isOpen} setTray={setTray}>
23− <header className="flex flex-shrink-0 justify-between items-center pb-8">
22+ <header className="flex flex-shrink-0 justify-between items-center pb-8">
24− <Link href="/">
23+ <Link href="/">
25− <a className="type-ui text-gray">Urbit</a>
24+ <a className="type-ui text-gray">Urbit</a>
26− </Link>
25+ </Link>
27− </header>
26+ </header>
28− {props.children}
27+ {props.children}
29− <div className="pt-64" />
28+ <div className="pt-64" />
30− </MenuTray>
29+ </MenuTray>
31− <button
32− onClick={(e) => {
33− e.stopPropagation();
34− props.search.toggleSearch(e);
35− }}
36− className={`z-10 fixed px-4 items-center justify-center type-ui rounded-xl h-16 bg-white text-gray left-4 right-4 bottom-4 mobile-search-button-width ${
37− isOpen ? "flex" : "hidden"
38− }`}
39− >
40− Search
41− </button>
42− <button
43− onClick={() => toggleTray(!isOpen)}
44− className="z-10 fixed bottom-4 right-4 w-16 h-16 bg-ultraDeepWall flex items-center justify-center rounded-full"
45− >
46− {isOpen ? (
47− <svg
48− width="16"
49− height="16"
50− viewBox="0 0 16 16"
51− fill="none"
52− xmlns="http://www.w3.org/2000/svg"
53− >
54− <path
55− fill-rule="evenodd"
56− clip-rule="evenodd"
57− d="M0.39382 13.7045C-0.131273 14.2296 -0.131274 15.081 0.39382 15.6061C0.918913 16.1312 1.77026 16.1312 2.29535 15.6061L7.99999 9.90142L13.7047 15.6061C14.2297 16.1312 15.0811 16.1312 15.6062 15.6061C16.1313 15.081 16.1313 14.2296 15.6062 13.7046L9.90152 7.99989L15.6061 2.29535C16.1312 1.77026 16.1312 0.918913 15.6061 0.39382C15.081 -0.131273 14.2296 -0.131273 13.7045 0.39382L7.99999 6.09836L2.29548 0.393844C1.77038 -0.131249 0.919038 -0.13125 0.393945 0.393844C-0.131148 0.918937 -0.131148 1.77028 0.393945 2.29537L6.09846 7.99989L0.39382 13.7045Z"
58− fill="white"
59− />
60− </svg>
61− ) : (
62− <svg
63− width="16"
64− height="16"
65− viewBox="0 0 16 17"
66− fill="none"
67− xmlns="http://www.w3.org/2000/svg"
68− >
69− <rect width="16" height="3" rx="1.5" fill="white" />
70− <rect y="7" width="16" height="3" rx="1.5" fill="white" />
71− <rect y="14" width="16" height="3" rx="1.5" fill="white" />
72− </svg>
73− )}
74− </button>
75− </div>
76 </>
30 </>
77 );
31 );
78}
32}
package-lock.json+5
@@ -5694,6 +5694,11 @@
5694 "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz",
5694 "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz",
5695 "integrity": "sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg=="
5695 "integrity": "sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg=="
5696 },
5696 },
5697+ "react-swipeable": {
5698+ "version": "6.1.2",
5699+ "resolved": "https://registry.npmjs.org/react-swipeable/-/react-swipeable-6.1.2.tgz",
5700+ "integrity": "sha512-vfZtOZNivwd/aI+ZZH1Grx0eQBdbV1UI3pB9p65jbW5guHHdSIPpKsND6XmaiZXP5REOOc9Ckfr36ChswPqwsA=="
5701+ },
5697 "readable-stream": {
5702 "readable-stream": {
5698 "version": "2.3.7",
5703 "version": "2.3.7",
5699 "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
5704 "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
package.json+1
@@ -26,6 +26,7 @@
26 "react": "17.0.2",
26 "react": "17.0.2",
27 "react-dom": "17.0.2",
27 "react-dom": "17.0.2",
28 "react-hotkeys": "^2.0.0",
28 "react-hotkeys": "^2.0.0",
29+ "react-swipeable": "^6.1.2",
29 "remark": "^13.0.0",
30 "remark": "^13.0.0",
30 "remark-gfm": "^1.0.0",
31 "remark-gfm": "^1.0.0",
31 "remark-heading-id": "^1.0.0",
32 "remark-heading-id": "^1.0.0",