added use_breakpoints

This commit is contained in:
Maccesch 2023-06-10 19:15:41 +01:00
parent 06815b1ca9
commit 55f0ea836a
27 changed files with 1098 additions and 23 deletions

2
.idea/leptos-use.iml generated
View file

@ -23,6 +23,7 @@
<sourceFolder url="file://$MODULE_DIR$/examples/use_favicon/src" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/examples/use_favicon/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/examples/use_media_query/src" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/examples/use_media_query/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/examples/use_storage/src" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/examples/use_storage/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/examples/use_breakpoints/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" />
@ -42,6 +43,7 @@
<excludeFolder url="file://$MODULE_DIR$/examples/use_favicon/target" /> <excludeFolder url="file://$MODULE_DIR$/examples/use_favicon/target" />
<excludeFolder url="file://$MODULE_DIR$/examples/use_media_query/target" /> <excludeFolder url="file://$MODULE_DIR$/examples/use_media_query/target" />
<excludeFolder url="file://$MODULE_DIR$/examples/use_storage/target" /> <excludeFolder url="file://$MODULE_DIR$/examples/use_storage/target" />
<excludeFolder url="file://$MODULE_DIR$/examples/use_breakpoints/target" />
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />

View file

@ -19,6 +19,7 @@
- `use_preferred_dark` - `use_preferred_dark`
- `use_preferred_contrast` - `use_preferred_contrast`
- `use_favicon` - `use_favicon`
- `use_breakpoints`
#### Other Changes #### Other Changes

View file

@ -21,7 +21,7 @@ default-struct-builder = { path = "../default-struct-builder" }
num = { version = "0.4.0", optional = true } num = { version = "0.4.0", optional = true }
serde = { version = "1.0.163", optional = true } serde = { version = "1.0.163", optional = true }
serde_json = { version = "1.0.96", optional = true } serde_json = { version = "1.0.96", optional = true }
paste = { version = "1.0.12", optional = true } paste = "1.0.12"
[dependencies.web-sys] [dependencies.web-sys]
version = "0.3.63" version = "0.3.63"
@ -51,7 +51,7 @@ features = [
[features] [features]
docs = [] docs = []
math = ["num", "paste"] math = ["num"]
storage = ["serde", "serde_json", "web-sys/Storage", "web-sys/StorageEvent"] storage = ["serde", "serde_json", "web-sys/Storage", "web-sys/StorageEvent"]
[package.metadata."docs.rs"] [package.metadata."docs.rs"]

View file

@ -10,19 +10,34 @@
<p align="center"> <p align="center">
<a href="https://crates.io/crates/leptos-use"><img src="https://img.shields.io/crates/v/leptos-use.svg?label=&color=%232C1275" alt="Crates.io"/></a> <a href="https://crates.io/crates/leptos-use"><img src="https://img.shields.io/crates/v/leptos-use.svg?label=&color=%232C1275" alt="Crates.io"/></a>
<a href="https://leptos-use.rs"><img src="https://img.shields.io/badge/-docs%20%26%20demos-%239A233F" alt="Docs & Demos"></a> <a href="https://leptos-use.rs"><img src="https://img.shields.io/badge/-docs%20%26%20demos-%239A233F" alt="Docs & Demos"></a>
<a href="https://leptos-use.rs"><img src="https://img.shields.io/badge/-23%20functions-%23EF3939" alt="23 Functions" /></a> <a href="https://leptos-use.rs"><img src="https://img.shields.io/badge/-25%20functions-%23EF3939" alt="25 Functions" /></a>
</p> </p>
<br/> <br/>
<br/> <br/>
<br/> <br/>
-----
## Usage
[![Docs](https://docs.rs/leptos-use/badge.svg)](https://docs.rs/leptos-use/) [![Docs](https://docs.rs/leptos-use/badge.svg)](https://docs.rs/leptos-use/)
[![MIT/Apache 2.0](https://img.shields.io/badge/license-MIT%2FApache-blue.svg)](https://github.com/synphonyte/leptos-use#license) [![MIT/Apache 2.0](https://img.shields.io/badge/license-MIT%2FApache-blue.svg)](https://github.com/synphonyte/leptos-use#license)
[![Build Status](https://github.com/synphonyte/leptos-use/actions/workflows/ci.yml/badge.svg)](https://github.com/synphonyte/leptos-use/actions/workflows/ci.yml) [![Build Status](https://github.com/synphonyte/leptos-use/actions/workflows/ci.yml/badge.svg)](https://github.com/synphonyte/leptos-use/actions/workflows/ci.yml)
```rust
use leptos::*;
use leptos_use::{use_mouse, UseMouseReturn};
#[component]
fn Demo(cx: Scope) -> impl IntoView {
let UseMouseReturn { x, y, .. } = use_mouse(cx);
view! { cx,
{x} " x " {y}
}
}
```
We have only just begun implementing the first dozen functions but they are already very usable and ergonomic. We have only just begun implementing the first dozen functions but they are already very usable and ergonomic.
Missing a function? Open a ticket or PR! Missing a function? Open a ticket or PR!

View file

@ -1,6 +1,6 @@
# Summary # Summary
[Introduction]() [Introduction](introduction.md)
[Get Started](get_started.md) [Get Started](get_started.md)
[Functions](functions.md) [Functions](functions.md)
[Changelog](changelog.md) [Changelog](changelog.md)
@ -18,6 +18,7 @@
# Browser # Browser
- [use_breakpoints](browser/use_breakpoints.md)
- [use_event_listener](browser/use_event_listener.md) - [use_event_listener](browser/use_event_listener.md)
- [use_favicon](browser/use_favicon.md) - [use_favicon](browser/use_favicon.md)
- [use_media_query](browser/use_media_query.md) - [use_media_query](browser/use_media_query.md)

View file

@ -0,0 +1,3 @@
# use_breakpoints
<!-- cmdrun python3 ../extract_doc_comment.py use_breakpoints -->

View file

@ -7,7 +7,7 @@ def main():
module = sys.argv[2] if len(sys.argv) > 2 else None module = sys.argv[2] if len(sys.argv) > 2 else None
generate_function_overview_for_category(entry, None) generate_function_overview_for_category(entry, module)
def generate_function_overview_for_category(category, module): def generate_function_overview_for_category(category, module):
@ -27,8 +27,9 @@ def generate_function_overview(category, name, module):
file_name = f"../../../src{module}/{name}.rs" file_name = f"../../../src{module}/{name}.rs"
with open(file_name) as f: with open(file_name) as f:
for line in f.readlines(): for line in f.readlines():
if line.startswith("///"): stripped_line = line.strip()
line = line.strip().replace("/// ", "") if stripped_line.startswith("///"):
line = stripped_line.replace("/// ", "")
print(f"- [{name}](/{category}/{name}.md) {line}") print(f"- [{name}](/{category}/{name}.md) {line}")
return return

View file

@ -0,0 +1,16 @@
<div style="display: flex; justify-content: center; height: calc(100vh - 200px); align-items: center; flex-direction: column">
<p>
<a href="https://github.com/synphonyte/leptos-use">
<img src="https://raw.githubusercontent.com/synphonyte/leptos-use/main/docs/logo.svg" alt="Leptos-Use Collection of essential Leptos utilities" width="150"/>
</a>
</p>
<h4>Collection of essential Leptos utilities</h4>
<p>Inspired by React-Use / VueUse / SolidJS-USE</p>
<p>
<a href="https://crates.io/crates/leptos-use"><img src="https://img.shields.io/crates/v/leptos-use.svg?label=&color=%232C1275" alt="Crates.io"/></a>
<a href="./get_started.html"><img src="https://img.shields.io/badge/-docs%20%26%20demos-%239A233F" alt="Docs & Demos"></a>
<a href="./functions.html"><img src="https://img.shields.io/badge/-25%20functions-%23EF3939" alt="25 Functions" /></a>
</p>
</div>

View file

@ -15,11 +15,8 @@ def main():
with open("README.md", "r") as f: with open("README.md", "r") as f:
original_text = f.read() original_text = f.read()
text = re.sub(
r'<img src="https://img.shields.io/badge/-\d+%20functions-%23EF3939" alt="\d+ Functions"', text = replace_functions_shield(count, original_text)
f'<img src="https://img.shields.io/badge/-{count}%20functions-%23EF3939" alt="{count} Functions"',
original_text
)
if len(sys.argv) > 1 and sys.argv[1] == "--check": if len(sys.argv) > 1 and sys.argv[1] == "--check":
if original_text != text: if original_text != text:
@ -33,5 +30,20 @@ def main():
with open("README.md", "w") as f: with open("README.md", "w") as f:
f.write(text) f.write(text)
with open("docs/book/src/introduction.md", "r") as f:
text = replace_functions_shield(count, f.read())
with open("docs/book/src/introduction.md", "w") as f:
f.write(text)
def replace_functions_shield(count, original_text):
text = re.sub(
r'<img src="https://img.shields.io/badge/-\d+%20functions-%23EF3939" alt="\d+ Functions"',
f'<img src="https://img.shields.io/badge/-{count}%20functions-%23EF3939" alt="{count} Functions"',
original_text
)
return text
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View file

@ -0,0 +1,16 @@
[package]
name = "use_breakpoints"
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"] }
web-sys = "0.3"
[dev-dependencies]
wasm-bindgen = "0.2"
wasm-bindgen-test = "0.3.0"

View file

@ -0,0 +1,23 @@
A simple example for `use_breakpoints`.
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
```

View file

@ -0,0 +1,2 @@
[build]
public_url = "/demo/"

View file

@ -0,0 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<link data-trunk rel="css" href="style/output.css">
</head>
<body></body>
</html>

View file

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View file

@ -0,0 +1,2 @@
[toolchain]
channel = "nightly"

View file

@ -0,0 +1,151 @@
use leptos::*;
use leptos_use::docs::{demo_or_body, BooleanDisplay};
use leptos_use::{breakpoints_tailwind, use_breakpoints, BreakpointsTailwind};
#[component]
fn Demo(cx: Scope) -> impl IntoView {
let breakpoints = breakpoints_tailwind();
let screen_size = use_breakpoints(cx, breakpoints.clone());
use BreakpointsTailwind::*;
let sm_width = *breakpoints.get(&Sm).expect("It's there!");
let current = screen_size.current();
let xs = screen_size.lt(Sm);
let xse = screen_size.le(Sm);
let sm = screen_size.between(Sm, Md);
let md = screen_size.between(Md, Lg);
let lg = screen_size.between(Lg, Xl);
let xl = screen_size.between(Xl, Xxl);
let xxl = screen_size.ge(Xxl);
let label_classes = "justify-self-end".to_string();
let svg_classes = "align-middle ml-3 mr-1 opacity-60".to_string();
view! { cx,
<div class="grid grid-cols-2 gap-x-4 gap-y-3">
<div class=label_classes.clone()>"Current breakpoints :"</div>
<code>{move || format!("{:?}", current.get()) }</code>
<div class=label_classes.clone()>
<code class="font-bold">"xs"</code>
<small>
" (< "
{ move || sm_width.to_string() }
"px)"
</small>
<svg xmlns="http://www.w3.org/2000/svg" class=svg_classes.clone() width="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M6 5a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v14a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2v-14z" />
<path d="M11 4h2" />
<path d="M12 17v.01" />
</svg>
":"
</div>
<BooleanDisplay value=xs />
<div class=label_classes.clone()>
<code class="font-bold">"xs"</code>
<small>
" (<= "
{ move || sm_width.to_string() }
"px)"
</small>
<svg xmlns="http://www.w3.org/2000/svg" class=svg_classes.clone() width="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M6 5a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v14a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2v-14z" />
<path d="M11 4h2" />
<path d="M12 17v.01" />
</svg>
":"
</div>
<BooleanDisplay value=xse />
<div class=label_classes.clone()>
<code class="font-bold">"sm"</code>
<svg xmlns="http://www.w3.org/2000/svg" class=svg_classes.clone() width="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M3 6m0 2a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2z" />
<path d="M20 11v2" />
<path d="M7 12h-.01" />
</svg>
":"
</div>
<BooleanDisplay value=sm />
<div class=label_classes.clone()>
<code class="font-bold">"md"</code>
<svg xmlns="http://www.w3.org/2000/svg" class=svg_classes.clone() width="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M5 4a1 1 0 0 1 1 -1h12a1 1 0 0 1 1 1v16a1 1 0 0 1 -1 1h-12a1 1 0 0 1 -1 -1v-16z" />
<path d="M11 17a1 1 0 1 0 2 0a1 1 0 0 0 -2 0" />
</svg>
":"
</div>
<BooleanDisplay value=md />
<div class=label_classes.clone()>
<code class="font-bold">"lg"</code>
<svg xmlns="http://www.w3.org/2000/svg" class=svg_classes.clone() width="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M3 19l18 0" />
<path d="M5 6m0 1a1 1 0 0 1 1 -1h12a1 1 0 0 1 1 1v8a1 1 0 0 1 -1 1h-12a1 1 0 0 1 -1 -1z" />
</svg>
":"
</div>
<BooleanDisplay value=lg />
<div class=label_classes.clone()>
<code class="font-bold">"xl"</code>
<svg xmlns="http://www.w3.org/2000/svg" class=svg_classes.clone() width="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M3 5a1 1 0 0 1 1 -1h16a1 1 0 0 1 1 1v10a1 1 0 0 1 -1 1h-16a1 1 0 0 1 -1 -1v-10z" />
<path d="M7 20h10" />
<path d="M9 16v4" />
<path d="M15 16v4" />
</svg>
":"
</div>
<BooleanDisplay value=xl />
<div class=label_classes.clone()>
<code class="font-bold">"xxl"</code>
<svg xmlns="http://www.w3.org/2000/svg" class=svg_classes.clone() width="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M13 16h-9a1 1 0 0 1 -1 -1v-10a1 1 0 0 1 1 -1h16a1 1 0 0 1 1 1v5.5" />
<path d="M7 20h6.5" />
<path d="M9 16v4" />
<path d="M21 15h-2.5a1.5 1.5 0 0 0 0 3h1a1.5 1.5 0 0 1 0 3h-2.5" />
<path d="M19 21v1m0 -8v1" />
</svg>
":"
</div>
<BooleanDisplay value=xxl />
</div>
}
}
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 /> }
})
}

View file

@ -0,0 +1,359 @@
[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: ;
}
.col-span-2 {
grid-column: span 2 / span 2;
}
.mx-2 {
margin-left: 0.5rem;
margin-right: 0.5rem;
}
.ml-2 {
margin-left: 0.5rem;
}
.mr-1 {
margin-right: 0.25rem;
}
.ml-3 {
margin-left: 0.75rem;
}
.grid {
display: grid;
}
.grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.grid-cols-4 {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.gap-4 {
gap: 1rem;
}
.gap-x-4 {
-moz-column-gap: 1rem;
column-gap: 1rem;
}
.gap-y-2 {
row-gap: 0.5rem;
}
.gap-y-3 {
row-gap: 0.75rem;
}
.justify-self-end {
justify-self: end;
}
.align-middle {
vertical-align: middle;
}
.font-bold {
font-weight: 700;
}
.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;
}
.opacity-50 {
opacity: 0.5;
}
.opacity-70 {
opacity: 0.7;
}
.opacity-60 {
opacity: 0.6;
}
@media (prefers-color-scheme: dark) {
.dark\:text-green-500 {
--tw-text-opacity: 1;
color: rgb(34 197 94 / var(--tw-text-opacity));
}
}

View 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'),
],
}

View file

@ -8,6 +8,9 @@ pub mod docs;
pub mod math; pub mod math;
#[cfg(feature = "storage")] #[cfg(feature = "storage")]
pub mod storage; pub mod storage;
pub mod utils;
mod use_breakpoints;
mod use_debounce_fn; mod use_debounce_fn;
#[cfg(web_sys_unstable_apis)] #[cfg(web_sys_unstable_apis)]
mod use_element_size; mod use_element_size;
@ -22,12 +25,12 @@ mod use_resize_observer;
mod use_scroll; mod use_scroll;
mod use_supported; mod use_supported;
mod use_throttle_fn; mod use_throttle_fn;
pub mod utils;
mod watch; mod watch;
mod watch_debounced; mod watch_debounced;
mod watch_pausable; mod watch_pausable;
mod watch_throttled; mod watch_throttled;
pub use use_breakpoints::*;
pub use use_debounce_fn::*; pub use use_debounce_fn::*;
#[cfg(web_sys_unstable_apis)] #[cfg(web_sys_unstable_apis)]
pub use use_element_size::*; pub use use_element_size::*;
@ -46,5 +49,3 @@ pub use watch::*;
pub use watch_debounced::*; pub use watch_debounced::*;
pub use watch_pausable::*; pub use watch_pausable::*;
pub use watch_throttled::*; pub use watch_throttled::*;
extern crate self as leptos_use;

View file

@ -1,5 +1,5 @@
use crate::math::shared::use_simple_math;
use leptos::*; use leptos::*;
use leptos_use::math::shared::use_simple_math;
use num::Float; use num::Float;
use paste::paste; use paste::paste;

View file

@ -1,5 +1,5 @@
use crate::math::shared::use_simple_math;
use leptos::*; use leptos::*;
use leptos_use::math::shared::use_simple_math;
use num::Float; use num::Float;
use paste::paste; use paste::paste;

445
src/use_breakpoints.rs Normal file
View file

@ -0,0 +1,445 @@
use crate::use_media_query;
use leptos::*;
use paste::paste;
use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::Hash;
/// Reactive viewport breakpoints.
///
/// ## Demo
///
/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_breakpoints)
///
/// ## Usage
///
/// ```
/// # use leptos::*;
/// # use leptos_use::{use_breakpoints, BreakpointsTailwind, breakpoints_tailwind};
/// #
/// # #[component]
/// # fn Demo(cx: Scope) -> impl IntoView {
/// #
/// let screen_width = use_breakpoints(cx, breakpoints_tailwind());
///
/// use BreakpointsTailwind::*;
///
/// let sm_and_larger = screen_width.ge(Sm);
/// let larger_than_sm = screen_width.gt(Sm);
/// let lg_and_smaller = screen_width.le(Lg);
/// let smaller_than_lg = screen_width.lt(Lg);
/// #
/// # view! { cx, }
/// # }
/// ```
///
/// ## Breakpoints
///
/// There are many predefined breakpoints for major UI frameworks. The following are provided.
/// * [`breakpoints_tailwind`]
/// * [`breakpoints_bootstrap_v5`]
/// * [`breakpoints_material`]
/// * [`breakpoints_ant_design`]
/// * [`breakpoints_quasar`]
/// * [`breakpoints_semantic`]
/// * [`breakpoints_master_css`]
///
/// You can also provide your own breakpoints.
///
/// ```
/// # use std::collections::HashMap;
/// use leptos::*;
/// # use leptos_use::use_breakpoints;
/// #
/// #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
/// enum MyBreakpoints {
/// Tablet,
/// Laptop,
/// Desktop,
/// }
///
/// fn my_breakpoints() -> HashMap<MyBreakpoints, u32> {
/// use MyBreakpoints::*;
///
/// HashMap::from([
/// (Tablet, 640),
/// (Laptop, 1024),
/// (Desktop, 1280),
/// ])
/// }
///
/// #[component]
/// fn Demo(cx: Scope) -> impl IntoView {
/// let screen_width = use_breakpoints(cx, my_breakpoints());
///
/// use MyBreakpoints::*;
///
/// let laptop = screen_width.between(Laptop, Desktop);
///
/// view! { cx, }
/// }
/// ```
///
/// ## Non-reactive methods
///
/// For every reactive method there is also a non-reactive variant that is prefixed with `is_`
///
/// ```
/// # use leptos::*;
/// # use leptos_use::{use_breakpoints, BreakpointsTailwind, breakpoints_tailwind};
/// #
/// # #[component]
/// # fn Demo(cx: Scope) -> impl IntoView {
/// #
/// let screen_width = use_breakpoints(cx, breakpoints_tailwind());
///
/// use BreakpointsTailwind::*;
///
/// let sm_and_larger = screen_width.is_ge(Sm);
/// let larger_than_sm = screen_width.is_gt(Sm);
/// let lg_and_smaller = screen_width.is_le(Lg);
/// let smaller_than_lg = screen_width.is_lt(Lg);
/// #
/// # view! { cx, }
/// # }
/// ```
pub fn use_breakpoints<K: Eq + Hash + Debug + Clone>(
cx: Scope,
breakpoints: HashMap<K, u32>,
) -> UseBreakpointsReturn<K> {
UseBreakpointsReturn { cx, breakpoints }
}
/// Return type of [`use_breakpoints`]
#[derive(Clone)]
pub struct UseBreakpointsReturn<K: Eq + Hash + Debug + Clone> {
cx: Scope,
breakpoints: HashMap<K, u32>,
}
macro_rules! query_suffix {
(>) => {
".1"
};
(<) => {
".9"
};
(=) => {
""
};
}
macro_rules! value_expr {
($v:ident, >) => {
$v
};
($v:ident, <) => {
$v - 1
};
($v:ident, =) => {
$v
};
}
macro_rules! format_media_query {
($cmp:tt, $suffix:tt, $v:ident) => {
format!(
"({}-width: {}{}px)",
$cmp,
value_expr!($v, $suffix),
query_suffix!($suffix)
)
};
}
macro_rules! impl_cmp_reactively {
( #[$attr:meta]
$fn:ident, $cmp:tt, $suffix:tt) => {
paste! {
// Reactive check if
#[$attr]
pub fn $fn(&self, key: K) -> Signal<bool> {
if let Some(value) = self.breakpoints.get(&key) {
use_media_query(self.cx, format_media_query!($cmp, $suffix, value))
} else {
self.not_found_signal(key)
}
}
// Static check if
#[$attr]
pub fn [<is_ $fn>](&self, key: K) -> bool {
if let Some(value) = self.breakpoints.get(&key) {
Self::match_(&format_media_query!($cmp, $suffix, value))
} else {
self.not_found(key)
}
}
}
};
}
impl<K: Eq + Hash + Debug + Clone> UseBreakpointsReturn<K> {
fn match_(query: &str) -> bool {
if let Ok(Some(query_list)) = window().match_media(query) {
return query_list.matches();
}
false
}
fn not_found_signal(&self, key: K) -> Signal<bool> {
error!("Breakpoint \"{:?}\" not found", key);
Signal::derive(self.cx, || false)
}
fn not_found(&self, key: K) -> bool {
error!("Breakpoint \"{:?}\" not found", key);
false
}
impl_cmp_reactively!(
/// `[screen size]` > `key`
gt, "min", >
);
impl_cmp_reactively!(
/// `[screen size]` >= `key`
ge, "min", =
);
impl_cmp_reactively!(
/// `[screen size]` < `key`
lt, "max", <
);
impl_cmp_reactively!(
/// `[screen size]` <= `key`
le, "max", =
);
fn between_media_query(min: &u32, max: &u32) -> String {
format!("(min-width: {min}px) and (max-width: {}.9px)", max - 1)
}
/// Reactive check if `min_key` <= `[screen size]` <= `max_key`
pub fn between(&self, min_key: K, max_key: K) -> Signal<bool> {
if let Some(min) = self.breakpoints.get(&min_key) {
if let Some(max) = self.breakpoints.get(&max_key) {
use_media_query(self.cx, Self::between_media_query(min, max))
} else {
self.not_found_signal(max_key)
}
} else {
self.not_found_signal(min_key)
}
}
/// Static check if `min_key` <= `[screen size]` <= `max_key`
pub fn is_between(&self, min_key: K, max_key: K) -> bool {
if let Some(min) = self.breakpoints.get(&min_key) {
if let Some(max) = self.breakpoints.get(&max_key) {
Self::match_(&Self::between_media_query(min, max))
} else {
self.not_found(max_key)
}
} else {
self.not_found(min_key)
}
}
/// Reactive Vec of all breakpoints that fulfill `[screen size]` >= `key`
pub fn current(&self) -> Signal<Vec<K>> {
let this = self.clone();
Signal::derive(self.cx, move || {
this.breakpoints
.keys()
.filter(|k| this.ge((**k).clone()).get())
.cloned()
.collect::<Vec<_>>()
})
}
}
/// Breakpoint keys for Tailwind V2
///
/// See https://tailwindcss.com/docs/breakpoints
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BreakpointsTailwind {
Sm,
Md,
Lg,
Xl,
Xxl,
}
/// Breakpoint definitions for Tailwind V2
///
/// See https://tailwindcss.com/docs/breakpoints
pub fn breakpoints_tailwind() -> HashMap<BreakpointsTailwind, u32> {
HashMap::from([
(BreakpointsTailwind::Sm, 640),
(BreakpointsTailwind::Md, 768),
(BreakpointsTailwind::Lg, 1024),
(BreakpointsTailwind::Xl, 1280),
(BreakpointsTailwind::Xxl, 1536),
])
}
/// Breakpoint keys for Bootstrap V5
///
/// See https://getbootstrap.com/docs/5.0/layout/breakpoints
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BreakpointsBootstrapV5 {
Sm,
Md,
Lg,
Xl,
Xxl,
}
/// Breakpoint definitions for Bootstrap V5
///
/// See https://getbootstrap.com/docs/5.0/layout/breakpoints
pub fn breakpoints_bootstrap_v5() -> HashMap<BreakpointsBootstrapV5, u32> {
HashMap::from([
(BreakpointsBootstrapV5::Sm, 576),
(BreakpointsBootstrapV5::Md, 768),
(BreakpointsBootstrapV5::Lg, 992),
(BreakpointsBootstrapV5::Xl, 1200),
(BreakpointsBootstrapV5::Xxl, 1400),
])
}
/// Breakpoint keys for Material UI V5
///
/// See https://mui.com/material-ui/customization/breakpoints/
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BreakpointsMaterial {
Xs,
Sm,
Md,
Lg,
Xl,
}
/// Breakpoint definitions for Material UI V5
///
/// See https://mui.com/material-ui/customization/breakpoints/
pub fn breakpoints_material() -> HashMap<BreakpointsMaterial, u32> {
HashMap::from([
(BreakpointsMaterial::Xs, 1),
(BreakpointsMaterial::Sm, 600),
(BreakpointsMaterial::Md, 900),
(BreakpointsMaterial::Lg, 1200),
(BreakpointsMaterial::Xl, 1536),
])
}
/// Breakpoint keys for Ant Design
///
/// See https://ant.design/components/layout/#breakpoint-width
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BreakpointsAntDesign {
Xs,
Sm,
Md,
Lg,
Xl,
Xxl,
}
/// Breakpoint definitions for Ant Design
///
/// See https://ant.design/components/layout/#breakpoint-width
pub fn breakpoints_ant_design() -> HashMap<BreakpointsAntDesign, u32> {
HashMap::from([
(BreakpointsAntDesign::Xs, 480),
(BreakpointsAntDesign::Sm, 576),
(BreakpointsAntDesign::Md, 768),
(BreakpointsAntDesign::Lg, 992),
(BreakpointsAntDesign::Xl, 1200),
(BreakpointsAntDesign::Xxl, 1600),
])
}
/// Breakpoint keys for Quasar V2
///
/// See https://quasar.dev/style/breakpoints
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BreakpointsQuasar {
Xs,
Sm,
Md,
Lg,
Xl,
}
/// Breakpoint definitions for Quasar V2
///
/// See https://quasar.dev/style/breakpoints
pub fn breakpoints_quasar() -> HashMap<BreakpointsQuasar, u32> {
HashMap::from([
(BreakpointsQuasar::Xs, 1),
(BreakpointsQuasar::Sm, 600),
(BreakpointsQuasar::Md, 1024),
(BreakpointsQuasar::Lg, 1440),
(BreakpointsQuasar::Xl, 1920),
])
}
/// Breakpoint keys for Sematic UI
///
/// See https://semantic-ui.com/elements/container.html
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BreakpointsSematic {
Mobile,
Tablet,
SmallMonitor,
LargeMonitor,
}
/// Breakpoint definitions for Sematic UI
///
/// See https://semantic-ui.com/elements/container.html
pub fn breakpoints_sematic() -> HashMap<BreakpointsSematic, u32> {
HashMap::from([
(BreakpointsSematic::Mobile, 1),
(BreakpointsSematic::Tablet, 768),
(BreakpointsSematic::SmallMonitor, 992),
(BreakpointsSematic::LargeMonitor, 1200),
])
}
/// Breakpoint keys for Master CSS
///
/// See https://docs.master.co/css/breakpoints
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BreakpointsMasterCss {
Xxxs,
Xxs,
Xs,
Sm,
Md,
Lg,
Xl,
Xxl,
Xxxl,
Xxxxl,
}
/// Breakpoint definitions for Master CSS
///
/// See https://docs.master.co/css/breakpoints
pub fn breakpoints_master_css() -> HashMap<BreakpointsMasterCss, u32> {
HashMap::from([
(BreakpointsMasterCss::Xxxs, 360),
(BreakpointsMasterCss::Xxs, 480),
(BreakpointsMasterCss::Xs, 600),
(BreakpointsMasterCss::Sm, 768),
(BreakpointsMasterCss::Md, 1024),
(BreakpointsMasterCss::Lg, 1280),
(BreakpointsMasterCss::Xl, 1440),
(BreakpointsMasterCss::Xxl, 1600),
(BreakpointsMasterCss::Xxxl, 1920),
(BreakpointsMasterCss::Xxxxl, 2560),
])
}

View file

@ -1,8 +1,8 @@
use crate::core::{ElementMaybeSignal, Size}; use crate::core::{ElementMaybeSignal, Size};
use crate::{use_resize_observer_with_options, UseResizeObserverOptions}; use crate::{use_resize_observer_with_options, UseResizeObserverOptions};
use crate::{watch_with_options, WatchOptions};
use default_struct_builder::DefaultBuilder; use default_struct_builder::DefaultBuilder;
use leptos::*; use leptos::*;
use leptos_use::{watch_with_options, WatchOptions};
use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::JsCast; use wasm_bindgen::JsCast;

View file

@ -1,5 +1,5 @@
use crate::use_media_query;
use leptos::*; use leptos::*;
use leptos_use::use_media_query;
use std::fmt::Display; use std::fmt::Display;
/// Reactive [prefers-contrast](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-contrast) media query. /// Reactive [prefers-contrast](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-contrast) media query.

View file

@ -1,5 +1,5 @@
use crate::use_media_query;
use leptos::*; use leptos::*;
use leptos_use::use_media_query;
/// Reactive [dark theme preference](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme). /// Reactive [dark theme preference](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme).
/// ///

View file

@ -1,6 +1,6 @@
use crate::{watch_with_options, DebounceOptions, WatchOptions};
use default_struct_builder::DefaultBuilder; use default_struct_builder::DefaultBuilder;
use leptos::*; use leptos::*;
use leptos_use::{watch_with_options, DebounceOptions, WatchOptions};
/// A debounced version of [`watch`]. /// A debounced version of [`watch`].
/// ///

View file

@ -1,6 +1,6 @@
use crate::{watch_with_options, ThrottleOptions, WatchOptions};
use default_struct_builder::DefaultBuilder; use default_struct_builder::DefaultBuilder;
use leptos::*; use leptos::*;
use leptos_use::{watch_with_options, ThrottleOptions, WatchOptions};
/// A throttled version of [`watch`]. /// A throttled version of [`watch`].
/// ///