Bug 1456828 - Part 2: Apply ProgressInspectionPanel to animations. r?gl draft
authorDaisuke Akatsuka <dakatsuka@mozilla.com>
Fri, 15 Jun 2018 13:38:27 +0900
changeset 807726 010a1ab9f0aa771dd49ab8fdc0e376b852a4d4d4
parent 807725 e3fe54fdda7b6799bae5ec9af314cc031d137f5d
child 807727 1acac721ab1ad45c2641c488470f75dd019e807c
push id113187
push userbmo:dakatsuka@mozilla.com
push dateFri, 15 Jun 2018 15:16:41 +0000
reviewersgl
bugs1456828
milestone62.0a1
Bug 1456828 - Part 2: Apply ProgressInspectionPanel to animations. r?gl MozReview-Commit-ID: 7It2CibH3oa
devtools/client/inspector/animation/components/AnimationListContainer.js
devtools/client/inspector/animation/components/AnimationListHeader.js
devtools/client/inspector/animation/components/AnimationTimelineTickItem.js
devtools/client/inspector/animation/components/AnimationTimelineTickList.js
devtools/client/inspector/animation/components/moz.build
devtools/client/inspector/animation/test/browser_animation_animation-timeline-tick.js
devtools/client/themes/animation.css
--- a/devtools/client/inspector/animation/components/AnimationListContainer.js
+++ b/devtools/client/inspector/animation/components/AnimationListContainer.js
@@ -1,21 +1,30 @@
 /* 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, PureComponent } =
   require("devtools/client/shared/vendor/react");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-const dom = require("devtools/client/shared/vendor/react-dom-factories");
+const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 
 const AnimationList = createFactory(require("./AnimationList"));
-const AnimationListHeader = createFactory(require("./AnimationListHeader"));
+const CurrentTimeScrubberController =
+  createFactory(require("./CurrentTimeScrubberController"));
+const ProgressInspectionPanel = createFactory(require("./ProgressInspectionPanel"));
+
+const { findOptimalTimeInterval } = require("../utils/utils");
+
+// The minimum spacing between 2 time graduation headers in the timeline (px).
+const TIME_GRADUATION_MIN_SPACING = 40;
 
 class AnimationListContainer extends PureComponent {
   static get propTypes() {
     return {
       addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
       animations: PropTypes.arrayOf(PropTypes.object).isRequired,
       emitEventForTest: PropTypes.func.isRequired,
       getAnimatedPropertyMap: PropTypes.func.isRequired,
@@ -27,16 +36,54 @@ class AnimationListContainer extends Pur
       setAnimationsCurrentTime: PropTypes.func.isRequired,
       setHighlightedNode: PropTypes.func.isRequired,
       setSelectedNode: PropTypes.func.isRequired,
       simulateAnimation: PropTypes.func.isRequired,
       timeScale: PropTypes.object.isRequired,
     };
   }
 
+  constructor(props) {
+    super(props);
+
+    this.state = {
+      // tick labels and lines on the progress inspection panel
+      ticks: [],
+    };
+  }
+
+  componentDidMount() {
+    this.updateState(this.props);
+  }
+
+  componentWillReceiveProps(nextProps) {
+    this.updateState(nextProps);
+  }
+
+  updateState(props) {
+    const { timeScale } = props;
+    const tickLinesEl = ReactDOM.findDOMNode(this).querySelector(".tick-lines");
+    const width = tickLinesEl.offsetWidth;
+    const animationDuration = timeScale.getDuration();
+    const minTimeInterval = TIME_GRADUATION_MIN_SPACING * animationDuration / width;
+    const intervalLength = findOptimalTimeInterval(minTimeInterval);
+    const intervalWidth = intervalLength * width / animationDuration;
+    const tickCount = width / intervalWidth;
+
+    const ticks = [];
+
+    for (let i = 0; i <= tickCount; i++) {
+      const position = i * intervalWidth * 100 / width;
+      const label = timeScale.formatTime(timeScale.distanceToRelativeTime(position));
+      ticks.push({ position, label });
+    }
+
+    this.setState({ ticks });
+  }
+
   render() {
     const {
       addAnimationsCurrentTimeListener,
       animations,
       emitEventForTest,
       getAnimatedPropertyMap,
       getNodeFromActor,
       onHideBoxModelHighlighter,
@@ -44,41 +91,53 @@ class AnimationListContainer extends Pur
       removeAnimationsCurrentTimeListener,
       selectAnimation,
       setAnimationsCurrentTime,
       setHighlightedNode,
       setSelectedNode,
       simulateAnimation,
       timeScale,
     } = this.props;
+    const { ticks } = this.state;
 
     return dom.div(
       {
         className: "animation-list-container"
       },
-      AnimationListHeader(
-        {
-          addAnimationsCurrentTimeListener,
-          removeAnimationsCurrentTimeListener,
-          setAnimationsCurrentTime,
-          timeScale,
-        }
-      ),
-      AnimationList(
+      ProgressInspectionPanel(
         {
-          animations,
-          emitEventForTest,
-          getAnimatedPropertyMap,
-          getNodeFromActor,
-          onHideBoxModelHighlighter,
-          onShowBoxModelHighlighterForNode,
-          selectAnimation,
-          setHighlightedNode,
-          setSelectedNode,
-          simulateAnimation,
-          timeScale,
+          indicator: CurrentTimeScrubberController(
+            {
+              addAnimationsCurrentTimeListener,
+              removeAnimationsCurrentTimeListener,
+              setAnimationsCurrentTime,
+              timeScale,
+            }
+          ),
+          list: AnimationList(
+            {
+              animations,
+              emitEventForTest,
+              getAnimatedPropertyMap,
+              getNodeFromActor,
+              onHideBoxModelHighlighter,
+              onShowBoxModelHighlighterForNode,
+              selectAnimation,
+              setHighlightedNode,
+              setSelectedNode,
+              simulateAnimation,
+              timeScale,
+            }
+          ),
+          ticks,
         }
       )
     );
   }
 }
 
-module.exports = AnimationListContainer;
+const mapStateToProps = state => {
+  return {
+    sidebarWidth: state.animations.sidebarSize ? state.animations.sidebarSize.width : 0
+  };
+};
+
+module.exports = connect(mapStateToProps)(AnimationListContainer);
deleted file mode 100644
--- a/devtools/client/inspector/animation/components/AnimationListHeader.js
+++ /dev/null
@@ -1,60 +0,0 @@
-/* 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, PureComponent } =
-  require("devtools/client/shared/vendor/react");
-const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-const dom = require("devtools/client/shared/vendor/react-dom-factories");
-
-const AnimationTimelineTickList = createFactory(require("./AnimationTimelineTickList"));
-const CurrentTimeScrubberController =
-  createFactory(require("./CurrentTimeScrubberController"));
-
-class AnimationListHeader extends PureComponent {
-  static get propTypes() {
-    return {
-      addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
-      removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
-      setAnimationsCurrentTime: PropTypes.func.isRequired,
-      timeScale: PropTypes.object.isRequired,
-    };
-  }
-
-  render() {
-    const {
-      addAnimationsCurrentTimeListener,
-      removeAnimationsCurrentTimeListener,
-      setAnimationsCurrentTime,
-      timeScale,
-    } = this.props;
-
-    return dom.div(
-      {
-        className: "animation-list-header"
-      },
-      dom.div(
-        {
-          className: "devtools-toolbar"
-        }
-      ),
-      AnimationTimelineTickList(
-        {
-          timeScale
-        }
-      ),
-      CurrentTimeScrubberController(
-        {
-          addAnimationsCurrentTimeListener,
-          removeAnimationsCurrentTimeListener,
-          setAnimationsCurrentTime,
-          timeScale,
-        }
-      )
-    );
-  }
-}
-
-module.exports = AnimationListHeader;
deleted file mode 100644
--- a/devtools/client/inspector/animation/components/AnimationTimelineTickItem.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/* 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 { PureComponent } = require("devtools/client/shared/vendor/react");
-const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-const dom = require("devtools/client/shared/vendor/react-dom-factories");
-
-class AnimationTimeTickItem extends PureComponent {
-  static get propTypes() {
-    return {
-      position: PropTypes.number.isRequired,
-      timeTickLabel: PropTypes.string.isRequired,
-    };
-  }
-
-  render() {
-    const { position, timeTickLabel } = this.props;
-
-    return dom.div(
-      {
-        className: "animation-timeline-tick-item",
-        style: { left: `${ position }%` }
-      },
-      timeTickLabel
-    );
-  }
-}
-
-module.exports = AnimationTimeTickItem;
deleted file mode 100644
--- a/devtools/client/inspector/animation/components/AnimationTimelineTickList.js
+++ /dev/null
@@ -1,101 +0,0 @@
-/* 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 { Component, createFactory } = require("devtools/client/shared/vendor/react");
-const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-const dom = require("devtools/client/shared/vendor/react-dom-factories");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
-const ReactDOM = require("devtools/client/shared/vendor/react-dom");
-
-const AnimationTimelineTickItem = createFactory(require("./AnimationTimelineTickItem"));
-
-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 Component {
-  static get propTypes() {
-    return {
-      sidebarWidth: PropTypes.number.isRequired,
-      timeScale: PropTypes.object.isRequired,
-    };
-  }
-
-  constructor(props) {
-    super(props);
-
-    this.state = {
-      tickList: [],
-    };
-  }
-
-  componentDidMount() {
-    this.updateTickList(this.props);
-  }
-
-  componentWillReceiveProps(nextProps) {
-    this.updateTickList(nextProps);
-  }
-
-  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.timeTickLabel !== nextTickItem.timeTickLabel) {
-        return true;
-      }
-    }
-
-    return false;
-  }
-
-  updateTickList(props) {
-    const { timeScale } = props;
-    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);
-    const intervalWidth = intervalLength * width / animationDuration;
-    const tickCount = width / intervalWidth;
-    const intervalPositionPercentage = 100 * intervalWidth / width;
-
-    const tickList = [];
-    for (let i = 0; i <= tickCount; i++) {
-      const position = i * intervalPositionPercentage;
-      const timeTickLabel =
-        timeScale.formatTime(timeScale.distanceToRelativeTime(position));
-      tickList.push({ position, timeTickLabel });
-    }
-
-    this.setState({ tickList });
-  }
-
-  render() {
-    const { tickList } = this.state;
-
-    return dom.div(
-      {
-        className: "animation-timeline-tick-list"
-      },
-      tickList.map(tickItem => AnimationTimelineTickItem(tickItem))
-    );
-  }
-}
-
-const mapStateToProps = state => {
-  return {
-    sidebarWidth: state.animations.sidebarSize ? state.animations.sidebarSize.width : 0
-  };
-};
-
-module.exports = connect(mapStateToProps)(AnimationTimelineTickList);
--- a/devtools/client/inspector/animation/components/moz.build
+++ b/devtools/client/inspector/animation/components/moz.build
@@ -13,20 +13,17 @@ DevToolsModules(
     'AnimatedPropertyListContainer.js',
     'AnimatedPropertyListHeader.js',
     'AnimatedPropertyName.js',
     'AnimationDetailContainer.js',
     'AnimationDetailHeader.js',
     'AnimationItem.js',
     'AnimationList.js',
     'AnimationListContainer.js',
-    'AnimationListHeader.js',
     'AnimationTarget.js',
-    'AnimationTimelineTickItem.js',
-    'AnimationTimelineTickList.js',
     'AnimationToolbar.js',
     'App.js',
     'CurrentTimeLabel.js',
     'CurrentTimeScrubber.js',
     'CurrentTimeScrubberController.js',
     'KeyframesProgressBar.js',
     'KeyframesProgressTickItem.js',
     'KeyframesProgressTickList.js',
--- a/devtools/client/inspector/animation/test/browser_animation_animation-timeline-tick.js
+++ b/devtools/client/inspector/animation/test/browser_animation_animation-timeline-tick.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test for following timeline tick items.
 // * animation list header elements existence
-// * timeline tick item elements existence
-// * count and label of timeline tick elements changing by the sidebar width
+// * tick labels elements existence
+// * count and text of tick label elements changing by the sidebar width
 
 const TimeScale = require("devtools/client/inspector/animation/utils/timescale");
 const { findOptimalTimeInterval } =
   require("devtools/client/inspector/animation/utils/utils");
 
 // Should be kept in sync with TIME_GRADUATION_MIN_SPACING in
 // AnimationTimeTickList component.
 const TIME_GRADUATION_MIN_SPACING = 40;
@@ -24,48 +24,44 @@ add_task(async function() {
   const timeScale = new TimeScale(animationInspector.state.animations);
 
   info("Checking animation list header element existence");
   const listContainerEl = panel.querySelector(".animation-list-container");
   const listHeaderEl = listContainerEl.querySelector(".devtools-toolbar");
   ok(listHeaderEl, "The header element should be in animation list container element");
 
   info("Checking time tick item elements existence");
-  assertTimelineTickItems(timeScale, listContainerEl);
-  const timelineTickItemLength =
-    listContainerEl.querySelectorAll(".animation-timeline-tick-item").length;
+  assertTickLabels(timeScale, listContainerEl);
+  const timelineTickItemLength = listContainerEl.querySelectorAll(".tick-label").length;
 
   info("Checking timeline tick item elements after enlarge sidebar width");
   await setSidebarWidth("100%", inspector);
-  assertTimelineTickItems(timeScale, listContainerEl);
-  ok(timelineTickItemLength <
-     listContainerEl.querySelectorAll(".animation-timeline-tick-item").length,
+  assertTickLabels(timeScale, listContainerEl);
+  ok(timelineTickItemLength < listContainerEl.querySelectorAll(".tick-label").length,
      "The timeline tick item elements should increase");
 });
 
 /**
- * Assert timeline tick item's position and label.
+ * Assert tick label's position and label.
  *
  * @param {TimeScale} - timeScale
  * @param {Element} - listContainerEl
  */
-function assertTimelineTickItems(timeScale, listContainerEl) {
-  const timelineTickListEl =
-    listContainerEl.querySelector(".animation-timeline-tick-list");
+function assertTickLabels(timeScale, listContainerEl) {
+  const timelineTickListEl = listContainerEl.querySelector(".tick-labels");
   ok(timelineTickListEl,
     "The animation timeline tick list element should be in header");
 
   const width = timelineTickListEl.offsetWidth;
   const animationDuration = timeScale.getDuration();
   const minTimeInterval = TIME_GRADUATION_MIN_SPACING * animationDuration / width;
   const interval = findOptimalTimeInterval(minTimeInterval);
   const expectedTickItem = Math.ceil(animationDuration / interval);
 
-  const timelineTickItemEls =
-    timelineTickListEl.querySelectorAll(".animation-timeline-tick-item");
+  const timelineTickItemEls = timelineTickListEl.querySelectorAll(".tick-label");
   is(timelineTickItemEls.length, expectedTickItem,
     "The expected number of timeline ticks were found");
 
   info("Make sure graduations are evenly distributed and show the right times");
   for (const [index, tickEl] of timelineTickItemEls.entries()) {
     const left = parseFloat(tickEl.style.left);
     const expectedPos = index * interval * 100 / animationDuration;
     is(Math.round(left), Math.round(expectedPos),
--- a/devtools/client/themes/animation.css
+++ b/devtools/client/themes/animation.css
@@ -7,16 +7,17 @@
 :root {
   --animation-even-background-color: rgba(0, 0, 0, 0.05);
   --command-pick-image: url(chrome://devtools/skin/images/command-pick.svg);
   --devtools-toolbar-height: 24px;
   --fast-track-image: url("images/animation-fast-track.svg");
   --fill-color-cssanimation: var(--theme-contrast-background);
   --fill-color-csstransition: var(--theme-highlight-blue);
   --fill-color-scriptanimation: var(--theme-graphs-green);
+  --graph-height: 30px;
   --graph-right-offset: 10px;
   --keyframe-marker-shadow-color: #c4c4c4;
   --pause-image: url(chrome://devtools/skin/images/pause.svg);
   --progress-bar-color: #909090;
   --resume-image: url(chrome://devtools/skin/images/play.svg);
   --rewind-image: url(chrome://devtools/skin/images/rewind.svg);
   --scrubber-color: #dd00a9;
   --sidebar-width: 200px;
@@ -83,72 +84,29 @@ select.playback-rate-selector.devtools-b
 }
 
 .rewind-button::before {
   background-image: var(--rewind-image);
 }
 
 /* Animation List Container */
 .animation-list-container {
-  height: 100%;
-  overflow-y: auto;
-  overflow-x: hidden;
-  position: relative;
+  overflow: hidden;
   width: 100%;
   -moz-user-select: none;
 }
 
 .animation-list-container.active-scrubber {
   cursor: col-resize;
 }
 
-/* Animation List Header */
-.animation-list-header {
-  display: grid;
-  grid-template-columns: var(--sidebar-width) calc(100% - var(--sidebar-width) - var(--graph-right-offset)) var(--graph-right-offset);
-  line-height: var(--devtools-toolbar-height);
-  min-height: 100%;
-  padding: 0;
-  pointer-events: none;
-  position: sticky;
-  top: 0;
-  z-index: 2;
-}
-
-.animation-list-header .devtools-toolbar {
-  position: absolute;
-  width: 100%;
-}
-
-/* Animation Timeline Tick List */
-.animation-timeline-tick-list {
-  grid-column: 2/3;
-  height: 100%;
-  position: relative;
-}
-
-.animation-timeline-tick-item {
-  height: 100%;
-  position: absolute;
-}
-
-.animation-timeline-tick-item::before {
-  border-left: var(--tick-line-style);
-  content: "";
-  height: 100%;
-  position: absolute;
-}
-
 /* Current Time Scrubber */
 .current-time-scrubber-controller {
   grid-column: 2 / 3;
-  height: 100%;
-  padding: 0;
-  position: absolute;
-  width: 100%;
+  z-index: 2;
 }
 
 .current-time-scrubber-controller::before {
   content: "";
   cursor: col-resize;
   height: var(--devtools-toolbar-height);
   pointer-events: auto;
   position: absolute;
@@ -158,17 +116,16 @@ select.playback-rate-selector.devtools-b
 
 .current-time-scrubber {
   cursor: col-resize;
   height: 100%;
   margin-left: -6px;
   pointer-events: auto;
   position: absolute;
   width: 12px;
-  z-index: 1;
 }
 
 .current-time-scrubber::before {
   border-left: 5px solid transparent;
   border-right: 5px solid transparent;
   border-top: 5px solid var(--scrubber-color);
   content: "";
   position: absolute;
@@ -181,32 +138,17 @@ select.playback-rate-selector.devtools-b
   content: "";
   height: 100%;
   left: 5px;
   position: absolute;
   top: 0;
   width: 0;
 }
 
-/* Animation List */
-.animation-list {
-  list-style-type: none;
-  margin: 0;
-  padding: 0;
-  position: absolute;
-  top: var(--devtools-toolbar-height);
-  width: 100%;
-}
-
 /* Animation Item */
-.animation-item {
-  display: flex;
-  height: 30px;
-}
-
 .animation-item:nth-child(2n+1) {
   background-color: var(--animation-even-background-color);
 }
 
 .animation-item.cssanimation {
   --computed-timing-graph-color: var(--fill-color-cssanimation);
   --effect-timing-graph-color: var(--stroke-color-cssanimation);
 }
@@ -224,19 +166,19 @@ select.playback-rate-selector.devtools-b
 .animation-item.selected {
   background-color: var(--theme-selection-background-hover);
 }
 
 /* Animation Target */
 .animation-target {
   align-items: center;
   display: flex;
-  height: 100%;
+  grid-column: 1 / 2;
+  height: var(--graph-height);
   padding-left: 4px;
-  width: var(--sidebar-width);
 }
 
 /* Reps component */
 .animation-target .objectBox {
   display: flex;
   max-width: 100%;
 }
 
@@ -254,35 +196,34 @@ select.playback-rate-selector.devtools-b
 .animation-target .objectBox .open-inspector:hover,
 .animation-target.highlighting .objectBox .open-inspector {
   background-color: var(--theme-highlight-blue);
 }
 
 /* Summary Graph */
 .animation-summary-graph {
   cursor: pointer;
-  height: 100%;
+  grid-column: 2 / 3;
+  height: var(--graph-height);
   padding-top: 5px;
   position: relative;
-  width: calc(100% - var(--sidebar-width) - var(--graph-right-offset));
 }
 
 .animation-summary-graph.compositor::after {
   background-image: var(--fast-track-image);
   background-repeat: no-repeat;
   content: "";
   display: block;
   fill: var(--theme-content-color3);
   height: 100%;
   position: absolute;
   right: 0;
   top: 5px;
   width: 15px;
   -moz-context-properties: fill;
-  z-index: 1;
 }
 
 .animation-summary-graph-path {
   height: 100%;
   width: 100%;
 }
 
 .animation-computed-timing-path path {
@@ -377,17 +318,17 @@ select.playback-rate-selector.devtools-b
 /* Animation Detail */
 .animation-detail-container {
   background-color: var(--theme-body-background);
   display: flex;
   flex-direction: column;
   height: 100%;
   overflow: hidden;
   width: 100%;
-  z-index: 1;
+  z-index: 2;
 }
 
 .animation-detail-header {
   display: flex;
   padding-inline-end: 0;
 }
 
 /* On OSX the cursor turns into a window-resizing cursor at the edges of the
@@ -522,17 +463,17 @@ select.playback-rate-selector.devtools-b
   position: absolute;
   top: var(--devtools-toolbar-height);
   width: 100%;
 }
 
 /* Animated Property Item */
 .animated-property-item {
   display: flex;
-  height: 30px;
+  height: var(--graph-height);
 }
 
 .animated-property-item:nth-child(2n+1) {
   background-color: var(--animation-even-background-color);
 }
 
 .animated-property-item.unchanged {
   opacity: 0.6;