Skip to content

Commit

Permalink
Configure darkmode with props (#28)
Browse files Browse the repository at this point in the history
* wip: configure darkmode with props

* chore: memoize context value + rename files & variablse

* Rename prop to `calendarColorScheme` and avoid leaking context

* Forward colorScheme to Calendar instead of using context in CalendarList

* add docs and changeset

---------

Co-authored-by: Marcelo T Prado <[email protected]>
  • Loading branch information
bpingris and MarceloPrado committed Apr 18, 2024
1 parent 8a3d4b5 commit 6d00992
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .changeset/eighty-papayas-bathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@marceloterreiro/flash-calendar": patch
---

- Add an optional `calendarColorScheme` prop that enables overriding the color scheme used by Flash Calendar.
34 changes: 33 additions & 1 deletion apps/docs/docs/fundamentals/tips-and-tricks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,9 @@ export function ImperativeScrolling() {

## Setting Border Radius to `Calendar.Item.Day`

To apply a border radius to the `Calendar.Item.Day` component, it's necessary to specify the radius for all four corners. Here's an example of how to achieve this:
To apply a border radius to the `Calendar.Item.Day` component, it's necessary to
specify the radius for all four corners. Here's an example of how to achieve
this:

```tsx
itemDay: {
Expand All @@ -123,3 +125,33 @@ itemDay: {
}),
}
```

## Avoiding dark mode

If your app doesn't support dynamic themes, you can override Flash Calendar's
color scheme by passing a `calendarColorScheme` prop:

```tsx
export const LightModeOnly = () => {
const { calendarActiveDateRanges, onCalendarDayPress } = useDateRange({
startId: "2024-02-04",
endId: "2024-02-09",
});

return (
<Calendar
calendarActiveDateRanges={calendarActiveDateRanges}
calendarColorScheme="light"
calendarMonthId={toDateId(startOfThisMonth)}
onCalendarDayPress={onCalendarDayPress}
/>
);
};
```

When set, Flash Calendar's theming system will use this scheme instead of the
user system's theme.

**Note**: you should avoid using this prop. Instead, your app should
support dynamic themes that react to the user's system preferences. The prop is
provided as an escape hatch for apps that doesn't support dynamic themes yet.
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { memo, useEffect } from "react";
import { Text } from "react-native";
import { uppercaseFirstLetter } from "@/helpers/strings";

import { useRenderCount } from "./useRenderCount";
import { PerfTestCalendarItemDayWithContainer } from "./PerfTestCalendarItemDay";
import { useRenderCount } from "./useRenderCount";

const BasePerfTestCalendar = memo(
({
Expand Down
22 changes: 22 additions & 0 deletions packages/flash-calendar/src/components/Calendar.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,25 @@ export const DateRangePicker = (args: typeof KichenSink.args) => {
/>
);
};

export const ControlledColorScheme: StoryObj<typeof Calendar> = {
args: {
calendarColorScheme: "dark",
},
};

export const LightModeOnly = () => {
const { calendarActiveDateRanges, onCalendarDayPress } = useDateRange({
startId: "2024-02-04",
endId: "2024-02-09",
});

return (
<Calendar
calendarActiveDateRanges={calendarActiveDateRanges}
calendarColorScheme="light"
calendarMonthId={toDateId(startOfThisMonth)}
onCalendarDayPress={onCalendarDayPress}
/>
);
};
25 changes: 23 additions & 2 deletions packages/flash-calendar/src/components/Calendar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { memo, useEffect } from "react";
import type { ColorSchemeName } from "react-native";

import type {
CalendarItemDayContainerProps,
Expand All @@ -22,6 +23,7 @@ import type { BaseTheme } from "@/helpers/tokens";
import type { UseCalendarParams } from "@/hooks/useCalendar";
import { useCalendar } from "@/hooks/useCalendar";
import { activeDateRangesEmitter } from "@/hooks/useOptimizedDayMetadata";
import { CalendarThemeProvider } from "@/components/CalendarThemeProvider";

export interface CalendarTheme {
rowMonth?: CalendarRowMonthProps["theme"];
Expand Down Expand Up @@ -66,6 +68,16 @@ export interface CalendarProps extends UseCalendarParams {
* @defaultValue 20
*/
calendarMonthHeaderHeight?: number;
/**
* When set, Flash Calendar will use this color scheme instead of the system's
* value (`light|dark`). This is useful if your app doesn't support dark-mode,
* for example.
*
* We don't advise using this prop - ideally, your app should reflect the
* user's preferences.
* @defaultValue undefined
*/
calendarColorScheme?: ColorSchemeName;
/** Theme to customize the calendar component. */
theme?: CalendarTheme;
}
Expand Down Expand Up @@ -150,7 +162,12 @@ const BaseCalendar = memo(
BaseCalendar.displayName = "BaseCalendar";

export const Calendar = memo(
({ calendarActiveDateRanges, calendarMonthId, ...props }: CalendarProps) => {
({
calendarActiveDateRanges,
calendarMonthId,
calendarColorScheme,
...props
}: CalendarProps) => {
useEffect(() => {
activeDateRangesEmitter.emit(
"onSetActiveDateRanges",
Expand All @@ -168,7 +185,11 @@ export const Calendar = memo(
*/
}, [calendarActiveDateRanges, calendarMonthId]);

return <BaseCalendar {...props} calendarMonthId={calendarMonthId} />;
return (
<CalendarThemeProvider colorScheme={calendarColorScheme}>
<BaseCalendar {...props} calendarMonthId={calendarMonthId} />
</CalendarThemeProvider>
);
}
);

Expand Down
13 changes: 8 additions & 5 deletions packages/flash-calendar/src/components/CalendarList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export const CalendarList = memo(
calendarAdditionalHeight = 0,

// Other props
calendarColorScheme,
theme,
onEndReached,
...props
Expand All @@ -137,20 +138,21 @@ export const CalendarList = memo(

const calendarProps = useMemo(
(): CalendarMonthEnhanced["calendarProps"] => ({
calendarColorScheme,
calendarActiveDateRanges,
calendarDayHeight,
calendarDisabledDateIds,
calendarFirstDayOfWeek,
getCalendarDayFormat,
getCalendarWeekDayFormat,
calendarFormatLocale,
calendarMaxDateId,
calendarMinDateId,
calendarFormatLocale,
calendarMonthHeaderHeight,
calendarRowHorizontalSpacing,
getCalendarMonthFormat,
calendarRowVerticalSpacing,
calendarWeekHeaderHeight,
calendarDisabledDateIds,
getCalendarDayFormat,
getCalendarMonthFormat,
getCalendarWeekDayFormat,
onCalendarDayPress,
theme,
}),
Expand All @@ -169,6 +171,7 @@ export const CalendarList = memo(
calendarRowVerticalSpacing,
calendarWeekHeaderHeight,
calendarDisabledDateIds,
calendarColorScheme,
onCalendarDayPress,
theme,
]
Expand Down
43 changes: 43 additions & 0 deletions packages/flash-calendar/src/components/CalendarThemeProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { ReactNode } from "react";
import { createContext, useContext, useMemo } from "react";
import type { ColorSchemeName } from "react-native";

export interface CalendarThemeContextType {
colorScheme?: ColorSchemeName;
}

const CalendarThemeContext = createContext<CalendarThemeContextType>({
colorScheme: undefined,
});

export const CalendarThemeProvider = ({
children,
colorScheme,
}: {
children: ReactNode;
/**
* When set, Flash Calendar will use this color scheme instead of the system's
* value (`light|dark`). This is useful if your app doesn't support dark-mode,
* for example.
*
* We don't advise using this prop - ideally, your app should reflect the
* user's preferences.
*/
colorScheme?: ColorSchemeName;
}) => {
const calendarThemeContextValue = useMemo<CalendarThemeContextType>(
() => ({ colorScheme }),
[colorScheme]
);

return (
<CalendarThemeContext.Provider value={calendarThemeContextValue}>
{children}
</CalendarThemeContext.Provider>
);
};

export const useCalendarTheme = () => {
const context = useContext(CalendarThemeContext);
return context;
};
7 changes: 6 additions & 1 deletion packages/flash-calendar/src/hooks/useTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@ import { useColorScheme } from "react-native";

import type { BaseTheme } from "@/helpers/tokens";
import { darkTheme, lightTheme } from "@/helpers/tokens";
import { useCalendarTheme } from "@/components/CalendarThemeProvider";

export const useTheme = (): BaseTheme => {
const appearance = useColorScheme();
return appearance === "dark" ? darkTheme : lightTheme;
const { colorScheme } = useCalendarTheme();

const theme = colorScheme ?? appearance;

return theme === "dark" ? darkTheme : lightTheme;
};

0 comments on commit 6d00992

Please sign in to comment.