Skip to content

Commit

Permalink
Merge b7b703d into 2d12640
Browse files Browse the repository at this point in the history
  • Loading branch information
iaronaraujo committed Apr 8, 2020
2 parents 2d12640 + b7b703d commit 52c052b
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 20 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
- `aspectRatio` and `maxHeight` on `ProductImage`.

## [2.52.3] - 2020-03-17
### Added
Expand Down
2 changes: 2 additions & 0 deletions docs/ProductSummaryImage.md
Expand Up @@ -48,6 +48,8 @@ Through the Storefront, you can change the `ProductSummaryImage`'s behavior and
| `mainImageLabel` | `String` | Works the same way as `hoverImageLabel` but to set the main image to display. If you pass a label and no image has that label, it will show the main image of the product | `""`|
| `width` | `[ResponsiveInput<Number>](https://github.com/vtex-apps/responsive-values#vtexresponsive-values)` | Sets the image width. | `undefined` |
| `height` | `[ResponsiveInput<Number>](https://github.com/vtex-apps/responsive-values#vtexresponsive-values)` | Sets the image height. | `undefined` |
| `aspectRatio` | `[ResponsiveInput<String>](https://github.com/vtex-apps/responsive-values#vtexresponsive-values)` | Sets the aspect ratio of the image, that is, whether the image should be square, portrait, landscape, etc. The value should follow the [common aspect ratio notation](https://en.wikipedia.org/wiki/Aspect_ratio_(image)) i.e. two numbers separated by a colon such as `1:1` for square, `3:4` for upright portrait, or `1920:1080` for even large values). Note that this prop won't work if you use `width` or `height`. | `undefined` |
| `maxHeight` | `[ResponsiveInput<Number>](https://github.com/vtex-apps/responsive-values#vtexresponsive-values)` | Sets the image max height. Note that this prop won't work if you use `width` or `height`.| `undefined` |

### Styles API

Expand Down
85 changes: 65 additions & 20 deletions react/components/ProductSummaryImage/ProductImage.js
Expand Up @@ -7,16 +7,47 @@ import { useDevice } from 'vtex.device-detector'
import { useResponsiveValues } from 'vtex.responsive-values'
import { useCssHandles, applyModifiers } from 'vtex.css-handles'
import { useProduct } from 'vtex.product-context'

import ImagePlaceholder from './ImagePlaceholder'

import { useProductSummary } from 'vtex.product-summary-context/ProductSummaryContext'

import ImagePlaceholder from './ImagePlaceholder'
import productSummary from '../../productSummary.css'

import { changeImageUrlSize } from '../../utils/normalize'
import { imageUrl } from '../../utils/aspectRatioUtil'

const CSS_HANDLES = ['image', 'imageContainer', 'product', 'imagePlaceholder']
const MAX_SIZE = 500
const DEFAULT_SIZE = 300

const getImageSrc = (src, width, height, dpi, aspectRatio) => {
if (width || height) {
return changeImageUrlSize(src, width * dpi, height * dpi)
}
if (aspectRatio) {
return imageUrl(src, DEFAULT_SIZE, MAX_SIZE, aspectRatio)
}
return src
}

const getStyle = (width, height, aspectRatio, maxHeight) => {
if (width || height) {
return {
width: '100%',
height,
objectFit: 'contain',
maxHeight: 'unset',
maxWidth: width,
}
}
if (aspectRatio || maxHeight) {
return {
width: '100%',
height: '100%',
objectFit: 'contain',
maxHeight: maxHeight || 'unset',
}
}
return null
}

const maybeBadge = ({ listPrice, price, label }) => shouldShow => component => {
if (shouldShow) {
Expand Down Expand Up @@ -48,7 +79,16 @@ const findImageByLabel = (images, selectedLabel) => {
return images.find(({ imageLabel }) => imageLabel === selectedLabel)
}

const Image = ({ src, width, height, onError, alt, className }) => {
const Image = ({
src,
width,
height,
onError,
alt,
className,
aspectRatio,
maxHeight,
}) => {
const { isMobile } = useDevice()

/** TODO: Previously it was as follows :
Expand All @@ -64,20 +104,8 @@ const Image = ({ src, width, height, onError, alt, className }) => {

return (
<img
src={
shouldResize ? changeImageUrlSize(src, width * dpi, height * dpi) : src
}
style={
shouldResize
? {
width: '100%',
height,
objectFit: 'contain',
maxHeight: 'unset',
maxWidth: width,
}
: null
}
src={getImageSrc(src, width, height, dpi, aspectRatio)}
style={getStyle(width, height, aspectRatio, maxHeight)}
loading={shouldResize ? 'lazy' : 'auto'}
alt={alt}
className={className}
Expand All @@ -98,6 +126,8 @@ const ProductImageContent = ({
showCollections,
width: widthProp,
height: heightProp,
aspectRatio,
maxHeight,
}) => {
const handles = useCssHandles(CSS_HANDLES)
const { isMobile } = useDevice()
Expand Down Expand Up @@ -180,6 +210,8 @@ const ProductImageContent = ({
src={imageUrl}
width={width}
height={height}
aspectRatio={aspectRatio}
maxHeight={maxHeight}
alt={name}
className={imageClassname}
onError={onError}
Expand All @@ -189,6 +221,8 @@ const ProductImageContent = ({
src={hoverImage.imageUrl}
width={width}
height={height}
aspectRatio={aspectRatio}
maxHeight={maxHeight}
alt={name}
className={hoverImageClassname}
onError={onError}
Expand All @@ -212,12 +246,21 @@ const ProductImage = ({
showCollections,
width: widthProp,
height: heightProp,
aspectRatio: aspectRatioProp,
maxHeight: maxHeightProp,
}) => {
const { product } = useProductSummary()

const { widthProp: width, heightProp: height } = useResponsiveValues({
const {
widthProp: width,
heightProp: height,
aspectRatioProp: aspectRatio,
maxHeightProp: maxHeight,
} = useResponsiveValues({
widthProp,
heightProp,
aspectRatioProp,
maxHeightProp,
})

const [error, setError] = useState(false)
Expand All @@ -231,6 +274,8 @@ const ProductImage = ({
<ProductImageContent
width={width}
height={height}
aspectRatio={aspectRatio}
maxHeight={maxHeight}
hasError={error}
product={product}
badgeText={badgeText}
Expand Down
61 changes: 61 additions & 0 deletions react/utils/aspectRatioUtil.js
@@ -0,0 +1,61 @@
import { changeImageUrlSize } from './normalize'

/** Parses ratio values into a multiplier to set the image height.
* For example, turns "3:4" into 1.333, so the image height will be
* 1.333 times its width.
*/
const parseAspectRatio = input => {
if (!input) {
return null
}
if (typeof input === 'string') {
if (input === 'auto') {
return null
}
const separator = ':'
const data = input.split(separator)
if (data.length !== 2) {
return null
}

const [width, height] = data
const ratio = parseFloat(height) / parseFloat(width)

if (typeof ratio !== 'number' || isNaN(ratio)) {
return null
}

return ratio
}

if (typeof input === 'number') {
return input
}

return null
}

export const imageUrl = (src, size, maxSize, aspectRatio) => {
let width = size
let height = 'auto'

if (aspectRatio && aspectRatio !== 'auto') {
height = size * (parseAspectRatio(aspectRatio) || 1)

if (width > maxSize) {
height = height / (width / maxSize)
width = maxSize
}

if (height > maxSize) {
width = width / (height / maxSize)
height = maxSize
}

width = Math.round(width)
height = Math.round(height)
} else {
width = Math.min(maxSize, width)
}
return changeImageUrlSize(src, width, height)
}

0 comments on commit 52c052b

Please sign in to comment.