CalendarComponent
View SourceLibrería de componentes Phoenix LiveView que renderiza un calendario interactivo basado en EventCalendar y un hook de LiveView. Se distribuye como librería (no una app completa) e incluye assets JS/CSS colocalizados.
Usage
Render the calendar component in HEEx and wire events with Phoenix LiveView JS:
alias Phoenix.LiveView.JS
~H"""
<.calendar
id="calendar"
events={@events}
on_event_click={JS.push("event_clicked", value: %{id: event.id})}
on_date_click={JS.push("date_clicked", value: %{date: date})}
on_month_change={JS.push("month_changed", value: %{month: month})}
/>
"""Server-side handlers (example):
@impl true
def handle_event("event_clicked", %{"id" => id}, socket), do: {:noreply, socket}
@impl true
def handle_event("date_clicked", %{"date" => date}, socket), do: {:noreply, socket}
@impl true
def handle_event("month_changed", %{"month" => month, "year" => year}, socket), do: {:noreply, socket}Register the JS hook in your app’s LiveSocket (ensure the built asset is loaded so window.LiveCalendarHooks exists):
import {Socket} from "phoenix"
import {LiveSocket} from "phoenix_live_view"
const Hooks = window.LiveCalendarHooks || {}
const liveSocket = new LiveSocket("/live", Socket, { hooks: Hooks })
liveSocket.connect()Note: This library compiles a hook at priv/static/assets/calendar-hooks.js and CSS at priv/static/assets/calendar-hooks.css. Ensure the asset is served/loaded in your host app (or copy the hook code into your app assets and register it).
Options mapping
- Pass any EventCalendar options via the
:optionsassign of<.calendar ... />. They are forwarded to the JS calendar. - Special integration key:
options.lvlets you rename the server events the hook will push:lv.onEventClick(default:"event_clicked")lv.onDateClick(default:"date_clicked")lv.onMonthChange(default:"month_changed")
- If you provide JS handlers in
options.eventClick,options.dateClick, oroptions.datesSet, the hook composes them and still pushes to LiveView. - The hook auto-picks plugins based on
options.view(timeGrid*,dayGrid*,list*). You can also passoptions.pluginsexplicitly if needed.
Implemented features in hook/component:
- Container render with
phx-hook="LiveCalendar"and data-json forevents/options. - JS initialization with
createCalendar(...)and plugin selection. - LiveView push for
eventClick,dateClick,datesSet(month change). - Runtime updates via
setOption("events", ...)and applying changed options onupdated. - Cleanup on
destroyedwithdestroyCalendar(...).
Refer to docs/event_calendar.md for the full list of EventCalendar options and methods. Most options can be passed through :options; unsupported options should be raised as issues/PRs if necessary.
Installation
If available in Hex, the package can be installed
by adding calendar_component to your list of dependencies in mix.exs:
def deps do
[
{:calendar_component, "~> 0.1.3"}
]
endLa documentación se genera con ExDoc y se publica en HexDocs. Una vez publicada, puede consultarse en https://hexdocs.pm/calendar_component.
Build assets
Run the esbuild task to (re)compile the JS hooks and CSS output:
mix assets.build
La documentación actual menciona usar:
import {Hooks as calendar_hook} from "calendar_component";Phoenix examples
1) Básico: render estático con eventos
LiveView module:
defmodule MyAppWeb.CalendarLive do
use MyAppWeb, :live_view
def mount(_params, _session, socket) do
events = [
%{id: 1, title: "Reunión", start: "2025-08-01T09:00:00"},
%{id: 2, title: "Demo", start: "2025-08-02"}
]
{:ok, assign(socket, events: events)}
end
endHEEx:
<.calendar id="calendar" events={@events} />2) Manejo de eventos del calendario (click de evento/fecha/mes)
Usa los nombres por defecto que el hook envía: "event_clicked", "date_clicked", "month_changed".
@impl true
def handle_event("event_clicked", %{"id" => id}, socket) do
{:noreply, put_flash(socket, :info, "Evento #{id} clickeado")}
end
@impl true
def handle_event("date_clicked", %{"date" => iso}, socket) do
{:noreply, put_flash(socket, :info, "Fecha #{iso}")}
end
@impl true
def handle_event("month_changed", %{"month" => m, "year" => y}, socket) do
{:noreply, socket}
end3) Personalizar vista y toolbar
opts = %{
view: "dayGridMonth",
headerToolbar: %{start: "title", end: "today prev,next"},
nowIndicator: true,
firstDay: 1
}
~H"""
<.calendar id="cal_toolbar" events={@events} options={opts} />
"""4) Actualizar eventos en vivo (agregar tras click en fecha)
@impl true
def handle_event("date_clicked", %{"date" => iso}, socket) do
new = %{id: System.unique_integer([:positive]), title: "Nuevo", start: iso}
{:noreply, update(socket, :events, fn ev -> [new | ev] end)}
end5) Renombrar eventos que el hook empuja (options.lv)
opts = %{lv: %{onEventClick: "my_event_click", onDateClick: "my_date_click", onMonthChange: "my_month"}}
~H"""
<.calendar id="cal_lv" events={@events} options={opts} />
"""
@impl true
def handle_event("my_event_click", payload, socket), do: {:noreply, socket}6) List view y localización
opts = %{
view: "listWeek",
locale: "es",
height: "auto",
dayMaxEvents: true
}
~H"""
<.calendar id="cal_list" events={@events} options={opts} />
"""7) Resource Timeline (plugins y recursos)
Para vistas de recursos/timeline, pasa los plugins explícitamente en options.plugins y datos en options.resources.
resources = [
%{id: "r1", title: "Sala A"},
%{id: "r2", title: "Sala B"}
]
events = [
%{id: "e1", title: "Reserva", start: "2025-08-03T10:00:00", end: "2025-08-03T12:00:00", resourceId: "r1"}
]
opts = %{
view: "resourceTimelineWeek",
resources: resources,
plugins: :keep_plugins, # ver nota debajo
headerToolbar: %{start: "title", end: "today prev,next"}
}
~H"""
<.calendar id="cal_resources" events={events} options={opts} />
"""
# Nota: Este paquete selecciona plugins por `view` (timeGrid/dayGrid/list) automáticamente.
# Para ResourceTimeline/ResourceTimeGrid, asegúrate de que tu asset incluya los plugins necesarios.
# Si usas los hooks compilados por esta librería, considera copiar el hook a tu app
# e importar los plugins de `@event-calendar/core` que requieras, por ejemplo:
# import { ResourceTimeline, ResourceTimeGrid } from "@event-calendar/core"
# y pasar `options.plugins: [ResourceTimeline]`.8) Edición drag & drop y resize
Habilita editable: true y maneja eventDrop/eventResize en el servidor o del lado del cliente.
opts = %{
view: "timeGridWeek",
editable: true,
eventDurationEditable: true
}
~H"""
<.calendar id="cal_edit" events={@events} options={opts} />
"""
# Del lado del servidor, el hook no empuja automáticamente estos eventos.
# Puedes manejarlo del lado del cliente con `options.eventDrop`/`eventResize` y
# usar `pushEvent` manualmente (copiando el hook a tu app para extenderlo) o enviar vía AJAX.