--- a/devtools/client/inspector/grids/components/Grid.js
+++ b/devtools/client/inspector/grids/components/Grid.js
@@ -20,16 +20,17 @@ module.exports = createClass({
propTypes: {
getSwatchColorPickerTooltip: PropTypes.func.isRequired,
grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
highlighterSettings: PropTypes.shape(Types.highlighterSettings).isRequired,
setSelectedNode: PropTypes.func.isRequired,
showGridOutline: PropTypes.bool.isRequired,
onHideBoxModelHighlighter: PropTypes.func.isRequired,
+ onScrollToGridCellHighlight: PropTypes.func.isRequired,
onSetGridOverlayColor: PropTypes.func.isRequired,
onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
onShowGridAreaHighlight: PropTypes.func.isRequired,
onShowGridCellHighlight: PropTypes.func.isRequired,
onShowGridLineNamesHighlight: PropTypes.func.isRequired,
onToggleGridHighlighter: PropTypes.func.isRequired,
onToggleShowGridAreas: PropTypes.func.isRequired,
onToggleShowGridLineNumbers: PropTypes.func.isRequired,
@@ -41,16 +42,17 @@ module.exports = createClass({
render() {
let {
getSwatchColorPickerTooltip,
grids,
highlighterSettings,
setSelectedNode,
showGridOutline,
onHideBoxModelHighlighter,
+ onScrollToGridCellHighlight,
onSetGridOverlayColor,
onShowBoxModelHighlighterForNode,
onShowGridAreaHighlight,
onShowGridCellHighlight,
onToggleShowGridAreas,
onToggleGridHighlighter,
onToggleShowGridLineNumbers,
onToggleShowInfiniteLines,
@@ -79,16 +81,17 @@ module.exports = createClass({
onToggleShowGridAreas,
onToggleShowGridLineNumbers,
onToggleShowInfiniteLines,
})
),
showGridOutline ?
GridOutline({
grids,
+ onScrollToGridCellHighlight,
onShowGridAreaHighlight,
onShowGridCellHighlight,
})
:
null
)
:
dom.div(
--- a/devtools/client/inspector/grids/components/GridOutline.js
+++ b/devtools/client/inspector/grids/components/GridOutline.js
@@ -28,28 +28,30 @@ const VIEWPORT_MIN_HEIGHT = 100;
const VIEWPORT_MAX_HEIGHT = 150;
module.exports = createClass({
displayName: "GridOutline",
propTypes: {
grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
+ onScrollToGridCellHighlight: PropTypes.func.isRequired,
onShowGridAreaHighlight: PropTypes.func.isRequired,
onShowGridCellHighlight: PropTypes.func.isRequired,
},
mixins: [ addons.PureRenderMixin ],
getInitialState() {
return {
height: 0,
selectedGrid: null,
showOutline: true,
width: 0,
+ showContextMenu: false,
};
},
componentWillReceiveProps({ grids }) {
let selectedGrid = grids.find(grid => grid.highlighted);
// Store the height of the grid container in the component state to prevent overflow
// issues. We want to store the width of the grid container as well so that the
@@ -124,16 +126,40 @@ module.exports = createClass({
return VIEWPORT_MAX_HEIGHT;
} else if (height <= VIEWPORT_MIN_HEIGHT) {
return VIEWPORT_MIN_HEIGHT;
}
return height;
},
+ onContextMenu(e, gridFragmentIndex, rowNumber, columnNumber) {
+ e.preventDefault();
+ const { clientX, clientY } = e;
+ const contextMenu = this.refs.contextmenu;
+ contextMenu.style.left = clientX + "px";
+ contextMenu.style.top = clientY + "px";
+
+ this.setState({
+ showContextMenu: true,
+ gridFragmentIndex,
+ rowNumber,
+ columnNumber
+ });
+ },
+
+ onGridOutlineClick(e) {
+ this.setState({
+ showContextMenu: false,
+ gridFragmentIndex: null,
+ rowNumber: null,
+ columnNumber: null,
+ });
+ },
+
onHighlightCell({ target, type }) {
// Debounce the highlighting of cells.
// This way we don't end up sending many requests to the server for highlighting when
// cells get hovered in a rapid succession We only send a request if the user settles
// on a cell for some time.
if (this.highlightTimeout) {
clearTimeout(this.highlightTimeout);
}
@@ -142,42 +168,61 @@ module.exports = createClass({
this.doHighlightCell(target, type === "mouseleave");
this.highlightTimeout = null;
}, GRID_HIGHLIGHTING_DEBOUNCE);
},
doHighlightCell(target, hide) {
const {
grids,
+ onScrollToGridCellHighlight,
onShowGridAreaHighlight,
onShowGridCellHighlight,
} = this.props;
const name = target.dataset.gridAreaName;
const id = target.dataset.gridId;
const fragmentIndex = target.dataset.gridFragmentIndex;
const color = target.closest(".grid-cell-group").dataset.gridLineColor;
const rowNumber = target.dataset.gridRow;
const columnNumber = target.dataset.gridColumn;
if (hide) {
onShowGridAreaHighlight(grids[id].nodeFront, null, color);
onShowGridCellHighlight(grids[id].nodeFront, color);
+ onScrollToGridCellHighlight(grids[id].nodeFront, color);
return;
}
if (name) {
onShowGridAreaHighlight(grids[id].nodeFront, name, color);
}
if (fragmentIndex && rowNumber && columnNumber) {
onShowGridCellHighlight(grids[id].nodeFront, color, fragmentIndex,
rowNumber, columnNumber);
+ onScrollToGridCellHighlight(grids[id].nodeFront, color);
}
},
+ onScrollToGridCell({ target }) {
+ const { grids, onScrollToGridCellHighlight } = this.props;
+ const { gridFragmentIndex, columnNumber, rowNumber, selectedGrid } = this.state;
+ const { id, color } = selectedGrid;
+
+ onScrollToGridCellHighlight(grids[id].nodeFront, color,
+ gridFragmentIndex, rowNumber, columnNumber, true);
+
+ this.setState({
+ showContextMenu: false,
+ gridFragmentIndex: null,
+ rowNumber: null,
+ columnNumber: null,
+ });
+ },
+
/**
* Displays a message text "Cannot show outline for this grid".
*/
renderCannotShowOutlineText() {
return dom.div(
{
className: "grid-outline-text"
},
@@ -281,22 +326,44 @@ module.exports = createClass({
"data-grid-id": id,
"data-grid-row": rowNumber,
"data-grid-column": columnNumber,
x,
y,
width,
height,
fill: "none",
+ onContextMenu: (e) => this.onContextMenu(e,
+ gridFragmentIndex, rowNumber, columnNumber),
onMouseEnter: this.onHighlightCell,
onMouseLeave: this.onHighlightCell,
}
);
},
+ renderGridCellContextMenu() {
+ const { showContextMenu } = this.state;
+
+ return dom.div(
+ {
+ className: "grid-outline-context-menu",
+ ref: "contextmenu"
+ },
+ showContextMenu ?
+ dom.div({
+ onClick: this.onScrollToGridCell,
+ className: "grid-outline-context-menu-item",
+ },
+ "Scroll to grid cell"
+ )
+ :
+ null
+ );
+ },
+
renderGridOutline(grid) {
let { color } = grid;
return dom.g(
{
"className": "grid-cell-group",
"data-grid-line-color": color,
"style": { color }
@@ -341,16 +408,18 @@ module.exports = createClass({
render() {
const { selectedGrid } = this.state;
return selectedGrid ?
dom.div(
{
className: "grid-outline",
+ onClick: this.onGridOutlineClick,
},
- this.renderOutline()
+ this.renderOutline(),
+ this.renderGridCellContextMenu()
)
:
null;
},
});
--- a/devtools/client/inspector/grids/grid-inspector.js
+++ b/devtools/client/inspector/grids/grid-inspector.js
@@ -47,16 +47,17 @@ function GridInspector(inspector, window
this.getSwatchColorPickerTooltip = this.getSwatchColorPickerTooltip.bind(this);
this.updateGridPanel = this.updateGridPanel.bind(this);
this.onGridLayoutChange = this.onGridLayoutChange.bind(this);
this.onHighlighterChange = this.onHighlighterChange.bind(this);
this.onMarkupMutation = this.onMarkupMutation.bind(this);
this.onReflow = this.onReflow.bind(this);
+ this.onScrollToGridCellHighlight = this.onScrollToGridCellHighlight.bind(this);
this.onSetGridOverlayColor = this.onSetGridOverlayColor.bind(this);
this.onShowGridAreaHighlight = this.onShowGridAreaHighlight.bind(this);
this.onShowGridCellHighlight = this.onShowGridCellHighlight.bind(this);
this.onShowGridLineNamesHighlight = this.onShowGridLineNamesHighlight.bind(this);
this.onSidebarSelect = this.onSidebarSelect.bind(this);
this.onToggleGridHighlighter = this.onToggleGridHighlighter.bind(this);
this.onToggleShowGridAreas = this.onToggleShowGridAreas.bind(this);
this.onToggleShowGridLineNumbers = this.onToggleShowGridLineNumbers.bind(this);
@@ -120,16 +121,17 @@ GridInspector.prototype = {
this.swatchColorPickerTooltip = null;
this.walker = null;
},
getComponentProps() {
return {
getSwatchColorPickerTooltip: this.getSwatchColorPickerTooltip,
onSetGridOverlayColor: this.onSetGridOverlayColor,
+ onScrollToGridCellHighlight: this.onScrollToGridCellHighlight,
onShowGridAreaHighlight: this.onShowGridAreaHighlight,
onShowGridCellHighlight: this.onShowGridCellHighlight,
onShowGridLineNamesHighlight: this.onShowGridLineNamesHighlight,
onToggleGridHighlighter: this.onToggleGridHighlighter,
onToggleShowGridAreas: this.onToggleShowGridAreas,
onToggleShowGridLineNumbers: this.onToggleShowGridLineNumbers,
onToggleShowInfiniteLines: this.onToggleShowInfiniteLines,
};
@@ -378,16 +380,54 @@ GridInspector.prototype = {
if (grid.nodeFront === node && grid.highlighted) {
let highlighterSettings = this.getGridHighlighterSettings(node);
this.showGridHighlighter(node, highlighterSettings);
}
}
},
/**
+ * Scrolls to the grid cell in the CSS Grid Highlighter for the given grid.
+ *
+ * @param {NodeFront} node
+ * The NodeFront of the grid container element for which the grid
+ * highlighter is highlighted for.
+ * @param {String} color
+ * The color of the grid cell for which the grid highlighter
+ * is highlighted for.
+ * @param {Number|null} gridFragmentIndex
+ * The index of the grid fragment for which the grid highlighter
+ * is highlighted for.
+ * @param {Number|null} rowNumber
+ * The row number of the grid cell for which the grid highlighter
+ * is highlighted for.
+ * @param {Number|null} columnNumber
+ * The column number of the grid cell for which the grid highlighter
+ * is highlighted for.
+ * @param {Boolean|null} scroll
+ * Whether or not to scroll to the grid cell for which the grid highlighter
+ * is highlighted for.
+ */
+ onScrollToGridCellHighlight(node,
+ color, gridFragmentIndex, rowNumber, columnNumber, scroll) {
+ let { highlighterSettings } = this.store.getState();
+
+ highlighterSettings.scrollToGridCell = scroll;
+
+ if (scroll) {
+ highlighterSettings.showGridCell = { gridFragmentIndex, rowNumber, columnNumber };
+ highlighterSettings.color = color;
+ }
+
+ this.showGridHighlighter(node, highlighterSettings);
+
+ this.store.dispatch(updateGridHighlighted(node, true));
+ },
+
+ /**
* Highlights the grid area in the CSS Grid Highlighter for the given grid.
*
* @param {NodeFront} node
* The NodeFront of the grid container element for which the grid
* highlighter is highlighted for.
* @param {String} gridAreaName
* The name of the grid area for which the grid highlighter
* is highlighted for.
--- a/devtools/client/inspector/layout/layout.js
+++ b/devtools/client/inspector/layout/layout.js
@@ -48,16 +48,17 @@ LayoutView.prototype = {
onHideBoxModelHighlighter,
onShowBoxModelEditor,
onShowBoxModelHighlighter,
onToggleGeometryEditor,
} = this.inspector.getPanel("boxmodel").getComponentProps();
let {
getSwatchColorPickerTooltip,
+ onScrollToGridCellHighlight,
onSetGridOverlayColor,
onShowGridAreaHighlight,
onShowGridCellHighlight,
onShowGridLineNamesHighlight,
onToggleGridHighlighter,
onToggleShowGridAreas,
onToggleShowGridLineNumbers,
onToggleShowInfiniteLines,
@@ -79,16 +80,17 @@ LayoutView.prototype = {
/**
* Shows the grid outline if user preferences are set to true, otherwise, hidden by
* default.
*/
showGridOutline: Services.prefs.getBoolPref(SHOW_GRID_OUTLINE_PREF),
onHideBoxModelHighlighter,
onPromoteLearnMoreClick,
+ onScrollToGridCellHighlight,
onSetGridOverlayColor,
onShowBoxModelEditor,
onShowBoxModelHighlighter,
onShowBoxModelHighlighterForNode,
onShowGridAreaHighlight,
onShowGridCellHighlight,
onShowGridLineNamesHighlight,
onToggleGeometryEditor,
--- a/devtools/client/themes/layout.css
+++ b/devtools/client/themes/layout.css
@@ -149,16 +149,36 @@
vector-effect: non-scaling-stroke;
}
.grid-outline-cell:hover {
opacity: 0.45;
fill: currentColor;
}
+.grid-outline-context-menu {
+ position: fixed;
+ background: white;
+ border-radius: 3px 3px 3px 3px;
+ box-shadow: 0px 2px 10px #999999;
+}
+
+.grid-outline-context-menu-item {
+ border-radius: 3px 3px 3px 3px;
+ padding: 6px 50px 5px 10px;
+ min-width: 160px;
+ cursor: pointer;
+ font-size: 12px;
+}
+
+.grid-outline-context-menu-item:hover {
+ background: linear-gradient(to top, #555, #333);
+ color: white;
+}
+
.grid-outline-line {
opacity: 0;
stroke-width: 10;
}
.grid-outline-text {
display: flex;
align-items: center;
--- a/devtools/server/actors/highlighters/css-grid.js
+++ b/devtools/server/actors/highlighters/css-grid.js
@@ -717,16 +717,61 @@ CssGridHighlighter.prototype = extend(Au
return this._update();
},
_clearCache() {
gCachedGridPattern.clear();
},
/**
+ * Scrolls to the grid cell highlight for the given grid cell options.
+ *
+ * @param {Number} options.gridFragmentIndex
+ * Index of the grid fragment to scroll to grid cell highlight.
+ * @param {Number} options.rowNumber
+ * Row number of the grid cell to scroll to.
+ * @param {Number} options.columnNumber
+ * Column number of the grid cell to scroll to.
+ */
+ scrollToGridCell({ gridFragmentIndex, rowNumber, columnNumber }) {
+ let fragment = this.gridData[gridFragmentIndex];
+ let displayPixelRatio = getDisplayPixelRatio(this.win);
+
+ if (!fragment) {
+ return;
+ }
+
+ let row = fragment.rows.tracks[rowNumber - 1];
+ let column = fragment.cols.tracks[columnNumber - 1];
+
+ if (!row || !column) {
+ return;
+ }
+
+ let currentZoom = getCurrentZoom(this.win);
+ let x1 = column.start;
+ let y1 = row.start;
+ let x2 = column.start + column.breadth;
+ let y2 = row.start + row.breadth;
+
+ let points = getPointsFromDiagonal(x1, y1, x2, y2, this.currentMatrix);
+
+ let bounds = getBoundsFromPoints(points.map(point => ({
+ x: Math.round(point.x / displayPixelRatio),
+ y: Math.round(point.y / displayPixelRatio)
+ })));
+
+ const { x, y } = bounds;
+ const xPos = x / currentZoom;
+ const yPos = y / currentZoom;
+
+ this.win.scrollTo(xPos * displayPixelRatio, (yPos - 100) * displayPixelRatio);
+ },
+
+ /**
* Shows the grid area highlight for the given area name.
*
* @param {String} areaName
* Grid area name.
*/
showGridArea(areaName) {
this.renderGridArea(areaName);
},
@@ -863,16 +908,20 @@ CssGridHighlighter.prototype = extend(Au
this.showAllGridAreas();
} else if (this.options.showGridArea) {
this.showGridArea(this.options.showGridArea);
}
// Display the grid cell highlights if needed.
if (this.options.showGridCell) {
this.showGridCell(this.options.showGridCell);
+
+ if (this.options.scrollToGridCell) {
+ this.scrollToGridCell(this.options.showGridCell);
+ }
}
// Display the grid line names if needed.
if (this.options.showGridLineNames) {
this.showGridLineNames(this.options.showGridLineNames);
}
this._showGrid();