leptos_use/use_clipboard.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
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! {
/// <Show
/// when=move || is_supported.get()
/// fallback=move || view! { <p>Your browser does not support Clipboard API</p> }
/// >
/// <button on:click={
/// let copy = copy.clone();
/// move |_| copy("Hello!")
/// }>
/// <Show when=move || copied.get() fallback=move || "Copy">
/// "Copied!"
/// </Show>
/// </button>
/// </Show>
/// }
/// # }
/// ```
///
/// ## 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<impl Fn(&str) + Clone> {
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<impl Fn(&str) + Clone> {
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<CopyFn>
where
CopyFn: Fn(&str) + Clone,
{
/// Whether the Clipboard API is supported.
pub is_supported: Signal<bool>,
/// The current state of the clipboard.
pub text: Signal<Option<String>>,
/// `true` for [`UseClipboardOptions::copied_reset_delay`] milliseconds after copying.
pub copied: Signal<bool>,
/// Copy the given text to the clipboard.
pub copy: CopyFn,
}