Skip to content

Commit

Permalink
ResponsiveWrapper: use aspect-ratio CSS prop and support SVG elements (
Browse files Browse the repository at this point in the history
…#48573)

* ResponsiveWrapper: use aspect-ratio + intrinsic width/height instead of padding-bottom hack

* Mark naturalHeight and naturalWidth props as optional

* Add SVG example story

* Add more information to README

* Update story description

* CHANGELOG
  • Loading branch information
ciampo authored Mar 7, 2023
1 parent 49c518c commit 83020ae
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 33 deletions.
6 changes: 5 additions & 1 deletion packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
### Enhancements
- `FontSizePicker`: Allow custom units for custom font size control ([#48468](https://github.com/WordPress/gutenberg/pull/48468)).

### Bug Fix

- `ResponsiveWrapper`: use `aspect-ratio` CSS prop, add support for `SVG` elements ([#48573](https://github.com/WordPress/gutenberg/pull/48573).

### Internal

- `Guide`: Convert to TypeScript ([#47493](https://github.com/WordPress/gutenberg/pull/47493)).
Expand All @@ -17,7 +21,7 @@

### Enhancements

- `ToolsPanel`: Separate reset all filter registration from items registration and support global resets ([#48123](https://github.com/WordPress/gutenberg/pull/48123#pullrequestreview-1308386926)).
- `ToolsPanel`: Separate reset all filter registration from items registration and support global resets ([#48123](https://github.com/WordPress/gutenberg/pull/48123)).

### Internal

Expand Down
10 changes: 8 additions & 2 deletions packages/components/src/responsive-wrapper/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ const MyResponsiveWrapper = () => (
);
```

### Usage with `SVG` elements

When passing an `SVG` element as the `<ResponsiveWrapper />`'s child, make sure that it has the `viewbox` and the `preserveAspectRatio` set.

When dealing with SVGs, it may not be possible to derive its `naturalWidth` and `naturalHeight` and therefore passing them as propertied to `<ResponsiveWrapper />`. In this case, the SVG simply keeps scaling up to fill its container, unless the `height` and `width` attributes are specified.

## Props

### `children`: `React.ReactElement`
Expand All @@ -36,10 +42,10 @@ If true, the wrapper will be `span` instead of `div`.

The intrinsic height of the element to wrap. Will be used to determine the aspect ratio.

- Required: Yes
- Required: No

### `naturalWidth`: `number`

The intrinsic width of the element to wrap. Will be used to determine the aspect ratio.

- Required: Yes
- Required: No
35 changes: 18 additions & 17 deletions packages/components/src/responsive-wrapper/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import classnames from 'classnames';
* WordPress dependencies
*/
import { cloneElement, Children } from '@wordpress/element';
import { useResizeObserver } from '@wordpress/compose';

/**
* Internal dependencies
Expand Down Expand Up @@ -36,28 +35,30 @@ function ResponsiveWrapper( {
children,
isInline = false,
}: ResponsiveWrapperProps ) {
const [ containerResizeListener, { width: containerWidth } ] =
useResizeObserver();
if ( Children.count( children ) !== 1 ) {
return null;
}
const imageStyle = {
paddingBottom:
naturalWidth < ( containerWidth ?? 0 )
? naturalHeight
: ( naturalHeight / naturalWidth ) * 100 + '%',
};

const TagName = isInline ? 'span' : 'div';
let aspectRatio;
if ( naturalWidth && naturalHeight ) {
aspectRatio = `${ naturalWidth } / ${ naturalHeight }`;
}

return (
<TagName className="components-responsive-wrapper">
{ containerResizeListener }
<TagName style={ imageStyle } />
{ cloneElement( children, {
className: classnames(
'components-responsive-wrapper__content',
children.props.className
),
} ) }
<div>
{ cloneElement( children, {
className: classnames(
'components-responsive-wrapper__content',
children.props.className
),
style: {
...children.props.style,
aspectRatio,
},
} ) }
</div>
</TagName>
);
}
Expand Down
41 changes: 41 additions & 0 deletions packages/components/src/responsive-wrapper/stories/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,44 @@ Default.args = {
/>
),
};

/**
* When passing an `SVG` element as the `<ResponsiveWrapper />`'s child, make
* sure that it has the `viewbox` and the `preserveAspectRatio` set.
*
* When dealing with SVGs, it may not be possible to derive its `naturalWidth`
* and `naturalHeight` and therefore passing them as propertied to
* `<ResponsiveWrapper />`. In this case, the SVG simply keeps scaling up to fill
* its container, unless the `height` and `width` attributes are specified.
*/
export const WithSVG: ComponentStory< typeof ResponsiveWrapper > =
Template.bind( {} );
WithSVG.args = {
children: (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 280 640"
preserveAspectRatio="xMinYMin meet"
width="280px"
height="640px"
>
<rect
x="0"
y="0"
width="280"
height="640"
style={ { fill: 'blue' } }
/>
<g>
<circle style={ { fill: 'red' } } cx="140" cy="160" r="60" />
<circle style={ { fill: 'yellow' } } cx="140" cy="320" r="60" />
<circle
style={ { fill: '#40CC40' } }
cx="140"
cy="480"
r="60"
/>
</g>
</svg>
),
};
16 changes: 5 additions & 11 deletions packages/components/src/responsive-wrapper/style.scss
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
.components-responsive-wrapper {
position: relative;
max-width: 100%;
&,
& > span {
display: block;
}
display: flex;
align-items: center;
justify-content: center;
}

.components-responsive-wrapper__content {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: block;
max-width: 100%;
width: 100%;
height: 100%;
margin: auto;
}
4 changes: 2 additions & 2 deletions packages/components/src/responsive-wrapper/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ export type ResponsiveWrapperProps = {
/**
* The intrinsic width of the element to wrap. Will be used to determine the aspect ratio.
*/
naturalWidth: number;
naturalWidth?: number;
/**
* The intrinsic height of the element to wrap. Will be used to determine the aspect ratio.
*/
naturalHeight: number;
naturalHeight?: number;
/**
* The element to wrap.
*/
Expand Down

0 comments on commit 83020ae

Please sign in to comment.