forked from ddrilling/asb_cloud_front
Добавлена подсветка совпадения в поиске
This commit is contained in:
parent
e773943b61
commit
685191484a
@ -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 { useLocation, useNavigate } from 'react-router-dom'
|
||||||
import { DefaultOptionType } from 'antd/lib/select'
|
|
||||||
import { AutoComplete } from 'antd'
|
|
||||||
import { BaseSelectRef } from 'rc-select'
|
import { BaseSelectRef } from 'rc-select'
|
||||||
|
import { AutoComplete } from 'antd'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
|
|
||||||
import { useWell } from '@asb/context'
|
import { useWell } from '@asb/context'
|
||||||
@ -20,29 +19,18 @@ const transliterationTable = {
|
|||||||
'z': 'я', 'x': 'ч', 'c': 'с', 'v': 'м', 'b': 'и', 'n': 'т', 'm': 'ь', ',': 'б', '.': 'ю', '<': 'б', '>': 'ю',
|
'z': 'я', 'x': 'ч', 'c': 'с', 'v': 'м', 'b': 'и', 'n': 'т', 'm': 'ь', ',': 'б', '.': 'ю', '<': 'б', '>': 'ю',
|
||||||
}
|
}
|
||||||
|
|
||||||
const transliterateToRu = (text: string): string => {
|
type OptionType = {
|
||||||
let out = text.toLowerCase()
|
value: string
|
||||||
Object.entries(transliterationTable).map(([en, ru]) => out = out.replaceAll(en, ru))
|
label: ReactNode
|
||||||
return out
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const transliterateToEn = (text: string): string => {
|
const transliterateToRu = (text: string) => Object.entries(transliterationTable).reduce((out, [en, ru]) => out.replaceAll(en, ru), text.toLowerCase())
|
||||||
let out = text.toLowerCase()
|
const transliterateToEn = (text: string) => Object.entries(transliterationTable).reduce((out, [en, ru]) => out.replaceAll(ru, en), text.toLowerCase())
|
||||||
Object.entries(transliterationTable).map(([en, ru]) => out = out.replaceAll(ru, en))
|
const applyVars = (route: string, vars?: object): string => !vars ? route :
|
||||||
return out
|
Object.entries(vars).reduce((out, [key, value]) => out.replaceAll(`{${key}}`, value), route)
|
||||||
}
|
|
||||||
|
|
||||||
const applyVars = (route: string, vars?: Record<string, any>): string => {
|
const makeOptions = (items: PrivateWellMenuItem[], vars?: object): OptionType[] => {
|
||||||
if (!vars) return route
|
const out: OptionType[] = []
|
||||||
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[] = []
|
|
||||||
items.forEach((item) => {
|
items.forEach((item) => {
|
||||||
if (!hasPermission(item.permissions)) return
|
if (!hasPermission(item.permissions)) return
|
||||||
out.push({
|
out.push({
|
||||||
@ -60,9 +48,23 @@ const makeOptions = (items: PrivateWellMenuItem[], vars?: Record<string, any>):
|
|||||||
return out
|
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(() => {
|
export const FastRunMenu = memo(() => {
|
||||||
const [isOpen, setIsOpen] = useState(false)
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
const [value, setValue] = useState<string | null>()
|
const [value, setValue] = useState<string | null>()
|
||||||
|
const [results, setResults] = useState<OptionType[]>([])
|
||||||
const ref = useRef<BaseSelectRef | null>(null)
|
const ref = useRef<BaseSelectRef | null>(null)
|
||||||
|
|
||||||
const [well] = useWell()
|
const [well] = useWell()
|
||||||
@ -97,6 +99,32 @@ export const FastRunMenu = memo(() => {
|
|||||||
onClose()
|
onClose()
|
||||||
}, [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(() => {
|
useEffect(() => {
|
||||||
const listener = (event: KeyboardEvent) => {
|
const listener = (event: KeyboardEvent) => {
|
||||||
if (event.altKey && event.code === 'KeyA')
|
if (event.altKey && event.code === 'KeyA')
|
||||||
@ -114,14 +142,7 @@ export const FastRunMenu = memo(() => {
|
|||||||
ref.current?.scrollTo(0)
|
ref.current?.scrollTo(0)
|
||||||
}, [isOpen])
|
}, [isOpen])
|
||||||
|
|
||||||
const onFilter = (text: string, option: DefaultOptionType | undefined) => {
|
useEffect(() => onSearch(''), [onSearch])
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
return isOpen ? (
|
return isOpen ? (
|
||||||
<div className={'fast-run-menu'}>
|
<div className={'fast-run-menu'}>
|
||||||
@ -129,13 +150,13 @@ export const FastRunMenu = memo(() => {
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
autoFocus
|
autoFocus
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
options={options}
|
options={results}
|
||||||
onBlur={onClose}
|
onBlur={onClose}
|
||||||
onChange={setValue}
|
onChange={setValue}
|
||||||
|
onSearch={onSearch}
|
||||||
placeholder={'Введите название страницы...'}
|
placeholder={'Введите название страницы...'}
|
||||||
onSelect={onTextChanged}
|
onSelect={onTextChanged}
|
||||||
value={value}
|
value={value}
|
||||||
filterOption={onFilter}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : <></>
|
) : <></>
|
||||||
|
@ -7,3 +7,7 @@
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
width: 50vw;
|
width: 50vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fast-run-menu-text-found {
|
||||||
|
background-color: yellow;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user