Skip to content

Commit

Permalink
Add flex layout support to the block editor (#33359)
Browse files Browse the repository at this point in the history
  • Loading branch information
youknowriad authored Jul 26, 2021
1 parent e0de351 commit bf405a3
Show file tree
Hide file tree
Showing 23 changed files with 445 additions and 285 deletions.
100 changes: 62 additions & 38 deletions lib/block-supports/layout.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,56 @@ 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.
*
* @return string CSS style.
*/
function gutenberg_get_layout_style( $selector, $layout ) {
$layout_type = isset( $layout['type'] ) ? $layout['type'] : 'default';

$style = '';
if ( 'default' === $layout_type ) {
$content_size = isset( $layout['contentSize'] ) ? $layout['contentSize'] : null;
$wide_size = isset( $layout['wideSize'] ) ? $layout['wideSize'] : null;

$all_max_width_value = $content_size ? $content_size : $wide_size;
$wide_max_width_value = $wide_size ? $wide_size : $content_size;

// Make sure there is a single CSS rule, and all tags are stripped for security.
// TODO: Use `safecss_filter_attr` instead - once https://core.trac.wordpress.org/ticket/46197 is patched.
$all_max_width_value = wp_strip_all_tags( explode( ';', $all_max_width_value )[0] );
$wide_max_width_value = wp_strip_all_tags( explode( ';', $wide_max_width_value )[0] );

$style = '';
if ( $content_size || $wide_size ) {
$style = "$selector > * {";
$style .= 'max-width: ' . esc_html( $all_max_width_value ) . ';';
$style .= 'margin-left: auto !important;';
$style .= 'margin-right: auto !important;';
$style .= '}';

$style .= "$selector > .alignwide { max-width: " . esc_html( $wide_max_width_value ) . ';}';

$style .= "$selector .alignfull { max-width: none; }";
}

$style .= "$selector .alignleft { float: left; margin-right: 2em; }";
$style .= "$selector .alignright { float: right; margin-left: 2em; }";
} elseif ( 'flex' === $layout_type ) {
$style = "$selector {";
$style .= 'display: flex;';
$style .= 'column-gap: 0.5em;';
$style .= 'align-items: center;';
$style .= '}';
}

return $style;
}

/**
* Renders the layout config to the block wrapper.
*
Expand All @@ -49,34 +99,8 @@ function gutenberg_render_layout_support_flag( $block_content, $block ) {
$used_layout = $default_layout;
}

$id = uniqid();
$content_size = isset( $used_layout['contentSize'] ) ? $used_layout['contentSize'] : null;
$wide_size = isset( $used_layout['wideSize'] ) ? $used_layout['wideSize'] : null;

$all_max_width_value = $content_size ? $content_size : $wide_size;
$wide_max_width_value = $wide_size ? $wide_size : $content_size;

// Make sure there is a single CSS rule, and all tags are stripped for security.
// TODO: Use `safecss_filter_attr` instead - once https://core.trac.wordpress.org/ticket/46197 is patched.
$all_max_width_value = wp_strip_all_tags( explode( ';', $all_max_width_value )[0] );
$wide_max_width_value = wp_strip_all_tags( explode( ';', $wide_max_width_value )[0] );

$style = '';
if ( $content_size || $wide_size ) {
$style = ".wp-container-$id > * {";
$style .= 'max-width: ' . esc_html( $all_max_width_value ) . ';';
$style .= 'margin-left: auto !important;';
$style .= 'margin-right: auto !important;';
$style .= '}';

$style .= ".wp-container-$id > .alignwide { max-width: " . esc_html( $wide_max_width_value ) . ';}';

$style .= ".wp-container-$id .alignfull { max-width: none; }";
}

$style .= ".wp-container-$id .alignleft { float: left; margin-right: 2em; }";
$style .= ".wp-container-$id .alignright { float: right; margin-left: 2em; }";

$id = uniqid();
$style = gutenberg_get_layout_style( ".wp-container-$id", $used_layout );
// This assumes the hook only applies to blocks with a single wrapper.
// I think this is a reasonable limitation for that particular hook.
$content = preg_replace(
Expand All @@ -99,17 +123,17 @@ function () use ( $style ) {
return $content;
}

// This can be removed when plugin support requires WordPress 5.8.0+.
if ( ! function_exists( 'wp_render_layout_support_flag' ) ) {
// Register the block support.
WP_Block_Supports::get_instance()->register(
'layout',
array(
'register_attribute' => 'gutenberg_register_layout_support',
)
);
add_filter( 'render_block', 'gutenberg_render_layout_support_flag', 10, 2 );
// Register the block support. (overrides core one).
WP_Block_Supports::get_instance()->register(
'layout',
array(
'register_attribute' => 'gutenberg_register_layout_support',
)
);
if ( function_exists( 'wp_render_layout_support_flag' ) ) {
remove_filter( 'render_block', 'wp_render_layout_support_flag' );
}
add_filter( 'render_block', 'gutenberg_render_layout_support_flag', 10, 2 );

/**
* For themes without theme.json file, make sure
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,35 @@ import { useSelect } from '@wordpress/data';
*/
import { useLayout } from '../block-list/layout';
import { store as blockEditorStore } from '../../store';
import { getLayoutType } from '../../layouts';

const DEFAULT_CONTROLS = [ 'left', 'center', 'right', 'wide', 'full' ];
const WIDE_CONTROLS = [ 'wide', 'full' ];

export default function useAvailableAlignments( controls = DEFAULT_CONTROLS ) {
const { wideControlsEnabled = false } = useSelect( ( select ) => {
const { getSettings } = select( blockEditorStore );
const settings = getSettings();
return {
wideControlsEnabled: settings.alignWide,
};
}, [] );
const { wideControlsEnabled = false, themeSupportsLayout } = useSelect(
( select ) => {
const { getSettings } = select( blockEditorStore );
const settings = getSettings();
return {
wideControlsEnabled: settings.alignWide,
themeSupportsLayout: settings.supportsLayout,
};
},
[]
);
const layout = useLayout();
const supportsAlignments = layout.type === 'default';
const layoutType = getLayoutType( layout?.type );
const layoutAlignments = layoutType.getAlignments( layout );

if ( themeSupportsLayout ) {
return layoutAlignments.filter( ( control ) =>
controls.includes( control )
);
}

if ( ! supportsAlignments ) {
// Starting here, it's the fallback for themes not supporting the layout config.
if ( layoutType.name !== 'default' ) {
return [];
}
const { alignments: availableAlignments = DEFAULT_CONTROLS } = layout;
Expand Down
60 changes: 11 additions & 49 deletions packages/block-editor/src/components/block-list/layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,15 @@
*/
import { createContext, useContext } from '@wordpress/element';

/**
* Internal dependencies
*/
import { getLayoutType } from '../../layouts';

export const defaultLayout = { type: 'default' };

const Layout = createContext( defaultLayout );

function appendSelectors( selectors, append ) {
// Ideally we shouldn't need the `.editor-styles-wrapper` increased specificity here
// The problem though is that we have a `.editor-styles-wrapper p { margin: reset; }` style
// it's used to reset the default margin added by wp-admin to paragraphs
// so we need this to be higher speficity otherwise, it won't be applied to paragraphs inside containers
// When the post editor is fully iframed, this extra classname could be removed.

return selectors
.split( ',' )
.map(
( subselector ) =>
`.editor-styles-wrapper ${ subselector } ${ append }`
)
.join( ',' );
}

/**
* Allows to define the layout.
*/
Expand All @@ -35,39 +24,12 @@ export function useLayout() {
return useContext( Layout );
}

export function LayoutStyle( { selector, layout = {} } ) {
const { contentSize, wideSize } = layout;

let style =
!! contentSize || !! wideSize
? `
${ appendSelectors( selector, '> *' ) } {
max-width: ${ contentSize ?? wideSize };
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;
}
`
: '';

style += `
${ appendSelectors( selector, '> [data-align="left"]' ) } {
float: left;
margin-right: 2em;
}
export function LayoutStyle( { layout = {}, ...props } ) {
const layoutType = getLayoutType( layout.type );

${ appendSelectors( selector, '> [data-align="right"]' ) } {
float: right;
margin-left: 2em;
}
`;
if ( layoutType ) {
return <layoutType.save layout={ layout } { ...props } />;
}

return <style>{ style }</style>;
return null;
}
9 changes: 0 additions & 9 deletions packages/block-editor/src/components/block-list/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -251,15 +251,6 @@
&[data-clear="true"] {
float: none;
}

// This essentially duplicates the mobile styles for the appender component.
// It would be nice to be able to use element queries in that component instead https://github.com/tomhodgins/element-queries-spec
.block-editor-block-list__layout {
.block-editor-default-block-appender .block-editor-inserter {
left: auto;
right: $grid-unit-10;
}
}
}

.is-outline-mode .block-editor-block-list__block:not(.remove-outline) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/**
* External dependencies
*/
import classnames from 'classnames';

/**
* WordPress dependencies
*/
Expand Down Expand Up @@ -37,7 +42,9 @@ export function DefaultBlockAppender( {
return (
<div
data-root-client-id={ rootClientId || '' }
className="block-editor-default-block-appender"
className={ classnames( 'block-editor-default-block-appender', {
'has-visible-prompt': showPrompt,
} ) }
>
<p
tabIndex="0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@
// Left side.
.block-editor-block-list__empty-block-inserter,
.block-editor-default-block-appender .block-editor-inserter {
right: $grid-unit-10; // Show to the right on mobile.

@include break-small {
display: flex;
height: 100%;
Expand All @@ -49,3 +47,8 @@
display: none;
}
}

.block-editor-default-block-appender.has-visible-prompt .block-editor-inserter,
.block-editor-block-list__empty-block-inserter {
right: 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

exports[`DefaultBlockAppender should append a default block when input focused 1`] = `
<div
className="block-editor-default-block-appender"
className="block-editor-default-block-appender has-visible-prompt"
data-root-client-id=""
>
<p
Expand Down Expand Up @@ -38,7 +38,7 @@ exports[`DefaultBlockAppender should append a default block when input focused 1

exports[`DefaultBlockAppender should match snapshot 1`] = `
<div
className="block-editor-default-block-appender"
className="block-editor-default-block-appender has-visible-prompt"
data-root-client-id=""
>
<p
Expand Down
3 changes: 2 additions & 1 deletion packages/block-editor/src/components/inner-blocks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ function UncontrolledInnerBlocks( props ) {
allowedBlocks,
templateLock,
captureToolbars,
orientation
orientation,
__experimentalLayout
);

useInnerBlockTemplateSync(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import isShallowEqual from '@wordpress/is-shallow-equal';
* Internal dependencies
*/
import { store as blockEditorStore } from '../../store';
import { getLayoutType } from '../../layouts';

/**
* This hook is a side effect which updates the block-editor store when changes
Expand All @@ -27,13 +28,15 @@ import { store as blockEditorStore } from '../../store';
* the child block.
* @param {string} orientation The direction in which the block
* should face.
* @param {Object} layout The layout object for the block container.
*/
export default function useNestedSettingsUpdate(
clientId,
allowedBlocks,
templateLock,
captureToolbars,
orientation
orientation,
layout
) {
const { updateBlockListSettings } = useDispatch( blockEditorStore );

Expand Down Expand Up @@ -71,8 +74,13 @@ export default function useNestedSettingsUpdate(
newSettings.__experimentalCaptureToolbars = captureToolbars;
}

// Orientation depends on layout,
// ideally the separate orientation prop should be deprecated.
if ( orientation !== undefined ) {
newSettings.orientation = orientation;
} else {
const layoutType = getLayoutType( layout?.type );
newSettings.orientation = layoutType.getOrientation( layout );
}

if ( ! isShallowEqual( blockListSettings, newSettings ) ) {
Expand All @@ -87,5 +95,6 @@ export default function useNestedSettingsUpdate(
captureToolbars,
orientation,
updateBlockListSettings,
layout,
] );
}
Loading

0 comments on commit bf405a3

Please sign in to comment.