let hit = ({hit, children}: DocSearch.hitComponent) => {
let toTitle = str =>
str->Js.String2.charAt(0)->Js.String2.toUpperCase ++ Js.String2.sliceToEnd(str, ~from=1)
let description = switch hit.url
->Js.String2.split("/")
->Js.Array2.sliceFrom(1)
->Belt.List.fromArray {
| list{"blog" as r | "community" as r, ..._} => r->toTitle
| list{"docs", doc, version, ...rest} =>
let path = rest->Belt.List.toArray
let info =
path
->Js.Array2.slice(~start=0, ~end_=Js.Array2.length(path) - 1)
->Js.Array2.map(path =>
switch path {
| "api" => "API"
| other => toTitle(other)
}
)
[doc->toTitle, version->toTitle]->Js.Array2.concat(info)->Js.Array2.joinWith(" / ")
| _ => ""
}
{description->React.string}
children
}
let transformItems = (items: DocSearch.transformItems) => {
items->Belt.Array.keepMap(item => {
let url = try Webapi.URL.make(item.url)->Some catch {
| Js.Exn.Error(obj) =>
Js.Console.error2(`Failed to parse URL ${item.url}`, obj)
None
}
switch url {
| Some({pathname, hash}) => {...item, url: pathname ++ hash}->Some
| None => None
}
})
}
@react.component
let make = () => {
let (state, setState) = React.useState(_ => Inactive)
let router = Next.Router.useRouter()
let version = switch Url.parse(router.route).version {
| Version(v) => v
| _ => "latest"
}
let handleCloseModal = () => {
let () = switch ReactDOM.querySelector(".DocSearch-Modal") {
| Some(modal) =>
switch ReactDOM.querySelector("body") {
| Some(body) =>
open Webapi
body->Element.classList->ClassList.remove("DocSearch--active")
modal->Element.addEventListener("transitionend", () => {
setState(_ => Inactive)
})
| None => setState(_ => Inactive)
}
| None => ()
}
}
React.useEffect(() => {
let isEditableTag = el =>
switch el->tagName {
| "TEXTAREA" | "SELECT" | "INPUT" => true
| _ => false
}
let focusSearch = e => {
switch activeElement {
| Some(el) if el->isEditableTag || el->isContentEditable => ()
| _ =>
setState(_ => Active)
e->keyboardEventPreventDefault
}
}
let handleGlobalKeyDown = e => {
switch e.key {
| "/" => focusSearch(e)
| "k" if e.ctrlKey || e.metaKey => focusSearch(e)
| "Escape" => handleCloseModal()
| _ => ()
}
}
addKeyboardEventListener("keydown", handleGlobalKeyDown)
Some(() => removeKeyboardEventListener("keydown", handleGlobalKeyDown))
}, [setState])
let onClick = _ => {
setState(_ => Active)
}
let onClose = React.useCallback(() => {
handleCloseModal()
}, [setState])
<>
{switch state {
| Active =>
switch ReactDOM.querySelector("body") {
| Some(element) =>
ReactDOM.createPortal(
scrollY}
transformItems={transformItems}
hitComponent=hit
/>
element,
)
| None => React.null
}
| Inactive => React.null
}}
>
}
let comparable = (type key, ~cmp) => {
module N = MakeComparable({
type t = key
let cmp = cmp
})
module(N: Comparable with type t = key)
}
{description->React.string}
children