summaryrefslogtreecommitdiffstats
path: root/devtools/client/inspector/flexbox/components/FlexItemSizingOutline.js
blob: f0c35cd6bec672358959cb7bd957d56e130ccbd1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/* 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 {
  PureComponent,
} = require("resource://devtools/client/shared/vendor/react.js");
const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");

const Types = require("resource://devtools/client/inspector/flexbox/types.js");

class FlexItemSizingOutline extends PureComponent {
  static get propTypes() {
    return {
      flexDirection: PropTypes.string.isRequired,
      flexItem: PropTypes.shape(Types.flexItem).isRequired,
    };
  }

  renderBasisOutline(mainBaseSize) {
    return dom.div({
      className: "flex-outline-basis" + (!mainBaseSize ? " zero-basis" : ""),
    });
  }

  renderDeltaOutline(mainDeltaSize) {
    if (!mainDeltaSize) {
      return null;
    }

    return dom.div({
      className: "flex-outline-delta",
    });
  }

  renderFinalOutline(isClamped) {
    return dom.div({
      className: "flex-outline-final" + (isClamped ? " clamped" : ""),
    });
  }

  renderPoint(className, label = className) {
    return dom.div({
      key: className,
      className: `flex-outline-point ${className}`,
      "data-label": label,
    });
  }

  render() {
    const { flexItemSizing } = this.props.flexItem;
    const {
      mainAxisDirection,
      mainBaseSize,
      mainDeltaSize,
      mainMaxSize,
      mainMinSize,
      clampState,
    } = flexItemSizing;

    // Calculate the final size. This is base + delta, then clamped by min or max.
    let mainFinalSize = mainBaseSize + mainDeltaSize;
    mainFinalSize = Math.max(mainFinalSize, mainMinSize);
    mainFinalSize =
      mainMaxSize === null
        ? mainFinalSize
        : Math.min(mainFinalSize, mainMaxSize);

    // Just don't display anything if there isn't anything useful.
    if (!mainFinalSize && !mainBaseSize && !mainDeltaSize) {
      return null;
    }

    // The max size is only interesting to show if it did clamp the item.
    const showMax = clampState === "clamped_to_max";

    // The min size is only really interesting if it actually clamped the item.
    // TODO: We might also want to check if the min-size property is defined.
    const showMin = clampState === "clamped_to_min";

    // Create an array of all of the sizes we want to display that we can use to create
    // a grid track template.
    let sizes = [
      { name: "basis-start", size: 0 },
      { name: "basis-end", size: mainBaseSize },
      { name: "final-start", size: 0 },
      { name: "final-end", size: mainFinalSize },
    ];

    // Because mainDeltaSize is just a delta from base, make sure to make it absolute like
    // the other sizes in the array, so we can later sort all sizes in the same way.
    if (mainDeltaSize > 0) {
      sizes.push({ name: "delta-start", size: mainBaseSize });
      sizes.push({ name: "delta-end", size: mainBaseSize + mainDeltaSize });
    } else {
      sizes.push({ name: "delta-start", size: mainBaseSize + mainDeltaSize });
      sizes.push({ name: "delta-end", size: mainBaseSize });
    }

    if (showMax) {
      sizes.push({ name: "max", size: mainMaxSize });
    }
    if (showMin) {
      sizes.push({ name: "min", size: mainMinSize });
    }

    // Sort all of the dimensions so we can create the grid track template correctly.
    sizes = sizes.sort((a, b) => a.size - b.size);

    // In some cases, the delta-start may be negative (when an item wanted to shrink more
    // than the item's base size). As a negative value would break the grid track template
    // offset all values so they're all positive.
    const offsetBy = sizes.reduce(
      (acc, curr) => (curr.size < acc ? curr.size : acc),
      0
    );
    sizes = sizes.map(entry => ({
      size: entry.size - offsetBy,
      name: entry.name,
    }));

    let gridTemplateColumns = "[";
    let accumulatedSize = 0;
    for (const { name, size } of sizes) {
      const breadth = Math.round(size - accumulatedSize);
      if (breadth === 0) {
        gridTemplateColumns += ` ${name}`;
        continue;
      }

      gridTemplateColumns += `] ${breadth}fr [${name}`;
      accumulatedSize = size;
    }
    gridTemplateColumns += "]";

    // Check the final and basis points to see if they are the same and if so, combine
    // them into a single rendered point.
    const renderedBaseAndFinalPoints = [];
    if (mainFinalSize === mainBaseSize) {
      renderedBaseAndFinalPoints.push(
        this.renderPoint("basisfinal", "basis/final")
      );
    } else {
      renderedBaseAndFinalPoints.push(this.renderPoint("basis"));
      renderedBaseAndFinalPoints.push(this.renderPoint("final"));
    }

    return dom.div(
      { className: "flex-outline-container" },
      dom.div(
        {
          className:
            `flex-outline ${mainAxisDirection}` +
            (mainDeltaSize > 0 ? " growing" : " shrinking"),
          style: {
            gridTemplateColumns,
          },
        },
        renderedBaseAndFinalPoints,
        showMin ? this.renderPoint("min") : null,
        showMax ? this.renderPoint("max") : null,
        this.renderBasisOutline(mainBaseSize),
        this.renderDeltaOutline(mainDeltaSize),
        this.renderFinalOutline(clampState !== "unclamped")
      )
    );
  }
}

module.exports = FlexItemSizingOutline;