Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: PopupCourseBlock Component #79

Merged
merged 25 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
27c19fe
make simple component and storybook entry
DhruvArora-03 Feb 3, 2024
7cf503c
uhhh made stuff
DhruvArora-03 Feb 3, 2024
ae57090
Merge branch 'sghsri:main' into popup-course-block
DhruvArora-03 Feb 3, 2024
f9dc9b9
working still
DhruvArora-03 Feb 4, 2024
afb4770
Merge branch 'sghsri:main' into popup-course-block
DhruvArora-03 Feb 4, 2024
3b655f8
add more props and fix up stories a bit
DhruvArora-03 Feb 5, 2024
ce275a8
export interface of props
DhruvArora-03 Feb 5, 2024
8e83774
fix up stories
DhruvArora-03 Feb 5, 2024
804f850
editing stories still
DhruvArora-03 Feb 5, 2024
1591e2e
added more variants, but still cant fix drag handle
DhruvArora-03 Feb 5, 2024
655451c
still trying to fix bug
DhruvArora-03 Feb 5, 2024
12101be
fix ugly bug
DhruvArora-03 Feb 5, 2024
1d10a1b
create icon helper
DhruvArora-03 Feb 5, 2024
69b1d55
use icon helper
DhruvArora-03 Feb 5, 2024
9b9c0ef
Merge branch 'icon-helper' into popup-course-block
DhruvArora-03 Feb 5, 2024
e42e66f
use icon helper
DhruvArora-03 Feb 5, 2024
8421f94
Merge commit '1b51d65c89a4b544f4f2a9c60106e14300f9a3b0' into popup-co…
DhruvArora-03 Feb 6, 2024
600096d
update icons thingy to use new component version
DhruvArora-03 Feb 6, 2024
d48c33b
Merge remote-tracking branch 'origin/main' into popup-course-block
DhruvArora-03 Feb 6, 2024
54b221c
Merge branch 'popup-course-block' of https://github.com/DhruvArora-03…
DhruvArora-03 Feb 6, 2024
1f38257
switch to clsx
DhruvArora-03 Feb 6, 2024
37581a0
colors utility
Razboy20 Feb 7, 2024
64809c7
unocss reset
Razboy20 Feb 7, 2024
dc01f84
move text stylings to layer
Razboy20 Feb 8, 2024
0939351
popup course block modifications
Razboy20 Feb 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"@unocss/postcss": "^0.58.4",
"@unocss/preset-uno": "^0.58.4",
"@unocss/preset-web-fonts": "^0.58.4",
"@unocss/reset": "^0.58.5",
"@unocss/transformer-directives": "^0.58.4",
"@unocss/transformer-variant-group": "^0.58.4",
"@vitejs/plugin-react-swc": "^3.6.0",
Expand Down
7 changes: 7 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 50 additions & 0 deletions src/shared/util/colors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { theme } from 'unocss/preset-mini';

export interface CourseColors {
primaryColor: string;
secondaryColor: string;
}

// calculates luminance of a hex string
function getLuminance(hex: string): number {
let r = parseInt(hex.substring(1, 3), 16);
let g = parseInt(hex.substring(3, 5), 16);
let b = parseInt(hex.substring(5, 7), 16);

[r, g, b] = [r, g, b].map(color => {
let c = color / 255;

c = c > 0.03928 ? ((c + 0.055) / 1.055) ** 2.4 : (c /= 12.92);

return c;
});

return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}

// calculates contrast ratio between two hex strings
function contrastRatioPair(hex1: string, hex2: string) {
const lum1 = getLuminance(hex1);
const lum2 = getLuminance(hex2);

return (Math.max(lum1, lum2) + 0.05) / (Math.min(lum1, lum2) + 0.05);
}

/**
* Generate a tailwind classname for the font color based on the background color
* @param bgColor the tailwind classname for background ex. "bg-emerald-500"
*/
export function pickFontColor(bgColor: string): 'text-white' | 'text-black' {
return contrastRatioPair(bgColor, '#606060') > contrastRatioPair(bgColor, '#ffffff') ? 'text-black' : 'text-white';
}

/**
* Get primary and secondary colors from a tailwind colorway
* @param colorway the tailwind colorway ex. "emerald"
*/
export function getCourseColors(colorway: keyof typeof theme.colors): CourseColors {
return {
primaryColor: theme.colors[colorway][600] as string,
secondaryColor: theme.colors[colorway][800] as string,
};
}
118 changes: 118 additions & 0 deletions src/stories/components/PopupCourseBlock.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import type { Meta, StoryObj } from '@storybook/react';
import React from 'react';
import { Course, Status } from 'src/shared/types/Course';
import { CourseMeeting } from 'src/shared/types/CourseMeeting';
import Instructor from 'src/shared/types/Instructor';
import PopupCourseBlock from '@views/components/common/PopupCourseBlock/PopupCourseBlock';
import { getCourseColors } from 'src/shared/util/colors';
import { theme } from 'unocss/preset-mini';

const exampleCourse: Course = new Course({
courseName: 'ELEMS OF COMPTRS/PROGRAMMNG-WB',
creditHours: 3,
department: 'C S',
description: [
'Problem solving and fundamental algorithms for various applications in science, business, and on the World Wide Web, and introductory programming in a modern object-oriented programming language.',
'Only one of the following may be counted: Computer Science 303E, 312, 312H. Credit for Computer Science 303E may not be earned after a student has received credit for Computer Science 314, or 314H. May not be counted toward a degree in computer science.',
'May be counted toward the Quantitative Reasoning flag requirement.',
'Designed to accommodate 100 or more students.',
'Taught as a Web-based course.',
],
flags: ['Quantitative Reasoning'],
fullName: 'C S 303E ELEMS OF COMPTRS/PROGRAMMNG-WB',
instructionMode: 'Online',
instructors: [
new Instructor({
firstName: 'Bevo',
lastName: 'Bevo',
fullName: 'Bevo Bevo',
}),
],
isReserved: false,
number: '303E',
schedule: {
meetings: [
new CourseMeeting({
days: ['Tuesday', 'Thursday'],
endTime: 660,
startTime: 570,
}),
],
},
semester: {
code: '12345',
season: 'Spring',
year: 2024,
},
status: Status.WAITLISTED,
uniqueId: 12345,
url: 'https://utdirect.utexas.edu/apps/registrar/course_schedule/20242/12345/',
});

// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
const meta = {
title: 'Components/Common/PopupCourseBlock',
component: PopupCourseBlock,
parameters: {
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
layout: 'centered',
},
// This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
tags: ['autodocs'],
// More on argTypes: https://storybook.js.org/docs/api/argtypes
args: {
colors: getCourseColors('emerald'),
course: exampleCourse,
},
argTypes: {
colors: {
description: 'the colors to use for the course block',
control: 'object',
},
course: {
description: 'the course to show data for',
control: 'object',
},
},
} satisfies Meta<typeof PopupCourseBlock>;

export default meta;
type Story = StoryObj<typeof meta>;

// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
export const Default: Story = {
args: {},
};

export const Variants: Story = {
render: props => (
<div className='grid grid-cols-2 max-w-2xl w-90vw gap-x-4 gap-y-2'>
<PopupCourseBlock {...props} course={new Course({ ...exampleCourse, status: Status.OPEN })} />
<PopupCourseBlock {...props} course={new Course({ ...exampleCourse, status: Status.CLOSED })} />
<PopupCourseBlock {...props} course={new Course({ ...exampleCourse, status: Status.WAITLISTED })} />
<PopupCourseBlock {...props} course={new Course({ ...exampleCourse, status: Status.CANCELLED })} />
</div>
),
};

const colors = Object.keys(theme.colors)
// check that the color is a colorway (is an object)
.filter(color => typeof theme.colors[color] === 'object')
.slice(0, 17)
.map(color => getCourseColors(color as keyof typeof theme.colors));

export const AllColors: Story = {
render: props => (
<div className='grid grid-rows-9 grid-cols-2 grid-flow-col max-w-2xl w-90vw gap-x-4 gap-y-2'>
{colors.map((color, i) => (
<PopupCourseBlock key={color.primaryColor} course={exampleCourse} colors={color} />
))}
</div>
),
parameters: {
design: {
type: 'figma',
url: 'https://www.figma.com/file/8tsCay2FRqctrdcZ3r9Ahw/UTRP?type=design&node-id=1046-6714&mode=design&t=5Bjr7qGHNXmjfMTc-0',
},
},
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import styles from './ExtensionRoot.module.scss';

import '@unocss/reset/tailwind-compat.css';
import 'uno.css';

interface Props {
Expand Down
61 changes: 61 additions & 0 deletions src/views/components/common/PopupCourseBlock/PopupCourseBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import clsx from 'clsx';
import React, { useState } from 'react';
import { Course, Status } from '@shared/types/Course';
import { StatusIcon } from '@shared/util/icons';
import { CourseColors, getCourseColors, pickFontColor } from '@shared/util/colors';
import DragIndicatorIcon from '~icons/material-symbols/drag-indicator';
import Text from '../Text/Text';

/**
* Props for PopupCourseBlock
*/
export interface PopupCourseBlockProps {
className?: string;
course: Course;
colors: CourseColors;
}

/**
* The "course block" to be used in the extension popup.
*
* @param props PopupCourseBlockProps
*/
export default function PopupCourseBlock({ className, course, colors }: PopupCourseBlockProps): JSX.Element {
// whiteText based on secondaryColor
const fontColor = pickFontColor(colors.primaryColor);

return (
<div
style={{
backgroundColor: colors.primaryColor,
}}
className={clsx('h-full w-full inline-flex items-center justify-center gap-1 rounded pr-3', className)}
>
<div
style={{
backgroundColor: colors.secondaryColor,
}}
className='flex cursor-move items-center self-stretch rounded rounded-r-0'
>
<DragIndicatorIcon className='h-6 w-6 text-white' />
</div>
<Text
className={clsx('flex-1 py-3.5 text-ellipsis whitespace-nowrap overflow-hidden', fontColor)}
variant='h1-course'
>
<span className='px-0.5 font-450'>{course.uniqueId}</span> {course.department} {course.number} &ndash;{' '}
{course.instructors.length === 0 ? 'Unknown' : course.instructors.map(v => v.lastName)}
</Text>
{course.status !== Status.OPEN && (
<div
style={{
backgroundColor: colors.secondaryColor,
}}
className='ml-1 flex items-center justify-center justify-self-end rounded p-1px text-white'
>
<StatusIcon status={course.status} className='h-5 w-5' />
</div>
)}
</div>
);
}
104 changes: 53 additions & 51 deletions src/views/components/common/Text/Text.module.scss
Original file line number Diff line number Diff line change
@@ -1,65 +1,67 @@
@use 'src/views/styles/colors.module.scss';
@use 'src/views/styles/fonts.module.scss';

.text {
font-family: 'Roboto Flex', sans-serif;
line-height: normal;
font-style: normal;
}
@layer theme {
.text {
font-family: 'Roboto Flex', sans-serif;
line-height: normal;
font-style: normal;
}

.mini {
font-size: 0.79rem;
font-weight: 500;
}
.mini {
font-size: 0.79rem;
font-weight: 500;
}

.small {
font-size: 0.88875rem;
font-weight: 500;
}
.small {
font-size: 0.88875rem;
font-weight: 500;
}

.p {
font-size: 1rem;
font-weight: 400;
letter-spacing: 0.025rem;
}
.p {
font-size: 1rem;
font-weight: 400;
letter-spacing: 0.025rem;
}

.h4 {
font-size: 1.125rem;
font-weight: 500;
}
.h4 {
font-size: 1.125rem;
font-weight: 500;
}

.h3-course {
font-size: 0.6875rem;
font-weight: 400;
line-height: 100%; /* 0.6875rem */
}
.h3-course {
font-size: 0.6875rem;
font-weight: 400;
line-height: 100%; /* 0.6875rem */
}

.h3 {
font-size: 1.26563rem;
font-weight: 600;
text-transform: uppercase;
}
.h3 {
font-size: 1.26563rem;
font-weight: 600;
text-transform: uppercase;
}

.h2-course {
font-size: 1rem;
font-weight: 500;
letter-spacing: 0.03125rem;
text-transform: capitalize;
}
.h2-course {
font-size: 1rem;
font-weight: 500;
letter-spacing: 0.03125rem;
text-transform: capitalize;
}

.h2 {
font-size: 1.42375rem;
font-weight: 500;
}
.h2 {
font-size: 1.42375rem;
font-weight: 500;
}

.h1-course {
font-size: 1rem;
font-weight: 600;
text-transform: capitalize;
}
.h1-course {
font-size: 1rem;
font-weight: 600;
text-transform: capitalize;
}

.h1 {
font-size: 1.60188rem;
font-weight: 700;
text-transform: uppercase;
.h1 {
font-size: 1.60188rem;
font-weight: 700;
text-transform: uppercase;
}
}
Loading