Skip to content

Commit

Permalink
[field] Fix aspect ratio of image, hotspot and crop
Browse files Browse the repository at this point in the history
  • Loading branch information
mariuslundgard authored and rexxars committed Oct 6, 2020
1 parent bbd5f43 commit 3ed6301
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 51 deletions.
63 changes: 48 additions & 15 deletions packages/@sanity/field/src/types/image/diff/HotspotCropSVG.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,38 @@ interface HotspotCropSVGProps {
diff: ObjectDiff<Image>
hash: string
hotspot?: Hotspot
width?: number
height?: number
}

export function HotspotCropSVG(
props: HotspotCropSVGProps & Omit<React.SVGProps<SVGElement>, 'ref'>
props: HotspotCropSVGProps & Omit<React.SVGProps<SVGElement>, 'ref' | 'width' | 'height'>
) {
const {crop, diff, hash, hotspot, ...restProps} = props
const {crop, diff, hash, hotspot, width = 100, height = 100, ...restProps} = props
const cropColor = useDiffAnnotationColor(diff, 'crop')
const hotspotColor = useDiffAnnotationColor(diff, 'hotspot')

return (
<svg {...restProps} fill="none" viewBox="0 0 100 100">
<svg
{...restProps}
fill="none"
width={width}
height={height}
viewBox={`0 0 ${width} ${height}`}
>
<defs>
{crop && hotspot && (
<mask id={`mask-hotspot-${hash}`}>
<rect x={0} y={0} width={100} height={100} fill="#fff" />
<HotspotSVG hotspot={hotspot} fill="#000" offset={1} />
<rect x={0} y={0} width={width} height={height} fill="#fff" />
<HotspotSVG
hotspot={hotspot}
fill="#000"
offset={1}
width={width}
height={height}
stroke="#000"
strokeWidth={3}
/>
</mask>
)}
</defs>
Expand All @@ -36,6 +52,8 @@ export function HotspotCropSVG(
mask={hotspot ? `url(#mask-hotspot-${hash})` : undefined}
stroke={cropColor.border}
strokeWidth={1}
width={width}
height={height}
/>
</DiffAnnotationTooltip>
)}
Expand All @@ -47,19 +65,29 @@ export function HotspotCropSVG(
fill={hexToRgba(hotspotColor.border, 0.25)}
stroke={hotspotColor.border}
strokeWidth={1}
width={width}
height={height}
/>
</DiffAnnotationTooltip>
)}
</svg>
)
}

function CropSVG({crop, ...restProps}: {crop: Crop} & React.SVGProps<SVGRectElement>) {
function CropSVG({
crop,
width,
height,
...restProps
}: {crop: Crop; width: number; height: number} & Omit<
React.SVGProps<SVGRectElement>,
'width' | 'height'
>) {
const rectProps = {
x: crop.left * 100,
y: crop.top * 100,
width: (1 - crop.right - crop.left) * 100,
height: (1 - crop.bottom - crop.top) * 100
x: crop.left * width,
y: crop.top * height,
width: (1 - crop.right - crop.left) * width,
height: (1 - crop.bottom - crop.top) * height
}

return <rect {...restProps} {...rectProps} style={{vectorEffect: 'non-scaling-stroke'}} />
Expand All @@ -68,13 +96,18 @@ function CropSVG({crop, ...restProps}: {crop: Crop} & React.SVGProps<SVGRectElem
function HotspotSVG({
hotspot,
offset = 0,
width,
height,
...restProps
}: {hotspot: Hotspot; offset?: number} & React.SVGProps<SVGEllipseElement>) {
}: {hotspot: Hotspot; offset?: number; width: number; height: number} & Omit<
React.SVGProps<SVGEllipseElement>,
'width' | 'height'
>) {
const ellipseProps = {
cx: hotspot.x * 100,
cy: hotspot.y * 100,
rx: (hotspot.width / 2) * 100 + offset,
ry: (hotspot.height / 2) * 100 + offset
cx: hotspot.x * width,
cy: hotspot.y * height,
rx: (hotspot.width / 2) * width + offset,
ry: (hotspot.height / 2) * height + offset
}

return <ellipse {...restProps} {...ellipseProps} style={{vectorEffect: 'non-scaling-stroke'}} />
Expand Down
38 changes: 17 additions & 21 deletions packages/@sanity/field/src/types/image/diff/ImagePreview.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,29 @@
grid-template-rows: 1fr auto;
}

.header {
padding: var(--small-padding) var(--small-padding) 0;
}

.imageWrapper {
max-height: 190px;
position: relative;
background-color: color(var(--gray-lightest) alpha(50%));
background-image: linear-gradient(45deg, var(--checkerboard-color) 25%, transparent 25%),
linear-gradient(-45deg, var(--checkerboard-color) 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, var(--checkerboard-color) 75%),
linear-gradient(-45deg, transparent 75%, var(--checkerboard-color) 75%);
background-size: 16px 16px;
background-position: 0 0, 0 8px, 8px -8px, -8px 0;
display: flex;
flex: 1;
overflow: hidden;

/* "Border as padding" to fix absolutely positioned crop */
border: 0px solid transparent;
border-width: var(--small-padding) var(--small-padding) 0;
flex-direction: column;

@nest & .image {
background-color: color(var(--gray-lightest) alpha(50%));
background-image: linear-gradient(45deg, var(--checkerboard-color) 25%, transparent 25%),
linear-gradient(-45deg, var(--checkerboard-color) 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, var(--checkerboard-color) 75%),
linear-gradient(-45deg, transparent 75%, var(--checkerboard-color) 75%);
background-size: 16px 16px;
background-position: 0 0, 0 8px, 8px -8px, -8px 0;
flex: 1;
min-height: 0;
object-fit: contain;
width: 100%;
height: 100%;

@nest &[data-action='removed'] {
opacity: 0.5;
Expand All @@ -41,21 +43,15 @@
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
}

.imageWrapperChanged {
composes: imageWrapper;
opacity: 0.45;
}

.crop,
.hotspot {
position: absolute;
border: 1px solid transparent;
}

.strikethrough {
text-decoration: line-through;
}
35 changes: 20 additions & 15 deletions packages/@sanity/field/src/types/image/diff/ImagePreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,31 @@ export default function ImagePreview(props: ImagePreviewProps): React.ReactEleme
.fit('max')

const assetChanged = diff.fromValue?.asset?._ref !== diff.toValue?.asset?._ref
const className = is === 'from' && assetChanged ? styles.imageWrapperChanged : styles.imageWrapper
const imageWrapperClassName =
is === 'from' && assetChanged ? styles.imageWrapperChanged : styles.imageWrapper
const metaAction = action === 'changed' ? undefined : action

return (
<div className={styles.root}>
<div className={className}>
<img
className={styles.image}
src={imageSource.toString() || ''}
alt={title}
data-action={metaAction}
/>
<div className={styles.header}>
<div className={imageWrapperClassName}>
<img
className={styles.image}
src={imageSource.toString() || ''}
alt={title}
data-action={metaAction}
/>

<HotspotCropSVG
className={styles.hotspotCrop}
crop={crop && !isDefaultCrop(crop) ? crop : undefined}
diff={diff}
hash={simpleHash(`${imageSource.toString() || ''}-${is}`)}
hotspot={hotspot && !isDefaultHotspot(hotspot) ? hotspot : undefined}
/>
<HotspotCropSVG
className={styles.hotspotCrop}
crop={crop && !isDefaultCrop(crop) ? crop : undefined}
diff={diff}
hash={simpleHash(`${imageSource.toString() || ''}-${is}`)}
hotspot={hotspot && !isDefaultHotspot(hotspot) ? hotspot : undefined}
width={dimensions?.width}
height={dimensions?.height}
/>
</div>
</div>

<MetaInfo title={title} icon={ImageIcon} markRemoved={assetChanged && is === 'from'}>
Expand Down

0 comments on commit 3ed6301

Please sign in to comment.