React-App für Todoliste aus Beispiel aus der Ellmann-Vorlesung und der ursprünglichen Todo-App zusammengebastelt

This commit is contained in:
2026-07-14 14:26:09 +02:00
parent d18af0ddf1
commit 9680fd3d39
6 changed files with 130 additions and 242 deletions
+2 -2
View File
@@ -1,5 +1,5 @@
# React App zum Laufen bringen # React App zum Laufen bringen
1. 'Erster React Test' Ordner in Eingabeaufforderung oder PowerShell öffnen 1. 'react-todolist' Ordner in Eingabeaufforderung oder PowerShell öffnen
2. `npm install` -> npm installiert Abhängigkeiten für React 2. `npm install` -> npm installiert Abhängigkeiten für React
3. `npm run dev` -> npm startet React-Development-Server; Ausgabe sollte ungefähr sein: 3. `npm run dev` -> npm startet React-Development-Server; Ausgabe sollte ungefähr sein:
``` ```
@@ -11,4 +11,4 @@
``` ```
4. Eingabeaufforderung / PowerShell geöffnet lassen, da sonst auch der Server beendet wird 4. Eingabeaufforderung / PowerShell geöffnet lassen, da sonst auch der Server beendet wird
5. Falls sich Browserfenster nicht automatisch öffnet, ist die React App über den Link (siehe oben) erreichbar 5. Falls sich Browserfenster nicht automatisch öffnet, ist die React App über den Link (siehe oben) erreichbar
6. Dateien innerhalb von 'Erster React Test/src' verändern, um die React App weiterzuentwickeln 6. Dateien innerhalb von 'react-todolist/src' verändern, um die React App weiterzuentwickeln
+8 -9
View File
@@ -1,13 +1,12 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8"/>
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>react-todolist</title> <title>react-todolist</title>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
<script type="module" src="/src/main.jsx"></script> <script type="module" src="/src/main.jsx"></script>
</body> </body>
</html> </html>
+34 -113
View File
@@ -1,122 +1,43 @@
import { useState } from 'react' import {useState} from "react";
import reactLogo from './assets/react.svg'
import viteLogo from './assets/vite.svg'
import heroImg from './assets/hero.png'
import './App.css'
function App() { export default function App() {
const [count, setCount] = useState(0) const [input, setInput] = useState("");
const [todos, setTodos] = useState([]);
function addTodo() {
if (input.trim() === "") return;
setTodos([...todos, {id: Date.now(), text: input, erledigt: false}]);
setInput("");
}
function deleteTodo(id) {
setTodos(todos.filter((todo) => todo.id !== id));
}
function toggleTodo(id) {
setTodos(todos.map((todo) =>
todo.id === id ? {...todo, erledigt: !todo.erledigt} : todo
));
}
return ( return (
<> <div className="container schatten-farbverlauf">
<section id="center"> <div className="aufgaben-app">
<div className="hero"> <h2>Aufgabenliste</h2>
<img src={heroImg} className="base" width="170" height="179" alt="" /> <div className="input-container">
<img src={reactLogo} className="framework" alt="React logo" /> <input type="text" onChange={(e) => setInput(e.target.value)} placeholder="Neue Aufgabe eintragen..."/>
<img src={viteLogo} className="vite" alt="Vite logo" /> <button onClick={() => addTodo()}>Hinzufügen</button>
</div> </div>
<div> <ul id="todo-list-container">
<h1>Get started</h1> {todos.map((todo) => (
<p>
Edit <code>src/App.jsx</code> and save to test <code>HMR</code>
</p>
</div>
<button
type="button"
className="counter"
onClick={() => setCount((count) => count + 1)}
>
Count is {count}
</button>
</section>
<div className="ticks"></div>
<section id="next-steps">
<div id="docs">
<svg className="icon" role="presentation" aria-hidden="true">
<use href="/icons.svg#documentation-icon"></use>
</svg>
<h2>Documentation</h2>
<p>Your questions, answered</p>
<ul>
<li> <li>
<a href="https://vite.dev/" target="_blank"> <span onClick={() => toggleTodo(todo.id)}>
<img className="logo" src={viteLogo} alt="" /> {todo.text}: {todo.erledigt ? "bereits " : "noch nicht "} erledigt
Explore Vite </span>
</a> <button onClick={() => deleteTodo(todo.id)}>Entfernen</button>
</li> </li>))}
<li>
<a href="https://react.dev/" target="_blank">
<img className="button-icon" src={reactLogo} alt="" />
Learn more
</a>
</li>
</ul> </ul>
</div> </div>
<div id="social">
<svg className="icon" role="presentation" aria-hidden="true">
<use href="/icons.svg#social-icon"></use>
</svg>
<h2>Connect with us</h2>
<p>Join the Vite community</p>
<ul>
<li>
<a href="https://github.com/vitejs/vite" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#github-icon"></use>
</svg>
GitHub
</a>
</li>
<li>
<a href="https://chat.vite.dev/" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#discord-icon"></use>
</svg>
Discord
</a>
</li>
<li>
<a href="https://x.com/vite_js" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#x-icon"></use>
</svg>
X.com
</a>
</li>
<li>
<a href="https://bsky.app/profile/vite.dev" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#bluesky-icon"></use>
</svg>
Bluesky
</a>
</li>
</ul>
</div> </div>
</section> );
<div className="ticks"></div>
<section id="spacer"></section>
</>
)
} }
export default App
-111
View File
@@ -1,111 +0,0 @@
:root {
--text: #6b6375;
--text-h: #08060d;
--bg: #fff;
--border: #e5e4e7;
--code-bg: #f4f3ec;
--accent: #aa3bff;
--accent-bg: rgba(170, 59, 255, 0.1);
--accent-border: rgba(170, 59, 255, 0.5);
--social-bg: rgba(244, 243, 236, 0.5);
--shadow:
rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px;
--sans: system-ui, 'Segoe UI', Roboto, sans-serif;
--heading: system-ui, 'Segoe UI', Roboto, sans-serif;
--mono: ui-monospace, Consolas, monospace;
font: 18px/145% var(--sans);
letter-spacing: 0.18px;
color-scheme: light dark;
color: var(--text);
background: var(--bg);
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
@media (max-width: 1024px) {
font-size: 16px;
}
}
@media (prefers-color-scheme: dark) {
:root {
--text: #9ca3af;
--text-h: #f3f4f6;
--bg: #16171d;
--border: #2e303a;
--code-bg: #1f2028;
--accent: #c084fc;
--accent-bg: rgba(192, 132, 252, 0.15);
--accent-border: rgba(192, 132, 252, 0.5);
--social-bg: rgba(47, 48, 58, 0.5);
--shadow:
rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px;
}
#social .button-icon {
filter: invert(1) brightness(2);
}
}
body {
margin: 0;
}
#root {
width: 1126px;
max-width: 100%;
margin: 0 auto;
text-align: center;
border-inline: 1px solid var(--border);
min-height: 100svh;
display: flex;
flex-direction: column;
box-sizing: border-box;
}
h1,
h2 {
font-family: var(--heading);
font-weight: 500;
color: var(--text-h);
}
h1 {
font-size: 56px;
letter-spacing: -1.68px;
margin: 32px 0;
@media (max-width: 1024px) {
font-size: 36px;
margin: 20px 0;
}
}
h2 {
font-size: 24px;
line-height: 118%;
letter-spacing: -0.24px;
margin: 0 0 8px;
@media (max-width: 1024px) {
font-size: 20px;
}
}
p {
margin: 0;
}
code,
.counter {
font-family: var(--mono);
display: inline-flex;
border-radius: 4px;
color: var(--text-h);
}
code {
font-size: 15px;
line-height: 135%;
padding: 4px 8px;
background: var(--code-bg);
}
+1 -1
View File
@@ -1,6 +1,6 @@
import { StrictMode } from 'react' import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client' import { createRoot } from 'react-dom/client'
import './index.css' import './style.css'
import App from './App.jsx' import App from './App.jsx'
createRoot(document.getElementById('root')).render( createRoot(document.getElementById('root')).render(
+79
View File
@@ -0,0 +1,79 @@
@font-face {
font-family: 'Fira-Sans';
src: url("./media/fonts/FiraSans-Regular.ttf") format("truetype");
}
html {
font-size: 20px;
box-sizing: border-box;
}
* {
font-family: 'Fira-Sans', sans-serif;
}
.container {
margin: auto;
border-radius: 1em;
corner-shape: squircle bevel;
width: clamp(400px, 60vw, 800px);
}
.aufgaben-app {
margin: 10px;
padding: 10px;
border-radius: 1em;
corner-shape: squircle bevel;
background-color: ivory;
}
.aufgaben-app h2 {
font-size: 2rem;
}
.aufgaben-app h2 img {
max-height: 1em;
}
.input-container {
display: flex;
flex-wrap: nowrap;
gap: 1em;
}
.input-container #input-box {
flex-grow: 1;
}
.input-container button {
cursor: pointer;
background-color: lightskyblue;
border-radius: 100%;
corner-shape: squircle;
}
.schatten-farbverlauf {
background: linear-gradient(45deg, red, orange, yellow, green, blue, violet, pink);
padding: 3px;
}
.checked-list-item
{
text-decoration: line-through;
color: lightslategrey;
}
ul {
list-style: none;
}
li {
display: flex;
gap: .7em;
}
label {
flex-grow: 1;
}