Merge branch 'upstream' into router-typesafe

This commit is contained in:
Evan Almloff committed 2023-05-31 17:16:27 -05:00
commit 92755a381d
21 files changed
+297 -94

No files matched your search

+1
View File
@@ -38,6 +38,7 @@ slab = "0.4"
futures-util = "0.3.25"
rfd = "0.11.3"
urlencoding = "2.1.2"
[target.'cfg(target_os = "ios")'.dependencies]
objc = "0.2.7"
+4 -2
View File
@@ -43,7 +43,7 @@ fn module_loader(root_name: &str) -> String {
let rootname = "{root_name}";
let root = window.document.getElementById(rootname);
if (root != null) {{
window.interpreter = new Interpreter(root);
window.interpreter = new Interpreter(root, new InterpreterConfig(true));
window.ipc.postMessage(serializeIpcMessage("initialize"));
}}
</script>
@@ -87,7 +87,9 @@ pub(super) fn desktop_handler(
}
// Else, try to serve a file from the filesystem.
let path = PathBuf::from(request.uri().path().trim_start_matches('/'));
let decoded = urlencoding::decode(request.uri().path().trim_start_matches('/'))
.expect("expected URL to be UTF-8 encoded");
let path = PathBuf::from(&*decoded);
// If the path is relative, we'll try to serve it from the assets directory.
let mut asset = get_asset_root()
+2 -9
View File
@@ -41,17 +41,10 @@ impl Shortcut {
impl ShortcutRegistry {
pub fn new<T>(target: &EventLoopWindowTarget<T>) -> Self {
let myself = Self {
Self {
manager: Rc::new(RefCell::new(ShortcutManager::new(target))),
shortcuts: Rc::new(RefCell::new(HashMap::new())),
};
// prevent CTRL+R from reloading the page which breaks apps
let _ = myself.add_shortcut(
Some(ModifiersState::CONTROL),
KeyCode::KeyR,
Box::new(|| {}),
);
myself
}
}
pub(crate) fn call_handlers(&self, id: AcceleratorId) {
+1 -1
View File
@@ -35,7 +35,7 @@ log = "0.4.14"
rand = { version = "0.8.4", features = ["small_rng"] }
criterion = "0.3.5"
thiserror = "1.0.30"
env_logger = "0.9.0"
env_logger = "0.10.0"
tokio = { version = "1.21.2", features = ["full"] }
# dioxus-edit-stream = { path = "../edit-stream" }
+6
View File
@@ -70,6 +70,12 @@ impl<T: 'static> UseAtomRef<T> {
self.value.borrow()
}
/// This is silent operation
/// call `.force_update()` manually if required
pub fn with_mut_silent(&self, cb: impl FnOnce(&mut T)) {
cb(&mut *self.write_silent())
}
pub fn write(&self) -> RefMut<T> {
self.root.force_update(self.ptr);
self.value.borrow_mut()
+4 -2
View File
@@ -13,7 +13,7 @@ keywords = ["dom", "ui", "gui", "react", "ssr", "fullstack"]
[dependencies]
# server functions
server_fn = { git = "https://github.com/leptos-rs/leptos", rev = "671b1e4a8fff7a2e05bb621ef08e87be2b18ccae", features = ["stable"] }
server_fn = { git = "https://github.com/leptos-rs/leptos", rev = "671b1e4a8fff7a2e05bb621ef08e87be2b18ccae", default-features = false, features = ["stable"] }
dioxus_server_macro = { path = "server-macro" }
# warp
@@ -56,9 +56,11 @@ dioxus-hot-reload = { path = "../hot-reload" }
web-sys = { version = "0.3.61", features = ["Window", "Document", "Element", "HtmlDocument", "Storage", "console"] }
[features]
default = ["hot-reload"]
default = ["hot-reload", "default-tls"]
hot-reload = ["serde_json", "tokio-stream", "futures-util"]
warp = ["dep:warp", "http-body", "ssr"]
axum = ["dep:axum", "tower-http", "ssr"]
salvo = ["dep:salvo", "ssr"]
ssr = ["server_fn/ssr", "tokio", "dioxus-ssr", "hyper", "http"]
default-tls = ["server_fn/default-tls"]
rustls = ["server_fn/rustls"]
+1 -1
View File
@@ -18,7 +18,7 @@ dioxus-html = { path = "../html", features = ["hot-reload-context"], version = "
interprocess = { version = "1.2.1" }
notify = "5.0.0"
chrono = "0.4.23"
chrono = { version = "0.4.24", default-features = false, features = ["clock"] }
serde_json = "1.0.91"
serde = { version = "1", features = ["derive"] }
execute = "0.2.11"
+5 -1
View File
@@ -6,10 +6,14 @@ use web_sys::Element;
#[wasm_bindgen(module = "/src/interpreter.js")]
extern "C" {
pub type InterpreterConfig;
#[wasm_bindgen(constructor)]
pub fn new(intercept_link_redirects: bool) -> InterpreterConfig;
pub type Interpreter;
#[wasm_bindgen(constructor)]
pub fn new(arg: Element) -> Interpreter;
pub fn new(arg: Element, config: InterpreterConfig) -> Interpreter;
#[wasm_bindgen(method)]
pub fn SaveTemplate(this: &Interpreter, template: JsValue);
+29 -20
View File
@@ -53,8 +53,15 @@ class ListenerMap {
}
}
class InterpreterConfig {
constructor(intercept_link_redirects) {
this.intercept_link_redirects = intercept_link_redirects;
}
}
class Interpreter {
constructor(root) {
constructor(root, config) {
this.config = config;
this.root = root;
this.listeners = new ListenerMap(root);
this.nodes = [root];
@@ -397,7 +404,7 @@ class Interpreter {
);
} else {
this.NewEventListener(edit.name, edit.id, bubbles, (event) => {
handler(event, edit.name, bubbles);
handler(event, edit.name, bubbles, this.config);
});
}
break;
@@ -407,32 +414,34 @@ class Interpreter {
// this handler is only provided on the desktop and liveview implementations since this
// method is not used by the web implementation
function handler(event, name, bubbles) {
function handler(event, name, bubbles, config) {
let target = event.target;
if (target != null) {
let preventDefaultRequests = target.getAttribute(`dioxus-prevent-default`);
if (event.type === "click") {
// todo call prevent default if it's the right type of event
let a_element = target.closest("a");
if (a_element != null) {
event.preventDefault();
if (config.intercept_link_redirects) {
let a_element = target.closest("a");
if (a_element != null) {
event.preventDefault();
let elementShouldPreventDefault =
preventDefaultRequests && preventDefaultRequests.includes(`onclick`);
let aElementShouldPreventDefault = a_element.getAttribute(
`dioxus-prevent-default`
);
let linkShouldPreventDefault =
aElementShouldPreventDefault &&
aElementShouldPreventDefault.includes(`onclick`);
let elementShouldPreventDefault =
preventDefaultRequests && preventDefaultRequests.includes(`onclick`);
let aElementShouldPreventDefault = a_element.getAttribute(
`dioxus-prevent-default`
);
let linkShouldPreventDefault =
aElementShouldPreventDefault &&
aElementShouldPreventDefault.includes(`onclick`);
if (!elementShouldPreventDefault && !linkShouldPreventDefault) {
const href = a_element.getAttribute("href");
if (href !== "" && href !== null && href !== undefined) {
window.ipc.postMessage(
serializeIpcMessage("browser_open", { href })
);
if (!elementShouldPreventDefault && !linkShouldPreventDefault) {
const href = a_element.getAttribute("href");
if (href !== "" && href !== null && href !== undefined) {
window.ipc.postMessage(
serializeIpcMessage("browser_open", { href })
);
}
}
}
}
+1 -1
View File
@@ -46,7 +46,7 @@ once_cell = "1.17.1"
# actix-ws = { version = "0.2.5", optional = true }
[dev-dependencies]
pretty_env_logger = { version = "0.4.0" }
pretty_env_logger = { version = "0.5.0" }
tokio = { version = "1.22.0", features = ["full"] }
dioxus = { path = "../dioxus", version = "0.3.0" }
warp = "0.3.3"
+1 -1
View File
@@ -8,7 +8,7 @@ function main() {
class IPC {
constructor(root) {
// connect to the websocket
window.interpreter = new Interpreter(root);
window.interpreter = new Interpreter(root, new InterpreterConfig(false));
let ws = new WebSocket(WS_ADDR);
+2 -2
View File
@@ -1,6 +1,6 @@
use convert_case::{Case, Casing};
use dioxus_rsx::{
BodyNode, CallBody, Component, Element, ElementAttr, ElementAttrNamed, IfmtInput,
BodyNode, CallBody, Component, Element, ElementAttr, ElementAttrNamed, ElementName, IfmtInput,
};
pub use html_parser::{Dom, Node};
use proc_macro2::{Ident, Span};
@@ -21,7 +21,7 @@ pub fn rsx_node_from_html(node: &Node) -> Option<BodyNode> {
Node::Text(text) => Some(BodyNode::Text(ifmt_from_text(text))),
Node::Element(el) => {
let el_name = el.name.to_case(Case::Snake);
let el_name = Ident::new(el_name.as_str(), Span::call_site());
let el_name = ElementName::Ident(Ident::new(el_name.as_str(), Span::call_site()));
let mut attributes: Vec<_> = el
.attributes
+109 -12
View File
@@ -1,9 +1,13 @@
use std::fmt::{Display, Formatter};
use super::*;
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::{quote, ToTokens, TokenStreamExt};
use syn::{
parse::{Parse, ParseBuffer, ParseStream},
punctuated::Punctuated,
spanned::Spanned,
Error, Expr, Ident, LitStr, Result, Token,
};
@@ -12,7 +16,7 @@ use syn::{
// =======================================
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub struct Element {
pub name: Ident,
pub name: ElementName,
pub key: Option<IfmtInput>,
pub attributes: Vec<ElementAttrNamed>,
pub children: Vec<BodyNode>,
@@ -22,7 +26,7 @@ pub struct Element {
impl Parse for Element {
fn parse(stream: ParseStream) -> Result<Self> {
let el_name = Ident::parse(stream)?;
let el_name = ElementName::parse(stream)?;
// parse the guts
let content: ParseBuffer;
@@ -181,7 +185,7 @@ impl ToTokens for Element {
tokens.append_all(quote! {
__cx.element(
dioxus_elements::#name,
#name,
__cx.bump().alloc([ #(#listeners),* ]),
__cx.bump().alloc([ #(#attr),* ]),
__cx.bump().alloc([ #(#children),* ]),
@@ -191,6 +195,75 @@ impl ToTokens for Element {
}
}
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub enum ElementName {
Ident(Ident),
Custom(LitStr),
}
impl ElementName {
pub(crate) fn tag_name(&self) -> TokenStream2 {
match self {
ElementName::Ident(i) => quote! { dioxus_elements::#i::TAG_NAME },
ElementName::Custom(s) => quote! { #s },
}
}
}
impl ElementName {
pub fn span(&self) -> Span {
match self {
ElementName::Ident(i) => i.span(),
ElementName::Custom(s) => s.span(),
}
}
}
impl PartialEq<&str> for ElementName {
fn eq(&self, other: &&str) -> bool {
match self {
ElementName::Ident(i) => i == *other,
ElementName::Custom(s) => s.value() == *other,
}
}
}
impl Display for ElementName {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ElementName::Ident(i) => write!(f, "{}", i),
ElementName::Custom(s) => write!(f, "{}", s.value()),
}
}
}
impl Parse for ElementName {
fn parse(stream: ParseStream) -> Result<Self> {
let raw = Punctuated::<Ident, Token![-]>::parse_separated_nonempty(stream)?;
if raw.len() == 1 {
Ok(ElementName::Ident(raw.into_iter().next().unwrap()))
} else {
let span = raw.span();
let tag = raw
.into_iter()
.map(|ident| ident.to_string())
.collect::<Vec<_>>()
.join("-");
let tag = LitStr::new(&tag, span);
Ok(ElementName::Custom(tag))
}
}
}
impl ToTokens for ElementName {
fn to_tokens(&self, tokens: &mut TokenStream2) {
match self {
ElementName::Ident(i) => tokens.append_all(quote! { dioxus_elements::#i }),
ElementName::Custom(s) => tokens.append_all(quote! { #s }),
}
}
}
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub enum ElementAttr {
/// `attribute: "value"`
@@ -234,7 +307,7 @@ impl ElementAttr {
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub struct ElementAttrNamed {
pub el_name: Ident,
pub el_name: ElementName,
pub attr: ElementAttr,
}
@@ -242,24 +315,46 @@ impl ToTokens for ElementAttrNamed {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let ElementAttrNamed { el_name, attr } = self;
tokens.append_all(match attr {
let ns = |name| match el_name {
ElementName::Ident(i) => quote! { dioxus_elements::#i::#name.1 },
ElementName::Custom(_) => quote! { None },
};
let volitile = |name| match el_name {
ElementName::Ident(_) => quote! { #el_name::#name.2 },
ElementName::Custom(_) => quote! { false },
};
let attribute = |name: &Ident| match el_name {
ElementName::Ident(_) => quote! { #el_name::#name.0 },
ElementName::Custom(_) => {
let as_string = name.to_string();
quote!(#as_string)
}
};
let attribute = match attr {
ElementAttr::AttrText { name, value } => {
let ns = ns(name);
let volitile = volitile(name);
let attribute = attribute(name);
quote! {
__cx.attr(
dioxus_elements::#el_name::#name.0,
#attribute,
#value,
dioxus_elements::#el_name::#name.1,
dioxus_elements::#el_name::#name.2
#ns,
#volitile
)
}
}
ElementAttr::AttrExpression { name, value } => {
let ns = ns(name);
let volitile = volitile(name);
let attribute = attribute(name);
quote! {
__cx.attr(
dioxus_elements::#el_name::#name.0,
#attribute,
#value,
dioxus_elements::#el_name::#name.1,
dioxus_elements::#el_name::#name.2
#ns,
#volitile
)
}
}
@@ -288,7 +383,9 @@ impl ToTokens for ElementAttrNamed {
dioxus_elements::events::#name(__cx, #tokens)
}
}
});
};
tokens.append_all(attribute);
}
}
+19 -4
View File
@@ -426,13 +426,25 @@ impl<'a> DynamicContext<'a> {
match root {
BodyNode::Element(el) => {
let el_name = &el.name;
let ns = |name| match el_name {
ElementName::Ident(i) => quote! { dioxus_elements::#i::#name },
ElementName::Custom(_) => quote! { None },
};
let static_attrs = el.attributes.iter().map(|attr| match &attr.attr {
ElementAttr::AttrText { name, value } if value.is_static() => {
let value = value.to_static().unwrap();
let ns = ns(quote!(#name.1));
let name = match el_name {
ElementName::Ident(_) => quote! { #el_name::#name.0 },
ElementName::Custom(_) => {
let as_string = name.to_string();
quote! { #as_string }
}
};
quote! {
::dioxus::core::TemplateAttribute::Static {
name: dioxus_elements::#el_name::#name.0,
namespace: dioxus_elements::#el_name::#name.1,
name: #name,
namespace: #ns,
value: #value,
// todo: we don't diff these so we never apply the volatile flag
@@ -479,10 +491,13 @@ impl<'a> DynamicContext<'a> {
let _opt = el.children.len() == 1;
let children = quote! { #(#children),* };
let ns = ns(quote!(NAME_SPACE));
let el_name = el_name.tag_name();
quote! {
::dioxus::core::TemplateNode::Element {
tag: dioxus_elements::#el_name::TAG_NAME,
namespace: dioxus_elements::#el_name::NAME_SPACE,
tag: #el_name,
namespace: #ns,
attrs: &[ #attrs ],
children: &[ #children ],
}
+9
View File
@@ -50,7 +50,16 @@ impl Parse for BodyNode {
return Ok(BodyNode::Text(stream.parse()?));
}
// if this is a dash-separated path, it's a web component (custom element)
let body_stream = stream.fork();
if let Ok(ElementName::Custom(name)) = body_stream.parse::<ElementName>() {
if name.value().contains('-') && body_stream.peek(token::Brace) {
return Ok(BodyNode::Element(stream.parse::<Element>()?));
}
}
let body_stream = stream.fork();
if let Ok(path) = body_stream.parse::<syn::Path>() {
// this is an Element if path match of:
// - one ident