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
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
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
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>
<html lang="en">
<head>
<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" />
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>react-todolist</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
+39 -118
View File
@@ -1,122 +1,43 @@
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'
import {useState} from "react";
function App() {
const [count, setCount] = useState(0)
export default function App() {
const [input, setInput] = useState("");
const [todos, setTodos] = useState([]);
return (
<>
<section id="center">
<div className="hero">
<img src={heroImg} className="base" width="170" height="179" alt="" />
<img src={reactLogo} className="framework" alt="React logo" />
<img src={viteLogo} className="vite" alt="Vite logo" />
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 (
<div className="container schatten-farbverlauf">
<div className="aufgaben-app">
<h2>Aufgabenliste</h2>
<div className="input-container">
<input type="text" onChange={(e) => setInput(e.target.value)} placeholder="Neue Aufgabe eintragen..."/>
<button onClick={() => addTodo()}>Hinzufügen</button>
</div>
<ul id="todo-list-container">
{todos.map((todo) => (
<li>
<span onClick={() => toggleTodo(todo.id)}>
{todo.text}: {todo.erledigt ? "bereits " : "noch nicht "} erledigt
</span>
<button onClick={() => deleteTodo(todo.id)}>Entfernen</button>
</li>))}
</ul>
</div>
</div>
<div>
<h1>Get started</h1>
<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>
<a href="https://vite.dev/" target="_blank">
<img className="logo" src={viteLogo} alt="" />
Explore Vite
</a>
</li>
<li>
<a href="https://react.dev/" target="_blank">
<img className="button-icon" src={reactLogo} alt="" />
Learn more
</a>
</li>
</ul>
</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>
</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 { createRoot } from 'react-dom/client'
import './index.css'
import './style.css'
import App from './App.jsx'
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;
}