clean up native js more
This commit is contained in:
7 files changed
+173
-351
No files matched your search
+88
-79
@@ -12,95 +12,104 @@ fn main() {
|
||||
|
||||
fn app() -> Element {
|
||||
let mut values = use_signal(|| HashMap::new());
|
||||
let mut submitted_values = use_signal(|| HashMap::new());
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
h1 { "Form" }
|
||||
div { style: "display: flex",
|
||||
div { style: "width: 50%",
|
||||
h1 { "Form" }
|
||||
|
||||
// The form element is used to create an HTML form for user input
|
||||
// You can attach regular attributes to it
|
||||
form {
|
||||
id: "cool-form",
|
||||
style: "display: flex; flex-direction: column;",
|
||||
|
||||
// You can attach a handler to the entire form
|
||||
oninput: move |ev| {
|
||||
println!("Input event: {:#?}", ev);
|
||||
values.set(ev.values());
|
||||
},
|
||||
|
||||
// On desktop/liveview, the form will not navigate the page - the expectation is that you handle
|
||||
// The form event.
|
||||
// Howver, if your form doesn't have a submit handler, it might navigate the page depending on the webview.
|
||||
// We suggest always attaching a submit handler to the form.
|
||||
onsubmit: move |ev| {
|
||||
println!("Submit event: {:#?}", ev);
|
||||
},
|
||||
|
||||
// Regular text inputs with handlers
|
||||
label { r#for: "username", "Username" }
|
||||
input {
|
||||
r#type: "text",
|
||||
name: "username",
|
||||
oninput: move |ev| {
|
||||
println!("setting username");
|
||||
values.set(ev.values());
|
||||
}
|
||||
if !submitted_values.read().is_empty() {
|
||||
h2 { "Submitted! ✅" }
|
||||
}
|
||||
|
||||
// And then the various inputs that might exist
|
||||
// Note for a value to be returned in .values(), it must be named!
|
||||
// The form element is used to create an HTML form for user input
|
||||
// You can attach regular attributes to it
|
||||
form {
|
||||
id: "cool-form",
|
||||
style: "display: flex; flex-direction: column;",
|
||||
|
||||
label { r#for: "full-name", "Full Name" }
|
||||
input { r#type: "text", name: "full-name" }
|
||||
|
||||
label { r#for: "email", "Email" }
|
||||
input { r#type: "email", pattern: ".+@example\\.com", size: "30", required: "true", id: "email", name: "email" }
|
||||
|
||||
label { r#for: "password", "Password" }
|
||||
input { r#type: "password", name: "password" }
|
||||
|
||||
label { r#for: "color", "Color" }
|
||||
input { r#type: "radio", checked: true, name: "color", value: "red" }
|
||||
input { r#type: "radio", name: "color", value: "blue" }
|
||||
input { r#type: "radio", name: "color", value: "green" }
|
||||
|
||||
// Select multiple comes in as a comma separated list of selected values
|
||||
// You should split them on the comma to get the values manually
|
||||
label { r#for: "country", "Country" }
|
||||
select {
|
||||
name: "country",
|
||||
multiple: true,
|
||||
// You can attach a handler to the entire form
|
||||
oninput: move |ev| {
|
||||
println!("Input event: {:#?}", ev);
|
||||
println!("Values: {:#?}", ev.value().split(',').collect::<Vec<_>>());
|
||||
values.set(ev.values());
|
||||
},
|
||||
option { value: "usa", "USA" }
|
||||
option { value: "canada", "Canada" }
|
||||
option { value: "mexico", "Mexico" }
|
||||
|
||||
// On desktop/liveview, the form will not navigate the page - the expectation is that you handle
|
||||
// The form event.
|
||||
// Howver, if your form doesn't have a submit handler, it might navigate the page depending on the webview.
|
||||
// We suggest always attaching a submit handler to the form.
|
||||
onsubmit: move |ev| {
|
||||
println!("Submit event: {:#?}", ev);
|
||||
submitted_values.set(ev.values());
|
||||
},
|
||||
|
||||
// Regular text inputs with handlers
|
||||
label { r#for: "username", "Username" }
|
||||
input {
|
||||
r#type: "text",
|
||||
name: "username",
|
||||
oninput: move |ev| {
|
||||
println!("setting username");
|
||||
values.set(ev.values());
|
||||
}
|
||||
}
|
||||
|
||||
// And then the various inputs that might exist
|
||||
// Note for a value to be returned in .values(), it must be named!
|
||||
|
||||
label { r#for: "full-name", "Full Name" }
|
||||
input { r#type: "text", name: "full-name" }
|
||||
|
||||
label { r#for: "email", "Email" }
|
||||
input { r#type: "email", pattern: ".+@example\\.com", size: "30", required: "true", id: "email", name: "email" }
|
||||
|
||||
label { r#for: "password", "Password" }
|
||||
input { r#type: "password", name: "password" }
|
||||
|
||||
label { r#for: "color", "Color" }
|
||||
input { r#type: "radio", checked: true, name: "color", value: "red" }
|
||||
input { r#type: "radio", name: "color", value: "blue" }
|
||||
input { r#type: "radio", name: "color", value: "green" }
|
||||
|
||||
// Select multiple comes in as a comma separated list of selected values
|
||||
// You should split them on the comma to get the values manually
|
||||
label { r#for: "country", "Country" }
|
||||
select {
|
||||
name: "country",
|
||||
multiple: true,
|
||||
oninput: move |ev| {
|
||||
println!("Input event: {:#?}", ev);
|
||||
println!("Values: {:#?}", ev.value().split(',').collect::<Vec<_>>());
|
||||
},
|
||||
option { value: "usa", "USA" }
|
||||
option { value: "canada", "Canada" }
|
||||
option { value: "mexico", "Mexico" }
|
||||
}
|
||||
|
||||
// Safari can be quirky with color inputs on mac.
|
||||
// We recommend always providing a text input for color as a fallback.
|
||||
label { r#for: "color", "Color" }
|
||||
input { r#type: "color", value: "#000002", name: "head", id: "head" }
|
||||
|
||||
// Dates!
|
||||
input {
|
||||
min: "2018-01-01",
|
||||
value: "2018-07-22",
|
||||
r#type: "date",
|
||||
name: "trip-start",
|
||||
max: "2025-12-31",
|
||||
id: "start"
|
||||
}
|
||||
|
||||
// Buttons will submit your form by default.
|
||||
button { r#type: "submit", value: "Submit", "Submit the form" }
|
||||
}
|
||||
|
||||
// Safari can be quirky with color inputs on mac.
|
||||
// We recommend always providing a text input for color as a fallback.
|
||||
label { r#for: "color", "Color" }
|
||||
input { r#type: "color", value: "#000002", name: "head", id: "head" }
|
||||
|
||||
// Dates!
|
||||
input {
|
||||
min: "2018-01-01",
|
||||
value: "2018-07-22",
|
||||
r#type: "date",
|
||||
name: "trip-start",
|
||||
max: "2025-12-31",
|
||||
id: "start"
|
||||
}
|
||||
|
||||
// Buttons will submit your form by default.
|
||||
button { r#type: "submit", value: "Submit", "Submit the form" }
|
||||
}
|
||||
}
|
||||
div {
|
||||
h1 { "Oninput Values" }
|
||||
pre { "{values:#?}" }
|
||||
div { style: "width: 50%",
|
||||
h1 { "Oninput Values" }
|
||||
pre { "{values:#?}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::{assets::*, edits::EditQueue};
|
||||
use dioxus_interpreter_js::unified_bindings::native_js;
|
||||
use dioxus_interpreter_js::unified_bindings::SLEDGEHAMMER_JS;
|
||||
use dioxus_interpreter_js::NATIVE_JS;
|
||||
use std::path::{Path, PathBuf};
|
||||
use wry::{
|
||||
http::{status::StatusCode, Request, Response},
|
||||
@@ -12,46 +13,16 @@ const EDITS_PATH: &str = "http://dioxus.index.html/edits";
|
||||
#[cfg(not(any(target_os = "android", target_os = "windows")))]
|
||||
const EDITS_PATH: &str = "dioxus://index.html/edits";
|
||||
|
||||
const PREVENT_FILE_UPLOAD: &str = include_str!("../js/prevent_file_upload.js");
|
||||
// const PREVENT_FILE_UPLOAD: &str = include_str!("../js/prevent_file_upload.js");
|
||||
|
||||
fn handle_edits_code() -> String {
|
||||
let polling_request = format!(
|
||||
format!(
|
||||
r#"// Poll for requests
|
||||
window.interpreter = new JSChannel();
|
||||
window.interpreter.wait_for_request = (headless) => {{
|
||||
fetch(new Request("{EDITS_PATH}"))
|
||||
.then(response => {{
|
||||
response.arrayBuffer()
|
||||
.then(bytes => {{
|
||||
// In headless mode, the requestAnimationFrame callback is never called, so we need to run the bytes directly
|
||||
if (headless) {{
|
||||
window.interpreter.run_from_bytes(bytes);
|
||||
}}
|
||||
else {{
|
||||
requestAnimationFrame(() => {{
|
||||
window.interpreter.run_from_bytes(bytes);
|
||||
}});
|
||||
}}
|
||||
window.interpreter.wait_for_request(headless);
|
||||
}});
|
||||
}})
|
||||
}}"#
|
||||
);
|
||||
|
||||
let mut interpreter = native_js()
|
||||
.replace("/*POST_HANDLE_EDITS*/", PREVENT_FILE_UPLOAD)
|
||||
.replace("export", "")
|
||||
+ &polling_request;
|
||||
|
||||
while let Some(import_start) = interpreter.find("import") {
|
||||
let import_end = interpreter[import_start..]
|
||||
.find(|c| c == ';' || c == '\n')
|
||||
.map(|i| i + import_start)
|
||||
.unwrap_or_else(|| interpreter.len());
|
||||
interpreter.replace_range(import_start..import_end, "");
|
||||
}
|
||||
|
||||
format!("{interpreter}\nconst intercept_link_redirects = true;")
|
||||
{SLEDGEHAMMER_JS}
|
||||
{NATIVE_JS}
|
||||
window.interpreter = new NativeInterpreter("{EDITS_PATH}");
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
static DEFAULT_INDEX: &str = include_str!("./index.html");
|
||||
|
||||
@@ -88,9 +88,7 @@ class BaseInterpreter {
|
||||
stack;
|
||||
templates;
|
||||
m;
|
||||
constructor(root, handler) {
|
||||
this.handler = handler;
|
||||
this.initialize(root);
|
||||
constructor() {
|
||||
}
|
||||
initialize(root, handler = null) {
|
||||
this.global = {};
|
||||
|
||||
@@ -1 +1 @@
|
||||
7906913262979346544
|
||||
5236313184333634830
|
||||
@@ -1,210 +1,3 @@
|
||||
// src/ts/set_attribute.ts
|
||||
function setAttributeInner(node, field, value, ns) {
|
||||
if (ns === "style") {
|
||||
node.style.setProperty(field, value);
|
||||
return;
|
||||
}
|
||||
if (!!ns) {
|
||||
node.setAttributeNS(ns, field, value);
|
||||
return;
|
||||
}
|
||||
switch (field) {
|
||||
case "value":
|
||||
if (node.value !== value) {
|
||||
node.value = value;
|
||||
}
|
||||
break;
|
||||
case "initial_value":
|
||||
node.defaultValue = value;
|
||||
break;
|
||||
case "checked":
|
||||
node.checked = truthy(value);
|
||||
break;
|
||||
case "initial_checked":
|
||||
node.defaultChecked = truthy(value);
|
||||
break;
|
||||
case "selected":
|
||||
node.selected = truthy(value);
|
||||
break;
|
||||
case "initial_selected":
|
||||
node.defaultSelected = truthy(value);
|
||||
break;
|
||||
case "dangerous_inner_html":
|
||||
node.innerHTML = value;
|
||||
break;
|
||||
default:
|
||||
if (!truthy(value) && isBoolAttr(field)) {
|
||||
node.removeAttribute(field);
|
||||
} else {
|
||||
node.setAttribute(field, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
var truthy = function(val) {
|
||||
return val === "true" || val === true;
|
||||
};
|
||||
var isBoolAttr = function(field) {
|
||||
switch (field) {
|
||||
case "allowfullscreen":
|
||||
case "allowpaymentrequest":
|
||||
case "async":
|
||||
case "autofocus":
|
||||
case "autoplay":
|
||||
case "checked":
|
||||
case "controls":
|
||||
case "default":
|
||||
case "defer":
|
||||
case "disabled":
|
||||
case "formnovalidate":
|
||||
case "hidden":
|
||||
case "ismap":
|
||||
case "itemscope":
|
||||
case "loop":
|
||||
case "multiple":
|
||||
case "muted":
|
||||
case "nomodule":
|
||||
case "novalidate":
|
||||
case "open":
|
||||
case "playsinline":
|
||||
case "readonly":
|
||||
case "required":
|
||||
case "reversed":
|
||||
case "selected":
|
||||
case "truespeed":
|
||||
case "webkitdirectory":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// src/ts/core.ts
|
||||
class BaseInterpreter {
|
||||
global;
|
||||
local;
|
||||
root;
|
||||
handler;
|
||||
nodes;
|
||||
stack;
|
||||
templates;
|
||||
m;
|
||||
constructor(root, handler) {
|
||||
this.handler = handler;
|
||||
this.initialize(root);
|
||||
}
|
||||
initialize(root, handler = null) {
|
||||
this.global = {};
|
||||
this.local = {};
|
||||
this.root = root;
|
||||
this.nodes = [root];
|
||||
this.stack = [root];
|
||||
this.templates = {};
|
||||
if (handler) {
|
||||
this.handler = handler;
|
||||
}
|
||||
}
|
||||
createListener(event_name, element, bubbles) {
|
||||
if (bubbles) {
|
||||
if (this.global[event_name] === undefined) {
|
||||
this.global[event_name] = { active: 1, callback: this.handler };
|
||||
this.root.addEventListener(event_name, this.handler);
|
||||
} else {
|
||||
this.global[event_name].active++;
|
||||
}
|
||||
} else {
|
||||
const id = element.getAttribute("data-dioxus-id");
|
||||
if (!this.local[id]) {
|
||||
this.local[id] = {};
|
||||
}
|
||||
element.addEventListener(event_name, this.handler);
|
||||
}
|
||||
}
|
||||
removeListener(element, event_name, bubbles) {
|
||||
if (bubbles) {
|
||||
this.removeBubblingListener(event_name);
|
||||
} else {
|
||||
this.removeNonBubblingListener(element, event_name);
|
||||
}
|
||||
}
|
||||
removeBubblingListener(event_name) {
|
||||
this.global[event_name].active--;
|
||||
if (this.global[event_name].active === 0) {
|
||||
this.root.removeEventListener(event_name, this.global[event_name].callback);
|
||||
delete this.global[event_name];
|
||||
}
|
||||
}
|
||||
removeNonBubblingListener(element, event_name) {
|
||||
const id = element.getAttribute("data-dioxus-id");
|
||||
delete this.local[id][event_name];
|
||||
if (Object.keys(this.local[id]).length === 0) {
|
||||
delete this.local[id];
|
||||
}
|
||||
element.removeEventListener(event_name, this.handler);
|
||||
}
|
||||
removeAllNonBubblingListeners(element) {
|
||||
const id = element.getAttribute("data-dioxus-id");
|
||||
delete this.local[id];
|
||||
}
|
||||
getNode(id) {
|
||||
return this.nodes[id];
|
||||
}
|
||||
appendChildren(id, many) {
|
||||
const root = this.nodes[id];
|
||||
const els = this.stack.splice(this.stack.length - many);
|
||||
for (let k = 0;k < many; k++) {
|
||||
root.appendChild(els[k]);
|
||||
}
|
||||
}
|
||||
loadChild(ptr, len) {
|
||||
let node = this.stack[this.stack.length - 1];
|
||||
let ptr_end = ptr + len;
|
||||
for (;ptr < ptr_end; ptr++) {
|
||||
let end = this.m.getUint8(ptr);
|
||||
for (node = node.firstChild;end > 0; end--) {
|
||||
node = node.nextSibling;
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
saveTemplate(nodes, tmpl_id) {
|
||||
this.templates[tmpl_id] = nodes;
|
||||
}
|
||||
hydrateRoot(ids) {
|
||||
const hydrateNodes = document.querySelectorAll("[data-node-hydration]");
|
||||
for (let i = 0;i < hydrateNodes.length; i++) {
|
||||
const hydrateNode = hydrateNodes[i];
|
||||
const hydration = hydrateNode.getAttribute("data-node-hydration");
|
||||
const split = hydration.split(",");
|
||||
const id = ids[parseInt(split[0])];
|
||||
this.nodes[id] = hydrateNode;
|
||||
if (split.length > 1) {
|
||||
hydrateNode.listening = split.length - 1;
|
||||
hydrateNode.setAttribute("data-dioxus-id", id.toString());
|
||||
for (let j = 1;j < split.length; j++) {
|
||||
const listener = split[j];
|
||||
const split2 = listener.split(":");
|
||||
const event_name = split2[0];
|
||||
const bubbles = split2[1] === "1";
|
||||
this.createListener(event_name, hydrateNode, bubbles);
|
||||
}
|
||||
}
|
||||
}
|
||||
const treeWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_COMMENT);
|
||||
let currentNode = treeWalker.nextNode();
|
||||
while (currentNode) {
|
||||
const id = currentNode.textContent;
|
||||
const split = id.split("node-id");
|
||||
if (split.length > 1) {
|
||||
this.nodes[ids[parseInt(split[1])]] = currentNode.nextSibling;
|
||||
}
|
||||
currentNode = treeWalker.nextNode();
|
||||
}
|
||||
}
|
||||
setAttributeInner(node, field, value, ns) {
|
||||
setAttributeInner(node, field, value, ns);
|
||||
}
|
||||
}
|
||||
|
||||
// src/ts/form.ts
|
||||
function retriveValues(event, target) {
|
||||
let contents = {
|
||||
@@ -438,13 +231,21 @@ var targetId = function(target) {
|
||||
}
|
||||
return parseInt(realId);
|
||||
};
|
||||
var JSChannel_;
|
||||
if (RawInterpreter) {
|
||||
JSChannel_ = RawInterpreter;
|
||||
}
|
||||
|
||||
class PlatformInterpreter extends BaseInterpreter {
|
||||
class NativeInterpreter extends JSChannel_ {
|
||||
intercept_link_redirects;
|
||||
ipc;
|
||||
editsPath;
|
||||
liveview;
|
||||
constructor(root) {
|
||||
super(root, (event) => this.handleEvent(event, event.type, true));
|
||||
constructor(editsPath) {
|
||||
super();
|
||||
this.editsPath = editsPath;
|
||||
}
|
||||
initialize(root) {
|
||||
this.intercept_link_redirects = true;
|
||||
this.liveview = false;
|
||||
window.addEventListener("dragover", function(e) {
|
||||
@@ -458,13 +259,12 @@ class PlatformInterpreter extends BaseInterpreter {
|
||||
}
|
||||
}, false);
|
||||
this.ipc = window.ipc;
|
||||
const handler = (event) => this.handleEvent(event, event.type, true);
|
||||
super.initialize(root, handler);
|
||||
}
|
||||
serializeIpcMessage(method, params = {}) {
|
||||
return JSON.stringify({ method, params });
|
||||
}
|
||||
setAttributeInner(node, field, value, ns) {
|
||||
setAttributeInner(node, field, value, ns);
|
||||
}
|
||||
scrollTo(id, behavior) {
|
||||
const node = this.nodes[id];
|
||||
if (node instanceof HTMLElement) {
|
||||
@@ -492,7 +292,7 @@ class PlatformInterpreter extends BaseInterpreter {
|
||||
}
|
||||
}
|
||||
}
|
||||
LoadChild(array) {
|
||||
loadChild(array) {
|
||||
let node = this.stack[this.stack.length - 1];
|
||||
for (let i = 0;i < array.length; i++) {
|
||||
let end = array[i];
|
||||
@@ -502,7 +302,7 @@ class PlatformInterpreter extends BaseInterpreter {
|
||||
}
|
||||
return node;
|
||||
}
|
||||
AppendChildren(id, many) {
|
||||
appendChildren(id, many) {
|
||||
const root = this.nodes[id];
|
||||
const els = this.stack.splice(this.stack.length - many);
|
||||
for (let k = 0;k < many; k++) {
|
||||
@@ -510,6 +310,7 @@ class PlatformInterpreter extends BaseInterpreter {
|
||||
}
|
||||
}
|
||||
handleEvent(event, name, bubbles) {
|
||||
console.log("handling event: ", event);
|
||||
const target = event.target;
|
||||
const realId = targetId(target);
|
||||
const contents = serializeEvent(event, target);
|
||||
@@ -529,6 +330,7 @@ class PlatformInterpreter extends BaseInterpreter {
|
||||
} else {
|
||||
const message = this.serializeIpcMessage("user_event", body);
|
||||
this.ipc.postMessage(message);
|
||||
console.log("sent message to host: ", message);
|
||||
}
|
||||
}
|
||||
async readFiles(target, contents, bubbles, realId, name) {
|
||||
@@ -584,7 +386,17 @@ class PlatformInterpreter extends BaseInterpreter {
|
||||
}
|
||||
}
|
||||
}
|
||||
wait_for_request(headless) {
|
||||
fetch(new Request(this.editsPath)).then((response) => response.arrayBuffer()).then((bytes) => {
|
||||
if (headless) {
|
||||
this.run_from_bytes(bytes);
|
||||
} else {
|
||||
requestAnimationFrame(() => this.run_from_bytes(bytes));
|
||||
}
|
||||
this.wait_for_request(headless);
|
||||
});
|
||||
}
|
||||
}
|
||||
export {
|
||||
PlatformInterpreter
|
||||
NativeInterpreter
|
||||
};
|
||||
@@ -28,10 +28,7 @@ export class BaseInterpreter {
|
||||
// sledgehammer is generating this...
|
||||
m: any;
|
||||
|
||||
constructor(root: HTMLElement, handler: EventListener) {
|
||||
this.handler = handler;
|
||||
this.initialize(root);
|
||||
}
|
||||
constructor() { }
|
||||
|
||||
initialize(root: HTMLElement, handler: EventListener | null = null) {
|
||||
this.global = {};
|
||||
|
||||
@@ -5,18 +5,31 @@
|
||||
|
||||
import { BaseInterpreter, NodeId } from "./core";
|
||||
import { SerializedEvent, serializeEvent } from "./serialize";
|
||||
import { setAttributeInner } from "./set_attribute";
|
||||
|
||||
export class PlatformInterpreter extends BaseInterpreter {
|
||||
// okay so, we've got this JSChannel thing from sledgehammer, implicitly imported into our scope
|
||||
// we want to extend it, and it technically extends base intepreter. To make typescript happy,
|
||||
// we're going to bind the JSChannel_ object to the JSChannel object, and then extend it
|
||||
var JSChannel_: typeof BaseInterpreter;
|
||||
|
||||
// @ts-ignore - this is coming from the host
|
||||
if (RawInterpreter) { JSChannel_ = RawInterpreter; }
|
||||
|
||||
export class NativeInterpreter extends JSChannel_ {
|
||||
intercept_link_redirects: boolean;
|
||||
ipc: any;
|
||||
editsPath: string;
|
||||
|
||||
// eventually we want to remove liveview and build it into the server-side-events of fullstack
|
||||
// however, for now we need to support it since SSE in fullstack doesn't exist yet
|
||||
liveview: boolean;
|
||||
|
||||
constructor(root: HTMLElement) {
|
||||
super(root, (event) => this.handleEvent(event, event.type, true));
|
||||
constructor(editsPath: string) {
|
||||
super();
|
||||
this.editsPath = editsPath;
|
||||
}
|
||||
|
||||
initialize(root: HTMLElement): void {
|
||||
|
||||
this.intercept_link_redirects = true;
|
||||
this.liveview = false;
|
||||
|
||||
@@ -39,16 +52,16 @@ export class PlatformInterpreter extends BaseInterpreter {
|
||||
|
||||
// @ts-ignore - wry gives us this
|
||||
this.ipc = window.ipc;
|
||||
|
||||
// make sure we pass the handler to the base interpreter
|
||||
const handler: EventListener = (event) => this.handleEvent(event, event.type, true);
|
||||
super.initialize(root, handler);
|
||||
}
|
||||
|
||||
serializeIpcMessage(method: string, params = {}) {
|
||||
return JSON.stringify({ method, params });
|
||||
}
|
||||
|
||||
setAttributeInner(node: HTMLElement, field: string, value: string, ns: string) {
|
||||
setAttributeInner(node, field, value, ns);
|
||||
}
|
||||
|
||||
scrollTo(id: NodeId, behavior: ScrollBehavior) {
|
||||
const node = this.nodes[id];
|
||||
if (node instanceof HTMLElement) {
|
||||
@@ -80,7 +93,9 @@ export class PlatformInterpreter extends BaseInterpreter {
|
||||
}
|
||||
}
|
||||
|
||||
LoadChild(array: number[]) {
|
||||
// ignore the fact the base interpreter uses ptr + len but we use array
|
||||
// @ts-ignore
|
||||
loadChild(array: number[]) {
|
||||
// iterate through each number and get that child
|
||||
let node = this.stack[this.stack.length - 1];
|
||||
|
||||
@@ -94,7 +109,7 @@ export class PlatformInterpreter extends BaseInterpreter {
|
||||
return node;
|
||||
}
|
||||
|
||||
AppendChildren(id: NodeId, many: number) {
|
||||
appendChildren(id: NodeId, many: number) {
|
||||
const root = this.nodes[id];
|
||||
const els = this.stack.splice(this.stack.length - many);
|
||||
|
||||
@@ -104,6 +119,8 @@ export class PlatformInterpreter extends BaseInterpreter {
|
||||
}
|
||||
|
||||
handleEvent(event: Event, name: string, bubbles: boolean) {
|
||||
console.log("handling event: ", event);
|
||||
|
||||
const target = event.target!;
|
||||
const realId = targetId(target)!;
|
||||
const contents = serializeEvent(event, target);
|
||||
@@ -137,6 +154,8 @@ export class PlatformInterpreter extends BaseInterpreter {
|
||||
const message = this.serializeIpcMessage("user_event", body);
|
||||
this.ipc.postMessage(message);
|
||||
|
||||
console.log("sent message to host: ", message);
|
||||
|
||||
// // Run the event handler on the virtualdom
|
||||
// // capture/prevent default of the event if the virtualdom wants to
|
||||
// const res = handleVirtualdomEventSync(JSON.stringify(body));
|
||||
@@ -243,6 +262,22 @@ export class PlatformInterpreter extends BaseInterpreter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wait_for_request(headless: boolean) {
|
||||
fetch(new Request(this.editsPath))
|
||||
.then(response => response.arrayBuffer())
|
||||
.then(bytes => {
|
||||
// In headless mode, the requestAnimationFrame callback is never called, so we need to run the bytes directly
|
||||
if (headless) {
|
||||
// @ts-ignore
|
||||
this.run_from_bytes(bytes);
|
||||
} else {
|
||||
// @ts-ignore
|
||||
requestAnimationFrame(() => this.run_from_bytes(bytes));
|
||||
}
|
||||
this.wait_for_request(headless);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
type EventSyncResult = {
|
||||
|
||||
Reference in new issue
Block a user