Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adjust event position based on timeZone prop #356

Merged
merged 2 commits into from
Apr 26, 2024
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
48 changes: 43 additions & 5 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,54 @@ import { Scheduler } from "./lib";
import { EVENTS } from "./events";
import { useRef } from "react";
import { SchedulerRef } from "./lib/types";
import { MenuItem, Stack, TextField } from "@mui/material";

function App() {
const calendarRef = useRef<SchedulerRef>(null);

const onChangeTimeZone = (timeZone: string) => {
calendarRef.current?.scheduler.handleState(timeZone, "timeZone");
};

return (
<Scheduler
ref={calendarRef}
events={EVENTS}
// events={generateRandomEvents(200)}
/>
<Stack mt={2} spacing={2}>
<TextField label="Time Zone" select onChange={(e) => onChangeTimeZone(e.target.value)}>
<MenuItem value="Etc/GMT+12">UTC-12</MenuItem>
<MenuItem value="Etc/GMT+11">UTC-11</MenuItem>
<MenuItem value="Etc/GMT+10">UTC-10</MenuItem>
<MenuItem value="Etc/GMT+9">UTC-9</MenuItem>
<MenuItem value="Etc/GMT+8">UTC-8</MenuItem>
<MenuItem value="Etc/GMT+7">UTC-7</MenuItem>
<MenuItem value="Etc/GMT+6">UTC-6</MenuItem>
<MenuItem value="Etc/GMT+5">UTC-5</MenuItem>
<MenuItem value="Etc/GMT+4">UTC-4</MenuItem>
<MenuItem value="Etc/GMT+3">UTC-3</MenuItem>
<MenuItem value="Etc/GMT+2">UTC-2</MenuItem>
<MenuItem value="Etc/GMT+1">UTC-1</MenuItem>
<MenuItem value="Etc/GMT-0">UTC+0</MenuItem>
<MenuItem value="Etc/GMT-1">UTC+1</MenuItem>
<MenuItem value="Etc/GMT-2">UTC+2</MenuItem>
<MenuItem value="Etc/GMT-3">UTC+3</MenuItem>
<MenuItem value="Etc/GMT-4">UTC+4</MenuItem>
<MenuItem value="Etc/GMT-5">UTC+5</MenuItem>
<MenuItem value="Etc/GMT-6">UTC+6</MenuItem>
<MenuItem value="Etc/GMT-7">UTC+7</MenuItem>
<MenuItem value="Etc/GMT-8">UTC+8</MenuItem>
</TextField>
<Scheduler
ref={calendarRef}
events={EVENTS}
// events={generateRandomEvents(200)}
week={{
weekDays: [0, 1, 2, 3, 4, 5, 6],
weekStartOn: 0,
startHour: 0,
endHour: 24,
step: 60,
navigation: true,
}}
/>
</Stack>
);
}

Expand Down
7 changes: 4 additions & 3 deletions src/lib/components/events/MonthEvents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { ProcessedEvent } from "../../types";
import { Typography } from "@mui/material";
import EventItem from "./EventItem";
import { MONTH_NUMBER_HEIGHT, MULTI_DAY_EVENT_HEIGHT } from "../../helpers/constants";
import { differenceInDaysOmitTime } from "../../helpers/generals";
import { convertEventTimeZone, differenceInDaysOmitTime } from "../../helpers/generals";
import useStore from "../../hooks/useStore";
import usePosition from "../../positionManger/usePosition";

Expand All @@ -37,14 +37,14 @@ const MonthEvents = ({
cellHeight,
}: MonthEventProps) => {
const LIMIT = Math.round((cellHeight - MONTH_NUMBER_HEIGHT) / MULTI_DAY_EVENT_HEIGHT - 1);
const { translations, month, locale } = useStore();
const { translations, month, locale, timeZone } = useStore();
const { renderedSlots } = usePosition();

const renderEvents = useMemo(() => {
const elements: JSX.Element[] = [];

for (let i = 0; i < Math.min(events.length, LIMIT + 1); i++) {
const event = events[i];
const event = convertEventTimeZone(events[i], timeZone);
const fromPrevWeek = !!eachFirstDayInCalcRow && isBefore(event.start, eachFirstDayInCalcRow);
const start = fromPrevWeek && eachFirstDayInCalcRow ? eachFirstDayInCalcRow : event.start;
let eventLength = differenceInDaysOmitTime(start, event.end) + 1;
Expand Down Expand Up @@ -127,6 +127,7 @@ const MonthEvents = ({
daysList.length,
translations.moreEvents,
onViewMore,
timeZone,
]);

return <Fragment>{renderEvents}</Fragment>;
Expand Down
38 changes: 36 additions & 2 deletions src/lib/helpers/generals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export const differenceInDaysOmitTime = (start: Date, end: Date) => {
export const filterTodayEvents = (events: ProcessedEvent[], today: Date, timeZone?: string) => {
const list: ProcessedEvent[] = [];
for (let i = 0; i < events.length; i++) {
const event = events[i];
const event = convertEventTimeZone(events[i], timeZone);
if (
!event.allDay &&
isSameDay(today, event.start) &&
Expand Down Expand Up @@ -175,7 +175,7 @@ export const filterMultiDaySlot = (
const list: ProcessedEvent[] = [];
const multiPerDay: Record<string, ProcessedEvent[]> = {};
for (let i = 0; i < events.length; i++) {
const event = events[i];
const event = convertEventTimeZone(events[i], timeZone);
let withinSlot = event.allDay || differenceInDaysOmitTime(event.start, event.end) > 0;
if (!withinSlot) continue;
if (isMultiDates) {
Expand Down Expand Up @@ -234,6 +234,28 @@ export const getTimeZonedDate = (date: Date, timeZone?: string) => {
);
};

/**
* Performs the reverse of getTimeZonedDate, IE: the given date is assumed
* to already be in the provided timeZone and is reverted to the local
* browser's timeZone.
* @param date The date to convert.
* @param timeZone The timeZone to convert from.
* @returns A new date reverted from the given timeZone to local time.
*/
export const revertTimeZonedDate = (date: Date, timeZone?: string) => {
if (!timeZone) {
return date;
}

// This always gets the offset between the local computer's time
// and UTC. It has nothing to do with the value of the date object,
// despite being an instance method.
const localOffset = -date.getTimezoneOffset();
const desiredOffset = getTimezoneOffset(timeZone);
const diff = localOffset - desiredOffset;
return new Date(date.getTime() + diff * 60 * 1000);
};

export const isTimeZonedToday = ({
dateLeft,
dateRight,
Expand All @@ -249,3 +271,15 @@ export const isTimeZonedToday = ({
export const getHourFormat = (hourFormat: "12" | "24") => {
return hourFormat === "12" ? "hh:mm a" : "HH:mm";
};

/**
* Gets the offset in minutes of the provided timeZone.
* @param timeZone The timeZone to get the offset for.
* @returns The offset in minutes of the provided timeZone.
*/
function getTimezoneOffset(timeZone: string) {
const now = new Date();
const localizedTime = new Date(now.toLocaleString("en-US", { timeZone }));
const utcTime = new Date(now.toLocaleString("en-US", { timeZone: "UTC" }));
return Math.round((localizedTime.getTime() - utcTime.getTime()) / (60 * 1000));
}
15 changes: 12 additions & 3 deletions src/lib/hooks/useCellAttributes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { DragEvent } from "react";
import { alpha, useTheme } from "@mui/material";
import useStore from "./useStore";
import { revertTimeZonedDate } from "../helpers/generals";

interface Props {
start: Date;
Expand All @@ -9,8 +10,15 @@ interface Props {
resourceVal: string | number;
}
export const useCellAttributes = ({ start, end, resourceKey, resourceVal }: Props) => {
const { triggerDialog, onCellClick, onDrop, currentDragged, setCurrentDragged, editable } =
useStore();
const {
triggerDialog,
onCellClick,
onDrop,
currentDragged,
setCurrentDragged,
editable,
timeZone,
} = useStore();
const theme = useTheme();

return {
Expand Down Expand Up @@ -49,7 +57,8 @@ export const useCellAttributes = ({ start, end, resourceKey, resourceVal }: Prop
if (currentDragged && currentDragged.event_id) {
e.preventDefault();
e.currentTarget.style.backgroundColor = "";
onDrop(e, currentDragged.event_id.toString(), start, resourceKey, resourceVal);
const zonedStart = revertTimeZonedDate(start, timeZone);
onDrop(e, currentDragged.event_id.toString(), zonedStart, resourceKey, resourceVal);
setCurrentDragged();
}
},
Expand Down
16 changes: 13 additions & 3 deletions src/lib/store/default.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { enUS } from "date-fns/locale";
import { SchedulerProps } from "../types";
import { getOneView } from "../helpers/generals";
import { getOneView, getTimeZonedDate } from "../helpers/generals";

const defaultMonth = {
weekDays: [0, 1, 2, 3, 4, 5, 6],
Expand Down Expand Up @@ -86,7 +86,17 @@ const defaultViews = (props: Partial<SchedulerProps>) => {
};

export const defaultProps = (props: Partial<SchedulerProps>) => {
const { month, week, day, translations, resourceFields, view, agenda, ...otherProps } = props;
const {
month,
week,
day,
translations,
resourceFields,
view,
agenda,
selectedDate,
...otherProps
} = props;
const views = defaultViews(props);
const defaultView = view || "week";
const initialView = views[defaultView] ? defaultView : getOneView(views);
Expand All @@ -95,11 +105,11 @@ export const defaultProps = (props: Partial<SchedulerProps>) => {
translations: defaultTranslations(translations),
resourceFields: Object.assign(defaultResourceFields, resourceFields),
view: initialView,
selectedDate: getTimeZonedDate(selectedDate || new Date(), props.timeZone),
...Object.assign(
{
height: 600,
navigation: true,
selectedDate: new Date(),
disableViewNavigator: false,
events: [],
fields: [],
Expand Down
6 changes: 5 additions & 1 deletion src/lib/views/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
SchedulerHelpers,
} from "../types";
import { EditorSelect } from "../components/inputs/SelectInput";
import { arraytizeFieldVal } from "../helpers/generals";
import { arraytizeFieldVal, revertTimeZonedDate } from "../helpers/generals";
import { SelectedRange } from "../store/types";
import useStore from "../hooks/useStore";

Expand Down Expand Up @@ -91,6 +91,7 @@ const Editor = () => {
confirmEvent,
dialogMaxWidth,
translations,
timeZone,
} = useStore();
const [state, setState] = useState(initialState(fields, selectedEvent || selectedRange));
const [touched, setTouched] = useState(false);
Expand Down Expand Up @@ -139,6 +140,9 @@ const Editor = () => {
selectedEvent?.event_id || Date.now().toString(36) + Math.random().toString(36).slice(2);
}

body.start = revertTimeZonedDate(body.start, timeZone);
body.end = revertTimeZonedDate(body.end, timeZone);

confirmEvent(body, action);
handleClose(true);
} catch (error) {
Expand Down
Loading