mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-22 20:23:09 +00:00
Feat: some docs
This commit is contained in:
parent
508c560320
commit
1919f88f03
10 changed files with 179 additions and 182 deletions
2
.vscode/spellright.dict
vendored
2
.vscode/spellright.dict
vendored
|
@ -43,3 +43,5 @@ virtualdom
|
|||
namespaced
|
||||
namespacing
|
||||
impl
|
||||
destructured
|
||||
linting
|
||||
|
|
|
@ -10,7 +10,7 @@ struct MyProps {
|
|||
|
||||
fn Example(ctx: Context<MyProps>) -> VNode {
|
||||
ctx.render(html! {
|
||||
<div> "Hello {ctx.props().name}!" </div>
|
||||
<div> "Hello {ctx.name}!" </div>
|
||||
})
|
||||
}
|
||||
```
|
||||
|
|
|
@ -35,7 +35,7 @@ struct ExampleProps {
|
|||
fn Example(ctx: &mut Context<ExampleProps>) -> VNode {
|
||||
let ExampleProps {
|
||||
name, pending, count
|
||||
} = ctx.props();
|
||||
} = ctx.props;
|
||||
|
||||
rsx! {
|
||||
<div>
|
||||
|
|
|
@ -4,10 +4,12 @@ Dioxus comes preloaded with two macros for creating VNodes.
|
|||
|
||||
## html! macro
|
||||
|
||||
The html! macro supports the html standard. This macro will happily accept a copy-paste from something like tailwind builder. Writing this one by hand is a bit tedious and doesn't come with much help from Rust IDE tools.
|
||||
The html! macro supports a limited subset of the html standard. This macro will happily accept a copy-paste from something like tailwind builder. However, writing HTML by hand is a bit tedious - IDE tools for Rust don't support linting/autocomplete/syntax highlighting. RSX is much more natural for Rust programs and _does_ integrate well with Rust IDE tools.
|
||||
|
||||
There is also limited support for dynamic handlers, but it will function similarly to JSX.
|
||||
|
||||
You'll want to write RSX where you can, and in a future release we'll have a tool that automatically converts HTML to RSX.
|
||||
|
||||
```rust
|
||||
#[fc]
|
||||
fn Example(ctx: Context, name: &str, pending: bool, count: i32 ) -> VNode {
|
||||
|
@ -23,9 +25,9 @@ fn Example(ctx: Context, name: &str, pending: bool, count: i32 ) -> VNode {
|
|||
|
||||
## rsx! macro
|
||||
|
||||
The rsx! macro is a VNode builder macro designed especially for Rust. Writing these should feel very natural, much like assembling a struct. VSCode also supports these with code folding, bracket-tabbing, bracket highlighting, and section selecting.
|
||||
The rsx! macro is a VNode builder macro designed especially for Rust programs. Writing these should feel very natural, much like assembling a struct. VSCode also supports these with code folding, bracket-tabbing, bracket highlighting, and section selecting.
|
||||
|
||||
The Dioxus VSCode extension provides a function to convert a selection of html! template and turn it into rsx!, so you'll never need to transcribe templates by hand.
|
||||
The Dioxus VSCode extension will eventually provide a macro to convert a selection of html! template and turn it into rsx!, so you'll never need to transcribe templates by hand.
|
||||
|
||||
It's also a bit easier on the eyes 🙂.
|
||||
|
||||
|
@ -51,30 +53,55 @@ Each element takes a comma-separated list of expressions to build the node. Roug
|
|||
- `CustomTag {}` adds a new child component
|
||||
- `{expr}` pastes the `expr` tokens literally. They must be IntoCtx<Vnode> to work properly
|
||||
|
||||
Lists must include commas, much like how struct definitions work.
|
||||
Commas are entirely optional, but might be useful to delineate between elements and attributes.
|
||||
|
||||
The `render` function provides an **extremely efficient** allocator for VNodes and text, so try not to use the `format!` macro in your components. Rust's default `ToString` methods pass through the global allocator, but all text in components is allocated inside a manually-managed Bump arena.
|
||||
|
||||
```rust
|
||||
static Example: FC<()> = |ctx| {
|
||||
|
||||
let text = "example";
|
||||
|
||||
ctx.render(rsx!{
|
||||
div {
|
||||
h1 { "Example" },
|
||||
|
||||
// fstring interpolation
|
||||
"{text}"
|
||||
|
||||
p {
|
||||
// Props
|
||||
// Attributes
|
||||
tag: "type",
|
||||
|
||||
// Anything that implements display can be an attribute
|
||||
abc: 123,
|
||||
enabled: true,
|
||||
class: "big small wide short",
|
||||
|
||||
// attributes also supports interpolation
|
||||
// `class` is not a restricted keyword unlike JS and ClassName
|
||||
class: "big small wide short {text}",
|
||||
|
||||
// bool-based classnames
|
||||
classes: [("big", true), ("small", false)]
|
||||
|
||||
// Bool-based props
|
||||
// *must* be in the tuple form, cannot enter as a variable
|
||||
tag: ("type", false)
|
||||
|
||||
tag: {"these tokens are placed directly"}
|
||||
|
||||
// Children
|
||||
a { "abcder" },
|
||||
|
||||
// Children with props
|
||||
h2 { "whatsup", class: "abc-123" },
|
||||
// Children with attributes
|
||||
h2 { "hello", class: "abc-123" },
|
||||
|
||||
// Child components
|
||||
CustomComponent { a: 123, b: 456, key: "1" },
|
||||
|
||||
// Child components with paths
|
||||
crate::components::CustomComponent { a: 123, b: 456, key: "1" },
|
||||
|
||||
// Iterators
|
||||
{ 0..3.map(|i| rsx!{ h1 {"{:i}"} }) },
|
||||
|
||||
|
@ -82,7 +109,34 @@ static Example: FC<()> = |ctx| {
|
|||
{ rsx! { div { } } },
|
||||
{ html! { <div> </div> } },
|
||||
|
||||
// Any expression that is Into<VNode>
|
||||
// Matching
|
||||
// Requires rendering the nodes first.
|
||||
// rsx! is lazy, and the underlying closures cannot have the same type
|
||||
// Rendering produces the VNode type
|
||||
{match rand::gen_range::<i32>(1..3) {
|
||||
1 => rsx!(in ctx, h1 { "big" })
|
||||
2 => rsx!(in ctx, h2 { "medium" })
|
||||
_ => rsx!(in ctx, h3 { "small" })
|
||||
}}
|
||||
|
||||
// Optionals
|
||||
{true.and_then(|f| rsx!{ h1 {"Conditional Rendering"} })}
|
||||
|
||||
// Bool options
|
||||
{(rsx!{ h1 {"Conditional Rendering"}, true)}
|
||||
|
||||
// Child nodes
|
||||
// Returns &[VNode]
|
||||
{ctx.children()}
|
||||
|
||||
// Duplicating nodes
|
||||
// Clones the nodes by reference, so they are literally identical
|
||||
{{
|
||||
let node = rsx!(in ctx, h1{ "TopNode" });
|
||||
(0..10).map(|_| node.clone())
|
||||
}}
|
||||
|
||||
// Any expression that is `IntoVNode`
|
||||
{expr}
|
||||
}
|
||||
}
|
||||
|
|
56
docs/website/homepage/ex1.md
Normal file
56
docs/website/homepage/ex1.md
Normal file
|
@ -0,0 +1,56 @@
|
|||
# A Simple Component
|
||||
|
||||
```rust
|
||||
#[derive(PartialEq, Properties)]
|
||||
struct Props { name: &'static str }
|
||||
|
||||
static HelloMessage: FC<Props> = |ctx| {
|
||||
ctx.render(rsx!{
|
||||
div { "Hello {ctx.props.name}" }
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
# Any syntax you like
|
||||
|
||||
Choose from a close-to-html syntax or the standard rsx! syntax
|
||||
|
||||
```rust
|
||||
static HelloMessage: FC<()> = |ctx| {
|
||||
ctx.render(html!{
|
||||
<div> Hello World! </div>
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
# A Stateful Component
|
||||
|
||||
Store state with hooks!
|
||||
|
||||
```rust
|
||||
enum LightState {
|
||||
Green
|
||||
Yellow,
|
||||
Red,
|
||||
}
|
||||
static HelloMessage: FC<()> = |ctx| {
|
||||
let (color, set_color) = use_state(ctx, || LightState::Green);
|
||||
|
||||
let title = match color {
|
||||
Green => "Green means go",
|
||||
Yellow => "Yellow means slow down",
|
||||
Red => "Red means stop",
|
||||
};
|
||||
|
||||
ctx.render(rsx!{
|
||||
h1 {"{title}"}
|
||||
button { "tick"
|
||||
onclick: move |_| set_color(match color {
|
||||
Green => Yellow,
|
||||
Yellow => Red,
|
||||
Red => Green,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
|
@ -22,6 +22,7 @@ static Example: FC<()> = |ctx| {
|
|||
//
|
||||
rsx! { in ctx,
|
||||
div {
|
||||
h1 {"these are children nodes: "}
|
||||
{nodes}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,11 @@ static Header: FC<()> = |ctx| {
|
|||
|
||||
ctx.render(dioxus::prelude::LazyNodes::new(|nodectx| {
|
||||
builder::ElementBuilder::new(nodectx, "div")
|
||||
.child(VNode::Component(VComponent::new(Bottom, (), None)))
|
||||
.child(VNode::Component(nodectx.bump().alloc(VComponent::new(
|
||||
Bottom,
|
||||
(),
|
||||
None,
|
||||
))))
|
||||
.finish()
|
||||
}))
|
||||
};
|
||||
|
@ -36,128 +40,3 @@ fn Top(ctx: Context<()>) -> VNode {
|
|||
</div>
|
||||
})
|
||||
}
|
||||
|
||||
struct Callback<T>(Box<T>);
|
||||
|
||||
// impl<O, T: Fn() -> O> From<T> for Callback<T> {
|
||||
// fn from(_: T) -> Self {
|
||||
// todo!()
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<O, A> From<&dyn Fn(A) -> O> for Callback<&dyn Fn(A) -> O> {
|
||||
fn from(_: &dyn Fn(A) -> O) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<O, A, B> From<&dyn Fn(A, B) -> O> for Callback<&dyn Fn(A, B) -> O> {
|
||||
fn from(_: &dyn Fn(A, B) -> O) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
// compile time reordering of arguments
|
||||
// Allows for transparently calling
|
||||
#[derive(Default)]
|
||||
pub struct Args<A, B, C> {
|
||||
pub a: CuOpt<A>,
|
||||
pub b: CuOpt<B>,
|
||||
pub c: CuOpt<C>,
|
||||
}
|
||||
|
||||
pub enum CuOpt<T> {
|
||||
Some(T),
|
||||
None,
|
||||
}
|
||||
impl<T> Default for CuOpt<T> {
|
||||
fn default() -> Self {
|
||||
CuOpt::None
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> CuOpt<T> {
|
||||
fn unwrap(self) -> T {
|
||||
match self {
|
||||
CuOpt::Some(t) => t,
|
||||
CuOpt::None => panic!(""),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait IsMemo {
|
||||
fn memo(&self, other: &Self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq> IsMemo for CuOpt<T> {
|
||||
fn memo(&self, other: &Self) -> bool {
|
||||
self == other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq> PartialEq for CuOpt<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(CuOpt::Some(a), CuOpt::Some(b)) => a == b,
|
||||
(CuOpt::Some(_), CuOpt::None) => false,
|
||||
(CuOpt::None, CuOpt::Some(_)) => false,
|
||||
(CuOpt::None, CuOpt::None) => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IsMemo for &CuOpt<T> {
|
||||
fn memo(&self, other: &Self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// #[test]
|
||||
#[test]
|
||||
fn try_test() {
|
||||
// test_poc()
|
||||
}
|
||||
|
||||
// fn test_poc(ctx: Context) {
|
||||
// let b = Bump::new();
|
||||
|
||||
// let h = Args {
|
||||
// a: CuOpt::Some("ASD"),
|
||||
// b: CuOpt::Some(123),
|
||||
// c: CuOpt::Some(|| {}),
|
||||
// // c: CuOpt::Some(b.alloc(|| {})),
|
||||
// // c: CuOpt::Some(Box::new(|| {}) as Box<dyn Fn()>),
|
||||
// };
|
||||
|
||||
// let h2 = Args {
|
||||
// a: CuOpt::Some("ASD"),
|
||||
// b: CuOpt::Some(123),
|
||||
// c: CuOpt::Some(|| {}),
|
||||
// // c: CuOpt::Some(b.alloc(|| {})),
|
||||
// // c: CuOpt::Some(Box::new(|| {}) as Box<dyn Fn()>),
|
||||
// // c: CuOpt::Some(Box::new(|| {}) as Box<dyn Fn()>),
|
||||
// // c: CuOpt::Some(Box::new(|| {}) as Box<dyn Fn()>),
|
||||
// };
|
||||
|
||||
// // dbg!((&h.a).memo((&&h2.a)));
|
||||
// // dbg!((&h.b).memo((&&h2.b)));
|
||||
// // dbg!((&h.c).memo((&&h2.c)));
|
||||
// //
|
||||
// // ctx: Context
|
||||
// Top(ctx, &h.a.unwrap(), &h.b.unwrap(), &h.c.unwrap());
|
||||
// }
|
||||
|
||||
// fn test_realzies() {
|
||||
// let h = Args {
|
||||
// a: CuOpt::Some("ASD"),
|
||||
// b: CuOpt::Some(123),
|
||||
// c: CuOpt::Some(|| {}),
|
||||
// };
|
||||
|
||||
// let g = |ctx: Context| {
|
||||
// //
|
||||
// Top(ctx, &h.a.unwrap(), &h.b.unwrap(), &h.c.unwrap())
|
||||
// };
|
||||
// }
|
||||
|
|
|
@ -486,14 +486,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
// impl IntoIterator for VNode {
|
||||
// type Item = VNode;
|
||||
// type IntoIter = std::iter::Once<Self::Item>;
|
||||
// fn into_iter(self) -> Self::IntoIter {
|
||||
// std::iter::once(self)
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<'a> IntoIterator for VNode<'a> {
|
||||
type Item = VNode<'a>;
|
||||
type IntoIter = std::iter::Once<Self::Item>;
|
||||
|
@ -665,8 +657,9 @@ pub fn virtual_child<'a, T: Properties>(
|
|||
) -> VNode<'a> {
|
||||
// currently concerned about if props have a custom drop implementation
|
||||
// might override it with the props macro
|
||||
VNode::Component(
|
||||
ctx.bump()
|
||||
.alloc(crate::nodes::VComponent::new(f, props, key)),
|
||||
)
|
||||
todo!()
|
||||
// VNode::Component(
|
||||
// ctx.bump()
|
||||
// .alloc(crate::nodes::VComponent::new(f, props, key)),
|
||||
// )
|
||||
}
|
||||
|
|
|
@ -235,7 +235,7 @@ pub struct VComponent<'src> {
|
|||
pub children: &'src [VNode<'src>],
|
||||
|
||||
// a pointer into the bump arena (given by the 'src lifetime)
|
||||
raw_props: Box<dyn Any>,
|
||||
// raw_props: Box<dyn Any>,
|
||||
// raw_props: *const (),
|
||||
|
||||
// a pointer to the raw fn typ
|
||||
|
@ -243,13 +243,24 @@ pub struct VComponent<'src> {
|
|||
_p: PhantomData<&'src ()>,
|
||||
}
|
||||
|
||||
unsafe fn transmogrify<'a>(p: impl Properties + 'a) -> impl Properties + 'static {
|
||||
todo!()
|
||||
}
|
||||
impl<'a> VComponent<'a> {
|
||||
// use the type parameter on props creation and move it into a portable context
|
||||
// this lets us keep scope generic *and* downcast its props when we need to:
|
||||
// - perform comparisons when diffing (memoization)
|
||||
// TODO: lift the requirement that props need to be static
|
||||
// we want them to borrow references... maybe force implementing a "to_static_unsafe" trait
|
||||
pub fn new<P: Properties>(component: FC<P>, props: P, key: Option<&'a str>) -> Self {
|
||||
|
||||
pub fn new<P: Properties>(
|
||||
component: FC<P>,
|
||||
// props: bumpalo::boxed::Box<'a, P>,
|
||||
props: P,
|
||||
key: Option<&'a str>,
|
||||
) -> Self {
|
||||
// pub fn new<P: Properties + 'a>(component: FC<P>, props: P, key: Option<&'a str>) -> Self {
|
||||
// let bad_props = unsafe { transmogrify(props) };
|
||||
let caller_ref = component as *const ();
|
||||
|
||||
// let raw_props = props as *const P as *const ();
|
||||
|
@ -271,7 +282,19 @@ impl<'a> VComponent<'a> {
|
|||
// }
|
||||
// };
|
||||
|
||||
let caller: Rc<dyn Fn(&Scope) -> VNode + 'a> = Rc::new(move |scp| todo!());
|
||||
// let prref: &'a P = props.as_ref();
|
||||
|
||||
let caller: Rc<dyn Fn(&Scope) -> VNode> = Rc::new(move |scope| {
|
||||
//
|
||||
// let props2 = bad_props;
|
||||
// props.as_ref()
|
||||
// let ctx = Context {
|
||||
// props: prref,
|
||||
// scope,
|
||||
// };
|
||||
todo!()
|
||||
// component(ctx)
|
||||
});
|
||||
// let caller = Rc::new(create_closure(component, raw_props));
|
||||
|
||||
let key = match key {
|
||||
|
@ -283,7 +306,7 @@ impl<'a> VComponent<'a> {
|
|||
key,
|
||||
ass_scope: Rc::new(RefCell::new(None)),
|
||||
user_fc: caller_ref,
|
||||
raw_props: Box::new(props),
|
||||
// raw_props: Box::new(props),
|
||||
_p: PhantomData,
|
||||
children: &[],
|
||||
caller,
|
||||
|
@ -292,18 +315,3 @@ impl<'a> VComponent<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fn create_closure<P: Properties>(
|
||||
// component: FC<P>,
|
||||
// raw_props: *const (),
|
||||
// ) -> impl for<'r> Fn(&'r Scope) -> VNode<'r> {
|
||||
// move |ctx| -> VNode {
|
||||
// // cast back into the right lifetime
|
||||
// let safe_props: &P = unsafe { &*(raw_props as *const P) };
|
||||
// component(Context {
|
||||
// props: safe_props,
|
||||
// scope: ctx,
|
||||
// })
|
||||
// // component(ctx, safe_props)
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -137,12 +137,11 @@ impl VirtualDom {
|
|||
|
||||
// Normally, a component would be passed as a child in the RSX macro which automatically produces OpaqueComponents
|
||||
// Here, we need to make it manually, using an RC to force the Weak reference to stick around for the main scope.
|
||||
let _root_caller: Rc<OpaqueComponent> = Rc::new(move |ctx| {
|
||||
todo!()
|
||||
// root(Context {
|
||||
// props: &root_props,
|
||||
// scope: ctx,
|
||||
// })
|
||||
let _root_caller: Rc<OpaqueComponent<'static>> = Rc::new(move |scope| {
|
||||
// the lifetime of this closure is just as long as the lifetime on the scope reference
|
||||
// this closure moves root props (which is static) into this closure
|
||||
let props = unsafe { &*(&root_props as *const _) };
|
||||
root(Context { props, scope })
|
||||
});
|
||||
|
||||
// Create a weak reference to the OpaqueComponent for the root scope to use as its render function
|
||||
|
@ -618,18 +617,23 @@ impl Scope {
|
|||
.ok_or(Error::FatalInternal("Failed to get caller"))?;
|
||||
|
||||
// Cast the caller ptr from static to one with our own reference
|
||||
let new_head = unsafe {
|
||||
std::mem::transmute::<&OpaqueComponent<'static>, &OpaqueComponent<'sel>>(
|
||||
caller.as_ref(),
|
||||
)
|
||||
}(&self);
|
||||
let c2: &OpaqueComponent<'static> = caller.as_ref();
|
||||
let c3: &OpaqueComponent<'sel> = unsafe { std::mem::transmute(c2) };
|
||||
|
||||
todo!();
|
||||
// self.frames.cur_frame_mut().head_node = new_head;
|
||||
let unsafe_head = unsafe { self.own_vnodes(c3) };
|
||||
|
||||
self.frames.cur_frame_mut().head_node = unsafe_head;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// this is its own function so we can preciesly control how lifetimes flow
|
||||
unsafe fn own_vnodes<'a>(&'a self, f: &OpaqueComponent<'a>) -> VNode<'static> {
|
||||
let new_head: VNode<'a> = f(self);
|
||||
let out: VNode<'static> = std::mem::transmute(new_head);
|
||||
out
|
||||
}
|
||||
|
||||
// A safe wrapper around calling listeners
|
||||
// calling listeners will invalidate the list of listeners
|
||||
// The listener list will be completely drained because the next frame will write over previous listeners
|
||||
|
|
Loading…
Reference in a new issue