Skip to content
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

Adapt flex child controls for Spacer. #49362

Merged
merged 9 commits into from
Apr 19, 2023
13 changes: 13 additions & 0 deletions packages/block-editor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,19 @@ _Returns_

- `string|null`: A font-size value using clamp().

### getCustomValueFromPreset
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I totally forgot that we're of course working in a different package when making changes to the Spacer block, so this function needs to be exported now.

Shall we rename it to getSpacingCustomValueFromPreset or getCustomSpacingValueFromPreset since it's being exported from the block editor package? That way if we have other functions for different kinds of presets the need to be exported in the future, we won't have a collision?

That said, from a quick search I see that the native version of index.native.js for the block editor package is already exporting this function as-is here: https://github.com/WordPress/gutenberg/blob/trunk/packages/block-editor/src/components/index.native.js#L79.

So, perhaps another option would be to leave it as-is and if in the future we need a more generic getCustomValueFromPreset to be exported that handles other kinds of presets, then we could add a parent function that internally calls the existing spacing one.

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd leave it as is for now, and then perhaps if/when we need to handle other types of presets, we can reimplement this function to deal with any preset, so we don't have to change existing usage (we'd probably want to move it out of the spacing utils if we did that of course).


Converts a spacing preset into a custom value.

_Parameters_

- _value_ `string`: Value to convert
- _spacingSizes_ `Array`: Array of the current spacing preset objects

_Returns_

- `string`: Mapping of the spacing preset to its equivalent custom value.

### getFontSize

Returns the font size object based on an array of named font sizes and the namedFontSize and customFontSize values. If namedFontSize is undefined or not found in fontSizes an object with just the size value based on customFontSize is returned.
Expand Down
1 change: 1 addition & 0 deletions packages/block-editor/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export { default as __experimentalSpacingSizesControl } from './spacing-sizes-co
export {
getSpacingPresetCssVar,
isValueSpacingPreset,
getCustomValueFromPreset,
} from './spacing-sizes-control/utils';
/*
* Content Related Components
Expand Down
175 changes: 157 additions & 18 deletions packages/block-library/src/spacer/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import classnames from 'classnames';
*/
import {
useBlockProps,
useSetting,
getCustomValueFromPreset,
getSpacingPresetCssVar,
store as blockEditorStore,
} from '@wordpress/block-editor';
Expand Down Expand Up @@ -92,13 +94,20 @@ const SpacerEdit = ( {
} );
const { orientation } = context;
const { orientation: parentOrientation, type } = parentLayout || {};
// Check if the spacer is inside a flex container.
const isFlexLayout = type === 'flex';
// If the spacer is inside a flex container, it should either inherit the orientation
// of the parent or use the flex default orientation.
const inheritedOrientation =
! parentOrientation && type === 'flex'
! parentOrientation && isFlexLayout
? 'horizontal'
: parentOrientation || orientation;
const { height, width } = attributes;
const { height, width, style: blockStyle = {} } = attributes;

const { layout = {} } = blockStyle;
const { selfStretch, flexSize } = layout;

const spacingSizes = useSetting( 'spacing.spacingSizes' );

const [ isResizing, setIsResizing ] = useState( false );
const [ temporaryHeight, setTemporaryHeight ] = useState( null );
Expand All @@ -110,32 +119,80 @@ const SpacerEdit = ( {
const handleOnVerticalResizeStop = ( newHeight ) => {
onResizeStop();

if ( isFlexLayout ) {
setAttributes( {
style: {
...blockStyle,
layout: {
...layout,
flexSize: newHeight,
selfStretch: 'fixed',
},
},
} );
}

setAttributes( { height: newHeight } );
setTemporaryHeight( null );
};

const handleOnHorizontalResizeStop = ( newWidth ) => {
onResizeStop();

if ( isFlexLayout ) {
setAttributes( {
style: {
...blockStyle,
layout: {
...layout,
flexSize: newWidth,
selfStretch: 'fixed',
},
},
} );
}

setAttributes( { width: newWidth } );
setTemporaryWidth( null );
};

const getHeightForVerticalBlocks = () => {
if ( isFlexLayout ) {
return undefined;
}
return temporaryHeight || getSpacingPresetCssVar( height ) || undefined;
};

const getWidthForHorizontalBlocks = () => {
if ( isFlexLayout ) {
return undefined;
}
return temporaryWidth || getSpacingPresetCssVar( width ) || undefined;
};

const sizeConditionalOnOrientation =
inheritedOrientation === 'horizontal'
? temporaryWidth || flexSize
: temporaryHeight || flexSize;

const style = {
height:
inheritedOrientation === 'horizontal'
? 24
: temporaryHeight ||
getSpacingPresetCssVar( height ) ||
undefined,
: getHeightForVerticalBlocks(),
width:
inheritedOrientation === 'horizontal'
? temporaryWidth || getSpacingPresetCssVar( width ) || undefined
? getWidthForHorizontalBlocks()
: undefined,
// In vertical flex containers, the spacer shrinks to nothing without a minimum width.
minWidth:
inheritedOrientation === 'vertical' && type === 'flex'
inheritedOrientation === 'vertical' && isFlexLayout
? 48
: undefined,
// Add flex-basis so temporary sizes are respected.
flexBasis: isFlexLayout ? sizeConditionalOnOrientation : undefined,
// Remove flex-grow when resizing.
flexGrow: isFlexLayout && isResizing ? 0 : undefined,
};

const resizableBoxWithOrientation = ( blockOrientation ) => {
Expand Down Expand Up @@ -191,13 +248,93 @@ const SpacerEdit = ( {
};

useEffect( () => {
if ( inheritedOrientation === 'horizontal' && ! width ) {
if (
isFlexLayout &&
selfStretch !== 'fill' &&
selfStretch !== 'fit' &&
! flexSize
) {
if ( inheritedOrientation === 'horizontal' ) {
// If spacer is moving from a vertical container to a horizontal container,
// it might not have width but have height instead.
const newSize =
getCustomValueFromPreset( width, spacingSizes ) ||
getCustomValueFromPreset( height, spacingSizes ) ||
'100px';
setAttributes( {
width: '0px',
style: {
...blockStyle,
layout: {
...layout,
flexSize: newSize,
selfStretch: 'fixed',
},
},
} );
} else {
const newSize =
getCustomValueFromPreset( height, spacingSizes ) ||
getCustomValueFromPreset( width, spacingSizes ) ||
'100px';
setAttributes( {
height: '0px',
style: {
...blockStyle,
layout: {
...layout,
flexSize: newSize,
selfStretch: 'fixed',
},
},
} );
}
} else if (
isFlexLayout &&
( selfStretch === 'fill' || selfStretch === 'fit' )
) {
if ( inheritedOrientation === 'horizontal' ) {
setAttributes( {
width: undefined,
} );
} else {
setAttributes( {
height: undefined,
} );
}
} else if ( ! isFlexLayout && ( selfStretch || flexSize ) ) {
if ( inheritedOrientation === 'horizontal' ) {
setAttributes( {
width: flexSize,
} );
} else {
setAttributes( {
height: flexSize,
} );
}
setAttributes( {
height: '0px',
width: '72px',
style: {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One final nit (and not a blocker), but I just noticed that sometimes the serialized block markup contains an empty layout object within style. We can use cleanEmptyObject to remove these, e.g. like in the Cover block here:

style: cleanEmptyObject( {

It doesn't cause any block validation issues to retain the empty objects in the block markup, though, so I really don't think it's a blocker.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Funnily enough, running the object passed to setAttributes through cleanEmptyObject results in the layout attributes not being unset at all, I'm not sure why. I might leave it as is and avoid going down a rabbit hole with this 😅

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might leave it as is and avoid going down a rabbit hole with this 😅

Good thinking! I'm in support of the idea of merging this now that it's in a good and working state, and we can look at further tweaks in isolation 👍

...blockStyle,
layout: {
...layout,
flexSize: undefined,
selfStretch: undefined,
},
},
} );
}
}, [] );
}, [
blockStyle,
flexSize,
height,
inheritedOrientation,
isFlexLayout,
layout,
selfStretch,
setAttributes,
spacingSizes,
width,
] );

return (
<>
Expand All @@ -211,13 +348,15 @@ const SpacerEdit = ( {
>
{ resizableBoxWithOrientation( inheritedOrientation ) }
</View>
<SpacerControls
setAttributes={ setAttributes }
height={ temporaryHeight || height }
width={ temporaryWidth || width }
orientation={ inheritedOrientation }
isResizing={ isResizing }
/>
{ ! isFlexLayout && (
<SpacerControls
setAttributes={ setAttributes }
height={ temporaryHeight || height }
width={ temporaryWidth || width }
orientation={ inheritedOrientation }
isResizing={ isResizing }
/>
) }
</>
);
};
Expand Down