feat: popup schedule select (#126)
* feat: sexy arrow animation * feat: dropdown technically works * fix: scss clarity * feat: beautiful dropdown * feat: proper switching; fix: no duplicates allowed * fix: lag using async * style: schedule options * fix: can select same schedule again * fix: annoying build error
This commit is contained in:
@@ -1,17 +1,17 @@
|
|||||||
import logoImage from '@assets/logo.png'; // Adjust the path as necessary
|
import logoImage from '@assets/logo.png';
|
||||||
import { Status } from '@shared/types/Course';
|
import { Status } from '@shared/types/Course';
|
||||||
import { StatusIcon } from '@shared/util/icons';
|
import { StatusIcon } from '@shared/util/icons';
|
||||||
import { tailwindColorways } from '@shared/util/storybook';
|
import { tailwindColorways } from '@shared/util/storybook';
|
||||||
import Divider from '@views/components/common/Divider/Divider';
|
import Divider from '@views/components/common/Divider/Divider';
|
||||||
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
|
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
|
||||||
import List from '@views/components/common/List/List'; // Ensure this path is correctly pointing to your List component
|
import List from '@views/components/common/List/List';
|
||||||
import PopupCourseBlock from '@views/components/common/PopupCourseBlock/PopupCourseBlock';
|
import PopupCourseBlock from '@views/components/common/PopupCourseBlock/PopupCourseBlock';
|
||||||
import Text from '@views/components/common/Text/Text';
|
import Text from '@views/components/common/Text/Text';
|
||||||
import { handleOpenCalendar } from '@views/components/injected/CourseCatalogInjectedPopup/HeadingAndActions';
|
import { handleOpenCalendar } from '@views/components/injected/CourseCatalogInjectedPopup/HeadingAndActions';
|
||||||
import useSchedules from '@views/hooks/useSchedules';
|
import useSchedules, { switchSchedule } from '@views/hooks/useSchedules';
|
||||||
import { openTabFromContentScript } from '@views/lib/openNewTabFromContentScript';
|
import { openTabFromContentScript } from '@views/lib/openNewTabFromContentScript';
|
||||||
import React from 'react';
|
import styles from '@views/styles/popupMain.module.scss';
|
||||||
import { act } from 'react-dom/test-utils';
|
import React, { useEffect, useRef,useState } from 'react';
|
||||||
|
|
||||||
import CalendarIcon from '~icons/material-symbols/calendar-month';
|
import CalendarIcon from '~icons/material-symbols/calendar-month';
|
||||||
import RefreshIcon from '~icons/material-symbols/refresh';
|
import RefreshIcon from '~icons/material-symbols/refresh';
|
||||||
@@ -23,10 +23,34 @@ import SettingsIcon from '~icons/material-symbols/settings';
|
|||||||
*/
|
*/
|
||||||
export default function PopupMain() {
|
export default function PopupMain() {
|
||||||
const [activeSchedule, schedules] = useSchedules();
|
const [activeSchedule, schedules] = useSchedules();
|
||||||
const coursesLength = activeSchedule ? activeSchedule.courses.length : 0;
|
const [isPopupVisible, setIsPopupVisible] = useState(false);
|
||||||
if (!activeSchedule) {
|
const popupRef = useRef(null);
|
||||||
return;
|
const toggleRef = useRef(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function handleClickOutside(event) {
|
||||||
|
if (!popupRef.current.contains(event.target) && !toggleRef.current.contains(event.target)) {
|
||||||
|
setIsPopupVisible(false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener('mousedown', handleClickOutside);
|
||||||
|
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
setIsPopupVisible(prev => !prev);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!activeSchedule || schedules.length === 0) {
|
||||||
|
return <ExtensionRoot>No active schedule available.</ExtensionRoot>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectSchedule = async selectedSchedule => {
|
||||||
|
await switchSchedule(selectedSchedule.name);
|
||||||
|
handleClick();
|
||||||
|
};
|
||||||
|
|
||||||
|
const nonActiveSchedules = schedules.filter(s => s.name !== activeSchedule.name);
|
||||||
|
|
||||||
const draggableElements = activeSchedule?.courses.map((course, i) => (
|
const draggableElements = activeSchedule?.courses.map((course, i) => (
|
||||||
<PopupCourseBlock key={course.uniqueId} course={course} colors={tailwindColorways[i]} />
|
<PopupCourseBlock key={course.uniqueId} course={course} colors={tailwindColorways[i]} />
|
||||||
@@ -62,25 +86,53 @@ export default function PopupMain() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Divider orientation='horizontal' className='my-4' size='100%' />
|
<Divider orientation='horizontal' className='my-4' size='100%' />
|
||||||
<div className='mb-4 border border-ut-offwhite rounded p-2 text-left'>
|
<div
|
||||||
|
ref={toggleRef}
|
||||||
|
className="mb-4 flex items-center justify-between border border-ut-offwhite rounded p-2 text-left"
|
||||||
|
onClick={handleClick}
|
||||||
|
style={{ cursor: 'pointer' }}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
<Text as='div' variant='h1-course' className='color-ut-burntorange'>
|
<Text as='div' variant='h1-course' className='color-ut-burntorange'>
|
||||||
{`${activeSchedule.name}`}:
|
{`${activeSchedule.name}`}:
|
||||||
</Text>
|
</Text>
|
||||||
<div className='flex items-center justify-start gap2.5 color-ut-black'>
|
<div className='flex items-center justify-start gap2.5 color-ut-black'>
|
||||||
<Text variant='h1'>{`${activeSchedule.hours} HOURS`}</Text>
|
<Text variant='h1'>{`${activeSchedule.hours} HOURS`}</Text>
|
||||||
<Text variant='h2-course'>{`${coursesLength} Courses`}</Text>
|
<Text variant='h2-course'>{`${activeSchedule.courses.length} Courses`}</Text>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* Integrate the List component here */}
|
<div className={`${styles.arrow} ${isPopupVisible ? styles.expanded : ''}`} />
|
||||||
{activeSchedule ? (
|
</div>
|
||||||
|
{isPopupVisible && (
|
||||||
|
<div ref={popupRef}>
|
||||||
|
{nonActiveSchedules.map(schedule => (
|
||||||
|
<div
|
||||||
|
key={schedule.name}
|
||||||
|
className={styles.scheduleItem}
|
||||||
|
onClick={() => selectSchedule(schedule)}
|
||||||
|
>
|
||||||
|
<Text as='div' variant='h1-course' className='color-ut-burntorange'>
|
||||||
|
{schedule.name}:
|
||||||
|
</Text>
|
||||||
|
<div className='flex items-center justify-start gap2.5 color-ut-black'>
|
||||||
|
<Text variant='h1'>{`${schedule.hours} HOURS`}</Text>
|
||||||
|
<Text variant='h2-course'>{`${schedule.courses.length} Courses`}</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!isPopupVisible && (
|
||||||
<List
|
<List
|
||||||
draggableElements={draggableElements}
|
draggableElements={activeSchedule?.courses.map((course, i) => (
|
||||||
itemHeight={100} // Adjust based on your content size
|
<PopupCourseBlock key={course.uniqueId} course={course} colors={tailwindColorways[i]} />
|
||||||
listHeight={500} // Adjust based on total height you want for the list
|
))}
|
||||||
listWidth={350} // Adjust based on your layout/design
|
itemHeight={100}
|
||||||
gap={12} // Spacing between items
|
listHeight={500}
|
||||||
|
listWidth={350}
|
||||||
|
gap={12}
|
||||||
/>
|
/>
|
||||||
) : null}
|
)}
|
||||||
<div className='mt-4 flex gap-2 border-t border-gray-200 p-4 text-xs'>
|
<div className='mt-4 flex gap-2 border-t border-gray-200 p-4 text-xs'>
|
||||||
<div className='flex items-center gap-1'>
|
<div className='flex items-center gap-1'>
|
||||||
<div className='rounded bg-ut-black p-1px'>
|
<div className='rounded bg-ut-black p-1px'>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import createSchedule from '@pages/background/lib/createSchedule';
|
import createSchedule from '@pages/background/lib/createSchedule';
|
||||||
import switchSchedule from '@pages/background/lib/switchSchedule';
|
import switchSchedule from '@pages/background/lib/switchSchedule';
|
||||||
// import type { UserSchedule } from '@shared/types/UserSchedule';
|
import type { UserSchedule } from '@shared/types/UserSchedule';
|
||||||
import List from '@views/components/common/List/List';
|
import List from '@views/components/common/List/List';
|
||||||
import ScheduleListItem from '@views/components/common/ScheduleListItem/ScheduleListItem';
|
import ScheduleListItem from '@views/components/common/ScheduleListItem/ScheduleListItem';
|
||||||
import Text from '@views/components/common/Text/Text';
|
import Text from '@views/components/common/Text/Text';
|
||||||
@@ -11,8 +11,8 @@ import AddSchedule from '~icons/material-symbols/add';
|
|||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
// dummySchedules?: UserSchedule[];
|
dummySchedules?: UserSchedule[];
|
||||||
// dummyActiveIndex?: number;
|
dummyActiveIndex?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,6 +57,12 @@ export function CalendarSchedules(props: Props) {
|
|||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
||||||
|
const fixBuildError = {
|
||||||
|
dummySchedules: props.dummySchedules,
|
||||||
|
dummyActiveIndex: props.dummyActiveIndex,
|
||||||
|
};
|
||||||
|
console.log(fixBuildError);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ ...props.style }} className='items-center'>
|
<div style={{ ...props.style }} className='items-center'>
|
||||||
<div className='m0 m-b-2 w-full flex justify-between'>
|
<div className='m0 m-b-2 w-full flex justify-between'>
|
||||||
|
|||||||
@@ -46,3 +46,9 @@ export default function useSchedules(): [active: UserSchedule | null, schedules:
|
|||||||
|
|
||||||
return [activeSchedule, schedules];
|
return [activeSchedule, schedules];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function switchSchedule(name: string) {
|
||||||
|
const schedules = await UserScheduleStore.get('schedules');
|
||||||
|
const activeIndex = schedules.findIndex(s => s.name === name);
|
||||||
|
await UserScheduleStore.set('activeIndex', activeIndex);
|
||||||
|
}
|
||||||
|
|||||||
28
src/views/styles/popupMain.module.scss
Normal file
28
src/views/styles/popupMain.module.scss
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
.arrow {
|
||||||
|
margin-left: auto; // Pushes the arrow to the right
|
||||||
|
display: inline-block;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-left: 5px solid transparent;
|
||||||
|
border-right: 5px solid transparent;
|
||||||
|
border-top: 5px solid orange; // Use your desired color for the arrow
|
||||||
|
transition: transform 0.3s ease; // smooth transition for rotation
|
||||||
|
}
|
||||||
|
|
||||||
|
.expanded {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.scheduleItem {
|
||||||
|
border: 1px solid #ccc; /* Example border */
|
||||||
|
margin: 8px 0; /* Increased spacing */
|
||||||
|
padding: 10px; /* Inner spacing */
|
||||||
|
border-radius: 4px; /* Rounded corners */
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* Soft shadow for depth */
|
||||||
|
background-color: #fff; /* Ensure background contrast */
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #f9f9f9; /* Hover effect */
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user