Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Niedzwiedzw/43361dbc669ea329f92896ce3b2498be to your computer and use it in GitHub Desktop.
Save Niedzwiedzw/43361dbc669ea329f92896ce3b2498be to your computer and use it in GitHub Desktop.
on_mounted.rs
trait StaticCallbackOnce: FnOnce() + Send + 'static {}
impl<T> StaticCallbackOnce for T where T: FnOnce() + Send + 'static {}
struct Callback(Option<Box<dyn StaticCallbackOnce>>);
#[extension_traits::extension(pub trait TachysHookMountEventsExt)]
impl<T> T
where
Self: Sized,
T: IntoRender,
{
fn on_mount<M, U>(self, on_mounted: M, on_unmounted: U) -> OnMounted<<T as IntoRender>::Output>
where
M: StaticCallbackOnce,
U: StaticCallbackOnce,
{
OnMounted {
inner: self.into_render(),
mount_callbacks: MountCallbacks {
on_mounted: Callback::new(on_mounted),
on_unmounted: Callback::new(on_unmounted),
},
}
}
}
pub enum MountEvent {
Mounted,
Unmounted,
}
impl Callback {
const fn empty() -> Self {
Self(None)
}
fn new<F>(callback: F) -> Self
where
F: StaticCallbackOnce,
{
Self(Some(Box::new(callback)))
}
#[instrument(skip(self))]
pub fn call(&mut self) {
match self.0.take() {
Some(some) => (some)(),
None => warn!("this callback has already been executed"),
}
}
}
struct MountCallbacks {
on_mounted: Callback,
on_unmounted: Callback,
}
impl MountCallbacks {
const fn empty() -> Self {
Self {
on_mounted: Callback::empty(),
on_unmounted: Callback::empty(),
}
}
}
pub struct OnMounted<Inner> {
inner: Inner,
mount_callbacks: MountCallbacks,
}
pub struct OnMountedState<Inner> {
inner: Inner,
mount_callbacks: MountCallbacks,
}
impl<Inner> Render for OnMounted<Inner>
where
Inner: Render,
OnMountedState<Inner::State>: Mountable,
{
type State = OnMountedState<<Inner as Render>::State>;
fn build(self) -> Self::State {
let Self { inner, mount_callbacks } = self;
OnMountedState {
inner: inner.build(),
mount_callbacks,
}
}
fn rebuild(self, state: &mut Self::State) {
self.inner.rebuild(&mut state.inner)
}
}
impl<Inner> AddAnyAttr for OnMounted<Inner>
where
Inner: AddAnyAttr,
{
type Output<SomeNewAttr: attribute::Attribute> = OnMounted<<Inner as AddAnyAttr>::Output<SomeNewAttr>>;
fn add_any_attr<NewAttr: attribute::Attribute>(self, attr: NewAttr) -> Self::Output<NewAttr>
where
Self::Output<NewAttr>: RenderHtml,
{
OnMounted {
inner: self.inner.add_any_attr(attr),
mount_callbacks: self.mount_callbacks,
}
}
}
impl<Inner> RenderHtml for OnMounted<Inner>
where
Inner: RenderHtml + AddAnyAttr + Send,
{
type AsyncOutput = Inner::AsyncOutput;
type Owned = Inner::Owned;
const MIN_LENGTH: usize = Inner::MIN_LENGTH;
fn dry_resolve(&mut self) {
self.inner.dry_resolve();
}
fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send {
self.inner.resolve()
}
fn to_html_with_buf(
self,
buf: &mut String,
position: &mut tachys::view::Position,
escape: bool,
mark_branches: bool,
extra_attrs: Vec<attribute::any_attribute::AnyAttribute>,
) {
self.inner
.to_html_with_buf(buf, position, escape, mark_branches, extra_attrs);
}
fn hydrate<const FROM_SERVER: bool>(self, cursor: &tachys::hydration::Cursor, position: &tachys::view::PositionState) -> Self::State {
OnMountedState {
inner: self.inner.hydrate::<FROM_SERVER>(cursor, position),
mount_callbacks: MountCallbacks::empty(),
}
}
fn into_owned(self) -> Self::Owned {
self.into_owned()
}
}
impl<Inner> Mountable for OnMountedState<Inner>
where
Inner: Mountable,
{
fn unmount(&mut self) {
self.mount_callbacks.on_unmounted.call();
self.inner.unmount()
}
fn mount(&mut self, parent: &tachys::renderer::types::Element, marker: Option<&tachys::renderer::types::Node>) {
self.mount_callbacks.on_mounted.call();
self.inner.mount(parent, marker)
}
fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
self.inner.insert_before_this(child)
}
fn elements(&self) -> Vec<tachys::renderer::types::Element> {
self.inner.elements()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment