Repository navigation
feat!: constrain the image to the component's max height #42
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -39,7 +39,15 @@ | |||||||||||||||||||||||||||||
| * <a href="https://www.npmjs.com/package/react-image-crop">react-image-crop</a> | ||||||||||||||||||||||||||||||
| * library. | ||||||||||||||||||||||||||||||
| * This component allows users to define and manipulate crop areas on images. | ||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||
| * <p> | ||||||||||||||||||||||||||||||
| * The image is scaled down, keeping its aspect ratio, to fit the component's width | ||||||||||||||||||||||||||||||
| * and height, whether they are set on the component (a {@code height} or a | ||||||||||||||||||||||||||||||
| * {@code max-height} in any unit) or given by the layout it is in. For instance, | ||||||||||||||||||||||||||||||
| * {@code getStyle().set("max-height", "100vh")} keeps a large image within the | ||||||||||||||||||||||||||||||
| * viewport. Images smaller than these limits are shown at their natural size. | ||||||||||||||||||||||||||||||
|
Comment on lines
+44
to
+48
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as the README:
Suggested change
|
||||||||||||||||||||||||||||||
| * </p> | ||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||
| * @author Paola De Bartolo / Flowing Code | ||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||
| @NpmPackage(value = "react-image-crop", version = "11.0.6") | ||||||||||||||||||||||||||||||
|
|
@@ -48,8 +56,6 @@ | |||||||||||||||||||||||||||||
| @CssImport("react-image-crop/dist/ReactCrop.css") | ||||||||||||||||||||||||||||||
| @CssImport("./styles/image-crop-styles.css") | ||||||||||||||||||||||||||||||
| public class ImageCrop extends ReactAdapterComponent { | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| private static final String IMG_FULL_HEIGHT_CLASS_NAME = "img-full-height"; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| private static final String IMG_SRC_RESOURCE_ATTRIBUTE = "img-src"; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
@@ -497,21 +503,6 @@ public String getCroppedImageDataUri() { | |||||||||||||||||||||||||||||
| return this.croppedImageDataUri; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||
| * Sets the image to occupy the full viewport height when enabled. | ||||||||||||||||||||||||||||||
| * If {@code fullHeight} is {@code true}, applies a CSS class that | ||||||||||||||||||||||||||||||
| * sets the image height to 100vh. If {@code false}, removes the class | ||||||||||||||||||||||||||||||
| * to revert to the default height. | ||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||
| * @param fullHeight whether the image should fill the viewport height | ||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||
| public void setImageFullHeight(Boolean fullHeight) { | ||||||||||||||||||||||||||||||
| if (fullHeight) | ||||||||||||||||||||||||||||||
| this.addClassName(IMG_FULL_HEIGHT_CLASS_NAME); | ||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||
| this.removeClassName(IMG_FULL_HEIGHT_CLASS_NAME); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||
| * Decodes the cropped image data URI and returns it as a byte array. If the image data URI is not | ||||||||||||||||||||||||||||||
| * in the format "data:image/*;base64,", it will be decoded assuming it is a Base64 encoded | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -144,6 +144,40 @@ | |
| ) | ||
| : percentCrop; | ||
|
|
||
| /** | ||
| * Fits the image to the height the component actually gets, however it is | ||
| * defined: a height, a max-height in any unit, or the layout. CSS alone can't | ||
| * do it, since react-image-crop inherits max-height as written, so a | ||
| * percentage would resolve against an auto-height parent and be ignored. | ||
| * | ||
| * The limit is cleared before measuring, so the component is measured without | ||
| * it, and is set in pixels only when the image is taller than the available | ||
| * space. Smaller images are never upscaled. | ||
| */ | ||
| const fitImageHeight = () => { | ||
| const cropElement = this.querySelector<HTMLElement>(".ReactCrop"); | ||
| const img = imgRef.current; | ||
| if (!cropElement || !img) { | ||
| return; | ||
| } | ||
| cropElement.style.maxHeight = ""; | ||
| const style = getComputedStyle(this); | ||
| const available = this.clientHeight - parseFloat(style.paddingTop) - parseFloat(style.paddingBottom); | ||
|
Check warning on line 165 in src/main/resources/META-INF/frontend/src/image-crop.tsx
|
||
| if (img.getBoundingClientRect().height > available + 0.5) { | ||
|
Comment on lines
+165
to
+166
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| cropElement.style.maxHeight = `${available}px`; | ||
|
Comment on lines
+163
to
+167
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Measuring with the clamp cleared assumes the clamp doesn't change the space the layout gives the component. In a flex container where a sibling can also shrink, it does: the space each item loses is weighted by its flex basis, and the clamp lowers the component's basis. In a 500px-tall flex column with an |
||
| } | ||
| }; | ||
|
|
||
| // Refit when the component or its container is resized. | ||
| useEffect(() => { | ||
| const observer = new ResizeObserver(() => fitImageHeight()); | ||
| observer.observe(this); | ||
| if (this.parentElement) { | ||
| observer.observe(this.parentElement); | ||
| } | ||
| return () => observer.disconnect(); | ||
| }, []); | ||
|
|
||
| /** | ||
| * Normalizes the configured crop when the image loads. The crop is kept as a | ||
| * percentage of the image's natural size, so both the on-screen selection and | ||
|
|
@@ -156,6 +190,7 @@ | |
| * below). setCrop's Javadoc documents the difference. | ||
| */ | ||
| const onImageLoad = () => { | ||
| fitImageHeight(); | ||
| const img = imgRef.current; | ||
| if (!img || !crop) { | ||
| return; | ||
|
|
||
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 only holds when something limits the component's height. In a container that doesn't size its children, the component grows to the image's height, so nothing is clamped: with
new Div(imageCrop)anddiv.setHeight("400px"), a 1000×4000 image renders at 4000px and overflows the Div. Percentages also need a parent with a defined height. SinceHasSizewas removed,setSizeFull()isn't available either.