From 3a4860add4f229cd6e9b10fade1d1b30d1f895c6 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Wed, 19 Jun 2024 01:06:54 +0200 Subject: [PATCH] Document props and component macro (#2522) * document prop attributes * document children props * fix children doc test --- packages/core-macro/docs/component.md | 323 ++++++++++++++++++++++++ packages/core-macro/docs/props.md | 339 ++++++++++++++++++++++++++ packages/core-macro/docs/rsx.md | 185 ++++++++++++++ packages/core-macro/src/lib.rs | 235 +----------------- packages/core/src/global_context.rs | 2 +- packages/core/src/scope_context.rs | 2 +- packages/dioxus/src/lib.rs | 2 +- packages/rsx/src/sub_templates.rs | 2 +- 8 files changed, 861 insertions(+), 229 deletions(-) create mode 100644 packages/core-macro/docs/component.md create mode 100644 packages/core-macro/docs/props.md create mode 100644 packages/core-macro/docs/rsx.md diff --git a/packages/core-macro/docs/component.md b/packages/core-macro/docs/component.md new file mode 100644 index 000000000..f70958de1 --- /dev/null +++ b/packages/core-macro/docs/component.md @@ -0,0 +1,323 @@ +# Component + +The component macro turns a function with arguments that are [`Clone`] and [`PartialEq`] into a component. This is the recommended way of creating most components. If you want more fine grained control over how the overall prop struct implements the `Properties` trait, you can use an explicit props struct with the [`Props`] derive macro instead. + +## Arguments + +- `no_case_check` - Doesn't enforce `PascalCase` on your component names. + **This will be removed/deprecated in a future update in favor of a more complete Clippy-backed linting system.** + The reasoning behind this is that Clippy allows more robust and powerful lints, whereas + macros are extremely limited. + +## Features + +This attribute: + +- Enforces that your component uses `PascalCase` or `snake_case` with at least one underscore. +- Automatically creates a prop struct for your component if the function has arguments. +- Verifies the function signature is valid as a component. + +## Examples + +- Without props: + +```rust, no_run +# use dioxus::prelude::*; +#[component] +fn GreetBob() -> Element { + rsx! { "hello, bob" } +} +``` + +- With props: + +```rust, no_run +# use dioxus::prelude::*; +#[component] +fn GreetBob(bob: String) -> Element { + rsx! { "hello, {bob}" } +} +``` + +## Prop Modifiers + +You can use the `#[props()]` attribute to modify the behavior of the props the component macro creates: + +- [`#[props(default)]`](#default-props) - Makes the field optional in the component and uses the default value if it is not set when creating the component. +- [`#[props(!optional)]`](#optional-props) - Makes a field with the type `Option` required. +- [`#[props(into)]`](#converting-props) - Converts a field into the correct type by using the [`Into`] trait. +- [`#[props(extends = GlobalAttributes)]`](#extending-elements) - Extends the props with all the attributes from an element or the global element attributes. + +Props also act slightly differently when used with: + +- [`Option`](#optional-props) - The field is automatically optional with a default value of `None`. +- [`ReadOnlySignal`](#reactive-props) - The props macro will automatically convert `T` into `ReadOnlySignal` when it is passed as a prop. +- [`String`](#formatted-props) - The props macro will accept formatted strings for any prop field with the type `String`. +- [`children`](#children-props) - The props macro will accept child elements if you include the `children` prop. + +### Default Props + +The `default` attribute lets you define a default value for a field if it isn't set when creating the component + +```rust, no_run +# use dioxus::prelude::*; +#[component] +fn Button( + // The default attributes makes your field optional in the component and uses the default value if it is not set. + #[props(default)] + text: String, + // You can also set an explicit default value instead of using the `Default` implementation. + #[props(default = "red".to_string())] + color: String, +) -> Element { + rsx! { + button { + color: color, + "{text}" + } + } +} + +rsx! { + // You can skip setting props that have a default value when you use the component. + Button {} +}; +``` + +### Optional Props + +When defining a component, you may want to make a prop optional without defining an explicit default value. Any fields with the type `Option` are automatically optional with a default value of `None`. + +```rust, no_run +# use dioxus::prelude::*; +#[component] +fn Button( + // Since the `text` field is optional, you don't need to set it when you use the component. + text: Option, +) -> Element { + rsx! { + button { {text.unwrap_or("button".to_string())} } + } +} + +rsx! { + Button {} +}; +``` + +If you want to make your `Option` field required, you can use the `!optional` attribute: + +```rust, no_run +# use dioxus::prelude::*; +#[component] +fn Button( + // You can use the `!optional` attribute on a field with the type `Option` to make it required. + #[props(!optional)] + text: Option, +) -> Element { + rsx! { + button { {text.unwrap_or("button".to_string())} } + } +} + +rsx! { + Button { + text: None + } +}; +``` + +### Converting Props + +You can automatically convert a field into the correct type by using the `into` attribute. Any type you pass into the field will be converted with the [`Into`] trait: + +```rust, no_run +# use dioxus::prelude::*; +#[component] +fn Button( + // You can use the `into` attribute on a field to convert types you pass in with the Into trait. + #[props(into)] + number: u64, +) -> Element { + rsx! { + button { "{number}" } + } +} + +rsx! { + Button { + // Because we used the into attribute, we can pass in any type that implements Into + number: 10u8 + } +}; +``` + +### Formatted Props + +You can use formatted strings in attributes just like you would in an element. Any prop field with the type `String` can accept a formatted string: + +```rust, no_run +# use dioxus::prelude::*; +#[component] +fn Button(text: String,) -> Element { + rsx! { + button { "{text}" } + } +} + +let name = "Bob"; +rsx! { + Button { + // You can use formatted strings in props that accept String just like you would in an element. + text: "Hello {name}!" + } +}; +``` + +### Children Props + +Rather than passing the RSX through a regular prop, you may wish to accept children similarly to how elements can have children. The "magic" children prop lets you achieve this: + +```rust, no_run +# use dioxus::prelude::*; +#[component] +fn Clickable( + href: String, + children: Element, +) -> Element { + rsx! { + a { + href: "{href}", + class: "fancy-button", + {children} + } + } +} +``` + +This makes providing children to the component much simpler: simply put the RSX inside the {} brackets: + +```rust, no_run +# use dioxus::prelude::*; +# #[component] +# fn Clickable( +# href: String, +# children: Element, +# ) -> Element { +# rsx! { +# a { +# href: "{href}", +# class: "fancy-button", +# {children} +# } +# } +# } +rsx! { + Clickable { + href: "https://www.youtube.com/watch?v=C-M2hs3sXGo", + "How to " + i { "not" } + " be seen" + } +}; +``` + +### Reactive Props + +In dioxus, when a prop changes, the component will rerun with the new value to update the UI. For example, if count changes from 0 to 1, this component will rerun and update the UI to show "Count: 1": + +```rust, no_run +# use dioxus::prelude::*; +#[component] +fn Counter(count: i32) -> Element { + rsx! { + div { + "Count: {count}" + } + } +} +``` + +Generally, just rerunning the component is enough to update the UI. However, if you use your prop inside reactive hooks like `use_memo` or `use_resource`, you may also want to restart those hooks when the prop changes: + +```rust, no_run +# use dioxus::prelude::*; +#[component] +fn Counter(count: i32) -> Element { + // We can use a memo to calculate the doubled count. Since this memo will only be created the first time the component is run and `count` is not reactive, it will never update when `count` changes. + let doubled_count = use_memo(move || count * 2); + rsx! { + div { + "Count: {count}" + "Doubled Count: {doubled_count}" + } + } +} +``` + +To fix this issue you can either: + +1. Make the prop reactive by wrapping it in `ReadOnlySignal` (recommended): + +`ReadOnlySignal` is a `Copy` reactive value. Dioxus will automatically convert any value into a `ReadOnlySignal` when it is passed as a prop. + +```rust, no_run +# use dioxus::prelude::*; +#[component] +fn Counter(count: ReadOnlySignal) -> Element { + // Since we made count reactive, the memo will automatically rerun when count changes. + let doubled_count = use_memo(move || count() * 2); + rsx! { + div { + "Count: {count}" + "Doubled Count: {doubled_count}" + } + } +} +``` + +2. Explicitly add the prop as a dependency to the reactive hook with [`use_reactive`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/macro.use_reactive.html): + +```rust, no_run +# use dioxus::prelude::*; +#[component] +fn Counter(count: i32) -> Element { + // We can add the count prop as an explicit dependency to every reactive hook that uses it with use_reactive. + // The use_reactive macro takes a closure with explicit dependencies as its argument. + let doubled_count = use_memo(use_reactive!(|count| count * 2)); + rsx! { + div { + "Count: {count}" + "Doubled Count: {doubled_count}" + } + } +} +``` + +### Extending Elements + +The `extends` attribute lets you extend your props with all the attributes from an element or the global element attributes. + +```rust, no_run +# use dioxus::prelude::*; +#[component] +fn Button( + // You can use the `extends` attribute on a field with the type `Vec` to extend the props with all the attributes from an element or the global element attributes. + #[props(extends = GlobalAttributes)] + attributes: Vec, +) -> Element { + rsx! { + // Instead of copying over every single attribute, we can just spread the attributes from the props into the button. + button { ..attributes, "button" } + } +} + +rsx! { + // Since we extend global attributes, you can use any attribute that would normally appear on the button element. + Button { + width: "10px", + height: "10px", + color: "red", + } +}; +``` diff --git a/packages/core-macro/docs/props.md b/packages/core-macro/docs/props.md new file mode 100644 index 000000000..1175a0645 --- /dev/null +++ b/packages/core-macro/docs/props.md @@ -0,0 +1,339 @@ +# Props + +The props derive macro allows you to define what props your component accepts and how to accept those props. Every component must either accept no arguments or accept a single argument that implements the `Properties` trait. + +> Note: You should generally prefer using the `#[component]` macro instead of the `#[derive(Props)]` macro with explicit props. The `#[component]` macro will automatically generate the props struct for you and perform extra checks to ensure that your component is valid. + +## Example + +```rust, no_run +# use dioxus::prelude::*; +#[derive(Props, PartialEq, Clone)] +struct ButtonProps { + /// The text of the button + text: String, + + /// The color of the button + color: String, +} + +fn Button(props: ButtonProps) -> Element { + rsx! { + button { + color: props.color, + "{props.text}" + } + } +} + +rsx! { + // Any fields you defined on the props struct will be turned into props for the component. + Button { + text: "Click me!", + color: "red", + } +}; +``` + +## Prop Modifiers + +You can use the `#[props()]` attribute to modify the behavior of the props derive macro: + +- [`#[props(default)]`](#default-props) - Makes the field optional in the component and uses the default value if it is not set when creating the component. +- [`#[props(!optional)]`](#optional-props) - Makes a field with the type `Option` required. +- [`#[props(into)]`](#converting-props) - Converts a field into the correct type by using the [`Into`] trait. +- [`#[props(extends = GlobalAttributes)]`](#extending-elements) - Extends the props with all the attributes from an element or the global element attributes. + +Props also act slightly differently when used with: + +- [`Option`](#optional-props) - The field is automatically optional with a default value of `None`. +- [`ReadOnlySignal`](#reactive-props) - The props macro will automatically convert `T` into `ReadOnlySignal` when it is passed as a prop. +- [`String`](#formatted-props) - The props macro will accept formatted strings for any prop field with the type `String`. +- [`children`](#children-props) - The props macro will accept child elements if you include the `children` prop. + +### Default Props + +The `default` attribute lets you define a default value for a field if it isn't set when creating the component + +```rust, no_run +# use dioxus::prelude::*; +#[derive(Props, PartialEq, Clone)] +struct ButtonProps { + // The default attributes makes your field optional in the component and uses the default value if it is not set. + #[props(default)] + text: String, + + /// You can also set an explicit default value instead of using the `Default` implementation. + #[props(default = "red".to_string())] + color: String, +} + +fn Button(props: ButtonProps) -> Element { + rsx! { + button { + color: props.color, + "{props.text}" + } + } +} + +rsx! { + // You can skip setting props that have a default value when you use the component. + Button {} +}; +``` + +### Optional Props + +When defining props, you may want to make a prop optional without defining an explicit default value. Any fields with the type `Option` are automatically optional with a default value of `None`. + +```rust, no_run +# use dioxus::prelude::*; +#[derive(Props, PartialEq, Clone)] +struct ButtonProps { + // Since the `text` field is optional, you don't need to set it when you use the component. + text: Option, +} + +fn Button(props: ButtonProps) -> Element { + rsx! { + button { {props.text.unwrap_or("button".to_string())} } + } +} + +rsx! { + Button {} +}; +``` + +If you want to make your `Option` field required, you can use the `!optional` attribute: + +```rust, no_run +# use dioxus::prelude::*; +#[derive(Props, PartialEq, Clone)] +struct ButtonProps { + /// You can use the `!optional` attribute on a field with the type `Option` to make it required. + #[props(!optional)] + text: Option, +} + +fn Button(props: ButtonProps) -> Element { + rsx! { + button { {props.text.unwrap_or("button".to_string())} } + } +} + +rsx! { + Button { + text: None + } +}; +``` + +### Converting Props + +You can automatically convert a field into the correct type by using the `into` attribute. Any type you pass into the field will be converted with the [`Into`] trait: + +```rust, no_run +# use dioxus::prelude::*; +#[derive(Props, PartialEq, Clone)] +struct ButtonProps { + /// You can use the `into` attribute on a field to convert types you pass in with the Into trait. + #[props(into)] + number: u64, +} + +fn Button(props: ButtonProps) -> Element { + rsx! { + button { "{props.number}" } + } +} + +rsx! { + Button { + // Because we used the into attribute, we can pass in any type that implements Into + number: 10u8 + } +}; +``` + +### Formatted Props + +You can use formatted strings in attributes just like you would in an element. Any prop field with the type `String` can accept a formatted string: + +```rust, no_run +# use dioxus::prelude::*; +#[derive(Props, PartialEq, Clone)] +struct ButtonProps { + text: String, +} + +fn Button(props: ButtonProps) -> Element { + rsx! { + button { "{props.text}" } + } +} + +let name = "Bob"; +rsx! { + Button { + // You can use formatted strings in props that accept String just like you would in an element. + text: "Hello {name}!" + } +}; +``` + +### Children Props + +Rather than passing the RSX through a regular prop, you may wish to accept children similarly to how elements can have children. The "magic" children prop lets you achieve this: + +```rust, no_run +# use dioxus::prelude::*; +#[derive(PartialEq, Clone, Props)] +struct ClickableProps { + href: String, + children: Element, +} + +fn Clickable(props: ClickableProps) -> Element { + rsx! { + a { + href: "{props.href}", + class: "fancy-button", + {props.children} + } + } +} +``` + +This makes providing children to the component much simpler: simply put the RSX inside the {} brackets: + +```rust, no_run +# use dioxus::prelude::*; +# #[derive(PartialEq, Clone, Props)] +# struct ClickableProps { +# href: String, +# children: Element, +# } +# +# fn Clickable(props: ClickableProps) -> Element { +# rsx! { +# a { +# href: "{props.href}", +# class: "fancy-button", +# {props.children} +# } +# } +# } +rsx! { + Clickable { + href: "https://www.youtube.com/watch?v=C-M2hs3sXGo", + "How to " + i { "not" } + " be seen" + } +}; +``` + +### Reactive Props + +In dioxus, when a prop changes, the component will rerun with the new value to update the UI. For example, if count changes from 0 to 1, this component will rerun and update the UI to show "Count: 1": + +```rust, no_run +# use dioxus::prelude::*; +#[component] +fn Counter(count: i32) -> Element { + rsx! { + div { + "Count: {count}" + } + } +} +``` + +Generally, just rerunning the component is enough to update the UI. However, if you use your prop inside reactive hooks like `use_memo` or `use_resource`, you may also want to restart those hooks when the prop changes: + +```rust, no_run +# use dioxus::prelude::*; +#[component] +fn Counter(count: i32) -> Element { + // We can use a memo to calculate the doubled count. Since this memo will only be created the first time the component is run and `count` is not reactive, it will never update when `count` changes. + let doubled_count = use_memo(move || count * 2); + rsx! { + div { + "Count: {count}" + "Doubled Count: {doubled_count}" + } + } +} +``` + +To fix this issue you can either: + +1. Make the prop reactive by wrapping it in `ReadOnlySignal` (recommended): + +`ReadOnlySignal` is a `Copy` reactive value. Dioxus will automatically convert any value into a `ReadOnlySignal` when it is passed as a prop. + +```rust, no_run +# use dioxus::prelude::*; +#[component] +fn Counter(count: ReadOnlySignal) -> Element { + // Since we made count reactive, the memo will automatically rerun when count changes. + let doubled_count = use_memo(move || count() * 2); + rsx! { + div { + "Count: {count}" + "Doubled Count: {doubled_count}" + } + } +} +``` + +2. Explicitly add the prop as a dependency to the reactive hook with [`use_reactive`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/macro.use_reactive.html): + +```rust, no_run +# use dioxus::prelude::*; +#[component] +fn Counter(count: i32) -> Element { + // We can add the count prop as an explicit dependency to every reactive hook that uses it with use_reactive. + // The use_reactive macro takes a closure with explicit dependencies as its argument. + let doubled_count = use_memo(use_reactive!(|count| count * 2)); + rsx! { + div { + "Count: {count}" + "Doubled Count: {doubled_count}" + } + } +} +``` + +### Extending Elements + +The `extends` attribute lets you extend your props with all the attributes from an element or the global element attributes. + +```rust, no_run +# use dioxus::prelude::*; +#[derive(Props, PartialEq, Clone)] +struct ButtonProps { + /// You can use the `extends` attribute on a field with the type `Vec` to extend the props with all the attributes from an element or the global element attributes. + #[props(extends = GlobalAttributes)] + attributes: Vec, +} + +#[component] +fn Button(props: ButtonProps) -> Element { + rsx! { + // Instead of copying over every single attribute, we can just spread the attributes from the props into the button. + button { ..props.attributes, "button" } + } +} + +rsx! { + // Since we extend global attributes, you can use any attribute that would normally appear on the button element. + Button { + width: "10px", + height: "10px", + color: "red", + } +}; +``` diff --git a/packages/core-macro/docs/rsx.md b/packages/core-macro/docs/rsx.md new file mode 100644 index 000000000..a7814b872 --- /dev/null +++ b/packages/core-macro/docs/rsx.md @@ -0,0 +1,185 @@ +The rsx! macro makes it easy for developers to write jsx-style markup in their components. + +## Elements + +You can render elements with rsx! with the element name and then braces surrounding the attributes and children. + +```rust, no_run +# use dioxus::prelude::*; +rsx! { + div { + div {} + } +}; +``` + +
+Web Components + +Dioxus will automatically render any elements with `-` as a untyped web component: + +```rust, no_run +# use dioxus::prelude::*; +rsx! { + div-component { + div {} + } +}; +``` + +You can wrap your web component in a custom component to add type checking: + +```rust, no_run +# use dioxus::prelude::*; +#[component] +fn MyDivComponent(width: i64) -> Element { + rsx! { + div-component { + "width": width + } + } +} +``` + +
+ +## Attributes + +You can add attributes to any element inside the braces. Attributes are key-value pairs separated by a colon. + +```rust, no_run +# use dioxus::prelude::*; +let width = 100; +rsx! { + div { + // Set the class attribute to "my-class" + class: "my-class", + // attribute strings are automatically formatted with the format macro + width: "{width}px", + } +}; +``` + +### Optional Attributes + +You can include optional attributes with an unterminated if statement as the value of the attribute: + +```rust, no_run +# use dioxus::prelude::*; +# let first_boolean = true; +# let second_boolean = false; +rsx! { + div { + // Set the class attribute to "my-class" if true + class: if first_boolean { + "my-class" + }, + // Set the class attribute to "my-other-class" if false + class: if second_boolean { + "my-other-class" + } + } +}; +``` + +### Raw Attributes + +Dioxus defaults to attributes that are type checked as html. If you want to include an attribute that is not included in the html spec, you can use the `raw` attribute surrounded by quotes: + +```rust, no_run +# use dioxus::prelude::*; +rsx! { + div { + // Set the data-count attribute to "1" + "data-count": "1" + } +}; +``` + +## Text + +You can include text in your markup as a string literal: + +```rust, no_run +# use dioxus::prelude::*; +let name = "World"; +rsx! { + div { + "Hello World" + // Just like attributes, you can included formatted segments inside your text + "Hello {name}" + } +}; +``` + +## Components + +You can render any [`macro@crate::component`]s you created inside your markup just like elements. Components must either start with a capital letter or contain a `_` character. + +```rust, no_run +# use dioxus::prelude::*; +#[component] +fn HelloWorld() -> Element { + rsx! { "hello world!" } +} + +rsx! { + div { + HelloWorld {} + } +}; +``` + +## If statements + +You can use if statements to conditionally render children. The body of the for if statement is parsed as rsx markup: + +```rust, no_run +# use dioxus::prelude::*; +let first_boolean = true; +let second_boolean = false; +rsx! { + if first_boolean { + div { + "first" + } + } + + if second_boolean { + "second" + } +}; +``` + +## For loops + +You can also use for loops to iterate over a collection of items. The body of the for loop is parsed as rsx markup: + +```rust, no_run +# use dioxus::prelude::*; +let numbers = vec![1, 2, 3]; +rsx! { + for number in numbers { + div { + "{number}" + } + } +}; +``` + +## Raw Expressions + +You can include raw expressions inside your markup inside curly braces. Your expression must implement the [`IntoDynNode`](https://docs.rs/dioxus-core/latest/dioxus_core/trait.IntoDynNode.html) trait: + +```rust, no_run +# use dioxus::prelude::*; +let name = "World"; +rsx! { + div { + // Text can be converted into a dynamic node in rsx + {name} + } + // Iterators can also be converted into dynamic nodes + {(0..10).map(|n| n * n).map(|number| rsx! { div { "{number}" } })} +}; +``` diff --git a/packages/core-macro/src/lib.rs b/packages/core-macro/src/lib.rs index 6b87a17d8..9046036ba 100644 --- a/packages/core-macro/src/lib.rs +++ b/packages/core-macro/src/lib.rs @@ -13,6 +13,13 @@ mod utils; use dioxus_rsx as rsx; +/// Format a string with inline rust expressions. [`format_args_f!`] is very similar to [`format_args`], but it allows you to use arbitrary rust expressions inside braces instead of just variables: +/// +/// ```rust,no_run +/// # use dioxus::prelude::*; +/// let formatted_with_variables = format_args!("{} + {} = {}", 1, 2, 1 + 2); +/// let formatted_with_inline_expressions = format_args_f!("{1} + {2} = {1 + 2}"); +/// ``` #[proc_macro] pub fn format_args_f(input: TokenStream) -> TokenStream { use rsx::*; @@ -21,6 +28,7 @@ pub fn format_args_f(input: TokenStream) -> TokenStream { .into() } +#[doc = include_str!("../docs/props.md")] #[proc_macro_derive(Props, attributes(props))] pub fn derive_typed_builder(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as syn::DeriveInput); @@ -30,192 +38,7 @@ pub fn derive_typed_builder(input: TokenStream) -> TokenStream { } } -/// The rsx! macro makes it easy for developers to write jsx-style markup in their components. -/// -/// ## Elements -/// -/// You can render elements with rsx! with the element name and then braces surrounding the attributes and children. -/// -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// rsx! { -/// div { -/// div {} -/// } -/// }; -/// ``` -/// -///
-/// Web Components -/// -/// -/// Dioxus will automatically render any elements with `-` as a untyped web component: -/// -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// rsx! { -/// div-component { -/// div {} -/// } -/// }; -/// ``` -/// -/// You can wrap your web component in a custom component to add type checking: -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// #[component] -/// fn MyDivComponent(width: i64) -> Element { -/// rsx! { -/// div-component { -/// "width": width -/// } -/// } -/// } -/// ``` -/// -/// -///
-/// -/// ## Attributes -/// -/// You can add attributes to any element inside the braces. Attributes are key-value pairs separated by a colon. -/// -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// let width = 100; -/// rsx! { -/// div { -/// // Set the class attribute to "my-class" -/// class: "my-class", -/// // attribute strings are automatically formatted with the format macro -/// width: "{width}px", -/// } -/// }; -/// ``` -/// -/// ### Optional Attributes -/// -/// You can include optional attributes with an unterminated if statement as the value of the attribute: -/// -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// # let first_boolean = true; -/// # let second_boolean = false; -/// rsx! { -/// div { -/// // Set the class attribute to "my-class" if true -/// class: if first_boolean { -/// "my-class" -/// }, -/// // Set the class attribute to "my-other-class" if false -/// class: if second_boolean { -/// "my-other-class" -/// } -/// } -/// }; -/// ``` -/// -/// ### Raw Attributes -/// -/// Dioxus defaults to attributes that are type checked as html. If you want to include an attribute that is not included in the html spec, you can use the `raw` attribute surrounded by quotes: -/// -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// rsx! { -/// div { -/// // Set the data-count attribute to "1" -/// "data-count": "1" -/// } -/// }; -/// ``` -/// -/// ## Text -/// -/// You can include text in your markup as a string literal: -/// -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// let name = "World"; -/// rsx! { -/// div { -/// "Hello World" -/// // Just like attributes, you can included formatted segments inside your text -/// "Hello {name}" -/// } -/// }; -/// ``` -/// -/// ## Components -/// -/// You can render any [`macro@crate::component`]s you created inside your markup just like elements. Components must either start with a capital letter or contain a `_` character. -/// -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// #[component] -/// fn HelloWorld() -> Element { -/// rsx! { "hello world!" } -/// } -/// -/// rsx! { -/// div { -/// HelloWorld {} -/// } -/// }; -/// ``` -/// -/// ## If statements -/// -/// You can use if statements to conditionally render children. The body of the for if statement is parsed as rsx markup: -/// -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// let first_boolean = true; -/// let second_boolean = false; -/// rsx! { -/// if first_boolean { -/// div { -/// "first" -/// } -/// } -/// -/// if second_boolean { -/// "second" -/// } -/// }; -/// ``` -/// -/// ## For loops -/// -/// You can also use for loops to iterate over a collection of items. The body of the for loop is parsed as rsx markup: -/// -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// let numbers = vec![1, 2, 3]; -/// rsx! { -/// for number in numbers { -/// div { -/// "{number}" -/// } -/// } -/// }; -/// ``` -/// -/// ## Raw Expressions -/// -/// You can include raw expressions inside your markup inside curly braces. Your expression must implement the [`IntoDynNode`](https://docs.rs/dioxus-core/latest/dioxus_core/trait.IntoDynNode.html) trait: -/// -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// let name = "World"; -/// rsx! { -/// div { -/// // Text can be converted into a dynamic node in rsx -/// {name} -/// } -/// // Iterators can also be converted into dynamic nodes -/// {(0..10).map(|n| n * n).map(|number| rsx! { div { "{number}" } })} -/// }; -/// ``` +#[doc = include_str!("../docs/rsx.md")] #[proc_macro] pub fn rsx(tokens: TokenStream) -> TokenStream { match syn::parse::(tokens) { @@ -231,45 +54,7 @@ pub fn render(tokens: TokenStream) -> TokenStream { rsx(tokens) } -/// Streamlines component creation. -/// This is the recommended way of creating components, -/// though you might want lower-level control with more advanced uses. -/// -/// # Arguments -/// * `no_case_check` - Doesn't enforce `PascalCase` on your component names. -/// **This will be removed/deprecated in a future update in favor of a more complete Clippy-backed linting system.** -/// The reasoning behind this is that Clippy allows more robust and powerful lints, whereas -/// macros are extremely limited. -/// -/// # Features -/// This attribute: -/// * Enforces that your component uses `PascalCase`. -/// No warnings are generated for the `PascalCase` -/// function name, but everything else will still raise a warning if it's incorrectly `PascalCase`. -/// Does not disable warnings anywhere else, so if you, for example, -/// accidentally don't use `snake_case` -/// for a variable name in the function, the compiler will still warn you. -/// * Automatically uses `#[inline_props]` if there's more than 1 parameter in the function. -/// * Verifies the validity of your component. -/// -/// # Examples -/// * Without props: -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// #[component] -/// fn GreetBob() -> Element { -/// rsx! { "hello, bob" } -/// } -/// ``` -/// -/// * With props: -/// ```rust, no_run -/// # use dioxus::prelude::*; -/// #[component] -/// fn GreetBob(bob: String) -> Element { -/// rsx! { "hello, {bob}" } -/// } -/// ``` +#[doc = include_str!("../docs/component.md")] #[proc_macro_attribute] pub fn component(_args: TokenStream, input: TokenStream) -> TokenStream { parse_macro_input!(input as ComponentBody) diff --git a/packages/core/src/global_context.rs b/packages/core/src/global_context.rs index dec7832fa..7821172b1 100644 --- a/packages/core/src/global_context.rs +++ b/packages/core/src/global_context.rs @@ -270,7 +270,7 @@ pub fn schedule_update_any() -> Arc { /// Creates a callback that will be run before the component is removed. /// This can be used to clean up side effects from the component -/// (created with [`use_effect`](crate::use_effect)). +/// (created with [`use_effect`](dioxus::prelude::use_effect)). /// /// Example: /// ```rust diff --git a/packages/core/src/scope_context.rs b/packages/core/src/scope_context.rs index 74f2047db..a22424109 100644 --- a/packages/core/src/scope_context.rs +++ b/packages/core/src/scope_context.rs @@ -438,7 +438,7 @@ impl ScopeId { /// Create a subscription that schedules a future render for the reference component. Unlike [`Self::needs_update`], this function will work outside of the dioxus runtime. /// - /// ## Notice: you should prefer using [`schedule_update_any`] + /// ## Notice: you should prefer using [`crate::prelude::schedule_update_any`] pub fn schedule_update(&self) -> Arc { Runtime::with_scope(*self, |cx| cx.schedule_update()).expect("to be in a dioxus runtime") } diff --git a/packages/dioxus/src/lib.rs b/packages/dioxus/src/lib.rs index 5fc5992e1..86e72af4b 100644 --- a/packages/dioxus/src/lib.rs +++ b/packages/dioxus/src/lib.rs @@ -12,7 +12,7 @@ //! - `router`: exports the [router](https://dioxuslabs.com/learn/0.5/router) and enables any router features for the current platform //! - `third-party-renderer`: Just disables warnings about no active platform when no renderers are enabled //! -//! Platform features (the current platform determines what platform the [`launch`](dioxus::prelude::launch) function runs): +//! Platform features (the current platform determines what platform the [`launch()`] function runs): //! //! - `fullstack`: enables the fullstack platform. This must be used in combination with the `web` feature for wasm builds and `axum` feature for server builds //! - `desktop`: enables the desktop platform diff --git a/packages/rsx/src/sub_templates.rs b/packages/rsx/src/sub_templates.rs index 237051cd4..adb815ce0 100644 --- a/packages/rsx/src/sub_templates.rs +++ b/packages/rsx/src/sub_templates.rs @@ -42,4 +42,4 @@ //! /// } //! /// //! /// We only track this when the template changes -//! pub discovered_templates: Vec