ravel_web/
any.rs

1use std::{any::Any, marker::PhantomData, ops::DerefMut};
2
3use ravel::State;
4use web_sys::wasm_bindgen::UnwrapThrowExt as _;
5
6use crate::{
7    dom::{clear, Position},
8    BuildCx, Builder, RebuildCx, View, ViewMarker, Web,
9};
10
11/// A wrapper around a [`trait@View`], erasing its [`State`] type.
12pub struct AnyView<V: View, Output> {
13    inner: V,
14    phantom: PhantomData<fn(&mut Output)>,
15}
16
17impl<V: View, Output: 'static> Builder<Web> for AnyView<V, Output>
18where
19    V::State: State<Output>,
20{
21    type State = AnyState<Output>;
22
23    fn build(self, cx: BuildCx) -> Self::State {
24        let start = web_sys::Comment::new_with_data("{").unwrap_throw();
25        let end = web_sys::Comment::new_with_data("}").unwrap_throw();
26
27        cx.position.insert(&start);
28        let state = Box::new(self.inner.build(cx));
29        cx.position.insert(&end);
30
31        AnyState { state, start, end }
32    }
33
34    fn rebuild(self, cx: RebuildCx, state: &mut Self::State) {
35        match (state.state.as_mut_dyn_any().deref_mut() as &mut dyn Any)
36            .downcast_mut::<V::State>()
37        {
38            Some(state) => self.inner.rebuild(cx, state),
39            None => {
40                clear(cx.parent, &state.start, &state.end);
41
42                state.state = Box::new(self.inner.build(BuildCx {
43                    position: Position {
44                        parent: cx.parent,
45                        insert_before: &state.end,
46                        waker: cx.waker,
47                    },
48                }))
49            }
50        }
51    }
52}
53
54/// The state for an [`AnyView`].
55pub struct AnyState<Output> {
56    state: Box<dyn State<Output>>,
57    start: web_sys::Comment,
58    end: web_sys::Comment,
59}
60
61impl<Output: 'static> State<Output> for AnyState<Output> {
62    fn run(&mut self, output: &mut Output) {
63        self.state.run(output)
64    }
65}
66
67impl<Output> ViewMarker for AnyState<Output> {}
68
69/// Wraps a [`trait@View`], erasing its [`State`] type.
70///
71/// Using this inside a [`ravel::with`] callback makes it possible to dynamically
72/// choose an implementation type.
73pub fn any<V: View, Output: 'static>(view: V) -> AnyView<V, Output> {
74    AnyView {
75        inner: view,
76        phantom: PhantomData,
77    }
78}