Improve inline docs (#2460)
Improve inline docs * improve incorrect event handler return error message * Improve event handler docs * document the eval functions * document spawn and common spawn errors * fix event handler docs * add notes about how you use attributes and elements in rsx * add doc aliases for attributes and events we rename * add some more aliases for common search terms * don't doc ignore any public examples in core * don't ignore public doc examples in ssr * don't ignore examples in the dioxus package readme * add a warning when you launch without a renderer enabled * fix some outdated element docs * add a bunch of examples to resource * add notes about desktop events * add more docs for use_resource * add on_unimplemented hint to Dependency * fix some unresolved links * add examples to each of the router traits * add not implemented errors for router traits * add an example to the routable trait * expand rsx macro docs * improve memo docs * update the dioxus readme * mention dioxus crate features in the docs * fix a bunch of doc tests * fix html doc tests * fix router doc tests * fix dioxus signals doc tests * fix dioxus ssr doc tests * fix use_future example in the hooks cheat sheet * add a javascript alias for eval * fix hook explanation values * remove unused embed-doc-image dependency
This commit is contained in:
1 parent
054351139f
commit
0127501dbf
71 files changed
+4132
-1162
No files matched your search
@@ -30,7 +30,7 @@ impl FormValue {
|
||||
self.0.first().unwrap().clone()
|
||||
}
|
||||
|
||||
/// Convert into Vec<String>
|
||||
/// Convert into [`Vec<String>`]
|
||||
pub fn to_vec(self) -> Vec<String> {
|
||||
self.0.clone()
|
||||
}
|
||||
@@ -287,7 +287,50 @@ impl_event! {
|
||||
/// onchange
|
||||
onchange
|
||||
|
||||
/// oninput handler
|
||||
/// The `oninput` event is fired when the value of a `<input>`, `<select>`, or `<textarea>` element is changed.
|
||||
///
|
||||
/// There are two main approaches to updating your input element:
|
||||
/// 1) Controlled inputs directly update the value of the input element as the user interacts with the element
|
||||
///
|
||||
/// ```rust
|
||||
/// use dioxus::prelude::*;
|
||||
///
|
||||
/// fn App() -> Element {
|
||||
/// let mut value = use_signal(|| "hello world".to_string());
|
||||
///
|
||||
/// rsx! {
|
||||
/// input {
|
||||
/// // We directly set the value of the input element to our value signal
|
||||
/// value: "{value}",
|
||||
/// // The `oninput` event handler will run every time the user changes the value of the input element
|
||||
/// // We can set the `value` signal to the new value of the input element
|
||||
/// oninput: move |event| value.set(event.value())
|
||||
/// }
|
||||
/// // Since this is a controlled input, we can also update the value of the input element directly
|
||||
/// button {
|
||||
/// onclick: move |_| value.write().clear(),
|
||||
/// "Clear"
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// 2) Uncontrolled inputs just read the value of the input element as it changes
|
||||
///
|
||||
/// ```rust
|
||||
/// use dioxus::prelude::*;
|
||||
///
|
||||
/// fn App() -> Element {
|
||||
/// rsx! {
|
||||
/// input {
|
||||
/// // In uncontrolled inputs, we don't set the value of the input element directly
|
||||
/// // But you can still read the value of the input element
|
||||
/// oninput: move |event| println!("{}", event.value()),
|
||||
/// }
|
||||
/// // Since we don't directly control the value of the input element, we can't easily modify it
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
oninput
|
||||
|
||||
/// oninvalid
|
||||
|
||||
@@ -0,0 +1,397 @@
|
||||
#![doc = include_str!("../../docs/event_handlers.md")]
|
||||
|
||||
use std::any::Any;
|
||||
use std::sync::RwLock;
|
||||
|
||||
macro_rules! impl_event {
|
||||
(
|
||||
$data:ty;
|
||||
$(
|
||||
$( #[$attr:meta] )*
|
||||
$name:ident $(: $js_name:literal)?
|
||||
)*
|
||||
) => {
|
||||
$(
|
||||
$( #[$attr] )*
|
||||
/// <details open>
|
||||
/// <summary>General Event Handler Information</summary>
|
||||
///
|
||||
#[doc = include_str!("../../docs/event_handlers.md")]
|
||||
///
|
||||
/// </details>
|
||||
///
|
||||
#[doc = include_str!("../../docs/common_event_handler_errors.md")]
|
||||
$(
|
||||
#[doc(alias = $js_name)]
|
||||
)?
|
||||
#[inline]
|
||||
pub fn $name<E: crate::EventReturn<T>, T>(mut _f: impl FnMut(::dioxus_core::Event<$data>) -> E + 'static) -> ::dioxus_core::Attribute {
|
||||
::dioxus_core::Attribute::new(
|
||||
impl_event!(@name $name $($js_name)?),
|
||||
::dioxus_core::AttributeValue::listener(move |e: ::dioxus_core::Event<crate::PlatformEventData>| {
|
||||
_f(e.map(|e|e.into())).spawn();
|
||||
}),
|
||||
None,
|
||||
false,
|
||||
).into()
|
||||
}
|
||||
)*
|
||||
};
|
||||
|
||||
(@name $name:ident $js_name:literal) => {
|
||||
$js_name
|
||||
};
|
||||
(@name $name:ident) => {
|
||||
stringify!($name)
|
||||
};
|
||||
}
|
||||
|
||||
static EVENT_CONVERTER: RwLock<Option<Box<dyn HtmlEventConverter>>> = RwLock::new(None);
|
||||
|
||||
#[inline]
|
||||
pub fn set_event_converter(converter: Box<dyn HtmlEventConverter>) {
|
||||
*EVENT_CONVERTER.write().unwrap() = Some(converter);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn with_event_converter<F, R>(f: F) -> R
|
||||
where
|
||||
F: FnOnce(&dyn HtmlEventConverter) -> R,
|
||||
{
|
||||
let converter = EVENT_CONVERTER.read().unwrap();
|
||||
f(converter.as_ref().unwrap().as_ref())
|
||||
}
|
||||
|
||||
/// A platform specific event.
|
||||
pub struct PlatformEventData {
|
||||
event: Box<dyn Any>,
|
||||
}
|
||||
|
||||
impl PlatformEventData {
|
||||
pub fn new(event: Box<dyn Any>) -> Self {
|
||||
Self { event }
|
||||
}
|
||||
|
||||
pub fn inner(&self) -> &Box<dyn Any> {
|
||||
&self.event
|
||||
}
|
||||
|
||||
pub fn downcast<T: 'static>(&self) -> Option<&T> {
|
||||
self.event.downcast_ref::<T>()
|
||||
}
|
||||
|
||||
pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
|
||||
self.event.downcast_mut::<T>()
|
||||
}
|
||||
|
||||
pub fn into_inner<T: 'static>(self) -> Option<T> {
|
||||
self.event.downcast::<T>().ok().map(|e| *e)
|
||||
}
|
||||
}
|
||||
|
||||
/// A converter between a platform specific event and a general event. All code in a renderer that has a large binary size should be placed in this trait. Each of these functions should be snipped in high levels of optimization.
|
||||
pub trait HtmlEventConverter: Send + Sync {
|
||||
/// Convert a general event to an animation data event
|
||||
fn convert_animation_data(&self, event: &PlatformEventData) -> AnimationData;
|
||||
/// Convert a general event to a clipboard data event
|
||||
fn convert_clipboard_data(&self, event: &PlatformEventData) -> ClipboardData;
|
||||
/// Convert a general event to a composition data event
|
||||
fn convert_composition_data(&self, event: &PlatformEventData) -> CompositionData;
|
||||
/// Convert a general event to a drag data event
|
||||
fn convert_drag_data(&self, event: &PlatformEventData) -> DragData;
|
||||
/// Convert a general event to a focus data event
|
||||
fn convert_focus_data(&self, event: &PlatformEventData) -> FocusData;
|
||||
/// Convert a general event to a form data event
|
||||
fn convert_form_data(&self, event: &PlatformEventData) -> FormData;
|
||||
/// Convert a general event to an image data event
|
||||
fn convert_image_data(&self, event: &PlatformEventData) -> ImageData;
|
||||
/// Convert a general event to a keyboard data event
|
||||
fn convert_keyboard_data(&self, event: &PlatformEventData) -> KeyboardData;
|
||||
/// Convert a general event to a media data event
|
||||
fn convert_media_data(&self, event: &PlatformEventData) -> MediaData;
|
||||
/// Convert a general event to a mounted data event
|
||||
fn convert_mounted_data(&self, event: &PlatformEventData) -> MountedData;
|
||||
/// Convert a general event to a mouse data event
|
||||
fn convert_mouse_data(&self, event: &PlatformEventData) -> MouseData;
|
||||
/// Convert a general event to a pointer data event
|
||||
fn convert_pointer_data(&self, event: &PlatformEventData) -> PointerData;
|
||||
/// Convert a general event to a scroll data event
|
||||
fn convert_scroll_data(&self, event: &PlatformEventData) -> ScrollData;
|
||||
/// Convert a general event to a selection data event
|
||||
fn convert_selection_data(&self, event: &PlatformEventData) -> SelectionData;
|
||||
/// Convert a general event to a toggle data event
|
||||
fn convert_toggle_data(&self, event: &PlatformEventData) -> ToggleData;
|
||||
/// Convert a general event to a touch data event
|
||||
fn convert_touch_data(&self, event: &PlatformEventData) -> TouchData;
|
||||
/// Convert a general event to a transition data event
|
||||
fn convert_transition_data(&self, event: &PlatformEventData) -> TransitionData;
|
||||
/// Convert a general event to a wheel data event
|
||||
fn convert_wheel_data(&self, event: &PlatformEventData) -> WheelData;
|
||||
}
|
||||
|
||||
impl From<&PlatformEventData> for AnimationData {
|
||||
fn from(val: &PlatformEventData) -> Self {
|
||||
with_event_converter(|c| c.convert_animation_data(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PlatformEventData> for ClipboardData {
|
||||
fn from(val: &PlatformEventData) -> Self {
|
||||
with_event_converter(|c| c.convert_clipboard_data(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PlatformEventData> for CompositionData {
|
||||
fn from(val: &PlatformEventData) -> Self {
|
||||
with_event_converter(|c| c.convert_composition_data(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PlatformEventData> for DragData {
|
||||
fn from(val: &PlatformEventData) -> Self {
|
||||
with_event_converter(|c| c.convert_drag_data(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PlatformEventData> for FocusData {
|
||||
fn from(val: &PlatformEventData) -> Self {
|
||||
with_event_converter(|c| c.convert_focus_data(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PlatformEventData> for FormData {
|
||||
fn from(val: &PlatformEventData) -> Self {
|
||||
with_event_converter(|c| c.convert_form_data(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PlatformEventData> for ImageData {
|
||||
fn from(val: &PlatformEventData) -> Self {
|
||||
with_event_converter(|c| c.convert_image_data(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PlatformEventData> for KeyboardData {
|
||||
fn from(val: &PlatformEventData) -> Self {
|
||||
with_event_converter(|c| c.convert_keyboard_data(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PlatformEventData> for MediaData {
|
||||
fn from(val: &PlatformEventData) -> Self {
|
||||
with_event_converter(|c| c.convert_media_data(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PlatformEventData> for MountedData {
|
||||
fn from(val: &PlatformEventData) -> Self {
|
||||
with_event_converter(|c| c.convert_mounted_data(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PlatformEventData> for MouseData {
|
||||
fn from(val: &PlatformEventData) -> Self {
|
||||
with_event_converter(|c| c.convert_mouse_data(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PlatformEventData> for PointerData {
|
||||
fn from(val: &PlatformEventData) -> Self {
|
||||
with_event_converter(|c| c.convert_pointer_data(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PlatformEventData> for ScrollData {
|
||||
fn from(val: &PlatformEventData) -> Self {
|
||||
with_event_converter(|c| c.convert_scroll_data(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PlatformEventData> for SelectionData {
|
||||
fn from(val: &PlatformEventData) -> Self {
|
||||
with_event_converter(|c| c.convert_selection_data(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PlatformEventData> for ToggleData {
|
||||
fn from(val: &PlatformEventData) -> Self {
|
||||
with_event_converter(|c| c.convert_toggle_data(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PlatformEventData> for TouchData {
|
||||
fn from(val: &PlatformEventData) -> Self {
|
||||
with_event_converter(|c| c.convert_touch_data(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PlatformEventData> for TransitionData {
|
||||
fn from(val: &PlatformEventData) -> Self {
|
||||
with_event_converter(|c| c.convert_transition_data(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PlatformEventData> for WheelData {
|
||||
fn from(val: &PlatformEventData) -> Self {
|
||||
with_event_converter(|c| c.convert_wheel_data(val))
|
||||
}
|
||||
}
|
||||
|
||||
mod animation;
|
||||
mod clipboard;
|
||||
mod composition;
|
||||
mod drag;
|
||||
mod focus;
|
||||
mod form;
|
||||
mod image;
|
||||
mod keyboard;
|
||||
mod media;
|
||||
mod mounted;
|
||||
mod mouse;
|
||||
mod pointer;
|
||||
mod scroll;
|
||||
mod selection;
|
||||
mod toggle;
|
||||
mod touch;
|
||||
mod transition;
|
||||
mod wheel;
|
||||
|
||||
pub use animation::*;
|
||||
pub use clipboard::*;
|
||||
pub use composition::*;
|
||||
pub use drag::*;
|
||||
pub use focus::*;
|
||||
pub use form::*;
|
||||
pub use image::*;
|
||||
pub use keyboard::*;
|
||||
pub use media::*;
|
||||
pub use mounted::*;
|
||||
pub use mouse::*;
|
||||
pub use pointer::*;
|
||||
pub use scroll::*;
|
||||
pub use selection::*;
|
||||
pub use toggle::*;
|
||||
pub use touch::*;
|
||||
pub use transition::*;
|
||||
pub use wheel::*;
|
||||
|
||||
pub fn event_bubbles(evt: &str) -> bool {
|
||||
match evt {
|
||||
"copy" => true,
|
||||
"cut" => true,
|
||||
"paste" => true,
|
||||
"compositionend" => true,
|
||||
"compositionstart" => true,
|
||||
"compositionupdate" => true,
|
||||
"keydown" => true,
|
||||
"keypress" => true,
|
||||
"keyup" => true,
|
||||
"focus" => false,
|
||||
"focusout" => true,
|
||||
"focusin" => true,
|
||||
"blur" => false,
|
||||
"change" => true,
|
||||
"input" => true,
|
||||
"invalid" => true,
|
||||
"reset" => true,
|
||||
"submit" => true,
|
||||
"click" => true,
|
||||
"contextmenu" => true,
|
||||
"doubleclick" => true,
|
||||
"dblclick" => true,
|
||||
"drag" => true,
|
||||
"dragend" => true,
|
||||
"dragenter" => false,
|
||||
"dragexit" => false,
|
||||
"dragleave" => true,
|
||||
"dragover" => true,
|
||||
"dragstart" => true,
|
||||
"drop" => true,
|
||||
"mousedown" => true,
|
||||
"mouseenter" => false,
|
||||
"mouseleave" => false,
|
||||
"mousemove" => true,
|
||||
"mouseout" => true,
|
||||
"scroll" => false,
|
||||
"mouseover" => true,
|
||||
"mouseup" => true,
|
||||
"pointerdown" => true,
|
||||
"pointermove" => true,
|
||||
"pointerup" => true,
|
||||
"pointercancel" => true,
|
||||
"gotpointercapture" => true,
|
||||
"lostpointercapture" => true,
|
||||
"pointerenter" => false,
|
||||
"pointerleave" => false,
|
||||
"pointerover" => true,
|
||||
"pointerout" => true,
|
||||
"select" => true,
|
||||
"touchcancel" => true,
|
||||
"touchend" => true,
|
||||
"touchmove" => true,
|
||||
"touchstart" => true,
|
||||
"wheel" => true,
|
||||
"abort" => false,
|
||||
"canplay" => false,
|
||||
"canplaythrough" => false,
|
||||
"durationchange" => false,
|
||||
"emptied" => false,
|
||||
"encrypted" => true,
|
||||
"ended" => false,
|
||||
"error" => false,
|
||||
"loadeddata" => false,
|
||||
"loadedmetadata" => false,
|
||||
"loadstart" => false,
|
||||
"load" => false,
|
||||
"pause" => false,
|
||||
"play" => false,
|
||||
"playing" => false,
|
||||
"progress" => false,
|
||||
"ratechange" => false,
|
||||
"seeked" => false,
|
||||
"seeking" => false,
|
||||
"stalled" => false,
|
||||
"suspend" => false,
|
||||
"timeupdate" => false,
|
||||
"volumechange" => false,
|
||||
"waiting" => false,
|
||||
"animationstart" => true,
|
||||
"animationend" => true,
|
||||
"animationiteration" => true,
|
||||
"transitionend" => true,
|
||||
"toggle" => true,
|
||||
"mounted" => false,
|
||||
_ => {
|
||||
tracing::warn!("Unknown event name: {evt}");
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[rustversion::attr(
|
||||
since(1.78.0),
|
||||
diagnostic::on_unimplemented(
|
||||
message = "`EventHandlerReturn` is not implemented for `{Self}`",
|
||||
label = "Return Value",
|
||||
note = "Event handlers in dioxus need to return either: nothing (the unit type `()`), or an async block that dioxus will automatically spawn",
|
||||
note = "You likely need to add a semicolon to the end of the event handler to make it return nothing",
|
||||
)
|
||||
)]
|
||||
pub trait EventReturn<P>: Sized {
|
||||
fn spawn(self) {}
|
||||
}
|
||||
|
||||
impl EventReturn<()> for () {}
|
||||
#[doc(hidden)]
|
||||
pub struct AsyncMarker;
|
||||
|
||||
impl<T> EventReturn<AsyncMarker> for T
|
||||
where
|
||||
T: std::future::Future<Output = ()> + 'static,
|
||||
{
|
||||
#[inline]
|
||||
fn spawn(self) {
|
||||
dioxus_core::prelude::spawn(self);
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,7 @@ impl RenderedElementBacking for () {
|
||||
|
||||
/// The way that scrolling should be performed
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[doc(alias = "ScrollIntoViewOptions")]
|
||||
pub enum ScrollBehavior {
|
||||
/// Scroll to the element immediately
|
||||
#[cfg_attr(feature = "serialize", serde(rename = "instant"))]
|
||||
@@ -84,21 +85,27 @@ impl MountedData {
|
||||
}
|
||||
|
||||
/// Get the number of pixels that an element's content is scrolled
|
||||
#[doc(alias = "scrollTop")]
|
||||
#[doc(alias = "scrollLeft")]
|
||||
pub async fn get_scroll_offset(&self) -> MountedResult<PixelsVector2D> {
|
||||
self.inner.get_scroll_offset().await
|
||||
}
|
||||
|
||||
/// Get the size of an element's content, including content not visible on the screen due to overflow
|
||||
#[doc(alias = "scrollWidth")]
|
||||
#[doc(alias = "scrollHeight")]
|
||||
pub async fn get_scroll_size(&self) -> MountedResult<PixelsSize> {
|
||||
self.inner.get_scroll_size().await
|
||||
}
|
||||
|
||||
/// Get the bounding rectangle of the element relative to the viewport (this does not include the scroll position)
|
||||
#[doc(alias = "getBoundingClientRect")]
|
||||
pub async fn get_client_rect(&self) -> MountedResult<PixelsRect> {
|
||||
self.inner.get_client_rect().await
|
||||
}
|
||||
|
||||
/// Scroll to make the element visible
|
||||
#[doc(alias = "scrollIntoView")]
|
||||
pub fn scroll_to(
|
||||
&self,
|
||||
behavior: ScrollBehavior,
|
||||
@@ -107,6 +114,8 @@ impl MountedData {
|
||||
}
|
||||
|
||||
/// Set the focus on the element
|
||||
#[doc(alias = "focus")]
|
||||
#[doc(alias = "blur")]
|
||||
pub fn set_focus(&self, focus: bool) -> Pin<Box<dyn Future<Output = MountedResult<()>>>> {
|
||||
self.inner.set_focus(focus)
|
||||
}
|
||||
@@ -126,7 +135,64 @@ pub type MountedEvent = Event<MountedData>;
|
||||
impl_event! [
|
||||
MountedData;
|
||||
|
||||
/// mounted
|
||||
#[doc(alias = "ref")]
|
||||
#[doc(alias = "createRef")]
|
||||
#[doc(alias = "useRef")]
|
||||
/// The onmounted event is fired when the element is first added to the DOM. This event gives you a [`MountedData`] object and lets you interact with the raw DOM element.
|
||||
///
|
||||
/// This event is fired once per element. If you need to access the element multiple times, you can store the [`MountedData`] object in a [`use_signal`] hook and use it as needed.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust, no_run
|
||||
/// # use dioxus::prelude::*;
|
||||
/// fn App() -> Element {
|
||||
/// let mut header_element = use_signal(|| None);
|
||||
///
|
||||
/// rsx! {
|
||||
/// div {
|
||||
/// h1 {
|
||||
/// // The onmounted event will run the first time the h1 element is mounted
|
||||
/// onmounted: move |element| header_element.set(Some(element.data())),
|
||||
/// "Scroll to top example"
|
||||
/// }
|
||||
///
|
||||
/// for i in 0..100 {
|
||||
/// div { "Item {i}" }
|
||||
/// }
|
||||
///
|
||||
/// button {
|
||||
/// // When you click the button, if the header element has been mounted, we scroll to that element
|
||||
/// onclick: move |_| async move {
|
||||
/// if let Some(header) = header_element.cloned() {
|
||||
/// let _ = header.scroll_to(ScrollBehavior::Smooth).await;
|
||||
/// }
|
||||
/// },
|
||||
/// "Scroll to top"
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The `MountedData` struct contains cross platform APIs that work on the desktop, mobile, liveview and web platforms. For the web platform, you can also downcast the `MountedData` event to the `web-sys::Element` type for more web specific APIs:
|
||||
///
|
||||
/// ```rust, no_run
|
||||
/// # use dioxus::prelude::*;
|
||||
/// # use dioxus_web::WebEventExt;
|
||||
/// fn App() -> Element {
|
||||
/// rsx! {
|
||||
/// div {
|
||||
/// id: "some-id",
|
||||
/// onmounted: move |element| {
|
||||
/// // You can use the web_event trait to downcast the element to a web specific event. For the mounted event, this will be a web_sys::Element
|
||||
/// let web_sys_element = element.web_event();
|
||||
/// assert_eq!(web_sys_element.id(), "some-id");
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
onmounted
|
||||
];
|
||||
|
||||
|
||||
Reference in new issue
Block a user