From 77566788223668582ef424a8049c623366316caf Mon Sep 17 00:00:00 2001 From: Joe Cheng Date: Tue, 1 Oct 2024 12:54:16 -0700 Subject: [PATCH] Fix #4135: Plot outputs incorrectly sized inside scaled outputs CSS zoom property affects el.getBoundingClientRect() but not el.offsetWidth/Height. When reporting sizes of outputs from client to server, we need to back out the CSS zoom because those sizes are used as CSS width/height, which will be affected by zoom. (Note that something similar happens with CSS transforms but we don't have a good way to deal with them) --- srcts/src/shiny/index.ts | 5 +++-- srcts/src/utils/index.ts | 13 +++++++++++++ srcts/types/src/utils/index.d.ts | 6 +++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/srcts/src/shiny/index.ts b/srcts/src/shiny/index.ts index 4679f15ea..09a84549f 100644 --- a/srcts/src/shiny/index.ts +++ b/srcts/src/shiny/index.ts @@ -21,6 +21,7 @@ import { debounce, Debouncer } from "../time"; import { $escape, compareVersion, + getBoundingClientSizeBeforeZoom, getComputedLinkColor, getStyle, hasDefinedProperty, @@ -289,7 +290,7 @@ class ShinyClass { $(".shiny-image-output, .shiny-plot-output, .shiny-report-size").each( function () { const id = getIdFromEl(this), - rect = this.getBoundingClientRect(); + rect = getBoundingClientSizeBeforeZoom(this); if (rect.width !== 0 || rect.height !== 0) { initialValues[".clientdata_output_" + id + "_width"] = rect.width; @@ -425,7 +426,7 @@ class ShinyClass { $(".shiny-image-output, .shiny-plot-output, .shiny-report-size").each( function () { const id = getIdFromEl(this), - rect = this.getBoundingClientRect(); + rect = getBoundingClientSizeBeforeZoom(this); if (rect.width !== 0 || rect.height !== 0) { inputs.setInput(".clientdata_output_" + id + "_width", rect.width); diff --git a/srcts/src/utils/index.ts b/srcts/src/utils/index.ts index d2dc3132d..e4bd60ee1 100644 --- a/srcts/src/utils/index.ts +++ b/srcts/src/utils/index.ts @@ -144,6 +144,18 @@ function pixelRatio(): number { } } +function getBoundingClientSizeBeforeZoom(el: HTMLElement): { + width: number; + height: number; +} { + const rect = el.getBoundingClientRect(); + const zoom = el.currentCSSZoom || 1; + return { + width: rect.width / zoom, + height: rect.height / zoom, + }; +} + // Takes a string expression and returns a function that takes an argument. // // When the function is executed, it will evaluate that expression using @@ -398,6 +410,7 @@ export { formatDateUTC, makeResizeFilter, pixelRatio, + getBoundingClientSizeBeforeZoom, scopeExprToFunc, asArray, mergeSort, diff --git a/srcts/types/src/utils/index.d.ts b/srcts/types/src/utils/index.d.ts index 60ef4455b..e2f8c0f2c 100644 --- a/srcts/types/src/utils/index.d.ts +++ b/srcts/types/src/utils/index.d.ts @@ -10,6 +10,10 @@ declare function parseDate(dateString: string): Date; declare function formatDateUTC(x: Date): string; declare function makeResizeFilter(el: HTMLElement, func: (width: HTMLElement["offsetWidth"], height: HTMLElement["offsetHeight"]) => void): () => void; declare function pixelRatio(): number; +declare function getBoundingClientSizeBeforeZoom(el: HTMLElement): { + width: number; + height: number; +}; declare function scopeExprToFunc(expr: string): (scope: unknown) => unknown; declare function asArray(value: T | T[] | null | undefined): T[]; declare function mergeSort(list: Item[], sortfunc: (a: Item, b: Item) => boolean | number): Item[]; @@ -26,4 +30,4 @@ declare function updateLabel(labelTxt: string | undefined, labelNode: JQuery(str: T): Lowercase; -export { escapeHTML, randomId, strToBool, getStyle, padZeros, roundSignif, parseDate, formatDateUTC, makeResizeFilter, pixelRatio, scopeExprToFunc, asArray, mergeSort, $escape, mapValues, isnan, _equal, equal, compareVersion, updateLabel, getComputedLinkColor, hasOwnProperty, hasDefinedProperty, isBS3, toLowerCase, }; +export { escapeHTML, randomId, strToBool, getStyle, padZeros, roundSignif, parseDate, formatDateUTC, makeResizeFilter, pixelRatio, getBoundingClientSizeBeforeZoom, scopeExprToFunc, asArray, mergeSort, $escape, mapValues, isnan, _equal, equal, compareVersion, updateLabel, getComputedLinkColor, hasOwnProperty, hasDefinedProperty, isBS3, toLowerCase, };