/* 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);