$task['id'] ?? uniqid('task_', true), 'title' => trim((string) ($task['title'] ?? '')), 'done' => (bool) ($task['done'] ?? false), ]; }, $data)); }; $saveTasks = static function (string $path, array $tasks): void { file_put_contents( $path, json_encode($tasks, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), LOCK_EX, ); }; $tasks = $loadTasks($storagePath); $app = new Application(); $window = new Window('Todo Beispiel', 520, 720); $statusLabel = new Label('Bereit.', 'text-sm text-gray-600'); $main = new Container('flex flex-col bg-gray-100 gap-4 p-4 h-full w-full'); $title = new Label('Todo Liste', 'text-2xl font-bold text-black'); $main->addComponent($title); $input = new TextInput('Neue Aufgabe hinzufügen …', 'flex-1 border border-gray-300 rounded px-3 py-2 bg-white text-black'); $addButton = new Button('Hinzufügen', 'px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700'); $inputRow = new Container('flex flex-row gap-3 w-full'); $inputRow->addComponent($input); $inputRow->addComponent($addButton); $main->addComponent($inputRow); $listWrapper = new Container('flex-1 w-full overflow-auto bg-white rounded border border-gray-300 p-2'); $listContainer = new Container('flex flex-col gap-2 w-full'); $listWrapper->addComponent($listContainer); $main->addComponent($listWrapper); $main->addComponent($statusLabel); $renderTasks = null; $renderTasks = function () use (&$tasks, $listContainer, $statusLabel, $storagePath, $saveTasks, &$renderTasks) { $listContainer->clearChildren(); if (empty($tasks)) { $emptyLabel = new Label('Keine Aufgaben vorhanden. Erstelle die erste oben im Feld.', 'text-sm text-gray-500'); $listContainer->addComponent($emptyLabel); return; } foreach ($tasks as $index => $task) { $row = new Container('flex flex-row items-center gap-3 w-full border border-gray-200 rounded px-3 py-2 bg-white shadow-sm'); $taskLabelStyles = $task['done'] ? 'flex-1 text-gray-500 line-through' : 'flex-1 text-black'; $taskLabel = new Label($task['title'], $taskLabelStyles); $row->addComponent($taskLabel); $toggleButton = new Button( $task['done'] ? 'Reaktivieren' : 'Erledigt', $task['done'] ? 'px-3 py-1 text-sm bg-amber-500 text-white rounded hover:bg-amber-600' : 'px-3 py-1 text-sm bg-emerald-500 text-white rounded hover:bg-emerald-600', ); $toggleButton->setOnClick(function () use (&$tasks, $task, $storagePath, $saveTasks, $statusLabel, $renderTasks) { foreach ($tasks as &$entry) { if ($entry['id'] === $task['id']) { $entry['done'] = !$entry['done']; break; } } $saveTasks($storagePath, $tasks); $statusLabel->setText(($task['done'] ? 'Aufgabe reaktiviert: ' : 'Aufgabe erledigt: ') . $task['title']); $renderTasks(); }); $deleteButton = new Button( 'Löschen', 'px-3 py-1 text-sm bg-red-500 text-white rounded hover:bg-red-600', ); $deleteButton->setOnClick(function () use (&$tasks, $index, $task, $storagePath, $saveTasks, $statusLabel, $renderTasks) { array_splice($tasks, $index, 1); $saveTasks($storagePath, $tasks); $statusLabel->setText('Aufgabe entfernt: ' . $task['title']); $renderTasks(); }); $row->addComponent($toggleButton); $row->addComponent($deleteButton); $listContainer->addComponent($row); } }; $renderTasks(); $addButton->setOnClick(function () use (&$tasks, $input, $saveTasks, $storagePath, $statusLabel, $renderTasks) { $title = trim($input->getValue()); if ($title === '') { $statusLabel->setText('Bitte zuerst einen Aufgabentext eingeben.'); return; } $tasks[] = [ 'id' => uniqid('task_', true), 'title' => $title, 'done' => false, ]; $saveTasks($storagePath, $tasks); $input->setValue(''); $statusLabel->setText('Aufgabe hinzugefügt: ' . $title); $renderTasks(); }); $window->setRoot($main); $app->addWindow($window); $app->run();