82 lines
2.3 KiB
JavaScript
82 lines
2.3 KiB
JavaScript
import React, { useState, useEffect, useCallback } from 'react'
|
|
import { debounce } from 'debounce'
|
|
import classNames from 'classnames'
|
|
import { FiSearch } from 'react-icons/fi'
|
|
import { useDispatch } from 'react-redux'
|
|
import { useTranslation } from 'react-i18next'
|
|
import axios from 'axios'
|
|
|
|
import { search } from '../lib/gutenberg'
|
|
import { Spinner } from '../styles/Spinner'
|
|
import { Dropdown } from './generics/Dropdown'
|
|
import { Book } from './Book'
|
|
|
|
import styles from './SearchBar.css'
|
|
import { CursorList } from './generics/CursorList'
|
|
|
|
import { setText, setLang } from '../store/actions.js'
|
|
|
|
export const SearchBar = () => {
|
|
const { t } = useTranslation()
|
|
const dispatch = useDispatch()
|
|
const [searchTerm, setSearchTerm] = useState('')
|
|
const [results, setResults] = useState([])
|
|
const [loading, setLoading] = useState(false)
|
|
const [isFocused, setFocus] = useState(false)
|
|
|
|
const debouncedSearch = useCallback(
|
|
debounce(async term => {
|
|
await search(term, 100).then(setResults)
|
|
setLoading(false)
|
|
}, 500),
|
|
[]
|
|
)
|
|
|
|
const handleClick = async idx => {
|
|
const { id, language } = results[idx]
|
|
const url = `https://gutenberg.muperfredi.de/texts/${id}/stripped-body`
|
|
const text = await axios.get(url).then(res => res.data.body)
|
|
dispatch(setText(text))
|
|
dispatch(setLang(language[0]))
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (searchTerm.length > 0) {
|
|
setLoading(true)
|
|
debouncedSearch(searchTerm)
|
|
} else {
|
|
setLoading(false)
|
|
}
|
|
}, [searchTerm, debouncedSearch])
|
|
|
|
return (
|
|
<>
|
|
<div>
|
|
<label className={styles.label}>
|
|
<input
|
|
type="text"
|
|
value={searchTerm}
|
|
onChange={e => setSearchTerm(e.target.value)}
|
|
className={classNames({
|
|
[styles.loading]: false,
|
|
[styles.search]: true
|
|
})}
|
|
onFocus={() => setFocus(true)}
|
|
onBlur={() => setFocus(false)}
|
|
placeholder={t('search') + '...'}
|
|
></input>
|
|
{loading ? <Spinner /> : <FiSearch />}
|
|
</label>
|
|
<Dropdown visible={isFocused && searchTerm.length !== 0}>
|
|
<CursorList
|
|
items={results.map(entry => (
|
|
<Book {...entry} />
|
|
))}
|
|
onSelect={handleClick}
|
|
/>
|
|
</Dropdown>
|
|
</div>
|
|
</>
|
|
)
|
|
}
|