Py.Cafe

iisakkirotko/

solara-todo-store-advanced

Todo List Manager

DocsPricing
  • app.py
  • requirements.txt
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
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()