Compare commits

...

14 Commits

Author SHA1 Message Date
Carson
f694e708c1 Add comment; removing logging 2023-03-31 10:28:39 -05:00
Carson
66a972604e Use actionQueue to guarantee bind happens after async rendering has completed 2023-03-31 10:23:18 -05:00
Carson
91df0b15b7 Update news 2023-03-29 14:57:23 -05:00
Carson
a1e5514f81 Merge branch 'main' into bindAfterRegister 2023-03-29 14:49:40 -05:00
Carson
d93136ca1b lint; yarn build 2023-03-29 14:47:20 -05:00
Carson Sievert
4702ff480c Update srcts/src/shiny/init.ts 2023-03-29 14:41:59 -05:00
Carson Sievert
490803fc10 Update srcts/src/shiny/init.ts 2023-03-29 14:36:30 -05:00
Carson Sievert
1f752b6420 Merge branch 'main' into bindAfterRegister 2023-01-18 15:15:54 -06:00
wch
0f00ecfc20 Sync package version (GitHub Actions) 2023-01-06 22:08:33 +00:00
Winston Chang
0fe804012a Merge branch 'main' into bindAfterRegister 2023-01-06 16:00:57 -06:00
Winston Chang
ed12e92d60 Merge branch 'main' into bindAfterRegister 2022-10-05 12:46:13 -05:00
Carson
d0bf86e5e2 Add a clear() method to Callbacks 2022-07-07 13:37:37 -05:00
Carson
b023350b90 Introduce an onRegister() method on BindingRegistry to help solve the problem with sharing state 2022-07-07 13:36:21 -05:00
Carson
7bccfeb774 Close #3635: attempt another bind when registering a binding outside a renderHtml() context 2022-07-07 13:35:07 -05:00
10 changed files with 1863 additions and 1643 deletions

View File

@@ -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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -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 {

View File

@@ -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(

View 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 };

View File

@@ -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
View 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 };