### Install Flash Calendar Package Source: https://marceloprado.github.io/flash-calendar Add the Flash Calendar package to your project using npm. ```bash npm add @marceloterreiro/flash-calendar ``` -------------------------------- ### Slow Calendar.List with Date Ranges (Problematic) Source: https://marceloprado.github.io/flash-calendar/fundamentals/troubleshooting This example demonstrates performance issues with Calendar.List due to excessive re-renders when date ranges change. Avoid this pattern; it's for illustrating the problem. ```typescript import { Calendar, toDateId } from "@marceloterreiro/flash-calendar"; import { addMonths } from "date-fns"; import { Text } from "react-native"; const todayId = toDateId(new Date()); const maxDateId = toDateId(addMonths(new Date(), 12)); export const SlowExample = () => { const [dateIds, setDateIds] = useState([]); const dateRanges = dateIds.map((dateId) => ({ startId: dateId, endId: dateId, })); return ( ⚠️ Don't copy-paste this example, it has performance issues { if (dateIds.includes(dateId)) { setDateIds(dateIds.filter((id) => id !== dateId)); } else { setDateIds([...dateIds, dateId]); } }} /> ); }; ``` -------------------------------- ### FlashList Integration for Scroll Performance Source: https://marceloprado.github.io/flash-calendar/fundamentals/principles The `` component is a wrapper around FlashList, inheriting its performance characteristics. Performance tuning guides for FlashList apply directly. ```javascript import { FlashList } from '@shopify/flash-list'; const CalendarList = () => ( } estimatedItemSize={100} // Example size /> ); ``` -------------------------------- ### Apply Linear Theme to Calendar - JavaScript Source: https://marceloprado.github.io/flash-calendar/fundamentals/customization This example demonstrates how to create a custom theme object to style the calendar, mimicking the appearance of Linear's calendar. It defines styles for various calendar elements like month headers, week names, and day items, including active and today states. ```javascript import { CalendarTheme } from "@marceloterreiro/flash-calendar"; const linearAccent = "#585ABF"; const linearTheme: CalendarTheme = { rowMonth: { content: { textAlign: "left", color: "rgba(255, 255, 255, 0.5)", fontWeight: "700", }, }, rowWeek: { container: { borderBottomWidth: 1, borderBottomColor: "rgba(255, 255, 255, 0.1)", borderStyle: "solid", }, }, itemWeekName: { content: { color: "rgba(255, 255, 255, 0.5)" } }, itemDayContainer: { activeDayFiller: { backgroundColor: linearAccent, }, }, itemDay: { idle: ({ isPressed, isWeekend }) => ({ container: { backgroundColor: isPressed ? linearAccent : "transparent", borderRadius: 4, }, content: { color: isWeekend && !isPressed ? "rgba(255, 255, 255, 0.5)" : "#ffffff", }, }), today: ({ isPressed }) => ({ container: { borderColor: "rgba(255, 255, 255, 0.5)", borderRadius: isPressed ? 4 : 30, backgroundColor: isPressed ? linearAccent : "transparent", }, content: { color: isPressed ? "#ffffff" : "rgba(255, 255, 255, 0.5)", }, }), active: ({ isEndOfRange, isStartOfRange }) => ({ container: { backgroundColor: linearAccent, borderTopLeftRadius: isStartOfRange ? 4 : 0, borderBottomLeftRadius: isStartOfRange ? 4 : 0, borderTopRightRadius: isEndOfRange ? 4 : 0, borderBottomRightRadius: isEndOfRange ? 4 : 0, }, content: { color: "#ffffff", }, }), }, }; export const LinearCalendar = memo(() => { return ( console.log(`Pressed date ${dateId}`)} theme={linearTheme} /> ); }); ``` -------------------------------- ### Demonstrate Scrolling Backwards Limitation in Calendar Source: https://marceloprado.github.io/flash-calendar/fundamentals/limitations This example shows the limitation where the calendar does not scroll past the initial month when attempting to scroll backwards. It highlights the expected versus actual behavior. ```javascript export const ScrollingBackwardsLimitation = () => { return ( Notice how it doesn't scroll past January ); }; ``` -------------------------------- ### Optimized Calendar.List with Date Ranges (Solution) Source: https://marceloprado.github.io/flash-calendar/fundamentals/troubleshooting This example shows the corrected approach for Calendar.List performance issues using a memoized `onCalendarDayPress` handler and the updater function pattern. This prevents unnecessary re-renders. ```typescript import type { CalendarOnDayPress } from "@marceloterreiro/flash-calendar"; import { Calendar, toDateId } from "@marceloterreiro/flash-calendar"; import { addMonths } from "date-fns"; import { useCallback, useState } from "react"; import { Text } from "react-native"; const todayId = toDateId(new Date()); const maxDateId = toDateId(addMonths(new Date(), 12)); export const SlowExampleAddressed = () => { const [dateIds, setDateIds] = useState([]); const dateRanges = dateIds.map((dateId) => ({ startId: dateId, endId: dateId, })); // This is the fix: memoized onCalendarDayPress and updater function pattern // It keeps `BaseCalendar` props stable, allowing each month to skip re-renders const handleCalendarDayPress = useCallback((dateId) => { setDateIds((dateIds) => { if (dateIds.includes(dateId)) { return dateIds.filter((id) => id !== dateId); } else { return [...dateIds, dateId]; } }); }, []); return ( ✅ This is safe to copy, perf issues addressed ); }; ``` -------------------------------- ### Customize Calendar Theme with Interaction States Source: https://marceloprado.github.io/flash-calendar/fundamentals/customization Leverage `isHovered` and `isFocused` alongside `isPressed` to support web Pressable InteractionStates. This example defines a custom theme for a linear calendar appearance. ```typescript import { CalendarTheme } from "@marceloterreiro/flash-calendar"; const linearAccent = "#585ABF"; const linearTheme: CalendarTheme = { rowMonth: { content: { textAlign: "left", color: "rgba(255, 255, 255, 0.5)", fontWeight: "700", }, }, rowWeek: { container: { borderBottomWidth: 1, borderBottomColor: "rgba(255, 255, 255, 0.1)", borderStyle: "solid", }, }, itemWeekName: { content: { color: "rgba(255, 255, 255, 0.5)" } }, itemDayContainer: { activeDayFiller: { backgroundColor: linearAccent, }, }, itemDay: { idle: ({ isPressed, isHovered, isWeekend }) => ({ container: { backgroundColor: isPressed || isHovered ? linearAccent : "transparent", borderRadius: 4, }, content: { color: isWeekend && !isPressed ? "rgba(255, 255, 255, 0.5)" : "#ffffff", }, }), today: ({ isPressed, isHovered }) => ({ container: { borderColor: "rgba(255, 255, 255, 0.5)", borderRadius: isPressed || isHovered ? 4 : 30, backgroundColor: isPressed || isHovered ? linearAccent : "transparent", }, content: { color: isPressed || isHovered ? "#ffffff" : "rgba(255, 255, 255, 0.5)", }, }), active: ({ isEndOfRange, isStartOfRange }) => ({ container: { backgroundColor: linearAccent, borderTopLeftRadius: isStartOfRange ? 4 : 0, borderBottomLeftRadius: isStartOfRange ? 4 : 0, borderTopRightRadius: isEndOfRange ? 4 : 0, borderBottomRightRadius: isEndOfRange ? 4 : 0, }, content: { color: "#ffffff", }, }), }, }; export const LinearCalendar = memo(() => { return ( console.log(`Pressed date ${dateId}`)} theme={linearTheme} /> ); }); ``` -------------------------------- ### Basic Calendar Usage Source: https://marceloprado.github.io/flash-calendar/fundamentals/usage Renders a minimal Calendar component. Use this for a standard calendar view. Ensure `toDateId` is imported for date formatting. ```javascript import { Calendar, toDateId } from "@marceloterreiro/flash-calendar"; import { useState } from "react"; import { Text, View } from "react-native"; const today = toDateId(new Date()); export function BasicCalendar() { const [selectedDate, setSelectedDate] = useState(today); return ( Selected date: {selectedDate} ); } ``` -------------------------------- ### Recommended Date ID Conversion with toDateId Source: https://marceloprado.github.io/flash-calendar/fundamentals/tips-and-tricks This is the recommended approach for converting dates to Date IDs and back, ensuring consistency and avoiding timezone pitfalls. ```javascript const endOfJanuaryId = toDateId("2024-01-31"); const endOfJanuary = fromDateId(endOfJanuaryId); // Date object ``` -------------------------------- ### Basic Calendar List Usage Source: https://marceloprado.github.io/flash-calendar/fundamentals/usage Renders a minimal Calendar.List component for a scrollable month view. Use this when you need a list of calendar days. Ensure `toDateId` is imported for date formatting. ```javascript import { Calendar, toDateId } from "@marceloterreiro/flash-calendar"; import { useState } from "react"; import { Text, View } from "react-native"; const today = toDateId(new Date()); export function BasicCalendarList() { const [selectedDate, setSelectedDate] = useState(today); return ( Selected date: {selectedDate} ); } ``` -------------------------------- ### Render Two Calendars Source: https://marceloprado.github.io/flash-calendar/fundamentals/usage Use the `calendarInstanceId` prop to render multiple independent calendars on the same screen. This allows for managing separate states and interactions for each calendar instance. ```javascript import { Calendar, useDateRange } from "@marceloterreiro/flash-calendar"; export const TwoCalendarsMounted = () => { const dateRangeOne = useDateRange(); const dateRangeTwo = useDateRange(); return ( First calendar Second calendar ); }; ``` -------------------------------- ### Bring Your Own Date Formatting Library Source: https://marceloprado.github.io/flash-calendar/fundamentals/principles Flash Calendar allows users to integrate their preferred date formatting library (e.g., `date-fns`, `dayjs`) to avoid adding bloat and provide full control over localization. ```javascript import { Calendar } from 'flash-calendar'; import { format } from 'date-fns'; // Example using date-fns const MyCalendar = () => ( (
{format(date, 'dd')}
)} /> ); ``` -------------------------------- ### Programmatically Scroll Calendar to Date, Month, or Offset Source: https://marceloprado.github.io/flash-calendar/fundamentals/tips-and-tricks Use the `ref` to imperatively scroll the calendar. Pass `true` as the second argument to animate the scroll. The `scrollToDate` method accepts a date, `scrollToMonth` accepts a date representing the month, and `scrollToOffset` accepts a pixel value. ```javascript import { addMonths, subMonths, startOfMonth } from "date-fns"; import type { CalendarListRef } from "@marceloterreiro/flash-calendar"; import { Calendar, toDateId } from "@marceloterreiro/flash-calendar"; import { useRef, useState } from "react"; import { Button, Text, View } from "react-native"; export function ImperativeScrolling() { const [currentMonth, setCurrentMonth] = useState(startOfMonth(new Date())); const ref = useRef(null); const onCalendarDayPress = useCallback((dateId: string) => { ref.current?.scrollToDate(fromDateId(dateId), true); }, []); return (