summaryrefslogtreecommitdiffstats
path: root/devtools/client/inspector/animation/components/AnimationTarget.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--devtools/client/inspector/animation/components/AnimationTarget.js182
1 files changed, 182 insertions, 0 deletions
diff --git a/devtools/client/inspector/animation/components/AnimationTarget.js b/devtools/client/inspector/animation/components/AnimationTarget.js
new file mode 100644
index 0000000000..2fa392b3f6
--- /dev/null
+++ b/devtools/client/inspector/animation/components/AnimationTarget.js
@@ -0,0 +1,182 @@
+/* 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,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const {
+ connect,
+} = require("resource://devtools/client/shared/vendor/react-redux.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const {
+ translateNodeFrontToGrip,
+} = require("resource://devtools/client/inspector/shared/utils.js");
+
+const {
+ REPS,
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/index.js");
+const { Rep } = REPS;
+const ElementNode = REPS.ElementNode;
+
+const {
+ getInspectorStr,
+} = require("resource://devtools/client/inspector/animation/utils/l10n.js");
+
+const {
+ highlightNode,
+ unhighlightNode,
+} = require("resource://devtools/client/inspector/boxmodel/actions/box-model-highlighter.js");
+
+class AnimationTarget extends Component {
+ static get propTypes() {
+ return {
+ animation: PropTypes.object.isRequired,
+ dispatch: PropTypes.func.isRequired,
+ getNodeFromActor: PropTypes.func.isRequired,
+ highlightedNode: PropTypes.string.isRequired,
+ setHighlightedNode: PropTypes.func.isRequired,
+ setSelectedNode: PropTypes.func.isRequired,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ nodeFront: null,
+ };
+ }
+
+ // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507
+ UNSAFE_componentWillMount() {
+ this.updateNodeFront(this.props.animation);
+ }
+
+ // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507
+ UNSAFE_componentWillReceiveProps(nextProps) {
+ if (this.props.animation.actorID !== nextProps.animation.actorID) {
+ this.updateNodeFront(nextProps.animation);
+ }
+ }
+
+ shouldComponentUpdate(nextProps, nextState) {
+ return (
+ this.state.nodeFront !== nextState.nodeFront ||
+ this.props.highlightedNode !== nextState.highlightedNode
+ );
+ }
+
+ async updateNodeFront(animation) {
+ const { getNodeFromActor } = this.props;
+
+ // Try and get it from the playerFront directly.
+ let nodeFront = animation.animationTargetNodeFront;
+
+ // Next, get it from the walkerActor if it wasn't found.
+ if (!nodeFront) {
+ try {
+ nodeFront = await getNodeFromActor(animation.actorID);
+ } catch (e) {
+ // If an error occured while getting the nodeFront and if it can't be
+ // attributed to the panel having been destroyed in the meantime, this
+ // error needs to be logged and render needs to stop.
+ console.error(e);
+ this.setState({ nodeFront: null });
+ return;
+ }
+ }
+
+ this.setState({ nodeFront });
+ }
+
+ async ensureNodeFront() {
+ if (!this.state.nodeFront.actorID) {
+ // In case of no actorID, the node front had been destroyed.
+ // This will occur when the pseudo element was re-generated.
+ await this.updateNodeFront(this.props.animation);
+ }
+ }
+
+ async highlight() {
+ await this.ensureNodeFront();
+
+ if (this.state.nodeFront) {
+ this.props.dispatch(
+ highlightNode(this.state.nodeFront, {
+ hideInfoBar: true,
+ hideGuides: true,
+ })
+ );
+ }
+ }
+
+ async select() {
+ await this.ensureNodeFront();
+
+ if (this.state.nodeFront) {
+ this.props.setSelectedNode(this.state.nodeFront);
+ }
+ }
+
+ render() {
+ const { dispatch, highlightedNode, setHighlightedNode } = this.props;
+ const { nodeFront } = this.state;
+
+ if (!nodeFront) {
+ return dom.div({
+ className: "animation-target",
+ });
+ }
+
+ const isHighlighted = nodeFront.actorID === highlightedNode;
+
+ return dom.div(
+ {
+ className: "animation-target" + (isHighlighted ? " highlighting" : ""),
+ },
+ Rep({
+ defaultRep: ElementNode,
+ mode: MODE.TINY,
+ inspectIconTitle: getInspectorStr(
+ "inspector.nodePreview.highlightNodeLabel"
+ ),
+ inspectIconClassName: "highlight-node",
+ object: translateNodeFrontToGrip(nodeFront),
+ onDOMNodeClick: () => this.select(),
+ onDOMNodeMouseOut: () => {
+ if (!isHighlighted) {
+ dispatch(unhighlightNode());
+ }
+ },
+ onDOMNodeMouseOver: () => {
+ if (!isHighlighted) {
+ this.highlight();
+ }
+ },
+ onInspectIconClick: (_, e) => {
+ e.stopPropagation();
+
+ if (!isHighlighted) {
+ // At first, hide highlighter which was created by onDOMNodeMouseOver.
+ dispatch(unhighlightNode());
+ }
+
+ setHighlightedNode(isHighlighted ? null : nodeFront);
+ },
+ })
+ );
+ }
+}
+
+const mapStateToProps = state => {
+ return {
+ highlightedNode: state.animations.highlightedNode,
+ };
+};
+
+module.exports = connect(mapStateToProps)(AnimationTarget);