UNPKG

@symbiot.dev/react-native-timegrid

Version:

A lightweight and customizable time grid calendar component for React Native, Web, and Expo.

533 lines (402 loc) โ€ข 26.3 kB
<div align="center"> <h1>React Native TimeGrid ๐Ÿ—“๏ธ ๐Ÿš€</h1> </div> > A powerful and flexible time grid and calendar component for **React Native**, designed to work seamlessly across **iOS**, **Android**, **Web**, **Expo**, and **Expo Go**. Built for performance and customization, react-native-timegrid enables developers to implement rich, interactive calendar views for a wide range of scheduling and planning needs. <div align="center"> <img alt="NPM downloads" src="https://img.shields.io/npm/dw/@symbiot.dev/react-native-timegrid?logo=npm&label=npm&cacheSeconds=3600"/> <img alt="NPM version" src="https://img.shields.io/npm/v/@symbiot.dev/react-native-timegrid?logo=npm&label=npm&cacheSeconds=3600"/> <img alt="Bundle size" src="https://img.shields.io/bundlephobia/minzip/@symbiot.dev/react-native-timegrid?label=size&cacheSeconds=3600"/> <img alt="Typescript" src="https://img.shields.io/static/v1?label=&message=TS&color=blue"/> </div> --- #### ๐Ÿ“ฑ Demo <img alt="Demo" src="https://github.com/symbiotdev/react-native-timegrid/blob/main/demo.gif" width="300px" height="600px" /> --- ## [โœจ **Key Features**](#features) ๐Ÿ“† **Time-Based Grid Layout** โ€“ Intuitive hourly/daily layout ideal for agenda, schedule, or shift views. ๐ŸŽจ **Fully Customizable** โ€“ Modify headers, time slots, event appearance, and grid styling to fit any design system. ๐Ÿง  **Lightweight** โ€“ Minimal dependencies and optimized bundle size for faster load times and efficient performance. ๐Ÿš€ **Cross-Platform Compatibility** โ€“ Works out of the box with: - **React Native CLI** - **Expo** - **Expo Go** - **React Native Web** for browser support โš™๏ธ **Optimized Performance** โ€“ Supports virtual scrolling and large datasets with smooth interactions. โœ‹ **Interactive Features** โ€“ Tap, drag, and dynamic updates for real-time scheduling apps. ๐Ÿ“ฑ **Responsive Design** โ€“ Adapts to various screen sizes and device orientations. ๐Ÿงฉ **Modular Integration** โ€“ Use as a standalone calendar or integrate into more complex scheduling systems. --- ## ๐Ÿ›  [**Ideal For**](#for) - ๐Ÿ“… Personal or team calendars - ๐Ÿ›Ž๏ธ Booking and reservation systems (salons, gyms, clinics) - ๐Ÿ‘ฅ Employee shift and availability planners - ๐Ÿ“† Event timeline visualizations - ๐Ÿง‘โ€๐Ÿซ Class schedules and academic timetables - ๐Ÿง  Task and time management tools - ๐Ÿฅ Appointment scheduling for healthcare or therapy - ๐Ÿ“ฆ Delivery or logistics scheduling - ๐Ÿ’ผ Project planning dashboards with time allocation - ๐Ÿ›  Maintenance or service job assignment trackers - ๐ŸŽ“ Student agendas or learning planners - ๐ŸŽค Conference session planners and expo booth schedules - ๐Ÿ”ฅ and more ... Whether you're building a cross-platform scheduling app or integrating a responsive calendar view into an existing project, react-native-timegrid provides a modern, customizable, and performant solution for all major platforms. --- ## [๐Ÿ‘ฉ๐Ÿฟโ€๐Ÿ’ป **Installation**](#installation) ```bash npm install @symbiot.dev/react-native-timegrid react-native-reanimated react-native-gesture-handler date-fns # or yarn add @symbiot.dev/react-native-timegrid react-native-reanimated react-native-gesture-handler date-fns # for web support npm i react-native-web # or yarn add react-native-web # to support recurring events npm i rrule # or yarn add rrule ``` ```tsx import { TimeGrid } from '@symbiot.dev/react-native-timegrid'; // ... and voilร  ๐Ÿ’ซ <TimeGrid /> ``` > Just import it and boom โ€” youโ€™ve got a slick, cross-platform calendar view. > Fast setup, full control, and zero headaches. ๐Ÿ’ช๐Ÿป๐Ÿ’ฅ --- ## ๐ŸŽจ `TimeGridThemeProps` โ€“ [Theming & Appearance](#theme) Customize the look and feel of the `TimeGrid` with flexible styling options. You can define global styles, override them based on theme (`light` or `dark`), or combine both approaches. ### ๐Ÿงฉ Structure The `TimeGridThemeProps` type supports three layers: 1. **Global Styling (applies to all schemes unless overridden)** 2. **Per-scheme Overrides** โ€“ Use `light` and `dark` keys to define specific values 3. **Theme Scheme Selector** โ€“ Control active theme using `scheme` ```ts type TimeGridThemeProps = TimeGridStyling & { scheme?: 'light' | 'dark'; // system default light?: TimeGridStyling; dark?: TimeGridStyling; }; ``` ### ๐Ÿงฑ Available Styling Props | Prop Name | Description | iOS | Android | Web | Default Value | |---------------------------------------|----------------------------------------------------|:---:|:-------:|:---:|---------------------------------------| | `backgroundColor` | Main background color of the grid view | โœ… | โœ… | โœ… | light: `#FFFFFF`, dark: `#000000` | | `dayHeaderBackgroundColor` | Background for the day-of-week header bar | โœ… | โœ… | โœ… | light: `#FFFFFF`, dark: `#000000` | | `dayHeaderTextColor` | Text color for day-of-week labels | โœ… | โœ… | โœ… | light: `#000000`, dark: `#FFFFFF` | | `headerSafeAreaBackgroundColor` | Background for top safe area (e.g., notch spacing) | โœ… | โœ… | โœ… | light: `#FFFFFF`, dark: `#000000` | | `allDayEventsSafeAreaBackgroundColor` | Background for all-day events safe area | โœ… | โœ… | โœ… | light: `#FFFFFF`, dark: `#000000` | | `allDayEventsSafeAreaTextColor` | Text color for all-day events safe area | โœ… | โœ… | โœ… | light: `#000000`, dark: `#FFFFFF` | | `timelineBackgroundColor` | Background color for timeline column | โœ… | โœ… | โœ… | light: `#FFFFFF`, dark: `#000000` | | `timelineTextColor` | Text color for hour/time labels | โœ… | โœ… | โœ… | light: `#A9A9A9`, dark: `#A9A9A9` | | `verticalLineColor` | Color of vertical time slot dividers | โœ… | โœ… | โœ… | light: `#F1F1F1`, dark: `#303030` | | `horizontalLineColor` | Color of horizontal grid lines | โœ… | โœ… | โœ… | light: `#F1F1F1`, dark: `#303030` | | `nowIndicatorColor` | Color of the โ€œnowโ€ line showing current time | โœ… | โœ… | โœ… | `#FF6F61` | | `unavailableTimeBackgroundColor` | Color for visually marking unavailable time blocks | โœ… | โœ… | โœ… | light: `#F1F1F170`, dark: `#30303070` | | `dropZoneBackgroundColor` | Background for areas that accept dragged events | โœ… | โœ… | โœ… | `transparent` | | `eventBackgroundColor` | Default background for event blocks | โœ… | โœ… | โœ… | light: `#D8E4F1`, dark: `#475867` | | `eventTextColor` | Text color used in event blocks | โœ… | โœ… | โœ… | light: `#000000`, dark: `#FFFFFF` | | `eventBorderColor` | Border color for events | โœ… | โœ… | โœ… | light: `#FFFFFF`, dark: `#000000` | ```tsx <TimeGrid //... theme={{ backgroundColor: '#f0f0f0', // for light and dark mode }} /> // ---- <TimeGrid //... theme={{ backgroundColor: '#f0f0f0', // only applies to light mode eventTextColor: '#323232', // only applies to dark mode dark: { backgroundColor: '#020202', }, light: { eventTextColor: '#505050', } }} /> ``` --- ## ๐Ÿ“ `TimeGridDimensionsProps` โ€“ [Layout & Dimensions](#dimensions) Control the physical dimensions of key areas inside the `TimeGrid`. All values are in **pixels** and optional โ€” sensible defaults are provided. ### ๐Ÿงฑ Available Dimension Props | Prop Name | Description | iOS | Android | Web | Default Value | |-----------------------|-------------------------------------------------------|:---:|:-------:|:---:|---------------| | `width` | TimeGrid width | โœ… | โœ… | โœ… | `system` | | `dayHeaderHeight` | Height of the day header (weekday labels) | โœ… | โœ… | โœ… | `50` | | `allDayEventHeight` | Height for all-day events | โœ… | โœ… | โœ… | `25` | | `timelineWidth` | Width of the left-side timeline area | โœ… | โœ… | โœ… | `58` | | `horizontalLineSize` | Height of horizontal divider lines | โœ… | โœ… | โœ… | `0.5` | | `nowIndicatorHeight` | Thickness of the โ€œnowโ€ line | โœ… | โœ… | โœ… | `0.5` | | `gridVerticalPadding` | Defines the vertical spacing (top and bottom padding) | โœ… | โœ… | โœ… | `0` | | `eventBorderWidth` | Width of the border around an event element | โœ… | โœ… | โœ… | `1` | | `eventBorderRadius` | Radius of the border corners for an event element | โœ… | โœ… | โœ… | `5` | ```tsx <TimeGrid //... timelineWidth={60} /> ``` ___ ## ๐Ÿ“… `TimeGridDatetimeProps` โ€“ [Date & Time Configuration](#datetime) Set the visible time range, number of days, start date, timezone, and other calendar-related behaviors. These props give you precise control over how the `TimeGrid` renders days and time slots. ### ๐Ÿงฑ Available Datetime Props | Prop Name | Description | iOS | Android | Web | Default Value | |----------------|--------------------------------------------------------------------------|:---:|:-------:|:---:|---------------| | `startDate` | The starting datetime for the visible grid (defaults to current date) | โœ… | โœ… | โœ… | `new Date()` | | `weekStartsOn` | Day the week starts on (0=Sun, 1=Mon, etc.) | โœ… | โœ… | โœ… | `0` (Sunday) | | `hiddenDays` | Days of the week to hide (e.g., `[0, 6]` to hide Sun & Sat) | โœ… | โœ… | โœ… | `[]` | | `numberOfDays` | Number of days to render in the view | โœ… | โœ… | โœ… | `1` | | `timeFrom` | Start of the visible time range (in **minutes**, e.g., `480` = 8:00 AM) | โœ… | โœ… | โœ… | `0` | | `timeTo` | End of the visible time range (in **minutes**, e.g., `1200` = 8:00 PM) | โœ… | โœ… | โœ… | `1440` | | `timeInterval` | Minutes between each horizontal time line | โœ… | โœ… | โœ… | `60` | | `timeStep` | Time rounding when placing events (in minutes) | โœ… | โœ… | โœ… | `15` | | `minDate` | Earliest date to allow navigation | โœ… | โœ… | โœ… | `โ€”` | | `maxDate` | Latest date to allow navigation | โœ… | โœ… | โœ… | `โ€”` | | `timezone` | Timezone name (e.g., `'America/New_York', '+02:00'`) for offset handling | โœ… | โœ… | โœ… | `local` | ```tsx <TimeGrid //... weekStartsOn={1} // starts on Monday numberOfDays={3} hiddenDays={[0, 6]} // exclude weekends (Sun,Sut) /> ``` ___ ## ๐Ÿ—“๏ธ `TimeGridEventsProps` โ€“ [Events, Rendering & Recurrence](#events) Configure how events appear and behave inside the calendar. Supports custom colors, recurring events, readonly mode, and timezone-specific scheduling. > ๐ŸŸฆ๐ŸŸจ **Overlapping Events** > Events that share time ranges are **fully supported out-of-the-box**. > The grid will automatically stack or split them side-by-side depending on layout space. ### ๐Ÿงฑ Available Event Props | Prop Name | Description | iOS | Android | Web | Default Value | |----------------------------|--------------------------------------------------------------------|:---:|:-------:|:---:|---------------| | `createEventDuration` | Duration (in **minutes**) to assign when user creates a new event. | โœ… | โœ… | โœ… | `60` | | `allDayEventsSafeAreaText` | Placeholder for all-day events safe area | โœ… | โœ… | โœ… | `All-day` | | `events` | Array of `TimeGridEvent` to render inside the time grid | โœ… | โœ… | โœ… | `[]` | ### `TimeGridEvent` | Field Name | Description | Type | Required | Default | |-------------------|-------------------------------------------------|----------------------------|----------|--------------| | `id` | Unique identifier for the event | `string` | โœ… | โ€” | | `start` | Start datetime of the event | `Date \| string \| number` | โœ… | โ€” | | `end` | End datetime of the event | `Date \| string \| number` | โœ… | โ€” | | `text` | Event title or description | `string` | optional | `datetime` | | `backgroundColor` | Override default event background color | `string` | optional | `theme` | | `textColor` | Override text color inside the event | `string` | optional | `theme` | | `timezone` | IANA timezone name for localized event handling | `string` | optional | `grid-level` | | `readonly` | Prevent this event from being dragged | `boolean` | optional | `false` | | `rrule` | RRULE string (e.g., `'RRULE:FREQ=DAILY'`) | `string` | optional | โ€” | > โ„น๏ธ **Need help creating a recurrence rule?** > Use the visual generator at [rrule.js playground](https://jkbrzt.github.io/rrule/) to quickly create and test valid RRULE strings. ```tsx <TimeGrid //... createEventDuration={30} // new created events equals 30 mins events={[ //... { id: 'timestamp', start: '2025-04-25T18:00:00.500Z', end: '2025-04-25T19:00:00.800Z', backgroundColor: '#DDDDDD', textColor: '#000000', readonly: true // prevent from being dragged } ]} /> ``` ___ ## ๐Ÿšซ `TimeGridUnavailableTimeProps` โ€“ [Blocking Unavailable Times](unavailable-time) Mark specific time ranges as **unavailable** โ€” either for a specific date or recurring on weekdays. These periods can block event creation, display with a distinct background and could be used with a different combinations. ```ts export type TimeGridUnavailableTime = { from: number; // in minutes (e.g. 480 = 8:00 AM) to: number; // in minutes (e.g. 1020 = 5:00 PM) start?: Date | string; end?: Date | string; } & ( | { weekday?: Day; // 0 (Sunday) to 6 (Saturday) date?: never; } | { date?: Date | string; weekday?: never; } ); export type TimeGridUnavailableTimeProps = { unavailableTime?: TimeGridUnavailableTime[]; }; ``` ### ๐Ÿงฑ Available Unavailable Time Props | Prop Name | Description | iOS | Android | Web | Default Value | |-------------------|--------------------------------------------------------------------------------------------------------------|:---:|:-------:|:---:|---------------| | `unavailableTime` | Array of time rules (by weekday or specific date). Uses `from`/`to` in **minutes**; Priority: date, weekday; | โœ… | โœ… | โœ… | `[]` | ```tsx <TimeGrid //... unavailableTime={[ { from: 0, to: 480 } // everyday 00:00 (12:00 AM) -> 08:00 AM ]} /> // ---- <TimeGrid //... unavailableTime={[ { from: 0, to: 480, start: '2025-04-25T18:00:00.500Z' } // daily, starting April 25 ]} /> // ---- <TimeGrid //... unavailableTime={[ { from: 0, to: 480, start: '2025-04-25T00:00:00.500Z', end: '2025-04-26T00:00:00.500Z' } // only 2 days - April 25 and 26 ]} /> // ---- // Priority behaviour <TimeGrid //... unavailableTime={[ { from: 0, to: 480 }, // daily { from: 1020, to: 1440, weekday: 1 }, // ignore the first element of the array and apply every Monday { from: 480, to: 900, date: '2025-04-28T18:00:00.500Z' } // ignore the first and second rules for Monday, April 28th ]} /> // ---- // Save behaviour <TimeGrid //... unavailableTime={[ { from: 0, to: 480 }, // daily { from: 0, to: 480, date: '2025-04-28T18:00:00.500Z' }, // save the daily rule for April 28 { from: 480, to: 900, date: '2025-04-28T18:00:00.500Z' } // ignore the first and second rules for Monday, April 28th ]} /> ``` ___ ## ๐ŸŽฎ `TimeGridControllerProps` โ€“ [Interaction & Display Toggles](#controller) Control user interaction features like swiping, dragging, snapping, and visibility of UI components like the timeline, all-day events etc. ### ๐Ÿงฑ Available Controller Props | Prop Name | Description | iOS | Android | Web | Default Value | |-------------------------|----------------------------------------------------------------|:---:|:-------:|:---:|---------------| | `lazy` | Enable to improve performance on the slow devices | โœ… | โœ… | โœ… | `true` | | `swipeable` | Enable swiping between days or weeks | โœ… | โœ… | โœ… | `false` | | `snappable` | Snap scroll/drag to the nearest date | โœ… | โœ… | โœ… | `false` | | `draggable` | Allow dragging events to create/reschedule them | โœ… | โœ… | โœ… | `true` | | `debounceable` | Delays date change handler to avoid rapid successive triggers. | โœ… | โœ… | โœ… | `false` | | `rtl` | Enable right-to-left layout (for RTL languages) | โœ… | โœ… | โœ… | `false` | | `isDayHeaderVisible` | Toggle visibility of the top day-of-week header | โœ… | โœ… | โœ… | `true` | | `isTimelineVisible` | Toggle visibility of the left-side timeline | โœ… | โœ… | โœ… | `true` | | `isNowIndicatorVisible` | Show/hide the "now" indicator line | โœ… | โœ… | โœ… | `true` | | `isAllDayEventsEnabled` | Enable support for all-day events | โœ… | โœ… | โœ… | `true` | | `isAllDayEventsVisible` | Toggle visibility of the all-day events | โœ… | โœ… | โœ… | `true` | ```tsx <TimeGrid //... swipeable // swipe actions turned on draggable // user can drag events snappable // date snapping enabled /> ``` ___ ## โฑ `TimeGridFormatterProps` โ€“ [Text Formatting](#formatter) Customize how dates and times appear within the TimeGrid component using format strings (e.g., `dd`, `HH:mm`, `EEE`). These follow [date-fns](https://date-fns.org/docs/format) formatting syntax. ### ๐Ÿงฑ Available Formatter Props | Prop Name | Description | iOS | Android | Web | Default Value | |-----------------------|----------------------------------------------------------------|:---:|:-------:|:---:|---------------| | `dayHeaderTextFormat` | Format for the day header labels (e.g., `Mon`, `Apr 20`) | โœ… | โœ… | โœ… | `EEEEEE dd` | | `timelineTextFormat` | Format for the left-side time labels (e.g., `08:00`) | โœ… | โœ… | โœ… | `p` | | `locale` | Locale object from `date-fns` for internationalized formatting | โœ… | โœ… | โœ… | `en` | ```tsx import { zhCN } from 'date-fns/locale/zh-CN'; <TimeGrid //... locale={zhCN} // Chinese localization /> ``` ___ ## ๐ŸŽฏ `TimeGridActionsProps` โ€“ [User Interaction Callbacks](#actions) Listen and respond to interactions like taps, long presses, event edits, and grid context menus. ### ๐Ÿงฑ Available Action Callbacks | Prop Name | Description | iOS | Android | Web | |-------------------------------|------------------------------------------------------------------------------------------------|:---:|:-------:|:---:| | `onChangeDate` | Called when the grid's date range changes | โœ… | โœ… | โœ… | | `onHeaderSafeAreaPress` | Triggered when the top header area is tapped | โœ… | โœ… | โœ… | | `onAllDayEventsSafeAreaPress` | Triggered when the all-day events header area is tapped | โœ… | โœ… | โœ… | | `onDayHeaderPress` | Called when a specific day header is pressed | โœ… | โœ… | โœ… | | `onLoad` | Called once the grid is initialized and ready | โœ… | โœ… | โœ… | | `onUnavailableTimePress` | Fired on tap inside an unavailable time block | โœ… | โœ… | โœ… | | `onUnavailableTimeLongPress` | Fired on long press inside an unavailable time block | โœ… | โœ… | โœ… | | `onDatetimeContextMenu` | Right-click/context menu trigger at specific date/time (with x/y coords) | ๐Ÿšซ | ๐Ÿšซ | โœ… | | `onDatetimePress` | Triggered on a tap inside an available time cell | โœ… | โœ… | โœ… | | `onDatetimeLongPress` | Triggered on long press inside an available time cell | โœ… | โœ… | โœ… | | `onEventPress` | Called when an event is tapped | โœ… | โœ… | โœ… | | `onEventLongPress` | Called when an event is long-pressed | โœ… | โœ… | โœ… | | `onEventUpdated` | Triggered when an event is changed (drag); return `false` to keep updating, `reject` to cancel | โœ… | โœ… | โœ… | | `onEventCreated` | Triggered when a new event is created by the user | โœ… | โœ… | โœ… | ```tsx import { useState } from 'react'; const [selectedDate, setSelectedDate] = useState(new Date()); <TimeGrid //... onChangeDate={setSelectedDate} // selectedDate updated /> ``` ___ ## ๐Ÿ”ง `TimeGridRef` โ€“ [Imperative Control API](#ref) Control and interact with the TimeGrid instance programmatically using a React ref. Great for custom toolbars, buttons, or dynamic navigation. ### ๐Ÿงฑ Available Ref Methods | Method | Description | Returns | |-----------------|---------------------------------------------------------------------------------|---------------------| | `getPrevDate()` | Returns the previous valid date range (optionally ignoring `minDate`/`maxDate`) | `Date \| undefined` | | `getNextDate()` | Returns the next valid date range (optionally ignoring `minDate`/`maxDate`) | `Date \| undefined` | | `prevDate()` | Navigate to the previous available date | `void` | | `nextDate()` | Navigate to the next available date | `void` | | `back()` | Navigate backward through a specified number of days (`numberOfDays`) | `void` | | `forward()` | Navigate forward through a specified number of days (`numberOfDays`) | `void` | | `toDatetime()` | Scroll or jump to a specific date/time | `void` | | `createEvent()` | Programmatically create a new event at a given datetime | `void` | ```tsx import { useRef } from 'react'; import { TimeGrid, TimeGridRef } from '@symbiot.dev/react-native-timegrid'; const timeGridRef = useRef<TimeGridRef>(null); <TimeGrid //... ref={timeGridRef} onDatetimeLongPress={(params) => timeGridRef.current?.createEvent(params)} /> ``` ___ ### [๐ŸŒ **Platform Support**](#support) - โœ… iOS - โœ… Android - โœ… Web - โœ… Expo & Expo Go --- ### [๐Ÿ“ฆ **Bundle Size**](#size) Lightweight and fast โ€” see actual size on [Bundlephobia](https://bundlephobia.com/package/@symbiot.dev/react-native-timegrid). --- ### [๐Ÿ’ฌ Contributing](#contributing) Contributions welcome! Feel free to open issues, discussions, or suggestions. --- ### [๐ŸŒŸ Who's Using This?](#usage) Using `react-native-timegrid` in your app or product? Feel free to share your project โ€” it might be featured in this section! > โœ‰๏ธ Open pull request, or reach out directly to get included. ___ ### [๐Ÿš€ Got Ideas or Gaps to Fill?](#ideas) Know of existing libraries or functionality that could be improved? Have an idea for something completely new? **Symbiot** is ready to take on the challenge โ€” feedback, feature requests, or collaboration ideas are always welcome! > ๐Ÿง  Letโ€™s build better tools together. Start the conversation or contact via [email](mailto:info@symbiot.dev). ___ ### [๐Ÿงพ License](#license) MIT โ€” Made with โค๏ธ by [Symbiot](https://symbiot.dev)