refactor(popup): match styles/reduce paint flicker (#136)

* refactor: match popup styles/reduce paint flicker

* fix: useSchedules hook

* feat: popup 

* fix: repaint issue on popup body

* fix: initial active schedule

* fix: center justification

* fix: reactivity error
This commit is contained in:
Razboy20
2024-03-13 12:33:54 -05:00
committed by GitHub
parent 0dff12232c
commit 1d8da6579e
8 changed files with 149 additions and 179 deletions

View File

@@ -1,5 +1,3 @@
import { Status } from '@shared/types/Course';
import { StatusIcon } from '@shared/util/icons';
import { tailwindColorways } from '@shared/util/storybook';
import Divider from '@views/components/common/Divider/Divider';
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
@@ -10,13 +8,16 @@ import { handleOpenCalendar } from '@views/components/injected/CourseCatalogInje
import useSchedules, { switchSchedule } from '@views/hooks/useSchedules';
import { openTabFromContentScript } from '@views/lib/openNewTabFromContentScript';
import clsx from 'clsx';
import React, { useEffect, useRef, useState } from 'react';
import React, { useState } from 'react';
import CalendarIcon from '~icons/material-symbols/calendar-month';
import RefreshIcon from '~icons/material-symbols/refresh';
import SettingsIcon from '~icons/material-symbols/settings';
import CourseStatus from './common/CourseStatus/CourseStatus';
import { LogoIcon } from './common/LogoIcon';
import ScheduleDropdown from './common/ScheduleDropdown/ScheduleDropdown';
import ScheduleListItem from './common/ScheduleListItem/ScheduleListItem';
/**
* Renders the main popup component.
@@ -24,38 +25,7 @@ import { LogoIcon } from './common/LogoIcon';
*/
export default function PopupMain(): JSX.Element {
const [activeSchedule, schedules] = useSchedules();
const [isPopupVisible, setIsPopupVisible] = useState(false);
const popupRef = useRef(null);
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) => (
<PopupCourseBlock key={course.uniqueId} course={course} colors={tailwindColorways[i]} />
));
const [isRefreshing, setIsRefreshing] = useState(false);
const handleOpenOptions = async () => {
const url = chrome.runtime.getURL('/options.html');
@@ -64,108 +34,79 @@ export default function PopupMain(): JSX.Element {
return (
<ExtensionRoot>
<div className='mx-auto max-w-sm rounded bg-white p-4 shadow-md'>
<div className='mb-2 flex items-center justify-between bg-white'>
<div className='flex items-center gap-2'>
<LogoIcon />
<div>
<Text as='div' variant='h1-course' className='color-ut-burntorange'>
UT Registration
</Text>
<Text as='div' variant='h1-course' className='color-ut-orange'>
Plus
</Text>
</div>
</div>
<div className='flex items-center'>
<button className='rounded-lg bg-ut-burntorange p2' onClick={handleOpenCalendar}>
<CalendarIcon className='text-white' />
</button>
<button className='bg-transparent btn' onClick={handleOpenOptions}>
<SettingsIcon className='h-5 w-5 color-ut-black' />
</button>
</div>
</div>
<Divider orientation='horizontal' className='my-4' size='100%' />
<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'>
{`${activeSchedule.name}`}:
</Text>
<div className='flex items-center justify-start gap2.5 color-ut-black'>
<Text variant='h1'>{activeSchedule.hours} HOURS</Text>
<Text variant='h2-course'>{activeSchedule.courses.length} Courses</Text>
</div>
</div>
<div
className={clsx(
'ml-auto inline-block h-0 w-0 border-l-5 border-r-5 border-t-5 border-transparent border-ut-orange transition-transform duration-300 ease-in-out',
{ 'rotate-180': isPopupVisible }
)}
/>
</div>
{isPopupVisible && (
<div ref={popupRef}>
{nonActiveSchedules.map(schedule => (
<div
key={schedule.name}
className='my-2 cursor-pointer border border-gray-300 rounded-md border-solid bg-white p-2 shadow-sm hover:bg-gray-100'
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 className='h-screen max-h-full flex flex-col bg-white'>
<div className='p-5 py-3.5'>
<div className='flex items-center justify-between bg-white'>
<div className='flex items-center gap-2'>
<LogoIcon />
<div className='flex flex-col'>
<span className='text-lg text-ut-burntorange font-medium leading-[18px]'>
UT Registration
<br />
</span>
<span className='text-lg text-ut-orange font-medium leading-[18px]'>Plus</span>
</div>
))}
</div>
)}
{!isPopupVisible && (
<List
draggableElements={activeSchedule?.courses.map((course, i) => (
<PopupCourseBlock key={course.uniqueId} course={course} colors={tailwindColorways[i]} />
))}
gap={12}
/>
)}
<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='rounded bg-ut-black p-1px'>
<StatusIcon status={Status.WAITLISTED} className='h-5 w-5 text-white' />
</div>
<Text as='span' variant='mini'>
WAITLISTED
</Text>
</div>
<div className='flex items-center gap-1'>
<div className='rounded bg-ut-black p-1px'>
<StatusIcon status={Status.CLOSED} className='h-5 w-5 text-white' />
<div className='flex items-center gap-2.5'>
<button className='bg-ut-burntorange px-2 py-1.25 btn' onClick={handleOpenCalendar}>
<CalendarIcon className='size-6 text-white' />
</button>
<button className='bg-transparent px-2 py-1.25 btn' onClick={handleOpenOptions}>
<SettingsIcon className='size-6 color-ut-black' />
</button>
</div>
<Text as='span' variant='mini'>
CLOSED
</Text>
</div>
<div className='flex items-center gap-1'>
<div className='rounded bg-ut-black p-1px'>
<StatusIcon status={Status.CANCELLED} className='h-5 w-5 text-white' />
</div>
<Text as='span' variant='mini'>
CANCELLED
</Text>
</div>
</div>
<div className='mt-2 text-center text-xs'>
<div className='inline-flex items-center justify-center text-ut-gray'>
<Text variant='mini'>DATA UPDATED ON: 12:00 AM 02/01/2024</Text>
<RefreshIcon className='ml-2 h-4 w-4 color-gray-600' />
<Divider orientation='horizontal' size='100%' />
<div className='px-5 pb-2.5 pt-3.75'>
<ScheduleDropdown>
<List
draggableElements={schedules.map((schedule, index) => (
<ScheduleListItem
active={false}
name={schedule.name}
onClick={() => {
switchSchedule(schedule.name);
}}
/>
))}
gap={10}
/>
</ScheduleDropdown>
</div>
<div className='flex-1 self-stretch overflow-y-auto px-5'>
{activeSchedule?.courses?.length > 0 && (
<List
draggableElements={activeSchedule?.courses.map((course, i) => (
<PopupCourseBlock key={course.uniqueId} course={course} colors={tailwindColorways[i]} />
))}
gap={10}
/>
)}
</div>
<div className='w-full flex flex-col items-center gap-1.25 p-5 pt-3.75'>
<div className='flex gap-2.5'>
<CourseStatus status='WAITLISTED' size='mini' />
<CourseStatus status='CLOSED' size='mini' />
<CourseStatus status='CANCELLED' size='mini' />
</div>
<div className='inline-flex items-center self-center gap-1'>
<Text variant='mini' className='text-ut-gray'>
DATA UPDATED ON: 12:00 AM 02/01/2024
</Text>
<button
className='h-4 w-4 bg-transparent p-0 btn'
onClick={() => {
setIsRefreshing(true);
}}
>
<RefreshIcon
className={clsx('h-4 w-4 text-ut-black animate-duration-800', {
'animate-spin': isRefreshing,
})}
/>
</button>
</div>
</div>
</div>

View File

@@ -1,12 +1,6 @@
@import 'src/views/styles/base.module.scss';
.extensionRoot {
@apply font-sans;
@apply font-sans h-full;
color: #303030;
-webkit-box-sizing: border-box;
box-sizing: border-box;
font-family: Inter, sans-serif;
font-weight: 300;
font-size: 14px;
line-height: 18px;
}

View File

@@ -45,9 +45,10 @@ export default function ScheduleDropdown(props: Props) {
</Disclosure.Button>
<Transition
className='contain-paint max-h-55 origin-top overflow-auto transition-all duration-400 ease-out-expo'
className='contain-paint max-h-55 origin-top overflow-auto transition-all duration-400 ease-in-out-expo'
enterFrom='transform scale-98 opacity-0 max-h-0!'
enterTo='transform scale-100 opacity-100 max-h-55'
leave='ease-out-expo'
leaveFrom='transform scale-100 opacity-100 max-h-55'
leaveTo='transform scale-98 opacity-0 max-h-0!'
>