|
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