{"id":51555970,"url":"https://github.com/mdon/phoenix_live_calendar","last_synced_at":"2026-07-10T05:00:48.494Z","repository":{"id":368230315,"uuid":"1283265589","full_name":"mdon/phoenix_live_calendar","owner":"mdon","description":"A comprehensive calendar and scheduling component library for Phoenix LiveView","archived":false,"fork":false,"pushed_at":"2026-06-29T16:54:09.000Z","size":129,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-29T17:07:17.367Z","etag":null,"topics":["calendar","elixir","liveview","phoenix","phoenix-liveview","scheduling"],"latest_commit_sha":null,"homepage":null,"language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mdon.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-06-28T18:23:06.000Z","updated_at":"2026-06-29T16:54:30.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mdon/phoenix_live_calendar","commit_stats":null,"previous_names":["mdon/phoenix_live_calendar"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/mdon/phoenix_live_calendar","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdon%2Fphoenix_live_calendar","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdon%2Fphoenix_live_calendar/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdon%2Fphoenix_live_calendar/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdon%2Fphoenix_live_calendar/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mdon","download_url":"https://codeload.github.com/mdon/phoenix_live_calendar/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdon%2Fphoenix_live_calendar/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35321224,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-10T02:00:06.465Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["calendar","elixir","liveview","phoenix","phoenix-liveview","scheduling"],"created_at":"2026-07-10T05:00:45.286Z","updated_at":"2026-07-10T05:00:48.488Z","avatar_url":"https://github.com/mdon.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PhoenixLiveCalendar\n\nA comprehensive calendar and scheduling component library for Phoenix LiveView.\n\nServer-rendered calendar views with optional drag interactions, real-time PubSub sync, booking constraints, and Ecto persistence. Zero JavaScript required for the base layer.\n\n## Phoenix-first — it looks right without JavaScript\n\nThis is the guiding principle: every view (month, week, day, N-day, year,\nagenda, timeline, resource) is computed **in Elixir** and rendered as plain\nHEEx + Tailwind over the LiveView socket — no charting JS, no `\u003ccanvas\u003e`, and\nnothing that has to boot on the client for the layout to be correct. The JS\nhooks are **progressive enhancement only** (drag-to-select / move / resize, the\nday-marker ticker, touch handling). With them absent the calendar still renders\nand works: navigation, view switching, date/event clicks, and the detail\npopover are all server-driven `phx-click`s, and a day with multiple markers\nstill shows its first marker (you only lose the cycling). Add the hooks for\nricher interaction; never depend on them for the page to look right.\n\n## Features\n\n- **8 view types**: Month, Week, Day, N-day (flexible), Year, Agenda, Timeline, Resource columns\n- **Pure Elixir base layer**: Works without any JavaScript\n- **Progressive enhancement**: Optional JS hooks for drag-to-select, drag-to-move, resize\n- **Real-time sync**: Optional PubSub integration for multi-user calendars\n- **Booking system**: Availability windows, slot constraints, capacity, buffers, validation\n- **Accessibility-minded**: ARIA grid roles, roving tabindex, and screen-reader labels (full arrow-key grid navigation + focus restoration are on the roadmap)\n- **RTL support**: Full right-to-left layout for Arabic, Hebrew, Persian, Urdu\n- **i18n**: All labels translatable via Gettext or override map\n- **Tailwind CSS**: Uses daisyUI semantic classes, works with any Tailwind theme\n- **Optional Ecto**: Opt-in persistence with Oban-style versioned migrations\n- **Dashboard-ready**: All components work at any container size\n\n\u003e **View maturity:** All eight views render server-side and work today. **Month**\n\u003e is the most polished and the view tuned for small screens; the others are\n\u003e functional but less refined — in particular the time-grid views (week / day /\n\u003e N-day) are not yet optimised for phone widths.\n\n## Installation\n\nAdd `phoenix_live_calendar` to your dependencies:\n\n```elixir\ndef deps do\n  [\n    {:phoenix_live_calendar, \"~\u003e 0.1.0\"}\n  ]\nend\n```\n\nAdd to your `assets/css/app.css` so Tailwind scans the component templates:\n\n```css\n@source \"../../deps/phoenix_live_calendar\";\n```\n\n### Optional: JS hooks\n\nFor drag interactions, add to `assets/js/app.js`:\n\n```javascript\nimport \"../../deps/phoenix_live_calendar/priv/static/assets/phoenix_live_calendar.js\"\n\nlet liveSocket = new LiveSocket(\"/live\", Socket, {\n  hooks: { ...window.PhoenixLiveCalendarHooks, ...Hooks }\n})\n```\n\n### Optional: Ecto persistence\n\n```elixir\n# config/config.exs\nconfig :phoenix_live_calendar, repo: MyApp.Repo\n\n# Generate and run the migration\nmix ecto.gen.migration add_phoenix_live_calendar\n```\n\nEdit the migration:\n\n```elixir\ndefmodule MyApp.Repo.Migrations.AddPhoenixLiveCalendar do\n  use Ecto.Migration\n\n  def up, do: PhoenixLiveCalendar.Store.Ecto.Migrations.up(version: 1)\n  def down, do: PhoenixLiveCalendar.Store.Ecto.Migrations.down(version: 1)\nend\n```\n\n## Quick Start\n\n```elixir\ndefmodule MyAppWeb.CalendarLive do\n  use MyAppWeb, :live_view\n\n  def mount(_params, _session, socket) do\n    events = [\n      PhoenixLiveCalendar.event(\"1\", ~U[2026-04-01 09:00:00Z],\n        title: \"Team Standup\",\n        end: ~U[2026-04-01 09:30:00Z],\n        color: \"bg-primary\"\n      ),\n      PhoenixLiveCalendar.event(\"2\", ~D[2026-04-05],\n        title: \"Company Holiday\",\n        all_day: true,\n        color: \"bg-success\"\n      )\n    ]\n\n    {:ok, assign(socket, events: events)}\n  end\n\n  def render(assigns) do\n    ~H\"\"\"\n    \u003c.live_component\n      module={PhoenixLiveCalendar.CalendarComponent}\n      id=\"my-calendar\"\n      events={@events}\n      views={[:month, :week, :day, :agenda]}\n      on_date_select={fn date -\u003e send(self(), {:date_selected, date}) end}\n      on_event_click={fn id -\u003e send(self(), {:event_clicked, id}) end}\n    /\u003e\n    \"\"\"\n  end\n\n  def handle_info({:date_selected, date}, socket) do\n    IO.inspect(date, label: \"Selected date\")\n    {:noreply, socket}\n  end\n\n  def handle_info({:event_clicked, event_id}, socket) do\n    IO.inspect(event_id, label: \"Clicked event\")\n    {:noreply, socket}\n  end\nend\n```\n\n## Configuration Options\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `view` | atom | `:month` | Initial view (`:month`, `:week`, `:day`, `:year`, `:agenda`, `:timeline`, `:resource`) |\n| `views` | list | `[:month, :week, :day]` | Available views in the switcher |\n| `date` | Date | today | Initial date |\n| `week_start` | integer | `1` | First day of week (1=Mon, 7=Sun) |\n| `min_time` | Time | `~T[00:00:00]` | Earliest visible time in grid |\n| `max_time` | Time | `~T[23:59:59]` | Latest visible time in grid |\n| `slot_duration` | integer | `30` | Time slot duration in minutes |\n| `time_format` | atom | `:h24` | `:h24` or `:h12` |\n| `show_week_numbers` | boolean | `false` | Show ISO week numbers |\n| `show_weekends` | boolean | `true` | Show Saturday/Sunday |\n| `max_events` | integer | `3` | Max events per month cell |\n| `n_days` | integer | `4` | Number of days for N-day view |\n| `dir` | atom | `:ltr` | Text direction (`:ltr` or `:rtl`) |\n| `translations` | map | `%{}` | Label overrides |\n| `business_hours` | list | `[]` | Availability windows to highlight |\n\n## Callbacks\n\n| Callback | Payload | Description |\n|----------|---------|-------------|\n| `on_date_select` | `Date.t()` | Date clicked |\n| `on_time_select` | `%{date, time, datetime, resource_id}` | Time slot clicked |\n| `on_event_click` | `event_id` | Event clicked |\n| `on_view_change` | `%{view, date}` | View switched |\n| `on_date_range_change` | `%{start, end, view, date}` | Visible range changed |\n\n## Using Individual Views\n\nYou can use any view component standalone without the LiveComponent wrapper:\n\n```elixir\n\u003cPhoenixLiveCalendar.Views.MonthGrid.month_grid\n  date={~D[2026-04-01]}\n  events={@events}\n  on_date_click={JS.push(\"date_clicked\")}\n/\u003e\n\n\u003cPhoenixLiveCalendar.Views.Agenda.agenda\n  date={Date.utc_today()}\n  events={@events}\n  days={14}\n/\u003e\n```\n\n## License\n\nMIT License - see [LICENSE](LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmdon%2Fphoenix_live_calendar","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmdon%2Fphoenix_live_calendar","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmdon%2Fphoenix_live_calendar/lists"}