leptos-posthoc 0.1.0

A crate for dynamically hydrating static/opaque HTML using leptos components
Documentation
# leptos posthoc

Allows for "hydrating" an existent DOM with reactive leptos components,
without the entire DOM having to be generated by leptos components.

## Why would you want that?
1. **CSR:** It allows for building scripts that others can just embed in their arbitrary HTML documents, that add
    `<insert your favourite fancy feature here>`. For an example, see the `examples/csr` directory:
    the `index.html` has a node `<script src='csr_example.js'></script>`, which "hydrates" nodes with the
    `data-replace-with-leptos`-attribute with leptos components that add a hover-popup (using
    [thaw]https://docs.rs/thaw).
2. **SSR:** Occasionally, you might want to dynamically insert some HTML string into the DOM, for example one that
    gets generated from some data and returned by a server function. This HTML might contain certain nodes that
    we want to attach reactive functionality to. For an example, see the `examples/ssr` directory.

## CSR Example
Say we want to replace all elements with the attribute `data-replace-with-leptos` with a leptos component
`MyReplacementComponent`, that simply wraps the original children in a `div` with a solid red border. This
component would roughly look like this:
```
#[component]
fn MyReplacementComponent(orig:OriginalNode) -> impl IntoView {
   view! {
      <div style="border: 1px solid red;">
        <DomChildren orig />
     </div>
  }
}
```
This component takes an `orig:`[`OriginalNode`] that represents the, well, original [`Element`].

So, where do we get `orig` from?
- If we already have an `e:&`[`Element`], we can simply call `e.into()`.
- More likely, we don't have an [`Element`] yet. Moreover, we probably want to iterate over the entire body
  *once* to find all nodes we want to make reactive, and we also need to set up a global reactive system for all
  our inserted components.

  To do that, we call [`hydrate_body`] requires the `csr` feature flag with a function that takes the
  [`OriginalNode`] of the body and returns some leptos view; e.g.:

```
#[component]
fn MainBody(orig:OriginalNode) -> impl IntoView {
  // set up some signals, provide context etc.
  view!{
    <DomChildren orig/>
  }
}
#[wasm_bindgen(start)]
pub fn run() {
  console_error_panic_hook::set_once();
  hydrate_body(|orig| view!(<MainBody orig/>).into_any())
}
```

This sets up the reactive system, but does not yet replace any elements further down in the DOM. To do that,
we provide a function that takes an `&`[`Element`] and optionally returns an
[`FnOnce`]`() -> impl `[`IntoView`]`+'static`, if the element should be changed. This function is then passed to
[`DomChildrenCont`], which will iterate over all children of the replaced element and replace them with the
provided function.

Let's modify our `MainBody` to replace all elements with the attribute `data-replace-with-leptos` with a
`MyReplacementComponent`:

```
fn replace(e:&Element) -> Option<impl FnOnce() -> AnyView> {
  e.get_attribute("data-replace-with-leptos").map(|_| {
    let orig: OriginalNode = e.clone().into();
    || view!(<MyReplacementComponent orig/>).into_any()
  })
}

#[component]
fn MainBody(orig:OriginalNode) -> impl IntoView {
  // set up some signals, provide context etc.
  view!{
    <DomChildrenCont orig cont=replace/>
  }
}

#[component]
fn MyReplacementComponent(orig:OriginalNode) -> impl IntoView {
  view! {
    <div style="border: 1px solid red;">
      <DomChildrenCont orig cont=replace/>
    </div>
  }
}
```

...now, `replace` will get called on every element of the DOM, including those that were "moved around" in
earlier `MyReplacementComponent`s, respecting the proper reactive graph (regardin signal inheritance etc.).

### SSR Example

In general, for SSR we can simply use the normal leptos components to generate the entire DOM. We control the
server, hence we control the DOM anyway.

However, it might occasionally be the case that we want to dynamically *extend* the DOM at some point by
retrieving HTML from elsewhere, and then want to do a similar "hydration" iteration over the freshly inserted
nodes. This is what [`DomStringCont`] is for, and it does not require the `csr` feature:

```
#[component]
fn MyComponentThatGetsAStringFromSomewhere() -> impl IntoView {
  // get some HTML string from somewhere
  // e.g. some API call
  let html = "<div data-replace-with-leptos>...</div>".to_string();
  view! {
    <DomStringCont html cont=replace/>
  }
}
```

See the `examples/ssr` directory for a full example.