Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
node_modules
.cache
dist
.parcel-cache
.parcel-cache
coverage
31 changes: 19 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ npm install @zach.codes/react-calendar date-fns
[See this code in action](https://calendar.zach.codes/?path=/story/monthly-calendar--basic-monthly-calendar)

```tsx
import {format, subHours, startOfMonth} from 'date-fns'
import { format, subHours, startOfMonth } from 'date-fns';
import {
MonthlyBody,
MonthlyDay,
MonthlyCalendar,
MonthlyNav,
DefaultMonthlyEventItem,
Expand All @@ -46,17 +47,20 @@ export const MyMonthlyCalendar = () => {
{ title: 'Call John', date: subHours(new Date(), 1) },
{ title: 'Meeting with Bob', date: new Date() },
]}
renderDay={data =>
data.map((item, index) => (
<DefaultMonthlyEventItem
key={index}
title={item.title}
// Format the date here to be in the format you prefer
date={format(item.date, 'k:mm')}
/>
))
}
/>
>
<MonthlyDay<EventType>
renderDay={data =>
data.map((item, index) => (
<DefaultMonthlyEventItem
key={index}
title={item.title}
// Format the date here to be in the format you prefer
date={format(item.date, 'k:mm')}
/>
))
}
/>
</MonthlyBody>
</MonthlyCalendar>
);
};
Expand Down Expand Up @@ -105,6 +109,9 @@ No props at this time

- `omitDays` lets you hide certain days from the calendar, for instance, hide Saturday and Sunday. Days are represented as 0-6, as seen in the [date-fns](https://date-fns.org/v2.19.0/docs/getDay#returns) documentation. Hiding Monday would be `omitDays={[1]}` Hiding the weekend would be `omitDays={[0, 6]}`
- `events` this is an array of events, the only thing required inside each object is a `date` field with a Date object representing the exact time of the event

`MonthlyDay`

- `renderDay` callback function that is passed an array of events for each day displayed, letting you render the events for the day

## WeeklyCalendar
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "0.0.3",
"version": "0.1.0",
"license": "MIT",
"name": "@zach.codes/react-calendar",
"main": "dist/index.js",
Expand Down
135 changes: 135 additions & 0 deletions src/Monthly/MonthlyBody.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import React, { ReactNode, useContext } from 'react';
import { useMonthlyCalendar } from './MonthlyCalendar';
import { daysInWeek } from '../shared';
import { format, getDay, isSameDay } from 'date-fns';

const MonthlyBodyContext = React.createContext({} as any);
type BodyState<DayData> = {
day: Date;
events: DayData[];
};

export function useMonthlyBody<DayData>() {
return useContext<BodyState<DayData>>(MonthlyBodyContext);
}

type OmittedDaysProps = {
days: Date[];
omitDays?: number[];
};

export const handleOmittedDays = ({ days, omitDays }: OmittedDaysProps) => {
let headings = daysInWeek;
let daysToRender = days;

//omit the headings and days of the week that were passed in
if (omitDays) {
headings = daysInWeek.filter(day => !omitDays.includes(day.day));
daysToRender = days.filter(day => !omitDays.includes(getDay(day)));
}

// omit the padding if an omitted day was before the start of the month
let firstDayOfMonth = getDay(daysToRender[0]) as number;
if (omitDays) {
let subtractOmittedDays = omitDays.filter(day => day < firstDayOfMonth)
.length;
firstDayOfMonth = firstDayOfMonth - subtractOmittedDays;
}
let padding = new Array(firstDayOfMonth).fill(0);

return { headings, daysToRender, padding };
};

//to prevent these from being purged in production, we make a lookup object
const headingClasses = {
l3: 'lg:grid-cols-3',
l4: 'lg:grid-cols-4',
l5: 'lg:grid-cols-5',
l6: 'lg:grid-cols-6',
l7: 'lg:grid-cols-7',
};

type MonthlyBodyProps<DayData> = {
/*
skip days, an array of days, starts at sunday (0), saturday is 6
ex: [0,6] would remove sunday and saturday from rendering
*/
omitDays?: number[];
events: (DayData & { date: Date })[];
children: ReactNode;
};

export function MonthlyBody<DayData>({
omitDays,
events,
children,
}: MonthlyBodyProps<DayData>) {
let { days } = useMonthlyCalendar();
let { headings, daysToRender, padding } = handleOmittedDays({
days,
omitDays,
});

let headingClassName = 'border-b-2 p-2 border-r-2 lg:block hidden';
return (
<div className="bg-white border-l-2 border-t-2">
<div
className={`grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 ${
//@ts-ignore
headingClasses[`l${headings.length}`]
}`}
>
{headings.map(day => (
<div
key={day.day}
className={headingClassName}
aria-label="Day of Week"
>
{day.label}
</div>
))}
{padding.map((_, index) => (
<div
key={index}
className={headingClassName}
aria-label="Empty Day"
/>
))}
{daysToRender.map(day => (
<MonthlyBodyContext.Provider
key={day.toISOString()}
value={{
day,
events: events.filter(data => isSameDay(data.date, day)),
}}
>
{children}
</MonthlyBodyContext.Provider>
))}
</div>
</div>
);
}

type MonthlyDayProps<DayData> = {
renderDay: (events: DayData[]) => ReactNode;
};
export function MonthlyDay<DayData>({ renderDay }: MonthlyDayProps<DayData>) {
let { day, events } = useMonthlyBody<DayData>();
let dayNumber = format(day, 'd');

return (
<div
aria-label={`Events for day ${dayNumber}`}
className="h-48 p-2 border-b-2 border-r-2"
>
<div className="flex justify-between">
<div className="font-bold">{dayNumber}</div>
<div className="lg:hidden block">{format(day, 'EEEE')}</div>
</div>
<ul className="divide-gray-200 divide-y overflow-hidden max-h-36 overflow-y-auto">
{renderDay(events)}
</ul>
</div>
);
}
111 changes: 0 additions & 111 deletions src/Monthly/MonthlyCalendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,8 @@ import {
subMonths,
addMonths,
getYear,
getDay,
isSameDay,
} from 'date-fns';
import React, { ReactNode, useContext } from 'react';
import { daysInWeek } from '../shared';

type CalendarState = {
days: Date[];
Expand Down Expand Up @@ -80,111 +77,3 @@ export const MonthlyNav = () => {
</div>
);
};
type ExtraMonthData = { date: Date };

type MonthlyBodyProps<MonthData> = {
/*
skip days, an array of days, starts at sunday (0), saturday is 6
ex: [0,6] would remove sunday and saturday from rendering
*/
omitDays?: number[];
events: (MonthData & ExtraMonthData)[];

renderDay: (data: (MonthData & ExtraMonthData)[]) => ReactNode;
};

type OmittedDaysProps = {
days: Date[];
omitDays?: number[];
};

const handleOmittedDays = ({ days, omitDays }: OmittedDaysProps) => {
let headings = daysInWeek;
let daysToRender = days;

//omit the headings and days of the week that were passed in
if (omitDays) {
headings = daysInWeek.filter(day => !omitDays.includes(day.day));
daysToRender = days.filter(day => !omitDays.includes(getDay(day)));
}

// omit the padding if an omitted day was before the start of the month
let firstDayOfMonth = getDay(daysToRender[0]) as number;
if (omitDays) {
let subtractOmittedDays = omitDays.filter(day => day < firstDayOfMonth)
.length;
firstDayOfMonth = firstDayOfMonth - subtractOmittedDays;
}
let padding = new Array(firstDayOfMonth).fill(0);

return { headings, daysToRender, padding };
};

//to prevent these from being purged in production, we make a lookup object
const headingClasses = {
l3: 'lg:grid-cols-3',
l4: 'lg:grid-cols-4',
l5: 'lg:grid-cols-5',
l6: 'lg:grid-cols-6',
l7: 'lg:grid-cols-7',
};

export function MonthlyBody<MonthData>({
omitDays,
events,
renderDay,
}: MonthlyBodyProps<MonthData>) {
let { days } = useMonthlyCalendar();
let { headings, daysToRender, padding } = handleOmittedDays({
days,
omitDays,
});

let headingClassName = 'border-b-2 p-2 border-r-2 lg:block hidden';
return (
<div className="bg-white border-l-2 border-t-2">
<div
className={`grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 ${
//@ts-ignore
headingClasses[`l${headings.length}`]
}`}
>
{headings.map(day => (
<div
key={day.day}
className={headingClassName}
aria-label="Day of Week"
>
{day.label}
</div>
))}
{padding.map((_, index) => (
<div
key={index}
className={headingClassName}
aria-label="Empty Day"
/>
))}
{daysToRender.map(day => {
let dayData = events.filter(data => isSameDay(data.date, day));
let dayNumber = format(day, 'd');
return (
<div
key={day.toISOString()}
aria-label={`Events for day ${dayNumber}`}
className="h-48 p-2 border-b-2 border-r-2"
>
<div className="flex justify-between">
<div className="font-bold">{dayNumber}</div>
<div className="lg:hidden block">{format(day, 'EEEE')}</div>
</div>
<ul className="divide-gray-200 divide-y overflow-hidden max-h-36 overflow-y-auto">
{renderDay(dayData)}
</ul>
</div>
);
})}
</div>
</div>
);
}
1 change: 1 addition & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './Monthly/MonthlyCalendar';
export * from './Monthly/MonthlyBody';
export * from './Monthly/MonthlyEventItems';

export * from './Weekly/WeeklyCalendar';
Expand Down
Loading