use crate::{js, js_fut, use_event_listener, use_supported, UseTimeoutFnReturn}; use default_struct_builder::DefaultBuilder; use leptos::ev::{copy, cut}; use leptos::*; /// Reactive [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API). /// Provides the ability to respond to clipboard commands (cut, copy, and paste) /// as well as to asynchronously read from and write to the system clipboard. /// Access to the contents of the clipboard is gated behind the /// [Permissions API](https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API). /// Without user permission, reading or altering the clipboard contents is not permitted. /// /// ## Demo /// /// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_clipboard) /// /// ## Usage /// /// ``` /// # use leptos::*; /// # use leptos_use::{use_clipboard, UseClipboardReturn}; /// # /// # #[component] /// # fn Demo() -> impl IntoView { /// let UseClipboardReturn { is_supported, text, copied, copy } = use_clipboard(); /// /// view! { /// Your browser does not support Clipboard API

} /// > /// ///
/// } /// # } /// ``` /// /// ## Server-Side Rendering /// /// On the server the returnd `text` signal will always be `None` and `copy` is a no-op. pub fn use_clipboard() -> UseClipboardReturn { use_clipboard_with_options(UseClipboardOptions::default()) } /// Version of [`use_clipboard`] that takes a `UseClipboardOptions`. See [`use_clipboard`] for how to use. pub fn use_clipboard_with_options( options: UseClipboardOptions, ) -> UseClipboardReturn { let UseClipboardOptions { copied_reset_delay, read, } = options; let is_supported = use_supported(|| { js!("clipboard" in &window() .navigator()) }); let (text, set_text) = create_signal(None); let (copied, set_copied) = create_signal(false); let UseTimeoutFnReturn { start, .. } = crate::use_timeout_fn::use_timeout_fn( move |_: ()| { set_copied.set(false); }, copied_reset_delay, ); let update_text = move |_| { if is_supported.get() { spawn_local(async move { let clipboard = window().navigator().clipboard(); if let Ok(text) = js_fut!(clipboard.read_text()).await { set_text.set(text.as_string()); } }) } }; if is_supported.get() && read { let _ = use_event_listener(window(), copy, update_text); let _ = use_event_listener(window(), cut, update_text); } let do_copy = { let start = start.clone(); move |value: &str| { if is_supported.get() { let start = start.clone(); let value = value.to_owned(); spawn_local(async move { let clipboard = window().navigator().clipboard(); if js_fut!(clipboard.write_text(&value)).await.is_ok() { set_text.set(Some(value)); set_copied.set(true); start(()); } }); } } }; UseClipboardReturn { is_supported, text: text.into(), copied: copied.into(), copy: do_copy, } } /// Options for [`use_clipboard_with_options`]. #[derive(DefaultBuilder)] pub struct UseClipboardOptions { /// When `true` event handlers are added so that the returned signal `text` is updated whenever the clipboard changes. /// Defaults to `false`. /// /// > Please note that clipboard changes are only detected when copying or cutting text inside the same document. read: bool, /// After how many milliseconds after copying should the returned signal `copied` be set to `false`? /// Defaults to 1500. copied_reset_delay: f64, } impl Default for UseClipboardOptions { fn default() -> Self { Self { read: false, copied_reset_delay: 1500.0, } } } /// Return type of [`use_clipboard`]. pub struct UseClipboardReturn where CopyFn: Fn(&str) + Clone, { /// Whether the Clipboard API is supported. pub is_supported: Signal, /// The current state of the clipboard. pub text: Signal>, /// `true` for [`UseClipboardOptions::copied_reset_delay`] milliseconds after copying. pub copied: Signal, /// Copy the given text to the clipboard. pub copy: CopyFn, }