mod clock_number;
mod dial;
mod interaction;
mod pointer;
mod styles;
use leptos::prelude::*;
use orbital_macros::component_doc;
use orbital_theme::use_theme_options;
use crate::shared::{
commit_time, now_on_anchor, picker_style_sheet, resolve_anchor, snap_minute,
use_datetime_locale,
};
use super::field_types::{TimeClockAppearance, TimeClockBind};
pub use dial::{ClockView, TimeClockDial};
use styles::time_clock_styles;
#[component_doc(
category = "Calendar & Time",
preview_slug = "time-clock",
preview_label = "Time Clock",
preview_icon = icondata::AiClockCircleOutlined,
)]
#[component]
pub fn TimeClock(
#[prop(optional, into)]
bind: TimeClockBind,
#[prop(optional, into)]
appearance: TimeClockAppearance,
#[prop(optional, into)]
class: MaybeProp<String>,
) -> impl IntoView {
let TimeClockBind { value } = bind;
let TimeClockAppearance {
ampm,
minute_step,
reference_date,
timezone,
disabled,
} = appearance;
let value = StoredValue::new(value);
let locale = use_datetime_locale();
let theme_options = use_theme_options();
let resolved_timezone = Signal::derive(move || timezone.get());
let resolved_reference = Signal::derive(move || reference_date.get().start_of_day());
let anchor = move || {
resolve_anchor(
value.get_value().get(),
resolved_reference.get(),
resolved_timezone.get(),
)
};
let view = RwSignal::new(ClockView::Hours);
let draft_hour_24 = RwSignal::new(0u32);
let draft_minute = RwSignal::new(0u32);
let is_pm = RwSignal::new(false);
Effect::new(move |_| {
let tz = resolved_timezone.get();
let anchor_day = anchor();
if let Some(current) = value.get_value().get() {
if let Some((hour, minute, _)) = current.hour_minute_second() {
draft_hour_24.set(hour);
draft_minute.set(minute);
is_pm.set(hour >= 12);
return;
}
}
let (hour, minute, pm) = now_on_anchor(anchor_day);
draft_hour_24.set(hour);
draft_minute.set(minute);
is_pm.set(pm);
let _ = tz;
});
let on_minute_selected = Callback::new(move |(hour, minute): (u32, u32)| {
if disabled.get_untracked() {
return;
}
let step = minute_step.get_untracked().max(1);
let snapped = snap_minute(minute, step);
if let Some(committed) = commit_time(
anchor(),
hour,
snapped,
0,
resolved_timezone.get_untracked(),
) {
value.get_value().set(Some(committed));
}
});
let root_class = move || {
let mut parts = vec!["orb-picker-time-clock".to_string()];
match theme_options.get().density {
orbital_theme::Density::Compact => {
parts.push("orb-picker-time-clock--density-compact".to_string())
}
orbital_theme::Density::Spacious => {
parts.push("orb-picker-time-clock--density-spacious".to_string())
}
orbital_theme::Density::Default => {}
}
if let Some(extra) = class.get() {
if !extra.is_empty() {
parts.push(extra);
}
}
let _ = locale.locale;
parts.join(" ")
};
view! {
<style>{time_clock_styles()}</style>
<style>{picker_style_sheet()}</style>
<div class=root_class data-orbital-picker="" role="group" aria-label="Time clock">
<TimeClockDial
view=view
draft_hour_24=draft_hour_24
draft_minute=draft_minute
is_pm=is_pm
ampm=ampm
minute_step=minute_step
disabled=disabled
on_minute_selected=on_minute_selected
/>
</div>
}
}