t) { return; } this.shortcuts = new KeyShortcuts({ window, }); this.shortcuts.on(this.props.keyShortcut, event => { if (this.props.onFocusKeyboardShortcut?.(event)) { return; } event.preventDefault(); this.focus(); }); } componentWillUnmount() { if (this.shortcuts) { this.shortcuts.destroy(); } // Clean up an existing timeout. if (this.searchTimeout) { clearTimeout(this.searchTimeout); } } focus() { if (this.inputRef) { this.inputRef.current.focus(); } } onChange(inputValue = "") { if (this.state.value !== inputValue) { this.setState({ focused: true, value: inputValue, }); } if (!this.props.delay) { this.props.onChange(inputValue); return; } // Clean up an existing timeout before creating a new one. if (this.searchTimeout) { clearTimeout(this.searchTimeout); } // Execute the search after a timeout. It makes the UX // smoother if the user is typing quickly. this.searchTimeout = setTimeout(() => { this.searchTimeout = null; this.props.onChange(this.state.value); }, this.props.delay); } onClearButtonClick() { this.onChange(""); if (this.props.onClearButtonClick) { this.props.onClearButtonClick(); } } onFocus() { if (this.props.onFocus) { this.props.onFocus(); } this.setState({ focused: true }); } onBlur() { if (this.props.onBlur) { this.props.onBlur(); } this.setState({ focused: false }); } onKeyDown(e) { if (this.props.onKeyDown) { this.props.onKeyDown(e); } const autocomplete = this.autocompleteRef.current; if (!autocomplete || autocomplete.state.list.length <= 0) { return; } switch (e.key) { case "ArrowDown": e.preventDefault(); autocomplete.jumpBy(1); break; case "ArrowUp": e.preventDefault(); autocomplete.jumpBy(-1); break; case "PageDown": e.preventDefault(); autocomplete.jumpBy(5); break; case "PageUp": e.preventDefault(); autocomplete.jumpBy(-5); break; case "Enter": case "Tab": e.preventDefault(); autocomplete.select(); break; case "Escape": e.preventDefault(); this.onBlur(); break; case "Home": e.preventDefault(); autocomplete.jumpToTop(); break; case "End": e.preventDefault(); autocomplete.jumpToBottom(); break; } } render() { const { autocompleteProvider, summary, summaryId, summaryTooltip, learnMoreTitle, learnMoreUrl, placeholder, type = "search", } = this.props; const { value } = this.state; const showAutocomplete = autocompleteProvider && this.state.focused && value !== ""; const showLearnMoreLink = learnMoreUrl && value === ""; return dom.div( { className: "devtools-searchbox" }, dom.input({ className: `devtools-${type}input`, onBlur: this.onBlur, onChange: e => this.onChange(e.target.value), onFocus: this.onFocus, onKeyDown: this.onKeyDown, placeholder, ref: this.inputRef, value, type: "search", "aria-describedby": (summary && summaryId) || undefined, }), showLearnMoreLink && MDNLink({ title: learnMoreTitle, url: learnMoreUrl, }), summary ? dom.span( { className: "devtools-searchinput-summary", id: summaryId, title: summaryTooltip, }, summary ) : null, dom.button({ className: "devtools-searchinput-clear", hidden: value === "", onClick: this.onClearButtonClick, title: l10n.getStr("searchBox.clearButtonTitle"), }), showAutocomplete && SearchBoxAutocompletePopup({ autocompleteProvider, filter: value, onItemSelected: itemValue => this.onChange(itemValue), ref: this.autocompleteRef, }) ); } } module.exports = SearchBox; PK