diff --git a/src/components/Calendar/index.tsx b/src/components/Calendar/index.tsx index d1d55e19..a3f8c7d4 100644 --- a/src/components/Calendar/index.tsx +++ b/src/components/Calendar/index.tsx @@ -21,11 +21,12 @@ export type CalendarProps = { compare?: boolean; pinnedFriendSchedules?: string[]; pinSelf?: boolean; + overlayFriendSchedules?: string[]; isAutosized?: boolean; }; // Object for storing Event object and Meeting object in the same array. -type CommmonMeetingObject = { +type CommonMeetingObject = { id: string; days: string[]; period: Period; @@ -55,6 +56,7 @@ export default function Calendar({ compare = false, pinnedFriendSchedules = [], pinSelf = true, + overlayFriendSchedules = [], isAutosized = false, }: CalendarProps): React.ReactElement { const [{ pinnedCrns, oscar, events, currentVersion }] = @@ -63,17 +65,10 @@ export default function Calendar({ const [{ friends }] = useContext(FriendContext); // Contains the rowIndex's and rowSize's passed into each crn's TimeBlocks - // e.g. crnSizeInfo[crn][day]["period.start-period.end"].rowIndex - const crnSizeInfo: Record< + // e.g. meetingSizeInfo[crn/id][day]["period.start-period.end"].rowIndex + const meetingSizeInfo: Record< string, - Record> - > = {}; - - // Contains the rowIndex's and rowSize's passed into each custom event's - // TimeBlocks, consistent with the rowIndex's and rowSize's of crns - const eventSizeInfo: Record< - string, - Record> + Record> > = {}; const daysRef = React.useRef(null); @@ -118,7 +113,7 @@ export default function Calendar({ // Find section using crn and convert the meetings into // an array of CommonMeetingObject - const crnMeetings: (CommmonMeetingObject | null)[] = crns + const crnMeetings: (CommonMeetingObject | null)[] = crns .flatMap((crn) => { const section = oscar.findSection(crn); if (section == null) return null; @@ -130,15 +125,14 @@ export default function Calendar({ days: meeting.days, period: meeting.period, event: false, - } as CommmonMeetingObject; + } as CommonMeetingObject; }); return temp; }) .filter((m) => m != null); - const meetings: CommmonMeetingObject[] = - crnMeetings as CommmonMeetingObject[]; + const meetings: CommonMeetingObject[] = crnMeetings as CommonMeetingObject[]; if (!compare || pinSelf) { // Add events to meetings array @@ -149,7 +143,7 @@ export default function Calendar({ days: event.days, period: event.period, event: true, - } as CommmonMeetingObject; + } as CommonMeetingObject; }) ); } @@ -160,21 +154,28 @@ export default function Calendar({ a.period.end - a.period.start - (b.period.end - b.period.start) ?? 0 ); - const friendSchedules: FriendCrnData[] = []; - const friendEvents: FriendEventData[] = []; + const friendSchedules: { data: FriendCrnData; overlay: boolean }[] = []; + const friendEvents: { data: FriendEventData; overlay: boolean }[] = []; if (compare) { Object.values(friends).forEach((friend) => Object.entries(friend.versions) - .filter((schedule) => pinnedFriendSchedules.includes(schedule[0])) + .filter( + (schedule) => + pinnedFriendSchedules.includes(schedule[0]) || + overlayFriendSchedules.includes(schedule[0]) + ) .forEach((schedule) => { - const friendMeetings: CommmonMeetingObject[] = []; + const friendMeetings: CommonMeetingObject[] = []; schedule[1].schedule.pinnedCrns.forEach((crn) => { friendSchedules.push({ - friend: friend.name, - scheduleName: schedule[1].name, - scheduleId: schedule[0], - crn, - } as FriendCrnData); + data: { + friend: friend.name, + scheduleName: schedule[1].name, + scheduleId: schedule[0], + crn, + } as FriendCrnData, + overlay: !pinnedFriendSchedules.includes(schedule[0]), + }); const section = oscar.findSection(crn); if (section == null) return; @@ -186,23 +187,26 @@ export default function Calendar({ days: meeting.days, period: meeting.period, event: false, - } as CommmonMeetingObject); + } as CommonMeetingObject); }); }); schedule[1].schedule.events.forEach((event) => { friendEvents.push({ - friend: friend.name, - scheduleName: schedule[1].name, - scheduleId: schedule[0], - id: event.id, - event, - } as FriendEventData); + data: { + friend: friend.name, + scheduleName: schedule[1].name, + scheduleId: schedule[0], + id: event.id, + event, + } as FriendEventData, + overlay: !pinnedFriendSchedules.includes(schedule[0]), + }); friendMeetings.push({ id: `${schedule[0]}-${event.id}`, days: event.days, period: event.period, event: true, - } as CommmonMeetingObject); + } as CommonMeetingObject); }); friendMeetings.sort( (a, b) => @@ -223,21 +227,13 @@ export default function Calendar({ if (period == null) return; meeting.days.forEach((day) => { - const crnPeriodInfos = Object.values(crnSizeInfo) - .flatMap((days) => - days != null ? Object.values(days[day] ?? {}) : [] - ) - .flatMap((info) => (info == null ? [] : [info])); - - const eventPeriodInfos = Object.values(eventSizeInfo) - .flatMap((days) => - days != null ? Object.values(days[day] ?? {}) : [] + const dayPeriodInfos = Object.values(meetingSizeInfo) + .flatMap( + (days) => (days != null ? Object.values(days[day] ?? {}) : []) ) - .flatMap((info) => (info == null ? [] : [info])); - - const dayPeriodInfos: (SectionBlockPosition | EventBlockPosition)[] = - crnPeriodInfos; - dayPeriodInfos.push(...eventPeriodInfos); + .flatMap((info) => + info == null ? [] : [info] + ); const curRowSize = dayPeriodInfos .filter( @@ -258,13 +254,13 @@ export default function Calendar({ curRowSize ); - if (!meeting.event) { - const courseSizeInfo = crnSizeInfo[meeting.id] || {}; - crnSizeInfo[meeting.id] = courseSizeInfo; + const mSizeInfo = meetingSizeInfo[meeting.id] || {}; + meetingSizeInfo[meeting.id] = mSizeInfo; - const daySizeInfo = courseSizeInfo[day] || {}; - courseSizeInfo[day] = daySizeInfo; + const daySizeInfo = mSizeInfo[day] || {}; + mSizeInfo[day] = daySizeInfo; + if (!meeting.event) { daySizeInfo[makeSizeInfoKey(period)] = { period, crn: meeting.id, @@ -272,13 +268,7 @@ export default function Calendar({ rowSize: curRowSize, }; } else { - const evtSizeInfo = eventSizeInfo[meeting.id] || {}; - eventSizeInfo[meeting.id] = evtSizeInfo; - - const eventDaySizeInfo = evtSizeInfo[day] || {}; - evtSizeInfo[day] = eventDaySizeInfo; - - eventDaySizeInfo[makeSizeInfoKey(meeting.period)] = { + daySizeInfo[makeSizeInfoKey(period)] = { period: meeting.period, id: meeting.id, rowIndex: curRowSize - 1, @@ -372,7 +362,7 @@ export default function Calendar({ capture={capture} includeDetailsPopover={!isAutosized && !capture} includeContent={!preview} - sizeInfo={crnSizeInfo[crn] ?? {}} + sizeInfo={meetingSizeInfo[crn] ?? {}} selectedMeeting={ selectedMeeting !== null && selectedMeeting[0] === crn ? [selectedMeeting[1], selectedMeeting[2]] @@ -401,7 +391,7 @@ export default function Calendar({ includeContent={!preview} capture={capture} includeDetailsPopover={false} - sizeInfo={crnSizeInfo[crn] ?? {}} + sizeInfo={meetingSizeInfo[crn] ?? {}} /> ))} {events && @@ -411,7 +401,7 @@ export default function Calendar({ scheduleId={compare ? currentVersion : undefined} event={event} capture={capture} - sizeInfo={eventSizeInfo[event.id] ?? {}} + sizeInfo={meetingSizeInfo[event.id] ?? {}} includeDetailsPopover={!isAutosized && !capture} includeContent={!preview} canBeTabFocused={!isAutosized && !capture} @@ -433,20 +423,21 @@ export default function Calendar({ /> ))} {compare && - friendSchedules.map((crn) => ( + friendSchedules.map(({ data, overlay }) => ( ))} {compare && - friendEvents.map((event) => ( + friendEvents.map(({ data, overlay }) => ( void; pinnedSchedules: string[]; pinSelf: boolean; @@ -419,6 +421,24 @@ export default function ComparisonContainer({ color={colorMap[scheduleId]} paletteInfo={paletteInfo} setPaletteInfo={setPaletteInfo} + hoverFriendSchedule={(): void => { + handleCompareSchedules( + undefined, + undefined, + undefined, + undefined, + [scheduleId] + ); + }} + unhoverFriendSchedule={(): void => { + handleCompareSchedules( + undefined, + undefined, + undefined, + undefined, + [] + ); + }} handleNameEditOnBlur={handleNameEditOnBlur} /> ); @@ -485,6 +505,8 @@ type ScheduleRowProps = { editInfo?: EditInfo; setEditInfo?: (info: EditInfo) => void; editValue?: string; + hoverFriendSchedule?: () => void; + unhoverFriendSchedule?: () => void; handleNameEditOnBlur?: () => void; }; @@ -514,6 +536,8 @@ function ScheduleRow({ editValue, setInvitationModalOpen, setInvitationModalEmail, + hoverFriendSchedule, + unhoverFriendSchedule, handleNameEditOnBlur, }: ScheduleRowProps): React.ReactElement { const tooltipId = useId(); @@ -530,7 +554,19 @@ function ScheduleRow({ const palette = hasPalette && paletteInfo === id; return ( -
+
{ + if (type === 'Schedule') { + hoverFriendSchedule?.(); + } + }} + onMouseLeave={(): void => { + if (type === 'Schedule') { + unhoverFriendSchedule?.(); + } + }} + >
void; pinnedSchedules: string[]; pinSelf: boolean; diff --git a/src/components/EventBlocks/index.tsx b/src/components/EventBlocks/index.tsx index b5b5615b..6f987ea7 100644 --- a/src/components/EventBlocks/index.tsx +++ b/src/components/EventBlocks/index.tsx @@ -23,6 +23,7 @@ export type EventBlocksProps = { owner?: string; scheduleName?: string; scheduleId?: string; + overlay?: boolean; capture: boolean; includeDetailsPopover: boolean; includeContent: boolean; @@ -43,6 +44,7 @@ export default function EventBlocks({ owner, scheduleName, scheduleId, + overlay = false, capture, sizeInfo, includeDetailsPopover, @@ -248,6 +250,7 @@ export default function EventBlocks({ ].join(' '), }, ])} + overlay={overlay} capture={capture} sizeInfo={sizeInfo} includeDetailsPopover={!dragging && includeDetailsPopover} diff --git a/src/components/Scheduler/index.tsx b/src/components/Scheduler/index.tsx index 5770d7ec..ddfceed5 100644 --- a/src/components/Scheduler/index.tsx +++ b/src/components/Scheduler/index.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { classes } from '../../utils/misc'; import { @@ -33,6 +33,23 @@ export default function Scheduler(): React.ReactElement { const { compare, pinned, pinSelf, expanded, setCompareState } = useCompareStateFromStorage(); + const [overlaySchedules, setOverlaySchedules] = useState([]); + + const handleCompareSchedules = useCallback( + ( + newCompare?: boolean, + newPinnedSchedules?: string[], + newPinSelf?: boolean, + newExpanded?: boolean, + newOverlaySchedules?: string[] + ) => { + setCompareState(newCompare, newPinnedSchedules, newPinSelf, newExpanded); + if (newOverlaySchedules !== undefined) { + setOverlaySchedules(newOverlaySchedules); + } + }, + [setCompareState, setOverlaySchedules] + ); return ( <> @@ -61,12 +78,13 @@ export default function Scheduler(): React.ReactElement { compare={compare} pinnedFriendSchedules={pinned} pinSelf={!compare || pinSelf} + overlayFriendSchedules={overlaySchedules} />
)} {(!mobile || tabIndex === 3) && (