docs: update local examples and docs to support new syntaxes
This commit is contained in:
1 parent
e495b09bf1
commit
4de16c4779
118 files changed
+520
-1154
No files matched your search
@@ -30,7 +30,7 @@ pub fn derive_typed_builder(input: proc_macro::TokenStream) -> proc_macro::Token
|
||||
///
|
||||
/// ## Complete Reference Guide:
|
||||
/// ```
|
||||
/// const Example: FC<()> = |cx, props|{
|
||||
/// const Example: Component<()> = |cx, props|{
|
||||
/// let formatting = "formatting!";
|
||||
/// let formatting_tuple = ("a", "b");
|
||||
/// let lazy_fmt = format_args!("lazily formatted text");
|
||||
|
||||
@@ -43,7 +43,11 @@ impl Parse for AmbiguousElement {
|
||||
if first_char.is_ascii_uppercase() {
|
||||
input.parse::<Component>().map(AmbiguousElement::Component)
|
||||
} else {
|
||||
input.parse::<Element>().map(AmbiguousElement::Element)
|
||||
if input.peek2(syn::token::Paren) {
|
||||
input.parse::<Component>().map(AmbiguousElement::Component)
|
||||
} else {
|
||||
input.parse::<Element>().map(AmbiguousElement::Element)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(Error::new(input.span(), "Not a valid Html tag"))
|
||||
|
||||
@@ -39,7 +39,14 @@ impl Parse for Component {
|
||||
|
||||
// parse the guts
|
||||
let content: ParseBuffer;
|
||||
syn::braced!(content in stream);
|
||||
|
||||
// if we see a `{` then we have a block
|
||||
// else parse as a function-like call
|
||||
if stream.peek(token::Brace) {
|
||||
syn::braced!(content in stream);
|
||||
} else {
|
||||
syn::parenthesized!(content in stream);
|
||||
}
|
||||
|
||||
let cfg: BodyConfig = BodyConfig {
|
||||
allow_children: true,
|
||||
|
||||
@@ -22,7 +22,7 @@ longest-increasing-subsequence = "0.1.0"
|
||||
# internall used
|
||||
log = { version = "0.4", features = ["release_max_level_off"] }
|
||||
|
||||
futures-util = { version = "0.3.15", default-features = false }
|
||||
futures-util = { version = "0.3", default-features = false }
|
||||
|
||||
smallvec = "1.6"
|
||||
|
||||
@@ -48,7 +48,7 @@ fern = { version = "0.6.0", features = ["colored"] }
|
||||
rand = { version = "0.8.4", features = ["small_rng"] }
|
||||
simple_logger = "1.13.0"
|
||||
dioxus-core-macro = { path = "../core-macro", version = "0.1.2" }
|
||||
dioxus-hooks = { path = "../hooks" }
|
||||
# dioxus-hooks = { path = "../hooks" }
|
||||
criterion = "0.3.5"
|
||||
|
||||
[features]
|
||||
|
||||
+16
-41
@@ -2,7 +2,7 @@
|
||||
|
||||
This is the core crate for the Dioxus Virtual DOM. This README will focus on the technical design and layout of this Virtual DOM implementation. If you want to read more about using Dioxus, then check out the Dioxus crate, documentation, and website.
|
||||
|
||||
To build new apps with Dioxus or to extend the ecosystem with new hooks or components, use the `Dioxus` crate with the appropriate feature flags.
|
||||
To build new apps with Dioxus or to extend the ecosystem with new hooks or components, use the higher-level `dioxus` crate with the appropriate feature flags.
|
||||
|
||||
## Internals
|
||||
|
||||
@@ -10,69 +10,44 @@ Dioxus-core builds off the many frameworks that came before it. Notably, Dioxus
|
||||
|
||||
- React: hooks, concurrency, suspense
|
||||
- Dodrio: bump allocation, double buffering, and some diffing architecture
|
||||
- Percy: html! macro architecture, platform-agnostic edits
|
||||
- InfernoJS: approach to keyed diffing
|
||||
- Preact: approach for normalization and ref
|
||||
- Yew: passion and inspiration ❤️
|
||||
|
||||
Dioxus-core leverages some really cool techniques and hits a very high level of parity with mature frameworks. However, Dioxus also brings some new unique features:
|
||||
|
||||
- managed lifetimes for borrowed data
|
||||
- suspended nodes (task/fiber endpoints) for asynchronous vnodes
|
||||
- placeholder approach for suspended vnodes
|
||||
- fiber/interruptible diffing algorithm
|
||||
- custom memory allocator for vnodes and all text content
|
||||
- support for fragments w/ lazy normalization
|
||||
- slab allocator for scopes
|
||||
- mirrored-slab approach for remote vdoms
|
||||
- dedicated subtrees for rendering into separate contexts from the same app
|
||||
|
||||
There's certainly more to the story, but these optimizations make Dioxus memory use and allocation count extremely minimal. For an average application, it is possible that zero allocations will need to be performed once the app has been mounted. Only when new components are added to the dom will allocations occur - and only en mass. The space of old VNodes is dynamically recycled as new nodes are added. Additionally, Dioxus tracks the average memory footprint of previous components to estimate how much memory allocate for future components.
|
||||
There's certainly more to the story, but these optimizations make Dioxus memory use and allocation count extremely minimal. For an average application, it is possible that zero allocations will need to be performed once the app has been loaded. Only when new components are added to the dom will allocations occur. For a given component, the space of old VNodes is dynamically recycled as new nodes are added. Additionally, Dioxus tracks the average memory footprint of previous components to estimate how much memory allocate for future components.
|
||||
|
||||
All in all, Dioxus treats memory as an incredibly valuable resource. Combined with the memory-efficient footprint of Wasm compilation, Dioxus apps can scale to thousands of components and still stay snappy and respect your RAM usage.
|
||||
All in all, Dioxus treats memory as a valuable resource. Combined with the memory-efficient footprint of Wasm compilation, Dioxus apps can scale to thousands of components and still stay snappy.
|
||||
|
||||
## Goals
|
||||
|
||||
We have big goals for Dioxus. The final implementation must:
|
||||
The final implementation of Dioxus must:
|
||||
|
||||
- Be **fast**. Allocators are typically slow in Wasm/Rust, so we should have a smart way of allocating.
|
||||
- Be extremely memory efficient. Servers should handle tens of thousands of simultaneous VDoms with no problem.
|
||||
- Be concurrent. Components should be able to pause rendering using a threading mechanism.
|
||||
- Be "remote". Edit lists should be separate from the Renderer implementation.
|
||||
- Support SSR. VNodes should render to a string that can be served via a web server.
|
||||
- Be memory efficient. Servers should handle tens of thousands of simultaneous VDoms with no problem.
|
||||
- Be concurrent. Components should be able to pause rendering to let the screen paint the next frame.
|
||||
- Be disconnected from a specific renderer (no WebSys dependency in the core crate).
|
||||
- Support server-side-rendering (SSR). VNodes should render to a string that can be served via a web server.
|
||||
- Be "live". Components should be able to be both server rendered and client rendered without needing frontend APIs.
|
||||
- Be modular. Components and hooks should be work anywhere without worrying about target platform.
|
||||
|
||||
|
||||
|
||||
## Safety
|
||||
|
||||
Dioxus uses unsafe. The design of Dioxus *requires* unsafe (self-referential trees).
|
||||
|
||||
All of our test suite passes MIRI without errors.
|
||||
|
||||
Dioxus deals with arenas, lifetimes, asynchronous tasks, custom allocators, pinning, and a lot more foundational low-level work that is very difficult to implement with 0 unsafe.
|
||||
|
||||
If you don't want to use a crate that uses unsafe, then this crate is not for you.
|
||||
|
||||
however, we are always interested in decreasing the scope of the core VirtualDom to make it easier to review.
|
||||
However, we are always interested in decreasing the scope of the core VirtualDom to make it easier to review. We'd be happy to welcome PRs that can eliminate unsafe code while still upholding the numerous variants required to execute certain features.
|
||||
|
||||
We'd also be happy to welcome PRs that can eliminate unsafe code while still upholding the numerous variants required to execute certain features.
|
||||
|
||||
There's a few invariants that are very important:
|
||||
|
||||
- References to `ScopeInner` and `Props` passed into components are *always* valid for as long as the component exists. Even if the scope backing is resized to fit more scopes, the scope has to stay the same place in memory.
|
||||
|
||||
|
||||
|
||||
## Suspense
|
||||
|
||||
Suspense is done through combinators on values.
|
||||
|
||||
```rust
|
||||
let name = get_name(cx).suspend();
|
||||
|
||||
rsx!(
|
||||
div {
|
||||
{name}
|
||||
div {
|
||||
div {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
@@ -1,5 +1,7 @@
|
||||
# Dioxus Core Architecture:
|
||||
|
||||
This document is mostly a brain-dump on how things work. A lot of this information might be outdated. Certain features (priority queues, swim lanes) might not be implemented yet.
|
||||
|
||||
Main topics covered here:
|
||||
- Fiber, Concurrency, and Cooperative Scheduling
|
||||
- Suspense
|
||||
|
||||
@@ -23,7 +23,7 @@ criterion_group!(mbenches, create_rows);
|
||||
criterion_main!(mbenches);
|
||||
|
||||
fn create_rows(c: &mut Criterion) {
|
||||
static App: Component<()> = |cx, _| {
|
||||
static App: Component<()> = |cx| {
|
||||
let mut rng = SmallRng::from_entropy();
|
||||
let rows = (0..10_000_usize).map(|f| {
|
||||
let label = Label::new(&mut rng);
|
||||
@@ -53,11 +53,11 @@ struct RowProps {
|
||||
row_id: usize,
|
||||
label: Label,
|
||||
}
|
||||
fn Row(cx: Scope, props: &RowProps) -> Element {
|
||||
let [adj, col, noun] = props.label.0;
|
||||
fn Row(cx: Scope<RowProps>) -> Element {
|
||||
let [adj, col, noun] = cx.props.label.0;
|
||||
cx.render(rsx! {
|
||||
tr {
|
||||
td { class:"col-md-1", "{props.row_id}" }
|
||||
td { class:"col-md-1", "{cx.props.row_id}" }
|
||||
td { class:"col-md-1", onclick: move |_| { /* run onselect */ }
|
||||
a { class: "lbl", "{adj}" "{col}" "{noun}" }
|
||||
}
|
||||
|
||||
@@ -31,21 +31,3 @@ fn bleat() {
|
||||
let blah = String::from("asd");
|
||||
eat(&blah);
|
||||
}
|
||||
|
||||
// struct Lower {}
|
||||
|
||||
// #[derive(Clone, Copy)]
|
||||
// struct Upper {}
|
||||
// impl std::ops::Deref for Upper {
|
||||
// type Target = Lower;
|
||||
|
||||
// fn deref(&self) -> &Self::Target {
|
||||
// todo!()
|
||||
// }
|
||||
// }
|
||||
|
||||
// fn mark(f: &Lower) {}
|
||||
// fn bark() {
|
||||
// let up = Upper {};
|
||||
// mark(&up);
|
||||
// }
|
||||
@@ -1,16 +1,10 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_hooks::use_state;
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
fn main() {}
|
||||
|
||||
fn App(cx: Scope<()>) -> Element {
|
||||
let color = use_state(&cx, || "white");
|
||||
|
||||
cx.render(rsx!(
|
||||
div { onclick: move |_| color.set("red"), "red" }
|
||||
div { onclick: move |_| color.set("blue"), "blue" }
|
||||
))
|
||||
todo!()
|
||||
}
|
||||
@@ -1,412 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg version="1.1" width="1200" height="518" onload="init(evt)" viewBox="0 0 1200 518" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:fg="http://github.com/jonhoo/inferno"><!--Flame graph stack visualization. See https://github.com/brendangregg/FlameGraph for latest version, and http://www.brendangregg.com/flamegraphs.html for examples.--><!--NOTES: --><defs><linearGradient id="background" y1="0" y2="1" x1="0" x2="0"><stop stop-color="#eeeeee" offset="5%"/><stop stop-color="#eeeeb0" offset="95%"/></linearGradient></defs><style type="text/css">
|
||||
text { font-family:"Verdana"; font-size:12px; fill:rgb(0,0,0); }
|
||||
#title { text-anchor:middle; font-size:17px; }
|
||||
#search { opacity:0.1; cursor:pointer; }
|
||||
#search:hover, #search.show { opacity:1; }
|
||||
#subtitle { text-anchor:middle; font-color:rgb(160,160,160); }
|
||||
#unzoom { cursor:pointer; }
|
||||
#frames > *:hover { stroke:black; stroke-width:0.5; cursor:pointer; }
|
||||
.hide { display:none; }
|
||||
.parent { opacity:0.5; }
|
||||
</style><script type="text/ecmascript"><![CDATA[var nametype = 'Function:';
|
||||
var fontsize = 12;
|
||||
var fontwidth = 0.59;
|
||||
var xpad = 10;
|
||||
var inverted = false;
|
||||
var searchcolor = 'rgb(230,0,230)';
|
||||
var fluiddrawing = true;
|
||||
var truncate_text_right = false;]]><![CDATA["use strict";
|
||||
var details, searchbtn, unzoombtn, matchedtxt, svg, searching, frames;
|
||||
function init(evt) {
|
||||
details = document.getElementById("details").firstChild;
|
||||
searchbtn = document.getElementById("search");
|
||||
unzoombtn = document.getElementById("unzoom");
|
||||
matchedtxt = document.getElementById("matched");
|
||||
svg = document.getElementsByTagName("svg")[0];
|
||||
frames = document.getElementById("frames");
|
||||
total_samples = parseInt(frames.attributes.total_samples.value);
|
||||
searching = 0;
|
||||
|
||||
// Use GET parameters to restore a flamegraph's state.
|
||||
var restore_state = function() {
|
||||
var params = get_params();
|
||||
if (params.x && params.y)
|
||||
zoom(find_group(document.querySelector('[*|x="' + params.x + '"][y="' + params.y + '"]')));
|
||||
if (params.s)
|
||||
search(params.s);
|
||||
};
|
||||
|
||||
if (fluiddrawing) {
|
||||
// Make width dynamic so the SVG fits its parent's width.
|
||||
svg.removeAttribute("width");
|
||||
// Edge requires us to have a viewBox that gets updated with size changes.
|
||||
var isEdge = /Edge\/\d./i.test(navigator.userAgent);
|
||||
if (!isEdge) {
|
||||
svg.removeAttribute("viewBox");
|
||||
}
|
||||
var update_for_width_change = function() {
|
||||
if (isEdge) {
|
||||
svg.attributes.viewBox.value = "0 0 " + svg.width.baseVal.value + " " + svg.height.baseVal.value;
|
||||
}
|
||||
|
||||
// Keep consistent padding on left and right of frames container.
|
||||
frames.attributes.width.value = svg.width.baseVal.value - xpad * 2;
|
||||
|
||||
// Text truncation needs to be adjusted for the current width.
|
||||
var el = frames.children;
|
||||
for(var i = 0; i < el.length; i++) {
|
||||
update_text(el[i]);
|
||||
}
|
||||
|
||||
// Keep search elements at a fixed distance from right edge.
|
||||
var svgWidth = svg.width.baseVal.value;
|
||||
searchbtn.attributes.x.value = svgWidth - xpad - 100;
|
||||
matchedtxt.attributes.x.value = svgWidth - xpad - 100;
|
||||
};
|
||||
window.addEventListener('resize', function() {
|
||||
update_for_width_change();
|
||||
});
|
||||
// This needs to be done asynchronously for Safari to work.
|
||||
setTimeout(function() {
|
||||
unzoom();
|
||||
update_for_width_change();
|
||||
restore_state();
|
||||
}, 0);
|
||||
} else {
|
||||
restore_state();
|
||||
}
|
||||
}
|
||||
// event listeners
|
||||
window.addEventListener("click", function(e) {
|
||||
var target = find_group(e.target);
|
||||
if (target) {
|
||||
if (target.nodeName == "a") {
|
||||
if (e.ctrlKey === false) return;
|
||||
e.preventDefault();
|
||||
}
|
||||
if (target.classList.contains("parent")) unzoom();
|
||||
zoom(target);
|
||||
|
||||
// set parameters for zoom state
|
||||
var el = target.querySelector("rect");
|
||||
if (el && el.attributes && el.attributes.y && el.attributes["fg:x"]) {
|
||||
var params = get_params()
|
||||
params.x = el.attributes["fg:x"].value;
|
||||
params.y = el.attributes.y.value;
|
||||
history.replaceState(null, null, parse_params(params));
|
||||
}
|
||||
}
|
||||
else if (e.target.id == "unzoom") {
|
||||
unzoom();
|
||||
|
||||
// remove zoom state
|
||||
var params = get_params();
|
||||
if (params.x) delete params.x;
|
||||
if (params.y) delete params.y;
|
||||
history.replaceState(null, null, parse_params(params));
|
||||
}
|
||||
else if (e.target.id == "search") search_prompt();
|
||||
}, false)
|
||||
// mouse-over for info
|
||||
// show
|
||||
window.addEventListener("mouseover", function(e) {
|
||||
var target = find_group(e.target);
|
||||
if (target) details.nodeValue = nametype + " " + g_to_text(target);
|
||||
}, false)
|
||||
// clear
|
||||
window.addEventListener("mouseout", function(e) {
|
||||
var target = find_group(e.target);
|
||||
if (target) details.nodeValue = ' ';
|
||||
}, false)
|
||||
// ctrl-F for search
|
||||
window.addEventListener("keydown",function (e) {
|
||||
if (e.keyCode === 114 || (e.ctrlKey && e.keyCode === 70)) {
|
||||
e.preventDefault();
|
||||
search_prompt();
|
||||
}
|
||||
}, false)
|
||||
// functions
|
||||
function get_params() {
|
||||
var params = {};
|
||||
var paramsarr = window.location.search.substr(1).split('&');
|
||||
for (var i = 0; i < paramsarr.length; ++i) {
|
||||
var tmp = paramsarr[i].split("=");
|
||||
if (!tmp[0] || !tmp[1]) continue;
|
||||
params[tmp[0]] = decodeURIComponent(tmp[1]);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
function parse_params(params) {
|
||||
var uri = "?";
|
||||
for (var key in params) {
|
||||
uri += key + '=' + encodeURIComponent(params[key]) + '&';
|
||||
}
|
||||
if (uri.slice(-1) == "&")
|
||||
uri = uri.substring(0, uri.length - 1);
|
||||
if (uri == '?')
|
||||
uri = window.location.href.split('?')[0];
|
||||
return uri;
|
||||
}
|
||||
function find_child(node, selector) {
|
||||
var children = node.querySelectorAll(selector);
|
||||
if (children.length) return children[0];
|
||||
return;
|
||||
}
|
||||
function find_group(node) {
|
||||
var parent = node.parentElement;
|
||||
if (!parent) return;
|
||||
if (parent.id == "frames") return node;
|
||||
return find_group(parent);
|
||||
}
|
||||
function orig_save(e, attr, val) {
|
||||
if (e.attributes["fg:orig_" + attr] != undefined) return;
|
||||
if (e.attributes[attr] == undefined) return;
|
||||
if (val == undefined) val = e.attributes[attr].value;
|
||||
e.setAttribute("fg:orig_" + attr, val);
|
||||
}
|
||||
function orig_load(e, attr) {
|
||||
if (e.attributes["fg:orig_"+attr] == undefined) return;
|
||||
e.attributes[attr].value = e.attributes["fg:orig_" + attr].value;
|
||||
e.removeAttribute("fg:orig_" + attr);
|
||||
}
|
||||
function g_to_text(e) {
|
||||
var text = find_child(e, "title").firstChild.nodeValue;
|
||||
return (text)
|
||||
}
|
||||
function g_to_func(e) {
|
||||
var func = g_to_text(e);
|
||||
// if there's any manipulation we want to do to the function
|
||||
// name before it's searched, do it here before returning.
|
||||
return (func);
|
||||
}
|
||||
function update_text(e) {
|
||||
var r = find_child(e, "rect");
|
||||
var t = find_child(e, "text");
|
||||
var w = parseFloat(r.attributes.width.value) * frames.attributes.width.value / 100 - 3;
|
||||
var txt = find_child(e, "title").textContent.replace(/\([^(]*\)$/,"");
|
||||
t.attributes.x.value = format_percent((parseFloat(r.attributes.x.value) + (100 * 3 / frames.attributes.width.value)));
|
||||
// Smaller than this size won't fit anything
|
||||
if (w < 2 * fontsize * fontwidth) {
|
||||
t.textContent = "";
|
||||
return;
|
||||
}
|
||||
t.textContent = txt;
|
||||
// Fit in full text width
|
||||
if (/^ *\$/.test(txt) || t.getComputedTextLength() < w)
|
||||
return;
|
||||
if (truncate_text_right) {
|
||||
// Truncate the right side of the text.
|
||||
for (var x = txt.length - 2; x > 0; x--) {
|
||||
if (t.getSubStringLength(0, x + 2) <= w) {
|
||||
t.textContent = txt.substring(0, x) + "..";
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Truncate the left side of the text.
|
||||
for (var x = 2; x < txt.length; x++) {
|
||||
if (t.getSubStringLength(x - 2, txt.length) <= w) {
|
||||
t.textContent = ".." + txt.substring(x, txt.length);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
t.textContent = "";
|
||||
}
|
||||
// zoom
|
||||
function zoom_reset(e) {
|
||||
if (e.tagName == "rect") {
|
||||
e.attributes.x.value = format_percent(100 * parseInt(e.attributes["fg:x"].value) / total_samples);
|
||||
e.attributes.width.value = format_percent(100 * parseInt(e.attributes["fg:w"].value) / total_samples);
|
||||
}
|
||||
if (e.childNodes == undefined) return;
|
||||
for(var i = 0, c = e.childNodes; i < c.length; i++) {
|
||||
zoom_reset(c[i]);
|
||||
}
|
||||
}
|
||||
function zoom_child(e, x, zoomed_width_samples) {
|
||||
if (e.tagName == "text") {
|
||||
var parent_x = parseFloat(find_child(e.parentNode, "rect[x]").attributes.x.value);
|
||||
e.attributes.x.value = format_percent(parent_x + (100 * 3 / frames.attributes.width.value));
|
||||
} else if (e.tagName == "rect") {
|
||||
e.attributes.x.value = format_percent(100 * (parseInt(e.attributes["fg:x"].value) - x) / zoomed_width_samples);
|
||||
e.attributes.width.value = format_percent(100 * parseInt(e.attributes["fg:w"].value) / zoomed_width_samples);
|
||||
}
|
||||
if (e.childNodes == undefined) return;
|
||||
for(var i = 0, c = e.childNodes; i < c.length; i++) {
|
||||
zoom_child(c[i], x, zoomed_width_samples);
|
||||
}
|
||||
}
|
||||
function zoom_parent(e) {
|
||||
if (e.attributes) {
|
||||
if (e.attributes.x != undefined) {
|
||||
e.attributes.x.value = "0.0%";
|
||||
}
|
||||
if (e.attributes.width != undefined) {
|
||||
e.attributes.width.value = "100.0%";
|
||||
}
|
||||
}
|
||||
if (e.childNodes == undefined) return;
|
||||
for(var i = 0, c = e.childNodes; i < c.length; i++) {
|
||||
zoom_parent(c[i]);
|
||||
}
|
||||
}
|
||||
function zoom(node) {
|
||||
var attr = find_child(node, "rect").attributes;
|
||||
var width = parseInt(attr["fg:w"].value);
|
||||
var xmin = parseInt(attr["fg:x"].value);
|
||||
var xmax = xmin + width;
|
||||
var ymin = parseFloat(attr.y.value);
|
||||
unzoombtn.classList.remove("hide");
|
||||
var el = frames.children;
|
||||
for (var i = 0; i < el.length; i++) {
|
||||
var e = el[i];
|
||||
var a = find_child(e, "rect").attributes;
|
||||
var ex = parseInt(a["fg:x"].value);
|
||||
var ew = parseInt(a["fg:w"].value);
|
||||
// Is it an ancestor
|
||||
if (!inverted) {
|
||||
var upstack = parseFloat(a.y.value) > ymin;
|
||||
} else {
|
||||
var upstack = parseFloat(a.y.value) < ymin;
|
||||
}
|
||||
if (upstack) {
|
||||
// Direct ancestor
|
||||
if (ex <= xmin && (ex+ew) >= xmax) {
|
||||
e.classList.add("parent");
|
||||
zoom_parent(e);
|
||||
update_text(e);
|
||||
}
|
||||
// not in current path
|
||||
else
|
||||
e.classList.add("hide");
|
||||
}
|
||||
// Children maybe
|
||||
else {
|
||||
// no common path
|
||||
if (ex < xmin || ex >= xmax) {
|
||||
e.classList.add("hide");
|
||||
}
|
||||
else {
|
||||
zoom_child(e, xmin, width);
|
||||
update_text(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function unzoom() {
|
||||
unzoombtn.classList.add("hide");
|
||||
var el = frames.children;
|
||||
for(var i = 0; i < el.length; i++) {
|
||||
el[i].classList.remove("parent");
|
||||
el[i].classList.remove("hide");
|
||||
zoom_reset(el[i]);
|
||||
update_text(el[i]);
|
||||
}
|
||||
}
|
||||
// search
|
||||
function reset_search() {
|
||||
var el = document.querySelectorAll("#frames rect");
|
||||
for (var i = 0; i < el.length; i++) {
|
||||
orig_load(el[i], "fill")
|
||||
}
|
||||
var params = get_params();
|
||||
delete params.s;
|
||||
history.replaceState(null, null, parse_params(params));
|
||||
}
|
||||
function search_prompt() {
|
||||
if (!searching) {
|
||||
var term = prompt("Enter a search term (regexp " +
|
||||
"allowed, eg: ^ext4_)", "");
|
||||
if (term != null) {
|
||||
search(term)
|
||||
}
|
||||
} else {
|
||||
reset_search();
|
||||
searching = 0;
|
||||
searchbtn.classList.remove("show");
|
||||
searchbtn.firstChild.nodeValue = "Search"
|
||||
matchedtxt.classList.add("hide");
|
||||
matchedtxt.firstChild.nodeValue = ""
|
||||
}
|
||||
}
|
||||
function search(term) {
|
||||
var re = new RegExp(term);
|
||||
var el = frames.children;
|
||||
var matches = new Object();
|
||||
var maxwidth = 0;
|
||||
for (var i = 0; i < el.length; i++) {
|
||||
var e = el[i];
|
||||
// Skip over frames which are either not visible, or below the zoomed-to frame
|
||||
if (e.classList.contains("hide") || e.classList.contains("parent")) {
|
||||
continue;
|
||||
}
|
||||
var func = g_to_func(e);
|
||||
var rect = find_child(e, "rect");
|
||||
if (func == null || rect == null)
|
||||
continue;
|
||||
// Save max width. Only works as we have a root frame
|
||||
var w = parseInt(rect.attributes["fg:w"].value);
|
||||
if (w > maxwidth)
|
||||
maxwidth = w;
|
||||
if (func.match(re)) {
|
||||
// highlight
|
||||
var x = parseInt(rect.attributes["fg:x"].value);
|
||||
orig_save(rect, "fill");
|
||||
rect.attributes.fill.value = searchcolor;
|
||||
// remember matches
|
||||
if (matches[x] == undefined) {
|
||||
matches[x] = w;
|
||||
} else {
|
||||
if (w > matches[x]) {
|
||||
// overwrite with parent
|
||||
matches[x] = w;
|
||||
}
|
||||
}
|
||||
searching = 1;
|
||||
}
|
||||
}
|
||||
if (!searching)
|
||||
return;
|
||||
var params = get_params();
|
||||
params.s = term;
|
||||
history.replaceState(null, null, parse_params(params));
|
||||
|
||||
searchbtn.classList.add("show");
|
||||
searchbtn.firstChild.nodeValue = "Reset Search";
|
||||
// calculate percent matched, excluding vertical overlap
|
||||
var count = 0;
|
||||
var lastx = -1;
|
||||
var lastw = 0;
|
||||
var keys = Array();
|
||||
for (k in matches) {
|
||||
if (matches.hasOwnProperty(k))
|
||||
keys.push(k);
|
||||
}
|
||||
// sort the matched frames by their x location
|
||||
// ascending, then width descending
|
||||
keys.sort(function(a, b){
|
||||
return a - b;
|
||||
});
|
||||
// Step through frames saving only the biggest bottom-up frames
|
||||
// thanks to the sort order. This relies on the tree property
|
||||
// where children are always smaller than their parents.
|
||||
for (var k in keys) {
|
||||
var x = parseInt(keys[k]);
|
||||
var w = matches[keys[k]];
|
||||
if (x >= lastx + lastw) {
|
||||
count += w;
|
||||
lastx = x;
|
||||
lastw = w;
|
||||
}
|
||||
}
|
||||
// display matched percent
|
||||
matchedtxt.classList.remove("hide");
|
||||
var pct = 100 * count / maxwidth;
|
||||
if (pct != 100) pct = pct.toFixed(1);
|
||||
matchedtxt.firstChild.nodeValue = "Matched: " + pct + "%";
|
||||
}
|
||||
function format_percent(n) {
|
||||
return n.toFixed(4) + "%";
|
||||
}
|
||||
]]></script><rect x="0" y="0" width="100%" height="518" fill="url(#background)"/><text id="title" x="50.0000%" y="24.00">Flame Graph</text><text id="details" x="10" y="501.00"> </text><text id="unzoom" class="hide" x="10" y="24.00">Reset Zoom</text><text id="search" x="1090" y="24.00">Search</text><text id="matched" x="1090" y="501.00"> </text><svg id="frames" x="10" width="1180" total_samples="55"><g><title>jsframework-a8f4acf5955e8e7f`core::ptr::drop_in_place<dioxus_core::virtual_dom::VirtualDom> (1 samples, 1.82%)</title><rect x="0.0000%" y="261" width="1.8182%" height="15" fill="rgb(227,0,7)" fg:x="0" fg:w="1"/><text x="0.2500%" y="271.50">j..</text></g><g><title>jsframework-a8f4acf5955e8e7f`core::ptr::drop_in_place<alloc::boxed::Box<dioxus_core::scopearena::ScopeArena>> (1 samples, 1.82%)</title><rect x="0.0000%" y="245" width="1.8182%" height="15" fill="rgb(217,0,24)" fg:x="0" fg:w="1"/><text x="0.2500%" y="255.50">j..</text></g><g><title>jsframework-a8f4acf5955e8e7f`<bumpalo::Bump as core::ops::drop::Drop>::drop (1 samples, 1.82%)</title><rect x="0.0000%" y="229" width="1.8182%" height="15" fill="rgb(221,193,54)" fg:x="0" fg:w="1"/><text x="0.2500%" y="239.50">j..</text></g><g><title>libsystem_malloc.dylib`free_medium (1 samples, 1.82%)</title><rect x="0.0000%" y="213" width="1.8182%" height="15" fill="rgb(248,212,6)" fg:x="0" fg:w="1"/><text x="0.2500%" y="223.50">l..</text></g><g><title>libsystem_kernel.dylib`madvise (1 samples, 1.82%)</title><rect x="0.0000%" y="197" width="1.8182%" height="15" fill="rgb(208,68,35)" fg:x="0" fg:w="1"/><text x="0.2500%" y="207.50">l..</text></g><g><title>libsystem_malloc.dylib`_malloc_zone_malloc (2 samples, 3.64%)</title><rect x="9.0909%" y="181" width="3.6364%" height="15" fill="rgb(232,128,0)" fg:x="5" fg:w="2"/><text x="9.3409%" y="191.50">libs..</text></g><g><title>libsystem_malloc.dylib`szone_malloc_should_clear (2 samples, 3.64%)</title><rect x="9.0909%" y="165" width="3.6364%" height="15" fill="rgb(207,160,47)" fg:x="5" fg:w="2"/><text x="9.3409%" y="175.50">libs..</text></g><g><title>libsystem_malloc.dylib`tiny_malloc_should_clear (2 samples, 3.64%)</title><rect x="9.0909%" y="149" width="3.6364%" height="15" fill="rgb(228,23,34)" fg:x="5" fg:w="2"/><text x="9.3409%" y="159.50">libs..</text></g><g><title>libsystem_malloc.dylib`tiny_malloc_from_free_list (1 samples, 1.82%)</title><rect x="10.9091%" y="133" width="1.8182%" height="15" fill="rgb(218,30,26)" fg:x="6" fg:w="1"/><text x="11.1591%" y="143.50">l..</text></g><g><title>libsystem_malloc.dylib`tiny_free_list_add_ptr (1 samples, 1.82%)</title><rect x="10.9091%" y="117" width="1.8182%" height="15" fill="rgb(220,122,19)" fg:x="6" fg:w="1"/><text x="11.1591%" y="127.50">l..</text></g><g><title>jsframework-a8f4acf5955e8e7f`dioxus_core::diff::DiffState::create_element_node (7 samples, 12.73%)</title><rect x="1.8182%" y="229" width="12.7273%" height="15" fill="rgb(250,228,42)" fg:x="1" fg:w="7"/><text x="2.0682%" y="239.50">jsframework-a8f4acf..</text></g><g><title>jsframework-a8f4acf5955e8e7f`alloc::raw_vec::RawVec<T,A>::reserve::do_reserve_and_handle (3 samples, 5.45%)</title><rect x="9.0909%" y="213" width="5.4545%" height="15" fill="rgb(240,193,28)" fg:x="5" fg:w="3"/><text x="9.3409%" y="223.50">jsframe..</text></g><g><title>jsframework-a8f4acf5955e8e7f`alloc::raw_vec::finish_grow (3 samples, 5.45%)</title><rect x="9.0909%" y="197" width="5.4545%" height="15" fill="rgb(216,20,37)" fg:x="5" fg:w="3"/><text x="9.3409%" y="207.50">jsframe..</text></g><g><title>libsystem_malloc.dylib`realloc (1 samples, 1.82%)</title><rect x="12.7273%" y="181" width="1.8182%" height="15" fill="rgb(206,188,39)" fg:x="7" fg:w="1"/><text x="12.9773%" y="191.50">l..</text></g><g><title>libsystem_malloc.dylib`malloc_zone_realloc (1 samples, 1.82%)</title><rect x="12.7273%" y="165" width="1.8182%" height="15" fill="rgb(217,207,13)" fg:x="7" fg:w="1"/><text x="12.9773%" y="175.50">l..</text></g><g><title>libsystem_malloc.dylib`szone_realloc (1 samples, 1.82%)</title><rect x="12.7273%" y="149" width="1.8182%" height="15" fill="rgb(231,73,38)" fg:x="7" fg:w="1"/><text x="12.9773%" y="159.50">l..</text></g><g><title>libsystem_platform.dylib`_platform_memmove$VARIANT$Haswell (1 samples, 1.82%)</title><rect x="12.7273%" y="133" width="1.8182%" height="15" fill="rgb(225,20,46)" fg:x="7" fg:w="1"/><text x="12.9773%" y="143.50">l..</text></g><g><title>jsframework-a8f4acf5955e8e7f`dioxus_core::scopearena::ScopeArena::fin_head (1 samples, 1.82%)</title><rect x="16.3636%" y="213" width="1.8182%" height="15" fill="rgb(210,31,41)" fg:x="9" fg:w="1"/><text x="16.6136%" y="223.50">j..</text></g><g><title>jsframework-a8f4acf5955e8e7f`<bumpalo::Bump as core::default::Default>::default (4 samples, 7.27%)</title><rect x="21.8182%" y="197" width="7.2727%" height="15" fill="rgb(221,200,47)" fg:x="12" fg:w="4"/><text x="22.0682%" y="207.50">jsframewor..</text></g><g><title>jsframework-a8f4acf5955e8e7f`<bumpalo::BLine truncated
|
||||
|
Before Width: | Height: | Size: 38 KiB |
@@ -58,7 +58,7 @@ impl<'a, const A: bool> FragmentBuilder<'a, A> {
|
||||
/// cx.render(rsx!{
|
||||
/// div {
|
||||
/// h1 {"Title card"}
|
||||
/// {props.children}
|
||||
/// {cx.props.children}
|
||||
/// }
|
||||
/// })
|
||||
/// }
|
||||
|
||||
@@ -440,7 +440,7 @@ impl<'bump> DiffState<'bump> {
|
||||
let height = parent_scope.height + 1;
|
||||
let subtree = parent_scope.subtree.get();
|
||||
|
||||
let parent_scope = unsafe { self.scopes.get_scope_raw(parent_idx) };
|
||||
let parent_scope = self.scopes.get_scope_raw(parent_idx);
|
||||
let caller = unsafe { std::mem::transmute(vcomponent.caller as *const _) };
|
||||
let fc_ptr = vcomponent.user_fc;
|
||||
|
||||
@@ -716,13 +716,12 @@ impl<'bump> DiffState<'bump> {
|
||||
new.associated_scope.set(Some(scope_addr));
|
||||
|
||||
// make sure the component's caller function is up to date
|
||||
let scope = unsafe {
|
||||
self.scopes
|
||||
.get_scope_mut(scope_addr)
|
||||
.unwrap_or_else(|| panic!("could not find {:?}", scope_addr))
|
||||
};
|
||||
let scope = self
|
||||
.scopes
|
||||
.get_scope(scope_addr)
|
||||
.unwrap_or_else(|| panic!("could not find {:?}", scope_addr));
|
||||
|
||||
scope.caller = unsafe { std::mem::transmute(new.caller) };
|
||||
scope.caller.set(unsafe { std::mem::transmute(new.caller) });
|
||||
|
||||
// React doesn't automatically memoize, but we do.
|
||||
let props_are_the_same = old.comparator.unwrap();
|
||||
|
||||
@@ -520,8 +520,8 @@ impl<'a> NodeFactory<'a> {
|
||||
move |other: &VComponent| {
|
||||
if user_fc == other.user_fc {
|
||||
// Safety
|
||||
// - We guarantee that FC<P> is the same by function pointer
|
||||
// - Because FC<P> is the same, then P must be the same (even with generics)
|
||||
// - We guarantee that Component<P> is the same by function pointer
|
||||
// - Because Component<P> is the same, then P must be the same (even with generics)
|
||||
// - Non-static P are autoderived to memoize as false
|
||||
// - This comparator is only called on a corresponding set of bumpframes
|
||||
//
|
||||
|
||||
@@ -28,7 +28,7 @@ use bumpalo::{boxed::Box as BumpBox, Bump};
|
||||
/// }
|
||||
///
|
||||
/// fn Example(cx: Context, props: &ExampleProps) -> Element {
|
||||
/// cx.render(rsx!{ div {"Hello, {props.name}"} })
|
||||
/// cx.render(rsx!{ div {"Hello, {cx.props.name}"} })
|
||||
/// }
|
||||
/// ```
|
||||
pub struct Scope<'a, P> {
|
||||
@@ -88,7 +88,7 @@ pub struct ScopeState {
|
||||
|
||||
pub(crate) frames: [BumpFrame; 2],
|
||||
|
||||
pub(crate) caller: *const dyn Fn(&ScopeState) -> Element,
|
||||
pub(crate) caller: Cell<*const dyn Fn(&ScopeState) -> Element>,
|
||||
|
||||
pub(crate) items: RefCell<SelfReferentialItems<'static>>,
|
||||
|
||||
@@ -269,12 +269,12 @@ impl ScopeState {
|
||||
/// ```rust, ignore
|
||||
/// struct SharedState(&'static str);
|
||||
///
|
||||
/// static App: FC<()> = |cx, props|{
|
||||
/// static App: Component<()> = |cx, props|{
|
||||
/// cx.use_hook(|_| cx.provide_state(SharedState("world")), |_| {}, |_| {});
|
||||
/// rsx!(cx, Child {})
|
||||
/// }
|
||||
///
|
||||
/// static Child: FC<()> = |cx, props| {
|
||||
/// static Child: Component<()> = |cx| {
|
||||
/// let state = cx.consume_state::<SharedState>();
|
||||
/// rsx!(cx, div { "hello {state.0}" })
|
||||
/// }
|
||||
@@ -344,7 +344,7 @@ impl ScopeState {
|
||||
/// ## Example
|
||||
///
|
||||
/// ```ignore
|
||||
/// fn Component(cx: Scope, props: &Props) -> Element {
|
||||
/// fn Component(cx: Scope<Props>) -> Element {
|
||||
/// // Lazy assemble the VNode tree
|
||||
/// let lazy_nodes = rsx!("hello world");
|
||||
///
|
||||
|
||||
@@ -72,14 +72,10 @@ impl ScopeArena {
|
||||
unsafe { self.scopes.borrow().get(&id).map(|f| &**f) }
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn get_scope_raw(&self, id: ScopeId) -> Option<*mut ScopeState> {
|
||||
pub(crate) fn get_scope_raw(&self, id: ScopeId) -> Option<*mut ScopeState> {
|
||||
self.scopes.borrow().get(&id).copied()
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn get_scope_mut(&self, id: ScopeId) -> Option<&mut ScopeState> {
|
||||
self.scopes.borrow().get(&id).map(|s| &mut **s)
|
||||
}
|
||||
|
||||
pub(crate) fn new_with_key(
|
||||
&self,
|
||||
fc_ptr: *const (),
|
||||
@@ -95,35 +91,13 @@ impl ScopeArena {
|
||||
if let Some(old_scope) = self.free_scopes.borrow_mut().pop() {
|
||||
let scope = unsafe { &mut *old_scope };
|
||||
|
||||
scope.caller = caller;
|
||||
scope.caller.set(caller);
|
||||
scope.parent_scope = parent_scope;
|
||||
scope.height = height;
|
||||
scope.subtree = Cell::new(subtree);
|
||||
scope.our_arena_idx = new_scope_id;
|
||||
scope.container = container;
|
||||
|
||||
// scope.frames[0].nodes.get_mut().push({
|
||||
// let vnode = scope.frames[0]
|
||||
// .bump
|
||||
// .alloc(VNode::Text(scope.frames[0].bump.alloc(VText {
|
||||
// dom_id: Default::default(),
|
||||
// is_static: false,
|
||||
// text: "",
|
||||
// })));
|
||||
// unsafe { std::mem::transmute(vnode as *mut VNode) }
|
||||
// });
|
||||
|
||||
// scope.frames[1].nodes.get_mut().push({
|
||||
// let vnode = scope.frames[1]
|
||||
// .bump
|
||||
// .alloc(VNode::Text(scope.frames[1].bump.alloc(VText {
|
||||
// dom_id: Default::default(),
|
||||
// is_static: false,
|
||||
// text: "",
|
||||
// })));
|
||||
// unsafe { std::mem::transmute(vnode as *mut VNode) }
|
||||
// });
|
||||
|
||||
let any_item = self.scopes.borrow_mut().insert(new_scope_id, scope);
|
||||
debug_assert!(any_item.is_none());
|
||||
} else {
|
||||
@@ -148,7 +122,7 @@ impl ScopeArena {
|
||||
subtree: Cell::new(subtree),
|
||||
is_subtree_root: Cell::new(false),
|
||||
|
||||
caller,
|
||||
caller: Cell::new(caller),
|
||||
generation: 0.into(),
|
||||
|
||||
shared_contexts: Default::default(),
|
||||
@@ -277,18 +251,20 @@ impl ScopeArena {
|
||||
// Remove all the outdated listeners
|
||||
self.ensure_drop_safety(id);
|
||||
|
||||
let scope = unsafe { &mut *self.get_scope_mut(id).expect("could not find scope") };
|
||||
// todo: we *know* that this is aliased by the contents of the scope itself
|
||||
let scope = unsafe { &mut *self.get_scope_raw(id).expect("could not find scope") };
|
||||
|
||||
// Safety:
|
||||
// - We dropped the listeners, so no more &mut T can be used while these are held
|
||||
// - All children nodes that rely on &mut T are replaced with a new reference
|
||||
scope.hook_idx.set(0);
|
||||
|
||||
// Safety:
|
||||
// - We've dropped all references to the wip bump frame with "ensure_drop_safety"
|
||||
unsafe { scope.reset_wip_frame() };
|
||||
|
||||
// book keeping to ensure safety around the borrowed data
|
||||
{
|
||||
// Safety:
|
||||
// - We've dropped all references to the wip bump frame with "ensure_drop_safety"
|
||||
unsafe { scope.reset_wip_frame() };
|
||||
|
||||
let mut items = scope.items.borrow_mut();
|
||||
|
||||
// just forget about our suspended nodes while we're at it
|
||||
@@ -298,12 +274,9 @@ impl ScopeArena {
|
||||
debug_assert!(items.listeners.is_empty());
|
||||
debug_assert!(items.borrowed_props.is_empty());
|
||||
debug_assert!(items.tasks.is_empty());
|
||||
|
||||
// Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders.
|
||||
// scope.wip_frame().nodes
|
||||
}
|
||||
|
||||
let render: &dyn Fn(&ScopeState) -> Element = unsafe { &*scope.caller };
|
||||
let render: &dyn Fn(&ScopeState) -> Element = unsafe { &*scope.caller.get() };
|
||||
|
||||
if let Some(node) = render(scope) {
|
||||
if !scope.items.borrow().tasks.is_empty() {
|
||||
@@ -319,6 +292,10 @@ impl ScopeArena {
|
||||
scope.cycle_frame();
|
||||
true
|
||||
} else {
|
||||
// the component bailed out early
|
||||
// this leaves the wip frame and all descendents in a state where
|
||||
// their WIP frames are invalid
|
||||
// todo: descendents should not cause the app to crash
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ use std::{any::Any, collections::VecDeque, pin::Pin, sync::Arc, task::Poll};
|
||||
///
|
||||
/// fn App(cx: Context, props: &AppProps) -> Element {
|
||||
/// cx.render(rsx!(
|
||||
/// div {"hello, {props.title}"}
|
||||
/// div {"hello, {cx.props.title}"}
|
||||
/// ))
|
||||
/// }
|
||||
/// ```
|
||||
@@ -36,7 +36,7 @@ use std::{any::Any, collections::VecDeque, pin::Pin, sync::Arc, task::Poll};
|
||||
/// fn App(cx: Context, props: &AppProps) -> Element {
|
||||
/// cx.render(rsx!(
|
||||
/// NavBar { routes: ROUTES }
|
||||
/// Title { "{props.title}" }
|
||||
/// Title { "{cx.props.title}" }
|
||||
/// Footer {}
|
||||
/// ))
|
||||
/// }
|
||||
@@ -459,7 +459,7 @@ impl VirtualDom {
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust, ignore
|
||||
/// static App: FC<()> = |cx, props| cx.render(rsx!{ "hello world" });
|
||||
/// static App: Component<()> = |cx, props| cx.render(rsx!{ "hello world" });
|
||||
/// let mut dom = VirtualDom::new();
|
||||
/// let edits = dom.rebuild();
|
||||
///
|
||||
@@ -497,7 +497,7 @@ impl VirtualDom {
|
||||
/// value: Shared<&'static str>,
|
||||
/// }
|
||||
///
|
||||
/// static App: FC<AppProps> = |cx, props|{
|
||||
/// static App: Component<AppProps> = |cx, props|{
|
||||
/// let val = cx.value.borrow();
|
||||
/// cx.render(rsx! { div { "{val}" } })
|
||||
/// };
|
||||
|
||||
@@ -7,7 +7,6 @@ use dioxus_core::DomEdit::*;
|
||||
use dioxus_core::ScopeId;
|
||||
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_hooks::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@ This crate provides an ergonomic API for Dioxus to build desktop apps.
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
dioxus::desktop::launch(App, |c| c)
|
||||
dioxus::desktop::launch(App)
|
||||
}
|
||||
|
||||
static App: FC<()> = |cx, props| {
|
||||
let (count, set_count) = use_state(cx, || 0);
|
||||
static App: Component<()> = |cx| {
|
||||
let (count, set_count) = use_state(&cx, || 0);
|
||||
|
||||
cx.render(rsx!(
|
||||
WebviewWindow {
|
||||
@@ -34,7 +34,7 @@ Window management, system trays, notifications, and other desktop-related functi
|
||||
Managing windows is done by simply rendering content into a `WebviewWindow` component.
|
||||
|
||||
```rust
|
||||
static App: FC<()> = |cx, props| {
|
||||
static App: Component<()> = |cx| {
|
||||
rsx!(cx, WebviewWindow { "hello world" } )
|
||||
}
|
||||
```
|
||||
@@ -46,7 +46,7 @@ Notifications also use a declarative approach. Sending a notification has never
|
||||
The api has been somewhat modeled after https://github.com/mikaelbr/node-notifier
|
||||
|
||||
```rust
|
||||
static Notifications: FC<()> = |cx, props| {
|
||||
static Notifications: Component<()> = |cx| {
|
||||
cx.render(rsx!(
|
||||
Notification {
|
||||
title: "title"
|
||||
@@ -78,7 +78,7 @@ static Notifications: FC<()> = |cx, props| {
|
||||
Dioxus Desktop supports app trays, which can be built with native menu groups or with a custom window.
|
||||
|
||||
```rust
|
||||
static Tray: FC<()> = |cx, props| {
|
||||
static Tray: Component<()> = |cx| {
|
||||
cx.render(rsx!(
|
||||
GlobalTray {
|
||||
MenuGroup {
|
||||
@@ -90,7 +90,7 @@ static Tray: FC<()> = |cx, props| {
|
||||
};
|
||||
|
||||
// using a builder
|
||||
static Tray: FC<()> = |cx, props| {
|
||||
static Tray: Component<()> = |cx| {
|
||||
let menu = MenuGroup::builder(cx)
|
||||
.with_items([
|
||||
MenuGroupItem::builder()
|
||||
@@ -107,7 +107,7 @@ static Tray: FC<()> = |cx, props| {
|
||||
}
|
||||
|
||||
// or with a custom window
|
||||
static Tray: FC<()> = |cx, props| {
|
||||
static Tray: Component<()> = |cx| {
|
||||
rsx!(cx, GlobalTray { div { "custom buttons here" } })
|
||||
};
|
||||
```
|
||||
@@ -116,7 +116,7 @@ static Tray: FC<()> = |cx, props| {
|
||||
Declaring menus is convenient and cross-platform.
|
||||
|
||||
```rust
|
||||
static Menu: FC<()> = |cx, props| {
|
||||
static Menu: Component<()> = |cx| {
|
||||
cx.render(rsx!(
|
||||
MenuBarMajorItem { title: "File"
|
||||
MenuGroup {
|
||||
|
||||
@@ -12,11 +12,11 @@ use dioxus_html as dioxus_elements;
|
||||
|
||||
fn main() {
|
||||
simple_logger::init().unwrap();
|
||||
dioxus_desktop::launch(App, |c| c);
|
||||
dioxus_desktop::launch(app);
|
||||
}
|
||||
|
||||
static App: Component<()> = |cx, props| {
|
||||
let mut count = use_state(cx, || 0);
|
||||
fn app(cx: Scope<()>) -> Element {
|
||||
let mut count = use_state(&cx, || 0);
|
||||
log::debug!("count is {:?}", count);
|
||||
|
||||
cx.push_task(|| async move {
|
||||
|
||||
@@ -90,7 +90,7 @@ fn syntax_works() {
|
||||
use dioxus_hooks::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
static App: FC<()> = |cx, props| {
|
||||
static App: Component<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
// left window
|
||||
WebviewWindow {
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
mod cfg;
|
||||
mod escape;
|
||||
mod events;
|
||||
pub use wry;
|
||||
pub use wry::application as tao;
|
||||
|
||||
// mod desktop_context;
|
||||
|
||||
use cfg::DesktopConfig;
|
||||
@@ -28,7 +31,10 @@ use wry::{
|
||||
webview::{WebView, WebViewBuilder},
|
||||
};
|
||||
|
||||
pub fn launch(
|
||||
pub fn launch(root: Component<()>) {
|
||||
launch_with_props(root, (), |c| c)
|
||||
}
|
||||
pub fn launch_cfg(
|
||||
root: Component<()>,
|
||||
config_builder: impl for<'a, 'b> FnOnce(&'b mut DesktopConfig<'a>) -> &'b mut DesktopConfig<'a>,
|
||||
) {
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
[package]
|
||||
name = "dioxus-hooks"
|
||||
version = "0.0.0"
|
||||
version = "0.1.3"
|
||||
authors = ["Jonathan Kelley <jkelleyrtp@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
dioxus-core = { path = "../../packages/core", version = "0.1.3" }
|
||||
futures = "0.3"
|
||||
dioxus-core = { path = "../../packages/core" }
|
||||
@@ -16,10 +16,10 @@ You can always use it "normally" with the `split` method:
|
||||
|
||||
```rust
|
||||
// Normal usage:
|
||||
let value = use_state(cx, || 10);
|
||||
let value = use_state(&cx, || 10);
|
||||
|
||||
// "Classic" usage:
|
||||
let (value, set_value) = use_state(cx, || 0).split();
|
||||
let (value, set_value) = use_state(&cx, || 0).split();
|
||||
```
|
||||
|
||||
## use_ref
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
mod usestate;
|
||||
pub use usestate::{use_state, AsyncUseState, UseState};
|
||||
|
||||
mod usestate2;
|
||||
// pub use usestate2::use_state2;
|
||||
|
||||
mod useref;
|
||||
pub use useref::*;
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ This is moderately efficient because the fields of the map are moved, but the da
|
||||
However, if you used similar approach with Dioxus:
|
||||
|
||||
```rust
|
||||
let (map, set_map) = use_state(cx, || HashMap::new());
|
||||
let (map, set_map) = use_state(&cx, || HashMap::new());
|
||||
set_map({
|
||||
let mut newmap = map.clone();
|
||||
newmap.set(key, value);
|
||||
@@ -36,7 +36,7 @@ uses the same memoization on top of the use_context API.
|
||||
|
||||
Here's a fully-functional todo app using the use_map API:
|
||||
```rust
|
||||
static TodoList: FC<()> = |cx, props|{
|
||||
static TodoList: Component<()> = |cx, props|{
|
||||
let todos = use_map(cx, || HashMap::new());
|
||||
let input = use_ref(|| None);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use dioxus_core::ScopeState;
|
||||
use futures::Future;
|
||||
use std::future::Future;
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
pin::Pin,
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
//! In these cases, we provide `use_model` - a convenient way of abstracting over some state and async functions.
|
||||
|
||||
use dioxus_core::prelude::ScopeState;
|
||||
use futures::Future;
|
||||
use std::{
|
||||
cell::{Cell, Ref, RefCell, RefMut},
|
||||
future::Future,
|
||||
marker::PhantomData,
|
||||
pin::Pin,
|
||||
rc::Rc,
|
||||
|
||||
@@ -39,7 +39,7 @@ impl<'a, P, T> UseStateA<'a, T> for Scope<'a, P> {
|
||||
/// On top of the methods to set/get state, `use_state` also supports fancy combinators to extend its functionality:
|
||||
/// - `.classic()` and `.split()` convert the hook into the classic React-style hook
|
||||
/// ```rust
|
||||
/// let (state, set_state) = use_state(cx, || 10).split()
|
||||
/// let (state, set_state) = use_state(&cx, || 10).split()
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
|
||||
@@ -1,227 +0,0 @@
|
||||
// use dioxus_core::prelude::Context;
|
||||
// use std::{
|
||||
// borrow::{Borrow, BorrowMut},
|
||||
// cell::{Cell, Ref, RefCell, RefMut},
|
||||
// fmt::{Debug, Display},
|
||||
// ops::Not,
|
||||
// rc::Rc,
|
||||
// };
|
||||
|
||||
// /// Store state between component renders!
|
||||
// ///
|
||||
// /// ## Dioxus equivalent of UseStateInner2, designed for Rust
|
||||
// ///
|
||||
// /// The Dioxus version of `UseStateInner2` is the "king daddy" of state management. It allows you to ergonomically store and
|
||||
// /// modify state between component renders. When the state is updated, the component will re-render.
|
||||
// ///
|
||||
// /// Dioxus' use_state basically wraps a RefCell with helper methods and integrates it with the VirtualDOM update system.
|
||||
// ///
|
||||
// /// [`use_state`] exposes a few helper methods to modify the underlying state:
|
||||
// /// - `.set(new)` allows you to override the "work in progress" value with a new value
|
||||
// /// - `.get_mut()` allows you to modify the WIP value
|
||||
// /// - `.get_wip()` allows you to access the WIP value
|
||||
// /// - `.deref()` provides the previous value (often done implicitly, though a manual dereference with `*` might be required)
|
||||
// ///
|
||||
// /// Additionally, a ton of std::ops traits are implemented for the `UseStateInner2` wrapper, meaning any mutative type operations
|
||||
// /// will automatically be called on the WIP value.
|
||||
// ///
|
||||
// /// ## Combinators
|
||||
// ///
|
||||
// /// On top of the methods to set/get state, `use_state` also supports fancy combinators to extend its functionality:
|
||||
// /// - `.classic()` and `.split()` convert the hook into the classic React-style hook
|
||||
// /// ```rust
|
||||
// /// let (state, set_state) = use_state(cx, || 10).split()
|
||||
// /// ```
|
||||
// ///
|
||||
// ///
|
||||
// /// Usage:
|
||||
// /// ```ignore
|
||||
// /// const Example: FC<()> = |cx, props|{
|
||||
// /// let counter = use_state(cx, || 0);
|
||||
// /// let increment = |_| counter += 1;
|
||||
// /// let decrement = |_| counter += 1;
|
||||
// ///
|
||||
// /// html! {
|
||||
// /// <div>
|
||||
// /// <h1>"Counter: {counter}" </h1>
|
||||
// /// <button onclick={increment}> "Increment" </button>
|
||||
// /// <button onclick={decrement}> "Decrement" </button>
|
||||
// /// </div>
|
||||
// /// }
|
||||
// /// }
|
||||
// /// ```
|
||||
// pub fn use_state2<'a, T: 'static>(
|
||||
// cx: Context<'a>,
|
||||
// initial_state_fn: impl FnOnce() -> T,
|
||||
// ) -> &'a UseState2<T> {
|
||||
// cx.use_hook(
|
||||
// move |_| {
|
||||
// UseState2(Rc::new(UseStateInner2 {
|
||||
// current_val: initial_state_fn(),
|
||||
// update_callback: cx.schedule_update(),
|
||||
// wip: None,
|
||||
// update_scheuled: Cell::new(false),
|
||||
// }))
|
||||
// },
|
||||
// move |hook: &mut UseState2<T>| {
|
||||
// {
|
||||
// let r = hook.0.as_ref();
|
||||
// let mut state = r.borrow_mut();
|
||||
// state.update_scheuled.set(false);
|
||||
// if state.wip.is_some() {
|
||||
// state.current_val = state.wip.take().unwrap();
|
||||
// }
|
||||
// }
|
||||
// &*hook
|
||||
// },
|
||||
// )
|
||||
// }
|
||||
|
||||
// pub struct UseState2<T: 'static>(Rc<UseStateInner2<T>>);
|
||||
|
||||
// impl<T> ToOwned for UseState2<T> {
|
||||
// type Owned = UseState2<T>;
|
||||
// fn to_owned(&self) -> Self::Owned {
|
||||
// UseState2(self.0.clone())
|
||||
// }
|
||||
// }
|
||||
|
||||
// pub struct UseStateInner2<T: 'static> {
|
||||
// current_val: T,
|
||||
// update_scheuled: Cell<bool>,
|
||||
// update_callback: Rc<dyn Fn()>,
|
||||
// wip: Option<T>,
|
||||
// }
|
||||
|
||||
// impl<T: Debug> Debug for UseStateInner2<T> {
|
||||
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// write!(f, "{:?}", self.current_val)
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl<T> UseState2<T> {
|
||||
// /// Tell the Dioxus Scheduler that we need to be processed
|
||||
// pub fn needs_update(&self) {
|
||||
// if !self.update_scheuled.get() {
|
||||
// self.update_scheuled.set(true);
|
||||
// (self.update_callback)();
|
||||
// }
|
||||
// }
|
||||
|
||||
// pub fn set(&mut self, new_val: T) -> Option<T> {
|
||||
// self.needs_update();
|
||||
// ip.wip.replace(new_val)
|
||||
// }
|
||||
|
||||
// pub fn get(&self) -> &T {
|
||||
// &self.current_val
|
||||
// }
|
||||
// }
|
||||
|
||||
// // impl<T: 'static + ToOwned<Owned = T>> UseState2<T> {
|
||||
// // /// Gain mutable access to the new value. This method is only available when the value is a `ToOwned` type.
|
||||
// // ///
|
||||
// // /// Mutable access is derived by calling "ToOwned" (IE cloning) on the current value.
|
||||
// // ///
|
||||
// // /// To get a reference to the current value, use `.get()`
|
||||
// // pub fn modify(&self) -> RefMut<T> {
|
||||
// // // make sure we get processed
|
||||
// // self.0.needs_update();
|
||||
|
||||
// // // Bring out the new value, cloning if it we need to
|
||||
// // // "get_mut" is locked behind "ToOwned" to make it explicit that cloning occurs to use this
|
||||
// // RefMut::map(self.wip.borrow_mut(), |slot| {
|
||||
// // if slot.is_none() {
|
||||
// // *slot = Some(self.current_val.to_owned());
|
||||
// // }
|
||||
// // slot.as_mut().unwrap()
|
||||
// // })
|
||||
// // }
|
||||
|
||||
// // pub fn inner(self) -> T {
|
||||
// // self.current_val.to_owned()
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// impl<T> std::ops::Deref for UseStateInner2<T> {
|
||||
// type Target = T;
|
||||
|
||||
// fn deref(&self) -> &Self::Target {
|
||||
// self.get()
|
||||
// }
|
||||
// }
|
||||
|
||||
// use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
|
||||
|
||||
// use crate::UseState;
|
||||
|
||||
// impl<T: Copy + Add<T, Output = T>> Add<T> for UseStateInner2<T> {
|
||||
// type Output = T;
|
||||
|
||||
// fn add(self, rhs: T) -> Self::Output {
|
||||
// self.current_val.add(rhs)
|
||||
// }
|
||||
// }
|
||||
// impl<T: Copy + Add<T, Output = T>> AddAssign<T> for UseStateInner2<T> {
|
||||
// fn add_assign(&mut self, rhs: T) {
|
||||
// self.set(self.current_val.add(rhs));
|
||||
// }
|
||||
// }
|
||||
// impl<T: Copy + Sub<T, Output = T>> Sub<T> for UseStateInner2<T> {
|
||||
// type Output = T;
|
||||
|
||||
// fn sub(self, rhs: T) -> Self::Output {
|
||||
// self.current_val.sub(rhs)
|
||||
// }
|
||||
// }
|
||||
// impl<T: Copy + Sub<T, Output = T>> SubAssign<T> for UseStateInner2<T> {
|
||||
// fn sub_assign(&mut self, rhs: T) {
|
||||
// self.set(self.current_val.sub(rhs));
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// MUL
|
||||
// impl<T: Copy + Mul<T, Output = T>> Mul<T> for UseStateInner2<T> {
|
||||
// type Output = T;
|
||||
|
||||
// fn mul(self, rhs: T) -> Self::Output {
|
||||
// self.current_val.mul(rhs)
|
||||
// }
|
||||
// }
|
||||
// impl<T: Copy + Mul<T, Output = T>> MulAssign<T> for UseStateInner2<T> {
|
||||
// fn mul_assign(&mut self, rhs: T) {
|
||||
// self.set(self.current_val.mul(rhs));
|
||||
// }
|
||||
// }
|
||||
// /// DIV
|
||||
// impl<T: Copy + Div<T, Output = T>> Div<T> for UseStateInner2<T> {
|
||||
// type Output = T;
|
||||
|
||||
// fn div(self, rhs: T) -> Self::Output {
|
||||
// self.current_val.div(rhs)
|
||||
// }
|
||||
// }
|
||||
// impl<T: Copy + Div<T, Output = T>> DivAssign<T> for UseStateInner2<T> {
|
||||
// fn div_assign(&mut self, rhs: T) {
|
||||
// self.set(self.current_val.div(rhs));
|
||||
// }
|
||||
// }
|
||||
// impl<V, T: PartialEq<V>> PartialEq<V> for UseStateInner2<T> {
|
||||
// fn eq(&self, other: &V) -> bool {
|
||||
// self.get() == other
|
||||
// }
|
||||
// }
|
||||
// impl<O, T: Not<Output = O> + Copy> Not for UseStateInner2<T> {
|
||||
// type Output = O;
|
||||
|
||||
// fn not(self) -> Self::Output {
|
||||
// !*self.get()
|
||||
// }
|
||||
// }
|
||||
|
||||
// // enable displaty for the handle
|
||||
// impl<T: 'static + Display> std::fmt::Display for UseStateInner2<T> {
|
||||
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// write!(f, "{}", self.current_val)
|
||||
// }
|
||||
// }
|
||||
Whitespace-only changes.
@@ -40,7 +40,7 @@ async fn order_shoes(mut req: WebsocketRequest) -> Response {
|
||||
}
|
||||
|
||||
fn App(cx: Context, props: &()) -> Element {
|
||||
let mut count = use_state(cx, || 0);
|
||||
let mut count = use_state(&cx, || 0);
|
||||
cx.render(rsx!(
|
||||
button { onclick: move |_| count += 1, "Incr" }
|
||||
button { onclick: move |_| count -= 1, "Decr" }
|
||||
|
||||
@@ -69,7 +69,7 @@ impl<T: 'static + Send> WebviewRenderer<T> {
|
||||
redits: Option<Vec<DomEdit<'static>>>,
|
||||
) -> anyhow::Result<()> {
|
||||
// pub fn run_with_edits(
|
||||
// root: FC<T>,
|
||||
// root: Component<T>,
|
||||
// props: T,
|
||||
// user_builder: fn(WindowBuilder) -> WindowBuilder,
|
||||
// redits: Option<Vec<DomEdit<'static>>>,
|
||||
|
||||
@@ -16,7 +16,7 @@ enum AppRoute {
|
||||
NotFound
|
||||
}
|
||||
|
||||
static App: FC<()> = |cx, props| {
|
||||
static App: Component<()> = |cx| {
|
||||
let route = use_router(cx, AppRoute::parse);
|
||||
|
||||
match route {
|
||||
@@ -30,7 +30,7 @@ static App: FC<()> = |cx, props| {
|
||||
Adding links into your app:
|
||||
|
||||
```rust
|
||||
static Leaf: FC<()> = |cx, props| {
|
||||
static Leaf: Component<()> = |cx| {
|
||||
rsx!(cx, div {
|
||||
Link { to: AppRoute::Home }
|
||||
})
|
||||
|
||||
@@ -6,10 +6,10 @@ use dioxus_router::*;
|
||||
fn main() {
|
||||
console_error_panic_hook::set_once();
|
||||
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
|
||||
dioxus_web::launch(App, |c| c);
|
||||
dioxus_web::launch(APP);
|
||||
}
|
||||
|
||||
static App: Component<()> = |cx, props| {
|
||||
static APP: Component<()> = |cx| {
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
enum Route {
|
||||
Home,
|
||||
@@ -17,7 +17,7 @@ static App: Component<()> = |cx, props| {
|
||||
NotFound,
|
||||
}
|
||||
|
||||
let route = use_router(cx, |s| match s {
|
||||
let route = use_router(&cx, |s| match s {
|
||||
"/" => Route::Home,
|
||||
"/about" => Route::About,
|
||||
_ => Route::NotFound,
|
||||
|
||||
@@ -5,7 +5,7 @@ Render a Dioxus VirtualDOM to a string.
|
||||
|
||||
```rust
|
||||
// Our app:
|
||||
const App: FC<()> = |cx, props| rsx!(cx, div {"hello world!"});
|
||||
const App: Component<()> = |cx, props| rsx!(cx, div {"hello world!"});
|
||||
|
||||
// Build the virtualdom from our app
|
||||
let mut vdom = VirtualDOM::new(App);
|
||||
@@ -14,7 +14,7 @@ let mut vdom = VirtualDOM::new(App);
|
||||
let _ = vdom.rebuild();
|
||||
|
||||
// Render the entire virtualdom from the root
|
||||
let text = dioxus::ssr::render_vdom(&vdom, |c| c);
|
||||
let text = dioxus::ssr::render_vdom(&vdom);
|
||||
assert_eq!(text, "<div>hello world!</div>")
|
||||
```
|
||||
|
||||
@@ -30,7 +30,7 @@ With pre-rendering enabled, this crate will generate element nodes with Element
|
||||
To enable pre-rendering, simply configure the `SsrConfig` with pre-rendering enabled.
|
||||
|
||||
```rust
|
||||
let dom = VirtualDom::new(App, |c| c);
|
||||
let dom = VirtualDom::new(App);
|
||||
|
||||
let text = dioxus::ssr::render_vdom(App, |cfg| cfg.pre_render(true));
|
||||
```
|
||||
@@ -40,7 +40,7 @@ let text = dioxus::ssr::render_vdom(App, |cfg| cfg.pre_render(true));
|
||||
Dioxus SSR can also be to render on the server. Obviously, you can just render the VirtualDOM to a string and send that down.
|
||||
|
||||
```rust
|
||||
let text = dioxus::ssr::render_vdom(&vdom, |c| c);
|
||||
let text = dioxus::ssr::render_vdom(&vdom);
|
||||
assert_eq!(text, "<div>hello world!</div>")
|
||||
```
|
||||
|
||||
|
||||
+10
-7
@@ -82,7 +82,10 @@ pub fn render_lazy<'a>(f: Option<LazyNodes<'a, '_>>) -> String {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn render_vdom(dom: &VirtualDom, cfg: impl FnOnce(SsrConfig) -> SsrConfig) -> String {
|
||||
pub fn render_vdom(dom: &VirtualDom) -> String {
|
||||
format!("{:}", TextRenderer::from_vdom(dom, SsrConfig::default()))
|
||||
}
|
||||
pub fn render_vdom_cfg(dom: &VirtualDom, cfg: impl FnOnce(SsrConfig) -> SsrConfig) -> String {
|
||||
format!(
|
||||
"{:}",
|
||||
TextRenderer::from_vdom(dom, cfg(SsrConfig::default()))
|
||||
@@ -110,7 +113,7 @@ pub fn render_vdom_scope(vdom: &VirtualDom, scope: ScopeId) -> Option<String> {
|
||||
///
|
||||
/// ## Example
|
||||
/// ```ignore
|
||||
/// static App: FC<()> = |cx, props|cx.render(rsx!(div { "hello world" }));
|
||||
/// static App: Component<()> = |cx, props|cx.render(rsx!(div { "hello world" }));
|
||||
/// let mut vdom = VirtualDom::new(App);
|
||||
/// vdom.rebuild();
|
||||
///
|
||||
@@ -343,28 +346,28 @@ mod tests {
|
||||
fn to_string_works() {
|
||||
let mut dom = VirtualDom::new(SIMPLE_APP);
|
||||
dom.rebuild();
|
||||
dbg!(render_vdom(&dom, |c| c));
|
||||
dbg!(render_vdom(&dom));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hydration() {
|
||||
let mut dom = VirtualDom::new(NESTED_APP);
|
||||
dom.rebuild();
|
||||
dbg!(render_vdom(&dom, |c| c.pre_render(true)));
|
||||
dbg!(render_vdom_cfg(&dom, |c| c.pre_render(true)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested() {
|
||||
let mut dom = VirtualDom::new(NESTED_APP);
|
||||
dom.rebuild();
|
||||
dbg!(render_vdom(&dom, |c| c));
|
||||
dbg!(render_vdom(&dom));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fragment_app() {
|
||||
let mut dom = VirtualDom::new(FRAGMENT_APP);
|
||||
dom.rebuild();
|
||||
dbg!(render_vdom(&dom, |c| c));
|
||||
dbg!(render_vdom(&dom));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -394,7 +397,7 @@ mod tests {
|
||||
|
||||
let mut dom = VirtualDom::new(STLYE_APP);
|
||||
dom.rebuild();
|
||||
dbg!(render_vdom(&dom, |c| c));
|
||||
dbg!(render_vdom(&dom));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -11,11 +11,11 @@ use dioxus_web;
|
||||
use gloo_timers::future::TimeoutFuture;
|
||||
|
||||
fn main() {
|
||||
dioxus_web::launch(App, |c| c);
|
||||
dioxus_web::launch(App);
|
||||
}
|
||||
|
||||
static App: Component<()> = |cx, props| {
|
||||
let mut count = use_state(cx, || 0);
|
||||
static App: Component<()> = |cx| {
|
||||
let mut count = use_state(&cx, || 0);
|
||||
|
||||
cx.push_task(|| async move {
|
||||
TimeoutFuture::new(100).await;
|
||||
|
||||
@@ -6,7 +6,6 @@ use dioxus_core_macro::*;
|
||||
use dioxus_hooks::{use_ref, use_state};
|
||||
use dioxus_html as dioxus_elements;
|
||||
use dioxus_web;
|
||||
use gloo_timers::future::TimeoutFuture;
|
||||
use rand::prelude::*;
|
||||
|
||||
fn main() {
|
||||
@@ -73,7 +72,7 @@ fn main() {
|
||||
wasm_bindgen::intern(&x.to_string());
|
||||
}
|
||||
|
||||
dioxus_web::launch(App, |c| c.rootname("main"));
|
||||
dioxus_web::launch(App);
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Copy)]
|
||||
@@ -107,9 +106,9 @@ impl Label {
|
||||
}
|
||||
}
|
||||
|
||||
static App: Component<()> = |cx, _props| {
|
||||
let mut items = use_ref(cx, || vec![]);
|
||||
let mut selected = use_state(cx, || None);
|
||||
static App: Component<()> = |cx| {
|
||||
let mut items = use_ref(&cx, || vec![]);
|
||||
let mut selected = use_state(&cx, || None);
|
||||
|
||||
cx.render(rsx! {
|
||||
div { class: "container"
|
||||
@@ -175,10 +174,10 @@ struct ActionButtonProps<'a> {
|
||||
onclick: &'a dyn Fn(),
|
||||
}
|
||||
|
||||
fn ActionButton(cx: Scope, props: &ActionButtonProps) -> Element {
|
||||
fn ActionButton<'a>(cx: Scope<'a, ActionButtonProps<'a>>) -> Element {
|
||||
rsx!(cx, div { class: "col-sm-6 smallpad"
|
||||
button { class:"btn btn-primary btn-block", r#type: "button", id: "{props.id}", onclick: move |_| (props.onclick)(),
|
||||
"{props.name}"
|
||||
button { class:"btn btn-primary btn-block", r#type: "button", id: "{cx.props.id}", onclick: move |_| (cx.props.onclick)(),
|
||||
"{cx.props.name}"
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -229,9 +228,9 @@ static NOUNS: &[&str] = &[
|
||||
|
||||
// fn Row(cx: Context, props: &RowProps) -> Element {
|
||||
// rsx!(cx, tr {
|
||||
// td { class:"col-md-1", "{props.row_id}" }
|
||||
// td { class:"col-md-1", "{cx.props.row_id}" }
|
||||
// td { class:"col-md-1", onclick: move |_| { /* run onselect */ }
|
||||
// a { class: "lbl", {props.label.labels} }
|
||||
// a { class: "lbl", {cx.props.label.labels} }
|
||||
// }
|
||||
// td { class: "col-md-1"
|
||||
// a { class: "remove", onclick: move |_| {/* remove */}
|
||||
|
||||
@@ -7,16 +7,14 @@ use dioxus_core as dioxus;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_hooks::use_state;
|
||||
use dioxus_html as dioxus_elements;
|
||||
use dioxus_web;
|
||||
use gloo_timers::future::TimeoutFuture;
|
||||
|
||||
fn main() {
|
||||
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
|
||||
dioxus_web::launch(App, |c| c);
|
||||
dioxus_web::launch(App);
|
||||
}
|
||||
|
||||
static App: Component<()> = |cx, props| {
|
||||
let show = use_state(cx, || true);
|
||||
static App: Component<()> = |cx| {
|
||||
let show = use_state(&cx, || true);
|
||||
|
||||
let inner = match *show {
|
||||
true => {
|
||||
|
||||
@@ -11,10 +11,10 @@ use dioxus_html as dioxus_elements;
|
||||
use dioxus_web;
|
||||
|
||||
fn main() {
|
||||
dioxus_web::launch(App, |c| c);
|
||||
dioxus_web::launch(App);
|
||||
}
|
||||
|
||||
static App: Component<()> = |cx, _| {
|
||||
static App: Component<()> = |cx| {
|
||||
let doggo = cx.suspend(|| async move {
|
||||
#[derive(serde::Deserialize)]
|
||||
struct Doggo {
|
||||
|
||||
@@ -84,12 +84,12 @@ mod ric_raf;
|
||||
/// dioxus_web::launch(App);
|
||||
/// }
|
||||
///
|
||||
/// static App: Component<()> = |cx, props| {
|
||||
/// static App: Component<()> = |cx| {
|
||||
/// rsx!(cx, div {"hello world"})
|
||||
/// }
|
||||
/// ```
|
||||
pub fn launch(root_component: Component<()>) {
|
||||
launch_with_props(root_component, (), |c| c);
|
||||
launch_with_props(root_component, ());
|
||||
}
|
||||
|
||||
/// Launches the VirtualDOM from the specified component function and props.
|
||||
@@ -100,7 +100,7 @@ pub fn launch(root_component: Component<()>) {
|
||||
///
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// dioxus_web::launch_with_props(App, RootProps { name: String::from("joe") }, |c| c);
|
||||
/// dioxus_web::launch_with_props(App, RootProps { name: String::from("joe") });
|
||||
/// }
|
||||
///
|
||||
/// #[derive(ParitalEq, Props)]
|
||||
@@ -108,8 +108,8 @@ pub fn launch(root_component: Component<()>) {
|
||||
/// name: String
|
||||
/// }
|
||||
///
|
||||
/// static App: Component<RootProps> = |cx, props| {
|
||||
/// rsx!(cx, div {"hello {props.name}"})
|
||||
/// static App: Component<RootProps> = |cx| {
|
||||
/// rsx!(cx, div {"hello {cx.props.name}"})
|
||||
/// }
|
||||
/// ```
|
||||
pub fn launch_with_props<T, F>(
|
||||
@@ -132,7 +132,7 @@ pub fn launch_with_props<T, F>(
|
||||
///
|
||||
/// ```ignore
|
||||
/// fn main() {
|
||||
/// let app_fut = dioxus_web::run_with_props(App, RootProps { name: String::from("joe") }, |c| c);
|
||||
/// let app_fut = dioxus_web::run_with_props(App, RootProps { name: String::from("joe") });
|
||||
/// wasm_bindgen_futures::spawn_local(app_fut);
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
Reference in new issue
Block a user