UNPKG

3.68 kBJavaScriptView Raw
1// Copyright (c) Jupyter Development Team.
2// Distributed under the terms of the Modified BSD License.
3import { InputGroup } from '@jupyterlab/ui-components';
4import { StringExt } from '@lumino/algorithm';
5import React, { useEffect, useState } from 'react';
6import { ReactWidget } from './vdom';
7/**
8 * Perform a fuzzy search on a single item.
9 */
10export function fuzzySearch(source, query) {
11 // Set up the match score and indices array.
12 let score = Infinity;
13 let indices = null;
14 // The regex for search word boundaries
15 const rgx = /\b\w/g;
16 let continueSearch = true;
17 // Search the source by word boundary.
18 while (continueSearch) {
19 // Find the next word boundary in the source.
20 let rgxMatch = rgx.exec(source);
21 // Break if there is no more source context.
22 if (!rgxMatch) {
23 break;
24 }
25 // Run the string match on the relevant substring.
26 let match = StringExt.matchSumOfDeltas(source, query, rgxMatch.index);
27 // Break if there is no match.
28 if (!match) {
29 break;
30 }
31 // Update the match if the score is better.
32 if (match && match.score <= score) {
33 score = match.score;
34 indices = match.indices;
35 }
36 }
37 // Bail if there was no match.
38 if (!indices || score === Infinity) {
39 return null;
40 }
41 // Handle a split match.
42 return {
43 score,
44 indices
45 };
46}
47export const updateFilterFunction = (value, useFuzzyFilter, caseSensitive) => {
48 return (item) => {
49 if (useFuzzyFilter) {
50 // Run the fuzzy search for the item and query.
51 const query = value.toLowerCase();
52 // Ignore the item if it is not a match.
53 return fuzzySearch(item, query);
54 }
55 if (!caseSensitive) {
56 item = item.toLocaleLowerCase();
57 value = value.toLocaleLowerCase();
58 }
59 const i = item.indexOf(value);
60 if (i === -1) {
61 return null;
62 }
63 return {
64 indices: [...Array(item.length).keys()].map(x => x + 1)
65 };
66 };
67};
68export const FilterBox = (props) => {
69 var _a;
70 const [filter, setFilter] = useState((_a = props.initialQuery) !== null && _a !== void 0 ? _a : '');
71 if (props.forceRefresh) {
72 useEffect(() => {
73 props.updateFilter((item) => {
74 return {};
75 });
76 }, []);
77 }
78 useEffect(() => {
79 // If there is an initial search value, pass the parent the initial filter function for that value.
80 if (props.initialQuery !== undefined) {
81 props.updateFilter(updateFilterFunction(props.initialQuery, props.useFuzzyFilter, props.caseSensitive), props.initialQuery);
82 }
83 }, []);
84 /**
85 * Handler for search input changes.
86 */
87 const handleChange = (e) => {
88 const target = e.target;
89 setFilter(target.value);
90 props.updateFilter(updateFilterFunction(target.value, props.useFuzzyFilter, props.caseSensitive), target.value);
91 };
92 return (React.createElement(InputGroup, { type: "text", rightIcon: "ui-components:search", placeholder: props.placeholder, onChange: handleChange, className: "jp-FilterBox", value: filter }));
93};
94/**
95 * A widget which hosts a input textbox to filter on file names.
96 */
97export const FilenameSearcher = (props) => {
98 return ReactWidget.create(React.createElement(FilterBox, { updateFilter: props.updateFilter, useFuzzyFilter: props.useFuzzyFilter, placeholder: props.placeholder, forceRefresh: props.forceRefresh, caseSensitive: props.caseSensitive }));
99};
100//# sourceMappingURL=search.js.map
\No newline at end of file