Skip to content

Commit

Permalink
feat(web): Support InteractionStates like hovered and focused for web (
Browse files Browse the repository at this point in the history
…#68)

* feat(web): extending InteractionState to support hovered and focused

* chore(web): adding documentation example

* chore(changeset): adding changeset for changelog

* chore(lint): pass tests
  • Loading branch information
theonetheycallneo authored Dec 4, 2024
1 parent 3c3891d commit b66668c
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 31 deletions.
5 changes: 5 additions & 0 deletions .changeset/rich-cars-taste.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@marceloterreiro/flash-calendar": minor
---

Added support for react-native-web Pressable InteractionState to support hovered and focused alongside pressed
80 changes: 80 additions & 0 deletions apps/docs/docs/fundamentals/customization.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -219,3 +219,83 @@ export const PerfTestCalendarItemDayWithContainer = ({
);
};
```

### Overriding the hovered and focused interaction states

If you need to support web [Pressable InteractionStates](https://necolas.github.io/react-native-web/docs/pressable/#examples), you can also leverage `isHovered` and `isFocused` alongside `isPressed`:

```tsx
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 (
<Calendar
calendarDayHeight={30}
calendarFirstDayOfWeek="sunday"
calendarMonthId={toDateId(new Date())}
calendarRowHorizontalSpacing={16}
calendarRowVerticalSpacing={16}
onCalendarDayPress={(dateId) => console.log(`Pressed date ${dateId}`)}
theme={linearTheme}
/>
);
});
```
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import type { Meta } from "@storybook/react";
import { useCallback, useMemo, useState } from "react";
import { Alert, Text, View } from "react-native";
import { useTheme } from "@/hooks/useTheme";
import { VStack } from "@/components/VStack";

const CalendarMeta: Meta<typeof Calendar> = {
title: "Calendar.List/Github Issues",
Expand Down
87 changes: 57 additions & 30 deletions packages/flash-calendar/src/components/CalendarItemDay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ import type { CalendarDayMetadata } from "@/hooks/useCalendar";
import { useOptimizedDayMetadata } from "@/hooks/useOptimizedDayMetadata";
import { useTheme } from "@/hooks/useTheme";

// react-native-web/overrides.ts
declare module "react-native" {
interface PressableStateCallbackType {
hovered?: boolean;
focused?: boolean;
}
}

const styles = StyleSheet.create({
baseContainer: {
padding: 6,
Expand All @@ -33,6 +41,8 @@ type CalendarItemDayTheme = Record<
isStartOfRange: boolean;
isEndOfRange: boolean;
isPressed: boolean;
isHovered?: boolean;
isFocused?: boolean;
}) => DayTheme
>;

Expand All @@ -43,28 +53,29 @@ const buildBaseStyles = (theme: BaseTheme): CalendarItemDayTheme => {
};

return {
active: ({ isPressed, isStartOfRange, isEndOfRange }) => {
const baseStyles: DayTheme & { container: ViewStyle } = isPressed
? {
container: {
...styles.baseContainer,
backgroundColor: theme.colors.background.tertiary,
},
content: {
...baseContent,
color: theme.colors.content.primary,
},
}
: {
container: {
...styles.baseContainer,
backgroundColor: theme.colors.background.inverse.primary,
},
content: {
...baseContent,
color: theme.colors.content.inverse.primary,
},
};
active: ({ isPressed, isHovered, isStartOfRange, isEndOfRange }) => {
const baseStyles: DayTheme & { container: ViewStyle } =
isPressed || isHovered
? {
container: {
...styles.baseContainer,
backgroundColor: theme.colors.background.tertiary,
},
content: {
...baseContent,
color: theme.colors.content.primary,
},
}
: {
container: {
...styles.baseContainer,
backgroundColor: theme.colors.background.inverse.primary,
},
content: {
...baseContent,
color: theme.colors.content.inverse.primary,
},
};

baseStyles.container.borderRadius = 0;
if (isStartOfRange) {
Expand All @@ -87,8 +98,8 @@ const buildBaseStyles = (theme: BaseTheme): CalendarItemDayTheme => {
color: theme.colors.content.disabled,
},
}),
idle: ({ isPressed }) => {
return isPressed
idle: ({ isPressed, isHovered }) => {
return isPressed || isHovered
? {
container: {
...styles.baseContainer,
Expand All @@ -104,8 +115,8 @@ const buildBaseStyles = (theme: BaseTheme): CalendarItemDayTheme => {
content: baseContent,
};
},
today: ({ isPressed }) => {
return isPressed
today: ({ isPressed, isHovered }) => {
return isPressed || isHovered
? {
container: {
...styles.baseContainer,
Expand Down Expand Up @@ -136,6 +147,8 @@ export interface CalendarItemDayProps {
(
params: CalendarDayMetadata & {
isPressed: boolean;
isHovered?: boolean;
isFocused?: boolean;
}
) => Partial<DayTheme>
>
Expand Down Expand Up @@ -175,9 +188,15 @@ export const CalendarItemDay = ({
<Pressable
disabled={metadata.state === "disabled"}
onPress={handlePress}
style={({ pressed: isPressed }) => {
style={({
pressed: isPressed,
hovered: isHovered,
focused: isFocused,
}) => {
const params = {
isPressed,
isHovered,
isFocused,
isEndOfRange: metadata.isEndOfRange ?? false,
isStartOfRange: metadata.isStartOfRange ?? false,
};
Expand All @@ -190,9 +209,11 @@ export const CalendarItemDay = ({
};
}}
>
{({ pressed: isPressed }) => {
{({ pressed: isPressed, hovered: isHovered, focused: isFocused }) => {
const params = {
isPressed,
isHovered,
isFocused,
isEndOfRange: metadata.isEndOfRange ?? false,
isStartOfRange: metadata.isStartOfRange ?? false,
};
Expand All @@ -203,8 +224,14 @@ export const CalendarItemDay = ({
style={{
...content,
...(textProps?.style ?? ({} as object)),
...theme?.base?.({ ...metadata, isPressed }).content,
...theme?.[metadata.state]?.({ ...metadata, isPressed }).content,
...theme?.base?.({ ...metadata, isPressed, isHovered, isFocused })
.content,
...theme?.[metadata.state]?.({
...metadata,
isPressed,
isHovered,
isFocused,
}).content,
}}
>
{children}
Expand Down

0 comments on commit b66668c

Please sign in to comment.