feat: upload component

This commit is contained in:
luoxiao 2023-10-24 17:39:09 +08:00
parent 0f0fde49a3
commit 5c1943bdd4
5 changed files with 108 additions and 6 deletions

View file

@ -14,7 +14,7 @@ license = "MIT"
[dependencies] [dependencies]
leptos = { version = "0.5.1", features = ["csr"] } leptos = { version = "0.5.1", features = ["csr"] }
web-sys = { version = "0.3.62", features = ["DomRect"] } web-sys = { version = "0.3.62", features = ["DomRect", "File", "FileList"] }
wasm-bindgen = "0.2.85" wasm-bindgen = "0.2.85"
icondata = { version = "0.1.0", features = [ icondata = { version = "0.1.0", features = [
"AiCloseOutlined", "AiCloseOutlined",

View file

@ -40,7 +40,6 @@ pub fn Skeleton(
css_vars css_vars
}); });
(0..repeat.get()) (0..repeat.get())
.into_iter()
.map(|_| { .map(|_| {
view! { <div class="melt-skeleton" style=move || css_vars.get()></div> } view! { <div class="melt-skeleton" style=move || css_vars.get()></div> }
}) })

View file

@ -1,20 +1,32 @@
use crate::{mount_style, utils::AsyncCallback};
use leptos::*; use leptos::*;
use crate::mount_style; use web_sys::FileList;
// TODO
#[component] #[component]
pub fn Upload( pub fn Upload(
#[prop(optional, into)] accept: MaybeSignal<String>, #[prop(optional, into)] accept: MaybeSignal<String>,
#[prop(optional, into)] multiple: MaybeSignal<bool>, #[prop(optional, into)] multiple: MaybeSignal<bool>,
#[prop(optional, into)] on_before_upload: Option<AsyncCallback<FileList, bool>>,
children: Children, children: Children,
) -> impl IntoView { ) -> impl IntoView {
mount_style("upload", include_str!("./upload.css")); mount_style("upload", include_str!("./upload.css"));
let on_file_addition = move || {
let on_file_addition = move |files: FileList| {
spawn_local(async move {
if let Some(on_before_upload) = on_before_upload {
let is_allow = on_before_upload.call(files).await;
if is_allow {
//TODO submit
}
}
});
}; };
let input_ref = create_node_ref::<html::Input>(); let input_ref = create_node_ref::<html::Input>();
let on_change = move |_| { let on_change = move |_| {
if let Some(input_ref) = input_ref.get_untracked() { if let Some(input_ref) = input_ref.get_untracked() {
if let Some(files) = input_ref.files() {
on_file_addition(files);
}
} }
}; };
let on_click = move |_| { let on_click = move |_| {

89
src/utils/callback.rs Normal file
View file

@ -0,0 +1,89 @@
use leptos::StoredValue;
use std::{fmt, future::Future, pin::Pin, rc::Rc};
pub struct AsyncCallback<In: 'static, Out: 'static = ()>(
#[allow(clippy::complexity)] StoredValue<Rc<dyn Fn(In) -> Pin<Box<dyn Future<Output = Out>>>>>,
);
impl<In> fmt::Debug for AsyncCallback<In> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
fmt.write_str("AsyncCallback")
}
}
impl<In, Out> Clone for AsyncCallback<In, Out> {
fn clone(&self) -> Self {
*self
}
}
impl<In, Out> Copy for AsyncCallback<In, Out> {}
impl<In, Out> AsyncCallback<In, Out> {
pub fn new<F, Fu>(f: F) -> Self
where
F: Fn(In) -> Fu + 'static,
Fu: Future<Output = Out> + 'static,
{
let f = Rc::new(move |input: In| {
let fut = f(input);
Box::pin(fut) as Pin<Box<dyn Future<Output = Out>>>
});
Self(StoredValue::new(f))
}
pub async fn call(&self, input: In) -> Out {
let f = self.0.get_value();
f(input).await
}
}
impl<F, In, Fu, Out> From<F> for AsyncCallback<In, Out>
where
F: Fn(In) -> Fu + 'static,
Fu: Future<Output = Out> + 'static,
{
fn from(f: F) -> AsyncCallback<In, Out> {
AsyncCallback::new(f)
}
}
#[cfg(test)]
mod tests {
use crate::utils::AsyncCallback;
use leptos::create_runtime;
struct NoClone {}
#[test]
fn clone_async_callback() {
let rt = create_runtime();
let callback = AsyncCallback::new(move |_no_clone: NoClone| async { NoClone {} });
let _cloned = callback.clone();
rt.dispose();
}
#[test]
fn async_callback_from() {
let rt = create_runtime();
let _callback: AsyncCallback<(), String> = (|()| async { "test".to_string() }).into();
rt.dispose();
}
#[test]
fn async_callback_from_html() {
let rt = create_runtime();
use leptos::{
html::{HtmlElement, H1},
*,
};
let _callback: AsyncCallback<String, HtmlElement<H1>> = (|x: String| async move {
view! {
<h1>{x}</h1>
}
})
.into();
rt.dispose();
}
}

View file

@ -1,6 +1,8 @@
mod callback;
pub mod maybe_rw_signal; pub mod maybe_rw_signal;
mod maybe_signal_store; mod maybe_signal_store;
pub mod mount_style; pub mod mount_style;
pub mod signal; pub mod signal;
pub use callback::AsyncCallback;
pub use maybe_signal_store::*; pub use maybe_signal_store::*;