diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php
index 92763e010d775..1bacc0b9aa5f2 100644
--- a/lib/block-supports/layout.php
+++ b/lib/block-supports/layout.php
@@ -29,7 +29,7 @@ function gutenberg_register_layout_support( $block_type ) {
* Generates the CSS corresponding to the provided layout.
*
* @param string $selector CSS selector.
- * @param array $layout Layout object.
+ * @param array $layout Layout object. The one that is passed has already checked the existance of default block layout.
* @param boolean $has_block_gap_support Whether the theme has support for the block gap.
*
* @return string CSS style.
@@ -68,6 +68,13 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support
$style .= "$selector > * + * { margin-top: var( --wp--style--block-gap ); margin-bottom: 0; }";
}
} elseif ( 'flex' === $layout_type ) {
+ $justify_content_options = array(
+ 'left' => 'flex-start',
+ 'right' => 'flex-end',
+ 'center' => 'center',
+ 'space-between' => 'space-between',
+ );
+
$style = "$selector {";
$style .= 'display: flex;';
if ( $has_block_gap_support ) {
@@ -77,6 +84,14 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support
}
$style .= 'flex-wrap: wrap;';
$style .= 'align-items: center;';
+ /**
+ * Add this style only if is not empty for backwards compatibility,
+ * since we intend to convert blocks that had flex layout implemented
+ * by custom css.
+ */
+ if ( ! empty( $layout['justifyContent'] ) && array_key_exists( $layout['justifyContent'], $justify_content_options ) ) {
+ $style .= "justify-content: {$justify_content_options[ $layout['justifyContent'] ]};";
+ }
$style .= '}';
$style .= "$selector > * { margin: 0; }";
diff --git a/packages/block-editor/src/hooks/layout.js b/packages/block-editor/src/hooks/layout.js
index 796de8c479a2f..a1f17c525c0c6 100644
--- a/packages/block-editor/src/hooks/layout.js
+++ b/packages/block-editor/src/hooks/layout.js
@@ -40,23 +40,32 @@ function LayoutPanel( { setAttributes, attributes, name: blockName } ) {
return getSettings().supportsLayout;
}, [] );
- if ( ! themeSupportsLayout ) {
- return null;
- }
-
+ const layoutBlockSupport = getBlockSupport(
+ blockName,
+ layoutBlockSupportKey,
+ {}
+ );
const {
- allowSwitching: canBlockSwitchLayout,
+ allowSwitching,
allowEditing = true,
allowInheriting = true,
default: defaultBlockLayout,
- } = getBlockSupport( blockName, layoutBlockSupportKey ) || {};
+ } = layoutBlockSupport;
if ( ! allowEditing ) {
return null;
}
- const usedLayout = layout ? layout : defaultBlockLayout || {};
+ const usedLayout = layout || defaultBlockLayout || {};
const { inherit = false, type = 'default' } = usedLayout;
+ /**
+ * `themeSupportsLayout` is only relevant to the `default/flow`
+ * layout and it should not be taken into account when other
+ * `layout` types are used.
+ */
+ if ( type === 'default' && ! themeSupportsLayout ) {
+ return null;
+ }
const layoutType = getLayoutType( type );
const onChangeType = ( newType ) =>
@@ -65,33 +74,45 @@ function LayoutPanel( { setAttributes, attributes, name: blockName } ) {
setAttributes( { layout: newLayout } );
return (
-
-
- { allowInheriting && !! defaultThemeLayout && (
-
- setAttributes( { layout: { inherit: ! inherit } } )
- }
- />
- ) }
-
- { ! inherit && canBlockSwitchLayout && (
-
- ) }
-
- { ! inherit && layoutType && (
-
- ) }
-
-
+ <>
+
+
+ { allowInheriting && !! defaultThemeLayout && (
+
+ setAttributes( {
+ layout: { inherit: ! inherit },
+ } )
+ }
+ />
+ ) }
+
+ { ! inherit && allowSwitching && (
+
+ ) }
+
+ { ! inherit && layoutType && (
+
+ ) }
+
+
+ { ! inherit && layoutType && (
+
+ ) }
+ >
);
}
diff --git a/packages/block-editor/src/layouts/flex.js b/packages/block-editor/src/layouts/flex.js
index 268674ee3e503..2c1b9f69be4a5 100644
--- a/packages/block-editor/src/layouts/flex.js
+++ b/packages/block-editor/src/layouts/flex.js
@@ -1,27 +1,63 @@
/**
* WordPress dependencies
*/
-import { __ } from '@wordpress/i18n';
+import { __, _x } from '@wordpress/i18n';
+import {
+ __experimentalToggleGroupControl as ToggleGroupControl,
+ __experimentalToggleGroupControlOption as ToggleGroupControlOption,
+} from '@wordpress/components';
/**
* Internal dependencies
*/
import { appendSelectors } from './utils';
import useSetting from '../components/use-setting';
+import { BlockControls, JustifyContentControl } from '../components';
+
+const justifyContentMap = {
+ left: 'flex-start',
+ right: 'flex-end',
+ center: 'center',
+ 'space-between': 'space-between',
+};
export default {
name: 'flex',
-
label: __( 'Flex' ),
-
- edit() {
- return null;
+ inspectorControls: function FlexLayoutInspectorControls( {
+ layout = {},
+ onChange,
+ } ) {
+ return (
+
+ );
},
-
- save: function FlexLayoutStyle( { selector } ) {
+ toolBarControls: function FlexLayoutToolbarControls( {
+ layout = {},
+ onChange,
+ layoutBlockSupport,
+ } ) {
+ if ( layoutBlockSupport?.allowSwitching ) {
+ return null;
+ }
+ return (
+
+
+
+ );
+ },
+ save: function FlexLayoutStyle( { selector, layout } ) {
const blockGapSupport = useSetting( 'spacing.blockGap' );
const hasBlockGapStylesSupport = blockGapSupport !== null;
-
+ const justifyContent =
+ justifyContentMap[ layout.justifyContent ] || 'flex-start';
return (
);
},
-
getOrientation() {
return 'horizontal';
},
-
getAlignments() {
return [];
},
};
+
+function FlexLayoutJustifyContentControl( {
+ layout,
+ onChange,
+ isToolbar = false,
+} ) {
+ const { justifyContent = 'left' } = layout;
+ if ( isToolbar ) {
+ return (
+ {
+ onChange( {
+ ...layout,
+ justifyContent: value,
+ } );
+ } }
+ popoverProps={ {
+ position: 'bottom right',
+ isAlternate: true,
+ } }
+ />
+ );
+ }
+ return (
+ {
+ onChange( {
+ ...layout,
+ justifyContent: value,
+ } );
+ } }
+ isBlock
+ >
+
+
+
+
+
+ );
+}
diff --git a/packages/block-editor/src/layouts/flow.js b/packages/block-editor/src/layouts/flow.js
index 3e0fc374ee548..97741a6a93017 100644
--- a/packages/block-editor/src/layouts/flow.js
+++ b/packages/block-editor/src/layouts/flow.js
@@ -17,10 +17,11 @@ import { appendSelectors } from './utils';
export default {
name: 'default',
-
label: __( 'Flow' ),
-
- edit: function LayoutDefaultEdit( { layout, onChange } ) {
+ inspectorControls: function DefaultLayoutInspectorControls( {
+ layout,
+ onChange,
+ } ) {
const { wideSize, contentSize } = layout;
const units = useCustomUnits( {
availableUnits: useSetting( 'spacing.units' ) || [
@@ -101,7 +102,9 @@ export default {
>
);
},
-
+ toolBarControls: function DefaultLayoutToolbarControls() {
+ return null;
+ },
save: function DefaultLayoutStyle( { selector, layout = {} } ) {
const { contentSize, wideSize } = layout;
const blockGapSupport = useSetting( 'spacing.blockGap' );
@@ -115,11 +118,11 @@ export default {
margin-left: auto !important;
margin-right: auto !important;
}
-
+
${ appendSelectors( selector, '> [data-align="wide"]' ) } {
max-width: ${ wideSize ?? contentSize };
}
-
+
${ appendSelectors( selector, '> [data-align="full"]' ) } {
max-width: none;
}
@@ -131,7 +134,7 @@ export default {
float: left;
margin-right: 2em;
}
-
+
${ appendSelectors( selector, '> [data-align="right"]' ) } {
float: right;
margin-left: 2em;
@@ -150,11 +153,9 @@ export default {
return ;
},
-
getOrientation() {
return 'vertical';
},
-
getAlignments( layout ) {
if ( layout.alignments !== undefined ) {
return layout.alignments;
diff --git a/packages/block-library/src/social-links/block.json b/packages/block-library/src/social-links/block.json
index 73ac6142c7042..b9c8eddbdc4d6 100644
--- a/packages/block-library/src/social-links/block.json
+++ b/packages/block-library/src/social-links/block.json
@@ -41,7 +41,14 @@
"supports": {
"align": [ "left", "center", "right" ],
"anchor": true,
- "__experimentalExposeControlsToChildren": true
+ "__experimentalExposeControlsToChildren": true,
+ "__experimentalLayout": {
+ "allowSwitching": false,
+ "allowInheriting": false,
+ "default": {
+ "type": "flex"
+ }
+ }
},
"styles": [
{ "name": "default", "label": "Default", "isDefault": true },
diff --git a/packages/block-library/src/social-links/deprecated.js b/packages/block-library/src/social-links/deprecated.js
index e8615282201f5..8f1ae2c42a68e 100644
--- a/packages/block-library/src/social-links/deprecated.js
+++ b/packages/block-library/src/social-links/deprecated.js
@@ -8,8 +8,101 @@ import classNames from 'classnames';
*/
import { InnerBlocks, useBlockProps } from '@wordpress/block-editor';
+/**
+ * The specific handling by `className` below is needed because `itemsJustification`
+ * was introduced in https://github.com/WordPress/gutenberg/pull/28980/files and wasn't
+ * declared in block.json.
+ *
+ * @param {Object} attributes Block's attributes.
+ */
+const migrateWithLayout = ( attributes ) => {
+ if ( !! attributes.layout ) {
+ return attributes;
+ }
+ const { className } = attributes;
+ // Matches classes with `items-justified-` prefix.
+ const prefix = `items-justified-`;
+ const justifiedItemsRegex = new RegExp( `\\b${ prefix }[^ ]*[ ]?\\b`, 'g' );
+ const newAttributes = {
+ ...attributes,
+ className: className?.replace( justifiedItemsRegex, '' ).trim(),
+ };
+ /**
+ * Add `layout` prop only if `justifyContent` is defined, for backwards
+ * compatibility. In other cases the block's default layout will be used.
+ * Also noting that due to the missing attribute, it's possible for a block
+ * to have more than one of `justified` classes.
+ */
+ const justifyContent = className
+ ?.match( justifiedItemsRegex )?.[ 0 ]
+ ?.trim();
+ if ( justifyContent ) {
+ Object.assign( newAttributes, {
+ layout: {
+ type: 'flex',
+ justifyContent: justifyContent.slice( prefix.length ),
+ },
+ } );
+ }
+ return newAttributes;
+};
+
// Social Links block deprecations.
const deprecated = [
+ // Implement `flex` layout.
+ {
+ attributes: {
+ iconColor: {
+ type: 'string',
+ },
+ customIconColor: {
+ type: 'string',
+ },
+ iconColorValue: {
+ type: 'string',
+ },
+ iconBackgroundColor: {
+ type: 'string',
+ },
+ customIconBackgroundColor: {
+ type: 'string',
+ },
+ iconBackgroundColorValue: {
+ type: 'string',
+ },
+ openInNewTab: {
+ type: 'boolean',
+ default: false,
+ },
+ size: {
+ type: 'string',
+ },
+ },
+ isEligible: ( { layout } ) => ! layout,
+ migrate: migrateWithLayout,
+ save( props ) {
+ const {
+ attributes: {
+ iconBackgroundColorValue,
+ iconColorValue,
+ itemsJustification,
+ size,
+ },
+ } = props;
+
+ const className = classNames( size, {
+ 'has-icon-color': iconColorValue,
+ 'has-icon-background-color': iconBackgroundColorValue,
+ [ `items-justified-${ itemsJustification }` ]: itemsJustification,
+ } );
+
+ return (
+
+ );
+ },
+ },
// V1. Remove CSS variable use for colors.
{
attributes: {
@@ -46,6 +139,7 @@ const deprecated = [
align: [ 'left', 'center', 'right' ],
anchor: true,
},
+ migrate: migrateWithLayout,
save: ( props ) => {
const {
attributes: {
diff --git a/packages/block-library/src/social-links/edit.js b/packages/block-library/src/social-links/edit.js
index cd4a10b171adb..3b3214be223ff 100644
--- a/packages/block-library/src/social-links/edit.js
+++ b/packages/block-library/src/social-links/edit.js
@@ -6,15 +6,13 @@ import classNames from 'classnames';
/**
* WordPress dependencies
*/
-
+import { getBlockSupport } from '@wordpress/blocks';
import { Fragment, useEffect } from '@wordpress/element';
-
import {
BlockControls,
__experimentalUseInnerBlocksProps as useInnerBlocksProps,
useBlockProps,
InspectorControls,
- JustifyContentControl,
ContrastChecker,
PanelColorSettings,
withColors,
@@ -38,8 +36,17 @@ const sizeOptions = [
{ name: __( 'Huge' ), value: 'has-huge-icon-size' },
];
+const getDefaultBlockLayout = ( blockTypeOrName ) => {
+ const layoutBlockSupportConfig = getBlockSupport(
+ blockTypeOrName,
+ '__experimentalLayout'
+ );
+ return layoutBlockSupportConfig?.default;
+};
+
export function SocialLinksEdit( props ) {
const {
+ name,
attributes,
iconBackgroundColor,
iconColor,
@@ -52,10 +59,11 @@ export function SocialLinksEdit( props ) {
const {
iconBackgroundColorValue,
iconColorValue,
- itemsJustification,
openInNewTab,
size,
+ layout,
} = attributes;
+ const usedLayout = layout || getDefaultBlockLayout( name );
// Remove icon background color if logos only style selected.
const logosOnly =
@@ -93,16 +101,15 @@ export function SocialLinksEdit( props ) {
'has-icon-color': iconColor.color || iconColorValue,
'has-icon-background-color':
iconBackgroundColor.color || iconBackgroundColorValue,
- [ `items-justified-${ itemsJustification }` ]: itemsJustification,
} );
const blockProps = useBlockProps( { className } );
const innerBlocksProps = useInnerBlocksProps( blockProps, {
allowedBlocks: ALLOWED_BLOCKS,
- orientation: 'horizontal',
placeholder: isSelected ? SelectedSocialPlaceholder : SocialPlaceholder,
templateLock: false,
__experimentalAppenderTagName: 'li',
+ __experimentalLayout: usedLayout,
} );
const POPOVER_PROPS = {
@@ -111,24 +118,6 @@ export function SocialLinksEdit( props ) {
return (
-
-
- setAttributes( { itemsJustification: value } )
- }
- popoverProps={ {
- position: 'bottom right',
- isAlternate: true,
- } }
- />
-
@@ -16,13 +14,7 @@
box-shadow: none;
}
- // Vertically balance the margin of each icon.
.wp-social-link {
- // This needs specificity to override some themes.
- &.wp-social-link.wp-social-link {
- margin: 4px 8px 4px 0;
- }
-
// By setting the font size, we can scale icons and paddings consistently based on that.
// This also allows themes to override this, if need be.
a {
diff --git a/test/integration/fixtures/blocks/core__social-links__deprecated-1.html b/test/integration/fixtures/blocks/core__social-links__deprecated-1.html
new file mode 100644
index 0000000000000..b18602d8e8c14
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__social-links__deprecated-1.html
@@ -0,0 +1,7 @@
+
+
+
diff --git a/test/integration/fixtures/blocks/core__social-links__deprecated-1.json b/test/integration/fixtures/blocks/core__social-links__deprecated-1.json
new file mode 100644
index 0000000000000..e1c9d8dd21021
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__social-links__deprecated-1.json
@@ -0,0 +1,55 @@
+[
+ {
+ "clientId": "_clientId_0",
+ "name": "core/social-links",
+ "isValid": true,
+ "attributes": {
+ "customIconColor": "#ffffff",
+ "iconColorValue": "#ffffff",
+ "customIconBackgroundColor": "#3962e3",
+ "iconBackgroundColorValue": "#3962e3",
+ "openInNewTab": false,
+ "className": "",
+ "layout": {
+ "type": "flex",
+ "justifyContent": "space-between"
+ }
+ },
+ "innerBlocks": [
+ {
+ "clientId": "_clientId_0",
+ "name": "core/social-link",
+ "isValid": true,
+ "attributes": {
+ "url": "https://wordpress.org",
+ "service": "wordpress"
+ },
+ "innerBlocks": [],
+ "originalContent": ""
+ },
+ {
+ "clientId": "_clientId_1",
+ "name": "core/social-link",
+ "isValid": true,
+ "attributes": {
+ "url": "#",
+ "service": "chain"
+ },
+ "innerBlocks": [],
+ "originalContent": ""
+ },
+ {
+ "clientId": "_clientId_2",
+ "name": "core/social-link",
+ "isValid": true,
+ "attributes": {
+ "url": "#",
+ "service": "mail"
+ },
+ "innerBlocks": [],
+ "originalContent": ""
+ }
+ ],
+ "originalContent": ""
+ }
+]
diff --git a/test/integration/fixtures/blocks/core__social-links__deprecated-1.parsed.json b/test/integration/fixtures/blocks/core__social-links__deprecated-1.parsed.json
new file mode 100644
index 0000000000000..9746fed44ab7e
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__social-links__deprecated-1.parsed.json
@@ -0,0 +1,54 @@
+[
+ {
+ "blockName": "core/social-links",
+ "attrs": {
+ "customIconColor": "#ffffff",
+ "iconColorValue": "#ffffff",
+ "customIconBackgroundColor": "#3962e3",
+ "iconBackgroundColorValue": "#3962e3",
+ "className": "has-icon-color"
+ },
+ "innerBlocks": [
+ {
+ "blockName": "core/social-link",
+ "attrs": {
+ "url": "https://wordpress.org",
+ "service": "wordpress"
+ },
+ "innerBlocks": [],
+ "innerHTML": "",
+ "innerContent": []
+ },
+ {
+ "blockName": "core/social-link",
+ "attrs": {
+ "url": "#",
+ "service": "chain"
+ },
+ "innerBlocks": [],
+ "innerHTML": "",
+ "innerContent": []
+ },
+ {
+ "blockName": "core/social-link",
+ "attrs": {
+ "url": "#",
+ "service": "mail"
+ },
+ "innerBlocks": [],
+ "innerHTML": "",
+ "innerContent": []
+ }
+ ],
+ "innerHTML": "\n\n",
+ "innerContent": [
+ "\n\n\t",
+ null,
+ "\n\t",
+ null,
+ "\n\t",
+ null,
+ "\n
\n"
+ ]
+ }
+]
diff --git a/test/integration/fixtures/blocks/core__social-links__deprecated-1.serialized.html b/test/integration/fixtures/blocks/core__social-links__deprecated-1.serialized.html
new file mode 100644
index 0000000000000..b2fa19fb12064
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__social-links__deprecated-1.serialized.html
@@ -0,0 +1,7 @@
+
+
+