Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

* `textAreaInput()` gains a `autoresize` option, which automatically resizes the text area to fit its content. (#4210)

* The `callback` argument of Shiny.js' `InputBinding.subscribe()` method gains support for a value of `"event"`. This makes it possible for an input binding to use event priority when updating the value (i.e., send immediately and always resend, even if the value hasn't changed). (#4211)

## Changes

* Shiny no longer suspends input changes when _any_ `<input type="submit">` or `<button type="submit">` is on the page. Instead, it now only suspends when a `submitButton()` is present. If you have reason for creating a submit button from custom HTML, add a CSS class of `shiny-submit-button` to the button. (#4209)
Expand Down
14 changes: 5 additions & 9 deletions inst/www/shared/shiny.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions inst/www/shared/shiny.js.map

Large diffs are not rendered by default.

28 changes: 14 additions & 14 deletions inst/www/shared/shiny.min.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions inst/www/shared/shiny.min.js.map

Large diffs are not rendered by default.

15 changes: 11 additions & 4 deletions srcts/src/bindings/input/inputBinding.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { EventPriority } from "../../inputPolicies/inputPolicy";
import type { RatePolicyModes } from "../../inputPolicies/inputRateDecorator";
import type { BindScope } from "../../shiny/bind";

Expand Down Expand Up @@ -26,10 +27,16 @@ class InputBinding {
el; // unused var
}

// The callback method takes one argument, whose value is boolean. If true,
// allow deferred (debounce or throttle) sending depending on the value of
// getRatePolicy. If false, send value immediately. Default behavior is `false`
subscribe(el: HTMLElement, callback: (value: boolean) => void): void {
// Historically, the callback value could only be boolean. In this case:
// * false: send value immediately (i.e., priority = "immediate")
// * true: send value later (i.e., priority = "deferred")
// * The input rate policy is also consulted on whether to debounce or throttle
// In recent versions, the value can also be "event", meaning that the
// value should be sent regardless of whether it has changed.
subscribe(
el: HTMLElement,
callback: (value: EventPriority | boolean) => void
): void {
// empty
el; // unused var
callback; // unused var
Expand Down
32 changes: 18 additions & 14 deletions srcts/src/shiny/bind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
InputRateDecorator,
InputValidateDecorator,
} from "../inputPolicies";
import type { InputPolicyOpts } from "../inputPolicies/inputPolicy";
import { shinyAppBindOutput, shinyAppUnbindOutput } from "./initedMethods";
import { sendImageSizeFns } from "./sendImageSize";

Expand All @@ -27,7 +28,7 @@ function valueChangeCallback(
inputs: InputValidateDecorator,
binding: InputBinding,
el: HTMLElement,
allowDeferred: boolean
priority: InputPolicyOpts["priority"]
) {
let id = binding.getId(el);

Expand All @@ -37,17 +38,7 @@ function valueChangeCallback(

if (type) id = id + ":" + type;

const opts: {
priority: "deferred" | "immediate";
binding: typeof binding;
el: typeof el;
} = {
priority: allowDeferred ? "deferred" : "immediate",
binding: binding,
el: el,
};

inputs.setInput(id, value, opts);
inputs.setInput(id, value, { priority, binding, el });
}
}

Expand Down Expand Up @@ -272,8 +263,21 @@ function bindInputs(
const thisBinding = binding;
const thisEl = el;

return function (allowDeferred: boolean) {
valueChangeCallback(inputs, thisBinding, thisEl, allowDeferred);
// Historically speaking, this callback has only accepted a boolean value,
// but in recent versions it can also accept an input priority.
// The `priority` parameter can be:
// - A boolean: `true` maps to "deferred", and `false` maps to "immediate".
// - A string or other value representing a specific priority mode, which is passed through as-is.
// This conversion ensures backward compatibility while supporting new priority modes.
return function (priority: InputPolicyOpts["priority"] | boolean) {
const normalizedPriority =
typeof priority !== "boolean"
? priority
: priority
? "deferred"
: "immediate";

valueChangeCallback(inputs, thisBinding, thisEl, normalizedPriority);
};
})();

Expand Down
3 changes: 2 additions & 1 deletion srcts/types/src/bindings/input/inputBinding.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { EventPriority } from "../../inputPolicies/inputPolicy";
import type { RatePolicyModes } from "../../inputPolicies/inputRateDecorator";
import type { BindScope } from "../../shiny/bind";
declare class InputBinding {
Expand All @@ -6,7 +7,7 @@ declare class InputBinding {
getId(el: HTMLElement): string;
getType(el: HTMLElement): string | null;
getValue(el: HTMLElement): any;
subscribe(el: HTMLElement, callback: (value: boolean) => void): void;
subscribe(el: HTMLElement, callback: (value: EventPriority | boolean) => void): void;
unsubscribe(el: HTMLElement): void;
receiveMessage(el: HTMLElement, data: unknown): Promise<void> | void;
getState(el: HTMLElement): unknown;
Expand Down