From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- .../animation/components/AnimationTarget.js | 182 +++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 devtools/client/inspector/animation/components/AnimationTarget.js (limited to 'devtools/client/inspector/animation/components/AnimationTarget.js') 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); -- cgit v1.2.3