{"id":51603150,"url":"https://github.com/afonsojramos/super-calendar","last_synced_at":"2026-07-11T23:31:02.492Z","repository":{"id":366621111,"uuid":"1277030997","full_name":"afonsojramos/super-calendar","owner":"afonsojramos","description":"Gesture-driven, virtualized month / week / day calendar and date picker for React Native and the web.","archived":false,"fork":false,"pushed_at":"2026-07-07T11:01:21.000Z","size":2856,"stargazers_count":178,"open_issues_count":0,"forks_count":3,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-07T11:10:22.952Z","etag":null,"topics":["agenda","android","calendar","date-picker","datepicker","day-view","expo","gesture-handler","ios","month-view","range-picker","react-native","react-native-web","reanimated","scheduler","timetable","typescript","week-view"],"latest_commit_sha":null,"homepage":"https://super-calendar.afonsojramos.me/","language":"TypeScript","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/afonsojramos.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},"funding":{"github":"afonsojramos"}},"created_at":"2026-06-22T14:14:43.000Z","updated_at":"2026-07-07T11:01:28.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/afonsojramos/super-calendar","commit_stats":null,"previous_names":["afonsojramos/react-native-bigger-calendar","afonsojramos/react-native-super-calendar","afonsojramos/super-calendar"],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/afonsojramos/super-calendar","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afonsojramos%2Fsuper-calendar","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afonsojramos%2Fsuper-calendar/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afonsojramos%2Fsuper-calendar/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afonsojramos%2Fsuper-calendar/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/afonsojramos","download_url":"https://codeload.github.com/afonsojramos/super-calendar/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afonsojramos%2Fsuper-calendar/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35377925,"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-11T02:00:05.354Z","response_time":104,"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":["agenda","android","calendar","date-picker","datepicker","day-view","expo","gesture-handler","ios","month-view","range-picker","react-native","react-native-web","reanimated","scheduler","timetable","typescript","week-view"],"created_at":"2026-07-11T23:31:01.625Z","updated_at":"2026-07-11T23:31:02.476Z","avatar_url":"https://github.com/afonsojramos.png","language":"TypeScript","funding_links":["https://github.com/sponsors/afonsojramos"],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003esuper-calendar\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003eA generic, themeable \u003cstrong\u003emonth / week / day\u003c/strong\u003e calendar for React Native.\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.npmjs.com/package/@super-calendar/native\"\u003e\u003cimg alt=\"npm version\" src=\"https://img.shields.io/npm/v/@super-calendar/native?style=flat-square\u0026amp;color=1F6FEB\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://jsr.io/@super-calendar/native\"\u003e\u003cimg alt=\"JSR version\" src=\"https://img.shields.io/jsr/v/@super-calendar/native?style=flat-square\u0026amp;label=JSR\u0026amp;color=F7DF1E\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://npmx.dev/package/@super-calendar/native\"\u003e\u003cimg alt=\"npmx\" src=\"https://img.shields.io/badge/npmx-view-8A2BE2?style=flat-square\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/afonsojramos/super-calendar/actions/workflows/ci.yml\"\u003e\u003cimg alt=\"CI status\" src=\"https://img.shields.io/github/actions/workflow/status/afonsojramos/super-calendar/ci.yml?branch=main\u0026amp;style=flat-square\u0026amp;label=CI\" /\u003e\u003c/a\u003e\n  \u003ca href=\"#license\"\u003e\u003cimg alt=\"MIT license\" src=\"https://img.shields.io/npm/l/@super-calendar/native?style=flat-square\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"Month, week, 3-day, day and schedule views of super-calendar\" src=\"./.github/assets/preview.png\" /\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://super-calendar.afonsojramos.me\"\u003e\u003cstrong\u003eDocumentation\u003c/strong\u003e\u003c/a\u003e ·\n  \u003ca href=\"https://super-calendar.afonsojramos.me/quickstart\"\u003eQuickstart\u003c/a\u003e ·\n  \u003ca href=\"https://super-calendar.afonsojramos.me/demo\"\u003eLive demo\u003c/a\u003e ·\n  \u003ca href=\"https://super-calendar.afonsojramos.me/reference/api\"\u003eAPI reference\u003c/a\u003e\n\u003c/p\u003e\n\n- 📆 Month grid plus day / 3-day / week / custom-N time-grids\n- 🤏 Zoomable week/day grid: pinch on iOS \u0026 Android, Ctrl/Cmd + scroll on web (UI thread, no re-renders)\n- ♾️ Virtualized, snap-paging months/weeks/days via [`@legendapp/list`](https://legendapp.com/open-source/list/)\n- 🧩 Bring-your-own event type (`CalendarEvent\u003cT\u003e`) and a `renderEvent` escape hatch\n- 🗓️ Date selection (single / multiple / range via `useDateRange`), disabled days, and a scrolling `MonthList`\n- 🪝 Headless `useMonthGrid` hook to build a fully custom calendar\n- 🎨 Fully themeable, with sensible defaults (no styling library required)\n- 🌐 Runs on iOS, Android and web (web via [react-native-web](https://necolas.github.io/react-native-web/); see [Web](#web))\n\n## Relationship to react-native-big-calendar\n\nThis is a ground-up reimagining inspired by the excellent\n[`react-native-big-calendar`](https://github.com/acro5piano/react-native-big-calendar).\nIt keeps the familiar month/week/day model but is built around Reanimated and\nmodern list virtualization — trading framework-agnosticism for a richer,\ngesture-driven experience. It's **not a fork**; the API differs, and the name is\nan homage. 🙇\n\n**Already using react-native-big-calendar?** The [migration guide](https://super-calendar.afonsojramos.me/migrating-from-big-calendar) has a copy-paste prompt for your coding agent plus a manual prop mapping.\n\n### At a glance\n\n| Capability                                   | super-calendar                             | react-native-big-calendar       |\n| -------------------------------------------- | ------------------------------------------ | ------------------------------- |\n| Month / week / day / 3-day / custom / agenda | ✅                                         | ✅                              |\n| Generic event typing (`CalendarEvent\u003cT\u003e`)    | ✅                                         | ✅                              |\n| Virtualized, snap-paged views                | ✅                                         | ❌ renders all dates            |\n| Pinch-to-zoom (native) / Ctrl-scroll (web)   | ✅                                         | ❌                              |\n| Drag to move \u0026 resize events                 | ✅                                         | ❌ (declined upstream)          |\n| Date selection (single / range / multiple)   | ✅ `useDateRange` + disabled days          | ❌                              |\n| Headless grid hook (`useMonthGrid`)          | ✅                                         | ❌                              |\n| Overlapping events                           | ✅ side-by-side columns                    | ⚠️ stacked / indented           |\n| Month paging fires `onChangeDate`            | ✅                                         | ⚠️ known gaps                   |\n| Recurring events                             | ✅ `expandRecurringEvents`                 | ❌ expand them yourself         |\n| Time-zone display                            | ✅ `eventsInTimeZone`                      | ❌                              |\n| Dark mode                                    | ✅ `darkTheme` preset                      | ❌ bring your own palette       |\n| `renderEvent` across every mode \u0026 event type | ✅                                         | ⚠️ breaks for all-day/multi-day |\n| Web                                          | ✅ arrow-key paging, Ctrl-scroll zoom      | ⚠️ partial                      |\n| Runtime dependencies                         | Reanimated + Gesture Handler + Legend List | dayjs + calendarize (lighter)   |\n\nLegend: ✅ supported · ⚠️ partial or with known issues · ❌ not available. The\nlast row is the honest trade-off: big-calendar has a smaller footprint and fewer\nnative peers, so it can be the simpler choice when you don't need the gestures,\nvirtualization, or the helpers above.\n\n**What it adds over react-native-big-calendar**\n\n- 🤏 **Pinch-to-zoom** time grid — row height is a Reanimated shared value, so\n  zooming runs on the UI thread with zero React re-renders.\n- ♾️ **Virtualized, snap-paged** views (via `@legendapp/list`) — swipe across\n  years of dates, with native one-page paging (or opt into `freeSwipe`).\n- 🧩 **Generic events + render-prop _component_** — `CalendarEvent\u003cT\u003e` carries your\n  own fields, and `renderEvent` is a component (so it may use hooks) that receives\n  the event box's live pixel height for progressive disclosure as the grid zooms.\n\n**Feature parity.** It also covers the rest of react-native-big-calendar's\nsurface: month / day / 3-day / week / **custom N-day** (and `weekEndsOn`\npartial-weeks) / **agenda (`schedule`)** modes, **all-day events** (lane +\n`allDay` flag, toggle the lane with `showAllDayEventCell`), multi-day clipping,\n`minHour`/`maxHour`, `ampm` (hour axis and event times), `showTime`, `timeslots`,\n`hideHours`, `showWeekNumber`, `weekNumberPrefix`, `hourComponent`,\n`sortedMonthView`, `moreLabel`, `showAdjacentMonths`, `showSixWeeks`,\n`disableMonthEventCellPress`, a default month weekday header\n(`renderHeaderForMonthView`), a custom month date badge\n(`renderCustomDateForMonth`), `activeDate`, per-event\n`disabled`, `onPress`/`onLongPress` for events, cells and date headers,\n`onChangeDateRange`, `resetPageOnPressCell`, `swipeEnabled`,\n`verticalScrollEnabled`, `showVerticalScrollIndicator`, an agenda\n`itemSeparatorComponent`, `eventCellStyle`, `calendarCellStyle`, a\n`headerComponent` slot, date-fns `locale`, right-to-left column order (`isRTL`),\nand theming. Text styling that big-calendar exposes via `calendarCellTextStyle`\nis covered by `CalendarTheme.text`; overlapping events are laid out in\nside-by-side columns automatically.\n\n**Trade-offs (where react-native-big-calendar may suit you better)**\n\n- It's **opinionated about peers**: Reanimated, Gesture Handler and\n  `@legendapp/list` are required. `react-native-big-calendar` is more\n  self-contained (no Reanimated/Gesture Handler).\n- **RTL** is cosmetic (`isRTL` reverses the day-column order, like\n  big-calendar's): the hour gutter stays on the left and paging follows the\n  system scroll direction. Enable React Native's `I18nManager` for full RTL.\n\n## Relationship to flash-calendar\n\nThe date-picker surface (`MonthList`, `useDateRange`, and the headless\n`useMonthGrid`) is inspired by\n[`flash-calendar`](https://github.com/MarceloPrado/flash-calendar), an excellent\nheadless date picker for React Native. If you only need date selection,\nflash-calendar is the lighter, more focused choice: a dedicated, FlashList-based\npicker with no event model. super-calendar folds picking into a\nfull gesture calendar, so one library covers events and date selection, at the\ncost of the Reanimated, Gesture Handler, and Legend List peers. Pick\nflash-calendar for a standalone picker; pick this when you also need the event\nviews.\n\n## Install\n\n```sh\nnpm install @super-calendar/native\n```\n\nAlso published on [JSR](https://jsr.io/@super-calendar/native): `deno add jsr:@super-calendar/native` (or `npx jsr add @super-calendar/native`).\n\n### Peer dependencies\n\nThe full calendar relies on the following being installed in your app:\n\n```sh\nnpm install react-native-reanimated react-native-worklets react-native-gesture-handler @legendapp/list date-fns\n```\n\nMake sure Reanimated and Gesture Handler are set up per their own docs (Babel\nplugin, `GestureHandlerRootView` at the root of your app).\n\nThese are declared as optional peers so web-only installs (`@super-calendar/dom`)\nand the picker (the `@super-calendar/native/picker` subpath) aren't asked to\ninstall React Native packages they don't use. The full calendar still needs them:\nbecause its components import Reanimated\nand Gesture Handler directly, a missing one surfaces as a clear Metro\n`Unable to resolve \"react-native-reanimated\"` build error rather than a silent\nfailure, so install the line above when you use `Calendar` or the time grid.\n\n### Picker only? Skip Reanimated\n\nIf you only need date selection, import it from the\n`@super-calendar/native/picker` subpath. It contains the month grid,\nselection, and the headless `useMonthGrid`, with **none of the timetable code and\nno Reanimated dependency**, so it works on every bundler (Metro included) without\nshipping the week/day grid. A picker-only app installs just:\n\n```sh\nnpm install react-native-gesture-handler @legendapp/list date-fns\n```\n\n```tsx\nimport { MonthList, useDateRange } from \"@super-calendar/native/picker\";\n```\n\n`react-native-reanimated` and `react-native-worklets` are declared as optional\npeers, so this subpath won't pull them in. (Metro doesn't tree-shake the\n`@super-calendar/native` barrel, so the subpath is what guarantees the timetable\ncode is left out.)\n\n### React DOM (web without React Native)\n\nFor a plain react-dom app (no React Native, no react-native-web), install the\n`@super-calendar/dom` package. It ships real DOM components,\n`MonthView`, `MonthList` (the date picker), and `TimeGrid` (day/week/N-day, with\nCtrl/⌘-scroll and pinch zoom plus drag to move and resize), built on the same\npure core and Legend List's DOM renderer. A web app installs just:\n\n```sh\nnpm install @super-calendar/dom react react-dom @legendapp/list date-fns\n```\n\n```tsx\nimport { MonthList, TimeGrid, useDateRange } from \"@super-calendar/dom\";\n```\n\nThe React Native peers (`react-native`, `react-native-gesture-handler`,\n`react-native-reanimated`, `react-native-worklets`) are all optional, so a web\ninstall pulls none of them. Styling is plain inline styles driven by a `theme`\nprop (`defaultDomTheme` / `darkDomTheme`), no stylesheet import required.\n\nA selected range renders as a centered rounded \"pill\" band by default (its\nheight and colour are the `rangeBandHeight` / `rangeBackground` theme tokens).\nPass `fillCellOnSelection` to `MonthView` / `MonthList` to fill the whole cell\nedge to edge instead.\n\n### Headless core (any renderer)\n\nWant the date math and selection model without any of the built-in UI? The\n`@super-calendar/core` package exports just the pure pieces,\n`buildMonthGrid` / `useMonthGrid`, `useDateRange` and the selection helpers,\n`layoutDayEvents`, and the date utilities, with zero React Native, Reanimated, or\nLegend List imports. It's what the DOM components are built on, and it works in\nany React renderer (react-dom, Solid via its React compat, your own).\n\n```sh\nnpm install @super-calendar/core react date-fns\n```\n\n```tsx\nimport { buildMonthGrid, nextDateRange } from \"@super-calendar/core\";\n```\n\n## Usage\n\n```tsx\nimport { useState } from \"react\";\nimport { Calendar, type CalendarEvent } from \"@super-calendar/native\";\n\ntype MyEvent = { id: string; color: string };\n\nconst events: CalendarEvent\u003cMyEvent\u003e[] = [\n  {\n    id: \"1\",\n    color: \"#1F6FEB\",\n    title: \"Lecture\",\n    start: new Date(2026, 5, 19, 10, 0),\n    end: new Date(2026, 5, 19, 11, 30),\n  },\n];\n\nexport function MyCalendar() {\n  const [mode, setMode] = useState\u003c\"month\" | \"week\" | \"day\"\u003e(\"week\");\n  const [date, setDate] = useState(new Date());\n\n  return (\n    \u003cCalendar\n      mode={mode}\n      date={date}\n      events={events}\n      weekStartsOn={1}\n      onChangeDate={setDate}\n      onPressEvent={(event) =\u003e console.log(event.id)}\n      onPressDay={(day) =\u003e {\n        setDate(day);\n        setMode(\"day\");\n      }}\n    /\u003e\n  );\n}\n```\n\n### Custom events\n\nThe built-in renderer draws a simple titled box. Pass `renderEvent` — **a React\ncomponent**, not a callback — to take full control. Because it's rendered as a\ncomponent, it may use hooks. The same renderer is used in **every** mode — month\nchips, the all-day lane, the timed grid and the schedule list — and always\nreceives `isAllDay` (plus `continuesBefore`/`continuesAfter` for clipped\nmulti-day segments on the grid), so one component covers them all. On the\nweek/day grid you also receive `boxHeight`, a Reanimated shared value tracking\nthe live pixel height of the box (driven by pinch-zoom), so you can reveal\ndetail progressively without re-rendering:\n\n```tsx\nimport Animated, { useAnimatedStyle } from \"react-native-reanimated\";\nimport { Pressable, Text } from \"react-native\";\nimport type { RenderEventArgs } from \"@super-calendar/native\";\n\n// Define the component once (don't inline it, or it remounts every render).\nfunction MyEvent({ event, boxHeight, onPress }: RenderEventArgs\u003cMyEvent\u003e) {\n  const detailStyle = useAnimatedStyle(() =\u003e ({\n    display: (boxHeight?.value ?? Infinity) \u003e= 84 ? \"flex\" : \"none\",\n  }));\n  return (\n    \u003cPressable style={{ flex: 1, backgroundColor: event.color }} onPress={onPress}\u003e\n      \u003cText\u003e{event.title}\u003c/Text\u003e\n      \u003cAnimated.View style={detailStyle}\u003e\n        \u003cText\u003e{event.start.toLocaleTimeString()}\u003c/Text\u003e\n      \u003c/Animated.View\u003e\n    \u003c/Pressable\u003e\n  );\n}\n\n\u003cCalendar /* ... */ renderEvent={MyEvent} /\u003e;\n```\n\nThe built-in renderer hard-clips a title that overflows its box. Pass\n`ellipsizeTitle` to `\u003cCalendar\u003e` for a trailing ellipsis (…) instead.\n\n### Drag to move and resize\n\nPass `onDragEvent` to make events draggable on the week/day grid. Move an event\n(**long-press** it on native, **click-drag** it on web) — drag **vertically to\nchange the time, horizontally to move it to another day** (within the visible\nrange) — or **drag the grip at its bottom edge** to resize. The handler receives\nthe new `start`/`end`, snapped to `dragStepMinutes` (default 15) — update your own\nevent state in response. On web a plain click still selects and right-click still\nfires, so drag coexists with both:\n\n```tsx\n\u003cCalendar\n  /* ... */\n  onDragEvent={(event, start, end) =\u003e\n    setEvents((prev) =\u003e prev.map((e) =\u003e (e.id === event.id ? { ...e, start, end } : e)))\n  }\n/\u003e\n```\n\n**Reject a drop.** Return `false` from `onDragEvent` to refuse the new placement\n— the event snaps back to where it started. Use it to forbid overlaps,\nout-of-bounds slots, or locked events:\n\n```tsx\nonDragEvent={(event, start, end) =\u003e {\n  if (event.locked || overlapsAnother(event, start, end)) return false;\n  setEvents((prev) =\u003e prev.map((e) =\u003e (e.id === event.id ? { ...e, start, end } : e)));\n}}\n```\n\n**Haptics on grab.** `onDragStart` fires the instant an event is picked up for a\nmove or resize, before anything is committed. The library stays expo-free, so\nbring your own haptics, e.g. [`expo-haptics`](https://docs.expo.dev/versions/latest/sdk/haptics/):\n\n```tsx\nimport * as Haptics from \"expo-haptics\";\n\n\u003cCalendar\n  /* ... */\n  onDragStart={() =\u003e {\n    void Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);\n  }}\n/\u003e;\n```\n\n**Drag to create.** Pass `onCreateEvent` to sweep out a new event on empty grid\nspace: **long-press and drag** on native, **click-drag** on web. The handler\nreceives the snapped `start`/`end` on release (a stationary press yields a\none-step range) — create your own event in response. On native it supersedes\n`onLongPressCell` on empty space; on web, dragging empty space creates instead of\nscrolling (use the wheel to scroll), matching desktop calendars, and **Escape**\ncancels an in-progress sweep before it commits.\n\n```tsx\n\u003cCalendar\n  /* ... */\n  onCreateEvent={(start, end) =\u003e\n    setEvents((prev) =\u003e [...prev, { id: makeId(), title: \"New event\", start, end }])\n  }\n/\u003e\n```\n\n### Recurring events\n\nGive an event a `recurrence` rule and expand it into concrete occurrences for the\nrange you're showing with `expandRecurringEvents`. The calendar doesn't expand\nrecurrences itself, so you control the window (and can memoize it):\n\n```tsx\nimport { Calendar, expandRecurringEvents } from \"@super-calendar/native\";\n\nconst events = [\n  // Every weekday standup, 20 occurrences:\n  {\n    title: \"Standup\",\n    start,\n    end,\n    recurrence: { freq: \"weekly\", weekdays: [1, 2, 3, 4, 5], count: 20 },\n  },\n];\n\nconst visible = expandRecurringEvents(events, rangeStart, rangeEnd);\n\u003cCalendar /* ... */ events={visible} /\u003e;\n```\n\nRules support `freq` (`daily`/`weekly`/`monthly`/`yearly`), `interval`, `count`,\n`until`, and `weekdays` (for `weekly`). Each occurrence keeps the original\nduration and fields; non-recurring events pass through unchanged.\n\n### Date selection\n\nDate picking lives on `MonthList`, the vertically-scrolling month list (the\nhorizontally-paged `month` view is for browsing events, not picking). A range's\nendpoints get a filled badge (the `selectedBackground` token) and the span gets\na centered rounded \"pill\" band behind it; today keeps its own badge. For ranges,\nthe `useDateRange` hook\nowns the state machine: the first press sets the start, the second sets the end\n(auto-swapping if earlier), a third press starts over. Tap two days, or\nlong-press and drag to sweep a range (the list auto-scrolls at the edges, so a\nrange can span months):\n\n```tsx\nimport { MonthList, useDateRange } from \"@super-calendar/native\";\n\nfunction RangePicker() {\n  const [date, setDate] = useState(new Date());\n  const { range, onPressDate, selectRange } = useDateRange();\n\n  return (\n    \u003cMonthList\n      date={date}\n      weekStartsOn={1}\n      selectedRange={range ?? undefined}\n      onPressDay={onPressDate}\n      onSelectDrag={selectRange}\n      onChangeVisibleMonth={setDate}\n    /\u003e\n  );\n}\n```\n\nUse `selectedDates` to mark discrete days instead of a range. The band's colour\nand height are the `rangeBackground` / `rangeBandHeight` theme tokens; pass\n`fillCellOnSelection` to `MonthList` to fill the whole cell edge to edge instead\nof the pill.\n\n**Disabled days.** `minDate`, `maxDate` and `isDateDisabled` render days dimmed,\nignore taps, and keep them out of any selection (drag included). Hand the same\nconstraints to `useDateRange` so a blocked day never opens a range:\n\n```tsx\nconst minDate = useMemo(() =\u003e new Date(), []); // no past dates\nconst { range, onPressDate, selectRange } = useDateRange({ minDate });\n\n\u003cMonthList\n  date={date}\n  weekStartsOn={1}\n  selectedRange={range ?? undefined}\n  minDate={minDate}\n  isDateDisabled={(d) =\u003e d.getDay() === 0} // also block Sundays\n  onPressDay={onPressDate}\n  onSelectDrag={selectRange}\n/\u003e;\n```\n\n### Month list\n\n`MonthList` is the continuous, virtualized vertical scroll of months behind the\npicker above (a month title then its grid, under a fixed weekday header), sized\nper month with no adjacent-month fill. It also renders events: pass `events`,\nand optionally a `renderEvent`. Both `renderEvent` and `keyExtractor` default,\nso an events-free picker needs neither:\n\n```tsx\nimport { MonthList } from \"@super-calendar/native\";\n\n\u003cMonthList\n  date={new Date()}\n  events={events}\n  weekStartsOn={1}\n  renderEvent={MyEvent}\n  keyExtractor={(event) =\u003e event.id}\n  onChangeVisibleMonth={setDate}\n/\u003e;\n```\n\n### Headless month grid\n\nWant your own day-cell markup but not the date maths? `useMonthGrid(month,\noptions)` returns the weeks, the weekday headers, and per-day state\n(`isToday`/`isSelected`/`isInRange`/`isDisabled`/`isCurrentMonth`/…) for you to\nrender however you like:\n\n```tsx\nimport { useMonthGrid } from \"@super-calendar/native\";\n\nconst { weeks, weekdays } = useMonthGrid(month, { weekStartsOn: 1, selectedRange: range });\n// weekdays -\u003e header cells; weeks[].days -\u003e your own \u003cDayCell /\u003e\n```\n\nNeed it outside React (tests, exports)? Call the pure `buildMonthGrid(month,\noptions)`; the hook is just a memoized wrapper. `buildMonthWeeks(month,\nweekStartsOn)` returns the raw `Date[][]`.\n\n### Time zones\n\nEvents lay out from their local wall-clock time. To display them in a specific\nIANA zone regardless of the device, run them through `eventsInTimeZone` (or a\nsingle date through `toZonedTime`). It's DST-correct via `Intl`:\n\n```tsx\nimport { Calendar, eventsInTimeZone } from \"@super-calendar/native\";\n\n// Render every event at its New York wall-clock time.\n\u003cCalendar /* ... */ events={eventsInTimeZone(events, \"America/New_York\")} /\u003e;\n```\n\nThe returned dates are for display only — they carry the zone's wall clock, not\nthe original instant, so keep your source events around for editing/saving.\n\n### Theming\n\n```tsx\n\u003cCalendar\n  // ...\n  theme={{\n    colors: { todayBackground: \"#E5484D\", nowIndicator: \"#E5484D\" },\n    text: { dayNumber: { fontSize: 24, fontWeight: \"800\" } },\n  }}\n/\u003e\n```\n\nSee `CalendarTheme` for the full set of tokens. Anything you omit falls back to\n`defaultTheme`.\n\nFor dark mode, pass the built-in `darkTheme` (switch on the system scheme with\n`useColorScheme()`):\n\n```tsx\nimport { Calendar, darkTheme, defaultTheme } from \"@super-calendar/native\";\nimport { useColorScheme } from \"react-native\";\n\nconst scheme = useColorScheme();\n\u003cCalendar /* ... */ theme={scheme === \"dark\" ? darkTheme : defaultTheme} /\u003e;\n```\n\n### Modes\n\n`mode` is one of `month`, `week`, `day`, `3days`, `custom`, or `schedule`. For\n`custom`, set `numberOfDays` (e.g. `mode=\"custom\" numberOfDays={5}` for a\nwork-week). Day/3-day/custom views page by their column count; `week` pages by\nthe calendar week. `schedule` is a vertical, day-grouped agenda list of the\nevents you pass (no time grid).\n\n### Month view\n\nEach day cell shows as many event chips as its height allows and collapses the\nrest into a `+N more` label (tap it via `onPressMore`). The fit is measured at\nruntime, so taller grids (fewer week rows, larger screens) show more.\n\n```tsx\n\u003cCalendar mode=\"month\" /* ... */ /\u003e          // auto-fit (default)\n\u003cCalendar mode=\"month\" maxVisibleEventCount={3} /* ... */ /\u003e // fixed cap\n```\n\nPass `maxVisibleEventCount` for a fixed cap instead — recommended when you pass a\ncustom `renderEvent`, since auto-fit assumes the built-in chip height. Customize\nthe overflow text with `moreLabel` (e.g. `\"+{moreCount}\"`).\n\n### Localization\n\nPass a date-fns [`Locale`](https://date-fns.org/docs/I18n) to localize weekday and\ndate labels:\n\n```tsx\nimport { fr } from \"date-fns/locale\";\n\n\u003cCalendar /* ... */ locale={fr} weekStartsOn={1} /\u003e;\n```\n\nPass `isRTL` to reverse the day-column order in every view (month grid, week/day\ngrid and the all-day lane). It's cosmetic — the hour gutter stays on the left and\npaging follows the system scroll direction — so enable React Native's\n`I18nManager` alongside it for full right-to-left behaviour.\n\n```tsx\n\u003cCalendar /* ... */ isRTL locale={ar} weekStartsOn={6} /\u003e\n```\n\n### Week/day grid options\n\n```tsx\n\u003cCalendar\n  mode=\"week\"\n  // ...\n  minHour={7} // window the grid to 07:00–21:00\n  maxHour={21}\n  ampm // 12-hour hour labels (\"7 AM\")\n  onPressCell={(date) =\u003e createEventAt(date)} // tap empty space -\u003e date+time\n/\u003e\n```\n\n- `minHour` / `maxHour` clamp the visible hours (defaults `0` / `24`); events and\n  the now-line outside the window are hidden, and the initial scroll is adjusted.\n- `ampm` switches hour labels to 12-hour AM/PM (default 24h).\n- `onPressCell(date)` fires when empty grid space is tapped, with the date+time\n  under the touch — handy for \"create event\". (Event taps still go to `onPressEvent`.)\n- **Long-press** mirrors every tap: `onLongPressEvent(event)`, `onLongPressCell(date)`\n  (week/day), and `onLongPressDay(date)` (month). All optional.\n- **All-day events** render in a lane above the time grid (and as chips in month\n  cells), excluded from the timed columns. Mark an event `allDay: true`, or it's\n  inferred when it spans whole days (midnight-to-midnight). `renderEvent` receives\n  `isAllDay` so you can style the chip. The lane is hidden when there are none.\n- `freeSwipe` (default `false`) controls paging: by default one day/week/month\n  moves per swipe; set it to allow a fling to carry across several pages (still\n  snapping to a page boundary). Applies to all modes.\n\n### Business hours\n\nPass `businessHours` to tint the closed hours on the week/day grid. It's a\nfunction of the day, so open hours can vary (and weekends can read as closed) —\nreturn `{ start, end }` (hours, fractions allowed) to shade outside that range,\nor `null` to shade the whole day. The tint colour is the theme's\n`outsideHoursBackground`.\n\n```tsx\n\u003cCalendar\n  /* ... */\n  businessHours={(date) =\u003e {\n    const weekday = date.getDay();\n    if (weekday === 0 || weekday === 6) return null; // weekends closed\n    return { start: 9, end: 17 };\n  }}\n/\u003e\n```\n\n### Web\n\nThe calendar runs on [react-native-web](https://necolas.github.io/react-native-web/);\nits dependencies (`@legendapp/list` v3, Reanimated and Gesture Handler) all\nsupport web. Add the web peers to your app:\n\n```sh\nnpx expo install react-dom react-native-web @expo/metro-runtime\n```\n\nAll modes render and navigate. Two touch gestures are remapped for web:\nhorizontal swipe paging becomes **←** / **→** arrow-key paging (previous / next\npage), and pinch-to-zoom on the week/day grid becomes **Ctrl/Cmd + scroll**. The\nrunnable [`example/`](./example) builds with `expo start --web`.\n\nIf a `renderEvent` wraps events in a portaling overlay (a context menu, popover,\netc.) from a UI library, portal it into your app's React root, not\n`document.body`. react-native-web registers React's event delegation on the root\nelement (`#root` under Expo), so an overlay mounted outside it renders correctly\nbut its click handlers never fire. Most libraries take a `container` prop for\nthis; the example's context menu portals into `#root` for exactly this reason.\n\n## Components\n\n`\u003cCalendar\u003e` is the batteries-included entry point. The building blocks it wraps\nare also exported for advanced layouts:\n\n| Export             | Description                                                |\n| ------------------ | ---------------------------------------------------------- |\n| `Calendar`         | Top-level component; switches between month/week/day.      |\n| `MonthView`        | A single month grid.                                       |\n| `MonthPager`       | Horizontally-paged, virtualized months.                    |\n| `MonthList`        | Vertically-scrolling, continuous list of months.           |\n| `TimeGrid`         | Paged, pinch-zoomable week/day time-grid.                  |\n| `DefaultEvent`     | The built-in event renderer.                               |\n| `useDateRange`     | Range-selection state machine for the month view.          |\n| `useMonthGrid`     | Headless grid data (weeks + per-day state) for custom UIs. |\n| `useCalendarTheme` | Read the active theme inside a custom renderer.            |\n\n## Notes \u0026 limitations\n\n- **Multi-day events** are supported: pass one event and it appears on every day\n  it spans. On the week/day grid each day shows the clipped segment (so a\n  23:00→01:00 event renders 23:00–24:00, then 00:00–01:00), and `renderEvent`\n  receives `continuesBefore`/`continuesAfter` so you can draw continuation hints.\n  All-day events (an explicit `allDay` flag or a midnight-to-midnight span) render\n  in a dedicated lane above the time grid.\n- **`weekStartsOn` defaults to `0` (Sunday).** Pass `1` for Monday-first.\n- **Controlled `date`.** The calendar is controlled: echo `onChangeDate` back\n  into the `date` prop, or paging and the \"today\" realign won't track.\n- **External `cellHeight`.** If you own `cellHeight`, drive zoom through the\n  pinch gesture. Programmatic writes outside a pinch won't propagate to\n  off-screen pages until the next gesture settles.\n- **Stable props.** Pass stable `renderEvent`/`keyExtractor`/`on*` references\n  (module scope or `useCallback`) so the memoized inner views can skip renders.\n\n## Example app\n\nA runnable Expo demo lives in [`example/`](./example) — month/week/day modes, a\nmulti-day event, drill-into-day on tap, and one-page paging.\n\n```sh\ncd example\npnpm install\npnpm expo run:ios   # or: pnpm expo run:android\n```\n\nIt consumes the library straight from `../src` (via the example's\n`metro.config.js`), so edits to the package hot-reload into the demo. A custom\ndev build is required (Reanimated worklets aren't available in Expo Go).\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fafonsojramos%2Fsuper-calendar","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fafonsojramos%2Fsuper-calendar","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fafonsojramos%2Fsuper-calendar/lists"}