mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-27 06:30:20 +00:00
wip: web now links against the js interprter code
This commit is contained in:
parent
5bf6c96f9f
commit
10db6ad65b
10 changed files with 1661 additions and 645 deletions
|
@ -1,5 +1,6 @@
|
|||
"use strict";
|
||||
exports.__esModule = true;
|
||||
exports.Interpreter = void 0;
|
||||
function serialize_event(event) {
|
||||
var _a, _b;
|
||||
switch (event.type) {
|
||||
|
@ -247,84 +248,79 @@ var Interpreter = /** @class */ (function () {
|
|||
Interpreter.prototype.pop = function () {
|
||||
return this.stack.pop();
|
||||
};
|
||||
Interpreter.prototype.PushRoot = function (edit) {
|
||||
var id = edit.root;
|
||||
var node = this.nodes[id];
|
||||
Interpreter.prototype.PushRoot = function (root) {
|
||||
var node = this.nodes[root];
|
||||
this.stack.push(node);
|
||||
};
|
||||
Interpreter.prototype.AppendChildren = function (edit) {
|
||||
var root = this.stack[this.stack.length - (1 + edit.many)];
|
||||
var to_add = this.stack.splice(this.stack.length - edit.many);
|
||||
for (var i = 0; i < edit.many; i++) {
|
||||
Interpreter.prototype.AppendChildren = function (many) {
|
||||
var root = this.stack[this.stack.length - (1 + many)];
|
||||
var to_add = this.stack.splice(this.stack.length - many);
|
||||
for (var i = 0; i < many; i++) {
|
||||
root.appendChild(to_add[i]);
|
||||
}
|
||||
};
|
||||
Interpreter.prototype.ReplaceWith = function (edit) {
|
||||
var root = this.nodes[edit.root];
|
||||
var els = this.stack.splice(this.stack.length - edit.m);
|
||||
Interpreter.prototype.ReplaceWith = function (root_id, m) {
|
||||
var root = this.nodes[root_id];
|
||||
var els = this.stack.splice(this.stack.length - m);
|
||||
root.replaceWith.apply(root, els);
|
||||
};
|
||||
Interpreter.prototype.InsertAfter = function (edit) {
|
||||
var old = this.nodes[edit.root];
|
||||
var new_nodes = this.stack.splice(this.stack.length - edit.n);
|
||||
Interpreter.prototype.InsertAfter = function (root, n) {
|
||||
var old = this.nodes[root];
|
||||
var new_nodes = this.stack.splice(this.stack.length - n);
|
||||
old.after.apply(old, new_nodes);
|
||||
};
|
||||
Interpreter.prototype.InsertBefore = function (edit) {
|
||||
var old = this.nodes[edit.root];
|
||||
var new_nodes = this.stack.splice(this.stack.length - edit.n);
|
||||
Interpreter.prototype.InsertBefore = function (root, n) {
|
||||
var old = this.nodes[root];
|
||||
var new_nodes = this.stack.splice(this.stack.length - n);
|
||||
old.before.apply(old, new_nodes);
|
||||
};
|
||||
Interpreter.prototype.Remove = function (edit) {
|
||||
var node = this.nodes[edit.root];
|
||||
Interpreter.prototype.Remove = function (root) {
|
||||
var node = this.nodes[root];
|
||||
if (node !== undefined) {
|
||||
node.remove();
|
||||
}
|
||||
};
|
||||
Interpreter.prototype.CreateTextNode = function (edit) {
|
||||
Interpreter.prototype.CreateTextNode = function (text, root) {
|
||||
// todo: make it so the types are okay
|
||||
var node = document.createTextNode(edit.text);
|
||||
this.nodes[edit.root] = node;
|
||||
var node = document.createTextNode(text);
|
||||
this.nodes[root] = node;
|
||||
this.stack.push(node);
|
||||
};
|
||||
Interpreter.prototype.CreateElement = function (edit) {
|
||||
var el = document.createElement(edit.tag);
|
||||
el.setAttribute("dioxus-id", "".concat(edit.root));
|
||||
this.nodes[edit.root] = el;
|
||||
Interpreter.prototype.CreateElement = function (tag, root) {
|
||||
var el = document.createElement(tag);
|
||||
el.setAttribute("dioxus-id", "".concat(root));
|
||||
this.nodes[root] = el;
|
||||
this.stack.push(el);
|
||||
};
|
||||
Interpreter.prototype.CreateElementNs = function (edit) {
|
||||
var el = document.createElementNS(edit.ns, edit.tag);
|
||||
Interpreter.prototype.CreateElementNs = function (tag, root, ns) {
|
||||
var el = document.createElementNS(ns, tag);
|
||||
this.stack.push(el);
|
||||
this.nodes[edit.root] = el;
|
||||
this.nodes[root] = el;
|
||||
};
|
||||
Interpreter.prototype.CreatePlaceholder = function (edit) {
|
||||
Interpreter.prototype.CreatePlaceholder = function (root) {
|
||||
var el = document.createElement("pre");
|
||||
el.hidden = true;
|
||||
this.stack.push(el);
|
||||
this.nodes[edit.root] = el;
|
||||
this.nodes[root] = el;
|
||||
};
|
||||
Interpreter.prototype.RemoveEventListener = function (edit) { };
|
||||
Interpreter.prototype.NewEventListener = function (edit, handler) {
|
||||
var event_name = edit.event_name;
|
||||
var mounted_node_id = edit.root;
|
||||
var scope = edit.scope;
|
||||
console.log('new event listener', event_name, mounted_node_id, scope);
|
||||
var element = this.nodes[edit.root];
|
||||
element.setAttribute("dioxus-event-".concat(event_name), "".concat(scope, ".").concat(mounted_node_id));
|
||||
if (!this.listeners[event_name]) {
|
||||
this.listeners[event_name] = handler;
|
||||
this.root.addEventListener(event_name, handler);
|
||||
}
|
||||
Interpreter.prototype.NewEventListener = function (event_name, scope, root) {
|
||||
console.log('new event listener', event_name, root, scope);
|
||||
var element = this.nodes[root];
|
||||
element.setAttribute("dioxus-event-".concat(event_name), "".concat(scope, ".").concat(root));
|
||||
// if (!this.listeners[event_name]) {
|
||||
// this.listeners[event_name] = handler;
|
||||
// this.root.addEventListener(event_name, handler);
|
||||
// }
|
||||
};
|
||||
Interpreter.prototype.SetText = function (edit) {
|
||||
this.nodes[edit.root].textContent = edit.text;
|
||||
Interpreter.prototype.RemoveEventListener = function (root, event_name, scope) {
|
||||
//
|
||||
};
|
||||
Interpreter.prototype.SetAttribute = function (edit) {
|
||||
// console.log("setting attr", edit);
|
||||
var name = edit.field;
|
||||
var value = edit.value;
|
||||
var ns = edit.ns;
|
||||
var node = this.nodes[edit.root];
|
||||
Interpreter.prototype.SetText = function (root, text) {
|
||||
this.nodes[root].textContent = text;
|
||||
};
|
||||
Interpreter.prototype.SetAttribute = function (root, field, value, ns) {
|
||||
var name = field;
|
||||
var node = this.nodes[root];
|
||||
if (ns == "style") {
|
||||
// @ts-ignore
|
||||
node.style[name] = value;
|
||||
|
@ -359,9 +355,8 @@ var Interpreter = /** @class */ (function () {
|
|||
}
|
||||
}
|
||||
};
|
||||
Interpreter.prototype.RemoveAttribute = function (edit) {
|
||||
var name = edit.name;
|
||||
var node = this.nodes[edit.root];
|
||||
Interpreter.prototype.RemoveAttribute = function (root, name) {
|
||||
var node = this.nodes[root];
|
||||
node.removeAttribute(name);
|
||||
if (name === "value") {
|
||||
node.value = "";
|
||||
|
@ -376,81 +371,85 @@ var Interpreter = /** @class */ (function () {
|
|||
Interpreter.prototype.handleEdits = function (edits) {
|
||||
console.log("handling edits ", edits);
|
||||
this.stack.push(this.root);
|
||||
var _loop_1 = function (edit) {
|
||||
switch (edit.type) {
|
||||
case "AppendChildren":
|
||||
this_1.AppendChildren(edit);
|
||||
break;
|
||||
case "ReplaceWith":
|
||||
this_1.ReplaceWith(edit);
|
||||
break;
|
||||
case "InsertAfter":
|
||||
this_1.InsertAfter(edit);
|
||||
break;
|
||||
case "InsertBefore":
|
||||
this_1.InsertBefore(edit);
|
||||
break;
|
||||
case "Remove":
|
||||
this_1.Remove(edit);
|
||||
break;
|
||||
case "CreateTextNode":
|
||||
this_1.CreateTextNode(edit);
|
||||
break;
|
||||
case "CreateElement":
|
||||
this_1.CreateElement(edit);
|
||||
break;
|
||||
case "CreateElementNs":
|
||||
this_1.CreateElementNs(edit);
|
||||
break;
|
||||
case "CreatePlaceholder":
|
||||
this_1.CreatePlaceholder(edit);
|
||||
break;
|
||||
case "RemoveEventListener":
|
||||
this_1.RemoveEventListener(edit);
|
||||
break;
|
||||
case "NewEventListener":
|
||||
// todo: only on desktop should we make our own handler
|
||||
var handler = function (event) {
|
||||
var target = event.target;
|
||||
console.log("event", event);
|
||||
if (target != null) {
|
||||
var real_id = target.getAttribute("dioxus-id");
|
||||
var should_prevent_default = target.getAttribute("dioxus-prevent-default");
|
||||
var contents = serialize_event(event);
|
||||
if (should_prevent_default === "on".concat(event.type)) {
|
||||
event.preventDefault();
|
||||
}
|
||||
if (real_id == null) {
|
||||
return;
|
||||
}
|
||||
window.rpc.call("user_event", {
|
||||
event: edit.event_name,
|
||||
mounted_dom_id: parseInt(real_id),
|
||||
contents: contents
|
||||
});
|
||||
}
|
||||
};
|
||||
this_1.NewEventListener(edit, handler);
|
||||
break;
|
||||
case "SetText":
|
||||
this_1.SetText(edit);
|
||||
break;
|
||||
case "SetAttribute":
|
||||
this_1.SetAttribute(edit);
|
||||
break;
|
||||
case "RemoveAttribute":
|
||||
this_1.RemoveAttribute(edit);
|
||||
break;
|
||||
}
|
||||
};
|
||||
var this_1 = this;
|
||||
for (var _i = 0, edits_1 = edits; _i < edits_1.length; _i++) {
|
||||
var edit = edits_1[_i];
|
||||
_loop_1(edit);
|
||||
this.handleEdit(edit);
|
||||
}
|
||||
};
|
||||
Interpreter.prototype.handleEdit = function (edit) {
|
||||
switch (edit.type) {
|
||||
case "PushRoot":
|
||||
this.PushRoot(edit.root);
|
||||
break;
|
||||
case "AppendChildren":
|
||||
this.AppendChildren(edit.many);
|
||||
break;
|
||||
case "ReplaceWith":
|
||||
this.ReplaceWith(edit.root, edit.m);
|
||||
break;
|
||||
case "InsertAfter":
|
||||
this.InsertAfter(edit.root, edit.n);
|
||||
break;
|
||||
case "InsertBefore":
|
||||
this.InsertBefore(edit.root, edit.n);
|
||||
break;
|
||||
case "Remove":
|
||||
this.Remove(edit.root);
|
||||
break;
|
||||
case "CreateTextNode":
|
||||
this.CreateTextNode(edit.text, edit.root);
|
||||
break;
|
||||
case "CreateElement":
|
||||
this.CreateElement(edit.tag, edit.root);
|
||||
break;
|
||||
case "CreateElementNs":
|
||||
this.CreateElementNs(edit.tag, edit.root, edit.ns);
|
||||
break;
|
||||
case "CreatePlaceholder":
|
||||
this.CreatePlaceholder(edit.root);
|
||||
break;
|
||||
case "RemoveEventListener":
|
||||
this.RemoveEventListener(edit.root, edit.event_name, edit.scope);
|
||||
break;
|
||||
case "NewEventListener":
|
||||
// todo: only on desktop should we make our own handler
|
||||
var handler = function (event) {
|
||||
var target = event.target;
|
||||
console.log("event", event);
|
||||
if (target != null) {
|
||||
var real_id = target.getAttribute("dioxus-id");
|
||||
var should_prevent_default = target.getAttribute("dioxus-prevent-default");
|
||||
var contents = serialize_event(event);
|
||||
if (should_prevent_default === "on".concat(event.type)) {
|
||||
event.preventDefault();
|
||||
}
|
||||
if (real_id == null) {
|
||||
return;
|
||||
}
|
||||
window.rpc.call("user_event", {
|
||||
event: edit.event_name,
|
||||
mounted_dom_id: parseInt(real_id),
|
||||
contents: contents
|
||||
});
|
||||
}
|
||||
};
|
||||
this.NewEventListener(edit.event_name, edit.scope, edit.root);
|
||||
// this.NewEventListener(edit, handler);
|
||||
break;
|
||||
case "SetText":
|
||||
this.SetText(edit.root, edit.text);
|
||||
break;
|
||||
case "SetAttribute":
|
||||
this.SetAttribute(edit.root, edit.field, edit.value, edit.ns);
|
||||
break;
|
||||
case "RemoveAttribute":
|
||||
this.RemoveAttribute(edit.root, edit.name);
|
||||
break;
|
||||
}
|
||||
};
|
||||
return Interpreter;
|
||||
}());
|
||||
exports.Interpreter = Interpreter;
|
||||
function main() {
|
||||
var root = window.document.getElementById("main");
|
||||
if (root != null) {
|
||||
|
|
|
@ -318,7 +318,7 @@ const bool_attrs = {
|
|||
truespeed: true,
|
||||
};
|
||||
|
||||
class Interpreter {
|
||||
export class Interpreter {
|
||||
root: Element;
|
||||
stack: Element[];
|
||||
listeners: { [key: string]: (event: Event) => void };
|
||||
|
@ -343,106 +343,102 @@ class Interpreter {
|
|||
return this.stack.pop();
|
||||
}
|
||||
|
||||
PushRoot(edit: PushRoot) {
|
||||
const id = edit.root;
|
||||
const node = this.nodes[id];
|
||||
PushRoot(root: number) {
|
||||
const node = this.nodes[root];
|
||||
this.stack.push(node);
|
||||
}
|
||||
|
||||
AppendChildren(edit: AppendChildren) {
|
||||
let root = this.stack[this.stack.length - (1 + edit.many)];
|
||||
AppendChildren(many: number) {
|
||||
let root = this.stack[this.stack.length - (1 + many)];
|
||||
|
||||
let to_add = this.stack.splice(this.stack.length - edit.many);
|
||||
let to_add = this.stack.splice(this.stack.length - many);
|
||||
|
||||
for (let i = 0; i < edit.many; i++) {
|
||||
for (let i = 0; i < many; i++) {
|
||||
root.appendChild(to_add[i]);
|
||||
}
|
||||
}
|
||||
|
||||
ReplaceWith(edit: ReplaceWith) {
|
||||
let root = this.nodes[edit.root] as Element;
|
||||
let els = this.stack.splice(this.stack.length - edit.m);
|
||||
ReplaceWith(root_id: number, m: number) {
|
||||
let root = this.nodes[root_id] as Element;
|
||||
let els = this.stack.splice(this.stack.length - m);
|
||||
|
||||
root.replaceWith(...els);
|
||||
}
|
||||
|
||||
InsertAfter(edit: InsertAfter) {
|
||||
let old = this.nodes[edit.root] as Element;
|
||||
let new_nodes = this.stack.splice(this.stack.length - edit.n);
|
||||
InsertAfter(root: number, n: number) {
|
||||
let old = this.nodes[root] as Element;
|
||||
let new_nodes = this.stack.splice(this.stack.length - n);
|
||||
old.after(...new_nodes);
|
||||
}
|
||||
|
||||
InsertBefore(edit: InsertBefore) {
|
||||
let old = this.nodes[edit.root] as Element;
|
||||
let new_nodes = this.stack.splice(this.stack.length - edit.n);
|
||||
InsertBefore(root: number, n: number) {
|
||||
let old = this.nodes[root] as Element;
|
||||
let new_nodes = this.stack.splice(this.stack.length - n);
|
||||
old.before(...new_nodes);
|
||||
}
|
||||
|
||||
Remove(edit: Remove) {
|
||||
let node = this.nodes[edit.root] as Element;
|
||||
Remove(root: number) {
|
||||
let node = this.nodes[root] as Element;
|
||||
if (node !== undefined) {
|
||||
node.remove();
|
||||
}
|
||||
}
|
||||
|
||||
CreateTextNode(edit: CreateTextNode) {
|
||||
CreateTextNode(text: string, root: number) {
|
||||
// todo: make it so the types are okay
|
||||
const node = document.createTextNode(edit.text) as any as Element;
|
||||
this.nodes[edit.root] = node;
|
||||
const node = document.createTextNode(text) as any as Element;
|
||||
this.nodes[root] = node;
|
||||
this.stack.push(node);
|
||||
}
|
||||
|
||||
CreateElement(edit: CreateElement) {
|
||||
const el = document.createElement(edit.tag);
|
||||
el.setAttribute("dioxus-id", `${edit.root}`);
|
||||
CreateElement(tag: string, root: number) {
|
||||
const el = document.createElement(tag);
|
||||
el.setAttribute("dioxus-id", `${root}`);
|
||||
|
||||
this.nodes[edit.root] = el;
|
||||
this.nodes[root] = el;
|
||||
this.stack.push(el);
|
||||
}
|
||||
|
||||
CreateElementNs(edit: CreateElementNs) {
|
||||
let el = document.createElementNS(edit.ns, edit.tag);
|
||||
CreateElementNs(tag: string, root: number, ns: string) {
|
||||
let el = document.createElementNS(ns, tag);
|
||||
this.stack.push(el);
|
||||
this.nodes[edit.root] = el;
|
||||
this.nodes[root] = el;
|
||||
}
|
||||
|
||||
CreatePlaceholder(edit: CreatePlaceholder) {
|
||||
CreatePlaceholder(root: number) {
|
||||
let el = document.createElement("pre");
|
||||
el.hidden = true;
|
||||
this.stack.push(el);
|
||||
this.nodes[edit.root] = el;
|
||||
this.nodes[root] = el;
|
||||
}
|
||||
|
||||
RemoveEventListener(edit: RemoveEventListener) { }
|
||||
|
||||
NewEventListener(edit: NewEventListener, handler: (event: Event) => void) {
|
||||
const event_name = edit.event_name;
|
||||
const mounted_node_id = edit.root;
|
||||
const scope = edit.scope;
|
||||
console.log('new event listener', event_name, mounted_node_id, scope);
|
||||
|
||||
const element = this.nodes[edit.root];
|
||||
NewEventListener(event_name: string, scope: number, root: number) {
|
||||
console.log('new event listener', event_name, root, scope);
|
||||
const element = this.nodes[root];
|
||||
element.setAttribute(
|
||||
`dioxus-event-${event_name}`,
|
||||
`${scope}.${mounted_node_id}`
|
||||
`${scope}.${root}`
|
||||
);
|
||||
|
||||
if (!this.listeners[event_name]) {
|
||||
this.listeners[event_name] = handler;
|
||||
this.root.addEventListener(event_name, handler);
|
||||
}
|
||||
// if (!this.listeners[event_name]) {
|
||||
// this.listeners[event_name] = handler;
|
||||
// this.root.addEventListener(event_name, handler);
|
||||
// }
|
||||
}
|
||||
|
||||
SetText(edit: SetText) {
|
||||
this.nodes[edit.root].textContent = edit.text;
|
||||
|
||||
RemoveEventListener(root: number, event_name: string, scope: number) {
|
||||
//
|
||||
}
|
||||
|
||||
SetAttribute(edit: SetAttribute) {
|
||||
// console.log("setting attr", edit);
|
||||
const name = edit.field;
|
||||
const value = edit.value;
|
||||
const ns = edit.ns;
|
||||
const node = this.nodes[edit.root];
|
||||
|
||||
SetText(root: number, text: string) {
|
||||
this.nodes[root].textContent = text;
|
||||
}
|
||||
|
||||
SetAttribute(root: number, field: string, value: string, ns: string | undefined) {
|
||||
const name = field;
|
||||
const node = this.nodes[root];
|
||||
|
||||
if (ns == "style") {
|
||||
|
||||
|
@ -477,10 +473,9 @@ class Interpreter {
|
|||
}
|
||||
}
|
||||
}
|
||||
RemoveAttribute(edit: RemoveAttribute) {
|
||||
const name = edit.name;
|
||||
RemoveAttribute(root: number, name: string) {
|
||||
|
||||
const node = this.nodes[edit.root];
|
||||
const node = this.nodes[root];
|
||||
node.removeAttribute(name);
|
||||
|
||||
if (name === "value") {
|
||||
|
@ -501,79 +496,87 @@ class Interpreter {
|
|||
this.stack.push(this.root);
|
||||
|
||||
for (let edit of edits) {
|
||||
switch (edit.type) {
|
||||
case "AppendChildren":
|
||||
this.AppendChildren(edit);
|
||||
break;
|
||||
case "ReplaceWith":
|
||||
this.ReplaceWith(edit);
|
||||
break;
|
||||
case "InsertAfter":
|
||||
this.InsertAfter(edit);
|
||||
break;
|
||||
case "InsertBefore":
|
||||
this.InsertBefore(edit);
|
||||
break;
|
||||
case "Remove":
|
||||
this.Remove(edit);
|
||||
break;
|
||||
case "CreateTextNode":
|
||||
this.CreateTextNode(edit);
|
||||
break;
|
||||
case "CreateElement":
|
||||
this.CreateElement(edit);
|
||||
break;
|
||||
case "CreateElementNs":
|
||||
this.CreateElementNs(edit);
|
||||
break;
|
||||
case "CreatePlaceholder":
|
||||
this.CreatePlaceholder(edit);
|
||||
break;
|
||||
case "RemoveEventListener":
|
||||
this.RemoveEventListener(edit);
|
||||
break;
|
||||
case "NewEventListener":
|
||||
// todo: only on desktop should we make our own handler
|
||||
let handler = (event: Event) => {
|
||||
const target = event.target as Element | null;
|
||||
console.log("event", event);
|
||||
if (target != null) {
|
||||
this.handleEdit(edit);
|
||||
}
|
||||
}
|
||||
|
||||
const real_id = target.getAttribute(`dioxus-id`);
|
||||
handleEdit(edit: DomEdit) {
|
||||
switch (edit.type) {
|
||||
case "PushRoot":
|
||||
this.PushRoot(edit.root);
|
||||
break;
|
||||
case "AppendChildren":
|
||||
this.AppendChildren(edit.many);
|
||||
break;
|
||||
case "ReplaceWith":
|
||||
this.ReplaceWith(edit.root, edit.m);
|
||||
break;
|
||||
case "InsertAfter":
|
||||
this.InsertAfter(edit.root, edit.n);
|
||||
break;
|
||||
case "InsertBefore":
|
||||
this.InsertBefore(edit.root, edit.n);
|
||||
break;
|
||||
case "Remove":
|
||||
this.Remove(edit.root);
|
||||
break;
|
||||
case "CreateTextNode":
|
||||
this.CreateTextNode(edit.text, edit.root);
|
||||
break;
|
||||
case "CreateElement":
|
||||
this.CreateElement(edit.tag, edit.root);
|
||||
break;
|
||||
case "CreateElementNs":
|
||||
this.CreateElementNs(edit.tag, edit.root, edit.ns);
|
||||
break;
|
||||
case "CreatePlaceholder":
|
||||
this.CreatePlaceholder(edit.root);
|
||||
break;
|
||||
case "RemoveEventListener":
|
||||
this.RemoveEventListener(edit.root, edit.event_name, edit.scope);
|
||||
break;
|
||||
case "NewEventListener":
|
||||
// todo: only on desktop should we make our own handler
|
||||
let handler = (event: Event) => {
|
||||
const target = event.target as Element | null;
|
||||
console.log("event", event);
|
||||
if (target != null) {
|
||||
|
||||
const should_prevent_default = target.getAttribute(
|
||||
`dioxus-prevent-default`
|
||||
);
|
||||
const real_id = target.getAttribute(`dioxus-id`);
|
||||
|
||||
let contents = serialize_event(event);
|
||||
const should_prevent_default = target.getAttribute(
|
||||
`dioxus-prevent-default`
|
||||
);
|
||||
|
||||
if (should_prevent_default === `on${event.type}`) {
|
||||
event.preventDefault();
|
||||
}
|
||||
let contents = serialize_event(event);
|
||||
|
||||
if (real_id == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.rpc.call("user_event", {
|
||||
event: (edit as NewEventListener).event_name,
|
||||
mounted_dom_id: parseInt(real_id),
|
||||
contents: contents,
|
||||
});
|
||||
if (should_prevent_default === `on${event.type}`) {
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
this.NewEventListener(edit, handler);
|
||||
break;
|
||||
case "SetText":
|
||||
this.SetText(edit);
|
||||
break;
|
||||
case "SetAttribute":
|
||||
this.SetAttribute(edit);
|
||||
break;
|
||||
case "RemoveAttribute":
|
||||
this.RemoveAttribute(edit);
|
||||
break;
|
||||
}
|
||||
|
||||
if (real_id == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.rpc.call("user_event", {
|
||||
event: (edit as NewEventListener).event_name,
|
||||
mounted_dom_id: parseInt(real_id),
|
||||
contents: contents,
|
||||
});
|
||||
}
|
||||
};
|
||||
this.NewEventListener(edit.event_name, edit.scope, edit.root);
|
||||
// this.NewEventListener(edit, handler);
|
||||
break;
|
||||
case "SetText":
|
||||
this.SetText(edit.root, edit.text);
|
||||
break;
|
||||
case "SetAttribute":
|
||||
this.SetAttribute(edit.root, edit.field, edit.value, edit.ns);
|
||||
break;
|
||||
case "RemoveAttribute":
|
||||
this.RemoveAttribute(edit.root, edit.name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -632,5 +635,3 @@ declare global {
|
|||
|
||||
|
||||
type Edits = DomEdit[];
|
||||
|
||||
main();
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
// "module": "None",
|
||||
"module": "commonjs",
|
||||
"strict": true,
|
||||
"outDir": "dist",
|
||||
|
|
|
@ -11,8 +11,8 @@ documentation = "https://dioxuslabs.com"
|
|||
keywords = ["dom", "ui", "gui", "react", "wasm"]
|
||||
|
||||
[dependencies]
|
||||
dioxus-core = { path = "../core", version ="^0.1.7"}
|
||||
dioxus-html = { path = "../html", version ="^0.1.4"}
|
||||
dioxus-core = { path = "../core", version = "^0.1.7" }
|
||||
dioxus-html = { path = "../html", version = "^0.1.4" }
|
||||
js-sys = "0.3"
|
||||
wasm-bindgen = { version = "0.2.78", features = ["enable-interning"] }
|
||||
lazy_static = "1.4.0"
|
||||
|
|
59
packages/web/src/bindings.rs
Normal file
59
packages/web/src/bindings.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
use dioxus_core::DomEdit;
|
||||
use wasm_bindgen::{convert::IntoWasmAbi, describe::WasmDescribe, prelude::*};
|
||||
use web_sys::{Element, HtmlElement, Node};
|
||||
|
||||
#[wasm_bindgen(module = "/src/interpreter.js")]
|
||||
extern "C" {
|
||||
pub type Interpreter;
|
||||
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(arg: Element) -> Interpreter;
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub fn set_node(this: &Interpreter, id: usize, node: Node);
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub fn PushRoot(this: &Interpreter, root: u64);
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub fn AppendChildren(this: &Interpreter, many: u32);
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub fn ReplaceWith(this: &Interpreter, root: u64, m: u32);
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub fn InsertAfter(this: &Interpreter, root: u64, n: u32);
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub fn InsertBefore(this: &Interpreter, root: u64, n: u32);
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub fn Remove(this: &Interpreter, root: u64);
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub fn CreateTextNode(this: &Interpreter, text: &str, root: u64);
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub fn CreateElement(this: &Interpreter, tag: &str, root: u64);
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub fn CreateElementNs(this: &Interpreter, tag: &str, root: u64, ns: &str);
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub fn CreatePlaceholder(this: &Interpreter, root: u64);
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub fn NewEventListener(this: &Interpreter, name: &str, scope: usize, root: u64);
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub fn RemoveEventListener(this: &Interpreter, root: u64, name: &str);
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub fn SetText(this: &Interpreter, root: u64, text: &str);
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub fn SetAttribute(this: &Interpreter, root: u64, field: &str, value: &str, ns: Option<&str>);
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub fn RemoveAttribute(this: &Interpreter, root: u64, field: &str);
|
||||
}
|
|
@ -7,10 +7,11 @@
|
|||
//! - tests to ensure dyn_into works for various event types.
|
||||
//! - Partial delegation?>
|
||||
|
||||
use crate::bindings::Interpreter;
|
||||
use dioxus_core::{DomEdit, ElementId, SchedulerMsg, ScopeId, UserEvent};
|
||||
use fxhash::FxHashMap;
|
||||
use std::{any::Any, fmt::Debug, rc::Rc, sync::Arc};
|
||||
use wasm_bindgen::{closure::Closure, JsCast};
|
||||
use wasm_bindgen::{closure::Closure, JsCast, JsValue};
|
||||
use web_sys::{
|
||||
CssStyleDeclaration, Document, Element, Event, HtmlElement, HtmlInputElement,
|
||||
HtmlOptionElement, HtmlTextAreaElement, Node,
|
||||
|
@ -19,13 +20,10 @@ use web_sys::{
|
|||
use crate::{nodeslab::NodeSlab, WebConfig};
|
||||
|
||||
pub struct WebsysDom {
|
||||
stack: Stack,
|
||||
|
||||
/// A map from ElementID (index) to Node
|
||||
pub(crate) nodes: NodeSlab,
|
||||
|
||||
document: Document,
|
||||
|
||||
pub interpreter: Interpreter,
|
||||
|
||||
pub(crate) root: Element,
|
||||
|
||||
sender_callback: Rc<dyn Fn(SchedulerMsg)>,
|
||||
|
@ -42,7 +40,6 @@ impl WebsysDom {
|
|||
pub fn new(cfg: WebConfig, sender_callback: Rc<dyn Fn(SchedulerMsg)>) -> Self {
|
||||
let document = load_document();
|
||||
|
||||
let nodes = NodeSlab::new(2000);
|
||||
let listeners = FxHashMap::default();
|
||||
|
||||
let mut stack = Stack::with_capacity(10);
|
||||
|
@ -52,8 +49,7 @@ impl WebsysDom {
|
|||
stack.push(root_node);
|
||||
|
||||
Self {
|
||||
stack,
|
||||
nodes,
|
||||
interpreter: Interpreter::new(root.clone()),
|
||||
listeners,
|
||||
document,
|
||||
sender_callback,
|
||||
|
@ -64,399 +60,38 @@ impl WebsysDom {
|
|||
pub fn apply_edits(&mut self, mut edits: Vec<DomEdit>) {
|
||||
for edit in edits.drain(..) {
|
||||
match edit {
|
||||
DomEdit::PushRoot { root } => self.push(root),
|
||||
DomEdit::AppendChildren { many } => self.append_children(many),
|
||||
DomEdit::ReplaceWith { m, root } => self.replace_with(m, root),
|
||||
DomEdit::Remove { root } => self.remove(root),
|
||||
DomEdit::CreateTextNode { text, root: id } => self.create_text_node(text, id),
|
||||
DomEdit::CreateElement { tag, root: id } => self.create_element(tag, None, id),
|
||||
DomEdit::CreateElementNs { tag, root: id, ns } => {
|
||||
self.create_element(tag, Some(ns), id)
|
||||
DomEdit::PushRoot { root } => self.interpreter.PushRoot(root),
|
||||
DomEdit::AppendChildren { many } => self.interpreter.AppendChildren(many),
|
||||
DomEdit::ReplaceWith { root, m } => self.interpreter.ReplaceWith(root, m),
|
||||
DomEdit::InsertAfter { root, n } => self.interpreter.InsertAfter(root, n),
|
||||
DomEdit::InsertBefore { root, n } => self.interpreter.InsertBefore(root, n),
|
||||
DomEdit::Remove { root } => self.interpreter.Remove(root),
|
||||
DomEdit::CreateTextNode { text, root } => {
|
||||
self.interpreter.CreateTextNode(text, root)
|
||||
}
|
||||
DomEdit::CreatePlaceholder { root: id } => self.create_placeholder(id),
|
||||
DomEdit::CreateElement { tag, root } => self.interpreter.CreateElement(tag, root),
|
||||
DomEdit::CreateElementNs { tag, root, ns } => {
|
||||
self.interpreter.CreateElementNs(tag, root, ns)
|
||||
}
|
||||
DomEdit::CreatePlaceholder { root } => self.interpreter.CreatePlaceholder(root),
|
||||
DomEdit::NewEventListener {
|
||||
event_name,
|
||||
scope,
|
||||
root: mounted_node_id,
|
||||
} => self.new_event_listener(event_name, scope, mounted_node_id),
|
||||
|
||||
DomEdit::RemoveEventListener { event, root } => {
|
||||
self.remove_event_listener(event, root)
|
||||
root,
|
||||
} => self.interpreter.NewEventListener(event_name, scope.0, root),
|
||||
DomEdit::RemoveEventListener { root, event } => {
|
||||
self.interpreter.RemoveEventListener(root, event)
|
||||
}
|
||||
|
||||
DomEdit::SetText { text, root } => self.set_text(text, root),
|
||||
DomEdit::SetText { root, text } => self.interpreter.SetText(root, text),
|
||||
DomEdit::SetAttribute {
|
||||
root,
|
||||
field,
|
||||
value,
|
||||
ns,
|
||||
root,
|
||||
} => self.set_attribute(field, value, ns, root),
|
||||
DomEdit::RemoveAttribute { name, root } => self.remove_attribute(name, root),
|
||||
|
||||
DomEdit::InsertAfter { n, root } => self.insert_after(n, root),
|
||||
DomEdit::InsertBefore { n, root } => self.insert_before(n, root),
|
||||
}
|
||||
}
|
||||
}
|
||||
fn push(&mut self, root: u64) {
|
||||
let key = root as usize;
|
||||
let domnode = &self.nodes[key];
|
||||
|
||||
let real_node: Node = match domnode {
|
||||
Some(n) => n.clone(),
|
||||
None => todo!(),
|
||||
};
|
||||
|
||||
self.stack.push(real_node);
|
||||
}
|
||||
|
||||
fn append_children(&mut self, many: u32) {
|
||||
let root: Node = self
|
||||
.stack
|
||||
.list
|
||||
.get(self.stack.list.len() - (1 + many as usize))
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
// We need to make sure to add comments between text nodes
|
||||
// We ensure that the text siblings are patched by preventing the browser from merging
|
||||
// neighboring text nodes. Originally inspired by some of React's work from 2016.
|
||||
// -> https://reactjs.org/blog/2016/04/07/react-v15.html#major-changes
|
||||
// -> https://github.com/facebook/react/pull/5753
|
||||
/*
|
||||
todo: we need to track this for replacing/insert after/etc
|
||||
*/
|
||||
let mut last_node_was_text = false;
|
||||
|
||||
for child in self
|
||||
.stack
|
||||
.list
|
||||
.drain((self.stack.list.len() - many as usize)..)
|
||||
{
|
||||
if child.dyn_ref::<web_sys::Text>().is_some() {
|
||||
if last_node_was_text {
|
||||
let comment_node = self
|
||||
.document
|
||||
.create_comment("dioxus")
|
||||
.dyn_into::<Node>()
|
||||
.unwrap();
|
||||
root.append_child(&comment_node).unwrap();
|
||||
} => self.interpreter.SetAttribute(root, field, value, ns),
|
||||
DomEdit::RemoveAttribute { root, name } => {
|
||||
self.interpreter.RemoveAttribute(root, name)
|
||||
}
|
||||
last_node_was_text = true;
|
||||
} else {
|
||||
last_node_was_text = false;
|
||||
}
|
||||
root.append_child(&child).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn replace_with(&mut self, m: u32, root: u64) {
|
||||
let old = self.nodes[root as usize].as_ref().unwrap();
|
||||
|
||||
let arr: js_sys::Array = self
|
||||
.stack
|
||||
.list
|
||||
.drain((self.stack.list.len() - m as usize)..)
|
||||
.collect();
|
||||
|
||||
if let Some(el) = old.dyn_ref::<Element>() {
|
||||
el.replace_with_with_node(&arr).unwrap();
|
||||
} else if let Some(el) = old.dyn_ref::<web_sys::CharacterData>() {
|
||||
el.replace_with_with_node(&arr).unwrap();
|
||||
} else if let Some(el) = old.dyn_ref::<web_sys::DocumentType>() {
|
||||
el.replace_with_with_node(&arr).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn remove(&mut self, root: u64) {
|
||||
let node = self.nodes[root as usize].as_ref().unwrap();
|
||||
if let Some(element) = node.dyn_ref::<Element>() {
|
||||
element.remove();
|
||||
} else {
|
||||
if let Some(parent) = node.parent_node() {
|
||||
parent.remove_child(&node).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_placeholder(&mut self, id: u64) {
|
||||
self.create_element("pre", None, id);
|
||||
self.set_attribute("hidden", "", None, id);
|
||||
}
|
||||
|
||||
fn create_text_node(&mut self, text: &str, id: u64) {
|
||||
let textnode = self
|
||||
.document
|
||||
.create_text_node(text)
|
||||
.dyn_into::<Node>()
|
||||
.unwrap();
|
||||
|
||||
self.stack.push(textnode.clone());
|
||||
|
||||
self.nodes[(id as usize)] = Some(textnode);
|
||||
}
|
||||
|
||||
fn create_element(&mut self, tag: &str, ns: Option<&'static str>, id: u64) {
|
||||
let tag = wasm_bindgen::intern(tag);
|
||||
|
||||
let el = match ns {
|
||||
Some(ns) => self
|
||||
.document
|
||||
.create_element_ns(Some(ns), tag)
|
||||
.unwrap()
|
||||
.dyn_into::<Node>()
|
||||
.unwrap(),
|
||||
None => self
|
||||
.document
|
||||
.create_element(tag)
|
||||
.unwrap()
|
||||
.dyn_into::<Node>()
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
use smallstr::SmallString;
|
||||
use std::fmt::Write;
|
||||
|
||||
let mut s: SmallString<[u8; 8]> = smallstr::SmallString::new();
|
||||
write!(s, "{}", id).unwrap();
|
||||
|
||||
let el2 = el.dyn_ref::<Element>().unwrap();
|
||||
el2.set_attribute("dioxus-id", s.as_str()).unwrap();
|
||||
|
||||
self.stack.push(el.clone());
|
||||
self.nodes[(id as usize)] = Some(el);
|
||||
}
|
||||
|
||||
fn new_event_listener(&mut self, event: &'static str, _scope: ScopeId, _real_id: u64) {
|
||||
let event = wasm_bindgen::intern(event);
|
||||
|
||||
// attach the correct attributes to the element
|
||||
// these will be used by accessing the event's target
|
||||
// This ensures we only ever have one handler attached to the root, but decide
|
||||
// dynamically when we want to call a listener.
|
||||
|
||||
let el = self.stack.top();
|
||||
|
||||
let el = el.dyn_ref::<Element>().unwrap();
|
||||
|
||||
el.set_attribute("dioxus-event", event).unwrap();
|
||||
|
||||
// Register the callback to decode
|
||||
|
||||
if let Some(entry) = self.listeners.get_mut(event) {
|
||||
entry.0 += 1;
|
||||
} else {
|
||||
let trigger = self.sender_callback.clone();
|
||||
|
||||
let c: Box<dyn FnMut(&Event)> = Box::new(move |event: &web_sys::Event| {
|
||||
// "Result" cannot be received from JS
|
||||
// Instead, we just build and immediately execute a closure that returns result
|
||||
match decode_trigger(event) {
|
||||
Ok(synthetic_event) => {
|
||||
let target = event.target().unwrap();
|
||||
if let Some(node) = target.dyn_ref::<HtmlElement>() {
|
||||
if let Some(name) = node.get_attribute("dioxus-prevent-default") {
|
||||
if name == synthetic_event.name
|
||||
|| name.trim_start_matches("on") == synthetic_event.name
|
||||
{
|
||||
log::trace!("Preventing default");
|
||||
event.prevent_default();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trigger.as_ref()(SchedulerMsg::Event(synthetic_event))
|
||||
}
|
||||
Err(e) => log::error!("Error decoding Dioxus event attribute. {:#?}", e),
|
||||
};
|
||||
});
|
||||
|
||||
let handler = Closure::wrap(c);
|
||||
|
||||
self.root
|
||||
.add_event_listener_with_callback(event, (&handler).as_ref().unchecked_ref())
|
||||
.unwrap();
|
||||
|
||||
// Increment the listeners
|
||||
self.listeners.insert(event.into(), (1, handler));
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_event_listener(&mut self, _event: &str, _root: u64) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_text(&mut self, text: &str, root: u64) {
|
||||
let el = self.nodes[root as usize].as_ref().unwrap();
|
||||
el.set_text_content(Some(text))
|
||||
}
|
||||
|
||||
fn set_attribute(&mut self, name: &str, value: &str, ns: Option<&str>, root: u64) {
|
||||
let node = self.nodes[root as usize].as_ref().unwrap();
|
||||
if ns == Some("style") {
|
||||
if let Some(el) = node.dyn_ref::<Element>() {
|
||||
let el = el.dyn_ref::<HtmlElement>().unwrap();
|
||||
let style_dc: CssStyleDeclaration = el.style();
|
||||
style_dc.set_property(name, value).unwrap();
|
||||
}
|
||||
} else {
|
||||
let fallback = || {
|
||||
let el = node.dyn_ref::<Element>().unwrap();
|
||||
el.set_attribute(name, value).unwrap()
|
||||
};
|
||||
match name {
|
||||
"dangerous_inner_html" => {
|
||||
if let Some(el) = node.dyn_ref::<Element>() {
|
||||
el.set_inner_html(value);
|
||||
}
|
||||
}
|
||||
"value" => {
|
||||
if let Some(input) = node.dyn_ref::<HtmlInputElement>() {
|
||||
/*
|
||||
if the attribute being set is the same as the value of the input, then don't bother setting it.
|
||||
This is used in controlled components to keep the cursor in the right spot.
|
||||
|
||||
this logic should be moved into the virtualdom since we have the notion of "volatile"
|
||||
*/
|
||||
if input.value() != value {
|
||||
input.set_value(value);
|
||||
}
|
||||
} else if let Some(node) = node.dyn_ref::<HtmlTextAreaElement>() {
|
||||
if name == "value" {
|
||||
node.set_value(value);
|
||||
}
|
||||
} else {
|
||||
fallback();
|
||||
}
|
||||
}
|
||||
"checked" => {
|
||||
if let Some(input) = node.dyn_ref::<HtmlInputElement>() {
|
||||
match value {
|
||||
"true" => input.set_checked(true),
|
||||
"false" => input.set_checked(false),
|
||||
_ => fallback(),
|
||||
}
|
||||
} else {
|
||||
fallback();
|
||||
}
|
||||
}
|
||||
"selected" => {
|
||||
if let Some(node) = node.dyn_ref::<HtmlOptionElement>() {
|
||||
node.set_selected(true);
|
||||
} else {
|
||||
fallback();
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// https://github.com/facebook/react/blob/8b88ac2592c5f555f315f9440cbb665dd1e7457a/packages/react-dom/src/shared/DOMProperty.js#L352-L364
|
||||
if value == "false" {
|
||||
if let Some(el) = node.dyn_ref::<Element>() {
|
||||
match name {
|
||||
"allowfullscreen"
|
||||
| "allowpaymentrequest"
|
||||
| "async"
|
||||
| "autofocus"
|
||||
| "autoplay"
|
||||
| "checked"
|
||||
| "controls"
|
||||
| "default"
|
||||
| "defer"
|
||||
| "disabled"
|
||||
| "formnovalidate"
|
||||
| "hidden"
|
||||
| "ismap"
|
||||
| "itemscope"
|
||||
| "loop"
|
||||
| "multiple"
|
||||
| "muted"
|
||||
| "nomodule"
|
||||
| "novalidate"
|
||||
| "open"
|
||||
| "playsinline"
|
||||
| "readonly"
|
||||
| "required"
|
||||
| "reversed"
|
||||
| "selected"
|
||||
| "truespeed" => {
|
||||
let _ = el.remove_attribute(name);
|
||||
}
|
||||
_ => {
|
||||
let _ = el.set_attribute(name, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
} else {
|
||||
fallback();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_attribute(&mut self, name: &str, root: u64) {
|
||||
let node = self.nodes[root as usize].as_ref().unwrap();
|
||||
if let Some(node) = node.dyn_ref::<web_sys::Element>() {
|
||||
node.remove_attribute(name).unwrap();
|
||||
}
|
||||
if let Some(node) = node.dyn_ref::<HtmlInputElement>() {
|
||||
// Some attributes are "volatile" and don't work through `removeAttribute`.
|
||||
if name == "value" {
|
||||
node.set_value("");
|
||||
}
|
||||
if name == "checked" {
|
||||
node.set_checked(false);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(node) = node.dyn_ref::<HtmlOptionElement>() {
|
||||
if name == "selected" {
|
||||
node.set_selected(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_after(&mut self, n: u32, root: u64) {
|
||||
let old = self.nodes[root as usize].as_ref().unwrap();
|
||||
|
||||
let arr: js_sys::Array = self
|
||||
.stack
|
||||
.list
|
||||
.drain((self.stack.list.len() - n as usize)..)
|
||||
.collect();
|
||||
|
||||
if let Some(el) = old.dyn_ref::<Element>() {
|
||||
el.after_with_node(&arr).unwrap();
|
||||
} else if let Some(el) = old.dyn_ref::<web_sys::CharacterData>() {
|
||||
el.after_with_node(&arr).unwrap();
|
||||
} else if let Some(el) = old.dyn_ref::<web_sys::DocumentType>() {
|
||||
el.after_with_node(&arr).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_before(&mut self, n: u32, root: u64) {
|
||||
let anchor = self.nodes[root as usize].as_ref().unwrap();
|
||||
|
||||
if n == 1 {
|
||||
let before = self.stack.pop();
|
||||
|
||||
anchor
|
||||
.parent_node()
|
||||
.unwrap()
|
||||
.insert_before(&before, Some(&anchor))
|
||||
.unwrap();
|
||||
} else {
|
||||
let arr: js_sys::Array = self
|
||||
.stack
|
||||
.list
|
||||
.drain((self.stack.list.len() - n as usize)..)
|
||||
.collect();
|
||||
|
||||
if let Some(el) = anchor.dyn_ref::<Element>() {
|
||||
el.before_with_node(&arr).unwrap();
|
||||
} else if let Some(el) = anchor.dyn_ref::<web_sys::CharacterData>() {
|
||||
el.before_with_node(&arr).unwrap();
|
||||
} else if let Some(el) = anchor.dyn_ref::<web_sys::DocumentType>() {
|
||||
el.before_with_node(&arr).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
529
packages/web/src/interpreter.js
Normal file
529
packages/web/src/interpreter.js
Normal file
|
@ -0,0 +1,529 @@
|
|||
"use strict";
|
||||
var exports = {};
|
||||
exports.__esModule = true;
|
||||
exports.Interpreter = void 0;
|
||||
function serialize_event(event) {
|
||||
var _a, _b;
|
||||
switch (event.type) {
|
||||
case "copy":
|
||||
case "cut":
|
||||
case "past":
|
||||
return {};
|
||||
case "compositionend":
|
||||
case "compositionstart":
|
||||
case "compositionupdate":
|
||||
var data = event.data;
|
||||
return {
|
||||
data: data,
|
||||
};
|
||||
case "keydown":
|
||||
case "keypress":
|
||||
case "keyup":
|
||||
var _c = event,
|
||||
charCode = _c.charCode,
|
||||
key = _c.key,
|
||||
altKey = _c.altKey,
|
||||
ctrlKey = _c.ctrlKey,
|
||||
metaKey = _c.metaKey,
|
||||
keyCode = _c.keyCode,
|
||||
shiftKey = _c.shiftKey,
|
||||
location_1 = _c.location,
|
||||
repeat = _c.repeat,
|
||||
which = _c.which;
|
||||
return {
|
||||
char_code: charCode,
|
||||
key: key,
|
||||
alt_key: altKey,
|
||||
ctrl_key: ctrlKey,
|
||||
meta_key: metaKey,
|
||||
key_code: keyCode,
|
||||
shift_key: shiftKey,
|
||||
location: location_1,
|
||||
repeat: repeat,
|
||||
which: which,
|
||||
locale: "locale",
|
||||
};
|
||||
case "focus":
|
||||
case "blur":
|
||||
return {};
|
||||
case "change":
|
||||
var target = event.target;
|
||||
var value = void 0;
|
||||
if (target.type === "checkbox" || target.type === "radio") {
|
||||
value = target.checked ? "true" : "false";
|
||||
} else {
|
||||
value =
|
||||
(_a = target.value) !== null && _a !== void 0
|
||||
? _a
|
||||
: target.textContent;
|
||||
}
|
||||
return {
|
||||
value: value,
|
||||
};
|
||||
case "input":
|
||||
case "invalid":
|
||||
case "reset":
|
||||
case "submit": {
|
||||
var target_1 = event.target;
|
||||
var value_1 =
|
||||
(_b = target_1.value) !== null && _b !== void 0
|
||||
? _b
|
||||
: target_1.textContent;
|
||||
if (target_1.type == "checkbox") {
|
||||
value_1 = target_1.checked ? "true" : "false";
|
||||
}
|
||||
return {
|
||||
value: value_1,
|
||||
};
|
||||
}
|
||||
case "click":
|
||||
case "contextmenu":
|
||||
case "doubleclick":
|
||||
case "drag":
|
||||
case "dragend":
|
||||
case "dragenter":
|
||||
case "dragexit":
|
||||
case "dragleave":
|
||||
case "dragover":
|
||||
case "dragstart":
|
||||
case "drop":
|
||||
case "mousedown":
|
||||
case "mouseenter":
|
||||
case "mouseleave":
|
||||
case "mousemove":
|
||||
case "mouseout":
|
||||
case "mouseover":
|
||||
case "mouseup": {
|
||||
var _d = event,
|
||||
altKey_1 = _d.altKey,
|
||||
button = _d.button,
|
||||
buttons = _d.buttons,
|
||||
clientX = _d.clientX,
|
||||
clientY = _d.clientY,
|
||||
ctrlKey_1 = _d.ctrlKey,
|
||||
metaKey_1 = _d.metaKey,
|
||||
pageX = _d.pageX,
|
||||
pageY = _d.pageY,
|
||||
screenX_1 = _d.screenX,
|
||||
screenY_1 = _d.screenY,
|
||||
shiftKey_1 = _d.shiftKey;
|
||||
return {
|
||||
alt_key: altKey_1,
|
||||
button: button,
|
||||
buttons: buttons,
|
||||
client_x: clientX,
|
||||
client_y: clientY,
|
||||
ctrl_key: ctrlKey_1,
|
||||
meta_key: metaKey_1,
|
||||
page_x: pageX,
|
||||
page_y: pageY,
|
||||
screen_x: screenX_1,
|
||||
screen_y: screenY_1,
|
||||
shift_key: shiftKey_1,
|
||||
};
|
||||
}
|
||||
case "pointerdown":
|
||||
case "pointermove":
|
||||
case "pointerup":
|
||||
case "pointercancel":
|
||||
case "gotpointercapture":
|
||||
case "lostpointercapture":
|
||||
case "pointerenter":
|
||||
case "pointerleave":
|
||||
case "pointerover":
|
||||
case "pointerout": {
|
||||
var _e = event,
|
||||
altKey_2 = _e.altKey,
|
||||
button = _e.button,
|
||||
buttons = _e.buttons,
|
||||
clientX = _e.clientX,
|
||||
clientY = _e.clientY,
|
||||
ctrlKey_2 = _e.ctrlKey,
|
||||
metaKey_2 = _e.metaKey,
|
||||
pageX = _e.pageX,
|
||||
pageY = _e.pageY,
|
||||
screenX_2 = _e.screenX,
|
||||
screenY_2 = _e.screenY,
|
||||
shiftKey_2 = _e.shiftKey,
|
||||
pointerId = _e.pointerId,
|
||||
width = _e.width,
|
||||
height = _e.height,
|
||||
pressure = _e.pressure,
|
||||
tangentialPressure = _e.tangentialPressure,
|
||||
tiltX = _e.tiltX,
|
||||
tiltY = _e.tiltY,
|
||||
twist = _e.twist,
|
||||
pointerType = _e.pointerType,
|
||||
isPrimary = _e.isPrimary;
|
||||
return {
|
||||
alt_key: altKey_2,
|
||||
button: button,
|
||||
buttons: buttons,
|
||||
client_x: clientX,
|
||||
client_y: clientY,
|
||||
ctrl_key: ctrlKey_2,
|
||||
meta_key: metaKey_2,
|
||||
page_x: pageX,
|
||||
page_y: pageY,
|
||||
screen_x: screenX_2,
|
||||
screen_y: screenY_2,
|
||||
shift_key: shiftKey_2,
|
||||
pointer_id: pointerId,
|
||||
width: width,
|
||||
height: height,
|
||||
pressure: pressure,
|
||||
tangential_pressure: tangentialPressure,
|
||||
tilt_x: tiltX,
|
||||
tilt_y: tiltY,
|
||||
twist: twist,
|
||||
pointer_type: pointerType,
|
||||
is_primary: isPrimary,
|
||||
};
|
||||
}
|
||||
case "select":
|
||||
return {};
|
||||
case "touchcancel":
|
||||
case "touchend":
|
||||
case "touchmove":
|
||||
case "touchstart": {
|
||||
var _f = event,
|
||||
altKey_3 = _f.altKey,
|
||||
ctrlKey_3 = _f.ctrlKey,
|
||||
metaKey_3 = _f.metaKey,
|
||||
shiftKey_3 = _f.shiftKey;
|
||||
return {
|
||||
// changed_touches: event.changedTouches,
|
||||
// target_touches: event.targetTouches,
|
||||
// touches: event.touches,
|
||||
alt_key: altKey_3,
|
||||
ctrl_key: ctrlKey_3,
|
||||
meta_key: metaKey_3,
|
||||
shift_key: shiftKey_3,
|
||||
};
|
||||
}
|
||||
case "scroll":
|
||||
return {};
|
||||
case "wheel": {
|
||||
var _g = event,
|
||||
deltaX = _g.deltaX,
|
||||
deltaY = _g.deltaY,
|
||||
deltaZ = _g.deltaZ,
|
||||
deltaMode = _g.deltaMode;
|
||||
return {
|
||||
delta_x: deltaX,
|
||||
delta_y: deltaY,
|
||||
delta_z: deltaZ,
|
||||
delta_mode: deltaMode,
|
||||
};
|
||||
}
|
||||
case "animationstart":
|
||||
case "animationend":
|
||||
case "animationiteration": {
|
||||
var _h = event,
|
||||
animationName = _h.animationName,
|
||||
elapsedTime = _h.elapsedTime,
|
||||
pseudoElement = _h.pseudoElement;
|
||||
return {
|
||||
animation_name: animationName,
|
||||
elapsed_time: elapsedTime,
|
||||
pseudo_element: pseudoElement,
|
||||
};
|
||||
}
|
||||
case "transitionend": {
|
||||
var _j = event,
|
||||
propertyName = _j.propertyName,
|
||||
elapsedTime = _j.elapsedTime,
|
||||
pseudoElement = _j.pseudoElement;
|
||||
return {
|
||||
property_name: propertyName,
|
||||
elapsed_time: elapsedTime,
|
||||
pseudo_element: pseudoElement,
|
||||
};
|
||||
}
|
||||
case "abort":
|
||||
case "canplay":
|
||||
case "canplaythrough":
|
||||
case "durationchange":
|
||||
case "emptied":
|
||||
case "encrypted":
|
||||
case "ended":
|
||||
case "error":
|
||||
case "loadeddata":
|
||||
case "loadedmetadata":
|
||||
case "loadstart":
|
||||
case "pause":
|
||||
case "play":
|
||||
case "playing":
|
||||
case "progress":
|
||||
case "ratechange":
|
||||
case "seeked":
|
||||
case "seeking":
|
||||
case "stalled":
|
||||
case "suspend":
|
||||
case "timeupdate":
|
||||
case "volumechange":
|
||||
case "waiting":
|
||||
return {};
|
||||
case "toggle":
|
||||
return {};
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
var bool_attrs = {
|
||||
allowfullscreen: true,
|
||||
allowpaymentrequest: true,
|
||||
async: true,
|
||||
autofocus: true,
|
||||
autoplay: true,
|
||||
checked: true,
|
||||
controls: true,
|
||||
default: true,
|
||||
defer: true,
|
||||
disabled: true,
|
||||
formnovalidate: true,
|
||||
hidden: true,
|
||||
ismap: true,
|
||||
itemscope: true,
|
||||
loop: true,
|
||||
multiple: true,
|
||||
muted: true,
|
||||
nomodule: true,
|
||||
novalidate: true,
|
||||
open: true,
|
||||
playsinline: true,
|
||||
readonly: true,
|
||||
required: true,
|
||||
reversed: true,
|
||||
selected: true,
|
||||
truespeed: true,
|
||||
};
|
||||
export var Interpreter = /** @class */ (function () {
|
||||
function Interpreter(root) {
|
||||
this.root = root;
|
||||
this.stack = [root];
|
||||
this.listeners = {};
|
||||
this.lastNodeWasText = false;
|
||||
this.nodes = [root];
|
||||
}
|
||||
Interpreter.prototype.top = function () {
|
||||
return this.stack[this.stack.length - 1];
|
||||
};
|
||||
Interpreter.prototype.pop = function () {
|
||||
return this.stack.pop();
|
||||
};
|
||||
Interpreter.prototype.PushRoot = function (root) {
|
||||
var node = this.nodes[root];
|
||||
this.stack.push(node);
|
||||
};
|
||||
Interpreter.prototype.AppendChildren = function (many) {
|
||||
var root = this.stack[this.stack.length - (1 + many)];
|
||||
var to_add = this.stack.splice(this.stack.length - many);
|
||||
for (var i = 0; i < many; i++) {
|
||||
root.appendChild(to_add[i]);
|
||||
}
|
||||
};
|
||||
Interpreter.prototype.ReplaceWith = function (root_id, m) {
|
||||
var root = this.nodes[root_id];
|
||||
var els = this.stack.splice(this.stack.length - m);
|
||||
root.replaceWith.apply(root, els);
|
||||
};
|
||||
Interpreter.prototype.InsertAfter = function (root, n) {
|
||||
var old = this.nodes[root];
|
||||
var new_nodes = this.stack.splice(this.stack.length - n);
|
||||
old.after.apply(old, new_nodes);
|
||||
};
|
||||
Interpreter.prototype.InsertBefore = function (root, n) {
|
||||
var old = this.nodes[root];
|
||||
var new_nodes = this.stack.splice(this.stack.length - n);
|
||||
old.before.apply(old, new_nodes);
|
||||
};
|
||||
Interpreter.prototype.Remove = function (root) {
|
||||
var node = this.nodes[root];
|
||||
if (node !== undefined) {
|
||||
node.remove();
|
||||
}
|
||||
};
|
||||
Interpreter.prototype.CreateTextNode = function (text, root) {
|
||||
// todo: make it so the types are okay
|
||||
var node = document.createTextNode(text);
|
||||
this.nodes[root] = node;
|
||||
this.stack.push(node);
|
||||
};
|
||||
Interpreter.prototype.CreateElement = function (tag, root) {
|
||||
var el = document.createElement(tag);
|
||||
el.setAttribute("dioxus-id", "".concat(root));
|
||||
this.nodes[root] = el;
|
||||
this.stack.push(el);
|
||||
};
|
||||
Interpreter.prototype.CreateElementNs = function (tag, root, ns) {
|
||||
var el = document.createElementNS(ns, tag);
|
||||
this.stack.push(el);
|
||||
this.nodes[root] = el;
|
||||
};
|
||||
Interpreter.prototype.CreatePlaceholder = function (root) {
|
||||
var el = document.createElement("pre");
|
||||
el.hidden = true;
|
||||
this.stack.push(el);
|
||||
this.nodes[root] = el;
|
||||
};
|
||||
Interpreter.prototype.NewEventListener = function (event_name, scope, root) {
|
||||
console.log("new event listener", event_name, root, scope);
|
||||
var element = this.nodes[root];
|
||||
element.setAttribute(
|
||||
"dioxus-event-".concat(event_name),
|
||||
"".concat(scope, ".").concat(root)
|
||||
);
|
||||
// if (!this.listeners[event_name]) {
|
||||
// this.listeners[event_name] = handler;
|
||||
// this.root.addEventListener(event_name, handler);
|
||||
// }
|
||||
};
|
||||
Interpreter.prototype.RemoveEventListener = function (
|
||||
root,
|
||||
event_name,
|
||||
scope
|
||||
) {
|
||||
//
|
||||
};
|
||||
Interpreter.prototype.SetText = function (root, text) {
|
||||
this.nodes[root].textContent = text;
|
||||
};
|
||||
Interpreter.prototype.SetAttribute = function (root, field, value, ns) {
|
||||
var name = field;
|
||||
var node = this.nodes[root];
|
||||
if (ns == "style") {
|
||||
// @ts-ignore
|
||||
node.style[name] = value;
|
||||
} else if (ns != null || ns != undefined) {
|
||||
node.setAttributeNS(ns, name, value);
|
||||
} else {
|
||||
switch (name) {
|
||||
case "value":
|
||||
if (value != node.value) {
|
||||
node.value = value;
|
||||
}
|
||||
break;
|
||||
case "checked":
|
||||
node.checked = value === "true";
|
||||
break;
|
||||
case "selected":
|
||||
node.selected = value === "true";
|
||||
break;
|
||||
case "dangerous_inner_html":
|
||||
node.innerHTML = value;
|
||||
break;
|
||||
default:
|
||||
// https://github.com/facebook/react/blob/8b88ac2592c5f555f315f9440cbb665dd1e7457a/packages/react-dom/src/shared/DOMProperty.js#L352-L364
|
||||
if (value == "false" && bool_attrs.hasOwnProperty(name)) {
|
||||
node.removeAttribute(name);
|
||||
} else {
|
||||
node.setAttribute(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Interpreter.prototype.RemoveAttribute = function (root, name) {
|
||||
var node = this.nodes[root];
|
||||
node.removeAttribute(name);
|
||||
if (name === "value") {
|
||||
node.value = "";
|
||||
}
|
||||
if (name === "checked") {
|
||||
node.checked = false;
|
||||
}
|
||||
if (name === "selected") {
|
||||
node.selected = false;
|
||||
}
|
||||
};
|
||||
Interpreter.prototype.handleEdits = function (edits) {
|
||||
console.log("handling edits ", edits);
|
||||
this.stack.push(this.root);
|
||||
for (var _i = 0, edits_1 = edits; _i < edits_1.length; _i++) {
|
||||
var edit = edits_1[_i];
|
||||
this.handleEdit(edit);
|
||||
}
|
||||
};
|
||||
Interpreter.prototype.handleEdit = function (edit) {
|
||||
switch (edit.type) {
|
||||
case "PushRoot":
|
||||
this.PushRoot(edit.root);
|
||||
break;
|
||||
case "AppendChildren":
|
||||
this.AppendChildren(edit.many);
|
||||
break;
|
||||
case "ReplaceWith":
|
||||
this.ReplaceWith(edit.root, edit.m);
|
||||
break;
|
||||
case "InsertAfter":
|
||||
this.InsertAfter(edit.root, edit.n);
|
||||
break;
|
||||
case "InsertBefore":
|
||||
this.InsertBefore(edit.root, edit.n);
|
||||
break;
|
||||
case "Remove":
|
||||
this.Remove(edit.root);
|
||||
break;
|
||||
case "CreateTextNode":
|
||||
this.CreateTextNode(edit.text, edit.root);
|
||||
break;
|
||||
case "CreateElement":
|
||||
this.CreateElement(edit.tag, edit.root);
|
||||
break;
|
||||
case "CreateElementNs":
|
||||
this.CreateElementNs(edit.tag, edit.root, edit.ns);
|
||||
break;
|
||||
case "CreatePlaceholder":
|
||||
this.CreatePlaceholder(edit.root);
|
||||
break;
|
||||
case "RemoveEventListener":
|
||||
this.RemoveEventListener(edit.root, edit.event_name, edit.scope);
|
||||
break;
|
||||
case "NewEventListener":
|
||||
// todo: only on desktop should we make our own handler
|
||||
var handler = function (event) {
|
||||
var target = event.target;
|
||||
console.log("event", event);
|
||||
if (target != null) {
|
||||
var real_id = target.getAttribute("dioxus-id");
|
||||
var should_prevent_default = target.getAttribute(
|
||||
"dioxus-prevent-default"
|
||||
);
|
||||
var contents = serialize_event(event);
|
||||
if (should_prevent_default === "on".concat(event.type)) {
|
||||
event.preventDefault();
|
||||
}
|
||||
if (real_id == null) {
|
||||
return;
|
||||
}
|
||||
window.rpc.call("user_event", {
|
||||
event: edit.event_name,
|
||||
mounted_dom_id: parseInt(real_id),
|
||||
contents: contents,
|
||||
});
|
||||
}
|
||||
};
|
||||
this.NewEventListener(edit.event_name, edit.scope, edit.root);
|
||||
// this.NewEventListener(edit, handler);
|
||||
break;
|
||||
case "SetText":
|
||||
this.SetText(edit.root, edit.text);
|
||||
break;
|
||||
case "SetAttribute":
|
||||
this.SetAttribute(edit.root, edit.field, edit.value, edit.ns);
|
||||
break;
|
||||
case "RemoveAttribute":
|
||||
this.RemoveAttribute(edit.root, edit.name);
|
||||
break;
|
||||
}
|
||||
};
|
||||
return Interpreter;
|
||||
})();
|
||||
exports.Interpreter = Interpreter;
|
||||
function main() {
|
||||
var root = window.document.getElementById("main");
|
||||
if (root != null) {
|
||||
window.interpreter = new Interpreter(root);
|
||||
window.rpc.call("initialize");
|
||||
}
|
||||
}
|
|
@ -61,6 +61,7 @@ pub use dioxus_core as dioxus;
|
|||
use dioxus_core::prelude::Component;
|
||||
use futures_util::FutureExt;
|
||||
|
||||
pub(crate) mod bindings;
|
||||
mod cache;
|
||||
mod cfg;
|
||||
mod dom;
|
||||
|
|
789
packages/web/src/olddom.rs
Normal file
789
packages/web/src/olddom.rs
Normal file
|
@ -0,0 +1,789 @@
|
|||
//! Implementation of a renderer for Dioxus on the web.
|
||||
//!
|
||||
//! Oustanding todos:
|
||||
//! - Removing event listeners (delegation)
|
||||
//! - Passive event listeners
|
||||
//! - no-op event listener patch for safari
|
||||
//! - tests to ensure dyn_into works for various event types.
|
||||
//! - Partial delegation?>
|
||||
|
||||
use dioxus_core::{DomEdit, ElementId, SchedulerMsg, ScopeId, UserEvent};
|
||||
use fxhash::FxHashMap;
|
||||
use std::{any::Any, fmt::Debug, rc::Rc, sync::Arc};
|
||||
use wasm_bindgen::{closure::Closure, JsCast};
|
||||
use web_sys::{
|
||||
CssStyleDeclaration, Document, Element, Event, HtmlElement, HtmlInputElement,
|
||||
HtmlOptionElement, HtmlTextAreaElement, Node,
|
||||
};
|
||||
|
||||
use crate::{nodeslab::NodeSlab, WebConfig};
|
||||
|
||||
pub struct WebsysDom {
|
||||
stack: Stack,
|
||||
|
||||
/// A map from ElementID (index) to Node
|
||||
pub(crate) nodes: NodeSlab,
|
||||
|
||||
document: Document,
|
||||
|
||||
pub(crate) root: Element,
|
||||
|
||||
sender_callback: Rc<dyn Fn(SchedulerMsg)>,
|
||||
|
||||
// map of listener types to number of those listeners
|
||||
// This is roughly a delegater
|
||||
// TODO: check how infero delegates its events - some are more performant
|
||||
listeners: FxHashMap<&'static str, ListenerEntry>,
|
||||
}
|
||||
|
||||
type ListenerEntry = (usize, Closure<dyn FnMut(&Event)>);
|
||||
|
||||
impl WebsysDom {
|
||||
pub fn new(cfg: WebConfig, sender_callback: Rc<dyn Fn(SchedulerMsg)>) -> Self {
|
||||
let document = load_document();
|
||||
|
||||
let nodes = NodeSlab::new(2000);
|
||||
let listeners = FxHashMap::default();
|
||||
|
||||
let mut stack = Stack::with_capacity(10);
|
||||
|
||||
let root = load_document().get_element_by_id(&cfg.rootname).unwrap();
|
||||
let root_node = root.clone().dyn_into::<Node>().unwrap();
|
||||
stack.push(root_node);
|
||||
|
||||
Self {
|
||||
stack,
|
||||
nodes,
|
||||
listeners,
|
||||
document,
|
||||
sender_callback,
|
||||
root,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_edits(&mut self, mut edits: Vec<DomEdit>) {
|
||||
for edit in edits.drain(..) {
|
||||
match edit {
|
||||
DomEdit::PushRoot { root } => self.push(root),
|
||||
DomEdit::AppendChildren { many } => self.append_children(many),
|
||||
DomEdit::ReplaceWith { m, root } => self.replace_with(m, root),
|
||||
DomEdit::Remove { root } => self.remove(root),
|
||||
DomEdit::CreateTextNode { text, root: id } => self.create_text_node(text, id),
|
||||
DomEdit::CreateElement { tag, root: id } => self.create_element(tag, None, id),
|
||||
DomEdit::CreateElementNs { tag, root: id, ns } => {
|
||||
self.create_element(tag, Some(ns), id)
|
||||
}
|
||||
DomEdit::CreatePlaceholder { root: id } => self.create_placeholder(id),
|
||||
DomEdit::NewEventListener {
|
||||
event_name,
|
||||
scope,
|
||||
root: mounted_node_id,
|
||||
} => self.new_event_listener(event_name, scope, mounted_node_id),
|
||||
|
||||
DomEdit::RemoveEventListener { event, root } => {
|
||||
self.remove_event_listener(event, root)
|
||||
}
|
||||
|
||||
DomEdit::SetText { text, root } => self.set_text(text, root),
|
||||
DomEdit::SetAttribute {
|
||||
field,
|
||||
value,
|
||||
ns,
|
||||
root,
|
||||
} => self.set_attribute(field, value, ns, root),
|
||||
DomEdit::RemoveAttribute { name, root } => self.remove_attribute(name, root),
|
||||
|
||||
DomEdit::InsertAfter { n, root } => self.insert_after(n, root),
|
||||
DomEdit::InsertBefore { n, root } => self.insert_before(n, root),
|
||||
}
|
||||
}
|
||||
}
|
||||
fn push(&mut self, root: u64) {
|
||||
let key = root as usize;
|
||||
let domnode = &self.nodes[key];
|
||||
|
||||
let real_node: Node = match domnode {
|
||||
Some(n) => n.clone(),
|
||||
None => todo!(),
|
||||
};
|
||||
|
||||
self.stack.push(real_node);
|
||||
}
|
||||
|
||||
fn append_children(&mut self, many: u32) {
|
||||
let root: Node = self
|
||||
.stack
|
||||
.list
|
||||
.get(self.stack.list.len() - (1 + many as usize))
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
// We need to make sure to add comments between text nodes
|
||||
// We ensure that the text siblings are patched by preventing the browser from merging
|
||||
// neighboring text nodes. Originally inspired by some of React's work from 2016.
|
||||
// -> https://reactjs.org/blog/2016/04/07/react-v15.html#major-changes
|
||||
// -> https://github.com/facebook/react/pull/5753
|
||||
/*
|
||||
todo: we need to track this for replacing/insert after/etc
|
||||
*/
|
||||
let mut last_node_was_text = false;
|
||||
|
||||
for child in self
|
||||
.stack
|
||||
.list
|
||||
.drain((self.stack.list.len() - many as usize)..)
|
||||
{
|
||||
if child.dyn_ref::<web_sys::Text>().is_some() {
|
||||
if last_node_was_text {
|
||||
let comment_node = self
|
||||
.document
|
||||
.create_comment("dioxus")
|
||||
.dyn_into::<Node>()
|
||||
.unwrap();
|
||||
root.append_child(&comment_node).unwrap();
|
||||
}
|
||||
last_node_was_text = true;
|
||||
} else {
|
||||
last_node_was_text = false;
|
||||
}
|
||||
root.append_child(&child).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn replace_with(&mut self, m: u32, root: u64) {
|
||||
let old = self.nodes[root as usize].as_ref().unwrap();
|
||||
|
||||
let arr: js_sys::Array = self
|
||||
.stack
|
||||
.list
|
||||
.drain((self.stack.list.len() - m as usize)..)
|
||||
.collect();
|
||||
|
||||
if let Some(el) = old.dyn_ref::<Element>() {
|
||||
el.replace_with_with_node(&arr).unwrap();
|
||||
} else if let Some(el) = old.dyn_ref::<web_sys::CharacterData>() {
|
||||
el.replace_with_with_node(&arr).unwrap();
|
||||
} else if let Some(el) = old.dyn_ref::<web_sys::DocumentType>() {
|
||||
el.replace_with_with_node(&arr).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn remove(&mut self, root: u64) {
|
||||
let node = self.nodes[root as usize].as_ref().unwrap();
|
||||
if let Some(element) = node.dyn_ref::<Element>() {
|
||||
element.remove();
|
||||
} else {
|
||||
if let Some(parent) = node.parent_node() {
|
||||
parent.remove_child(&node).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_placeholder(&mut self, id: u64) {
|
||||
self.create_element("pre", None, id);
|
||||
self.set_attribute("hidden", "", None, id);
|
||||
}
|
||||
|
||||
fn create_text_node(&mut self, text: &str, id: u64) {
|
||||
let textnode = self
|
||||
.document
|
||||
.create_text_node(text)
|
||||
.dyn_into::<Node>()
|
||||
.unwrap();
|
||||
|
||||
self.stack.push(textnode.clone());
|
||||
|
||||
self.nodes[(id as usize)] = Some(textnode);
|
||||
}
|
||||
|
||||
fn create_element(&mut self, tag: &str, ns: Option<&'static str>, id: u64) {
|
||||
let tag = wasm_bindgen::intern(tag);
|
||||
|
||||
let el = match ns {
|
||||
Some(ns) => self
|
||||
.document
|
||||
.create_element_ns(Some(ns), tag)
|
||||
.unwrap()
|
||||
.dyn_into::<Node>()
|
||||
.unwrap(),
|
||||
None => self
|
||||
.document
|
||||
.create_element(tag)
|
||||
.unwrap()
|
||||
.dyn_into::<Node>()
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
use smallstr::SmallString;
|
||||
use std::fmt::Write;
|
||||
|
||||
let mut s: SmallString<[u8; 8]> = smallstr::SmallString::new();
|
||||
write!(s, "{}", id).unwrap();
|
||||
|
||||
let el2 = el.dyn_ref::<Element>().unwrap();
|
||||
el2.set_attribute("dioxus-id", s.as_str()).unwrap();
|
||||
|
||||
self.stack.push(el.clone());
|
||||
self.nodes[(id as usize)] = Some(el);
|
||||
}
|
||||
|
||||
fn new_event_listener(&mut self, event: &'static str, _scope: ScopeId, _real_id: u64) {
|
||||
let event = wasm_bindgen::intern(event);
|
||||
|
||||
// attach the correct attributes to the element
|
||||
// these will be used by accessing the event's target
|
||||
// This ensures we only ever have one handler attached to the root, but decide
|
||||
// dynamically when we want to call a listener.
|
||||
|
||||
let el = self.stack.top();
|
||||
|
||||
let el = el.dyn_ref::<Element>().unwrap();
|
||||
|
||||
el.set_attribute("dioxus-event", event).unwrap();
|
||||
|
||||
// Register the callback to decode
|
||||
|
||||
if let Some(entry) = self.listeners.get_mut(event) {
|
||||
entry.0 += 1;
|
||||
} else {
|
||||
let trigger = self.sender_callback.clone();
|
||||
|
||||
let c: Box<dyn FnMut(&Event)> = Box::new(move |event: &web_sys::Event| {
|
||||
// "Result" cannot be received from JS
|
||||
// Instead, we just build and immediately execute a closure that returns result
|
||||
match decode_trigger(event) {
|
||||
Ok(synthetic_event) => {
|
||||
let target = event.target().unwrap();
|
||||
if let Some(node) = target.dyn_ref::<HtmlElement>() {
|
||||
if let Some(name) = node.get_attribute("dioxus-prevent-default") {
|
||||
if name == synthetic_event.name
|
||||
|| name.trim_start_matches("on") == synthetic_event.name
|
||||
{
|
||||
log::trace!("Preventing default");
|
||||
event.prevent_default();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trigger.as_ref()(SchedulerMsg::Event(synthetic_event))
|
||||
}
|
||||
Err(e) => log::error!("Error decoding Dioxus event attribute. {:#?}", e),
|
||||
};
|
||||
});
|
||||
|
||||
let handler = Closure::wrap(c);
|
||||
|
||||
self.root
|
||||
.add_event_listener_with_callback(event, (&handler).as_ref().unchecked_ref())
|
||||
.unwrap();
|
||||
|
||||
// Increment the listeners
|
||||
self.listeners.insert(event.into(), (1, handler));
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_event_listener(&mut self, _event: &str, _root: u64) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_text(&mut self, text: &str, root: u64) {
|
||||
let el = self.nodes[root as usize].as_ref().unwrap();
|
||||
el.set_text_content(Some(text))
|
||||
}
|
||||
|
||||
fn set_attribute(&mut self, name: &str, value: &str, ns: Option<&str>, root: u64) {
|
||||
let node = self.nodes[root as usize].as_ref().unwrap();
|
||||
if ns == Some("style") {
|
||||
if let Some(el) = node.dyn_ref::<Element>() {
|
||||
let el = el.dyn_ref::<HtmlElement>().unwrap();
|
||||
let style_dc: CssStyleDeclaration = el.style();
|
||||
style_dc.set_property(name, value).unwrap();
|
||||
}
|
||||
} else {
|
||||
let fallback = || {
|
||||
let el = node.dyn_ref::<Element>().unwrap();
|
||||
el.set_attribute(name, value).unwrap()
|
||||
};
|
||||
match name {
|
||||
"dangerous_inner_html" => {
|
||||
if let Some(el) = node.dyn_ref::<Element>() {
|
||||
el.set_inner_html(value);
|
||||
}
|
||||
}
|
||||
"value" => {
|
||||
if let Some(input) = node.dyn_ref::<HtmlInputElement>() {
|
||||
/*
|
||||
if the attribute being set is the same as the value of the input, then don't bother setting it.
|
||||
This is used in controlled components to keep the cursor in the right spot.
|
||||
|
||||
this logic should be moved into the virtualdom since we have the notion of "volatile"
|
||||
*/
|
||||
if input.value() != value {
|
||||
input.set_value(value);
|
||||
}
|
||||
} else if let Some(node) = node.dyn_ref::<HtmlTextAreaElement>() {
|
||||
if name == "value" {
|
||||
node.set_value(value);
|
||||
}
|
||||
} else {
|
||||
fallback();
|
||||
}
|
||||
}
|
||||
"checked" => {
|
||||
if let Some(input) = node.dyn_ref::<HtmlInputElement>() {
|
||||
match value {
|
||||
"true" => input.set_checked(true),
|
||||
"false" => input.set_checked(false),
|
||||
_ => fallback(),
|
||||
}
|
||||
} else {
|
||||
fallback();
|
||||
}
|
||||
}
|
||||
"selected" => {
|
||||
if let Some(node) = node.dyn_ref::<HtmlOptionElement>() {
|
||||
node.set_selected(true);
|
||||
} else {
|
||||
fallback();
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// https://github.com/facebook/react/blob/8b88ac2592c5f555f315f9440cbb665dd1e7457a/packages/react-dom/src/shared/DOMProperty.js#L352-L364
|
||||
if value == "false" {
|
||||
if let Some(el) = node.dyn_ref::<Element>() {
|
||||
match name {
|
||||
"allowfullscreen"
|
||||
| "allowpaymentrequest"
|
||||
| "async"
|
||||
| "autofocus"
|
||||
| "autoplay"
|
||||
| "checked"
|
||||
| "controls"
|
||||
| "default"
|
||||
| "defer"
|
||||
| "disabled"
|
||||
| "formnovalidate"
|
||||
| "hidden"
|
||||
| "ismap"
|
||||
| "itemscope"
|
||||
| "loop"
|
||||
| "multiple"
|
||||
| "muted"
|
||||
| "nomodule"
|
||||
| "novalidate"
|
||||
| "open"
|
||||
| "playsinline"
|
||||
| "readonly"
|
||||
| "required"
|
||||
| "reversed"
|
||||
| "selected"
|
||||
| "truespeed" => {
|
||||
let _ = el.remove_attribute(name);
|
||||
}
|
||||
_ => {
|
||||
let _ = el.set_attribute(name, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
} else {
|
||||
fallback();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_attribute(&mut self, name: &str, root: u64) {
|
||||
let node = self.nodes[root as usize].as_ref().unwrap();
|
||||
if let Some(node) = node.dyn_ref::<web_sys::Element>() {
|
||||
node.remove_attribute(name).unwrap();
|
||||
}
|
||||
if let Some(node) = node.dyn_ref::<HtmlInputElement>() {
|
||||
// Some attributes are "volatile" and don't work through `removeAttribute`.
|
||||
if name == "value" {
|
||||
node.set_value("");
|
||||
}
|
||||
if name == "checked" {
|
||||
node.set_checked(false);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(node) = node.dyn_ref::<HtmlOptionElement>() {
|
||||
if name == "selected" {
|
||||
node.set_selected(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_after(&mut self, n: u32, root: u64) {
|
||||
let old = self.nodes[root as usize].as_ref().unwrap();
|
||||
|
||||
let arr: js_sys::Array = self
|
||||
.stack
|
||||
.list
|
||||
.drain((self.stack.list.len() - n as usize)..)
|
||||
.collect();
|
||||
|
||||
if let Some(el) = old.dyn_ref::<Element>() {
|
||||
el.after_with_node(&arr).unwrap();
|
||||
} else if let Some(el) = old.dyn_ref::<web_sys::CharacterData>() {
|
||||
el.after_with_node(&arr).unwrap();
|
||||
} else if let Some(el) = old.dyn_ref::<web_sys::DocumentType>() {
|
||||
el.after_with_node(&arr).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_before(&mut self, n: u32, root: u64) {
|
||||
let anchor = self.nodes[root as usize].as_ref().unwrap();
|
||||
|
||||
if n == 1 {
|
||||
let before = self.stack.pop();
|
||||
|
||||
anchor
|
||||
.parent_node()
|
||||
.unwrap()
|
||||
.insert_before(&before, Some(&anchor))
|
||||
.unwrap();
|
||||
} else {
|
||||
let arr: js_sys::Array = self
|
||||
.stack
|
||||
.list
|
||||
.drain((self.stack.list.len() - n as usize)..)
|
||||
.collect();
|
||||
|
||||
if let Some(el) = anchor.dyn_ref::<Element>() {
|
||||
el.before_with_node(&arr).unwrap();
|
||||
} else if let Some(el) = anchor.dyn_ref::<web_sys::CharacterData>() {
|
||||
el.before_with_node(&arr).unwrap();
|
||||
} else if let Some(el) = anchor.dyn_ref::<web_sys::DocumentType>() {
|
||||
el.before_with_node(&arr).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct Stack {
|
||||
list: Vec<Node>,
|
||||
}
|
||||
|
||||
impl Stack {
|
||||
#[inline]
|
||||
fn with_capacity(cap: usize) -> Self {
|
||||
Stack {
|
||||
list: Vec::with_capacity(cap),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn push(&mut self, node: Node) {
|
||||
self.list.push(node);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn pop(&mut self) -> Node {
|
||||
self.list.pop().unwrap()
|
||||
}
|
||||
|
||||
fn top(&self) -> &Node {
|
||||
match self.list.last() {
|
||||
Some(a) => a,
|
||||
None => panic!("Called 'top' of an empty stack, make sure to push the root first"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DioxusWebsysEvent(web_sys::Event);
|
||||
|
||||
// safety: currently the web is not multithreaded and our VirtualDom exists on the same thread
|
||||
unsafe impl Send for DioxusWebsysEvent {}
|
||||
unsafe impl Sync for DioxusWebsysEvent {}
|
||||
|
||||
// todo: some of these events are being casted to the wrong event type.
|
||||
// We need tests that simulate clicks/etc and make sure every event type works.
|
||||
fn virtual_event_from_websys_event(event: web_sys::Event) -> Arc<dyn Any + Send + Sync> {
|
||||
use dioxus_html::on::*;
|
||||
use dioxus_html::KeyCode;
|
||||
|
||||
match event.type_().as_str() {
|
||||
"copy" | "cut" | "paste" => Arc::new(ClipboardData {}),
|
||||
"compositionend" | "compositionstart" | "compositionupdate" => {
|
||||
let evt: &web_sys::CompositionEvent = event.dyn_ref().unwrap();
|
||||
Arc::new(CompositionData {
|
||||
data: evt.data().unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
"keydown" | "keypress" | "keyup" => {
|
||||
let evt: &web_sys::KeyboardEvent = event.dyn_ref().unwrap();
|
||||
Arc::new(KeyboardData {
|
||||
alt_key: evt.alt_key(),
|
||||
char_code: evt.char_code(),
|
||||
key: evt.key(),
|
||||
key_code: KeyCode::from_raw_code(evt.key_code() as u8),
|
||||
ctrl_key: evt.ctrl_key(),
|
||||
locale: "not implemented".to_string(),
|
||||
location: evt.location() as usize,
|
||||
meta_key: evt.meta_key(),
|
||||
repeat: evt.repeat(),
|
||||
shift_key: evt.shift_key(),
|
||||
which: evt.which() as usize,
|
||||
})
|
||||
}
|
||||
"focus" | "blur" => Arc::new(FocusData {}),
|
||||
|
||||
// todo: these handlers might get really slow if the input box gets large and allocation pressure is heavy
|
||||
// don't have a good solution with the serialized event problem
|
||||
"change" | "input" | "invalid" | "reset" | "submit" => {
|
||||
let evt: &web_sys::Event = event.dyn_ref().unwrap();
|
||||
|
||||
let target: web_sys::EventTarget = evt.target().unwrap();
|
||||
let value: String = (&target)
|
||||
.dyn_ref()
|
||||
.map(|input: &web_sys::HtmlInputElement| {
|
||||
// todo: special case more input types
|
||||
match input.type_().as_str() {
|
||||
"checkbox" => {
|
||||
match input.checked() {
|
||||
true => "true".to_string(),
|
||||
false => "false".to_string(),
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
input.value()
|
||||
}
|
||||
}
|
||||
})
|
||||
.or_else(|| {
|
||||
target
|
||||
.dyn_ref()
|
||||
.map(|input: &web_sys::HtmlTextAreaElement| input.value())
|
||||
})
|
||||
// select elements are NOT input events - because - why woudn't they be??
|
||||
.or_else(|| {
|
||||
target
|
||||
.dyn_ref()
|
||||
.map(|input: &web_sys::HtmlSelectElement| input.value())
|
||||
})
|
||||
.or_else(|| {
|
||||
target
|
||||
.dyn_ref::<web_sys::HtmlElement>()
|
||||
.unwrap()
|
||||
.text_content()
|
||||
})
|
||||
.expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener");
|
||||
|
||||
Arc::new(FormData { value })
|
||||
}
|
||||
"click" | "contextmenu" | "doubleclick" | "drag" | "dragend" | "dragenter" | "dragexit"
|
||||
| "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown" | "mouseenter"
|
||||
| "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => {
|
||||
let evt: &web_sys::MouseEvent = event.dyn_ref().unwrap();
|
||||
Arc::new(MouseData {
|
||||
alt_key: evt.alt_key(),
|
||||
button: evt.button(),
|
||||
buttons: evt.buttons(),
|
||||
client_x: evt.client_x(),
|
||||
client_y: evt.client_y(),
|
||||
ctrl_key: evt.ctrl_key(),
|
||||
meta_key: evt.meta_key(),
|
||||
screen_x: evt.screen_x(),
|
||||
screen_y: evt.screen_y(),
|
||||
shift_key: evt.shift_key(),
|
||||
page_x: evt.page_x(),
|
||||
page_y: evt.page_y(),
|
||||
})
|
||||
}
|
||||
"pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
|
||||
| "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => {
|
||||
let evt: &web_sys::PointerEvent = event.dyn_ref().unwrap();
|
||||
Arc::new(PointerData {
|
||||
alt_key: evt.alt_key(),
|
||||
button: evt.button(),
|
||||
buttons: evt.buttons(),
|
||||
client_x: evt.client_x(),
|
||||
client_y: evt.client_y(),
|
||||
ctrl_key: evt.ctrl_key(),
|
||||
meta_key: evt.meta_key(),
|
||||
page_x: evt.page_x(),
|
||||
page_y: evt.page_y(),
|
||||
screen_x: evt.screen_x(),
|
||||
screen_y: evt.screen_y(),
|
||||
shift_key: evt.shift_key(),
|
||||
pointer_id: evt.pointer_id(),
|
||||
width: evt.width(),
|
||||
height: evt.height(),
|
||||
pressure: evt.pressure(),
|
||||
tangential_pressure: evt.tangential_pressure(),
|
||||
tilt_x: evt.tilt_x(),
|
||||
tilt_y: evt.tilt_y(),
|
||||
twist: evt.twist(),
|
||||
pointer_type: evt.pointer_type(),
|
||||
is_primary: evt.is_primary(),
|
||||
// get_modifier_state: evt.get_modifier_state(),
|
||||
})
|
||||
}
|
||||
"select" => Arc::new(SelectionData {}),
|
||||
"touchcancel" | "touchend" | "touchmove" | "touchstart" => {
|
||||
let evt: &web_sys::TouchEvent = event.dyn_ref().unwrap();
|
||||
Arc::new(TouchData {
|
||||
alt_key: evt.alt_key(),
|
||||
ctrl_key: evt.ctrl_key(),
|
||||
meta_key: evt.meta_key(),
|
||||
shift_key: evt.shift_key(),
|
||||
})
|
||||
}
|
||||
"scroll" => Arc::new(()),
|
||||
"wheel" => {
|
||||
let evt: &web_sys::WheelEvent = event.dyn_ref().unwrap();
|
||||
Arc::new(WheelData {
|
||||
delta_x: evt.delta_x(),
|
||||
delta_y: evt.delta_y(),
|
||||
delta_z: evt.delta_z(),
|
||||
delta_mode: evt.delta_mode(),
|
||||
})
|
||||
}
|
||||
"animationstart" | "animationend" | "animationiteration" => {
|
||||
let evt: &web_sys::AnimationEvent = event.dyn_ref().unwrap();
|
||||
Arc::new(AnimationData {
|
||||
elapsed_time: evt.elapsed_time(),
|
||||
animation_name: evt.animation_name(),
|
||||
pseudo_element: evt.pseudo_element(),
|
||||
})
|
||||
}
|
||||
"transitionend" => {
|
||||
let evt: &web_sys::TransitionEvent = event.dyn_ref().unwrap();
|
||||
Arc::new(TransitionData {
|
||||
elapsed_time: evt.elapsed_time(),
|
||||
property_name: evt.property_name(),
|
||||
pseudo_element: evt.pseudo_element(),
|
||||
})
|
||||
}
|
||||
"abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted"
|
||||
| "ended" | "error" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play"
|
||||
| "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
|
||||
| "timeupdate" | "volumechange" | "waiting" => Arc::new(MediaData {}),
|
||||
"toggle" => Arc::new(ToggleData {}),
|
||||
_ => Arc::new(()),
|
||||
}
|
||||
}
|
||||
|
||||
/// This function decodes a websys event and produces an EventTrigger
|
||||
/// With the websys implementation, we attach a unique key to the nodes
|
||||
fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<UserEvent> {
|
||||
use anyhow::Context;
|
||||
|
||||
let target = event
|
||||
.target()
|
||||
.expect("missing target")
|
||||
.dyn_into::<Element>()
|
||||
.expect("not a valid element");
|
||||
|
||||
let typ = event.type_();
|
||||
|
||||
let element_id = target
|
||||
.get_attribute("dioxus-id")
|
||||
.context("Could not find element id on event target")?
|
||||
.parse()?;
|
||||
|
||||
Ok(UserEvent {
|
||||
name: event_name_from_typ(&typ),
|
||||
data: virtual_event_from_websys_event(event.clone()),
|
||||
element: Some(ElementId(element_id)),
|
||||
scope_id: None,
|
||||
priority: dioxus_core::EventPriority::Medium,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn load_document() -> Document {
|
||||
web_sys::window()
|
||||
.expect("should have access to the Window")
|
||||
.document()
|
||||
.expect("should have access to the Document")
|
||||
}
|
||||
|
||||
fn event_name_from_typ(typ: &str) -> &'static str {
|
||||
match typ {
|
||||
"copy" => "copy",
|
||||
"cut" => "cut",
|
||||
"paste" => "paste",
|
||||
"compositionend" => "compositionend",
|
||||
"compositionstart" => "compositionstart",
|
||||
"compositionupdate" => "compositionupdate",
|
||||
"keydown" => "keydown",
|
||||
"keypress" => "keypress",
|
||||
"keyup" => "keyup",
|
||||
"focus" => "focus",
|
||||
"blur" => "blur",
|
||||
"change" => "change",
|
||||
"input" => "input",
|
||||
"invalid" => "invalid",
|
||||
"reset" => "reset",
|
||||
"submit" => "submit",
|
||||
"click" => "click",
|
||||
"contextmenu" => "contextmenu",
|
||||
"doubleclick" => "doubleclick",
|
||||
"drag" => "drag",
|
||||
"dragend" => "dragend",
|
||||
"dragenter" => "dragenter",
|
||||
"dragexit" => "dragexit",
|
||||
"dragleave" => "dragleave",
|
||||
"dragover" => "dragover",
|
||||
"dragstart" => "dragstart",
|
||||
"drop" => "drop",
|
||||
"mousedown" => "mousedown",
|
||||
"mouseenter" => "mouseenter",
|
||||
"mouseleave" => "mouseleave",
|
||||
"mousemove" => "mousemove",
|
||||
"mouseout" => "mouseout",
|
||||
"mouseover" => "mouseover",
|
||||
"mouseup" => "mouseup",
|
||||
"pointerdown" => "pointerdown",
|
||||
"pointermove" => "pointermove",
|
||||
"pointerup" => "pointerup",
|
||||
"pointercancel" => "pointercancel",
|
||||
"gotpointercapture" => "gotpointercapture",
|
||||
"lostpointercapture" => "lostpointercapture",
|
||||
"pointerenter" => "pointerenter",
|
||||
"pointerleave" => "pointerleave",
|
||||
"pointerover" => "pointerover",
|
||||
"pointerout" => "pointerout",
|
||||
"select" => "select",
|
||||
"touchcancel" => "touchcancel",
|
||||
"touchend" => "touchend",
|
||||
"touchmove" => "touchmove",
|
||||
"touchstart" => "touchstart",
|
||||
"scroll" => "scroll",
|
||||
"wheel" => "wheel",
|
||||
"animationstart" => "animationstart",
|
||||
"animationend" => "animationend",
|
||||
"animationiteration" => "animationiteration",
|
||||
"transitionend" => "transitionend",
|
||||
"abort" => "abort",
|
||||
"canplay" => "canplay",
|
||||
"canplaythrough" => "canplaythrough",
|
||||
"durationchange" => "durationchange",
|
||||
"emptied" => "emptied",
|
||||
"encrypted" => "encrypted",
|
||||
"ended" => "ended",
|
||||
"error" => "error",
|
||||
"loadeddata" => "loadeddata",
|
||||
"loadedmetadata" => "loadedmetadata",
|
||||
"loadstart" => "loadstart",
|
||||
"pause" => "pause",
|
||||
"play" => "play",
|
||||
"playing" => "playing",
|
||||
"progress" => "progress",
|
||||
"ratechange" => "ratechange",
|
||||
"seeked" => "seeked",
|
||||
"seeking" => "seeking",
|
||||
"stalled" => "stalled",
|
||||
"suspend" => "suspend",
|
||||
"timeupdate" => "timeupdate",
|
||||
"volumechange" => "volumechange",
|
||||
"waiting" => "waiting",
|
||||
"toggle" => "toggle",
|
||||
_ => {
|
||||
panic!("unsupported event type")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -80,7 +80,8 @@ impl WebsysDom {
|
|||
|
||||
*last_node_was_text = true;
|
||||
|
||||
self.nodes[node_id.0] = Some(node);
|
||||
self.interpreter.set_node(node_id.0, node);
|
||||
// self.nodes[node_id.0] = Some(node);
|
||||
|
||||
*cur_place += 1;
|
||||
}
|
||||
|
@ -105,7 +106,8 @@ impl WebsysDom {
|
|||
.set_attribute("dioxus-id", s.as_str())
|
||||
.unwrap();
|
||||
|
||||
self.nodes[node_id.0] = Some(node.clone());
|
||||
self.interpreter.set_node(node_id.0, node.clone());
|
||||
// self.nodes[node_id.0] = Some(node.clone());
|
||||
|
||||
*cur_place += 1;
|
||||
|
||||
|
@ -135,7 +137,9 @@ impl WebsysDom {
|
|||
let cur_place = place.last_mut().unwrap();
|
||||
let node = nodes.last().unwrap().child_nodes().get(*cur_place).unwrap();
|
||||
|
||||
self.nodes[node_id.0] = Some(node);
|
||||
self.interpreter.set_node(node_id.0, node);
|
||||
|
||||
// self.nodes[node_id.0] = Some(node);
|
||||
|
||||
*cur_place += 1;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue