Bug 1343447 - Rebase maintain aspect ratio patch. r?pbro draft
authorMicah Tigley <tigleym@gmail.com>
Fri, 24 Mar 2017 14:01:09 -0600
changeset 526791 7facc8e5232c31f815cde5e817844277ddd89c0d
parent 504632 4c987b7ed54a630a7de76adcc2eb00dab49d5dfd
child 550739 f365c1c45419e1f079af1040467befe330f6c822
push id50929
push userbmo:tigleym@gmail.com
push dateFri, 24 Mar 2017 20:03:05 +0000
reviewerspbro
bugs1343447
milestone55.0a1
Bug 1343447 - Rebase maintain aspect ratio patch. r?pbro MozReview-Commit-ID: 97mdXIyDhSw
devtools/client/inspector/grids/components/GridOutline.js
devtools/client/inspector/grids/grid-inspector.js
devtools/client/themes/layout.css
--- a/devtools/client/inspector/grids/components/GridOutline.js
+++ b/devtools/client/inspector/grids/components/GridOutline.js
@@ -10,48 +10,58 @@ const { throttle } = require("devtools/c
 
 const Types = require("../types");
 
 // The delay prior to executing the grid cell highlighting.
 const GRID_CELL_MOUSEOVER_TIMEOUT = 150;
 
 // Move SVG grid to the right 100 units, so that it is not flushed against the edge of
 // layout border
-const TRANSLATE_X = -100;
+const TRANSLATE_X = 0;
 const TRANSLATE_Y = 0;
 
-const VIEWPORT_HEIGHT = 100;
-const VIEWPORT_WIDTH = 450;
+const GRID_CELL_SCALE_FACTOR = 50;
+
+const VIEWPORT_MIN_HEIGHT = 100;
+const VIEWPORT_MAX_HEIGHT = 150;
 
 module.exports = createClass({
 
   displayName: "GridOutline",
 
   propTypes: {
     grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
     onShowGridAreaHighlight: PropTypes.func.isRequired,
     onShowGridCellHighlight: PropTypes.func.isRequired,
   },
 
   mixins: [ addons.PureRenderMixin ],
 
   getInitialState() {
     return {
       selectedGrids: [],
+      height: 0,
+      width: 0,
     };
   },
 
   componentWillMount() {
     // Throttle the grid highlighting of grid cells. It makes the UX smoother by not
     // lagging the grid cell highlighting if a lot of grid cells are mouseover in a
     // quick succession.
     this.highlightCell = throttle(this.highlightCell, GRID_CELL_MOUSEOVER_TIMEOUT);
   },
 
   componentWillReceiveProps({ grids }) {
+    if (this.state.selectedGrids.length < 2) {
+      this.setState({
+        height: 0,
+        width: 0,
+      });
+    }
     this.setState({
       selectedGrids: grids.filter(grid => grid.highlighted),
     });
   },
 
   /**
    * Returns the grid area name if the given grid cell is part of a grid area, otherwise
    * null.
@@ -72,16 +82,33 @@ module.exports = createClass({
 
     if (!gridArea) {
       return null;
     }
 
     return gridArea.name;
   },
 
+  /**
+   * Returns the height of the grid outline ranging between a minimum and maximum height.
+   *
+   * @return {Number} The height of the grid outline.
+   */
+  getHeight() {
+    const { height } = this.state;
+
+    if (height >= VIEWPORT_MAX_HEIGHT) {
+      return VIEWPORT_MAX_HEIGHT;
+    } else if (height <= VIEWPORT_MIN_HEIGHT) {
+      return VIEWPORT_MIN_HEIGHT;
+    }
+
+    return height;
+  },
+
   highlightCell({ target }) {
     const {
       grids,
       onShowGridAreaHighlight,
       onShowGridCellHighlight,
     } = this.props;
     const name = target.getAttribute("data-grid-area-name");
     const id = target.getAttribute("data-grid-id");
@@ -92,61 +119,83 @@ module.exports = createClass({
 
     target.setAttribute("fill", color);
 
     if (name) {
       onShowGridAreaHighlight(grids[id].nodeFront, name, color);
     }
 
     if (fragmentIndex && rowNumber && columnNumber) {
-      onShowGridCellHighlight(grids[id].nodeFront, fragmentIndex,
+      onShowGridCellHighlight(grids[id].nodeFront, color, fragmentIndex,
         rowNumber, columnNumber);
     }
   },
 
-  /**
-   * Renders the grid outline for the given grid container object.
-   *
-   * @param  {Object} grid
-   *         A single grid container in the document.
-   */
+    /**
+      * Renders the grid outline for the given grid container object.
+      *
+      * @param  {Object} grid
+      *         A single grid container in the document.
+      */
   renderGrid(grid) {
     const { id, color, gridFragments } = grid;
     // TODO: We are drawing the first fragment since only one is currently being stored.
     // In the future we will need to iterate over all fragments of a grid.
     let gridFragmentIndex = 0;
     const { rows, cols, areas } = gridFragments[gridFragmentIndex];
     const numberOfColumns = cols.lines.length - 1;
     const numberOfRows = rows.lines.length - 1;
     const rectangles = [];
-
-    // Draw a rectangle that acts as a border for the grid outline.
-    const border = this.renderGridOutlineBorder(numberOfRows, numberOfColumns, color);
-    rectangles.push(border);
-
     let x = 1;
     let y = 1;
-    const width = 10;
-    const height = 10;
+    let width = 0;
+    let height = 0;
+    // The grid outline border height/width is the total height/width of grid cells drawn.
+    let totalHeight = 0;
+    let totalWidth = 0;
 
-    // Draw the cells within the grid outline border.
+      // Draw the cells contained within the grid outline border.
     for (let rowNumber = 1; rowNumber <= numberOfRows; rowNumber++) {
+      height = GRID_CELL_SCALE_FACTOR * (rows.tracks[rowNumber - 1].breadth / 100);
       for (let columnNumber = 1; columnNumber <= numberOfColumns; columnNumber++) {
+        width = GRID_CELL_SCALE_FACTOR * (cols.tracks[columnNumber - 1].breadth / 100);
+
         const gridAreaName = this.getGridAreaName(columnNumber, rowNumber, areas);
-        const gridCell = this.renderGridCell(id, gridFragmentIndex, x, y, rowNumber,
-          columnNumber, color, gridAreaName);
+        const gridCell = this.renderGridCell(id, gridFragmentIndex, x, y,
+            rowNumber, columnNumber, color, gridAreaName, width, height);
 
         rectangles.push(gridCell);
         x += width;
       }
 
       x = 1;
       y += height;
+      totalHeight += height;
     }
 
+    // Find the total width of the grid container so we can draw the border for it
+    for (let columnNumber = 0; columnNumber < numberOfColumns; columnNumber++) {
+      totalWidth += GRID_CELL_SCALE_FACTOR * (cols.tracks[columnNumber].breadth / 100);
+    }
+
+    // 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 viewbox
+    // is only the calculated width of the grid outline.
+    if (totalHeight > this.state.height || totalWidth > this.state.width) {
+      this.setState({
+        height: totalHeight + 20,
+        width: totalWidth,
+      });
+    }
+
+    // Draw a rectangle that acts as the grid outline border.
+    const border = this.renderGridOutlineBorder(totalWidth, totalHeight, color);
+    rectangles.unshift(border);
+
     return rectangles;
   },
 
   /**
    * Renders the grid cell of a grid fragment.
    *
    * @param  {Number} id
    *         The grid id stored on the grid fragment
@@ -157,31 +206,35 @@ module.exports = createClass({
    * @param  {Number} y
    *         The y-position of the grid cell.
    * @param  {Number} rowNumber
    *         The row number of the grid cell.
    * @param  {Number} columnNumber
    *         The column number of the grid cell.
    * @param  {String|null} gridAreaName
    *         The grid area name or null if the grid cell is not part of a grid area.
+   * @param  {Number} width
+   *         The width of grid cell.
+   * @param  {Number} height
+   *         The height of the grid cell.
    */
   renderGridCell(id, gridFragmentIndex, x, y, rowNumber, columnNumber, color,
-    gridAreaName) {
+    gridAreaName, width, height) {
     return dom.rect(
       {
         className: "grid-outline-cell",
         "data-grid-area-name": gridAreaName,
         "data-grid-fragment-index": gridFragmentIndex,
         "data-grid-id": id,
         "data-grid-row": rowNumber,
         "data-grid-column": columnNumber,
         x,
         y,
-        width: 10,
-        height: 10,
+        width,
+        height,
         fill: "none",
         stroke: color,
         onMouseOver: this.onMouseOverCell,
         onMouseOut: this.onMouseLeaveCell,
       }
     );
   },
 
@@ -189,24 +242,24 @@ module.exports = createClass({
     return dom.g(
       {
         className: "grid-cell-group",
       },
       grids.map(grid => this.renderGrid(grid))
     );
   },
 
-  renderGridOutlineBorder(numberOfRows, numberOfColumns, color) {
+  renderGridOutlineBorder(borderWidth, borderHeight, color) {
     return dom.rect(
       {
         className: "grid-outline-border",
         x: 1,
         y: 1,
-        width: numberOfColumns * 10,
-        height: numberOfRows * 10,
+        width: borderWidth,
+        height: borderHeight,
         stroke: color,
       }
     );
   },
 
   onMouseLeaveCell({ target }) {
     const {
       grids,
@@ -214,32 +267,34 @@ module.exports = createClass({
       onShowGridCellHighlight,
     } = this.props;
     const id = target.getAttribute("data-grid-id");
     const color = target.getAttribute("stroke");
 
     target.setAttribute("fill", "none");
 
     onShowGridAreaHighlight(grids[id].nodeFront, null, color);
-    onShowGridCellHighlight(grids[id].nodeFront);
+    onShowGridCellHighlight(grids[id].nodeFront, color);
   },
 
   onMouseOverCell(event) {
     event.persist();
     this.highlightCell(event);
   },
 
   render() {
-    return this.state.selectedGrids.length ?
+    const { selectedGrids, height, width } = this.state;
+
+    return selectedGrids.length ?
       dom.svg(
         {
           className: "grid-outline",
           width: "100%",
-          height: 100,
-          viewBox: `${TRANSLATE_X} ${TRANSLATE_Y} ${VIEWPORT_WIDTH} ${VIEWPORT_HEIGHT}`,
+          height: this.getHeight(),
+          viewBox: `${TRANSLATE_X} ${TRANSLATE_Y} ${width} ${height}`,
         },
-        this.renderGridOutline(this.state.selectedGrids)
+        this.renderGridOutline(selectedGrids)
       )
       :
       null;
   },
 
 });
--- a/devtools/client/inspector/grids/grid-inspector.js
+++ b/devtools/client/inspector/grids/grid-inspector.js
@@ -369,29 +369,34 @@ GridInspector.prototype = {
   },
 
   /**
    * Highlights 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.
    */
-  onShowGridCellHighlight(node, gridFragmentIndex, rowNumber, columnNumber) {
+  onShowGridCellHighlight(node, color, gridFragmentIndex, rowNumber, columnNumber) {
     let { highlighterSettings } = this.store.getState();
+
     highlighterSettings.showGridCell = { gridFragmentIndex, rowNumber, columnNumber };
+    highlighterSettings.color = color;
 
     this.highlighters.showGridHighlighter(node, highlighterSettings);
   },
 
   /**
    * Handler for the inspector sidebar select event. Starts listening for
    * "grid-layout-changed" if the layout panel is visible. Otherwise, stop
    * listening for grid layout changes. Finally, refresh the layout view if
--- a/devtools/client/themes/layout.css
+++ b/devtools/client/themes/layout.css
@@ -58,23 +58,25 @@
  * Grid Outline
  */
 
 #grid-outline {
   margin: 5px auto;
 }
 
 .grid-outline-border {
+  fill: none;
   stroke-width: 0.75;
-  fill: none;
+  vector-effect: non-scaling-stroke;
 }
 
 .grid-outline-cell {
   pointer-events: all;
   stroke-dasharray: 0.5, 2;
+  vector-effect: non-scaling-stroke;
 }
 
 .grid-outline-cell:hover {
   opacity: 0.45;
 }
 
 /**
  * Container when no grids are present