Skip to content

Commit

Permalink
enhance(search): add global / shortcut to focus search
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelgerber committed Aug 30, 2024
1 parent 9ff5a51 commit 0568f92
Showing 1 changed file with 56 additions and 25 deletions.
81 changes: 56 additions & 25 deletions site/search/Autocomplete.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useEffect } from "react"
import React, { useEffect, useRef, useState } from "react"
import { render } from "react-dom"
import {
AutocompleteApi,
AutocompleteSource,
Render,
autocomplete,
Expand All @@ -27,6 +28,7 @@ import {
} from "./searchClient.js"
import { queryParamsToStr } from "@ourworldindata/utils"
import { SiteAnalytics } from "../SiteAnalytics.js"
import Mousetrap from "mousetrap"

const siteAnalytics = new SiteAnalytics()

Expand Down Expand Up @@ -242,11 +244,17 @@ export function Autocomplete({
detachedMediaQuery?: string
panelClassName?: string
}) {
const containerRef = useRef<HTMLDivElement>(null)

const [search, setSearch] = useState<AutocompleteApi<BaseItem> | null>(null)

useEffect(() => {
if (!containerRef.current) return

const search = autocomplete({
placeholder,
detachedMediaQuery,
container: AUTOCOMPLETE_CONTAINER_ID,
container: containerRef.current,
classNames: {
panel: panelClassName,
},
Expand Down Expand Up @@ -282,31 +290,54 @@ export function Autocomplete({
plugins: [recentSearchesPlugin],
})

const container = document.querySelector(AUTOCOMPLETE_CONTAINER_ID)
if (container) {
const input = container.querySelector<HTMLInputElement>("input")
if (input) {
const inputId = input.id
const button = container.querySelector(
`label[for='${inputId}'] button`
)
// Disable the button on mount. We know there's no input because the element is created by JS
// and thus isn't persisted between navigations
button?.setAttribute("disabled", "true")
setSearch(search)

const input =
containerRef.current.querySelector<HTMLInputElement>("input")
if (input) {
const inputId = input.id
const button = containerRef.current.querySelector(
`label[for='${inputId}'] button`
)
// Disable the button on mount. We know there's no input because the element is created by JS
// and thus isn't persisted between navigations
button?.setAttribute("disabled", "true")

input.addEventListener("input", () => {
const isFormValid = input.checkValidity()
if (isFormValid) {
button?.removeAttribute("disabled")
} else {
button?.setAttribute("disabled", "true")
}
})
}

input.addEventListener("input", () => {
const isFormValid = input.checkValidity()
if (isFormValid) {
button?.removeAttribute("disabled")
} else {
button?.setAttribute("disabled", "true")
}
})
}
return () => {
console.log("destroy")

Check warning on line 317 in site/search/Autocomplete.tsx

View workflow job for this annotation

GitHub Actions / eslint

Unexpected console statement

Check warning on line 317 in site/search/Autocomplete.tsx

View workflow job for this annotation

GitHub Actions / eslint

Unexpected console statement
search.destroy()
}
}, [
onActivate,
onClose,
placeholder,
detachedMediaQuery,
panelClassName,
containerRef,
])

return () => search.destroy()
}, [onActivate, onClose, placeholder, detachedMediaQuery, panelClassName])
// Register a global shortcut to open the search box on typing "/"
useEffect(() => {
if (!search) return
Mousetrap.bind("/", (e) => {
e.preventDefault() // don't type "/" into input
search.setIsOpen(true)
})

return () => {
Mousetrap.unbind("/")
}
}, [search, containerRef])

return <div className={className} id="autocomplete" />
return <div className={className} ref={containerRef} id="autocomplete" />
}

0 comments on commit 0568f92

Please sign in to comment.