diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md
index 51813220e58240..307f46ca97ff80 100644
--- a/docs/reference-guides/core-blocks.md
+++ b/docs/reference-guides/core-blocks.md
@@ -302,7 +302,7 @@ Gather blocks in a layout container. ([Source](https://github.com/WordPress/gute
- **Name:** core/group
- **Category:** design
-- **Supports:** align (full, wide), anchor, ariaLabel, color (background, gradients, heading, link, text), dimensions (minHeight), layout (allowSizingOnChildren), position (sticky), spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~
+- **Supports:** align (full, wide), anchor, ariaLabel, color (background, gradients, heading, link, text), dimensions (minHeight), layout (allowSizingOnChildren, allowSwitching), position (sticky), spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~
- **Attributes:** allowedBlocks, tagName, templateLock
## Heading
diff --git a/packages/block-editor/src/components/block-edit/index.js b/packages/block-editor/src/components/block-edit/index.js
index 6faefbc6261929..d5dfcc55d0c0d2 100644
--- a/packages/block-editor/src/components/block-edit/index.js
+++ b/packages/block-editor/src/components/block-edit/index.js
@@ -26,17 +26,23 @@ export default function BlockEdit( props ) {
isSelected,
clientId,
attributes = {},
+ setAttributes,
__unstableLayoutClassNames,
} = props;
+
const { layout = null } = attributes;
const layoutSupport =
hasBlockSupport( name, 'layout', false ) ||
hasBlockSupport( name, '__experimentalLayout', false );
+ const updateLayoutType = ( newLayout ) => {
+ setAttributes( { layout: { ...layout, type: newLayout } } );
+ };
const context = {
name,
isSelected,
clientId,
layout: layoutSupport ? layout : null,
+ updateLayoutType,
__unstableLayoutClassNames,
};
return (
diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js
index a2acb3c7b53be6..225f8cb3b416bc 100644
--- a/packages/block-editor/src/components/block-list/block.js
+++ b/packages/block-editor/src/components/block-list/block.js
@@ -91,6 +91,7 @@ function BlockListBlock( {
onInsertBlocksAfter,
onMerge,
toggleSelection,
+ updateLayoutType,
} ) {
const {
themeSupportsLayout,
@@ -138,6 +139,7 @@ function BlockListBlock( {
__unstableParentLayout={
Object.keys( parentLayout ).length ? parentLayout : undefined
}
+ updateParentLayoutType={ updateLayoutType }
/>
);
diff --git a/packages/block-editor/src/components/block-list/index.js b/packages/block-editor/src/components/block-list/index.js
index 04b767d9568b78..3fb66e0e74f2cc 100644
--- a/packages/block-editor/src/components/block-list/index.js
+++ b/packages/block-editor/src/components/block-list/index.js
@@ -140,6 +140,7 @@ function Items( {
renderAppender,
__experimentalAppenderTagName,
layout = defaultLayout,
+ updateLayoutType,
} ) {
const { order, selectedBlocks, visibleBlocks } = useSelect(
( select ) => {
@@ -172,6 +173,7 @@ function Items( {
) ) }
diff --git a/packages/block-editor/src/components/child-layout-control/index.js b/packages/block-editor/src/components/child-layout-control/index.js
index b7679234ae5939..e55aed1f9bf4e6 100644
--- a/packages/block-editor/src/components/child-layout-control/index.js
+++ b/packages/block-editor/src/components/child-layout-control/index.js
@@ -26,17 +26,18 @@ function helpText( selfStretch, parentLayout ) {
/**
* Form to edit the child layout value.
*
- * @param {Object} props Props.
- * @param {Object} props.value The child layout value.
- * @param {Function} props.onChange Function to update the child layout value.
- * @param {Object} props.parentLayout The parent layout value.
- *
+ * @param {Object} props Props.
+ * @param {Object} props.value The child layout value.
+ * @param {Function} props.onChange Function to update the child layout value.
+ * @param {Object} props.parentLayout The parent layout value.
+ * @param {Function} props.updateParentLayoutType Function to update the parent layout type.
* @return {WPElement} child layout edit element.
*/
export default function ChildLayoutControl( {
value: childLayout = {},
onChange,
parentLayout,
+ updateParentLayoutType,
} ) {
const { selfStretch, flexSize } = childLayout;
@@ -59,6 +60,9 @@ export default function ChildLayoutControl( {
help={ helpText( selfStretch, parentLayout ) }
onChange={ ( value ) => {
const newFlexSize = value !== 'fixed' ? null : flexSize;
+ if ( value === 'fill' ) {
+ updateParentLayoutType( 'flex' );
+ }
onChange( {
...childLayout,
selfStretch: value,
diff --git a/packages/block-editor/src/components/global-styles/dimensions-panel.js b/packages/block-editor/src/components/global-styles/dimensions-panel.js
index b5eb6175c8c5e4..38659f0f529250 100644
--- a/packages/block-editor/src/components/global-styles/dimensions-panel.js
+++ b/packages/block-editor/src/components/global-styles/dimensions-panel.js
@@ -85,7 +85,7 @@ function useHasChildLayout( settings ) {
} = settings?.parentLayout ?? {};
const support =
- ( defaultParentLayoutType === 'flex' || parentLayoutType === 'flex' ) &&
+ ( defaultParentLayoutType || parentLayoutType ) &&
allowSizingOnChildren;
return !! settings?.layout && support;
@@ -206,6 +206,7 @@ export default function DimensionsPanel( {
// Special case because the layout controls are not part of the dimensions panel
// in global styles but not in block inspector.
includeLayoutControls = false,
+ updateParentLayoutType,
} ) {
const { dimensions, spacing } = settings;
@@ -633,6 +634,7 @@ export default function DimensionsPanel( {
value={ childLayout }
onChange={ setChildLayout }
parentLayout={ settings?.parentLayout }
+ updateParentLayoutType={ updateParentLayoutType }
/>
) }
diff --git a/packages/block-editor/src/components/inner-blocks/index.js b/packages/block-editor/src/components/inner-blocks/index.js
index 9e0e4f19cfc7ea..370f8a702f299d 100644
--- a/packages/block-editor/src/components/inner-blocks/index.js
+++ b/packages/block-editor/src/components/inner-blocks/index.js
@@ -60,6 +60,7 @@ function UncontrolledInnerBlocks( props ) {
orientation,
placeholder,
layout,
+ updateLayoutType,
} = props;
useNestedSettingsUpdate(
@@ -125,6 +126,7 @@ function UncontrolledInnerBlocks( props ) {
layout={ memoedLayout }
wrapperRef={ wrapperRef }
placeholder={ placeholder }
+ updateLayoutType={ updateLayoutType }
/>
);
@@ -175,6 +177,7 @@ export function useInnerBlocksProps( props = {}, options = {} ) {
clientId,
layout = null,
__unstableLayoutClassNames: layoutClassNames = '',
+ updateLayoutType,
} = useBlockEditContext();
const isSmallScreen = useViewportMatch( 'medium', '<' );
const { __experimentalCaptureToolbars, hasOverlay } = useSelect(
@@ -222,6 +225,7 @@ export function useInnerBlocksProps( props = {}, options = {} ) {
const innerBlocksProps = {
__experimentalCaptureToolbars,
layout,
+ updateLayoutType,
...options,
};
const InnerBlocks =
diff --git a/packages/block-editor/src/hooks/dimensions.js b/packages/block-editor/src/hooks/dimensions.js
index 084763f0c21b16..f5fce760f70b6a 100644
--- a/packages/block-editor/src/hooks/dimensions.js
+++ b/packages/block-editor/src/hooks/dimensions.js
@@ -72,6 +72,7 @@ export function DimensionsPanel( props ) {
attributes,
setAttributes,
__unstableParentLayout,
+ updateParentLayoutType,
} = props;
const settings = useBlockSettings( name, __unstableParentLayout );
const isEnabled = useHasDimensionsPanel( settings );
@@ -110,6 +111,7 @@ export function DimensionsPanel( props ) {
onChange={ onChange }
defaultControls={ defaultControls }
onVisualize={ setVisualizedProperty }
+ updateParentLayoutType={ updateParentLayoutType }
/>
{ !! settings?.spacing?.padding && (
{
const { getSettings } = select( blockEditorStore );
return {
@@ -149,34 +174,25 @@ function LayoutPanel( { setAttributes, attributes, name: blockName } ) {
{}
);
const {
- allowSwitching,
+ allowSwitching = false,
allowEditing = true,
- allowInheriting = true,
- default: defaultBlockLayout,
+ // allowInheriting = true,
+ default: defaultBlockLayout = { type: 'default' },
} = layoutBlockSupport;
if ( ! allowEditing ) {
return null;
}
- // Only show the inherit toggle if it's supported,
- // a default theme layout is set (e.g. one that provides `contentSize` and/or `wideSize` values),
- // and either the default / flow or the constrained layout type is in use, as the toggle switches from one to the other.
- const showInheritToggle = !! (
- allowInheriting &&
- !! defaultThemeLayout &&
- ( ! layout?.type ||
- layout?.type === 'default' ||
- layout?.type === 'constrained' ||
- layout?.inherit )
- );
-
const usedLayout = layout || defaultBlockLayout || {};
const {
inherit = false,
type = 'default',
contentSize = null,
+ orientation = 'horizontal',
+ flexWrap = 'nowrap',
} = usedLayout;
+ const { type: defaultBlockLayoutType } = defaultBlockLayout;
/**
* `themeSupportsLayout` is only relevant to the `default/flow` or
* `constrained` layouts and it should not be taken into account when other
@@ -192,74 +208,473 @@ function LayoutPanel( { setAttributes, attributes, name: blockName } ) {
const constrainedType = getLayoutType( 'constrained' );
const displayControlsForLegacyLayouts =
! usedLayout.type && ( contentSize || inherit );
- const hasContentSizeOrLegacySettings = !! inherit || !! contentSize;
- const onChangeType = ( newType ) =>
- setAttributes( { layout: { type: newType } } );
+ const innerWidthOptions = [
+ {
+ value: 'theme',
+ icon: (
+
+ ),
+ label: __( 'Fixed' ),
+ },
+ {
+ value: 'fill',
+ icon: (
+
+ ),
+ label: __( 'Fill' ),
+ },
+ ];
+
+ if ( allowSwitching || defaultBlockLayoutType === 'flex' ) {
+ innerWidthOptions.unshift( {
+ value: 'fit',
+ icon: (
+
+ ),
+ label: __( 'Fit' ),
+ } );
+ }
+ const horizontalAlignmentOptions = [
+ {
+ value: 'left',
+ icon: justifyLeft,
+ label: __( 'Left' ),
+ },
+ {
+ value: 'center',
+ icon: justifyCenter,
+ label: __( 'Middle' ),
+ },
+ {
+ value: 'right',
+ icon: justifyRight,
+ label: __( 'Right' ),
+ },
+ ];
+
+ if ( type === 'flex' ) {
+ horizontalAlignmentOptions.push( {
+ value: 'space-between',
+ icon: justifySpaceBetween,
+ label: __( 'Space Between' ),
+ } );
+ }
+
+ const verticalAlignmentOptions = [
+ {
+ value: 'top',
+ icon: alignTop,
+ label: __( 'Top' ),
+ },
+ {
+ value: 'center',
+ icon: alignCenter,
+ label: __( 'Middle' ),
+ },
+ {
+ value: 'bottom',
+ icon: alignBottom,
+ label: __( 'Bottom' ),
+ },
+ ];
+
+ if ( orientation === 'horizontal' ) {
+ verticalAlignmentOptions.push( {
+ value: 'stretch',
+ icon: alignStretch,
+ label: __( 'Stretch' ),
+ } );
+ } else {
+ verticalAlignmentOptions.push( {
+ value: 'space-between',
+ icon: spaceBetween,
+ label: __( 'Space Between' ),
+ } );
+ }
+
+ const onChangeType = ( newType ) => {
+ if ( newType === 'stack' ) {
+ const { type: previousLayoutType } = usedLayout;
+ if ( previousLayoutType === 'flex' ) {
+ setAttributes( {
+ layout: {
+ ...usedLayout,
+ type: 'flex',
+ orientation: 'vertical',
+ },
+ } );
+ } else {
+ setAttributes( { layout: { type: 'default' } } );
+ }
+ } else {
+ setAttributes( {
+ layout: {
+ ...usedLayout,
+ type: newType,
+ orientation: 'horizontal',
+ },
+ } );
+ }
+ };
+
+ const onChangeInnerWidth = ( key ) => {
+ if ( key === 'theme' ) {
+ setAttributes( {
+ layout: { ...usedLayout, type: 'constrained' },
+ } );
+ } else if ( key === 'fit' ) {
+ setAttributes( {
+ layout: {
+ ...usedLayout,
+ type: 'flex',
+ orientation: 'vertical',
+ },
+ } );
+ } else {
+ setAttributes( {
+ layout: { ...usedLayout, type: 'default' },
+ } );
+ }
+ };
+
const onChangeLayout = ( newLayout ) =>
setAttributes( { layout: newLayout } );
+ const onChangeGap = ( newGap ) => {
+ setAttributes( {
+ style: {
+ ...style,
+ spacing: {
+ ...style?.spacing,
+ blockGap: `${ newGap }px`,
+ },
+ },
+ } );
+ };
+
+ const onChangeWrap = ( newWrap ) => {
+ setAttributes( {
+ layout: {
+ ...usedLayout,
+ flexWrap: newWrap,
+ },
+ } );
+ };
+
+ const defaultHorizontalAlign = type === 'constrained' ? 'center' : 'left';
+
+ let defaultContentWidthValue = 'fill';
+ if ( defaultBlockLayoutType === 'constrained' ) {
+ defaultContentWidthValue = 'theme';
+ } else if ( defaultBlockLayoutType === 'flex' ) {
+ defaultContentWidthValue = 'fit';
+ }
+
+ let usedContentWidthValue = 'fill';
+ if ( type === 'constrained' ) {
+ usedContentWidthValue = 'theme';
+ } else if ( type === 'flex' ) {
+ usedContentWidthValue = 'fit';
+ }
+
return (
<>
- { showInheritToggle && (
- <>
-
+ { ( allowSwitching ||
+ defaultBlockLayoutType === 'flex' ) && (
+
- setAttributes( {
- layout: {
- type:
- layoutType?.name ===
- 'constrained' ||
- hasContentSizeOrLegacySettings
- ? 'default'
- : 'constrained',
- },
- } )
- }
- help={
- layoutType?.name === 'constrained' ||
- hasContentSizeOrLegacySettings
- ? __(
- 'Nested blocks use content width with options for full and wide widths.'
- )
- : __(
- 'Nested blocks will fill the width of this container. Toggle to constrain.'
- )
+ style={ { marginBottom: 0, marginTop: 0 } }
+ size={ '__unstable-large' }
+ label={ __( 'Layout direction' ) }
+ value={
+ type === 'default' ||
+ type === 'constrained' ||
+ ( type === 'flex' &&
+ orientation === 'vertical' )
+ ? 'stack'
+ : type
}
+ onChange={ onChangeType }
+ isBlock={ true }
+ className="components-toggle-group-control__full-width"
+ >
+
+
+
+
+ { allowSwitching && (
+
+ ) }
+
+ ) }
+ { type === 'grid' && (
+
- >
- ) }
-
- { ! inherit && allowSwitching && (
-
- ) }
-
- { layoutType && layoutType.name !== 'default' && (
-
- ) }
- { constrainedType && displayControlsForLegacyLayouts && (
-
+ { innerWidthOptions.map( ( option ) => (
+
+ ) ) }
+
+ ) }
+
+ { type === 'flex' && (
+
+ {
+ onChangeLayout( {
+ ...usedLayout,
+ verticalAlignment: selectedItem,
+ } );
+ } }
+ isBlock={ true }
+ className="components-toggle-group-control__full-width"
+ >
+ { verticalAlignmentOptions.map(
+ ( option ) => (
+
+ )
+ ) }
+
+
+ ) }
+
+ { ( type === 'flex' ||
+ type === 'constrained' ) && (
+ {
+ onChangeLayout( {
+ ...usedLayout,
+ justifyContent: selectedItem,
+ } );
+ } }
+ className="components-toggle-group-control__full-width"
+ >
+ { horizontalAlignmentOptions.map(
+ ( { value, icon, label } ) => (
+
+ )
+ ) }
+
+ ) }
+
+
+
+ { type === 'flex' && orientation === 'horizontal' && (
+
+
+
+
+ ) }
+ { constrainedType &&
+ displayControlsForLegacyLayouts && (
+
+ ) }
+
- ) }
+
{ ! inherit && blockEditingMode === 'default' && layoutType && (
@@ -273,24 +688,6 @@ function LayoutPanel( { setAttributes, attributes, name: blockName } ) {
);
}
-function LayoutTypeSwitcher( { type, onChange } ) {
- return (
-
- { getLayoutTypes().map( ( { name, label } ) => {
- return (
-
- );
- } ) }
-
- );
-}
-
/**
* Filters registered block settings, extending attributes to include `layout`.
*
diff --git a/packages/block-editor/src/hooks/layout.scss b/packages/block-editor/src/hooks/layout.scss
index 83a044e3cdca75..37843fd23166c4 100644
--- a/packages/block-editor/src/hooks/layout.scss
+++ b/packages/block-editor/src/hooks/layout.scss
@@ -39,3 +39,21 @@
.block-editor-hooks__toggle-control.block-editor-hooks__toggle-control {
margin-bottom: $grid-unit-20;
}
+
+
+// Temporary
+
+.components-toggle-group-control__full-width {
+ .components-toggle-group-control-option-base {
+ width: 100%;
+ button {
+ width: 100%;
+ }
+ }
+}
+
+.components-wrapper-vstack {
+ > div {
+ margin-bottom: 0 !important;
+ }
+}
diff --git a/packages/block-library/src/group/block.json b/packages/block-library/src/group/block.json
index 135aedf03589d0..11b3296fc7b168 100644
--- a/packages/block-library/src/group/block.json
+++ b/packages/block-library/src/group/block.json
@@ -77,7 +77,8 @@
}
},
"layout": {
- "allowSizingOnChildren": true
+ "allowSizingOnChildren": true,
+ "allowSwitching": true
}
},
"editorStyle": "wp-block-group-editor",
diff --git a/packages/block-library/src/group/variations.js b/packages/block-library/src/group/variations.js
index 3f5dcc0a45a9e4..543f70d6c6690e 100644
--- a/packages/block-library/src/group/variations.js
+++ b/packages/block-library/src/group/variations.js
@@ -11,7 +11,7 @@ const variations = [
description: __( 'Gather blocks in a container.' ),
attributes: { layout: { type: 'constrained' } },
isDefault: true,
- scope: [ 'block', 'inserter', 'transform' ],
+ scope: [ 'block', 'inserter' ],
isActive: ( blockAttributes ) =>
! blockAttributes.layout ||
! blockAttributes.layout?.type ||
@@ -24,7 +24,7 @@ const variations = [
title: _x( 'Row', 'single horizontal line' ),
description: __( 'Arrange blocks horizontally.' ),
attributes: { layout: { type: 'flex', flexWrap: 'nowrap' } },
- scope: [ 'block', 'inserter', 'transform' ],
+ scope: [ 'block', 'inserter' ],
isActive: ( blockAttributes ) =>
blockAttributes.layout?.type === 'flex' &&
( ! blockAttributes.layout?.orientation ||
@@ -36,7 +36,7 @@ const variations = [
title: __( 'Stack' ),
description: __( 'Arrange blocks vertically.' ),
attributes: { layout: { type: 'flex', orientation: 'vertical' } },
- scope: [ 'block', 'inserter', 'transform' ],
+ scope: [ 'block', 'inserter' ],
isActive: ( blockAttributes ) =>
blockAttributes.layout?.type === 'flex' &&
blockAttributes.layout?.orientation === 'vertical',
@@ -50,7 +50,7 @@ if ( window?.__experimentalEnableGroupGridVariation ) {
title: __( 'Grid' ),
description: __( 'Arrange blocks in a grid.' ),
attributes: { layout: { type: 'grid' } },
- scope: [ 'block', 'inserter', 'transform' ],
+ scope: [ 'block', 'inserter' ],
isActive: ( blockAttributes ) =>
blockAttributes.layout?.type === 'grid',
icon: grid,