stash
This commit is contained in:
parent
c8441f0f00
commit
17732a6e6a
|
@ -18,6 +18,8 @@ leptos_server = { workspace = true }
|
|||
leptos_config = { workspace = true }
|
||||
leptos-spin-macro = { version = "0.2", optional = true }
|
||||
tracing = "0.1"
|
||||
reactive_graph = { workspace = true }
|
||||
tachys = { workspace = true, features = ["reactive_graph"] }
|
||||
typed-builder = "0.18"
|
||||
typed-builder-macro = "0.18"
|
||||
serde = { version = "1", optional = true }
|
||||
|
@ -66,6 +68,7 @@ nightly = [
|
|||
"leptos_macro/nightly",
|
||||
"leptos_reactive/nightly",
|
||||
"leptos_server/nightly",
|
||||
"tachys/nightly",
|
||||
]
|
||||
serde = ["leptos_reactive/serde"]
|
||||
serde-lite = ["leptos_reactive/serde-lite"]
|
||||
|
|
|
@ -1,20 +1,26 @@
|
|||
use leptos_dom::Fragment;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use tachys::{
|
||||
renderer::dom::Dom,
|
||||
view::{
|
||||
any_view::{AnyView, IntoAny},
|
||||
RenderHtml,
|
||||
},
|
||||
};
|
||||
|
||||
/// The most common type for the `children` property on components,
|
||||
/// which can only be called once.
|
||||
pub type Children = Box<dyn FnOnce() -> Fragment>;
|
||||
pub type Children = Box<dyn FnOnce() -> AnyView<Dom>>;
|
||||
|
||||
/// A type for the `children` property on components that can be called
|
||||
/// more than once.
|
||||
pub type ChildrenFn = Rc<dyn Fn() -> Fragment>;
|
||||
pub type ChildrenFn = Arc<dyn Fn() -> AnyView<Dom>>;
|
||||
|
||||
/// A type for the `children` property on components that can be called
|
||||
/// more than once, but may mutate the children.
|
||||
pub type ChildrenFnMut = Box<dyn FnMut() -> Fragment>;
|
||||
pub type ChildrenFnMut = Box<dyn FnMut() -> AnyView<Dom>>;
|
||||
|
||||
// This is to still support components that accept `Box<dyn Fn() -> Fragment>` as a children.
|
||||
type BoxedChildrenFn = Box<dyn Fn() -> Fragment>;
|
||||
// This is to still support components that accept `Box<dyn Fn() -> AnyView>` as a children.
|
||||
type BoxedChildrenFn = Box<dyn Fn() -> AnyView<Dom>>;
|
||||
|
||||
/// This trait can be used when constructing a component that takes children without needing
|
||||
/// to know exactly what children type the component expects. This is used internally by the
|
||||
|
@ -93,42 +99,74 @@ pub trait ToChildren<F> {
|
|||
fn to_children(f: F) -> Self;
|
||||
}
|
||||
|
||||
impl<F> ToChildren<F> for Children
|
||||
impl<F, C> ToChildren<F> for Children
|
||||
where
|
||||
F: FnOnce() -> Fragment + 'static,
|
||||
F: FnOnce() -> C + 'static,
|
||||
C: RenderHtml<Dom> + 'static,
|
||||
{
|
||||
#[inline]
|
||||
fn to_children(f: F) -> Self {
|
||||
Box::new(f)
|
||||
Box::new(move || f().into_any())
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> ToChildren<F> for ChildrenFn
|
||||
impl<F, C> ToChildren<F> for ChildrenFn
|
||||
where
|
||||
F: Fn() -> Fragment + 'static,
|
||||
F: Fn() -> C + 'static,
|
||||
C: RenderHtml<Dom> + 'static,
|
||||
{
|
||||
#[inline]
|
||||
fn to_children(f: F) -> Self {
|
||||
Rc::new(f)
|
||||
Arc::new(move || f().into_any())
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> ToChildren<F> for ChildrenFnMut
|
||||
impl<F, C> ToChildren<F> for ChildrenFnMut
|
||||
where
|
||||
F: FnMut() -> Fragment + 'static,
|
||||
F: Fn() -> C + 'static,
|
||||
C: RenderHtml<Dom> + 'static,
|
||||
{
|
||||
#[inline]
|
||||
fn to_children(f: F) -> Self {
|
||||
Box::new(f)
|
||||
Box::new(move || f().into_any())
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> ToChildren<F> for BoxedChildrenFn
|
||||
impl<F, C> ToChildren<F> for BoxedChildrenFn
|
||||
where
|
||||
F: Fn() -> Fragment + 'static,
|
||||
F: Fn() -> C + 'static,
|
||||
C: RenderHtml<Dom> + 'static,
|
||||
{
|
||||
#[inline]
|
||||
fn to_children(f: F) -> Self {
|
||||
Box::new(f)
|
||||
Box::new(move || f().into_any())
|
||||
}
|
||||
}
|
||||
|
||||
/// New-type wrapper for the a function that returns a view with `From` and `Default` traits implemented
|
||||
/// to enable optional props in for example `<Show>` and `<Suspense>`.
|
||||
#[derive(Clone)]
|
||||
pub struct ViewFn(Arc<dyn Fn() -> AnyView<Dom>>);
|
||||
|
||||
impl Default for ViewFn {
|
||||
fn default() -> Self {
|
||||
Self(Arc::new(|| ().into_any()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, C> From<F> for ViewFn
|
||||
where
|
||||
F: Fn() -> C + 'static,
|
||||
C: RenderHtml<Dom> + 'static,
|
||||
{
|
||||
fn from(value: F) -> Self {
|
||||
Self(Arc::new(move || value().into_any()))
|
||||
}
|
||||
}
|
||||
|
||||
impl ViewFn {
|
||||
/// Execute the wrapped function
|
||||
pub fn run(&self) -> AnyView<Dom> {
|
||||
(self.0)()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
//! Utility traits and functions that allow building components,
|
||||
//! as either functions of their props or functions with no arguments,
|
||||
//! without knowing the name of the props struct.
|
||||
|
||||
pub trait Component<P> {}
|
||||
|
||||
pub trait Props {
|
||||
type Builder;
|
||||
|
||||
fn builder() -> Self::Builder;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait PropsOrNoPropsBuilder {
|
||||
type Builder;
|
||||
|
||||
fn builder_or_not() -> Self::Builder;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct EmptyPropsBuilder {}
|
||||
|
||||
impl EmptyPropsBuilder {
|
||||
pub fn build(self) {}
|
||||
}
|
||||
|
||||
impl<P: Props> PropsOrNoPropsBuilder for P {
|
||||
type Builder = <P as Props>::Builder;
|
||||
|
||||
fn builder_or_not() -> Self::Builder {
|
||||
Self::builder()
|
||||
}
|
||||
}
|
||||
|
||||
impl PropsOrNoPropsBuilder for EmptyPropsBuilder {
|
||||
type Builder = EmptyPropsBuilder;
|
||||
|
||||
fn builder_or_not() -> Self::Builder {
|
||||
EmptyPropsBuilder {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, R> Component<EmptyPropsBuilder> for F where F: FnOnce() -> R {}
|
||||
|
||||
impl<P, F, R> Component<P> for F
|
||||
where
|
||||
F: FnOnce(P) -> R,
|
||||
P: Props,
|
||||
{
|
||||
}
|
||||
|
||||
pub fn component_props_builder<P: PropsOrNoPropsBuilder>(
|
||||
_f: &impl Component<P>,
|
||||
) -> <P as PropsOrNoPropsBuilder>::Builder {
|
||||
<P as PropsOrNoPropsBuilder>::builder_or_not()
|
||||
}
|
||||
|
||||
pub fn component_view<P, T>(f: impl ComponentConstructor<P, T>, props: P) -> T {
|
||||
f.construct(props)
|
||||
}
|
||||
pub trait ComponentConstructor<P, T> {
|
||||
fn construct(self, props: P) -> T;
|
||||
}
|
||||
|
||||
impl<Func, T> ComponentConstructor<(), T> for Func
|
||||
where
|
||||
Func: FnOnce() -> T,
|
||||
{
|
||||
fn construct(self, (): ()) -> T {
|
||||
(self)()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Func, T, P> ComponentConstructor<P, T> for Func
|
||||
where
|
||||
Func: FnOnce(P) -> T,
|
||||
P: PropsOrNoPropsBuilder,
|
||||
{
|
||||
fn construct(self, props: P) -> T {
|
||||
(self)(props)
|
||||
}
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
use leptos_dom::IntoView;
|
||||
use leptos_macro::component;
|
||||
use std::hash::Hash;
|
||||
use std::{hash::Hash, marker::PhantomData};
|
||||
use tachys::{
|
||||
renderer::Renderer,
|
||||
view::{keyed::keyed, RenderHtml},
|
||||
};
|
||||
|
||||
/// Iterates over children and displays them, keyed by the `key` function given.
|
||||
///
|
||||
|
@ -38,64 +41,60 @@ use std::hash::Hash;
|
|||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
tracing::instrument(level = "trace", skip_all)
|
||||
)]
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip_all))]
|
||||
#[component(transparent)]
|
||||
pub fn For<IF, I, T, EF, N, KF, K>(
|
||||
pub fn For<Rndr, IF, I, T, EF, N, KF, K>(
|
||||
/// Items over which the component should iterate.
|
||||
each: IF,
|
||||
/// A key function that will be applied to each item.
|
||||
key: KF,
|
||||
/// A function that takes the item, and returns the view that will be displayed for each item.
|
||||
///
|
||||
/// ## Syntax
|
||||
/// This can be passed directly in the `view` children of the `<For/>` by using the
|
||||
/// `let:` syntax to specify the name for the data variable passed in the argument.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use leptos::*;
|
||||
/// # if false {
|
||||
/// let (data, set_data) = create_signal(vec![0, 1, 2]);
|
||||
/// view! {
|
||||
/// <For
|
||||
/// each=move || data.get()
|
||||
/// key=|n| *n
|
||||
/// // stores the item in each row in a variable named `data`
|
||||
/// let:data
|
||||
/// >
|
||||
/// <p>{data}</p>
|
||||
/// </For>
|
||||
/// }
|
||||
/// # ;
|
||||
/// # }
|
||||
/// ```
|
||||
/// is the same as
|
||||
/// ```rust
|
||||
/// # use leptos::*;
|
||||
/// # if false {
|
||||
/// let (data, set_data) = create_signal(vec![0, 1, 2]);
|
||||
/// view! {
|
||||
/// <For
|
||||
/// each=move || data.get()
|
||||
/// key=|n| *n
|
||||
/// children=|data| view! { <p>{data}</p> }
|
||||
/// />
|
||||
/// }
|
||||
/// # ;
|
||||
/// # }
|
||||
/// ```
|
||||
children: EF,
|
||||
) -> impl IntoView
|
||||
#[prop(optional)] _rndr: PhantomData<Rndr>,
|
||||
) -> impl RenderHtml<Rndr>
|
||||
where
|
||||
IF: Fn() -> I + 'static,
|
||||
I: IntoIterator<Item = T>,
|
||||
EF: Fn(T) -> N + 'static,
|
||||
N: IntoView + 'static,
|
||||
KF: Fn(&T) -> K + 'static,
|
||||
EF: Fn(T) -> N + Clone + 'static,
|
||||
N: RenderHtml<Rndr> + 'static,
|
||||
KF: Fn(&T) -> K + Clone + 'static,
|
||||
K: Eq + Hash + 'static,
|
||||
T: 'static,
|
||||
Rndr: Renderer + 'static,
|
||||
Rndr::Node: Clone,
|
||||
Rndr::Element: Clone,
|
||||
{
|
||||
leptos_dom::Each::new(each, key, children).into_view()
|
||||
move || keyed(each(), key.clone(), children.clone())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::For;
|
||||
use leptos_macro::view;
|
||||
use reactive_graph::{signal::RwSignal, signal_traits::SignalGet};
|
||||
use tachys::{
|
||||
html::element::HtmlElement, prelude::ElementChild,
|
||||
renderer::mock_dom::MockDom, view::Render,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn creates_list() {
|
||||
let values = RwSignal::new(vec![1, 2, 3, 4, 5]);
|
||||
let list: HtmlElement<_, _, _, MockDom> = view! {
|
||||
<ol>
|
||||
<For
|
||||
each=move || values.get()
|
||||
key=|i| *i
|
||||
let:i
|
||||
>
|
||||
<li>{i}</li>
|
||||
</For>
|
||||
</ol>
|
||||
};
|
||||
let list = list.build();
|
||||
assert_eq!(
|
||||
list.el.to_debug_html(),
|
||||
"<ol><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ol>"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
(function (pkg_path, output_name, wasm_output_name) {
|
||||
import(`/${pkg_path}/${output_name}.js`)
|
||||
.then(mod => {
|
||||
mod.default(`/${pkg_path}/${wasm_output_name}.wasm`).then(() => {
|
||||
mod.hydrate();
|
||||
});
|
||||
})
|
||||
})
|
|
@ -0,0 +1,26 @@
|
|||
(function (pkg_path, output_name, wasm_output_name) {
|
||||
function idle(c) {
|
||||
if ("requestIdleCallback" in window) {
|
||||
window.requestIdleCallback(c);
|
||||
} else {
|
||||
c();
|
||||
}
|
||||
}
|
||||
idle(() => {
|
||||
import(`/${pkg_path}/${output_name}.js`)
|
||||
.then(mod => {
|
||||
mod.default(`/${pkg_path}/${wasm_output_name}.wasm`).then(() => {
|
||||
mod.hydrate();
|
||||
for (let e of document.querySelectorAll("leptos-island")) {
|
||||
const l = e.dataset.component;
|
||||
const islandFn = mod["_island_" + l];
|
||||
if (islandFn) {
|
||||
islandFn(e);
|
||||
} else {
|
||||
console.warn(`Could not find WASM function for the island ${l}.`);
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
})
|
|
@ -0,0 +1,58 @@
|
|||
#![allow(clippy::needless_lifetimes)]
|
||||
|
||||
use crate::prelude::*;
|
||||
use leptos_config::LeptosOptions;
|
||||
use leptos_macro::{component, view};
|
||||
use tachys::view::RenderHtml;
|
||||
|
||||
#[component]
|
||||
pub fn AutoReload<'a>(
|
||||
#[prop(optional)] disable_watch: bool,
|
||||
#[prop(optional)] nonce: Option<&'a str>,
|
||||
options: LeptosOptions,
|
||||
) -> impl RenderHtml<Dom> + 'a {
|
||||
(!disable_watch && std::env::var("LEPTOS_WATCH").is_ok()).then(|| {
|
||||
let reload_port = match options.reload_external_port {
|
||||
Some(val) => val,
|
||||
None => options.reload_port,
|
||||
};
|
||||
let protocol = match options.reload_ws_protocol {
|
||||
leptos_config::ReloadWSProtocol::WS => "'ws://'",
|
||||
leptos_config::ReloadWSProtocol::WSS => "'wss://'",
|
||||
};
|
||||
|
||||
let script = include_str!("reload_script.js");
|
||||
view! {
|
||||
<script crossorigin=nonce>
|
||||
{format!("{script}({reload_port:?}, {protocol})")}
|
||||
</script>
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn HydrationScripts(
|
||||
options: LeptosOptions,
|
||||
#[prop(optional)] islands: bool,
|
||||
) -> impl RenderHtml<Dom> {
|
||||
let pkg_path = &options.site_pkg_dir;
|
||||
let output_name = &options.output_name;
|
||||
let mut wasm_output_name = output_name.clone();
|
||||
if std::option_env!("LEPTOS_OUTPUT_NAME").is_none() {
|
||||
wasm_output_name.push_str("_bg");
|
||||
}
|
||||
let nonce = None::<String>; // use_nonce(); // TODO
|
||||
let script = if islands {
|
||||
include_str!("./island_script.js")
|
||||
} else {
|
||||
include_str!("./hydration_script.js")
|
||||
};
|
||||
|
||||
view! {
|
||||
<link rel="modulepreload" href=format!("/{pkg_path}/{output_name}.js") nonce=nonce.clone()/>
|
||||
<link rel="preload" href=format!("/{pkg_path}/{wasm_output_name}.wasm") r#as="fetch" r#type="application/wasm" crossorigin=nonce.clone().unwrap_or_default()/>
|
||||
<script type="module" nonce=nonce>
|
||||
{format!("{script}({pkg_path:?}, {output_name:?}, {wasm_output_name:?})")}
|
||||
</script>
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
(function (reload_port, protocol) {
|
||||
let host = window.location.hostname;
|
||||
let ws = new WebSocket(`${protocol}${host}:${reload_port}/live_reload`);
|
||||
ws.onmessage = (ev) => {
|
||||
let msg = JSON.parse(ev.data);
|
||||
if (msg.all) window.location.reload();
|
||||
if (msg.css) {
|
||||
let found = false;
|
||||
document.querySelectorAll("link").forEach((link) => {
|
||||
if (link.getAttribute('href').includes(msg.css)) {
|
||||
let newHref = '/' + msg.css + '?version=' + new Date().getMilliseconds();
|
||||
link.setAttribute('href', newHref);
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
if (!found) console.warn(`CSS hot-reload: Could not find a <link href=/\"${msg.css}\"> element`);
|
||||
};
|
||||
if(msg.view) {
|
||||
patch(msg.view);
|
||||
}
|
||||
};
|
||||
ws.onclose = () => console.warn('Live-reload stopped. Manual reload necessary.');
|
||||
})
|
|
@ -0,0 +1,120 @@
|
|||
use tachys::{
|
||||
hydration::Cursor,
|
||||
renderer::dom::Dom,
|
||||
ssr::StreamBuilder,
|
||||
view::{Mountable, Position, PositionState, Render, RenderHtml},
|
||||
};
|
||||
|
||||
pub struct View<T>(T);
|
||||
|
||||
pub trait IntoView: Sized {
|
||||
fn into_view(self) -> View<Self>;
|
||||
}
|
||||
|
||||
impl<T: Render<Dom> + RenderHtml<Dom>> IntoView for T {
|
||||
fn into_view(self) -> View<Self> {
|
||||
View(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Render<Dom>> Render<Dom> for View<T> {
|
||||
type State = T::State;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
self.0.build()
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
self.0.rebuild(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RenderHtml<Dom>> RenderHtml<Dom> for View<T> {
|
||||
const MIN_LENGTH: usize = <T as RenderHtml<Dom>>::MIN_LENGTH;
|
||||
|
||||
fn to_html_with_buf(self, buf: &mut String, position: &mut Position) {
|
||||
self.0.to_html_with_buf(buf, position);
|
||||
}
|
||||
|
||||
fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
|
||||
self,
|
||||
buf: &mut StreamBuilder,
|
||||
position: &mut Position,
|
||||
) where
|
||||
Self: Sized,
|
||||
{
|
||||
self.0.to_html_async_with_buf::<OUT_OF_ORDER>(buf, position)
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor<Dom>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
self.0.hydrate::<FROM_SERVER>(cursor, position)
|
||||
}
|
||||
}
|
||||
|
||||
/*pub trait IntoView {
|
||||
const MIN_HTML_LENGTH: usize;
|
||||
|
||||
type State: Mountable<Dom>;
|
||||
|
||||
fn build(self) -> Self::State;
|
||||
|
||||
fn rebuild(self, state: &mut Self::State);
|
||||
|
||||
fn to_html_with_buf(self, buf: &mut String, position: &mut Position);
|
||||
|
||||
fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
|
||||
self,
|
||||
buf: &mut StreamBuilder,
|
||||
position: &mut Position,
|
||||
);
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor<Dom>,
|
||||
position: &PositionState,
|
||||
) -> Self::State;
|
||||
}
|
||||
|
||||
impl<T: RenderHtml<Dom>> IntoView for T {}
|
||||
|
||||
impl<T: IntoView> Render<Dom> for T {
|
||||
type State = <Self as IntoView>::State;
|
||||
|
||||
fn build(self) -> Self::State {
|
||||
IntoView::build(self)
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
IntoView::rebuild(self, state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IntoView> RenderHtml<Dom> for T {
|
||||
const MIN_LENGTH: usize = T::MIN_HTML_LENGTH;
|
||||
|
||||
fn to_html_with_buf(self, buf: &mut String, position: &mut Position) {
|
||||
IntoView::to_html_with_buf(self, buf, position);
|
||||
}
|
||||
|
||||
fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
|
||||
self,
|
||||
buf: &mut StreamBuilder,
|
||||
position: &mut Position,
|
||||
) where
|
||||
Self: Sized,
|
||||
{
|
||||
IntoView::to_html_async_with_buf::<OUT_OF_ORDER>(self, buf, position);
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(
|
||||
self,
|
||||
cursor: &Cursor<Dom>,
|
||||
position: &PositionState,
|
||||
) -> Self::State {
|
||||
IntoView::hydrate::<FROM_SERVER>(self, cursor, position)
|
||||
}
|
||||
}*/
|
|
@ -1,4 +1,4 @@
|
|||
#![deny(missing_docs)]
|
||||
//#![deny(missing_docs)] // TODO restore
|
||||
#![forbid(unsafe_code)]
|
||||
//! # About Leptos
|
||||
//!
|
||||
|
@ -139,7 +139,34 @@
|
|||
//! # }
|
||||
//! ```
|
||||
|
||||
mod additional_attributes;
|
||||
extern crate self as leptos;
|
||||
|
||||
pub mod prelude {
|
||||
pub use reactive_graph::prelude::*;
|
||||
pub use tachys::prelude::*;
|
||||
}
|
||||
|
||||
pub mod children;
|
||||
pub mod component;
|
||||
mod for_loop;
|
||||
mod hydration_scripts;
|
||||
mod show;
|
||||
pub use for_loop::*;
|
||||
pub use hydration_scripts::*;
|
||||
pub use leptos_macro::*;
|
||||
pub use reactive_graph::{
|
||||
self,
|
||||
signal::{arc_signal, create_signal, signal},
|
||||
};
|
||||
pub use show::*;
|
||||
#[doc(hidden)]
|
||||
pub use typed_builder;
|
||||
#[doc(hidden)]
|
||||
pub use typed_builder_macro;
|
||||
mod into_view;
|
||||
pub use into_view::IntoView;
|
||||
|
||||
/*mod additional_attributes;
|
||||
pub use additional_attributes::*;
|
||||
mod await_;
|
||||
pub use await_::*;
|
||||
|
@ -209,10 +236,9 @@ pub use serde;
|
|||
#[cfg(feature = "experimental-islands")]
|
||||
pub use serde_json;
|
||||
pub use show::*;
|
||||
pub use suspense_component::*;
|
||||
mod suspense_component;
|
||||
mod transition;
|
||||
|
||||
//pub use suspense_component::*;
|
||||
//mod suspense_component;
|
||||
//mod transition;
|
||||
#[cfg(any(debug_assertions, feature = "ssr"))]
|
||||
#[doc(hidden)]
|
||||
pub use tracing;
|
||||
|
@ -373,4 +399,4 @@ where
|
|||
fn construct(self, props: P) -> View {
|
||||
(self)(props).into_view()
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
|
|
@ -1,35 +1,11 @@
|
|||
use leptos::{component, ChildrenFn, ViewFn};
|
||||
use leptos_dom::IntoView;
|
||||
use leptos_reactive::signal_prelude::*;
|
||||
use crate::children::{ChildrenFn, ViewFn};
|
||||
use leptos_macro::component;
|
||||
use reactive_graph::{computed::ArcMemo, traits::Get};
|
||||
use tachys::{
|
||||
renderer::dom::Dom,
|
||||
view::{either::Either, RenderHtml},
|
||||
};
|
||||
|
||||
/// A component that will show its children when the `when` condition is `true`,
|
||||
/// and show the fallback when it is `false`, without rerendering every time
|
||||
/// the condition changes.
|
||||
///
|
||||
/// The fallback prop is optional and defaults to rendering nothing.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use leptos_reactive::*;
|
||||
/// # use leptos_macro::*;
|
||||
/// # use leptos_dom::*; use leptos::*;
|
||||
/// # let runtime = create_runtime();
|
||||
/// let (value, set_value) = create_signal(0);
|
||||
///
|
||||
/// view! {
|
||||
/// <Show
|
||||
/// when=move || value.get() < 5
|
||||
/// fallback=|| view! { "Big number!" }
|
||||
/// >
|
||||
/// "Small number!"
|
||||
/// </Show>
|
||||
/// }
|
||||
/// # ;
|
||||
/// # runtime.dispose();
|
||||
/// ```
|
||||
#[cfg_attr(
|
||||
any(debug_assertions, feature = "ssr"),
|
||||
tracing::instrument(level = "trace", skip_all)
|
||||
)]
|
||||
#[component]
|
||||
pub fn Show<W>(
|
||||
/// The children will be shown whenever the condition in the `when` closure returns `true`.
|
||||
|
@ -39,14 +15,14 @@ pub fn Show<W>(
|
|||
/// A closure that returns what gets rendered if the when statement is false. By default this is the empty view.
|
||||
#[prop(optional, into)]
|
||||
fallback: ViewFn,
|
||||
) -> impl IntoView
|
||||
) -> impl RenderHtml<Dom>
|
||||
where
|
||||
W: Fn() -> bool + 'static,
|
||||
W: Fn() -> bool + Send + Sync + 'static,
|
||||
{
|
||||
let memoized_when = create_memo(move |_| when());
|
||||
let memoized_when = ArcMemo::new(move |_| when());
|
||||
|
||||
move || match memoized_when.get() {
|
||||
true => children().into_view(),
|
||||
false => fallback.run(),
|
||||
true => Either::Left(children()),
|
||||
false => Either::Right(fallback.run()),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue