Skip to content
Merged
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
Binary file added frontend/public/andrea.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/jennie.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/josefine.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions frontend/src/app/dashboard/page.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,30 @@
padding: 2.75rem;
}

.dashboardDivider {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 70vh;
}

.aboutUsButton {
align-self: flex-end;
transform-origin: center;
}

.aboutUsButton:hover {
animation: wave 0.8s ease-in-out infinite;
}

@keyframes wave {
0% {
transform: rotate(0deg);
}
50% {
transform: rotate(30deg);
}
100% {
transform: rotate(0deg);
}
}
4 changes: 3 additions & 1 deletion frontend/src/app/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,21 @@
import CircleButton from "@/elements/CircleButton/CircleButton";

export default function DashboardPage() {
const { user, isLoading } = useAuth();

Check warning on line 12 in frontend/src/app/dashboard/page.tsx

View workflow job for this annotation

GitHub Actions / lint

'isLoading' is assigned a value but never used

Check warning on line 12 in frontend/src/app/dashboard/page.tsx

View workflow job for this annotation

GitHub Actions / lint

'user' is assigned a value but never used
const { openModal } = useModal();
return (
<>
<ProtectedRoute>
<section className={styles.content}>
<UserDesigns />
<UserDesigns />
<div className={styles.dashboardDivider}>
<UserInfo />
<CircleButton
buttonIcon="./icons/waving-blue-icon.svg"
className={styles.aboutUsButton}
onClick={() => openModal("about-us")}
/>
</div>
</section>
</ProtectedRoute>
</>
Expand Down
32 changes: 25 additions & 7 deletions frontend/src/app/page.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,6 @@
height: calc(100vh - 4.03rem);
}

.aboutUsButton {
position: absolute;
z-index: 1000;
bottom: 0;
right: 0;
margin: 1rem;
}

.creatingButton {
align-self: center;
Expand All @@ -21,4 +14,29 @@
display: flex;
flex-direction: column;
gap: 0.5rem;
}

.aboutUsButton {
position: absolute;
z-index: 1000;
bottom: 0;
right: 0;
margin: 1rem;
transform-origin: center;
}

.aboutUsButton:hover {
animation: wave 0.8s ease-in-out infinite;
}

@keyframes wave {
0% {
transform: rotate(0deg);
}
50% {
transform: rotate(30deg);
}
100% {
transform: rotate(0deg);
}
}
24 changes: 15 additions & 9 deletions frontend/src/elements/ScrollBar/ScrollBar.module.css
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
.scrollBarWrapper {
position: relative;
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
}

.scrollTrack {
position: absolute;
left: 0;
top: 0;
bottom: 0;
position: relative;
width: 0.313rem;
background-color: var(--dark-vanilla-dark);
border-radius: 0.156rem;
cursor: pointer;
z-index: 10;
flex-shrink: 0;
}

.scrollThumb {
Expand All @@ -29,20 +30,25 @@
background-color: var(--sunflower-seed);
}

.scrollThumb:active {
.scrollThumbDragging {
cursor: grabbing;
background-color: var(--sunflower-seed);
}

.scrollContent {
flex: 1;
.scrollContainer {
width: 100%;
height: 100%;
max-height: 100%;
min-height: 0;
overflow-y: auto;
overflow-x: hidden;

display: block;
position: relative;
/* Hide default scrollbar */
scrollbar-width: none;
-ms-overflow-style: none;
}

.scrollContent::-webkit-scrollbar {
.scrollContainer::-webkit-scrollbar {
display: none;
}
198 changes: 105 additions & 93 deletions frontend/src/elements/ScrollBar/ScrollBar.tsx
Original file line number Diff line number Diff line change
@@ -1,124 +1,131 @@
import React, { ReactNode, useRef, useEffect, useState } from "react";
import styles from "./ScrollBar.module.css";
import React, { ReactNode, CSSProperties, useRef, useEffect, useState } from 'react';
import styles from './ScrollBar.module.css';

interface ScrollBarProps {
children: ReactNode;
maxHeight: string;
className?: string;
maxHeight?: string;
contentClassName?: string;
style?: CSSProperties;
}

export const ScrollBar: React.FC<ScrollBarProps> = ({
children,
maxHeight,
className = "",
contentClassName = "",
export const ScrollBar: React.FC<ScrollBarProps> = ({
children,
maxHeight = 'auto',
contentClassName = '',
style = {}
}) => {
const scrollRef = useRef<HTMLDivElement>(null);
const thumbRef = useRef<HTMLDivElement>(null);
const scrollPosRef = useRef<number>(0);
const [isDragging, setIsDragging] = useState(false);
const [thumbHeight, setThumbHeight] = useState(0);
const [thumbTop, setThumbTop] = useState(0);
const [isDragging, setIsDragging] = useState(false);
const [showScrollbar, setShowScrollbar] = useState(false);

// Reference to the actual scrollable content div
const contentRef = useRef<HTMLDivElement>(null);

// Save scroll position before re-render
useEffect(() => {
const updateScrollbar = () => {
if (!contentRef.current) return;

// Get scroll measurements from the content div
const { scrollHeight, clientHeight, scrollTop } = contentRef.current;
const hasScroll = scrollHeight > clientHeight;

setShowScrollbar(hasScroll);

if (hasScroll) {
const thumbHeightCalc =
(clientHeight / scrollHeight) * clientHeight * 0.5;
setThumbHeight(Math.max(thumbHeightCalc, 40));

// Calculate thumb position based on scroll percentage
const maxScroll = scrollHeight - clientHeight;
const scrollPercentage = scrollTop / maxScroll;
const maxThumbTop = clientHeight - thumbHeightCalc;
setThumbTop(scrollPercentage * maxThumbTop);
}
};

const content = contentRef.current;
if (!content) return;
const element = scrollRef.current;
if (!element) return;

content.addEventListener("scroll", updateScrollbar);
updateScrollbar();
const handleScroll = () => {
scrollPosRef.current = element.scrollTop;
updateThumbPosition();
};

// Update scrollbar when content size changes
const resizeObserver = new ResizeObserver(updateScrollbar);
resizeObserver.observe(content);
element.addEventListener('scroll', handleScroll);
return () => element.removeEventListener('scroll', handleScroll);
}, []);

return () => {
content.removeEventListener("scroll", updateScrollbar);
resizeObserver.disconnect();
};
}, [children]);
// Restore scroll position after re-render
useEffect(() => {
const element = scrollRef.current;
if (element && scrollPosRef.current > 0) {
element.scrollTop = scrollPosRef.current;
}
});

// Calculate thumb size and position
const updateThumbPosition = () => {
const element = scrollRef.current;
if (!element) return;

const scrollHeight = element.scrollHeight;
const clientHeight = element.clientHeight;
const scrollTop = element.scrollTop;

if (scrollHeight <= clientHeight) {
setThumbHeight(0);
return;
}

const thumbHeightCalc = (clientHeight / scrollHeight) * clientHeight;
const thumbTopCalc = (scrollTop / scrollHeight) * clientHeight;

setThumbHeight(thumbHeightCalc);
setThumbTop(thumbTopCalc);
};

const handleTrackClick = (e: React.MouseEvent<HTMLDivElement>) => {
if (!contentRef.current) return;
// Update thumb on mount and when content changes
useEffect(() => {
updateThumbPosition();

const track = e.currentTarget;
const trackRect = track.getBoundingClientRect();
const clickPosition = e.clientY - trackRect.top;
const element = scrollRef.current;
if (!element) return;

const { scrollHeight, clientHeight } = contentRef.current;
const scrollPercentage = clickPosition / clientHeight;
const maxScroll = scrollHeight - clientHeight;
const resizeObserver = new ResizeObserver(updateThumbPosition);
resizeObserver.observe(element);

// Jump scroll to the clicked position
contentRef.current.scrollTop = scrollPercentage * maxScroll;
};
return () => resizeObserver.disconnect();
}, [children]);

// Handle thumb dragging
const handleThumbMouseDown = (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
setIsDragging(true);
};

useEffect(() => {
if (!isDragging) return;

const handleMouseMove = (e: MouseEvent) => {
if (!contentRef.current) return;

const { scrollHeight, clientHeight } = contentRef.current;
const maxScroll = scrollHeight - clientHeight;
const maxThumbTop = clientHeight - thumbHeight;

// Calculate new thumb position based on mouse movement
const deltaY = e.movementY;
const newThumbTop = Math.max(0, Math.min(thumbTop + deltaY, maxThumbTop));

const scrollPercentage = newThumbTop / maxThumbTop;
contentRef.current.scrollTop = scrollPercentage * maxScroll;
const startY = e.clientY;
const startTop = thumbTop;
const element = scrollRef.current;
if (!element) return;

const handleMouseMove = (moveEvent: MouseEvent) => {
const deltaY = moveEvent.clientY - startY;
const newTop = Math.max(0, Math.min(element.clientHeight - thumbHeight, startTop + deltaY));

const scrollPercentage = newTop / (element.clientHeight - thumbHeight);
element.scrollTop = scrollPercentage * (element.scrollHeight - element.clientHeight);
};

const handleMouseUp = () => {
setIsDragging(false);
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
};

document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("mouseup", handleMouseUp);
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
};

return () => {
document.removeEventListener("mousemove", handleMouseMove);
document.removeEventListener("mouseup", handleMouseUp);
};
}, [isDragging, thumbTop, thumbHeight]);
// Handle track click
const handleTrackClick = (e: React.MouseEvent) => {
if (e.target !== e.currentTarget) return;

const element = scrollRef.current;
if (!element) return;

const trackRect = e.currentTarget.getBoundingClientRect();
const clickY = e.clientY - trackRect.top;
const scrollPercentage = clickY / trackRect.height;

element.scrollTop = scrollPercentage * (element.scrollHeight - element.clientHeight);
};

return (
<div className={`${styles.scrollBarWrapper} ${className}`}>
{showScrollbar && (
<div className={styles.scrollBarWrapper}>
{thumbHeight > 0 && (
<div className={styles.scrollTrack} onClick={handleTrackClick}>
<div
className={styles.scrollThumb}
ref={thumbRef}
className={`${styles.scrollThumb} ${isDragging ? styles.scrollThumbDragging : ''}`}
style={{
height: `${thumbHeight}px`,
top: `${thumbTop}px`,
Expand All @@ -127,13 +134,18 @@ export const ScrollBar: React.FC<ScrollBarProps> = ({
/>
</div>
)}
<div
ref={contentRef}
className={`${styles.scrollContent} ${contentClassName}`}
style={{ maxHeight }}
<div
ref={scrollRef}
className={styles.scrollContainer}
style={{
maxHeight,
...style
}}
>
{children}
<div className={contentClassName}>
{children}
</div>
</div>
</div>
);
};
};
Loading
Loading