--- a/devtools/client/inspector/animation/animation.js
+++ b/devtools/client/inspector/animation/animation.js
@@ -577,16 +577,21 @@ class AnimationInspector {
* https://drafts.csswg.org/web-animations/#the-animation-interface
*
* @param {Object} effectTiming
* e.g. { duration: 1000, fill: "both" }
* @return {Animation}
* https://drafts.csswg.org/web-animations/#the-animation-interface
*/
simulateAnimationForKeyframesProgressBar(effectTiming) {
+ // Don't simulate animation if the animation inspector is already destroyed.
+ if (!this.win) {
+ return null;
+ }
+
if (!this.simulatedAnimationForKeyframesProgressBar) {
this.simulatedAnimationForKeyframesProgressBar = new this.win.Animation();
}
this.simulatedAnimationForKeyframesProgressBar.effect =
new this.win.KeyframeEffect(null, null, effectTiming);
return this.simulatedAnimationForKeyframesProgressBar;
--- a/devtools/client/inspector/animation/components/AnimatedPropertyList.js
+++ b/devtools/client/inspector/animation/components/AnimatedPropertyList.js
@@ -1,28 +1,39 @@
/* 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 { 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 AnimatedPropertyItem = createFactory(require("./AnimatedPropertyItem"));
+const InspectListBox = createFactory(require("./InspectListBox"));
+const KeyframesProgressBar = createFactory(require("./KeyframesProgressBar"));
+const TickLabels = createFactory(require("./TickLabels"));
+const TickLines = createFactory(require("./TickLines"));
-class AnimatedPropertyList extends Component {
+const { getFormatStr } = require("../utils/l10n");
+
+class AnimatedPropertyList extends PureComponent {
static get propTypes() {
return {
+ addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
animation: PropTypes.object.isRequired,
emitEventForTest: PropTypes.func.isRequired,
getAnimatedPropertyMap: PropTypes.func.isRequired,
+ getAnimationsCurrentTime: PropTypes.func.isRequired,
getComputedStyle: PropTypes.func.isRequired,
+ removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
simulateAnimation: PropTypes.func.isRequired,
+ simulateAnimationForKeyframesProgressBar: PropTypes.func.isRequired,
+ timeScale: PropTypes.object.isRequired,
};
}
constructor(props) {
super(props);
this.state = {
// Array of object which has the property name, the keyframes, its aniamtion type
@@ -104,42 +115,69 @@ class AnimatedPropertyList extends Compo
}
);
emitEventForTest("animation-keyframes-rendered");
}
render() {
const {
+ addAnimationsCurrentTimeListener,
+ animation,
+ getAnimationsCurrentTime,
getComputedStyle,
+ removeAnimationsCurrentTimeListener,
simulateAnimation,
+ simulateAnimationForKeyframesProgressBar,
+ timeScale,
} = this.props;
- const {
- animatedProperties,
- } = this.state;
+ const { animatedProperties } = this.state;
if (!animatedProperties) {
return null;
}
- return dom.ul(
+ const ticks = [0, 50, 100].map(position => {
+ return {
+ position,
+ label: getFormatStr("detail.propertiesHeader.percentage", position),
+ };
+ });
+
+ return dom.div(
{
- className: "animated-property-list"
+ className: `animated-property-list ${ animation.state.type }`
},
- animatedProperties.map(({ isUnchanged, keyframes, name, type }) => {
- const state = this.getPropertyState(name);
- return AnimatedPropertyItem(
- {
- getComputedStyle,
- isUnchanged,
- keyframes,
- name,
- simulateAnimation,
- state,
- type,
- }
- );
- })
+ InspectListBox(
+ {
+ background: TickLines({ ticks }),
+ header: TickLabels({ ticks }),
+ items: animatedProperties.map(({ isUnchanged, keyframes, name, type }) => {
+ const state = this.getPropertyState(name);
+ return AnimatedPropertyItem(
+ {
+ getComputedStyle,
+ isUnchanged,
+ keyframes,
+ name,
+ simulateAnimation,
+ state,
+ type,
+ }
+ );
+ }),
+ indicator: KeyframesProgressBar(
+ {
+ addAnimationsCurrentTimeListener,
+ animation,
+ getAnimationsCurrentTime,
+ removeAnimationsCurrentTimeListener,
+ simulateAnimationForKeyframesProgressBar,
+ timeScale,
+ }
+ )
+ }
+ )
);
}
}
module.exports = AnimatedPropertyList;
deleted file mode 100644
--- a/devtools/client/inspector/animation/components/AnimatedPropertyListContainer.js
+++ /dev/null
@@ -1,77 +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 dom = require("devtools/client/shared/vendor/react-dom-factories");
-const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-
-const AnimatedPropertyList = createFactory(require("./AnimatedPropertyList"));
-const AnimatedPropertyListHeader = createFactory(require("./AnimatedPropertyListHeader"));
-
-class AnimatedPropertyListContainer extends PureComponent {
- static get propTypes() {
- return {
- addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
- animation: PropTypes.object.isRequired,
- emitEventForTest: PropTypes.func.isRequired,
- getAnimatedPropertyMap: PropTypes.func.isRequired,
- getAnimationsCurrentTime: PropTypes.func.isRequired,
- getComputedStyle: PropTypes.func.isRequired,
- removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
- simulateAnimation: PropTypes.func.isRequired,
- simulateAnimationForKeyframesProgressBar: PropTypes.func.isRequired,
- timeScale: PropTypes.object.isRequired,
- };
- }
-
- render() {
- const {
- addAnimationsCurrentTimeListener,
- animation,
- emitEventForTest,
- getAnimatedPropertyMap,
- getAnimationsCurrentTime,
- getComputedStyle,
- removeAnimationsCurrentTimeListener,
- simulateAnimation,
- simulateAnimationForKeyframesProgressBar,
- timeScale,
- } = this.props;
-
- return dom.div(
- {
- className: `animated-property-list-container ${ animation.state.type }`
- },
- AnimatedPropertyListHeader(
- {
- addAnimationsCurrentTimeListener,
- animation,
- getAnimationsCurrentTime,
- removeAnimationsCurrentTimeListener,
- simulateAnimationForKeyframesProgressBar,
- timeScale,
- }
- ),
- dom.div(
- {
- className: "animated-property-list-background",
- },
- dom.span()
- ),
- AnimatedPropertyList(
- {
- animation,
- emitEventForTest,
- getAnimatedPropertyMap,
- getComputedStyle,
- simulateAnimation,
- }
- )
- );
- }
-}
-
-module.exports = AnimatedPropertyListContainer;
deleted file mode 100644
--- a/devtools/client/inspector/animation/components/AnimatedPropertyListHeader.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 dom = require("devtools/client/shared/vendor/react-dom-factories");
-const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-
-const KeyframesProgressBar = createFactory(require("./KeyframesProgressBar"));
-const KeyframesProgressTickList = createFactory(require("./KeyframesProgressTickList"));
-
-class AnimatedPropertyListHeader extends PureComponent {
- static get propTypes() {
- return {
- addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
- animation: PropTypes.object.isRequired,
- getAnimationsCurrentTime: PropTypes.func.isRequired,
- removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
- simulateAnimationForKeyframesProgressBar: PropTypes.func.isRequired,
- timeScale: PropTypes.object.isRequired,
- };
- }
-
- render() {
- const {
- addAnimationsCurrentTimeListener,
- animation,
- getAnimationsCurrentTime,
- removeAnimationsCurrentTimeListener,
- simulateAnimationForKeyframesProgressBar,
- timeScale,
- } = this.props;
-
- return dom.div(
- {
- className: "animated-property-list-header"
- },
- dom.div(
- {
- className: "devtools-toolbar"
- }
- ),
- KeyframesProgressTickList(),
- KeyframesProgressBar(
- {
- addAnimationsCurrentTimeListener,
- animation,
- getAnimationsCurrentTime,
- removeAnimationsCurrentTimeListener,
- simulateAnimationForKeyframesProgressBar,
- timeScale,
- }
- )
- );
- }
-}
-
-module.exports = AnimatedPropertyListHeader;
--- a/devtools/client/inspector/animation/components/AnimationDetailContainer.js
+++ b/devtools/client/inspector/animation/components/AnimationDetailContainer.js
@@ -5,18 +5,17 @@
"use strict";
const { connect } = require("devtools/client/shared/vendor/react-redux");
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 AnimationDetailHeader = createFactory(require("./AnimationDetailHeader"));
-const AnimatedPropertyListContainer =
- createFactory(require("./AnimatedPropertyListContainer"));
+const AnimatedPropertyList = createFactory(require("./AnimatedPropertyList"));
class AnimationDetailContainer extends PureComponent {
static get propTypes() {
return {
addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
animation: PropTypes.object.isRequired,
emitEventForTest: PropTypes.func.isRequired,
getAnimatedPropertyMap: PropTypes.func.isRequired,
@@ -54,17 +53,17 @@ class AnimationDetailContainer extends P
{
animation,
setDetailVisibility,
}
)
:
null,
animation ?
- AnimatedPropertyListContainer(
+ AnimatedPropertyList(
{
addAnimationsCurrentTimeListener,
animation,
emitEventForTest,
getAnimatedPropertyMap,
getAnimationsCurrentTime,
getComputedStyle,
removeAnimationsCurrentTimeListener,
--- a/devtools/client/inspector/animation/components/AnimationList.js
+++ b/devtools/client/inspector/animation/components/AnimationList.js
@@ -1,70 +1,147 @@
/* 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 { 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 PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const AnimationItem = createFactory(require("./AnimationItem"));
+const CurrentTimeScrubber = createFactory(require("./CurrentTimeScrubber"));
+const InspectListBox = createFactory(require("./InspectListBox"));
+const TickLabels = createFactory(require("./TickLabels"));
+const TickLines = createFactory(require("./TickLines"));
+
+const { findOptimalTimeInterval } = require("../utils/utils");
+
+// The minimum spacing between 2 time graduation headers in the timeline (px).
+const TIME_GRADUATION_MIN_SPACING = 40;
class AnimationList extends PureComponent {
static get propTypes() {
return {
+ addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
animations: PropTypes.arrayOf(PropTypes.object).isRequired,
emitEventForTest: PropTypes.func.isRequired,
getAnimatedPropertyMap: PropTypes.func.isRequired,
getNodeFromActor: PropTypes.func.isRequired,
onHideBoxModelHighlighter: PropTypes.func.isRequired,
onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
+ removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
selectAnimation: PropTypes.func.isRequired,
+ setAnimationsCurrentTime: PropTypes.func.isRequired,
setHighlightedNode: PropTypes.func.isRequired,
setSelectedNode: PropTypes.func.isRequired,
+ sidebarWidth: PropTypes.number.isRequired,
simulateAnimation: PropTypes.func.isRequired,
timeScale: PropTypes.object.isRequired,
};
}
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ 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 = Math.ceil(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,
onShowBoxModelHighlighterForNode,
+ removeAnimationsCurrentTimeListener,
selectAnimation,
+ setAnimationsCurrentTime,
setHighlightedNode,
setSelectedNode,
simulateAnimation,
timeScale,
} = this.props;
+ const { ticks } = this.state;
- return dom.ul(
+ return dom.div(
{
- className: "animation-list"
+ className: "animation-list",
},
- animations.map(animation =>
- AnimationItem(
- {
- animation,
- emitEventForTest,
- getAnimatedPropertyMap,
- getNodeFromActor,
- onHideBoxModelHighlighter,
- onShowBoxModelHighlighterForNode,
- selectAnimation,
- setHighlightedNode,
- setSelectedNode,
- simulateAnimation,
- timeScale,
- }
- )
+ InspectListBox(
+ {
+ background: TickLines({ ticks }),
+ header: TickLabels({ ticks }),
+ items: animations.map(animation =>
+ AnimationItem(
+ {
+ animation,
+ emitEventForTest,
+ getAnimatedPropertyMap,
+ getNodeFromActor,
+ onHideBoxModelHighlighter,
+ onShowBoxModelHighlighterForNode,
+ selectAnimation,
+ setHighlightedNode,
+ setSelectedNode,
+ simulateAnimation,
+ timeScale,
+ }
+ )
+ ),
+ indicator: CurrentTimeScrubber(
+ {
+ addAnimationsCurrentTimeListener,
+ removeAnimationsCurrentTimeListener,
+ setAnimationsCurrentTime,
+ timeScale,
+ }
+ )
+ }
)
);
}
}
-module.exports = AnimationList;
+const mapStateToProps = state => {
+ return {
+ sidebarWidth: state.animations.sidebarSize ? state.animations.sidebarSize.width : 0
+ };
+};
+
+module.exports = connect(mapStateToProps)(AnimationList);
deleted file mode 100644
--- a/devtools/client/inspector/animation/components/AnimationListContainer.js
+++ /dev/null
@@ -1,84 +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 AnimationList = createFactory(require("./AnimationList"));
-const AnimationListHeader = createFactory(require("./AnimationListHeader"));
-
-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,
- getNodeFromActor: PropTypes.func.isRequired,
- onHideBoxModelHighlighter: PropTypes.func.isRequired,
- onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
- removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
- selectAnimation: PropTypes.func.isRequired,
- setAnimationsCurrentTime: PropTypes.func.isRequired,
- setHighlightedNode: PropTypes.func.isRequired,
- setSelectedNode: PropTypes.func.isRequired,
- simulateAnimation: PropTypes.func.isRequired,
- timeScale: PropTypes.object.isRequired,
- };
- }
-
- render() {
- const {
- addAnimationsCurrentTimeListener,
- animations,
- emitEventForTest,
- getAnimatedPropertyMap,
- getNodeFromActor,
- onHideBoxModelHighlighter,
- onShowBoxModelHighlighterForNode,
- removeAnimationsCurrentTimeListener,
- selectAnimation,
- setAnimationsCurrentTime,
- setHighlightedNode,
- setSelectedNode,
- simulateAnimation,
- timeScale,
- } = this.props;
-
- return dom.div(
- {
- className: "animation-list-container"
- },
- AnimationListHeader(
- {
- addAnimationsCurrentTimeListener,
- removeAnimationsCurrentTimeListener,
- setAnimationsCurrentTime,
- timeScale,
- }
- ),
- AnimationList(
- {
- animations,
- emitEventForTest,
- getAnimatedPropertyMap,
- getNodeFromActor,
- onHideBoxModelHighlighter,
- onShowBoxModelHighlighterForNode,
- selectAnimation,
- setHighlightedNode,
- setSelectedNode,
- simulateAnimation,
- timeScale,
- }
- )
- );
- }
-}
-
-module.exports = 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/App.js
+++ b/devtools/client/inspector/animation/components/App.js
@@ -5,17 +5,17 @@
"use strict";
const { Component, createFactory } = 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 { connect } = require("devtools/client/shared/vendor/react-redux");
const AnimationDetailContainer = createFactory(require("./AnimationDetailContainer"));
-const AnimationListContainer = createFactory(require("./AnimationListContainer"));
+const AnimationList = createFactory(require("./AnimationList"));
const AnimationToolbar = createFactory(require("./AnimationToolbar"));
const NoAnimationPanel = createFactory(require("./NoAnimationPanel"));
const SplitBox = createFactory(require("devtools/client/shared/components/splitter/SplitBox"));
class App extends Component {
static get propTypes() {
return {
addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
@@ -108,17 +108,17 @@ class App extends Component {
simulateAnimation,
simulateAnimationForKeyframesProgressBar,
timeScale,
}
),
endPanelControl: true,
initialHeight: "50%",
splitterSize: 1,
- startPanel: AnimationListContainer(
+ startPanel: AnimationList(
{
addAnimationsCurrentTimeListener,
animations,
emitEventForTest,
getAnimatedPropertyMap,
getNodeFromActor,
onHideBoxModelHighlighter,
onShowBoxModelHighlighterForNode,
--- a/devtools/client/inspector/animation/components/CurrentTimeScrubber.js
+++ b/devtools/client/inspector/animation/components/CurrentTimeScrubber.js
@@ -1,32 +1,140 @@
/* 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 { 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 ReactDOM = require("devtools/client/shared/vendor/react-dom");
+
+const IndicationBar = createFactory(require("./IndicationBar"));
class CurrentTimeScrubber extends PureComponent {
static get propTypes() {
return {
- offset: PropTypes.number.isRequired,
+ addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
+ removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
+ setAnimationsCurrentTime: PropTypes.func.isRequired,
+ timeScale: PropTypes.object.isRequired,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ const { addAnimationsCurrentTimeListener } = props;
+ this.onCurrentTimeUpdated = this.onCurrentTimeUpdated.bind(this);
+ this.onMouseDown = this.onMouseDown.bind(this);
+ this.onMouseMove = this.onMouseMove.bind(this);
+ this.onMouseOut = this.onMouseOut.bind(this);
+ this.onMouseUp = this.onMouseUp.bind(this);
+
+ this.state = {
+ // position for the scrubber
+ position: 0,
};
+
+ addAnimationsCurrentTimeListener(this.onCurrentTimeUpdated);
+ }
+
+ componentDidMount() {
+ const el = ReactDOM.findDOMNode(this);
+ el.addEventListener("mousedown", this.onMouseDown);
+ }
+
+ componentWillUnmount() {
+ const { removeAnimationsCurrentTimeListener } = this.props;
+ removeAnimationsCurrentTimeListener(this.onCurrentTimeUpdated);
+ }
+
+ onCurrentTimeUpdated(currentTime) {
+ const { timeScale } = this.props;
+
+ const position = currentTime / timeScale.getDuration();
+ this.setState({ position });
+ }
+
+ onMouseDown(event) {
+ event.stopPropagation();
+ const thisEl = ReactDOM.findDOMNode(this);
+ this.controllerArea = thisEl.getBoundingClientRect();
+ this.listenerTarget = thisEl.closest(".animation-list");
+ this.listenerTarget.addEventListener("mousemove", this.onMouseMove);
+ this.listenerTarget.addEventListener("mouseout", this.onMouseOut);
+ this.listenerTarget.addEventListener("mouseup", this.onMouseUp);
+ this.listenerTarget.classList.add("active-scrubber");
+
+ this.updateAnimationsCurrentTime(event.pageX, true);
+ }
+
+ onMouseMove(event) {
+ event.stopPropagation();
+ this.isMouseMoved = true;
+ this.updateAnimationsCurrentTime(event.pageX);
+ }
+
+ onMouseOut(event) {
+ event.stopPropagation();
+
+ if (!this.listenerTarget.contains(event.relatedTarget)) {
+ const endX = this.controllerArea.x + this.controllerArea.width;
+ const pageX = endX < event.pageX ? endX : event.pageX;
+ this.updateAnimationsCurrentTime(pageX, true);
+ this.uninstallListeners();
+ }
+ }
+
+ onMouseUp(event) {
+ event.stopPropagation();
+
+ if (this.isMouseMoved) {
+ this.updateAnimationsCurrentTime(event.pageX, true);
+ this.isMouseMoved = null;
+ }
+
+ this.uninstallListeners();
+ }
+
+ uninstallListeners() {
+ this.listenerTarget.removeEventListener("mousemove", this.onMouseMove);
+ this.listenerTarget.removeEventListener("mouseout", this.onMouseOut);
+ this.listenerTarget.removeEventListener("mouseup", this.onMouseUp);
+ this.listenerTarget.classList.remove("active-scrubber");
+ this.listenerTarget = null;
+ this.controllerArea = null;
+ }
+
+ updateAnimationsCurrentTime(pageX, needRefresh) {
+ const {
+ setAnimationsCurrentTime,
+ timeScale,
+ } = this.props;
+
+ const time = pageX - this.controllerArea.x < 0 ?
+ 0 :
+ (pageX - this.controllerArea.x) /
+ this.controllerArea.width * timeScale.getDuration();
+
+ setAnimationsCurrentTime(time, needRefresh);
}
render() {
- const { offset } = this.props;
+ const { position } = this.state;
return dom.div(
{
- className: "current-time-scrubber",
- style: {
- transform: `translateX(${ offset }px)`,
- },
- }
+ className: "current-time-scrubber-area",
+ },
+ IndicationBar(
+ {
+ className: "current-time-scrubber",
+ position
+ }
+ )
);
}
}
module.exports = CurrentTimeScrubber;
deleted file mode 100644
--- a/devtools/client/inspector/animation/components/CurrentTimeScrubberController.js
+++ /dev/null
@@ -1,141 +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 dom = require("devtools/client/shared/vendor/react-dom-factories");
-const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-const ReactDOM = require("devtools/client/shared/vendor/react-dom");
-
-const CurrentTimeScrubber = createFactory(require("./CurrentTimeScrubber"));
-
-class CurrentTimeScrubberController extends PureComponent {
- static get propTypes() {
- return {
- addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
- removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
- setAnimationsCurrentTime: PropTypes.func.isRequired,
- timeScale: PropTypes.object.isRequired,
- };
- }
-
- constructor(props) {
- super(props);
-
- const { addAnimationsCurrentTimeListener } = props;
- this.onCurrentTimeUpdated = this.onCurrentTimeUpdated.bind(this);
- this.onMouseDown = this.onMouseDown.bind(this);
- this.onMouseMove = this.onMouseMove.bind(this);
- this.onMouseOut = this.onMouseOut.bind(this);
- this.onMouseUp = this.onMouseUp.bind(this);
-
- this.state = {
- // offset of the position for the scrubber
- offset: 0,
- };
-
- addAnimationsCurrentTimeListener(this.onCurrentTimeUpdated);
- }
-
- componentDidMount() {
- const el = ReactDOM.findDOMNode(this);
- el.addEventListener("mousedown", this.onMouseDown);
- }
-
- componentWillUnmount() {
- const { removeAnimationsCurrentTimeListener } = this.props;
- removeAnimationsCurrentTimeListener(this.onCurrentTimeUpdated);
- }
-
- onCurrentTimeUpdated(currentTime) {
- const { timeScale } = this.props;
-
- const thisEl = ReactDOM.findDOMNode(this);
- const offset =
- thisEl ? currentTime / timeScale.getDuration() * thisEl.clientWidth : 0;
- this.setState({ offset });
- }
-
- onMouseDown(event) {
- event.stopPropagation();
- const thisEl = ReactDOM.findDOMNode(this);
- this.controllerArea = thisEl.getBoundingClientRect();
- this.listenerTarget = thisEl.closest(".animation-list-container");
- this.listenerTarget.addEventListener("mousemove", this.onMouseMove);
- this.listenerTarget.addEventListener("mouseout", this.onMouseOut);
- this.listenerTarget.addEventListener("mouseup", this.onMouseUp);
- this.listenerTarget.classList.add("active-scrubber");
-
- this.updateAnimationsCurrentTime(event.pageX, true);
- }
-
- onMouseMove(event) {
- event.stopPropagation();
- this.isMouseMoved = true;
- this.updateAnimationsCurrentTime(event.pageX);
- }
-
- onMouseOut(event) {
- event.stopPropagation();
-
- if (!this.listenerTarget.contains(event.relatedTarget)) {
- const endX = this.controllerArea.x + this.controllerArea.width;
- const pageX = endX < event.pageX ? endX : event.pageX;
- this.updateAnimationsCurrentTime(pageX, true);
- this.uninstallListeners();
- }
- }
-
- onMouseUp(event) {
- event.stopPropagation();
-
- if (this.isMouseMoved) {
- this.updateAnimationsCurrentTime(event.pageX, true);
- this.isMouseMoved = null;
- }
-
- this.uninstallListeners();
- }
-
- uninstallListeners() {
- this.listenerTarget.removeEventListener("mousemove", this.onMouseMove);
- this.listenerTarget.removeEventListener("mouseout", this.onMouseOut);
- this.listenerTarget.removeEventListener("mouseup", this.onMouseUp);
- this.listenerTarget.classList.remove("active-scrubber");
- this.listenerTarget = null;
- this.controllerArea = null;
- }
-
- updateAnimationsCurrentTime(pageX, needRefresh) {
- const {
- setAnimationsCurrentTime,
- timeScale,
- } = this.props;
-
- const time = pageX - this.controllerArea.x < 0 ?
- 0 :
- (pageX - this.controllerArea.x) /
- this.controllerArea.width * timeScale.getDuration();
-
- setAnimationsCurrentTime(time, needRefresh);
- }
-
- render() {
- const { offset } = this.state;
-
- return dom.div(
- {
- className: "current-time-scrubber-controller",
- },
- CurrentTimeScrubber(
- {
- offset,
- }
- )
- );
- }
-}
-
-module.exports = CurrentTimeScrubberController;
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/components/IndicationBar.js
@@ -0,0 +1,69 @@
+/* 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 dom = require("devtools/client/shared/vendor/react-dom-factories");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const ReactDOM = require("devtools/client/shared/vendor/react-dom");
+
+class IndicationBar extends PureComponent {
+ static get propTypes() {
+ return {
+ className: PropTypes.string.isRequired,
+ position: PropTypes.number.isRequired,
+ sidebarWidth: PropTypes.number.isRequired,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ // offset of the position for this indicator
+ offset: 0,
+ };
+ }
+
+ componentDidMount() {
+ this.updateState(this.props);
+ }
+
+ componentWillReceiveProps(nextProps) {
+ this.updateState(nextProps);
+ }
+
+ updateState(props) {
+ const { position } = props;
+
+ const parentEl = ReactDOM.findDOMNode(this).parentNode;
+ const offset = parentEl.offsetWidth * position;
+
+ this.setState({ offset });
+ }
+
+ render() {
+ const { className } = this.props;
+ const { offset } = this.state;
+
+ return dom.div(
+ {
+ className: `indication-bar ${ className }`,
+ style: {
+ transform: `translateX(${ offset }px)`,
+ },
+ }
+ );
+ }
+}
+
+const mapStateToProps = state => {
+ return {
+ sidebarWidth: state.animations.sidebarSize ? state.animations.sidebarSize.width : 0
+ };
+};
+
+module.exports = connect(mapStateToProps)(IndicationBar);
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/components/InspectListBox.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 { 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 InspectListBox extends PureComponent {
+ static get propTypes() {
+ return {
+ background: PropTypes.any.isRequired,
+ header: PropTypes.any.isRequired,
+ indicator: PropTypes.any.isRequired,
+ items: PropTypes.arrayOf(PropTypes.any).isRequired,
+ };
+ }
+
+ render() {
+ const {
+ background,
+ header,
+ indicator,
+ items
+ } = this.props;
+
+ return dom.div(
+ {
+ className: "inspect-list-box",
+ },
+ dom.div({ className: "background" }, background),
+ dom.div({ className: "indicator" }, indicator),
+ dom.div({ className: "header devtools-toolbar" }, header),
+ dom.ul({}, items)
+ );
+ }
+}
+
+module.exports = InspectListBox;
--- a/devtools/client/inspector/animation/components/KeyframesProgressBar.js
+++ b/devtools/client/inspector/animation/components/KeyframesProgressBar.js
@@ -1,18 +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 { PureComponent } = require("devtools/client/shared/vendor/react");
+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 ReactDOM = require("devtools/client/shared/vendor/react-dom");
+
+const IndicationBar = createFactory(require("./IndicationBar"));
class KeyframesProgressBar extends PureComponent {
static get propTypes() {
return {
addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
animation: PropTypes.object.isRequired,
getAnimationsCurrentTime: PropTypes.func.isRequired,
removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
@@ -22,50 +23,59 @@ class KeyframesProgressBar extends PureC
}
constructor(props) {
super(props);
this.onCurrentTimeUpdated = this.onCurrentTimeUpdated.bind(this);
this.state = {
- // offset of the position for the progress bar
- offset: 0,
+ // position for the progress bar
+ position: 0,
};
}
- componentDidMount() {
- const { addAnimationsCurrentTimeListener } = this.props;
+ componentWillMount() {
+ const {
+ animation,
+ addAnimationsCurrentTimeListener,
+ getAnimationsCurrentTime,
+ timeScale,
+ } = this.props;
- this.element = ReactDOM.findDOMNode(this);
+ addAnimationsCurrentTimeListener(this.onCurrentTimeUpdated);
this.setupAnimation(this.props);
- addAnimationsCurrentTimeListener(this.onCurrentTimeUpdated);
+ this.updateState(getAnimationsCurrentTime(), animation, timeScale);
}
componentWillReceiveProps(nextProps) {
const { animation, getAnimationsCurrentTime, timeScale } = nextProps;
this.setupAnimation(nextProps);
- this.updateOffset(getAnimationsCurrentTime(), animation, timeScale);
+ this.updateState(getAnimationsCurrentTime(), animation, timeScale);
}
componentWillUnmount() {
const { removeAnimationsCurrentTimeListener } = this.props;
removeAnimationsCurrentTimeListener(this.onCurrentTimeUpdated);
- this.element = null;
this.simulatedAnimation = null;
}
onCurrentTimeUpdated(currentTime) {
const { animation, timeScale } = this.props;
- this.updateOffset(currentTime, animation, timeScale);
+ this.updateState(currentTime, animation, timeScale);
}
- updateOffset(currentTime, animation, timeScale) {
+ updateState(currentTime, animation, timeScale) {
+ if (!this.simulatedAnimation) {
+ // Animation inspector has been destroyed.
+ return;
+ }
+
const {
createdTime,
playbackRate,
} = animation.state;
// If createdTime is not defined (which happens when connected to server older
// than FF62), use previousStartTime instead. See bug 1454392
const baseTime = typeof createdTime === "undefined"
@@ -75,20 +85,19 @@ class KeyframesProgressBar extends PureC
if (isNaN(time)) {
// Setting an invalid currentTime will throw so bail out if time is not a number for
// any reason.
return;
}
this.simulatedAnimation.currentTime = time;
- const offset = this.element.offsetWidth *
- this.simulatedAnimation.effect.getComputedTiming().progress;
-
- this.setState({ offset });
+ this.setState({
+ position: this.simulatedAnimation.effect.getComputedTiming().progress,
+ });
}
setupAnimation(props) {
const {
animation,
simulateAnimationForKeyframesProgressBar,
} = props;
@@ -99,27 +108,25 @@ class KeyframesProgressBar extends PureC
const timing = Object.assign({}, animation.state, {
iterations: animation.state.iterationCount || Infinity
});
this.simulatedAnimation = simulateAnimationForKeyframesProgressBar(timing);
}
render() {
- const { offset } = this.state;
+ const { position } = this.state;
return dom.div(
{
- className: "keyframes-progress-bar-area devtools-toolbar",
+ className: "keyframes-progress-bar-area",
},
- dom.div(
+ IndicationBar(
{
className: "keyframes-progress-bar",
- style: {
- transform: `translateX(${ offset }px)`,
- },
+ position,
}
)
);
}
}
module.exports = KeyframesProgressBar;
deleted file mode 100644
--- a/devtools/client/inspector/animation/components/KeyframesProgressTickItem.js
+++ /dev/null
@@ -1,37 +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 KeyframesProgressTickItem extends PureComponent {
- static get propTypes() {
- return {
- direction: PropTypes.string.isRequired,
- position: PropTypes.number.isRequired,
- progressTickLabel: PropTypes.string.isRequired,
- };
- }
-
- render() {
- const {
- direction,
- position,
- progressTickLabel,
- } = this.props;
-
- return dom.div(
- {
- className: `keyframes-progress-tick-item ${ direction }`,
- style: { [direction]: `${ position }%` }
- },
- progressTickLabel
- );
- }
-}
-
-module.exports = KeyframesProgressTickItem;
deleted file mode 100644
--- a/devtools/client/inspector/animation/components/KeyframesProgressTickList.js
+++ /dev/null
@@ -1,37 +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 dom = require("devtools/client/shared/vendor/react-dom-factories");
-
-const KeyframesProgressTickItem = createFactory(require("./KeyframesProgressTickItem"));
-const { getFormatStr } = require("../utils/l10n");
-
-class KeyframesProgressTickList extends PureComponent {
- render() {
- return dom.div(
- {
- className: "keyframes-progress-tick-list"
- },
- [0, 50, 100].map(progress => {
- const direction = progress === 100 ? "right" : "left";
- const position = progress === 100 ? 0 : progress;
- const progressTickLabel =
- getFormatStr("detail.propertiesHeader.percentage", progress);
-
- return KeyframesProgressTickItem(
- {
- direction,
- position,
- progressTickLabel,
- }
- );
- })
- );
- }
-}
-
-module.exports = KeyframesProgressTickList;
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/components/TickLabels.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 TickLabels extends PureComponent {
+ static get propTypes() {
+ return {
+ ticks: PropTypes.array.isRequired,
+ };
+ }
+
+ render() {
+ const { ticks } = this.props;
+
+ return dom.div(
+ {
+ className: "tick-labels"
+ },
+ ticks.map(tick =>
+ dom.div(
+ {
+ className: "tick-label",
+ style: { left: `${ tick.position }%` },
+ },
+ tick.label
+ )
+ )
+ );
+ }
+}
+
+module.exports = TickLabels;
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/animation/components/TickLines.js
@@ -0,0 +1,37 @@
+/* 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 TickLines extends PureComponent {
+ static get propTypes() {
+ return {
+ ticks: PropTypes.array.isRequired,
+ };
+ }
+
+ render() {
+ const { ticks } = this.props;
+
+ return dom.div(
+ {
+ className: "tick-lines"
+ },
+ ticks.map(tick =>
+ dom.div(
+ {
+ className: "tick-line",
+ style: { left: `${ tick.position }%` }
+ }
+ )
+ )
+ );
+ }
+}
+
+module.exports = TickLines;
--- a/devtools/client/inspector/animation/components/moz.build
+++ b/devtools/client/inspector/animation/components/moz.build
@@ -5,33 +5,28 @@
DIRS += [
'graph',
'keyframes-graph'
]
DevToolsModules(
'AnimatedPropertyItem.js',
'AnimatedPropertyList.js',
- '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',
+ 'IndicationBar.js',
+ 'InspectListBox.js',
'KeyframesProgressBar.js',
- 'KeyframesProgressTickItem.js',
- 'KeyframesProgressTickList.js',
'NoAnimationPanel.js',
'PauseResumeButton.js',
'PlaybackRateSelector.js',
'RewindButton.js',
+ 'TickLabels.js',
+ 'TickLines.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
@@ -19,53 +19,49 @@ const TIME_GRADUATION_MIN_SPACING = 40;
add_task(async function() {
await pushPref("devtools.inspector.three-pane-enabled", false);
await addTab(URL_ROOT + "doc_simple_animation.html");
await removeAnimatedElementsExcept([".end-delay", ".negative-delay"]);
const { animationInspector, inspector, panel } = await openAnimationInspector();
const timeScale = new TimeScale(animationInspector.state.animations);
info("Checking animation list header element existence");
- const listContainerEl = panel.querySelector(".animation-list-container");
+ const listContainerEl = panel.querySelector(".animation-list");
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;
+ 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,
+ ok(timelineTickItemLength < listContainerEl.querySelectorAll(".tick-label").length,
"The timeline tick item elements should increase");
});
/**
* Assert timeline tick item's position and label.
*
* @param {TimeScale} - timeScale
* @param {Element} - listContainerEl
*/
function assertTimelineTickItems(timeScale, listContainerEl) {
- const timelineTickListEl =
- listContainerEl.querySelector(".animation-timeline-tick-list");
+ 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 expectedTickItem = Math.ceil(animationDuration / interval) + 1;
- 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/inspector/animation/test/browser_animation_current-time-scrubber.js
+++ b/devtools/client/inspector/animation/test/browser_animation_current-time-scrubber.js
@@ -10,17 +10,17 @@
// * mouse drag on the scrubber
add_task(async function() {
await addTab(URL_ROOT + "doc_simple_animation.html");
await removeAnimatedElementsExcept([".long"]);
const { animationInspector, panel } = await openAnimationInspector();
info("Checking scrubber controller existence");
- const controllerEl = panel.querySelector(".current-time-scrubber-controller");
+ const controllerEl = panel.querySelector(".current-time-scrubber-area");
ok(controllerEl, "scrubber controller should exist");
info("Checking scrubber existence");
const scrubberEl = controllerEl.querySelector(".current-time-scrubber");
ok(scrubberEl, "scrubber should exist");
info("Checking scrubber changes current time of animation and the position");
const duration = animationInspector.state.timeScale.getDuration();
@@ -49,18 +49,18 @@ add_task(async function() {
previousX = scrubberEl.getBoundingClientRect().x;
await dragOnCurrentTimeScrubber(animationInspector, panel, 0.5, 2, 30);
currentX = scrubberEl.getBoundingClientRect().x;
isnot(previousX, currentX, "Scrubber should be draggable");
info("Checking a behavior which mouse out from animation inspector area " +
"during dragging from controller");
await dragOnCurrentTimeScrubberController(animationInspector, panel, 0.5, 2);
- ok(!panel.querySelector(".animation-list-container")
- .classList.contains("active-scrubber"), "Click and DnD should be inactive");
+ ok(!panel.querySelector(".animation-list").classList.contains("active-scrubber"),
+ "Click and DnD should be inactive");
});
function assertPosition(scrubberEl, controllerEl, time, animationInspector) {
const controllerBounds = controllerEl.getBoundingClientRect();
const scrubberBounds = scrubberEl.getBoundingClientRect();
const scrubberX = scrubberBounds.x + scrubberBounds.width / 2 - controllerBounds.x;
const timeScale = animationInspector.state.timeScale;
const expected = Math.round(time / timeScale.getDuration() * controllerBounds.width);
--- a/devtools/client/inspector/animation/test/browser_animation_keyframes-progress-bar.js
+++ b/devtools/client/inspector/animation/test/browser_animation_keyframes-progress-bar.js
@@ -84,13 +84,14 @@ add_task(async function() {
assertPosition(barEl, areaEl, expectedPositions[i], animationInspector);
}
}
});
function assertPosition(barEl, areaEl, expectedRate, animationInspector) {
const controllerBounds = areaEl.getBoundingClientRect();
const barBounds = barEl.getBoundingClientRect();
- const barX = barBounds.x + barBounds.width / 2 - controllerBounds.x;
+ // 6 means margin-left of .indication-bar
+ const barX = barBounds.x + barBounds.width / 2 - controllerBounds.x + 6;
const expected = controllerBounds.width * expectedRate;
ok(expected - 1 < barX && barX < expected + 1,
`Position should apploximately be ${ expected } (x of bar is ${ barX })`);
}
--- a/devtools/client/inspector/animation/test/browser_animation_keyframes-progress-bar_after-resuming.js
+++ b/devtools/client/inspector/animation/test/browser_animation_keyframes-progress-bar_after-resuming.js
@@ -29,14 +29,15 @@ async function assertPosition(panel, scr
const barEl = areaEl.querySelector(".keyframes-progress-bar");
const controllerBounds = areaEl.getBoundingClientRect();
for (let i = 0; i < scrubberPositions.length; i++) {
info(`Scrubber position is ${ scrubberPositions[i] }`);
await clickOnCurrentTimeScrubberController(animationInspector,
panel, scrubberPositions[i]);
const barBounds = barEl.getBoundingClientRect();
- const barX = barBounds.x + barBounds.width / 2 - controllerBounds.x;
+ // 6 means margin-left of .indication-bar
+ const barX = barBounds.x + barBounds.width / 2 - controllerBounds.x + 6;
const expected = controllerBounds.width * expectedPositions[i];
ok(expected - 1 < barX && barX < expected + 1,
`Position should apploximately be ${ expected } (x of bar is ${ barX })`);
}
}
--- a/devtools/client/inspector/animation/test/head.js
+++ b/devtools/client/inspector/animation/test/head.js
@@ -185,17 +185,17 @@ const clickOnRewindButton = async functi
* This method calculates
* `mouseDownPosition * offsetWidth + offsetLeft of scrubber controller pane`
* as the clientX of MouseEvent.
*/
const clickOnCurrentTimeScrubberController = async function(animationInspector,
panel,
mouseDownPosition,
mouseMovePosition) {
- const controllerEl = panel.querySelector(".current-time-scrubber-controller");
+ const controllerEl = panel.querySelector(".current-time-scrubber-area");
const bounds = controllerEl.getBoundingClientRect();
const mousedonwX = bounds.width * mouseDownPosition;
info(`Click ${ mousedonwX } on scrubber controller`);
EventUtils.synthesizeMouse(controllerEl, mousedonwX, 0, {}, controllerEl.ownerGlobal);
await waitForSummaryAndDetail(animationInspector);
};
@@ -332,17 +332,17 @@ const dragOnCurrentTimeScrubber = async
* @param {Number} mouseMovePosition
* Dispatch mousemove event with mouseMovePosition after mousedown.
* Calculation for clinetX is same to above.
*/
const dragOnCurrentTimeScrubberController = async function(animationInspector,
panel,
mouseDownPosition,
mouseMovePosition) {
- const controllerEl = panel.querySelector(".current-time-scrubber-controller");
+ const controllerEl = panel.querySelector(".current-time-scrubber-area");
const bounds = controllerEl.getBoundingClientRect();
const mousedonwX = bounds.width * mouseDownPosition;
const mousemoveX = bounds.width * mouseMovePosition;
info(`Drag on scrubber controller from ${ mousedonwX } to ${ mousemoveX }`);
EventUtils.synthesizeMouse(controllerEl, mousedonwX, 0,
{ type: "mousedown" }, controllerEl.ownerGlobal);
await waitForSummaryAndDetail(animationInspector);
@@ -363,17 +363,17 @@ const dragOnCurrentTimeScrubberControlle
* @param {Number} pixels
* @return {Object}
* {
* duration,
* rate,
* }
*/
const getDurationAndRate = function(animationInspector, panel, pixels) {
- const controllerEl = panel.querySelector(".current-time-scrubber-controller");
+ const controllerEl = panel.querySelector(".current-time-scrubber-area");
const bounds = controllerEl.getBoundingClientRect();
const duration =
animationInspector.state.timeScale.getDuration() / bounds.width * pixels;
const rate = 1 / bounds.width * pixels;
return { duration, rate };
};
/**
--- a/devtools/client/themes/animation.css
+++ b/devtools/client/themes/animation.css
@@ -82,128 +82,60 @@ select.playback-rate-selector.devtools-b
border-color: var(--toolbarbutton-hover-border-color);
}
.rewind-button::before {
background-image: var(--rewind-image);
}
/* Animation List Container */
-.animation-list-container {
+.animation-list {
height: 100%;
- overflow-y: auto;
- overflow-x: hidden;
- position: relative;
+ overflow: hidden;
width: 100%;
-moz-user-select: none;
}
-.animation-list-container.active-scrubber {
+.animation-list.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;
+/* Current Time Scrubber */
+.current-time-scrubber-area {
+ grid-column: 2 / 3;
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%;
-}
-
-.current-time-scrubber-controller::before {
+.current-time-scrubber-area::before {
content: "";
cursor: col-resize;
height: var(--devtools-toolbar-height);
pointer-events: auto;
position: absolute;
/* In order to click on edge of current-time-scrubber-controller element */
width: calc(100% + 1px);
}
-.current-time-scrubber {
+.current-time-scrubber-area .indication-bar {
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;
- top: 0;
- width: 0;
}
-.current-time-scrubber::after {
- border-left: 1px solid var(--scrubber-color);
- content: "";
- height: 100%;
- left: 5px;
- position: absolute;
- top: 0;
- width: 0;
+.current-time-scrubber-area .indication-bar::before {
+ border-top-color: var(--scrubber-color);
}
-/* Animation List */
-.animation-list {
- list-style-type: none;
- margin: 0;
- padding: 0;
- position: absolute;
- top: var(--devtools-toolbar-height);
- width: 100%;
+.current-time-scrubber-area .indication-bar::after {
+ border-left-color: var(--scrubber-color);
}
/* Animation Item */
.animation-item {
- display: flex;
height: 30px;
}
.animation-item:nth-child(2n+1) {
background-color: var(--animation-even-background-color);
}
.animation-item.cssanimation {
@@ -224,19 +156,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: inherit;
padding-left: 4px;
- width: var(--sidebar-width);
}
/* Reps component */
.animation-target .objectBox {
display: flex;
max-width: 100%;
}
@@ -254,20 +186,20 @@ 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: inherit;
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);
@@ -377,17 +309,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;
}
.animation-detail-title {
flex: 1;
@@ -396,165 +328,73 @@ select.playback-rate-selector.devtools-b
white-space: nowrap;
}
.animation-detail-close-button::before {
background-image: url(chrome://devtools/skin/images/close.svg);
}
/* Animated Property List Container */
-.animated-property-list-container {
+.animated-property-list {
display: flex;
flex: 1;
flex-direction: column;
overflow-y: auto;
position: relative;
}
-/* Animated Property List Header */
-.animated-property-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: 1;
-}
-
-.animated-property-list-header .devtools-toolbar {
- position: absolute;
- width: 100%;
-}
-
-/* Keyframes Progress Tick List */
-.keyframes-progress-tick-list {
- grid-column: 2 / 3;
- height: 100%;
- position: absolute;
- width: 100%;
-}
-
-.keyframes-progress-tick-item {
- position: absolute;
-}
-
-.keyframes-progress-tick-item.left {
- border-left: var(--tick-line-style);
-}
-
-.keyframes-progress-tick-item.right {
- border-right: var(--tick-line-style);
-}
-
/* Keyframes Progress Bar */
.keyframes-progress-bar-area {
- background: none;
grid-column: 2 / 3;
- padding: 0;
- height: 100%;
- position: absolute;
- width: 100%;
}
-.keyframes-progress-bar {
- height: 100%;
- position: absolute;
- z-index: 1;
-}
-
-.keyframes-progress-bar::before {
- border-left: 5px solid transparent;
- border-right: 5px solid transparent;
- border-top: 5px solid var(--progress-bar-color);
- content: "";
- left: -5px;
- position: absolute;
- top: 0;
- width: 0;
-}
-
-.keyframes-progress-bar::after {
- border-left: 1px solid var(--progress-bar-color);
- content: "";
- height: 100%;
- position: absolute;
- top: 0;
- width: 0;
+.keyframes-progress-bar-area .indication-bar::before {
+ border-top-color: var(--progress-bar-color);
}
-/* Animated Property List */
-.animated-property-list-background {
- border-left: var(--tick-line-style);
- border-right: var(--tick-line-style);
- bottom: 0;
- left: var(--sidebar-width);
- min-height: 100%;
- position: sticky;
- top: 0;
- width: calc(100% - var(--sidebar-width) - var(--graph-right-offset));
-}
-
-.animated-property-list-background span {
- border-left: var(--tick-line-style);
- height: 100%;
- left: 50%;
- position: absolute;
-}
-
-.animated-property-list {
- flex: 1;
- list-style-type: none;
- margin: 0;
- padding: 0;
- position: absolute;
- top: var(--devtools-toolbar-height);
- width: 100%;
+.keyframes-progress-bar-area .indication-bar::after {
+ border-left-color: var(--progress-bar-color);
}
/* Animated Property Item */
.animated-property-item {
- display: flex;
height: 30px;
}
.animated-property-item:nth-child(2n+1) {
background-color: var(--animation-even-background-color);
}
.animated-property-item.unchanged {
opacity: 0.6;
}
/* Animated Property Name */
.animated-property-name {
align-items: center;
display: flex;
- height: 100%;
+ height: inherit;
justify-content: flex-end;
padding-right: 10px;
- width: var(--sidebar-width);
}
.animated-property-name.compositor span {
padding-left: 15px;
position: relative;
}
-.animated-property-list-container.cssanimation .animated-property-name.compositor {
+.animated-property-list.cssanimation .animated-property-name.compositor {
--fast-track-color: var(--stroke-color-cssanimation);
}
-.animated-property-list-container.csstransition .animated-property-name.compositor {
+.animated-property-list.csstransition .animated-property-name.compositor {
--fast-track-color: var(--stroke-color-csstransition);
}
-.animated-property-list-container.scriptanimation .animated-property-name.compositor {
+.animated-property-list.scriptanimation .animated-property-name.compositor {
--fast-track-color: var(--stroke-color-scriptanimation);
}
.animated-property-name.compositor span::before {
background-image: var(--fast-track-image);
background-repeat: no-repeat;
background-size: contain;
content: "";
@@ -568,19 +408,18 @@ select.playback-rate-selector.devtools-b
.animated-property-name.warning span {
text-decoration: underline dotted;
}
/* Keyframes Graph */
.keyframes-graph {
padding-top: 3px;
- height: 100%;
+ height: inherit;
position: relative;
- width: calc(100% - var(--sidebar-width) - var(--graph-right-offset));
}
.keyframes-graph-path {
height: 100%;
width: 100%;
}
.keyframes-graph-path path {
@@ -644,32 +483,125 @@ select.playback-rate-selector.devtools-b
pointer-events: auto;
position: absolute;
top: 50%;
height: 10px;
transform: translate(-5px, -3px);
width: 10px;
}
-.animated-property-list-container.cssanimation .keyframe-marker-item {
+.animated-property-list.cssanimation .keyframe-marker-item {
background-color: var(--fill-color-cssanimation);
}
-.animated-property-list-container.csstransition .keyframe-marker-item {
+.animated-property-list.csstransition .keyframe-marker-item {
background-color: var(--fill-color-csstransition);
}
-.animated-property-list-container.scriptanimation .keyframe-marker-item {
+.animated-property-list.scriptanimation .keyframe-marker-item {
background-color: var(--fill-color-scriptanimation);
}
/* No Animation Panel */
.animation-error-message {
overflow: auto;
}
.animation-error-message > p {
white-space: pre;
}
.animation-element-picker::before {
background-image: var(--command-pick-image);
}
+
+/* Inspect List Box */
+.inspect-list-box {
+ height: 100%;
+ overflow-y: auto;
+ overflow-x: hidden;
+}
+
+.inspect-list-box ul {
+ list-style-type: none;
+ margin: 0;
+ padding: 0;
+}
+
+.inspect-list-box .background,
+.inspect-list-box .indicator,
+.inspect-list-box .header,
+.inspect-list-box ul li {
+ display: grid;
+ grid-template-columns:
+ var(--sidebar-width) calc(100% - var(--sidebar-width) - var(--graph-right-offset)) var(--graph-right-offset);
+}
+
+.inspect-list-box .header {
+ padding: 0;
+ position: sticky;
+ top: 0;
+ z-index: 1;
+}
+
+/* Tick Lines */
+.tick-lines {
+ grid-column: 2/3;
+ position: relative;
+}
+
+.tick-line {
+ position: absolute;
+}
+
+.tick-line::before {
+ border-left: var(--tick-line-style);
+ content: "";
+ height: 100vh;
+ position: fixed;
+}
+
+/* Tick Labels */
+.tick-labels {
+ grid-column: 2/3;
+ height: 100%;
+ position: relative;
+}
+
+.tick-label {
+ border-left: var(--tick-line-style);
+ height: 100%;
+ position: absolute;
+}
+
+.animated-property-list .tick-label:last-child {
+ border-left: none;
+ border-right: var(--tick-line-style);
+ transform: translateX(calc(-100% + 0.5px));
+}
+
+/* Indication Bar */
+.indication-bar {
+ height: 100%;
+ margin-left: -6px;
+ position: absolute;
+ z-index: 2;
+}
+
+.indication-bar::before {
+ border-left: 5px solid transparent;
+ border-right: 5px solid transparent;
+ border-top: 5px solid;
+ content: "";
+ position: absolute;
+ top: 0;
+ width: 0;
+}
+
+.indication-bar::after {
+ border-left: 1px solid;
+ content: "";
+ height: 100%;
+ left: 5px;
+ position: absolute;
+ top: 0;
+ width: 0;
+}