Compare commits

...

9 Commits

23 changed files with 911 additions and 625 deletions

File diff suppressed because one or more lines are too long

View File

@@ -1,2 +1,2 @@
/*! shiny 1.7.2 | (c) 2012-2022 RStudio, PBC. | License: GPL-3 | file LICENSE */
/*! shiny 1.7.2.9000 | (c) 2012-2022 RStudio, PBC. | License: GPL-3 | file LICENSE */
#showcase-well{border-radius:0}.shiny-code{background-color:#fff;margin-bottom:0}.shiny-code code{font-family:Menlo,Consolas,"Courier New",monospace}.shiny-code-container{margin-top:20px;clear:both}.shiny-code-container h3{display:inline;margin-right:15px}.showcase-header{font-size:16px;font-weight:normal}.showcase-code-link{text-align:right;padding:15px}#showcase-app-container{vertical-align:top}#showcase-code-tabs{margin-right:15px}#showcase-code-tabs pre{border:none;line-height:1em}#showcase-code-tabs .nav{margin-bottom:0}#showcase-code-tabs ul{margin-bottom:0}#showcase-code-tabs .tab-content{border-style:solid;border-color:#e5e5e5;border-width:0px 1px 1px 1px;overflow:auto;border-bottom-right-radius:4px;border-bottom-left-radius:4px}#showcase-app-code{width:100%}#showcase-code-position-toggle{float:right}#showcase-sxs-code{padding-top:20px;vertical-align:top}.showcase-code-license{display:block;text-align:right}#showcase-code-content pre{background-color:#fff}

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,3 @@
/*! shiny 1.7.2 | (c) 2012-2022 RStudio, PBC. | License: GPL-3 | file LICENSE */
/*! shiny 1.7.2.9000 | (c) 2012-2022 RStudio, PBC. | License: GPL-3 | file LICENSE */
(function(){var a=eval;window.addEventListener("message",function(i){var e=i.data;e.code&&a(e.code)});})();
//# sourceMappingURL=shiny-testmode.js.map

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -3,7 +3,7 @@
"homepage": "https://shiny.rstudio.com",
"repository": "github:rstudio/shiny",
"name": "@types/rstudio-shiny",
"version": "1.7.2",
"version": "1.7.2-alpha.9000",
"license": "GPL-3.0-only",
"main": "",
"browser": "",
@@ -24,6 +24,7 @@
"@types/datatables.net": "^1.10.19",
"@types/ion-rangeslider": "2.3.0",
"@types/jquery": "patch:@types/jquery@3.5.5#./srcts/patch/types-jquery.patch",
"@types/resize-observer-browser": "^0.1.7",
"@types/selectize": "0.12.34"
},
"devDependencies": {

View File

@@ -290,10 +290,12 @@ class ImageOutputBinding extends OutputBinding {
width: number | string,
height: number | string
): void {
$(el).find("img").trigger("resize");
return;
width;
height;
const img = $(el).find("img");
img.attr("width", width);
img.attr("height", height);
img.trigger("resize");
}
}

View File

@@ -7,7 +7,7 @@ import type {
InputValidateDecorator,
} from "../inputPolicies";
import { shinyAppBindOutput, shinyAppUnbindOutput } from "./initedMethods";
import { sendImageSizeFns } from "./sendImageSize";
import { sendOutputInfoFns } from "./sendOutputInfo";
const boundInputs = {};
@@ -38,8 +38,6 @@ type BindInputsCtx = {
inputsRate: InputRateDecorator;
inputBindings: BindingRegistry<InputBinding>;
outputBindings: BindingRegistry<OutputBinding>;
sendOutputHiddenState: () => void;
maybeAddThemeObserver: (el: HTMLElement) => void;
initDeferredIframes: () => void;
};
function bindInputs(
@@ -120,11 +118,7 @@ function bindInputs(
}
function bindOutputs(
{
sendOutputHiddenState,
maybeAddThemeObserver,
outputBindings,
}: BindInputsCtx,
{ outputBindings }: BindInputsCtx,
scope: BindScope = document.documentElement
): void {
const $scope = $(scope);
@@ -155,12 +149,6 @@ function bindOutputs(
continue;
}
// If this element reports its CSS styles to getCurrentOutputInfo()
// then it should have a MutationObserver() to resend CSS if its
// style/class attributes change. This observer should already exist
// for _static_ UI, but not yet for _dynamic_ UI
maybeAddThemeObserver(el);
const bindingAdapter = new OutputBindingAdapter(el, binding);
shinyAppBindOutput(id, bindingAdapter);
@@ -177,8 +165,7 @@ function bindOutputs(
}
// Send later in case DOM layout isn't final yet.
setTimeout(sendImageSizeFns.regular, 0);
setTimeout(sendOutputHiddenState, 0);
setTimeout(sendOutputInfoFns.regular, 0);
}
function unbindInputs(
@@ -212,7 +199,6 @@ function unbindInputs(
}
}
function unbindOutputs(
{ sendOutputHiddenState }: BindInputsCtx,
scope: BindScope = document.documentElement,
includeSelf = false
) {
@@ -243,8 +229,7 @@ function unbindOutputs(
}
// Send later in case DOM layout isn't final yet.
setTimeout(sendImageSizeFns.regular, 0);
setTimeout(sendOutputHiddenState, 0);
setTimeout(sendOutputInfoFns.regular, 0);
}
// (Named used before TS conversion)
@@ -256,13 +241,9 @@ function _bindAll(
bindOutputs(shinyCtx, scope);
return bindInputs(shinyCtx, scope);
}
function unbindAll(
shinyCtx: BindInputsCtx,
scope: BindScope,
includeSelf = false
): void {
function unbindAll(scope: BindScope, includeSelf = false): void {
unbindInputs(scope, includeSelf);
unbindOutputs(shinyCtx, scope, includeSelf);
unbindOutputs(scope, includeSelf);
}
function bindAll(shinyCtx: BindInputsCtx, scope: BindScope): void {
// _bindAll returns input values; it doesn't send them to the server.

View File

@@ -10,11 +10,11 @@ import {
} from "../inputPolicies";
import type { InputPolicy } from "../inputPolicies";
import { addDefaultInputOpts } from "../inputPolicies/inputValidateDecorator";
import { debounce, Debouncer } from "../time";
import { debounce } from "../time";
import {
getComputedLinkColor,
getStyle,
hasOwnProperty,
isHidden,
mapValues,
pixelRatio,
} from "../utils";
@@ -22,7 +22,7 @@ import { bindAll, unbindAll, _bindAll } from "./bind";
import type { BindInputsCtx, BindScope } from "./bind";
import { setShinyObj } from "./initedMethods";
import { registerDependency } from "./render";
import { sendImageSizeFns } from "./sendImageSize";
import { sendOutputInfoFns } from "./sendOutputInfo";
import { ShinyApp } from "./shinyapp";
import { registerNames as singletonsRegisterNames } from "./singletons";
import type { InputPolicyOpts } from "../inputPolicies/inputPolicy";
@@ -87,8 +87,6 @@ function initShiny(windowShiny: Shiny): void {
return {
inputs,
inputsRate,
sendOutputHiddenState,
maybeAddThemeObserver,
inputBindings,
outputBindings,
initDeferredIframes,
@@ -99,7 +97,7 @@ function initShiny(windowShiny: Shiny): void {
bindAll(shinyBindCtx(), scope);
};
windowShiny.unbindAll = function (scope: BindScope, includeSelf = false) {
unbindAll(shinyBindCtx(), scope, includeSelf);
unbindAll(scope, includeSelf);
};
// Calls .initialize() for all of the input objects in all input bindings,
@@ -127,12 +125,11 @@ function initShiny(windowShiny: Shiny): void {
}
windowShiny.initializeInputs = initializeInputs;
function getIdFromEl(el: HTMLElement) {
function getIdFromEl(el: HTMLElement): string | null {
const $el = $(el);
const bindingAdapter = $el.data("shiny-output-binding");
if (!bindingAdapter) return null;
else return bindingAdapter.getId();
return bindingAdapter ? bindingAdapter.getId() : null;
}
// Initialize all input objects in the document, before binding
@@ -150,306 +147,204 @@ function initShiny(windowShiny: Shiny): void {
(x) => x.value
);
// The server needs to know the size of each image and plot output element,
// in case it is auto-sizing
$(".shiny-image-output, .shiny-plot-output, .shiny-report-size").each(
function () {
const id = getIdFromEl(this);
if (this.offsetWidth !== 0 || this.offsetHeight !== 0) {
initialValues[".clientdata_output_" + id + "_width"] = this.offsetWidth;
initialValues[".clientdata_output_" + id + "_height"] =
this.offsetHeight;
}
// Helper function for both initializing and updating input values
function setInput(name: string, value: unknown, initial = false): void {
if (initial) {
initialValues[name] = value;
} else {
inputs.setInput(name, value);
}
);
function getComputedBgColor(el) {
if (!el) {
// Top of document, can't recurse further
return null;
}
const bgColor = getStyle(el, "background-color");
const m = bgColor.match(
/^rgba\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*\)$/
);
if (bgColor === "transparent" || (m && parseFloat(m[4]) === 0)) {
// No background color on this element. See if it has a background image.
const bgImage = getStyle(el, "background-image");
if (bgImage && bgImage !== "none") {
// Failed to detect background color, since it has a background image
return null;
} else {
// Recurse
return getComputedBgColor(el.parentElement);
}
}
return bgColor;
}
function getComputedFont(el) {
const fontFamily = getStyle(el, "font-family");
const fontSize = getStyle(el, "font-size");
function doSendSize(el: HTMLElement, initial = false): void {
const id = getIdFromEl(el);
return {
families: fontFamily.replace(/"/g, "").split(", "),
size: fontSize,
};
if (el.offsetWidth !== 0 || el.offsetHeight !== 0) {
setInput(".clientdata_output_" + id + "_width", el.offsetWidth, initial);
setInput(
".clientdata_output_" + id + "_height",
el.offsetHeight,
initial
);
}
}
$(".shiny-image-output, .shiny-plot-output, .shiny-report-theme").each(
function () {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const el = this;
const id = getIdFromEl(el);
function doTriggerResize(el: HTMLElement): void {
const $el = $(el),
binding = $el.data("shiny-output-binding");
initialValues[".clientdata_output_" + id + "_bg"] =
getComputedBgColor(el);
initialValues[".clientdata_output_" + id + "_fg"] = getStyle(el, "color");
initialValues[".clientdata_output_" + id + "_accent"] =
getComputedLinkColor(el);
initialValues[".clientdata_output_" + id + "_font"] = getComputedFont(el);
maybeAddThemeObserver(el);
}
);
// Resend computed styles if *an output element's* class or style attribute changes.
// This gives us some level of confidence that getCurrentOutputInfo() will be
// properly invalidated if output container is mutated; but unfortunately,
// we don't have a reasonable way to detect change in *inherited* styles
// (other than session$setCurrentTheme())
// https://github.com/rstudio/shiny/issues/3196
// https://github.com/rstudio/shiny/issues/2998
function maybeAddThemeObserver(el: HTMLElement): void {
if (!window.MutationObserver) {
return; // IE10 and lower
}
const cl = el.classList;
const reportTheme =
cl.contains("shiny-image-output") ||
cl.contains("shiny-plot-output") ||
cl.contains("shiny-report-theme");
if (!reportTheme) {
return;
}
const $el = $(el);
if ($el.data("shiny-theme-observer")) {
return; // i.e., observer is already observing
}
const observerCallback = new Debouncer(null, () => doSendTheme(el), 100);
const observer = new MutationObserver(() => observerCallback.normalCall());
const config = { attributes: true, attributeFilter: ["style", "class"] };
observer.observe(el, config);
$el.data("shiny-theme-observer", observer);
$el.trigger({
type: "shiny:visualchange",
// @ts-expect-error; Can not remove info on a established, malformed Event object
visible: !isHidden(el),
binding: binding,
});
binding.onResize();
}
function doSendTheme(el) {
function doSendTheme(el: HTMLElement, initial = false): void {
// Sending theme info on error isn't necessary (it'd add an unnecessary additional round-trip)
if (el.classList.contains("shiny-output-error")) {
return;
}
const id = getIdFromEl(el);
inputs.setInput(".clientdata_output_" + id + "_bg", getComputedBgColor(el));
inputs.setInput(".clientdata_output_" + id + "_fg", getStyle(el, "color"));
inputs.setInput(
".clientdata_output_" + id + "_accent",
getComputedLinkColor(el)
);
inputs.setInput(".clientdata_output_" + id + "_font", getComputedFont(el));
}
function getComputedBgColor(el: HTMLElement): string {
if (!el) {
// Top of document, can't recurse further
return null;
}
function doSendImageSize() {
$(".shiny-image-output, .shiny-plot-output, .shiny-report-size").each(
function () {
const id = getIdFromEl(this);
const bgColor = getStyle(el, "background-color");
const m = bgColor.match(
/^rgba\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*\)$/
);
if (this.offsetWidth !== 0 || this.offsetHeight !== 0) {
inputs.setInput(
".clientdata_output_" + id + "_width",
this.offsetWidth
);
inputs.setInput(
".clientdata_output_" + id + "_height",
this.offsetHeight
);
if (bgColor === "transparent" || (m && parseFloat(m[4]) === 0)) {
// No background color on this element. See if it has a background image.
const bgImage = getStyle(el, "background-image");
if (bgImage && bgImage !== "none") {
// Failed to detect background color, since it has a background image
return null;
} else {
// Recurse
return getComputedBgColor(el.parentElement);
}
}
);
return bgColor;
}
$(".shiny-image-output, .shiny-plot-output, .shiny-report-theme").each(
function () {
doSendTheme(this);
}
);
function getComputedFont(el: HTMLElement): {
families: string[];
size: string;
} {
const fontFamily = getStyle(el, "font-family");
const fontSize = getStyle(el, "font-size");
return {
families: fontFamily.replace(/"/g, "").split(", "),
size: fontSize,
};
}
const id = getIdFromEl(el);
setInput(
".clientdata_output_" + id + "_bg",
getComputedBgColor(el),
initial
);
setInput(
".clientdata_output_" + id + "_fg",
getStyle(el, "color"),
initial
);
setInput(
".clientdata_output_" + id + "_accent",
getComputedLinkColor(el),
initial
);
setInput(
".clientdata_output_" + id + "_font",
getComputedFont(el),
initial
);
}
// eslint-disable-next-line prefer-const
let visibleOutputs = new Set();
function doSendHiddenState(el: HTMLElement, initial = false): void {
const id = getIdFromEl(el);
const hidden = isHidden(el);
if (!hidden) visibleOutputs.add(id);
setInput(".clientdata_output_" + id + "_hidden", hidden, initial);
}
function doSendOutputInfo(initial = false) {
const outputIds = new Set();
// TODO: can we always rely on this class existing when we're calling this (especially initially)?
$(".shiny-bound-output").each(function () {
const $this = $(this),
binding = $this.data("shiny-output-binding");
// eslint-disable-next-line @typescript-eslint/no-this-alias
const el = this,
id = getIdFromEl(el),
isPlot =
el.classList.contains("shiny-image-output") ||
el.classList.contains("shiny-plot-output");
$this.trigger({
type: "shiny:visualchange",
// @ts-expect-error; Can not remove info on a established, malformed Event object
visible: !isHidden(this),
binding: binding,
});
binding.onResize();
outputIds.add(id);
function handleResize(initial = false) {
doTriggerResize(el);
doSendHiddenState(el, initial);
if (isPlot || el.classList.contains("shiny-report-size")) {
doSendSize(el, initial);
}
}
// TODO: do we need a polyfill for ResizeObserver?
if (!$(el).data("shiny-resize-observer")) {
const onResize = debounce(100, handleResize);
const ro = new ResizeObserver(() => onResize(false));
ro.observe(el);
$(el).data("shiny-resize-observer", ro);
}
function handleIntersect(entries) {
entries.forEach(() => {
handleResize(false);
});
}
// TODO: do we need a polyfill for IntersectionObserver?
if (!$(el).data("shiny-intersection-observer")) {
const onIntersect = debounce(100, handleIntersect);
const io = new IntersectionObserver(onIntersect);
io.observe(el);
$(el).data("shiny-intersection-observer", io);
}
function handleMutate(initial = false) {
if (isPlot || el.classList.contains("shiny-report-theme")) {
doSendTheme(el, initial);
}
}
// TODO: do we need a polyfill for MutationObserver?
if (!$(el).data("shiny-mutate-observer")) {
const onMutate = debounce(100, handleMutate);
const mo = new MutationObserver(() => onMutate(false));
mo.observe(el, {
attributes: true,
attributeFilter: ["style", "class"],
});
$(el).data("shiny-mutate-observer", mo);
}
handleResize(initial);
handleMutate(initial);
});
// It could be that previously visible outputs have been removed from the DOM,
// in that case, consider them hidden.
visibleOutputs.forEach((id) => {
if (!outputIds.has(id)) {
visibleOutputs.delete(id);
setInput(".clientdata_output_" + id + "_hidden", true, initial);
}
});
}
sendImageSizeFns.setImageSend(inputBatchSender, doSendImageSize);
// Return true if the object or one of its ancestors in the DOM tree has
// style='display:none'; otherwise return false.
function isHidden(obj) {
// null means we've hit the top of the tree. If width or height is
// non-zero, then we know that no ancestor has display:none.
if (obj === null || obj.offsetWidth !== 0 || obj.offsetHeight !== 0) {
return false;
} else if (getStyle(obj, "display") === "none") {
return true;
} else {
return isHidden(obj.parentNode);
}
}
let lastKnownVisibleOutputs = {};
// Set initial state of outputs to hidden, if needed
$(".shiny-bound-output").each(function () {
const id = getIdFromEl(this);
if (isHidden(this)) {
initialValues[".clientdata_output_" + id + "_hidden"] = true;
} else {
lastKnownVisibleOutputs[id] = true;
initialValues[".clientdata_output_" + id + "_hidden"] = false;
}
});
// Send update when hidden state changes
function doSendOutputHiddenState() {
const visibleOutputs = {};
$(".shiny-bound-output").each(function () {
const id = getIdFromEl(this);
delete lastKnownVisibleOutputs[id];
// Assume that the object is hidden when width and height are 0
const hidden = isHidden(this),
evt = {
type: "shiny:visualchange",
visible: !hidden,
};
if (hidden) {
inputs.setInput(".clientdata_output_" + id + "_hidden", true);
} else {
visibleOutputs[id] = true;
inputs.setInput(".clientdata_output_" + id + "_hidden", false);
}
const $this = $(this);
// @ts-expect-error; Can not remove info on a established, malformed Event object
evt.binding = $this.data("shiny-output-binding");
// @ts-expect-error; Can not remove info on a established, malformed Event object
$this.trigger(evt);
});
// Anything left in lastKnownVisibleOutputs is orphaned
for (const name in lastKnownVisibleOutputs) {
if (hasOwnProperty(lastKnownVisibleOutputs, name))
inputs.setInput(".clientdata_output_" + name + "_hidden", true);
}
// Update the visible outputs for next time
lastKnownVisibleOutputs = visibleOutputs;
}
// sendOutputHiddenState gets called each time DOM elements are shown or
// hidden. This can be in the hundreds or thousands of times at startup.
// We'll debounce it, so that we do the actual work once per tick.
const sendOutputHiddenStateDebouncer = new Debouncer(
null,
doSendOutputHiddenState,
0
);
function sendOutputHiddenState() {
sendOutputHiddenStateDebouncer.normalCall();
}
// We need to make sure doSendOutputHiddenState actually gets called before
// the inputBatchSender sends data to the server. The lastChanceCallback
// here does that - if the debouncer has a pending call, flush it.
inputBatchSender.lastChanceCallback.push(function () {
if (sendOutputHiddenStateDebouncer.isPending())
sendOutputHiddenStateDebouncer.immediateCall();
});
// Given a namespace and a handler function, return a function that invokes
// the handler only when e's namespace matches. For example, if the
// namespace is "bs", it would match when e.namespace is "bs" or "bs.tab".
// If the namespace is "bs.tab", it would match for "bs.tab", but not "bs".
function filterEventsByNamespace(namespace, handler, ...args) {
namespace = namespace.split(".");
return function (e) {
const eventNamespace = e.namespace.split(".");
// If any of the namespace strings aren't present in this event, quit.
for (let i = 0; i < namespace.length; i++) {
if (eventNamespace.indexOf(namespace[i]) === -1) return;
}
handler.apply(this, [namespace, handler, ...args]);
};
}
// The size of each image may change either because the browser window was
// resized, or because a tab was shown/hidden (hidden elements report size
// of 0x0). It's OK to over-report sizes because the input pipeline will
// filter out values that haven't changed.
$(window).resize(debounce(500, sendImageSizeFns.regular));
// Need to register callbacks for each Bootstrap 3 class.
const bs3classes = [
"modal",
"dropdown",
"tab",
"tooltip",
"popover",
"collapse",
];
$.each(bs3classes, function (idx, classname) {
$(document.body).on(
"shown.bs." + classname + ".sendImageSize",
"*",
filterEventsByNamespace("bs", sendImageSizeFns.regular)
);
$(document.body).on(
"shown.bs." +
classname +
".sendOutputHiddenState " +
"hidden.bs." +
classname +
".sendOutputHiddenState",
"*",
filterEventsByNamespace("bs", sendOutputHiddenState)
);
});
// This is needed for Bootstrap 2 compatibility and for non-Bootstrap
// related shown/hidden events (like conditionalPanel)
$(document.body).on("shown.sendImageSize", "*", sendImageSizeFns.regular);
$(document.body).on(
"shown.sendOutputHiddenState hidden.sendOutputHiddenState",
"*",
sendOutputHiddenState
);
// Send initial input values to the server and also register a callback
// to send updated input values whenever we receive new UI, etc.
doSendOutputInfo(true);
sendOutputInfoFns.setSendMethod(inputBatchSender, doSendOutputInfo);
// Send initial pixel ratio, and update it if it changes
initialValues[".clientdata_pixelratio"] = pixelRatio();

View File

@@ -7,7 +7,7 @@ import {
shinyInitializeInputs,
shinyUnbindAll,
} from "./initedMethods";
import { sendImageSizeFns } from "./sendImageSize";
import { sendOutputInfoFns } from "./sendOutputInfo";
import { renderHtml as singletonsRenderHtml } from "./singletons";
import type { WherePosition } from "./singletons";
@@ -250,7 +250,7 @@ function addStylesheetsAndRestyle(links: HTMLLinkElement[]): void {
// should have been applied synchronously.
oldStyle.remove();
removeSheet(oldSheet);
sendImageSizeFns.transitioned();
sendOutputInfoFns.transitioned();
};
xhr.send();
};
@@ -310,7 +310,7 @@ function addStylesheetsAndRestyle(links: HTMLLinkElement[]): void {
// base64-encoded and inlined into the href. We also add a dummy DOM
// element that the CSS applies to. The dummy CSS includes a
// transition, and when the `transitionend` event happens, we call
// sendImageSizeFns.transitioned() and remove the old sheet. We also remove the
// sendOutputInfoFns.transitioned() and remove the old sheet. We also remove the
// dummy DOM element and dummy CSS content.
//
// The reason this works is because (we assume) that if multiple
@@ -320,7 +320,7 @@ function addStylesheetsAndRestyle(links: HTMLLinkElement[]): void {
//
// Because it is common for multiple stylesheets to arrive close
// together, but not on exactly the same tick, we call
// sendImageSizeFns.transitioned(), which is debounced. Otherwise, it can result in
// sendOutputInfoFns.transitioned(), which is debounced. Otherwise, it can result in
// the same plot being redrawn multiple times with different
// styling.
$link.attr("onload", () => {
@@ -333,7 +333,7 @@ function addStylesheetsAndRestyle(links: HTMLLinkElement[]): void {
$dummyEl.one("transitionend", () => {
$dummyEl.remove();
removeSheet(oldSheet);
sendImageSizeFns.transitioned();
sendOutputInfoFns.transitioned();
});
$(document.body).append($dummyEl);

View File

@@ -1,36 +0,0 @@
import type { InputBatchSender } from "../inputPolicies";
import { debounce, Debouncer } from "../time";
class SendImageSize {
// This function gets defined in initShiny() and 'hoisted' so it can be reused
// (to send CSS info) inside of Shiny.renderDependencies()
regular: () => void;
transitioned: () => void;
setImageSend(
inputBatchSender: InputBatchSender,
doSendImageSize: () => void
): Debouncer<typeof doSendImageSize> {
const sendImageSizeDebouncer = new Debouncer(null, doSendImageSize, 0);
this.regular = function () {
sendImageSizeDebouncer.normalCall();
};
// Make sure sendImageSize actually gets called before the inputBatchSender
// sends data to the server.
inputBatchSender.lastChanceCallback.push(function () {
if (sendImageSizeDebouncer.isPending())
sendImageSizeDebouncer.immediateCall();
});
// A version of sendImageSize which debounces for longer.
this.transitioned = debounce(200, this.regular);
return sendImageSizeDebouncer;
}
}
const sendImageSizeFns = new SendImageSize();
export { sendImageSizeFns };

View File

@@ -0,0 +1,36 @@
import type { InputBatchSender } from "../inputPolicies";
import { debounce, Debouncer } from "../time";
class SendOutputInfo {
// This function gets defined in initShiny() and 'hoisted' so it can be reused
// (to send CSS info) inside of Shiny.renderDependencies()
regular: () => void;
transitioned: () => void;
setSendMethod(
inputBatchSender: InputBatchSender,
doSendOutputInfo: () => void
): Debouncer<typeof doSendOutputInfo> {
const sendOutputInfoDebouncer = new Debouncer(null, doSendOutputInfo, 0);
this.regular = function () {
sendOutputInfoDebouncer.normalCall();
};
// Make sure sendOutputInfo actually gets called before the inputBatchSender
// sends data to the server.
inputBatchSender.lastChanceCallback.push(function () {
if (sendOutputInfoDebouncer.isPending())
sendOutputInfoDebouncer.immediateCall();
});
// A version of sendOutputInfo which debounces for longer.
this.transitioned = debounce(200, this.regular);
return sendOutputInfoDebouncer;
}
}
const sendOutputInfoFns = new SendOutputInfo();
export { sendOutputInfoFns };

View File

@@ -9,7 +9,7 @@ function escapeHTML(str: string): string {
"<": "&lt;",
">": "&gt;",
// eslint-disable-next-line prettier/prettier
"\"": "&quot;",
'"': "&quot;",
"'": "&#039;",
"/": "&#x2F;",
};
@@ -54,6 +54,20 @@ function getStyle(el: Element, styleProp: string): string | undefined {
return x;
}
// Return true if the object or one of its ancestors in the DOM tree has
// style='display:none'; otherwise return false.
function isHidden(obj: HTMLElement): boolean {
// null means we've hit the top of the tree. If width or height is
// non-zero, then we know that no ancestor has display:none.
if (obj === null || obj.offsetWidth !== 0 || obj.offsetHeight !== 0) {
return false;
} else if (getStyle(obj, "display") === "none") {
return true;
} else {
return isHidden(obj.parentElement);
}
}
// Convert a number to a string with leading zeros
function padZeros(n: number, digits: number): string {
let str = n.toString();
@@ -384,6 +398,7 @@ export {
randomId,
strToBool,
getStyle,
isHidden,
padZeros,
roundSignif,
parseDate,

View File

@@ -7,8 +7,6 @@ declare type BindInputsCtx = {
inputsRate: InputRateDecorator;
inputBindings: BindingRegistry<InputBinding>;
outputBindings: BindingRegistry<OutputBinding>;
sendOutputHiddenState: () => void;
maybeAddThemeObserver: (el: HTMLElement) => void;
initDeferredIframes: () => void;
};
declare function bindInputs(shinyCtx: BindInputsCtx, scope?: BindScope): {
@@ -22,7 +20,7 @@ declare function bindInputs(shinyCtx: BindInputsCtx, scope?: BindScope): {
};
};
declare function _bindAll(shinyCtx: BindInputsCtx, scope: BindScope): ReturnType<typeof bindInputs>;
declare function unbindAll(shinyCtx: BindInputsCtx, scope: BindScope, includeSelf?: boolean): void;
declare function unbindAll(scope: BindScope, includeSelf?: boolean): void;
declare function bindAll(shinyCtx: BindInputsCtx, scope: BindScope): void;
export { unbindAll, bindAll, _bindAll };
export type { BindScope, BindInputsCtx };

View File

@@ -1,9 +0,0 @@
import type { InputBatchSender } from "../inputPolicies";
import { Debouncer } from "../time";
declare class SendImageSize {
regular: () => void;
transitioned: () => void;
setImageSend(inputBatchSender: InputBatchSender, doSendImageSize: () => void): Debouncer<typeof doSendImageSize>;
}
declare const sendImageSizeFns: SendImageSize;
export { sendImageSizeFns };

View File

@@ -0,0 +1,9 @@
import type { InputBatchSender } from "../inputPolicies";
import { Debouncer } from "../time";
declare class SendOutputInfo {
regular: () => void;
transitioned: () => void;
setSendMethod(inputBatchSender: InputBatchSender, doSendOutputInfo: () => void): Debouncer<typeof doSendOutputInfo>;
}
declare const sendOutputInfoFns: SendOutputInfo;
export { sendOutputInfoFns };

View File

@@ -4,6 +4,7 @@ declare function escapeHTML(str: string): string;
declare function randomId(): string;
declare function strToBool(str: string): boolean | undefined;
declare function getStyle(el: Element, styleProp: string): string | undefined;
declare function isHidden(obj: HTMLElement): boolean;
declare function padZeros(n: number, digits: number): string;
declare function roundSignif(x: number, digits?: number): number;
declare function parseDate(dateString: string): Date;
@@ -29,4 +30,4 @@ declare function updateLabel(labelTxt: string | undefined, labelNode: JQuery<HTM
declare function getComputedLinkColor(el: HTMLElement): string;
declare function isBS3(): boolean;
declare function toLowerCase<T extends string>(str: T): Lowercase<T>;
export { escapeHTML, randomId, strToBool, getStyle, padZeros, roundSignif, parseDate, formatDateUTC, makeResizeFilter, pixelRatio, scopeExprToFunc, asArray, mergeSort, $escape, mapValues, isnan, _equal, equal, compareVersion, updateLabel, getComputedLinkColor, makeBlob, hasOwnProperty, isBS3, toLowerCase, };
export { escapeHTML, randomId, strToBool, getStyle, isHidden, padZeros, roundSignif, parseDate, formatDateUTC, makeResizeFilter, pixelRatio, scopeExprToFunc, asArray, mergeSort, $escape, mapValues, isnan, _equal, equal, compareVersion, updateLabel, getComputedLinkColor, makeBlob, hasOwnProperty, isBS3, toLowerCase, };

View File

@@ -2,6 +2,7 @@
"declaration": true,
"compilerOptions": {
"target": "ES5",
"lib": ["dom"],
"isolatedModules": true,
"esModuleInterop": true,
"declaration": true,

View File

@@ -1935,6 +1935,13 @@ __metadata:
languageName: node
linkType: hard
"@types/resize-observer-browser@npm:^0.1.7":
version: 0.1.7
resolution: "@types/resize-observer-browser@npm:0.1.7"
checksum: eef8c82c9d3f9f4c57cad6134e795f4eebfcf11a8790d21636539e4314bf0e0c140e525c4a8cb440dd42788855daddb2c52c18f93e1c59bed20e9be6e65de290
languageName: node
linkType: hard
"@types/rstudio-shiny@workspace:.":
version: 0.0.0-use.local
resolution: "@types/rstudio-shiny@workspace:."
@@ -1958,6 +1965,7 @@ __metadata:
"@types/jqueryui": 1.12.15
"@types/lodash": ^4.14.170
"@types/node": ^15.6.1
"@types/resize-observer-browser": ^0.1.7
"@types/selectize": 0.12.34
"@types/showdown": ^1.9.3
"@typescript-eslint/eslint-plugin": ^4.25.0