Bug 1406287 - Part 3: Correspond for changing size of sidebar. r?gl draft
authorDaisuke Akatsuka <dakatsuka@mozilla.com>
Mon, 13 Nov 2017 17:43:25 +0900
changeset 697016 11b0a5f8ae00f663c641d8da4a2fb535eaa3c8c7
parent 697015 c86a160ef96269d0b50973995f4dbb7f9d2a1fec
child 697017 c4dccaec9fee3b7a588dd181078fd59d80852c4a
push id88864
push userbmo:dakatsuka@mozilla.com
push dateMon, 13 Nov 2017 08:47:24 +0000
reviewersgl
bugs1406287
milestone58.0a1
Bug 1406287 - Part 3: Correspond for changing size of sidebar. r?gl MozReview-Commit-ID: 9ndHImmpM9c
devtools/client/inspector/animation/actions/index.js
devtools/client/inspector/animation/actions/moz.build
devtools/client/inspector/animation/actions/sidebar.js
devtools/client/inspector/animation/animation.js
devtools/client/inspector/animation/components/AnimationTimelineTickList.js
devtools/client/inspector/animation/reducers/moz.build
devtools/client/inspector/animation/reducers/sidebar.js
devtools/client/inspector/inspector.js
devtools/client/inspector/reducers.js
devtools/client/shared/components/splitter/SplitBox.js
--- a/devtools/client/inspector/animation/actions/index.js
+++ b/devtools/client/inspector/animation/actions/index.js
@@ -2,13 +2,19 @@
  * 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 { createEnum } = require("devtools/client/shared/enum");
 
 createEnum([
+
   // Update the list of animation.
   "UPDATE_ANIMATIONS",
+
   // Update state of the picker enabled.
   "UPDATE_ELEMENT_PICKER_ENABLED",
+
+  // Update sidebar size.
+  "UPDATE_SIDEBAR_SIZE",
+
 ], module.exports);
--- a/devtools/client/inspector/animation/actions/moz.build
+++ b/devtools/client/inspector/animation/actions/moz.build
@@ -1,9 +1,10 @@
 # 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/.
 
 DevToolsModules(
     'animations.js',
     'element-picker.js',
-    'index.js'
+    'index.js',
+    'sidebar.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/actions/sidebar.js
@@ -0,0 +1,19 @@
+/* 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 { UPDATE_SIDEBAR_SIZE } = require("./index");
+
+module.exports = {
+  /**
+   * Update the sidebar size.
+   */
+  updateSidebarSize(size) {
+    return {
+      type: UPDATE_SIDEBAR_SIZE,
+      size,
+    };
+  }
+};
--- a/devtools/client/inspector/animation/animation.js
+++ b/devtools/client/inspector/animation/animation.js
@@ -5,27 +5,31 @@
 "use strict";
 
 const { AnimationsFront } = require("devtools/shared/fronts/animation");
 const { createElement, createFactory } = require("devtools/client/shared/vendor/react");
 const { Provider } = require("devtools/client/shared/vendor/react-redux");
 
 const App = createFactory(require("./components/App"));
 const { isAllTimingEffectEqual } = require("./utils/utils");
+
 const { updateAnimations } = require("./actions/animations");
 const { updateElementPickerEnabled } = require("./actions/element-picker");
+const { updateSidebarSize } = require("./actions/sidebar");
 
 class AnimationInspector {
   constructor(inspector) {
     this.inspector = inspector;
 
     this.toggleElementPicker = this.toggleElementPicker.bind(this);
     this.update = this.update.bind(this);
     this.onElementPickerStarted = this.onElementPickerStarted.bind(this);
     this.onElementPickerStopped = this.onElementPickerStopped.bind(this);
+    this.onSidebarResized = this.onSidebarResized.bind(this);
+    this.onSidebarSelect = this.onSidebarSelect.bind(this);
 
     this.init();
   }
 
   init() {
     const target = this.inspector.target;
     this.animationsFront = new AnimationsFront(target.client, target.form);
 
@@ -39,24 +43,26 @@ class AnimationInspector {
         {
           toggleElementPicker: this.toggleElementPicker
         }
       )
     );
     this.provider = provider;
 
     this.inspector.selection.on("new-node-front", this.update);
-    this.inspector.sidebar.on("newanimationinspector-selected", this.update);
+    this.inspector.sidebar.on("newanimationinspector-selected", this.onSidebarSelect);
+    this.inspector.toolbox.on("inspector-sidebar-resized", this.onSidebarResized);
     this.inspector.toolbox.on("picker-started", this.onElementPickerStarted);
     this.inspector.toolbox.on("picker-stopped", this.onElementPickerStopped);
   }
 
   destroy() {
     this.inspector.selection.off("new-node-front", this.update);
-    this.inspector.sidebar.off("newanimationinspector-selected", this.update);
+    this.inspector.sidebar.off("newanimationinspector-selected", this.onSidebarSelect);
+    this.inspector.toolbox.off("inspector-sidebar-resized", this.onSidebarResized);
     this.inspector.toolbox.off("picker-started", this.onElementPickerStarted);
     this.inspector.toolbox.off("picker-stopped", this.onElementPickerStopped);
 
     this.inspector = null;
   }
 
   async update() {
     if (!this.inspector || !this.isPanelVisible()) {
@@ -92,11 +98,24 @@ class AnimationInspector {
 
   onElementPickerStarted() {
     this.inspector.store.dispatch(updateElementPickerEnabled(true));
   }
 
   onElementPickerStopped() {
     this.inspector.store.dispatch(updateElementPickerEnabled(false));
   }
+
+  onSidebarSelect() {
+    this.update();
+    this.onSidebarResized(null, this.inspector.getSidebarSize());
+  }
+
+  onSidebarResized(type, size) {
+    if (!this.isPanelVisible()) {
+      return;
+    }
+
+    this.inspector.store.dispatch(updateSidebarSize(size));
+  }
 }
 
 module.exports = AnimationInspector;
--- a/devtools/client/inspector/animation/components/AnimationTimelineTickList.js
+++ b/devtools/client/inspector/animation/components/AnimationTimelineTickList.js
@@ -1,45 +1,68 @@
 /* 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 { createFactory, DOM: dom, PropTypes, PureComponent } =
   require("devtools/client/shared/vendor/react");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
 const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 
 const AnimationTimelineTickItem = createFactory(require("./AnimationTimelineTickItem"));
 
 const TimeScale = require("../utils/timescale");
 const { findOptimalTimeInterval } = require("../utils/utils");
 
 // The minimum spacing between 2 time graduation headers in the timeline (px).
 const TIME_GRADUATION_MIN_SPACING = 40;
 
 class AnimationTimelineTickList extends PureComponent {
   static get propTypes() {
     return {
       animations: PropTypes.arrayOf(PropTypes.object).isRequired,
+      sidebarWidth: PropTypes.number.isRequired,
     };
   }
 
   constructor(props) {
     super(props);
 
     this.state = {
       tickList: [],
     };
   }
 
   componentDidMount() {
     this.updateTickList();
   }
 
+  componentWillReceiveProps(nextProps) {
+    this.updateTickList();
+  }
+
+  shouldComponentUpdate(nextProps, nextState) {
+    if (this.state.tickList.length !== nextState.tickList.length) {
+      return true;
+    }
+
+    for (let i = 0; i < this.state.tickList.length; i++) {
+      const currentTickItem = this.state.tickList[i];
+      const nextTickItem = nextState.tickList[i];
+
+      if (currentTickItem.text !== nextTickItem.text) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
   updateTickList() {
     const { animations } = this.props;
     const timeScale = new TimeScale(animations);
     const tickListEl = ReactDOM.findDOMNode(this);
     const width = tickListEl.offsetWidth;
     const animationDuration = timeScale.getDuration();
     const minTimeInterval = TIME_GRADUATION_MIN_SPACING * animationDuration / width;
     const intervalLength = findOptimalTimeInterval(minTimeInterval);
@@ -65,9 +88,15 @@ class AnimationTimelineTickList extends 
       {
         className: "animation-timeline-tick-list"
       },
       tickList.map(tickItem => AnimationTimelineTickItem(tickItem))
     );
   }
 }
 
-module.exports = AnimationTimelineTickList;
+const mapStateToProps = state => {
+  return {
+    sidebarWidth: state.animationSidebar.width
+  };
+};
+
+module.exports = connect(mapStateToProps)(AnimationTimelineTickList);
--- a/devtools/client/inspector/animation/reducers/moz.build
+++ b/devtools/client/inspector/animation/reducers/moz.build
@@ -1,8 +1,9 @@
 # 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/.
 
 DevToolsModules(
     'animations.js',
     'element-picker.js',
+    'sidebar.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/reducers/sidebar.js
@@ -0,0 +1,23 @@
+/* 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 { UPDATE_SIDEBAR_SIZE } = require("../actions/index");
+
+const INITIAL_SIZE = {
+  width: 0,
+  height: 0
+};
+
+const reducers = {
+  [UPDATE_SIDEBAR_SIZE](_, { size }) {
+    return size;
+  }
+};
+
+module.exports = function (size = INITIAL_SIZE, action) {
+  const reducer = reducers[action.type];
+  return reducer ? reducer(size, action) : size;
+};
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -121,16 +121,17 @@ function Inspector(toolbox) {
   this.onDetached = this.onDetached.bind(this);
   this.onMarkupLoaded = this.onMarkupLoaded.bind(this);
   this.onNewSelection = this.onNewSelection.bind(this);
   this.onNewRoot = this.onNewRoot.bind(this);
   this.onPanelWindowResize = this.onPanelWindowResize.bind(this);
   this.onShowBoxModelHighlighterForNode =
     this.onShowBoxModelHighlighterForNode.bind(this);
   this.onSidebarHidden = this.onSidebarHidden.bind(this);
+  this.onSidebarResized = this.onSidebarResized.bind(this);
   this.onSidebarSelect = this.onSidebarSelect.bind(this);
   this.onSidebarShown = this.onSidebarShown.bind(this);
 
   this._target.on("will-navigate", this._onBeforeNavigate);
   this._detectingActorFeatures = this._detectActorFeatures();
 }
 
 Inspector.prototype = {
@@ -498,16 +499,17 @@ Inspector.prototype = {
       endPanelControl: true,
       startPanel: this.InspectorTabPanel({
         id: "inspector-main-content"
       }),
       endPanel: this.InspectorTabPanel({
         id: "inspector-sidebar-container"
       }),
       vert: this.useLandscapeMode(),
+      onControlledPanelResized: this.onSidebarResized,
     });
 
     this._splitter = this.ReactDOM.render(splitter,
       this.panelDoc.getElementById("inspector-splitter-box"));
 
     this.panelWin.addEventListener("resize", this.onPanelWindowResize, true);
   },
 
@@ -568,16 +570,20 @@ Inspector.prototype = {
 
     // Then forces the panel creation by calling getPanel
     // (This allows lazy loading the panels only once we select them)
     this.getPanel(toolId);
 
     this.toolbox.emit("inspector-sidebar-select", toolId);
   },
 
+  onSidebarResized: function (width, height) {
+    this.toolbox.emit("inspector-sidebar-resized", { width, height });
+  },
+
   /**
    * Lazily get and create panel instances displayed in the sidebar
    */
   getPanel: function (id) {
     if (this._panels.has(id)) {
       return this._panels.get(id);
     }
     let panel;
--- a/devtools/client/inspector/reducers.js
+++ b/devtools/client/inspector/reducers.js
@@ -5,16 +5,18 @@
 "use strict";
 
 // This file exposes the Redux reducers of the box model, grid and grid highlighter
 // settings.
 
 exports.animations = require("devtools/client/inspector/animation/reducers/animations");
 exports.animationElementPicker =
   require("devtools/client/inspector/animation/reducers/element-picker");
+exports.animationSidebar =
+  require("devtools/client/inspector/animation/reducers/sidebar");
 exports.boxModel = require("devtools/client/inspector/boxmodel/reducers/box-model");
 exports.changes = require("devtools/client/inspector/changes/reducers/changes");
 exports.events = require("devtools/client/inspector/events/reducers/events");
 exports.extensionsSidebar = require("devtools/client/inspector/extensions/reducers/sidebar");
 exports.flexboxes = require("devtools/client/inspector/flexbox/reducers/flexboxes");
 exports.fontOptions = require("devtools/client/inspector/fonts/reducers/font-options");
 exports.fonts = require("devtools/client/inspector/fonts/reducers/fonts");
 exports.grids = require("devtools/client/inspector/grids/reducers/grids");
--- a/devtools/client/shared/components/splitter/SplitBox.js
+++ b/devtools/client/shared/components/splitter/SplitBox.js
@@ -35,16 +35,18 @@ class SplitBox extends Component {
       // True if the right/bottom panel should be controlled.
       endPanelControl: PropTypes.bool,
       // Size of the splitter handle bar.
       splitterSize: PropTypes.string,
       // True if the splitter bar is vertical (default is vertical).
       vert: PropTypes.bool,
       // Style object.
       style: PropTypes.object,
+      // Call when controlled panel was resized.
+      onControlledPanelResized: PropTypes.func,
     };
   }
 
   static get defaultProps() {
     return {
       splitterSize: 5,
       vert: true,
       endPanelControl: false
@@ -85,16 +87,23 @@ class SplitBox extends Component {
       nextProps.startPanel != this.props.startPanel ||
       nextProps.endPanel != this.props.endPanel ||
       nextProps.endPanelControl != this.props.endPanelControl ||
       nextProps.minSize != this.props.minSize ||
       nextProps.maxSize != this.props.maxSize ||
       nextProps.splitterSize != this.props.splitterSize;
   }
 
+  componentDidUpdate(prevProps, prevState) {
+    if (this.props.onControlledPanelResized && (prevState.width !== this.state.width ||
+                                                prevState.height !== this.state.height)) {
+      this.props.onControlledPanelResized(this.state.width, this.state.height);
+    }
+  }
+
   // Dragging Events
 
   /**
    * Set 'resizing' cursor on entire document during splitter dragging.
    * This avoids cursor-flickering that happens when the mouse leaves
    * the splitter bar area (happens frequently).
    */
   onStartMove() {