added more review components

This commit is contained in:
2026-05-14 13:33:57 +02:00
parent 9591131da1
commit e692d0e23c
4 changed files with 137 additions and 41 deletions
+6
View File
@@ -6,6 +6,12 @@
<link rel="icon" type="image/svg+xml" href="/generated_two_no_background.png" /> <link rel="icon" type="image/svg+xml" href="/generated_two_no_background.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Birdy</title> <title>Birdy</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link
href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap"
rel="stylesheet">
</head> </head>
<body> <body>
+45 -3
View File
@@ -1,17 +1,27 @@
.input-box { .input-box {
display: flex; display: flex;
align-items: center;
justify-content: center;
gap: 80px;
width: 100%;
padding: 40px;
height: fit-content;
} }
.review-select-dropdown { .review-select-dropdown {
width: 200px; width: 200px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: flex-start; justify-content: center;
height: calc(50px + 40px * 2); height: fit-content;
gap: 0px; gap: 0px;
user-select: none; user-select: none;
} }
.review-select-dropdown.rating {
width: 60px;
}
.review-select-button { .review-select-button {
width: 100%; width: 100%;
height: 50px; height: 50px;
@@ -19,7 +29,8 @@
background: var(--surface); background: var(--surface);
border: 1px solid var(--surface); border: 1px solid var(--surface);
outline: none; outline: none;
color: white; color: black;
font-weight: bold;
border-radius: 16px; border-radius: 16px;
} }
@@ -58,3 +69,34 @@
cursor: pointer; cursor: pointer;
border-color: white; border-color: white;
} }
.review-text-box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: fit-content;
height: fit-content;
}
.review-text {
resize: none;
overflow: hidden;
width: 300px;
height: 160px;
border-radius: 8px;
border: 1px solid var(--surface);
background: var(--surface);
color: black;
padding: 10px;
font-family: "Space Mono", monospace;
font-size: 15px;
}
.review-text:focus {
border: 1px solid white;
}
.review-text::placeholder {
color: white;
}
+31 -38
View File
@@ -1,13 +1,15 @@
import { useState, useRef, useEffect } from 'react'; import { useState, useRef, useEffect } from 'react';
import './Reviews.css'; import './Reviews.css';
import SelectDropdown from './components/SelectDropdown';
function Reviews() { function Reviews() {
const [error, setError] = useState(null); const [error, setError] = useState(null);
const [selectedWebsite, setSelectedWebsite] = useState("Not Specified"); const [selectedWebsite, setSelectedWebsite] = useState("Not Specified");
const [selectWebsiteVisibility, setSelectWebsiteVisibility] = useState(false); const [selectedRating, setSelectedRating] = useState(0);
const websiteSelectButton = useRef();
const [text, setText] = useState(""); const [text, setText] = useState("");
const MAX_TEXT = 180;
const fetchReviews = async () => { const fetchReviews = async () => {
const response = await fetch(`api.marvinkrausser/review`, { const response = await fetch(`api.marvinkrausser/review`, {
@@ -47,26 +49,25 @@ function Reviews() {
setSelectedWebsite(website); setSelectedWebsite(website);
}; };
const changeSelectedRating = (rating) => {
setSelectedRating(rating);
}
const websites = [ const websites = [
"CNN (current)", "CNN (current)",
"Portfolio", "Portfolio",
"Not Specified" "Not Specified"
]; ];
useEffect(() => { const ratings = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
function handleClick(event) {
if (websiteSelectButton.current && !websiteSelectButton.current.contains(event.target) const handleTextInput = (e) => {
) { const textInput = e.target.value;
setSelectWebsiteVisibility(false);
} if (textInput.length <= MAX_TEXT || textInput.length < text.length) {
setText(textInput);
} }
};
document.addEventListener("click", handleClick);
return () => {
document.removeEventListener("click", handleClick);
};
}, []);
return (<> return (<>
@@ -74,31 +75,23 @@ function Reviews() {
<h1 className='site-headline'>Reviews</h1> <h1 className='site-headline'>Reviews</h1>
<div className='input-box'> <div className='input-box'>
<div className='review-select-dropdown'> <SelectDropdown values={websites} defaultValue={websites.at(0)} classNames={"website"} changeInputParent={changeSelectedWebsite} />
<button ref={websiteSelectButton} className={selectWebsiteVisibility ? "review-select-button active" : "review-select-button"} onClick={() => { setSelectWebsiteVisibility(!selectWebsiteVisibility) }}>
{selectedWebsite} <div className='review-text-box'>
</button> <textarea
{selectWebsiteVisibility && <div className='review-select-menu'> autoCorrect="off"
{websites.map((site, i) => autoCapitalize="off"
selectedWebsite !== site && ( spellCheck={false}
<div className='review-text'
key={site} type="text"
className={i >= (websites.length - (selectedWebsite == websites.at(-1) ? 2 : 1)) ? "review-select-item last" : "review-select-item"} value={text}
onClick={() => changeSelectedWebsite(site)} onChange={(e) => handleTextInput(e)}
> placeholder="Type something..."
{site} />
</div> <p>{text.length}/{MAX_TEXT}</p>
)
)}
</div>}
</div> </div>
<input <SelectDropdown values={ratings} defaultValue={ratings.at(0)} classNames={"rating"} changeInputParent={changeSelectedRating} />
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="Type something..."
/>
</div> </div>
</div> </div>
</>) </>)
+55
View File
@@ -0,0 +1,55 @@
import { useState, useRef, useEffect } from 'react';
function SelectDropdown(props) {
const values = props.values;
const defaultValue = props.defaultValue;
const classNames = props.classNames;
const changeInputParent = props.changeInputParent;
const [selectedInput, setselectedInput,] = useState(defaultValue);
const [selectVisibility, setSelectVisibility] = useState(false);
const SelectButton = useRef();
useEffect(() => {
function handleClick(event) {
if (SelectButton.current && !SelectButton.current.contains(event.target)
) {
setSelectVisibility(false);
}
}
document.addEventListener("click", handleClick);
return () => {
document.removeEventListener("click", handleClick);
};
}, []);
const changeSelectedInput = (input) => {
setselectedInput(input);
changeInputParent(input);
};
return (
<div className={`review-select-dropdown ${classNames}`} >
<button ref={SelectButton} className={selectVisibility ? `review-select-button ${classNames} active` : `review-select-button ${classNames}`} onClick={() => { setSelectVisibility(!selectVisibility) }}>
{selectedInput}
</button>
{selectVisibility && <div className={`review-select-menu ${classNames}`}>
{values.map((item, i) =>
String(selectedInput) !== String(item) && (
<div
key={item}
className={i >= (values.length - (selectedInput == values.at(-1) ? 2 : 1)) ? `review-select-item ${classNames} last` : `review-select-item ${classNames}`}
onClick={() => changeSelectedInput(item)}
>
{item}
</div>
)
)}
</div>}
</ div>
);
}
export default SelectDropdown;