123 lines
2.8 KiB
JavaScript
123 lines
2.8 KiB
JavaScript
import { SitePathsContext } from 'Components/utils/withSitePaths'
|
|
import { encodeRuleName, parentName } from 'Engine/rules.js'
|
|
import { pick, sortBy, take } from 'ramda'
|
|
import React, { useContext, useEffect, useState } from 'react'
|
|
import Highlighter from 'react-highlight-words'
|
|
import { useTranslation } from 'react-i18next'
|
|
import { Link, Redirect } from 'react-router-dom'
|
|
import Worker from 'worker-loader!./SearchBar.worker.js'
|
|
import { capitalise0 } from '../utils'
|
|
|
|
const worker = new Worker()
|
|
|
|
export default function SearchBar({
|
|
rules,
|
|
showDefaultList,
|
|
finally: finallyCallback
|
|
}) {
|
|
const sitePaths = useContext(SitePathsContext)
|
|
const [input, setInput] = useState('')
|
|
const [selectedOption, setSelectedOption] = useState(null)
|
|
const [results, setResults] = useState([])
|
|
const { i18n } = useTranslation()
|
|
|
|
useEffect(() => {
|
|
worker.postMessage({
|
|
rules: rules.map(
|
|
pick(['title', 'espace', 'description', 'name', 'dottedName'])
|
|
)
|
|
})
|
|
|
|
worker.onmessage = ({ data: results }) => setResults(results)
|
|
}, [rules])
|
|
|
|
let renderOptions = rules => {
|
|
let options =
|
|
(rules && sortBy(rule => rule.dottedName, rules)) || take(5)(results)
|
|
return <ul>{options.map(option => renderOption(option))}</ul>
|
|
}
|
|
|
|
let renderOption = option => {
|
|
let { title, dottedName, name } = option
|
|
return (
|
|
<li
|
|
key={dottedName}
|
|
css={`
|
|
padding: 0.4rem;
|
|
border-radius: 0.3rem;
|
|
:hover {
|
|
background: var(--colour);
|
|
color: var(--textColour);
|
|
}
|
|
:hover a {
|
|
color: var(--textColour);
|
|
}
|
|
`}
|
|
onClick={() => setSelectedOption(option)}
|
|
>
|
|
<div
|
|
style={{
|
|
fontWeight: '300',
|
|
fontSize: '85%',
|
|
lineHeight: '.9em'
|
|
}}
|
|
>
|
|
<Highlighter
|
|
searchWords={[input]}
|
|
textToHighlight={
|
|
parentName(dottedName)
|
|
? parentName(dottedName)
|
|
.split(' . ')
|
|
.map(capitalise0)
|
|
.join(' - ')
|
|
: ''
|
|
}
|
|
/>
|
|
</div>
|
|
<Link
|
|
to={sitePaths.documentation.index + '/' + encodeRuleName(dottedName)}
|
|
>
|
|
<Highlighter
|
|
searchWords={[input]}
|
|
textToHighlight={title || capitalise0(name)}
|
|
/>
|
|
</Link>
|
|
</li>
|
|
)
|
|
}
|
|
|
|
if (selectedOption != null) {
|
|
finallyCallback && finallyCallback()
|
|
return (
|
|
<Redirect
|
|
to={
|
|
sitePaths.documentation.index +
|
|
'/' +
|
|
encodeRuleName(selectedOption.dottedName)
|
|
}
|
|
/>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<input
|
|
type="text"
|
|
value={input}
|
|
placeholder={i18n.t('Entrez des mots clefs ici')}
|
|
onChange={e => {
|
|
let input = e.target.value
|
|
setInput(input)
|
|
if (input.length > 2) worker.postMessage({ input })
|
|
}}
|
|
/>
|
|
{input.length > 2 &&
|
|
!results.length &&
|
|
i18n.t('noresults', {
|
|
defaultValue: "Nous n'avons rien trouvé…"
|
|
})}
|
|
{showDefaultList && !input ? renderOptions(rules) : renderOptions()}
|
|
</>
|
|
)
|
|
}
|