feat: sticky calendar header and days (#568)

* feat: sticky calendar days

* feat: partial height borders for day labels

* feat: make calendar header actually sticky

* fix: remove unneeded gap

* refactor: add preston as co-author

Co-authored-by: Preston-Cook <preston.l.cook@gmail.com>

* fix: z-index issues with export sub-buttons

---------

Co-authored-by: Preston-Cook <preston.l.cook@gmail.com>
Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
This commit is contained in:
Samuel Gunter
2025-03-23 19:49:11 -05:00
committed by GitHub
parent 4a5f67f0fd
commit fa9f78b46e
6 changed files with 39 additions and 20 deletions

View File

@@ -3,7 +3,7 @@ import CalendarCourseCell from '@views/components/calendar/CalendarCourseCell';
import Text from '@views/components/common/Text/Text';
import { ColorPickerProvider } from '@views/contexts/ColorPickerContext';
import type { CalendarGridCourse } from '@views/hooks/useFlattenedCourseSchedule';
import React from 'react';
import React, { Fragment } from 'react';
import CalendarCell from './CalendarGridCell';
@@ -30,13 +30,13 @@ function makeGridRow(row: number, cols: number): JSX.Element {
const hour = hoursOfDay[row]!;
return (
<>
<Fragment key={row}>
<CalendarHour hour={hour} />
<div className='grid-row-span-2 w-4 border-b border-r border-gray-300' />
{[...Array(cols).keys()].map(col => (
<CalendarCell key={`${row}${col}`} row={row} col={col} />
))}
</>
</Fragment>
);
}
@@ -56,23 +56,40 @@ export default function CalendarGrid({
setCourse,
}: React.PropsWithChildren<Props>): JSX.Element {
return (
<div className='grid grid-cols-[auto_auto_repeat(5,1fr)] grid-rows-[auto_repeat(26,1fr)] h-full'>
<div className='grid grid-cols-[auto_auto_repeat(5,1fr)] grid-rows-[auto_auto_repeat(27,1fr)] h-full'>
{/* Cover top left corner of grid, so time gets cut off at the top of the partial border */}
<div className='sticky top-[85px] z-10 col-span-2 h-3 bg-white' />
{/* Displaying day labels */}
<div />
<div className='w-4 border-b border-r border-gray-300' />
{daysOfWeek.map(day => (
<div className='h-4 flex items-end justify-center border-b border-r border-gray-300 pb-1.5'>
<Text key={day} variant='small' className='text-center text-ut-burntorange' as='div'>
{day}
</Text>
<div
// Full height with background to prevent grid lines from showing behind
className='sticky top-[85px] z-10 row-span-2 h-7 flex flex-col items-end self-start justify-end bg-white'
key={day}
>
{/* Partial border height because that's what Isaiah wants */}
<div className='h-4 w-full flex items-end border-b border-r border-gray-300'>
{/* Alignment for text */}
<div className='h-[calc(1.75rem_-_1px)] w-full flex items-center justify-center'>
<Text variant='small' className='text-center text-ut-burntorange' as='div'>
{day}
</Text>
</div>
</div>
</div>
))}
{/* empty slot, for alignment */}
<div />
{/* time tick for the first hour */}
<div className='h-4 w-4 self-end border-b border-r border-gray-300' />
{[...Array(13).keys()].map(i => makeGridRow(i, 5))}
<CalendarHour hour={21} />
{Array(6)
.fill(1)
.map(() => (
<div className='h-4 flex items-end justify-center border-r border-gray-300' />
.map((_, i) => (
// Key suppresses warning about duplicate keys,
// and index is fine because it doesn't change between renders
// eslint-disable-next-line react/no-array-index-key
<div key={i} className='h-4 flex items-end justify-center border-r border-gray-300' />
))}
<ColorPickerProvider>
{courseCells && <AccountForCourseConflicts courseCells={courseCells} setCourse={setCourse} />}