Created
September 27, 2025 23:15
-
-
Save freedomtowin/581691a076a582ce143ecd6015d2c999 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| use dioxus::prelude::*; | |
| #[derive(Props, Debug, PartialEq, Clone)] | |
| pub struct MovableDivProps { | |
| pub element_size: Signal<f64>, | |
| pub is_dragging_div: Signal<bool>, | |
| pub orientation: DividerOrientation, | |
| pub min_size: Option<f64>, | |
| pub max_size: Option<f64>, | |
| } | |
| #[derive(Debug, PartialEq, Clone)] | |
| pub enum DividerOrientation { | |
| Horizontal, | |
| Vertical, | |
| } | |
| #[component] | |
| pub fn MovableDiv(mut props: MovableDivProps) -> Element { | |
| let is_dragging_div = props.is_dragging_div; | |
| let mut is_hovering = use_signal(|| false); | |
| let mut start_pos = use_signal(|| 0.0); | |
| let mut start_size = use_signal(|| 0.0); | |
| let is_horizontal = props.orientation == DividerOrientation::Horizontal; | |
| let min_size = props.min_size.unwrap_or(175.0); | |
| let max_size = props.max_size.unwrap_or(550.0); | |
| let resizer_size = use_memo(move || { | |
| if *props.is_dragging_div.read() { | |
| 150.0 | |
| } else { | |
| 20.0 | |
| } | |
| }); | |
| let cursor_style = if is_horizontal { | |
| "row-resize" | |
| } else { | |
| "col-resize" | |
| }; | |
| let divider_style = if is_horizontal { | |
| "height: 1px; width: 100%;" | |
| } else { | |
| "width: 1px; height: 100%;" | |
| }; | |
| let handle_style = if is_horizontal { | |
| format!("height: {}px; width: 100%;", resizer_size()) | |
| } else { | |
| format!("width: {}px; height: 100%;", resizer_size()) | |
| }; | |
| rsx! { | |
| div { | |
| position: "relative", | |
| style: "{divider_style}", | |
| display: "flex", | |
| justify_content: "center", | |
| align_items: "center", | |
| border: "1px solid", | |
| onmousedown: move |evt| { | |
| let coords = evt.data.client_coordinates(); | |
| let mouse_pos = if is_horizontal { | |
| coords.y as f64 | |
| } else { | |
| coords.x as f64 | |
| }; | |
| let current_size = *props.element_size.read(); | |
| if *is_hovering.read() { | |
| props.is_dragging_div.set(true); | |
| start_pos.set(mouse_pos); | |
| start_size.set(current_size); | |
| } | |
| }, | |
| onmousemove: move |evt| { | |
| if *is_dragging_div.read() { | |
| let coords = evt.data.client_coordinates(); | |
| let current_pos = if is_horizontal { | |
| coords.y as f64 | |
| } else { | |
| coords.x as f64 | |
| }; | |
| // Calculate the offset from the starting position | |
| // This is the key fix to prevent jumping | |
| let offset = current_pos - start_pos.read().clone(); | |
| let new_size = start_size.read().clone() + offset; | |
| props.element_size.set(new_size.clamp(min_size, max_size)); | |
| } | |
| }, | |
| div { | |
| style: "{handle_style}", | |
| cursor: "{cursor_style}", | |
| position: "absolute", | |
| user_select: "none", | |
| background_color: "rgba(0, 0, 0, 0.0)", | |
| display: "flex", | |
| justify_content: "center", | |
| align_items: "center", | |
| z_index: "1000", | |
| onmouseover: move |_| { | |
| is_hovering.set(true); | |
| }, | |
| onmouseout: move |_| { | |
| is_hovering.set(false); | |
| }, | |
| onmouseup: move |_| { | |
| props.is_dragging_div.set(false); | |
| }, | |
| onmouseleave: move |_| { | |
| if *is_dragging_div.read() { | |
| props.is_dragging_div.set(false); | |
| } | |
| is_hovering.set(false); | |
| }, | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment