improved documentation and refactored naming to be more self-explanatory

This commit is contained in:
Pascal committed 2023-12-07 16:36:20 +01:00
1 parent e4d60c8dfc
commit adfbb6c380
91 files changed
+1663 -1553

No files matched your search

+8 -7
View File
@@ -43,14 +43,15 @@ const RECT_STYLE: &str = r#"
fn app(cx: Scope) -> Element {
let events = use_ref(cx, std::collections::VecDeque::new);
let log_event = move |event: Event| {
let mut events = events.write();
let log_event =
move |event: Event| {
let mut events = events.write();
if events.len() >= MAX_EVENTS {
events.pop_front();
}
events.push_back(event);
};
if events.len() >= MAX_EVENTS {
events.pop_front();
}
events.push_back(event);
};
cx.render(rsx! (
div {
+10 -9
View File
@@ -5,16 +5,17 @@ fn main() {
}
fn app(cx: Scope) -> Element {
let login = use_callback!(cx, move |_| async move {
let res = reqwest::get("https://dog.ceo/api/breeds/list/all")
.await
.unwrap()
.text()
.await
.unwrap();
let login =
use_callback!(cx, move |_| async move {
let res = reqwest::get("https://dog.ceo/api/breeds/list/all")
.await
.unwrap()
.text()
.await
.unwrap();
println!("{res:#?}, ");
});
println!("{res:#?}, ");
});
cx.render(rsx! {
button { onclick: login, "Click me!" }
+8 -7
View File
@@ -12,14 +12,15 @@ fn app(cx: Scope) -> Element {
let window = use_window(cx);
let emails_sent = use_ref(cx, Vec::new);
let tx = use_coroutine(cx, |mut rx: UnboundedReceiver<String>| {
to_owned![emails_sent];
async move {
while let Some(message) = rx.next().await {
emails_sent.write().push(message);
let tx =
use_coroutine(cx, |mut rx: UnboundedReceiver<String>| {
to_owned![emails_sent];
async move {
while let Some(message) = rx.next().await {
emails_sent.write().push(message);
}
}
}
});
});
cx.render(rsx! {
div {
+5 -4
View File
@@ -8,10 +8,11 @@ fn main() {
fn App(cx: Scope) -> Element {
let val = use_state(cx, || "0.0001");
let num = match val.parse::<f32>() {
Err(_) => return cx.render(rsx!("Parsing failed")),
Ok(num) => num,
};
let num =
match val.parse::<f32>() {
Err(_) => return cx.render(rsx!("Parsing failed")),
Ok(num) => num,
};
cx.render(rsx! {
h1 { "The parsed value is {num}" }
+14 -13
View File
@@ -7,25 +7,26 @@ fn main() {
fn app(cx: Scope) -> Element {
let eval_provider = use_eval(cx);
let future = use_future(cx, (), |_| {
to_owned![eval_provider];
async move {
let eval = eval_provider(
r#"
let future =
use_future(cx, (), |_| {
to_owned![eval_provider];
async move {
let eval = eval_provider(
r#"
dioxus.send("Hi from JS!");
let msg = await dioxus.recv();
console.log(msg);
return "hello world";
"#,
)
.unwrap();
)
.unwrap();
eval.send("Hi from Rust!".into()).unwrap();
let res = eval.recv().await.unwrap();
println!("{:?}", eval.await);
res
}
});
eval.send("Hi from Rust!".into()).unwrap();
let res = eval.recv().await.unwrap();
println!("{:?}", eval.await);
res
}
});
match future.value() {
Some(v) => cx.render(rsx!(
+6 -5
View File
@@ -73,11 +73,12 @@ struct Files {
impl Files {
fn new() -> Self {
let mut files = Self {
path_stack: vec!["./".to_string()],
path_names: vec![],
err: None,
};
let mut files =
Self {
path_stack: vec!["./".to_string()],
path_names: vec![],
err: None,
};
files.reload_path_list();
+28 -27
View File
@@ -112,33 +112,34 @@ fn ActionButton<'a>(cx: Scope<'a, ActionButtonProps<'a>>) -> Element {
})
}
static ADJECTIVES: &[&str] = &[
"pretty",
"large",
"big",
"small",
"tall",
"short",
"long",
"handsome",
"plain",
"quaint",
"clean",
"elegant",
"easy",
"angry",
"crazy",
"helpful",
"mushy",
"odd",
"unsightly",
"adorable",
"important",
"inexpensive",
"cheap",
"expensive",
"fancy",
];
static ADJECTIVES: &[&str] =
&[
"pretty",
"large",
"big",
"small",
"tall",
"short",
"long",
"handsome",
"plain",
"quaint",
"clean",
"elegant",
"easy",
"angry",
"crazy",
"helpful",
"mushy",
"odd",
"unsightly",
"adorable",
"important",
"inexpensive",
"cheap",
"expensive",
"fancy",
];
static COLOURS: &[&str] = &[
"red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black",
+23 -19
View File
@@ -8,28 +8,32 @@ fn main() {
}
fn app(cx: Scope) -> Element {
let onsubmit = move |evt: FormEvent| {
cx.spawn(async move {
let resp = reqwest::Client::new()
.post("http://localhost:8080/login")
.form(&[
("username", &evt.values["username"]),
("password", &evt.values["password"]),
])
.send()
.await;
let onsubmit =
move |evt: FormEvent| {
cx.spawn(async move {
let resp =
reqwest::Client::new()
.post("http://localhost:8080/login")
.form(&[
("username", &evt.values["username"]),
("password", &evt.values["password"]),
])
.send()
.await;
match resp {
// Parse data from here, such as storing a response token
Ok(_data) => println!("Login successful!"),
match resp {
// Parse data from here, such as storing a response token
Ok(_data) => println!("Login successful!"),
//Handle any errors from the fetch here
Err(_err) => {
println!("Login failed - you need a login server running on localhost:8080.")
//Handle any errors from the fetch here
Err(_err) => {
println!(
"Login failed - you need a login server running on localhost:8080."
)
}
}
}
});
};
});
};
cx.render(rsx! {
h1 { "Login" }
+6 -5
View File
@@ -112,11 +112,12 @@ pub async fn exchange_refresh_token(
pub async fn log_out_url(id_token_hint: CoreIdToken) -> Result<Url, crate::errors::Error> {
let provider_metadata = init_provider_metadata().await?;
let end_session_url = provider_metadata
.additional_metadata()
.clone()
.end_session_endpoint
.unwrap();
let end_session_url =
provider_metadata
.additional_metadata()
.clone()
.end_session_endpoint
.unwrap();
let logout_request: LogoutRequest = LogoutRequest::from(end_session_url);
Ok(logout_request
.set_client_id(ClientId::new(DIOXUS_FRONT_CLIENT_ID.to_string()))
@@ -22,29 +22,31 @@ pub fn LogOut(cx: Scope<ClientProps>) -> Element {
Some(fermi_auth_token_read) => match fermi_auth_token_read.id_token.clone() {
Some(id_token) => match log_out_url_state.get() {
Some(log_out_url_result) => match log_out_url_result {
Some(uri) => match uri {
Ok(uri) => {
rsx! {
Link {
onclick: move |_| {
{
AuthTokenState::persistent_set(
fermi_auth_token,
Some(AuthTokenState::default()),
);
}
},
to: uri.to_string(),
"Log out"
Some(uri) => {
match uri {
Ok(uri) => {
rsx! {
Link {
onclick: move |_| {
{
AuthTokenState::persistent_set(
fermi_auth_token,
Some(AuthTokenState::default()),
);
}
},
to: uri.to_string(),
"Log out"
}
}
}
Err(error) => {
rsx! {
div { format!{"Failed to load disconnection url: {:?}", error} }
}
}
}
Err(error) => {
rsx! {
div { format!{"Failed to load disconnection url: {:?}", error} }
}
}
},
}
None => {
rsx! { div { "Loading... Please wait" } }
}
+14 -12
View File
@@ -13,19 +13,21 @@ fn main() {
}
fn example(cx: Scope) -> Element {
let items = use_state(cx, || {
vec![Thing {
a: "asd".to_string(),
b: 10,
}]
});
let items =
use_state(cx, || {
vec![Thing {
a: "asd".to_string(),
b: 10,
}]
});
let things = use_ref(cx, || {
vec![Thing {
a: "asd".to_string(),
b: 10,
}]
});
let things =
use_ref(cx, || {
vec![Thing {
a: "asd".to_string(),
b: 10,
}]
});
let things_list = things.read();
let mything = use_ref(cx, || Some(String::from("asd")));
+15 -15
View File
@@ -63,23 +63,23 @@ pub fn Die<'a>(cx: Scope<'a, DieProps<'a>>) -> Element {
let active_dots = &DOTS_FOR_VALUE[(value - 1) as usize];
let fill = if keep { HELD_COLOR } else { UNHELD_COLOR };
let dots = DOTS
.iter()
.zip(active_dots.iter())
.filter(|(_, &active)| active)
.map(|((x, y), _)| {
let dcx = x * OFFSET;
let dcy = y * OFFSET;
let dots =
DOTS.iter()
.zip(active_dots.iter())
.filter(|(_, &active)| active)
.map(|((x, y), _)| {
let dcx = x * OFFSET;
let dcy = y * OFFSET;
rsx! {
circle {
cx: "{dcx}",
cy: "{dcy}",
r: "{DOT_RADIUS}",
fill: "#333"
rsx! {
circle {
cx: "{dcx}",
cy: "{dcy}",
r: "{DOT_RADIUS}",
fill: "#333"
}
}
}
});
});
cx.render(rsx! {
svg {
+8 -7
View File
@@ -202,13 +202,14 @@ pub fn ListFooter<'a>(cx: Scope<'a, ListFooterProps<'a>>) -> Element {
let active_todo_count = cx.props.active_todo_count;
let active_todo_text = cx.props.active_todo_text;
let selected = |state| {
if *cx.props.filter == state {
"selected"
} else {
"false"
}
};
let selected =
|state| {
if *cx.props.filter == state {
"selected"
} else {
"false"
}
};
cx.render(rsx! {
footer { class: "footer",
+8 -7
View File
@@ -34,13 +34,14 @@ impl Writer<'_> {
for (id, line) in self.src[start.line..end.line].iter().enumerate() {
writeln!(self.out)?;
// check if this is the last line
let line = {
if id == (end.line - start.line) - 1 {
&line[..end.column]
} else {
line
}
};
let line =
{
if id == (end.line - start.line) - 1 {
&line[..end.column]
} else {
line
}
};
// trim the leading whitespace
let previous_indent = crate::leading_whitespaces(line) / 4;
+55 -54
View File
@@ -67,19 +67,21 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result<BuildResult> {
cmd.arg("--quiet")
};
let cmd = if config.custom_profile.is_some() {
let custom_profile = config.custom_profile.as_ref().unwrap();
cmd.arg("--profile").arg(custom_profile)
} else {
cmd
};
let cmd =
if config.custom_profile.is_some() {
let custom_profile = config.custom_profile.as_ref().unwrap();
cmd.arg("--profile").arg(custom_profile)
} else {
cmd
};
let cmd = if config.features.is_some() {
let features_str = config.features.as_ref().unwrap().join(" ");
cmd.arg("--features").arg(features_str)
} else {
cmd
};
let cmd =
if config.features.is_some() {
let features_str = config.features.as_ref().unwrap().join(" ");
cmd.arg("--features").arg(features_str)
} else {
cmd
};
let cmd = match executable {
ExecutableType::Binary(name) => cmd.arg("--bin").arg(name),
@@ -92,13 +94,14 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result<BuildResult> {
// [2] Establish the output directory structure
let bindgen_outdir = out_dir.join("assets").join("dioxus");
let build_profile = if config.custom_profile.is_some() {
config.custom_profile.as_ref().unwrap()
} else if config.release {
"release"
} else {
"debug"
};
let build_profile =
if config.custom_profile.is_some() {
config.custom_profile.as_ref().unwrap()
} else if config.release {
"release"
} else {
"debug"
};
let input_path = match executable {
ExecutableType::Binary(name) | ExecutableType::Lib(name) => target_dir
@@ -207,14 +210,15 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result<BuildResult> {
}
// this code will copy all public file to the output dir
let copy_options = fs_extra::dir::CopyOptions {
overwrite: true,
skip_exist: false,
buffer_size: 64000,
copy_inside: false,
content_only: false,
depth: 0,
};
let copy_options =
fs_extra::dir::CopyOptions {
overwrite: true,
skip_exist: false,
buffer_size: 64000,
copy_inside: false,
content_only: false,
depth: 0,
};
if asset_dir.is_dir() {
for entry in std::fs::read_dir(asset_dir)? {
let path = entry?.path();
@@ -287,20 +291,21 @@ pub fn build_desktop(config: &CrateConfig, _is_serve: bool) -> Result<BuildResul
};
let file_name: String;
let mut res_path = match &config.executable {
crate::ExecutableType::Binary(name) | crate::ExecutableType::Lib(name) => {
file_name = name.clone();
config.target_dir.join(release_type).join(name)
}
crate::ExecutableType::Example(name) => {
file_name = name.clone();
config
.target_dir
.join(release_type)
.join("examples")
.join(name)
}
};
let mut res_path =
match &config.executable {
crate::ExecutableType::Binary(name) | crate::ExecutableType::Lib(name) => {
file_name = name.clone();
config.target_dir.join(release_type).join(name)
}
crate::ExecutableType::Example(name) => {
file_name = name.clone();
config
.target_dir
.join(release_type)
.join("examples")
.join(name)
}
};
let target_file = if cfg!(windows) {
res_path.set_extension("exe");
@@ -396,9 +401,7 @@ fn prettier_build(cmd: subprocess::Exec) -> anyhow::Result<Vec<Diagnostic>> {
match message.level {
cargo_metadata::diagnostic::DiagnosticLevel::Error => {
return {
Err(anyhow::anyhow!(message
.rendered
.unwrap_or("Unknown".into())))
Err(anyhow::anyhow!(message.rendered.unwrap_or("Unknown".into())))
};
}
cargo_metadata::diagnostic::DiagnosticLevel::Warning => {
@@ -475,10 +478,7 @@ pub fn gen_page(config: &DioxusConfig, serve: bool) -> String {
let mut script_str = String::new();
for script in script_list {
script_str.push_str(&format!(
"<script src=\"{}\"></script>\n",
&script.to_str().unwrap(),
))
script_str.push_str(&format!("<script src=\"{}\"></script>\n", &script.to_str().unwrap(),))
}
replace_or_insert_before("{script_include}", &script_str, "</body", &mut html);
@@ -502,10 +502,11 @@ pub fn gen_page(config: &DioxusConfig, serve: bool) -> String {
html = html.replace("{base_path}", base_path);
} else {
// If not, insert the script
html = html.replace(
"</body",
&format!(
r#"<script type="module">
html =
html.replace(
"</body",
&format!(
r#"<script type="module">
import init from "/{base_path}/assets/dioxus/{app_name}.js";
init("/{base_path}/assets/dioxus/{app_name}_bg.wasm").then(wasm => {{
if (wasm.__wbindgen_start == undefined) {{
@@ -514,8 +515,8 @@ pub fn gen_page(config: &DioxusConfig, serve: bool) -> String {
}});
</script>
</body"#
),
);
),
);
}
let title = config
+3 -3
View File
@@ -34,9 +34,9 @@ pub fn crate_root() -> Result<PathBuf> {
}
None
})
.ok_or_else(|| {
Error::CargoError("Failed to find directory containing Cargo.toml".to_string())
})
.ok_or_else(
|| Error::CargoError("Failed to find directory containing Cargo.toml".to_string())
)
}
/// Checks if the directory contains `Cargo.toml`
+37 -36
View File
@@ -93,47 +93,48 @@ async fn autoformat_project(check: bool) -> Result<()> {
let mut files_to_format = vec![];
collect_rs_files(&crate_config.crate_dir, &mut files_to_format);
let counts = files_to_format
.into_iter()
.filter(|file| {
if file.components().any(|f| f.as_os_str() == "target") {
return false;
}
true
})
.map(|path| async {
let _path = path.clone();
let res = tokio::spawn(async move {
let contents = tokio::fs::read_to_string(&path).await?;
let edits = dioxus_autofmt::fmt_file(&contents);
let len = edits.len();
if !edits.is_empty() {
let out = dioxus_autofmt::apply_formats(&contents, edits);
tokio::fs::write(&path, out).await?;
let counts =
files_to_format
.into_iter()
.filter(|file| {
if file.components().any(|f| f.as_os_str() == "target") {
return false;
}
Ok(len) as Result<usize, tokio::io::Error>
true
})
.await;
.map(|path| async {
let _path = path.clone();
let res = tokio::spawn(async move {
let contents = tokio::fs::read_to_string(&path).await?;
match res {
Err(err) => {
eprintln!("error formatting file: {}\n{err}", _path.display());
None
let edits = dioxus_autofmt::fmt_file(&contents);
let len = edits.len();
if !edits.is_empty() {
let out = dioxus_autofmt::apply_formats(&contents, edits);
tokio::fs::write(&path, out).await?;
}
Ok(len) as Result<usize, tokio::io::Error>
})
.await;
match res {
Err(err) => {
eprintln!("error formatting file: {}\n{err}", _path.display());
None
}
Ok(Err(err)) => {
eprintln!("error formatting file: {}\n{err}", _path.display());
None
}
Ok(Ok(res)) => Some(res),
}
Ok(Err(err)) => {
eprintln!("error formatting file: {}\n{err}", _path.display());
None
}
Ok(Ok(res)) => Some(res),
}
})
.collect::<FuturesUnordered<_>>()
.collect::<Vec<_>>()
.await;
})
.collect::<FuturesUnordered<_>>()
.collect::<Vec<_>>()
.await;
let files_formatted: usize = counts
.into_iter()
+8 -7
View File
@@ -35,13 +35,14 @@ impl Create {
let toml_paths = [path.join("Cargo.toml"), path.join("Dioxus.toml")];
for toml_path in &toml_paths {
let toml = std::fs::read_to_string(toml_path)?;
let mut toml = toml.parse::<toml_edit::Document>().map_err(|e| {
anyhow::anyhow!(
"failed to parse toml at {}: {}",
toml_path.display(),
e.to_string()
)
})?;
let mut toml =
toml.parse::<toml_edit::Document>().map_err(|e| {
anyhow::anyhow!(
"failed to parse toml at {}: {}",
toml_path.display(),
e.to_string()
)
})?;
toml.as_table_mut().fmt();
+6 -5
View File
@@ -67,11 +67,12 @@ impl UserData for PluginFileSystem {
let file = PathBuf::from(args.0);
let target = PathBuf::from(args.1);
let tar_gz = if let Ok(v) = File::open(file) {
v
} else {
return Ok(false);
};
let tar_gz =
if let Ok(v) = File::open(file) {
v
} else {
return Ok(false);
};
let tar = GzDecoder::new(tar_gz);
let mut archive = Archive::new(tar);
+6 -5
View File
@@ -317,11 +317,12 @@ impl PluginManager {
let name = i.get::<_, String>("name").unwrap();
let loader = i.get::<_, bool>("loader").unwrap();
let text = if loader {
format!("{name} [:loader]")
} else {
name
};
let text =
if loader {
format!("{name} [:loader]")
} else {
name
};
res.push(text);
}
}
+19 -17
View File
@@ -22,12 +22,13 @@ pub fn print_console_info(
options: PrettierOptions,
web_info: Option<WebServerInfo>,
) {
if let Ok(native_clearseq) = Command::new(if cfg!(target_os = "windows") {
"cls"
} else {
"clear"
})
.output()
if let Ok(native_clearseq) =
Command::new(if cfg!(target_os = "windows") {
"cls"
} else {
"clear"
})
.output()
{
print!("{}", String::from_utf8_lossy(&native_clearseq.stdout));
} else {
@@ -46,17 +47,18 @@ pub fn print_console_info(
} else {
"Default"
};
let url_rewrite = if config
.dioxus_config
.web
.watcher
.index_on_404
.unwrap_or(false)
{
"True"
} else {
"False"
};
let url_rewrite =
if config
.dioxus_config
.web
.watcher
.index_on_404
.unwrap_or(false)
{
"True"
} else {
"False"
};
let proxies = config.dioxus_config.web.proxy.as_ref();
+6 -7
View File
@@ -165,9 +165,7 @@ async fn get_rustls(config: &CrateConfig) -> Result<Option<RustlsConfig>> {
get_rustls_without_mkcert(web_config)?
};
Ok(Some(
RustlsConfig::from_pem_file(cert_path, key_path).await?,
))
Ok(Some(RustlsConfig::from_pem_file(cert_path, key_path).await?))
}
fn get_rustls_with_mkcert(web_config: &WebHttpsConfig) -> Result<(String, String)> {
@@ -314,10 +312,11 @@ async fn setup_router(
));
// Setup routes
router = router
.route("/_dioxus/hot_reload", get(hot_reload_handler))
.layer(cors)
.layer(Extension(ws_reload));
router =
router
.route("/_dioxus/hot_reload", get(hot_reload_handler))
.layer(cors)
.layer(Extension(ws_reload));
if let Some(hot_reload) = hot_reload {
router = router.layer(Extension(hot_reload))
+26 -24
View File
@@ -162,10 +162,11 @@ impl Tool {
)
}
Self::Tailwind => {
let windows_extension = match self.target_platform() {
"windows" => ".exe",
_ => "",
};
let windows_extension =
match self.target_platform() {
"windows" => ".exe",
_ => "",
};
format!(
"https://github.com/tailwindlabs/tailwindcss/releases/download/{version}/tailwindcss-{target}-x64{optional_ext}",
version = self.tool_version(),
@@ -280,29 +281,30 @@ impl Tool {
pub fn call(&self, command: &str, args: Vec<&str>) -> anyhow::Result<Vec<u8>> {
let bin_path = tools_path().join(self.name()).join(self.bin_path());
let command_file = match self {
Tool::Binaryen => {
if cfg!(target_os = "windows") {
format!("{}.exe", command)
} else {
command.to_string()
let command_file =
match self {
Tool::Binaryen => {
if cfg!(target_os = "windows") {
format!("{}.exe", command)
} else {
command.to_string()
}
}
}
Tool::Sass => {
if cfg!(target_os = "windows") {
format!("{}.bat", command)
} else {
command.to_string()
Tool::Sass => {
if cfg!(target_os = "windows") {
format!("{}.bat", command)
} else {
command.to_string()
}
}
}
Tool::Tailwind => {
if cfg!(target_os = "windows") {
format!("{}.exe", command)
} else {
command.to_string()
Tool::Tailwind => {
if cfg!(target_os = "windows") {
format!("{}.exe", command)
} else {
command.to_string()
}
}
}
};
};
if !bin_path.join(&command_file).is_file() {
return Err(anyhow::anyhow!("Command file not found."));
@@ -67,11 +67,12 @@ fn get_props_struct(component_body: &ComponentBody) -> ItemStruct {
let struct_ident = Ident::new(&format!("{fn_ident}Props"), fn_ident.span());
let first_lifetime = if let Some(GenericParam::Lifetime(lt)) = generics.params.first() {
Some(lt)
} else {
None
};
let first_lifetime =
if let Some(GenericParam::Lifetime(lt)) = generics.params.first() {
Some(lt)
} else {
None
};
let struct_attrs = if first_lifetime.is_some() {
quote! { #[derive(Props)] }
@@ -250,16 +251,18 @@ fn get_function(component_body: &ComponentBody) -> ItemFn {
let struct_ident = Ident::new(&format!("{fn_ident}Props"), fn_ident.span());
// Skip first arg since that's the context
let struct_field_names = inputs.iter().skip(1).filter_map(|f| match f {
FnArg::Receiver(_) => unreachable!(), // ComponentBody prohibits receiver parameters.
FnArg::Typed(pt) => Some(&pt.pat),
});
let struct_field_names =
inputs.iter().skip(1).filter_map(|f| match f {
FnArg::Receiver(_) => unreachable!(), // ComponentBody prohibits receiver parameters.
FnArg::Typed(pt) => Some(&pt.pat),
});
let first_lifetime = if let Some(GenericParam::Lifetime(lt)) = generics.params.first() {
Some(lt)
} else {
None
};
let first_lifetime =
if let Some(GenericParam::Lifetime(lt)) = generics.params.first() {
Some(lt)
} else {
None
};
let (scope_lifetime, fn_generics) = if let Some(lt) = first_lifetime {
(quote! { #lt, }, generics.clone())
+107 -110
View File
@@ -41,16 +41,10 @@ pub fn impl_my_derive(ast: &syn::DeriveInput) -> Result<TokenStream, Error> {
}
}
syn::Fields::Unnamed(_) => {
return Err(Error::new(
ast.span(),
"Props is not supported for tuple structs",
))
return Err(Error::new(ast.span(), "Props is not supported for tuple structs"))
}
syn::Fields::Unit => {
return Err(Error::new(
ast.span(),
"Props is not supported for unit structs",
))
return Err(Error::new(ast.span(), "Props is not supported for unit structs"))
}
},
syn::Data::Enum(_) => {
@@ -334,10 +328,9 @@ mod field_info {
}
Ok(())
}
_ => Err(Error::new_spanned(
&assign,
format!("Unknown parameter {name:?}"),
)),
_ => {
Err(Error::new_spanned(&assign, format!("Unknown parameter {name:?}")))
}
}
}
@@ -452,12 +445,13 @@ fn type_from_inside_option(ty: &syn::Type, check_option_name: bool) -> Option<&s
if check_option_name && segment.ident != "Option" {
return None;
}
let generic_params =
if let syn::PathArguments::AngleBracketed(generic_params) = &segment.arguments {
generic_params
} else {
return None;
};
let generic_params = if let syn::PathArguments::AngleBracketed(generic_params) =
&segment.arguments
{
generic_params
} else {
return None;
};
if let syn::GenericArgument::Type(ty) = generic_params.args.first()? {
Some(ty)
} else {
@@ -562,37 +556,38 @@ mod struct_info {
}
syn::GenericParam::Const(_cnst) => None,
});
let builder_method_doc = match self.builder_attr.builder_method_doc {
Some(ref doc) => quote!(#doc),
None => {
let doc = format!(
"
let builder_method_doc =
match self.builder_attr.builder_method_doc {
Some(ref doc) => quote!(#doc),
None => {
let doc = format!(
"
Create a builder for building `{name}`.
On the builder, call {setters} to set the values of the fields.
Finally, call `.build()` to create the instance of `{name}`.
",
name = self.name,
setters = {
let mut result = String::new();
let mut is_first = true;
for field in self.included_fields() {
use std::fmt::Write;
if is_first {
is_first = false;
} else {
write!(&mut result, ", ").unwrap();
}
write!(&mut result, "`.{}(...)`", field.name).unwrap();
if field.builder_attr.default.is_some() {
write!(&mut result, "(optional)").unwrap();
name = self.name,
setters = {
let mut result = String::new();
let mut is_first = true;
for field in self.included_fields() {
use std::fmt::Write;
if is_first {
is_first = false;
} else {
write!(&mut result, ", ").unwrap();
}
write!(&mut result, "`.{}(...)`", field.name).unwrap();
if field.builder_attr.default.is_some() {
write!(&mut result, "(optional)").unwrap();
}
}
result
}
result
}
);
quote!(#doc)
}
};
);
quote!(#doc)
}
};
let builder_type_doc = if self.builder_attr.doc {
match self.builder_attr.builder_type_doc {
Some(ref doc) => quote!(#[doc = #doc]),
@@ -623,10 +618,11 @@ Finally, call `.build()` to create the instance of `{name}`.
false => quote! { self == other },
};
let is_static = match are_there_generics {
true => quote! { false },
false => quote! { true },
};
let is_static =
match are_there_generics {
true => quote! { false },
false => quote! { true },
};
Ok(quote! {
impl #impl_generics #name #ty_generics #where_clause {
@@ -711,14 +707,15 @@ Finally, call `.build()` to create the instance of `{name}`.
ref builder_name, ..
} = *self;
let descructuring = self.included_fields().map(|f| {
if f.ordinal == field.ordinal {
quote!(_)
} else {
let name = f.name;
quote!(#name)
}
});
let descructuring =
self.included_fields().map(|f| {
if f.ordinal == field.ordinal {
quote!(_)
} else {
let name = f.name;
quote!(#name)
}
});
let reconstructing = self.included_fields().map(|f| f.name);
let mut ty_generics: Vec<syn::GenericArgument> = self
@@ -785,16 +782,17 @@ Finally, call `.build()` to create the instance of `{name}`.
// NOTE: both auto_into and strip_option affect `arg_type` and `arg_expr`, but the order of
// nesting is different so we have to do this little dance.
let arg_type = if field.builder_attr.strip_option {
field.type_from_inside_option(false).ok_or_else(|| {
Error::new_spanned(
field_type,
"can't `strip_option` - field is not `Option<...>`",
)
})?
} else {
field_type
};
let arg_type =
if field.builder_attr.strip_option {
field.type_from_inside_option(false).ok_or_else(|| {
Error::new_spanned(
field_type,
"can't `strip_option` - field is not `Option<...>`",
)
})?
} else {
field_type
};
let (arg_type, arg_expr) = if field.builder_attr.auto_into {
(
quote!(impl ::core::convert::Into<#arg_type>),
@@ -880,44 +878,45 @@ Finally, call `.build()` to create the instance of `{name}`.
})
.collect();
let mut builder_generics_tuple = empty_type_tuple();
let generics = self.modify_generics(|g| {
let index_after_lifetime_in_generics = g
.params
.iter()
.filter(|arg| matches!(arg, syn::GenericParam::Lifetime(_)))
.count();
for f in self.included_fields() {
if f.builder_attr.default.is_some() {
// `f` is not mandatory - it does not have it's own fake `build` method, so `field` will need
// to warn about missing `field` whether or not `f` is set.
assert!(
f.ordinal != field.ordinal,
"`required_field_impl` called for optional field {}",
field.name
);
g.params
.insert(index_after_lifetime_in_generics, f.generic_ty_param());
builder_generics_tuple.elems.push_value(f.type_ident());
} else if f.ordinal < field.ordinal {
// Only add a `build` method that warns about missing `field` if `f` is set. If `f` is not set,
// `f`'s `build` method will warn, since it appears earlier in the argument list.
builder_generics_tuple
.elems
.push_value(f.tuplized_type_ty_param());
} else if f.ordinal == field.ordinal {
builder_generics_tuple.elems.push_value(empty_type());
} else {
// `f` appears later in the argument list after `field`, so if they are both missing we will
// show a warning for `field` and not for `f` - which means this warning should appear whether
// or not `f` is set.
g.params
.insert(index_after_lifetime_in_generics, f.generic_ty_param());
builder_generics_tuple.elems.push_value(f.type_ident());
}
let generics =
self.modify_generics(|g| {
let index_after_lifetime_in_generics = g
.params
.iter()
.filter(|arg| matches!(arg, syn::GenericParam::Lifetime(_)))
.count();
for f in self.included_fields() {
if f.builder_attr.default.is_some() {
// `f` is not mandatory - it does not have it's own fake `build` method, so `field` will need
// to warn about missing `field` whether or not `f` is set.
assert!(
f.ordinal != field.ordinal,
"`required_field_impl` called for optional field {}",
field.name
);
g.params
.insert(index_after_lifetime_in_generics, f.generic_ty_param());
builder_generics_tuple.elems.push_value(f.type_ident());
} else if f.ordinal < field.ordinal {
// Only add a `build` method that warns about missing `field` if `f` is set. If `f` is not set,
// `f`'s `build` method will warn, since it appears earlier in the argument list.
builder_generics_tuple
.elems
.push_value(f.tuplized_type_ty_param());
} else if f.ordinal == field.ordinal {
builder_generics_tuple.elems.push_value(empty_type());
} else {
// `f` appears later in the argument list after `field`, so if they are both missing we will
// show a warning for `field` and not for `f` - which means this warning should appear whether
// or not `f` is set.
g.params
.insert(index_after_lifetime_in_generics, f.generic_ty_param());
builder_generics_tuple.elems.push_value(f.type_ident());
}
builder_generics_tuple.elems.push_punct(Default::default());
}
});
builder_generics_tuple.elems.push_punct(Default::default());
}
});
let index_after_lifetime_in_generics = builder_generics
.iter()
@@ -1139,10 +1138,9 @@ Finally, call `.build()` to create the instance of `{name}`.
self.doc = true;
Ok(())
}
_ => Err(Error::new_spanned(
&assign,
format!("Unknown parameter {name:?}"),
)),
_ => {
Err(Error::new_spanned(&assign, format!("Unknown parameter {name:?}")))
}
}
}
syn::Expr::Path(path) => {
@@ -1153,10 +1151,9 @@ Finally, call `.build()` to create the instance of `{name}`.
self.doc = true;
Ok(())
}
_ => Err(Error::new_spanned(
&path,
format!("Unknown parameter {name:?}"),
)),
_ => {
Err(Error::new_spanned(&path, format!("Unknown parameter {name:?}")))
}
}
}
syn::Expr::Call(call) => {
+4 -7
View File
@@ -31,9 +31,9 @@ pub fn format_type_string(ty: &Type) -> String {
// This should always be valid syntax.
// Not Rust code, but syntax, which is the only thing that `syn` cares about.
let Ok(file_unformatted) = syn::parse_file(&format!(
"{FORMATTED_TYPE_START}{ty_unformatted}{FORMATTED_TYPE_END}"
)) else {
let Ok(file_unformatted) =
syn::parse_file(&format!("{FORMATTED_TYPE_START}{ty_unformatted}{FORMATTED_TYPE_END}"))
else {
return ty_unformatted.to_string();
};
@@ -60,10 +60,7 @@ impl DeprecatedAttribute {
/// Returns `None` if the given attribute was not a valid form of the `#[deprecated]` attribute.
pub fn from_meta(meta: &Meta) -> syn::Result<Self> {
if meta.path() != &parse_quote!(deprecated) {
return Err(syn::Error::new(
meta.span(),
"attribute path is not `deprecated`",
));
return Err(syn::Error::new(meta.span(), "attribute path is not `deprecated`"));
}
match &meta {
+8 -7
View File
@@ -36,13 +36,14 @@ fn sort_bfs(paths: &[&'static [u8]]) -> Vec<(usize, &'static [u8])> {
#[test]
#[cfg(debug_assertions)]
fn sorting() {
let r: [(usize, &[u8]); 5] = [
(0, &[0, 1]),
(1, &[0, 2]),
(2, &[1, 0]),
(3, &[1, 0, 1]),
(4, &[1, 2]),
];
let r: [(usize, &[u8]); 5] =
[
(0, &[0, 1]),
(1, &[0, 2]),
(2, &[1, 0]),
(3, &[1, 0, 1]),
(4, &[1, 2]),
];
assert_eq!(
sort_bfs(&[&[0, 1,], &[0, 2,], &[1, 0,], &[1, 0, 1,], &[1, 2,],]),
r
+3 -3
View File
@@ -531,9 +531,9 @@ impl<'a> From<&'a AttributeValue<'a>> for BorrowedAttributeValue<'a> {
}
AttributeValue::Any(value) => {
let value = value.borrow();
BorrowedAttributeValue::Any(std::cell::Ref::map(value, |value| {
&**value.as_ref().unwrap()
}))
BorrowedAttributeValue::Any(
std::cell::Ref::map(value, |value| &**value.as_ref().unwrap())
)
}
AttributeValue::None => BorrowedAttributeValue::None,
}
+5 -4
View File
@@ -267,10 +267,11 @@ impl VirtualDom {
suspended_scopes: Default::default(),
};
let root = dom.new_scope(
Box::new(VProps::new(root, |_, _| unreachable!(), root_props)),
"app",
);
let root =
dom.new_scope(
Box::new(VProps::new(root, |_, _| unreachable!(), root_props)),
"app",
);
// Unlike react, we provide a default error boundary that just renders the error as a string
root.provide_context(Rc::new(ErrorBoundary::new(ScopeId::ROOT)));
+16 -13
View File
@@ -9,20 +9,23 @@ use dioxus_core::BorrowedAttributeValue;
#[test]
fn attrs_cycle() {
let mut dom = VirtualDom::new(|cx| {
let id = cx.generation();
match cx.generation() % 2 {
0 => cx.render(rsx! {
div {}
}),
1 => cx.render(rsx! {
div {
h1 { class: "{id}", id: "{id}" }
let mut dom =
VirtualDom::new(|cx| {
let id = cx.generation();
match cx.generation() % 2 {
0 => {
cx.render(rsx! {
div {}
})
}
}),
_ => unreachable!(),
}
});
1 => cx.render(rsx! {
div {
h1 { class: "{id}", id: "{id}" }
}
}),
_ => unreachable!(),
}
});
let bump = Bump::new();
+6 -5
View File
@@ -3,11 +3,12 @@
use dioxus::prelude::*;
fn app(cx: Scope) -> Element {
let raw = match cx.generation() % 2 {
0 => "123.123",
1 => "123.123.123",
_ => unreachable!(),
};
let raw =
match cx.generation() % 2 {
0 => "123.123",
1 => "123.123.123",
_ => unreachable!(),
};
let value = raw.parse::<f32>().unwrap_or(123.123);
+25 -22
View File
@@ -11,15 +11,16 @@ use dioxus_core::ElementId;
#[test]
fn test_original_diff() {
let mut dom = VirtualDom::new(|cx| {
cx.render(rsx! {
div {
let mut dom =
VirtualDom::new(|cx| {
cx.render(rsx! {
div {
"Hello, world!"
div {
"Hello, world!"
}
}
}
})
});
})
});
let edits = dom.rebuild().santize();
@@ -82,11 +83,12 @@ fn create() {
#[test]
fn create_list() {
let mut dom = VirtualDom::new(|cx| {
cx.render(rsx! {
(0..3).map(|f| rsx!( div { "hello" } ))
})
});
let mut dom =
VirtualDom::new(|cx| {
cx.render(rsx! {
(0..3).map(|f| rsx!( div { "hello" } ))
})
});
let _edits = dom.rebuild().santize();
@@ -160,16 +162,17 @@ fn create_components() {
#[test]
fn anchors() {
let mut dom = VirtualDom::new(|cx| {
cx.render(rsx! {
if true {
rsx!( div { "hello" } )
}
if false {
rsx!( div { "goodbye" } )
}
})
});
let mut dom =
VirtualDom::new(|cx| {
cx.render(rsx! {
if true {
rsx!( div { "hello" } )
}
if false {
rsx!( div { "goodbye" } )
}
})
});
// note that the template under "false" doesn't show up since it's not loaded
let edits = dom.rebuild().santize();
+7 -6
View File
@@ -24,12 +24,13 @@ fn empty_fragment_creates_nothing() {
#[test]
fn root_fragments_work() {
let mut vdom = VirtualDom::new(|cx| {
cx.render(rsx!(
div { "hello" }
div { "goodbye" }
))
});
let mut vdom =
VirtualDom::new(|cx| {
cx.render(rsx!(
div { "hello" }
div { "goodbye" }
))
});
assert_eq!(
vdom.rebuild().edits.last().unwrap(),
+33 -30
View File
@@ -4,17 +4,18 @@ use pretty_assertions::assert_eq;
#[test]
fn list_creates_one_by_one() {
let mut dom = VirtualDom::new(|cx| {
let gen = cx.generation();
let mut dom =
VirtualDom::new(|cx| {
let gen = cx.generation();
cx.render(rsx! {
div {
(0..gen).map(|i| rsx! {
div { "{i}" }
})
}
})
});
cx.render(rsx! {
div {
(0..gen).map(|i| rsx! {
div { "{i}" }
})
}
})
});
// load the div and then assign the empty fragment as a placeholder
assert_eq!(
@@ -73,17 +74,18 @@ fn list_creates_one_by_one() {
#[test]
fn removes_one_by_one() {
let mut dom = VirtualDom::new(|cx| {
let gen = 3 - cx.generation() % 4;
let mut dom =
VirtualDom::new(|cx| {
let gen = 3 - cx.generation() % 4;
cx.render(rsx! {
div {
(0..gen).map(|i| rsx! {
div { "{i}" }
})
}
})
});
cx.render(rsx! {
div {
(0..gen).map(|i| rsx! {
div { "{i}" }
})
}
})
});
// load the div and then assign the empty fragment as a placeholder
assert_eq!(
@@ -150,16 +152,17 @@ fn removes_one_by_one() {
#[test]
fn list_shrink_multiroot() {
let mut dom = VirtualDom::new(|cx| {
cx.render(rsx! {
div {
(0..cx.generation()).map(|i| rsx! {
div { "{i}" }
div { "{i}" }
})
}
})
});
let mut dom =
VirtualDom::new(|cx| {
cx.render(rsx! {
div {
(0..cx.generation()).map(|i| rsx! {
div { "{i}" }
div { "{i}" }
})
}
})
});
assert_eq!(
dom.rebuild().santize().edits,
+59 -54
View File
@@ -8,9 +8,7 @@ fn random_ns() -> Option<&'static str> {
let namespace = rand::random::<u8>() % 2;
match namespace {
0 => None,
1 => Some(Box::leak(
format!("ns{}", rand::random::<usize>()).into_boxed_str(),
)),
1 => Some(Box::leak(format!("ns{}", rand::random::<usize>()).into_boxed_str())),
_ => unreachable!(),
}
}
@@ -22,13 +20,15 @@ fn create_random_attribute(attr_idx: &mut usize) -> TemplateAttribute<'static> {
value: Box::leak(format!("value{}", rand::random::<usize>()).into_boxed_str()),
namespace: random_ns(),
},
1 => TemplateAttribute::Dynamic {
id: {
let old_idx = *attr_idx;
*attr_idx += 1;
old_idx
},
},
1 => {
TemplateAttribute::Dynamic {
id: {
let old_idx = *attr_idx;
*attr_idx += 1;
old_idx
},
}
}
_ => unreachable!(),
}
}
@@ -73,22 +73,26 @@ fn create_random_template_node(
1 => TemplateNode::Text {
text: Box::leak(format!("{}", rand::random::<usize>()).into_boxed_str()),
},
2 => TemplateNode::DynamicText {
id: {
let old_idx = *template_idx;
*template_idx += 1;
dynamic_node_types.push(DynamicNodeType::Text);
old_idx
},
},
3 => TemplateNode::Dynamic {
id: {
let old_idx = *template_idx;
*template_idx += 1;
dynamic_node_types.push(DynamicNodeType::Other);
old_idx
},
},
2 => {
TemplateNode::DynamicText {
id: {
let old_idx = *template_idx;
*template_idx += 1;
dynamic_node_types.push(DynamicNodeType::Text);
old_idx
},
}
}
3 => {
TemplateNode::Dynamic {
id: {
let old_idx = *template_idx;
*template_idx += 1;
dynamic_node_types.push(DynamicNodeType::Other);
old_idx
},
}
}
_ => unreachable!(),
}
}
@@ -207,9 +211,9 @@ fn create_random_dynamic_node(cx: &ScopeState, depth: usize) -> DynamicNode {
fn create_random_dynamic_attr(cx: &ScopeState) -> Attribute {
let value = match rand::random::<u8>() % 7 {
0 => AttributeValue::Text(Box::leak(
format!("{}", rand::random::<usize>()).into_boxed_str(),
)),
0 => {
AttributeValue::Text(Box::leak(format!("{}", rand::random::<usize>()).into_boxed_str()))
}
1 => AttributeValue::Float(rand::random()),
2 => AttributeValue::Int(rand::random()),
3 => AttributeValue::Bool(rand::random()),
@@ -272,31 +276,32 @@ fn create_random_element(cx: Scope<DepthProps>) -> Element {
.into_boxed_str(),
));
// println!("{template:#?}");
let node = VNode {
key: None,
parent: None,
template: Cell::new(template),
root_ids: bumpalo::collections::Vec::new_in(cx.bump()).into(),
dynamic_nodes: {
let dynamic_nodes: Vec<_> = dynamic_node_types
.iter()
.map(|ty| match ty {
DynamicNodeType::Text => DynamicNode::Text(VText::new(Box::leak(
format!("{}", rand::random::<usize>()).into_boxed_str(),
))),
DynamicNodeType::Other => {
create_random_dynamic_node(cx, cx.props.depth + 1)
}
})
.collect();
cx.bump().alloc(dynamic_nodes)
},
dynamic_attrs: cx.bump().alloc(
(0..template.attr_paths.len())
.map(|_| create_random_dynamic_attr(cx))
.collect::<Vec<_>>(),
),
};
let node =
VNode {
key: None,
parent: None,
template: Cell::new(template),
root_ids: bumpalo::collections::Vec::new_in(cx.bump()).into(),
dynamic_nodes: {
let dynamic_nodes: Vec<_> = dynamic_node_types
.iter()
.map(|ty| match ty {
DynamicNodeType::Text => DynamicNode::Text(VText::new(Box::leak(
format!("{}", rand::random::<usize>()).into_boxed_str(),
))),
DynamicNodeType::Other => {
create_random_dynamic_node(cx, cx.props.depth + 1)
}
})
.collect();
cx.bump().alloc(dynamic_nodes)
},
dynamic_attrs: cx.bump().alloc(
(0..template.attr_paths.len())
.map(|_| create_random_dynamic_attr(cx))
.collect::<Vec<_>>(),
),
};
Some(node)
}
_ => None,
+6 -5
View File
@@ -34,12 +34,13 @@ fn use_inner_html(cx: &ScopeState, id: &'static str) -> Option<String> {
to_owned![value, eval_provider];
async move {
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
let html = eval_provider(&format!(
r#"let element = document.getElementById('{}');
let html =
eval_provider(&format!(
r#"let element = document.getElementById('{}');
return element.innerHTML"#,
id
))
.unwrap();
id
))
.unwrap();
if let Ok(serde_json::Value::String(html)) = html.await {
println!("html: {}", html);
value.set(Some(html));
+7 -5
View File
@@ -36,7 +36,7 @@ pub struct Config {
pub(crate) root_name: String,
pub(crate) background_color: Option<(u8, u8, u8, u8)>,
pub(crate) last_window_close_behaviour: WindowCloseBehaviour,
pub(crate) disable_default_menu_bar: bool,
pub(crate) enable_default_menu_bar: bool,
}
type DropHandler = Box<dyn Fn(&Window, FileDropEvent) -> bool>;
@@ -66,13 +66,15 @@ impl Config {
root_name: "main".to_string(),
background_color: None,
last_window_close_behaviour: WindowCloseBehaviour::LastWindowExitsApp,
disable_default_menu_bar: false,
enable_default_menu_bar: true,
}
}
/// Set whether the default menu bar should be disabled.
pub fn with_disable_default_menu_bar(mut self, disable: bool) -> Self {
self.disable_default_menu_bar = disable;
/// Set whether the default menu bar should be enabled.
///
/// > Note: `enable` is `true` by default. To disable the default menu bar pass `false`.
pub fn with_default_menu_bar(mut self, enable: bool) -> Self {
self.enable_default_menu_bar = enable;
self
}
+11 -9
View File
@@ -240,18 +240,20 @@ pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, cfg: Config)
Event::UserEvent(event) => match event.0 {
#[cfg(all(feature = "hot-reload", debug_assertions))]
EventData::HotReloadEvent(msg) => match msg {
dioxus_hot_reload::HotReloadMsg::UpdateTemplate(template) => {
for webview in webviews.values_mut() {
webview.dom.replace_template(template);
EventData::HotReloadEvent(msg) => {
match msg {
dioxus_hot_reload::HotReloadMsg::UpdateTemplate(template) => {
for webview in webviews.values_mut() {
webview.dom.replace_template(template);
poll_vdom(webview);
poll_vdom(webview);
}
}
dioxus_hot_reload::HotReloadMsg::Shutdown => {
*control_flow = ControlFlow::Exit;
}
}
dioxus_hot_reload::HotReloadMsg::Shutdown => {
*control_flow = ControlFlow::Exit;
}
},
}
EventData::CloseWindow => {
webviews.remove(&event.1);
+8 -4
View File
@@ -19,8 +19,8 @@ pub fn build(
let index_file = cfg.custom_index.clone();
let root_name = cfg.root_name.clone();
if !cfg.disable_default_menu_bar {
builder = builder.with_menu(create_default_menu_bar());
if cfg.enable_default_menu_bar {
builder = builder.with_menu(build_default_menu_bar());
}
let window = builder.with_visible(false).build(event_loop).unwrap();
@@ -103,8 +103,12 @@ pub fn build(
(webview.build().unwrap(), web_context)
}
/// Creates a standard menu bar depending on the platform
fn create_default_menu_bar() -> MenuBar {
/// Builds a standard menu bar depending on the users platform. It may be used as a starting point
/// to further customize the menu bar and pass it to a [`WindowBuilder`](tao::window::WindowBuilder).
/// > Note: The default menu bar enables macOS shortcuts like cut/copy/paste.
/// > The menu bar differs per platform because of constraints introduced
/// > by [`MenuItem`](tao::menu::MenuItem).
pub fn build_default_menu_bar() -> MenuBar {
let mut menu_bar = MenuBar::new();
// since it is uncommon on windows to have an "application menu"
@@ -40,9 +40,10 @@ fn app(cx: Scope) -> Element {
rsx!(p { "{trimmed}" })
});
let log_event = move |event: Event| {
events.write().push(event);
};
let log_event =
move |event: Event| {
events.write().push(event);
};
cx.render(rsx! {
div {
+28 -27
View File
@@ -87,33 +87,34 @@ impl Label {
}
}
static ADJECTIVES: &[&str] = &[
"pretty",
"large",
"big",
"small",
"tall",
"short",
"long",
"handsome",
"plain",
"quaint",
"clean",
"elegant",
"easy",
"angry",
"crazy",
"helpful",
"mushy",
"odd",
"unsightly",
"adorable",
"important",
"inexpensive",
"cheap",
"expensive",
"fancy",
];
static ADJECTIVES: &[&str] =
&[
"pretty",
"large",
"big",
"small",
"tall",
"short",
"long",
"handsome",
"plain",
"quaint",
"clean",
"elegant",
"easy",
"angry",
"crazy",
"helpful",
"mushy",
"odd",
"unsightly",
"adorable",
"important",
"inexpensive",
"cheap",
"expensive",
"fancy",
];
static COLOURS: &[&str] = &[
"red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black",
+28 -27
View File
@@ -62,33 +62,34 @@ impl Label {
}
}
static ADJECTIVES: &[&str] = &[
"pretty",
"large",
"big",
"small",
"tall",
"short",
"long",
"handsome",
"plain",
"quaint",
"clean",
"elegant",
"easy",
"angry",
"crazy",
"helpful",
"mushy",
"odd",
"unsightly",
"adorable",
"important",
"inexpensive",
"cheap",
"expensive",
"fancy",
];
static ADJECTIVES: &[&str] =
&[
"pretty",
"large",
"big",
"small",
"tall",
"short",
"long",
"handsome",
"plain",
"quaint",
"clean",
"elegant",
"easy",
"angry",
"crazy",
"helpful",
"mushy",
"odd",
"unsightly",
"adorable",
"important",
"inexpensive",
"cheap",
"expensive",
"fancy",
];
static COLOURS: &[&str] = &[
"red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black",
@@ -37,12 +37,13 @@ fn main() {
//To enable Private cookies for integrity, and authenticity please check the next Example.
let session_config = SessionConfig::default().with_table_name("test_table");
let auth_config = AuthConfig::<i64>::default().with_anonymous_user_id(Some(1));
let session_store = SessionStore::<SessionSqlitePool>::new(
Some(pool.clone().into()),
session_config,
)
.await
.unwrap();
let session_store =
SessionStore::<SessionSqlitePool>::new(
Some(pool.clone().into()),
session_config,
)
.await
.unwrap();
//Create the Database table for storing our Session Data.
session_store.initiate().await.unwrap();
@@ -16,9 +16,9 @@ use serde::{Deserialize, Serialize};
#[tokio::main]
async fn main() {
pre_cache_static_routes_with_props(
&ServeConfigBuilder::new_with_router(dioxus_fullstack::router::FullstackRouterConfig::<
Route,
>::default())
&ServeConfigBuilder::new_with_router(
dioxus_fullstack::router::FullstackRouterConfig::<Route>::default()
)
.assets_path("docs")
.incremental(IncrementalRendererConfig::default().static_dir("docs"))
.build(),
@@ -280,12 +280,13 @@ where
let assets_path = assets_path.into();
// Serve all files in dist folder except index.html
let dir = std::fs::read_dir(&assets_path).unwrap_or_else(|e| {
panic!(
"Couldn't read assets directory at {:?}: {}",
&assets_path, e
)
});
let dir =
std::fs::read_dir(&assets_path).unwrap_or_else(|e| {
panic!(
"Couldn't read assets directory at {:?}: {}",
&assets_path, e
)
});
for entry in dir.flatten() {
let path = entry.path();
@@ -297,9 +298,9 @@ where
.unwrap()
.iter()
.map(|segment| {
segment.to_str().unwrap_or_else(|| {
panic!("Failed to convert path segment {:?} to string", segment)
})
segment.to_str().unwrap_or_else(
|| panic!("Failed to convert path segment {:?} to string", segment)
)
})
.collect::<Vec<_>>()
.join("/");
@@ -244,12 +244,13 @@ impl DioxusRouterExt for Router {
let assets_path = assets_path.into();
// Serve all files in dist folder except index.html
let dir = std::fs::read_dir(&assets_path).unwrap_or_else(|e| {
panic!(
"Couldn't read assets directory at {:?}: {}",
&assets_path, e
)
});
let dir =
std::fs::read_dir(&assets_path).unwrap_or_else(|e| {
panic!(
"Couldn't read assets directory at {:?}: {}",
&assets_path, e
)
});
for entry in dir.flatten() {
let path = entry.path();
@@ -261,9 +262,9 @@ impl DioxusRouterExt for Router {
.unwrap()
.iter()
.map(|segment| {
segment.to_str().unwrap_or_else(|| {
panic!("Failed to convert path segment {:?} to string", segment)
})
segment.to_str().unwrap_or_else(
|| panic!("Failed to convert path segment {:?} to string", segment)
)
})
.collect::<Vec<_>>()
.join("/");
@@ -7,13 +7,14 @@ use super::HTMLDataCursor;
#[allow(unused)]
pub(crate) fn serde_from_bytes<T: DeserializeOwned>(string: &[u8]) -> Option<T> {
let decompressed = match STANDARD.decode(string) {
Ok(bytes) => bytes,
Err(err) => {
tracing::error!("Failed to decode base64: {}", err);
return None;
}
};
let decompressed =
match STANDARD.decode(string) {
Ok(bytes) => bytes,
Err(err) => {
tracing::error!("Failed to decode base64: {}", err);
return None;
}
};
match postcard::from_bytes(&decompressed) {
Ok(data) => Some(data),
+3 -5
View File
@@ -46,11 +46,9 @@ where
> + Send,
>,
> {
let fut = self.call(req).instrument(tracing::trace_span!(
"service",
"{}",
std::any::type_name::<S>()
));
let fut = self.call(req).instrument(
tracing::trace_span!("service", "{}", std::any::type_name::<S>())
);
Box::pin(async move { fut.await.map_err(|err| err.into()) })
}
}
+5 -4
View File
@@ -91,10 +91,11 @@ impl SsrRendererPool {
Ok((freshness, html))
}
Self::Incremental(pool) => {
let mut renderer =
pool.write().unwrap().pop().unwrap_or_else(|| {
incremental_pre_renderer(cfg.incremental.as_ref().unwrap())
});
let mut renderer = pool
.write()
.unwrap()
.pop()
.unwrap_or_else(|| incremental_pre_renderer(cfg.incremental.as_ref().unwrap()));
let (tx, rx) = tokio::sync::oneshot::channel();
+9 -7
View File
@@ -122,14 +122,16 @@ fn load_index_html(path: PathBuf, root_id: &'static str) -> IndexHtml {
let (pre_main, post_main) = contents.split_once(&format!("id=\"{root_id}\"")).unwrap_or_else(|| panic!("Failed to find id=\"{root_id}\" in index.html. The id is used to inject the application into the page."));
let post_main = post_main.split_once('>').unwrap_or_else(|| {
panic!("Failed to find closing > after id=\"{root_id}\" in index.html.")
});
let post_main =
post_main.split_once('>').unwrap_or_else(
|| panic!("Failed to find closing > after id=\"{root_id}\" in index.html.")
);
let (pre_main, post_main) = (
pre_main.to_string() + &format!("id=\"{root_id}\"") + post_main.0 + ">",
post_main.1.to_string(),
);
let (pre_main, post_main) =
(
pre_main.to_string() + &format!("id=\"{root_id}\"") + post_main.0 + ">",
post_main.1.to_string(),
);
IndexHtml {
pre_main,
+9 -9
View File
@@ -22,9 +22,9 @@ impl Default for DioxusServerContext {
fn default() -> Self {
Self {
shared_context: std::sync::Arc::new(std::sync::RwLock::new(anymap::Map::new())),
response_parts: std::sync::Arc::new(RwLock::new(
http::response::Response::new(()).into_parts().0,
)),
response_parts: std::sync::Arc::new(
RwLock::new(http::response::Response::new(()).into_parts().0)
),
parts: std::sync::Arc::new(RwLock::new(http::request::Request::new(()).into_parts().0)),
html_data: Arc::new(RwLock::new(HTMLData::default())),
}
@@ -45,9 +45,9 @@ mod server_fn_impl {
Self {
parts: parts.into(),
shared_context: Arc::new(RwLock::new(SendSyncAnyMap::new())),
response_parts: std::sync::Arc::new(RwLock::new(
http::response::Response::new(()).into_parts().0,
)),
response_parts: std::sync::Arc::new(
RwLock::new(http::response::Response::new(()).into_parts().0)
),
html_data: Arc::new(RwLock::new(HTMLData::default())),
}
}
@@ -221,9 +221,9 @@ impl<T: Send + Sync + Clone + 'static> FromServerContext for FromContext<T> {
type Rejection = NotFoundInServerContext<T>;
async fn from_request(req: &DioxusServerContext) -> Result<Self, Self::Rejection> {
Ok(Self(req.clone().get::<T>().ok_or_else(|| {
NotFoundInServerContext::<T>(std::marker::PhantomData::<T>)
})?))
Ok(Self(req.clone().get::<T>().ok_or_else(
|| NotFoundInServerContext::<T>(std::marker::PhantomData::<T>)
)?))
}
}
+6 -5
View File
@@ -129,11 +129,12 @@ mod tests {
use futures_util::StreamExt;
fn app(cx: Scope, name: String) -> Element {
let task = use_coroutine(cx, |mut rx: UnboundedReceiver<i32>| async move {
while let Some(msg) = rx.next().await {
println!("got message: {msg}");
}
});
let task =
use_coroutine(cx, |mut rx: UnboundedReceiver<i32>| async move {
while let Some(msg) = rx.next().await {
println!("got message: {msg}");
}
});
let task2 = use_coroutine(cx, view_task);
+25 -18
View File
@@ -226,10 +226,12 @@ impl<T> UseSharedState<T> {
pub fn read(&self) -> Ref<'_, T> {
match self.try_read() {
Ok(value) => value,
Err(message) => panic!(
"Reading the shared state failed: {}\n({:?})",
message, message
),
Err(message) => {
panic!(
"Reading the shared state failed: {}\n({:?})",
message, message
)
}
}
}
@@ -258,10 +260,12 @@ impl<T> UseSharedState<T> {
pub fn write(&self) -> RefMut<'_, T> {
match self.try_write() {
Ok(value) => value,
Err(message) => panic!(
"Writing to shared state failed: {}\n({:?})",
message, message
),
Err(message) => {
panic!(
"Writing to shared state failed: {}\n({:?})",
message, message
)
}
}
}
@@ -284,10 +288,12 @@ impl<T> UseSharedState<T> {
pub fn write_silent(&self) -> RefMut<'_, T> {
match self.try_write_silent() {
Ok(value) => value,
Err(message) => panic!(
"Writing to shared state silently failed: {}\n({:?})",
message, message
),
Err(message) => {
panic!(
"Writing to shared state silently failed: {}\n({:?})",
message, message
)
}
}
}
@@ -363,12 +369,13 @@ impl<T> PartialEq for UseSharedState<T> {
/// ```
pub fn use_shared_state_provider<T: 'static>(cx: &ScopeState, f: impl FnOnce() -> T) {
cx.use_hook(|| {
let state: ProvidedState<T> = Rc::new(RefCell::new(ProvidedStateInner {
value: f(),
notify_any: cx.schedule_update_any(),
consumers: HashSet::new(),
gen: 0,
}));
let state: ProvidedState<T> =
Rc::new(RefCell::new(ProvidedStateInner {
value: f(),
notify_any: cx.schedule_update_any(),
consumers: HashSet::new(),
gen: 0,
}));
cx.provide_context(state);
});
+25 -24
View File
@@ -35,36 +35,37 @@ pub fn use_state<T: 'static>(
cx: &ScopeState,
initial_state_fn: impl FnOnce() -> T,
) -> &UseState<T> {
let hook = cx.use_hook(move || {
let current_val = Rc::new(initial_state_fn());
let update_callback = cx.schedule_update();
let slot = Rc::new(RefCell::new(current_val.clone()));
let setter = Rc::new({
to_owned![update_callback, slot];
move |new| {
{
let mut slot = slot.borrow_mut();
let hook =
cx.use_hook(move || {
let current_val = Rc::new(initial_state_fn());
let update_callback = cx.schedule_update();
let slot = Rc::new(RefCell::new(current_val.clone()));
let setter = Rc::new({
to_owned![update_callback, slot];
move |new| {
{
let mut slot = slot.borrow_mut();
// if there's only one reference (weak or otherwise), we can just swap the values
// Typically happens when the state is set multiple times - we don't want to create a new Rc for each new value
if let Some(val) = Rc::get_mut(&mut slot) {
*val = new;
} else {
*slot = Rc::new(new);
// if there's only one reference (weak or otherwise), we can just swap the values
// Typically happens when the state is set multiple times - we don't want to create a new Rc for each new value
if let Some(val) = Rc::get_mut(&mut slot) {
*val = new;
} else {
*slot = Rc::new(new);
}
}
update_callback();
}
update_callback();
});
UseState {
current_val,
update_callback,
setter,
slot,
}
});
UseState {
current_val,
update_callback,
setter,
slot,
}
});
hook.current_val = hook.slot.borrow().clone();
hook
+3 -3
View File
@@ -37,9 +37,9 @@ async fn main() {
.route(
"/",
get(move || async move {
index_page_with_glue(&dioxus_liveview::interpreter_glue(&format!(
"ws://{addr}/ws"
)))
index_page_with_glue(
&dioxus_liveview::interpreter_glue(&format!("ws://{addr}/ws"))
)
}),
)
.route(
+8 -7
View File
@@ -112,13 +112,14 @@ impl<S> LiveViewSocket for S where
/// You might need to transform the error types of the web backend into the LiveView error type.
pub async fn run(mut vdom: VirtualDom, ws: impl LiveViewSocket) -> Result<(), LiveViewError> {
#[cfg(all(feature = "hot-reload", debug_assertions))]
let mut hot_reload_rx = {
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
dioxus_hot_reload::connect(move |template| {
let _ = tx.send(template);
});
rx
};
let mut hot_reload_rx =
{
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
dioxus_hot_reload::connect(move |template| {
let _ = tx.send(template);
});
rx
};
// Create the a proxy for query engine
let (query_tx, mut query_rx) = tokio::sync::mpsc::unbounded_channel();
+51 -42
View File
@@ -66,12 +66,13 @@ pub fn partial_derive_state(_: TokenStream, input: TokenStream) -> TokenStream {
Some(tuple) => {
let mut parent_dependencies = Vec::new();
for type_ in &tuple.elems {
let mut type_ = extract_type_path(type_).unwrap_or_else(|| {
panic!(
"ParentDependencies must be a tuple of type paths, found {}",
quote!(#type_)
)
});
let mut type_ =
extract_type_path(type_).unwrap_or_else(|| {
panic!(
"ParentDependencies must be a tuple of type paths, found {}",
quote!(#type_)
)
});
if type_ == self_path {
type_ = this_type.clone();
}
@@ -80,21 +81,24 @@ pub fn partial_derive_state(_: TokenStream, input: TokenStream) -> TokenStream {
}
parent_dependencies
}
_ => panic!(
"ParentDependencies must be a tuple, found {}",
quote!(#parent_dependencies)
),
_ => {
panic!(
"ParentDependencies must be a tuple, found {}",
quote!(#parent_dependencies)
)
}
};
let child_dependencies = match extract_tuple(child_dependencies) {
Some(tuple) => {
let mut child_dependencies = Vec::new();
for type_ in &tuple.elems {
let mut type_ = extract_type_path(type_).unwrap_or_else(|| {
panic!(
"ChildDependencies must be a tuple of type paths, found {}",
quote!(#type_)
)
});
let mut type_ =
extract_type_path(type_).unwrap_or_else(|| {
panic!(
"ChildDependencies must be a tuple of type paths, found {}",
quote!(#type_)
)
});
if type_ == self_path {
type_ = this_type.clone();
}
@@ -103,34 +107,39 @@ pub fn partial_derive_state(_: TokenStream, input: TokenStream) -> TokenStream {
}
child_dependencies
}
_ => panic!(
"ChildDependencies must be a tuple, found {}",
quote!(#child_dependencies)
),
};
let node_dependencies = match extract_tuple(node_dependencies) {
Some(tuple) => {
let mut node_dependencies = Vec::new();
for type_ in &tuple.elems {
let mut type_ = extract_type_path(type_).unwrap_or_else(|| {
panic!(
"NodeDependencies must be a tuple of type paths, found {}",
quote!(#type_)
)
});
if type_ == self_path {
type_ = this_type.clone();
}
combined_dependencies.insert(type_.clone());
node_dependencies.push(type_);
}
node_dependencies
_ => {
panic!(
"ChildDependencies must be a tuple, found {}",
quote!(#child_dependencies)
)
}
_ => panic!(
"NodeDependencies must be a tuple, found {}",
quote!(#node_dependencies)
),
};
let node_dependencies =
match extract_tuple(node_dependencies) {
Some(tuple) => {
let mut node_dependencies = Vec::new();
for type_ in &tuple.elems {
let mut type_ = extract_type_path(type_).unwrap_or_else(|| {
panic!(
"NodeDependencies must be a tuple of type paths, found {}",
quote!(#type_)
)
});
if type_ == self_path {
type_ = this_type.clone();
}
combined_dependencies.insert(type_.clone());
node_dependencies.push(type_);
}
node_dependencies
}
_ => {
panic!(
"NodeDependencies must be a tuple, found {}",
quote!(#node_dependencies)
)
}
};
combined_dependencies.insert(this_type.clone());
let combined_dependencies: Vec<_> = combined_dependencies.into_iter().collect();
+7 -6
View File
@@ -160,12 +160,13 @@ impl State<X> for Border {
_context: &SendAnyMap,
) -> bool {
// check if the node contians a border attribute
let new = Self(
node_view
.attributes()
.and_then(|mut attrs| attrs.next().map(|a| a.attribute.name == "border"))
.is_some(),
);
let new =
Self(
node_view
.attributes()
.and_then(|mut attrs| attrs.next().map(|a| a.attribute.name == "border"))
.is_some(),
);
// check if the member has changed
let changed = new != *self;
*self = new;
+7 -6
View File
@@ -151,12 +151,13 @@ impl State for Border {
_context: &SendAnyMap,
) -> bool {
// check if the node contians a border attribute
let new = Self(
node_view
.attributes()
.and_then(|mut attrs| attrs.next().map(|a| a.attribute.name == "border"))
.is_some(),
);
let new =
Self(
node_view
.attributes()
.and_then(|mut attrs| attrs.next().map(|a| a.attribute.name == "border"))
.is_some(),
);
// check if the member has changed
let changed = new != *self;
*self = new;
@@ -152,12 +152,13 @@ impl State for Border {
_context: &SendAnyMap,
) -> bool {
// check if the node contians a border attribute
let new = Self(
node_view
.attributes()
.and_then(|mut attrs| attrs.next().map(|a| a.attribute.name == "border"))
.is_some(),
);
let new =
Self(
node_view
.attributes()
.and_then(|mut attrs| attrs.next().map(|a| a.attribute.name == "border"))
.is_some(),
);
// check if the member has changed
let changed = new != *self;
*self = new;
+27 -24
View File
@@ -340,26 +340,27 @@ pub fn apply_layout_attributes_cfg(
}
Property::JustifyContent(justify, _) => {
use AlignContent::*;
style.justify_content = match justify {
align::JustifyContent::ContentDistribution(distribution) => {
match distribution {
align::ContentDistribution::SpaceBetween => Some(SpaceBetween),
align::ContentDistribution::SpaceAround => Some(SpaceAround),
align::ContentDistribution::SpaceEvenly => Some(SpaceEvenly),
_ => return,
style.justify_content =
match justify {
align::JustifyContent::ContentDistribution(distribution) => {
match distribution {
align::ContentDistribution::SpaceBetween => Some(SpaceBetween),
align::ContentDistribution::SpaceAround => Some(SpaceAround),
align::ContentDistribution::SpaceEvenly => Some(SpaceEvenly),
_ => return,
}
}
}
align::JustifyContent::ContentPosition {
value: position, ..
} => match position {
align::ContentPosition::Center => Some(Center),
align::ContentPosition::Start => Some(Start),
align::ContentPosition::FlexStart => Some(FlexStart),
align::ContentPosition::End => Some(End),
align::ContentPosition::FlexEnd => Some(FlexEnd),
},
_ => return,
};
align::JustifyContent::ContentPosition {
value: position, ..
} => match position {
align::ContentPosition::Center => Some(Center),
align::ContentPosition::Start => Some(Start),
align::ContentPosition::FlexStart => Some(FlexStart),
align::ContentPosition::End => Some(End),
align::ContentPosition::FlexEnd => Some(FlexEnd),
},
_ => return,
};
}
Property::AlignSelf(align, _) => {
use AlignItems::*;
@@ -585,11 +586,13 @@ fn convert_grid_track_size(input: grid::TrackSize) -> NonRepeatedTrackSizingFunc
convert_track_breadth_min(&min),
convert_track_breadth_max(&max),
),
grid::TrackSize::FitContent(limit) => match limit {
DimensionPercentage::Dimension(LengthValue::Px(len)) => minmax(auto(), points(len)),
DimensionPercentage::Percentage(Percentage(pct)) => minmax(auto(), percent(pct)),
_ => unimplemented!(),
},
grid::TrackSize::FitContent(limit) => {
match limit {
DimensionPercentage::Dimension(LengthValue::Px(len)) => minmax(auto(), points(len)),
DimensionPercentage::Percentage(Percentage(pct)) => minmax(auto(), percent(pct)),
_ => unimplemented!(),
}
}
}
}
@@ -488,8 +488,5 @@ fn persist_instertions_after() {
));
let text = "hello world".to_string();
let idx = iter.next(&rdom).id();
assert!(matches!(
&*rdom.get(idx).unwrap().node_type(),
NodeType::Text(text)
));
assert!(matches!(&*rdom.get(idx).unwrap().node_type(), NodeType::Text(text)));
}
+33 -29
View File
@@ -9,9 +9,7 @@ fn random_ns() -> Option<&'static str> {
let namespace = rand::random::<u8>() % 2;
match namespace {
0 => None,
1 => Some(Box::leak(
format!("ns{}", rand::random::<usize>()).into_boxed_str(),
)),
1 => Some(Box::leak(format!("ns{}", rand::random::<usize>()).into_boxed_str())),
_ => unreachable!(),
}
}
@@ -23,13 +21,15 @@ fn create_random_attribute(attr_idx: &mut usize) -> TemplateAttribute<'static> {
value: Box::leak(format!("value{}", rand::random::<usize>()).into_boxed_str()),
namespace: random_ns(),
},
1 => TemplateAttribute::Dynamic {
id: {
let old_idx = *attr_idx;
*attr_idx += 1;
old_idx
},
},
1 => {
TemplateAttribute::Dynamic {
id: {
let old_idx = *attr_idx;
*attr_idx += 1;
old_idx
},
}
}
_ => unreachable!(),
}
}
@@ -74,22 +74,26 @@ fn create_random_template_node(
1 => TemplateNode::Text {
text: Box::leak(format!("{}", rand::random::<usize>()).into_boxed_str()),
},
2 => TemplateNode::DynamicText {
id: {
let old_idx = *template_idx;
*template_idx += 1;
dynamic_node_types.push(DynamicNodeType::Text);
old_idx
},
},
3 => TemplateNode::Dynamic {
id: {
let old_idx = *template_idx;
*template_idx += 1;
dynamic_node_types.push(DynamicNodeType::Other);
old_idx
},
},
2 => {
TemplateNode::DynamicText {
id: {
let old_idx = *template_idx;
*template_idx += 1;
dynamic_node_types.push(DynamicNodeType::Text);
old_idx
},
}
}
3 => {
TemplateNode::Dynamic {
id: {
let old_idx = *template_idx;
*template_idx += 1;
dynamic_node_types.push(DynamicNodeType::Other);
old_idx
},
}
}
_ => unreachable!(),
}
}
@@ -206,9 +210,9 @@ fn create_random_dynamic_node(cx: &ScopeState, depth: usize) -> DynamicNode {
fn create_random_dynamic_attr(cx: &ScopeState) -> Attribute {
let value = match rand::random::<u8>() % 6 {
0 => AttributeValue::Text(Box::leak(
format!("{}", rand::random::<usize>()).into_boxed_str(),
)),
0 => {
AttributeValue::Text(Box::leak(format!("{}", rand::random::<usize>()).into_boxed_str()))
}
1 => AttributeValue::Float(rand::random()),
2 => AttributeValue::Int(rand::random()),
3 => AttributeValue::Bool(rand::random()),
+26 -25
View File
@@ -83,36 +83,37 @@ impl State for Focus {
_: &SendAnyMap,
) -> bool {
let new = Focus {
level: if let Some(a) = node_view
.attributes()
.and_then(|mut a| a.find(|a| a.attribute.name == "tabindex"))
{
if let Some(index) = a
.value
.as_int()
.or_else(|| a.value.as_text().and_then(|v| v.parse::<i64>().ok()))
level:
if let Some(a) = node_view
.attributes()
.and_then(|mut a| a.find(|a| a.attribute.name == "tabindex"))
{
match index.cmp(&0) {
Ordering::Less => FocusLevel::Unfocusable,
Ordering::Equal => FocusLevel::Focusable,
Ordering::Greater => {
FocusLevel::Ordered(NonZeroU16::new(index as u16).unwrap())
if let Some(index) = a
.value
.as_int()
.or_else(|| a.value.as_text().and_then(|v| v.parse::<i64>().ok()))
{
match index.cmp(&0) {
Ordering::Less => FocusLevel::Unfocusable,
Ordering::Equal => FocusLevel::Focusable,
Ordering::Greater => {
FocusLevel::Ordered(NonZeroU16::new(index as u16).unwrap())
}
}
} else {
FocusLevel::Unfocusable
}
} else if node_view
.listeners()
.and_then(
|mut listeners| listeners.any(|l| FOCUS_EVENTS.contains(&l)).then_some(())
)
.is_some()
{
FocusLevel::Focusable
} else {
FocusLevel::Unfocusable
}
} else if node_view
.listeners()
.and_then(|mut listeners| {
listeners.any(|l| FOCUS_EVENTS.contains(&l)).then_some(())
})
.is_some()
{
FocusLevel::Focusable
} else {
FocusLevel::Unfocusable
},
},
};
if *self != new {
*self = new;
+46 -44
View File
@@ -585,13 +585,14 @@ impl RinkInputHandler {
let queued_events = Rc::new(RefCell::new(Vec::new()));
let queued_events2 = Rc::downgrade(&queued_events);
let regester_event = move |evt: crossterm::event::Event| {
if let Some(evt) = get_event(evt) {
if let Some(v) = queued_events2.upgrade() {
(*v).borrow_mut().push(evt);
let regester_event =
move |evt: crossterm::event::Event| {
if let Some(evt) = get_event(evt) {
if let Some(v) = queued_events2.upgrade() {
(*v).borrow_mut().push(evt);
}
}
}
};
};
let state = Rc::new(RefCell::new(InnerInputState::create(rdom)));
@@ -680,48 +681,49 @@ fn get_event(evt: TermEvent) -> Option<(&'static str, EventData)> {
let ctrl = m.modifiers.contains(KeyModifiers::CONTROL);
let meta = false;
let get_mouse_data = |crossterm_button: Option<MouseButton>| {
let button = crossterm_button.map(|b| match b {
MouseButton::Left => DioxusMouseButton::Primary,
MouseButton::Right => DioxusMouseButton::Secondary,
MouseButton::Middle => DioxusMouseButton::Auxiliary,
});
let get_mouse_data =
|crossterm_button: Option<MouseButton>| {
let button = crossterm_button.map(|b| match b {
MouseButton::Left => DioxusMouseButton::Primary,
MouseButton::Right => DioxusMouseButton::Secondary,
MouseButton::Middle => DioxusMouseButton::Auxiliary,
});
// from https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
// from https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
// The `page` and `screen` coordinates are inconsistent with the MDN definition, as they are relative to the viewport (client), not the target element/page/screen, respectively.
// todo?
// But then, MDN defines them in terms of pixels, yet crossterm provides only row/column, and it might not be possible to get pixels. So we can't get 100% consistency anyway.
let coordinates = Coordinates::new(
ScreenPoint::new(x, y),
ClientPoint::new(x, y),
// offset x/y are set when the origin of the event is assigned to an element
ElementPoint::new(0., 0.),
PagePoint::new(x, y),
);
// The `page` and `screen` coordinates are inconsistent with the MDN definition, as they are relative to the viewport (client), not the target element/page/screen, respectively.
// todo?
// But then, MDN defines them in terms of pixels, yet crossterm provides only row/column, and it might not be possible to get pixels. So we can't get 100% consistency anyway.
let coordinates = Coordinates::new(
ScreenPoint::new(x, y),
ClientPoint::new(x, y),
// offset x/y are set when the origin of the event is assigned to an element
ElementPoint::new(0., 0.),
PagePoint::new(x, y),
);
let mut modifiers = Modifiers::empty();
if shift {
modifiers.insert(Modifiers::SHIFT);
}
if ctrl {
modifiers.insert(Modifiers::CONTROL);
}
if meta {
modifiers.insert(Modifiers::META);
}
if alt {
modifiers.insert(Modifiers::ALT);
}
let mut modifiers = Modifiers::empty();
if shift {
modifiers.insert(Modifiers::SHIFT);
}
if ctrl {
modifiers.insert(Modifiers::CONTROL);
}
if meta {
modifiers.insert(Modifiers::META);
}
if alt {
modifiers.insert(Modifiers::ALT);
}
// held mouse buttons get set later by maintaining state, as crossterm does not provide them
EventData::Mouse(MouseData::new(
coordinates,
button,
DioxusMouseButtons::empty(),
modifiers,
))
};
// held mouse buttons get set later by maintaining state, as crossterm does not provide them
EventData::Mouse(MouseData::new(
coordinates,
button,
DioxusMouseButtons::empty(),
modifiers,
))
};
let get_wheel_data = |up| {
let y = if up { -1.0 } else { 1.0 };
+6 -5
View File
@@ -195,11 +195,12 @@ impl State for TaffyLayout {
taffy.set_children(n, &child_layout).unwrap();
}
} else {
self.node = PossiblyUninitalized::Initialized(
taffy
.new_with_children(scaled_style, &child_layout)
.unwrap(),
);
self.node =
PossiblyUninitalized::Initialized(
taffy
.new_with_children(scaled_style, &child_layout)
.unwrap(),
);
changed = true;
}
}
+22 -21
View File
@@ -43,27 +43,28 @@ impl State for PreventDefault {
_: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
_: &SendAnyMap,
) -> bool {
let new = match node_view.attributes().and_then(|mut attrs| {
attrs
.find(|a| a.attribute.name == "dioxus-prevent-default")
.and_then(|a| a.value.as_text())
}) {
Some("onfocus") => PreventDefault::Focus,
Some("onkeypress") => PreventDefault::KeyPress,
Some("onkeyrelease") => PreventDefault::KeyRelease,
Some("onkeydown") => PreventDefault::KeyDown,
Some("onkeyup") => PreventDefault::KeyUp,
Some("onclick") => PreventDefault::Click,
Some("onmousedown") => PreventDefault::MouseDown,
Some("onmouseup") => PreventDefault::MouseUp,
Some("onmouseenter") => PreventDefault::MouseEnter,
Some("onmouseover") => PreventDefault::MouseOver,
Some("onmouseleave") => PreventDefault::MouseLeave,
Some("onmouseout") => PreventDefault::MouseOut,
Some("onwheel") => PreventDefault::Wheel,
Some("oncontextmenu") => PreventDefault::ContextMenu,
_ => return false,
};
let new =
match node_view.attributes().and_then(|mut attrs| {
attrs
.find(|a| a.attribute.name == "dioxus-prevent-default")
.and_then(|a| a.value.as_text())
}) {
Some("onfocus") => PreventDefault::Focus,
Some("onkeypress") => PreventDefault::KeyPress,
Some("onkeyrelease") => PreventDefault::KeyRelease,
Some("onkeydown") => PreventDefault::KeyDown,
Some("onkeyup") => PreventDefault::KeyUp,
Some("onclick") => PreventDefault::Click,
Some("onmousedown") => PreventDefault::MouseDown,
Some("onmouseup") => PreventDefault::MouseUp,
Some("onmouseenter") => PreventDefault::MouseEnter,
Some("onmouseover") => PreventDefault::MouseOver,
Some("onmouseleave") => PreventDefault::MouseLeave,
Some("onmouseout") => PreventDefault::MouseOut,
Some("onwheel") => PreventDefault::Wheel,
Some("oncontextmenu") => PreventDefault::ContextMenu,
_ => return false,
};
if new == *self {
false
} else {
+18 -16
View File
@@ -350,24 +350,26 @@ pub fn convert(mode: RenderingMode, c: Color) -> Color {
let rgb = to_rgb(c);
Color::Rgb(rgb[0], rgb[1], rgb[2])
}
crate::RenderingMode::Ansi => match c {
Color::Indexed(_) => c,
_ => {
let rgb = to_rgb(c);
// 16-231: 6 × 6 × 6 color cube
// 232-255: 23 step grayscale
if rgb[0] == rgb[1] && rgb[1] == rgb[2] {
let idx = 232 + (rgb[0] as u16 * 23 / 255) as u8;
Color::Indexed(idx)
} else {
let r = (rgb[0] as u16 * 5) / 255;
let g = (rgb[1] as u16 * 5) / 255;
let b = (rgb[2] as u16 * 5) / 255;
let idx = 16 + r * 36 + g * 6 + b;
Color::Indexed(idx as u8)
crate::RenderingMode::Ansi => {
match c {
Color::Indexed(_) => c,
_ => {
let rgb = to_rgb(c);
// 16-231: 6 × 6 × 6 color cube
// 232-255: 23 step grayscale
if rgb[0] == rgb[1] && rgb[1] == rgb[2] {
let idx = 232 + (rgb[0] as u16 * 23 / 255) as u8;
Color::Indexed(idx)
} else {
let r = (rgb[0] as u16 * 5) / 255;
let g = (rgb[1] as u16 * 5) / 255;
let b = (rgb[2] as u16 * 5) / 255;
let idx = 16 + r * 36 + g * 6 + b;
Color::Indexed(idx as u8)
}
}
}
},
}
}
}
}
+6 -5
View File
@@ -90,11 +90,12 @@ impl Button {
}
fn switch(&mut self, ctx: &WidgetContext, node: NodeMut) {
let data = FormData {
value: self.value.to_string(),
values: HashMap::new(),
files: None,
};
let data =
FormData {
value: self.value.to_string(),
values: HashMap::new(),
files: None,
};
ctx.send(crate::Event {
id: node.id(),
name: "input",
+16 -13
View File
@@ -236,10 +236,11 @@ impl Slider {
let id = root.id();
let rdom = root.real_dom_mut();
let world = rdom.raw_world_mut();
let taffy = {
let query: UniqueView<Query> = world.borrow().unwrap();
query.stretch.clone()
};
let taffy =
{
let query: UniqueView<Query> = world.borrow().unwrap();
query.stretch.clone()
};
let taffy = taffy.lock().unwrap();
@@ -267,10 +268,11 @@ impl CustomElement for Slider {
panic!("input must be an element")
};
let value = el.attributes.get(&OwnedAttributeDiscription {
name: "value".to_string(),
namespace: None,
});
let value =
el.attributes.get(&OwnedAttributeDiscription {
name: "value".to_string(),
namespace: None,
});
let value = value
.and_then(|value| match value {
OwnedAttributeValue::Text(text) => text.as_str().parse().ok(),
@@ -301,11 +303,12 @@ impl CustomElement for Slider {
let cursor_text = rdom.create_node("|".to_string());
let cursor_text_id = cursor_text.id();
let mut cursor_span = rdom.create_node(NodeType::Element(ElementNode {
tag: "div".to_string(),
attributes: [].into_iter().collect(),
..Default::default()
}));
let mut cursor_span =
rdom.create_node(NodeType::Element(ElementNode {
tag: "div".to_string(),
attributes: [].into_iter().collect(),
..Default::default()
}));
cursor_span.add_child(cursor_text_id);
let cursor_span_id = cursor_span.id();
+13 -11
View File
@@ -144,13 +144,14 @@ impl Route {
_ => Vec::new(),
};
let (route_segments, query) = {
parse_route_segments(
variant.ident.span(),
fields.iter().map(|f| (&f.0, &f.1)),
&route,
)?
};
let (route_segments, query) =
{
parse_route_segments(
variant.ident.span(),
fields.iter().map(|f| (&f.0, &f.1)),
&route,
)?
};
Ok(Self {
ty,
@@ -323,10 +324,11 @@ impl Route {
pub fn error_type(&self) -> TokenStream2 {
let error_name = self.error_ident();
let child_type = match &self.ty {
RouteType::Child(field) => Some(&field.ty),
RouteType::Leaf { .. } => None,
};
let child_type =
match &self.ty {
RouteType::Child(field) => Some(&field.ty),
RouteType::Leaf { .. } => None,
};
create_error_type(error_name, &self.segments, child_type)
}
+20 -17
View File
@@ -135,10 +135,11 @@ pub fn parse_route_segments<'a>(
) -> syn::Result<(Vec<RouteSegment>, Option<QuerySegment>)> {
let mut route_segments = Vec::new();
let (route_string, query) = match route.rsplit_once('?') {
Some((route, query)) => (route, Some(query)),
None => (route, None),
};
let (route_string, query) =
match route.rsplit_once('?') {
Some((route, query)) => (route, Some(query)),
None => (route, None),
};
let mut iterator = route_string.split('/');
// skip the first empty segment
@@ -157,11 +158,12 @@ pub fn parse_route_segments<'a>(
if let Some(segment) = segment.strip_prefix(':') {
let spread = segment.starts_with("..");
let ident = if spread {
segment[2..].to_string()
} else {
segment.to_string()
};
let ident =
if spread {
segment[2..].to_string()
} else {
segment.to_string()
};
let field = fields.find(|(name, _)| **name == ident);
@@ -205,14 +207,15 @@ pub fn parse_route_segments<'a>(
let query_ident = Ident::new(query, Span::call_site());
let field = fields.find(|(name, _)| *name == &query_ident);
let ty = if let Some((_, ty)) = field {
ty.clone()
} else {
return Err(syn::Error::new(
route_span,
format!("Could not find a field with the name '{}'", query_ident),
));
};
let ty =
if let Some((_, ty)) = field {
ty.clone()
} else {
return Err(syn::Error::new(
route_span,
format!("Could not find a field with the name '{}'", query_ident),
));
};
Some(QuerySegment {
ident: query_ident,
+12 -11
View File
@@ -77,17 +77,18 @@ where
self.routes.retain(|&lhs, _| lhs <= last_visited);
}
};
let state = match state {
Some(state) => {
self.current_index = state.index;
state
}
None => {
let index = depth - 1;
self.current_index = index;
State { index }
}
};
let state =
match state {
Some(state) => {
self.current_index = state.index;
state
}
None => {
let index = depth - 1;
self.current_index = index;
State { index }
}
};
self.routes.insert(state.index, route);
state
}
+18 -16
View File
@@ -302,15 +302,16 @@ where
let s = self.listener_animation_frame.clone();
let d = self.do_scroll_restoration;
self.listener_navigation = Some(EventListener::new(&self.window, "popstate", move |_| {
(*callback)();
if d {
let mut s = s.lock().expect("unpoisoned scroll mutex");
if let Some(current_state) = get_current::<WebHistoryState<R>>(&h) {
*s = Some(current_state.scroll.scroll_to(w.clone()));
self.listener_navigation =
Some(EventListener::new(&self.window, "popstate", move |_| {
(*callback)();
if d {
let mut s = s.lock().expect("unpoisoned scroll mutex");
if let Some(current_state) = get_current::<WebHistoryState<R>>(&h) {
*s = Some(current_state.scroll.scroll_to(w.clone()));
}
}
}
}));
}));
}
}
@@ -377,14 +378,15 @@ where
let s = self.listener_animation_frame.clone();
let d = self.do_scroll_restoration;
self.listener_navigation = Some(EventListener::new(&self.window, "popstate", move |_| {
(*callback)();
if d {
let mut s = s.lock().expect("unpoisoned scroll mutex");
if let Some([x, y]) = get_current(&h) {
*s = Some(ScrollPosition { x, y }.scroll_to(w.clone()));
self.listener_navigation =
Some(EventListener::new(&self.window, "popstate", move |_| {
(*callback)();
if d {
let mut s = s.lock().expect("unpoisoned scroll mutex");
if let Some([x, y]) = get_current(&h) {
*s = Some(ScrollPosition { x, y }.scroll_to(w.clone()));
}
}
}
}));
}));
}
}
+3 -5
View File
@@ -114,11 +114,9 @@ impl<R: Routable> FromStr for NavigationTarget<R> {
fn from_str(s: &str) -> Result<Self, Self::Err> {
match Url::parse(s) {
Ok(_) => Ok(Self::External(s.to_string())),
Err(ParseError::RelativeUrlWithoutBase) => {
Ok(Self::Internal(R::from_str(s).map_err(|e| {
NavigationTargetParseError::InvalidInternalURL(e)
})?))
}
Err(ParseError::RelativeUrlWithoutBase) => Ok(Self::Internal(
R::from_str(s).map_err(|e| NavigationTargetParseError::InvalidInternalURL(e))?,
)),
Err(e) => Err(NavigationTargetParseError::InvalidUrl(e)),
}
}
+6 -5
View File
@@ -134,11 +134,12 @@ impl<I: std::iter::FromIterator<String>> FromRouteSegments for I {
/// A flattened version of [`Routable::SITE_MAP`].
/// This essentially represents a `Vec<Vec<SegmentType>>`, which you can collect it into.
type SiteMapFlattened<'a> = FlatMap<
Iter<'a, SiteMapSegment>,
Vec<Vec<SegmentType>>,
fn(&SiteMapSegment) -> Vec<Vec<SegmentType>>,
>;
type SiteMapFlattened<'a> =
FlatMap<
Iter<'a, SiteMapSegment>,
Vec<Vec<SegmentType>>,
fn(&SiteMapSegment) -> Vec<Vec<SegmentType>>,
>;
fn seg_strs_to_route<T>(segs_maybe: &Option<Vec<&str>>) -> Option<T>
where
@@ -11,14 +11,15 @@ use crate::prelude::*;
/// - [`None`], when the current component isn't a descendant of a [`Link`] component.
/// - Otherwise [`Some`].
pub(crate) fn use_router_internal(cx: &ScopeState) -> &Option<RouterContext> {
let inner = cx.use_hook(|| {
let router = cx.consume_context::<RouterContext>()?;
let inner =
cx.use_hook(|| {
let router = cx.consume_context::<RouterContext>()?;
let id = cx.scope_id();
router.subscribe(id);
let id = cx.scope_id();
router.subscribe(id);
Some(Subscription { router, id })
});
Some(Subscription { router, id })
});
cx.use_hook(|| inner.as_ref().map(|s| s.router.clone()))
}
+3 -2
View File
@@ -36,7 +36,8 @@ fn simple_elements() {
#[test]
fn deeply_nested() {
let html = r#"
let html =
r#"
<div>
<div class="asd">
<div class="asd">
@@ -48,7 +49,7 @@ fn deeply_nested() {
</div>
</div>
"#
.trim();
.trim();
let dom = Dom::parse(html).unwrap();
+10 -8
View File
@@ -162,10 +162,11 @@ impl ToTokens for Component {
}
if !self.children.is_empty() {
let renderer: TemplateRenderer = TemplateRenderer {
roots: &self.children,
location: None,
};
let renderer: TemplateRenderer =
TemplateRenderer {
roots: &self.children,
location: None,
};
toks.append_all(quote! {
.children(
@@ -183,10 +184,11 @@ impl ToTokens for Component {
let fn_name = self.name.segments.last().unwrap().ident.to_string();
let gen_name = match &self.prop_gen_args {
Some(gen) => quote! { #name #gen },
None => quote! { #name },
};
let gen_name =
match &self.prop_gen_args {
Some(gen) => quote! { #name #gen },
None => quote! { #name },
};
tokens.append_all(quote! {
__cx.component(
+49 -48
View File
@@ -332,59 +332,60 @@ impl ToTokens for ElementAttrNamed {
}
};
let attribute = match attr {
ElementAttr::AttrText { name, value } => {
let ns = ns(name);
let volitile = volitile(name);
let attribute = attribute(name);
quote! {
__cx.attr(
#attribute,
#value,
#ns,
#volitile
)
let attribute =
match attr {
ElementAttr::AttrText { name, value } => {
let ns = ns(name);
let volitile = volitile(name);
let attribute = attribute(name);
quote! {
__cx.attr(
#attribute,
#value,
#ns,
#volitile
)
}
}
}
ElementAttr::AttrExpression { name, value } => {
let ns = ns(name);
let volitile = volitile(name);
let attribute = attribute(name);
quote! {
__cx.attr(
#attribute,
#value,
#ns,
#volitile
)
ElementAttr::AttrExpression { name, value } => {
let ns = ns(name);
let volitile = volitile(name);
let attribute = attribute(name);
quote! {
__cx.attr(
#attribute,
#value,
#ns,
#volitile
)
}
}
}
ElementAttr::CustomAttrText { name, value } => {
quote! {
__cx.attr(
#name,
#value,
None,
false
)
ElementAttr::CustomAttrText { name, value } => {
quote! {
__cx.attr(
#name,
#value,
None,
false
)
}
}
}
ElementAttr::CustomAttrExpression { name, value } => {
quote! {
__cx.attr(
#name,
#value,
None,
false
)
ElementAttr::CustomAttrExpression { name, value } => {
quote! {
__cx.attr(
#name,
#value,
None,
false
)
}
}
}
ElementAttr::EventTokens { name, tokens } => {
quote! {
dioxus_elements::events::#name(__cx, #tokens)
ElementAttr::EventTokens { name, tokens } => {
quote! {
dioxus_elements::events::#name(__cx, #tokens)
}
}
}
};
};
tokens.append_all(attribute);
}
@@ -77,11 +77,12 @@ impl<Ctx: HotReloadingContext> FileMap<Ctx> {
}
let FileMapSearchResult { map, errors } = find_rs_files(path, &mut filter)?;
let result = Self {
map,
in_workspace: HashMap::new(),
phantom: std::marker::PhantomData,
};
let result =
Self {
map,
in_workspace: HashMap::new(),
phantom: std::marker::PhantomData,
};
Ok(FileMapBuildResult {
map: result,
errors,
+16 -19
View File
@@ -99,10 +99,9 @@ impl FromStr for IfmtInput {
current_literal.push(c);
continue;
} else {
return Err(Error::new(
Span::call_site(),
"unmatched closing '}' in format string",
));
return Err(
Error::new(Span::call_site(), "unmatched closing '}' in format string")
);
}
}
current_literal.push(c);
@@ -147,17 +146,18 @@ impl ToTokens for IfmtInput {
}
}
let positional_args = self.segments.iter().filter_map(|seg| {
if let Segment::Formatted(FormattedSegment {
segment: FormattedSegmentType::Expr(expr),
..
}) = seg
{
Some(expr)
} else {
None
}
});
let positional_args =
self.segments.iter().filter_map(|seg| {
if let Segment::Formatted(FormattedSegment {
segment: FormattedSegmentType::Expr(expr),
..
}) = seg
{
Some(expr)
} else {
None
}
});
// remove duplicate idents
let named_args_idents: HashSet<_> = self
@@ -228,10 +228,7 @@ impl FormattedSegmentType {
if let Ok(expr) = parse_str(input) {
Ok(Self::Expr(Box::new(expr)))
} else {
Err(Error::new(
Span::call_site(),
"Expected Ident or Expression",
))
Err(Error::new(Span::call_site(), "Expected Ident or Expression"))
}
}
}
+12 -10
View File
@@ -199,10 +199,11 @@ impl<'a> ToTokens for TemplateRenderer<'a> {
_ => None,
};
let key_tokens = match key {
Some(tok) => quote! { Some( __cx.raw_text(#tok) ) },
None => quote! { None },
};
let key_tokens =
match key {
Some(tok) => quote! { Some( __cx.raw_text(#tok) ) },
None => quote! { None },
};
let spndbg = format!("{:?}", self.roots[0].span());
let root_col = spndbg
@@ -518,12 +519,13 @@ impl<'a> DynamicContext<'a> {
let attrs = quote! { #(#static_attrs),*};
let children = el.children.iter().enumerate().map(|(idx, root)| {
self.current_path.push(idx as u8);
let out = self.render_static_node(root);
self.current_path.pop();
out
});
let children =
el.children.iter().enumerate().map(|(idx, root)| {
self.current_path.push(idx as u8);
let out = self.render_static_node(root);
self.current_path.pop();
out
});
let _opt = el.children.len() == 1;
let children = quote! { #(#children),* };
+5 -4
View File
@@ -65,10 +65,11 @@ use syn::{
#[proc_macro_attribute]
pub fn server(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
// before we pass this off to the server function macro, we apply extractors and middleware
let mut function: syn::ItemFn = match syn::parse(s).map_err(|e| e.to_compile_error()) {
Ok(f) => f,
Err(e) => return e.into(),
};
let mut function: syn::ItemFn =
match syn::parse(s).map_err(|e| e.to_compile_error()) {
Ok(f) => f,
Err(e) => return e.into(),
};
// extract all #[middleware] attributes
let mut middlewares: Vec<Middleware> = vec![];
+6 -5
View File
@@ -87,11 +87,12 @@ impl Effect {
///
/// The signal will be owned by the current component and will be dropped when the component is dropped.
pub fn new(callback: impl FnMut() + 'static) -> Self {
let myself = Self {
source: current_scope_id().expect("in a virtual dom"),
callback: CopyValue::new(Box::new(callback)),
effect_stack: get_effect_stack(),
};
let myself =
Self {
source: current_scope_id().expect("in a virtual dom"),
callback: CopyValue::new(Box::new(callback)),
effect_stack: get_effect_stack(),
};
myself.try_run();
+4 -3
View File
@@ -72,9 +72,10 @@ where
///
/// Selectors can be used to efficiently compute derived data from signals.
pub fn selector<R: PartialEq>(mut f: impl FnMut() -> R + 'static) -> ReadOnlySignal<R> {
let state = Signal::<R> {
inner: CopyValue::invalid(),
};
let state =
Signal::<R> {
inner: CopyValue::invalid(),
};
let effect = Effect {
source: current_scope_id().expect("in a virtual dom"),
callback: CopyValue::invalid(),
+23 -20
View File
@@ -6,13 +6,14 @@ use dioxus_signals::*;
#[test]
fn create_signals_global() {
let mut dom = VirtualDom::new(|cx| {
render! {
for _ in 0..10 {
Child {}
let mut dom =
VirtualDom::new(|cx| {
render! {
for _ in 0..10 {
Child {}
}
}
}
});
});
fn Child(cx: Scope) -> Element {
let signal = create_without_cx();
@@ -31,13 +32,14 @@ fn create_signals_global() {
#[test]
fn deref_signal() {
let mut dom = VirtualDom::new(|cx| {
render! {
for _ in 0..10 {
Child {}
let mut dom =
VirtualDom::new(|cx| {
render! {
for _ in 0..10 {
Child {}
}
}
}
});
});
fn Child(cx: Scope) -> Element {
let signal = Signal::new("hello world".to_string());
@@ -55,16 +57,17 @@ fn deref_signal() {
#[test]
fn drop_signals() {
let mut dom = VirtualDom::new(|cx| {
let generation = cx.generation();
let mut dom =
VirtualDom::new(|cx| {
let generation = cx.generation();
let count = if generation % 2 == 0 { 10 } else { 0 };
render! {
for _ in 0..count {
Child {}
let count = if generation % 2 == 0 { 10 } else { 0 };
render! {
for _ in 0..count {
Child {}
}
}
}
});
});
fn Child(cx: Scope) -> Element {
let signal = create_without_cx();
+9 -8
View File
@@ -100,14 +100,15 @@ fn memos_prevents_component_rerun() {
let signal = cx.props.signal;
counter.borrow_mut().component += 1;
let memo = cx.use_hook(move || {
to_owned![counter];
selector(move || {
counter.borrow_mut().effect += 1;
println!("Signal: {:?}", signal);
signal.value()
})
});
let memo =
cx.use_hook(move || {
to_owned![counter];
selector(move || {
counter.borrow_mut().effect += 1;
println!("Signal: {:?}", signal);
signal.value()
})
});
match cx.generation() {
0 => {
assert_eq!(memo.value(), 0);
+301 -300
View File
@@ -1,300 +1,301 @@
pub static BUILTIN_INTERNED_STRINGS: &[&str] = &[
// Important tags to dioxus
"dioxus-id",
"dioxus",
"dioxus-event-click", // todo: more events
"click",
// All the HTML Tags
"a",
"abbr",
"address",
"area",
"article",
"aside",
"audio",
"b",
"base",
"bdi",
"bdo",
"big",
"blockquote",
"body",
"br",
"button",
"canvas",
"caption",
"cite",
"code",
"col",
"colgroup",
"command",
"data",
"datalist",
"dd",
"del",
"details",
"dfn",
"dialog",
"div",
"dl",
"dt",
"em",
"embed",
"fieldset",
"figcaption",
"figure",
"footer",
"form",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"head",
"header",
"hr",
"html",
"i",
"iframe",
"img",
"input",
"ins",
"kbd",
"keygen",
"label",
"legend",
"li",
"link",
"main",
"map",
"mark",
"menu",
"menuitem",
"meta",
"meter",
"nav",
"noscript",
"object",
"ol",
"optgroup",
"option",
"output",
"p",
"param",
"picture",
"pre",
"progress",
"q",
"rp",
"rt",
"ruby",
"s",
"samp",
"script",
"section",
"select",
"small",
"source",
"span",
"strong",
"style",
"sub",
"summary",
"sup",
"table",
"tbody",
"td",
"textarea",
"tfoot",
"th",
"thead",
"time",
"title",
"tr",
"track",
"u",
"ul",
"var",
"video",
"wbr",
// All the event handlers
"Attribute",
"accept",
"accept-charset",
"accesskey",
"action",
"alt",
"async",
"autocomplete",
"autofocus",
"autoplay",
"charset",
"checked",
"cite",
"class",
"cols",
"colspan",
"content",
"contenteditable",
"controls",
"coords",
"data",
"data-*",
"datetime",
"default",
"defer",
"dir",
"dirname",
"disabled",
"download",
"draggable",
"enctype",
"for",
"form",
"formaction",
"headers",
"height",
"hidden",
"high",
"href",
"hreflang",
"http-equiv",
"id",
"ismap",
"kind",
"label",
"lang",
"list",
"loop",
"low",
"max",
"maxlength",
"media",
"method",
"min",
"multiple",
"muted",
"name",
"novalidate",
"onabort",
"onafterprint",
"onbeforeprint",
"onbeforeunload",
"onblur",
"oncanplay",
"oncanplaythrough",
"onchange",
"onclick",
"oncontextmenu",
"oncopy",
"oncuechange",
"oncut",
"ondblclick",
"ondrag",
"ondragend",
"ondragenter",
"ondragleave",
"ondragover",
"ondragstart",
"ondrop",
"ondurationchange",
"onemptied",
"onended",
"onerror",
"onfocus",
"onfocusout",
"onfocusin",
"onhashchange",
"oninput",
"oninvalid",
"onkeydown",
"onkeypress",
"onkeyup",
"onload",
"onloadeddata",
"onloadedmetadata",
"onloadstart",
"onmousedown",
"onmousemove",
"onmouseout",
"onmouseover",
"onmouseup",
"onmousewheel",
"onoffline",
"ononline",
"onpageshow",
"onpaste",
"onpause",
"onplay",
"onplaying",
"onprogress",
"onratechange",
"onreset",
"onresize",
"onscroll",
"onsearch",
"onseeked",
"onseeking",
"onselect",
"onstalled",
"onsubmit",
"onsuspend",
"ontimeupdate",
"ontoggle",
"onunload",
"onvolumechange",
"onwaiting",
"onwheel",
"open",
"optimum",
"pattern",
"placeholder",
"poster",
"preload",
"readonly",
"rel",
"required",
"reversed",
"rows",
"rowspan",
"sandbox",
"scope",
"selected",
"shape",
"size",
"sizes",
"span",
"spellcheck",
"src",
"srcdoc",
"srclang",
"srcset",
"start",
"step",
"style",
"tabindex",
"target",
"title",
"translate",
"type",
"usemap",
"value",
"width",
"wrap",
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10",
"11",
"12",
"13",
"14",
];
pub static BUILTIN_INTERNED_STRINGS: &[&str] =
&[
// Important tags to dioxus
"dioxus-id",
"dioxus",
"dioxus-event-click", // todo: more events
"click",
// All the HTML Tags
"a",
"abbr",
"address",
"area",
"article",
"aside",
"audio",
"b",
"base",
"bdi",
"bdo",
"big",
"blockquote",
"body",
"br",
"button",
"canvas",
"caption",
"cite",
"code",
"col",
"colgroup",
"command",
"data",
"datalist",
"dd",
"del",
"details",
"dfn",
"dialog",
"div",
"dl",
"dt",
"em",
"embed",
"fieldset",
"figcaption",
"figure",
"footer",
"form",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"head",
"header",
"hr",
"html",
"i",
"iframe",
"img",
"input",
"ins",
"kbd",
"keygen",
"label",
"legend",
"li",
"link",
"main",
"map",
"mark",
"menu",
"menuitem",
"meta",
"meter",
"nav",
"noscript",
"object",
"ol",
"optgroup",
"option",
"output",
"p",
"param",
"picture",
"pre",
"progress",
"q",
"rp",
"rt",
"ruby",
"s",
"samp",
"script",
"section",
"select",
"small",
"source",
"span",
"strong",
"style",
"sub",
"summary",
"sup",
"table",
"tbody",
"td",
"textarea",
"tfoot",
"th",
"thead",
"time",
"title",
"tr",
"track",
"u",
"ul",
"var",
"video",
"wbr",
// All the event handlers
"Attribute",
"accept",
"accept-charset",
"accesskey",
"action",
"alt",
"async",
"autocomplete",
"autofocus",
"autoplay",
"charset",
"checked",
"cite",
"class",
"cols",
"colspan",
"content",
"contenteditable",
"controls",
"coords",
"data",
"data-*",
"datetime",
"default",
"defer",
"dir",
"dirname",
"disabled",
"download",
"draggable",
"enctype",
"for",
"form",
"formaction",
"headers",
"height",
"hidden",
"high",
"href",
"hreflang",
"http-equiv",
"id",
"ismap",
"kind",
"label",
"lang",
"list",
"loop",
"low",
"max",
"maxlength",
"media",
"method",
"min",
"multiple",
"muted",
"name",
"novalidate",
"onabort",
"onafterprint",
"onbeforeprint",
"onbeforeunload",
"onblur",
"oncanplay",
"oncanplaythrough",
"onchange",
"onclick",
"oncontextmenu",
"oncopy",
"oncuechange",
"oncut",
"ondblclick",
"ondrag",
"ondragend",
"ondragenter",
"ondragleave",
"ondragover",
"ondragstart",
"ondrop",
"ondurationchange",
"onemptied",
"onended",
"onerror",
"onfocus",
"onfocusout",
"onfocusin",
"onhashchange",
"oninput",
"oninvalid",
"onkeydown",
"onkeypress",
"onkeyup",
"onload",
"onloadeddata",
"onloadedmetadata",
"onloadstart",
"onmousedown",
"onmousemove",
"onmouseout",
"onmouseover",
"onmouseup",
"onmousewheel",
"onoffline",
"ononline",
"onpageshow",
"onpaste",
"onpause",
"onplay",
"onplaying",
"onprogress",
"onratechange",
"onreset",
"onresize",
"onscroll",
"onsearch",
"onseeked",
"onseeking",
"onselect",
"onstalled",
"onsubmit",
"onsuspend",
"ontimeupdate",
"ontoggle",
"onunload",
"onvolumechange",
"onwaiting",
"onwheel",
"open",
"optimum",
"pattern",
"placeholder",
"poster",
"preload",
"readonly",
"rel",
"required",
"reversed",
"rows",
"rowspan",
"sandbox",
"scope",
"selected",
"shape",
"size",
"sizes",
"span",
"spellcheck",
"src",
"srcdoc",
"srclang",
"srcset",
"start",
"step",
"style",
"tabindex",
"target",
"title",
"translate",
"type",
"usemap",
"value",
"width",
"wrap",
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10",
"11",
"12",
"13",
"14",
];
+7 -6
View File
@@ -80,12 +80,13 @@ impl WebsysDom {
}
let data = virtual_event_from_websys_event(event.clone(), target);
let _ = event_channel.unbounded_send(UiEvent {
name,
bubbles,
element,
data,
});
let _ =
event_channel.unbounded_send(UiEvent {
name,
bubbles,
element,
data,
});
}
}
}));
+6 -12
View File
@@ -62,24 +62,18 @@ impl WebEvaluator {
if let Ok(stringified) = js_sys::JSON::stringify(&result) {
if !stringified.is_undefined() && stringified.is_valid_utf16() {
let string: String = stringified.into();
Value::from_str(&string).map_err(|e| {
EvalError::Communication(format!("Failed to parse result - {}", e))
})?
Value::from_str(&string).map_err(
|e| EvalError::Communication(format!("Failed to parse result - {}", e))
)?
} else {
return Err(EvalError::Communication(
"Failed to stringify result".into(),
));
return Err(EvalError::Communication("Failed to stringify result".into()));
}
} else {
return Err(EvalError::Communication(
"Failed to stringify result".into(),
));
return Err(EvalError::Communication("Failed to stringify result".into()));
}
}
Err(err) => {
return Err(EvalError::InvalidJs(
err.as_string().unwrap_or("unknown".to_string()),
));
return Err(EvalError::InvalidJs(err.as_string().unwrap_or("unknown".to_string())));
}
};