slider.rs 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. use std::collections::HashMap;
  2. use crate::widgets::get_root_id;
  3. use dioxus::prelude::*;
  4. use dioxus_elements::input_data::keyboard_types::Key;
  5. use dioxus_html as dioxus_elements;
  6. use dioxus_html::FormData;
  7. use rink::Query;
  8. #[derive(Props)]
  9. pub(crate) struct SliderProps<'a> {
  10. #[props(!optional)]
  11. raw_oninput: Option<&'a EventHandler<'a, FormData>>,
  12. #[props(!optional)]
  13. value: Option<&'a str>,
  14. #[props(!optional)]
  15. width: Option<&'a str>,
  16. #[props(!optional)]
  17. height: Option<&'a str>,
  18. #[props(!optional)]
  19. min: Option<&'a str>,
  20. #[props(!optional)]
  21. max: Option<&'a str>,
  22. #[props(!optional)]
  23. step: Option<&'a str>,
  24. }
  25. #[allow(non_snake_case)]
  26. pub(crate) fn Slider<'a>(cx: Scope<'a, SliderProps>) -> Element<'a> {
  27. let tui_query: Query = cx.consume_context().unwrap();
  28. let value_state = use_state(cx, || 0.0);
  29. let value: Option<f32> = cx.props.value.and_then(|v| v.parse().ok());
  30. let width = cx.props.width.unwrap_or("20px");
  31. let height = cx.props.height.unwrap_or("1px");
  32. let min = cx.props.min.and_then(|v| v.parse().ok()).unwrap_or(0.0);
  33. let max = cx.props.max.and_then(|v| v.parse().ok()).unwrap_or(100.0);
  34. let size = max - min;
  35. let step = cx
  36. .props
  37. .step
  38. .and_then(|v| v.parse().ok())
  39. .unwrap_or(size / 10.0);
  40. let current_value = match value {
  41. Some(value) => value,
  42. None => *value_state.get(),
  43. }
  44. .clamp(min, max);
  45. let fst_width = 100.0 * (current_value - min) / size;
  46. let snd_width = 100.0 * (max - current_value) / size;
  47. assert!(fst_width + snd_width > 99.0 && fst_width + snd_width < 101.0);
  48. let update = |value: String| {
  49. if let Some(oninput) = cx.props.raw_oninput {
  50. oninput.call(FormData {
  51. value,
  52. values: HashMap::new(),
  53. files: None,
  54. });
  55. }
  56. };
  57. render! {
  58. div{
  59. width: "{width}",
  60. height: "{height}",
  61. display: "flex",
  62. flex_direction: "row",
  63. onkeydown: move |event| {
  64. match event.key() {
  65. Key::ArrowLeft => {
  66. value_state.set((current_value - step).clamp(min, max));
  67. update(value_state.current().to_string());
  68. }
  69. Key::ArrowRight => {
  70. value_state.set((current_value + step).clamp(min, max));
  71. update(value_state.current().to_string());
  72. }
  73. _ => ()
  74. }
  75. },
  76. onmousemove: move |evt| {
  77. let mouse = evt.data;
  78. if !mouse.held_buttons().is_empty(){
  79. let node = tui_query.get(get_root_id(cx).unwrap());
  80. let width = node.size().unwrap().width;
  81. let offset = mouse.element_coordinates();
  82. value_state.set(min + size*(offset.x as f32) / width as f32);
  83. update(value_state.current().to_string());
  84. }
  85. },
  86. div{
  87. width: "{fst_width}%",
  88. background_color: "rgba(10,10,10,0.5)",
  89. }
  90. div{
  91. "|"
  92. }
  93. div{
  94. width: "{snd_width}%",
  95. background_color: "rgba(10,10,10,0.5)",
  96. }
  97. }
  98. }
  99. }