324 lines
9.9 KiB
JavaScript
324 lines
9.9 KiB
JavaScript
/* 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.mjs");
|
|
const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
|
|
const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.mjs");
|
|
const {
|
|
getStr,
|
|
} = require("resource://devtools/client/inspector/layout/utils/l10n.js");
|
|
|
|
const Types = require("resource://devtools/client/inspector/flexbox/types.js");
|
|
|
|
const getFlexibilityReasons = ({
|
|
lineGrowthState,
|
|
computedFlexGrow,
|
|
computedFlexShrink,
|
|
grew,
|
|
shrank,
|
|
}) => {
|
|
const reasons = [];
|
|
|
|
// Tell users whether the item was set to grow or shrink.
|
|
if (computedFlexGrow && lineGrowthState === "growing") {
|
|
reasons.push(getStr("flexbox.itemSizing.setToGrow"));
|
|
}
|
|
if (computedFlexShrink && lineGrowthState === "shrinking") {
|
|
reasons.push(getStr("flexbox.itemSizing.setToShrink"));
|
|
}
|
|
if (!computedFlexGrow && !grew && !shrank && lineGrowthState === "growing") {
|
|
reasons.push(getStr("flexbox.itemSizing.notSetToGrow"));
|
|
}
|
|
if (
|
|
!computedFlexShrink &&
|
|
!grew &&
|
|
!shrank &&
|
|
lineGrowthState === "shrinking"
|
|
) {
|
|
reasons.push(getStr("flexbox.itemSizing.notSetToShrink"));
|
|
}
|
|
|
|
return reasons;
|
|
};
|
|
|
|
class FlexItemSizingProperties extends PureComponent {
|
|
static get propTypes() {
|
|
return {
|
|
flexDirection: PropTypes.string.isRequired,
|
|
flexItem: PropTypes.shape(Types.flexItem).isRequired,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Rounds some size in pixels and render it.
|
|
* The rendered value will end with 'px' (unless the dimension is 0 in which case the
|
|
* unit will be omitted)
|
|
*
|
|
* @param {Number} value
|
|
* The number to be rounded
|
|
* @param {Boolean} prependPlusSign
|
|
* If set to true, the + sign will be printed before a positive value
|
|
* @return {Object}
|
|
* The React component representing this rounded size
|
|
*/
|
|
renderSize(value, prependPlusSign) {
|
|
if (value == 0) {
|
|
return dom.span({ className: "value" }, "0");
|
|
}
|
|
|
|
value = Math.round(value * 100) / 100;
|
|
if (prependPlusSign && value > 0) {
|
|
value = "+" + value;
|
|
}
|
|
|
|
return dom.span(
|
|
{ className: "value" },
|
|
value,
|
|
dom.span({ className: "unit" }, "px")
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Render an authored CSS property.
|
|
*
|
|
* @param {String} name
|
|
* The name for this CSS property
|
|
* @param {String} value
|
|
* The property value
|
|
* @return {Object}
|
|
* The React component representing this CSS property
|
|
*/
|
|
renderCssProperty(name, value) {
|
|
return dom.span({ className: "css-property-link" }, `(${name}: ${value})`);
|
|
}
|
|
|
|
/**
|
|
* Render a list of sentences to be displayed in the UI as reasons why a certain sizing
|
|
* value happened.
|
|
*
|
|
* @param {Array} sentences
|
|
* The list of sentences as Strings
|
|
* @return {Object}
|
|
* The React component representing these sentences
|
|
*/
|
|
renderReasons(sentences) {
|
|
return dom.ul(
|
|
{ className: "reasons" },
|
|
sentences.map(sentence => dom.li({}, sentence))
|
|
);
|
|
}
|
|
|
|
renderBaseSizeSection({ mainBaseSize }, properties, dimension) {
|
|
const flexBasisValue = properties["flex-basis"];
|
|
const dimensionValue = properties[dimension];
|
|
|
|
let title = getStr("flexbox.itemSizing.baseSizeSectionHeader");
|
|
let property = null;
|
|
|
|
if (flexBasisValue) {
|
|
// If flex-basis is defined, then that's what is used for the base size.
|
|
property = this.renderCssProperty("flex-basis", flexBasisValue);
|
|
} else if (dimensionValue) {
|
|
// If not and width/height is defined, then that's what defines the base size.
|
|
property = this.renderCssProperty(dimension, dimensionValue);
|
|
} else {
|
|
// Finally, if nothing is set, then the base size is the max-content size.
|
|
// In this case replace the section's title.
|
|
title = getStr("flexbox.itemSizing.itemContentSize");
|
|
}
|
|
|
|
const className = "section base";
|
|
return dom.li(
|
|
{ className: className + (property ? "" : " no-property") },
|
|
dom.span({ className: "name" }, title, property),
|
|
this.renderSize(mainBaseSize)
|
|
);
|
|
}
|
|
|
|
renderFlexibilitySection(
|
|
flexItemSizing,
|
|
mainFinalSize,
|
|
properties,
|
|
computedStyle
|
|
) {
|
|
const { mainDeltaSize, mainBaseSize, lineGrowthState } = flexItemSizing;
|
|
|
|
// Don't display anything if all interesting sizes are 0.
|
|
if (!mainFinalSize && !mainBaseSize && !mainDeltaSize) {
|
|
return null;
|
|
}
|
|
|
|
// Also don't display anything if the item did not grow or shrink.
|
|
const grew = mainDeltaSize > 0;
|
|
const shrank = mainDeltaSize < 0;
|
|
if (!grew && !shrank) {
|
|
return null;
|
|
}
|
|
|
|
const definedFlexGrow = properties["flex-grow"];
|
|
const computedFlexGrow = computedStyle.flexGrow;
|
|
const definedFlexShrink = properties["flex-shrink"];
|
|
const computedFlexShrink = computedStyle.flexShrink;
|
|
|
|
const reasons = getFlexibilityReasons({
|
|
lineGrowthState,
|
|
computedFlexGrow,
|
|
computedFlexShrink,
|
|
grew,
|
|
shrank,
|
|
});
|
|
|
|
let property = null;
|
|
|
|
if (grew && definedFlexGrow && computedFlexGrow) {
|
|
// If the item grew it's normally because it was set to grow (flex-grow is non 0).
|
|
property = this.renderCssProperty("flex-grow", definedFlexGrow);
|
|
} else if (shrank && definedFlexShrink && computedFlexShrink) {
|
|
// If the item shrank it's either because flex-shrink is non 0.
|
|
property = this.renderCssProperty("flex-shrink", definedFlexShrink);
|
|
} else if (shrank && computedFlexShrink) {
|
|
// Or also because it's default value is 1 anyway.
|
|
property = this.renderCssProperty(
|
|
"flex-shrink",
|
|
computedFlexShrink,
|
|
true
|
|
);
|
|
}
|
|
|
|
// Don't display the section at all if there's nothing useful to show users.
|
|
if (!property && !reasons.length) {
|
|
return null;
|
|
}
|
|
|
|
const className = "section flexibility";
|
|
return dom.li(
|
|
{ className: className + (property ? "" : " no-property") },
|
|
dom.span(
|
|
{ className: "name" },
|
|
getStr("flexbox.itemSizing.flexibilitySectionHeader"),
|
|
property
|
|
),
|
|
this.renderSize(mainDeltaSize, true),
|
|
this.renderReasons(reasons)
|
|
);
|
|
}
|
|
|
|
renderMinimumSizeSection(flexItemSizing, properties, dimension) {
|
|
const { clampState, mainMinSize, mainDeltaSize } = flexItemSizing;
|
|
const grew = mainDeltaSize > 0;
|
|
const shrank = mainDeltaSize < 0;
|
|
const minDimensionValue = properties[`min-${dimension}`];
|
|
|
|
// We only display the minimum size when the item actually violates that size during
|
|
// layout & is clamped.
|
|
if (clampState !== "clamped_to_min") {
|
|
return null;
|
|
}
|
|
|
|
const reasons = [];
|
|
if (grew || shrank) {
|
|
// The item may have wanted to grow less, but was min-clamped to a larger size.
|
|
// Or the item may have wanted to shrink more but was min-clamped to a larger size.
|
|
reasons.push(getStr("flexbox.itemSizing.clampedToMin"));
|
|
}
|
|
|
|
return dom.li(
|
|
{ className: "section min" },
|
|
dom.span(
|
|
{ className: "name" },
|
|
getStr("flexbox.itemSizing.minSizeSectionHeader"),
|
|
minDimensionValue.length
|
|
? this.renderCssProperty(`min-${dimension}`, minDimensionValue)
|
|
: null
|
|
),
|
|
this.renderSize(mainMinSize),
|
|
this.renderReasons(reasons)
|
|
);
|
|
}
|
|
|
|
renderMaximumSizeSection(flexItemSizing, properties, dimension) {
|
|
const { clampState, mainMaxSize, mainDeltaSize } = flexItemSizing;
|
|
const grew = mainDeltaSize > 0;
|
|
const shrank = mainDeltaSize < 0;
|
|
const maxDimensionValue = properties[`max-${dimension}`];
|
|
|
|
if (clampState !== "clamped_to_max") {
|
|
return null;
|
|
}
|
|
|
|
const reasons = [];
|
|
if (grew || shrank) {
|
|
// The item may have wanted to grow more than it did, because it was max-clamped.
|
|
// Or the item may have wanted shrink more, but it was clamped to its max size.
|
|
reasons.push(getStr("flexbox.itemSizing.clampedToMax"));
|
|
}
|
|
|
|
return dom.li(
|
|
{ className: "section max" },
|
|
dom.span(
|
|
{ className: "name" },
|
|
getStr("flexbox.itemSizing.maxSizeSectionHeader"),
|
|
maxDimensionValue.length
|
|
? this.renderCssProperty(`max-${dimension}`, maxDimensionValue)
|
|
: null
|
|
),
|
|
this.renderSize(mainMaxSize),
|
|
this.renderReasons(reasons)
|
|
);
|
|
}
|
|
|
|
renderFinalSizeSection(mainFinalSize) {
|
|
return dom.li(
|
|
{ className: "section final no-property" },
|
|
dom.span(
|
|
{ className: "name" },
|
|
getStr("flexbox.itemSizing.finalSizeSectionHeader")
|
|
),
|
|
this.renderSize(mainFinalSize)
|
|
);
|
|
}
|
|
|
|
render() {
|
|
const { flexItem } = this.props;
|
|
const { computedStyle, flexItemSizing, properties } = flexItem;
|
|
const {
|
|
mainAxisDirection,
|
|
mainBaseSize,
|
|
mainDeltaSize,
|
|
mainMaxSize,
|
|
mainMinSize,
|
|
} = flexItemSizing;
|
|
const dimension = mainAxisDirection.startsWith("horizontal")
|
|
? "width"
|
|
: "height";
|
|
|
|
// 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);
|
|
|
|
return dom.ul(
|
|
{ className: "flex-item-sizing" },
|
|
this.renderBaseSizeSection(flexItemSizing, properties, dimension),
|
|
this.renderFlexibilitySection(
|
|
flexItemSizing,
|
|
mainFinalSize,
|
|
properties,
|
|
computedStyle
|
|
),
|
|
this.renderMinimumSizeSection(flexItemSizing, properties, dimension),
|
|
this.renderMaximumSizeSection(flexItemSizing, properties, dimension),
|
|
this.renderFinalSizeSection(mainFinalSize)
|
|
);
|
|
}
|
|
}
|
|
|
|
module.exports = FlexItemSizingProperties;
|