mirror of
https://github.com/rstudio/shiny.git
synced 2026-01-10 23:48:01 -05:00
Compare commits
14 Commits
webr-next
...
bindAfterR
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f694e708c1 | ||
|
|
66a972604e | ||
|
|
91df0b15b7 | ||
|
|
a1e5514f81 | ||
|
|
d93136ca1b | ||
|
|
4702ff480c | ||
|
|
490803fc10 | ||
|
|
1f752b6420 | ||
|
|
0f00ecfc20 | ||
|
|
0fe804012a | ||
|
|
ed12e92d60 | ||
|
|
d0bf86e5e2 | ||
|
|
b023350b90 | ||
|
|
7bccfeb774 |
2
NEWS.md
2
NEWS.md
@@ -12,6 +12,8 @@
|
||||
|
||||
* `Map` objects are now initialized at load time instead of build time. This avoids potential problems that could arise from storing `fastmap` objects into the built Shiny package. (#3775)
|
||||
|
||||
* Closed #3635: `window.Shiny.outputBindings` and `window.Shiny.inputBindings` gain a `onRegister()` method, to register callbacks that execute whenever a new binding is registered. Internally, Shiny uses this to check whether it should re-bind to the DOM when a binding has been registered. (#3638)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fixed #3771: Sometimes the error `ion.rangeSlider.min.js: i.stopPropagation is not a function` would appear in the JavaScript console. (#3772)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
2
inst/www/shared/shiny.min.js
vendored
2
inst/www/shared/shiny.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,4 +1,5 @@
|
||||
import { mergeSort } from "../utils";
|
||||
import { Callbacks } from "../utils/callbacks";
|
||||
|
||||
interface BindingBase {
|
||||
name: string;
|
||||
@@ -14,6 +15,7 @@ class BindingRegistry<Binding extends BindingBase> {
|
||||
name!: string;
|
||||
bindings: Array<BindingObj<Binding>> = [];
|
||||
bindingNames: { [key: string]: BindingObj<Binding> } = {};
|
||||
registerCallbacks: Callbacks = new Callbacks();
|
||||
|
||||
register(binding: Binding, bindingName: string, priority = 0): void {
|
||||
const bindingObj = { binding, priority };
|
||||
@@ -23,6 +25,12 @@ class BindingRegistry<Binding extends BindingBase> {
|
||||
this.bindingNames[bindingName] = bindingObj;
|
||||
binding.name = bindingName;
|
||||
}
|
||||
|
||||
this.registerCallbacks.invoke();
|
||||
}
|
||||
|
||||
onRegister(fn: () => void, once = true): void {
|
||||
this.registerCallbacks.register(fn, once);
|
||||
}
|
||||
|
||||
setPriority(bindingName: string, priority: number): void {
|
||||
|
||||
@@ -150,6 +150,24 @@ function initShiny(windowShiny: Shiny): void {
|
||||
(x) => x.value
|
||||
);
|
||||
|
||||
// When future bindings are registered, rebind to the DOM once the current
|
||||
// event loop is done. This is necessary since the binding might be registered
|
||||
// after Shiny has already bound to the DOM (#3635)
|
||||
let enqueuedCount = 0;
|
||||
const enqueueRebind = () => {
|
||||
enqueuedCount++;
|
||||
windowShiny.shinyapp?.actionQueue.enqueue(() => {
|
||||
enqueuedCount--;
|
||||
// If this function is scheduled more than once in the queue, don't do anything.
|
||||
// Only do the bindAll when we get to the last instance of this function in the queue.
|
||||
if (enqueuedCount === 0) {
|
||||
windowShiny.bindAll?.(document.documentElement);
|
||||
}
|
||||
});
|
||||
};
|
||||
inputBindings.onRegister(enqueueRebind, false);
|
||||
outputBindings.onRegister(enqueueRebind, false);
|
||||
|
||||
// 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(
|
||||
|
||||
45
srcts/src/utils/callbacks.ts
Normal file
45
srcts/src/utils/callbacks.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
type Cb = {
|
||||
once: boolean;
|
||||
fn: () => void;
|
||||
};
|
||||
|
||||
type Cbs = {
|
||||
[key: string]: Cb;
|
||||
};
|
||||
|
||||
class Callbacks {
|
||||
callbacks: Cbs = {};
|
||||
id = 0;
|
||||
|
||||
register(fn: () => void, once = true): () => void {
|
||||
this.id += 1;
|
||||
const id = this.id;
|
||||
|
||||
this.callbacks[id] = { fn, once };
|
||||
return () => {
|
||||
delete this.callbacks[id];
|
||||
};
|
||||
}
|
||||
|
||||
invoke(): void {
|
||||
for (const id in this.callbacks) {
|
||||
const cb = this.callbacks[id];
|
||||
|
||||
try {
|
||||
cb.fn();
|
||||
} finally {
|
||||
if (cb.once) delete this.callbacks[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.callbacks = {};
|
||||
}
|
||||
|
||||
count(): number {
|
||||
return Object.keys(this.callbacks).length;
|
||||
}
|
||||
}
|
||||
|
||||
export { Callbacks };
|
||||
3
srcts/types/src/bindings/registry.d.ts
vendored
3
srcts/types/src/bindings/registry.d.ts
vendored
@@ -1,3 +1,4 @@
|
||||
import { Callbacks } from "../utils/callbacks";
|
||||
interface BindingBase {
|
||||
name: string;
|
||||
}
|
||||
@@ -12,7 +13,9 @@ declare class BindingRegistry<Binding extends BindingBase> {
|
||||
bindingNames: {
|
||||
[key: string]: BindingObj<Binding>;
|
||||
};
|
||||
registerCallbacks: Callbacks;
|
||||
register(binding: Binding, bindingName: string, priority?: number): void;
|
||||
onRegister(fn: () => void, once?: boolean): void;
|
||||
setPriority(bindingName: string, priority: number): void;
|
||||
getPriority(bindingName: string): number | false;
|
||||
getBindings(): Array<BindingObj<Binding>>;
|
||||
|
||||
16
srcts/types/src/utils/callbacks.d.ts
vendored
Normal file
16
srcts/types/src/utils/callbacks.d.ts
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
type Cb = {
|
||||
once: boolean;
|
||||
fn: () => void;
|
||||
};
|
||||
type Cbs = {
|
||||
[key: string]: Cb;
|
||||
};
|
||||
declare class Callbacks {
|
||||
callbacks: Cbs;
|
||||
id: number;
|
||||
register(fn: () => void, once?: boolean): () => void;
|
||||
invoke(): void;
|
||||
clear(): void;
|
||||
count(): number;
|
||||
}
|
||||
export { Callbacks };
|
||||
Reference in New Issue
Block a user