UNPKG

9.57 kBJavaScriptView Raw
1"use strict";
2// *****************************************************************************
3// Copyright (C) 2018 TypeFox and others.
4//
5// This program and the accompanying materials are made available under the
6// terms of the Eclipse Public License v. 2.0 which is available at
7// http://www.eclipse.org/legal/epl-2.0.
8//
9// This Source Code may also be made available under the following Secondary
10// Licenses when the conditions for such availability set forth in the Eclipse
11// Public License v. 2.0 are satisfied: GNU General Public License, version 2
12// with the GNU Classpath Exception which is available at
13// https://www.gnu.org/software/classpath/license.html.
14//
15// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
16// *****************************************************************************
17Object.defineProperty(exports, "__esModule", { value: true });
18exports.SearchBoxFactory = exports.SearchBox = exports.SearchBoxProps = void 0;
19const search_box_debounce_1 = require("../tree/search-box-debounce");
20const widget_1 = require("../widgets/widget");
21const event_1 = require("../../common/event");
22const keys_1 = require("../keyboard/keys");
23const nls_1 = require("../../common/nls");
24var SearchBoxProps;
25(function (SearchBoxProps) {
26 /**
27 * The default search box widget option.
28 */
29 SearchBoxProps.DEFAULT = search_box_debounce_1.SearchBoxDebounceOptions.DEFAULT;
30})(SearchBoxProps = exports.SearchBoxProps || (exports.SearchBoxProps = {}));
31/**
32 * The search box widget.
33 */
34class SearchBox extends widget_1.BaseWidget {
35 constructor(props, debounce) {
36 super();
37 this.props = props;
38 this.debounce = debounce;
39 this.nextEmitter = new event_1.Emitter();
40 this.previousEmitter = new event_1.Emitter();
41 this.closeEmitter = new event_1.Emitter();
42 this.textChangeEmitter = new event_1.Emitter();
43 this.filterToggleEmitter = new event_1.Emitter();
44 this._isFiltering = false;
45 this.toDispose.pushAll([
46 this.nextEmitter,
47 this.previousEmitter,
48 this.closeEmitter,
49 this.textChangeEmitter,
50 this.filterToggleEmitter,
51 this.debounce,
52 this.debounce.onChanged(data => this.fireTextChange(data))
53 ]);
54 this.hide();
55 this.update();
56 const { input, filter } = this.createContent();
57 this.input = input;
58 this.filter = filter;
59 }
60 get onPrevious() {
61 return this.previousEmitter.event;
62 }
63 get onNext() {
64 return this.nextEmitter.event;
65 }
66 get onClose() {
67 return this.closeEmitter.event;
68 }
69 get onTextChange() {
70 return this.textChangeEmitter.event;
71 }
72 get onFilterToggled() {
73 return this.filterToggleEmitter.event;
74 }
75 get isFiltering() {
76 return this._isFiltering;
77 }
78 get keyCodePredicate() {
79 return this.canHandle.bind(this);
80 }
81 firePrevious() {
82 this.previousEmitter.fire(undefined);
83 }
84 fireNext() {
85 this.nextEmitter.fire(undefined);
86 }
87 fireClose() {
88 this.closeEmitter.fire(undefined);
89 }
90 fireTextChange(input) {
91 this.textChangeEmitter.fire(input);
92 }
93 fireFilterToggle() {
94 this.doFireFilterToggle();
95 }
96 doFireFilterToggle(toggleTo = !this._isFiltering) {
97 if (this.filter) {
98 if (toggleTo) {
99 this.filter.classList.add(SearchBox.Styles.FILTER_ON);
100 }
101 else {
102 this.filter.classList.remove(SearchBox.Styles.FILTER_ON);
103 }
104 this._isFiltering = toggleTo;
105 this.filterToggleEmitter.fire(toggleTo);
106 this.update();
107 }
108 }
109 handle(event) {
110 event.preventDefault();
111 const keyCode = keys_1.KeyCode.createKeyCode(event);
112 if (this.canHandle(keyCode)) {
113 if (keys_1.Key.equals(keys_1.Key.ESCAPE, keyCode) || this.isCtrlBackspace(keyCode)) {
114 this.hide();
115 }
116 else {
117 this.show();
118 this.handleKey(keyCode);
119 }
120 }
121 }
122 handleArrowUp() {
123 this.firePrevious();
124 }
125 handleArrowDown() {
126 this.fireNext();
127 }
128 onBeforeHide() {
129 this.removeClass(SearchBox.Styles.NO_MATCH);
130 this.doFireFilterToggle(false);
131 this.debounce.append(undefined);
132 this.fireClose();
133 }
134 handleKey(keyCode) {
135 const character = keys_1.Key.equals(keys_1.Key.BACKSPACE, keyCode) ? '\b' : keyCode.character;
136 const data = this.debounce.append(character);
137 if (data) {
138 this.input.textContent = this.getTrimmedContent(data);
139 this.update();
140 }
141 else {
142 this.hide();
143 }
144 }
145 getTrimmedContent(data) {
146 if (data.length > SearchBox.MAX_CONTENT_LENGTH) {
147 return '...' + data.substring(data.length - SearchBox.MAX_CONTENT_LENGTH);
148 }
149 return data;
150 }
151 canHandle(keyCode) {
152 if (keyCode === undefined) {
153 return false;
154 }
155 const { ctrl, alt, meta } = keyCode;
156 if (this.isCtrlBackspace(keyCode)) {
157 return true;
158 }
159 if (ctrl || alt || meta || keyCode.key === keys_1.Key.SPACE) {
160 return false;
161 }
162 if (keyCode.character || (this.isVisible && SearchBox.SPECIAL_KEYS.some(key => keys_1.Key.equals(key, keyCode)))) {
163 return true;
164 }
165 return false;
166 }
167 isCtrlBackspace(keyCode) {
168 if (keyCode.ctrl && keys_1.Key.equals(keys_1.Key.BACKSPACE, keyCode)) {
169 return true;
170 }
171 return false;
172 }
173 updateHighlightInfo(info) {
174 if (info.filterText && info.filterText.length > 0) {
175 if (info.matched === 0) {
176 this.addClass(SearchBox.Styles.NO_MATCH);
177 }
178 else {
179 this.removeClass(SearchBox.Styles.NO_MATCH);
180 }
181 }
182 }
183 createContent() {
184 this.node.setAttribute('tabIndex', '0');
185 this.addClass(SearchBox.Styles.SEARCH_BOX);
186 const input = document.createElement('span');
187 input.classList.add(SearchBox.Styles.SEARCH_INPUT);
188 this.node.appendChild(input);
189 const buttons = document.createElement('div');
190 buttons.classList.add(SearchBox.Styles.SEARCH_BUTTONS_WRAPPER);
191 this.node.appendChild(buttons);
192 let filter;
193 if (this.props.showFilter) {
194 filter = document.createElement('div');
195 filter.classList.add(SearchBox.Styles.BUTTON, ...SearchBox.Styles.FILTER);
196 filter.title = nls_1.nls.localizeByDefault('Filter on Type');
197 buttons.appendChild(filter);
198 filter.onclick = this.fireFilterToggle.bind(this);
199 }
200 let previous;
201 let next;
202 let close;
203 if (!!this.props.showButtons) {
204 previous = document.createElement('div');
205 previous.classList.add(SearchBox.Styles.BUTTON, SearchBox.Styles.BUTTON_PREVIOUS);
206 previous.title = nls_1.nls.localize('theia/core/searchbox/previous', 'Previous (Up)');
207 buttons.appendChild(previous);
208 previous.onclick = () => this.firePrevious.bind(this)();
209 next = document.createElement('div');
210 next.classList.add(SearchBox.Styles.BUTTON, SearchBox.Styles.BUTTON_NEXT);
211 next.title = nls_1.nls.localize('theia/core/searchbox/next', 'Next (Down)');
212 buttons.appendChild(next);
213 next.onclick = () => this.fireNext.bind(this)();
214 }
215 if (this.props.showButtons || this.props.showFilter) {
216 close = document.createElement('div');
217 close.classList.add(SearchBox.Styles.BUTTON, SearchBox.Styles.BUTTON_CLOSE);
218 close.title = nls_1.nls.localize('theia/core/searchbox/close', 'Close (Escape)');
219 buttons.appendChild(close);
220 close.onclick = () => this.hide.bind(this)();
221 }
222 return {
223 container: this.node,
224 input,
225 filter,
226 previous,
227 next,
228 close
229 };
230 }
231 onAfterAttach(msg) {
232 super.onAfterAttach(msg);
233 // eslint-disable-next-line @typescript-eslint/no-explicit-any
234 this.addEventListener(this.input, 'selectstart', () => false);
235 }
236}
237exports.SearchBox = SearchBox;
238SearchBox.SPECIAL_KEYS = [
239 keys_1.Key.ESCAPE,
240 keys_1.Key.BACKSPACE
241];
242SearchBox.MAX_CONTENT_LENGTH = 15;
243(function (SearchBox) {
244 /**
245 * CSS classes for the search box widget.
246 */
247 let Styles;
248 (function (Styles) {
249 Styles.SEARCH_BOX = 'theia-search-box';
250 Styles.SEARCH_INPUT = 'theia-search-input';
251 Styles.SEARCH_BUTTONS_WRAPPER = 'theia-search-buttons-wrapper';
252 Styles.BUTTON = 'theia-search-button';
253 Styles.FILTER = ['codicon', 'codicon-filter'];
254 Styles.FILTER_ON = 'filter-active';
255 Styles.BUTTON_PREVIOUS = 'theia-search-button-previous';
256 Styles.BUTTON_NEXT = 'theia-search-button-next';
257 Styles.BUTTON_CLOSE = 'theia-search-button-close';
258 Styles.NON_SELECTABLE = 'theia-non-selectable';
259 Styles.NO_MATCH = 'no-match';
260 })(Styles = SearchBox.Styles || (SearchBox.Styles = {}));
261})(SearchBox = exports.SearchBox || (exports.SearchBox = {}));
262/**
263 * Search box factory.
264 */
265exports.SearchBoxFactory = Symbol('SearchBoxFactory');
266//# sourceMappingURL=search-box.js.map
\No newline at end of file