Skip to content

Commit 6a356e0

Browse files
✨ Feat: Auto scroll when dragging (#262)
* chore(web): Remove `gridScrollRef`, simplify code, fix few TS errors. - Remove `gridScrollRef` and replace with `mainGridRef`. Having `gridScrollRef` as a separate entity and having `mainGridRef` as a callback function that is not a ref and does not return a ref did not makes sense to me. I believe this change makes the code simpler and more readable. - Remove unused `react-merge-refs` dependency as a consequence of above change - Handle few TS errors * feature(web): smart scroll on drag POC * bug(web): Use `draftContext.state.isDragging` to get whether we are dragging or not * bug(web): handle edge case with dragging an event to very bottom of the page, causing event to overflow past container. Refactor code this occurs when we drag an event to the very bottom until the event's end time is past midnight, instead of preventing me from dragging the event past midnight, the event overflows past its container. * refactor(web): Simplify draft event dragging logic in useDraftActions * fix: skip smart scroll if drafting all-day event without this, the main grid would screen when dragging an all-day event * chore: format and cleanup --------- Co-authored-by: Tyler Dane <[email protected]>
1 parent 787f6c6 commit 6a356e0

File tree

14 files changed

+143
-78
lines changed

14 files changed

+143
-78
lines changed

packages/web/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
"react-dom": "^18.1.0",
3636
"react-google-button": "^0.8.0",
3737
"react-hotkeys-hook": "^4.4.1",
38-
"react-merge-refs": "^1.1.0",
3938
"react-modal": "^3.16.1",
4039
"react-redux": "^8.1.2",
4140
"react-router-dom": "^6.8.1",

packages/web/src/views/Calendar/Calendar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ export const CalendarView = () => {
3333
weekProps.component.week,
3434
);
3535

36-
const scrollUtil = useScroll(gridRefs.gridScrollRef);
36+
const scrollUtil = useScroll(gridRefs.mainGridRef);
3737

38-
const dateCalcs = useDateCalcs(measurements, gridRefs.gridScrollRef);
38+
const dateCalcs = useDateCalcs(measurements, gridRefs.mainGridRef);
3939

4040
const isCurrentWeek = weekProps.component.isCurrentWeek;
4141
const util = weekProps.util;

packages/web/src/views/Calendar/components/Draft/hooks/actions/useDraftActions.ts

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -159,34 +159,40 @@ export const useDraftActions = (
159159
const drag = useCallback(
160160
(e: MouseEvent) => {
161161
const updateTimesDuringDrag = (e: MouseEvent) => {
162-
setDraft((_draft) => {
163-
const x = getX(e, isSidebarOpen);
164-
const _initialStart = dateCalcs.getDateByXY(
165-
x,
166-
e.clientY,
167-
weekProps.component.startOfView,
168-
);
169-
170-
const startDate = _draft?.isAllDay
171-
? _initialStart.format(YEAR_MONTH_DAY_FORMAT)
172-
: _initialStart.format();
173-
174-
const _end = _initialStart.add(
175-
dragStatus?.durationMin || 0,
176-
"minutes",
177-
);
178-
179-
const endDate = _draft.isAllDay
180-
? _end.format(YEAR_MONTH_DAY_FORMAT)
181-
: _end.format();
162+
if (!draft) return;
182163

183-
return {
184-
..._draft,
185-
startDate,
186-
endDate,
187-
priority: _draft?.priority || Priorities.UNASSIGNED,
188-
};
189-
});
164+
const x = getX(e, isSidebarOpen);
165+
const startEndDurationMin = dragStatus?.durationMin || 0;
166+
167+
let eventStart = dateCalcs.getDateByXY(
168+
x,
169+
e.clientY,
170+
weekProps.component.startOfView,
171+
);
172+
173+
let eventEnd = eventStart.add(startEndDurationMin, "minutes");
174+
175+
if (!draft.isAllDay) {
176+
// Edge case: timed events' end times can overflow past midnight at the bottom of the grid.
177+
// Below logic prevents that from occurring.
178+
if (eventEnd.date() !== eventStart.date()) {
179+
eventEnd = eventEnd.hour(0).minute(0);
180+
eventStart = eventEnd.subtract(startEndDurationMin, "minutes");
181+
}
182+
}
183+
184+
const _draft: Schema_GridEvent = {
185+
...draft,
186+
startDate: draft.isAllDay
187+
? eventStart.format(YEAR_MONTH_DAY_FORMAT)
188+
: eventStart.format(),
189+
endDate: draft.isAllDay
190+
? eventEnd.format(YEAR_MONTH_DAY_FORMAT)
191+
: eventEnd.format(),
192+
priority: draft.priority || Priorities.UNASSIGNED,
193+
};
194+
195+
setDraft(_draft);
190196
};
191197

192198
if (!isDragging) {

packages/web/src/views/Calendar/components/Event/Grid/GridEventPreview/GridEventPreview.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export interface Props {
2828
measurements: Measurements_Grid;
2929
mouseCoords: MouseCoords;
3030
startOfView: WeekProps["component"]["startOfView"];
31-
gridScrollRef: Refs_Grid["gridScrollRef"];
31+
mainGridRef: Refs_Grid["mainGridRef"];
3232
}
3333

3434
export const GridEventPreview: FC<Props> = memo(function GridEventPreview({
@@ -40,7 +40,7 @@ export const GridEventPreview: FC<Props> = memo(function GridEventPreview({
4040
measurements,
4141
mouseCoords,
4242
startOfView,
43-
gridScrollRef,
43+
mainGridRef,
4444
}) {
4545
const { colWidths } = measurements;
4646
const { x, y } = mouseCoords;
@@ -83,7 +83,7 @@ export const GridEventPreview: FC<Props> = memo(function GridEventPreview({
8383
x,
8484
y,
8585
measurements,
86-
gridScrollRef.current?.scrollTop || 0,
86+
mainGridRef.current?.scrollTop || 0,
8787
);
8888

8989
return (

packages/web/src/views/Calendar/components/Grid/Grid.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export const Grid: FC<Props> = ({
2424
today,
2525
weekProps,
2626
}) => {
27-
const { allDayRef, gridScrollRef, mainGridRef } = gridRefs;
27+
const { allDayRef, mainGridRef } = gridRefs;
2828

2929
return (
3030
<>
@@ -41,7 +41,6 @@ export const Grid: FC<Props> = ({
4141
isSidebarOpen={isSidebarOpen}
4242
mainGridRef={mainGridRef}
4343
measurements={measurements}
44-
scrollRef={gridScrollRef}
4544
today={today}
4645
weekProps={weekProps}
4746
/>

packages/web/src/views/Calendar/components/Grid/MainGrid/MainGrid.tsx

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1-
import React, { FC, MouseEvent } from "react";
2-
import mergeRefs from "react-merge-refs";
1+
import React, { FC, MouseEvent, MutableRefObject } from "react";
32
import { Dayjs } from "dayjs";
43
import { Categories_Event } from "@core/types/event.types";
54
import { DRAFT_DURATION_MIN } from "@web/views/Calendar/layout.constants";
65
import { useAppDispatch, useAppSelector } from "@web/store/store.hooks";
7-
import { Ref_Callback } from "@web/common/types/util.types";
86
import { WeekProps } from "@web/views/Calendar/hooks/useWeek";
97
import { DateCalcs } from "@web/views/Calendar/hooks/grid/useDateCalcs";
10-
import { Ref_Grid } from "@web/views/Calendar/components/Grid/grid.types";
118
import { ID_GRID_MAIN } from "@web/common/constants/web.constants";
129
import { Measurements_Grid } from "@web/views/Calendar/hooks/grid/useGridLayout";
1310
import { assembleDefaultEvent } from "@web/common/utils/event.util";
@@ -24,14 +21,14 @@ import { MainGridEvents } from "./MainGridEvents";
2421
import { MainGridColumns } from "../Columns/MainGridColumns";
2522
import { selectIsDrafting } from "@web/ducks/events/selectors/draft.selectors";
2623
import { isRightClick } from "@web/common/utils/mouse/mouse.util";
24+
import { useDragEventSmartScroll } from "@web/views/Calendar/hooks/grid/useDragEventSmartScroll";
2725

2826
interface Props {
2927
dateCalcs: DateCalcs;
3028
isSidebarOpen: boolean;
31-
mainGridRef: Ref_Callback;
29+
mainGridRef: MutableRefObject<HTMLDivElement | null>;
3230
measurements: Measurements_Grid;
3331
today: Dayjs;
34-
scrollRef: Ref_Grid;
3532
weekProps: WeekProps;
3633
}
3734

@@ -41,15 +38,15 @@ export const MainGrid: FC<Props> = ({
4138
mainGridRef,
4239
measurements,
4340
today,
44-
scrollRef,
4541
weekProps,
4642
}) => {
4743
const dispatch = useAppDispatch();
48-
4944
const { component } = weekProps;
5045
const { isCurrentWeek, week, weekDays } = component;
5146
const isDrafting = useAppSelector(selectIsDrafting);
5247

48+
useDragEventSmartScroll(mainGridRef);
49+
5350
const onMouseDown = async (e: MouseEvent) => {
5451
if (isDrafting) {
5552
dispatch(draftSlice.actions.discard());
@@ -80,7 +77,7 @@ export const MainGrid: FC<Props> = ({
8077
};
8178

8279
return (
83-
<StyledMainGrid id={ID_GRID_MAIN} ref={mergeRefs([mainGridRef, scrollRef])}>
80+
<StyledMainGrid id={ID_GRID_MAIN} ref={mainGridRef}>
8481
<MainGridColumns
8582
isCurrentWeek={isCurrentWeek}
8683
today={today}

packages/web/src/views/Calendar/components/Sidebar/SomedayTab/MonthSection/MonthSection.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export const MonthSection: FC<Props> = ({
4848
measurements={measurements}
4949
sidebarProps={somedayProps}
5050
viewStart={viewStart}
51-
gridScrollRef={gridRefs.gridScrollRef}
51+
mainGridRef={gridRefs.mainGridRef}
5252
/>
5353
</SidebarSection>
5454
);

packages/web/src/views/Calendar/components/Sidebar/SomedayTab/SomedayEvents/SomedayEvents.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ interface Props {
2727
measurements: Measurements_Grid;
2828
sidebarProps: SidebarProps;
2929
viewStart: WeekProps["component"]["startOfView"];
30-
gridScrollRef: Refs_Grid["gridScrollRef"];
30+
mainGridRef: Refs_Grid["mainGridRef"];
3131
}
3232

3333
const getSomedayEvents = (
@@ -49,7 +49,7 @@ export const SomedayEvents: FC<Props> = ({
4949
measurements,
5050
sidebarProps,
5151
viewStart,
52-
gridScrollRef,
52+
mainGridRef,
5353
}) => {
5454
const { state, util } = sidebarProps;
5555
const gridX = state.mouseCoords.x - (SIDEBAR_OPEN_WIDTH + GRID_X_START);
@@ -75,7 +75,7 @@ export const SomedayEvents: FC<Props> = ({
7575
measurements={measurements}
7676
mouseCoords={state.mouseCoords}
7777
startOfView={viewStart}
78-
gridScrollRef={gridScrollRef}
78+
mainGridRef={mainGridRef}
7979
/>
8080
)}
8181

packages/web/src/views/Calendar/components/Sidebar/SomedayTab/WeekSection/WeekSection.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export const WeekSection: FC<Props> = ({
4747
measurements={measurements}
4848
sidebarProps={sidebarProps}
4949
viewStart={viewStart}
50-
gridScrollRef={gridRefs.gridScrollRef}
50+
mainGridRef={gridRefs.mainGridRef}
5151
/>
5252
</SidebarSection>
5353
);

packages/web/src/views/Calendar/hooks/grid/useDateCalcs.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { MutableRefObject } from "react";
12
import dayjs, { Dayjs } from "dayjs";
23
import utc from "dayjs/plugin/utc";
34
import timezone from "dayjs/plugin/timezone";
@@ -9,15 +10,13 @@ import { ACCEPTED_TIMES } from "@web/common/constants/web.constants";
910
import { Measurements_Grid } from "@web/views/Calendar/hooks/grid/useGridLayout";
1011
import { GRID_X_START } from "@web/views/Calendar/layout.constants";
1112

12-
import { Ref_Grid } from "../../components/Grid/grid.types";
13-
1413
dayjs.extend(weekPlugin);
1514
dayjs.extend(utc);
1615
dayjs.extend(timezone);
1716

1817
export const useDateCalcs = (
1918
measurements: Measurements_Grid,
20-
scrollRef: Ref_Grid,
19+
mainGridRef: MutableRefObject<HTMLDivElement | null>,
2120
) => {
2221
const getDateByX = (x: number, firstDayInView: Dayjs) => {
2322
const gridX = x - GRID_X_START;
@@ -71,7 +70,7 @@ export const useDateCalcs = (
7170
const getMinuteByY = (y: number) => {
7271
if (!measurements.mainGrid) return 0; // TS guard. This should never happen
7372

74-
const scrollTop = scrollRef.current.scrollTop;
73+
const scrollTop = mainGridRef.current?.scrollTop || 0;
7574
// gridY is the distance from the top of the grid (main grid) to the click
7675
const gridY = y - measurements.mainGrid.top + scrollTop;
7776

0 commit comments

Comments
 (0)