use crate::{react_bindings, KeyType, VNode};
use std::any::{type_name, Any};
use wasm_bindgen::prelude::*;
#[doc(hidden)]
pub struct BuildParams {
name: &'static str,
key: Option<JsValue>,
}
pub trait Component: Sized {
fn render(&self) -> VNode;
fn key(self, key: Option<impl KeyType>) -> Keyed<Self> {
Keyed(self, key.map(|x| x.into()))
}
#[doc(hidden)]
fn _build_params(&self) -> BuildParams {
BuildParams {
name: type_name::<Self>(),
key: None,
}
}
#[doc(hidden)]
fn _build_with_name_and_key(self, name: &str, key: Option<JsValue>) -> VNode {
VNode(react_bindings::create_rust_component(
name,
&key.unwrap_or(JsValue::UNDEFINED),
unsafe { ComponentWrapperWithLifetime(Box::new(self)).extend_lifetime() },
))
}
fn build(self) -> VNode {
let BuildParams { name, key } = self._build_params();
self._build_with_name_and_key(name, key)
}
fn memoized(self) -> Memoized<Self>
where
Self: PartialEq + 'static,
{
Memoized(self)
}
}
#[derive(Debug, PartialEq)]
pub struct Keyed<T>(T, Option<JsValue>);
impl<T: Component> Component for Keyed<T> {
fn render(&self) -> VNode {
self.0.render()
}
fn _build_params(&self) -> BuildParams {
let BuildParams { name, .. } = self.0._build_params();
BuildParams {
name,
key: self.1.clone(),
}
}
fn _build_with_name_and_key(self, name: &str, key: Option<JsValue>) -> VNode {
self.0._build_with_name_and_key(name, key)
}
}
#[derive(Debug, PartialEq)]
pub struct Memoized<T>(T);
impl<T: Component + PartialEq + 'static> Component for Memoized<T> {
fn render(&self) -> VNode {
self.0.render()
}
fn _build_params(&self) -> BuildParams {
let BuildParams { name, key, .. } = self.0._build_params();
BuildParams { name, key }
}
fn _build_with_name_and_key(self, name: &str, key: Option<JsValue>) -> VNode {
VNode(react_bindings::create_rust_memo_component(
name,
&key.unwrap_or(JsValue::UNDEFINED),
MemoComponentWrapper(Box::new(self.0)),
))
}
}
trait ObjectSafeComponent {
fn render(&self) -> VNode;
}
impl<T: Component> ObjectSafeComponent for T {
fn render(&self) -> VNode {
Component::render(self)
}
}
struct ComponentWrapperWithLifetime<'a>(Box<dyn ObjectSafeComponent + 'a>);
impl ComponentWrapperWithLifetime<'_> {
unsafe fn extend_lifetime(self) -> ComponentWrapper {
ComponentWrapper(std::mem::transmute::<
Self,
ComponentWrapperWithLifetime<'static>,
>(self))
}
}
#[doc(hidden)]
#[wasm_bindgen(js_name = __WasmReact_ComponentWrapper)]
pub struct ComponentWrapper(ComponentWrapperWithLifetime<'static>);
#[wasm_bindgen(js_class = __WasmReact_ComponentWrapper)]
impl ComponentWrapper {
#[wasm_bindgen]
pub fn render(&self) -> JsValue {
self.0 .0.render().into()
}
}
trait ObjectSafeMemoComponent: ObjectSafeComponent {
fn as_any(&self) -> &dyn Any;
fn eq(&self, other: &dyn ObjectSafeMemoComponent) -> bool;
}
impl<'a, T: Component + PartialEq + 'static> ObjectSafeMemoComponent for T {
fn as_any(&self) -> &dyn Any {
self
}
fn eq(&self, other: &dyn ObjectSafeMemoComponent) -> bool {
other
.as_any()
.downcast_ref::<T>()
.map(|other| PartialEq::eq(self, other))
.unwrap_or(false)
}
}
#[doc(hidden)]
#[wasm_bindgen(js_name = __WasmReact_MemoComponentWrapper)]
pub struct MemoComponentWrapper(Box<dyn ObjectSafeMemoComponent>);
#[wasm_bindgen(js_class = __WasmReact_MemoComponentWrapper)]
impl MemoComponentWrapper {
#[wasm_bindgen]
pub fn render(&self) -> JsValue {
self.0.render().into()
}
#[wasm_bindgen]
pub fn eq(&self, other: &MemoComponentWrapper) -> bool {
self.0.eq(&*other.0)
}
}