Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
946ba49
feat: close someday form on ESC or outside press
tyler-dane Feb 5, 2025
f12d9b0
refactor: remove discardIfDrafting util and replace Schema_GridEvent …
tyler-dane Feb 6, 2025
7fdb69c
fix: improve onClose handling in SomedayEvent and SomedayEventForm co…
tyler-dane Feb 7, 2025
00b6795
refactor: streamline useEventForm hook by consolidating dismiss logic…
tyler-dane Feb 8, 2025
ed8bd46
chore: remove Escape key handling from EventForm component
tyler-dane Feb 8, 2025
0e76e70
chore: remove unused onSectionClick handler from Header component
tyler-dane Feb 8, 2025
463761e
checkpoint: feat: update how events are created using mouse handlers
tyler-dane Feb 9, 2025
70857ad
refactor: update import paths for position utility and enhance mouse …
tyler-dane Feb 9, 2025
1c8696b
feat: add mouse position utility and integrate it into grid event han…
tyler-dane Feb 9, 2025
df20a6f
checkpoint(refactor): move handlers to root mouse hook instead of in …
tyler-dane Feb 9, 2025
309a316
refactor: update handler in previous commit to use ID rather than man…
tyler-dane Feb 9, 2025
0633202
refactor: update all-day row constants and references for improved cl…
tyler-dane Feb 9, 2025
a79cdb2
refactor: simplify draft event handling with extracted callback methods
tyler-dane Feb 10, 2025
cf4faee
refactor: extract draft form handling into separate hook
tyler-dane Feb 10, 2025
1e765a9
refactor: create useDraft hook to centralize draft event management
tyler-dane Feb 10, 2025
588c948
refactor: extract draft actions into separate hook for improved modul…
tyler-dane Feb 10, 2025
88c51c8
refactor: separate draft effects into useDraftEffects hook for improv…
tyler-dane Feb 10, 2025
f0c25c2
refactor: reorganize draft hooks and components for improved structur…
tyler-dane Feb 10, 2025
1009dc9
fix: add position props during create shortcut
tyler-dane Feb 11, 2025
8b989d0
fix: resize events and discard with outside click
tyler-dane Feb 11, 2025
2258ab2
feat: trigger main grid events on click or `C`
tyler-dane Feb 11, 2025
038c9aa
feat(checkpoint): use more global state
tyler-dane Feb 13, 2025
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
4 changes: 3 additions & 1 deletion packages/web/src/common/constants/web.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ export const GOOGLE = "google";

export const ID_ALLDAY_COLUMNS = "allDayColumns";
export const ID_EVENT_FORM = "Event Form";
export const ID_GRID_ALLDAY_ROW = "allDayRow";
export const ID_GRID_ALLDAY_CONTAINER = "allDayRowContainer";
export const ID_GRID_EVENTS_ALLDAY = "allDayEvents";
export const ID_GRID_EVENTS_TIMED = "timedEvents";
export const ID_GRID_MAIN = "mainGrid";
export const ID_GRID_ROW = "gridRow";
export const ID_GRID_ROW_CONTAINER = "mainGridContainer";
export const ID_MAIN = "mainSection";
export const ID_DATEPICKER_SIDEBAR = "sidebarDatePicker";
export const ID_SOMEDAY_DRAFT = "somedayDraft";
Expand Down
13 changes: 12 additions & 1 deletion packages/web/src/common/types/web.event.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,20 @@ export interface Schema_SomedayEventsColumn {
[key: string]: Schema_Event;
};
}

type Activity = "createShortcut" | "dragging" | "gridClick" | "resizing";

export enum Location_Draft {
ALLDAY_ROW = "alldayRow",
ALLDAY_EVENT = "alldayEvent",
MAIN_GRID = "mainGrid",
MAIN_GRID_EVENT = "mainGridEvent",
}

export interface Status_DraftEvent {
activity: string | null;
activity: Activity | null;
eventType: Categories_Event | null;
isDrafting: boolean;
dateToResize: "startDate" | "endDate" | null;
location: Location_Draft | null;
}
105 changes: 105 additions & 0 deletions packages/web/src/common/utils/draft/draft.util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { MouseEvent } from "react";
import dayjs, { Dayjs } from "dayjs";
import {
ID_GRID_EVENTS_ALLDAY,
ID_GRID_EVENTS_TIMED,
} from "@web/common/constants/web.constants";
import { roundToNext } from "@web/common/utils";
import { getElemById, getX } from "@web/common/utils/grid.util";
import {
DRAFT_DURATION_MIN,
GRID_TIME_STEP,
GRID_Y_START,
SIDEBAR_X_START,
} from "@web/views/Calendar/layout.constants";
import { Categories_Event } from "@core/types/event.types";
import { assembleDefaultEvent } from "@web/common/utils/event.util";
import { DateCalcs } from "@web/views/Calendar/hooks/grid/useDateCalcs";
import { Schema_GridEvent } from "@web/common/types/web.event.types";
import { getMousePosition } from "../position/mouse.position";

export const assembleAlldayDraft = async (
e: MouseEvent,
dateCalcs: DateCalcs,
isSidebarOpen: boolean,
startOfView: Dayjs
): Promise<Schema_GridEvent> => {
const x = getX(e, isSidebarOpen);
const _start = dateCalcs.getDateByXY(x, e.clientY, startOfView);
const startDate = _start.format();
const endDate = _start.add(1, "day").format();

const event = (await assembleDefaultEvent(
Categories_Event.ALLDAY,
startDate,
endDate
)) as Schema_GridEvent;
return event;
};

export const assembleTimedDraft = async (
e: MouseEvent,
dateCalcs: DateCalcs,
isSidebarOpen: boolean,
startOfView: Dayjs
): Promise<Schema_GridEvent> => {
const x = getX(e, isSidebarOpen);
const _start = dateCalcs.getDateByXY(x, e.clientY, startOfView);
const startDate = _start.format();
const endDate = _start.add(DRAFT_DURATION_MIN, "minutes").format();

const event = (await assembleDefaultEvent(
Categories_Event.TIMED,
startDate,
endDate
)) as Schema_GridEvent;
return event;
};

export const getDraftTimes = (isCurrentWeek: boolean, startOfWeek: Dayjs) => {
const currentMinute = dayjs().minute();
const nextMinuteInterval = roundToNext(currentMinute, GRID_TIME_STEP);

const fullStart = isCurrentWeek ? dayjs() : startOfWeek.hour(dayjs().hour());
const _start = fullStart.minute(nextMinuteInterval).second(0);

const _end = _start.add(1, "hour");
const startDate = _start.format();
const endDate = _end.format();

return { startDate, endDate };
};

export const getDraftContainer = (isAllDay: boolean) => {
if (isAllDay) {
return getElemById(ID_GRID_EVENTS_ALLDAY);
}

return getElemById(ID_GRID_EVENTS_TIMED);
};

export const isOverMainGrid = (
x: number,
y: number,
allDayRow: DOMRect | null
) => {
if (!allDayRow?.bottom || !allDayRow?.top) {
throw Error("Missing measurements for all-day row");
return false;
}

const { isOverMainGrid } = getMousePosition(
{
allDayRowBottom: allDayRow.bottom,
allDayRowTop: allDayRow.top,
gridYStart: GRID_Y_START,
sidebarXStart: SIDEBAR_X_START,
},
{
x,
y,
}
);

return isOverMainGrid;
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
getAllDayEventWidth,
getLeftPosition,
widthMinusPadding,
} from "@web/common/utils/position.util";
} from "@web/common/utils/position/event.position";

describe("getAllDayEventWidth", () => {
it("is never wider than 1 week", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import {
GRID_MARGIN_LEFT,
} from "@web/views/Calendar/layout.constants";
import { Category } from "@web/ducks/events/event.types";
import { ACCEPTED_TIMES } from "../constants/web.constants";
import { Schema_GridEvent } from "../types/web.event.types";
import { ACCEPTED_TIMES } from "../../constants/web.constants";
import { Schema_GridEvent } from "../../types/web.event.types";

dayjs.extend(dayOfYear);
dayjs.extend(weekPlugin);
Expand Down Expand Up @@ -136,7 +136,7 @@ export const getEventCategory = (
return Category.ThisWeekOnly;
};

export const getPosition = (
export const getEventPosition = (
event: Schema_GridEvent,
startOfView: Dayjs,
endOfView: Dayjs,
Expand Down
49 changes: 49 additions & 0 deletions packages/web/src/common/utils/position/mouse.position.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { getMousePosition } from "./mouse.position";

describe("getMousePosition", () => {
const layout = {
allDayRowBottom: 100,
allDayRowTop: 50,
gridYStart: 150,
sidebarXStart: 200,
};
it("should return false when x is less than sidebarXStart", () => {
const result = getMousePosition(layout, { x: 100, y: 100 });

expect(result).toEqual({
isOverGrid: false,
isOverMainGrid: false,
isOverAllDayRow: false,
});
});

it("should return isOverAllDayRow as true when y is between allDayRowTop and allDayRowBottom", () => {
const result = getMousePosition(layout, { x: 250, y: 75 });

expect(result).toEqual({
isOverGrid: true,
isOverMainGrid: false,
isOverAllDayRow: true,
});
});

it("should return isOverMainGrid as true when y is greater than gridYStart", () => {
const result = getMousePosition(layout, { x: 250, y: 200 });

expect(result).toEqual({
isOverGrid: true,
isOverMainGrid: true,
isOverAllDayRow: false,
});
});

it("should return isOverGrid as false when y is less than allDayRowTop and gridYStart", () => {
const result = getMousePosition(layout, { x: 250, y: 25 });

expect(result).toEqual({
isOverGrid: false,
isOverMainGrid: false,
isOverAllDayRow: false,
});
});
});
28 changes: 28 additions & 0 deletions packages/web/src/common/utils/position/mouse.position.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Coordinates } from "@web/common/types/util.types";

type Layout = {
allDayRowBottom: number;
allDayRowTop: number;
gridYStart: number;
sidebarXStart: number;
};

export const getMousePosition = (layout: Layout, coordinates: Coordinates) => {
const { allDayRowTop, allDayRowBottom, gridYStart, sidebarXStart } = layout;
const { x, y } = coordinates;

const isPastSidebar = x > sidebarXStart;

const isOverAllDayRow =
isPastSidebar && y < allDayRowBottom && y > allDayRowTop;

const isOverMainGrid = isPastSidebar && !isOverAllDayRow && y > gridYStart;

const isOverGrid = isOverAllDayRow || isOverMainGrid;

return {
isOverGrid,
isOverMainGrid,
isOverAllDayRow,
};
};
11 changes: 10 additions & 1 deletion packages/web/src/ducks/events/selectors/draft.selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const selectDraftStatus = (state: RootState) =>
state.events.draft.status;

export const selectIsDrafting = (state: RootState) =>
state.events.draft.status.isDrafting;
state.events.draft.status?.isDrafting;

export const selectIsDraftingExisting = (state: RootState) =>
state.events.draft.event?._id !== undefined;
Expand All @@ -16,5 +16,14 @@ export const selectIsDraftingSomeday = (state: RootState) =>
state.events.draft.status.eventType === Categories_Event.SOMEDAY_WEEK ||
state.events.draft.status.eventType === Categories_Event.SOMEDAY_MONTH;

export const selectIsDragging = (state: RootState) =>
state.events.draft.status?.activity === "dragging";

export const selectIsResizing = (state: RootState) =>
state.events.draft.status?.activity === "resizing";

export const selectDraftId = (state: RootState) =>
state.events.draft.event?._id;

export const selectDraftLocation = (state: RootState) =>
state.events.draft.status?.location;
16 changes: 13 additions & 3 deletions packages/web/src/ducks/events/slices/draft.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
Action_DraftEvent,
Action_Draft_Resize,
Action_Draft_Swap,
Action_Location_Set,
} from "./draft.slice.types";

interface State_DraftEvent {
Expand All @@ -19,6 +20,7 @@ const initialDraft = {
isDrafting: false,
eventType: null,
dateToResize: null,
location: null,
},
event: null,
};
Expand All @@ -39,7 +41,6 @@ export const draftSlice = createSlice({
eventType,
};
},

startResizing: (state, action: Action_Draft_Resize) => {
const { event, dateToChange } = action.payload;
return {
Expand All @@ -52,7 +53,6 @@ export const draftSlice = createSlice({
},
};
},

startDragging: (state, action) => {
const { event } = action.payload;
state.event = event;
Expand All @@ -62,7 +62,11 @@ export const draftSlice = createSlice({
isDrafting: true,
};
},

resetActivity: (state) => {
if (state.status) {
state.status.activity = null;
}
},
swap: (state, action: Action_Draft_Swap) => {
const { category, event } = action.payload;
state.event = event;
Expand All @@ -72,5 +76,11 @@ export const draftSlice = createSlice({
eventType: category,
};
},
setLocation: (state, action: Action_Location_Set) => {
const { location } = action.payload;
if (state.status) {
state.status.location = location;
}
},
},
});
15 changes: 13 additions & 2 deletions packages/web/src/ducks/events/slices/draft.slice.types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { Action } from "redux";
import { Schema_Event, Categories_Event } from "@core/types/event.types";
import { Schema_GridEvent } from "@web/common/types/web.event.types";
import {
Location_Draft,
Schema_GridEvent,
} from "@web/common/types/web.event.types";

export interface Action_DraftEvent extends Action {
payload: Payload_DraftEvent;
Expand All @@ -17,14 +20,18 @@ export interface Action_Draft_Swap extends Action {
payload: Payload_Draft_Swap;
}

export interface Action_Location_Set extends Action {
payload: Payload_Location_Set;
}

interface Payload_Draft_Drag {
event: Schema_Event;
}

interface Payload_DraftEvent {
activity?: "createShortcut" | "dragging" | "gridClick" | "resizing";
event?: Schema_Event;
eventType: Categories_Event;
activity?: "createShortcut" | "dragging" | "resizing";
}

interface Payload_Draft_Resize {
Expand All @@ -36,3 +43,7 @@ interface Payload_Draft_Swap {
event: Schema_GridEvent;
category: Categories_Event;
}

interface Payload_Location_Set {
location: Location_Draft;
}
Loading
Loading