mod color; use crate::{mount_style, teleport::Teleport, utils::maybe_rw_signal::MaybeRwSignal}; pub use color::*; use leptos::*; use leptos_dom::helpers::WindowListenerHandle; use wasm_bindgen::__rt::IntoJsResult; #[component] pub fn ColorPicker(#[prop(optional, into)] value: MaybeRwSignal) -> impl IntoView { mount_style("color-picker", include_str!("./color-picker.css")); let hue = create_rw_signal(0); let sv = create_rw_signal((0.0, 0.0)); let label = create_rw_signal(String::new()); let style = create_memo(move |_| { let mut style = String::new(); value.with(|value| { let value = value.to_hex_string(); style.push_str(&format!("background-color: {value};")); let (s, v) = sv.get_untracked(); if s < 0.5 && v > 0.5 { style.push_str("color: #000;"); } else { style.push_str("color: #fff;"); } label.set(value); }); style }); create_effect(move |prev| { let (s, v) = sv.get(); let hue_value = hue.get(); if prev.is_none() { let HSV { hue: h, saturation: s, value: v, .. } = value.get_untracked().into(); hue.set(h); sv.set((s, v)) } else { value.set(RGBA::from(HSV::new(hue_value, s, v))); } }); let is_show_popover = create_rw_signal(false); let trigger_ref = create_node_ref::(); let popover_ref = create_node_ref::(); let show_popover = move |_| { let rect = trigger_ref.get().unwrap().get_bounding_client_rect(); is_show_popover.set(true); if let Some(popover_ref) = popover_ref.get() { popover_ref.style( "transform", format!( "translateX({}px) translateY({}px)", rect.x(), rect.y() + rect.height() ), ); } }; let timer = window_event_listener(ev::click, move |ev| { let el = ev.target(); let mut el: Option = el.into_js_result().map_or(None, |el| Some(el.into())); let body = document().body().unwrap(); while let Some(current_el) = el { if current_el == *body { break; }; if current_el == ***popover_ref.get().unwrap() || current_el == ***trigger_ref.get().unwrap() { return; } el = current_el.parent_element(); } is_show_popover.set(false); }); on_cleanup(move || timer.remove()); view! {
{move || label.get()}
} } #[component] fn Panel(hue: ReadSignal, sv: RwSignal<(f64, f64)>) -> impl IntoView { let panel_ref = create_node_ref::(); let mouse = store_value(Vec::::new()); let on_mouse_down = move |_| { let on_mouse_move = window_event_listener(ev::mousemove, move |ev| { if let Some(panel) = panel_ref.get_untracked() { let rect = panel.get_bounding_client_rect(); let ev_x = f64::from(ev.x()); let ev_y = f64::from(ev.y()); let v = (rect.bottom() - ev_y) / rect.height(); let s = (ev_x - rect.x()) / rect.width(); let v = if v > 1.0 { 1.0 } else if v < 0.0 { 0.0 } else { v }; let s = if s > 1.0 { 1.0 } else if s < 0.0 { 0.0 } else { s }; sv.set((s, v)) } }); let on_mouse_up = window_event_listener(ev::mouseup, move |_| { mouse.update_value(|value| { for handle in value.drain(..).into_iter() { handle.remove(); } }); }); mouse.update_value(|value| { value.push(on_mouse_move); value.push(on_mouse_up); }); }; view! {
} } #[component] fn HueSlider(hue: RwSignal) -> impl IntoView { let rail_ref = create_node_ref::(); let mouse = store_value(Vec::::new()); let on_mouse_down = move |_| { let on_mouse_move = window_event_listener(ev::mousemove, move |ev| { if let Some(rail) = rail_ref.get_untracked() { let rect = rail.get_bounding_client_rect(); let ev_x = f64::from(ev.x()); let value = (ev_x - rect.x()) / (rect.width() - 12.0) * 360.0; let value = if value < 0.0 { 0 } else if value > 359.0 { 359 } else { value.round().to_string().parse::().unwrap() }; hue.set(value); } }); let on_mouse_up = window_event_listener(ev::mouseup, move |_| { mouse.update_value(|value| { for handle in value.drain(..).into_iter() { handle.remove(); } }); }); mouse.update_value(|value| { value.push(on_mouse_move); value.push(on_mouse_up); }); }; view! {
} }