Skip to content

Commit e4b72c4

Browse files
:sparkles feat(day-view): show preview for new events (#1371)
* :sparkles feat(day-view): show preview for new events - Added `useOpenAgendaEventPreview` and `useOpenEventContextMenu` hooks to manage event previews and context menus. - Created tests for both hooks to ensure correct functionality when interacting with events. - Refactored event form handling in `useOpenEventForm` to streamline event creation and editing. - Introduced `useCloseEventForm` hook to handle closing event forms and resetting drafts. - Updated `useSaveEventForm` to integrate with the new close event form logic. - Enhanced utility functions for event handling and focus management. - Removed unnecessary context providers from `DayViewContent` for cleaner component structure. - add a re-resizable package * :sparkles feat: refactor event handling and improve test setups * fix: update existing event check to return false instead of null * Update packages/web/src/components/DND/Resizable.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * :bug fix: ensure element scrolls into view before focusing in focusElement function --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent c8eaa3b commit e4b72c4

52 files changed

Lines changed: 1577 additions & 966 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"mini-css-extract-plugin": "^2.3.0",
2727
"normalizr": "^3.6.1",
2828
"posthog-js": "^1.259.0",
29+
"re-resizable": "^6.11.2",
2930
"react": "^18.1.0",
3031
"react-cmdk": "^1.3.9",
3132
"react-datepicker": "^4.2.1",

packages/web/src/common/constants/web.constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ export const ID_SOMEDAY_EVENT_FORM = "Someday Event Form";
2121
export const ID_EVENT_FORM_ACTION_MENU = "event-action-menu";
2222
export const ID_SOMEDAY_EVENT_ACTION_MENU = "someday-event-action-menu";
2323
export const DATA_EVENT_ELEMENT_ID = "data-event-id";
24+
export const DATA_DRAFT_EVENT = "data-draft-event";
25+
export const DATA_NEW_DRAFT_EVENT = "data-new-draft-event";
2426
export const DATA_TASK_ELEMENT_ID = "data-task-id";
2527
export const ID_CONTEXT_MENU_ITEMS = "context-menu-items";
2628
export const ID_ADD_TASK_BUTTON = "add-task-button";

packages/web/src/common/context/mouse-position.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ export function getElementAtCursor() {
7777
return getElementAtPoint(getCursorPosition());
7878
}
7979

80-
export function isElementInViewport(element: HTMLElement) {
80+
export function isElementInViewport(
81+
element: Pick<Element, "getBoundingClientRect">,
82+
) {
8183
const rect = element.getBoundingClientRect();
8284

8385
return (

packages/web/src/common/context/open-at-cursor.tsx

Lines changed: 0 additions & 156 deletions
This file was deleted.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { useCallback, useState } from "react";
2+
import {
3+
OpenChangeReason,
4+
Placement,
5+
ReferenceType,
6+
UseFloatingOptions,
7+
autoUpdate,
8+
flip,
9+
offset,
10+
useFloating,
11+
} from "@floating-ui/react";
12+
import {
13+
closeFloatingAtCursor,
14+
useFloatingNodeIdAtCursor,
15+
useFloatingPlacementAtCursor,
16+
useFloatingReferenceAtCursor,
17+
useFloatingStrategyAtCursor,
18+
} from "@web/common/hooks/useOpenAtCursor";
19+
import { theme } from "@web/common/styles/theme";
20+
21+
const themeSpacing = parseInt(theme.spacing.xs);
22+
23+
const placements: Placement[] = [
24+
"right-start",
25+
"bottom-start",
26+
"top-start",
27+
"left-start",
28+
];
29+
30+
export function useFloatingAtCursor(
31+
onOpenChange?: UseFloatingOptions<ReferenceType>["onOpenChange"],
32+
): ReturnType<typeof useFloating> {
33+
const [open, setOpen] = useState(false);
34+
const nodeId = useFloatingNodeIdAtCursor() ?? undefined;
35+
const placement = useFloatingPlacementAtCursor();
36+
const strategy = useFloatingStrategyAtCursor();
37+
const reference = useFloatingReferenceAtCursor();
38+
39+
const handleOpenChange = useCallback(
40+
(open: boolean, event?: Event, reason?: OpenChangeReason) => {
41+
onOpenChange?.(open, event, reason);
42+
43+
if (!open) closeFloatingAtCursor();
44+
45+
setOpen(open);
46+
},
47+
[onOpenChange],
48+
);
49+
50+
const floating = useFloating({
51+
open,
52+
nodeId,
53+
placement,
54+
strategy,
55+
elements: { reference },
56+
middleware: [
57+
offset(({ rects, placement }) => {
58+
switch (placement) {
59+
case "bottom":
60+
case "top":
61+
return -rects.reference.height / 2 - rects.floating.height / 2;
62+
default:
63+
return themeSpacing;
64+
}
65+
}),
66+
flip(({ placement }) => {
67+
return {
68+
fallbackPlacements: placements.filter((p) => p !== placement),
69+
fallbackStrategy: "bestFit",
70+
fallbackAxisSideDirection: "start",
71+
crossAxis: placement.includes("-"),
72+
};
73+
}),
74+
],
75+
onOpenChange: handleOpenChange,
76+
whileElementsMounted: autoUpdate,
77+
});
78+
79+
return floating;
80+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { useEffect, useState } from "react";
2+
import { distinctUntilChanged, share } from "rxjs/operators";
3+
import { maxGridZIndex$ } from "@web/common/utils/dom/grid-organization.util";
4+
5+
const maxZIndex$ = maxGridZIndex$.pipe(distinctUntilChanged(), share());
6+
7+
export function useGridMaxZIndex() {
8+
const [maxZIndex, setMaxZIndex] = useState(maxGridZIndex$.getValue());
9+
10+
useEffect(() => {
11+
const subscription = maxZIndex$.subscribe(setMaxZIndex);
12+
13+
return () => subscription.unsubscribe();
14+
}, []);
15+
16+
return maxZIndex;
17+
}
Lines changed: 85 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,90 @@
1-
import { useEffect } from "react";
2-
import { UseFloatingOptions } from "@floating-ui/react";
3-
import {
4-
OpenAtCursorContext,
5-
openedAtCursorChange$,
6-
} from "@web/common/context/open-at-cursor";
7-
import { useMetaContext } from "@web/common/hooks/useMetaContext";
8-
9-
export function useOpenAtCursor({
10-
onOpenChange,
11-
}: Pick<UseFloatingOptions, "onOpenChange"> = {}) {
12-
const context = useMetaContext(OpenAtCursorContext, "useOpenAtCursor");
1+
import { useEffect, useState } from "react";
2+
import { BehaviorSubject, Observable, distinctUntilChanged, share } from "rxjs";
3+
import { Placement, Strategy } from "@floating-ui/react";
4+
5+
export enum CursorItem {
6+
EventForm = "EventForm",
7+
EventPreview = "EventPreview",
8+
EventContextMenu = "EventContextMenu",
9+
}
10+
11+
const nodeId$ = new BehaviorSubject<CursorItem | null>(null);
12+
const placement$ = new BehaviorSubject<Placement>("right-start");
13+
const strategy$ = new BehaviorSubject<Strategy>("absolute");
14+
const reference$ = new BehaviorSubject<Element | null>(null);
15+
const $nodeId = nodeId$.pipe(distinctUntilChanged(), share());
16+
const $placement = placement$.pipe(distinctUntilChanged(), share());
17+
const $strategy = strategy$.pipe(distinctUntilChanged(), share());
18+
const $reference = reference$.pipe(distinctUntilChanged(), share());
19+
20+
function useValue<T>(
21+
subject: BehaviorSubject<T>,
22+
sharedSubject: Observable<T>,
23+
): T {
24+
const [value, setValue] = useState<T>(subject.getValue());
1325

1426
useEffect(() => {
15-
if (onOpenChange) {
16-
const subscription = openedAtCursorChange$.subscribe((args) =>
17-
onOpenChange(...args),
18-
);
27+
const subscription = sharedSubject.subscribe((v) => {
28+
setValue(v);
29+
});
30+
31+
return () => subscription.unsubscribe();
32+
});
33+
34+
return value;
35+
}
36+
37+
export function useFloatingNodeIdAtCursor(): CursorItem | null {
38+
return useValue<CursorItem | null>(nodeId$, $nodeId);
39+
}
40+
41+
export function useFloatingPlacementAtCursor(): Placement {
42+
return useValue<Placement>(placement$, $placement);
43+
}
44+
45+
export function useFloatingStrategyAtCursor(): Strategy {
46+
return useValue<Strategy>(strategy$, $strategy);
47+
}
48+
49+
export function useFloatingReferenceAtCursor(): Element | null {
50+
return useValue<Element | null>(reference$, $reference);
51+
}
52+
53+
export function setFloatingNodeIdAtCursor(nodeId: CursorItem | null) {
54+
nodeId$.next(nodeId);
55+
}
56+
57+
export function setFloatingPlacementAtCursor(placement: Placement) {
58+
placement$.next(placement);
59+
}
60+
61+
export function setFloatingStrategyAtCursor(strategy: Strategy) {
62+
strategy$.next(strategy);
63+
}
1964

20-
return () => subscription.unsubscribe();
21-
}
22-
}, [onOpenChange]);
65+
export function setFloatingReferenceAtCursor(reference: Element | null) {
66+
reference$.next(reference);
67+
}
68+
69+
export function openFloatingAtCursor({
70+
nodeId,
71+
reference,
72+
placement = "right-start",
73+
strategy = "absolute",
74+
}: {
75+
nodeId: CursorItem;
76+
reference: Element;
77+
placement?: Placement;
78+
strategy?: Strategy;
79+
}) {
80+
setFloatingNodeIdAtCursor(nodeId);
81+
setFloatingPlacementAtCursor(placement);
82+
setFloatingStrategyAtCursor(strategy);
83+
setFloatingReferenceAtCursor(reference);
84+
}
2385

24-
return context;
86+
export function closeFloatingAtCursor() {
87+
setFloatingNodeIdAtCursor(null);
88+
setFloatingPlacementAtCursor("right-start");
89+
setFloatingReferenceAtCursor(null);
2590
}

0 commit comments

Comments
 (0)