refactor: support path mode in Safari

This commit is contained in:
EnixCoda 2021-03-08 14:24:03 +08:00
parent 1bcdc4dae2
commit 7dd0f4d191
No known key found for this signature in database
GPG key ID: CBBA59B5858C717E
4 changed files with 42 additions and 32 deletions

View file

@ -1,18 +1,23 @@
import * as React from 'react'
export function Highlight(props: { text: string; match?: RegExp }) {
export function Highlight(props: { text: string; match?: RegExp | string }) {
const { text, match } = props
const $match = React.useMemo(() => {
if (match instanceof RegExp) {
if (match.flags.includes('g')) return match
return new RegExp(match.source, 'g' + match.flags)
if (match) {
if (match instanceof RegExp) {
if (match.flags.includes('g')) return match
return new RegExp(match.source, 'g' + match.flags)
}
return new RegExp(match, 'g')
}
return null
}, [match])
if (!($match instanceof RegExp) || typeof text !== 'string') return <>{text}</>
if (!$match) return <>{text}</>
const matchedPieces = Array.from(text.matchAll($match)).map(([text]) => text)
const matchedPieces = Array.from(text.matchAll($match)).map(
([text, highlightText = text]) => highlightText,
)
const preservedPieces = text.split($match)
const content = []

View file

@ -2,7 +2,6 @@ import * as React from 'react'
import { cx } from 'utils/cx'
import { hasUpperCase } from 'utils/general'
import { ModeShape } from '.'
import { Highlight } from '../Highlight'
export const fuzzyMode: ModeShape = {
getSearchParams(searchKey) {
@ -26,13 +25,13 @@ export const fuzzyMode: ModeShape = {
let progress = 0
return chunks.map((chunk, index, chunks) => {
const chunkIndexes = indexes.filter(i => i >= progress && i < chunk.length + progress)
const regexp = chunkIndexes.length
? new RegExp(chunkIndexes.map(i => `(?<=^.{${i - progress}}).`).join('|'))
: undefined
const $indexes = chunkIndexes.length ? chunkIndexes.map(i => i - progress) : undefined
progress += chunk.length + 1
return (
<span key={chunk} className={cx({ prefix: index + 1 !== chunks.length })}>
<Highlight match={regexp} text={index + 1 === chunks.length ? chunk : chunk + '/'} />
<Highlight match={$indexes} text={index + 1 === chunks.length ? chunk : chunk + '/'} />
</span>
)
})
@ -61,3 +60,19 @@ function fuzzyMatchIndexes(input: string, sample: string, shift: number = 0) {
}
return indexes
}
function Highlight(props: { text: string; match?: number[] }) {
const { text, match } = props
if (!match?.length) return <>{text}</>
return (
<>
{text
.split('')
.map((char, i) =>
match.includes(i) ? <mark key={i}>{char}</mark> : <span key={i}>{char}</span>,
)}
</>
)
}

View file

@ -1,29 +1,22 @@
import * as React from 'react'
import { cx } from 'utils/cx'
import { hasUpperCase, isValidRegexpSource, searchKeyToRegexp } from 'utils/general'
import { searchKeyToRegexp } from 'utils/general'
import { ModeShape } from '.'
import { Highlight } from '../Highlight'
export const regexMode: ModeShape = {
getSearchParams(searchKey) {
if (!searchKey) return null
const regexp = searchKeyToRegexp(searchKey)
if (regexp) {
const matchNode = (node: TreeNode) => regexp.test(node.name)
return {
matchNode,
matchNode: node => regexp.test(node.name),
}
}
return null
},
renderNodeLabelText(node, searchKey) {
const regex =
searchKey && isValidRegexpSource(searchKey)
? new RegExp(searchKey, 'g' + (hasUpperCase(searchKey) ? '' : 'i'))
: undefined
const regex = searchKeyToRegexp(searchKey) || undefined
const { name } = node
return name.includes('/') ? (
name.split('/').map((chunk, index, chunks) => (

View file

@ -146,13 +146,7 @@ export async function JSONRequest(url: string, data: any, extra: RequestInit = {
export function searchKeyToRegexp(searchKey: string) {
if (!searchKey) return null
try {
const flags = hasUpperCase(searchKey) ? '' : 'i'
// case-sensitive when searchKey contains uppercase char
return new RegExp(searchKey, flags)
} catch (err) {
return null // matching nothing if failed transforming regexp
}
return safeRegexp(searchKey, hasUpperCase(searchKey) ? 'g' : 'gi')
}
export function hasUpperCase(input: string) {
@ -168,15 +162,18 @@ export async function renderReact(element: ReactElement) {
})
}
export function isValidRegexpSource(source: string) {
export function safeRegexp(pattern: string | RegExp, flags?: string | undefined) {
try {
new RegExp(source)
return true
return new RegExp(pattern, flags)
} catch (err) {
return false
return null
}
}
export function isValidRegexpSource(source: string) {
return Boolean(safeRegexp(source))
}
export function withEffect<Method extends (...args: any[]) => any>(
method: Method,
effect: (payload: ReturnType<Method>) => void,