refactor: ccpopup (#172)

* refactor: dialog animation improvements

* refactor: update ccpopup to match designs
This commit is contained in:
Razboy20
2024-03-15 23:49:26 -05:00
committed by GitHub
parent d04818ccd8
commit ed4fbe5651
7 changed files with 70 additions and 49 deletions

View File

@@ -27,7 +27,7 @@ export function Chip({ label }: React.PropsWithChildren<Props>): JSX.Element {
<Text <Text
as='div' as='div'
variant='h4' variant='h4'
className='min-w-5 inline-flex items-center justify-center gap-2.5 rounded-lg px-1 py-0.5' className='min-w-5 inline-flex items-center justify-center gap-2.5 rounded-lg px-1.5 py-0.5'
style={{ style={{
backgroundColor: '#FFD600', backgroundColor: '#FFD600',
}} }}

View File

@@ -25,28 +25,28 @@ export default function Dialog(props: PropsWithChildren<DialogProps>): JSX.Eleme
<ExtensionRoot> <ExtensionRoot>
<Transition.Child <Transition.Child
as={Fragment} as={Fragment}
enter='transition duration-300 ease-out' enter='transition duration-300 motion-reduce:duration-150 ease-out'
enterFrom='opacity-0' enterFrom='opacity-0'
enterTo='opacity-100' enterTo='opacity-100'
leave='transition duration-150 ease-in delay-25' leave='transition duration-150 ease-in delay-25'
leaveFrom='opacity-100' leaveFrom='opacity-100'
leaveTo='opacity-0' leaveTo='opacity-0'
> >
<div className={clsx('fixed inset-0 z-50 bg-neutral-500/25')} /> <div className={clsx('fixed inset-0 z-50 bg-slate-700/35')} />
</Transition.Child> </Transition.Child>
<Transition.Child <Transition.Child
as={Fragment} as={Fragment}
enter='transition duration-400 ease-[cubic-bezier(0.15,0.3,0.2,1)]' enter='transition duration-375 motion-reduce:duration-0 ease-[cubic-bezier(0.05,0.4,0.2,1)]'
enterFrom='transform scale-95 opacity-0' enterFrom='transform-gpu scale-95 opacity-0'
enterTo='transform scale-100 opacity-100' enterTo='transform-gpu scale-100 opacity-100'
leave='transition duration-250 ease-[cubic-bezier(0.23,0.01,0.92,0.72)]' leave='transition duration-250 motion-reduce:duration-0 ease-[cubic-bezier(0.23,0.01,0.92,0.72)]'
leaveFrom='transform scale-100 opacity-100' leaveFrom='transform-gpu scale-100 opacity-100'
leaveTo='transform scale-95 opacity-0' leaveTo='transform-gpu scale-95 opacity-0'
> >
<div className='fixed inset-0 z-50 flex items-center justify-center'> <div className='fixed inset-0 z-50 flex items-center justify-center'>
<HDialog.Panel <HDialog.Panel
className={clsx( className={clsx(
'z-99 max-h-[80vh] flex flex-col overflow-y-auto rounded bg-white shadow-xl', 'z-99 max-h-[90vh] flex flex-col overflow-y-auto border border-ut-offwhite rounded-lg bg-white shadow-xl ml-[calc(100vw-100%)] mt-[calc(100vw-100%)]',
className className
)} )}
> >

View File

@@ -19,7 +19,7 @@
.p { .p {
font-size: 1rem; font-size: 1rem;
font-weight: 400; font-weight: 400;
letter-spacing: 0.025rem; letter-spacing: 0.02em;
} }
.h4 { .h4 {
@@ -42,7 +42,7 @@
.h2-course { .h2-course {
font-size: 1rem; font-size: 1rem;
font-weight: 500; font-weight: 500;
letter-spacing: 0.03125rem; letter-spacing: 0.03125em;
text-transform: capitalize; text-transform: capitalize;
} }

View File

@@ -27,11 +27,13 @@ function CourseCatalogInjectedPopup({ course, ...rest }: CourseCatalogInjectedPo
const [activeSchedule] = useSchedules(); const [activeSchedule] = useSchedules();
return ( return (
<Dialog className='max-w-[780px] px-6' {...rest} initialFocus={emptyRef}> <Dialog className='max-w-[780px] overflow-y-hidden px-4' {...rest} initialFocus={emptyRef}>
<div className='hidden' ref={emptyRef} /> <div className='hidden' ref={emptyRef} />
<HeadingAndActions course={course} onClose={rest.onClose as () => void} activeSchedule={activeSchedule} /> <HeadingAndActions course={course} onClose={rest.onClose as () => void} activeSchedule={activeSchedule} />
<Description course={course} /> <div className='overflow-y-auto px-2'>
<GradeDistribution course={course} /> <Description course={course} />
<GradeDistribution course={course} />
</div>
</Dialog> </Dialog>
); );
} }

View File

@@ -56,26 +56,24 @@ export default function Description({ course }: DescriptionProps): JSX.Element {
return ( return (
<> <>
{status === LoadStatus.ERROR && ( {status === LoadStatus.ERROR && (
<Text color='theme-red'>Please refresh the page and log back in using your UT EID and password</Text> <Text color='theme-red'>Please refresh the page and log back in using your UT EID and password.</Text>
)} )}
{/* TODO (achadaga): would be nice to have a new spinner here */} {/* TODO (achadaga): would be nice to have a new spinner here */}
{status === LoadStatus.LOADING && <Spinner />} {status === LoadStatus.LOADING && <Spinner />}
{status === LoadStatus.DONE && ( {status === LoadStatus.DONE && (
<ul className='my-[5px] space-y-1.5 children:marker:text-ut-burntorange'> <ul className='ml-6 mt-1.5 list-disc list-outside space-y-1.5'>
{description.map(line => { {description.map(line => {
const isKeywordPresent = keywords.some(keyword => line.toLowerCase().includes(keyword)); const isKeywordPresent = keywords.some(keyword => line.toLowerCase().includes(keyword));
return ( return (
<div key={line} className='flex gap-2'> <li
<span className='text-ut-burntorange'></span> key={line}
<li key={line}> className={clsx({
<Text 'children:font-bold! text-ut-burntorange marker:text-ut-burntorange':
variant='p' isKeywordPresent,
className={clsx({ 'font-bold! text-ut-burntorange': isKeywordPresent })} })}
> >
{line} <Text variant='p'>{line}</Text>
</Text> </li>
</li>
</div>
); );
})} })}
</ul> </ul>

View File

@@ -97,13 +97,13 @@ export default function GradeDistribution({ course }: GradeDistributionProps): J
subtitle: { text: undefined }, subtitle: { text: undefined },
legend: { enabled: false }, legend: { enabled: false },
xAxis: { xAxis: {
title: { text: 'Grade' }, title: { text: 'Grades' },
categories: ['A', 'A-', 'B+', 'B', 'B-', 'C+', 'C', 'C-', 'D+', 'D', 'D-', 'F'], categories: ['A', 'A-', 'B+', 'B', 'B-', 'C+', 'C', 'C-', 'D+', 'D', 'D-', 'F'],
crosshair: true, crosshair: true,
}, },
yAxis: { yAxis: {
min: 0, min: 0,
title: { text: 'Number of Students' }, title: { text: 'Students' },
}, },
chart: { chart: {
style: { fontFamily: 'Roboto Flex, Roboto Flex Local', fontWeight: '600' }, style: { fontFamily: 'Roboto Flex, Roboto Flex Local', fontWeight: '600' },

View File

@@ -1,11 +1,11 @@
import { background } from '@shared/messages'; import { background } from '@shared/messages';
import type { Course } from '@shared/types/Course'; import type { Course } from '@shared/types/Course';
import { Status } from '@shared/types/Course';
import type Instructor from '@shared/types/Instructor'; import type Instructor from '@shared/types/Instructor';
import type { UserSchedule } from '@shared/types/UserSchedule'; import type { UserSchedule } from '@shared/types/UserSchedule';
import { Button } from '@views/components/common/Button/Button'; import { Button } from '@views/components/common/Button/Button';
import { Chip, flagMap } from '@views/components/common/Chip/Chip'; import { Chip, flagMap } from '@views/components/common/Chip/Chip';
import Divider from '@views/components/common/Divider/Divider'; import Divider from '@views/components/common/Divider/Divider';
import Link from '@views/components/common/Link/Link';
import Text from '@views/components/common/Text/Text'; import Text from '@views/components/common/Text/Text';
import React from 'react'; import React from 'react';
@@ -66,7 +66,8 @@ export default function HeadingAndActions({ course, activeSchedule, onClose }: H
return `${capitalizeString(firstName)} ${capitalizeString(lastName)}`; return `${capitalizeString(firstName)} ${capitalizeString(lastName)}`;
}; };
const instructorString = instructors.map(getInstructorFullName).join(', '); const getBuildingUrl = (building: string) =>
`https://utdirect.utexas.edu/apps/campus/buildings/nlogon/maps/UTM/${building}`;
const handleCopy = () => { const handleCopy = () => {
navigator.clipboard.writeText(formattedUniqueId); navigator.clipboard.writeText(formattedUniqueId);
@@ -108,50 +109,71 @@ export default function HeadingAndActions({ course, activeSchedule, onClose }: H
}; };
return ( return (
<div className='w-full pb-3 pt-6'> <div className='w-full px-2 pb-3 pt-6 text-ut-black'>
<div className='flex flex-col'> <div className='flex flex-col'>
<div className='flex items-center gap-1'> <div className='flex items-center gap-1'>
<Text variant='h1' className='truncate'> <Text variant='h1' as='h1' className='truncate text-theme-black'>
{courseName} {courseName}
</Text> </Text>
<Text variant='h1' className='flex-1 whitespace-nowrap'> <Text variant='h1' as='h2' className='flex-1 whitespace-nowrap'>
{' '}
({department} {courseNumber}) ({department} {courseNumber})
</Text> </Text>
<Button color='ut-burntorange' variant='single' icon={Copy} onClick={handleCopy}> <Button color='ut-burntorange' variant='single' icon={Copy} onClick={handleCopy}>
{formattedUniqueId} {formattedUniqueId}
</Button> </Button>
<button className='bg-transparent p-0 btn' onClick={onClose}> <button className='bg-transparent p-0 text-theme-black btn' onClick={onClose}>
<CloseIcon className='h-7 w-7' /> <CloseIcon className='h-7 w-7' />
</button> </button>
</div> </div>
<div className='flex gap-2 flex-content-center'> <div className='flex items-center gap-2'>
{instructorString.length > 0 && ( {instructors.length > 0 && (
<Text variant='h4' className='inline-flex items-center justify-center'> <Text variant='h4' as='p' className='items-center justify-center'>
with {instructorString} with{' '}
{instructors
.map(instructor => (
<Link
key={instructor.fullName}
variant='h4'
href={instructor.getDirectoryUrl()}
className='link'
>
{getInstructorFullName(instructor)}
</Link>
))
.flatMap((el, i) => (i === 0 ? [el] : [', ', el]))}
</Text> </Text>
)} )}
<div className='flex-content-centr flex gap-1'> <div className='flex gap-1'>
{flags.map(flag => ( {flags.map(flag => (
<Chip key={flagMap[flag]} label={flagMap[flag]} /> <Chip key={flagMap[flag]} label={flagMap[flag]} />
))} ))}
</div> </div>
</div> </div>
<div className='flex flex-col'> <div className='mt-1 flex flex-col'>
{schedule.meetings.map(meeting => { {schedule.meetings.map(meeting => {
const daysString = meeting.getDaysString({ format: 'long', separator: 'long' }); const daysString = meeting.getDaysString({ format: 'long', separator: 'long' });
const timeString = meeting.getTimeString({ separator: ' to ', capitalize: false }); const timeString = meeting.getTimeString({ separator: ' to ', capitalize: false });
const locationString = meeting.location ? ` in ${meeting.location.building}` : '';
return ( return (
<Text key={daysString + timeString + locationString} variant='h4'> <Text key={daysString + timeString + meeting.location.building} variant='h4' as='p'>
{daysString} {timeString} {daysString} {timeString}
{locationString} {meeting.location && (
<>
{' in '}
<Link
href={getBuildingUrl(meeting.location.building)}
className='link'
variant='h4'
>
{meeting.location.building}
</Link>
</>
)}
</Text> </Text>
); );
})} })}
</div> </div>
</div> </div>
<div className='my-3 flex flex-wrap items-center gap-[15px]'> <div className='my-3 flex flex-wrap items-center gap-x-3.75 gap-y-2.5'>
<Button variant='filled' color='ut-burntorange' icon={CalendarMonth} onClick={handleOpenCalendar} /> <Button variant='filled' color='ut-burntorange' icon={CalendarMonth} onClick={handleOpenCalendar} />
<Divider size='1.75rem' orientation='vertical' /> <Divider size='1.75rem' orientation='vertical' />
<Button variant='outline' color='ut-blue' icon={Reviews} onClick={handleOpenRateMyProf}> <Button variant='outline' color='ut-blue' icon={Reviews} onClick={handleOpenRateMyProf}>
@@ -165,8 +187,7 @@ export default function HeadingAndActions({ course, activeSchedule, onClose }: H
</Button> </Button>
<Button <Button
variant='filled' variant='filled'
disabled={course.status !== Status.OPEN} color={!courseAdded ? 'ut-green' : 'theme-red'}
color={!courseAdded ? 'ut-green' : 'ut-red'}
icon={!courseAdded ? Add : Remove} icon={!courseAdded ? Add : Remove}
onClick={handleAddOrRemoveCourse} onClick={handleAddOrRemoveCourse}
> >