From e87fec8f30f7952a47be33a241723643b2676e32 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 2 Feb 2021 22:56:44 +0800 Subject: [PATCH] feat: search modes --- src/components/searchModes/fuzzyMode.tsx | 48 ++++++++++++++++++++++++ src/components/searchModes/index.tsx | 16 ++++++++ src/components/searchModes/regexMode.tsx | 35 +++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 src/components/searchModes/fuzzyMode.tsx create mode 100644 src/components/searchModes/index.tsx create mode 100644 src/components/searchModes/regexMode.tsx diff --git a/src/components/searchModes/fuzzyMode.tsx b/src/components/searchModes/fuzzyMode.tsx new file mode 100644 index 0000000..5d78f47 --- /dev/null +++ b/src/components/searchModes/fuzzyMode.tsx @@ -0,0 +1,48 @@ +import * as React from 'react' +import { ModeShape } from '.' +import { Highlight } from '../Highlight' + +export const fuzzyMode: ModeShape = { + getSearchParams(searchKey) { + const matchNode = (node: TreeNode) => fuzzyMatch(searchKey, node.path) + return { + matchNode, + } + }, + renderNodeLabelText(node, searchKey) { + const { name, path } = node + const indexes = fuzzyMatchIndexes(searchKey, path, path.length - name.length) + return ( + + (i === 0 ? `^.` : `(?<=^.{${i}}).`)).join('|'))} + text={name} + /> + + ) + }, +} + +function fuzzyMatch(input: string, sample: string) { + let i = 0, + j = 0 + while (i < input.length && j < sample.length) { + if (input[i] === sample[j]) i++ + j++ + } + return i === input.length +} + +function fuzzyMatchIndexes(input: string, sample: string, shift: number = 0) { + const r: number[] = [] + let i = 0, + j = 0 + while (i < input.length && j < sample.length) { + if (input[i] === sample[j]) { + if (j >= shift) r.push(j - shift) + i++ + } + j++ + } + return r +} diff --git a/src/components/searchModes/index.tsx b/src/components/searchModes/index.tsx new file mode 100644 index 0000000..e007e49 --- /dev/null +++ b/src/components/searchModes/index.tsx @@ -0,0 +1,16 @@ +import { ReactNode } from 'react' +import { SearchParams } from 'utils/VisibleNodesGenerator' +import { fuzzyMode } from './fuzzyMode' +import { regexMode } from './regexMode' + +export type SearchMode = 'regex' | 'fuzzy' + +export type ModeShape = { + getSearchParams(searchKey: string): Pick | null + renderNodeLabelText(node: TreeNode, searchKey: string): ReactNode +} + +export const searchModes: Record = { + regex: regexMode, + fuzzy: fuzzyMode, +} diff --git a/src/components/searchModes/regexMode.tsx b/src/components/searchModes/regexMode.tsx new file mode 100644 index 0000000..dfed221 --- /dev/null +++ b/src/components/searchModes/regexMode.tsx @@ -0,0 +1,35 @@ +import * as React from 'react' +import { cx } from 'utils/cx' +import { isValidRegexpSource, searchKeyToRegexp } from 'utils/general' +import { ModeShape } from '.' +import { Highlight } from '../Highlight' + +export const regexMode: ModeShape = { + getSearchParams(searchKey) { + const regexp = searchKeyToRegexp(searchKey) + if (regexp) { + const matchNode = (node: TreeNode) => regexp.test(node.name) + + return { + matchNode, + } + } + + return null + }, + renderNodeLabelText(node, searchKey) { + const regex = + searchKey && isValidRegexpSource(searchKey) ? new RegExp(searchKey, 'gi') : undefined + const { name } = node + return name.includes('/') ? ( + name.split('/').map((chunk, index, arr) => ( + + + {index + 1 !== arr.length && '/'} + + )) + ) : ( + + ) + }, +}