use crate::{
create_element, hooks::RefContainerValue, props::Props, react_bindings,
Component, VNode,
};
use js_sys::Reflect;
use std::{marker::PhantomData, rc::Rc, thread::LocalKey};
use wasm_bindgen::{intern, JsValue, UnwrapThrowExt};
#[derive(Debug)]
pub struct Context<T> {
js_context: JsValue,
phantom: PhantomData<T>,
}
impl<T> AsRef<JsValue> for Context<T> {
fn as_ref(&self) -> &JsValue {
&self.js_context
}
}
impl<T> From<Context<T>> for JsValue {
fn from(value: Context<T>) -> Self {
value.js_context
}
}
pub fn create_context<T: 'static>(init: Rc<T>) -> Context<T> {
Context {
js_context: react_bindings::create_context(RefContainerValue(init)),
phantom: PhantomData,
}
}
#[derive(Debug, Clone)]
pub struct ContextProvider<T: 'static> {
context: &'static LocalKey<Context<T>>,
value: Option<Rc<T>>,
children: VNode,
}
impl<T: 'static> ContextProvider<T> {
pub fn from(context: &'static LocalKey<Context<T>>) -> Self {
Self {
context,
value: None,
children: ().into(),
}
}
pub fn value(mut self, value: Option<Rc<T>>) -> Self {
self.value = value;
self
}
pub fn build(mut self, children: impl Into<VNode>) -> VNode {
self.children = children.into();
Component::build(self)
}
}
impl<T: 'static> Component for ContextProvider<T> {
fn render(&self) -> VNode {
self.context.with(|context| {
create_element(
&Reflect::get(context.as_ref(), &intern("Provider").into())
.expect_throw("cannot read from context object"),
&{
let mut props = Props::new();
if let Some(value) = self.value.as_ref() {
props = props.insert(
intern("value"),
&RefContainerValue(value.clone()).into(),
);
}
props
},
self.children.clone(),
)
})
}
}