mirror of
https://github.com/adoyle0/leptos-use.git
synced 2025-01-23 09:09:21 -05:00
added use_storage and use_round
This commit is contained in:
parent
0a5eb5e895
commit
a14842391a
27 changed files with 1363 additions and 498 deletions
2
.idea/leptos-use.iml
generated
2
.idea/leptos-use.iml
generated
|
@ -20,6 +20,7 @@
|
||||||
<sourceFolder url="file://$MODULE_DIR$/examples/watch_debounced/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/examples/watch_debounced/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/examples/watch_pausable/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/examples/watch_pausable/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/examples/watch_throttled/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/examples/watch_throttled/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/examples/use_storage/src" isTestSource="false" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/examples/use_event_listener/target" />
|
<excludeFolder url="file://$MODULE_DIR$/examples/use_event_listener/target" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/docs/book/book" />
|
<excludeFolder url="file://$MODULE_DIR$/docs/book/book" />
|
||||||
|
@ -36,6 +37,7 @@
|
||||||
<excludeFolder url="file://$MODULE_DIR$/examples/watch_debounced/target" />
|
<excludeFolder url="file://$MODULE_DIR$/examples/watch_debounced/target" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/examples/watch_pausable/target" />
|
<excludeFolder url="file://$MODULE_DIR$/examples/watch_pausable/target" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/examples/watch_throttled/target" />
|
<excludeFolder url="file://$MODULE_DIR$/examples/watch_throttled/target" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/examples/use_storage/target" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
|
|
@ -42,3 +42,4 @@
|
||||||
- [use_floor](math/use_floor.md)
|
- [use_floor](math/use_floor.md)
|
||||||
- [use_max](math/use_max.md)
|
- [use_max](math/use_max.md)
|
||||||
- [use_min](math/use_min.md)
|
- [use_min](math/use_min.md)
|
||||||
|
- [use_round](math/use_round.md)
|
3
docs/book/src/math/use_round.md
Normal file
3
docs/book/src/math/use_round.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# use_round
|
||||||
|
|
||||||
|
<!-- cmdrun python3 ../extract_doc_comment.py math/use_round math -->
|
|
@ -1 +1,3 @@
|
||||||
# use_storage
|
# use_storage
|
||||||
|
|
||||||
|
<!-- cmdrun python3 ../extract_doc_comment.py storage/use_storage storage -->
|
||||||
|
|
16
examples/use_round/Cargo.toml
Normal file
16
examples/use_round/Cargo.toml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
[package]
|
||||||
|
name = "use_round"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
leptos = "0.3"
|
||||||
|
console_error_panic_hook = "0.1"
|
||||||
|
console_log = "1"
|
||||||
|
log = "0.4"
|
||||||
|
leptos-use = { path = "../..", features = ["docs", "math"] }
|
||||||
|
web-sys = "0.3"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
wasm-bindgen = "0.2"
|
||||||
|
wasm-bindgen-test = "0.3.0"
|
23
examples/use_round/README.md
Normal file
23
examples/use_round/README.md
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
A simple example for `use_round`.
|
||||||
|
|
||||||
|
If you don't have it installed already, install [Trunk](https://trunkrs.dev/) and [Tailwind](https://tailwindcss.com/docs/installation)
|
||||||
|
as well as the nightly toolchain for Rust and the wasm32-unknown-unknown target:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo install trunk
|
||||||
|
npm install -D tailwindcss @tailwindcss/forms
|
||||||
|
rustup toolchain install nightly
|
||||||
|
rustup target add wasm32-unknown-unknown
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, open two terminals. In the first one, run:
|
||||||
|
|
||||||
|
```
|
||||||
|
npx tailwindcss -i ./input.css -o ./style/output.css --watch
|
||||||
|
```
|
||||||
|
|
||||||
|
In the second one, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
trunk serve --open
|
||||||
|
```
|
2
examples/use_round/Trunk.toml
Normal file
2
examples/use_round/Trunk.toml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[build]
|
||||||
|
public_url = "/demo/"
|
7
examples/use_round/index.html
Normal file
7
examples/use_round/index.html
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link data-trunk rel="css" href="style/output.css">
|
||||||
|
</head>
|
||||||
|
<body></body>
|
||||||
|
</html>
|
3
examples/use_round/input.css
Normal file
3
examples/use_round/input.css
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
2
examples/use_round/rust-toolchain.toml
Normal file
2
examples/use_round/rust-toolchain.toml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[toolchain]
|
||||||
|
channel = "nightly"
|
33
examples/use_round/src/main.rs
Normal file
33
examples/use_round/src/main.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
use leptos::*;
|
||||||
|
use leptos_use::docs::demo_or_body;
|
||||||
|
use leptos_use::math::use_round;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn Demo(cx: Scope) -> impl IntoView {
|
||||||
|
let (value, set_value) = create_signal(cx, 5.95);
|
||||||
|
|
||||||
|
let result: Signal<f64> = use_round(cx, value);
|
||||||
|
|
||||||
|
view! { cx,
|
||||||
|
<input
|
||||||
|
class="block"
|
||||||
|
prop:value=value
|
||||||
|
on:input=move |e| set_value(event_target_value(&e).parse().unwrap())
|
||||||
|
type="range"
|
||||||
|
min="0"
|
||||||
|
max="10"
|
||||||
|
step="0.01"
|
||||||
|
/>
|
||||||
|
<p>"Value: " {value}</p>
|
||||||
|
<p>"Rounded: " {result}</p>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
_ = console_log::init_with_level(log::Level::Debug);
|
||||||
|
console_error_panic_hook::set_once();
|
||||||
|
|
||||||
|
mount_to(demo_or_body(), |cx| {
|
||||||
|
view! { cx, <Demo /> }
|
||||||
|
})
|
||||||
|
}
|
289
examples/use_round/style/output.css
Normal file
289
examples/use_round/style/output.css
Normal file
|
@ -0,0 +1,289 @@
|
||||||
|
[type='text'],[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='search'],[type='tel'],[type='time'],[type='week'],[multiple],textarea,select {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
background-color: #fff;
|
||||||
|
border-color: #6b7280;
|
||||||
|
border-width: 1px;
|
||||||
|
border-radius: 0px;
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
padding-right: 0.75rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
padding-left: 0.75rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.5rem;
|
||||||
|
--tw-shadow: 0 0 #0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='text']:focus, [type='email']:focus, [type='url']:focus, [type='password']:focus, [type='number']:focus, [type='date']:focus, [type='datetime-local']:focus, [type='month']:focus, [type='search']:focus, [type='tel']:focus, [type='time']:focus, [type='week']:focus, [multiple]:focus, textarea:focus, select:focus {
|
||||||
|
outline: 2px solid transparent;
|
||||||
|
outline-offset: 2px;
|
||||||
|
--tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);
|
||||||
|
--tw-ring-offset-width: 0px;
|
||||||
|
--tw-ring-offset-color: #fff;
|
||||||
|
--tw-ring-color: #2563eb;
|
||||||
|
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
||||||
|
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
||||||
|
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||||
|
border-color: #2563eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
input::-moz-placeholder, textarea::-moz-placeholder {
|
||||||
|
color: #6b7280;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
input::placeholder,textarea::placeholder {
|
||||||
|
color: #6b7280;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-datetime-edit-fields-wrapper {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-date-and-time-value {
|
||||||
|
min-height: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field {
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
|
||||||
|
background-position: right 0.5rem center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 1.5em 1.5em;
|
||||||
|
padding-right: 2.5rem;
|
||||||
|
-webkit-print-color-adjust: exact;
|
||||||
|
print-color-adjust: exact;
|
||||||
|
}
|
||||||
|
|
||||||
|
[multiple] {
|
||||||
|
background-image: initial;
|
||||||
|
background-position: initial;
|
||||||
|
background-repeat: unset;
|
||||||
|
background-size: initial;
|
||||||
|
padding-right: 0.75rem;
|
||||||
|
-webkit-print-color-adjust: unset;
|
||||||
|
print-color-adjust: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='checkbox'],[type='radio'] {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
padding: 0;
|
||||||
|
-webkit-print-color-adjust: exact;
|
||||||
|
print-color-adjust: exact;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
background-origin: border-box;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
flex-shrink: 0;
|
||||||
|
height: 1rem;
|
||||||
|
width: 1rem;
|
||||||
|
color: #2563eb;
|
||||||
|
background-color: #fff;
|
||||||
|
border-color: #6b7280;
|
||||||
|
border-width: 1px;
|
||||||
|
--tw-shadow: 0 0 #0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='checkbox'] {
|
||||||
|
border-radius: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='radio'] {
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='checkbox']:focus,[type='radio']:focus {
|
||||||
|
outline: 2px solid transparent;
|
||||||
|
outline-offset: 2px;
|
||||||
|
--tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);
|
||||||
|
--tw-ring-offset-width: 2px;
|
||||||
|
--tw-ring-offset-color: #fff;
|
||||||
|
--tw-ring-color: #2563eb;
|
||||||
|
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
||||||
|
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
||||||
|
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='checkbox']:checked,[type='radio']:checked {
|
||||||
|
border-color: transparent;
|
||||||
|
background-color: currentColor;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='checkbox']:checked {
|
||||||
|
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='radio']:checked {
|
||||||
|
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e");
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='checkbox']:checked:hover,[type='checkbox']:checked:focus,[type='radio']:checked:hover,[type='radio']:checked:focus {
|
||||||
|
border-color: transparent;
|
||||||
|
background-color: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='checkbox']:indeterminate {
|
||||||
|
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");
|
||||||
|
border-color: transparent;
|
||||||
|
background-color: currentColor;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='checkbox']:indeterminate:hover,[type='checkbox']:indeterminate:focus {
|
||||||
|
border-color: transparent;
|
||||||
|
background-color: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='file'] {
|
||||||
|
background: unset;
|
||||||
|
border-color: inherit;
|
||||||
|
border-width: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-size: unset;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='file']:focus {
|
||||||
|
outline: 1px solid ButtonText;
|
||||||
|
outline: 1px auto -webkit-focus-ring-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
*, ::before, ::after {
|
||||||
|
--tw-border-spacing-x: 0;
|
||||||
|
--tw-border-spacing-y: 0;
|
||||||
|
--tw-translate-x: 0;
|
||||||
|
--tw-translate-y: 0;
|
||||||
|
--tw-rotate: 0;
|
||||||
|
--tw-skew-x: 0;
|
||||||
|
--tw-skew-y: 0;
|
||||||
|
--tw-scale-x: 1;
|
||||||
|
--tw-scale-y: 1;
|
||||||
|
--tw-pan-x: ;
|
||||||
|
--tw-pan-y: ;
|
||||||
|
--tw-pinch-zoom: ;
|
||||||
|
--tw-scroll-snap-strictness: proximity;
|
||||||
|
--tw-gradient-from-position: ;
|
||||||
|
--tw-gradient-via-position: ;
|
||||||
|
--tw-gradient-to-position: ;
|
||||||
|
--tw-ordinal: ;
|
||||||
|
--tw-slashed-zero: ;
|
||||||
|
--tw-numeric-figure: ;
|
||||||
|
--tw-numeric-spacing: ;
|
||||||
|
--tw-numeric-fraction: ;
|
||||||
|
--tw-ring-inset: ;
|
||||||
|
--tw-ring-offset-width: 0px;
|
||||||
|
--tw-ring-offset-color: #fff;
|
||||||
|
--tw-ring-color: rgb(59 130 246 / 0.5);
|
||||||
|
--tw-ring-offset-shadow: 0 0 #0000;
|
||||||
|
--tw-ring-shadow: 0 0 #0000;
|
||||||
|
--tw-shadow: 0 0 #0000;
|
||||||
|
--tw-shadow-colored: 0 0 #0000;
|
||||||
|
--tw-blur: ;
|
||||||
|
--tw-brightness: ;
|
||||||
|
--tw-contrast: ;
|
||||||
|
--tw-grayscale: ;
|
||||||
|
--tw-hue-rotate: ;
|
||||||
|
--tw-invert: ;
|
||||||
|
--tw-saturate: ;
|
||||||
|
--tw-sepia: ;
|
||||||
|
--tw-drop-shadow: ;
|
||||||
|
--tw-backdrop-blur: ;
|
||||||
|
--tw-backdrop-brightness: ;
|
||||||
|
--tw-backdrop-contrast: ;
|
||||||
|
--tw-backdrop-grayscale: ;
|
||||||
|
--tw-backdrop-hue-rotate: ;
|
||||||
|
--tw-backdrop-invert: ;
|
||||||
|
--tw-backdrop-opacity: ;
|
||||||
|
--tw-backdrop-saturate: ;
|
||||||
|
--tw-backdrop-sepia: ;
|
||||||
|
}
|
||||||
|
|
||||||
|
::backdrop {
|
||||||
|
--tw-border-spacing-x: 0;
|
||||||
|
--tw-border-spacing-y: 0;
|
||||||
|
--tw-translate-x: 0;
|
||||||
|
--tw-translate-y: 0;
|
||||||
|
--tw-rotate: 0;
|
||||||
|
--tw-skew-x: 0;
|
||||||
|
--tw-skew-y: 0;
|
||||||
|
--tw-scale-x: 1;
|
||||||
|
--tw-scale-y: 1;
|
||||||
|
--tw-pan-x: ;
|
||||||
|
--tw-pan-y: ;
|
||||||
|
--tw-pinch-zoom: ;
|
||||||
|
--tw-scroll-snap-strictness: proximity;
|
||||||
|
--tw-gradient-from-position: ;
|
||||||
|
--tw-gradient-via-position: ;
|
||||||
|
--tw-gradient-to-position: ;
|
||||||
|
--tw-ordinal: ;
|
||||||
|
--tw-slashed-zero: ;
|
||||||
|
--tw-numeric-figure: ;
|
||||||
|
--tw-numeric-spacing: ;
|
||||||
|
--tw-numeric-fraction: ;
|
||||||
|
--tw-ring-inset: ;
|
||||||
|
--tw-ring-offset-width: 0px;
|
||||||
|
--tw-ring-offset-color: #fff;
|
||||||
|
--tw-ring-color: rgb(59 130 246 / 0.5);
|
||||||
|
--tw-ring-offset-shadow: 0 0 #0000;
|
||||||
|
--tw-ring-shadow: 0 0 #0000;
|
||||||
|
--tw-shadow: 0 0 #0000;
|
||||||
|
--tw-shadow-colored: 0 0 #0000;
|
||||||
|
--tw-blur: ;
|
||||||
|
--tw-brightness: ;
|
||||||
|
--tw-contrast: ;
|
||||||
|
--tw-grayscale: ;
|
||||||
|
--tw-hue-rotate: ;
|
||||||
|
--tw-invert: ;
|
||||||
|
--tw-saturate: ;
|
||||||
|
--tw-sepia: ;
|
||||||
|
--tw-drop-shadow: ;
|
||||||
|
--tw-backdrop-blur: ;
|
||||||
|
--tw-backdrop-brightness: ;
|
||||||
|
--tw-backdrop-contrast: ;
|
||||||
|
--tw-backdrop-grayscale: ;
|
||||||
|
--tw-backdrop-hue-rotate: ;
|
||||||
|
--tw-backdrop-invert: ;
|
||||||
|
--tw-backdrop-opacity: ;
|
||||||
|
--tw-backdrop-saturate: ;
|
||||||
|
--tw-backdrop-sepia: ;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-\[--brand-color\] {
|
||||||
|
color: var(--brand-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-green-600 {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(22 163 74 / var(--tw-text-opacity));
|
||||||
|
}
|
||||||
|
|
||||||
|
.opacity-75 {
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.dark\:text-green-500 {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(34 197 94 / var(--tw-text-opacity));
|
||||||
|
}
|
||||||
|
}
|
15
examples/use_round/tailwind.config.js
Normal file
15
examples/use_round/tailwind.config.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
content: {
|
||||||
|
files: ["*.html", "./src/**/*.rs", "../../src/docs/**/*.rs"],
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
corePlugins: {
|
||||||
|
preflight: false,
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
require('@tailwindcss/forms'),
|
||||||
|
],
|
||||||
|
}
|
17
examples/use_storage/Cargo.toml
Normal file
17
examples/use_storage/Cargo.toml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
[package]
|
||||||
|
name = "use_storage"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
leptos = "0.3"
|
||||||
|
console_error_panic_hook = "0.1"
|
||||||
|
console_log = "1"
|
||||||
|
log = "0.4"
|
||||||
|
leptos-use = { path = "../..", features = ["docs", "storage"] }
|
||||||
|
web-sys = "0.3"
|
||||||
|
serde = "1.0.163"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
wasm-bindgen = "0.2"
|
||||||
|
wasm-bindgen-test = "0.3.0"
|
23
examples/use_storage/README.md
Normal file
23
examples/use_storage/README.md
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
A simple example for `use_storage`.
|
||||||
|
|
||||||
|
If you don't have it installed already, install [Trunk](https://trunkrs.dev/) and [Tailwind](https://tailwindcss.com/docs/installation)
|
||||||
|
as well as the nightly toolchain for Rust and the wasm32-unknown-unknown target:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo install trunk
|
||||||
|
npm install -D tailwindcss @tailwindcss/forms
|
||||||
|
rustup toolchain install nightly
|
||||||
|
rustup target add wasm32-unknown-unknown
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, open two terminals. In the first one, run:
|
||||||
|
|
||||||
|
```
|
||||||
|
npx tailwindcss -i ./input.css -o ./style/output.css --watch
|
||||||
|
```
|
||||||
|
|
||||||
|
In the second one, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
trunk serve --open
|
||||||
|
```
|
2
examples/use_storage/Trunk.toml
Normal file
2
examples/use_storage/Trunk.toml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[build]
|
||||||
|
public_url = "/demo/"
|
7
examples/use_storage/index.html
Normal file
7
examples/use_storage/index.html
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link data-trunk rel="css" href="style/output.css">
|
||||||
|
</head>
|
||||||
|
<body></body>
|
||||||
|
</html>
|
3
examples/use_storage/input.css
Normal file
3
examples/use_storage/input.css
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
2
examples/use_storage/rust-toolchain.toml
Normal file
2
examples/use_storage/rust-toolchain.toml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[toolchain]
|
||||||
|
channel = "nightly"
|
74
examples/use_storage/src/main.rs
Normal file
74
examples/use_storage/src/main.rs
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
use leptos::*;
|
||||||
|
use leptos_use::docs::{demo_or_body, Note};
|
||||||
|
use leptos_use::storage::use_storage;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct BananaState {
|
||||||
|
pub name: String,
|
||||||
|
pub color: String,
|
||||||
|
pub size: String,
|
||||||
|
pub count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn Demo(cx: Scope) -> impl IntoView {
|
||||||
|
let the_default = BananaState {
|
||||||
|
name: "Banana".to_string(),
|
||||||
|
color: "Yellow".to_string(),
|
||||||
|
size: "Medium".to_string(),
|
||||||
|
count: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let (state, set_state, _) = use_storage(cx, "banana-state", the_default.clone());
|
||||||
|
|
||||||
|
let (state2, ..) = use_storage(cx, "banana-state", the_default.clone());
|
||||||
|
|
||||||
|
view! { cx,
|
||||||
|
<input
|
||||||
|
class="block"
|
||||||
|
prop:value=move || state().name
|
||||||
|
on:input=move |e| set_state.update(|s| s.name = event_target_value(&e))
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
class="block"
|
||||||
|
prop:value=move || state().color
|
||||||
|
on:input=move |e| set_state.update(|s| s.color = event_target_value(&e))
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
class="block"
|
||||||
|
prop:value=move || state().size
|
||||||
|
on:input=move |e| set_state.update(|s| s.size = event_target_value(&e))
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
class="block"
|
||||||
|
prop:value=move || state().count
|
||||||
|
value=move || state().count
|
||||||
|
on:input=move |e| set_state.update(|s| s.count = event_target_value(&e).parse::<f64>().unwrap() as u32)
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="1"
|
||||||
|
max="1000"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<p>"Second "<b><code>"use_storage"</code></b>":"</p>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
{ move || format!("{:#?}", state2()) }
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<Note>"The values are persistant. When you reload the page the values will be the same."</Note>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
_ = console_log::init_with_level(log::Level::Debug);
|
||||||
|
console_error_panic_hook::set_once();
|
||||||
|
|
||||||
|
mount_to(demo_or_body(), |cx| {
|
||||||
|
view! { cx, <Demo /> }
|
||||||
|
})
|
||||||
|
}
|
289
examples/use_storage/style/output.css
Normal file
289
examples/use_storage/style/output.css
Normal file
|
@ -0,0 +1,289 @@
|
||||||
|
[type='text'],[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='search'],[type='tel'],[type='time'],[type='week'],[multiple],textarea,select {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
background-color: #fff;
|
||||||
|
border-color: #6b7280;
|
||||||
|
border-width: 1px;
|
||||||
|
border-radius: 0px;
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
padding-right: 0.75rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
padding-left: 0.75rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.5rem;
|
||||||
|
--tw-shadow: 0 0 #0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='text']:focus, [type='email']:focus, [type='url']:focus, [type='password']:focus, [type='number']:focus, [type='date']:focus, [type='datetime-local']:focus, [type='month']:focus, [type='search']:focus, [type='tel']:focus, [type='time']:focus, [type='week']:focus, [multiple]:focus, textarea:focus, select:focus {
|
||||||
|
outline: 2px solid transparent;
|
||||||
|
outline-offset: 2px;
|
||||||
|
--tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);
|
||||||
|
--tw-ring-offset-width: 0px;
|
||||||
|
--tw-ring-offset-color: #fff;
|
||||||
|
--tw-ring-color: #2563eb;
|
||||||
|
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
||||||
|
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
||||||
|
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||||
|
border-color: #2563eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
input::-moz-placeholder, textarea::-moz-placeholder {
|
||||||
|
color: #6b7280;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
input::placeholder,textarea::placeholder {
|
||||||
|
color: #6b7280;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-datetime-edit-fields-wrapper {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-date-and-time-value {
|
||||||
|
min-height: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field {
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
|
||||||
|
background-position: right 0.5rem center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 1.5em 1.5em;
|
||||||
|
padding-right: 2.5rem;
|
||||||
|
-webkit-print-color-adjust: exact;
|
||||||
|
print-color-adjust: exact;
|
||||||
|
}
|
||||||
|
|
||||||
|
[multiple] {
|
||||||
|
background-image: initial;
|
||||||
|
background-position: initial;
|
||||||
|
background-repeat: unset;
|
||||||
|
background-size: initial;
|
||||||
|
padding-right: 0.75rem;
|
||||||
|
-webkit-print-color-adjust: unset;
|
||||||
|
print-color-adjust: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='checkbox'],[type='radio'] {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
padding: 0;
|
||||||
|
-webkit-print-color-adjust: exact;
|
||||||
|
print-color-adjust: exact;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
background-origin: border-box;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
flex-shrink: 0;
|
||||||
|
height: 1rem;
|
||||||
|
width: 1rem;
|
||||||
|
color: #2563eb;
|
||||||
|
background-color: #fff;
|
||||||
|
border-color: #6b7280;
|
||||||
|
border-width: 1px;
|
||||||
|
--tw-shadow: 0 0 #0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='checkbox'] {
|
||||||
|
border-radius: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='radio'] {
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='checkbox']:focus,[type='radio']:focus {
|
||||||
|
outline: 2px solid transparent;
|
||||||
|
outline-offset: 2px;
|
||||||
|
--tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);
|
||||||
|
--tw-ring-offset-width: 2px;
|
||||||
|
--tw-ring-offset-color: #fff;
|
||||||
|
--tw-ring-color: #2563eb;
|
||||||
|
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
||||||
|
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
||||||
|
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='checkbox']:checked,[type='radio']:checked {
|
||||||
|
border-color: transparent;
|
||||||
|
background-color: currentColor;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='checkbox']:checked {
|
||||||
|
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='radio']:checked {
|
||||||
|
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e");
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='checkbox']:checked:hover,[type='checkbox']:checked:focus,[type='radio']:checked:hover,[type='radio']:checked:focus {
|
||||||
|
border-color: transparent;
|
||||||
|
background-color: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='checkbox']:indeterminate {
|
||||||
|
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");
|
||||||
|
border-color: transparent;
|
||||||
|
background-color: currentColor;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='checkbox']:indeterminate:hover,[type='checkbox']:indeterminate:focus {
|
||||||
|
border-color: transparent;
|
||||||
|
background-color: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='file'] {
|
||||||
|
background: unset;
|
||||||
|
border-color: inherit;
|
||||||
|
border-width: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-size: unset;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type='file']:focus {
|
||||||
|
outline: 1px solid ButtonText;
|
||||||
|
outline: 1px auto -webkit-focus-ring-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
*, ::before, ::after {
|
||||||
|
--tw-border-spacing-x: 0;
|
||||||
|
--tw-border-spacing-y: 0;
|
||||||
|
--tw-translate-x: 0;
|
||||||
|
--tw-translate-y: 0;
|
||||||
|
--tw-rotate: 0;
|
||||||
|
--tw-skew-x: 0;
|
||||||
|
--tw-skew-y: 0;
|
||||||
|
--tw-scale-x: 1;
|
||||||
|
--tw-scale-y: 1;
|
||||||
|
--tw-pan-x: ;
|
||||||
|
--tw-pan-y: ;
|
||||||
|
--tw-pinch-zoom: ;
|
||||||
|
--tw-scroll-snap-strictness: proximity;
|
||||||
|
--tw-gradient-from-position: ;
|
||||||
|
--tw-gradient-via-position: ;
|
||||||
|
--tw-gradient-to-position: ;
|
||||||
|
--tw-ordinal: ;
|
||||||
|
--tw-slashed-zero: ;
|
||||||
|
--tw-numeric-figure: ;
|
||||||
|
--tw-numeric-spacing: ;
|
||||||
|
--tw-numeric-fraction: ;
|
||||||
|
--tw-ring-inset: ;
|
||||||
|
--tw-ring-offset-width: 0px;
|
||||||
|
--tw-ring-offset-color: #fff;
|
||||||
|
--tw-ring-color: rgb(59 130 246 / 0.5);
|
||||||
|
--tw-ring-offset-shadow: 0 0 #0000;
|
||||||
|
--tw-ring-shadow: 0 0 #0000;
|
||||||
|
--tw-shadow: 0 0 #0000;
|
||||||
|
--tw-shadow-colored: 0 0 #0000;
|
||||||
|
--tw-blur: ;
|
||||||
|
--tw-brightness: ;
|
||||||
|
--tw-contrast: ;
|
||||||
|
--tw-grayscale: ;
|
||||||
|
--tw-hue-rotate: ;
|
||||||
|
--tw-invert: ;
|
||||||
|
--tw-saturate: ;
|
||||||
|
--tw-sepia: ;
|
||||||
|
--tw-drop-shadow: ;
|
||||||
|
--tw-backdrop-blur: ;
|
||||||
|
--tw-backdrop-brightness: ;
|
||||||
|
--tw-backdrop-contrast: ;
|
||||||
|
--tw-backdrop-grayscale: ;
|
||||||
|
--tw-backdrop-hue-rotate: ;
|
||||||
|
--tw-backdrop-invert: ;
|
||||||
|
--tw-backdrop-opacity: ;
|
||||||
|
--tw-backdrop-saturate: ;
|
||||||
|
--tw-backdrop-sepia: ;
|
||||||
|
}
|
||||||
|
|
||||||
|
::backdrop {
|
||||||
|
--tw-border-spacing-x: 0;
|
||||||
|
--tw-border-spacing-y: 0;
|
||||||
|
--tw-translate-x: 0;
|
||||||
|
--tw-translate-y: 0;
|
||||||
|
--tw-rotate: 0;
|
||||||
|
--tw-skew-x: 0;
|
||||||
|
--tw-skew-y: 0;
|
||||||
|
--tw-scale-x: 1;
|
||||||
|
--tw-scale-y: 1;
|
||||||
|
--tw-pan-x: ;
|
||||||
|
--tw-pan-y: ;
|
||||||
|
--tw-pinch-zoom: ;
|
||||||
|
--tw-scroll-snap-strictness: proximity;
|
||||||
|
--tw-gradient-from-position: ;
|
||||||
|
--tw-gradient-via-position: ;
|
||||||
|
--tw-gradient-to-position: ;
|
||||||
|
--tw-ordinal: ;
|
||||||
|
--tw-slashed-zero: ;
|
||||||
|
--tw-numeric-figure: ;
|
||||||
|
--tw-numeric-spacing: ;
|
||||||
|
--tw-numeric-fraction: ;
|
||||||
|
--tw-ring-inset: ;
|
||||||
|
--tw-ring-offset-width: 0px;
|
||||||
|
--tw-ring-offset-color: #fff;
|
||||||
|
--tw-ring-color: rgb(59 130 246 / 0.5);
|
||||||
|
--tw-ring-offset-shadow: 0 0 #0000;
|
||||||
|
--tw-ring-shadow: 0 0 #0000;
|
||||||
|
--tw-shadow: 0 0 #0000;
|
||||||
|
--tw-shadow-colored: 0 0 #0000;
|
||||||
|
--tw-blur: ;
|
||||||
|
--tw-brightness: ;
|
||||||
|
--tw-contrast: ;
|
||||||
|
--tw-grayscale: ;
|
||||||
|
--tw-hue-rotate: ;
|
||||||
|
--tw-invert: ;
|
||||||
|
--tw-saturate: ;
|
||||||
|
--tw-sepia: ;
|
||||||
|
--tw-drop-shadow: ;
|
||||||
|
--tw-backdrop-blur: ;
|
||||||
|
--tw-backdrop-brightness: ;
|
||||||
|
--tw-backdrop-contrast: ;
|
||||||
|
--tw-backdrop-grayscale: ;
|
||||||
|
--tw-backdrop-hue-rotate: ;
|
||||||
|
--tw-backdrop-invert: ;
|
||||||
|
--tw-backdrop-opacity: ;
|
||||||
|
--tw-backdrop-saturate: ;
|
||||||
|
--tw-backdrop-sepia: ;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-\[--brand-color\] {
|
||||||
|
color: var(--brand-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-green-600 {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(22 163 74 / var(--tw-text-opacity));
|
||||||
|
}
|
||||||
|
|
||||||
|
.opacity-75 {
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.dark\:text-green-500 {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(34 197 94 / var(--tw-text-opacity));
|
||||||
|
}
|
||||||
|
}
|
15
examples/use_storage/tailwind.config.js
Normal file
15
examples/use_storage/tailwind.config.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
content: {
|
||||||
|
files: ["*.html", "./src/**/*.rs", "../../src/docs/**/*.rs"],
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
corePlugins: {
|
||||||
|
preflight: false,
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
require('@tailwindcss/forms'),
|
||||||
|
],
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ mod use_throttle_fn;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
mod watch;
|
mod watch;
|
||||||
mod watch_debounced;
|
mod watch_debounced;
|
||||||
|
mod watch_pausable;
|
||||||
mod watch_throttled;
|
mod watch_throttled;
|
||||||
|
|
||||||
pub use use_debounce_fn::*;
|
pub use use_debounce_fn::*;
|
||||||
|
@ -35,6 +36,7 @@ pub use use_supported::*;
|
||||||
pub use use_throttle_fn::*;
|
pub use use_throttle_fn::*;
|
||||||
pub use watch::*;
|
pub use watch::*;
|
||||||
pub use watch_debounced::*;
|
pub use watch_debounced::*;
|
||||||
|
pub use watch_pausable::*;
|
||||||
pub use watch_throttled::*;
|
pub use watch_throttled::*;
|
||||||
|
|
||||||
extern crate self as leptos_use;
|
extern crate self as leptos_use;
|
||||||
|
|
|
@ -6,8 +6,10 @@ mod use_ceil;
|
||||||
mod use_floor;
|
mod use_floor;
|
||||||
mod use_max;
|
mod use_max;
|
||||||
mod use_min;
|
mod use_min;
|
||||||
|
mod use_round;
|
||||||
|
|
||||||
pub use use_ceil::*;
|
pub use use_ceil::*;
|
||||||
pub use use_floor::*;
|
pub use use_floor::*;
|
||||||
pub use use_max::*;
|
pub use use_max::*;
|
||||||
pub use use_min::*;
|
pub use use_min::*;
|
||||||
|
pub use use_round::*;
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use paste::paste;
|
|
||||||
|
|
||||||
macro_rules! use_partial_cmp {
|
macro_rules! use_partial_cmp {
|
||||||
($(#[$outer:meta])*
|
($(#[$outer:meta])*
|
||||||
$fn_name:ident,
|
$fn_name:ident,
|
||||||
|
|
30
src/math/use_round.rs
Normal file
30
src/math/use_round.rs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
use leptos::*;
|
||||||
|
use leptos_use::math::shared::use_simple_math;
|
||||||
|
use num::Float;
|
||||||
|
use paste::paste;
|
||||||
|
|
||||||
|
use_simple_math!(
|
||||||
|
/// Reactive `round()`.
|
||||||
|
///
|
||||||
|
/// ## Demo
|
||||||
|
///
|
||||||
|
/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_round)
|
||||||
|
///
|
||||||
|
/// ## Usage
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use leptos::*;
|
||||||
|
/// # use leptos_use::math::use_round;
|
||||||
|
/// #
|
||||||
|
/// # #[component]
|
||||||
|
/// # fn Demo(cx: Scope) -> impl IntoView {
|
||||||
|
/// let (value, set_value) = create_signal(cx, 45.95);
|
||||||
|
/// let result: Signal<f64> = use_round(cx, value); // 46
|
||||||
|
/// #
|
||||||
|
/// # assert_eq!(result.get(), 46.0);
|
||||||
|
/// # view! { cx, }
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[doc(cfg(feature = "math"))]
|
||||||
|
round
|
||||||
|
);
|
|
@ -1,495 +1,498 @@
|
||||||
// use crate::utils::{CloneableFnWithArg, FilterOptions};
|
use crate::utils::{CloneableFn, CloneableFnWithArg, FilterOptions};
|
||||||
// use crate::{
|
use crate::{
|
||||||
// filter_builder_methods, use_event_listener, watch_pausable_with_options, DebounceOptions,
|
filter_builder_methods, use_event_listener, watch_pausable_with_options, DebounceOptions,
|
||||||
// ThrottleOptions, WatchOptions, WatchPausableReturn,
|
ThrottleOptions, WatchOptions, WatchPausableReturn,
|
||||||
// };
|
};
|
||||||
// use default_struct_builder::DefaultBuilder;
|
use default_struct_builder::DefaultBuilder;
|
||||||
// use js_sys::Reflect;
|
use js_sys::Reflect;
|
||||||
// use leptos::*;
|
use leptos::*;
|
||||||
// use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
// use serde_json::Error;
|
use serde_json::Error;
|
||||||
// use std::time::Duration;
|
use std::time::Duration;
|
||||||
// use wasm_bindgen::{JsCast, JsValue};
|
use wasm_bindgen::{JsCast, JsValue};
|
||||||
//
|
|
||||||
// const CUSTOM_STORAGE_EVENT_NAME: &str = "leptos-use-storage";
|
const CUSTOM_STORAGE_EVENT_NAME: &str = "leptos-use-storage";
|
||||||
//
|
|
||||||
// /// Reactive [LocalStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) / [SessionStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage)
|
/// Reactive [LocalStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) / [SessionStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage)
|
||||||
// ///
|
///
|
||||||
// /// ## Demo
|
/// ## Demo
|
||||||
// ///
|
///
|
||||||
// /// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_storage)
|
/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_storage)
|
||||||
// ///
|
///
|
||||||
// /// ## Usage
|
/// ## Usage
|
||||||
// ///
|
///
|
||||||
// /// It returns a triplet `(read_signal, write_signal, delete_from_storage_func)` of type `(Signal<T>, WriteSignal<T>, Fn())`.
|
/// It returns a triplet `(read_signal, write_signal, delete_from_storage_func)` of type `(ReadSignal<T>, WriteSignal<T>, Fn())`.
|
||||||
// ///
|
///
|
||||||
// /// ```
|
/// ```
|
||||||
// /// # use leptos::*;
|
/// # use leptos::*;
|
||||||
// /// # use leptos_use::storage::{StorageType, use_storage, use_storage_with_options, UseStorageOptions};
|
/// # use leptos_use::storage::{StorageType, use_storage, use_storage_with_options, UseStorageOptions};
|
||||||
// /// # use serde::{Deserialize, Serialize};
|
/// # use serde::{Deserialize, Serialize};
|
||||||
// /// #
|
/// #
|
||||||
// /// #[derive(Serialize, Deserialize, Clone)]
|
/// #[derive(Serialize, Deserialize, Clone)]
|
||||||
// /// pub struct MyState {
|
/// pub struct MyState {
|
||||||
// /// pub hello: String,
|
/// pub hello: String,
|
||||||
// /// pub greeting: String,
|
/// pub greeting: String,
|
||||||
// /// }
|
/// }
|
||||||
// /// #
|
///
|
||||||
// /// # pub fn Demo(cx: Scope) -> impl IntoView {
|
/// # pub fn Demo(cx: Scope) -> impl IntoView {
|
||||||
// /// // bind struct. Must be serializable.
|
/// // bind struct. Must be serializable.
|
||||||
// /// let (state, set_state, _) = use_storage(
|
/// let (state, set_state, _) = use_storage(
|
||||||
// /// cx,
|
/// cx,
|
||||||
// /// "my-state",
|
/// "my-state",
|
||||||
// /// MyState {
|
/// MyState {
|
||||||
// /// hello: "hi".to_string(),
|
/// hello: "hi".to_string(),
|
||||||
// /// greeting: "Hello".to_string()
|
/// greeting: "Hello".to_string()
|
||||||
// /// },
|
/// },
|
||||||
// /// ); // returns Signal<MyState>
|
/// ); // returns Signal<MyState>
|
||||||
// ///
|
///
|
||||||
// /// // bind bool.
|
/// // bind bool.
|
||||||
// /// let (flag, set_flag, remove_flag) = use_storage(cx, "my-flag", true); // returns Signal<bool>
|
/// let (flag, set_flag, remove_flag) = use_storage(cx, "my-flag", true); // returns Signal<bool>
|
||||||
// ///
|
///
|
||||||
// /// // bind number
|
/// // bind number
|
||||||
// /// let (count, set_count, _) = use_storage(cx, "my-count", 0); // returns Signal<i32>
|
/// let (count, set_count, _) = use_storage(cx, "my-count", 0); // returns Signal<i32>
|
||||||
// ///
|
///
|
||||||
// /// // bind string with SessionStorage
|
/// // bind string with SessionStorage
|
||||||
// /// let (id, set_id, _) = use_storage_with_options(
|
/// let (id, set_id, _) = use_storage_with_options(
|
||||||
// /// cx,
|
/// cx,
|
||||||
// /// "my-id",
|
/// "my-id",
|
||||||
// /// "some_string_id".to_string(),
|
/// "some_string_id".to_string(),
|
||||||
// /// UseStorageOptions::default().storage_type(StorageType::Session),
|
/// UseStorageOptions::default().storage_type(StorageType::Session),
|
||||||
// /// );
|
/// );
|
||||||
// /// # view! { cx, }
|
/// # view! { cx, }
|
||||||
// /// # }
|
/// # }
|
||||||
// /// ```
|
/// ```
|
||||||
// ///
|
///
|
||||||
// /// ## Merge Defaults
|
/// ## Merge Defaults
|
||||||
// ///
|
///
|
||||||
// /// By default, [`use_storage`] will use the value from storage if it is present and ignores the default value.
|
/// By default, [`use_storage`] will use the value from storage if it is present and ignores the default value.
|
||||||
// /// Be aware that when you add more properties to the default value, the key might be `None`
|
/// Be aware that when you add more properties to the default value, the key might be `None`
|
||||||
// /// (in the case of an `Option<T>` field) if client's storage does not have that key
|
/// (in the case of an `Option<T>` field) if client's storage does not have that key
|
||||||
// /// or deserialization might fail altogether.
|
/// or deserialization might fail altogether.
|
||||||
// ///
|
///
|
||||||
// /// Let's say you had a struct `MyState` that has been saved to storage
|
/// Let's say you had a struct `MyState` that has been saved to storage
|
||||||
// ///
|
///
|
||||||
// /// ```ignore
|
/// ```ignore
|
||||||
// /// #[derive(Serialize, Deserialize, Clone)]
|
/// #[derive(Serialize, Deserialize, Clone)]
|
||||||
// /// struct MyState {
|
/// struct MyState {
|
||||||
// /// hello: String,
|
/// hello: String,
|
||||||
// /// }
|
/// }
|
||||||
// ///
|
///
|
||||||
// /// let (state, .. ) = use_storage(cx, "my-state", MyState { hello: "hello" });
|
/// let (state, .. ) = use_storage(cx, "my-state", MyState { hello: "hello" });
|
||||||
// /// ```
|
/// ```
|
||||||
// ///
|
///
|
||||||
// /// Now, in a newer version you added a field `greeting` to `MyState`.
|
/// Now, in a newer version you added a field `greeting` to `MyState`.
|
||||||
// ///
|
///
|
||||||
// /// ```ignore
|
/// ```ignore
|
||||||
// /// #[derive(Serialize, Deserialize, Clone)]
|
/// #[derive(Serialize, Deserialize, Clone)]
|
||||||
// /// struct MyState {
|
/// struct MyState {
|
||||||
// /// hello: String,
|
/// hello: String,
|
||||||
// /// greeting: String,
|
/// greeting: String,
|
||||||
// /// }
|
/// }
|
||||||
// ///
|
///
|
||||||
// /// let (state, .. ) = use_storage(
|
/// let (state, .. ) = use_storage(
|
||||||
// /// cx,
|
/// cx,
|
||||||
// /// "my-state",
|
/// "my-state",
|
||||||
// /// MyState { hello: "hi", greeting: "whatsup" },
|
/// MyState { hello: "hi", greeting: "whatsup" },
|
||||||
// /// ); // fails to deserialize -> default value
|
/// ); // fails to deserialize -> default value
|
||||||
// /// ```
|
/// ```
|
||||||
// ///
|
///
|
||||||
// /// This will fail to deserialize the stored string `{"hello": "hello"}` because it has no field `greeting`.
|
/// This will fail to deserialize the stored string `{"hello": "hello"}` because it has no field `greeting`.
|
||||||
// /// Hence it just uses the new default value provided and the previously saved value is lost.
|
/// Hence it just uses the new default value provided and the previously saved value is lost.
|
||||||
// ///
|
///
|
||||||
// /// To mitigate that you can provide a `merge_defaults` option. This is a pure function pointer
|
/// To mitigate that you can provide a `merge_defaults` option. This is a pure function pointer
|
||||||
// /// that takes the serialized (to json) stored value and the default value as arguments
|
/// that takes the serialized (to json) stored value and the default value as arguments
|
||||||
// /// and should return the serialized merged value.
|
/// and should return the serialized merged value.
|
||||||
// ///
|
///
|
||||||
// /// ```
|
/// ```
|
||||||
// /// # use leptos::*;
|
/// # use leptos::*;
|
||||||
// /// # use leptos_use::storage::{use_storage_with_options, UseStorageOptions};
|
/// # use leptos_use::storage::{use_storage_with_options, UseStorageOptions};
|
||||||
// /// # use serde::{Deserialize, Serialize};
|
/// # use serde::{Deserialize, Serialize};
|
||||||
// /// #
|
/// #
|
||||||
// /// #[derive(Serialize, Deserialize, Clone)]
|
/// #[derive(Serialize, Deserialize, Clone)]
|
||||||
// /// pub struct MyState {
|
/// pub struct MyState {
|
||||||
// /// pub hello: String,
|
/// pub hello: String,
|
||||||
// /// pub greeting: String,
|
/// pub greeting: String,
|
||||||
// /// }
|
/// }
|
||||||
// /// #
|
/// #
|
||||||
// /// # pub fn Demo(cx: Scope) -> impl IntoView {
|
/// # pub fn Demo(cx: Scope) -> impl IntoView {
|
||||||
// /// let (state, set_state) = use_storage_with_options(
|
/// let (state, set_state, _) = use_storage_with_options(
|
||||||
// /// cx,
|
/// cx,
|
||||||
// /// "my-state",
|
/// "my-state",
|
||||||
// /// MyState {
|
/// MyState {
|
||||||
// /// hello: "hi".to_string(),
|
/// hello: "hi".to_string(),
|
||||||
// /// greeting: "Hello".to_string()
|
/// greeting: "Hello".to_string()
|
||||||
// /// },
|
/// },
|
||||||
// /// UseStorageOptions::<MyState>::default().merge_defaults(|stored_value, default_value| {
|
/// UseStorageOptions::<MyState>::default().merge_defaults(|stored_value, default_value| {
|
||||||
// /// if stored_value.contains(r#""greeting":"#) {
|
/// if stored_value.contains(r#""greeting":"#) {
|
||||||
// /// stored_value.to_string()
|
/// stored_value.to_string()
|
||||||
// /// } else {
|
/// } else {
|
||||||
// /// // add "greeting": "Hello" to the string
|
/// // add "greeting": "Hello" to the string
|
||||||
// /// stored_value.replace("}", &format!(r#""greeting": "{}"}}"#, default_value.greeting))
|
/// stored_value.replace("}", &format!(r#""greeting": "{}"}}"#, default_value.greeting))
|
||||||
// /// }
|
/// }
|
||||||
// /// }),
|
/// }),
|
||||||
// /// );
|
/// );
|
||||||
// /// #
|
/// #
|
||||||
// /// # view! { cx, }
|
/// # view! { cx, }
|
||||||
// /// # }
|
/// # }
|
||||||
// /// ```
|
/// ```
|
||||||
// ///
|
///
|
||||||
// /// ## Filter Storage Write
|
/// ## Filter Storage Write
|
||||||
// ///
|
///
|
||||||
// /// You can specify `debounce` and `throttle` filter options for writing to storage.
|
/// You can specify `debounce` and `throttle` filter options for writing to storage.
|
||||||
// ///
|
///
|
||||||
// /// ## See also
|
/// ## See also
|
||||||
// ///
|
///
|
||||||
// ///
|
///
|
||||||
// /// * [`use_local_storage`]
|
/// * [`use_local_storage`]
|
||||||
// /// * [`use_session_storage`]
|
/// * [`use_session_storage`]
|
||||||
// #[doc(cfg(feature = "storage"))]
|
#[doc(cfg(feature = "storage"))]
|
||||||
// pub fn use_storage<T, D>(
|
pub fn use_storage<T, D>(
|
||||||
// cx: Scope,
|
cx: Scope,
|
||||||
// key: &str,
|
key: &str,
|
||||||
// defaults: D,
|
defaults: D,
|
||||||
// ) -> (Signal<T>, WriteSignal<Option<T>>, impl Fn() + Clone)
|
) -> (ReadSignal<T>, WriteSignal<T>, impl Fn() + Clone)
|
||||||
// where
|
where
|
||||||
// for<'de> T: Serialize + Deserialize<'de> + Clone + 'static,
|
for<'de> T: Serialize + Deserialize<'de> + Clone + 'static,
|
||||||
// D: Into<MaybeSignal<T>>,
|
D: Into<MaybeSignal<T>>,
|
||||||
// T: Clone,
|
T: Clone,
|
||||||
// {
|
{
|
||||||
// use_storage_with_options(cx, key, defaults, UseStorageOptions::default())
|
use_storage_with_options(cx, key, defaults, UseStorageOptions::default())
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// /// Version of [`use_storage`] that accepts [`UseStorageOptions`]. See [`use_storage`] for how to use.
|
/// Version of [`use_storage`] that accepts [`UseStorageOptions`]. See [`use_storage`] for how to use.
|
||||||
// #[doc(cfg(feature = "storage"))]
|
#[doc(cfg(feature = "storage"))]
|
||||||
// pub fn use_storage_with_options<T, D>(
|
pub fn use_storage_with_options<T, D>(
|
||||||
// cx: Scope,
|
cx: Scope,
|
||||||
// key: &str,
|
key: &str,
|
||||||
// defaults: D,
|
defaults: D,
|
||||||
// options: UseStorageOptions<T>,
|
options: UseStorageOptions<T>,
|
||||||
// ) -> (Signal<T>, WriteSignal<Option<T>>, impl Fn() + Clone)
|
) -> (ReadSignal<T>, WriteSignal<T>, impl Fn() + Clone)
|
||||||
// where
|
where
|
||||||
// for<'de> T: Serialize + Deserialize<'de> + Clone + 'static,
|
for<'de> T: Serialize + Deserialize<'de> + Clone + 'static,
|
||||||
// D: Into<MaybeSignal<T>>,
|
D: Into<MaybeSignal<T>>,
|
||||||
// T: Clone,
|
T: Clone,
|
||||||
// {
|
{
|
||||||
// let defaults = defaults.into();
|
let defaults = defaults.into();
|
||||||
//
|
|
||||||
// let UseStorageOptions {
|
let UseStorageOptions {
|
||||||
// storage_type,
|
storage_type,
|
||||||
// listen_to_storage_changes,
|
listen_to_storage_changes,
|
||||||
// write_defaults,
|
write_defaults,
|
||||||
// merge_defaults,
|
merge_defaults,
|
||||||
// on_error,
|
on_error,
|
||||||
// filter,
|
filter,
|
||||||
// } = options;
|
} = options;
|
||||||
//
|
|
||||||
// let (data, set_data) = create_signal(cx, defaults.get_untracked());
|
let (data, set_data) = create_signal(cx, defaults.get_untracked());
|
||||||
//
|
|
||||||
// let storage = storage_type.into_storage();
|
let storage = storage_type.into_storage();
|
||||||
//
|
|
||||||
// let remove = match storage {
|
let remove: Box<dyn CloneableFn> = match storage {
|
||||||
// Ok(Some(storage)) => {
|
Ok(Some(storage)) => {
|
||||||
// let on_err = on_error.clone();
|
let on_err = on_error.clone();
|
||||||
//
|
|
||||||
// let store = storage.clone();
|
let store = storage.clone();
|
||||||
// let k = key.to_string();
|
let k = key.to_string();
|
||||||
//
|
|
||||||
// let write = move |v: &Option<T>| {
|
let write = move |v: &T| {
|
||||||
// if let Some(v) = v {
|
match serde_json::to_string(&v) {
|
||||||
// match serde_json::to_string(&v) {
|
Ok(ref serialized) => match store.get_item(&k) {
|
||||||
// Ok(ref serialized) => match store.get_item(&k) {
|
Ok(old_value) => {
|
||||||
// Ok(old_value) => {
|
if old_value.as_ref() != Some(serialized) {
|
||||||
// if old_value.as_ref() != Some(serialized) {
|
if let Err(e) = store.set_item(&k, &serialized) {
|
||||||
// if let Err(e) = store.set_item(&k, &serialized) {
|
on_err(UseStorageError::StorageAccessError(e));
|
||||||
// on_err(UseStorageError::StorageAccessError(e));
|
} else {
|
||||||
// } else {
|
let mut event_init = web_sys::CustomEventInit::new();
|
||||||
// let mut event_init = web_sys::CustomEventInit::new();
|
event_init.detail(
|
||||||
// event_init.detail(
|
&StorageEventDetail {
|
||||||
// &StorageEventDetail {
|
key: Some(k.clone()),
|
||||||
// key: Some(k.clone()),
|
old_value,
|
||||||
// old_value,
|
new_value: Some(serialized.clone()),
|
||||||
// new_value: Some(serialized.clone()),
|
storage_area: Some(store.clone()),
|
||||||
// storage_area: Some(store.clone()),
|
}
|
||||||
// }
|
.into(),
|
||||||
// .into(),
|
);
|
||||||
// );
|
|
||||||
//
|
// importantly this should _not_ be a StorageEvent since those cannot
|
||||||
// // importantly this should _not_ be a StorageEvent since those cannot
|
// be constructed with a non-built-in storage area
|
||||||
// // be constructed with a non-built-in storage area
|
let _ = window().dispatch_event(
|
||||||
// let _ = window().dispatch_event(
|
&web_sys::CustomEvent::new_with_event_init_dict(
|
||||||
// &web_sys::CustomEvent::new_with_event_init_dict(
|
CUSTOM_STORAGE_EVENT_NAME,
|
||||||
// CUSTOM_STORAGE_EVENT_NAME,
|
&event_init,
|
||||||
// &event_init,
|
)
|
||||||
// )
|
.expect("Failed to create CustomEvent"),
|
||||||
// .expect("Failed to create CustomEvent"),
|
);
|
||||||
// );
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
Err(e) => {
|
||||||
// Err(e) => {
|
on_err.clone()(UseStorageError::StorageAccessError(e));
|
||||||
// on_err.clone()(UseStorageError::StorageAccessError(e));
|
}
|
||||||
// }
|
},
|
||||||
// },
|
Err(e) => {
|
||||||
// Err(e) => {
|
on_err.clone()(UseStorageError::SerializationError(e));
|
||||||
// on_err.clone()(UseStorageError::SerializationError(e));
|
}
|
||||||
// }
|
}
|
||||||
// }
|
};
|
||||||
// } else if let Err(e) = store.remove_item(&k) {
|
|
||||||
// on_err(UseStorageError::StorageAccessError(e));
|
let store = storage.clone();
|
||||||
// }
|
let on_err = on_error.clone();
|
||||||
// };
|
let k = key.to_string();
|
||||||
//
|
let def = defaults.clone();
|
||||||
// let store = storage.clone();
|
|
||||||
// let on_err = on_error.clone();
|
let read = move |event_detail: Option<StorageEventDetail>| -> Option<T> {
|
||||||
// let k = key.to_string();
|
let raw_init = match serde_json::to_string(&def.get_untracked()) {
|
||||||
// let def = defaults.clone();
|
Ok(serialized) => Some(serialized),
|
||||||
//
|
Err(e) => {
|
||||||
// let read = move |event_detail: Option<StorageEventDetail>| -> Option<T> {
|
on_err.clone()(UseStorageError::DefaultSerializationError(e));
|
||||||
// let raw_init = match serde_json::to_string(&def.get_untracked()) {
|
None
|
||||||
// Ok(serialized) => Some(serialized),
|
}
|
||||||
// Err(e) => {
|
};
|
||||||
// on_err.clone()(UseStorageError::DefaultSerializationError(e));
|
|
||||||
// None
|
let raw_value = if let Some(event_detail) = event_detail {
|
||||||
// }
|
event_detail.new_value
|
||||||
// };
|
} else {
|
||||||
//
|
match store.get_item(&k) {
|
||||||
// let raw_value = if let Some(event_detail) = event_detail {
|
Ok(raw_value) => match raw_value {
|
||||||
// event_detail.new_value
|
Some(raw_value) => {
|
||||||
// } else {
|
Some(merge_defaults(&raw_value, &def.get_untracked()))
|
||||||
// match store.get_item(&k) {
|
}
|
||||||
// Ok(raw_value) => match raw_value {
|
None => raw_init.clone(),
|
||||||
// Some(raw_value) => {
|
},
|
||||||
// Some(merge_defaults(&raw_value, &def.get_untracked()))
|
Err(e) => {
|
||||||
// }
|
on_err.clone()(UseStorageError::StorageAccessError(e));
|
||||||
// None => raw_init.clone(),
|
None
|
||||||
// },
|
}
|
||||||
// Err(e) => {
|
}
|
||||||
// on_err.clone()(UseStorageError::StorageAccessError(e));
|
};
|
||||||
// None
|
|
||||||
// }
|
match raw_value {
|
||||||
// }
|
Some(raw_value) => match serde_json::from_str(&raw_value) {
|
||||||
// };
|
Ok(v) => Some(v),
|
||||||
//
|
Err(e) => {
|
||||||
// match raw_value {
|
on_err.clone()(UseStorageError::SerializationError(e));
|
||||||
// Some(raw_value) => match serde_json::from_str(&raw_value) {
|
None
|
||||||
// Ok(v) => Some(v),
|
}
|
||||||
// Err(e) => {
|
},
|
||||||
// on_err.clone()(UseStorageError::SerializationError(e));
|
None => {
|
||||||
// None
|
if let Some(raw_init) = &raw_init {
|
||||||
// }
|
if write_defaults {
|
||||||
// },
|
if let Err(e) = store.set_item(&k, raw_init) {
|
||||||
// None => {
|
on_err(UseStorageError::StorageAccessError(e));
|
||||||
// if let Some(raw_init) = &raw_init {
|
}
|
||||||
// if write_defaults {
|
}
|
||||||
// if let Err(e) = store.set_item(&k, raw_init) {
|
}
|
||||||
// on_err(UseStorageError::StorageAccessError(e));
|
|
||||||
// }
|
Some(def.get_untracked())
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
//
|
};
|
||||||
// Some(def.get_untracked())
|
|
||||||
// }
|
let WatchPausableReturn {
|
||||||
// }
|
pause: pause_watch,
|
||||||
// };
|
resume: resume_watch,
|
||||||
//
|
..
|
||||||
// let WatchPausableReturn {
|
} = watch_pausable_with_options(
|
||||||
// pause: pause_watch,
|
cx,
|
||||||
// resume: resume_watch,
|
data,
|
||||||
// ..
|
move |data, _, _| write.clone()(data),
|
||||||
// } = watch_pausable_with_options(
|
WatchOptions::default().filter(filter),
|
||||||
// cx,
|
);
|
||||||
// data,
|
|
||||||
// move |data, _, _| write.clone()(data),
|
let k = key.to_string();
|
||||||
// WatchOptions::default().immediate(true).filter(filter),
|
let store = storage.clone();
|
||||||
// );
|
|
||||||
//
|
let update = move |event_detail: Option<StorageEventDetail>| {
|
||||||
// let k = key.to_string();
|
if let Some(event_detail) = &event_detail {
|
||||||
//
|
if event_detail.storage_area != Some(store) {
|
||||||
// let update = move |event_detail: Option<StorageEventDetail>| {
|
return;
|
||||||
// if let Some(event_detail) = &event_detail {
|
}
|
||||||
// if event_detail.storage_area != Some(storage) {
|
|
||||||
// return;
|
match &event_detail.key {
|
||||||
// }
|
None => {
|
||||||
//
|
set_data(defaults.get_untracked());
|
||||||
// match &event_detail.key {
|
return;
|
||||||
// None => {
|
}
|
||||||
// set_data(Some(defaults.get_untracked()));
|
Some(event_key) => {
|
||||||
// return;
|
if event_key != &k {
|
||||||
// }
|
return;
|
||||||
// Some(event_key) => {
|
}
|
||||||
// if event_key != &k {
|
}
|
||||||
// return;
|
};
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
// };
|
pause_watch();
|
||||||
// }
|
|
||||||
//
|
if let Some(value) = read(event_detail.clone()) {
|
||||||
// pause_watch();
|
set_data(value);
|
||||||
//
|
}
|
||||||
// set_data(read(event_detail.clone()));
|
|
||||||
//
|
if event_detail.is_some() {
|
||||||
// if event_detail.is_some() {
|
// use timeout to avoid inifinite loop
|
||||||
// // use timeout to avoid inifinite loop
|
let resume = resume_watch.clone();
|
||||||
// let resume = resume_watch.clone();
|
let _ = set_timeout_with_handle(resume, Duration::ZERO);
|
||||||
// let _ = set_timeout_with_handle(resume, Duration::ZERO);
|
} else {
|
||||||
// } else {
|
resume_watch();
|
||||||
// resume_watch();
|
}
|
||||||
// }
|
};
|
||||||
// };
|
|
||||||
//
|
let upd = update.clone();
|
||||||
// let upd = update.clone();
|
let update_from_custom_event =
|
||||||
// let update_from_custom_event =
|
move |event: web_sys::CustomEvent| upd.clone()(Some(event.into()));
|
||||||
// move |event: web_sys::CustomEvent| upd.clone()(Some(event.into()));
|
|
||||||
//
|
let upd = update.clone();
|
||||||
// let upd = update.clone();
|
let update_from_storage_event =
|
||||||
// let update_from_storage_event =
|
move |event: web_sys::StorageEvent| upd.clone()(Some(event.into()));
|
||||||
// move |event: web_sys::StorageEvent| upd.clone()(Some(event.into()));
|
|
||||||
//
|
if listen_to_storage_changes {
|
||||||
// if listen_to_storage_changes {
|
let _ = use_event_listener(cx, window(), ev::storage, update_from_storage_event);
|
||||||
// let _ = use_event_listener(cx, window(), ev::storage, update_from_storage_event);
|
let _ = use_event_listener(
|
||||||
// let _ = use_event_listener(
|
cx,
|
||||||
// cx,
|
window(),
|
||||||
// window(),
|
ev::Custom::new(CUSTOM_STORAGE_EVENT_NAME),
|
||||||
// ev::Custom::new(CUSTOM_STORAGE_EVENT_NAME),
|
update_from_custom_event,
|
||||||
// update_from_custom_event,
|
);
|
||||||
// );
|
}
|
||||||
// }
|
|
||||||
//
|
update(None);
|
||||||
// update(None);
|
|
||||||
//
|
let k = key.to_string();
|
||||||
// move || storage.remove_item(&k)
|
|
||||||
// }
|
Box::new(move || {
|
||||||
// Err(e) => {
|
let _ = storage.remove_item(&k);
|
||||||
// on_error(UseStorageError::NoStorage(e));
|
})
|
||||||
// move || {}
|
}
|
||||||
// }
|
Err(e) => {
|
||||||
// _ => {
|
on_error(UseStorageError::NoStorage(e));
|
||||||
// // do nothing
|
Box::new(move || {})
|
||||||
// move || {}
|
}
|
||||||
// }
|
_ => {
|
||||||
// };
|
// do nothing
|
||||||
//
|
Box::new(move || {})
|
||||||
// (data, set_data, remove)
|
}
|
||||||
// }
|
};
|
||||||
//
|
|
||||||
// #[derive(Clone)]
|
(data, set_data, move || remove.clone()())
|
||||||
// pub struct StorageEventDetail {
|
}
|
||||||
// pub key: Option<String>,
|
|
||||||
// pub old_value: Option<String>,
|
#[derive(Clone)]
|
||||||
// pub new_value: Option<String>,
|
pub struct StorageEventDetail {
|
||||||
// pub storage_area: Option<web_sys::Storage>,
|
pub key: Option<String>,
|
||||||
// }
|
pub old_value: Option<String>,
|
||||||
//
|
pub new_value: Option<String>,
|
||||||
// impl From<web_sys::StorageEvent> for StorageEventDetail {
|
pub storage_area: Option<web_sys::Storage>,
|
||||||
// fn from(event: web_sys::StorageEvent) -> Self {
|
}
|
||||||
// Self {
|
|
||||||
// key: event.key(),
|
impl From<web_sys::StorageEvent> for StorageEventDetail {
|
||||||
// old_value: event.old_value(),
|
fn from(event: web_sys::StorageEvent) -> Self {
|
||||||
// new_value: event.new_value(),
|
Self {
|
||||||
// storage_area: event.storage_area(),
|
key: event.key(),
|
||||||
// }
|
old_value: event.old_value(),
|
||||||
// }
|
new_value: event.new_value(),
|
||||||
// }
|
storage_area: event.storage_area(),
|
||||||
//
|
}
|
||||||
// impl From<web_sys::CustomEvent> for StorageEventDetail {
|
}
|
||||||
// fn from(event: web_sys::CustomEvent) -> Self {
|
}
|
||||||
// let detail = event.detail();
|
|
||||||
// Self {
|
impl From<web_sys::CustomEvent> for StorageEventDetail {
|
||||||
// key: get_optional_string(&detail, "key"),
|
fn from(event: web_sys::CustomEvent) -> Self {
|
||||||
// old_value: get_optional_string(&detail, "oldValue"),
|
let detail = event.detail();
|
||||||
// new_value: get_optional_string(&detail, "newValue"),
|
Self {
|
||||||
// storage_area: Reflect::get(&detail, &"storageArea".into())
|
key: get_optional_string(&detail, "key"),
|
||||||
// .map(|v| v.dyn_into::<web_sys::Storage>().ok())
|
old_value: get_optional_string(&detail, "oldValue"),
|
||||||
// .unwrap_or_default(),
|
new_value: get_optional_string(&detail, "newValue"),
|
||||||
// }
|
storage_area: Reflect::get(&detail, &"storageArea".into())
|
||||||
// }
|
.map(|v| v.dyn_into::<web_sys::Storage>().ok())
|
||||||
// }
|
.unwrap_or_default(),
|
||||||
//
|
}
|
||||||
// impl From<StorageEventDetail> for JsValue {
|
}
|
||||||
// fn from(event: StorageEventDetail) -> Self {
|
}
|
||||||
// let obj = js_sys::Object::new();
|
|
||||||
//
|
impl From<StorageEventDetail> for JsValue {
|
||||||
// let _ = Reflect::set(&obj, &"key".into(), &event.key.into());
|
fn from(event: StorageEventDetail) -> Self {
|
||||||
// let _ = Reflect::set(&obj, &"oldValue".into(), &event.old_value.into());
|
let obj = js_sys::Object::new();
|
||||||
// let _ = Reflect::set(&obj, &"newValue".into(), &event.new_value.into());
|
|
||||||
// let _ = Reflect::set(&obj, &"storageArea".into(), &event.storage_area.into());
|
let _ = Reflect::set(&obj, &"key".into(), &event.key.into());
|
||||||
//
|
let _ = Reflect::set(&obj, &"oldValue".into(), &event.old_value.into());
|
||||||
// obj.into()
|
let _ = Reflect::set(&obj, &"newValue".into(), &event.new_value.into());
|
||||||
// }
|
let _ = Reflect::set(&obj, &"storageArea".into(), &event.storage_area.into());
|
||||||
// }
|
|
||||||
//
|
obj.into()
|
||||||
// fn get_optional_string(v: &JsValue, key: &str) -> Option<String> {
|
}
|
||||||
// Reflect::get(v, &key.into())
|
}
|
||||||
// .map(|v| v.as_string())
|
|
||||||
// .unwrap_or_default()
|
fn get_optional_string(v: &JsValue, key: &str) -> Option<String> {
|
||||||
// }
|
Reflect::get(v, &key.into())
|
||||||
//
|
.map(|v| v.as_string())
|
||||||
// /// Error type for use_storage_with_options
|
.unwrap_or_default()
|
||||||
// #[doc(cfg(feature = "storage"))]
|
}
|
||||||
// pub enum UseStorageError<E = ()> {
|
|
||||||
// NoStorage(JsValue),
|
/// Error type for use_storage_with_options
|
||||||
// StorageAccessError(JsValue),
|
#[doc(cfg(feature = "storage"))]
|
||||||
// CustomStorageAccessError(E),
|
pub enum UseStorageError<E = ()> {
|
||||||
// SerializationError(Error),
|
NoStorage(JsValue),
|
||||||
// DefaultSerializationError(Error),
|
StorageAccessError(JsValue),
|
||||||
// }
|
CustomStorageAccessError(E),
|
||||||
//
|
SerializationError(Error),
|
||||||
// /// Options for [`use_storage_with_options`].
|
DefaultSerializationError(Error),
|
||||||
// #[doc(cfg(feature = "storage"))]
|
}
|
||||||
// #[derive(DefaultBuilder)]
|
|
||||||
// pub struct UseStorageOptions<T> {
|
/// Options for [`use_storage_with_options`].
|
||||||
// /// Type of storage. Can be `Local` (default), `Session` or `Custom(web_sys::Storage)`
|
#[doc(cfg(feature = "storage"))]
|
||||||
// storage_type: StorageType,
|
#[derive(DefaultBuilder)]
|
||||||
// /// Listen to changes to this storage key from somewhere else. Defaults to true.
|
pub struct UseStorageOptions<T> {
|
||||||
// listen_to_storage_changes: bool,
|
/// Type of storage. Can be `Local` (default), `Session` or `Custom(web_sys::Storage)`
|
||||||
// /// If no value for the give key is found in the storage, write it. Defaults to true.
|
storage_type: StorageType,
|
||||||
// write_defaults: bool,
|
/// Listen to changes to this storage key from somewhere else. Defaults to true.
|
||||||
// /// Takes the serialized (json) stored value and the default value and returns a merged version.
|
listen_to_storage_changes: bool,
|
||||||
// /// Defaults to simply returning the stored value.
|
/// If no value for the give key is found in the storage, write it. Defaults to true.
|
||||||
// merge_defaults: fn(&str, &T) -> String,
|
write_defaults: bool,
|
||||||
// /// Optional callback whenever an error occurs. The callback takes an argument of type [`UseStorageError`].
|
/// Takes the serialized (json) stored value and the default value and returns a merged version.
|
||||||
// on_error: Box<dyn CloneableFnWithArg<UseStorageError>>,
|
/// Defaults to simply returning the stored value.
|
||||||
//
|
merge_defaults: fn(&str, &T) -> String,
|
||||||
// /// Debounce or throttle the writing to storage whenever the value changes.
|
/// Optional callback whenever an error occurs. The callback takes an argument of type [`UseStorageError`].
|
||||||
// filter: FilterOptions,
|
on_error: Box<dyn CloneableFnWithArg<UseStorageError>>,
|
||||||
// }
|
|
||||||
//
|
/// Debounce or throttle the writing to storage whenever the value changes.
|
||||||
// impl<T> Default for UseStorageOptions<T> {
|
filter: FilterOptions,
|
||||||
// fn default() -> Self {
|
}
|
||||||
// Self {
|
|
||||||
// storage_type: Default::default(),
|
impl<T> Default for UseStorageOptions<T> {
|
||||||
// listen_to_storage_changes: true,
|
fn default() -> Self {
|
||||||
// write_defaults: true,
|
Self {
|
||||||
// merge_defaults: |stored_value, _default_value| stored_value.to_string(),
|
storage_type: Default::default(),
|
||||||
// on_error: Box::new(|_| ()),
|
listen_to_storage_changes: true,
|
||||||
// filter: Default::default(),
|
write_defaults: true,
|
||||||
// }
|
merge_defaults: |stored_value, _default_value| stored_value.to_string(),
|
||||||
// }
|
on_error: Box::new(|_| ()),
|
||||||
// }
|
filter: Default::default(),
|
||||||
//
|
}
|
||||||
// impl<T> UseStorageOptions<T> {
|
}
|
||||||
// filter_builder_methods!(
|
}
|
||||||
// /// the serializing and storing into storage
|
|
||||||
// filter
|
impl<T> UseStorageOptions<T> {
|
||||||
// );
|
filter_builder_methods!(
|
||||||
// }
|
/// the serializing and storing into storage
|
||||||
//
|
filter
|
||||||
// /// Local or session storage or a custom store that is a `web_sys::Storage`.
|
);
|
||||||
// #[doc(cfg(feature = "storage"))]
|
}
|
||||||
// #[derive(Default)]
|
|
||||||
// pub enum StorageType {
|
/// Local or session storage or a custom store that is a `web_sys::Storage`.
|
||||||
// #[default]
|
#[doc(cfg(feature = "storage"))]
|
||||||
// Local,
|
#[derive(Default)]
|
||||||
// Session,
|
pub enum StorageType {
|
||||||
// Custom(web_sys::Storage),
|
#[default]
|
||||||
// }
|
Local,
|
||||||
//
|
Session,
|
||||||
// impl StorageType {
|
Custom(web_sys::Storage),
|
||||||
// pub fn into_storage(self) -> Result<Option<web_sys::Storage>, JsValue> {
|
}
|
||||||
// match self {
|
|
||||||
// StorageType::Local => window().local_storage(),
|
impl StorageType {
|
||||||
// StorageType::Session => window().session_storage(),
|
pub fn into_storage(self) -> Result<Option<web_sys::Storage>, JsValue> {
|
||||||
// StorageType::Custom(storage) => Ok(Some(storage)),
|
match self {
|
||||||
// }
|
StorageType::Local => window().local_storage(),
|
||||||
// }
|
StorageType::Session => window().session_storage(),
|
||||||
// }
|
StorageType::Custom(storage) => Ok(Some(storage)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue