Добавлена подсветка совпадения в поиске

This commit is contained in:
Александр Сироткин 2022-10-31 04:06:34 +05:00
parent e773943b61
commit 685191484a
2 changed files with 58 additions and 33 deletions

View File

@ -1,8 +1,7 @@
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { memo, ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import { DefaultOptionType } from 'antd/lib/select'
import { AutoComplete } from 'antd'
import { BaseSelectRef } from 'rc-select'
import { AutoComplete } from 'antd'
import { join } from 'path'
import { useWell } from '@asb/context'
@ -20,29 +19,18 @@ const transliterationTable = {
'z': 'я', 'x': 'ч', 'c': 'с', 'v': 'м', 'b': 'и', 'n': 'т', 'm': 'ь', ',': 'б', '.': 'ю', '<': 'б', '>': 'ю',
}
const transliterateToRu = (text: string): string => {
let out = text.toLowerCase()
Object.entries(transliterationTable).map(([en, ru]) => out = out.replaceAll(en, ru))
return out
type OptionType = {
value: string
label: ReactNode
}
const transliterateToEn = (text: string): string => {
let out = text.toLowerCase()
Object.entries(transliterationTable).map(([en, ru]) => out = out.replaceAll(ru, en))
return out
}
const transliterateToRu = (text: string) => Object.entries(transliterationTable).reduce((out, [en, ru]) => out.replaceAll(en, ru), text.toLowerCase())
const transliterateToEn = (text: string) => Object.entries(transliterationTable).reduce((out, [en, ru]) => out.replaceAll(ru, en), text.toLowerCase())
const applyVars = (route: string, vars?: object): string => !vars ? route :
Object.entries(vars).reduce((out, [key, value]) => out.replaceAll(`{${key}}`, value), route)
const applyVars = (route: string, vars?: Record<string, any>): string => {
if (!vars) return route
let out = route
Object.entries(vars).forEach(([key, value]) => {
out = out.replaceAll(`{${key}}`, value)
})
return out
}
const makeOptions = (items: PrivateWellMenuItem[], vars?: Record<string, any>): DefaultOptionType[] => {
const out: DefaultOptionType[] = []
const makeOptions = (items: PrivateWellMenuItem[], vars?: object): OptionType[] => {
const out: OptionType[] = []
items.forEach((item) => {
if (!hasPermission(item.permissions)) return
out.push({
@ -60,9 +48,23 @@ const makeOptions = (items: PrivateWellMenuItem[], vars?: Record<string, any>):
return out
}
const makeFindInString = (text: string) => {
const searchText = text.toLowerCase()
const toRu = transliterateToRu(searchText)
const toEn = transliterateToEn(searchText)
return (sourceText: string) => {
const text = sourceText.toLowerCase()
let idx = text.indexOf(searchText)
if (idx < 0 && (idx = text.indexOf(toRu)) < 0 && (idx = text.indexOf(toEn)) < 0) return false
return { from: idx, to: idx + searchText.length }
}
}
export const FastRunMenu = memo(() => {
const [isOpen, setIsOpen] = useState(false)
const [value, setValue] = useState<string | null>()
const [results, setResults] = useState<OptionType[]>([])
const ref = useRef<BaseSelectRef | null>(null)
const [well] = useWell()
@ -97,6 +99,32 @@ export const FastRunMenu = memo(() => {
onClose()
}, [onClose])
const onSearch = useCallback((text: string) => {
if (text.trim() === '') {
setResults(options)
return
}
const findInString = makeFindInString(text)
const results = options.map((option) => {
const label = String(option.label)
const idx = findInString(label.toLowerCase())
if (!idx) return findInString(option.value.toLowerCase()) ? option : false
return {
...option,
label: <>
{label.slice(0, idx.from)}
<span className={'fast-run-menu-text-found'}>
{label.slice(idx.from, idx.to)}
</span>
{label.slice(idx.to)}
</>
}
}).filter(Boolean) as OptionType[]
setResults(results)
}, [options])
useEffect(() => {
const listener = (event: KeyboardEvent) => {
if (event.altKey && event.code === 'KeyA')
@ -114,14 +142,7 @@ export const FastRunMenu = memo(() => {
ref.current?.scrollTo(0)
}, [isOpen])
const onFilter = (text: string, option: DefaultOptionType | undefined) => {
if (!option) return false
const search = (String(option.label).replaceAll(' > ', '') + String(option.value).replaceAll('/', '')).toLowerCase()
if (search.includes(text.toLowerCase())) return true
if (search.includes(transliterateToRu(text))) return true
if (search.includes(transliterateToEn(text))) return true
return false
}
useEffect(() => onSearch(''), [onSearch])
return isOpen ? (
<div className={'fast-run-menu'}>
@ -129,13 +150,13 @@ export const FastRunMenu = memo(() => {
ref={ref}
autoFocus
style={{ width: '100%' }}
options={options}
options={results}
onBlur={onClose}
onChange={setValue}
onSearch={onSearch}
placeholder={'Введите название страницы...'}
onSelect={onTextChanged}
value={value}
filterOption={onFilter}
/>
</div>
) : <></>

View File

@ -7,3 +7,7 @@
border-radius: 5px;
width: 50vw;
}
.fast-run-menu-text-found {
background-color: yellow;
}