Flash Calendar

website·Indexed 30 days ago

https://marceloprado.github.io/flash-calendar

A fast and flexible library for building calendars in React Native, featuring a date range picker and customizable components.

Tokens
4.8K
Snippets
20
Records
27
Agent score
75%

What's inside Flash Calendar

  1. Flash Calendar core capabilities and scope

    Flash Calendar is designed as a lightweight, high-performance library for building calendars and calendar lists. It specifically targets use cases such as infinite lists, date pickers, and date range pickers. To maintain a small footprint and high polish, complex features like 'agenda mode' are explicitly out of scope.
  2. Work-around for backward scrolling in Flash Calendar

    To enable scrolling to previous months, you can preload the required months by combining calendarMinDateId and a sufficiently large calendarPastScrollRangeInMonths. While this keeps the month list in memory and may impact performance, Flash List is capable of handling large lists (e.g., 2,000 months).
    export const ScrollingBackwardsWorkaround = () => {
      return (
        <VStack alignItems="stretch" grow spacing={12}>
          <Text>This preloads all past months between Jan 1st 2020 and today</Text>
    
          <Calendar.List
            calendarFutureScrollRangeInMonths={1}
            calendarInitialMonthId="2024-02-01"
            calendarMaxDateId="2024-05-01"
            calendarMinDateId="2020-01-01"
            calendarPastScrollRangeInMonths={50}
            onCalendarDayPress={loggingHandler("onCalendarDayPress")}
          />
        </VStack>
      );
    };
  3. Track visible months in Calendar.List

    You can determine which months are currently visible to the user by using the onViewableItemsChanged prop on Calendar.List. This prop follows the FlashList API, providing a list of viewableItems where you can extract the month ID from the item.
    const handleViewableItemsChanged = useCallback(({ viewableItems }) => {
      const firstVisibleItem = viewableItems.find((item) => item.isViewable);
      if (firstVisibleItem) {
        console.log("Visible month ID:", firstVisibleItem.item.id);
      }
    }, []);
    
    <Calendar.List
      onViewableItemsChanged={handleViewableItemsChanged}
      // ... other props
    />
  4. Install Flash Calendar in React Native

    Flash Calendar is a fast and flexible calendar builder for React Native that supports iOS, Android, and Expo (no binary updates required). It features built-in localization, dark mode, infinite scroll, and date range support with a small bundle size (18kb minified).
    npm add @marceloterreiro/flash-calendar
  5. Render multiple calendars on a single screen

    To render more than one calendar instance on the same screen, provide a unique identifier to the calendarInstanceId prop. This is required for both the Calendar and Calendar.List components to ensure they operate independently. Each calendar should also be paired with its own date range state (e.g., via useDateRange).
    import { Calendar, useDateRange } from "@marceloterreiro/flash-calendar";
    
    export const TwoCalendarsMounted = () => {
      const dateRangeOne = useDateRange();
      const dateRangeTwo = useDateRange();
      return (
        <VStack grow spacing={48}>
          <VStack grow spacing={4}>
            <Text>First calendar</Text>
            <Calendar
              calendarInstanceId="First"
              calendarMonthId="2024-08-01"
              {...dateRangeOne}
            />
          </VStack>
          <VStack grow spacing={4}>
            <Text>Second calendar</Text>
            <Calendar
              calendarInstanceId="Second"
              calendarMonthId="2024-08-01"
              {...dateRangeTwo}
            />
          </VStack>
        </VStack>
      );
    };
  6. Programmatically scroll to a date or month

    The Calendar.List component exposes a ref of type CalendarListRef that allows imperative scrolling. Available methods include .scrollToDate(date, animated, options), .scrollToMonth(date, animated), and .scrollToOffset(offset). You can use the additionalOffset option to fine-tune the scroll position.
    import { useRef } from "react";
    import type { CalendarListRef } from "@marceloterreiro/flash-calendar";
    import { Calendar, fromDateId } from "@marceloterreiro/flash-calendar";
    
    const ref = useRef<CalendarListRef>(null);
    
    // Scroll to a specific date
    ref.current?.scrollToDate(fromDateId("2024-07-01"), true);
    
    // Scroll to a date with a fine-tuned offset
    ref.current?.scrollToDate(fromDateId("2024-07-01"), true, { additionalOffset: 4 });
    
    // Usage in JSX
    <Calendar.List ref={ref} />
  7. Integrate Calendar.List with Android Bottom Sheets

    To use Calendar.List inside an Android bottom sheet (e.g., @gorhom/react-native-bottom-sheet), replace the default FlashList by passing a compatible scroll component to the CalendarScrollComponent prop. It is recommended to use the default FlashList on iOS for better performance.
    import { Calendar } from "@marceloterreiro/flash-calendar";
    import { FlashList } from "@shopify/flash-list";
    import { Platform } from "react-native";
    import { BottomSheetFlashList } from "./components/BottomSheetFlashList";
    
    const SafeFlashList = Platform.select({
      android: BottomSheetFlashList,
      ios: FlashList,
    });
    
    export const BottomSheetCalendar = () => {
      return (
        <Calendar.List
          CalendarScrollComponent={SafeFlashList}
          calendarInitialMonthId="2024-02-01"
          onCalendarDayPress={(dateId) => console.log(dateId)}
        />
      );
    };
  8. Create a date range picker using useDateRange

    The useDateRange hook simplifies the implementation of date range selection by managing the active date ranges and providing a press handler compatible with Calendar and Calendar.List.
    import { Calendar, useDateRange } from "@marceloterreiro/flash-calendar";
    
    export const CalendarListDateRange = () => {
      const {
        calendarActiveDateRanges,
        onCalendarDayPress,
        // dateRange, // { startId?: string, endId?: string }
        // isDateRangeValid, // boolean
        // onClearDateRange, // () => void
      } = useDateRange();
      return (
        <Calendar.List
          calendarActiveDateRanges={calendarActiveDateRanges}
          onCalendarDayPress={onCalendarDayPress}
        />
      );
    };
  9. Optimize Flash Calendar list performance

    The <Calendar.List /> component is a wrapper around Shopify's FlashList. It inherits all of FlashList's performance characteristics and optimization benefits. Developers should apply standard FlashList performance tuning guides to optimize the calendar list. The library is optimized to ensure that only affected dates re-render during updates, maintaining responsiveness regardless of list length.
  10. Optimize Calendar.List performance when using date ranges

    Frame drops in Calendar.List often occur due to excessive re-renders of BaseCalendar components when the calendarActiveDateRanges prop changes. To maintain 60 FPS, avoid recreating the onCalendarDayPress handler on every render. Instead, memoize the handler using useCallback and use the state updater function pattern to keep props stable.
    import type { CalendarOnDayPress } from "@marceloterreiro/flash-calendar";
    import { Calendar, toDateId } from "@marceloterreiro/flash-calendar";
    import { useCallback, useState } from "react";
    
    export const OptimizedExample = () => {
      const [dateIds, setDateIds] = useState<string[]>([]);
      const dateRanges = dateIds.map((dateId) => ({
        startId: dateId,
        endId: dateId,
      }));
    
      // Fix: memoized handler and updater function pattern
      const handleCalendarDayPress = useCallback<CalendarOnDayPress>((dateId) => {
        setDateIds((prevDateIds) => {
          if (prevDateIds.includes(dateId)) {
            return prevDateIds.filter((id) => id !== dateId);
          } else {
            return [...prevDateIds, dateId];
          }
        });
      }, []);
    
      return (
        <Calendar.List
          calendarActiveDateRanges={dateRanges}
          onCalendarDayPress={handleCalendarDayPress}
          // ... other props
        />
      );
    };