Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
112 changes: 94 additions & 18 deletions src/components/RequirementsAccordion/RequirementsAccordion.css
Original file line number Diff line number Diff line change
@@ -1,11 +1,33 @@

.accordion {
display: flex;
padding: var(--S, 16px);
display: flex;
padding: var(--S, 16px);
gap: var(--XS, 8px);
flex-direction: column;
align-items: flex-start;
align-self: stretch;
flex-direction: column;
align-items: flex-start;
align-self: stretch;
}

.sidepanel-header-container {
display: flex;
justify-content: space-between;
align-items: center;
align-self: stretch;
cursor: pointer;
}

.requirement-header {
color: var(--Secondary-Text, #8A8A8A);
font-size: 0.75em;
font-weight: 580;
text-transform: uppercase;
flex-grow: 1;
}

.icon {
width: 20px;
height: 20px;
color: var(--Secondary-Text, #8A8A8A);
transition: transform 0.3s ease;
}

.accordion-contents {
Expand All @@ -14,24 +36,78 @@
align-items: flex-start;
gap: var(--XXS, 4px);
transition: max-height 0.3s ease, opacity 0.3s ease;
}
padding-left: var(--S, 16px);
}

.accordion-item {
display: flex;
align-items: flex-start;
gap: var(--XS, 8px);
align-items: center;
justify-content: space-between;
align-self: stretch;
padding-right: var(--M, 24px);
position: relative; /* For positioning the menu */
}

.icon {
width: 20px;
height: 20px;

.item-start {
display: flex;
align-items: center;
gap: var(--XS, 8px);
padding-left: 1.2em; /* Reserve space for the checkmark */
position: relative;
}

.checkmark-icon {
color: var(--Green, #13B56B);
width: 1.2em;
height: 1.2em;
position: absolute;
left: 0;
}

.requirement-text {
font-size: 0.875em;
color: var(--Primary-Text, #333);
line-height: 1.3;
padding-left: 0.2em; /* Small adjustment for text alignment */
}

.ellipsis {
color: var(--Secondary-Text, #8A8A8A);
transition: transform 0.3s ease;
cursor: pointer;
font-size: 1.1em;
padding: 0 6px;
align-self: center;
line-height: 1;
margin-left: auto; /* Push to the right */
}

.ellipsis:hover {
color: var(--Blue, #2F80ED);
}

.requirement-menu {
position: absolute;
right: 0;
top: 100%;
background-color: white;
border: 1px solid #EAEAEA;
border-radius: var(--XXS, 4px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
z-index: 10;
margin-top: var(--XXS, 4px);
}
.icon.expanded {
transform: rotate(0deg);

.requirement-menu button {
padding: var(--XS, 12px);
font-size: 0.875em;
border: none;
background: none;
text-align: left;
cursor: pointer;
}
.icon.collapsed {
transform: rotate(90deg);

.requirement-menu button:hover {
background-color: #F3F4F9;
}
94 changes: 66 additions & 28 deletions src/components/RequirementsAccordion/RequirementsAccordion.tsx
Original file line number Diff line number Diff line change
@@ -1,53 +1,91 @@
import React, { useState } from 'react';
import { Check, NavArrowDown, NavArrowRight } from 'iconoir-react';
import { Uni_Reqs, College_Reqs } from '../../lib/api';
import { Uni_Reqs } from '../../lib/api';
import "./RequirementsAccordion.css";

type RequirementEnum = Uni_Reqs | College_Reqs ;

interface RequirementsAccordionProps {
title: string;
requirements: RequirementEnum[];
requirements?: Uni_Reqs[];
}

export default function RequirementsAccordion({ title, requirements }: RequirementsAccordionProps) {
const [isExpanded, setIsExpanded] = useState(false);
const [fulfilledRequirements, setFulfilledRequirements] = useState<Set<Uni_Reqs>>(new Set());
const [hoveredRequirement, setHoveredRequirement] = useState<Uni_Reqs | null>(null);
const [activeMenuRequirement, setActiveMenuRequirement] = useState<Uni_Reqs | null>(null);

const toggleAccordion = () => {
setIsExpanded(prev => !prev);
};

// TODO: Add functionality
// TODO: Clean up code
const handleMouseEnter = (requirement: Uni_Reqs) => {
setHoveredRequirement(requirement);
};

const handleMouseLeave = () => {
setHoveredRequirement(null);
};

const openMenu = (requirement: Uni_Reqs) => {
setActiveMenuRequirement(requirement);
};

const closeMenu = () => {
setActiveMenuRequirement(null);
};

const markAsFulfilled = (requirement: Uni_Reqs) => {
const newFulfilledRequirements = new Set(fulfilledRequirements);
newFulfilledRequirements.add(requirement);
setFulfilledRequirements(newFulfilledRequirements);
closeMenu();
};

const markAsUnfulfilled = (requirement: Uni_Reqs) => {
const newFulfilledRequirements = new Set(fulfilledRequirements);
newFulfilledRequirements.delete(requirement);
setFulfilledRequirements(newFulfilledRequirements);
closeMenu();
};

return (
<div className="accordion">
{/* Accordion header */}
<div className="sidepanel-header-container" onClick={toggleAccordion}>
<div className="requirement-header">{title}</div>
{isExpanded ? (
<NavArrowDown className="icon" />
) : (
<NavArrowRight className="icon" />
)}
<div className="requirement-header">{title}</div>
{isExpanded ? <NavArrowDown className="icon" /> : <NavArrowRight className="icon" />}
</div>

{/* Conditionally rendered accordion contents */}
{isExpanded && (
{isExpanded && requirements && (
<div className="accordion-contents">
{Object.values(requirements).map((requirement, index) => {
const isFulfilled = false; // TODO: Replace with `checkRequirementFulfilled` logic
return (
<div
key={index}
className={`accordion-item ${isFulfilled ? "fulfilled" : "pending"}`}
{requirements.map((requirement, index) => (
<div
key={index}
className={`accordion-item`}
onMouseEnter={() => handleMouseEnter(requirement)}
onMouseLeave={handleMouseLeave}
>
<div className="item-start">
{fulfilledRequirements.has(requirement) && <Check className="checkmark-icon" />}
<p className="requirement-text">{requirement}</p>
</div>
<span
className="ellipsis"
onClick={() => openMenu(requirement)}
style={{ visibility: hoveredRequirement === requirement && activeMenuRequirement !== requirement ? 'visible' : 'hidden' }}
>
<div className={`icon ${isFulfilled ? "green" : ""}`}>
{isFulfilled && <Check />}
•••
</span>
{activeMenuRequirement === requirement && (
<div className="requirement-menu">
{fulfilledRequirements.has(requirement) ? (
<button onClick={() => markAsUnfulfilled(requirement)}>Mark as Unfulfilled</button>
) : (
<button onClick={() => markAsFulfilled(requirement)}>Mark as Fulfilled</button>
)}
</div>
<p>{requirement}</p>
</div>
);
})}
)}
</div>
))}
{!requirements && <p>No requirements listed.</p>}
</div>
)}
</div>
Expand Down