Skip to content

Bring back collapsible navbar on mobile layout #1034

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 106 additions & 78 deletions src/common/App.res
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,21 @@
hljs.registerLanguage('diff', diff)
`)

module EnableCollapsibleNavbar = {
@react.component
let make = (~children) => {
let scrollDir = Hooks.useScrollDirection()

<div
className={switch scrollDir {
| Up(_) => "group nav-appear"
| Down(_) => "group nav-disappear"
}}>
children
</div>
}
}

type pageComponent = React.component<{.}>
type pageProps = {.}

Expand All @@ -55,99 +70,112 @@ let make = (props: props): React.element => {
| {base: [], pagepath: []} => <LandingPageLayout> content </LandingPageLayout>
// docs routes
| {base: ["docs", "manual"], pagepath} =>
// check if it's an api route
switch pagepath[0] {
| Some("api") =>
switch url->Url.getVersionString {
| ("v11.0.0" | "v12.0.0") as version =>
switch (Array.length(pagepath), pagepath[1]) {
| (1, _) => <ApiOverviewLayout.Docs version> content </ApiOverviewLayout.Docs>
<EnableCollapsibleNavbar>
// check if it's an api route
{switch pagepath[0] {
| Some("api") =>
switch url->Url.getVersionString {
| ("v11.0.0" | "v12.0.0") as version =>
switch (Array.length(pagepath), pagepath[1]) {
| (1, _) => <ApiOverviewLayout.Docs version> content </ApiOverviewLayout.Docs>
| _ => content
}
| "v8.0.0" =>
switch (Array.length(pagepath), pagepath[1]) {
| (1, _) => <ApiOverviewLayout8_0_0.Docs> content </ApiOverviewLayout8_0_0.Docs>
| (2, Some("js")) => <JsDocsLayout8_0_0.Prose> content </JsDocsLayout8_0_0.Prose>
| (2, Some("belt")) => <BeltDocsLayout8_0_0.Prose> content </BeltDocsLayout8_0_0.Prose>
| (_, Some("js")) => <JsDocsLayout8_0_0.Docs> content </JsDocsLayout8_0_0.Docs>
| (_, Some("belt")) => <BeltDocsLayout8_0_0.Docs> content </BeltDocsLayout8_0_0.Docs>
| (_, Some("dom")) => <DomDocsLayout8_0_0.Docs> content </DomDocsLayout8_0_0.Docs>
| _ => React.null
}
| "v9.0.0" =>
switch (Array.length(pagepath), pagepath[1]) {
| (1, _) => <ApiOverviewLayout9_0_0.Docs> content </ApiOverviewLayout9_0_0.Docs>
| (2, Some("js")) => <JsDocsLayout9_0_0.Prose> content </JsDocsLayout9_0_0.Prose>
| (2, Some("belt")) => <BeltDocsLayout9_0_0.Prose> content </BeltDocsLayout9_0_0.Prose>
| (_, Some("js")) => <JsDocsLayout9_0_0.Docs> content </JsDocsLayout9_0_0.Docs>
| (_, Some("belt")) => <BeltDocsLayout9_0_0.Docs> content </BeltDocsLayout9_0_0.Docs>
| (_, Some("dom")) => <DomDocsLayout9_0_0.Docs> content </DomDocsLayout9_0_0.Docs>
| _ => React.null
}
| "v10.0.0" =>
switch (Array.length(pagepath), pagepath[1]) {
| (1, _) => <ApiOverviewLayout10_0_0.Docs> content </ApiOverviewLayout10_0_0.Docs>
| (2, Some("js")) => <JsDocsLayout10_0_0.Prose> content </JsDocsLayout10_0_0.Prose>
| (2, Some("belt")) => <BeltDocsLayout10_0_0.Prose> content </BeltDocsLayout10_0_0.Prose>
| (_, Some("js")) => <JsDocsLayout10_0_0.Docs> content </JsDocsLayout10_0_0.Docs>
| (_, Some("belt")) => <BeltDocsLayout10_0_0.Docs> content </BeltDocsLayout10_0_0.Docs>
| (_, Some("dom")) => <DomDocsLayout10_0_0.Docs> content </DomDocsLayout10_0_0.Docs>
| _ => React.null
}
| _ => content
}
| "v8.0.0" =>
switch (Array.length(pagepath), pagepath[1]) {
| (1, _) => <ApiOverviewLayout8_0_0.Docs> content </ApiOverviewLayout8_0_0.Docs>
| (2, Some("js")) => <JsDocsLayout8_0_0.Prose> content </JsDocsLayout8_0_0.Prose>
| (2, Some("belt")) => <BeltDocsLayout8_0_0.Prose> content </BeltDocsLayout8_0_0.Prose>
| (_, Some("js")) => <JsDocsLayout8_0_0.Docs> content </JsDocsLayout8_0_0.Docs>
| (_, Some("belt")) => <BeltDocsLayout8_0_0.Docs> content </BeltDocsLayout8_0_0.Docs>
| (_, Some("dom")) => <DomDocsLayout8_0_0.Docs> content </DomDocsLayout8_0_0.Docs>
| _ => React.null
}
| "v9.0.0" =>
switch (Array.length(pagepath), pagepath[1]) {
| (1, _) => <ApiOverviewLayout9_0_0.Docs> content </ApiOverviewLayout9_0_0.Docs>
| (2, Some("js")) => <JsDocsLayout9_0_0.Prose> content </JsDocsLayout9_0_0.Prose>
| (2, Some("belt")) => <BeltDocsLayout9_0_0.Prose> content </BeltDocsLayout9_0_0.Prose>
| (_, Some("js")) => <JsDocsLayout9_0_0.Docs> content </JsDocsLayout9_0_0.Docs>
| (_, Some("belt")) => <BeltDocsLayout9_0_0.Docs> content </BeltDocsLayout9_0_0.Docs>
| (_, Some("dom")) => <DomDocsLayout9_0_0.Docs> content </DomDocsLayout9_0_0.Docs>
| _ =>
switch url->Url.getVersionString {
| "v8.0.0" =>
<ManualDocsLayout.V800 frontmatter={component->frontmatter}>
content
</ManualDocsLayout.V800>
| "v9.0.0" =>
<ManualDocsLayout.V900 frontmatter={component->frontmatter}>
content
</ManualDocsLayout.V900>
| "v10.0.0" =>
<ManualDocsLayout.V1000 frontmatter={component->frontmatter}>
content
</ManualDocsLayout.V1000>
| "v11.0.0" =>
<ManualDocsLayout.V1100 frontmatter={component->frontmatter}>
content
</ManualDocsLayout.V1100>
| "v12.0.0" =>
<ManualDocsLayout.V1200 frontmatter={component->frontmatter}>
content
</ManualDocsLayout.V1200>
| _ => React.null
}
| "v10.0.0" =>
switch (Array.length(pagepath), pagepath[1]) {
| (1, _) => <ApiOverviewLayout10_0_0.Docs> content </ApiOverviewLayout10_0_0.Docs>
| (2, Some("js")) => <JsDocsLayout10_0_0.Prose> content </JsDocsLayout10_0_0.Prose>
| (2, Some("belt")) => <BeltDocsLayout10_0_0.Prose> content </BeltDocsLayout10_0_0.Prose>
| (_, Some("js")) => <JsDocsLayout10_0_0.Docs> content </JsDocsLayout10_0_0.Docs>
| (_, Some("belt")) => <BeltDocsLayout10_0_0.Docs> content </BeltDocsLayout10_0_0.Docs>
| (_, Some("dom")) => <DomDocsLayout10_0_0.Docs> content </DomDocsLayout10_0_0.Docs>
| _ => React.null
}
| _ => content
}
| _ =>
switch url->Url.getVersionString {
| "v8.0.0" =>
<ManualDocsLayout.V800 frontmatter={component->frontmatter}>
content
</ManualDocsLayout.V800>
| "v9.0.0" =>
<ManualDocsLayout.V900 frontmatter={component->frontmatter}>
content
</ManualDocsLayout.V900>
| "v10.0.0" =>
<ManualDocsLayout.V1000 frontmatter={component->frontmatter}>
}}
</EnableCollapsibleNavbar>

| {base: ["docs", "react"], version} =>
<EnableCollapsibleNavbar>
{switch version {
| Latest =>
<ReactDocsLayout.Latest frontmatter={component->frontmatter}>
content
</ManualDocsLayout.V1000>
| "v11.0.0" =>
<ManualDocsLayout.V1100 frontmatter={component->frontmatter}>
</ReactDocsLayout.Latest>
| Version("v0.10.0") =>
<ReactDocsLayout.V0100 frontmatter={component->frontmatter}>
content
</ManualDocsLayout.V1100>
| "v12.0.0" =>
<ManualDocsLayout.V1200 frontmatter={component->frontmatter}>
</ReactDocsLayout.V0100>
| Version("v0.11.0") =>
<ReactDocsLayout.V0110 frontmatter={component->frontmatter}>
content
</ManualDocsLayout.V1200>
</ReactDocsLayout.V0110>
| _ => React.null
}
}

| {base: ["docs", "react"], version} =>
switch version {
| Latest =>
<ReactDocsLayout.Latest frontmatter={component->frontmatter}>
content
</ReactDocsLayout.Latest>
| Version("v0.10.0") =>
<ReactDocsLayout.V0100 frontmatter={component->frontmatter}> content </ReactDocsLayout.V0100>
| Version("v0.11.0") =>
<ReactDocsLayout.V0110 frontmatter={component->frontmatter}> content </ReactDocsLayout.V0110>
| _ => React.null
}
}}
</EnableCollapsibleNavbar>

// common routes
| {base} =>
switch List.fromArray(base) {
| list{"community", ..._rest} =>
<CommunityLayout frontmatter={component->frontmatter}> content </CommunityLayout>
<EnableCollapsibleNavbar>
<CommunityLayout frontmatter={component->frontmatter}> content </CommunityLayout>
</EnableCollapsibleNavbar>

| list{"try"} => content
| list{"blog"} => content // Blog implements its own layout as well
| list{"syntax-lookup"} => content
| list{"packages"} => content
| list{"blog", ..._rest} => // Here, the layout will be handled by the Blog_Article component
// to keep the frontmatter parsing etc in one place
content
| list{"blog"} => <EnableCollapsibleNavbar> content </EnableCollapsibleNavbar> // Blog implements its own layout as well
| list{"syntax-lookup"} => <EnableCollapsibleNavbar> content </EnableCollapsibleNavbar>
| list{"packages"} => <EnableCollapsibleNavbar> content </EnableCollapsibleNavbar>
| list{"blog", ..._rest} =>
<EnableCollapsibleNavbar>
// Here, the layout will be handled by the Blog_Article component
// to keep the frontmatter parsing etc in one place
content
</EnableCollapsibleNavbar>
| _ =>
let fm = component->frontmatter->DocFrontmatter.decode
let title = switch url {
Expand Down
49 changes: 34 additions & 15 deletions src/common/Hooks.res
Original file line number Diff line number Diff line change
Expand Up @@ -38,31 +38,50 @@ let useScrollDirection = (~topMargin=80, ~threshold=20) => {
}))

React.useEffect(() => {
let onScroll = _e => {
let onScroll = e => {
let eventType = (e["type"] :> string)

setScrollDir(prev => {
let scrollY = Webapi.Window.scrollY
let enterTopMargin = scrollY <= topMargin

let action = switch prev {
| Up(_) if enterTopMargin => Skip
| Down(_) if enterTopMargin => EnterTop
| Up({scrollY: prevScrollY}) if prevScrollY < scrollY => UpToDown
| Up({scrollY: prevScrollY}) if prevScrollY - threshold >= scrollY => KeepUp
| Down({scrollY: prevScrollY}) if scrollY < prevScrollY => DownToUp
| Down({scrollY: prevScrollY}) if scrollY - threshold >= prevScrollY => KeepDown
| _ => Skip
}
if eventType === "reset-scroll-direction" {
Up({scrollY: scrollY})
} else {
let action = switch prev {
| Up(_) if enterTopMargin => Skip
| Down(_) if enterTopMargin => EnterTop
| Up({scrollY: prevScrollY}) if prevScrollY < scrollY => UpToDown
| Up({scrollY: prevScrollY}) if prevScrollY - threshold >= scrollY => KeepUp
| Down({scrollY: prevScrollY}) if scrollY < prevScrollY => DownToUp
| Down({scrollY: prevScrollY}) if scrollY - threshold >= prevScrollY => KeepDown
| _ => Skip
}

switch action {
| Skip => prev
| EnterTop | DownToUp | KeepUp => Up({scrollY: scrollY})
| UpToDown | KeepDown => Down({scrollY: scrollY})
switch action {
| Skip => prev
| EnterTop | DownToUp | KeepUp => Up({scrollY: scrollY})
| UpToDown | KeepDown => Down({scrollY: scrollY})
}
}
})
}
Webapi.Window.addEventListener("scroll", onScroll)
Some(() => Webapi.Window.removeEventListener("scroll", onScroll))
Webapi.Window.addEventListener("reset-scroll-direction", onScroll)
Some(
() => {
Webapi.Window.removeEventListener("scroll", onScroll)
Webapi.Window.removeEventListener("reset-scroll-direction", onScroll)
},
)
}, [topMargin, threshold])

scrollDir
}

let useResetScrollDirection = () => {
React.useCallback(() => {
// We really need better handling for plain DOM...
%raw(`window.dispatchEvent(new Event("reset-scroll-direction"))`)
}, [])
}
2 changes: 1 addition & 1 deletion src/components/Navigation.res
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ let make = (~fixed=true, ~isOverlayOpen: bool, ~setOverlayOpen: (bool => bool) =
<header
id="header"
style={ReactDOMStyle.make(~minWidth, ())}
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"}>
className={fixedNav ++ " items-center z-50 w-full transition duration-300 ease-out group-[.nav-disappear]:-translate-y-16 group-[.nav-disappear]:pointer-events-none md:group-[.nav-disappear]:transform-none"}>
<nav className="px-4 flex xs:justify-center bg-gray-90 shadow h-16 text-white-80 text-14">
<div className="flex justify-between items-center h-full w-full max-w-1280">
<div className="h-8 w-8 lg:h-10 lg:w-32">
Expand Down
8 changes: 6 additions & 2 deletions src/layouts/SidebarLayout.res
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,12 @@ let make = (

let breadcrumbs = breadcrumbs->Option.mapOr(React.null, crumbs => <BreadCrumbs crumbs />)

let resetScrollDirection = Hooks.useResetScrollDirection()
let (_isSidebarOpen, setSidebarOpen) = sidebarState
let toggleSidebar = () => setSidebarOpen(prev => !prev)
let toggleSidebar = () => {
setSidebarOpen(prev => !prev)
resetScrollDirection()
}

React.useEffect(() => {
open Next.Router.Events
Expand Down Expand Up @@ -313,7 +317,7 @@ let make = (
<main className="px-4 w-full pt-20 md:ml-12 lg:mr-8 mb-32 md:max-w-576 lg:max-w-740">
//width of the right content part
<div
className={"z-10 fixed border-b shadow top-[112px] left-0 pl-4 bg-white w-full py-4 md:relative md:border-none md:shadow-none md:p-0 md:top-auto flex items-center transition duration-300 ease-out group-[.nav-disappear]:-translate-y-32 md:group-[.nav-disappear]:transform-none"}>
className={"z-10 fixed border-b shadow top-[112px] left-0 pl-4 bg-white w-full py-4 md:relative md:border-none md:shadow-none md:p-0 md:top-auto flex items-center transition duration-300 ease-out group-[.nav-disappear]:-translate-y-[112px] md:group-[.nav-disappear]:transform-none"}>
<MobileDrawerButton hidden=isNavOpen onClick={handleDrawerButtonClick} />
<div
className="truncate overflow-x-auto touch-scroll flex items-center space-x-4 md:justify-between mr-4 w-full">
Expand Down