|
1 | 1 | module Link = Next.Link
|
2 | 2 |
|
3 |
| -let link = "no-underline block hover:cursor-pointer hover:text-fire-30 text-gray-40 mb-px" |
| 3 | +let link = "no-underline block hover:cursor-pointer hover:text-fire-30 mb-px" |
4 | 4 | let activeLink = "font-medium text-fire-30 border-b border-fire"
|
5 | 5 |
|
6 | 6 | let linkOrActiveLink = (~target, ~route) => target === route ? activeLink : link
|
7 | 7 |
|
8 | 8 | let linkOrActiveLinkSubroute = (~target, ~route) =>
|
9 | 9 | String.startsWith(route, target) ? activeLink : link
|
10 | 10 |
|
11 |
| -let isActiveLink = (~route: string, ~href: string) => { |
12 |
| - route == href ? activeLink : link |
| 11 | +let isActiveLink = (~includes: string, ~excludes: option<string>=?, ~route: string) => { |
| 12 | + // includes means we want the lnk to be active if it contains the expected text |
| 13 | + let includes = route->String.includes(includes) |
| 14 | + // excludes allows us to not have links be active even if they do have the includes text |
| 15 | + let excludes = switch excludes { |
| 16 | + | Some(excludes) => route->String.includes(excludes) |
| 17 | + | None => false |
| 18 | + } |
| 19 | + includes && !excludes ? activeLink : link |
13 | 20 | }
|
14 | 21 |
|
| 22 | +let isDocRoute = (~route) => |
| 23 | + route->String.includes("/docs/") || route->String.includes("/syntax-lookup") |
| 24 | + |
| 25 | +let isDocRouteActive = (~route) => isDocRoute(~route) ? activeLink : link |
| 26 | + |
15 | 27 | module MobileNav = {
|
16 | 28 | @react.component
|
17 | 29 | let make = (~route: string) => {
|
18 |
| - let base = "font-normal mx-4 py-5 text-gray-20 border-b border-gray-80" |
| 30 | + let base = "font-normal mx-4 py-5 text-gray-40 border-b border-gray-80" |
19 | 31 | let extLink = "block hover:cursor-pointer hover:text-white text-gray-60"
|
20 |
| - <div className="border-gray-80 border-t"> |
| 32 | + <div className="border-gray-80 border-tn"> |
21 | 33 | <ul>
|
22 | 34 | <li className=base>
|
23 | 35 | <Link href="/try" className={linkOrActiveLink(~target="/try", ~route)}>
|
@@ -73,98 +85,117 @@ let make = (~fixed=true, ~isOverlayOpen: bool, ~setOverlayOpen: (bool => bool) =
|
73 | 85 | let fixedNav = fixed ? "fixed top-0" : "relative"
|
74 | 86 |
|
75 | 87 | <>
|
76 |
| - <nav |
| 88 | + <header |
77 | 89 | id="header"
|
78 | 90 | style={ReactDOMStyle.make(~minWidth, ())}
|
79 |
| - className={fixedNav ++ " items-center z-50 px-4 flex xs:justify-center w-full h-16 bg-gray-90 shadow text-white-80 text-14 transition duration-300 ease-out group-[.nav-disappear]:-translate-y-16 md:group-[.nav-disappear]:transform-none"}> |
80 |
| - <div className="flex justify-between items-center h-full w-full max-w-1280"> |
81 |
| - <div className="h-8 w-8 lg:h-10 lg:w-32"> |
82 |
| - <a |
83 |
| - href="/" |
84 |
| - className="block hover:cursor-pointer w-full h-full flex justify-center items-center font-bold"> |
85 |
| - < img src="/static/[email protected]" className="lg:hidden" /> |
86 |
| - < img src="/static/[email protected]" className="hidden lg:block" /> |
87 |
| - </a> |
88 |
| - </div> |
89 |
| - /* Desktop horizontal navigation */ |
90 |
| - <div |
91 |
| - className="flex items-center xs:justify-between w-full bg-gray-90 sm:h-auto sm:relative"> |
92 |
| - <div |
93 |
| - className="flex ml-10 space-x-5 w-full max-w-320" |
94 |
| - style={ReactDOMStyle.make(~maxWidth="26rem", ())}> |
95 |
| - <Link |
96 |
| - href={`/docs/manual/${version}/introduction`} |
97 |
| - className={isActiveLink(~route, ~href=`/docs/manual/${version}/introduction`)}> |
98 |
| - {React.string("Docs")} |
99 |
| - </Link> |
100 |
| - <Link |
101 |
| - href={`/docs/manual/${version}/api`} |
102 |
| - className={isActiveLink(~route, ~href=`/docs/manual/${version}/api`)}> |
103 |
| - {React.string("API")} |
104 |
| - </Link> |
105 |
| - <Link href={`/syntax-lookup`} className={isActiveLink(~route, ~href=`/syntax-lookup`)}> |
106 |
| - {React.string("Syntax")} |
107 |
| - </Link> |
108 |
| - <Link |
109 |
| - href={`/docs/react/latest/introduction`} |
110 |
| - className={isActiveLink(~route, ~href=`/docs/react/latest/introduction`)}> |
111 |
| - {React.string("React")} |
112 |
| - </Link> |
113 |
| - <Link |
114 |
| - href="/try" |
115 |
| - className={"hidden xs:block " ++ linkOrActiveLink(~target="/try", ~route)}> |
116 |
| - {React.string("Playground")} |
117 |
| - </Link> |
118 |
| - <Link |
119 |
| - href="/blog" |
120 |
| - className={"hidden xs:block " ++ linkOrActiveLinkSubroute(~target="/blog", ~route)}> |
121 |
| - {React.string("Blog")} |
122 |
| - </Link> |
123 |
| - <Link |
124 |
| - href="/community/overview" |
125 |
| - className={"hidden xs:block " ++ linkOrActiveLink(~target="/community", ~route)}> |
126 |
| - {React.string("Community")} |
127 |
| - </Link> |
| 91 | + className={fixedNav ++ " items-center z-50 w-full transition duration-300 ease-out group-[.nav-disappear]:-translate-y-16 md:group-[.nav-disappear]:transform-none"}> |
| 92 | + <nav className="px-4 flex xs:justify-center bg-gray-90 shadow h-16 text-white-80 text-14"> |
| 93 | + <div className="flex justify-between items-center h-full w-full max-w-1280"> |
| 94 | + <div className="h-8 w-8 lg:h-10 lg:w-32"> |
| 95 | + <a |
| 96 | + href="/" |
| 97 | + className="block hover:cursor-pointer w-full h-full flex justify-center items-center font-bold"> |
| 98 | + < img src="/static/[email protected]" className="lg:hidden" /> |
| 99 | + < img src="/static/[email protected]" className="hidden lg:block" /> |
| 100 | + </a> |
128 | 101 | </div>
|
129 |
| - <div className="md:flex flex items-center"> |
130 |
| - <Search /> |
131 |
| - <div className="hidden md:flex items-center ml-5"> |
132 |
| - <a href=Constants.githubHref rel="noopener noreferrer" className={"mr-5 " ++ link}> |
133 |
| - <Icon.GitHub className="w-6 h-6 opacity-50 hover:opacity-100" /> |
134 |
| - </a> |
135 |
| - <a href=Constants.xHref rel="noopener noreferrer" className={"mr-5 " ++ link}> |
136 |
| - <Icon.X className="w-6 h-6 opacity-50 hover:opacity-100" /> |
137 |
| - </a> |
138 |
| - <a href=Constants.blueSkyHref rel="noopener noreferrer" className={"mr-5 " ++ link}> |
139 |
| - <Icon.Bluesky className="w-6 h-6 opacity-50 hover:opacity-100" /> |
140 |
| - </a> |
141 |
| - <a href=Constants.discourseHref rel="noopener noreferrer" className=link> |
142 |
| - <Icon.Discourse className="w-6 h-6 opacity-50 hover:opacity-100" /> |
143 |
| - </a> |
| 102 | + /* Desktop horizontal navigation */ |
| 103 | + <div |
| 104 | + className="flex items-center xs:justify-between w-full bg-gray-90 sm:h-auto sm:relative"> |
| 105 | + <div |
| 106 | + className="flex ml-10 space-x-5 w-full max-w-320 text-gray-40" |
| 107 | + style={ReactDOMStyle.make(~maxWidth="26rem", ())}> |
| 108 | + <Link |
| 109 | + href={`/docs/manual/${version}/introduction`} className={isDocRouteActive(~route)}> |
| 110 | + {React.string("Docs")} |
| 111 | + </Link> |
| 112 | + |
| 113 | + <Link |
| 114 | + href="/try" |
| 115 | + className={"hidden xs:block " ++ linkOrActiveLink(~target="/try", ~route)}> |
| 116 | + {React.string("Playground")} |
| 117 | + </Link> |
| 118 | + <Link |
| 119 | + href="/blog" |
| 120 | + className={"hidden xs:block " ++ linkOrActiveLinkSubroute(~target="/blog", ~route)}> |
| 121 | + {React.string("Blog")} |
| 122 | + </Link> |
| 123 | + <Link |
| 124 | + href="/community/overview" |
| 125 | + className={"hidden xs:block " ++ linkOrActiveLink(~target="/community", ~route)}> |
| 126 | + {React.string("Community")} |
| 127 | + </Link> |
| 128 | + </div> |
| 129 | + <div className="md:flex flex items-center text-gray-60"> |
| 130 | + <Search /> |
| 131 | + <div className="hidden md:flex items-center ml-5"> |
| 132 | + <a href=Constants.githubHref rel="noopener noreferrer" className={"mr-5 " ++ link}> |
| 133 | + <Icon.GitHub className="w-6 h-6 opacity-50 hover:opacity-100" /> |
| 134 | + </a> |
| 135 | + <a href=Constants.xHref rel="noopener noreferrer" className={"mr-5 " ++ link}> |
| 136 | + <Icon.X className="w-6 h-6 opacity-50 hover:opacity-100" /> |
| 137 | + </a> |
| 138 | + <a href=Constants.blueSkyHref rel="noopener noreferrer" className={"mr-5 " ++ link}> |
| 139 | + <Icon.Bluesky className="w-6 h-6 opacity-50 hover:opacity-100" /> |
| 140 | + </a> |
| 141 | + <a href=Constants.discourseHref rel="noopener noreferrer" className=link> |
| 142 | + <Icon.Discourse className="w-6 h-6 opacity-50 hover:opacity-100" /> |
| 143 | + </a> |
| 144 | + </div> |
144 | 145 | </div>
|
145 | 146 | </div>
|
146 | 147 | </div>
|
147 |
| - </div> |
148 |
| - /* Burger Button */ |
149 |
| - <button |
150 |
| - className="h-full px-4 xs:hidden flex items-center hover:text-white" |
151 |
| - onClick={evt => { |
152 |
| - ReactEvent.Mouse.preventDefault(evt) |
153 |
| - toggleOverlay() |
154 |
| - }}> |
155 |
| - <Icon.DrawerDots |
156 |
| - className={"h-1 w-auto block " ++ (isOverlayOpen ? "text-fire" : "text-gray-60")} |
157 |
| - /> |
158 |
| - </button> |
159 |
| - /* Mobile overlay */ |
160 |
| - <div |
161 |
| - style={ReactDOMStyle.make(~minWidth, ~top="4rem", ())} |
162 |
| - className={( |
163 |
| - isOverlayOpen ? "flex" : "hidden" |
164 |
| - ) ++ " sm:hidden flex-col fixed top-0 left-0 h-full w-full z-50 sm:w-9/12 bg-gray-100 sm:h-auto sm:flex sm:relative sm:flex-row sm:justify-between"}> |
165 |
| - <MobileNav route /> |
166 |
| - </div> |
167 |
| - </nav> |
| 148 | + /* Burger Button */ |
| 149 | + <button |
| 150 | + className="h-full px-4 xs:hidden flex items-center hover:text-white" |
| 151 | + onClick={evt => { |
| 152 | + ReactEvent.Mouse.preventDefault(evt) |
| 153 | + toggleOverlay() |
| 154 | + }}> |
| 155 | + <Icon.DrawerDots |
| 156 | + className={"h-1 w-auto block " ++ (isOverlayOpen ? "text-fire" : "text-gray-60")} |
| 157 | + /> |
| 158 | + </button> |
| 159 | + /* Mobile overlay */ |
| 160 | + <div |
| 161 | + style={ReactDOMStyle.make(~minWidth, ~top="4rem", ())} |
| 162 | + className={( |
| 163 | + isOverlayOpen ? "flex" : "hidden" |
| 164 | + ) ++ " sm:hidden flex-col fixed top-0 left-0 h-full w-full z-50 sm:w-9/12 bg-gray-100 sm:h-auto sm:flex sm:relative sm:flex-row sm:justify-between"}> |
| 165 | + <MobileNav route /> |
| 166 | + </div> |
| 167 | + </nav> |
| 168 | + // This is a subnav for documentation pages |
| 169 | + {isDocRoute(~route) |
| 170 | + ? <nav |
| 171 | + id="docs-subnav" |
| 172 | + className="bg-white z-50 px-4 w-full h-12 shadow text-gray-60 text-12 md:text-14 transition duration-300 ease-out group-[.nav-disappear]:-translate-y-16 md:group-[.nav-disappear]:transform-none"> |
| 173 | + <div |
| 174 | + className="pl-30 flex gap-6 lg:gap-10 items-center h-full w-full max-w-1280 m-auto"> |
| 175 | + <Link |
| 176 | + href={`/docs/manual/${version}/introduction`} |
| 177 | + className={isActiveLink(~includes="/docs/manual/", ~excludes="/api", ~route)}> |
| 178 | + {React.string("Language Manual")} |
| 179 | + </Link> |
| 180 | + <Link |
| 181 | + href={`/docs/manual/${version}/api`} |
| 182 | + className={isActiveLink(~includes="/api", ~route)}> |
| 183 | + {React.string("API")} |
| 184 | + </Link> |
| 185 | + <Link |
| 186 | + href={`/syntax-lookup`} |
| 187 | + className={isActiveLink(~includes="/syntax-lookup", ~route)}> |
| 188 | + {React.string("Syntax Lookup")} |
| 189 | + </Link> |
| 190 | + <Link |
| 191 | + href={`/docs/react/latest/introduction`} |
| 192 | + className={isActiveLink(~includes="/docs/react/", ~route)}> |
| 193 | + {React.string("React")} |
| 194 | + </Link> |
| 195 | + </div> |
| 196 | + </nav> |
| 197 | + : React.null} |
| 198 | + </header> |
168 | 199 | </>
|
169 | 200 | }
|
170 | 201 |
|
|
0 commit comments