/* 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"; const {createClass, createFactory} = require("devtools/client/shared/vendor/react"); const ToolboxToolbar = createFactory(require("devtools/client/framework/components/toolbox-toolbar")); const ELEMENT_PICKER_ID = "command-button-pick"; /** * This component serves as a state controller for the toolbox React component. It's a * thin layer for translating events and state of the outside world into the React update * cycle. This solution was used to keep the amount of code changes to a minimimum while * adapting the existing codebase to start using React. */ module.exports = createClass({ displayName: "ToolboxController", getInitialState() { // See the ToolboxToolbar propTypes for documentation on each of these items in state, // and for the defintions of the props that are expected to be passed in. return { focusedButton: ELEMENT_PICKER_ID, currentToolId: null, canRender: false, highlightedTool: "", areDockButtonsEnabled: true, panelDefinitions: [], hostTypes: [], canCloseToolbox: true, toolboxButtons: [], buttonIds: [], checkedButtonsUpdated: () => { this.forceUpdate(); } }; }, componentWillUnmount() { this.state.toolboxButtons.forEach(button => { button.off("updatechecked", this.state.checkedButtonsUpdated); }); }, /** * The button and tab ids must be known in order to be able to focus left and right * using the arrow keys. */ updateButtonIds() { const {panelDefinitions, toolboxButtons, optionsPanel, hostTypes, canCloseToolbox} = this.state; // This is a little gnarly, but go through all of the state and extract the IDs. this.setState({ buttonIds: [ ...toolboxButtons.filter(btn => btn.isInStartContainer).map(({id}) => id), ...panelDefinitions.map(({id}) => id), ...toolboxButtons.filter(btn => !btn.isInStartContainer).map(({id}) => id), optionsPanel ? optionsPanel.id : null, ...hostTypes.map(({position}) => "toolbox-dock-" + position), canCloseToolbox ? "toolbox-close" : null ].filter(id => id) }); this.updateFocusedButton(); }, updateFocusedButton() { this.setFocusedButton(this.state.focusedButton); }, setFocusedButton(focusedButton) { const {buttonIds} = this.state; this.setState({ focusedButton: focusedButton && buttonIds.includes(focusedButton) ? focusedButton : buttonIds[0] }); }, setCurrentToolId(currentToolId) { this.setState({currentToolId}); // Also set the currently focused button to this tool. this.setFocusedButton(currentToolId); }, setCanRender() { this.setState({ canRender: true }); this.updateButtonIds(); }, setOptionsPanel(optionsPanel) { this.setState({ optionsPanel }); this.updateButtonIds(); }, highlightTool(highlightedTool) { this.setState({ highlightedTool }); }, unhighlightTool(id) { if (this.state.highlightedTool === id) { this.setState({ highlightedTool: "" }); } }, setDockButtonsEnabled(areDockButtonsEnabled) { this.setState({ areDockButtonsEnabled }); this.updateButtonIds(); }, setHostTypes(hostTypes) { this.setState({ hostTypes }); this.updateButtonIds(); }, setCanCloseToolbox(canCloseToolbox) { this.setState({ canCloseToolbox }); this.updateButtonIds(); }, setPanelDefinitions(panelDefinitions) { this.setState({ panelDefinitions }); this.updateButtonIds(); }, setToolboxButtons(toolboxButtons) { // Listen for updates of the checked attribute. this.state.toolboxButtons.forEach(button => { button.off("updatechecked", this.state.checkedButtonsUpdated); }); toolboxButtons.forEach(button => { button.on("updatechecked", this.state.checkedButtonsUpdated); }); this.setState({ toolboxButtons }); this.updateButtonIds(); }, setCanMinimize(canMinimize) { /* Bug 1177463 - The minimize button is currently hidden until we agree on the UI for it, and until bug 1173849 is fixed too. */ // this.setState({ canMinimize }); }, render() { return ToolboxToolbar(Object.assign({}, this.props, this.state)); } });