Bug 1345119 - Part 3: Display offset parent of absolutely positioned node in box model. r=gl

MozReview-Commit-ID: 102vRTuIhEh
This commit is contained in:
Stanford Lockhart 2017-03-17 23:07:06 -03:00
parent ab224ce876
commit b598497c17
11 changed files with 144 additions and 9 deletions

View File

@ -7,6 +7,7 @@
const {
UPDATE_GEOMETRY_EDITOR_ENABLED,
UPDATE_LAYOUT,
UPDATE_OFFSET_PARENT,
} = require("./index");
module.exports = {
@ -34,4 +35,14 @@ module.exports = {
};
},
/**
* Update the offset parent state with the new DOM node.
*/
updateOffsetParent(offsetParent) {
return {
type: UPDATE_OFFSET_PARENT,
offsetParent,
};
}
};

View File

@ -14,4 +14,7 @@ createEnum([
// Update the layout state with the latest layout properties.
"UPDATE_LAYOUT",
// Update the offset parent state with the new DOM node.
"UPDATE_OFFSET_PARENT",
], module.exports);

View File

@ -13,6 +13,7 @@ const { InplaceEditor } = require("devtools/client/shared/inplace-editor");
const {
updateGeometryEditorEnabled,
updateLayout,
updateOffsetParent,
} = require("./actions/box-model");
const EditingSession = require("./utils/editing-session");
@ -167,6 +168,15 @@ BoxModel.prototype = {
isPositionEditable,
});
const actorCanGetOffSetParent
= yield this.inspector.target.actorHasMethod("domwalker", "getOffsetParent");
if (actorCanGetOffSetParent) {
// Update the redux store with the latest offset parent DOM node
let offsetParent = yield this.inspector.walker.getOffsetParent(node);
this.store.dispatch(updateOffsetParent(offsetParent));
}
// Update the redux store with the latest layout properties and update the box
// model view.
this.store.dispatch(updateLayout(layout));

View File

@ -19,10 +19,12 @@ module.exports = createClass({
propTypes: {
boxModel: PropTypes.shape(Types.boxModel).isRequired,
setSelectedNode: PropTypes.func.isRequired,
showBoxModelProperties: PropTypes.bool.isRequired,
onHideBoxModelHighlighter: PropTypes.func.isRequired,
onShowBoxModelEditor: PropTypes.func.isRequired,
onShowBoxModelHighlighter: PropTypes.func.isRequired,
onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
onToggleGeometryEditor: PropTypes.func.isRequired,
},
@ -31,10 +33,12 @@ module.exports = createClass({
render() {
let {
boxModel,
setSelectedNode,
showBoxModelProperties,
onHideBoxModelHighlighter,
onShowBoxModelEditor,
onShowBoxModelHighlighter,
onShowBoxModelHighlighterForNode,
onToggleGeometryEditor,
} = this.props;
@ -44,9 +48,11 @@ module.exports = createClass({
},
BoxModelMain({
boxModel,
setSelectedNode,
onHideBoxModelHighlighter,
onShowBoxModelEditor,
onShowBoxModelHighlighter,
onShowBoxModelHighlighterForNode,
}),
BoxModelInfo({
boxModel,

View File

@ -25,10 +25,12 @@ const BoxModelApp = createClass({
propTypes: {
boxModel: PropTypes.shape(Types.boxModel).isRequired,
setSelectedNode: PropTypes.func.isRequired,
showBoxModelProperties: PropTypes.bool.isRequired,
onHideBoxModelHighlighter: PropTypes.func.isRequired,
onShowBoxModelEditor: PropTypes.func.isRequired,
onShowBoxModelHighlighter: PropTypes.func.isRequired,
onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
onToggleGeometryEditor: PropTypes.func.isRequired,
},

View File

@ -10,6 +10,9 @@ const { addons, createClass, createFactory, DOM: dom, PropTypes } =
const { LocalizationHelper } = require("devtools/shared/l10n");
const BoxModelEditable = createFactory(require("./BoxModelEditable"));
// Reps
const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
const Rep = createFactory(REPS.Rep);
const Types = require("../types");
@ -25,9 +28,11 @@ module.exports = createClass({
propTypes: {
boxModel: PropTypes.shape(Types.boxModel).isRequired,
setSelectedNode: PropTypes.func.isRequired,
onHideBoxModelHighlighter: PropTypes.func.isRequired,
onShowBoxModelEditor: PropTypes.func.isRequired,
onShowBoxModelHighlighter: PropTypes.func.isRequired,
onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
},
mixins: [ addons.PureRenderMixin ],
@ -92,12 +97,62 @@ module.exports = createClass({
return layout[property] ? parseFloat(layout[property]) : "-";
},
/**
* While waiting for a reps fix in https://github.com/devtools-html/reps/issues/92,
* translate nodeFront to a grip-like object that can be used with an ElementNode rep.
*
* @params {NodeFront} nodeFront
* The NodeFront for which we want to create a grip-like object.
* @returns {Object} a grip-like object that can be used with Reps.
*/
translateNodeFrontToGrip(nodeFront) {
let {
attributes
} = nodeFront;
// The main difference between NodeFront and grips is that attributes are treated as
// a map in grips and as an array in NodeFronts.
let attributesMap = {};
for (let { name, value } of attributes) {
attributesMap[name] = value;
}
return {
actor: nodeFront.actorID,
preview: {
attributes: attributesMap,
attributesLength: attributes.length,
// nodeName is already lowerCased in Node grips
nodeName: nodeFront.nodeName.toLowerCase(),
nodeType: nodeFront.nodeType,
}
};
},
onHighlightMouseOver(event) {
let region = event.target.getAttribute("data-box");
if (!region) {
let el = event.target;
do {
el = el.parentNode;
if (el && el.getAttribute("data-box")) {
region = el.getAttribute("data-box");
break;
}
} while (el.parentNode);
this.props.onHideBoxModelHighlighter();
}
if (region === "offset-parent") {
this.props.onHideBoxModelHighlighter();
this.props.onShowBoxModelHighlighterForNode(this.props.boxModel.offsetParent);
return;
}
this.props.onShowBoxModelHighlighter({
region,
showOnly: region,
@ -106,10 +161,16 @@ module.exports = createClass({
},
render() {
let { boxModel, onShowBoxModelEditor } = this.props;
let { layout } = boxModel;
let {
boxModel,
setSelectedNode,
onShowBoxModelEditor,
} = this.props;
let { layout, offsetParent } = boxModel;
let { height, width, position } = layout;
let displayOffsetParent = offsetParent && layout.position === "absolute";
let borderTop = this.getBorderOrPaddingValue("border-top-width");
let borderRight = this.getBorderOrPaddingValue("border-right-width");
let borderBottom = this.getBorderOrPaddingValue("border-bottom-width");
@ -175,6 +236,23 @@ module.exports = createClass({
onMouseOver: this.onHighlightMouseOver,
onMouseOut: this.props.onHideBoxModelHighlighter,
},
displayOffsetParent ?
dom.span(
{
className: "boxmodel-offset-parent",
"data-box": "offset-parent",
},
Rep(
{
defaultRep: offsetParent,
mode: MODE.TINY,
object: this.translateNodeFrontToGrip(offsetParent),
onInspectIconClick: () => setSelectedNode(offsetParent, "box-model"),
}
)
)
:
null,
displayPosition ?
dom.span(
{

View File

@ -7,11 +7,13 @@
const {
UPDATE_GEOMETRY_EDITOR_ENABLED,
UPDATE_LAYOUT,
UPDATE_OFFSET_PARENT,
} = require("../actions/index");
const INITIAL_BOX_MODEL = {
geometryEditorEnabled: false,
layout: {},
offsetParent: null
};
let reducers = {
@ -28,6 +30,12 @@ let reducers = {
});
},
[UPDATE_OFFSET_PARENT](boxModel, { offsetParent }) {
return Object.assign({}, boxModel, {
offsetParent,
});
},
};
module.exports = function (boxModel = INITIAL_BOX_MODEL, action) {

View File

@ -17,4 +17,7 @@ exports.boxModel = {
// The layout information of the current selected node
layout: PropTypes.object,
// The offset parent for the selected node
offsetParent: PropTypes.object,
};

View File

@ -617,6 +617,11 @@ CssComputedView.prototype = {
* Render the box model view.
*/
createBoxModelView: function () {
let {
setSelectedNode,
onShowBoxModelHighlighterForNode,
} = this.inspector.getCommonComponentProps();
let {
onHideBoxModelHighlighter,
onShowBoxModelEditor,
@ -628,10 +633,12 @@ CssComputedView.prototype = {
Provider,
{ store: this.store },
BoxModelApp({
setSelectedNode,
showBoxModelProperties: false,
onHideBoxModelHighlighter,
onShowBoxModelEditor,
onShowBoxModelHighlighter,
onShowBoxModelHighlighterForNode,
onToggleGeometryEditor,
})
);

View File

@ -323,3 +323,12 @@
.boxmodel-properties-wrapper {
padding: 0 9px;
}
/* Box Model Main - Offset Parent */
.boxmodel-offset-parent {
position: absolute;
top: -20px;
right: -10px;
color: var(--theme-highlight-purple);
}

View File

@ -2649,15 +2649,13 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
},
/**
* Get the offset parent of the node
* If the offset parent is statically positioned, there is no offset parent
* and null is returned.
* Returns the DOMNode for the offset parent if it exists
* Returns the offset parent DOMNode of the given node if it exists, otherwise, it
* returns null.
*/
getOffsetParent: function (domnode) {
let offsetParent = domnode.rawNode.offsetParent;
getOffsetParent: function (node) {
let offsetParent = node.rawNode.offsetParent;
if (!offsetParent || CssLogic.getComputedStyle(offsetParent).position === "static") {
if (!offsetParent) {
return null;
}