-
+
diff --git a/src/SyntaxLookup.res b/src/SyntaxLookup.res
index 3d729b8e0..6b5af7ea2 100644
--- a/src/SyntaxLookup.res
+++ b/src/SyntaxLookup.res
@@ -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 =
@@ -372,7 +372,7 @@ let default = (props: props) => {
/>
-
+
diff --git a/src/Try.res b/src/Try.res
index 2562b92ac..b40e82442 100644
--- a/src/Try.res
+++ b/src/Try.res
@@ -1,7 +1,7 @@
type props = {versions: array}
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),
@@ -20,7 +20,7 @@ let default = props => {
diff --git a/src/bindings/Next.res b/src/bindings/Next.res
index 0179bf8c1..c783c5197 100644
--- a/src/bindings/Next.res
+++ b/src/bindings/Next.res
@@ -63,7 +63,6 @@ module Link = {
~children: React.element,
~className: string=?,
~target: string=?,
- ~hrefRel: string=?,
) => React.element = "default"
}
diff --git a/src/bindings/Next.resi b/src/bindings/Next.resi
index e225919df..00583343d 100644
--- a/src/bindings/Next.resi
+++ b/src/bindings/Next.resi
@@ -63,7 +63,6 @@ module Link: {
~children: React.element,
~className: string=?,
~target: string=?,
- ~hrefRel: string=?,
) => React.element
}
diff --git a/src/bindings/Webapi.res b/src/bindings/Webapi.res
index 27c3ff07a..5520fad21 100644
--- a/src/bindings/Webapi.res
+++ b/src/bindings/Webapi.res
@@ -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 = {
diff --git a/src/common/App.res b/src/common/App.res
index 2dabf82f7..4962cd772 100644
--- a/src/common/App.res
+++ b/src/common/App.res
@@ -56,93 +56,106 @@ let make = (props: props): React.element => {
// docs routes
| {base: ["docs", "manual"], pagepath, version} =>
// check if it's an api route
- switch Belt.Array.get(pagepath, 0) {
- | Some("api") =>
- switch version {
- | Latest =>
- switch (Belt.Array.length(pagepath), Belt.Array.get(pagepath, 1)) {
- | (1, _) => content
+
+ {switch Belt.Array.get(pagepath, 0) {
+ | Some("api") =>
+ switch version {
+ | Latest =>
+ switch (Belt.Array.length(pagepath), Belt.Array.get(pagepath, 1)) {
+ | (1, _) => content
+ | _ => content
+ }
+ | Version("v8.0.0") =>
+ switch (Belt.Array.length(pagepath), Belt.Array.get(pagepath, 1)) {
+ | (1, _) => content
+ | (2, Some("js")) => content
+ | (2, Some("belt")) => content
+ | (_, Some("js")) => content
+ | (_, Some("belt")) => content
+ | (_, Some("dom")) => content
+ | _ => React.null
+ }
+ | Version("v9.0.0") =>
+ switch (Belt.Array.length(pagepath), Belt.Array.get(pagepath, 1)) {
+ | (1, _) => content
+ | (2, Some("js")) => content
+ | (2, Some("belt")) => content
+ | (_, Some("js")) => content
+ | (_, Some("belt")) => content
+ | (_, Some("dom")) => content
+ | _ => React.null
+ }
+ | Version("v10.0.0") =>
+ switch (Belt.Array.length(pagepath), Belt.Array.get(pagepath, 1)) {
+ | (1, _) => content
+ | (2, Some("js")) => content
+ | (2, Some("belt")) => content
+ | (_, Some("js")) => content
+ | (_, Some("belt")) => content
+ | (_, Some("dom")) => content
+ | _ => React.null
+ }
| _ => content
}
- | Version("v8.0.0") =>
- switch (Belt.Array.length(pagepath), Belt.Array.get(pagepath, 1)) {
- | (1, _) => content
- | (2, Some("js")) => content
- | (2, Some("belt")) => content
- | (_, Some("js")) => content
- | (_, Some("belt")) => content
- | (_, Some("dom")) => content
- | _ => React.null
- }
- | Version("v9.0.0") =>
- switch (Belt.Array.length(pagepath), Belt.Array.get(pagepath, 1)) {
- | (1, _) => content
- | (2, Some("js")) => content
- | (2, Some("belt")) => content
- | (_, Some("js")) => content
- | (_, Some("belt")) => content
- | (_, Some("dom")) => content
+ | _ =>
+ switch version {
+ | Latest =>
+ frontmatter}>
+ content
+
+ | Version("v8.0.0") =>
+ frontmatter}>
+ content
+
+ | Version("v9.0.0") =>
+ frontmatter}>
+ content
+
+ | Version("v10.0.0") =>
+ frontmatter}>
+ content
+
| _ => React.null
}
- | Version("v10.0.0") =>
- switch (Belt.Array.length(pagepath), Belt.Array.get(pagepath, 1)) {
- | (1, _) => content
- | (2, Some("js")) => content
- | (2, Some("belt")) => content
- | (_, Some("js")) => content
- | (_, Some("belt")) => content
- | (_, Some("dom")) => content
- | _ => React.null
- }
- | _ => content
- }
- | _ =>
- switch version {
+ }}
+
+ | {base: ["docs", "react"], version} =>
+
+ {switch version {
| Latest =>
- frontmatter}>
- content
-
- | Version("v8.0.0") =>
- frontmatter}>
+ frontmatter}>
content
-
- | Version("v9.0.0") =>
- frontmatter}>
+
+ | Version("v0.10.0") =>
+ frontmatter}>
content
-
- | Version("v10.0.0") =>
- frontmatter}>
+
+ | Version("v0.11.0") =>
+ frontmatter}>
content
-
+
| _ => React.null
- }
- }
- | {base: ["docs", "react"], version} =>
- switch version {
- | Latest =>
- frontmatter}>
- content
-
- | Version("v0.10.0") =>
- frontmatter}> content
- | Version("v0.11.0") =>
- frontmatter}> content
- | _ => React.null
- }
+ }}
+
| {base: ["docs", "reason-compiler"], version: Latest} =>
- content
+
+ content
+
// common routes
| {base} =>
switch Belt.List.fromArray(base) {
| list{"community", ..._rest} =>
- frontmatter}> content
+
+ frontmatter}> content
+
| 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
+ | 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
+ content
| _ =>
let fm = component->frontmatter->DocFrontmatter.decode
let title = switch url {
diff --git a/src/common/EnableCollapsibleNavbar.res b/src/common/EnableCollapsibleNavbar.res
new file mode 100644
index 000000000..f8473822b
--- /dev/null
+++ b/src/common/EnableCollapsibleNavbar.res
@@ -0,0 +1,12 @@
+@react.component
+let make = (~children) => {
+ let scrollDir = Hooks.useScrollDirection()
+
+ = %raw(` () => {
- const isClient = typeof window === 'object';
+/** scrollDir is not memo-friendly.
+ It must be used with pattern matching.
+ Do not pass it directly to child components. */
+type scrollDir =
+ | Up({scrollY: int})
+ | Down({scrollY: int})
- function getSize() {
- return {
- width: isClient ? window.innerWidth : undefined,
- height: isClient ? window.innerHeight : undefined
- };
- }
-
- const [windowSize, setWindowSize] = React.useState(getSize);
+/**
+ This will cause highly frequent events, so use it only once in a root as possible.
+ And split the children components to prevent heavy ones from being re-rendered unnecessarily. */
+let useScrollDirection = () => {
+ let (_, startScrollEventTransition) = React.useTransition()
+ let (scrollDir, setScrollDir) = React.useState(() => Up({scrollY: %raw(`Infinity`)}))
React.useEffect(() => {
- if (!isClient) {
- return false;
+ 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))
+ }, [])
- function handleResize() {
- setWindowSize(getSize());
- }
-
- window.addEventListener('resize', handleResize);
- return () => window.removeEventListener('resize', handleResize);
- }, []); // Empty array ensures that effect is only run on mount and unmount
-
- if(windowSize) {
- return windowSize.width;
- }
- return null;
-}`)
+ scrollDir
+}
diff --git a/src/components/Footer.res b/src/components/Footer.res
index e1d4799a2..5947747f1 100644
--- a/src/components/Footer.res
+++ b/src/components/Footer.res
@@ -74,3 +74,5 @@ let make = () => {
}
+
+let make = React.memo(make)
diff --git a/src/components/Markdown.res b/src/components/Markdown.res
index e5792f9e5..a7ca402b6 100644
--- a/src/components/Markdown.res
+++ b/src/components/Markdown.res
@@ -380,11 +380,7 @@ module A = {
| [pathname] => Js.String2.replaceByRe(pathname, regex, "")
| _ => href
}
-
+
children
}
diff --git a/src/components/Navigation.res b/src/components/Navigation.res
index 02bbb42c2..7ff8dbb48 100644
--- a/src/components/Navigation.res
+++ b/src/components/Navigation.res
@@ -385,9 +385,9 @@ module MobileNav = {
/*
-
+
{React.string("Community")}
-
+
*/
@@ -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
@@ -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 = () =>
@@ -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"}>
-
+
playground
"group nav-appear"
+ | Down(_) => "group nav-disappear"
+ }}>
+ children
+
+}
diff --git a/src/common/Hooks.res b/src/common/Hooks.res
index 7df6c7872..5617bf2aa 100644
--- a/src/common/Hooks.res
+++ b/src/common/Hooks.res
@@ -1,6 +1,4 @@
/* Contains some generic hooks */
-%%raw("import React from 'react'")
-
let useOutsideClick: (ReactDOM.Ref.t, unit => unit) => unit = %raw(`(outerRef, trigger) => {
function handleClickOutside(event) {
if (outerRef.current && !outerRef.current.contains(event.target)) {
@@ -16,33 +14,38 @@ let useOutsideClick: (ReactDOM.Ref.t, unit => unit) => unit = %raw(`(outerRef, t
});
}`)
-let useWindowWidth: unit => option
bool) => unit)) => {
/>
>
}
+
+let make = React.memo(make)
diff --git a/src/components/Navigation.resi b/src/components/Navigation.resi
index 411584c8b..4c1f8c476 100644
--- a/src/components/Navigation.resi
+++ b/src/components/Navigation.resi
@@ -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
diff --git a/src/layouts/LandingPageLayout.res b/src/layouts/LandingPageLayout.res
index c2548b9f4..6292a9976 100644
--- a/src/layouts/LandingPageLayout.res
+++ b/src/layouts/LandingPageLayout.res
@@ -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!")
@@ -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)
<>
{
/>
-
+
// Delete this again, when ReScript 11.1 is out for some time.
diff --git a/src/layouts/MainLayout.res b/src/layouts/MainLayout.res
index dd3994a9a..83ad573f1 100644
--- a/src/layouts/MainLayout.res
+++ b/src/layouts/MainLayout.res
@@ -1,11 +1,11 @@
@react.component
let make = (~components=MarkdownComponents.default, ~children) => {
- let overlayState = React.useState(() => false)
+ let (isOverlayOpen, setOverlayOpen) = React.useState(() => false)
<>
-
+
-
+
children
diff --git a/src/layouts/SidebarLayout.res b/src/layouts/SidebarLayout.res
index f6e5665e2..8b38fc35d 100644
--- a/src/layouts/SidebarLayout.res
+++ b/src/layouts/SidebarLayout.res
@@ -254,6 +254,11 @@ let make = (
)
}, [])
+ let handleDrawerButtonClick = React.useCallback(evt => {
+ ReactEvent.Mouse.preventDefault(evt)
+ toggleSidebar()
+ }, [])
+
let editLinkEl = switch editHref {
| Some(href) =>
@@ -301,21 +306,15 @@ let make = (
-
+
sidebar
//width of the right content part
- {
- 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"}>
+
breadcrumbs