@difizen/libro-search 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/abstract-search-provider.d.ts +2 -1
- package/es/abstract-search-provider.d.ts.map +1 -1
- package/es/index.less +10 -8
- package/es/libro-search-engine-text.d.ts +1 -1
- package/es/libro-search-generic-provider.d.ts +5 -4
- package/es/libro-search-generic-provider.d.ts.map +1 -1
- package/es/libro-search-generic-provider.js +11 -11
- package/es/libro-search-manager.d.ts +1 -1
- package/es/libro-search-manager.d.ts.map +1 -1
- package/es/libro-search-manager.js +3 -3
- package/es/libro-search-model.d.ts +4 -4
- package/es/libro-search-model.d.ts.map +1 -1
- package/es/libro-search-model.js +19 -9
- package/es/libro-search-protocol.d.ts +1 -13
- package/es/libro-search-protocol.d.ts.map +1 -1
- package/es/libro-search-protocol.js +0 -5
- package/es/libro-search-provider.d.ts +11 -7
- package/es/libro-search-provider.d.ts.map +1 -1
- package/es/libro-search-provider.js +70 -13
- package/es/libro-search-utils.d.ts +1 -1
- package/es/libro-search-utils.d.ts.map +1 -1
- package/es/libro-search-utils.js +11 -7
- package/es/libro-search-view.d.ts +11 -11
- package/es/libro-search-view.d.ts.map +1 -1
- package/es/libro-search-view.js +136 -79
- package/package.json +4 -3
- package/src/abstract-search-provider.ts +1 -2
- package/src/index.less +10 -8
- package/src/libro-search-engine-text.ts +1 -1
- package/src/libro-search-generic-provider.ts +6 -6
- package/src/libro-search-manager.ts +4 -3
- package/src/libro-search-model.ts +10 -11
- package/src/libro-search-protocol.ts +1 -16
- package/src/libro-search-provider.ts +88 -41
- package/src/libro-search-utils.spec.ts +2 -2
- package/src/libro-search-utils.ts +11 -9
- package/src/libro-search-view.tsx +63 -43
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
+
// Copyright (c) Jupyter Development Team.
|
|
3
|
+
// Distributed under the terms of the Modified BSD License.
|
|
4
|
+
|
|
1
5
|
import type { Disposable } from '@difizen/mana-app';
|
|
2
6
|
import { DisposableCollection, Emitter } from '@difizen/mana-app';
|
|
3
7
|
import { inject, singleton } from '@difizen/mana-app';
|
|
@@ -12,15 +16,15 @@ import { LibroSearchUtils } from './libro-search-utils.js';
|
|
|
12
16
|
*/
|
|
13
17
|
@singleton()
|
|
14
18
|
export class LibroSearchModel implements Disposable {
|
|
15
|
-
utils: LibroSearchUtils;
|
|
16
|
-
protected _disposed?: boolean = false;
|
|
19
|
+
@inject(LibroSearchUtils) utils: LibroSearchUtils;
|
|
20
|
+
protected _disposed?: boolean | undefined = false;
|
|
17
21
|
protected _caseSensitive = false;
|
|
18
22
|
protected parsingError = '';
|
|
19
23
|
protected _filters: SearchFilters = {
|
|
20
24
|
searchCellOutput: true,
|
|
21
25
|
onlySearchSelectedCells: false,
|
|
22
26
|
};
|
|
23
|
-
protected _replaceText
|
|
27
|
+
protected _replaceText: string;
|
|
24
28
|
protected searchDebouncer: any;
|
|
25
29
|
protected _searchExpression = '';
|
|
26
30
|
protected _useRegex = false;
|
|
@@ -32,19 +36,14 @@ export class LibroSearchModel implements Disposable {
|
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
get disposed() {
|
|
35
|
-
return
|
|
39
|
+
return this._disposed;
|
|
36
40
|
}
|
|
37
41
|
/**
|
|
38
42
|
* Search document model
|
|
39
43
|
* @param searchProvider Provider for the current document
|
|
40
44
|
* @param searchDebounceTime Debounce search time
|
|
41
45
|
*/
|
|
42
|
-
constructor(
|
|
43
|
-
@inject(LibroSearchUtils) utils: LibroSearchUtils,
|
|
44
|
-
searchProvider: SearchProvider,
|
|
45
|
-
searchDebounceTime: number,
|
|
46
|
-
) {
|
|
47
|
-
this.utils = utils;
|
|
46
|
+
constructor(searchProvider: SearchProvider, searchDebounceTime: number) {
|
|
48
47
|
this.searchProvider = searchProvider;
|
|
49
48
|
// this._filters = {};
|
|
50
49
|
// if (this.searchProvider.getFilters) {
|
|
@@ -227,7 +226,7 @@ export class LibroSearchModel implements Disposable {
|
|
|
227
226
|
* @param name Filter name
|
|
228
227
|
* @param v Filter value
|
|
229
228
|
*/
|
|
230
|
-
async setFilter(
|
|
229
|
+
async setFilter(name: string, v: boolean): Promise<void> {
|
|
231
230
|
// if (this._filters[name] !== v) {
|
|
232
231
|
// if (this.searchProvider.validateFilter) {
|
|
233
232
|
// this._filters[name] = await this.searchProvider.validateFilter(name, v);
|
|
@@ -1,23 +1,8 @@
|
|
|
1
|
+
import type { SearchMatch } from '@difizen/libro-code-editor';
|
|
1
2
|
import type { CellView } from '@difizen/libro-core';
|
|
2
3
|
import type { Disposable, Event } from '@difizen/mana-app';
|
|
3
4
|
import type { View } from '@difizen/mana-app';
|
|
4
5
|
import { Syringe } from '@difizen/mana-app';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Base search match interface
|
|
8
|
-
*/
|
|
9
|
-
export interface SearchMatch {
|
|
10
|
-
/**
|
|
11
|
-
* Text of the exact match itself
|
|
12
|
-
*/
|
|
13
|
-
readonly text: string;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Start location of the match (in a text, this is the column)
|
|
17
|
-
*/
|
|
18
|
-
position: number;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
6
|
/**
|
|
22
7
|
* HTML search match interface
|
|
23
8
|
*/
|
|
@@ -1,18 +1,34 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import type { SearchMatch } from '@difizen/libro-code-editor';
|
|
2
|
+
import type { CellView, VirtualizedManager } from '@difizen/libro-core';
|
|
3
|
+
import {
|
|
4
|
+
EditorCellView,
|
|
5
|
+
LibroView,
|
|
6
|
+
VirtualizedManagerHelper,
|
|
7
|
+
} from '@difizen/libro-core';
|
|
8
|
+
import { inject, prop, transient, equals } from '@difizen/mana-app';
|
|
4
9
|
import { Deferred, DisposableCollection } from '@difizen/mana-app';
|
|
5
10
|
import { l10n } from '@difizen/mana-l10n';
|
|
6
11
|
|
|
7
12
|
import { AbstractSearchProvider } from './abstract-search-provider.js';
|
|
8
13
|
import { LibroCellSearchProvider } from './libro-cell-search-provider.js';
|
|
14
|
+
import { SearchProviderOption } from './libro-search-protocol.js';
|
|
9
15
|
import type {
|
|
10
16
|
CellSearchProvider,
|
|
11
17
|
SearchFilter,
|
|
12
|
-
SearchMatch,
|
|
13
18
|
SearchFilters,
|
|
14
19
|
} from './libro-search-protocol.js';
|
|
15
|
-
|
|
20
|
+
|
|
21
|
+
export function elementInViewport(el: HTMLElement): boolean {
|
|
22
|
+
const boundingClientRect = el.getBoundingClientRect();
|
|
23
|
+
return (
|
|
24
|
+
boundingClientRect.top >= 0 &&
|
|
25
|
+
boundingClientRect.bottom <=
|
|
26
|
+
(window.innerHeight || document.documentElement.clientHeight) &&
|
|
27
|
+
boundingClientRect.left >= 0 &&
|
|
28
|
+
boundingClientRect.right <=
|
|
29
|
+
(window.innerWidth || document.documentElement.clientWidth)
|
|
30
|
+
);
|
|
31
|
+
}
|
|
16
32
|
|
|
17
33
|
export type LibroSearchProviderFactory = (
|
|
18
34
|
option: SearchProviderOption,
|
|
@@ -44,6 +60,7 @@ export class LibroSearchProvider extends AbstractSearchProvider {
|
|
|
44
60
|
@prop() protected providerMap = new Map<string, CellSearchProvider>();
|
|
45
61
|
protected documentHasChanged = false;
|
|
46
62
|
protected override view: LibroView;
|
|
63
|
+
protected virtualizedManager: VirtualizedManager;
|
|
47
64
|
|
|
48
65
|
updateSearchCellOutput(value: boolean): void {
|
|
49
66
|
this.searchCellOutput = value;
|
|
@@ -52,16 +69,20 @@ export class LibroSearchProvider extends AbstractSearchProvider {
|
|
|
52
69
|
/**
|
|
53
70
|
* @param option Provide the view to search in
|
|
54
71
|
*/
|
|
55
|
-
constructor(
|
|
72
|
+
constructor(
|
|
73
|
+
@inject(SearchProviderOption) option: SearchProviderOption,
|
|
74
|
+
@inject(VirtualizedManagerHelper)
|
|
75
|
+
virtualizedManagerHelper: VirtualizedManagerHelper,
|
|
76
|
+
) {
|
|
56
77
|
super(option);
|
|
57
78
|
this.view = option.view as LibroView;
|
|
58
|
-
this.
|
|
59
|
-
this.toDispose.push(watch(this.view.model, 'cells', this.onCellsChanged));
|
|
79
|
+
this.virtualizedManager = virtualizedManagerHelper.getOrCreate(this.view.model);
|
|
60
80
|
}
|
|
61
81
|
|
|
62
82
|
protected getProvider = (cell: CellView) => {
|
|
63
83
|
return this.providerMap.get(cell.id);
|
|
64
84
|
};
|
|
85
|
+
|
|
65
86
|
/**
|
|
66
87
|
* Report whether or not this provider has the ability to search on the given object
|
|
67
88
|
*
|
|
@@ -230,7 +251,7 @@ export class LibroSearchProvider extends AbstractSearchProvider {
|
|
|
230
251
|
*/
|
|
231
252
|
startQuery = async (
|
|
232
253
|
query: RegExp,
|
|
233
|
-
|
|
254
|
+
filters?: SearchFilters,
|
|
234
255
|
highlightNext = true,
|
|
235
256
|
): Promise<void> => {
|
|
236
257
|
if (!this.view) {
|
|
@@ -379,12 +400,11 @@ export class LibroSearchProvider extends AbstractSearchProvider {
|
|
|
379
400
|
this.onSearchProviderChanged();
|
|
380
401
|
this.cellsChangeDeferred = undefined;
|
|
381
402
|
};
|
|
382
|
-
|
|
403
|
+
|
|
404
|
+
onCellsChanged = async (): Promise<void> => {
|
|
383
405
|
if (!this.cellsChangeDeferred) {
|
|
384
406
|
this.cellsChangeDeferred = new Deferred();
|
|
385
|
-
this.cellsChangeDeferred.promise.then(this.doCellsChanged).catch(
|
|
386
|
-
//
|
|
387
|
-
});
|
|
407
|
+
this.cellsChangeDeferred.promise.then(this.doCellsChanged).catch(console.error);
|
|
388
408
|
this.cellsChangeDeferred.resolve();
|
|
389
409
|
}
|
|
390
410
|
};
|
|
@@ -401,35 +421,62 @@ export class LibroSearchProvider extends AbstractSearchProvider {
|
|
|
401
421
|
}
|
|
402
422
|
return index;
|
|
403
423
|
};
|
|
424
|
+
|
|
425
|
+
protected selectCell(selectIndex: number) {
|
|
426
|
+
if (selectIndex >= 0 && selectIndex < this.view.model.cells.length - 1) {
|
|
427
|
+
this.view.model.selectCell(this.view.model.cells[selectIndex]);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
404
431
|
protected stepNext = async (
|
|
405
432
|
reverse = false,
|
|
406
433
|
loop = false,
|
|
407
434
|
): Promise<SearchMatch | undefined> => {
|
|
408
|
-
const activateNewMatch = async () => {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
435
|
+
const activateNewMatch = async (match: SearchMatch) => {
|
|
436
|
+
if (this.getActiveIndex() !== this.currentProviderIndex!) {
|
|
437
|
+
this.selectCell(this.currentProviderIndex!);
|
|
438
|
+
}
|
|
439
|
+
const activeCell = this.view.activeCell;
|
|
440
|
+
|
|
441
|
+
if (!activeCell) {
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const node = activeCell.container?.current;
|
|
446
|
+
|
|
447
|
+
if (!elementInViewport(node!)) {
|
|
448
|
+
try {
|
|
449
|
+
if (this.view.activeCell) {
|
|
450
|
+
if (this.virtualizedManager.isVirtualized) {
|
|
451
|
+
if (EditorCellView.is(activeCell)) {
|
|
452
|
+
const line = activeCell.editor?.getPositionAt(match.position)?.line;
|
|
453
|
+
|
|
454
|
+
this.view.model.scrollToCellView({
|
|
455
|
+
cellIndex: this.view.activeCellIndex,
|
|
456
|
+
lineIndex: line,
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
} else {
|
|
460
|
+
this.view.model.scrollToView(this.view.activeCell);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
} catch (error) {
|
|
464
|
+
// no-op
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
// Unhide cell
|
|
468
|
+
if (activeCell.hasInputHidden) {
|
|
469
|
+
activeCell.hasInputHidden = false;
|
|
470
|
+
}
|
|
471
|
+
if (!elementInViewport(node!)) {
|
|
472
|
+
// It will not be possible the cell is not in the view
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
if (EditorCellView.is(activeCell)) {
|
|
476
|
+
// await activeCell.editor;
|
|
477
|
+
const editor = activeCell.editor;
|
|
478
|
+
editor?.revealSelection(editor.getSelection());
|
|
479
|
+
}
|
|
433
480
|
};
|
|
434
481
|
if (this.currentProviderIndex === undefined) {
|
|
435
482
|
this.currentProviderIndex = this.getActiveIndex()!;
|
|
@@ -441,7 +488,7 @@ export class LibroSearchProvider extends AbstractSearchProvider {
|
|
|
441
488
|
? await searchEngine?.highlightPrevious()
|
|
442
489
|
: await searchEngine?.highlightNext();
|
|
443
490
|
if (match) {
|
|
444
|
-
await activateNewMatch();
|
|
491
|
+
await activateNewMatch(match);
|
|
445
492
|
return match;
|
|
446
493
|
} else {
|
|
447
494
|
this.currentProviderIndex = this.currentProviderIndex + (reverse ? -1 : 1);
|
|
@@ -469,7 +516,7 @@ export class LibroSearchProvider extends AbstractSearchProvider {
|
|
|
469
516
|
: await searchEngine?.highlightNext();
|
|
470
517
|
|
|
471
518
|
if (match) {
|
|
472
|
-
await activateNewMatch();
|
|
519
|
+
await activateNewMatch(match);
|
|
473
520
|
return match;
|
|
474
521
|
}
|
|
475
522
|
}
|
|
@@ -478,7 +525,7 @@ export class LibroSearchProvider extends AbstractSearchProvider {
|
|
|
478
525
|
return undefined;
|
|
479
526
|
};
|
|
480
527
|
|
|
481
|
-
|
|
528
|
+
onActiveCellChanged = async () => {
|
|
482
529
|
await this._onSelectionChanged();
|
|
483
530
|
|
|
484
531
|
if (this.getActiveIndex() !== this.currentProviderIndex) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import 'reflect-metadata';
|
|
2
1
|
import assert from 'assert';
|
|
3
2
|
|
|
4
|
-
import type { SearchMatch } from '
|
|
3
|
+
import type { SearchMatch } from '@difizen/libro-code-editor';
|
|
4
|
+
|
|
5
5
|
import { LibroSearchUtils } from './libro-search-utils.js';
|
|
6
6
|
|
|
7
7
|
describe('libro search utils', () => {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
+
import type { SearchMatch } from '@difizen/libro-code-editor';
|
|
1
2
|
import { singleton } from '@difizen/mana-app';
|
|
2
3
|
|
|
3
|
-
import type { SearchMatch } from './libro-search-protocol.js';
|
|
4
|
-
|
|
5
4
|
/**
|
|
6
5
|
* Search Utils
|
|
7
6
|
*/
|
|
@@ -69,15 +68,18 @@ export class LibroSearchUtils {
|
|
|
69
68
|
const queryText = regex
|
|
70
69
|
? queryString
|
|
71
70
|
: queryString.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
|
|
72
|
-
|
|
71
|
+
try {
|
|
72
|
+
const ret = new RegExp(queryText, flag);
|
|
73
|
+
// If the empty string is hit, the search logic will freeze the browser tab
|
|
74
|
+
// Trying /^/ or /$/ on the codemirror search demo, does not find anything.
|
|
75
|
+
// So this is a limitation of the editor.
|
|
76
|
+
if (ret.test('')) {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
73
79
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
// So this is a limitation of the editor.
|
|
77
|
-
if (ret.test('')) {
|
|
80
|
+
return ret;
|
|
81
|
+
} catch (error) {
|
|
78
82
|
return undefined;
|
|
79
83
|
}
|
|
80
|
-
|
|
81
|
-
return ret;
|
|
82
84
|
}
|
|
83
85
|
}
|
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
|
-
RightOutlined,
|
|
3
|
-
ArrowUpOutlined,
|
|
4
2
|
ArrowDownOutlined,
|
|
5
|
-
|
|
3
|
+
ArrowUpOutlined,
|
|
6
4
|
CloseOutlined,
|
|
7
5
|
createFromIconfontCN,
|
|
6
|
+
EllipsisOutlined,
|
|
7
|
+
RightOutlined,
|
|
8
8
|
} from '@ant-design/icons';
|
|
9
9
|
import type { LibroView } from '@difizen/libro-core';
|
|
10
10
|
import { LirboContextKey } from '@difizen/libro-core';
|
|
11
|
-
import { prop, useInject } from '@difizen/mana-app';
|
|
11
|
+
import { prop, useInject, watch } from '@difizen/mana-app';
|
|
12
12
|
import { BaseView, view, ViewInstance } from '@difizen/mana-app';
|
|
13
13
|
import { inject, transient } from '@difizen/mana-app';
|
|
14
14
|
import { l10n } from '@difizen/mana-l10n';
|
|
15
|
-
import {
|
|
15
|
+
import { Button, Checkbox, Input, Tag } from 'antd';
|
|
16
16
|
import type { CheckboxChangeEvent } from 'antd/es/checkbox';
|
|
17
17
|
import type { InputRef } from 'antd/es/input';
|
|
18
18
|
import classnames from 'classnames';
|
|
19
|
-
import type { FC } from 'react';
|
|
20
19
|
import { forwardRef, useEffect, useRef } from 'react';
|
|
21
20
|
|
|
22
21
|
import type { LibroSearchProvider } from './libro-search-provider.js';
|
|
@@ -27,7 +26,7 @@ const IconFont = createFromIconfontCN({
|
|
|
27
26
|
scriptUrl: '//at.alicdn.com/t/a/font_3381673_65wfctnq7rt.js',
|
|
28
27
|
});
|
|
29
28
|
|
|
30
|
-
export const ReplaceToggle
|
|
29
|
+
export const ReplaceToggle = () => {
|
|
31
30
|
const instance = useInject<LibroSearchView>(ViewInstance);
|
|
32
31
|
return (
|
|
33
32
|
<div className="libro-search-replace-toggle" onClick={instance.toggleReplace}>
|
|
@@ -41,16 +40,24 @@ export const ReplaceToggle: FC = () => {
|
|
|
41
40
|
);
|
|
42
41
|
};
|
|
43
42
|
|
|
44
|
-
export const SearchIndex
|
|
43
|
+
export const SearchIndex = () => {
|
|
45
44
|
const instance = useInject<LibroSearchView>(ViewInstance);
|
|
45
|
+
|
|
46
|
+
// TODO: trigger update when current match index changed, matchesCount dont work
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
//
|
|
49
|
+
}, [instance.currentMatchIndex]);
|
|
50
|
+
|
|
46
51
|
return (
|
|
47
52
|
<div className="libro-search-index">
|
|
48
|
-
{instance.
|
|
53
|
+
{instance.matchesCount !== undefined
|
|
54
|
+
? `${instance.currentMatchIndex}/${instance.matchesCount}`
|
|
55
|
+
: '无结果'}
|
|
49
56
|
</div>
|
|
50
57
|
);
|
|
51
58
|
};
|
|
52
59
|
|
|
53
|
-
export const SearchContent
|
|
60
|
+
export const SearchContent = () => {
|
|
54
61
|
const instance = useInject<LibroSearchView>(ViewInstance);
|
|
55
62
|
const findInputRef = useRef<InputRef>(null);
|
|
56
63
|
useEffect(() => {
|
|
@@ -67,6 +74,7 @@ export const SearchContent: FC = () => {
|
|
|
67
74
|
}
|
|
68
75
|
return;
|
|
69
76
|
}, [instance]);
|
|
77
|
+
|
|
70
78
|
return (
|
|
71
79
|
<div
|
|
72
80
|
className="libro-search-content"
|
|
@@ -81,6 +89,7 @@ export const SearchContent: FC = () => {
|
|
|
81
89
|
value={instance.findStr}
|
|
82
90
|
onChange={instance.handleFindChange}
|
|
83
91
|
size="small"
|
|
92
|
+
placeholder="搜索"
|
|
84
93
|
suffix={
|
|
85
94
|
<span className="libro-search-input-suffix">
|
|
86
95
|
<IconFont
|
|
@@ -89,6 +98,7 @@ export const SearchContent: FC = () => {
|
|
|
89
98
|
})}
|
|
90
99
|
onClick={instance.toggleCaseSensitive}
|
|
91
100
|
type="icon-Aa"
|
|
101
|
+
title="Match Case"
|
|
92
102
|
/>
|
|
93
103
|
|
|
94
104
|
<IconFont
|
|
@@ -97,6 +107,7 @@ export const SearchContent: FC = () => {
|
|
|
97
107
|
})}
|
|
98
108
|
onClick={instance.toggleUseRegex}
|
|
99
109
|
type="icon-zhengzeshi"
|
|
110
|
+
title="Use Regular Expression"
|
|
100
111
|
/>
|
|
101
112
|
</span>
|
|
102
113
|
}
|
|
@@ -106,11 +117,13 @@ export const SearchContent: FC = () => {
|
|
|
106
117
|
<SearchIndex />
|
|
107
118
|
<div>
|
|
108
119
|
<Button
|
|
120
|
+
title="Previous Match"
|
|
109
121
|
onClick={instance.previous}
|
|
110
122
|
icon={<ArrowUpOutlined />}
|
|
111
123
|
size="small"
|
|
112
124
|
/>
|
|
113
125
|
<Button
|
|
126
|
+
title="Next Match"
|
|
114
127
|
onClick={instance.next}
|
|
115
128
|
icon={<ArrowDownOutlined />}
|
|
116
129
|
size="small"
|
|
@@ -131,6 +144,7 @@ export const SearchContent: FC = () => {
|
|
|
131
144
|
value={instance.replaceStr}
|
|
132
145
|
onChange={instance.handleReplaceChange}
|
|
133
146
|
size="small"
|
|
147
|
+
placeholder="替换"
|
|
134
148
|
/>
|
|
135
149
|
</td>
|
|
136
150
|
<td className="libro-search-action">
|
|
@@ -139,12 +153,14 @@ export const SearchContent: FC = () => {
|
|
|
139
153
|
onClick={instance.replace}
|
|
140
154
|
icon={<IconFont type="icon-zifuchuantihuan_2" />}
|
|
141
155
|
size="small"
|
|
156
|
+
title="Replace"
|
|
142
157
|
/>
|
|
143
158
|
|
|
144
159
|
<Button
|
|
145
160
|
onClick={instance.replaceAll}
|
|
146
161
|
icon={<IconFont type="icon-tihuantupian" />}
|
|
147
162
|
size="small"
|
|
163
|
+
title="Replace All"
|
|
148
164
|
/>
|
|
149
165
|
</div>
|
|
150
166
|
</td>
|
|
@@ -171,7 +187,7 @@ export const SearchContent: FC = () => {
|
|
|
171
187
|
};
|
|
172
188
|
// TODO: 更改图标
|
|
173
189
|
export const SearchComponent = forwardRef<HTMLDivElement>(function SearchComponent(
|
|
174
|
-
|
|
190
|
+
props: { top?: number },
|
|
175
191
|
ref,
|
|
176
192
|
) {
|
|
177
193
|
const instance = useInject<LibroSearchView>(ViewInstance);
|
|
@@ -193,41 +209,18 @@ export const SearchComponent = forwardRef<HTMLDivElement>(function SearchCompone
|
|
|
193
209
|
@view('libro-search-view')
|
|
194
210
|
export class LibroSearchView extends BaseView {
|
|
195
211
|
findInputRef?: React.RefObject<InputRef> | null;
|
|
196
|
-
contextKey: LirboContextKey;
|
|
197
|
-
utils: LibroSearchUtils;
|
|
198
|
-
searchProviderFactory: LibroSearchProviderFactory;
|
|
199
|
-
|
|
200
|
-
constructor(
|
|
201
|
-
@inject(LirboContextKey) contextKey: LirboContextKey,
|
|
202
|
-
@inject(LibroSearchUtils) utils: LibroSearchUtils,
|
|
203
|
-
@inject(LibroSearchProviderFactory)
|
|
204
|
-
searchProviderFactory: LibroSearchProviderFactory,
|
|
205
|
-
) {
|
|
206
|
-
super();
|
|
207
|
-
this.contextKey = contextKey;
|
|
208
|
-
this.utils = utils;
|
|
209
|
-
this.searchProviderFactory = searchProviderFactory;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
protected _libro?: LibroView;
|
|
213
|
-
|
|
214
|
-
get libro(): LibroView | undefined {
|
|
215
|
-
return this._libro;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
set libro(value: LibroView | undefined) {
|
|
219
|
-
this._libro = value;
|
|
220
|
-
this.searchProvider = this.searchProviderFactory({ view: this.libro! });
|
|
221
|
-
}
|
|
222
|
-
|
|
212
|
+
@inject(LirboContextKey) contextKey: LirboContextKey;
|
|
213
|
+
@inject(LibroSearchUtils) utils: LibroSearchUtils;
|
|
214
|
+
@inject(LibroSearchProviderFactory) searchProviderFactory: LibroSearchProviderFactory;
|
|
215
|
+
libro?: LibroView;
|
|
223
216
|
@prop() searchProvider?: LibroSearchProvider;
|
|
224
217
|
@prop() searchVisible = false;
|
|
225
218
|
get replaceVisible(): boolean {
|
|
226
219
|
return this.searchProvider?.replaceMode ?? false;
|
|
227
220
|
}
|
|
228
221
|
@prop() settingVisible = false;
|
|
229
|
-
@prop() findStr?: string
|
|
230
|
-
@prop() lastSearch?: string
|
|
222
|
+
@prop() findStr?: string = undefined;
|
|
223
|
+
@prop() lastSearch?: string = undefined;
|
|
231
224
|
@prop() replaceStr = '';
|
|
232
225
|
@prop() caseSensitive = false;
|
|
233
226
|
@prop() useRegex = false;
|
|
@@ -255,8 +248,31 @@ export class LibroSearchView extends BaseView {
|
|
|
255
248
|
return this.searchProvider?.matchesCount;
|
|
256
249
|
}
|
|
257
250
|
|
|
258
|
-
|
|
259
|
-
this.searchProvider
|
|
251
|
+
override onViewMount = () => {
|
|
252
|
+
if (!this.searchProvider && this.libro) {
|
|
253
|
+
this.searchProvider = this.searchProviderFactory({ view: this.libro });
|
|
254
|
+
this.toDispose.push(watch(this.libro.model, 'active', this.onActiveCellChanged));
|
|
255
|
+
this.toDispose.push(
|
|
256
|
+
this.libro.model.onSourceChanged(() => this.onCellsChanged()),
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
onActiveCellChanged = () => {
|
|
262
|
+
if (this.searchVisible) {
|
|
263
|
+
this.searchProvider?.onActiveCellChanged();
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
onCellsChanged = () => {
|
|
268
|
+
if (this.searchVisible) {
|
|
269
|
+
this.searchProvider?.onCellsChanged();
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
onviewWillUnmount = async () => {
|
|
274
|
+
await this.searchProvider?.endQuery();
|
|
275
|
+
this.searchProvider?.dispose();
|
|
260
276
|
};
|
|
261
277
|
|
|
262
278
|
show = () => {
|
|
@@ -269,6 +285,9 @@ export class LibroSearchView extends BaseView {
|
|
|
269
285
|
this.searchVisible = false;
|
|
270
286
|
this.contextKey.enableCommandMode();
|
|
271
287
|
this.searchProvider?.endQuery();
|
|
288
|
+
if (this.searchProvider) {
|
|
289
|
+
this.searchProvider.replaceMode = false;
|
|
290
|
+
}
|
|
272
291
|
this.libro?.focus();
|
|
273
292
|
};
|
|
274
293
|
|
|
@@ -316,6 +335,7 @@ export class LibroSearchView extends BaseView {
|
|
|
316
335
|
|
|
317
336
|
toggleUseRegex = () => {
|
|
318
337
|
this.useRegex = !this.useRegex;
|
|
338
|
+
this.search();
|
|
319
339
|
};
|
|
320
340
|
|
|
321
341
|
next = () => {
|
|
@@ -342,8 +362,8 @@ export class LibroSearchView extends BaseView {
|
|
|
342
362
|
const init = this.searchProvider?.getInitialQuery();
|
|
343
363
|
if (init) {
|
|
344
364
|
this.findStr = init;
|
|
345
|
-
this.search(false);
|
|
346
365
|
}
|
|
366
|
+
this.search(false);
|
|
347
367
|
};
|
|
348
368
|
getHeaderHeight = () => {
|
|
349
369
|
let height = 32;
|