Skip to content

feat!: constrain the image to the component's max height - #42

Open
paodb wants to merge 2 commits into
masterfrom
feat/image-max-height
Open

paodb wants to merge 2 commits into
masterfrom
feat/image-max-height

Conversation

@paodb

@paodb paodb commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

As discussed in #12, this breaking change was deferred to the next major version, so it is included in 2.0.

The image now scales down to fit the component's max height (and the available width), keeping its aspect ratio and without upscaling small images. For instance, getStyle().set("max-height", "70vh") keeps large images within a dialog.

  • Propagate the component's max-height to the image via CSS
  • Remove setImageFullHeight (breaking): use getStyle().set("max-height", "100vh") instead
  • Update the upload demo and README

Close #12
Close #8

@coderabbitai

coderabbitai Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 2cc6a6d8-4615-4dc5-83b1-9f6f191daedd

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 2949e623-cc99-4309-b9db-1742d64295e1
📥 Commits

Reviewing files that changed from the base of the PR and between 2892700 and ea84365.

📒 Files selected for processing: 4
  • README.md
  • src/main/java/com/flowingcode/vaadin/addons/imagecrop/ImageCrop.java
  • src/main/resources/META-INF/frontend/styles/image-crop-styles.css
  • src/test/java/com/flowingcode/vaadin/addons/imagecrop/UploadImageCropDemo.java

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

Walkthrough

ImageCrop now implements Vaadin’s HasSize interface. Its styles constrain the crop element’s width and apply maximum-height inheritance to the crop wrapper. The README and upload demo show setting a maximum height of 70vh. The setImageFullHeight(Boolean) method and its viewport-height style were removed.

Changes

ImageCrop sizing

Layer / File(s) Summary
Component sizing API
src/main/java/com/flowingcode/vaadin/addons/imagecrop/ImageCrop.java
ImageCrop implements HasSize and documents how the image scales to the available width and maximum height. The setImageFullHeight(Boolean) method was removed.
Sizing styles and usage examples
src/main/resources/META-INF/frontend/styles/image-crop-styles.css, README.md, src/test/java/com/flowingcode/vaadin/addons/imagecrop/UploadImageCropDemo.java
The crop element displays as inline-block with a maximum width of 100%, and the .ReactCrop wrapper inherits max-height. The README and demo set the component’s maximum height to 70vh.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Feature · Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to ea843

The crop image is constrained by the configured maximum height, so the documented sizing behavior is supported. No actionable issue remains before merge.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 2 files. (2 skipped: 2… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Linked Issues check Passed The active linked issues have no conflicting coding acceptance criteria. For #12, the PR removes the obsolete setImageFullHeight(Boolean) API and documents setMaxHeight(...) as the replacement. Fo…
Out of Scope Changes check Passed The reviewed changes stay within #8 and #12. The Java API change, CSS rules, README example, and upload-demo change all support image sizing or the removal and replacement of setImageFullHeight. No …
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly summarizes the main change: constraining the image to the component's maximum height. The feat! marker also signals the breaking change caused by removing setImageFullHeight.
Full details: Linked Issues check

Explanation

The active linked issues have no conflicting coding acceptance criteria. For #12, the PR removes the obsolete setImageFullHeight(Boolean) API and documents setMaxHeight(...) as the replacement. For #8, ImageCrop now implements HasSize, the component has max-width: 100%, and the CSS propagates max-height through .ReactCrop to the image. The class documentation states that large images scale down with aspect ratio and small images are not upscaled. The README and upload demo document and use setMaxHeight("70vh"). Neither issue specifies an automated-test requirement.

Full details: Out of Scope Changes check

Explanation

The reviewed changes stay within #8 and #12. The Java API change, CSS rules, README example, and upload-demo change all support image sizing or the removal and replacement of setImageFullHeight. No unrelated behavior or files are identified in the supplied PR evidence.

Full details: Docstring Coverage

Explanation

Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 2 files. (2 skipped: 2 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@paodb
paodb marked this pull request as ready for review October 16, 2026 18:53
@javier-godoy
javier-godoy self-requested a review October 16, 2026 18:56

@javier-godoy javier-godoy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementing HasSize is an unrelated change

@paodb
paodb force-pushed the feat/image-max-height branch from ea84365 to ef5be8b Compare October 16, 2026 19:11
@paodb

paodb commented Oct 16, 2026 •

Copy link
Copy Markdown
Member Author

@javier-godoy Removed the HasSize implementation as it was unrelated; the examples now use getStyle().set("max-height", ...).

/* Propagate the component's max-height to the image (react-image-crop
applies max-height: inherit to its wrapper and to the image). */
image-crop .ReactCrop {
max-height: inherit;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inherit copies the max-height value as written, not as computed, so a percentage doesn't work. With max-height: 100% on an image-crop inside a 300px-tall container, the host resolves it to 300px. .ReactCrop also gets 100%, but resolves it against the host, whose height is auto, so it acts as none. The wrapper and the img inherit that, and a 4000×3000 image renders at 1280×960, overflowing the host. The same layout with max-height: 300px works (the image renders at 400×300). The same happens with calc() values that contain a percentage, and with a Dialog sized setMaxHeight("100%") like the one in the upload demo.

If only absolute and viewport lengths are meant to be supported, the Javadoc and README should say so; right now they promise the image fits "the component's max-height".

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: the image is now fitted in the .tsx to the component's measured height, so percentages, calc(), fixed heights and layout sizes all work. Javadoc and README updated accordingly.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed: in a 300px container, max-height: 100% and calc(100% - 20px) now render a 4000×3000 image at 400×300 and 373×280, and the image also fits in a Dialog with setMaxHeight("100%"). The docs now claim a bit more than the code does, though; see my comment on the README.

@paodb
paodb requested a review from javier-godoy October 20, 2026 19:31
paodb added 2 commits October 21, 2026 17:47
BREAKING CHANGE: setImageFullHeight was removed; use
getStyle().set("max-height", "100vh") instead.

Close #12
Close #8
@paodb
paodb force-pushed the feat/image-max-height branch from 7e52563 to 8895cf0 Compare October 21, 2026 20:47
@sonarqubecloud

Copy link
Copy Markdown

Comment on lines +165 to +166
const available = this.clientHeight - parseFloat(style.paddingTop) - parseFloat(style.paddingBottom);
if (img.getBoundingClientRect().height > available + 0.5) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getBoundingClientRect() includes CSS transforms, but clientHeight doesn't, and a transform change doesn't trigger the ResizeObserver. In a Dialog, the image is measured during the opening animation, while the overlay is scaled to 0.95. With max-height: 70vh at a 905px viewport (633.5px) and a 1000×650 image, the image measures 618px, so it isn't clamped. When the animation ends, it renders at 650px in a 634px component, overflowing by 17px, and nothing refits it. The same happens inside any container with a scale below 1. Comparing img.offsetHeight, which isn't affected by transforms, avoids this.

Comment on lines +163 to +167
cropElement.style.maxHeight = "";
const style = getComputedStyle(this);
const available = this.clientHeight - parseFloat(style.paddingTop) - parseFloat(style.paddingBottom);
if (img.getBoundingClientRect().height > available + 0.5) {
cropElement.style.maxHeight = `${available}px`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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 image-crop (1000×3000 image) and a sibling with flex: 0 1 200px, the unclamped component gets 469px, so the image is clamped to 469px. With that smaller basis the component then gets 351px, and the image overflows it by 118px. It stays that way: at 250px the overflow is 98px, at 700px it is 119px.

Comment thread README.md

* Constrain the size of large images

The image is scaled down to fit the component's size, whether it is set with `height` or `max-height` (in any unit) or given by the layout.

Copy link
Copy Markdown
Contributor

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) and div.setHeight("400px"), a 1000×4000 image renders at 4000px and overflows the Div. Percentages also need a parent with a defined height. Since HasSize was removed, setSizeFull() isn't available either.

Suggested change
The image is scaled down to fit the component's size, whether it is set with `height` or `max-height` (in any unit) or given by the layout.
The image is scaled down, keeping its aspect ratio, to fit the component's height when that height is limited: by a `height` or `max-height` set on the component, or by a flex layout such as `VerticalLayout` that sizes it. Percentages are resolved against the parent's height, so the parent needs a defined height. In a container that doesn't size its children, such as a `Div`, the component grows to the image's height; set a height on it, for instance `getStyle().set("height", "100%")`, to constrain the image.

Comment on lines +44 to +48
* 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as the README:

Suggested change
* 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.
* The image is scaled down, keeping its aspect ratio, to fit the component's width
* and height. The height is limited by a {@code height} or {@code max-height} set on
* the component, or by a flex layout that sizes it, such as a {@code VerticalLayout}.
* Percentages are resolved against the parent's height, so they only take effect when
* the parent has a defined height. In a container that does not size its children,
* such as a {@code Div}, the component grows to the image's height unless a height is
* set on it. 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.

@paodb

paodb commented Oct 23, 2026

Copy link
Copy Markdown
Member Author

@javier-godoy Each round of measuring in JS fixes one layout case and exposes another (transforms during the Dialog animation, flex-shrink feedback), and the general case of fitting the image to whatever height the layout gives the component seems hard to get right. #8 and #12 only ask to keep large images within the viewport or a dialog.

I'd suggest narrowing the scope: go back to the CSS-only approach from the first commit, drop fitImageHeight() and the ResizeObserver, and document that only absolute and viewport lengths for max-height are supported (px, vh, …), as you mentioned in your first review. Percentages, fixed heights and flex sizing could go to a separate issue if needed. What do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

Should imageFullHeight default to true? Constrain the size of the ImageCrop for large images.

2 participants