sidebar-things #3

Merged
jare2473 merged 3 commits from sidebar-things into main 2025-09-05 17:07:17 +02:00
3 changed files with 318 additions and 101 deletions
Showing only changes of commit e5d7cca9cb - Show all commits

View File

@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import styles from './Navigation.module.css'; // Import the CSS Module
import { Link, useLocation } from 'react-router-dom';
import { ThemeToggle } from './ThemeToggle';
@@ -8,11 +8,31 @@ import 'bootstrap-icons/font/bootstrap-icons.css';
const Navigation = () => {
const [menuOpen, setMenuOpen] = useState(false);
const [coursesOpen, setCoursesOpen] = useState(false);
const location = useLocation();
const { getCurrentUser } = useSettingsContext();
const toggleMenu = () => {
setMenuOpen(!menuOpen);
setCoursesOpen(false);
};
// Prevent body scroll when mobile menu is open
useEffect(() => {
if (menuOpen) {
document.body.style.overflow = 'hidden';
} else {
document.body.style.overflow = 'unset';
}
// Cleanup on unmount
return () => {
document.body.style.overflow = 'unset';
};
}, [menuOpen]);
const toggleCourses = () => {
setCoursesOpen(!coursesOpen);
};
// Helper function to get user's initials
@@ -52,18 +72,17 @@ const Navigation = () => {
</div>
</div>
{/* User Profile Section - Desktop only */}
<Link to="/profile" className={styles.userSection}>
<div className={styles.userAvatar}>
<div className={styles.avatarInitials}>
{getInitials(getCurrentUser().name)}
</div>
</div>
<span className={styles.userName}>{getCurrentUser().name}</span>
</Link>
{/* Navigation - always visible in sidebar mode, toggleable in mobile */}
<nav className={`${styles.nav} ${menuOpen ? styles.navOpen : ''}`}>
<div className={styles.navScrollContainer}>
<Link onClick={handleClick} to="/profile" className={`${styles.navLink} ${styles.mobileOnly} ${styles.mobileProfileLink} ${isActive('/profile') ? styles.active : ''}`}>
<div className={styles.mobileUserAvatar}>
<div className={styles.avatarInitials}>
{getInitials(getCurrentUser().name)}
</div>
</div>
<span className={styles.navText}>{getCurrentUser().name}</span>
</Link>
<Link onClick={handleClick} to="/home" className={`${styles.navLink} ${isActive('/home') ? styles.active : ''}`}>
<i className={`bi ${isActive('/home') ? 'bi-house-door-fill' : 'bi-house-door'} ${styles.navIcon}`}></i>
<span className={styles.navText}>Home</span>
@@ -80,6 +99,46 @@ const Navigation = () => {
<i className={`bi ${isActive('/booking-settings') ? 'bi-gear-fill' : 'bi-gear'} ${styles.navIcon}`}></i>
<span className={styles.navText}>Booking Settings</span>
</Link>
{/* Mobile Courses Accordion */}
<button
onClick={toggleCourses}
className={`${styles.mobileOnly} ${styles.navLink} ${styles.accordionToggle}`}
>
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
<i className="bi bi-book" style={{ fontSize: '1.2rem', width: '24px', textAlign: 'center' }}></i>
<span className={styles.navText}>Kurser</span>
</div>
<i className={`bi ${coursesOpen ? 'bi-chevron-up' : 'bi-chevron-down'} ${styles.chevron}`} style={{ fontSize: '1.2rem' }}></i>
</button>
<div className={`${styles.mobileOnly} ${styles.accordionContent} ${coursesOpen ? styles.accordionOpen : ''}`}>
<div className={styles.mobileTermGroup}>
<h4 className={styles.mobileTermTitle}>VT2025</h4>
<Link onClick={handleClick} to="/course/prototyper-inom-interaktionsdesign" className={styles.mobileCourseLink}>
Prototyper inom interaktionsdesign
</Link>
<Link onClick={handleClick} to="/course/analytiska-perspektiv-inom-mdi" className={styles.mobileCourseLink}>
Analytiska perspektiv inom MDI
</Link>
<Link onClick={handleClick} to="/course/vetenskaplig-metodik" className={styles.mobileCourseLink}>
Vetenskaplig metodik och kommunikation inom...
</Link>
<Link onClick={handleClick} to="/course/deltagande-design" className={styles.mobileCourseLink}>
Deltagande design
</Link>
</div>
<div className={styles.mobileTermGroup}>
<h4 className={styles.mobileTermTitle}>HT2024</h4>
<Link onClick={handleClick} to="/course/programmering-1" className={styles.mobileCourseLink}>
Programmering 1
</Link>
<Link onClick={handleClick} to="/course/tidigare-kurser" className={styles.mobileCourseLink}>
Tidigare kurser
</Link>
</div>
</div>
{/* Courses Section */}
<div className={styles.coursesSection}>
@@ -114,11 +173,23 @@ const Navigation = () => {
</Link>
</div>
</div>
</div>
</nav>
{/* Theme Toggle - Desktop only */}
<div className={styles.sidebarBottom}>
{/* User Profile Section - Desktop only */}
<Link to="/profile" className={styles.userSection}>
<div className={styles.userAvatar}>
<div className={styles.avatarInitials}>
{getInitials(getCurrentUser().name)}
</div>
</div>
<span className={styles.userName}>{getCurrentUser().name}</span>
</Link>
{/* Theme Toggle - Desktop only */}
<div className={styles.themeToggleContainer}>
<ThemeToggle />
</div>
</div>
</header>
);
};

View File

@@ -66,6 +66,40 @@
flex-direction: column;
background-color: var(--bg-primary);
border-top: 1px solid var(--border-light);
width: 100%;
overflow: hidden;
}
/* Mobile-specific height adjustments */
@media (max-width: 1023px) {
.nav {
height: calc(100vh - 60px);
min-height: calc(100vh - 60px);
}
}
/* iOS Safari mobile specific - more precise detection */
@media (max-width: 1023px) and (max-device-width: 768px) {
@supports (-webkit-touch-callout: none) {
.nav {
height: calc(100svh - 60px);
min-height: calc(100svh - 120px);
}
}
}
.navScrollContainer {
display: contents;
}
/* Mobile-specific scroll container */
@media (max-width: 1023px) {
.navScrollContainer {
display: block;
height: 100%;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
}
.navOpen {
@@ -89,6 +123,7 @@
.navLink:hover {
background-color: var(--bg-muted);
color: var(--text-primary);
}
.navIcon {
@@ -117,6 +152,119 @@
display: none;
}
/* Mobile-only profile link */
.mobileOnly {
display: flex;
}
.mobileProfileLink {
background-color: var(--bg-muted);
margin-bottom: var(--spacing-sm);
border-radius: var(--border-radius-md);
border-bottom: none !important;
}
.mobileUserAvatar {
width: 32px;
height: 32px;
flex-shrink: 0;
}
.mobileUserAvatar .avatarInitials {
width: 100%;
height: 100%;
border-radius: 50%;
background-color: var(--color-primary);
color: var(--color-white);
display: flex;
align-items: center;
justify-content: center;
font-size: 0.9rem;
font-weight: var(--font-weight-semibold);
}
/* Mobile Courses Accordion */
.mobileCoursesSection {
flex-direction: column;
}
.accordionToggle {
background: none;
border: none;
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
box-sizing: border-box;
padding: var(--spacing-lg) var(--spacing-xl);
min-height: auto;
height: auto;
}
.accordionToggle:hover {
background-color: var(--bg-muted);
}
.chevron {
transition: var(--transition-fast);
color: var(--text-secondary);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.accordionContent {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
background-color: var(--bg-tertiary);
display: flex;
flex-direction: column;
}
.accordionOpen {
max-height: none;
}
.mobileTermGroup {
padding: var(--spacing-md) var(--spacing-xl);
display: flex;
flex-direction: column;
}
.mobileTermGroup:first-child {
border-top: 1px solid var(--border-light);
}
.mobileTermTitle {
font-size: 0.8rem;
font-weight: var(--font-weight-semibold);
color: var(--text-secondary);
margin: 0 0 var(--spacing-sm) 0;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.mobileCourseLink {
display: block;
padding: var(--spacing-md) var(--spacing-lg);
margin-bottom: var(--spacing-sm);
font-size: 1rem;
color: var(--text-primary);
text-decoration: none;
border-radius: var(--border-radius-md);
transition: var(--transition-fast);
line-height: 1.4;
width: 100%;
}
.mobileCourseLink:hover {
background-color: var(--bg-muted);
color: var(--text-primary);
}
/* ===========================================
DESKTOP SIDEBAR STYLES (≥ 1024px)
========================================== */
@@ -129,46 +277,27 @@
left: 0;
width: 280px;
height: 100vh;
min-height: 100vh;
flex-direction: column;
/*box-shadow: var(--shadow-xl);*/
border-right: 1px solid var(--border-light);
overflow-y: auto;
z-index: var(--z-header);
}
.top {
flex-direction: column;
flex-direction: row;
padding: var(--spacing-2xl);
gap: var(--spacing-lg);
border-bottom: 1px solid var(--border-light);
min-height: fit-content;
}
.left {
flex-direction: column;
align-items: center;
gap: var(--spacing-md);
width: 100%;
}
.right {
width: 100%;
justify-content: center;
}
/* Hide mobile theme toggle on desktop */
.right > :first-child {
display: none;
}
.logo img {
height: 60px;
}
.brandText {
font-size: 1.3rem;
text-align: center;
}
.menuIcon {
display: none;
}
@@ -180,6 +309,8 @@
flex-direction: column;
padding: var(--spacing-xl);
gap: var(--spacing-xs);
height: auto;
overflow: visible;
}
.navLink {
@@ -308,7 +439,7 @@
.courseLink:hover {
background-color: var(--bg-muted);
color: var(--color-primary);
color: var(--text-primary);
}
/* Theme Toggle Container - Desktop */
@@ -318,6 +449,18 @@
justify-content: center;
border-bottom: 1px solid var(--border-light);
}
/* Hide mobile-only profile link on desktop */
.mobileOnly {
display: none;
}
.sidebarBottom {
background-color: var(--bg-primary);
border-top: 1px solid var(--border-light);
position: sticky;
bottom: 0;
}
}
/* ===========================================

View File

@@ -2,7 +2,9 @@
max-width: 800px;
margin: 0 auto;
padding: 2rem;
font-family: system-ui, -apple-system, sans-serif;
font-family: var(--font-secondary);
background-color: var(--bg-primary);
color: var(--text-primary);
}
.header {
@@ -12,13 +14,13 @@
.header h1 {
font-size: 2.5rem;
font-weight: 600;
color: #1f2937;
font-weight: var(--font-weight-semibold);
color: var(--text-primary);
margin-bottom: 0.5rem;
}
.header p {
color: #6b7280;
color: var(--text-secondary);
font-size: 1.1rem;
}
@@ -29,19 +31,19 @@
}
.section {
background: white;
border: 1px solid #e5e7eb;
border-radius: 12px;
background: var(--bg-secondary);
border: 1px solid var(--border-light);
border-radius: var(--border-radius-2xl);
padding: 1.5rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
box-shadow: var(--shadow-sm);
}
.section h2 {
font-size: 1.5rem;
font-weight: 600;
color: #1f2937;
font-weight: var(--font-weight-semibold);
color: var(--text-primary);
margin-bottom: 1.5rem;
border-bottom: 2px solid #f3f4f6;
border-bottom: 2px solid var(--border-light);
padding-bottom: 0.5rem;
}
@@ -60,14 +62,14 @@
.setting label strong {
font-size: 1rem;
font-weight: 600;
color: #374151;
font-weight: var(--font-weight-semibold);
color: var(--text-primary);
display: block;
}
.description {
font-size: 0.875rem;
color: #6b7280;
color: var(--text-secondary);
margin-top: 0.25rem;
display: block;
}
@@ -80,17 +82,18 @@
.dateInput, .numberInput, .select, .textInput {
padding: 0.75rem;
border: 1px solid #d1d5db;
border-radius: 6px;
border: 1px solid var(--input-border);
border-radius: var(--border-radius-md);
font-size: 1rem;
transition: border-color 0.2s, box-shadow 0.2s;
background: white;
transition: var(--transition-medium);
background: var(--input-bg);
color: var(--input-text);
}
.dateInput:focus, .numberInput:focus, .select:focus, .textInput:focus {
outline: none;
border-color: #2563eb;
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
border-color: var(--color-primary);
box-shadow: 0 0 0 3px var(--color-primary-light);
}
.dateInput {
@@ -111,34 +114,34 @@
.clearButton {
padding: 0.5rem 1rem;
background: #f3f4f6;
border: 1px solid #d1d5db;
border-radius: 6px;
background: var(--button-secondary-bg);
border: 1px solid var(--border-light);
border-radius: var(--border-radius-md);
font-size: 0.875rem;
color: #374151;
color: var(--button-secondary-text);
cursor: pointer;
transition: all 0.2s;
transition: var(--transition-medium);
width: fit-content;
}
.clearButton:hover {
background: #e5e7eb;
border-color: #9ca3af;
background: var(--button-secondary-hover-bg);
border-color: var(--border-medium);
}
.currentStatus {
margin-top: 0.5rem;
font-size: 0.875rem;
color: #4b5563;
color: var(--text-secondary);
}
.mockLabel {
background: #fbbf24;
color: #92400e;
background: var(--notification-warning-border);
color: var(--notification-warning-bg);
padding: 0.125rem 0.375rem;
border-radius: 4px;
border-radius: var(--border-radius-sm);
font-size: 0.75rem;
font-weight: 600;
font-weight: var(--font-weight-semibold);
text-transform: uppercase;
letter-spacing: 0.05em;
}
@@ -153,7 +156,7 @@
flex: 1;
height: 6px;
border-radius: 3px;
background: #e5e7eb;
background: var(--border-light);
outline: none;
cursor: pointer;
-webkit-appearance: none;
@@ -164,25 +167,25 @@
width: 20px;
height: 20px;
border-radius: 50%;
background: #2563eb;
background: var(--color-primary);
cursor: pointer;
border: 2px solid white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
border: 2px solid var(--bg-primary);
box-shadow: var(--shadow-sm);
}
.slider::-moz-range-thumb {
width: 20px;
height: 20px;
border-radius: 50%;
background: #2563eb;
background: var(--color-primary);
cursor: pointer;
border: 2px solid white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
border: 2px solid var(--bg-primary);
box-shadow: var(--shadow-sm);
}
.sliderValue {
font-weight: 600;
color: #2563eb;
font-weight: var(--font-weight-semibold);
color: var(--color-primary);
min-width: 40px;
text-align: center;
}
@@ -196,18 +199,18 @@
.toggle {
width: 50px;
height: 24px;
background: #d1d5db;
background: var(--border-light);
border-radius: 12px;
border: none;
position: relative;
cursor: pointer;
transition: background-color 0.3s ease;
transition: var(--transition-slow);
-webkit-appearance: none;
appearance: none;
}
.toggle:checked {
background: #10b981;
background: var(--notification-success-icon-bg);
}
.toggle:before {
@@ -216,11 +219,11 @@
width: 20px;
height: 20px;
border-radius: 50%;
background: white;
background: var(--bg-primary);
top: 2px;
left: 2px;
transition: transform 0.3s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
transition: var(--transition-slow);
box-shadow: var(--shadow-sm);
}
.toggle:checked:before {
@@ -228,60 +231,60 @@
}
.toggleStatus {
font-weight: 500;
color: #374151;
font-weight: var(--font-weight-medium);
color: var(--text-primary);
}
.actions {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;;
justify-content: center;
gap: 1rem;
padding: 2rem;
background: #f9fafb;
border-radius: 12px;
border: 1px solid #e5e7eb;
background: var(--bg-tertiary);
border-radius: var(--border-radius-2xl);
border: 1px solid var(--border-light);
width: 100%;
}
.resetButton {
padding: 0.75rem 2rem;
background: #dc2626;
color: white;
background: var(--reset-button-border);
color: var(--color-white);
border: none;
border-radius: 6px;
border-radius: var(--border-radius-md);
font-size: 1rem;
font-weight: 600;
font-weight: var(--font-weight-semibold);
cursor: pointer;
transition: background-color 0.2s;
transition: var(--transition-medium);
width: fit-content;
}
.resetButton:hover {
background: #b91c1c;
background: var(--reset-button-hover-border);
}
.testSessionButton {
padding: 0.75rem 2rem;
background: #2563eb;
color: white;
background: var(--color-primary);
color: var(--color-white);
border: none;
border-radius: 6px;
border-radius: var(--border-radius-md);
font-size: 1rem;
font-weight: 600;
font-weight: var(--font-weight-semibold);
cursor: pointer;
transition: background-color 0.2s;
transition: var(--transition-medium);
width: fit-content;
}
.testSessionButton:hover {
background: #1d4ed8;
background: var(--color-primary-hover);
}
.info {
font-size: 0.875rem;
color: #6b7280;
color: var(--text-secondary);
text-align: center;
}