Compare commits

..

1 commit

Author SHA1 Message Date
Adam
aa818b2823 fix: leptos 0.7-beta5 changes method trigger to notify 2024-09-08 22:54:00 -04:00
31 changed files with 460 additions and 612 deletions

View file

@ -1,16 +1,3 @@
## [0.3.4](https://github.com/thaw-ui/thaw/compare/v0.3.3...v0.3.4) (2024-09-11)
### Features
* Adds `Flex` component.
* Update leptos to v0.6.14.
### Bug Fixs
* Allow creating custom theme [#242](https://github.com/thaw-ui/thaw/pull/242).
* `CheckboxGroup` does not display default value.
* `Binder` component display position.
## [0.4.0-beta2](https://github.com/thaw-ui/thaw/compare/v0.4.0-beta...v0.4.0-beta2) (2024-09-02) ## [0.4.0-beta2](https://github.com/thaw-ui/thaw/compare/v0.4.0-beta...v0.4.0-beta2) (2024-09-02)
### Features ### Features

View file

@ -16,6 +16,6 @@ thaw_components = { version = "0.2.0-beta2", path = "./thaw_components" }
thaw_macro = { version = "0.1.0-beta2", path = "./thaw_macro" } thaw_macro = { version = "0.1.0-beta2", path = "./thaw_macro" }
thaw_utils = { version = "0.1.0-beta2", path = "./thaw_utils" } thaw_utils = { version = "0.1.0-beta2", path = "./thaw_utils" }
leptos = { version = "0.7.0-beta5" } leptos = { version = "0.7.0-beta4" }
leptos_meta = { version = "0.7.0-beta5" } leptos_meta = { version = "0.7.0-beta4" }
leptos_router = { version = "0.7.0-beta5" } leptos_router = { version = "0.7.0-beta4" }

View file

@ -59,59 +59,66 @@ fn TheRouter() -> impl IntoView {
<Route path=path!("/development/components") view=DevelopmentComponentsMdPage/> <Route path=path!("/development/components") view=DevelopmentComponentsMdPage/>
</ParentRoute> </ParentRoute>
<ParentRoute path=path!("/components") view=ComponentsPage> <ParentRoute path=path!("/components") view=ComponentsPage>
<Route path=path!("/accordion") view=AccordionMdPage/> {view!{
<Route path=path!("/anchor") view=AnchorMdPage/> <Route path=path!("/accordion") view=AccordionMdPage/>
<Route path=path!("/auto-complete") view=AutoCompleteMdPage/> <Route path=path!("/anchor") view=AnchorMdPage/>
<Route path=path!("/avatar") view=AvatarMdPage/> <Route path=path!("/auto-complete") view=AutoCompleteMdPage/>
<Route path=path!("/back-top") view=BackTopMdPage/> <Route path=path!("/avatar") view=AvatarMdPage/>
<Route path=path!("/badge") view=BadgeMdPage/> <Route path=path!("/back-top") view=BackTopMdPage/>
<Route path=path!("/breadcrumb") view=BreadcrumbMdPage/> <Route path=path!("/badge") view=BadgeMdPage/>
<Route path=path!("/button") view=ButtonMdPage/> <Route path=path!("/breadcrumb") view=BreadcrumbMdPage/>
<Route path=path!("/calendar") view=CalendarMdPage/> <Route path=path!("/button") view=ButtonMdPage/>
<Route path=path!("/card") view=CardMdPage/> <Route path=path!("/calendar") view=CalendarMdPage/>
<Route path=path!("/checkbox") view=CheckboxMdPage/> <Route path=path!("/card") view=CardMdPage/>
<Route path=path!("/color-picker") view=ColorPickerMdPage/> <Route path=path!("/checkbox") view=CheckboxMdPage/>
<Route path=path!("/combobox") view=ComboboxMdPage/> <Route path=path!("/color-picker") view=ColorPickerMdPage/>
<Route path=path!("/config-provider") view=ConfigProviderMdPage/> <Route path=path!("/combobox") view=ComboboxMdPage/>
<Route path=path!("/date-picker") view=DatePickerMdPage/> <Route path=path!("/config-provider") view=ConfigProviderMdPage/>
<Route path=path!("/dialog") view=DialogMdPage/> }.into_inner()}
<Route path=path!("/divider") view=DividerMdPage/> {view!{
<Route path=path!("/drawer") view=DrawerMdPage/> <Route path=path!("/date-picker") view=DatePickerMdPage/>
<Route path=path!("/field") view=FieldMdPage/> <Route path=path!("/dialog") view=DialogMdPage/>
<Route path=path!("/flex") view=FlexMdPage/> <Route path=path!("/divider") view=DividerMdPage/>
<Route path=path!("/menu") view=MenuMdPage/> <Route path=path!("/drawer") view=DrawerMdPage/>
<Route path=path!("/grid") view=GridMdPage/> <Route path=path!("/field") view=FieldMdPage/>
<Route path=path!("/icon") view=IconMdPage/> <Route path=path!("/menu") view=MenuMdPage/>
<Route path=path!("/image") view=ImageMdPage/> <Route path=path!("/grid") view=GridMdPage/>
<Route path=path!("/input") view=InputMdPage/> <Route path=path!("/icon") view=IconMdPage/>
<Route path=path!("/layout") view=LayoutMdPage/> <Route path=path!("/image") view=ImageMdPage/>
<Route path=path!("/link") view=LinkMdPage/> <Route path=path!("/input") view=InputMdPage/>
<Route path=path!("/loading-bar") view=LoadingBarMdPage/> <Route path=path!("/layout") view=LayoutMdPage/>
<Route path=path!("/message-bar") view=MessageBarMdPage/> <Route path=path!("/link") view=LinkMdPage/>
<Route path=path!("/nav") view=NavMdPage/> <Route path=path!("/loading-bar") view=LoadingBarMdPage/>
<Route path=path!("/pagination") view=PaginationMdPage/> <Route path=path!("/message-bar") view=MessageBarMdPage/>
<Route path=path!("/popover") view=PopoverMdPage/> <Route path=path!("/nav") view=NavMdPage/>
<Route path=path!("/progress-bar") view=ProgressBarMdPage/> }.into_inner()}
<Route path=path!("/radio") view=RadioMdPage/> {view!{
<Route path=path!("/scrollbar") view=ScrollbarMdPage/> <Route path=path!("/pagination") view=PaginationMdPage/>
<Route path=path!("/select") view=SelectMdPage/> <Route path=path!("/popover") view=PopoverMdPage/>
<Route path=path!("/skeleton") view=SkeletonMdPage/> <Route path=path!("/progress-bar") view=ProgressBarMdPage/>
<Route path=path!("/slider") view=SliderMdPage/> <Route path=path!("/radio") view=RadioMdPage/>
<Route path=path!("/space") view=SpaceMdPage/> <Route path=path!("/scrollbar") view=ScrollbarMdPage/>
<Route path=path!("/spin-button") view=SpinButtonMdPage/> <Route path=path!("/select") view=SelectMdPage/>
<Route path=path!("/spinner") view=SpinnerMdPage/> <Route path=path!("/skeleton") view=SkeletonMdPage/>
<Route path=path!("/switch") view=SwitchMdPage/> <Route path=path!("/slider") view=SliderMdPage/>
<Route path=path!("/tab-list") view=TabListMdPage/> <Route path=path!("/space") view=SpaceMdPage/>
<Route path=path!("/table") view=TableMdPage/> <Route path=path!("/spin-button") view=SpinButtonMdPage/>
<Route path=path!("/tag") view=TagMdPage/> <Route path=path!("/spinner") view=SpinnerMdPage/>
<Route path=path!("/tag-group") view=TagGroupMdPage/> <Route path=path!("/switch") view=SwitchMdPage/>
<Route path=path!("/tag-picker") view=TagPickerMdPage/> <Route path=path!("/tab-list") view=TabListMdPage/>
<Route path=path!("/text") view=TextMdPage/> <Route path=path!("/table") view=TableMdPage/>
<Route path=path!("/textarea") view=TextareaMdPage/> }.into_inner()}
<Route path=path!("/time-picker") view=TimePickerMdPage/> {view!{
<Route path=path!("/toast") view=ToastMdPage /> <Route path=path!("/tag") view=TagMdPage/>
<Route path=path!("/tooltip") view=TooltipMdPage /> <Route path=path!("/tag-group") view=TagGroupMdPage/>
<Route path=path!("/upload") view=UploadMdPage/> <Route path=path!("/tag-picker") view=TagPickerMdPage/>
<Route path=path!("/text") view=TextMdPage/>
<Route path=path!("/textarea") view=TextareaMdPage/>
<Route path=path!("/time-picker") view=TimePickerMdPage/>
<Route path=path!("/toast") view=ToastMdPage />
<Route path=path!("/tooltip") view=TooltipMdPage />
<Route path=path!("/upload") view=UploadMdPage/>
}.into_inner()}
</ParentRoute> </ParentRoute>
</Routes> </Routes>
</Router> </Router>

View file

@ -272,11 +272,6 @@ pub(crate) fn gen_nav_data() -> Vec<NavGroupOption> {
value: "/components/field", value: "/components/field",
label: "Field", label: "Field",
}, },
NavItemOption {
group: None,
value: "/components/flex",
label: "Flex",
},
NavItemOption { NavItemOption {
group: None, group: None,
value: "/components/grid", value: "/components/grid",

View file

@ -42,7 +42,6 @@ pub fn include_md(_token_stream: proc_macro::TokenStream) -> proc_macro::TokenSt
"DividerMdPage" => "../../thaw/src/divider/docs/mod.md", "DividerMdPage" => "../../thaw/src/divider/docs/mod.md",
"DrawerMdPage" => "../../thaw/src/drawer/docs/mod.md", "DrawerMdPage" => "../../thaw/src/drawer/docs/mod.md",
"FieldMdPage" => "../../thaw/src/field/docs/mod.md", "FieldMdPage" => "../../thaw/src/field/docs/mod.md",
"FlexMdPage" => "../../thaw/src/flex/docs/mod.md",
"GridMdPage" => "../../thaw/src/grid/docs/mod.md", "GridMdPage" => "../../thaw/src/grid/docs/mod.md",
"IconMdPage" => "../../thaw/src/icon/docs/mod.md", "IconMdPage" => "../../thaw/src/icon/docs/mod.md",
"ImageMdPage" => "../../thaw/src/image/docs/mod.md", "ImageMdPage" => "../../thaw/src/image/docs/mod.md",

View file

@ -9,10 +9,10 @@ crate-type = ["cdylib", "rlib"]
[dependencies] [dependencies]
axum = { version = "0.7", optional = true } axum = { version = "0.7", optional = true }
console_error_panic_hook = "0.1" console_error_panic_hook = "0.1"
leptos = { version = "0.7.0-beta5", features = ["experimental-islands"] } leptos = { version = "0.7.0-beta4", features = ["experimental-islands"] }
leptos_axum = { version = "0.7.0-beta5", optional = true } leptos_axum = { version = "0.7.0-beta4", optional = true }
leptos_meta = { version = "0.7.0-beta5" } leptos_meta = { version = "0.7.0-beta4" }
leptos_router = { version = "0.7.0-beta5" } leptos_router = { version = "0.7.0-beta4" }
tokio = { version = "1", features = ["rt-multi-thread"], optional = true } tokio = { version = "1", features = ["rt-multi-thread"], optional = true }
tower = { version = "0.4", optional = true } tower = { version = "0.4", optional = true }
tower-http = { version = "0.5", features = ["fs"], optional = true } tower-http = { version = "0.5", features = ["fs"], optional = true }

View file

@ -9,10 +9,10 @@ crate-type = ["cdylib", "rlib"]
[dependencies] [dependencies]
axum = { version = "0.7.5", optional = true } axum = { version = "0.7.5", optional = true }
console_error_panic_hook = "0.1" console_error_panic_hook = "0.1"
leptos = { version = "0.7.0-beta5" } leptos = { version = "0.7.0-beta4" }
leptos_axum = { version = "0.7.0-beta5", optional = true } leptos_axum = { version = "0.7.0-beta4", optional = true }
leptos_meta = { version = "0.7.0-beta5" } leptos_meta = { version = "0.7.0-beta4" }
leptos_router = { version = "0.7.0-beta5" } leptos_router = { version = "0.7.0-beta4" }
tokio = { version = "1", features = ["rt-multi-thread"], optional = true } tokio = { version = "1", features = ["rt-multi-thread"], optional = true }
tower = { version = "0.5.0", optional = true } tower = { version = "0.5.0", optional = true }
tower-http = { version = "0.5", features = ["fs"], optional = true } tower-http = { version = "0.5", features = ["fs"], optional = true }

View file

@ -2,7 +2,7 @@ use crate::{ConfigInjection, Icon};
use leptos::{either::Either, ev, html, prelude::*}; use leptos::{either::Either, ev, html, prelude::*};
use thaw_components::{CSSTransition, Teleport}; use thaw_components::{CSSTransition, Teleport};
use thaw_utils::{ use thaw_utils::{
add_event_listener, class_list, get_scroll_parent_element, mount_style, BoxCallback, add_event_listener, class_list, get_scroll_parent, mount_style, BoxCallback,
EventListenerHandle, EventListenerHandle,
}; };
@ -43,7 +43,7 @@ pub fn BackTop(
}; };
request_animation_frame(move || { request_animation_frame(move || {
let scroll_el = get_scroll_parent_element(&placeholder_el) let scroll_el = get_scroll_parent(&placeholder_el)
.unwrap_or_else(|| document().document_element().unwrap()); .unwrap_or_else(|| document().document_element().unwrap());
{ {

View file

@ -55,7 +55,7 @@ pub fn Combobox(
let Some(clear_icon_el) = clear_icon_ref.get() else { let Some(clear_icon_el) = clear_icon_ref.get() else {
return; return;
}; };
let handler = add_event_listener(clear_icon_el, ev::click, move |e| { let handler = add_event_listener(clear_icon_el.into(), ev::click, move |e| {
if disabled.get_untracked() { if disabled.get_untracked() {
return; return;
} }

View file

@ -1,110 +0,0 @@
# Flex
```rust demo
view! {
<Flex>
<Button>"1"</Button>
<Button>"2"</Button>
<Button>"3"</Button>
</Flex>
<Flex>
<For
each=move || 0..4
key=move |i| i.clone()
let:i
>
<Button>{i}</Button>
</For>
</Flex>
}
```
### Vertical
```rust demo
view! {
<Flex vertical=true>
<Button>"1"</Button>
<Button>"2"</Button>
<Button>"3"</Button>
</Flex>
}
```
### Gap
```rust demo
view! {
<Flex gap=FlexGap::Large>
<Button>"1"</Button>
<Button>"2"</Button>
<Button>"3"</Button>
</Flex>
<Flex gap=FlexGap::WH(36, 36)>
<Button>"1"</Button>
<Button>"2"</Button>
<Button>"3"</Button>
</Flex>
}
```
### Align
```rust demo
view! {
<Flex vertical=true>
<Flex align=FlexAlign::Start>
<Button attr:style="height: 60px">"Start"</Button>
<Button attr:style="height: 40px">"Start"</Button>
<Button>"Start"</Button>
</Flex>
<Flex align=FlexAlign::Center>
<Button attr:style="height: 60px">"Center"</Button>
<Button attr:style="height: 40px">"Center"</Button>
<Button>"Center"</Button>
</Flex>
<Flex align=FlexAlign::End>
<Button attr:style="height: 60px">"End"</Button>
<Button attr:style="height: 40px">"End"</Button>
<Button>"End"</Button>
</Flex>
</Flex>
}
```
### Justify
```rust demo
view! {
<Flex vertical=true>
<Flex justify=FlexJustify::SpaceAround>
<Button>"SpaceAround"</Button>
<Button>"SpaceAround"</Button>
<Button>"SpaceAround"</Button>
</Flex>
<Flex justify=FlexJustify::Center>
<Button>"Center"</Button>
<Button>"Center"</Button>
<Button>"Center"</Button>
</Flex>
<Flex justify=FlexJustify::End>
<Button>"End"</Button>
<Button>"End"</Button>
<Button>"End"</Button>
</Flex>
</Flex>
}
```
### Flex Props
| Name | Type | Default | Description |
| -------- | ------------------------ | -------------------- | -------------------------------------- |
| class | `MaybeProp<String>` | `Default::default()` | |
| style | `MaybeProp<String>` | `Default::default()` | |
| inline | `MaybeSignal<bool>` | `false` | Whether it's display is `inline-flex`. |
| vertical | `bool` | `false` | Whether to lay out vertically. |
| gap | `FlexGap` | `FlexGap::Medium` | Flex's gap. |
| align | `MaybeProp<FlexAlign>` | `None` | Vertical arrangement. |
| justify | `MaybeProp<FlexJustify>` | `None` | Horizontal arrangement. |
| children | `Children` | | |

View file

@ -1,130 +0,0 @@
use leptos::prelude::*;
use thaw_utils::class_list;
#[component]
pub fn Flex(
#[prop(optional, into)] class: MaybeProp<String>,
#[prop(optional, into)] style: MaybeProp<String>,
/// Flex's gap.
#[prop(optional)]
gap: FlexGap,
/// Whether to lay out vertically.
#[prop(optional)]
vertical: bool,
/// Whether it's display is `inline-flex`.
#[prop(optional, into)]
inline: MaybeSignal<bool>,
/// Vertical arrangement.
#[prop(optional, into)]
align: MaybeProp<FlexAlign>,
/// Horizontal arrangement.
#[prop(optional, into)]
justify: MaybeProp<FlexJustify>,
children: Children,
) -> impl IntoView {
let style = Memo::new(move |_| {
let mut s = String::new();
let display = if inline.get() {
"display: inline-flex;"
} else {
"display: flex;"
};
s.push_str(display);
let direction = if vertical {
"flex-direction: column;"
} else {
"flex-direction: row;"
};
let gap = match gap {
FlexGap::Small => "gap: 4px 8px;",
FlexGap::Medium => "gap: 8px 12px;",
FlexGap::Large => "gap: 12px 16px;",
FlexGap::Size(size) => &format!("gap: {size}px {size}px;"),
FlexGap::WH(width, height) => &format!("gap: {width}px {height}px;"),
};
s.push_str(direction);
s.push_str(gap);
if let Some(align) = align.get() {
s.push_str(&format!("align-items: {};", align.as_str()));
}
if let Some(justify) = justify.get() {
s.push_str(&format!("justify-content: {};", justify.as_str()));
}
style.with(|style| {
if let Some(style) = style.as_ref() {
s.push_str(style);
}
});
s
});
view! {
<div class=class_list!["thaw-flex", class] style=move || style.get()>
{children()}
</div>
}
}
#[derive(Default)]
pub enum FlexGap {
Small,
#[default]
Medium,
Large,
Size(u16),
/// width and height
WH(u16, u16),
}
#[derive(Clone)]
pub enum FlexAlign {
FlexStart,
FlexEnd,
Start,
End,
Center,
Baseline,
Stretch,
}
impl FlexAlign {
fn as_str(&self) -> &'static str {
match self {
Self::FlexStart => "flex-start",
Self::FlexEnd => "flex-end",
Self::Start => "start",
Self::End => "end",
Self::Center => "center",
Self::Baseline => "baseline",
Self::Stretch => "stretch",
}
}
}
#[derive(Clone)]
pub enum FlexJustify {
FlexStart,
FlexEnd,
Start,
End,
Center,
SpaceAround,
SpaceBetween,
SpaceEvenly,
}
impl FlexJustify {
fn as_str(&self) -> &'static str {
match self {
Self::FlexStart => "flex-start",
Self::FlexEnd => "flex-end",
Self::Start => "start",
Self::End => "end",
Self::Center => "center",
Self::SpaceAround => "space-around",
Self::SpaceBetween => "space-between",
Self::SpaceEvenly => "space-evenly",
}
}
}

View file

@ -1,3 +0,0 @@
mod flex;
pub use flex::*;

View file

@ -19,7 +19,6 @@ mod dialog;
mod divider; mod divider;
mod drawer; mod drawer;
mod field; mod field;
mod flex;
mod grid; mod grid;
mod icon; mod icon;
mod image; mod image;
@ -74,7 +73,6 @@ pub use dialog::*;
pub use divider::*; pub use divider::*;
pub use drawer::*; pub use drawer::*;
pub use field::*; pub use field::*;
pub use flex::*;
pub use grid::*; pub use grid::*;
pub use icon::*; pub use icon::*;
pub use image::*; pub use image::*;

View file

@ -77,4 +77,3 @@ view! {
| disabled | `MaybeSignal<bool>` | `false` | Whether the link is disabled. | | disabled | `MaybeSignal<bool>` | `false` | Whether the link is disabled. |
| disabled_focusable | `MaybeSignal<bool>` | `false` | When set, allows the link to be focusable even when it has been disabled. | | disabled_focusable | `MaybeSignal<bool>` | `false` | When set, allows the link to be focusable even when it has been disabled. |
| children | `Children` | | | | children | `Children` | | |
| target | `MaybeSignal<String>` | | Specifies where to open the linked document. |

View file

@ -16,9 +16,6 @@ pub fn Link(
#[prop(optional, into)] #[prop(optional, into)]
disabled_focusable: MaybeSignal<bool>, disabled_focusable: MaybeSignal<bool>,
children: Children, children: Children,
/// Specifies where to open the linked document.
#[prop(optional, into)]
target: MaybeSignal<String>,
) -> impl IntoView { ) -> impl IntoView {
mount_style("link", include_str!("./link.css")); mount_style("link", include_str!("./link.css"));
@ -49,7 +46,6 @@ pub fn Link(
href=href href=href
tabindex=tabindex tabindex=tabindex
aria-disabled=move || link_disabled.get().then_some("true") aria-disabled=move || link_disabled.get().then_some("true")
target=target
> >
{children()} {children()}
</a> </a>

View file

@ -80,7 +80,7 @@ pub fn Menu(
let Some(target_el) = target_ref.get() else { let Some(target_el) = target_ref.get() else {
return; return;
}; };
let handler = add_event_listener(target_el, ev::click, move |event| { let handler = add_event_listener(target_el.into(), ev::click, move |event| {
if trigger_type != MenuTriggerType::Click { if trigger_type != MenuTriggerType::Click {
return; return;
} }

View file

@ -99,7 +99,7 @@ pub fn Popover(
let Some(target_el) = target_ref.get() else { let Some(target_el) = target_ref.get() else {
return; return;
}; };
let handler = add_event_listener(target_el, ev::click, move |event| { let handler = add_event_listener(target_el.into(), ev::click, move |event| {
if trigger_type != PopoverTriggerType::Click { if trigger_type != PopoverTriggerType::Click {
return; return;
} }

View file

@ -23,22 +23,10 @@ view! {
} }
``` ```
### Custom label
```rust demo
view! {
<Spinner label="Label"/>
<Spinner>
<b style="color: blue">"Label"</b>
</Spinner>
}
```
### Spinner Props ### Spinner Props
| Name | Type | Default | Description | | Name | Type | Default | Description |
| -------- | -------------------------- | --------------------- | ---------------------------------- | | ----- | -------------------------- | --------------------- | ---------------------------------- |
| class | `MaybeProp<String>` | `Default::default()` | | | class | `MaybeProp<String>` | `Default::default()` | |
| label | `MaybeProp<String>` | `Default::default()` | An optional label for the Spinner. | | label | `MaybeProp<String>` | `Default::default()` | An optional label for the Spinner. |
| size | `MaybeSignal<SpinnerSize>` | `SpinnerSize::Medium` | The size of the spinner. | | size | `MaybeSignal<SpinnerSize>` | `SpinnerSize::Medium` | The size of the spinner. |
| children | `Option<Children>` | `None` | |

View file

@ -38,16 +38,14 @@ pub fn Spinner(
/// The size of the spinner. /// The size of the spinner.
#[prop(optional, into)] #[prop(optional, into)]
size: MaybeSignal<SpinnerSize>, size: MaybeSignal<SpinnerSize>,
#[prop(optional)] children: Option<Children>,
) -> impl IntoView { ) -> impl IntoView {
mount_style("spinner", include_str!("./spinner.css")); mount_style("spinner", include_str!("./spinner.css"));
let id = StoredValue::new(uuid::Uuid::new_v4().to_string()); let id = StoredValue::new(uuid::Uuid::new_v4().to_string());
let spinner_label = label.clone(); let spinner_label = label.clone();
let children_flag = children.is_some();
let labelledby = move || { let labelledby = move || {
spinner_label.with(|label| { spinner_label.with(|label| {
if label.is_some() || children_flag { if label.is_some() {
Some(id.get_value()) Some(id.get_value())
} else { } else {
None None
@ -68,29 +66,17 @@ pub fn Spinner(
<span class="thaw-spinner__spinner"> <span class="thaw-spinner__spinner">
<span class="thaw-spinner__spinner-tail"></span> <span class="thaw-spinner__spinner-tail"></span>
</span> </span>
{if let Some(children) = children { {move || {
view! { if let Some(label) = label.get() {
<label class="thaw-spinner__label" id=id.get_value()> view! {
{children()} <label class="thaw-spinner__label" id=id.get_value()>
</label> {label}
} </label>
.into_any()
} else {
{
move || {
if let Some(label) = label.get() {
view! {
<label class="thaw-spinner__label" id=id.get_value()>
{label}
</label>
}
.into()
} else {
None
}
} }
.into()
} else {
None
} }
.into_any()
}} }}
</div> </div>
} }

View file

@ -34,7 +34,7 @@ pub fn Upload(
let Some(trigger_el) = trigger_ref.get() else { let Some(trigger_el) = trigger_ref.get() else {
return; return;
}; };
let handle = add_event_listener(trigger_el, ev::click, move |_| { let handle = add_event_listener(trigger_el.into(), ev::click, move |_| {
if let Some(input_ref) = input_ref.get_untracked() { if let Some(input_ref) = input_ref.get_untracked() {
input_ref.click(); input_ref.click();
} }

View file

@ -1,4 +1,4 @@
.thaw-binder-follower { .thaw-binder-follower-container {
position: absolute; position: absolute;
left: 0; left: 0;
right: 0; right: 0;
@ -9,5 +9,5 @@
.thaw-binder-follower-content { .thaw-binder-follower-content {
position: absolute; position: absolute;
z-index: 1000000; z-index: 2000;
} }

View file

@ -1,7 +1,7 @@
use leptos::prelude::window; use leptos::prelude::window;
use web_sys::DomRect; use web_sys::DomRect;
#[derive(Clone, PartialEq)] #[derive(Clone)]
pub enum FollowerPlacement { pub enum FollowerPlacement {
Top, Top,
Bottom, Bottom,
@ -66,151 +66,309 @@ pub fn get_follower_placement_offset(
placement: FollowerPlacement, placement: FollowerPlacement,
target_rect: DomRect, target_rect: DomRect,
follower_rect: DomRect, follower_rect: DomRect,
content_rect: DomRect,
) -> Option<FollowerPlacementOffset> { ) -> Option<FollowerPlacementOffset> {
let (left, placement, top, transform) = match placement { match placement {
FollowerPlacement::Top | FollowerPlacement::TopStart | FollowerPlacement::TopEnd => { FollowerPlacement::Top => {
let Some(window_inner_height) = window_inner_height() else { let left = target_rect.x() + target_rect.width() / 2.0;
return None; let (top, placement) = {
}; let follower_height = follower_rect.height();
let content_height = content_rect.height(); let target_y = target_rect.y();
let target_top = target_rect.top(); let target_height = target_rect.height();
let target_bottom = target_rect.bottom(); let top = target_y - follower_height;
let top = target_top - content_height;
let (top, new_placement) = let Some(inner_height) = window_inner_height() else {
if top < 0.0 && target_bottom + content_height <= window_inner_height { return None;
(target_bottom, FollowerPlacement::Bottom) };
if top < 0.0 && target_y + target_height + follower_height <= inner_height {
(target_y + target_height, FollowerPlacement::Bottom)
} else { } else {
(top, FollowerPlacement::Top) (top, FollowerPlacement::Top)
}
};
Some(FollowerPlacementOffset {
top,
left,
transform: String::from("translateX(-50%)"),
placement,
})
}
FollowerPlacement::TopStart => {
let left = target_rect.x();
let (top, placement) = {
let follower_height = follower_rect.height();
let target_y = target_rect.y();
let target_height = target_rect.height();
let top = target_y - follower_height;
let Some(inner_height) = window_inner_height() else {
return None;
}; };
if placement == FollowerPlacement::Top { if top < 0.0 && target_y + target_height + follower_height <= inner_height {
let left = target_rect.left() + target_rect.width() / 2.0; (target_y + target_height, FollowerPlacement::BottomStart)
let transform = String::from("translateX(-50%)"); } else {
(left, new_placement, top, transform) (top, FollowerPlacement::TopStart)
} else if placement == FollowerPlacement::TopStart { }
let left = target_rect.left();
let transform = String::new();
(left, new_placement, top, transform)
} else if placement == FollowerPlacement::TopEnd {
let left = target_rect.right();
let transform = String::from("translateX(-100%)");
(left, new_placement, top, transform)
} else {
unreachable!()
}
}
FollowerPlacement::Bottom
| FollowerPlacement::BottomStart
| FollowerPlacement::BottomEnd => {
let Some(window_inner_height) = window_inner_height() else {
return None;
}; };
let content_height = content_rect.height(); Some(FollowerPlacementOffset {
let target_top = target_rect.top(); top,
let target_bottom = target_rect.bottom();
let top = target_bottom;
let (top, new_placement) = if top + content_height > window_inner_height
&& target_top - content_height >= 0.0
{
(target_top - content_height, FollowerPlacement::Top)
} else {
(top, FollowerPlacement::Bottom)
};
if placement == FollowerPlacement::Bottom {
let left = target_rect.left() + target_rect.width() / 2.0;
let transform = String::from("translateX(-50%)");
(left, new_placement, top, transform)
} else if placement == FollowerPlacement::BottomStart {
let left = target_rect.left();
let transform = String::new();
(left, new_placement, top, transform)
} else if placement == FollowerPlacement::BottomEnd {
let left = target_rect.right();
let transform = String::from("translateX(-100%)");
(left, new_placement, top, transform)
} else {
unreachable!()
}
}
FollowerPlacement::Left | FollowerPlacement::LeftStart | FollowerPlacement::LeftEnd => {
let Some(window_inner_width) = window_inner_width() else {
return None;
};
let content_width = content_rect.width();
let target_left = target_rect.left();
let target_right = target_rect.right();
let left = target_left - content_width;
leptos::logging::log!(
"{}-{} {} {}",
left, left,
target_right, transform: String::new(),
content_width, placement,
window_inner_width })
); }
let (left, new_placement) = FollowerPlacement::TopEnd => {
if left < 0.0 && target_right + content_width <= window_inner_width { let left = target_rect.x() + target_rect.width();
(target_right, FollowerPlacement::Right) let (top, placement) = {
let follower_height = follower_rect.height();
let target_y = target_rect.y();
let target_height = target_rect.height();
let top = target_y - follower_height;
let Some(inner_height) = window_inner_height() else {
return None;
};
if top < 0.0 && target_y + target_height + follower_height <= inner_height {
(target_y + target_height, FollowerPlacement::BottomEnd)
} else {
(top, FollowerPlacement::TopEnd)
}
};
Some(FollowerPlacementOffset {
top,
left,
transform: String::from("translateX(-100%)"),
placement,
})
}
FollowerPlacement::Left => {
let top = target_rect.y() + target_rect.height() / 2.0;
let (left, placement) = {
let follower_width = follower_rect.width();
let target_x = target_rect.x();
let target_width = target_rect.width();
let left = target_x - follower_width;
let Some(inner_width) = window_inner_width() else {
return None;
};
if left < 0.0 && target_x + target_width + follower_width > inner_width {
(target_x + follower_width, FollowerPlacement::Right)
} else { } else {
(left, FollowerPlacement::Left) (left, FollowerPlacement::Left)
}
};
Some(FollowerPlacementOffset {
top,
left,
transform: String::from("translateY(-50%)"),
placement,
})
}
FollowerPlacement::LeftStart => {
let top = target_rect.y();
let (left, placement) = {
let follower_width = follower_rect.width();
let target_x = target_rect.x();
let target_width = target_rect.width();
let left = target_x - follower_width;
let Some(inner_width) = window_inner_width() else {
return None;
}; };
if placement == FollowerPlacement::Left {
let top = target_rect.top() + target_rect.height() / 2.0; if left < 0.0 && target_x + target_width + follower_width > inner_width {
let transform = String::from("translateY(-50%)"); (target_x + follower_width, FollowerPlacement::RightStart)
(left, new_placement, top, transform) } else {
} else if placement == FollowerPlacement::LeftStart { (left, FollowerPlacement::LeftStart)
let top = target_rect.top(); }
let transform = String::new();
(left, new_placement, top, transform)
} else if placement == FollowerPlacement::LeftEnd {
let top = target_rect.bottom();
let transform = String::from("translateY(-100%)");
(left, new_placement, top, transform)
} else {
unreachable!()
}
}
FollowerPlacement::Right | FollowerPlacement::RightStart | FollowerPlacement::RightEnd => {
let Some(window_inner_width) = window_inner_width() else {
return None;
}; };
Some(FollowerPlacementOffset {
let content_width = content_rect.width(); top,
let target_left = target_rect.left(); left,
let target_right = target_rect.right(); transform: String::new(),
let left = target_right; placement,
let (left, new_placement) = if left + content_width > window_inner_width })
&& target_left - content_width >= 0.0
{
(target_left - content_width, FollowerPlacement::Left)
} else {
(left, FollowerPlacement::Right)
};
if placement == FollowerPlacement::Right {
let top = target_rect.top() + target_rect.height() / 2.0;
let transform = String::from("translateY(-50%)");
(left, new_placement, top, transform)
} else if placement == FollowerPlacement::RightStart {
let top = target_rect.top();
let transform = String::new();
(left, new_placement, top, transform)
} else if placement == FollowerPlacement::RightEnd {
let top = target_rect.bottom();
let transform = String::from("translateY(-100%)");
(left, new_placement, top, transform)
} else {
unreachable!()
}
} }
}; FollowerPlacement::LeftEnd => {
let top = target_rect.y() + target_rect.height();
let (left, placement) = {
let follower_width = follower_rect.width();
let target_x = target_rect.x();
let target_width = target_rect.width();
let left = target_x - follower_width;
Some(FollowerPlacementOffset { let Some(inner_width) = window_inner_width() else {
top: top - follower_rect.top(), return None;
left: left - follower_rect.left(), };
placement,
transform, if left < 0.0 && target_x + target_width + follower_width > inner_width {
}) (target_x + follower_width, FollowerPlacement::RightEnd)
} else {
(left, FollowerPlacement::LeftEnd)
}
};
Some(FollowerPlacementOffset {
top,
left,
transform: String::from("translateY(-100%)"),
placement,
})
}
FollowerPlacement::Right => {
let top = target_rect.y() + target_rect.height() / 2.0;
let (left, placement) = {
let follower_width = follower_rect.width();
let target_x = target_rect.x();
let target_width = target_rect.width();
let left = target_x + target_width;
let Some(inner_width) = window_inner_width() else {
return None;
};
if left + follower_width > inner_width && target_x - follower_width >= 0.0 {
(target_x - follower_width, FollowerPlacement::Left)
} else {
(left, FollowerPlacement::Right)
}
};
Some(FollowerPlacementOffset {
top,
left,
transform: String::from("translateY(-50%)"),
placement,
})
}
FollowerPlacement::RightStart => {
let top = target_rect.y();
let (left, placement) = {
let follower_width = follower_rect.width();
let target_x = target_rect.x();
let target_width = target_rect.width();
let left = target_x + target_width;
let Some(inner_width) = window_inner_width() else {
return None;
};
if left + follower_width > inner_width && target_x - follower_width >= 0.0 {
(target_x - follower_width, FollowerPlacement::LeftStart)
} else {
(left, FollowerPlacement::RightStart)
}
};
Some(FollowerPlacementOffset {
top,
left,
transform: String::new(),
placement,
})
}
FollowerPlacement::RightEnd => {
let top = target_rect.y() + target_rect.height();
let (left, placement) = {
let follower_width = follower_rect.width();
let target_x = target_rect.x();
let target_width = target_rect.width();
let left = target_x + target_width;
let Some(inner_width) = window_inner_width() else {
return None;
};
if left + follower_width > inner_width && target_x - follower_width >= 0.0 {
(target_x - follower_width, FollowerPlacement::LeftEnd)
} else {
(left, FollowerPlacement::RightEnd)
}
};
Some(FollowerPlacementOffset {
top,
left,
transform: String::from("translateY(-100%)"),
placement,
})
}
FollowerPlacement::Bottom => {
let left = target_rect.x() + target_rect.width() / 2.0;
let (top, placement) = {
let follower_height = follower_rect.height();
let target_y = target_rect.y();
let target_height = target_rect.height();
let top = target_y + target_height;
let Some(inner_height) = window_inner_height() else {
return None;
};
if top + follower_height > inner_height && target_y - follower_height >= 0.0 {
(target_y - follower_height, FollowerPlacement::Top)
} else {
(top, FollowerPlacement::Bottom)
}
};
Some(FollowerPlacementOffset {
top,
left,
transform: String::from("translateX(-50%)"),
placement,
})
}
FollowerPlacement::BottomStart => {
let left = target_rect.x();
let (top, placement) = {
let follower_height = follower_rect.height();
let target_y = target_rect.y();
let target_height = target_rect.height();
let top = target_y + target_height;
let Some(inner_height) = window_inner_height() else {
return None;
};
if top + follower_height > inner_height && target_y - follower_height >= 0.0 {
(target_y - follower_height, FollowerPlacement::TopStart)
} else {
(top, FollowerPlacement::BottomStart)
}
};
Some(FollowerPlacementOffset {
top,
left,
transform: String::new(),
placement,
})
}
FollowerPlacement::BottomEnd => {
let left = target_rect.x() + target_rect.width();
let (top, placement) = {
let follower_height = follower_rect.height();
let target_y = target_rect.y();
let target_height = target_rect.height();
let top = target_y + target_height;
let Some(inner_height) = window_inner_height() else {
return None;
};
if top + follower_height > inner_height && target_y - follower_height >= 0.0 {
(target_y - follower_height, FollowerPlacement::TopEnd)
} else {
(top, FollowerPlacement::BottomEnd)
}
};
Some(FollowerPlacementOffset {
top,
left,
transform: String::from("translateX(-100%)"),
placement,
})
}
}
} }
fn window_inner_width() -> Option<f64> { fn window_inner_width() -> Option<f64> {

View file

@ -14,7 +14,7 @@ use leptos::{
leptos_dom::helpers::WindowListenerHandle, leptos_dom::helpers::WindowListenerHandle,
prelude::*, prelude::*,
}; };
use thaw_utils::{add_event_listener, get_scroll_parent_node, mount_style, EventListenerHandle}; use thaw_utils::{add_event_listener, get_scroll_parent, mount_style, EventListenerHandle};
#[slot] #[slot]
pub struct Follower { pub struct Follower {
@ -82,21 +82,16 @@ where
let scrollable_element_handle_vec = StoredValue::<Vec<EventListenerHandle>>::new(vec![]); let scrollable_element_handle_vec = StoredValue::<Vec<EventListenerHandle>>::new(vec![]);
let resize_handle = StoredValue::new(None::<WindowListenerHandle>); let resize_handle = StoredValue::new(None::<WindowListenerHandle>);
let follower_ref = NodeRef::<html::Div>::new();
let content_ref = NodeRef::<html::Div>::new(); let content_ref = NodeRef::<html::Div>::new();
let content_style = RwSignal::new(String::new()); let content_style = RwSignal::new(String::new());
let placement_str = RwSignal::new(follower_placement.as_str()); let placement_str = RwSignal::new(follower_placement.as_str());
let sync_position = move || { let sync_position = move || {
let Some(follower_el) = follower_ref.get_untracked() else {
return;
};
let Some(content_ref) = content_ref.get_untracked() else { let Some(content_ref) = content_ref.get_untracked() else {
return; return;
}; };
let Some(target_ref) = target_ref.get_untracked() else { let Some(target_ref) = target_ref.get_untracked() else {
return; return;
}; };
let follower_rect = follower_el.get_bounding_client_rect();
let target_rect = target_ref.get_bounding_client_rect(); let target_rect = target_ref.get_bounding_client_rect();
let content_rect = content_ref.get_bounding_client_rect(); let content_rect = content_ref.get_bounding_client_rect();
let mut style = String::new(); let mut style = String::new();
@ -113,12 +108,8 @@ where
left, left,
transform, transform,
placement, placement,
}) = get_follower_placement_offset( }) = get_follower_placement_offset(follower_placement, target_rect, content_rect)
follower_placement, {
target_rect,
follower_rect,
content_rect,
) {
placement_str.set(placement.as_str()); placement_str.set(placement.as_str());
style.push_str(&format!( style.push_str(&format!(
"transform-origin: {};", "transform-origin: {};",
@ -141,12 +132,12 @@ where
}; };
let mut handle_vec = vec![]; let mut handle_vec = vec![];
let mut cursor = get_scroll_parent_node(&el); let mut cursor = get_scroll_parent(&el);
loop { loop {
if let Some(node) = cursor.take() { if let Some(el) = cursor.take() {
cursor = get_scroll_parent_node(&node); cursor = get_scroll_parent(&el);
let handle = add_event_listener(node, ev::scroll, move |_| { let handle = add_event_listener(el, ev::scroll, move |_| {
sync_position(); sync_position();
}); });
handle_vec.push(handle); handle_vec.push(handle);
@ -206,16 +197,18 @@ where
view! { view! {
{children()} {children()}
<Teleport immediate=follower_show> <Teleport immediate=follower_show>
<div class="thaw-binder-follower" node_ref=follower_ref> <Provider value=follower_injection>
<div <div class="thaw-binder-follower-container">
class="thaw-binder-follower-content" <div
data-thaw-placement=move || placement_str.get() class="thaw-binder-follower-content"
node_ref=content_ref data-thaw-placement=move || placement_str.get()
style=move || content_style.get() node_ref=content_ref
> style=move || content_style.get()
<Provider value=follower_injection>{follower_children()}</Provider> >
{follower_children()}
</div>
</div> </div>
</div> </Provider>
</Teleport> </Teleport>
} }
} }

View file

@ -92,13 +92,13 @@ where
} }
}; };
let handle = match types { let handle = match types {
AnimationTypes::Transition => { AnimationTypes::Transition => add_event_listener(
add_event_listener(el.deref().clone(), ev::transitionend, move |_| { el.deref().clone().into(),
event_listener() ev::transitionend,
}) move |_| event_listener(),
} ),
AnimationTypes::Animation => { AnimationTypes::Animation => {
add_event_listener(el.deref().clone(), ev::animationend, move |_| { add_event_listener(el.deref().clone().into(), ev::animationend, move |_| {
event_listener() event_listener()
}) })
} }

View file

@ -1,49 +1,39 @@
use leptos::prelude::*; use leptos::prelude::*;
use wasm_bindgen::JsCast; use web_sys::Element;
use web_sys::{Element, Node};
pub fn get_scroll_parent_node(node: &Node) -> Option<Node> { pub fn get_scroll_parent(element: &Element) -> Option<Element> {
let parent_node = node.parent_node()?; let Some(parent_element) = get_parent_element(element) else {
return None;
};
let node_type = parent_node.node_type(); if parent_element.node_type() == 9 {
if node_type == Node::ELEMENT_NODE { return Some(parent_element);
let el = parent_node.clone().dyn_into::<Element>().unwrap(); }
if let Some((overflow, overflow_x, overflow_y)) = get_overflow(&el) {
if parent_element.node_type() == 1 {
if let Some((overflow, overflow_x, overflow_y)) = get_overflow(&parent_element) {
let overflow = format!("{overflow}{overflow_x}{overflow_y}"); let overflow = format!("{overflow}{overflow_x}{overflow_y}");
if overflow.contains("auto") { if overflow.contains("auto") {
return Some(parent_node); return Some(parent_element);
} }
if overflow.contains("scroll") { if overflow.contains("scroll") {
return Some(parent_node); return Some(parent_element);
} }
if overflow.contains("overlay") { if overflow.contains("overlay") {
return Some(parent_node); return Some(parent_element);
} }
} }
} else if node_type == Node::DOCUMENT_NODE {
return Some(document().into());
} }
get_scroll_parent_node(&parent_node) get_scroll_parent(&parent_element)
} }
pub fn get_scroll_parent_element(element: &Element) -> Option<Element> { fn get_parent_element(element: &Element) -> Option<Element> {
let parent_element = element.parent_element()?; if element.node_type() == 9 {
None
if let Some((overflow, overflow_x, overflow_y)) = get_overflow(&parent_element) { } else {
let overflow = format!("{overflow}{overflow_x}{overflow_y}"); element.parent_element()
if overflow.contains("auto") {
return Some(parent_element);
}
if overflow.contains("scroll") {
return Some(parent_element);
}
if overflow.contains("overlay") {
return Some(parent_element);
}
} }
get_scroll_parent_element(&parent_element)
} }
fn get_overflow(parent_element: &Element) -> Option<(String, String, String)> { fn get_overflow(parent_element: &Element) -> Option<(String, String, String)> {

View file

@ -2,6 +2,6 @@ mod get_scroll_parent;
mod mount_style; mod mount_style;
mod scroll_into_view; mod scroll_into_view;
pub use get_scroll_parent::{get_scroll_parent_element, get_scroll_parent_node}; pub use get_scroll_parent::get_scroll_parent;
pub use mount_style::{mount_dynamic_style, mount_style}; pub use mount_style::{mount_dynamic_style, mount_style};
pub use scroll_into_view::scroll_into_view; pub use scroll_into_view::scroll_into_view;

View file

@ -3,8 +3,8 @@ use web_sys::HtmlElement;
pub fn scroll_into_view(el: &HtmlElement) { pub fn scroll_into_view(el: &HtmlElement) {
cfg_if! { if #[cfg(all(target_arch = "wasm32", any(feature = "csr", feature = "hydrate")))] { cfg_if! { if #[cfg(all(target_arch = "wasm32", any(feature = "csr", feature = "hydrate")))] {
use super::get_scroll_parent_element; use super::get_scroll_parent;
if let Some(parent) = get_scroll_parent_element(el) { if let Some(parent) = get_scroll_parent(el) {
let parent_rect = parent.get_bounding_client_rect(); let parent_rect = parent.get_bounding_client_rect();
let el_rect = el.get_bounding_client_rect(); let el_rect = el.get_bounding_client_rect();
if el_rect.y() < parent_rect.y() { if el_rect.y() < parent_rect.y() {

View file

@ -1,9 +1,9 @@
use ::wasm_bindgen::{prelude::Closure, JsCast}; use ::wasm_bindgen::{prelude::Closure, JsCast};
use leptos::ev; use leptos::ev;
use web_sys::EventTarget; use web_sys::{Element, EventTarget};
pub fn add_event_listener<E>( pub fn add_event_listener<E>(
target: impl Into<EventTarget>, target: Element,
event: E, event: E,
cb: impl Fn(E::EventType) + 'static, cb: impl Fn(E::EventType) + 'static,
) -> EventListenerHandle ) -> EventListenerHandle
@ -31,12 +31,12 @@ impl EventListenerHandle {
} }
fn add_event_listener_untyped( fn add_event_listener_untyped(
target: impl Into<EventTarget>, target: Element,
event_name: &str, event_name: &str,
cb: impl Fn(web_sys::Event) + 'static, cb: impl Fn(web_sys::Event) + 'static,
) -> EventListenerHandle { ) -> EventListenerHandle {
fn wel( fn wel(
target: EventTarget, target: Element,
cb: Box<dyn FnMut(web_sys::Event)>, cb: Box<dyn FnMut(web_sys::Event)>,
event_name: &str, event_name: &str,
) -> EventListenerHandle { ) -> EventListenerHandle {
@ -54,11 +54,11 @@ fn add_event_listener_untyped(
}) })
} }
wel(target.into(), Box::new(cb), event_name) wel(target, Box::new(cb), event_name)
} }
pub fn add_event_listener_with_bool<E: ev::EventDescriptor + 'static>( pub fn add_event_listener_with_bool<E: ev::EventDescriptor + 'static>(
target: impl Into<EventTarget>, target: impl IntoEventTarget,
event: E, event: E,
cb: impl Fn(E::EventType) + 'static, cb: impl Fn(E::EventType) + 'static,
use_capture: bool, use_capture: bool,
@ -67,7 +67,7 @@ where
E::EventType: JsCast, E::EventType: JsCast,
{ {
add_event_listener_untyped_with_bool( add_event_listener_untyped_with_bool(
target, target.into_event_target(),
&event.name(), &event.name(),
move |e| cb(e.unchecked_into::<E::EventType>()), move |e| cb(e.unchecked_into::<E::EventType>()),
use_capture, use_capture,
@ -75,7 +75,7 @@ where
} }
fn add_event_listener_untyped_with_bool( fn add_event_listener_untyped_with_bool(
target: impl Into<EventTarget>, target: EventTarget,
event_name: &str, event_name: &str,
cb: impl Fn(web_sys::Event) + 'static, cb: impl Fn(web_sys::Event) + 'static,
use_capture: bool, use_capture: bool,
@ -107,30 +107,24 @@ fn add_event_listener_untyped_with_bool(
}) })
} }
wel(target.into(), Box::new(cb), event_name, use_capture) wel(target, Box::new(cb), event_name, use_capture)
} }
// pub trait IntoEventTarget { pub trait IntoEventTarget {
// fn into_event_target(self) -> EventTarget; fn into_event_target(self) -> EventTarget;
// } }
// impl IntoEventTarget for EventTarget { impl IntoEventTarget for EventTarget {
// fn into_event_target(self) -> EventTarget { fn into_event_target(self) -> EventTarget {
// self self
// } }
// } }
// impl IntoEventTarget for web_sys::Document { impl IntoEventTarget for web_sys::Document {
// fn into_event_target(self) -> EventTarget { fn into_event_target(self) -> EventTarget {
// self.into() self.into()
// } }
// } }
// impl IntoEventTarget for Element {
// fn into_event_target(self) -> EventTarget {
// self.into()
// }
// }
// impl IntoEventTarget for HtmlElement<AnyElement> { // impl IntoEventTarget for HtmlElement<AnyElement> {
// fn into_event_target(self) -> EventTarget { // fn into_event_target(self) -> EventTarget {

View file

@ -4,6 +4,6 @@ mod signal_watch;
mod stored_maybe_signal; mod stored_maybe_signal;
pub use component_ref::ComponentRef; pub use component_ref::ComponentRef;
pub use model::{Model, OptionModel, OptionModelWithValue, VecModel, VecModelWithValue}; pub use model::{Model, OptionModel, VecModel, VecModelWithValue, OptionModelWithValue};
pub use signal_watch::SignalWatch; pub use signal_watch::SignalWatch;
pub use stored_maybe_signal::StoredMaybeSignal; pub use stored_maybe_signal::StoredMaybeSignal;

View file

@ -58,9 +58,7 @@ impl<T: Send + Sync> OptionModel<T> {
pub fn with_untracked<O>(&self, fun: impl FnOnce(OptionModelWithValue<T>) -> O) -> O { pub fn with_untracked<O>(&self, fun: impl FnOnce(OptionModelWithValue<T>) -> O) -> O {
match self { match self {
Self::T(read, _, _) => read.with_untracked(|value| fun(OptionModelWithValue::T(value))), Self::T(read, _, _) => read.with_untracked(|value| fun(OptionModelWithValue::T(value))),
Self::Option(read, _, _) => { Self::Option(read, _, _) => read.with_untracked(|value| fun(OptionModelWithValue::Option(value))),
read.with_untracked(|value| fun(OptionModelWithValue::Option(value)))
}
} }
} }
} }

View file

@ -67,7 +67,10 @@ impl<T: Send + Sync> VecModel<T> {
} }
} }
pub fn with<O>(&self, fun: impl FnOnce(VecModelWithValue<T>) -> O) -> O { pub fn with<O>(
&self,
fun: impl FnOnce(VecModelWithValue<T>) -> O,
) -> O {
match self { match self {
Self::T(read, _, _) => read.with(|value| fun(VecModelWithValue::T(value))), Self::T(read, _, _) => read.with(|value| fun(VecModelWithValue::T(value))),
Self::Option(read, _, _) => read.with(|value| fun(VecModelWithValue::Option(value))), Self::Option(read, _, _) => read.with(|value| fun(VecModelWithValue::Option(value))),