Skip to content

Commit

Permalink
Hide top nav on reading docs on mobile
Browse files Browse the repository at this point in the history
The top navigation disappears when you scroll down and reappears when you scroll up.
Like browsers, and many content services like Medium,

This is a necessary feature for mobile readers with a small viewport.

Also made some code changes here to add memoization,
because the scroll event is highly frequant.

`React.useState` preserves the reference of the set dispatcher, but doesn't for the surrounding tuple.
Adding a few memo, most children, except for components that are cheap to render, are not re-rendered.

Also no deep re-render, the appearance state is propagated via old good CSS cascading rather than React state.
  • Loading branch information
cometkim committed Apr 21, 2024
1 parent 3987e81 commit 6cb18fb
Show file tree
Hide file tree
Showing 14 changed files with 69 additions and 39 deletions.
4 changes: 2 additions & 2 deletions src/Blog.res
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ let default = (props: props): React.element => {
</>
}

let overlayState = React.useState(() => false)
let (isOverlayOpen, setOverlayOpen) = React.useState(() => false)
let title = "Blog | ReScript Documentation"

<>
Expand All @@ -298,7 +298,7 @@ let default = (props: props): React.element => {
/>
<div className="mt-16 pt-2">
<div className="text-gray-80 text-18">
<Navigation overlayState />
<Navigation isOverlayOpen setOverlayOpen />
<div className="flex justify-center overflow-hidden">
<main className="min-w-320 lg:align-center w-full lg:px-0 max-w-1280 pb-48">
<MdxProvider components=MarkdownComponents.default>
Expand Down
8 changes: 4 additions & 4 deletions src/Packages.res
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,8 @@ type state =

let scrollToTop: unit => unit = %raw(`function() {
window.scroll({
top: 0,
left: 0,
top: 0,
left: 0,
behavior: 'smooth'
});
}
Expand Down Expand Up @@ -462,7 +462,7 @@ let default = (props: props) => {
None
}, [state])

let overlayState = React.useState(() => false)
let (isOverlayOpen, setOverlayOpen) = React.useState(() => false)
<>
<Meta
siteName="ReScript Packages"
Expand All @@ -471,7 +471,7 @@ let default = (props: props) => {
/>
<div className="mt-16 pt-2">
<div className="text-gray-80 text-18">
<Navigation overlayState />
<Navigation isOverlayOpen setOverlayOpen />
<div className="flex overflow-hidden">
<div
className="flex justify-between min-w-320 px-4 pt-16 lg:align-center w-full lg:px-8 pb-48">
Expand Down
4 changes: 2 additions & 2 deletions src/SyntaxLookup.res
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ let default = (props: props) => {
onSearchValueChange("")
}

let overlayState = React.useState(() => false)
let (isOverlayOpen, setOverlayOpen) = React.useState(() => false)
let title = "Syntax Lookup | ReScript Documentation"

let content =
Expand Down Expand Up @@ -372,7 +372,7 @@ let default = (props: props) => {
/>
<div className="mt-4 xs:mt-16">
<div className="text-gray-80">
<Navigation overlayState />
<Navigation isOverlayOpen setOverlayOpen />
<div className="flex xs:justify-center overflow-hidden pb-48">
<main className="mt-16 min-w-320 lg:align-center w-full px-4 md:px-8 max-w-1280">
<MdxProvider components=MarkdownComponents.default>
Expand Down
4 changes: 2 additions & 2 deletions src/Try.res
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
type props = {versions: array<string>}

let default = props => {
let overlayState = React.useState(() => false)
let (isOverlayOpen, setOverlayOpen) = React.useState(() => false)

let lazyPlayground = Next.Dynamic.dynamic(
async () => await Js.import(Playground.make),
Expand All @@ -20,7 +20,7 @@ let default = props => {
</Next.Head>
<div className="text-16">
<div className="text-gray-40 text-14">
<Navigation fixed=false overlayState />
<Navigation fixed=false isOverlayOpen setOverlayOpen />
playground
</div>
</div>
Expand Down
1 change: 0 additions & 1 deletion src/bindings/Next.res
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ module Link = {
~children: React.element,
~className: string=?,
~target: string=?,
~hrefRel: string=?,
) => React.element = "default"
}

Expand Down
1 change: 0 additions & 1 deletion src/bindings/Next.resi
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ module Link: {
~children: React.element,
~className: string=?,
~target: string=?,
~hrefRel: string=?,
) => React.element
}

Expand Down
1 change: 1 addition & 0 deletions src/bindings/Webapi.res
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ module Window = {
external removeEventListener: (string, 'a => unit) => unit = "removeEventListener"
@scope("window") @val external innerWidth: int = "innerWidth"
@scope("window") @val external innerHeight: int = "innerHeight"
@scope("window") @val external scrollY: int = "scrollY"
}

module Fetch = {
Expand Down
2 changes: 2 additions & 0 deletions src/components/Footer.res
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,5 @@ let make = () => {
</div>
</footer>
}

let make = React.memo(make)
6 changes: 1 addition & 5 deletions src/components/Markdown.res
Original file line number Diff line number Diff line change
Expand Up @@ -380,11 +380,7 @@ module A = {
| [pathname] => Js.String2.replaceByRe(pathname, regex, "")
| _ => href
}
<Next.Link
href
hrefRel="noopener noreferrer"
className="no-underline text-fire hover:underline"
?target>
<Next.Link href className="no-underline text-fire hover:underline" ?target>
children
</Next.Link>
}
Expand Down
12 changes: 6 additions & 6 deletions src/components/Navigation.res
Original file line number Diff line number Diff line change
Expand Up @@ -385,9 +385,9 @@ module MobileNav = {
/*
<li className=base>
<Link href="/community" className={linkOrActiveLink(~target="/community", ~route)}>
{React.string("Community")}
</Link>
</li>
*/
Expand All @@ -413,7 +413,7 @@ module MobileNav = {

/* isOverlayOpen: if the mobile overlay is toggled open */
@react.component
let make = (~fixed=true, ~overlayState: (bool, (bool => bool) => unit)) => {
let make = (~fixed=true, ~isOverlayOpen: bool, ~setOverlayOpen: (bool => bool) => unit) => {
let minWidth = "20rem"
let router = Next.Router.useRouter()
let route = router.route
Expand Down Expand Up @@ -443,8 +443,6 @@ let make = (~fixed=true, ~overlayState: (bool, (bool => bool) => unit)) => {

let isSubnavOpen = Js.Array2.find(collapsibles, c => c.state !== Closed) !== None

let (isOverlayOpen, setOverlayOpen) = overlayState

let toggleOverlay = () => setOverlayOpen(prev => !prev)

let resetCollapsibles = () =>
Expand Down Expand Up @@ -518,7 +516,7 @@ let make = (~fixed=true, ~overlayState: (bool, (bool => bool) => unit)) => {
ref={ReactDOM.Ref.domRef(navRef)}
id="header"
style={ReactDOMStyle.make(~minWidth, ())}
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"}>
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"}>
<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">
<a
Expand Down Expand Up @@ -609,3 +607,5 @@ let make = (~fixed=true, ~overlayState: (bool, (bool => bool) => unit)) => {
/>
</>
}

let make = React.memo(make)
6 changes: 5 additions & 1 deletion src/components/Navigation.resi
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
@react.component
let make: (~fixed: bool=?, ~overlayState: (bool, (bool => bool) => unit)) => React.element
let make: (
~fixed: bool=?,
~isOverlayOpen: bool,
~setOverlayOpen: (bool => bool) => unit,
) => React.element
6 changes: 3 additions & 3 deletions src/layouts/LandingPageLayout.res
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ module QuickInstall = {
// and in the next tick, add the opacity-100 class, so the transition animation actually takes place.
// If we don't do that, the banner will essentially pop up without any animation
let bannerEl = Document.createElement("div")
bannerEl->Element.setClassName("foobar opacity-0 absolute top-0 mt-4 -mr-1 px-2 rounded right-0
bannerEl->Element.setClassName("foobar opacity-0 absolute top-0 mt-4 -mr-1 px-2 rounded right-0
bg-turtle text-gray-80-tr body-sm
transition-all duration-500 ease-in-out ")
let textNode = Document.createTextNode("Copied!")
Expand Down Expand Up @@ -694,7 +694,7 @@ module Sponsors = {

@react.component
let make = (~components=MarkdownComponents.default, ~children) => {
let overlayState = React.useState(() => false)
let (isOverlayOpen, setOverlayOpen) = React.useState(() => false)

<>
<Meta
Expand All @@ -705,7 +705,7 @@ let make = (~components=MarkdownComponents.default, ~children) => {
/>
<div className="mt-4 xs:mt-16">
<div className="text-gray-80 text-18 z">
<Navigation overlayState />
<Navigation isOverlayOpen setOverlayOpen />
<div className="absolute w-full top-16">
// Delete this again, when ReScript 11.1 is out for some time.
<Banner>
Expand Down
4 changes: 2 additions & 2 deletions src/layouts/MainLayout.res
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
@react.component
let make = (~components=MarkdownComponents.default, ~children) => {
let overlayState = React.useState(() => false)
let (isOverlayOpen, setOverlayOpen) = React.useState(() => false)

<>
<div className="mt-4 xs:mt-16">
<div className="text-gray-80">
<Navigation overlayState />
<Navigation isOverlayOpen setOverlayOpen />
<div className="flex xs:justify-center overflow-hidden pb-48">
<main className="mt-16 min-w-320 lg:align-center w-full px-4 md:px-8 max-w-1280 ">
<MdxProvider components> children </MdxProvider>
Expand Down
49 changes: 39 additions & 10 deletions src/layouts/SidebarLayout.res
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@ module MobileDrawerButton = {
</button>
}

type scrollDir =
| Up({scrollY: int})
| Down({scrollY: int})

@react.component
let make = (
~metaTitle: string,
Expand All @@ -221,6 +225,8 @@ let make = (
~children,
) => {
let (isNavOpen, setNavOpen) = React.useState(() => false)
let (_, startScrollEventTransition) = React.useTransition()
let (scrollDir, setScrollDir) = React.useState(() => Up({scrollY: %raw(`Infinity`)}))
let router = Next.Router.useRouter()
let version = Url.parse(router.route).version

Expand Down Expand Up @@ -254,6 +260,30 @@ let make = (
)
}, [])

React.useEffect(() => {
let onScroll = _e => {
startScrollEventTransition(() => {
setScrollDir(
prev => {
let Up({scrollY}) | Down({scrollY}) = prev
if scrollY === 0 || scrollY > Webapi.Window.scrollY {
Up({scrollY: Webapi.Window.scrollY})
} else {
Down({scrollY: Webapi.Window.scrollY})
}
},
)
})
}
Webapi.Window.addEventListener("scroll", onScroll)
Some(() => Webapi.Window.removeEventListener("scroll", onScroll))
}, [])

let handleDrawerButtonClick = React.useCallback(evt => {
ReactEvent.Mouse.preventDefault(evt)
toggleSidebar()
}, [])

let editLinkEl = switch editHref {
| Some(href) =>
<a href className="inline text-14 hover:underline text-fire" rel="noopener noreferrer">
Expand Down Expand Up @@ -297,25 +327,24 @@ let make = (
| None => React.null
}

let navAppearanceCascading = switch scrollDir {
| Up(_) => "nav-appear"
| Down(_) => "nav-disappear"
}

<>
<Meta title=metaTitle version />
<div className={"mt-16 min-w-320 " ++ theme}>
<div className={"mt-16 min-w-320 " ++ theme ++ " group " ++ navAppearanceCascading}>
<div className="w-full">
<Navigation overlayState=(isNavOpen, setNavOpen) />
<Navigation isOverlayOpen=isNavOpen setOverlayOpen=setNavOpen />
<div className="flex lg:justify-center">
<div className="flex w-full max-w-1280 md:mx-8">
sidebar
<main className="px-4 w-full pt-16 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-16 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">
<MobileDrawerButton
hidden=isNavOpen
onClick={evt => {
ReactEvent.Mouse.preventDefault(evt)
toggleSidebar()
}}
/>
className={"z-10 fixed border-b shadow top-16 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"}>
<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">
breadcrumbs
Expand Down

0 comments on commit 6cb18fb

Please sign in to comment.