diff --git a/devtools/client/inspector/flexbox/actions/flexbox.js b/devtools/client/inspector/flexbox/actions/flexbox.js index d1c15a0a688d..229ce7b4a498 100644 --- a/devtools/client/inspector/flexbox/actions/flexbox.js +++ b/devtools/client/inspector/flexbox/actions/flexbox.js @@ -6,7 +6,6 @@ const { CLEAR_FLEXBOX, - TOGGLE_FLEX_ITEM_SHOWN, UPDATE_FLEXBOX, UPDATE_FLEXBOX_COLOR, UPDATE_FLEXBOX_HIGHLIGHTED, @@ -23,21 +22,6 @@ module.exports = { }; }, - /** - * Toggles the display of flex item sizing information shown for the given flex item - * actor ID. - * - * @param {NodeFront} nodeFront - * The NodeFront of the flex item to toggle the sizing information displayed - * for. - */ - toggleFlexItemShown(nodeFront) { - return { - type: TOGGLE_FLEX_ITEM_SHOWN, - nodeFront, - }; - }, - /** * Updates the flexbox state with the newly selected flexbox. */ diff --git a/devtools/client/inspector/flexbox/actions/index.js b/devtools/client/inspector/flexbox/actions/index.js index dfab50946c5b..403e3d8d5d70 100644 --- a/devtools/client/inspector/flexbox/actions/index.js +++ b/devtools/client/inspector/flexbox/actions/index.js @@ -11,9 +11,6 @@ createEnum([ // Clears the flexbox state by resetting it back to the initial flexbox state. "CLEAR_FLEXBOX", - // Toggles the display of flex item sizing information shown. - "TOGGLE_FLEX_ITEM_SHOWN", - // Updates the flexbox state with the newly selected flexbox. "UPDATE_FLEXBOX", diff --git a/devtools/client/inspector/flexbox/components/FlexContainer.js b/devtools/client/inspector/flexbox/components/FlexContainer.js index 338fcb6e7ffb..2a11255e0d78 100644 --- a/devtools/client/inspector/flexbox/components/FlexContainer.js +++ b/devtools/client/inspector/flexbox/components/FlexContainer.js @@ -12,6 +12,7 @@ const { } = require("devtools/client/shared/vendor/react"); const dom = require("devtools/client/shared/vendor/react-dom-factories"); const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); +const { connect } = require("devtools/client/shared/vendor/react-redux"); const { translateNodeFrontToGrip } = require("devtools/client/inspector/shared/utils"); const { REPS, MODE } = require("devtools/client/shared/components/reps/reps"); @@ -23,7 +24,8 @@ const Types = require("../types"); class FlexContainer extends PureComponent { static get propTypes() { return { - flexbox: PropTypes.shape(Types.flexbox).isRequired, + color: PropTypes.string.isRequired, + flexContainer: PropTypes.shape(Types.flexContainer).isRequired, getSwatchColorPickerTooltip: PropTypes.func.isRequired, onHideBoxModelHighlighter: PropTypes.func.isRequired, onSetFlexboxOverlayColor: PropTypes.func.isRequired, @@ -43,23 +45,17 @@ class FlexContainer extends PureComponent { } componentDidMount() { - const { - flexbox, - getSwatchColorPickerTooltip, - onSetFlexboxOverlayColor, - } = this.props; - - const tooltip = getSwatchColorPickerTooltip(); + const tooltip = this.props.getSwatchColorPickerTooltip(); let previousColor; tooltip.addSwatch(this.swatchEl.current, { onCommit: this.setFlexboxColor, onPreview: this.setFlexboxColor, onRevert: () => { - onSetFlexboxOverlayColor(previousColor); + this.props.onSetFlexboxOverlayColor(previousColor); }, onShow: () => { - previousColor = flexbox.color; + previousColor = this.props.color; }, }); } @@ -75,21 +71,18 @@ class FlexContainer extends PureComponent { } onFlexboxInspectIconClick(nodeFront) { - const { setSelectedNode } = this.props; - setSelectedNode(nodeFront, { reason: "layout-panel" }); + this.props.setSelectedNode(nodeFront, { reason: "layout-panel" }); nodeFront.scrollIntoView().catch(e => console.error(e)); } render() { const { - flexbox, + color, + flexContainer, onHideBoxModelHighlighter, onShowBoxModelHighlighterForNode, } = this.props; - const { - color, - nodeFront, - } = flexbox; + const { nodeFront } = flexContainer; return createElement(Fragment, null, Rep({ @@ -123,4 +116,10 @@ class FlexContainer extends PureComponent { } } -module.exports = FlexContainer; +const mapStateToProps = state => { + return { + color: state.flexbox.color, + }; +}; + +module.exports = connect(mapStateToProps)(FlexContainer); diff --git a/devtools/client/inspector/flexbox/components/FlexItem.js b/devtools/client/inspector/flexbox/components/FlexItem.js index 37db8020d73a..00ec8757dcf4 100644 --- a/devtools/client/inspector/flexbox/components/FlexItem.js +++ b/devtools/client/inspector/flexbox/components/FlexItem.js @@ -23,23 +23,19 @@ class FlexItem extends PureComponent { static get propTypes() { return { flexItem: PropTypes.shape(Types.flexItem).isRequired, - onToggleFlexItemShown: PropTypes.func.isRequired, + setSelectedNode: PropTypes.func.isRequired, }; } render() { - const { - flexItem, - onToggleFlexItemShown, - } = this.props; - const { nodeFront } = flexItem; + const { nodeFront } = this.props.flexItem; return ( dom.li({}, dom.button( { className: "devtools-button devtools-monospace", - onClick: () => onToggleFlexItemShown(nodeFront), + onClick: () => this.props.setSelectedNode(nodeFront), }, Rep({ defaultRep: Rep.ElementNode, diff --git a/devtools/client/inspector/flexbox/components/FlexItemList.js b/devtools/client/inspector/flexbox/components/FlexItemList.js index 30bbe3fbbadc..f112bceda708 100644 --- a/devtools/client/inspector/flexbox/components/FlexItemList.js +++ b/devtools/client/inspector/flexbox/components/FlexItemList.js @@ -16,14 +16,14 @@ class FlexItemList extends PureComponent { static get propTypes() { return { flexItems: PropTypes.arrayOf(PropTypes.shape(Types.flexItem)).isRequired, - onToggleFlexItemShown: PropTypes.func.isRequired, + setSelectedNode: PropTypes.func.isRequired, }; } render() { const { flexItems, - onToggleFlexItemShown, + setSelectedNode, } = this.props; return ( @@ -32,7 +32,7 @@ class FlexItemList extends PureComponent { flexItems.map(flexItem => FlexItem({ key: flexItem.actorID, flexItem, - onToggleFlexItemShown, + setSelectedNode, })) ) ); diff --git a/devtools/client/inspector/flexbox/components/FlexItemSelector.js b/devtools/client/inspector/flexbox/components/FlexItemSelector.js index 65d2aa47c8d6..7ffc93f464e2 100644 --- a/devtools/client/inspector/flexbox/components/FlexItemSelector.js +++ b/devtools/client/inspector/flexbox/components/FlexItemSelector.js @@ -25,7 +25,7 @@ class FlexItemSelector extends PureComponent { return { flexItem: PropTypes.shape(Types.flexItem).isRequired, flexItems: PropTypes.arrayOf(PropTypes.shape(Types.flexItem)).isRequired, - onToggleFlexItemShown: PropTypes.func.isRequired, + setSelectedNode: PropTypes.func.isRequired, }; } @@ -38,7 +38,7 @@ class FlexItemSelector extends PureComponent { const { flexItem, flexItems, - onToggleFlexItemShown, + setSelectedNode, } = this.props; const menuItems = []; @@ -48,7 +48,7 @@ class FlexItemSelector extends PureComponent { label: getSelectorFromGrip(grip), type: "checkbox", checked: item === flexItem, - click: () => onToggleFlexItemShown(item.nodeFront), + click: () => setSelectedNode(item.nodeFront), }); } diff --git a/devtools/client/inspector/flexbox/components/Flexbox.js b/devtools/client/inspector/flexbox/components/Flexbox.js index d7a4a152cc60..5f7c5f0570f3 100644 --- a/devtools/client/inspector/flexbox/components/Flexbox.js +++ b/devtools/client/inspector/flexbox/components/Flexbox.js @@ -27,67 +27,68 @@ const Types = require("../types"); class Flexbox extends PureComponent { static get propTypes() { return { - flexbox: PropTypes.shape(Types.flexbox).isRequired, + flexContainer: PropTypes.shape(Types.flexContainer).isRequired, getSwatchColorPickerTooltip: PropTypes.func.isRequired, onHideBoxModelHighlighter: PropTypes.func.isRequired, onSetFlexboxOverlayColor: PropTypes.func.isRequired, onShowBoxModelHighlighterForNode: PropTypes.func.isRequired, onToggleFlexboxHighlighter: PropTypes.func.isRequired, - onToggleFlexItemShown: PropTypes.func.isRequired, setSelectedNode: PropTypes.func.isRequired, }; } - renderFlexItemList() { - const { - flexbox, - onToggleFlexItemShown, - } = this.props; - const { - flexItems, - flexItemShown, - } = flexbox; - - if (flexItemShown || !flexItems.length) { + renderFlexContainerProperties() { + // Don't show the flex container properties for the parent flex container of the + // selected element. + if (this.props.flexContainer.isFlexItemContainer) { return null; } - return FlexItemList({ - flexItems, - onToggleFlexItemShown, + return FlexContainerProperties({ + properties: this.props.flexContainer.properties, }); } - renderFlexItemSizingProperties() { - const { flexbox } = this.props; + renderFlexItemList() { + const { setSelectedNode } = this.props; + const { flexItems } = this.props.flexContainer; + + return FlexItemList({ + flexItems, + setSelectedNode, + }); + } + + renderFlexItemSizing() { const { flexItems, flexItemShown, - } = flexbox; + properties, + } = this.props.flexContainer; - if (!flexItemShown) { + const flexItem = flexItems.find(item => item.nodeFront.actorID === flexItemShown); + if (!flexItem) { return null; } return FlexItemSizingProperties({ - flexDirection: flexbox.properties["flex-direction"], - flexItem: flexItems.find(item => item.nodeFront.actorID === flexItemShown), + flexDirection: properties["flex-direction"], + flexItem, }); } render() { const { - flexbox, + flexContainer, getSwatchColorPickerTooltip, onHideBoxModelHighlighter, onSetFlexboxOverlayColor, onShowBoxModelHighlighterForNode, onToggleFlexboxHighlighter, - onToggleFlexItemShown, setSelectedNode, } = this.props; - if (!flexbox.actorID) { + if (!flexContainer.actorID) { return ( dom.div({ className: "devtools-sidepanel-no-result" }, getStr("flexbox.noFlexboxeOnThisPage") @@ -95,23 +96,25 @@ class Flexbox extends PureComponent { ); } + const { + flexItems, + flexItemShown, + } = flexContainer; + return ( dom.div({ id: "layout-flexbox-container" }, Header({ - flexbox, + flexContainer, getSwatchColorPickerTooltip, onHideBoxModelHighlighter, onSetFlexboxOverlayColor, onShowBoxModelHighlighterForNode, onToggleFlexboxHighlighter, - onToggleFlexItemShown, setSelectedNode, }), - this.renderFlexItemList(), - this.renderFlexItemSizingProperties(), - FlexContainerProperties({ - properties: flexbox.properties, - }) + !flexItemShown && flexItems.length > 0 ? this.renderFlexItemList() : null, + flexItemShown ? this.renderFlexItemSizing() : null, + this.renderFlexContainerProperties() ) ); } diff --git a/devtools/client/inspector/flexbox/components/Header.js b/devtools/client/inspector/flexbox/components/Header.js index cf02913e197e..4b9e7c4d7938 100644 --- a/devtools/client/inspector/flexbox/components/Header.js +++ b/devtools/client/inspector/flexbox/components/Header.js @@ -4,9 +4,15 @@ "use strict"; -const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react"); +const { + createElement, + createFactory, + Fragment, + PureComponent, +} = require("devtools/client/shared/vendor/react"); const dom = require("devtools/client/shared/vendor/react-dom-factories"); const PropTypes = require("devtools/client/shared/vendor/react-prop-types"); +const { connect } = require("devtools/client/shared/vendor/react-redux"); const FlexContainer = createFactory(require("./FlexContainer")); const FlexItemSelector = createFactory(require("./FlexItemSelector")); @@ -16,13 +22,13 @@ const Types = require("../types"); class Header extends PureComponent { static get propTypes() { return { - flexbox: PropTypes.shape(Types.flexbox).isRequired, + flexContainer: PropTypes.shape(Types.flexContainer).isRequired, getSwatchColorPickerTooltip: PropTypes.func.isRequired, + highlighted: PropTypes.bool.isRequired, onHideBoxModelHighlighter: PropTypes.func.isRequired, onSetFlexboxOverlayColor: PropTypes.func.isRequired, onShowBoxModelHighlighterForNode: PropTypes.func.isRequired, onToggleFlexboxHighlighter: PropTypes.func.isRequired, - onToggleFlexItemShown: PropTypes.func.isRequired, setSelectedNode: PropTypes.func.isRequired, }; } @@ -33,21 +39,34 @@ class Header extends PureComponent { } onFlexboxCheckboxClick() { - const { - flexbox, - onToggleFlexboxHighlighter, - } = this.props; + this.props.onToggleFlexboxHighlighter(this.props.flexContainer.nodeFront); + } - onToggleFlexboxHighlighter(flexbox.nodeFront); + renderFlexboxHighlighterToggle() { + // Don't show the flexbox highlighter toggle for the parent flex container of the + // selected element. + if (this.props.flexContainer.isFlexItemContainer) { + return null; + } + + return createElement(Fragment, null, + dom.div({ className: "devtools-separator" }), + dom.input({ + className: "devtools-checkbox-toggle", + checked: this.props.highlighted, + onChange: this.onFlexboxCheckboxClick, + type: "checkbox", + }) + ); } renderFlexContainer() { - if (this.props.flexbox.flexItemShown) { + if (this.props.flexContainer.flexItemShown) { return null; } const { - flexbox, + flexContainer, getSwatchColorPickerTooltip, onHideBoxModelHighlighter, onSetFlexboxOverlayColor, @@ -56,7 +75,7 @@ class Header extends PureComponent { } = this.props; return FlexContainer({ - flexbox, + flexContainer, getSwatchColorPickerTooltip, onHideBoxModelHighlighter, onSetFlexboxOverlayColor, @@ -66,42 +85,42 @@ class Header extends PureComponent { } renderFlexItemSelector() { - if (!this.props.flexbox.flexItemShown) { + if (!this.props.flexContainer.flexItemShown) { return null; } const { - flexbox, - onToggleFlexItemShown, + flexContainer, + setSelectedNode, } = this.props; const { flexItems, flexItemShown, - } = flexbox; + } = flexContainer; return FlexItemSelector({ flexItem: flexItems.find(item => item.nodeFront.actorID === flexItemShown), flexItems, - onToggleFlexItemShown, + setSelectedNode, }); } render() { const { - flexbox, - onToggleFlexItemShown, + flexContainer, + setSelectedNode, } = this.props; const { flexItemShown, - highlighted, - } = flexbox; + nodeFront, + } = flexContainer; return ( dom.div({ className: "flex-header devtools-monospace" }, flexItemShown ? dom.button({ className: "flex-header-button-prev devtools-button", - onClick: () => onToggleFlexItemShown(), + onClick: () => setSelectedNode(nodeFront), }) : null, @@ -113,16 +132,16 @@ class Header extends PureComponent { this.renderFlexContainer(), this.renderFlexItemSelector() ), - dom.div({ className: "devtools-separator" }), - dom.input({ - className: "devtools-checkbox-toggle", - checked: highlighted, - onChange: this.onFlexboxCheckboxClick, - type: "checkbox", - }) + this.renderFlexboxHighlighterToggle() ) ); } } -module.exports = Header; +const mapStateToProps = state => { + return { + highlighted: state.flexbox.highlighted, + }; +}; + +module.exports = connect(mapStateToProps)(Header); diff --git a/devtools/client/inspector/flexbox/flexbox.js b/devtools/client/inspector/flexbox/flexbox.js index 6d690aaff8b2..2b4767d9ce3a 100644 --- a/devtools/client/inspector/flexbox/flexbox.js +++ b/devtools/client/inspector/flexbox/flexbox.js @@ -9,7 +9,6 @@ const { throttle } = require("devtools/shared/throttle"); const { clearFlexbox, - toggleFlexItemShown, updateFlexbox, updateFlexboxColor, updateFlexboxHighlighted, @@ -35,7 +34,6 @@ class FlexboxInspector { this.onSetFlexboxOverlayColor = this.onSetFlexboxOverlayColor.bind(this); this.onSidebarSelect = this.onSidebarSelect.bind(this); this.onToggleFlexboxHighlighter = this.onToggleFlexboxHighlighter.bind(this); - this.onToggleFlexItemShown = this.onToggleFlexItemShown.bind(this); this.onUpdatePanel = this.onUpdatePanel.bind(this); this.init(); @@ -105,11 +103,55 @@ class FlexboxInspector { this.walker = null; } + /** + * If the current selected node is a flex container, check if it is also a flex item of + * a parent flex container and get its parent flex container if any and returns an + * object that consists of the parent flex container's items and properties. + * + * @param {NodeFront} containerNodeFront + * The current flex container of the selected node. + * @return {Object} consiting of the parent flex container's flex items and properties. + */ + async getAsFlexItem(containerNodeFront) { + // If the current selected node is not a flex container, we know it is a flex item. + // No need to look for the parent flex container. + if (containerNodeFront !== this.selection.nodeFront) { + return null; + } + + const flexboxFront = await this.layoutInspector.getCurrentFlexbox( + this.selection.nodeFront, true); + + if (!flexboxFront) { + return null; + } + + containerNodeFront = flexboxFront.containerNodeFront; + if (!containerNodeFront) { + containerNodeFront = await this.walker.getNodeFromActor(flexboxFront.actorID, + ["containerEl"]); + } + + let flexItemContainer = null; + if (flexboxFront) { + const flexItems = await this.getFlexItems(flexboxFront); + flexItemContainer = { + actorID: flexboxFront.actorID, + flexItems, + flexItemShown: this.selection.nodeFront.actorID, + isFlexItemContainer: true, + nodeFront: containerNodeFront, + properties: flexboxFront.properties, + }; + } + + return flexItemContainer; + } + getComponentProps() { return { onSetFlexboxOverlayColor: this.onSetFlexboxOverlayColor, onToggleFlexboxHighlighter: this.onToggleFlexboxHighlighter, - onToggleFlexItemShown: this.onToggleFlexItemShown, }; } @@ -230,7 +272,8 @@ class FlexboxInspector { onHighlighterChange(highlighted, nodeFront) { const { flexbox } = this.store.getState(); - if (flexbox.nodeFront === nodeFront && flexbox.highlighted !== highlighted) { + if (flexbox.flexContainer.nodeFront === nodeFront && + flexbox.highlighted !== highlighted) { this.store.dispatch(updateFlexboxHighlighted(highlighted)); } } @@ -341,24 +384,6 @@ class FlexboxInspector { this.highlighters.flexboxHighlighterShow)); } - /** - * Handler for a change in the input checkbox in the FlexItem and Header component. - * Toggles on/off the flex item highlighter for the provided flex item element and - * changes the selection to the given node. - * - * @param {NodeFront|null} node - * The NodeFront of the flex item element for which the flex item is toggled - * on/off for. - */ - onToggleFlexItemShown(node) { - this.highlighters.toggleFlexItemHighlighter(node); - this.store.dispatch(toggleFlexItemShown(node)); - - if (node) { - this.selection.setNodeFront(node); - } - } - /** * Handler for "new-root" event fired by the inspector and "new-node-front" event fired * by the inspector selection. Updates the flexbox panel if it is visible. @@ -412,6 +437,7 @@ class FlexboxInspector { ["containerEl"]); } + const flexItemContainer = await this.getAsFlexItem(containerNodeFront); const flexItems = await this.getFlexItems(flexboxFront); // If the current selected node is a flex item, display its flex item sizing // properties. @@ -422,13 +448,17 @@ class FlexboxInspector { const color = await this.getOverlayColor(); this.store.dispatch(updateFlexbox({ - actorID: flexboxFront.actorID, color, - flexItems, - flexItemShown: flexItemShown ? flexItemShown.nodeFront.actorID : null, + flexContainer: { + actorID: flexboxFront.actorID, + flexItems, + flexItemShown: flexItemShown ? flexItemShown.nodeFront.actorID : null, + isFlexItemContainer: false, + nodeFront: containerNodeFront, + properties: flexboxFront.properties, + }, + flexItemContainer, highlighted, - nodeFront: containerNodeFront, - properties: flexboxFront.properties, })); } catch (e) { // This call might fail if called asynchrously after the toolbox is finished diff --git a/devtools/client/inspector/flexbox/reducers/flexbox.js b/devtools/client/inspector/flexbox/reducers/flexbox.js index e81498e24020..b0adb24e6895 100644 --- a/devtools/client/inspector/flexbox/reducers/flexbox.js +++ b/devtools/client/inspector/flexbox/reducers/flexbox.js @@ -6,27 +6,51 @@ const { CLEAR_FLEXBOX, - TOGGLE_FLEX_ITEM_SHOWN, UPDATE_FLEXBOX, UPDATE_FLEXBOX_COLOR, UPDATE_FLEXBOX_HIGHLIGHTED, } = require("../actions/index"); const INITIAL_FLEXBOX = { - // The actor ID of the flex container. - actorID: null, // The color of the flexbox highlighter overlay. color: "", - // An array of flex items belonging to the current flex container. - flexItems: [], - // The NodeFront actor ID of the flex item to display the flex item sizing properties. - flexItemShown: null, + // The flex container of the selected element. + flexContainer: { + // The actor ID of the selected flex container. + actorID: "", + // An array of flex items belonging to the selected flex container. + flexItems: [], + // The NodeFront actor ID of the flex item to display in the flex item sizing + // properties. + flexItemShown: null, + // This flag specifies that the flex container data represents the selected flex + // container. + isFlexItemContainer: false, + // The NodeFront of the selected flex container. + nodeFront: null, + // The computed style properties of the selected flex container. + properties: null, + }, + // The selected flex container can also be a flex item. This object contains the + // parent flex container properties of the selected element. + flexItemContainer: { + // The actor ID of the parent flex container. + actorID: "", + // An array of flex items belonging to the parent flex container. + flexItems: [], + // The NodeFront actor ID of the flex item to display in the flex item sizing + // properties. + flexItemShown: null, + // This flag specifies that the flex container data represents the parent flex + // container of the selected element. + isFlexItemContainer: true, + // The NodeFront of the parent flex container. + nodeFront: null, + // The computed styles properties of the parent flex container. + properties: null, + }, // Whether or not the flexbox highlighter is highlighting the flex container. highlighted: false, - // The NodeFront of the flex container. - nodeFront: null, - // The computed style properties of the flex container. - properties: {}, }; const reducers = { @@ -35,34 +59,22 @@ const reducers = { return INITIAL_FLEXBOX; }, - [TOGGLE_FLEX_ITEM_SHOWN](flexbox, { nodeFront }) { - let flexItemShown = null; - - // Get the NodeFront actor ID of the flex item. - if (nodeFront) { - const flexItem = flexbox.flexItems.find(item => item.nodeFront === nodeFront); - flexItemShown = flexItem.nodeFront.actorID; - } - - return Object.assign({}, flexbox, { - flexItemShown, - }); - }, - [UPDATE_FLEXBOX](_, { flexbox }) { return flexbox; }, [UPDATE_FLEXBOX_COLOR](flexbox, { color }) { - return Object.assign({}, flexbox, { + return { + ...flexbox, color, - }); + }; }, [UPDATE_FLEXBOX_HIGHLIGHTED](flexbox, { highlighted }) { - return Object.assign({}, flexbox, { + return { + ...flexbox, highlighted, - }); + }; }, }; diff --git a/devtools/client/inspector/flexbox/types.js b/devtools/client/inspector/flexbox/types.js index ced19eafe645..c08719144fbf 100644 --- a/devtools/client/inspector/flexbox/types.js +++ b/devtools/client/inspector/flexbox/types.js @@ -108,22 +108,22 @@ const flexContainerProperties = exports.flexContainerProperties = { /** * A flex container data. */ -exports.flexbox = { +const flexContainer = exports.flexContainer = { // The actor ID of the flex container. actorID: PropTypes.string, - // The color of the flexbox highlighter overlay. - color: PropTypes.string, - - // Array of flex container's flex items. + // An array of flex items belonging to the flex container. flexItems: PropTypes.arrayOf(PropTypes.shape(flexItem)), - // The NodeFront actor ID of the flex item to display the flex item sizing properties. - flexItemShown: PropTypes.string, + // Whether or not the flex container data represents the selected flex container + // or the parent flex container. This is true if the flex container data represents + // the parent flex container. + isFlexItemContainer: PropTypes.bool, - // Whether or not the flexbox highlighter is highlighting the flex container. - highlighted: PropTypes.bool, + // The NodeFront actor ID of the flex item to display in the flex item sizing + // properties. + flexItemShown: PropTypes.string, // The NodeFront of the flex container. nodeFront: PropTypes.object, @@ -132,3 +132,23 @@ exports.flexbox = { properties: PropTypes.shape(flexContainerProperties), }; + +/** + * The Flexbox UI state. + */ +exports.flexbox = { + + // The color of the flexbox highlighter overlay. + color: PropTypes.string, + + // The selected flex container. + flexContainer: PropTypes.shape(flexContainer), + + // The selected flex container can also be a flex item. This object contains the + // parent flex container properties. + flexItemContainer: PropTypes.shape(flexContainer), + + // Whether or not the flexbox highlighter is highlighting the flex container. + highlighted: PropTypes.bool, + +}; diff --git a/devtools/client/inspector/layout/components/LayoutApp.js b/devtools/client/inspector/layout/components/LayoutApp.js index 460c8a7dfe66..50f40be5b910 100644 --- a/devtools/client/inspector/layout/components/LayoutApp.js +++ b/devtools/client/inspector/layout/components/LayoutApp.js @@ -51,7 +51,6 @@ class LayoutApp extends PureComponent { onShowBoxModelHighlighterForNode: PropTypes.func.isRequired, onShowGridOutlineHighlight: PropTypes.func.isRequired, onToggleFlexboxHighlighter: PropTypes.func.isRequired, - onToggleFlexItemShown: PropTypes.func.isRequired, onToggleGeometryEditor: PropTypes.func.isRequired, onToggleGridHighlighter: PropTypes.func.isRequired, onToggleShowGridAreas: PropTypes.func.isRequired, @@ -62,23 +61,21 @@ class LayoutApp extends PureComponent { }; } - getFlexboxHeader() { - const { flexbox } = this.props; - - if (!flexbox.actorID) { + getFlexboxHeader(flexContainer) { + if (!flexContainer.actorID) { // No flex container or flex item selected. return LAYOUT_L10N.getStr("flexbox.header"); - } else if (!flexbox.flexItemShown) { + } else if (!flexContainer.flexItemShown) { // No flex item selected. return LAYOUT_L10N.getStr("flexbox.flexContainer"); } - const grip = translateNodeFrontToGrip(flexbox.nodeFront); + const grip = translateNodeFrontToGrip(flexContainer.nodeFront); return LAYOUT_L10N.getFormatStr("flexbox.flexItemOf", getSelectorFromGrip(grip)); } render() { - let items = [ + const items = [ { component: Grid, componentProps: this.props, @@ -102,19 +99,43 @@ class LayoutApp extends PureComponent { ]; if (Services.prefs.getBoolPref(FLEXBOX_ENABLED_PREF)) { - items = [ - { + // Since the flexbox panel is hidden behind a pref. We insert the flexbox container + // to the first index of the accordion item list. + items.splice(0, 0, { + component: Flexbox, + componentProps: { + ...this.props, + flexContainer: this.props.flexbox.flexContainer, + }, + header: this.getFlexboxHeader(this.props.flexbox.flexContainer), + opened: Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF), + onToggled: () => { + const opened = Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF); + Services.prefs.setBoolPref(FLEXBOX_OPENED_PREF, !opened); + } + }); + + // If the current selected node is both a flex container and flex item. Render + // an accordion with another Flexbox component where the flexbox to show is the + // parent flex container of the current selected node. + if (this.props.flexbox.flexItemContainer && + this.props.flexbox.flexItemContainer.actorID) { + // Insert the parent flex container to the second index of the accordion item + // list. + items.splice(1, 0, { component: Flexbox, - componentProps: this.props, - header: this.getFlexboxHeader(), + componentProps: { + ...this.props, + flexContainer: this.props.flexbox.flexItemContainer, + }, + header: this.getFlexboxHeader(this.props.flexbox.flexItemContainer), opened: Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF), onToggled: () => { const opened = Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF); Services.prefs.setBoolPref(FLEXBOX_OPENED_PREF, !opened); } - }, - ...items - ]; + }); + } } return ( diff --git a/devtools/client/inspector/layout/layout.js b/devtools/client/inspector/layout/layout.js index 962788b8e2a5..5bdb5c393bdf 100644 --- a/devtools/client/inspector/layout/layout.js +++ b/devtools/client/inspector/layout/layout.js @@ -47,7 +47,6 @@ class LayoutView { const { onSetFlexboxOverlayColor, onToggleFlexboxHighlighter, - onToggleFlexItemShown, } = this.flexboxInspector.getComponentProps(); this.gridInspector = new GridInspector(this.inspector, this.inspector.panelWin); @@ -70,7 +69,6 @@ class LayoutView { onShowBoxModelHighlighterForNode, onShowGridOutlineHighlight, onToggleFlexboxHighlighter, - onToggleFlexItemShown, onToggleGeometryEditor, onToggleGridHighlighter, onToggleShowGridAreas, diff --git a/devtools/server/actors/layout.js b/devtools/server/actors/layout.js index 55fc7864d48f..7cb7ecd61534 100644 --- a/devtools/server/actors/layout.js +++ b/devtools/server/actors/layout.js @@ -12,6 +12,7 @@ const { gridSpec, layoutSpec, } = require("devtools/shared/specs/layout"); +const { ELEMENT_NODE } = require("devtools/shared/dom-node-constants"); const { SHOW_ELEMENT } = require("devtools/shared/dom-node-filter-constants"); const { getStringifiableFragments } = require("devtools/server/actors/utils/css-grid-utils"); @@ -102,6 +103,10 @@ const FlexboxActor = ActorClassWithSpec(flexboxSpec, { for (const line of flex.getLines()) { for (const item of line.getItems()) { + if (item.node.nodeType !== ELEMENT_NODE) { + continue; + } + flexItemActors.push(new FlexItemActor(this, item.node, { crossMaxSize: item.crossMaxSize, crossMinSize: item.crossMinSize, @@ -152,17 +157,20 @@ const FlexItemActor = ActorClassWithSpec(flexItemSpec, { return this.actorID; } - const { flexDirection } = CssLogic.getComputedStyle(this.containerEl); - const styles = CssLogic.getComputedStyle(this.element); - const clientRect = this.element.getBoundingClientRect(); - const dimension = flexDirection.startsWith("row") ? "width" : "height"; - const form = { actor: this.actorID, // The flex item sizing data. flexItemSizing: this.flexItemSizing, + }; + + if (this.element.nodeType === ELEMENT_NODE) { + const { flexDirection } = CssLogic.getComputedStyle(this.containerEl); + const styles = CssLogic.getComputedStyle(this.element); + const clientRect = this.element.getBoundingClientRect(); + const dimension = flexDirection.startsWith("row") ? "width" : "height"; + // The computed style properties of the flex item. - properties: { + form.properties = { "flex-basis": styles.flexBasis, "flex-grow": styles.flexGrow, "flex-shrink": styles.flexShrink, @@ -172,8 +180,8 @@ const FlexItemActor = ActorClassWithSpec(flexItemSpec, { [`max-${dimension}`]: styles[`max-${dimension}`], // Computed width/height of the flex item element. [dimension]: parseFloat(clientRect[dimension.toLowerCase()].toPrecision(6)), - }, - }; + }; + } // If the WalkerActor already knows the flex item element, then also return its // ActorID so we avoid the client from doing another round trip to get it in many @@ -261,15 +269,15 @@ const LayoutActor = ActorClassWithSpec(layoutSpec, { }, /** - * Helper function for getCurrentGrid and getCurrentFlexbox. Returns the grid or - * flex container (whichever is requested) found by iterating on the given selected - * node. The current node can be a grid/flex container or grid/flex item. If it is a - * grid/flex item, returns the parent grid/flex container. Otherwise, returns null - * if the current or parent node is not a grid/flex container. + * Helper function for getAsFlexItem, getCurrentGrid and getCurrentFlexbox. Returns the + * grid or flex container (whichever is requested) found by iterating on the given + * selected node. The current node can be a grid/flex container or grid/flex item. + * If it is a grid/flex item, returns the parent grid/flex container. Otherwise, returns + * null if the current or parent node is not a grid/flex container. * * @param {Node|NodeActor} node * The node to start iterating at. - * @param {String} type + * @param {String} type * Can be "grid" or "flex", the display type we are searching for. * @return {GridActor|FlexboxActor|null} The GridActor or FlexboxActor of the * grid/flex container of the give node. Otherwise, returns null. @@ -300,7 +308,8 @@ const LayoutActor = ActorClassWithSpec(layoutSpec, { return new GridActor(this, currentNode); } - // Otherwise, check if this is a flex item or the parent node is a flex container. + // Otherwise, check if this is a flex/grid item or the parent node is a flex/grid + // container. while ((currentNode = treeWalker.parentNode())) { if (!currentNode) { break; @@ -333,7 +342,7 @@ const LayoutActor = ActorClassWithSpec(layoutSpec, { * * @param {Node|NodeActor} node * The node to start iterating at. - * @return {GridActor|null} The GridActor of the grid container of the give node. + * @return {GridActor|null} The GridActor of the grid container of the given node. * Otherwise, returns null. */ getCurrentGrid(node) { @@ -348,10 +357,16 @@ const LayoutActor = ActorClassWithSpec(layoutSpec, { * * @param {Node|NodeActor} node * The node to start iterating at. - * @return {FlexboxActor|null} The FlexboxActor of the flex container of the give node. + * @param {Boolean|null} onlyLookAtParents + * Whether or not to only consider the parent node of the given node. + * @return {FlexboxActor|null} The FlexboxActor of the flex container of the given node. * Otherwise, returns null. */ - getCurrentFlexbox(node) { + getCurrentFlexbox(node, onlyLookAtParents) { + if (onlyLookAtParents) { + node = node.rawNode.parentNode; + } + return this.getCurrentDisplay(node, "flex"); }, diff --git a/devtools/shared/specs/layout.js b/devtools/shared/specs/layout.js index 91b2bfdb8ce5..e1e4104a9661 100644 --- a/devtools/shared/specs/layout.js +++ b/devtools/shared/specs/layout.js @@ -38,6 +38,7 @@ const layoutSpec = generateActorSpec({ getCurrentFlexbox: { request: { node: Arg(0, "domnode"), + onlyLookAtParents: Arg(1, "nullable:boolean"), }, response: { flexbox: RetVal("nullable:flexbox")