dioxus_audio/
components.rs1use dioxus::prelude::*;
4
5mod devices;
6mod player;
7mod recorder;
8mod visualizer;
9mod waveform;
10
11pub use devices::{AudioInputSelector, MicrophoneStatusIndicator};
12pub use player::AudioPlayer;
13pub use recorder::RecorderControls;
14pub use visualizer::{LevelMeter, LiveWaveform, SpectrumVisualizer};
15pub use waveform::{WaveformPreview, WaveformRangeSelector};
16
17pub static STYLESHEET: Asset = asset!("/assets/dioxus-audio.css");
18
19#[component]
20pub fn AudioStyles() -> Element {
21 rsx! { document::Stylesheet { href: STYLESHEET } }
22}
23
24#[component]
25pub fn AudioScrubber(
26 position_secs: f64,
27 duration_secs: f64,
28 on_seek: EventHandler<f64>,
29 #[props(default = false)] disabled: bool,
30 #[props(default)] aria_label: Option<String>,
31) -> Element {
32 let duration_secs = finite_non_negative(duration_secs);
33 let position_secs = finite_non_negative(position_secs).min(duration_secs);
34 let progress = if duration_secs > 0.0 {
35 position_secs / duration_secs * 100.0
36 } else {
37 0.0
38 };
39 let aria_label = aria_label.unwrap_or_else(|| "Seek audio".to_string());
40
41 rsx! {
42 div { class: "dioxus-audio dioxus-audio__scrubber",
43 div { class: "dioxus-audio__scrubber-track",
44 div {
45 class: "dioxus-audio__scrubber-fill",
46 style: "width: {progress}%",
47 }
48 }
49 input {
50 class: "dioxus-audio__scrubber-input",
51 r#type: "range",
52 min: "0",
53 max: "{duration_secs}",
54 step: "0.01",
55 value: "{position_secs}",
56 disabled,
57 aria_label,
58 oninput: move |event| {
59 if let Ok(value) = event.value().parse::<f64>() {
60 on_seek.call(value);
61 }
62 },
63 }
64 }
65 }
66}
67
68fn finite_non_negative(value: f64) -> f64 {
69 if value.is_finite() {
70 value.max(0.0)
71 } else {
72 0.0
73 }
74}