239 lines
6.9 KiB
JavaScript
239 lines
6.9 KiB
JavaScript
/* 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("devtools/client/shared/vendor/react");
|
|
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
|
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
|
|
|
const {
|
|
getReverseSearchTotalResults,
|
|
getReverseSearchResultPosition,
|
|
getReverseSearchResult,
|
|
} = require("devtools/client/webconsole/selectors/history");
|
|
|
|
loader.lazyRequireGetter(this, "PropTypes", "devtools/client/shared/vendor/react-prop-types");
|
|
loader.lazyRequireGetter(this, "actions", "devtools/client/webconsole/actions/index");
|
|
loader.lazyRequireGetter(this, "l10n", "devtools/client/webconsole/utils/messages", true);
|
|
loader.lazyRequireGetter(this, "PluralForm", "devtools/shared/plural-form", true);
|
|
loader.lazyRequireGetter(this, "KeyCodes", "devtools/client/shared/keycodes", true);
|
|
|
|
const Services = require("Services");
|
|
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,
|
|
evaluateInput: 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;
|
|
}
|
|
}
|
|
|
|
onInputKeyDown(event) {
|
|
const {
|
|
keyCode,
|
|
key,
|
|
ctrlKey,
|
|
shiftKey,
|
|
} = event;
|
|
|
|
const {
|
|
dispatch,
|
|
evaluateInput,
|
|
reverseSearchTotalResults,
|
|
} = this.props;
|
|
|
|
// On Enter, we trigger an execute.
|
|
if (keyCode === KeyCodes.DOM_VK_RETURN) {
|
|
event.stopPropagation();
|
|
dispatch(actions.reverseSearchInputToggle());
|
|
evaluateInput();
|
|
return;
|
|
}
|
|
|
|
// On Escape (and Ctrl + c on OSX), we close the reverse search input.
|
|
if (
|
|
keyCode === KeyCodes.DOM_VK_ESCAPE || (
|
|
isMacOS && ctrlKey === true && key.toLowerCase() === "c"
|
|
)
|
|
) {
|
|
event.stopPropagation();
|
|
dispatch(actions.reverseSearchInputToggle());
|
|
return;
|
|
}
|
|
|
|
const canNavigate = Number.isInteger(reverseSearchTotalResults)
|
|
&& reverseSearchTotalResults > 1;
|
|
|
|
if (
|
|
(!isMacOS && key === "F9" && shiftKey === false) ||
|
|
(isMacOS && ctrlKey === true && key.toLowerCase() === "r")
|
|
) {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
if (canNavigate) {
|
|
dispatch(actions.showReverseSearchBack());
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (
|
|
(!isMacOS && key === "F9" && shiftKey === true) ||
|
|
(isMacOS && ctrlKey === true && key.toLowerCase() === "s")
|
|
) {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
if (canNavigate) {
|
|
dispatch(actions.showReverseSearchNext());
|
|
}
|
|
}
|
|
}
|
|
|
|
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());
|
|
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());
|
|
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)),
|
|
}),
|
|
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);
|