-
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 image block aspect ratio control #51545
Merged
Merged
Changes from all commits
Commits
Show all changes
116 commits
Select commit
Hold shift + click to select a range
a3796d8
Simplify ImageSizeControl by using Auto as a placeholder
d931e04
Rename imageWidth and imageHeight props to naturalWidth and naturalHe…
767f942
Convert NumberControl onChange values to Numbers
f2d51bb
Simplify LatestPostsEdit to use updated ImageSizeControl
423127c
Add JSDoc types for debugging
0fda291
Remove unnecessary noop
ff2d89c
Fix possible undefined values in NumberControl onChange
5df76a0
Fix onChangeImage param type which may be undefined
6912a67
Rename OnChange callback prop
1926516
Inline JSDoc props instead of new object
1bc922a
Simplify handing undefined and NaN in onChange
7cf50dc
Revert prop name change since this isn't a private API
f0a3730
Add a privateApis export for experimental ImageSizeControl
0c8c296
Use the privateApis version of ImageSizeControl
f46967c
Add deprecation notice to the original component
7c33045
Revert image-size-control and create image-dimensions-control instead
d13b872
Re-add deprecation notice to image-size-control
d6021c9
Try making a whole new component
64177d9
Revert changes to image, latest-posts, and media-text blocks
5105298
Organize and update the dimensions tool panel item
6d9ba06
Reword size help text
5b80d23
Reorganize into reusable components
0e327c6
Add stories for other individual tools
78f42e5
Update stories path
b2837f2
Remove SelectControl __next prop
2e5d3da
Pass through isShownByDefault to ResolutionTool
1f777ce
Remove unused scss
50514be
Deprecate experimental ImageSizeControl
c44d8a4
Simplify ScaleTool onChange
aaa00fe
Add better defaults for value and onChange
45b5f4f
Fix circular dependency
02baab7
Update comment about auto and custom aspect ratios
8c6da27
Add JSDoc types for ScaleTool
251b241
Add JSDoc types for WidthHeightTool
b123a68
Add default value and onChange for WidthHeightTool
bd6b16e
Remove unused import
7243878
Add aspectRatio to image block attributes
8074a11
Add scale to image block attributes
44d7ac5
Update JSDoc comment
ajlende 0a2c29f
Add dimensions tool to image block
ajlende da2de5e
Rename naturalAspectRatio for clarity
ajlende ca90175
Fix aspect-ratio-tool lint
ajlende 677fa10
Fix scale-tool lint
ajlende 6c427f9
Fix width-height-tool lint
ajlende 5995b0f
Fix dimensions-tool lint
ajlende 553aefc
Fix resolution-tool lint
ajlende b586e13
Add @emption/styled to block-editor
ajlende 838421a
Fix image block lint
ajlende ddd6568
Update components changelog
ajlende 960f3cc
Fix AspectRatioTool reference
ajlende 3599b94
Support 'auto' in width-height-tool
ajlende c796ed2
Make null/undefined values mean 'auto' instead of defaultValue in asp…
ajlende 2786075
Add deprecation for image block
ajlende dcdf401
Fix ResizableBox interactions
ajlende 5daea64
Add comments for default values
ajlende 0725e22
Fix ResizableBox with auto w/h
ajlende b036600
Clear aspect-ratio on resize
ajlende d31da45
Add TODO comment for ResolutionTool defaultValue
ajlende d592833
Move the scale hide/show into dimensions controls
ajlende 38012d4
Add first test
ajlende c32612e
Fix scale being set after it was deleted
ajlende 725cb49
WIP writing tests
ajlende 27c2e91
Update test
ajlende ab5089e
UI tweaks
richtabor 8327aba
Move alt text as ToolsPanelItem
richtabor e1e5c43
Tweak default scale option help text
richtabor df0be1e
Only use contain and cover for image scale options
richtabor 914bc17
Update test
ajlende 5edf2b4
Test the remaining callback values
ajlende e9ce885
Add comment about toStrictEqual
ajlende 4137c7b
Add test for setting custom aspect ratio and then resetting
ajlende 9b4712f
Move custom scaleOptions to the image block
ajlende 1687ff7
Remember last aspect ratio so it can be restored when with/height are…
ajlende e49439b
Remove unused import
ajlende 031beb4
Format code
ajlende c2a06a5
Remove image w/h reset when a new image is added
ajlende f28f6c6
Use UnitControl's default units instead of spacing.units
ajlende 3dd4db4
Provide the complete set of object-fit options by default
ajlende 1e68dae
Update TODO that will be committed
ajlende 9d3728b
Clean up evalAspectRatio and add docs
ajlende a08ea96
Someone can file a bug report if offsetWidth/offsetHeight causes issues
ajlende eeaa22d
I couldn't figure out why height depended on having a custom border, …
ajlende 8890bf5
Update docs for image block
ajlende 119b59d
Update comment about default value
ajlende fe0abab
Fix redundant wording
ajlende 236bda2
I think the img width and height attributes can be removed if they're…
ajlende a80fe1f
Update package-lock.json with @emotion/styled dependency
ajlende 5f41483
Update mock calls for test example
ajlende cf25eb5
Simplify test values
ajlende 136f139
Consolidate mock calls expect
ajlende 66111d1
Require defaultScale and defaultAspectRatio for DimensionsTool
ajlende 73f3757
Add DimensionsTool tests for all custom transitions
ajlende 6ad1e81
Remove comment about matching aspect ratio options
ajlende d1800fb
Remove redundant check in tests
ajlende eb47133
Add comments to defaultAspectRatio and defaultScale
ajlende 26fc552
Organize tests by which field is being updated
ajlende fc78634
Fix type conversion
ajlende c723a0d
Add state diagram for last two tests
ajlende 92e9aff
Refactor and fix some tests
ajlende 02dd139
Fix and simplify WidthHeightTool onChange
ajlende 6cbe62d
Remove default scale option in image block.json
ajlende b5d493a
Simplify DimensionsTool onChange logic
ajlende 140c14c
Update block deprecations with width and height
ajlende 5ca29e7
Revert image block width and height attributes to numbers since we on…
ajlende db6beb8
Revert "Update block deprecations with width and height"
ajlende ddc0f87
Prevent NaN width/height
ajlende a508113
Fix DimensionTool width/height units
ajlende 83c69fc
Fix JSDoc Dimenstions width/height types
ajlende 4b930a8
No default needed for ResolutionTool
ajlende 10b4363
Fix drag handle aspect ratio reset
ajlende 341cd50
Simplify null checks
ajlende 9baf030
Stop using pxWidth and pxHeight
ajlende e6e7af7
Remove e2e tests that reference the scale button that was removed
ajlende 5cdf2d2
Fix image scaling for small images
ajlende 73569a8
Try fixing aspectRatio only images
ajlende c127347
Update test to respect the new aspect ratio behavior
jeryj File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
124 changes: 124 additions & 0 deletions
124
packages/block-editor/src/components/dimensions-tool/aspect-ratio-tool.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { | ||
SelectControl, | ||
__experimentalToolsPanelItem as ToolsPanelItem, | ||
} from '@wordpress/components'; | ||
import { __, _x } from '@wordpress/i18n'; | ||
|
||
/** | ||
* @typedef {import('@wordpress/components/build-types/select-control/types').SelectControlProps} SelectControlProps | ||
*/ | ||
|
||
/** | ||
* @type {SelectControlProps[]} | ||
*/ | ||
export const DEFAULT_ASPECT_RATIO_OPTIONS = [ | ||
{ | ||
label: _x( 'Original', 'Aspect ratio option for dimensions control' ), | ||
value: 'auto', | ||
}, | ||
{ | ||
label: _x( | ||
'Square - 1:1', | ||
'Aspect ratio option for dimensions control' | ||
), | ||
value: '1', | ||
}, | ||
{ | ||
label: _x( | ||
'Standard - 4:3', | ||
'Aspect ratio option for dimensions control' | ||
), | ||
value: '4/3', | ||
}, | ||
{ | ||
label: _x( | ||
'Portrait - 3:4', | ||
'Aspect ratio option for dimensions control' | ||
), | ||
value: '3/4', | ||
}, | ||
{ | ||
label: _x( | ||
'Classic - 3:2', | ||
'Aspect ratio option for dimensions control' | ||
), | ||
value: '3/2', | ||
}, | ||
{ | ||
label: _x( | ||
'Classic Portrait - 2:3', | ||
'Aspect ratio option for dimensions control' | ||
), | ||
value: '2/3', | ||
}, | ||
{ | ||
label: _x( | ||
'Wide - 16:9', | ||
'Aspect ratio option for dimensions control' | ||
), | ||
value: '16/9', | ||
}, | ||
{ | ||
label: _x( | ||
'Tall - 9:16', | ||
'Aspect ratio option for dimensions control' | ||
), | ||
value: '9/16', | ||
}, | ||
{ | ||
label: _x( 'Custom', 'Aspect ratio option for dimensions control' ), | ||
value: 'custom', | ||
disabled: true, | ||
hidden: true, | ||
}, | ||
]; | ||
|
||
/** | ||
* @callback AspectRatioToolPropsOnChange | ||
* @param {string} [value] New aspect ratio value. | ||
* @return {void} No return. | ||
*/ | ||
|
||
/** | ||
* @typedef {Object} AspectRatioToolProps | ||
* @property {string} [panelId] ID of the panel this tool is associated with. | ||
* @property {string} [value] Current aspect ratio value. | ||
* @property {AspectRatioToolPropsOnChange} [onChange] Callback to update the aspect ratio value. | ||
* @property {SelectControlProps[]} [options] Aspect ratio options. | ||
* @property {string} [defaultValue] Default aspect ratio value. | ||
* @property {boolean} [isShownByDefault] Whether the tool is shown by default. | ||
*/ | ||
|
||
export default function AspectRatioTool( { | ||
panelId, | ||
value, | ||
onChange = () => {}, | ||
options = DEFAULT_ASPECT_RATIO_OPTIONS, | ||
defaultValue = DEFAULT_ASPECT_RATIO_OPTIONS[ 0 ].value, | ||
isShownByDefault = true, | ||
} ) { | ||
// Match the CSS default so if the value is used directly in CSS it will look correct in the control. | ||
const displayValue = value ?? 'auto'; | ||
|
||
return ( | ||
<ToolsPanelItem | ||
hasValue={ () => displayValue !== defaultValue } | ||
label={ __( 'Aspect ratio' ) } | ||
onDeselect={ () => onChange( undefined ) } | ||
isShownByDefault={ isShownByDefault } | ||
panelId={ panelId } | ||
> | ||
<SelectControl | ||
label={ __( 'Aspect ratio' ) } | ||
value={ displayValue } | ||
options={ options } | ||
onChange={ onChange } | ||
size={ '__unstable-large' } | ||
__nextHasNoMarginBottom | ||
/> | ||
</ToolsPanelItem> | ||
); | ||
} |
212 changes: 212 additions & 0 deletions
212
packages/block-editor/src/components/dimensions-tool/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { useState } from '@wordpress/element'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import AspectRatioTool from './aspect-ratio-tool'; | ||
import ScaleTool from './scale-tool'; | ||
import WidthHeightTool from './width-height-tool'; | ||
|
||
/** | ||
* @typedef {import('@wordpress/components/build-types/select-control/types').SelectControlProps} SelectControlProps | ||
*/ | ||
|
||
/** | ||
* @typedef {import('@wordpress/components/build-types/unit-control/types').WPUnitControlUnit} WPUnitControlUnit | ||
*/ | ||
|
||
/** | ||
* @typedef {Object} Dimensions | ||
* @property {string} [width] CSS width property. | ||
* @property {string} [height] CSS height property. | ||
* @property {string} [scale] CSS object-fit property. | ||
* @property {string} [aspectRatio] CSS aspect-ratio property. | ||
*/ | ||
|
||
/** | ||
* @callback DimensionsControlsOnChange | ||
* @param {Dimensions} nextValue | ||
* @return {void} | ||
*/ | ||
|
||
/** | ||
* @typedef {Object} DimensionsControlsProps | ||
* @property {string} [panelId] ID of the panel that contains the controls. | ||
* @property {Dimensions} [value] Current dimensions values. | ||
* @property {DimensionsControlsOnChange} [onChange] Callback to update the dimensions values. | ||
* @property {SelectControlProps[]} [aspectRatioOptions] Aspect ratio options. | ||
* @property {SelectControlProps[]} [scaleOptions] Scale options. | ||
* @property {WPUnitControlUnit[]} [unitsOptions] Units options. | ||
*/ | ||
|
||
/** | ||
* Component that renders controls to edit the dimensions of an image or container. | ||
* | ||
* @param {DimensionsControlsProps} props The component props. | ||
* | ||
* @return {WPElement} The dimensions controls. | ||
*/ | ||
function DimensionsTool( { | ||
panelId, | ||
value = {}, | ||
onChange = () => {}, | ||
aspectRatioOptions, // Default options handled by AspectRatioTool. | ||
defaultAspectRatio = 'auto', // Match CSS default value for aspect-ratio. | ||
scaleOptions, // Default options handled by ScaleTool. | ||
defaultScale = 'fill', // Match CSS default value for object-fit. | ||
unitsOptions, // Default options handled by UnitControl. | ||
} ) { | ||
// Coerce undefined and CSS default values to be null. | ||
const width = | ||
value.width === undefined || value.width === 'auto' | ||
? null | ||
: value.width; | ||
const height = | ||
value.height === undefined || value.height === 'auto' | ||
? null | ||
: value.height; | ||
const aspectRatio = | ||
value.aspectRatio === undefined || value.aspectRatio === 'auto' | ||
? null | ||
: value.aspectRatio; | ||
const scale = | ||
value.scale === undefined || value.scale === 'fill' | ||
? null | ||
: value.scale; | ||
|
||
// Keep track of state internally, so when the value is cleared by means | ||
// other than directly editing that field, it's easier to restore the | ||
// previous value. | ||
const [ lastScale, setLastScale ] = useState( scale ); | ||
const [ lastAspectRatio, setLastAspectRatio ] = useState( aspectRatio ); | ||
|
||
// 'custom' is not a valid value for CSS aspect-ratio, but it is used in the | ||
// dropdown to indicate that setting both the width and height is the same | ||
// as a custom aspect ratio. | ||
const aspectRatioValue = width && height ? 'custom' : lastAspectRatio; | ||
|
||
const showScaleControl = aspectRatio || ( width && height ); | ||
|
||
return ( | ||
<> | ||
<AspectRatioTool | ||
panelId={ panelId } | ||
options={ aspectRatioOptions } | ||
defaultValue={ defaultAspectRatio } | ||
value={ aspectRatioValue } | ||
onChange={ ( nextAspectRatio ) => { | ||
const nextValue = { ...value }; | ||
|
||
// 'auto' is CSS default, so it gets treated as null. | ||
nextAspectRatio = | ||
nextAspectRatio === 'auto' ? null : nextAspectRatio; | ||
|
||
setLastAspectRatio( nextAspectRatio ); | ||
|
||
// Update aspectRatio. | ||
if ( ! nextAspectRatio ) { | ||
delete nextValue.aspectRatio; | ||
} else { | ||
nextValue.aspectRatio = nextAspectRatio; | ||
} | ||
|
||
// Auto-update scale. | ||
if ( ! nextAspectRatio ) { | ||
delete nextValue.scale; | ||
} else if ( lastScale ) { | ||
nextValue.scale = lastScale; | ||
} else { | ||
nextValue.scale = defaultScale; | ||
setLastScale( defaultScale ); | ||
} | ||
|
||
// Auto-update width and height. | ||
if ( nextAspectRatio && width && height ) { | ||
delete nextValue.height; | ||
} | ||
|
||
onChange( nextValue ); | ||
} } | ||
/> | ||
{ showScaleControl && ( | ||
<ScaleTool | ||
panelId={ panelId } | ||
options={ scaleOptions } | ||
defaultValue={ defaultScale } | ||
value={ lastScale } | ||
onChange={ ( nextScale ) => { | ||
const nextValue = { ...value }; | ||
|
||
// 'fill' is CSS default, so it gets treated as null. | ||
nextScale = nextScale === 'fill' ? null : nextScale; | ||
|
||
setLastScale( nextScale ); | ||
|
||
// Update scale. | ||
if ( ! nextScale ) { | ||
delete nextValue.scale; | ||
} else { | ||
nextValue.scale = nextScale; | ||
} | ||
|
||
onChange( nextValue ); | ||
} } | ||
/> | ||
) } | ||
<WidthHeightTool | ||
panelId={ panelId } | ||
units={ unitsOptions } | ||
value={ { width, height } } | ||
onChange={ ( { width: nextWidth, height: nextHeight } ) => { | ||
const nextValue = { ...value }; | ||
|
||
// 'auto' is CSS default, so it gets treated as null. | ||
nextWidth = nextWidth === 'auto' ? null : nextWidth; | ||
nextHeight = nextHeight === 'auto' ? null : nextHeight; | ||
|
||
// Update width. | ||
if ( ! nextWidth ) { | ||
delete nextValue.width; | ||
} else { | ||
nextValue.width = nextWidth; | ||
} | ||
|
||
// Update height. | ||
if ( ! nextHeight ) { | ||
delete nextValue.height; | ||
} else { | ||
nextValue.height = nextHeight; | ||
} | ||
|
||
// Auto-update aspectRatio. | ||
if ( nextWidth && nextHeight ) { | ||
delete nextValue.aspectRatio; | ||
} else if ( lastAspectRatio ) { | ||
nextValue.aspectRatio = lastAspectRatio; | ||
} else { | ||
// No setting defaultAspectRatio here, because | ||
// aspectRatio is optional in this scenario, | ||
// unlike scale. | ||
} | ||
|
||
// Auto-update scale. | ||
if ( ! lastAspectRatio && !! nextWidth !== !! nextHeight ) { | ||
delete nextValue.scale; | ||
} else if ( lastScale ) { | ||
nextValue.scale = lastScale; | ||
} else { | ||
nextValue.scale = defaultScale; | ||
setLastScale( defaultScale ); | ||
} | ||
|
||
onChange( nextValue ); | ||
} } | ||
/> | ||
</> | ||
); | ||
} | ||
|
||
export default DimensionsTool; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
This results in an unexpected transitive peer dependency on
@emotion/react
. See #52474 for details.