Switch to bun, clean up web implementation
This commit is contained in:
23 files changed
+240
-173
No files matched your search
@@ -1,3 +1,5 @@
|
|||||||
|
#![allow(unused)] // for whatever reason, the compiler is not recognizing the use of these functions
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_core::Element;
|
use dioxus_core::Element;
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
let target_id = find_real_id(target);
|
let target_id = find_real_id(target);
|
||||||
if (target_id !== null) {
|
if (target_id !== null) {
|
||||||
const send = (event_name) => {
|
const send = (event_name) => {
|
||||||
const message = window.interpreter.serializeIpcMessage("file_diolog", { accept: target.getAttribute("accept"), directory: target.getAttribute("webkitdirectory") === "true", multiple: target.hasAttribute("multiple"), target: parseInt(target_id), bubbles: event_bubbles(event_name), event: event_name });
|
const message = window.interpreter.serializeIpcMessage("file_dialog", { accept: target.getAttribute("accept"), directory: target.getAttribute("webkitdirectory") === "true", multiple: target.hasAttribute("multiple"), target: parseInt(target_id), bubbles: event_bubbles(event_name), event: event_name });
|
||||||
window.ipc.postMessage(message);
|
window.ipc.postMessage(message);
|
||||||
};
|
};
|
||||||
send("change&input");
|
send("change&input");
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ impl IpcMessage {
|
|||||||
pub(crate) fn method(&self) -> IpcMethod {
|
pub(crate) fn method(&self) -> IpcMethod {
|
||||||
match self.method.as_str() {
|
match self.method.as_str() {
|
||||||
// todo: this is a misspelling, needs to be fixed
|
// todo: this is a misspelling, needs to be fixed
|
||||||
"file_diolog" => IpcMethod::FileDialog,
|
"file_dialog" => IpcMethod::FileDialog,
|
||||||
"user_event" => IpcMethod::UserEvent,
|
"user_event" => IpcMethod::UserEvent,
|
||||||
"query" => IpcMethod::Query,
|
"query" => IpcMethod::Query,
|
||||||
"browser_open" => IpcMethod::BrowserOpen,
|
"browser_open" => IpcMethod::BrowserOpen,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::{assets::*, edits::EditQueue};
|
use crate::{assets::*, edits::EditQueue};
|
||||||
use dioxus_interpreter_js::unified_bindings::{native_js, SLEDGEHAMMER_JS};
|
use dioxus_interpreter_js::unified_bindings::native_js;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use wry::{
|
use wry::{
|
||||||
http::{status::StatusCode, Request, Response},
|
http::{status::StatusCode, Request, Response},
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
4429706825984325407
|
3599957386864841107
|
||||||
@@ -11,13 +11,16 @@ class Interpreter {
|
|||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
this.initialize(root);
|
this.initialize(root);
|
||||||
}
|
}
|
||||||
initialize(root) {
|
initialize(root, handler = null) {
|
||||||
this.global = {};
|
this.global = {};
|
||||||
this.local = {};
|
this.local = {};
|
||||||
this.root = root;
|
this.root = root;
|
||||||
this.nodes = [root];
|
this.nodes = [root];
|
||||||
this.stack = [root];
|
this.stack = [root];
|
||||||
this.templates = {};
|
this.templates = {};
|
||||||
|
if (handler) {
|
||||||
|
this.handler = handler;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
createListener(event_name, element, bubbles) {
|
createListener(event_name, element, bubbles) {
|
||||||
if (bubbles) {
|
if (bubbles) {
|
||||||
|
|||||||
@@ -11,13 +11,16 @@ class Interpreter {
|
|||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
this.initialize(root);
|
this.initialize(root);
|
||||||
}
|
}
|
||||||
initialize(root) {
|
initialize(root, handler = null) {
|
||||||
this.global = {};
|
this.global = {};
|
||||||
this.local = {};
|
this.local = {};
|
||||||
this.root = root;
|
this.root = root;
|
||||||
this.nodes = [root];
|
this.nodes = [root];
|
||||||
this.stack = [root];
|
this.stack = [root];
|
||||||
this.templates = {};
|
this.templates = {};
|
||||||
|
if (handler) {
|
||||||
|
this.handler = handler;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
createListener(event_name, element, bubbles) {
|
createListener(event_name, element, bubbles) {
|
||||||
if (bubbles) {
|
if (bubbles) {
|
||||||
@@ -73,6 +76,86 @@ class Interpreter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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/interpreter_web.ts
|
// src/ts/interpreter_web.ts
|
||||||
class PlatformInterpreter extends Interpreter {
|
class PlatformInterpreter extends Interpreter {
|
||||||
m;
|
m;
|
||||||
@@ -126,5 +209,6 @@ class PlatformInterpreter extends Interpreter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
export {
|
export {
|
||||||
|
setAttributeInner,
|
||||||
PlatformInterpreter
|
PlatformInterpreter
|
||||||
};
|
};
|
||||||
@@ -11,6 +11,12 @@ mod write_native_mutations;
|
|||||||
#[cfg(all(feature = "binary-protocol", feature = "sledgehammer"))]
|
#[cfg(all(feature = "binary-protocol", feature = "sledgehammer"))]
|
||||||
pub use write_native_mutations::*;
|
pub use write_native_mutations::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "sledgehammer")]
|
||||||
|
pub mod unified_bindings;
|
||||||
|
|
||||||
|
#[cfg(feature = "sledgehammer")]
|
||||||
|
pub use unified_bindings::*;
|
||||||
|
|
||||||
// Common bindings for minimal usage.
|
// Common bindings for minimal usage.
|
||||||
#[cfg(all(feature = "minimal_bindings", feature = "webonly"))]
|
#[cfg(all(feature = "minimal_bindings", feature = "webonly"))]
|
||||||
pub mod minimal_bindings {
|
pub mod minimal_bindings {
|
||||||
@@ -28,9 +34,3 @@ pub mod minimal_bindings {
|
|||||||
pub fn collectFormValues(node: JsValue) -> JsValue;
|
pub fn collectFormValues(node: JsValue) -> JsValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "sledgehammer")]
|
|
||||||
pub mod unified_bindings;
|
|
||||||
|
|
||||||
#[cfg(feature = "sledgehammer")]
|
|
||||||
pub use unified_bindings::*;
|
|
||||||
@@ -28,7 +28,7 @@ export class Interpreter {
|
|||||||
this.initialize(root);
|
this.initialize(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize(root: HTMLElement) {
|
initialize(root: HTMLElement, handler: EventListener | null = null) {
|
||||||
this.global = {};
|
this.global = {};
|
||||||
this.local = {};
|
this.local = {};
|
||||||
this.root = root;
|
this.root = root;
|
||||||
@@ -36,6 +36,10 @@ export class Interpreter {
|
|||||||
this.nodes = [root];
|
this.nodes = [root];
|
||||||
this.stack = [root];
|
this.stack = [root];
|
||||||
this.templates = {};
|
this.templates = {};
|
||||||
|
|
||||||
|
if (handler) {
|
||||||
|
this.handler = handler;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createListener(event_name: string, element: HTMLElement, bubbles: boolean) {
|
createListener(event_name: string, element: HTMLElement, bubbles: boolean) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
// We're using sledgehammer directly
|
// We're using sledgehammer directly
|
||||||
|
|
||||||
import { Interpreter } from "./interpreter_core";
|
import { Interpreter } from "./interpreter_core";
|
||||||
|
export { setAttributeInner } from "./set_attribute";
|
||||||
|
|
||||||
export class PlatformInterpreter extends Interpreter {
|
export class PlatformInterpreter extends Interpreter {
|
||||||
m: any;
|
m: any;
|
||||||
|
|||||||
@@ -6,40 +6,13 @@ use wasm_bindgen::prelude::wasm_bindgen;
|
|||||||
|
|
||||||
use sledgehammer_bindgen::bindgen;
|
use sledgehammer_bindgen::bindgen;
|
||||||
|
|
||||||
|
/// Combine the interpreter class with the sledgehammer_bindgen generated methods.
|
||||||
pub fn native_js() -> String {
|
pub fn native_js() -> String {
|
||||||
format!("{}\n{}", include_str!("./js/native.js"), GENERATED_JS,)
|
format!("{}\n{}", include_str!("./js/native.js"), GENERATED_JS,)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const SLEDGEHAMMER_JS: &str = GENERATED_JS;
|
pub const SLEDGEHAMMER_JS: &str = GENERATED_JS;
|
||||||
|
|
||||||
/// Extensions to the interpreter that are specific to the web platform.
|
|
||||||
#[cfg(feature = "webonly")]
|
|
||||||
#[wasm_bindgen(module = "src/js/web.js")]
|
|
||||||
extern "C" {
|
|
||||||
pub type WebInterpreter;
|
|
||||||
|
|
||||||
#[wasm_bindgen(method, js_name = "saveTemplate")]
|
|
||||||
pub fn save_template(this: &WebInterpreter, nodes: Vec<Node>, tmpl_id: u16);
|
|
||||||
|
|
||||||
#[wasm_bindgen(method)]
|
|
||||||
pub fn hydrate(this: &WebInterpreter, ids: Vec<u32>);
|
|
||||||
|
|
||||||
#[wasm_bindgen(method, js_name = "getNode")]
|
|
||||||
pub fn get_node(this: &WebInterpreter, id: u32) -> Node;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "webonly")]
|
|
||||||
type PlatformInterpreter = WebInterpreter;
|
|
||||||
|
|
||||||
#[cfg(feature = "webonly")]
|
|
||||||
impl Interpreter {
|
|
||||||
/// Convert the interpreter to a web interpreter, enabling methods like hydrate and save_template.
|
|
||||||
pub fn as_web(&self) -> &WebInterpreter {
|
|
||||||
use wasm_bindgen::prelude::JsCast;
|
|
||||||
&self.js_channel().unchecked_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bindgen(module)]
|
#[bindgen(module)]
|
||||||
mod js {
|
mod js {
|
||||||
/// The interpreter extends the core interpreter which contains the state for the interpreter along with some functions that all platforms use like `AppendChildren`.
|
/// The interpreter extends the core interpreter which contains the state for the interpreter along with some functions that all platforms use like `AppendChildren`.
|
||||||
@@ -222,3 +195,34 @@ mod js {
|
|||||||
"{this.els = this.stack.splice(this.stack.length - $n$); let node = this.LoadChild($array$); node.replaceWith(...this.els);}"
|
"{this.els = this.stack.splice(this.stack.length - $n$); let node = this.LoadChild($array$); node.replaceWith(...this.els);}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extensions to the interpreter that are specific to the web platform.
|
||||||
|
#[cfg(feature = "webonly")]
|
||||||
|
#[wasm_bindgen(module = "src/js/web.js")]
|
||||||
|
extern "C" {
|
||||||
|
pub type WebInterpreter;
|
||||||
|
|
||||||
|
#[wasm_bindgen(method)]
|
||||||
|
pub fn initialize(this: &WebInterpreter, root: Node, handler: &js_sys::Function);
|
||||||
|
|
||||||
|
#[wasm_bindgen(method, js_name = "saveTemplate")]
|
||||||
|
pub fn save_template(this: &WebInterpreter, nodes: Vec<Node>, tmpl_id: u16);
|
||||||
|
|
||||||
|
#[wasm_bindgen(method)]
|
||||||
|
pub fn hydrate(this: &WebInterpreter, ids: Vec<u32>);
|
||||||
|
|
||||||
|
#[wasm_bindgen(method, js_name = "getNode")]
|
||||||
|
pub fn get_node(this: &WebInterpreter, id: u32) -> Node;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "webonly")]
|
||||||
|
type PlatformInterpreter = WebInterpreter;
|
||||||
|
|
||||||
|
#[cfg(feature = "webonly")]
|
||||||
|
impl Interpreter {
|
||||||
|
/// Convert the interpreter to a web interpreter, enabling methods like hydrate and save_template.
|
||||||
|
pub fn as_web(&self) -> &WebInterpreter {
|
||||||
|
use wasm_bindgen::prelude::JsCast;
|
||||||
|
&self.js_channel().unchecked_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
// extends the base
|
|
||||||
"extends": "./tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"outFile": "src/js/native.js"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"src/ts/interpreter_native.ts"
|
|
||||||
],
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
// extends the base
|
|
||||||
"extends": "./tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"outFile": "src/js/web.js"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"src/ts/interpreter_web.ts"
|
|
||||||
],
|
|
||||||
}
|
|
||||||
@@ -31,7 +31,7 @@ pub enum LiveViewError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_edits_code() -> String {
|
fn handle_edits_code() -> String {
|
||||||
use dioxus_interpreter_js::binary_protocol::SLEDGEHAMMER_JS;
|
use dioxus_interpreter_js::unified_bindings::SLEDGEHAMMER_JS;
|
||||||
use minify_js::{minify, Session, TopLevelMode};
|
use minify_js::{minify, Session, TopLevelMode};
|
||||||
|
|
||||||
let serialize_file_uploads = r#"if (
|
let serialize_file_uploads = r#"if (
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ use dioxus_html::input_data::keyboard_types::{Code, Key, Location, Modifiers};
|
|||||||
use dioxus_html::input_data::{
|
use dioxus_html::input_data::{
|
||||||
MouseButton as DioxusMouseButton, MouseButtonSet as DioxusMouseButtons,
|
MouseButton as DioxusMouseButton, MouseButtonSet as DioxusMouseButtons,
|
||||||
};
|
};
|
||||||
use dioxus_html::FormValue;
|
|
||||||
use dioxus_html::{event_bubbles, prelude::*};
|
use dioxus_html::{event_bubbles, prelude::*};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -67,7 +66,7 @@ impl EventData {
|
|||||||
pub struct FormData {
|
pub struct FormData {
|
||||||
pub(crate) value: String,
|
pub(crate) value: String,
|
||||||
|
|
||||||
pub values: HashMap<String, FormValue>,
|
pub values: HashMap<String, String>,
|
||||||
|
|
||||||
pub(crate) files: Option<Files>,
|
pub(crate) files: Option<Files>,
|
||||||
}
|
}
|
||||||
@@ -77,7 +76,7 @@ impl HasFormData for FormData {
|
|||||||
self.value.clone()
|
self.value.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn values(&self) -> HashMap<String, FormValue> {
|
fn values(&self) -> HashMap<String, String> {
|
||||||
self.values.clone()
|
self.values.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
requestIdleCallback and requestAnimationFrame implemenation
|
||||||
|
|
||||||
|
These currently actually slow down our DOM patching and thus are temporarily removed. Technically we can schedule around rIC and rAF but choose not to.
|
||||||
File renamed without changes.
File renamed without changes.
+38
-33
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
use dioxus_core::ElementId;
|
use dioxus_core::ElementId;
|
||||||
use dioxus_html::PlatformEventData;
|
use dioxus_html::PlatformEventData;
|
||||||
use dioxus_interpreter_js::unified_bindings::{Interpreter, InterpreterInterface};
|
use dioxus_interpreter_js::{unified_bindings::Interpreter, WebInterpreter};
|
||||||
use futures_channel::mpsc;
|
use futures_channel::mpsc;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use wasm_bindgen::{closure::Closure, JsCast};
|
use wasm_bindgen::{closure::Closure, JsCast};
|
||||||
@@ -17,14 +17,16 @@ use web_sys::{Document, Element, Event};
|
|||||||
use crate::{load_document, virtual_event_from_websys_event, Config, WebEventConverter};
|
use crate::{load_document, virtual_event_from_websys_event, Config, WebEventConverter};
|
||||||
|
|
||||||
pub struct WebsysDom {
|
pub struct WebsysDom {
|
||||||
pub(crate) document: Document,
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) root: Element,
|
pub(crate) root: Element,
|
||||||
|
pub(crate) document: Document,
|
||||||
pub(crate) templates: FxHashMap<String, u16>,
|
pub(crate) templates: FxHashMap<String, u16>,
|
||||||
pub(crate) max_template_id: u16,
|
pub(crate) max_template_id: u16,
|
||||||
pub(crate) interpreter: InterpreterInterface,
|
pub(crate) interpreter: Interpreter,
|
||||||
|
|
||||||
#[cfg(feature = "mounted")]
|
#[cfg(feature = "mounted")]
|
||||||
pub(crate) event_channel: mpsc::UnboundedSender<UiEvent>,
|
pub(crate) event_channel: mpsc::UnboundedSender<UiEvent>,
|
||||||
|
|
||||||
#[cfg(feature = "mounted")]
|
#[cfg(feature = "mounted")]
|
||||||
pub(crate) queued_mounted_events: Vec<ElementId>,
|
pub(crate) queued_mounted_events: Vec<ElementId>,
|
||||||
}
|
}
|
||||||
@@ -64,7 +66,7 @@ impl WebsysDom {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let interpreter = InterpreterInterface::default();
|
let interpreter = Interpreter::default();
|
||||||
|
|
||||||
let handler: Closure<dyn FnMut(&Event)> = Closure::wrap(Box::new({
|
let handler: Closure<dyn FnMut(&Event)> = Closure::wrap(Box::new({
|
||||||
let event_channel = event_channel.clone();
|
let event_channel = event_channel.clone();
|
||||||
@@ -72,42 +74,45 @@ impl WebsysDom {
|
|||||||
let name = event.type_();
|
let name = event.type_();
|
||||||
let element = walk_event_for_id(event);
|
let element = walk_event_for_id(event);
|
||||||
let bubbles = dioxus_html::event_bubbles(name.as_str());
|
let bubbles = dioxus_html::event_bubbles(name.as_str());
|
||||||
if let Some((element, target)) = element {
|
|
||||||
let prevent_event;
|
|
||||||
if let Some(prevent_requests) = target
|
|
||||||
.get_attribute("dioxus-prevent-default")
|
|
||||||
.as_deref()
|
|
||||||
.map(|f| f.split_whitespace())
|
|
||||||
{
|
|
||||||
prevent_event = prevent_requests
|
|
||||||
.map(|f| f.trim_start_matches("on"))
|
|
||||||
.any(|f| f == name);
|
|
||||||
} else {
|
|
||||||
prevent_event = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent forms from submitting and redirecting
|
let Some((element, target)) = element else {
|
||||||
if name == "submit" {
|
return;
|
||||||
// On forms the default behavior is not to submit, if prevent default is set then we submit the form
|
};
|
||||||
if !prevent_event {
|
|
||||||
event.prevent_default();
|
let prevent_event;
|
||||||
}
|
if let Some(prevent_requests) = target
|
||||||
} else if prevent_event {
|
.get_attribute("dioxus-prevent-default")
|
||||||
|
.as_deref()
|
||||||
|
.map(|f| f.split_whitespace())
|
||||||
|
{
|
||||||
|
prevent_event = prevent_requests
|
||||||
|
.map(|f| f.trim_start_matches("on"))
|
||||||
|
.any(|f| f == name);
|
||||||
|
} else {
|
||||||
|
prevent_event = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent forms from submitting and redirecting
|
||||||
|
if name == "submit" {
|
||||||
|
// On forms the default behavior is not to submit, if prevent default is set then we submit the form
|
||||||
|
if !prevent_event {
|
||||||
event.prevent_default();
|
event.prevent_default();
|
||||||
}
|
}
|
||||||
|
} else if prevent_event {
|
||||||
let data = virtual_event_from_websys_event(event.clone(), target);
|
event.prevent_default();
|
||||||
let _ = event_channel.unbounded_send(UiEvent {
|
|
||||||
name,
|
|
||||||
bubbles,
|
|
||||||
element,
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let data = virtual_event_from_websys_event(event.clone(), target);
|
||||||
|
let _ = event_channel.unbounded_send(UiEvent {
|
||||||
|
name,
|
||||||
|
bubbles,
|
||||||
|
element,
|
||||||
|
data,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let _interpreter: &Interpreter = interpreter.as_ref();
|
let _interpreter: &WebInterpreter = interpreter.as_web();
|
||||||
_interpreter.initialize(
|
_interpreter.initialize(
|
||||||
root.clone().unchecked_into(),
|
root.clone().unchecked_into(),
|
||||||
handler.as_ref().unchecked_ref(),
|
handler.as_ref().unchecked_ref(),
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ use dioxus_html::{
|
|||||||
point_interaction::{
|
point_interaction::{
|
||||||
InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction,
|
InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction,
|
||||||
},
|
},
|
||||||
prelude::FormValue,
|
|
||||||
DragData, FileEngine, FormData, HasDragData, HasFileData, HasFormData, HasImageData,
|
DragData, FileEngine, FormData, HasDragData, HasFileData, HasFormData, HasImageData,
|
||||||
HasMouseData, HtmlEventConverter, ImageData, MountedData, PlatformEventData, ScrollData,
|
HasMouseData, HtmlEventConverter, ImageData, MountedData, PlatformEventData, ScrollData,
|
||||||
};
|
};
|
||||||
@@ -385,24 +384,14 @@ impl HasFormData for WebFormData {
|
|||||||
.expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener")
|
.expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn values(&self) -> HashMap<String, FormValue> {
|
fn values(&self) -> HashMap<String, String> {
|
||||||
let mut values = HashMap::new();
|
let mut values = HashMap::new();
|
||||||
|
|
||||||
fn insert_value(map: &mut HashMap<String, FormValue>, key: String, new_value: String) {
|
fn insert_value(map: &mut HashMap<String, String>, key: String, new_value: String) {
|
||||||
match map.entry(key) {
|
if let Some(value) = map.get(&key) {
|
||||||
std::collections::hash_map::Entry::Occupied(mut o) => {
|
map.insert(key, format!("{},{}", value, new_value));
|
||||||
let first_value = match o.get_mut() {
|
} else {
|
||||||
FormValue::Text(data) => std::mem::take(data),
|
map.insert(key, new_value);
|
||||||
FormValue::VecText(vec) => {
|
|
||||||
vec.push(new_value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let _ = o.insert(FormValue::VecText(vec![first_value, new_value]));
|
|
||||||
}
|
|
||||||
std::collections::hash_map::Entry::Vacant(v) => {
|
|
||||||
let _ = v.insert(FormValue::Text(new_value));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,8 +414,8 @@ impl HasFormData for WebFormData {
|
|||||||
}
|
}
|
||||||
} else if let Some(select) = self.element.dyn_ref::<web_sys::HtmlSelectElement>() {
|
} else if let Some(select) = self.element.dyn_ref::<web_sys::HtmlSelectElement>() {
|
||||||
// try to fill in select element values
|
// try to fill in select element values
|
||||||
let options = get_select_data(select);
|
let options = get_select_data(select).join(",");
|
||||||
values.insert("options".to_string(), FormValue::VecText(options));
|
values.insert("options".to_string(), options);
|
||||||
}
|
}
|
||||||
|
|
||||||
values
|
values
|
||||||
|
|||||||
+11
-11
@@ -30,16 +30,21 @@ use futures_util::{pin_mut, select, FutureExt, StreamExt};
|
|||||||
|
|
||||||
mod cfg;
|
mod cfg;
|
||||||
mod dom;
|
mod dom;
|
||||||
#[cfg(feature = "eval")]
|
|
||||||
mod eval;
|
|
||||||
mod event;
|
mod event;
|
||||||
pub mod launch;
|
pub mod launch;
|
||||||
mod mutations;
|
mod mutations;
|
||||||
pub use event::*;
|
pub use event::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "eval")]
|
||||||
|
mod eval;
|
||||||
|
|
||||||
#[cfg(feature = "file_engine")]
|
#[cfg(feature = "file_engine")]
|
||||||
mod file_engine;
|
mod file_engine;
|
||||||
|
|
||||||
#[cfg(all(feature = "hot_reload", debug_assertions))]
|
#[cfg(all(feature = "hot_reload", debug_assertions))]
|
||||||
mod hot_reload;
|
mod hot_reload;
|
||||||
|
|
||||||
#[cfg(feature = "hydrate")]
|
#[cfg(feature = "hydrate")]
|
||||||
mod rehydrate;
|
mod rehydrate;
|
||||||
|
|
||||||
@@ -49,7 +54,7 @@ mod rehydrate;
|
|||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```ignore
|
/// ```rust, ignore
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app_fut = dioxus_web::run_with_props(App, RootProps { name: String::from("joe") });
|
/// let app_fut = dioxus_web::run_with_props(App, RootProps { name: String::from("joe") });
|
||||||
/// wasm_bindgen_futures::spawn_local(app_fut);
|
/// wasm_bindgen_futures::spawn_local(app_fut);
|
||||||
@@ -61,12 +66,7 @@ pub async fn run(virtual_dom: VirtualDom, web_config: Config) {
|
|||||||
let mut dom = virtual_dom;
|
let mut dom = virtual_dom;
|
||||||
|
|
||||||
#[cfg(feature = "eval")]
|
#[cfg(feature = "eval")]
|
||||||
{
|
dom.in_runtime(|| eval::init_eval());
|
||||||
// Eval
|
|
||||||
dom.in_runtime(|| {
|
|
||||||
eval::init_eval();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "panic_hook")]
|
#[cfg(feature = "panic_hook")]
|
||||||
if web_config.default_panic_hook {
|
if web_config.default_panic_hook {
|
||||||
@@ -109,13 +109,12 @@ pub async fn run(virtual_dom: VirtualDom, web_config: Config) {
|
|||||||
websys_dom.mount();
|
websys_dom.mount();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
tracing::trace!("waiting for work");
|
|
||||||
|
|
||||||
// if virtual dom has nothing, wait for it to have something before requesting idle time
|
// if virtual dom has nothing, wait for it to have something before requesting idle time
|
||||||
// if there is work then this future resolves immediately.
|
// if there is work then this future resolves immediately.
|
||||||
let (mut res, template) = {
|
let (mut res, template) = {
|
||||||
let work = dom.wait_for_work().fuse();
|
let work = dom.wait_for_work().fuse();
|
||||||
pin_mut!(work);
|
pin_mut!(work);
|
||||||
|
|
||||||
let mut rx_next = rx.select_next_some();
|
let mut rx_next = rx.select_next_some();
|
||||||
|
|
||||||
#[cfg(all(feature = "hot_reload", debug_assertions))]
|
#[cfg(all(feature = "hot_reload", debug_assertions))]
|
||||||
@@ -127,6 +126,7 @@ pub async fn run(virtual_dom: VirtualDom, web_config: Config) {
|
|||||||
evt = rx_next => (Some(evt), None),
|
evt = rx_next => (Some(evt), None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(all(feature = "hot_reload", debug_assertions)))]
|
#[cfg(not(all(feature = "hot_reload", debug_assertions)))]
|
||||||
select! {
|
select! {
|
||||||
_ = work => (None, None),
|
_ = work => (None, None),
|
||||||
|
|||||||
@@ -5,9 +5,7 @@ use dioxus_core::WriteMutations;
|
|||||||
use dioxus_core::{AttributeValue, ElementId};
|
use dioxus_core::{AttributeValue, ElementId};
|
||||||
use dioxus_html::event_bubbles;
|
use dioxus_html::event_bubbles;
|
||||||
use dioxus_html::PlatformEventData;
|
use dioxus_html::PlatformEventData;
|
||||||
use dioxus_interpreter_js::get_node;
|
|
||||||
use dioxus_interpreter_js::minimal_bindings;
|
use dioxus_interpreter_js::minimal_bindings;
|
||||||
use dioxus_interpreter_js::save_template;
|
|
||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
use wasm_bindgen::JsValue;
|
use wasm_bindgen::JsValue;
|
||||||
|
|
||||||
@@ -58,19 +56,23 @@ impl WebsysDom {
|
|||||||
|
|
||||||
pub fn flush_edits(&mut self) {
|
pub fn flush_edits(&mut self) {
|
||||||
self.interpreter.flush();
|
self.interpreter.flush();
|
||||||
#[cfg(feature = "mounted")]
|
|
||||||
// Now that we've flushed the edits and the dom nodes exist, we can send the mounted events.
|
// Now that we've flushed the edits and the dom nodes exist, we can send the mounted events.
|
||||||
{
|
#[cfg(feature = "mounted")]
|
||||||
for id in self.queued_mounted_events.drain(..) {
|
self.flush_queued_mounted_events();
|
||||||
let node = get_node(self.interpreter.js_channel(), id.0 as u32);
|
}
|
||||||
if let Some(element) = node.dyn_ref::<web_sys::Element>() {
|
|
||||||
let _ = self.event_channel.unbounded_send(UiEvent {
|
#[cfg(feature = "mounted")]
|
||||||
name: "mounted".to_string(),
|
fn flush_queued_mounted_events(&mut self) {
|
||||||
bubbles: false,
|
for id in self.queued_mounted_events.drain(..) {
|
||||||
element: id,
|
let node = self.interpreter.as_web().get_node(id.0 as u32);
|
||||||
data: PlatformEventData::new(Box::new(element.clone())),
|
if let Some(element) = node.dyn_ref::<web_sys::Element>() {
|
||||||
});
|
let _ = self.event_channel.unbounded_send(UiEvent {
|
||||||
}
|
name: "mounted".to_string(),
|
||||||
|
bubbles: false,
|
||||||
|
element: id,
|
||||||
|
data: PlatformEventData::new(Box::new(element.clone())),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,14 +86,14 @@ impl WebsysDom {
|
|||||||
impl WriteMutations for WebsysDom {
|
impl WriteMutations for WebsysDom {
|
||||||
fn register_template(&mut self, template: Template) {
|
fn register_template(&mut self, template: Template) {
|
||||||
let mut roots = vec![];
|
let mut roots = vec![];
|
||||||
|
|
||||||
for root in template.roots {
|
for root in template.roots {
|
||||||
roots.push(self.create_template_node(root))
|
roots.push(self.create_template_node(root))
|
||||||
}
|
}
|
||||||
|
|
||||||
self.templates
|
self.templates
|
||||||
.insert(template.name.to_owned(), self.max_template_id);
|
.insert(template.name.to_owned(), self.max_template_id);
|
||||||
save_template(self.interpreter.js_channel(), roots, self.max_template_id);
|
self.interpreter
|
||||||
|
.as_web()
|
||||||
|
.save_template(roots, self.max_template_id);
|
||||||
self.max_template_id += 1
|
self.max_template_id += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,30 +186,24 @@ impl WriteMutations for WebsysDom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn create_event_listener(&mut self, name: &'static str, id: ElementId) {
|
fn create_event_listener(&mut self, name: &'static str, id: ElementId) {
|
||||||
match name {
|
// mounted events are fired immediately after the element is mounted.
|
||||||
// mounted events are fired immediately after the element is mounted.
|
if name == "mounted" {
|
||||||
"mounted" => {
|
#[cfg(feature = "mounted")]
|
||||||
#[cfg(feature = "mounted")]
|
self.send_mount_event(id);
|
||||||
self.send_mount_event(id);
|
return;
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
self.interpreter
|
|
||||||
.new_event_listener(name, id.0 as u32, event_bubbles(name) as u8);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.interpreter
|
||||||
|
.new_event_listener(name, id.0 as u32, event_bubbles(name) as u8);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_event_listener(&mut self, name: &'static str, id: ElementId) {
|
fn remove_event_listener(&mut self, name: &'static str, id: ElementId) {
|
||||||
match name {
|
if name == "mounted" {
|
||||||
"mounted" => {}
|
return;
|
||||||
_ => {
|
|
||||||
self.interpreter.remove_event_listener(
|
|
||||||
name,
|
|
||||||
id.0 as u32,
|
|
||||||
event_bubbles(name) as u8,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.interpreter
|
||||||
|
.remove_event_listener(name, id.0 as u32, event_bubbles(name) as u8);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_node(&mut self, id: ElementId) {
|
fn remove_node(&mut self, id: ElementId) {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ use dioxus_core::prelude::*;
|
|||||||
use dioxus_core::AttributeValue;
|
use dioxus_core::AttributeValue;
|
||||||
use dioxus_core::WriteMutations;
|
use dioxus_core::WriteMutations;
|
||||||
use dioxus_core::{DynamicNode, ElementId, ScopeState, TemplateNode, VNode, VirtualDom};
|
use dioxus_core::{DynamicNode, ElementId, ScopeState, TemplateNode, VNode, VirtualDom};
|
||||||
use dioxus_interpreter_js::save_template;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum RehydrationError {
|
pub enum RehydrationError {
|
||||||
@@ -23,7 +22,7 @@ impl WebsysDom {
|
|||||||
// Recursively rehydrate the dom from the VirtualDom
|
// Recursively rehydrate the dom from the VirtualDom
|
||||||
self.rehydrate_scope(root_scope, dom, &mut ids, &mut to_mount)?;
|
self.rehydrate_scope(root_scope, dom, &mut ids, &mut to_mount)?;
|
||||||
|
|
||||||
dioxus_interpreter_js::hydrate(self.interpreter.js_channel(), ids);
|
self.interpreter.as_web().hydrate(ids);
|
||||||
|
|
||||||
#[cfg(feature = "mounted")]
|
#[cfg(feature = "mounted")]
|
||||||
for id in to_mount {
|
for id in to_mount {
|
||||||
@@ -40,8 +39,7 @@ impl WebsysDom {
|
|||||||
ids: &mut Vec<u32>,
|
ids: &mut Vec<u32>,
|
||||||
to_mount: &mut Vec<ElementId>,
|
to_mount: &mut Vec<ElementId>,
|
||||||
) -> Result<(), RehydrationError> {
|
) -> Result<(), RehydrationError> {
|
||||||
let vnode = scope.root_node();
|
self.rehydrate_vnode(dom, scope.root_node(), ids, to_mount)
|
||||||
self.rehydrate_vnode(dom, vnode, ids, to_mount)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rehydrate_vnode(
|
fn rehydrate_vnode(
|
||||||
@@ -168,11 +166,10 @@ impl WriteMutations for OnlyWriteTemplates<'_> {
|
|||||||
self.0
|
self.0
|
||||||
.templates
|
.templates
|
||||||
.insert(template.name.to_owned(), self.0.max_template_id);
|
.insert(template.name.to_owned(), self.0.max_template_id);
|
||||||
save_template(
|
self.0
|
||||||
self.0.interpreter.js_channel(),
|
.interpreter
|
||||||
roots,
|
.as_web()
|
||||||
self.0.max_template_id,
|
.save_template(roots, self.0.max_template_id);
|
||||||
);
|
|
||||||
self.0.max_template_id += 1
|
self.0.max_template_id += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user