From de9f61bcf48c0d6e35e46c337b72a713c9f9f7d2 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 14 Jul 2021 23:18:02 -0400 Subject: [PATCH] feat: more suspended nodes! --- examples/async.rs | 31 +- notes/SUSPENSE.md | 12 +- packages/atoms/src/lib.rs | 4 +- packages/core/examples/async.rs | 2 +- packages/core/src/context.rs | 98 ++++-- packages/core/src/diff.rs | 8 +- packages/core/src/events.rs | 25 +- packages/core/src/hooklist.rs | 24 +- packages/core/src/scope.rs | 2 +- packages/core/src/serialize.rs | 14 +- packages/core/src/util.rs | 9 +- packages/core/src/virtual_dom.rs | 27 +- packages/hooks/src/usestate.rs | 2 +- packages/html/src/lib.rs | 458 +++++++++++++++++++++++++++-- packages/web/examples/async_web.rs | 19 +- packages/web/examples/blah.rs | 3 + packages/web/src/lib.rs | 19 +- packages/web/src/new.rs | 33 +-- src/lib.rs | 2 +- wip.md | 0 20 files changed, 644 insertions(+), 148 deletions(-) create mode 100644 wip.md diff --git a/examples/async.rs b/examples/async.rs index 988c99e05..11b3bac3e 100644 --- a/examples/async.rs +++ b/examples/async.rs @@ -21,25 +21,18 @@ const ENDPOINT: &str = "https://dog.ceo/api/breeds/image/random"; static App: FC<()> = |cx| { let mut count = use_state(cx, || 0); - let mut fut = cx.use_hook( - move || { - Box::pin(async { - // - let mut tick = 0; - loop { - async_std::task::sleep(Duration::from_millis(250)).await; - log::debug!("ticking forward... {}", tick); - tick += 1; - // match surf::get(ENDPOINT).recv_json::().await { - // Ok(_) => (), - // Err(_) => (), - // } - } - }) as Pin + 'static>> - }, - |h| h, - |_| {}, - ); + let mut fut = Box::pin(async { + let mut tick: i32 = 0; + loop { + async_std::task::sleep(Duration::from_millis(250)).await; + log::debug!("ticking forward... {}", tick); + tick += 1; + // match surf::get(ENDPOINT).recv_json::().await { + // Ok(_) => (), + // Err(_) => (), + // } + } + }); cx.submit_task(fut); diff --git a/notes/SUSPENSE.md b/notes/SUSPENSE.md index d991d8959..b377f1021 100644 --- a/notes/SUSPENSE.md +++ b/notes/SUSPENSE.md @@ -17,13 +17,11 @@ for more general tasks, we need some way of submitting a future or task into som ```rust -let task = use_hook(|| { - // create the future -}, || { - update the future if it needs to be updated -}, || { - -}); +let task = use_hook( + || { /* */ }, + || { /* update the future if it needs to be updated */ }, + || {} +); cx.poll_future() // let recoil_event_loop = cx.use_task(move |_| async move { // loop { diff --git a/packages/atoms/src/lib.rs b/packages/atoms/src/lib.rs index 8d2263b38..a9db0bbf3 100644 --- a/packages/atoms/src/lib.rs +++ b/packages/atoms/src/lib.rs @@ -392,7 +392,7 @@ mod hooks { ) -> &'a Rc { let api = use_recoil_api(cx); cx.use_hook( - move || { + move |_| { let api = api.clone(); let raw_id = writable.static_id(); Rc::new(move |new_val| { @@ -423,7 +423,7 @@ mod hooks { let api = use_recoil_api(cx); cx.use_hook( - move || { + move |_| { let mut api = api.as_ref().borrow_mut(); let update = cx.schedule_update(); diff --git a/packages/core/examples/async.rs b/packages/core/examples/async.rs index 2734bf3b5..87debae49 100644 --- a/packages/core/examples/async.rs +++ b/packages/core/examples/async.rs @@ -5,7 +5,7 @@ fn main() {} const App: FC<()> = |cx| { // create a new future let _fut = cx.use_hook( - || { + |_| { // async { loop {} } // Box::pin(async { loop {} }) as Pin>> diff --git a/packages/core/src/context.rs b/packages/core/src/context.rs index b0aeb8401..57d053143 100644 --- a/packages/core/src/context.rs +++ b/packages/core/src/context.rs @@ -1,7 +1,9 @@ use crate::innerlude::*; +use bumpalo::Bump; use futures_util::FutureExt; +use std::any::Any; use std::marker::PhantomData; use std::{ @@ -134,13 +136,13 @@ impl<'src, P> Context<'src, P> { where State: 'static, Output: 'src, - Init: FnOnce() -> State, + Init: FnOnce(usize) -> State, Run: FnOnce(&'src mut State) -> Output, Cleanup: FnOnce(State), { // If the idx is the same as the hook length, then we need to add the current hook if self.scope.hooks.at_end() { - let new_state = initializer(); + let new_state = initializer(self.scope.hooks.len()); self.scope.hooks.push(new_state); } @@ -172,7 +174,7 @@ Any function prefixed with "use" should not be called conditionally. let ty = TypeId::of::(); let is_initialized = self.use_hook( - || false, + |_| false, |s| { let i = s.clone(); *s = true; @@ -208,7 +210,7 @@ Any function prefixed with "use" should not be called conditionally. } self.use_hook( - move || UseContextHook { + move |_| UseContextHook { par: None as Option>, we: None as Option>, }, @@ -299,10 +301,9 @@ Any function prefixed with "use" should not be called conditionally. /// Awaits the given task, forcing the component to re-render when the value is ready. /// /// - pub fn use_task( - &self, - task_initializer: Init, - ) -> (&TaskHandle, &mut Option) + /// + /// + pub fn use_task(&self, task_initializer: Init) -> (&TaskHandle, &Option) where Out: 'static, Fut: Future + 'static, @@ -315,7 +316,7 @@ Any function prefixed with "use" should not be called conditionally. // whenever the task is complete, save it into th self.use_hook( - move || { + move |hook_idx| { let task_fut = task_initializer(); let task_dump = Rc::new(RefCell::new(None)); @@ -328,7 +329,7 @@ Any function prefixed with "use" should not be called conditionally. *slot.as_ref().borrow_mut() = Some(output); update(); EventTrigger { - event: VirtualEvent::FiberEvent, + event: VirtualEvent::AsyncEvent { hook_idx }, originator, priority: EventPriority::Low, real_node_id: None, @@ -344,12 +345,20 @@ Any function prefixed with "use" should not be called conditionally. if let Some(val) = hook.task_dump.as_ref().borrow_mut().take() { hook.value = Some(val); } - (&TaskHandle { _p: PhantomData }, &mut hook.value) + (&TaskHandle { _p: PhantomData }, &hook.value) }, |_| {}, ) } +} +pub(crate) struct SuspenseHook { + pub value: Rc>>>, + pub callback: SuspendedCallback, +} +type SuspendedCallback = Box Fn(SuspendedContext<'a>) -> VNode<'a>>; + +impl<'src, P> Context<'src, P> { /// Asynchronously render new nodes once the given future has completed. /// /// # Easda @@ -360,18 +369,67 @@ Any function prefixed with "use" should not be called conditionally. /// # Example /// /// - pub fn use_suspense( + pub fn use_suspense( &'src self, - _task_initializer: impl FnOnce() -> Fut, - _callback: impl FnOnce(SuspendedContext, Out) -> VNode<'src> + 'src, + task_initializer: impl FnOnce() -> Fut, + user_callback: Cb, ) -> VNode<'src> where - Out: 'src, - Fut: Future, + Fut: Future + 'static, + Out: 'static, + Cb: for<'a> Fn(SuspendedContext<'a>, &Out) -> VNode<'a> + 'static, { - // self.use_hook(|| , runner, cleanup) + self.use_hook( + move |hook_idx| { + let value = Rc::new(RefCell::new(None)); - todo!() + let slot = value.clone(); + let callback: SuspendedCallback = Box::new(move |ctx: SuspendedContext| { + let v: std::cell::Ref>> = slot.as_ref().borrow(); + let v: &dyn Any = v.as_ref().unwrap().as_ref(); + let real_val = v.downcast_ref::().unwrap(); + user_callback(ctx, real_val) + }); + + let originator = self.scope.arena_idx.clone(); + let task_fut = task_initializer(); + + let slot = value.clone(); + self.submit_task(Box::pin(task_fut.then(move |output| async move { + // When the new value arrives, set the hooks internal slot + // Dioxus will call the user's callback to generate new nodes outside of the diffing system + *slot.borrow_mut() = Some(Box::new(output) as Box); + EventTrigger { + event: VirtualEvent::SuspenseEvent { hook_idx }, + originator, + priority: EventPriority::Low, + real_node_id: None, + } + }))); + + SuspenseHook { value, callback } + }, + move |hook| { + match hook.value.borrow().as_ref() { + Some(val) => { + let cx = SuspendedContext { + bump: &self.scope.cur_frame().bump, + }; + (&hook.callback)(cx) + } + None => { + // + VNode { + dom_id: RealDomNode::empty_cell(), + key: None, + kind: VNodeKind::Suspended, + } + } + } + // + }, + |_| {}, + ) } } @@ -379,4 +437,6 @@ pub struct TaskHandle<'src> { _p: PhantomData<&'src ()>, } #[derive(Clone)] -pub struct SuspendedContext {} +pub struct SuspendedContext<'a> { + pub bump: &'a Bump, +} diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index 9733a6348..a7e1b183d 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -77,7 +77,8 @@ use DomEdit::*; impl<'real, 'bump> DomEditor<'real, 'bump> { // Navigation pub(crate) fn push(&mut self, root: RealDomNode) { - self.edits.push(PushRoot { root }); + let id = root.as_u64(); + self.edits.push(PushRoot { id }); } pub(crate) fn pop(&mut self) { self.edits.push(PopRoot {}); @@ -105,6 +106,7 @@ impl<'real, 'bump> DomEditor<'real, 'bump> { // Create pub(crate) fn create_text_node(&mut self, text: &'bump str, id: RealDomNode) { + let id = id.as_u64(); self.edits.push(CreateTextNode { text, id }); } pub(crate) fn create_element( @@ -113,6 +115,7 @@ impl<'real, 'bump> DomEditor<'real, 'bump> { ns: Option<&'static str>, id: RealDomNode, ) { + let id = id.as_u64(); match ns { Some(ns) => self.edits.push(CreateElementNs { id, ns, tag }), None => self.edits.push(CreateElement { id, tag }), @@ -121,6 +124,7 @@ impl<'real, 'bump> DomEditor<'real, 'bump> { // placeholders are nodes that don't get rendered but still exist as an "anchor" in the real dom pub(crate) fn create_placeholder(&mut self, id: RealDomNode) { + let id = id.as_u64(); self.edits.push(CreatePlaceholder { id }); } @@ -136,7 +140,7 @@ impl<'real, 'bump> DomEditor<'real, 'bump> { scope, event, idx: element_id, - node: realnode, + node: realnode.as_u64(), }); } pub(crate) fn remove_event_listener(&mut self, event: &'static str) { diff --git a/packages/core/src/events.rs b/packages/core/src/events.rs index 1fb836e12..4d9055f61 100644 --- a/packages/core/src/events.rs +++ b/packages/core/src/events.rs @@ -4,8 +4,6 @@ //! 3rd party renderers are responsible for converting their native events into these virtual event types. Events might //! be heavy or need to interact through FFI, so the events themselves are designed to be lazy. - - use crate::innerlude::{RealDomNode, ScopeIdx}; #[derive(Debug)] @@ -24,10 +22,10 @@ pub struct EventTrigger { } impl EventTrigger { - pub fn new_from_task(originator: ScopeIdx) -> Self { + pub fn new_from_task(originator: ScopeIdx, hook_idx: usize) -> Self { Self { originator, - event: VirtualEvent::FiberEvent, + event: VirtualEvent::AsyncEvent { hook_idx }, priority: EventPriority::Low, real_node_id: None, } @@ -103,11 +101,18 @@ pub enum VirtualEvent { MouseEvent(on::MouseEvent), PointerEvent(on::PointerEvent), - // Whenever a task is ready (complete) Dioxus produces this "FiberEvent" - FiberEvent, - // image event has conflicting method types // ImageEvent(event_data::ImageEvent), + + // Whenever a task is ready (complete) Dioxus produces this "AsyncEvent" + // + // Async events don't necessarily propagate into a scope being ran. It's up to the event itself + // to force an update for itself. + AsyncEvent { hook_idx: usize }, + + // These are more intrusive than the rest + SuspenseEvent { hook_idx: usize }, + OtherEvent, } @@ -159,10 +164,12 @@ pub mod on { $( $(#[$method_attr])* - pub fn $name<'a, F: FnMut($wrapper) + 'a>( + pub fn $name<'a, F>( c: NodeFactory<'a>, mut callback: F, - ) -> Listener<'a> { + ) -> Listener<'a> + where F: FnMut($wrapper) + 'a + { let bump = &c.bump(); Listener { event: stringify!($name), diff --git a/packages/core/src/hooklist.rs b/packages/core/src/hooklist.rs index 8942d934a..06ed54d32 100644 --- a/packages/core/src/hooklist.rs +++ b/packages/core/src/hooklist.rs @@ -29,8 +29,20 @@ impl InnerHook { } impl HookList { - pub(crate) fn push(&self, new: T) { - self.vals.push(InnerHook::new(Box::new(new))) + /// Unsafely get a mutable reference to any of the hooks + /// + /// This is unsafe because an &mut T might be aliased if the hook data is already borrowed/in use in the component + /// + /// This method should be reserved for internal methods that are guaranteed that this hook is not aliased anyhwere + /// inside the component body, or outside into children components. + /// + /// This method is currently used only by the suspense system whose hook implementation guarantees that all &T is dropped + /// before the suspense handler is ran. + pub(crate) unsafe fn get_mut(&self, idx: usize) -> Option<&mut T> { + self.vals.get(idx).and_then(|inn| { + let raw_box = unsafe { &mut *inn.cell.get() }; + raw_box.downcast_mut::() + }) } pub(crate) fn next(&self) -> Option<&mut T> { @@ -41,9 +53,17 @@ impl HookList { }) } + #[inline] + pub(crate) fn push(&self, new: T) { + self.vals.push(InnerHook::new(Box::new(new))) + } + /// This resets the internal iterator count /// It's okay that we've given out each hook, but now we have the opportunity to give it out again /// Therefore, resetting is cosudered unsafe + /// + /// This should only be ran by Dioxus itself before "running scope". + /// Dioxus knows how to descened through the tree to prevent mutable aliasing. pub(crate) unsafe fn reset(&mut self) { self.idx.set(0); } diff --git a/packages/core/src/scope.rs b/packages/core/src/scope.rs index 6ea56f21f..82589db12 100644 --- a/packages/core/src/scope.rs +++ b/packages/core/src/scope.rs @@ -177,7 +177,7 @@ impl Scope { .. } = trigger; - if let &VirtualEvent::FiberEvent = &event { + if let &VirtualEvent::AsyncEvent { .. } = &event { log::info!("arrived a fiber event"); return Ok(()); } diff --git a/packages/core/src/serialize.rs b/packages/core/src/serialize.rs index 954e9be9d..3cd9036dc 100644 --- a/packages/core/src/serialize.rs +++ b/packages/core/src/serialize.rs @@ -7,7 +7,7 @@ //! //! -use crate::{innerlude::ScopeIdx, RealDomNode}; +use crate::innerlude::ScopeIdx; use serde::{Deserialize, Serialize}; /// A `DomEdit` represents a serialzied form of the VirtualDom's trait-based API. This allows streaming edits across the @@ -16,7 +16,7 @@ use serde::{Deserialize, Serialize}; #[serde(tag = "type")] pub enum DomEdit<'bump> { PushRoot { - root: RealDomNode, + id: u64, }, PopRoot, AppendChildren { @@ -29,24 +29,24 @@ pub enum DomEdit<'bump> { RemoveAllChildren, CreateTextNode { text: &'bump str, - id: RealDomNode, + id: u64, }, CreateElement { tag: &'bump str, - id: RealDomNode, + id: u64, }, CreateElementNs { tag: &'bump str, - id: RealDomNode, + id: u64, ns: &'static str, }, CreatePlaceholder { - id: RealDomNode, + id: u64, }, NewEventListener { event: &'static str, scope: ScopeIdx, - node: RealDomNode, + node: u64, idx: usize, }, RemoveEventListener { diff --git a/packages/core/src/util.rs b/packages/core/src/util.rs index 76c1b6005..8e7bf94ea 100644 --- a/packages/core/src/util.rs +++ b/packages/core/src/util.rs @@ -4,7 +4,7 @@ use std::{ }; use futures_util::StreamExt; -use slotmap::{DefaultKey, KeyData}; +use slotmap::{DefaultKey, Key, KeyData}; use crate::innerlude::*; @@ -66,13 +66,16 @@ impl RealDomNode { let key: DefaultKey = data.into(); Self(key) } + pub fn empty_cell() -> Cell { + Cell::new(Self::empty()) + } pub fn from_u64(id: u64) -> Self { let data = KeyData::from_ffi(id); let key: DefaultKey = data.into(); Self(key) } - pub fn empty_cell() -> Cell { - Cell::new(Self::empty()) + pub fn as_u64(&self) -> u64 { + self.0.data().as_ffi() } } diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index c7f3734e0..1e74a2c11 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -297,10 +297,29 @@ impl VirtualDom { match &trigger.event { VirtualEvent::OtherEvent => todo!(), - // Fiber events - // VirtualEvent::FiberEvent => { - // // - // } + // Nothing yet + VirtualEvent::AsyncEvent { .. } => {} + + // Suspense Events! A component's suspended node is updated + VirtualEvent::SuspenseEvent { hook_idx } => { + let scope = self.components.try_get_mut(trigger.originator).unwrap(); + + // safety: we are sure that there are no other references to the inner content of this hook + let hook = unsafe { scope.hooks.get_mut::(*hook_idx) }.unwrap(); + + let cx = SuspendedContext { + bump: &scope.cur_frame().bump, + }; + + // generate the new node! + let callback: VNode<'s> = (&hook.callback)(cx); + + // diff that node with the node that was originally suspended! + + // hook.callback; + + // + } // This is the "meat" of our cooperative scheduler // As updates flow in, we re-evalute the event queue and decide if we should be switching the type of work diff --git a/packages/hooks/src/usestate.rs b/packages/hooks/src/usestate.rs index b2652336c..b8028bdd1 100644 --- a/packages/hooks/src/usestate.rs +++ b/packages/hooks/src/usestate.rs @@ -54,7 +54,7 @@ pub fn use_state<'a, 'c, T: 'static, F: FnOnce() -> T, P>( initial_state_fn: F, ) -> UseState { cx.use_hook( - move || UseStateInner { + move |_| UseStateInner { current_val: initial_state_fn(), callback: cx.schedule_update(), wip: Rc::new(RefCell::new(None)), diff --git a/packages/html/src/lib.rs b/packages/html/src/lib.rs index ba4bbea65..b5264bf6d 100644 --- a/packages/html/src/lib.rs +++ b/packages/html/src/lib.rs @@ -112,95 +112,505 @@ pub trait GlobalAttributes { translate; } style_trait_methods! { - background: "background", - background_attachment: "background-attachment", + /// Specifies the alignment of flexible container's items within the flex container. + align_content: "align-content", - /// ## Definition and Usage - /// - /// The background-color property sets the background color of an element. - /// - /// The background of an element is the total size of the element, including padding and border (but not the margin). - /// - /// Tip: Use a background color and a text color that makes the text easy to read. - /// - /// ## Example - /// - /// ``` - /// body { - /// style: { - /// background_color: "coral" - /// } - /// } - /// ``` + /// Specifies the default alignment for items within the flex container. + align_items: "align-items", + + /// Specifies the alignment for selected items within the flex container. + align_self: "align-self", + + /// Specifies the keyframe_based animations. + animation: "animation", + + /// Specifies when the animation will start. + animation_delay: "animation-delay", + + /// Specifies whether the animation should play in reverse on alternate cycles or not. + animation_direction: "animation-direction", + + /// Specifies the number of seconds or milliseconds an animation should take to complete one cycle + animation_duration: "animation-duration", + + /// Specifies how a CSS animation should apply styles to its target before and after it is executing + animation_fill_mode: "animation-fill-mode", + + /// Specifies the number of times an animation cycle should be played before stopping. + animation_iteration_count: "animation-iteration-count", + + /// Specifies the name of @keyframes defined animations that should be applied to the selected element + animation_name: "animation-name", + + /// Specifies whether the animation is running or paused. + animation_play_state: "animation-play-state", + + /// Specifies how a CSS animation should progress over the duration of each cycle. + animation_timing_function: "animation-timing-function", + + /// Specifies whether or not the "back" side of a transformed element is visible when facing the user. + backface_visibility: "backface-visibility", + + + /// Defines a variety of background properties within one declaration. + background: "background", + + /// Specify whether the background image is fixed in the viewport or scrolls. + background_attachment: "background-attachment", + /// Specifies the painting area of the background. + background_clip: "background-clip", + + + /// Defines an element's background color. background_color: "background-color", + + /// Defines an element's background image. background_image: "background-image", + /// Specifies the positioning area of the background images. + background_origin: "background-origin", + + + /// Defines the origin of a background image. background_position: "background-position", + + /// Specify whether/how the background image is tiled. background_repeat: "background-repeat", + /// Specifies the size of the background images. + background_size: "background-size", + + + /// Sets the width, style, and color for all four sides of an element's border. border: "border", + + /// Sets the width, style, and color of the bottom border of an element. border_bottom: "border-bottom", + + /// Sets the color of the bottom border of an element. border_bottom_color: "border-bottom-color", + /// Defines the shape of the bottom_left border corner of an element. + border_bottom_left_radius: "border-bottom-left-radius", + + /// Defines the shape of the bottom_right border corner of an element. + border_bottom_right_radius: "border-bottom-right-radius", + + + /// Sets the style of the bottom border of an element. border_bottom_style: "border-bottom-style", + + /// Sets the width of the bottom border of an element. border_bottom_width: "border-bottom-width", + + /// Specifies whether table cell borders are connected or separated. + border_collapse: "border-collapse", + + /// Sets the color of the border on all the four sides of an element. border_color: "border-color", + /// Specifies how an image is to be used in place of the border styles. + border_image: "border-image", + + /// Specifies the amount by which the border image area extends beyond the border box. + border_image_outset: "border-image-outset", + + /// Specifies whether the image_border should be repeated, rounded or stretched. + border_image_repeat: "border-image-repeat", + + /// Specifies the inward offsets of the image_border. + border_image_slice: "border-image-slice", + + /// Specifies the location of the image to be used as a border. + border_image_source: "border-image-source", + + /// Specifies the width of the image_border. + border_image_width: "border-image-width", + + + /// Sets the width, style, and color of the left border of an element. border_left: "border-left", + + /// Sets the color of the left border of an element. border_left_color: "border-left-color", + + /// Sets the style of the left border of an element. border_left_style: "border-left-style", + + /// Sets the width of the left border of an element. border_left_width: "border-left-width", + /// Defines the shape of the border corners of an element. + border_radius: "border-radius", + + + /// Sets the width, style, and color of the right border of an element. border_right: "border-right", + + /// Sets the color of the right border of an element. border_right_color: "border-right-color", + + /// Sets the style of the right border of an element. border_right_style: "border-right-style", + + /// Sets the width of the right border of an element. border_right_width: "border-right-width", + + /// Sets the spacing between the borders of adjacent table cells. + border_spacing: "border-spacing", + + /// Sets the style of the border on all the four sides of an element. border_style: "border-style", + + /// Sets the width, style, and color of the top border of an element. border_top: "border-top", + + /// Sets the color of the top border of an element. border_top_color: "border-top-color", + /// Defines the shape of the top_left border corner of an element. + border_top_left_radius: "border-top-left-radius", + + /// Defines the shape of the top_right border corner of an element. + border_top_right_radius: "border-top-right-radius", + + + /// Sets the style of the top border of an element. border_top_style: "border-top-style", + + /// Sets the width of the top border of an element. border_top_width: "border-top-width", + + /// Sets the width of the border on all the four sides of an element. border_width: "border-width", + + /// Specify the location of the bottom edge of the positioned element. + bottom: "bottom", + /// Applies one or more drop_shadows to the element's box. + box_shadow: "box-shadow", + + /// Alter the default CSS box model. + box_sizing: "box-sizing", + + + /// Specify the position of table's caption. + caption_side: "caption-side", + + /// Specifies the placement of an element in relation to floating elements. clear: "clear", + + /// Defines the clipping region. clip: "clip", + + /// Specify the color of the text of an element. color: "color", + /// Specifies the number of columns in a multi_column element. + column_count: "column-count", + + /// Specifies how columns will be filled. + column_fill: "column-fill", + + /// Specifies the gap between the columns in a multi_column element. + column_gap: "column-gap", + + /// Specifies a straight line, or "rule", to be drawn between each column in a multi_column element. + column_rule: "column-rule", + + /// Specifies the color of the rules drawn between columns in a multi_column layout. + column_rule_color: "column-rule-color", + + /// Specifies the style of the rule drawn between the columns in a multi_column layout. + column_rule_style: "column-rule-style", + + /// Specifies the width of the rule drawn between the columns in a multi_column layout. + column_rule_width: "column-rule-width", + + /// Specifies how many columns an element spans across in a multi_column layout. + column_span: "column-span", + + /// Specifies the optimal width of the columns in a multi_column element. + column_width: "column-width", + + /// A shorthand property for setting column_width and column_count properties. + columns: "columns", + + + /// Inserts generated content. + content: "content", + + /// Increments one or more counter values. + counter_increment: "counter-increment", + + /// Creates or resets one or more counters. + counter_reset: "counter-reset", + + /// Specify the type of cursor. cursor: "cursor", + + /// Define the text direction/writing direction. + direction: "direction", + + /// Specifies how an element is displayed onscreen. display: "display", - filter: "filter", - css_float: "css-float", + + /// Show or hide borders and backgrounds of empty table cells. + empty_cells: "empty-cells", + /// Specifies the components of a flexible length. + flex: "flex", + + /// Specifies the initial main size of the flex item. + flex_basis: "flex-basis", + + /// Specifies the direction of the flexible items. + flex_direction: "flex-direction", + + /// A shorthand property for the flex_direction and the flex_wrap properties. + flex_flow: "flex-flow", + + /// Specifies how the flex item will grow relative to the other items inside the flex container. + flex_grow: "flex-grow", + + /// Specifies how the flex item will shrink relative to the other items inside the flex container + flex_shrink: "flex-shrink", + + /// Specifies whether the flexible items should wrap or not. + flex_wrap: "flex-wrap", + + + /// Specifies whether or not a box should float. + float: "float", + + /// Defines a variety of font properties within one declaration. font: "font", + + /// Defines a list of fonts for element. font_family: "font-family", + + /// Defines the font size for the text. font_size: "font-size", + /// Preserves the readability of text when font fallback occurs. + font_size_adjust: "font-size-adjust", + + /// Selects a normal, condensed, or expanded face from a font. + font_stretch: "font-stretch", + + + /// Defines the font style for the text. + font_style: "font-style", + + /// Specify the font variant. font_variant: "font-variant", + + /// Specify the font weight of the text. font_weight: "font-weight", + + /// Specify the height of an element. height: "height", + /// Specifies how flex items are aligned along the main axis of the flex container after any flexible lengths and auto margins have been resolved. + justify_content: "auto margins have been resolved.", + + + /// Specify the location of the left edge of the positioned element. left: "left", + + /// Sets the extra spacing between letters. letter_spacing: "letter-spacing", + + /// Sets the height between lines of text. line_height: "line-height", + + /// Defines the display style for a list and list elements. list_style: "list-style", + + /// Specifies the image to be used as a list_item marker. list_style_image: "list-style-image", + + /// Specifies the position of the list_item marker. list_style_position: "list-style-position", + + /// Specifies the marker style for a list_item. list_style_type: "list-style-type", + + /// Sets the margin on all four sides of the element. margin: "margin", + + /// Sets the bottom margin of the element. margin_bottom: "margin-bottom", + + /// Sets the left margin of the element. margin_left: "margin-left", + + /// Sets the right margin of the element. margin_right: "margin-right", + + /// Sets the top margin of the element. margin_top: "margin-top", + + /// Specify the maximum height of an element. + max_height: "max-height", + + /// Specify the maximum width of an element. + max_width: "max-width", + + /// Specify the minimum height of an element. + min_height: "min-height", + + /// Specify the minimum width of an element. + min_width: "min-width", + /// Specifies the transparency of an element. + opacity: "opacity", + + /// Specifies the order in which a flex items are displayed and laid out within a flex container. + order: "order", + + + /// Sets the width, style, and color for all four sides of an element's outline. + outline: "outline", + + /// Sets the color of the outline. + outline_color: "outline-color", + /// Set the space between an outline and the border edge of an element. + outline_offset: "outline-offset", + + + /// Sets a style for an outline. + outline_style: "outline-style", + + /// Sets the width of the outline. + outline_width: "outline-width", + + /// Specifies the treatment of content that overflows the element's box. overflow: "overflow", + /// Specifies the treatment of content that overflows the element's box horizontally. + overflow_x: "overflow-x", + + /// Specifies the treatment of content that overflows the element's box vertically. + overflow_y: "overflow-y", + + + /// Sets the padding on all four sides of the element. padding: "padding", + + /// Sets the padding to the bottom side of an element. padding_bottom: "padding-bottom", + + /// Sets the padding to the left side of an element. padding_left: "padding-left", + + /// Sets the padding to the right side of an element. padding_right: "padding-right", + + /// Sets the padding to the top side of an element. padding_top: "padding-top", + + /// Insert a page breaks after an element. page_break_after: "page-break-after", + + /// Insert a page breaks before an element. page_break_before: "page-break-before", + + /// Insert a page breaks inside an element. + page_break_inside: "page-break-inside", + /// Defines the perspective from which all child elements of the object are viewed. + perspective: "perspective", + + /// Defines the origin (the vanishing point for the 3D space) for the perspective property. + perspective_origin: "perspective-origin", + + + /// Specifies how an element is positioned. position: "position", - stroke_dasharray: "stroke-dasharray", - stroke_dashoffset: "stroke-dashoffset", + + /// Specifies quotation marks for embedded quotations. + quotes: "quotes", + /// Specifies whether or not an element is resizable by the user. + resize: "resize", + + + /// Specify the location of the right edge of the positioned element. + right: "right", + /// Specifies the length of the tab character. + tab_size: "tab-size", + + /// Specifies a table layout algorithm. + table_layout: "table-layout", + + /// Sets the horizontal alignment of inline content. text_align: "text-align", + /// Specifies how the last line of a block or a line right before a forced line break is aligned when is justify.", + text_align_last: "text-align-last", + + /// Specifies the decoration added to text. text_decoration: "text-decoration", + /// Specifies the color of the text_decoration_line. + text_decoration_color: "text-decoration-color", + + /// Specifies what kind of line decorations are added to the element. + text_decoration_line: "text-decoration-line", + + /// Specifies the style of the lines specified by the text_decoration_line property + text_decoration_style: "text-decoration-style", + + + /// Indent the first line of text. text_indent: "text-indent", + /// Specifies the justification method to use when the text_align property is set to justify. + text_justify: "text-justify", + + /// Specifies how the text content will be displayed, when it overflows the block containers. + text_overflow: "text-overflow", + + /// Applies one or more shadows to the text content of an element. + text_shadow: "text-shadow", + + + /// Transforms the case of the text. text_transform: "text-transform", + + /// Specify the location of the top edge of the positioned element. top: "top", + /// Applies a 2D or 3D transformation to an element. + transform: "transform", + + /// Defines the origin of transformation for an element. + transform_origin: "transform-origin", + + /// Specifies how nested elements are rendered in 3D space. + transform_style: "transform-style", + + /// Defines the transition between two states of an element. + transition: "transition", + + /// Specifies when the transition effect will start. + transition_delay: "transition-delay", + + /// Specifies the number of seconds or milliseconds a transition effect should take to complete. + transition_duration: "transition-duration", + + /// Specifies the names of the CSS properties to which a transition effect should be applied. + transition_property: "transition-property", + + /// Specifies the speed curve of the transition effect. + transition_timing_function: "transition-timing-function", + + + /// Sets the vertical positioning of an element relative to the current text baseline. vertical_align: "vertical-align", + + /// Specifies whether or not an element is visible. visibility: "visibility", + + /// Specifies how white space inside the element is handled. + white_space: "white-space", + + /// Specify the width of an element. width: "width", - z_index: "z-index", + /// Specifies how to break lines within words. + word_break: "word-break", + + + /// Sets the spacing between words. + word_spacing: "word-spacing", + /// Specifies whether to break words when the content overflows the boundaries of its container. + word_wrap: "word-wrap", + + /// Specifies a layering or stacking order for positioned elements. + z_index : "z-index ", + } aria_trait_methods! { aria_current: "aria-current", diff --git a/packages/web/examples/async_web.rs b/packages/web/examples/async_web.rs index dbefbbf56..08a2c67dd 100644 --- a/packages/web/examples/async_web.rs +++ b/packages/web/examples/async_web.rs @@ -32,34 +32,33 @@ struct DogApi { const ENDPOINT: &str = "https://dog.ceo/api/breeds/image/random/"; static App: FC<()> = |cx| { - // let mut count = use_state(cx, || 0); let state = use_state(cx, || 0); - let set_val = state.setter(); - let (tas, g) = cx.use_task(|| async move { - set_val(10); - surf::get(ENDPOINT).recv_json::().await - }); + let request = cx.use_task(|| surf::get(ENDPOINT).recv_json::()).1; - let dog_node = match g.as_ref().and_then(|f| f.as_ref().ok()) { - Some(res) => rsx!(in cx, img { src: "{res.message}" }), - None => rsx!(in cx, div { "No doggos for you :(" }), + let dog_node = if let Some(Ok(res)) = request { + rsx!(in cx, img { src: "{res.message}" }) + } else { + rsx!(in cx, div { "No doggos for you :(" }) }; cx.render(rsx! { div { + style: { + align_items: "center" + } section { class: "py-12 px-4 text-center" div { class: "w-full max-w-2xl mx-auto" span { class: "text-sm font-semibold" "count: {state}" } + br {} div { button { onclick: move |_| state.set(state + 1) "incr" } br {} - br {} button { onclick: move |_| state.set(state - 1) "decr" diff --git a/packages/web/examples/blah.rs b/packages/web/examples/blah.rs index d17426425..e4b0f895a 100644 --- a/packages/web/examples/blah.rs +++ b/packages/web/examples/blah.rs @@ -28,6 +28,9 @@ static App: FC<()> = |cx| { let mut state = use_state(cx, || 0); cx.render(rsx! { div { + style: { + align_items: "center" + } section { class: "py-12 px-4 text-center" div { class: "w-full max-w-2xl mx-auto" span { class: "text-sm font-semibold" diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index aca261cb7..c6a1f3f1f 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -99,8 +99,6 @@ impl WebsysRenderer { if let Some(real_trigger) = trigger { log::info!("event received"); - // let root_node = body_element.first_child().unwrap(); - // websys_dom.stack.push(root_node.clone()); self.internal_dom.queue_event(real_trigger)?; @@ -110,23 +108,10 @@ impl WebsysRenderer { .await?; websys_dom.process_edits(&mut edits); } - - // let t2 = self.internal_dom.tasks.next(); - // futures::select! { - // trigger = t1 => { - // log::info!("event received"); - // let root_node = body_element.first_child().unwrap(); - // websys_dom.stack.push(root_node.clone()); - // self.internal_dom - // .progress_with_event(&mut websys_dom, trigger)?; - // }, - // () = t2 => {} - // }; } - // while let Some(trigger) = websys_dom.wait_for_event().await { - // } - Ok(()) // should actually never return from this, should be an error, rustc just cant see it + // should actually never return from this, should be an error, rustc just cant see it + Ok(()) } } diff --git a/packages/web/src/new.rs b/packages/web/src/new.rs index 5aee667ff..0595a31a2 100644 --- a/packages/web/src/new.rs +++ b/packages/web/src/new.rs @@ -74,7 +74,7 @@ impl WebsysDom { for edit in edits.drain(..) { log::info!("Handling edit: {:#?}", edit); match edit { - DomEdit::PushRoot { root } => self.push(root), + DomEdit::PushRoot { id: root } => self.push(root), DomEdit::PopRoot => self.pop(), DomEdit::AppendChildren { many } => self.append_children(many), DomEdit::ReplaceWith { many } => self.replace_with(many), @@ -97,8 +97,8 @@ impl WebsysDom { } } } - fn push(&mut self, root: RealDomNode) { - let key = root.0; + fn push(&mut self, root: u64) { + let key = DefaultKey::from(KeyData::from_ffi(root)); let domnode = self.nodes.get_mut(key); let domnode = domnode.unwrap().as_mut().unwrap(); @@ -186,10 +186,10 @@ impl WebsysDom { todo!() } - fn create_placeholder(&mut self, id: RealDomNode) { + fn create_placeholder(&mut self, id: u64) { self.create_element("pre", None, id) } - fn create_text_node(&mut self, text: &str, id: RealDomNode) { + fn create_text_node(&mut self, text: &str, id: u64) { // let nid = self.node_counter.next(); let textnode = self @@ -199,18 +199,13 @@ impl WebsysDom { .unwrap(); self.stack.push(textnode.clone()); - let mut slot = self.nodes.get_mut(id.0).unwrap(); - *slot = Some(textnode); - - // let nid = self.nodes.insert(textnode); - // let nid = nid.data().as_ffi(); - - // log::debug!("Called [`create_text_node`]: {}, {}", text, nid); - - // RealDomNode::new(nid) + *self + .nodes + .get_mut(DefaultKey::from(KeyData::from_ffi(id))) + .unwrap() = Some(textnode); } - fn create_element(&mut self, tag: &str, ns: Option<&'static str>, id: RealDomNode) { + fn create_element(&mut self, tag: &str, ns: Option<&'static str>, id: u64) { let tag = wasm_bindgen::intern(tag); let el = match ns { Some(ns) => self @@ -226,10 +221,10 @@ impl WebsysDom { .dyn_into::() .unwrap(), }; + let id = DefaultKey::from(KeyData::from_ffi(id)); self.stack.push(el.clone()); - let mut slot = self.nodes.get_mut(id.0).unwrap(); - *slot = Some(el); + *self.nodes.get_mut(id).unwrap() = Some(el); // let nid = self.node_counter.?next(); // let nid = self.nodes.insert(el).data().as_ffi(); // log::debug!("Called [`create_element`]: {}, {:?}", tag, nid); @@ -241,7 +236,7 @@ impl WebsysDom { event: &'static str, scope: ScopeIdx, _element_id: usize, - real_id: RealDomNode, + real_id: u64, ) { let (_on, event) = event.split_at(2); let event = wasm_bindgen::intern(event); @@ -260,7 +255,7 @@ impl WebsysDom { let scope_id = scope.data().as_ffi(); el.set_attribute( &format!("dioxus-event-{}", event), - &format!("{}.{}", scope_id, real_id.0.data().as_ffi()), + &format!("{}.{}", scope_id, real_id), ) .unwrap(); diff --git a/src/lib.rs b/src/lib.rs index 73be53ff3..69f4dc82b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -108,7 +108,7 @@ //! fn my_hook<'a>(cx: &impl Scoped<'a>) -> &'a String { //! cx.use_hook( //! // Initializer stores a value -//! || String::new("stored_data"), +//! |hook_idx| String::new("stored_data"), //! //! // Runner returns the hook value every time the component is rendered //! |hook| &*hook, diff --git a/wip.md b/wip.md new file mode 100644 index 000000000..e69de29bb