dioxuscut_player/
controls.rs1use dioxus::prelude::*;
4
5#[derive(Props, Clone, PartialEq)]
7pub struct ControlsProps {
8 pub frame: u32,
10 pub duration: u32,
12 pub playing: bool,
14 pub on_play_pause: EventHandler<MouseEvent>,
16 pub on_seek: EventHandler<u32>,
18}
19
20#[component]
22pub fn Controls(props: ControlsProps) -> Element {
23 let play_icon = if props.playing { "⏸" } else { "▶" };
24 let progress_pct = if props.duration > 1 {
25 props.frame as f64 / (props.duration - 1) as f64 * 100.0
26 } else {
27 0.0
28 };
29
30 let duration = props.duration;
31 let on_seek = props.on_seek;
32
33 rsx! {
34 div {
35 class: "dioxuscut-controls",
36 style: "
37 display: flex;
38 align-items: center;
39 gap: 10px;
40 width: 100%;
41 padding: 8px 12px;
42 background: rgba(20, 20, 30, 0.85);
43 backdrop-filter: blur(8px);
44 border-radius: 8px;
45 box-sizing: border-box;
46 ",
47
48 button {
50 onclick: props.on_play_pause,
51 style: "
52 background: none;
53 border: none;
54 color: white;
55 font-size: 18px;
56 cursor: pointer;
57 padding: 4px 8px;
58 border-radius: 4px;
59 transition: background 0.15s;
60 ",
61 onmouseenter: |e: MouseEvent| {
62 let _ = e;
64 },
65 "{play_icon}"
66 }
67
68 div {
70 style: "
71 flex: 1;
72 position: relative;
73 height: 20px;
74 display: flex;
75 align-items: center;
76 cursor: pointer;
77 ",
78 div {
80 style: "
81 position: absolute;
82 left: 0; right: 0;
83 height: 4px;
84 background: rgba(255,255,255,0.2);
85 border-radius: 2px;
86 ",
87 }
88 div {
90 style: "
91 position: absolute;
92 left: 0;
93 width: {progress_pct:.1}%;
94 height: 4px;
95 background: #6c63ff;
96 border-radius: 2px;
97 pointer-events: none;
98 ",
99 }
100 input {
102 r#type: "range",
103 min: "0",
104 max: "{duration.saturating_sub(1)}",
105 value: "{props.frame}",
106 style: "
107 position: absolute;
108 width: 100%;
109 opacity: 0;
110 cursor: pointer;
111 height: 20px;
112 margin: 0;
113 ",
114 oninput: move |e| {
115 if let Ok(v) = e.value().parse::<u32>() {
116 on_seek.call(v);
117 }
118 },
119 }
120 }
121
122 span {
124 style: "color: rgba(255,255,255,0.6); font-size: 12px; font-family: monospace; min-width: 60px; text-align: right;",
125 "{props.frame}/{duration.saturating_sub(1)}"
126 }
127 }
128 }
129}