Skip to content

Commit

Permalink
feat(ui): calendar header redesign (#479)
Browse files Browse the repository at this point in the history
* feat(ui): calendar header redesign (WIP)

* feat(ui): calendar header redesign (WIP)

* chore: use path alias

* feat(ui): calendar header redesign

* implement Isaiah changes

* refactor to prevent unnecessary recreations of resize observer

* clean up resize observer and remove unnecessary checks

* remove offwhite border from toolbar

* merge

* complete toolbar

* update screenreader functionality

* ensure truncation works

* merge

* finish new toolbar

* remove unused screen size hook and .mjs file

* add in export button with options

* add static size for export button dropdown to prevent shrinking on smaller viewports

* change schedule section min width to prevent shrinkage

* change text for schedule section to match small caps

* fix truncating issues with small caps

* remove hidden overflow

* add padding

* add min height for hader

* reserve scrollbar width

* tmp

* add sticky positioning to header

* fix inert prop issue

* remove pnpm lock file

* fix scrollbar appearing too early

* fix vertical stickiness

* fix(ui): fix header spacing

* fix(ui): update total hours and courses to be h4

* fix(ui): reduce top spacing on header

* fix(ui): remove header top padding

* fix(ui): stop bottom scrollbar from shifting layout

* feat: add functionality to header and fix screenshot spacing

* feat: add functionality to header and fix screenshot spacing

* fix(ui): allow scrollbar in header and adjust padding to compensate for reserved space

* fix(ui): make export options container hug children

* fix(ui): add offwhite border

* chore: add back lock file from main branch :)

* feat(ui): add reduced motion for accessability

* feat(ui): change right scrollbar on calendar grid to be hidden when not necessary

* chore: make all buttons except export invisible

* chore: remove all buttons except export and adjust hardcoded pixel widths for responsiveness

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
Co-authored-by: Razboy20 <razboy20@gmail.com>
  • Loading branch information
3 people authored Feb 6, 2025
1 parent 4752f58 commit 9c766c2
Show file tree
Hide file tree
Showing 9 changed files with 186 additions and 119 deletions.
2 changes: 1 addition & 1 deletion src/stories/components/calendar/CalendarHeader.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Meta, StoryObj } from '@storybook/react';
import CalendarHeader from '@views/components/calendar/CalendarHeader';
import CalendarHeader from '@views/components/calendar/CalendarHeader/CalendarHeader';

const meta = {
title: 'Components/Calendar/CalendarHeader',
Expand Down
18 changes: 13 additions & 5 deletions src/views/components/calendar/Calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { CRX_PAGES } from '@shared/types/CRXPages';
import { openReportWindow } from '@shared/util/openReportWindow';
import CalendarBottomBar from '@views/components/calendar/CalendarBottomBar';
import CalendarGrid from '@views/components/calendar/CalendarGrid';
import CalendarHeader from '@views/components/calendar/CalendarHeader';
import CalendarHeader from '@views/components/calendar/CalendarHeader/CalendarHeader';
import { CalendarSchedules } from '@views/components/calendar/CalendarSchedules';
import ResourceLinks from '@views/components/calendar/ResourceLinks';
import Divider from '@views/components/common/Divider';
Expand All @@ -23,6 +23,7 @@ import { Button } from '../common/Button';
import { LargeLogo } from '../common/LogoIcon';
import Text from '../common/Text/Text';
import CalendarFooter from './CalendarFooter';

/**
* Calendar page component
*/
Expand Down Expand Up @@ -64,15 +65,15 @@ export default function Calendar(): JSX.Element {
<div className='h-screen flex overflow-auto'>
<div
className={clsx(
'py-spacing-6 relative h-full min-h-screen w-full flex flex-none flex-col justify-between overflow-clip whitespace-nowrap border-r border-theme-offwhite1 shadow-[2px_0_10px,rgba(214_210_196_/_.1)] duration-300 ease-out-expo transition-property-max-width screenshot:hidden',
'py-spacing-6 relative h-full min-h-screen w-full flex flex-none flex-col justify-between overflow-clip whitespace-nowrap border-r border-theme-offwhite1 shadow-[2px_0_10px,rgba(214_210_196_/_.1)] motion-safe:duration-300 motion-safe:ease-out-expo motion-safe:transition-[max-width] screenshot:hidden',
{
'max-w-[20.3125rem] ': showSidebar,
'max-w-0 pointer-events-none': !showSidebar,
}
)}
tabIndex={showSidebar ? 0 : -1}
aria-hidden={!showSidebar}
{...{ inert: !showSidebar ? '' : undefined }}
{...{ inert: !showSidebar }}
>
<div className='sticky top-0 z-50 w-full flex items-center justify-between gap-x-3xl bg-white px-spacing-8 pb-spacing-6'>
<LargeLogo />
Expand Down Expand Up @@ -116,14 +117,21 @@ export default function Calendar(): JSX.Element {
<CalendarFooter />
</div>

<div className='h-full min-w-5xl flex flex-grow flex-col overflow-y-auto'>
<div
style={
{
// scrollbarGutter: 'stable',
}
}
className='h-full flex flex-grow flex-col overflow-x-scroll px-spacing-5'
>
<CalendarHeader
sidebarOpen={showSidebar}
onSidebarToggle={() => {
setShowSidebar(!showSidebar);
}}
/>
<div className='min-h-2xl flex-grow overflow-auto pl-2 pr-4 pt-6 screenshot:min-h-xl'>
<div className='min-h-2xl min-w-5xl flex-grow overflow-auto pl-spacing-3 pt-spacing-3 screenshot:min-h-xl'>
<CalendarGrid courseCells={courseCells} setCourse={setCourse} />
</div>
<CalendarBottomBar courseCells={courseCells} setCourse={setCourse} />
Expand Down
19 changes: 0 additions & 19 deletions src/views/components/calendar/CalendarBottomBar.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import { CalendarDots, ImageSquare } from '@phosphor-icons/react';
import type { Course } from '@shared/types/Course';
import { saveAsCal, saveCalAsPng } from '@views/components/calendar/utils';
import { Button } from '@views/components/common/Button';
import Divider from '@views/components/common/Divider';
import Text from '@views/components/common/Text/Text';
import { ColorPickerProvider } from '@views/contexts/ColorPickerContext';
import type { CalendarGridCourse } from '@views/hooks/useFlattenedCourseSchedule';
Expand Down Expand Up @@ -61,21 +57,6 @@ export default function CalendarBottomBar({ courseCells, setCourse }: CalendarBo
</>
)}
</div>
<div className='flex items-center screenshot:hidden'>
{displayCourses && <Divider orientation='vertical' size='1rem' className='mx-1.25' />}
<Button variant='minimal' color='ut-black' icon={CalendarDots} onClick={saveAsCal}>
Save as .CAL
</Button>
<Divider orientation='vertical' size='1rem' className='mx-1.25' />
<Button
variant='minimal'
color='ut-black'
icon={ImageSquare}
onClick={() => requestAnimationFrame(() => saveCalAsPng())}
>
Save as .PNG
</Button>
</div>
</div>
);
}
83 changes: 0 additions & 83 deletions src/views/components/calendar/CalendarHeader.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.cqInline {
container-type: inline-size;

@mixin visually-hidden {
border: 0;
clip: rect(0 0 0 0);
height: auto;
margin: 0;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
white-space: nowrap;
}

// value with all buttons is 610px
@container (width < 90px) {
.primaryActions span {
@include visually-hidden;
}
}

// @container (width < 445px) {
// .secondaryActions span {
// @include visually-hidden;
// }
// }
}
124 changes: 124 additions & 0 deletions src/views/components/calendar/CalendarHeader/CalendarHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react';
import { CalendarDots, Export, FilePng, Sidebar } from '@phosphor-icons/react';
import styles from '@views/components/calendar/CalendarHeader/CalendarHeader.module.scss';
import { Button } from '@views/components/common/Button';
import DialogProvider from '@views/components/common/DialogProvider/DialogProvider';
import Divider from '@views/components/common/Divider';
import { ExtensionRootWrapper, styleResetClass } from '@views/components/common/ExtensionRoot/ExtensionRoot';
import ScheduleTotalHoursAndCourses from '@views/components/common/ScheduleTotalHoursAndCourses';
import useSchedules from '@views/hooks/useSchedules';
import clsx from 'clsx';
import React from 'react';

import { saveAsCal, saveCalAsPng } from '../utils';

interface CalendarHeaderProps {
sidebarOpen?: boolean;
onSidebarToggle?: () => void;
}

/**
* Renders the header component for the calendar.
* @returns The JSX element representing the calendar header.
*/
export default function CalendarHeader({ sidebarOpen, onSidebarToggle }: CalendarHeaderProps): JSX.Element {
const [activeSchedule] = useSchedules();

return (
<div
style={{ scrollbarGutter: 'stable' }}
className='sticky left-0 right-0 min-h-[85px] flex items-center gap-5 overflow-x-scroll overflow-y-hidden pl-spacing-7 pt-spacing-5'
>
{!sidebarOpen && (
<Button
variant='minimal'
color='theme-black'
onClick={onSidebarToggle}
className='h-fit w-fit screenshot:hidden !p-0'
icon={Sidebar}
/>
)}

<div className='min-w-[11.5rem] screenshot:transform-origin-left screenshot:scale-120'>
<ScheduleTotalHoursAndCourses
scheduleName={activeSchedule.name}
totalHours={activeSchedule.hours}
totalCourses={activeSchedule.courses.length}
/>
</div>
<Divider className='self-center screenshot:hidden' size='1.75rem' orientation='vertical' />
{/* min-w-[310px] is the value with all the buttons */}
<div className={clsx(styles.cqInline, 'flex flex-1 gap-5 min-w-[45x] screenshot:hidden')}>
<div className={clsx(styles.primaryActions, 'min-w-fit flex gap-5')}>
<DialogProvider>
<Menu>
<MenuButton className='bg-transparent'>
<Button color='ut-black' size='small' variant='minimal' icon={Export}>
Export
</Button>
</MenuButton>
<MenuItems
as={ExtensionRootWrapper}
className={clsx([
styleResetClass,
'mt-spacing-3',
'w-fit cursor-pointer origin-top-right rounded bg-white p-1 text-black shadow-lg transition border border-theme-offwhite1 focus:outline-none',
'data-[closed]:(opacity-0 scale-95)',
'data-[enter]:(ease-out-expo duration-150)',
'data-[leave]:(ease-out duration-50)',
])}
transition
anchor='bottom start'
>
<MenuItem>
<Button
className='w-full flex justify-start'
onClick={() => requestAnimationFrame(() => saveCalAsPng())}
color='ut-black'
size='small'
variant='minimal'
icon={FilePng}
>
Save as .png
</Button>
</MenuItem>
<MenuItem>
<Button
className='w-full flex justify-start'
onClick={saveAsCal}
color='ut-black'
size='small'
variant='minimal'
icon={CalendarDots}
>
Save as .cal
</Button>
</MenuItem>
{/* <MenuItem>
<Button color='ut-black' size='small' variant='minimal' icon={FileTxt}>
Export Unique IDs
</Button>
</MenuItem> */}
</MenuItems>
</Menu>
</DialogProvider>
{/* <Button className='invisible' color='ut-black' size='small' variant='minimal' icon={PlusCircle}>
Quick Add
</Button>
<Button className='invisible' color='ut-black' size='small' variant='minimal' icon={SelectionPlus}>
Block
</Button> */}
</div>
{/* <Divider className='self-center' size='1.75rem' orientation='vertical' />
<div className={clsx(styles.secondaryActions, 'min-w-fit flex flex-1 justify-end gap-5')}>
<Button className='invisible' color='ut-black' size='small' variant='minimal' icon={BookmarkSimple}>
Bookmarks
</Button>
<Button className='invisible' color='ut-black' size='small' variant='minimal' icon={MapPinArea}>
UT Map
</Button>
</div> */}
</div>
</div>
);
}
3 changes: 3 additions & 0 deletions src/views/components/common/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import React from 'react';

interface Props {
className?: string;
ref?: React.ForwardedRef<HTMLButtonElement>;
style?: React.CSSProperties;
variant?: 'filled' | 'outline' | 'minimal';
size?: 'regular' | 'small' | 'mini';
Expand All @@ -24,6 +25,7 @@ interface Props {
*/
export function Button({
className,
ref,
style,
variant = 'filled',
size = 'regular',
Expand All @@ -42,6 +44,7 @@ export function Button({

return (
<button
ref={ref}
style={
{
color: colorHex,
Expand Down
25 changes: 16 additions & 9 deletions src/views/components/common/ScheduleTotalHoursAndCourses.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,24 @@ export default function ScheduleTotalHoursAndCourses({
totalCourses,
}: ScheduleTotalHoursAndCoursesProps): JSX.Element {
return (
<div className='min-w-full flex flex-col items-start whitespace-nowrap'>
<Text className='truncate text-ut-burntorange' variant='h1' as='span'>
{`${scheduleName} `}
</Text>
<div className='w-full flex flex-col items-start'>
<div className='max-w-full overflow-hidden'>
<Text className='block w-full truncate text-ut-burntorange' variant='h1' as='span'>
{scheduleName}
</Text>
</div>
<Text variant='h3' as='div' className='flex flex-row items-center gap-2.5 text-theme-black'>
<Text variant='h4' as='span' className='hidden text-ut-black uppercase screenshot:inline sm:inline'>
{totalHours} {totalHours === 1 ? 'Hour' : 'Hours'}
<Text variant='h4' as='span' className='inline text-theme-black'>
{totalHours}&nbsp;
<Text variant='h3' as='span' className='inline text-theme-black font-all-small-caps!'>
{totalHours === 1 ? 'Hour' : 'Hours'}
</Text>
</Text>

<Text variant='h4' as='span' className='hidden text-ut-black uppercase screenshot:inline sm:inline'>
{totalCourses} {totalCourses === 1 ? 'Course' : 'Courses'}
<Text variant='h4' as='span' className='inline text-theme-black'>
{totalCourses}&nbsp;
<Text variant='h3' as='span' className='inline text-theme-black font-all-small-caps!'>
{totalCourses === 1 ? 'Course' : 'Courses'}
</Text>
</Text>
</Text>
</div>
Expand Down
Loading

0 comments on commit 9c766c2

Please sign in to comment.