///|
pub fn calendar_controlled(
id~ : String,
month~ : @minimoon.Val[@headless.CalendarDate],
on_month_change~ : @minimoon.Emit[@headless.CalendarDate],
selected~ : @minimoon.Val[@headless.DateSelection],
on_change~ : @minimoon.Emit[@headless.DateSelection],
disabled_date? : (@headless.CalendarDate) -> Bool = _ => false,
week_start? : Int = 0,
today? : @headless.CalendarDate,
show_outside_days? : Bool = true,
disabled? : Bool = false,
) -> @minimoon.Val[@minimoon.Node] {
guard week_start >= 0 && week_start <= 6 else {
abort("calendar week_start must be between 0 and 6")
}
let dispatch = form_dispatch(
@minimoon.Val::map2(month, selected, (month, selected) => (month, selected)),
(input, request : CalendarRequest) => {
if disabled {
return @minimoon.none
}
let (month, selected) = input
match request {
Choose(date) =>
if disabled_date(date) {
@minimoon.none
} else {
on_change(selected.choose(date))
}
ShiftMonth(delta) => {
let next = month.add_months(delta)
if next == month {
@minimoon.none
} else {
on_month_change(next)
}
}
}
},
)
@minimoon.Val::view2(month, selected, (month, selected) => {
let first = @headless.calendar_date(
year=month.year,
month=month.month,
day=1,
)
let offset = (first.weekday() - week_start + 7) % 7
let first_ordinal = first.ordinal() - offset
let cells : Array[@minimoon.Node] = []
for index = 0; index < 42; index = index + 1 {
let ordinal = first_ordinal + index
if ordinal < 1 || ordinal > 3652059 {
cells.push(
@minimoon.div(
class="mmui-calendar-empty",
style="width:14.2857%;min-height:36px;",
semantics=@minimoon.semantics(hidden=true),
@minimoon.nothing(),
),
)
continue
}
let date = first.add_days(index - offset)
let outside = date.month != month.month || date.year != month.year
if outside && !show_outside_days {
cells.push(
@minimoon.div(
class="mmui-calendar-empty",
style="width:14.2857%;min-height:36px;",
semantics=@minimoon.semantics(hidden=true),
@minimoon.nothing(),
),
)
continue
}
let disabled = disabled || disabled_date(date)
cells.push(
@minimoon.button(
class="mmui-calendar-day" +
(if selected.contains(date) { " mmui-selected" } else { "" }) +
(if today == Some(date) { " mmui-calendar-today" } else { "" }) +
(if outside { " mmui-calendar-outside" } else { "" }),
style="width:14.2857%;",
disabled~,
event_key=id + "/date/" + index.to_string(),
on_tap=if disabled { @minimoon.none } else { dispatch(Choose(date)) },
semantics=@minimoon.semantics(
label=date.iso(),
selected=selected.contains(date),
disabled~,
),
date.day.to_string(),
),
)
}
ui_container("calendar", id~, [
ui_container("calendar-header", [
@minimoon.button(
event_key=id + "/previous",
disabled=disabled || (month.year == 1 && month.month == 1),
on_tap=if disabled || (month.year == 1 && month.month == 1) {
@minimoon.none
} else {
dispatch(ShiftMonth(-1))
},
"Previous month",
),
@minimoon.text(month.year.to_string() + "-" + month.month.to_string()),
@minimoon.button(
event_key=id + "/next",
disabled=disabled || (month.year == 9999 && month.month == 12),
on_tap=if disabled || (month.year == 9999 && month.month == 12) {
@minimoon.none
} else {
dispatch(ShiftMonth(1))
},
"Next month",
),
]),
ui_container(
"calendar-weekdays",
style="display:flex;",
[0, 1, 2, 3, 4, 5, 6].map(index => {
@minimoon.div(
style="width:14.2857%;text-align:center;",
["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][(
index + week_start
) %
7],
)
}),
),
ui_container("calendar-grid", style="display:flex;flex-wrap:wrap;", cells),
])
})
}
///|
pub fn calendar(
id~ : String,
initial_month~ : @headless.CalendarDate,
initial_selected? : @headless.DateSelection = @headless.Single(None),
disabled_date? : (@headless.CalendarDate) -> Bool = _ => false,
week_start? : Int = 0,
today? : @headless.CalendarDate,
show_outside_days? : Bool = true,
disabled? : Bool = false,
on_select? : @minimoon.Emit[@headless.DateSelection],
on_month_change? : @minimoon.Emit[@headless.CalendarDate],
) -> @minimoon.Val[@minimoon.Node] {
let (month, set_month) = @minimoon.create_variable(initial_month)
let (selected, set_selected) = @minimoon.create_variable(initial_selected)
calendar_controlled(
id~,
month~,
on_month_change=@minimoon.Emit(value => {
@minimoon.batch([
set_month(_ => value),
on_month_change.map(emit => emit(value)).unwrap_or(@minimoon.none),
])
}),
selected~,
on_change=@minimoon.Emit(value => {
@minimoon.batch([
set_selected(_ => value),
on_select.map(emit => emit(value)).unwrap_or(@minimoon.none),
])
}),
disabled_date~,
week_start~,
today?,
show_outside_days~,
disabled~,
)
}