Bug 1406285 - Part 3: Implement summary graph base. r?gl draft
authorDaisuke Akatsuka <dakatsuka@mozilla.com>
Thu, 18 Jan 2018 10:40:51 +0900
changeset 721932 61f56507a676eb5543c3a87172cc1f0378c3dc65
parent 721931 7b9453a9313039320e8985a6dda2f5b9c244ef13
child 721933 82078177a1cb16d6cf0609fbe68b59067fdc675d
push id96003
push userbmo:dakatsuka@mozilla.com
push dateThu, 18 Jan 2018 05:23:36 +0000
reviewersgl
bugs1406285
milestone59.0a1
Bug 1406285 - Part 3: Implement summary graph base. r?gl MozReview-Commit-ID: KMbUvJPDuNM
devtools/client/inspector/animation/components/AnimationItem.js
devtools/client/inspector/animation/components/AnimationList.js
devtools/client/inspector/animation/components/AnimationListContainer.js
devtools/client/inspector/animation/components/AnimationListHeader.js
devtools/client/inspector/animation/components/AnimationTimelineTickList.js
devtools/client/inspector/animation/components/graph/SummaryGraph.js
devtools/client/inspector/animation/components/graph/SummaryGraphPath.js
devtools/client/inspector/animation/components/graph/moz.build
devtools/client/inspector/animation/components/moz.build
devtools/client/themes/animation.css
--- a/devtools/client/inspector/animation/components/AnimationItem.js
+++ b/devtools/client/inspector/animation/components/AnimationItem.js
@@ -4,50 +4,59 @@
 
 "use strict";
 
 const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
 const AnimationTarget = createFactory(require("./AnimationTarget"));
+const SummaryGraph = createFactory(require("./graph/SummaryGraph"));
 
 class AnimationItem extends PureComponent {
   static get propTypes() {
     return {
       animation: PropTypes.object.isRequired,
       emitEventForTest: PropTypes.func.isRequired,
       getNodeFromActor: PropTypes.func.isRequired,
       onHideBoxModelHighlighter: PropTypes.func.isRequired,
       onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
       setSelectedNode: PropTypes.func.isRequired,
+      timeScale: PropTypes.object.isRequired,
     };
   }
 
   render() {
     const {
       animation,
       emitEventForTest,
       getNodeFromActor,
       onHideBoxModelHighlighter,
       onShowBoxModelHighlighterForNode,
       setSelectedNode,
+      timeScale,
     } = this.props;
 
     return dom.li(
       {
         className: "animation-item"
       },
       AnimationTarget(
         {
           animation,
           emitEventForTest,
           getNodeFromActor,
           onHideBoxModelHighlighter,
           onShowBoxModelHighlighterForNode,
           setSelectedNode,
         }
+      ),
+      SummaryGraph(
+        {
+          animation,
+          timeScale,
+        }
       )
     );
   }
 }
 
 module.exports = AnimationItem;
--- a/devtools/client/inspector/animation/components/AnimationList.js
+++ b/devtools/client/inspector/animation/components/AnimationList.js
@@ -14,42 +14,45 @@ class AnimationList extends PureComponen
   static get propTypes() {
     return {
       animations: PropTypes.arrayOf(PropTypes.object).isRequired,
       emitEventForTest: PropTypes.func.isRequired,
       getNodeFromActor: PropTypes.func.isRequired,
       onHideBoxModelHighlighter: PropTypes.func.isRequired,
       onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
       setSelectedNode: PropTypes.func.isRequired,
+      timeScale: PropTypes.object.isRequired,
     };
   }
 
   render() {
     const {
       animations,
       emitEventForTest,
       getNodeFromActor,
       onHideBoxModelHighlighter,
       onShowBoxModelHighlighterForNode,
       setSelectedNode,
+      timeScale,
     } = this.props;
 
     return dom.ul(
       {
         className: "animation-list"
       },
       animations.map(animation =>
         AnimationItem(
           {
             animation,
             emitEventForTest,
             getNodeFromActor,
             onHideBoxModelHighlighter,
             onShowBoxModelHighlighterForNode,
             setSelectedNode,
+            timeScale,
           }
         )
       )
     );
   }
 }
 
 module.exports = AnimationList;
--- a/devtools/client/inspector/animation/components/AnimationListContainer.js
+++ b/devtools/client/inspector/animation/components/AnimationListContainer.js
@@ -7,16 +7,18 @@
 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 AnimationList = createFactory(require("./AnimationList"));
 const AnimationListHeader = createFactory(require("./AnimationListHeader"));
 
+const TimeScale = require("../utils/timescale");
+
 class AnimationListContainer extends PureComponent {
   static get propTypes() {
     return {
       animations: PropTypes.arrayOf(PropTypes.object).isRequired,
       emitEventForTest: PropTypes.func.isRequired,
       getNodeFromActor: PropTypes.func.isRequired,
       onHideBoxModelHighlighter: PropTypes.func.isRequired,
       onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
@@ -28,33 +30,35 @@ class AnimationListContainer extends Pur
     const {
       animations,
       emitEventForTest,
       getNodeFromActor,
       onHideBoxModelHighlighter,
       onShowBoxModelHighlighterForNode,
       setSelectedNode,
     } = this.props;
+    const timeScale = new TimeScale(animations);
 
     return dom.div(
       {
         className: "animation-list-container"
       },
       AnimationListHeader(
         {
-          animations
+          timeScale,
         }
       ),
       AnimationList(
         {
           animations,
           emitEventForTest,
           getNodeFromActor,
           onHideBoxModelHighlighter,
           onShowBoxModelHighlighterForNode,
           setSelectedNode,
+          timeScale,
         }
       )
     );
   }
 }
 
 module.exports = AnimationListContainer;
--- a/devtools/client/inspector/animation/components/AnimationListHeader.js
+++ b/devtools/client/inspector/animation/components/AnimationListHeader.js
@@ -9,29 +9,29 @@ const { createFactory, PureComponent } =
 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"));
 
 class AnimationListHeader extends PureComponent {
   static get propTypes() {
     return {
-      animations: PropTypes.arrayOf(PropTypes.object).isRequired,
+      timeScale: PropTypes.object.isRequired,
     };
   }
 
   render() {
-    const { animations } = this.props;
+    const { timeScale } = this.props;
 
     return dom.div(
       {
         className: "animation-list-header devtools-toolbar"
       },
       AnimationTimelineTickList(
         {
-          animations
+          timeScale
         }
       )
     );
   }
 }
 
 module.exports = AnimationListHeader;
--- a/devtools/client/inspector/animation/components/AnimationTimelineTickList.js
+++ b/devtools/client/inspector/animation/components/AnimationTimelineTickList.js
@@ -8,27 +8,26 @@ 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 { 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,
+      timeScale: PropTypes.object.isRequired,
     };
   }
 
   constructor(props) {
     super(props);
 
     this.state = {
       tickList: [],
@@ -56,18 +55,17 @@ class AnimationTimelineTickList extends 
         return true;
       }
     }
 
     return false;
   }
 
   updateTickList() {
-    const { animations } = this.props;
-    const timeScale = new TimeScale(animations);
+    const { timeScale } = this.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;
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/components/graph/SummaryGraph.js
@@ -0,0 +1,41 @@
+/* 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 dom = require("devtools/client/shared/vendor/react-dom-factories");
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+
+const SummaryGraphPath = createFactory(require("./SummaryGraphPath"));
+
+class SummaryGraph extends PureComponent {
+  static get propTypes() {
+    return {
+      animation: PropTypes.object.isRequired,
+      timeScale: PropTypes.object.isRequired,
+    };
+  }
+
+  render() {
+    const {
+      animation,
+      timeScale,
+    } = this.props;
+
+    return dom.div(
+      {
+        className: "animation-summary-graph",
+      },
+      SummaryGraphPath(
+        {
+          animation,
+          timeScale,
+        }
+      )
+    );
+  }
+}
+
+module.exports = SummaryGraph;
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/components/graph/SummaryGraphPath.js
@@ -0,0 +1,38 @@
+/* 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 SummaryGraphPath extends PureComponent {
+  static get propTypes() {
+    return {
+      animation: PropTypes.object.isRequired,
+      timeScale: PropTypes.object.isRequired,
+    };
+  }
+
+  render() {
+    const {
+      animation,
+      timeScale,
+    } = this.props;
+
+    const totalDisplayedDuration = animation.state.playbackRate * timeScale.getDuration();
+    const startTime = timeScale.minStartTime;
+
+    return dom.svg(
+      {
+        className: "animation-summary-graph-path",
+        preserveAspectRatio: "none",
+        viewBox: `${ startTime } -1 ${ totalDisplayedDuration } 1`
+      }
+    );
+  }
+}
+
+module.exports = SummaryGraphPath;
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/components/graph/moz.build
@@ -0,0 +1,8 @@
+# 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(
+    'SummaryGraph.js',
+    'SummaryGraphPath.js'
+)
--- a/devtools/client/inspector/animation/components/moz.build
+++ b/devtools/client/inspector/animation/components/moz.build
@@ -1,12 +1,16 @@
 # 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/.
 
+DIRS += [
+    'graph'
+]
+
 DevToolsModules(
     'AnimationItem.js',
     'AnimationList.js',
     'AnimationListContainer.js',
     'AnimationListHeader.js',
     'AnimationTarget.js',
     'AnimationTimelineTickItem.js',
     'AnimationTimelineTickList.js',
--- a/devtools/client/themes/animation.css
+++ b/devtools/client/themes/animation.css
@@ -2,16 +2,17 @@
  * 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/. */
 
 /* Animation-inspector specific theme variables */
 
 :root {
   --animation-even-background-color: rgba(0, 0, 0, 0.05);
   --command-pick-image: url(chrome://devtools/skin/images/command-pick.svg);
+  --graph-right-offset: 10px;
   --sidebar-width: 200px;
 }
 
 :root.theme-dark {
   --animation-even-background-color: rgba(255, 255, 255, 0.05);
 }
 
 :root.theme-firebug {
@@ -22,19 +23,19 @@
 .animation-list-header {
   display: flex;
   justify-content: flex-end;
   padding: 0;
 }
 
 /* Animation Timeline Tick List */
 .animation-timeline-tick-list {
-  margin-right: 10px;
+  margin-right: var(--graph-right-offset);
   position: relative;
-  width: calc(100% - var(--sidebar-width) - 10px);
+  width: calc(100% - var(--sidebar-width) - var(--graph-right-offset));
 }
 
 .animation-timeline-tick-item {
   border-left: 0.5px solid rgba(128, 136, 144, .5);
   height: 100vh;
   position: absolute;
 }
 
@@ -42,16 +43,17 @@
 .animation-list {
   list-style-type: none;
   margin-top: 0;
   padding: 0;
 }
 
 /* Animation Item */
 .animation-item {
+  display: flex;
   height: 30px;
 }
 
 .animation-item:nth-child(2n+1) {
   background-color: var(--animation-even-background-color);
 }
 
 /* Animation Target */
@@ -62,16 +64,27 @@
   padding-left: 4px;
   width: var(--sidebar-width);
 }
 
 .animation-target .tag-name {
   cursor: default;
 }
 
+/* Summary Graph */
+.animation-summary-graph {
+  height: 100%;
+  width: calc(100% - var(--sidebar-width) - var(--graph-right-offset));
+}
+
+.animation-summary-graph-path {
+  height: 100%;
+  width: 100%;
+}
+
 /* No Animation Panel */
 .animation-error-message {
   overflow: auto;
 }
 
 .animation-error-message > p {
   white-space: pre;
 }