use hikari_palette::classes::{AudioPlayerClass, ClassesBuilder, TypedClass};
use crate::prelude::*;
use crate::styled::StyledComponent;
pub struct AudioPlayerComponent;
#[derive(Clone, Copy, PartialEq, Debug, Default)]
pub enum AudioPlayerSize {
#[default]
Medium,
Small,
Large,
}
#[define_props]
pub struct AudioPlayerProps {
pub src: String,
pub title: Option<String>,
pub artist: Option<String>,
pub cover: Option<String>,
#[default(false)]
pub autoplay: bool,
#[default(true)]
pub controls: bool,
#[default(false)]
pub loop_: bool,
#[default(false)]
pub muted: bool,
pub size: AudioPlayerSize,
#[default(String::default())]
pub class: String,
#[default(String::default())]
pub style: String,
}
#[component]
pub fn AudioPlayer(props: AudioPlayerProps) -> Element {
let size_class = match props.size {
AudioPlayerSize::Small => AudioPlayerClass::Sm,
AudioPlayerSize::Medium => AudioPlayerClass::Md,
AudioPlayerSize::Large => AudioPlayerClass::Lg,
};
let container_classes = ClassesBuilder::new()
.add_typed(AudioPlayerClass::Container)
.add_typed(size_class)
.add(&props.class)
.build();
rsx! {
div { class: container_classes, style: props.style,
div { class: AudioPlayerClass::Header.class_name(),
if let Some(cover) = &props.cover {
div { class: AudioPlayerClass::Cover.class_name(),
img {
src: cover,
alt: props.title.as_deref().unwrap_or("Audio cover"),
class: AudioPlayerClass::CoverImage.class_name(),
}
}
}
div { class: AudioPlayerClass::Info.class_name(),
if let Some(title) = &props.title {
div { class: AudioPlayerClass::Title.class_name(), "{title}" }
}
if let Some(artist) = &props.artist {
div { class: AudioPlayerClass::Artist.class_name(), "{artist}" }
}
}
}
audio {
class: AudioPlayerClass::Audio.class_name(),
src: props.src,
autoplay: props.autoplay,
controls: props.controls,
r#loop: props.loop_,
muted: props.muted,
}
}
}
}
impl StyledComponent for AudioPlayerComponent {
fn styles() -> &'static str {
r#"
.hi-audio-player {
background-color: var(--hi-color-bg-container);
border: 1px solid var(--hi-color-border);
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
display: flex;
flex-direction: column;
transition: all 0.3s ease;
}
.hi-audio-player:hover {
box-shadow: 0 6px 24px rgba(0, 0, 0, 0.18);
}
.hi-audio-player-sm {
max-width: 280px;
}
.hi-audio-player-md {
max-width: 360px;
}
.hi-audio-player-lg {
max-width: 480px;
}
[data-theme="dark"] .hi-audio-player {
background-color: var(--hi-surface);
border-color: var(--hi-color-border);
}
.hi-audio-player-header {
display: flex;
align-items: center;
gap: 12px;
padding: 16px;
border-bottom: 1px solid var(--hi-color-border);
}
.hi-audio-player-cover {
width: 64px;
height: 64px;
border-radius: 8px;
overflow: hidden;
flex-shrink: 0;
background-color: var(--hi-color-bg-elevated);
}
.hi-audio-player-cover-image {
width: 100%;
height: 100%;
object-fit: cover;
}
.hi-audio-player-info {
flex: 1;
min-width: 0;
}
.hi-audio-player-title {
font-size: 14px;
font-weight: 600;
color: var(--hi-text-primary);
margin-bottom: 4px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.hi-audio-player-artist {
font-size: 12px;
color: var(--hi-text-secondary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.hi-audio-player-audio {
width: 100%;
padding: 12px 16px;
}
.hi-audio-player-sm .hi-audio-player-header {
padding: 12px;
}
.hi-audio-player-sm .hi-audio-player-cover {
width: 48px;
height: 48px;
}
.hi-audio-player-sm .hi-audio-player-title {
font-size: 13px;
}
.hi-audio-player-sm .hi-audio-player-artist {
font-size: 11px;
}
.hi-audio-player-lg .hi-audio-player-header {
padding: 20px;
}
.hi-audio-player-lg .hi-audio-player-cover {
width: 80px;
height: 80px;
}
.hi-audio-player-lg .hi-audio-player-title {
font-size: 16px;
}
.hi-audio-player-lg .hi-audio-player-artist {
font-size: 13px;
}
"#
}
fn name() -> &'static str {
"audio-player"
}
}