diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /devtools/client/webconsole/components/Input/ReverseSearchInput.js | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'devtools/client/webconsole/components/Input/ReverseSearchInput.js')
-rw-r--r-- | devtools/client/webconsole/components/Input/ReverseSearchInput.js | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/devtools/client/webconsole/components/Input/ReverseSearchInput.js b/devtools/client/webconsole/components/Input/ReverseSearchInput.js new file mode 100644 index 0000000000..5cece45bc7 --- /dev/null +++ b/devtools/client/webconsole/components/Input/ReverseSearchInput.js @@ -0,0 +1,285 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// React & Redux +const { + Component, +} = require("resource://devtools/client/shared/vendor/react.js"); +const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js"); +const { + connect, +} = require("resource://devtools/client/shared/vendor/react-redux.js"); + +const { + getReverseSearchTotalResults, + getReverseSearchResultPosition, + getReverseSearchResult, +} = require("resource://devtools/client/webconsole/selectors/history.js"); + +loader.lazyRequireGetter( + this, + "PropTypes", + "resource://devtools/client/shared/vendor/react-prop-types.js" +); +loader.lazyRequireGetter( + this, + "actions", + "resource://devtools/client/webconsole/actions/index.js" +); +loader.lazyRequireGetter( + this, + "l10n", + "resource://devtools/client/webconsole/utils/messages.js", + true +); +loader.lazyRequireGetter( + this, + "PluralForm", + "resource://devtools/shared/plural-form.js", + true +); +loader.lazyRequireGetter( + this, + "KeyCodes", + "resource://devtools/client/shared/keycodes.js", + true +); + +const isMacOS = Services.appinfo.OS === "Darwin"; + +class ReverseSearchInput extends Component { + static get propTypes() { + return { + dispatch: PropTypes.func.isRequired, + setInputValue: PropTypes.func.isRequired, + focusInput: PropTypes.func.isRequired, + reverseSearchResult: PropTypes.string, + reverseSearchTotalResults: PropTypes.number, + reverseSearchResultPosition: PropTypes.number, + visible: PropTypes.bool, + initialValue: PropTypes.string, + }; + } + + constructor(props) { + super(props); + + this.onInputKeyDown = this.onInputKeyDown.bind(this); + } + + componentDidUpdate(prevProps) { + const { setInputValue, focusInput } = this.props; + if ( + prevProps.reverseSearchResult !== this.props.reverseSearchResult && + this.props.visible && + this.props.reverseSearchTotalResults > 0 + ) { + setInputValue(this.props.reverseSearchResult); + } + + if (prevProps.visible === true && this.props.visible === false) { + focusInput(); + } + + if ( + prevProps.visible === false && + this.props.visible === true && + this.props.initialValue + ) { + this.inputNode.value = this.props.initialValue; + } + } + + onEnterKeyboardShortcut(event) { + const { dispatch } = this.props; + event.stopPropagation(); + dispatch(actions.reverseSearchInputToggle()); + dispatch(actions.evaluateExpression(undefined, "reverse-search")); + } + + onEscapeKeyboardShortcut(event) { + const { dispatch } = this.props; + event.stopPropagation(); + dispatch(actions.reverseSearchInputToggle()); + } + + onBackwardNavigationKeyBoardShortcut(event, canNavigate) { + const { dispatch } = this.props; + event.stopPropagation(); + event.preventDefault(); + if (canNavigate) { + dispatch(actions.showReverseSearchBack({ access: "keyboard" })); + } + } + + onForwardNavigationKeyBoardShortcut(event, canNavigate) { + const { dispatch } = this.props; + event.stopPropagation(); + event.preventDefault(); + if (canNavigate) { + dispatch(actions.showReverseSearchNext({ access: "keyboard" })); + } + } + + onInputKeyDown(event) { + const { keyCode, key, ctrlKey, shiftKey } = event; + const { reverseSearchTotalResults } = this.props; + + // On Enter, we trigger an execute. + if (keyCode === KeyCodes.DOM_VK_RETURN) { + return this.onEnterKeyboardShortcut(event); + } + + const lowerCaseKey = key.toLowerCase(); + + // On Escape (and Ctrl + c on OSX), we close the reverse search input. + if ( + keyCode === KeyCodes.DOM_VK_ESCAPE || + (isMacOS && ctrlKey && lowerCaseKey === "c") + ) { + return this.onEscapeKeyboardShortcut(event); + } + + const canNavigate = + Number.isInteger(reverseSearchTotalResults) && + reverseSearchTotalResults > 1; + + if ( + (!isMacOS && key === "F9" && !shiftKey) || + (isMacOS && ctrlKey && lowerCaseKey === "r") + ) { + return this.onBackwardNavigationKeyBoardShortcut(event, canNavigate); + } + + if ( + (!isMacOS && key === "F9" && shiftKey) || + (isMacOS && ctrlKey && lowerCaseKey === "s") + ) { + return this.onForwardNavigationKeyBoardShortcut(event, canNavigate); + } + + return null; + } + + renderSearchInformation() { + const { reverseSearchTotalResults, reverseSearchResultPosition } = + this.props; + + if (!Number.isInteger(reverseSearchTotalResults)) { + return null; + } + + let text; + if (reverseSearchTotalResults === 0) { + text = l10n.getStr("webconsole.reverseSearch.noResult"); + } else { + const resultsString = l10n.getStr("webconsole.reverseSearch.results"); + text = PluralForm.get(reverseSearchTotalResults, resultsString) + .replace("#1", reverseSearchResultPosition) + .replace("#2", reverseSearchTotalResults); + } + + return dom.div({ className: "reverse-search-info" }, text); + } + + renderNavigationButtons() { + const { dispatch, reverseSearchTotalResults } = this.props; + + if ( + !Number.isInteger(reverseSearchTotalResults) || + reverseSearchTotalResults <= 1 + ) { + return null; + } + + return [ + dom.button({ + key: "search-result-button-prev", + className: "devtools-button search-result-button-prev", + title: l10n.getFormatStr( + "webconsole.reverseSearch.result.previousButton.tooltip", + [isMacOS ? "Ctrl + R" : "F9"] + ), + onClick: () => { + dispatch(actions.showReverseSearchBack({ access: "click" })); + this.inputNode.focus(); + }, + }), + dom.button({ + key: "search-result-button-next", + className: "devtools-button search-result-button-next", + title: l10n.getFormatStr( + "webconsole.reverseSearch.result.nextButton.tooltip", + [isMacOS ? "Ctrl + S" : "Shift + F9"] + ), + onClick: () => { + dispatch(actions.showReverseSearchNext({ access: "click" })); + this.inputNode.focus(); + }, + }), + ]; + } + + render() { + const { dispatch, visible, reverseSearchTotalResults } = this.props; + + if (!visible) { + return null; + } + + const classNames = ["reverse-search"]; + + if (reverseSearchTotalResults === 0) { + classNames.push("no-result"); + } + + return dom.div( + { className: classNames.join(" ") }, + dom.input({ + ref: node => { + this.inputNode = node; + }, + autoFocus: true, + placeholder: l10n.getStr("webconsole.reverseSearch.input.placeHolder"), + className: "reverse-search-input devtools-monospace", + onKeyDown: this.onInputKeyDown, + onInput: ({ target }) => + dispatch(actions.reverseSearchInputChange(target.value)), + }), + dom.div( + { + className: "reverse-search-actions", + }, + this.renderSearchInformation(), + this.renderNavigationButtons(), + dom.button({ + className: "devtools-button reverse-search-close-button", + title: l10n.getFormatStr( + "webconsole.reverseSearch.closeButton.tooltip", + ["Esc" + (isMacOS ? " | Ctrl + C" : "")] + ), + onClick: () => { + dispatch(actions.reverseSearchInputToggle()); + }, + }) + ) + ); + } +} + +const mapStateToProps = state => ({ + visible: state.ui.reverseSearchInputVisible, + reverseSearchTotalResults: getReverseSearchTotalResults(state), + reverseSearchResultPosition: getReverseSearchResultPosition(state), + reverseSearchResult: getReverseSearchResult(state), +}); + +const mapDispatchToProps = dispatch => ({ dispatch }); + +module.exports = connect( + mapStateToProps, + mapDispatchToProps +)(ReverseSearchInput); |