Skip to content

Commit

Permalink
feat: screenshot whole page, hide certain elements, screenshot fixed …
Browse files Browse the repository at this point in the history
…size (#180)

* feat: screenshot whole page, hide certain elements, screenshot fixed size

* refactor: use variants instead of groups and custom rules

* feat: scaled header, smaller body, weird padding/margin changes

* feat: consistent sizing & style regardless of zoom

* feat: use downloadBlob instead of hand-rolled image saving

* fix: be type safe is toBlob returns Promise<null>

* fix: revoke object url when it should be

* fix: animation scheduling

---------

Co-authored-by: Razboy20 <[email protected]>
  • Loading branch information
Samathingamajig and Razboy20 authored Mar 22, 2024
1 parent 2dfb10e commit 7d4c5d7
Show file tree
Hide file tree
Showing 11 changed files with 81 additions and 37 deletions.
2 changes: 1 addition & 1 deletion src/shared/util/downloadBlob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ export function downloadBlob(blobPart: BlobPart, type: MIMETypeKey, fileName: st
link.download = fileName;

link.addEventListener('click', () => {
URL.revokeObjectURL(url);
resolve();
});
link.addEventListener('error', () => {
URL.revokeObjectURL(url);
reject(new Error('Download failed'));
});
link.click();
URL.revokeObjectURL(url);
});
}
2 changes: 0 additions & 2 deletions src/stories/components/calendar/CalendarBottomBar.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ export const Default: Story = {
status: examplePsyCourse.status,
},
],
calendarRef: { current: null },
},
render: props => (
<div className='outline-red outline w-292.5!'>
Expand All @@ -107,7 +106,6 @@ export const Default: Story = {
export const Empty: Story = {
args: {
courses: [],
calendarRef: { current: null },
},
render: props => (
<div className='outline-red outline w-292.5!'>
Expand Down
11 changes: 5 additions & 6 deletions src/views/components/calendar/Calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Divider from '@views/components/common/Divider';
import CourseCatalogInjectedPopup from '@views/components/injected/CourseCatalogInjectedPopup/CourseCatalogInjectedPopup';
import { useFlattenedCourseSchedule } from '@views/hooks/useFlattenedCourseSchedule';
import { MessageListener } from 'chrome-extension-toolkit';
import React, { useEffect, useRef, useState } from 'react';
import React, { useEffect, useState } from 'react';

import CalendarFooter from './CalendarFooter';
import TeamLinks from './TeamLinks';
Expand All @@ -18,7 +18,6 @@ import TeamLinks from './TeamLinks';
* Calendar page component
*/
export default function Calendar(): JSX.Element {
const calendarRef = useRef<HTMLDivElement>(null);
const { courseCells, activeSchedule } = useFlattenedCourseSchedule();

const [course, setCourse] = useState<Course | null>((): Course | null => {
Expand Down Expand Up @@ -73,7 +72,7 @@ export default function Calendar(): JSX.Element {
/>
<div className='h-full flex overflow-auto pl-3'>
{showSidebar && (
<div className='h-full flex flex-none flex-col justify-between pb-5 pl-4.5'>
<div className='h-full flex flex-none flex-col justify-between pb-5 pl-4.5 screenshot:hidden'>
<div className='mb-3 h-full w-fit flex flex-col overflow-auto pb-2 pr-4 pt-5'>
<CalendarSchedules />
<Divider orientation='horizontal' size='100%' className='my-5' />
Expand All @@ -84,11 +83,11 @@ export default function Calendar(): JSX.Element {
<CalendarFooter />
</div>
)}
<div className='h-full min-w-4xl flex flex-grow flex-col overflow-y-auto' ref={calendarRef}>
<div className='min-h-2xl flex-grow overflow-auto pl-2 pr-4 pt-6'>
<div className='h-full min-w-4xl flex flex-grow flex-col overflow-y-auto'>
<div className='min-h-2xl flex-grow overflow-auto pl-2 pr-4 pt-6 screenshot:min-h-xl'>
<CalendarGrid courseCells={courseCells} setCourse={setCourse} />
</div>
<CalendarBottomBar calendarRef={calendarRef} />
<CalendarBottomBar />
</div>
</div>

Expand Down
13 changes: 8 additions & 5 deletions src/views/components/calendar/CalendarBottomBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,15 @@ import CalendarCourseBlock from './CalendarCourseCell';

type CalendarBottomBarProps = {
courses?: CalendarCourseCellProps[];
calendarRef: React.RefObject<HTMLDivElement>;
};

/**
* Renders the bottom bar of the calendar component.
*
* @param {Object[]} courses - The list of courses to display in the calendar.
* @param {React.RefObject} calendarRef - The reference to the calendar component.
* @returns {JSX.Element} The rendered bottom bar component.
*/
export default function CalendarBottomBar({ courses, calendarRef }: CalendarBottomBarProps): JSX.Element {
export default function CalendarBottomBar({ courses }: CalendarBottomBarProps): JSX.Element {
const displayCourses = courses && courses.length > 0;

return (
Expand All @@ -50,13 +48,18 @@ export default function CalendarBottomBar({ courses, calendarRef }: CalendarBott
</>
)}
</div>
<div className='flex items-center'>
<div className='flex items-center screenshot:hidden'>
{displayCourses && <Divider orientation='vertical' size='1rem' className='mx-1.25' />}
<Button variant='single' color='ut-black' icon={CalendarMonthIcon} onClick={saveAsCal}>
Save as .CAL
</Button>
<Divider orientation='vertical' size='1rem' className='mx-1.25' />
<Button variant='single' color='ut-black' icon={ImageIcon} onClick={() => saveCalAsPng(calendarRef)}>
<Button
variant='single'
color='ut-black'
icon={ImageIcon}
onClick={() => requestAnimationFrame(() => saveCalAsPng())}
>
Save as .PNG
</Button>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/views/components/calendar/CalendarCourseCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export default function CalendarCourseCell({
</div>
{rightIcon && (
<div
className='h-fit flex items-center justify-center justify-self-start rounded p-0.5 text-white'
className='h-fit flex items-center justify-center justify-self-start rounded p-0.5 text-white screenshot:hidden'
style={{
backgroundColor: colors.secondaryColor,
}}
Expand Down
2 changes: 1 addition & 1 deletion src/views/components/calendar/CalendarGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ function AccountForCourseConflicts({ courseCells, setCourse }: AccountForCourseC
gridRow: `${block.calendarGridPoint.startIndex} / ${block.calendarGridPoint.endIndex}`,
width: `calc(100% / ${block.totalColumns ?? 1})`,
marginLeft: `calc(100% * ${((block.gridColumnStart ?? 0) - 1) / (block.totalColumns ?? 1)})`,
padding: '0px 10px 4px 0px',
}}
className='pb-1 pl-0 pr-2.5 pt-0 screenshot:pb-0.5 screenshot:pr-0.5'
>
<CalendarCourseCell
courseDeptAndInstr={courseDeptAndInstr}
Expand Down
14 changes: 10 additions & 4 deletions src/views/components/calendar/CalenderHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,22 @@ export default function CalendarHeader({ onSidebarToggle }: CalendarHeaderProps)

return (
<div className='flex items-center gap-5 border-b border-ut-offwhite px-7 py-4'>
<Button variant='single' icon={MenuIcon} color='ut-gray' onClick={onSidebarToggle} />
<Button
variant='single'
icon={MenuIcon}
color='ut-gray'
onClick={onSidebarToggle}
className='screenshot:hidden'
/>
<LargeLogo />
<Divider className='mx-2 self-center md:mx-4' size='2.5rem' orientation='vertical' />
<div className='flex-1'>
<div className='flex-1 screenshot:transform-origin-left screenshot:scale-120'>
<ScheduleTotalHoursAndCourses
scheduleName={activeSchedule.name}
totalHours={activeSchedule.hours}
totalCourses={activeSchedule.courses.length}
/>
<div className='flex items-center gap-1'>
<div className='flex items-center gap-1 screenshot:hidden'>
<Text variant='mini' className='text-ut-gray'>
DATA LAST UPDATED: {getUpdatedAtDateTimeString(activeSchedule.updatedAt)}
</Text>
Expand All @@ -53,7 +59,7 @@ export default function CalendarHeader({ onSidebarToggle }: CalendarHeaderProps)
</button>
</div>
</div>
<div className='hidden flex-row items-center justify-end gap-6 lg:flex'>
<div className='hidden flex-row items-center justify-end gap-6 screenshot:hidden lg:flex'>
<CourseStatus size='small' status={Status.WAITLISTED} />
<CourseStatus size='small' status={Status.CLOSED} />
<CourseStatus size='small' status={Status.CANCELLED} />
Expand Down
57 changes: 43 additions & 14 deletions src/views/components/calendar/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
import type { UserSchedule } from '@shared/types/UserSchedule';
import { downloadBlob } from '@shared/util/downloadBlob';
import type { Serialized } from 'chrome-extension-toolkit';
import { toPng } from 'html-to-image';
import { toBlob } from 'html-to-image';

export const CAL_MAP = {
Sunday: 'SU',
Expand Down Expand Up @@ -91,17 +91,46 @@ export const saveAsCal = async () => {
*
* @param calendarRef - The reference to the calendar component.
*/
export const saveCalAsPng = (calendarRef: React.RefObject<HTMLDivElement>) => {
if (calendarRef.current) {
toPng(calendarRef.current, { cacheBust: true })
.then(dataUrl => {
const link = document.createElement('a');
link.download = 'my-calendar.png';
link.href = dataUrl;
link.click();
})
.catch(err => {
console.error(err);
});
}
export const saveCalAsPng = () => {
const rootNode = document.createElement('div');
rootNode.style.backgroundColor = 'white';
rootNode.style.position = 'fixed';
rootNode.style.zIndex = '1000';
rootNode.style.top = '-10000px';
rootNode.style.left = '-10000px';
rootNode.style.width = '1165px';
rootNode.style.height = '754px';
document.body.appendChild(rootNode);

const clonedNode = document.querySelector('#root')!.cloneNode(true) as HTMLDivElement;
clonedNode.style.backgroundColor = 'white';
(clonedNode.firstChild as HTMLDivElement).classList.add('screenshot-in-progress');

return new Promise<void>((resolve, reject) => {
requestAnimationFrame(async () => {
rootNode.appendChild(clonedNode);

try {
const screenshotBlob = await toBlob(clonedNode, {
cacheBust: true,
canvasWidth: 1165 * 2,
canvasHeight: 754 * 2,
skipAutoScale: true,
pixelRatio: 2,
});

if (!screenshotBlob) {
throw new Error('Failed to create screenshot');
}

downloadBlob(screenshotBlob, 'IMAGE', 'my-calendar.png');
} catch (e: unknown) {
console.error(e);
reject(e);
} finally {
document.body.removeChild(rootNode);
resolve();
}
});
});
};
2 changes: 1 addition & 1 deletion src/views/components/common/LogoIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export function LargeLogo({ className }: { className?: string }): JSX.Element {
return (
<div className={clsx('flex items-center gap-2', className)}>
<LogoIcon className='h-12 w-12' />
<div className='hidden flex-col text-[1.35rem] font-medium leading-[1em] md:flex'>
<div className='hidden flex-col text-[1.35rem] font-medium leading-[1em] md:flex screenshot:flex'>
<p className='text-nowrap text-ut-burntorange'>UT Registration</p>
<p className='text-ut-orange'>Plus</p>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default function ScheduleTotalHoursAndCourses({
</Text>
<Text variant='h3' as='div' className='flex flex-row items-center gap-2 text-theme-black'>
{totalHours} {totalHours === 1 ? 'Hour' : 'Hours'}
<Text variant='h4' as='span' className='hidden text-ut-black capitalize sm:inline'>
<Text variant='h4' as='span' className='hidden text-ut-black capitalize screenshot:inline sm:inline'>
{totalCourses} {totalCourses === 1 ? 'Course' : 'Courses'}
</Text>
</Text>
Expand Down
11 changes: 10 additions & 1 deletion unocss.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,16 @@ export default defineConfig({
},
colors,
},

variants: [
matcher => {
const search = 'screenshot:';
if (!matcher.startsWith(search)) return matcher;
return {
matcher: matcher.slice(search.length),
selector: s => `.screenshot-in-progress ${s}`,
};
},
],
presets: [
presetUno(),
presetWebFonts({
Expand Down

0 comments on commit 7d4c5d7

Please sign in to comment.