102 lines
3.9 KiB
Rust
102 lines
3.9 KiB
Rust
|
use std::rc::Rc;
|
||
|
|
||
|
use wasm_bindgen::JsCast;
|
||
|
use web_sys::{Element, PointerEvent};
|
||
|
use yew::{function_component, html, use_state_eq, Callback, Html, Properties, UseStateHandle};
|
||
|
#[derive(Clone, Eq, PartialEq)]
|
||
|
pub enum PanelDirection {
|
||
|
Horizontal,
|
||
|
Vertical,
|
||
|
}
|
||
|
|
||
|
#[derive(PartialEq, Properties)]
|
||
|
pub struct SplitPanelProps {
|
||
|
pub direction: PanelDirection,
|
||
|
pub first: Html,
|
||
|
pub second: Html,
|
||
|
}
|
||
|
|
||
|
#[function_component(SplitPanel)]
|
||
|
pub fn split_panel(props: &SplitPanelProps) -> Html {
|
||
|
let is_dragging: Rc<UseStateHandle<Option<f64>>> = use_state_eq(|| None).into();
|
||
|
let dragged_space: Rc<UseStateHandle<Option<f64>>> = use_state_eq(|| None).into();
|
||
|
let wrapper_class = match props.direction {
|
||
|
PanelDirection::Horizontal => "hpanelwrapper",
|
||
|
PanelDirection::Vertical => "vpanelwrapper",
|
||
|
};
|
||
|
let wrapper_class = if (**is_dragging).is_some() {
|
||
|
format!("{} dragging", wrapper_class)
|
||
|
} else {
|
||
|
wrapper_class.to_owned()
|
||
|
};
|
||
|
let panel_class = match props.direction {
|
||
|
PanelDirection::Horizontal => "hpanel",
|
||
|
PanelDirection::Vertical => "vpanel",
|
||
|
};
|
||
|
let divider_class = match props.direction {
|
||
|
PanelDirection::Horizontal => "hdivider",
|
||
|
PanelDirection::Vertical => "vdivider",
|
||
|
};
|
||
|
let row_or_col = match props.direction {
|
||
|
PanelDirection::Horizontal => "columns",
|
||
|
PanelDirection::Vertical => "rows",
|
||
|
};
|
||
|
|
||
|
let layout = match **dragged_space {
|
||
|
None => format!("grid-template-{}: 1fr 4px 1fr", row_or_col),
|
||
|
Some(custom) => format!("grid-template-{}: {}px 4px 1fr", row_or_col, custom),
|
||
|
};
|
||
|
let is_dragging_forup = is_dragging.clone();
|
||
|
let dragged_space_formove = dragged_space.clone();
|
||
|
let dir = props.direction.clone();
|
||
|
match **is_dragging {
|
||
|
None => {
|
||
|
html! {
|
||
|
<div class={wrapper_class} style={layout}>
|
||
|
<div class={panel_class}>{props.first.clone()}</div>
|
||
|
<div class={divider_class}
|
||
|
onpointerdown={Callback::from(move |e: PointerEvent| {
|
||
|
match e.target() {
|
||
|
None => {},
|
||
|
Some(t) => {
|
||
|
let el: Element = JsCast::unchecked_into::<Element>(
|
||
|
JsCast::unchecked_into::<Element>(t)
|
||
|
.parent_node().expect("Parent exists"));
|
||
|
let top =
|
||
|
match dir {
|
||
|
PanelDirection::Horizontal => el.get_bounding_client_rect().x(),
|
||
|
PanelDirection::Vertical => el.get_bounding_client_rect().y()
|
||
|
};
|
||
|
is_dragging.set(Some(top));
|
||
|
}
|
||
|
}
|
||
|
})}
|
||
|
/>
|
||
|
<div class={panel_class}>{props.second.clone()}</div>
|
||
|
</div>
|
||
|
}
|
||
|
}
|
||
|
Some(start) => {
|
||
|
html! {
|
||
|
<div class={wrapper_class} style={layout}
|
||
|
onpointerup={Callback::from(move |_| {
|
||
|
is_dragging_forup.set(None);
|
||
|
})}
|
||
|
onpointermove={Callback::from(move |e: PointerEvent| {
|
||
|
let distance = match dir {
|
||
|
PanelDirection::Horizontal => e.client_x() as f64,
|
||
|
PanelDirection::Vertical => e.client_y() as f64
|
||
|
} - start - 2.0;
|
||
|
(*dragged_space_formove).set(Some(distance));
|
||
|
})}
|
||
|
>
|
||
|
<div class={panel_class}>{props.first.clone()}</div>
|
||
|
<div class={divider_class}
|
||
|
/>
|
||
|
<div class={panel_class}>{props.second.clone()}</div>
|
||
|
</div>
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|