mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-17 23:35:34 +00:00
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:
parent
ab224ce876
commit
b598497c17
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
},
|
||||
|
||||
|
@ -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(
|
||||
{
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
||||
};
|
||||
|
@ -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,
|
||||
})
|
||||
);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user