mirror of
https://github.com/betagouv/mon-entreprise
synced 2025-02-10 01:05:02 +00:00
Cela permet l'inférence de type à partir des fichiers js qui ne sont pas encore convertis en TypeScript. Par ailleurs suppression des dernières traces de Flow. Ajout d'options plus strictes pour dans la config tsconfig.js
129 lines
3 KiB
TypeScript
129 lines
3 KiB
TypeScript
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 { Rule } from 'Types/rule'
|
|
import Worker from 'worker-loader!./SearchBar.worker.js'
|
|
import { capitalise0 } from '../utils'
|
|
|
|
const worker = new Worker()
|
|
|
|
type SearchBarProps = {
|
|
rules: Array<Rule>
|
|
showDefaultList: boolean
|
|
finally?: () => void
|
|
}
|
|
|
|
export default function SearchBar({
|
|
rules,
|
|
showDefaultList,
|
|
finally: finallyCallback
|
|
}: SearchBarProps) {
|
|
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?: Array<Rule>) => {
|
|
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()}
|
|
</>
|
|
)
|
|
}
|