Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit a250aa7

Browse files
committedJul 10, 2024·
feat: Implement model.off
1 parent b588447 commit a250aa7

File tree

1 file changed

+97
-22
lines changed

1 file changed

+97
-22
lines changed
 

‎inst/htmlwidgets/anyhtmlwidget.js

+97-22
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,113 @@
1+
class CallbackRegistry {
2+
#callbacks = {};
3+
add(name, callback) {
4+
if (!this.#callbacks[name]) {
5+
this.#callbacks[name] = [];
6+
}
7+
this.#callbacks[name].push(callback);
8+
}
9+
remove(name, callback) {
10+
if (!this.#callbacks[name]) {
11+
return [];
12+
}
13+
const callbacks = this.#callbacks[name];
14+
if (callback) {
15+
// Remove a specific callback
16+
return this.#callbacks[name] = callbacks.filter((cb) => cb !== callback);
17+
}
18+
// Remove all callbacks
19+
this.#callbacks[name] = [];
20+
return callbacks;
21+
}
22+
}
23+
24+
/**
25+
* An R-backed implementation of the @anywidget/types AnyModel interface.
26+
*
27+
* @see {@link https://github.com/manzt/anywidget/tree/main/packages/types}
28+
*/
129
class AnyModel {
30+
/** @type {Record<string, any>} */
31+
#state;
32+
/** @type {string} */
33+
#ns_id;
34+
/** @type {WebSocket | undefined} */
35+
#ws = undefined;
36+
/** @type {EventTarget} */
37+
#target = new EventTarget();
38+
/** @type {CallbackRegistry} */
39+
#callbacks = new CallbackRegistry();
40+
/** @type {Set<string>} */
41+
#unsavedKeys = new Set();
42+
43+
/**
44+
* @param {Record<string, any>} state - initial model state
45+
* @param {string} ns_id - the Shiny namespace ID
46+
* @param {WebSocket} [ws] - a WebSocket connection
47+
*/
248
constructor(state, ns_id, ws) {
3-
this.ns_id = ns_id;
4-
this.state = state;
5-
this.target = new EventTarget();
6-
this.ws = ws;
7-
this.unsavedKeys = new Set();
49+
this.#ns_id = ns_id;
50+
this.#state = state;
51+
this.#ws = ws;
852
}
53+
/** @param {string} name */
954
get(name) {
10-
return this.state[name];
55+
return this.#state[name];
1156
}
57+
/**
58+
* @param {string} key
59+
* @param {any} value
60+
*/
1261
set(key, value) {
13-
this.state[key] = value;
14-
this.unsavedKeys.add(key);
15-
this.target.dispatchEvent(
16-
new CustomEvent(`change:${key}`, { detail: value }),
17-
);
62+
this.#state[key] = value;
63+
this.#unsavedKeys.add(key);
64+
this.#target.dispatchEvent(
65+
new CustomEvent(`change:${key}`, { detail: value }),
66+
);
67+
this.#target.dispatchEvent(
68+
new CustomEvent("change", { detail: value }),
69+
);
1870
}
71+
/**
72+
* @param {string} name
73+
* @param {Function} callback
74+
*/
1975
on(name, callback) {
20-
this.target.addEventListener(name, callback);
76+
this.#target.addEventListener(name, callback);
77+
this.#callbacks.add(name, callback);
78+
}
79+
/**
80+
* @param {string} name
81+
* @param {Function} [callback]
82+
*/
83+
off(name, callback) {
84+
for (const cb of this.#callbacks.remove(name, callback)) {
85+
this.#target.removeEventListener(name, cb);
86+
}
2187
}
22-
off(name) {
23-
// Not yet implemented
88+
/**
89+
* @param {any} msg
90+
* @param {unknown} [callbacks]
91+
* @param {ArrayBuffer[]} [buffers]
92+
*/
93+
send(msg, callbacks, buffers) {
94+
// TODO: impeThrow?
95+
console.error(`model.send is not yet implemented for anyhtmlwidget`);
2496
}
2597
save_changes() {
2698
const unsavedState = Object.fromEntries(
27-
Array.from(this.unsavedKeys.values())
28-
.map(key => ([key, this.state[key]]))
99+
Array.from(this.#unsavedKeys.values())
100+
.map((key) => [key, this.#state[key]]),
29101
);
30-
this.unsavedKeys = new Set();
31-
if(window && window.Shiny && window.Shiny.setInputValue) {
32-
const eventPrefix = this.ns_id ? `${this.ns_id}-` : '';
33-
Shiny.setInputValue(`${eventPrefix}anyhtmlwidget_on_save_changes`, unsavedState);
34-
} else if(this.ws) {
35-
this.ws.send(JSON.stringify({
102+
this.#unsavedKeys = new Set();
103+
if (window && window.Shiny && window.Shiny.setInputValue) {
104+
const eventPrefix = this.#ns_id ? `${this.#ns_id}-` : "";
105+
Shiny.setInputValue(
106+
`${eventPrefix}anyhtmlwidget_on_save_changes`,
107+
unsavedState,
108+
);
109+
} else if (this.#ws) {
110+
this.#ws.send(JSON.stringify({
36111
type: "on_save_changes",
37112
payload: unsavedState,
38113
}));

0 commit comments

Comments
 (0)
Please sign in to comment.