-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for column and row span in grid children. #58539
Changes from 24 commits
f0e6c77
d746fb5
514c16c
578900a
1832758
cb72ade
2a113cd
43dfae2
35bff74
a0449e9
38c7c27
ea39794
7518c85
a70ddeb
23b8fc8
9d86505
8edd002
50f5f1b
d69b14d
a8fc9f9
f253b13
16b5753
24a76f9
cf964d9
4efe2ec
56f2934
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -476,7 +476,10 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support | |
|
||
$layout_styles[] = array( | ||
'selector' => $selector, | ||
'declarations' => array( 'grid-template-columns' => 'repeat(auto-fill, minmax(min(' . $minimum_column_width . ', 100%), 1fr))' ), | ||
'declarations' => array( | ||
'grid-template-columns' => 'repeat(auto-fill, minmax(min(' . $minimum_column_width . ', 100%), 1fr))', | ||
'container-type' => 'inline-size', | ||
), | ||
); | ||
} | ||
|
||
|
@@ -557,44 +560,95 @@ function gutenberg_incremental_id_per_prefix( $prefix = '' ) { | |
function gutenberg_render_layout_support_flag( $block_content, $block ) { | ||
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] ); | ||
$block_supports_layout = block_has_support( $block_type, array( 'layout' ), false ) || block_has_support( $block_type, array( '__experimentalLayout' ), false ); | ||
$layout_from_parent = $block['attrs']['style']['layout']['selfStretch'] ?? null; | ||
// If there is any value in style -> layout, the block has a child layout. | ||
$child_layout = $block['attrs']['style']['layout'] ?? null; | ||
|
||
if ( ! $block_supports_layout && ! $layout_from_parent ) { | ||
if ( ! $block_supports_layout && ! $child_layout ) { | ||
return $block_content; | ||
} | ||
|
||
$outer_class_names = array(); | ||
$outer_class_names = array(); | ||
$container_content_class = wp_unique_id( 'wp-container-content-' ); | ||
$child_layout_declarations = array(); | ||
$child_layout_styles = array(); | ||
|
||
if ( 'fixed' === $layout_from_parent || 'fill' === $layout_from_parent ) { | ||
$container_content_class = wp_unique_id( 'wp-container-content-' ); | ||
$self_stretch = isset( $block['attrs']['style']['layout']['selfStretch'] ) ? $block['attrs']['style']['layout']['selfStretch'] : null; | ||
|
||
$child_layout_styles = array(); | ||
if ( 'fixed' === $self_stretch && isset( $block['attrs']['style']['layout']['flexSize'] ) ) { | ||
$child_layout_declarations['flex-basis'] = $block['attrs']['style']['layout']['flexSize']; | ||
$child_layout_declarations['box-sizing'] = 'border-box'; | ||
} elseif ( 'fill' === $self_stretch ) { | ||
$child_layout_declarations['flex-grow'] = '1'; | ||
} | ||
|
||
if ( 'fixed' === $layout_from_parent && isset( $block['attrs']['style']['layout']['flexSize'] ) ) { | ||
$child_layout_styles[] = array( | ||
'selector' => ".$container_content_class", | ||
'declarations' => array( | ||
'flex-basis' => $block['attrs']['style']['layout']['flexSize'], | ||
'box-sizing' => 'border-box', | ||
), | ||
); | ||
} elseif ( 'fill' === $layout_from_parent ) { | ||
$child_layout_styles[] = array( | ||
'selector' => ".$container_content_class", | ||
'declarations' => array( | ||
'flex-grow' => '1', | ||
), | ||
); | ||
if ( isset( $block['attrs']['style']['layout']['columnSpan'] ) ) { | ||
$column_span = $block['attrs']['style']['layout']['columnSpan']; | ||
$child_layout_declarations['grid-column'] = "span $column_span"; | ||
} | ||
if ( isset( $block['attrs']['style']['layout']['rowSpan'] ) ) { | ||
$row_span = $block['attrs']['style']['layout']['rowSpan']; | ||
$child_layout_declarations['grid-row'] = "span $row_span"; | ||
} | ||
$child_layout_styles[] = array( | ||
'selector' => ".$container_content_class", | ||
'declarations' => $child_layout_declarations, | ||
); | ||
|
||
/** | ||
* If columnSpan is set, and the parent grid is responsive, i.e. if it has a minimumColumnWidth set, | ||
* the columnSpan should be removed on small grids. | ||
*/ | ||
if ( isset( $block['attrs']['style']['layout']['columnSpan'] ) && isset( $block['attrs']['style']['layout']['parentColumnWidth'] ) ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not a blocker for now, since we could always change it in a follow-up if it were possible, but I wonder if there'd be a way to pass down the parent's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm |
||
$column_span_number = floatval( $block['attrs']['style']['layout']['columnSpan'] ); | ||
$parent_column_width = $block['attrs']['style']['layout']['parentColumnWidth']; | ||
$parent_column_value = floatval( $parent_column_width ); | ||
$parent_column_unit = explode( $parent_column_value, $parent_column_width ); | ||
/** | ||
* If there is no unit, the width has somehow been mangled so we reset both unit and value | ||
* to defaults. | ||
* Additionally, the unit should be one of px, rem or em, so that also needs to be checked. | ||
*/ | ||
if ( count( $parent_column_unit ) <= 1 ) { | ||
$parent_column_unit = 'rem'; | ||
$parent_column_value = 12; | ||
} else { | ||
$parent_column_unit = $parent_column_unit[1]; | ||
|
||
if ( ! in_array( $parent_column_unit, array( 'px', 'rem', 'em' ), true ) ) { | ||
$parent_column_unit = 'rem'; | ||
} | ||
} | ||
|
||
gutenberg_style_engine_get_stylesheet_from_css_rules( | ||
$child_layout_styles, | ||
array( | ||
'context' => 'block-supports', | ||
'prettify' => false, | ||
) | ||
/** | ||
* A default gap value is used for this computation because custom gap values may not be | ||
* viable to use in the computation of the container query value. | ||
Comment on lines
+623
to
+624
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This only really impacts the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure, for example, could gap ever be a |
||
*/ | ||
$default_gap_value = 'px' === $parent_column_unit ? 24 : 1.5; | ||
$container_query_value = $column_span_number * $parent_column_value + ( $column_span_number - 1 ) * $default_gap_value; | ||
andrewserong marked this conversation as resolved.
Show resolved
Hide resolved
|
||
$container_query_value = $container_query_value . $parent_column_unit; | ||
andrewserong marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
$child_layout_styles[] = array( | ||
'at_rule' => "@container (max-width: $container_query_value )", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe this'll need a rebase now that #58922 has landed and for this to be updated to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done! |
||
'selector' => ".$container_content_class", | ||
'declarations' => array( | ||
'grid-column' => '1/-1', | ||
), | ||
); | ||
} | ||
|
||
/** | ||
* Add to the style engine store to enqueue and render layout styles. | ||
* Return styles here just to check if any exist. | ||
*/ | ||
$child_css = gutenberg_style_engine_get_stylesheet_from_css_rules( | ||
$child_layout_styles, | ||
array( | ||
'context' => 'block-supports', | ||
'prettify' => false, | ||
) | ||
); | ||
|
||
if ( $child_css ) { | ||
$outer_class_names[] = $container_content_class; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,8 @@ import { | |
__experimentalToggleGroupControl as ToggleGroupControl, | ||
__experimentalToggleGroupControlOption as ToggleGroupControlOption, | ||
__experimentalUnitControl as UnitControl, | ||
__experimentalInputControl as InputControl, | ||
__experimentalHStack as HStack, | ||
} from '@wordpress/components'; | ||
import { __ } from '@wordpress/i18n'; | ||
import { useEffect } from '@wordpress/element'; | ||
|
@@ -38,7 +40,18 @@ export default function ChildLayoutControl( { | |
onChange, | ||
parentLayout, | ||
} ) { | ||
const { selfStretch, flexSize } = childLayout; | ||
const { selfStretch, flexSize, columnSpan, rowSpan } = childLayout; | ||
const { | ||
type: parentLayoutType, | ||
minimumColumnWidth = '12rem', | ||
columnCount, | ||
} = parentLayout; | ||
|
||
/** | ||
* If columnCount is set, the parentColumnwidth isn't needed because | ||
* the grid has a fixed number of columns with responsive widths. | ||
*/ | ||
const parentColumnWidth = columnCount ? null : minimumColumnWidth; | ||
|
||
useEffect( () => { | ||
if ( selfStretch === 'fixed' && ! flexSize ) { | ||
|
@@ -51,49 +64,85 @@ export default function ChildLayoutControl( { | |
|
||
return ( | ||
<> | ||
<ToggleGroupControl | ||
__nextHasNoMarginBottom | ||
size={ '__unstable-large' } | ||
label={ childLayoutOrientation( parentLayout ) } | ||
value={ selfStretch || 'fit' } | ||
help={ helpText( selfStretch, parentLayout ) } | ||
onChange={ ( value ) => { | ||
const newFlexSize = value !== 'fixed' ? null : flexSize; | ||
onChange( { | ||
...childLayout, | ||
selfStretch: value, | ||
flexSize: newFlexSize, | ||
} ); | ||
} } | ||
isBlock={ true } | ||
> | ||
<ToggleGroupControlOption | ||
key={ 'fit' } | ||
value={ 'fit' } | ||
label={ __( 'Fit' ) } | ||
/> | ||
<ToggleGroupControlOption | ||
key={ 'fill' } | ||
value={ 'fill' } | ||
label={ __( 'Fill' ) } | ||
/> | ||
<ToggleGroupControlOption | ||
key={ 'fixed' } | ||
value={ 'fixed' } | ||
label={ __( 'Fixed' ) } | ||
/> | ||
</ToggleGroupControl> | ||
{ selfStretch === 'fixed' && ( | ||
<UnitControl | ||
size={ '__unstable-large' } | ||
onChange={ ( value ) => { | ||
onChange( { | ||
...childLayout, | ||
flexSize: value, | ||
} ); | ||
} } | ||
value={ flexSize } | ||
/> | ||
{ parentLayoutType === 'flex' && ( | ||
<> | ||
<ToggleGroupControl | ||
__nextHasNoMarginBottom | ||
size={ '__unstable-large' } | ||
label={ childLayoutOrientation( parentLayout ) } | ||
value={ selfStretch || 'fit' } | ||
help={ helpText( selfStretch, parentLayout ) } | ||
onChange={ ( value ) => { | ||
const newFlexSize = | ||
value !== 'fixed' ? null : flexSize; | ||
onChange( { | ||
selfStretch: value, | ||
flexSize: newFlexSize, | ||
} ); | ||
} } | ||
isBlock={ true } | ||
> | ||
<ToggleGroupControlOption | ||
key={ 'fit' } | ||
value={ 'fit' } | ||
label={ __( 'Fit' ) } | ||
/> | ||
<ToggleGroupControlOption | ||
key={ 'fill' } | ||
value={ 'fill' } | ||
label={ __( 'Fill' ) } | ||
/> | ||
<ToggleGroupControlOption | ||
key={ 'fixed' } | ||
value={ 'fixed' } | ||
label={ __( 'Fixed' ) } | ||
/> | ||
</ToggleGroupControl> | ||
{ selfStretch === 'fixed' && ( | ||
<UnitControl | ||
size={ '__unstable-large' } | ||
onChange={ ( value ) => { | ||
onChange( { | ||
selfStretch, | ||
flexSize: value, | ||
} ); | ||
} } | ||
value={ flexSize } | ||
/> | ||
) } | ||
</> | ||
) } | ||
{ parentLayoutType === 'grid' && ( | ||
<HStack> | ||
<InputControl | ||
size={ '__unstable-large' } | ||
label={ __( 'Column Span' ) } | ||
type="number" | ||
noisysocks marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, not a blocker for this PR, but it might be good to swap this out for 2024-02-14.15.12.18.mp4Alternately we could potentially add a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm I don't think hiding the spinners will remove the scrolling behaviour, at least playing with storybook it seems to persist. I'm not sure we'd want to remove it in any case? It's default input behaviour so folks will be expecting it to work. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Ah, fair enough! Might just be me who found it a bit confusing 🙂 |
||
onChange={ ( value ) => { | ||
onChange( { | ||
rowSpan, | ||
columnSpan: value, | ||
parentColumnWidth, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it necessary to store There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If it's being cached here will that be a problem if someone goes to update the parent column width after they've set the row/column span, or if they drag and drop between two grids? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's being cached after drag. I'm testing some nested layouts and have dragged a child to another grid parent and it still has the value of the former parent's "layout":{"columnSpan":"3","parentColumnWidth":"300px"}} Is that what you mean? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, that's what I was getting at. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh sorry I didn't see this one. It seems pretty unlikely someone will just drag and drop a block with a column span from one grid to another differently sized grid and not make any further adjustments (and if both grids are the same size it won't be an issue) but I'm open to suggestions if anyone can find a better way to do it! Even if someone does drag and drop a multi-column block between differently sized grids the very worst that can happen is the container query gets a bit wonky and doesn't break at exactly the point it should 🤷 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Oh really? Bugger. That seems like a shortcoming of the add_filter( 'render_block_data', function( $parsed_block, $source_block, $parent_block ) {
$parsed_block['parent_block'] = $parent_block;
}, 10, 3 );
add_filter( 'render_block', function( $block_content, $block ) {
var_dump($block['parent_block']);
}, 10, 2 ); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To be clear, is that something you think should be addressed now, as part of this PR? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
IMO, I don't think it'd be a blocker to landing, but would be good to look at in a follow-up before Grid is taken out of experimental. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok I might do this as a follow-up because if we want to remove the |
||
} ); | ||
} } | ||
value={ columnSpan } | ||
min={ 1 } | ||
/> | ||
<InputControl | ||
size={ '__unstable-large' } | ||
label={ __( 'Row Span' ) } | ||
type="number" | ||
onChange={ ( value ) => { | ||
onChange( { | ||
columnSpan, | ||
parentColumnWidth, | ||
rowSpan: value, | ||
} ); | ||
} } | ||
value={ rowSpan } | ||
min={ 1 } | ||
/> | ||
</HStack> | ||
) } | ||
</> | ||
); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now whenever I see the nullish coalescing operator, all I think is "one day we'll be able to use this properly"! 😄
(This one seems fine to leave in to me since it was already there)