use std::cell::RefCell;
use std::rc::Rc;
use crate::element::Element;
use crate::get_document;
use crate::signal::RenderingState;
use crate::state::{ComponentData, HookKey, S, State};
#[diagnostic::on_unimplemented(
message = "`{Self}` Missing `#[derive(Component)]`.",
note = "`#[derive(Component)]` Required for implementing `Component`"
)]
pub trait ComponentBase: Sized {
type Data: ComponentData;
fn into_data(self) -> Self::Data;
fn into_state(self) -> Rc<RefCell<State<Self::Data>>> {
State::new(self.into_data())
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a component.",
label = "Expected Component",
note = "`#[derive(Component)]` does not implement `Component`"
)]
pub trait Component: ComponentBase {
fn render() -> impl Element<Self::Data>;
fn on_mount(_ctx: &mut S<Self>) {}
}
pub struct C<I>(pub I);
impl<I, P> Element<P> for C<I>
where
I: Component + 'static,
{
fn render_box(
self: Box<Self>,
_ctx: &mut State<P>,
render_state: &mut RenderingState,
) -> web_sys::Node {
let data = self.0.into_state();
let element = I::render();
let mut borrow_data = data.borrow_mut();
I::on_mount(&mut borrow_data);
let mut hooks = Vec::new();
let mut state = RenderingState {
keep_alive: render_state.keep_alive,
hooks: &mut hooks,
parent_dep: HookKey::default(),
};
let node = element.render(&mut borrow_data, &mut state);
drop(borrow_data);
render_state.keep_alive.push(Box::new(data));
node
}
}
#[expect(
clippy::expect_used,
reason = "This is the entry point of the framework, and it fails fast."
)]
pub fn mount_at<C: Component>(component: C, target_id: &'static str) {
let data = component.into_state();
let element = C::render();
let mut borrow_data = data.borrow_mut();
C::on_mount(&mut borrow_data);
let mut keep_alive = Vec::new();
let mut hooks = Vec::new();
let mut state = RenderingState {
keep_alive: &mut keep_alive,
hooks: &mut hooks,
parent_dep: HookKey::default(),
};
let node = element.render(&mut borrow_data, &mut state);
let document = get_document();
let target = document
.get_element_by_id(target_id)
.expect("Failed to get mount point");
target
.replace_with_with_node_1(&node)
.expect("Failed to replace mount point");
drop(borrow_data);
std::mem::forget(data);
std::mem::forget(keep_alive);
}
pub fn mount<C: Component>(component: C) {
#[cfg(feature = "panic_hook")]
crate::panics::set_panic_hook();
mount_at(component, natrix_shared::MOUNT_POINT);
}