Calendar

Component

Interactive examples and API documentation

Basic
A basic calendar component with Year/Month switch.
2026
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
January
January
February
March
April
May
June
July
August
September
October
November
December
Su Mo Tu We Th Fr Sa
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Code
<%= render Hakumi::Calendar::Component.new %>
Notice Calendar
This component can be rendered by using date_cell_render with the data you need.
2026
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
January
January
February
March
April
May
June
July
August
September
October
November
December
Su Mo Tu We Th Fr Sa
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
This is warning event.
This is usual event.
25
26
27
28
29
This is warning event.
This is usual event.
This is error event.
30
31
Code
<%
  events = {
    Date.today => [
      { id: 1, type: :warning, content: "This is warning event." },
      { id: 2, type: :success, content: "This is usual event." }
    ],
    Date.today + 5 => [
      { id: 3, type: :warning, content: "This is warning event." },
      { id: 4, type: :success, content: "This is usual event." },
      { id: 5, type: :error, content: "This is error event." }
    ],
    Date.today + 10 => [
      { id: 6, type: :warning, content: "This is warning event." },
      { id: 7, type: :success, content: "This is very long usual event......" }
    ],
    Date.today + 15 => [
      { id: 8, type: :error, content: "This is error event 1." },
      { id: 9, type: :error, content: "This is error event 2." },
      { id: 10, type: :error, content: "This is error event 3." },
      { id: 11, type: :error, content: "This is error event 4." }
    ]
  }
%>

<%= render Hakumi::Calendar::Component.new(events: events, id: "notice-calendar") %>

<script>
  (() => {
    const calendarEl = document.getElementById("notice-calendar")
    if (!calendarEl) return

    let wired = false
    const wire = () => {
      if (wired) return true
      
      const api = calendarEl.hakumiComponent?.api
      if (!api) return false

      calendarEl.addEventListener("hakumi--calendar:select", (e) => {
        const date = e.detail.date
        const dateStr = date.toISOString().split("T")[0]
        const events = api.getEventsForDate(dateStr)
        
        console.log("📅 Selected date:", dateStr)
        console.log("📋 Events for this date:", events)
        
        if (events.length > 0) {
          console.table(events)
        }
      })

      console.log("🗓️ Calendar wired! All events:", api.getEvents())

      wired = true
      return true
    }

    if (wire()) return
    const interval = setInterval(() => { if (wire()) clearInterval(interval) }, 50)
    setTimeout(() => clearInterval(interval), 5000)
  })()
</script>
Card
Nested inside a container element for rendering in limited space.
2026
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
January
January
February
March
April
May
June
July
August
September
October
November
December
Su Mo Tu We Th Fr Sa
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Code
<%= render Hakumi::Calendar::Component.new(fullscreen: false) %>
Selectable Calendar
A basic calendar component with Year/Month switch. Listen to the select event to get the selected date.
2026
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
January
January
February
March
April
May
June
July
August
September
October
November
December
Su Mo Tu We Th Fr Sa
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Selected date will be shown here
Code
<%= render Hakumi::Calendar::Component.new(
  value: Date.today,
  id: "selectable-calendar"
) %>

<div style="margin-top: 16px;">
  <%= render Hakumi::Alert::Component.new(
    type: :info,
    message: "Selected date will be shown here",
    id: "selected-date-alert"
  ) %>
</div>

<script>
  (() => {
    const calendarEl = document.getElementById("selectable-calendar")
    const alertEl = document.getElementById("selected-date-alert")
    if (!calendarEl || !alertEl) return

    let wired = false
    const wire = () => {
      if (wired) return true
      
      const calendarApi = calendarEl.hakumiComponent?.api
      const alertApi = alertEl.hakumiComponent?.api
      if (!calendarApi || !alertApi) return false

      calendarEl.addEventListener("hakumi--calendar:select", (e) => {
        const date = e.detail.date
        const formatted = date.toLocaleDateString("en-US", {
          weekday: "long",
          year: "numeric",
          month: "long",
          day: "numeric"
        })
        alertApi.setMessage(`You selected: ${formatted}`)
      })

      wired = true
      return true
    }

    if (wire()) return
    const interval = setInterval(() => { if (wire()) clearInterval(interval) }, 50)
    setTimeout(() => clearInterval(interval), 5000)
  })()
</script>
Show Week
Show week number in fullscreen calendar by setting show_week prop to true.
2026
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
January
January
February
March
April
May
June
July
August
September
October
November
December
Su Mo Tu We Th Fr Sa
1
28
29
30
31
1
2
3
2
4
5
6
7
8
9
10
3
11
12
13
14
15
16
17
4
18
19
20
21
22
23
24
5
25
26
27
28
29
30
31
Code
<%= render Hakumi::Calendar::Component.new(show_week: true) %>
Customize Header
Customize Calendar header content using the header_content slot.
Custom Header
Su Mo Tu We Th Fr Sa
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Code
<%= render Hakumi::Calendar::Component.new(
  fullscreen: false,
  id: "custom-header-calendar"
) do |calendar|
  calendar.with_header_content do %>
    <div style="display: flex; align-items: center; justify-content: space-between; width: 100%;">
      <div style="display: flex; align-items: center; gap: 8px;">
        <%= render Hakumi::Button::Component.new(
          type: :text,
          size: :small,
          id: "cal-prev-year",
          icon: :double_left
        ) %>
        <%= render Hakumi::Button::Component.new(
          type: :text,
          size: :small,
          id: "cal-prev-month",
          icon: :left
        ) %>
      </div>

      <div id="cal-header-title" style="font-weight: 500;">
        Custom Header
      </div>

      <div style="display: flex; align-items: center; gap: 8px;">
        <%= render Hakumi::Button::Component.new(
          type: :text,
          size: :small,
          id: "cal-next-month",
          icon: :right
        ) %>
        <%= render Hakumi::Button::Component.new(
          type: :text,
          size: :small,
          id: "cal-next-year",
          icon: :double_right
        ) %>
      </div>
    </div>
  <% end %>
<% end %>

<script>
  (() => {
    const calendarEl = document.getElementById("custom-header-calendar")
    if (!calendarEl) return

    let wired = false
    const wire = () => {
      if (wired) return true
      
      const api = calendarEl.hakumiComponent?.api
      if (!api) return false

      const getTitleEl = () => document.getElementById("cal-header-title")

      const updateTitle = () => {
        const date = api.getValue()
        const monthNames = ["January", "February", "March", "April", "May", "June",
                           "July", "August", "September", "October", "November", "December"]
        const title = getTitleEl()
        if (title) {
          title.textContent = `${monthNames[date.getMonth()]} ${date.getFullYear()}`
        }
      }

      let detachControls = () => {}
      const attachControls = () => {
        detachControls()

        const removers = []
        const bindControl = (id, handler) => {
          const el = document.getElementById(id)
          if (!el) return
          const wrapped = (event) => {
            event.preventDefault()
            handler()
          }
          el.addEventListener("click", wrapped)
          removers.push(() => el.removeEventListener("click", wrapped))
        }

        bindControl("cal-prev-year", () => api.prevYear())
        bindControl("cal-prev-month", () => api.prevMonth())
        bindControl("cal-next-month", () => api.nextMonth())
        bindControl("cal-next-year", () => api.nextYear())

        detachControls = () => removers.forEach((off) => off())
      }

      calendarEl.addEventListener("hakumi--calendar:change", () => {
        updateTitle()
        attachControls()
      })

      attachControls()
      updateTitle()
      wired = true
      return true
    }

    if (wire()) return
    const interval = setInterval(() => { if (wire()) clearInterval(interval) }, 50)
    setTimeout(() => clearInterval(interval), 5000)
  })()
</script>

Calendar API

Prop Type Default Description
value Date or String Date.today The current selected date
default_value Date or String - The initial date when first rendered
mode :month or :year :month The display mode of the calendar
fullscreen Boolean true Whether to display in fullscreen mode
show_week Boolean false Whether to show week number column
locale :en or :es :en The locale for day/month names
valid_range [Date, Date] - Range of valid selectable dates
year_range Range or Array ±50 years Years to show in year selector. E.g., 2020..2030 or [2020, 2021, 2022]
events Hash {} Events to display on dates. Format: { Date => [{ type: :success/:warning/:error, content: 'text' }] }
date_cell_render Proc - Custom render function for date cells. Receives date as argument. Overrides events prop.
month_cell_render Proc - Custom render function for month cells. Receives month number as argument.
header_render Proc or false - Custom header render function, or false to hide header
disabled_date Proc - Function to determine if a date is disabled. Receives date as argument.
static Boolean auto Prevent client-side re-rendering. Auto-enabled when using date_cell_render/month_cell_render.

Calendar Slots

Prop Type Default Description
header_content Slot - Custom content for the calendar header

JavaScript API (element.hakumiCalendar)

Prop Type Default Description
getValue() Function - Returns the current Date object
setValue(date) Function - Sets the calendar to the specified date
getMode() Function - Returns current mode ('month' or 'year')
setMode(mode) Function - Sets the display mode
goToToday() Function - Navigates to current date
nextMonth() Function - Navigates to next month
prevMonth() Function - Navigates to previous month
nextYear() Function - Navigates to next year
prevYear() Function - Navigates to previous year

Events

Prop Type Default Description
hakumi--calendar:select Event - Fired when a date/month is selected. Detail: { date, source }
hakumi--calendar:change Event - Fired when year/month changes. Detail: { year, month, date }
hakumi--calendar:panel_change Event - Fired when mode changes. Detail: { mode, year, month }