import uuid
from typing import Callable
import solara
# this todo item is only a collection of reactive values
class TodoItem:
def __init__(self, text: str, done: bool = False):
self.text = solara.reactive(text)
self.done = solara.reactive(done)
self._uuid = solara.reactive(str(uuid.uuid4()))
self._dirty = solara.reactive(True)
def __str__(self) -> str:
return f"{self.text.value} ({'done' if self.done else 'not done'})"
# However, this class really adds some logic to the todo items
class TodoStore:
def __init__(self, items: list[TodoItem]):
# we keep the items as a protected attribute
self._items = solara.reactive(items)
self.add_item_text = solara.reactive("")
@property
def items(self):
# and make the items read only for a property
return self._items.value
def add_item(self, item):
self._items.value = [*self._items.value, item]
# reset the new text after adding a new item
self.add_item_text.value = ""
def add(self):
self.add_item(TodoItem(text=self.add_item_text.value))
def remove(self, item: TodoItem):
self._items.value = [k for k in self.items if k._uuid.value != item._uuid.value]
@property
def done_count(self):
return len([k for k in self.items if k.done.value])
@property
def done_percentage(self):
if len(self.items) == 0:
return 0
else:
return self.done_count / len(self.items) * 100
@solara.component
def TodoItemCard(item: TodoItem, on_remove: Callable[[TodoItem], None]):
with solara.Card():
with solara.Row(style={"align-items": "center"}):
solara.Checkbox(value=item.done)
solara.InputText("", value=item.text)
solara.Button(icon=True, icon_name="delete", color="error", on_click=lambda: on_remove(item))
# The TodoApp component is reusable, so in the future
# we could have multiple TodoApp components if needed
# (e.g. multiple lists of todos)
default_store = TodoStore(
[
TodoItem(text="Write a blog post", done=False),
TodoItem(text="Take out the trash", done=True),
TodoItem(text="Do the laundry", done=False),
]
)
@solara.component
def TodoApp(store: TodoStore = default_store):
for item in store.items:
TodoItemCard(item, on_remove=store.remove)
with solara.Card("New item"):
solara.InputText(label="Text", value=store.add_item_text)
solara.Button("Add new", on_click=store.add)
solara.ProgressLinear(value=store.done_percentage)
@solara.component
def Page():
TodoApp()