From b8dd5f6c39133b542af40b58e8653fe46c69fe23 Mon Sep 17 00:00:00 2001 From: Kevin Wu Date: Sat, 7 Dec 2024 23:25:01 -0800 Subject: [PATCH] refactor: move code around and optimize --- .../src/components/Calendar/CalendarRoot.tsx | 5 +- .../components/Calendar/CalendarToolbar.tsx | 258 +----------------- .../add-schedule-button.tsx | 36 +++ .../delete-schedule-button.tsx | 40 +++ .../rename-schedule-button.tsx | 35 +++ .../schedule-select/schedule-select.tsx | 147 ++++++++++ .../src/components/dialogs/RenameSchedule.tsx | 2 +- 7 files changed, 269 insertions(+), 254 deletions(-) create mode 100644 apps/antalmanac/src/components/Calendar/Toolbar/schedule-select/schedule-select-buttons/add-schedule-button.tsx create mode 100644 apps/antalmanac/src/components/Calendar/Toolbar/schedule-select/schedule-select-buttons/delete-schedule-button.tsx create mode 100644 apps/antalmanac/src/components/Calendar/Toolbar/schedule-select/schedule-select-buttons/rename-schedule-button.tsx create mode 100644 apps/antalmanac/src/components/Calendar/Toolbar/schedule-select/schedule-select.tsx diff --git a/apps/antalmanac/src/components/Calendar/CalendarRoot.tsx b/apps/antalmanac/src/components/Calendar/CalendarRoot.tsx index 479b7c045..bbe670e33 100644 --- a/apps/antalmanac/src/components/Calendar/CalendarRoot.tsx +++ b/apps/antalmanac/src/components/Calendar/CalendarRoot.tsx @@ -7,7 +7,7 @@ import { memo, SyntheticEvent, useCallback, useEffect, useMemo, useState } from import { Calendar, DateLocalizer, momentLocalizer, Views } from 'react-big-calendar'; import { shallow } from 'zustand/shallow'; -import CalendarToolbar from './CalendarToolbar'; +import { CalendarToolbar } from './CalendarToolbar'; import CourseCalendarEvent, { CalendarEvent, CourseEvent } from './CourseCalendarEvent'; import { CalendarCourseEvent } from '$components/Calendar/calendar-course-event'; @@ -17,6 +17,7 @@ import { useHoveredStore } from '$stores/HoveredStore'; import { useTimeFormatStore } from '$stores/SettingsStore'; const localizer = momentLocalizer(moment); +const views = [Views.WEEK, Views.WORK_WEEK]; const components = { event: CalendarCourseEvent }; export const ScheduleCalendar = memo(() => { @@ -149,8 +150,6 @@ export const ScheduleCalendar = memo(() => { [showFinalsSchedule, finalsDateFormat, calendarGutterTimeFormat, calendarTimeFormat] ); - const views = useMemo(() => [Views.WEEK, Views.WORK_WEEK], []); - useEffect(() => { /** * If a final is on a Saturday or Sunday, let the calendar start on Saturday diff --git a/apps/antalmanac/src/components/Calendar/CalendarToolbar.tsx b/apps/antalmanac/src/components/Calendar/CalendarToolbar.tsx index aba79750f..de97f93a6 100644 --- a/apps/antalmanac/src/components/Calendar/CalendarToolbar.tsx +++ b/apps/antalmanac/src/components/Calendar/CalendarToolbar.tsx @@ -1,43 +1,17 @@ -import { - Add as AddIcon, - ArrowDropDown as ArrowDropDownIcon, - Edit as EditIcon, - Undo as UndoIcon, - Clear as ClearIcon, -} from '@mui/icons-material'; -import { Box, Button, IconButton, Paper, Popover, Tooltip, Typography, useTheme } from '@mui/material'; -import { useState, useMemo, useCallback, useEffect } from 'react'; +import { Undo as UndoIcon } from '@mui/icons-material'; +import { Box, Button, IconButton, Paper, Tooltip } from '@mui/material'; +import { useState, useCallback, useEffect, memo } from 'react'; import CustomEventDialog from './Toolbar/CustomEventDialog/CustomEventDialog'; -import { changeCurrentSchedule, undoDelete } from '$actions/AppStoreActions'; +import { undoDelete } from '$actions/AppStoreActions'; +import { SelectSchedulePopover } from '$components/Calendar/Toolbar/schedule-select/schedule-select'; import { ClearScheduleButton } from '$components/buttons/Clear'; -import { CopyScheduleButton } from '$components/buttons/Copy'; import DownloadButton from '$components/buttons/Download'; import ScreenshotButton from '$components/buttons/Screenshot'; -import AddScheduleDialog from '$components/dialogs/AddSchedule'; -import DeleteScheduleDialog from '$components/dialogs/DeleteSchedule'; -import RenameScheduleDialog from '$components/dialogs/RenameSchedule'; import analyticsEnum, { logAnalytics } from '$lib/analytics'; import AppStore from '$stores/AppStore'; -function handleScheduleChange(index: number) { - logAnalytics({ - category: analyticsEnum.calendar.title, - action: analyticsEnum.calendar.actions.CHANGE_SCHEDULE, - }); - changeCurrentSchedule(index); -} - -/** - * Creates an event handler callback that will change the current schedule to the one at a specified index. - */ -function createScheduleSelector(index: number) { - return () => { - handleScheduleChange(index); - }; -} - function handleUndo() { logAnalytics({ category: analyticsEnum.calendar.title, @@ -46,222 +20,6 @@ function handleUndo() { undoDelete(null); } -interface RenameScheduleButtonProps { - index: number; - disabled?: boolean; -} - -function RenameScheduleButton({ index, disabled }: RenameScheduleButtonProps) { - const [open, setOpen] = useState(false); - - const handleOpen = useCallback(() => { - setOpen(true); - }, []); - - const handleClose = useCallback(() => { - setOpen(false); - }, []); - - return ( - - - - - - - - - - - ); -} - -interface DeleteScheduleButtonProps { - index: number; - disabled?: boolean; -} - -function DeleteScheduleButton({ index, disabled }: DeleteScheduleButtonProps) { - const [open, setOpen] = useState(false); - - const handleOpen = useCallback(() => { - setOpen(true); - }, []); - - const handleClose = useCallback(() => { - setOpen(false); - }, []); - - return ( - - - - - - - - - - - ); -} - -interface AddScheduleButtonProps { - disabled: boolean; -} - -/** - * MenuItem nested in the select menu to add a new schedule through a dialog. - */ -function AddScheduleButton({ disabled }: AddScheduleButtonProps) { - const [open, setOpen] = useState(false); - - const handleOpen = useCallback(() => { - setOpen(true); - }, []); - - const handleClose = useCallback(() => { - setOpen(false); - }, []); - - return ( - <> - - - - ); -} - -/** - * Simulates an HTML select element using a popover. - * - * Can select a schedule, and also control schedule settings with buttons. - */ -function SelectSchedulePopover(props: { scheduleNames: string[] }) { - const [currentScheduleIndex, setCurrentScheduleIndex] = useState(() => AppStore.getCurrentScheduleIndex()); - const [skeletonMode, setSkeletonMode] = useState(() => AppStore.getSkeletonMode()); - - const [anchorEl, setAnchorEl] = useState(); - - const theme = useTheme(); - - // TODO: maybe these widths should be dynamic based on i.e. the viewport width? - - const minWidth = useMemo(() => 100, []); - const maxWidth = useMemo(() => 150, []); - - const open = useMemo(() => Boolean(anchorEl), [anchorEl]); - - const currentScheduleName = useMemo(() => { - return props.scheduleNames[currentScheduleIndex]; - }, [props.scheduleNames, currentScheduleIndex]); - - const handleClick = useCallback((event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }, []); - - const handleClose = useCallback(() => { - setAnchorEl(undefined); - }, []); - - const handleScheduleIndexChange = useCallback(() => { - setCurrentScheduleIndex(AppStore.getCurrentScheduleIndex()); - }, []); - - const handleSkeletonModeChange = () => { - setSkeletonMode(AppStore.getSkeletonMode()); - }; - - useEffect(() => { - AppStore.on('addedCoursesChange', handleScheduleIndexChange); - AppStore.on('customEventsChange', handleScheduleIndexChange); - AppStore.on('colorChange', handleScheduleIndexChange); - AppStore.on('currentScheduleIndexChange', handleScheduleIndexChange); - AppStore.on('skeletonModeChange', handleSkeletonModeChange); - - return () => { - AppStore.off('addedCoursesChange', handleScheduleIndexChange); - AppStore.off('customEventsChange', handleScheduleIndexChange); - AppStore.off('colorChange', handleScheduleIndexChange); - AppStore.off('currentScheduleIndexChange', handleScheduleIndexChange); - AppStore.off('skeletonModeChange', handleSkeletonModeChange); - }; - }, [handleScheduleIndexChange]); - - return ( - - - - - - {props.scheduleNames.map((name, index) => ( - - - - - - - - - - - ))} - - - - - - - - ); -} - export interface CalendarPaneToolbarProps { scheduleNames: string[]; currentScheduleIndex: number; @@ -272,7 +30,7 @@ export interface CalendarPaneToolbarProps { /** * The root toolbar will pass down the schedule names to its children. */ -function CalendarPaneToolbar(props: CalendarPaneToolbarProps) { +export const CalendarToolbar = memo((props: CalendarPaneToolbarProps) => { const { showFinalsSchedule, toggleDisplayFinalsSchedule } = props; const [scheduleNames, setScheduleNames] = useState(AppStore.getScheduleNames()); const [skeletonMode, setSkeletonMode] = useState(AppStore.getSkeletonMode()); @@ -359,6 +117,6 @@ function CalendarPaneToolbar(props: CalendarPaneToolbarProps) { ); -} +}); -export default CalendarPaneToolbar; +CalendarToolbar.displayName = 'CalendarToolbar'; diff --git a/apps/antalmanac/src/components/Calendar/Toolbar/schedule-select/schedule-select-buttons/add-schedule-button.tsx b/apps/antalmanac/src/components/Calendar/Toolbar/schedule-select/schedule-select-buttons/add-schedule-button.tsx new file mode 100644 index 000000000..887adbcc4 --- /dev/null +++ b/apps/antalmanac/src/components/Calendar/Toolbar/schedule-select/schedule-select-buttons/add-schedule-button.tsx @@ -0,0 +1,36 @@ +import { Add as AddIcon } from '@mui/icons-material'; +import { Box, Button, Typography } from '@mui/material'; +import { useCallback, useState } from 'react'; + +import AddScheduleDialog from '$components/dialogs/AddSchedule'; + +interface AddScheduleButtonProps { + disabled: boolean; +} + +/** + * MenuItem nested in the select menu to add a new schedule through a dialog. + */ +export function AddScheduleButton({ disabled }: AddScheduleButtonProps) { + const [open, setOpen] = useState(false); + + const handleOpen = useCallback(() => { + setOpen(true); + }, []); + + const handleClose = useCallback(() => { + setOpen(false); + }, []); + + return ( + + + + + ); +} diff --git a/apps/antalmanac/src/components/Calendar/Toolbar/schedule-select/schedule-select-buttons/delete-schedule-button.tsx b/apps/antalmanac/src/components/Calendar/Toolbar/schedule-select/schedule-select-buttons/delete-schedule-button.tsx new file mode 100644 index 000000000..9b67f8796 --- /dev/null +++ b/apps/antalmanac/src/components/Calendar/Toolbar/schedule-select/schedule-select-buttons/delete-schedule-button.tsx @@ -0,0 +1,40 @@ +import { Clear as ClearIcon } from '@mui/icons-material'; +import { Box, IconButton, Tooltip } from '@mui/material'; +import { useCallback, useState } from 'react'; + +import DeleteScheduleDialog from '$components/dialogs/DeleteSchedule'; +import AppStore from '$stores/AppStore'; + +interface DeleteScheduleButtonProps { + index: number; + disabled?: boolean; +} + +export function DeleteScheduleButton({ index, disabled }: DeleteScheduleButtonProps) { + const [open, setOpen] = useState(false); + + const handleOpen = useCallback(() => { + setOpen(true); + }, []); + + const handleClose = useCallback(() => { + setOpen(false); + }, []); + + return ( + + + + + + + + + + + ); +} diff --git a/apps/antalmanac/src/components/Calendar/Toolbar/schedule-select/schedule-select-buttons/rename-schedule-button.tsx b/apps/antalmanac/src/components/Calendar/Toolbar/schedule-select/schedule-select-buttons/rename-schedule-button.tsx new file mode 100644 index 000000000..ac69be9ec --- /dev/null +++ b/apps/antalmanac/src/components/Calendar/Toolbar/schedule-select/schedule-select-buttons/rename-schedule-button.tsx @@ -0,0 +1,35 @@ +import { Edit as EditIcon } from '@mui/icons-material'; +import { Box, IconButton, Tooltip } from '@mui/material'; +import { useCallback, useState } from 'react'; + +import RenameScheduleDialog from '$components/dialogs/RenameSchedule'; + +interface RenameScheduleButtonProps { + index: number; + disabled?: boolean; +} + +export function RenameScheduleButton({ index, disabled }: RenameScheduleButtonProps) { + const [open, setOpen] = useState(false); + + const handleOpen = useCallback(() => { + setOpen(true); + }, []); + + const handleClose = useCallback(() => { + setOpen(false); + }, []); + + return ( + + + + + + + + + + + ); +} diff --git a/apps/antalmanac/src/components/Calendar/Toolbar/schedule-select/schedule-select.tsx b/apps/antalmanac/src/components/Calendar/Toolbar/schedule-select/schedule-select.tsx new file mode 100644 index 000000000..02b770168 --- /dev/null +++ b/apps/antalmanac/src/components/Calendar/Toolbar/schedule-select/schedule-select.tsx @@ -0,0 +1,147 @@ +import { ArrowDropDown as ArrowDropDownIcon } from '@mui/icons-material'; +import { Box, Button, Popover, Typography, useTheme } from '@mui/material'; +import { useCallback, useEffect, useMemo, useState } from 'react'; + +import { changeCurrentSchedule } from '$actions/AppStoreActions'; +import { AddScheduleButton } from '$components/Calendar/Toolbar/schedule-select/schedule-select-buttons/add-schedule-button'; +import { DeleteScheduleButton } from '$components/Calendar/Toolbar/schedule-select/schedule-select-buttons/delete-schedule-button'; +import { RenameScheduleButton } from '$components/Calendar/Toolbar/schedule-select/schedule-select-buttons/rename-schedule-button'; +import { CopyScheduleButton } from '$components/buttons/Copy'; +import analyticsEnum, { logAnalytics } from '$lib/analytics'; +import AppStore from '$stores/AppStore'; + +function handleScheduleChange(index: number) { + logAnalytics({ + category: analyticsEnum.calendar.title, + action: analyticsEnum.calendar.actions.CHANGE_SCHEDULE, + }); + changeCurrentSchedule(index); +} + +/** + * Creates an event handler callback that will change the current schedule to the one at a specified index. + */ +function createScheduleSelector(index: number) { + return () => { + handleScheduleChange(index); + }; +} + +/** + * Simulates an HTML select element using a popover. + * + * Can select a schedule, and also control schedule settings with buttons. + */ +export function SelectSchedulePopover(props: { scheduleNames: string[] }) { + const theme = useTheme(); + + const [currentScheduleIndex, setCurrentScheduleIndex] = useState(() => AppStore.getCurrentScheduleIndex()); + const [skeletonMode, setSkeletonMode] = useState(() => AppStore.getSkeletonMode()); + const [anchorEl, setAnchorEl] = useState(); + + // TODO: maybe these widths should be dynamic based on i.e. the viewport width? + const minWidth = useMemo(() => 100, []); + const maxWidth = useMemo(() => 150, []); + + const open = useMemo(() => Boolean(anchorEl), [anchorEl]); + + const currentScheduleName = useMemo(() => { + return props.scheduleNames[currentScheduleIndex]; + }, [props.scheduleNames, currentScheduleIndex]); + + const handleClick = useCallback((event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }, []); + + const handleClose = useCallback(() => { + setAnchorEl(undefined); + }, []); + + const handleScheduleIndexChange = useCallback(() => { + setCurrentScheduleIndex(AppStore.getCurrentScheduleIndex()); + }, []); + + const handleSkeletonModeChange = () => { + setSkeletonMode(AppStore.getSkeletonMode()); + }; + + useEffect(() => { + AppStore.on('addedCoursesChange', handleScheduleIndexChange); + AppStore.on('customEventsChange', handleScheduleIndexChange); + AppStore.on('colorChange', handleScheduleIndexChange); + AppStore.on('currentScheduleIndexChange', handleScheduleIndexChange); + AppStore.on('skeletonModeChange', handleSkeletonModeChange); + + return () => { + AppStore.off('addedCoursesChange', handleScheduleIndexChange); + AppStore.off('customEventsChange', handleScheduleIndexChange); + AppStore.off('colorChange', handleScheduleIndexChange); + AppStore.off('currentScheduleIndexChange', handleScheduleIndexChange); + AppStore.off('skeletonModeChange', handleSkeletonModeChange); + }; + }, [handleScheduleIndexChange]); + + return ( + + + + + + {props.scheduleNames.map((name, index) => ( + + + + + + + + + + + ))} + + + + + + + + ); +} diff --git a/apps/antalmanac/src/components/dialogs/RenameSchedule.tsx b/apps/antalmanac/src/components/dialogs/RenameSchedule.tsx index a365413d3..aca9c387a 100644 --- a/apps/antalmanac/src/components/dialogs/RenameSchedule.tsx +++ b/apps/antalmanac/src/components/dialogs/RenameSchedule.tsx @@ -38,7 +38,7 @@ function RenameScheduleDialog(props: ScheduleNameDialogProps) { const handleCancel = useCallback(() => { onClose?.({}, 'escapeKeyDown'); - }, [onClose, index]); + }, [onClose]); const handleNameChange = useCallback((event: React.ChangeEvent) => { setName(event.target.value);