mirror of
https://github.com/pyscript/pyscript.git
synced 2025-12-19 18:27:29 -05:00
209 lines
7.0 KiB
HTML
209 lines
7.0 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
|
|
<title>Todo App</title>
|
|
|
|
<link rel="icon" type="image/png" href="favicon.png" />
|
|
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
|
|
|
|
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
|
|
<link rel="stylesheet" href="./assets/css/examples.css" />
|
|
<link rel="stylesheet" href="./assets/prism/prism.css" />
|
|
<script defer src="./assets/prism/prism.js"></script>
|
|
</head>
|
|
|
|
<body>
|
|
<nav class="navbar" style="background-color: #000000;">
|
|
<div class="app-header">
|
|
<a href="/">
|
|
<img src="./logo.png" class="logo">
|
|
</a>
|
|
<a class="title" href="" style="color: #f0ab3c;">Todo App</a>
|
|
</div>
|
|
</nav>
|
|
<section class="pyscript">
|
|
<py-config>
|
|
[[fetch]]
|
|
files = ["./utils.py"]
|
|
</py-config>
|
|
|
|
<py-script src="./todo.py"> </py-script>
|
|
|
|
<main>
|
|
<section>
|
|
|
|
<div class="text-center w-full mb-8">
|
|
<h1 class="text-3xl font-bold text-gray-800 uppercase tracking-tight">To Do List</h1>
|
|
</div>
|
|
<div>
|
|
<input id="new-task-content" class="py-input" type="text">
|
|
<button id="new-task-btn" class="py-button" type="submit" py-click="add_task()">
|
|
Add task
|
|
</button>
|
|
</div>
|
|
|
|
<py-list id="myList"></py-list>
|
|
<div id="list-tasks-container" class="flex flex-col-reverse mt-4">
|
|
</div>
|
|
|
|
<template id="task-template">
|
|
<section class="task py-li-element">
|
|
<label for="flex items-center p-2 ">
|
|
<input class="mr-2" type="checkbox">
|
|
<p class="m-0 inline"></p>
|
|
</label>
|
|
</section>
|
|
</template>
|
|
|
|
</section>
|
|
</main>
|
|
</section>
|
|
<section class="code">
|
|
<div id="view-code-button" role="button" aria-pressed="false" tabindex="0">View Code</div>
|
|
<div id="code-section" class="code-section-hidden">
|
|
<p>index.html</p>
|
|
<pre class="prism-code language-html">
|
|
<code class="language-html">
|
|
<py-config>
|
|
[[fetch]]
|
|
files = ["./utils.py"]
|
|
</py-config>
|
|
<py-script src="./todo.py">
|
|
</py-script>
|
|
<main>
|
|
<section>
|
|
|
|
<div class="text-center w-full mb-8">
|
|
<h1 class="text-3xl font-bold text-gray-800 uppercase tracking-tight">To Do List</h1>
|
|
</div>
|
|
<div>
|
|
<input id="new-task-content" class="py-input" type="text">
|
|
<button id="new-task-btn" class="py-button" type="submit" py-click="add_task()">
|
|
Add task
|
|
</button>
|
|
</div>
|
|
|
|
<py-list id="myList"></py-list>
|
|
<div id="list-tasks-container" class="flex flex-col-reverse mt-4">
|
|
</div>
|
|
|
|
<template id="task-template">
|
|
<section class="task py-li-element">
|
|
<label for="flex items-center p-2 ">
|
|
<input class="mr-2" type="checkbox">
|
|
<p class="m-0 inline"></p>
|
|
</label>
|
|
</section>
|
|
</template>
|
|
|
|
</section>
|
|
</main>
|
|
</code>
|
|
</pre>
|
|
<p>utils.py</p>
|
|
<pre class="prism-code language-python">
|
|
<code class="language-python">
|
|
from datetime import datetime as dt
|
|
|
|
|
|
def format_date(dt_, fmt="%m/%d/%Y, %H:%M:%S"):
|
|
return f"{dt_:{fmt}}"
|
|
|
|
|
|
def now(fmt="%m/%d/%Y, %H:%M:%S"):
|
|
return format_date(dt.now(), fmt)
|
|
|
|
|
|
def remove_class(element, class_name):
|
|
element.element.classList.remove(class_name)
|
|
|
|
|
|
def add_class(element, class_name):
|
|
element.element.classList.add(class_name)
|
|
</code>
|
|
</pre>
|
|
<p>todo.py</p>
|
|
<pre class="prism-code language-python">
|
|
<code class="language-python">
|
|
from datetime import datetime as dt
|
|
|
|
from utils import add_class, remove_class
|
|
|
|
tasks = []
|
|
|
|
# define the task template that will be use to render new templates to the page
|
|
task_template = Element("task-template").select(".task", from_content=True)
|
|
task_list = Element("list-tasks-container")
|
|
new_task_content = Element("new-task-content")
|
|
|
|
|
|
def add_task(*ags, **kws):
|
|
# ignore empty task
|
|
if not new_task_content.element.value:
|
|
return None
|
|
|
|
# create task
|
|
task_id = f"task-{len(tasks)}"
|
|
task = {
|
|
"id": task_id,
|
|
"content": new_task_content.element.value,
|
|
"done": False,
|
|
"created_at": dt.now(),
|
|
}
|
|
|
|
tasks.append(task)
|
|
|
|
# add the task element to the page as new node in the list by cloning from a
|
|
# template
|
|
task_html = task_template.clone(task_id)
|
|
task_html_content = task_html.select("p")
|
|
task_html_content.element.innerText = task["content"]
|
|
task_html_check = task_html.select("input")
|
|
task_list.element.appendChild(task_html.element)
|
|
|
|
def check_task(evt=None):
|
|
task["done"] = not task["done"]
|
|
if task["done"]:
|
|
add_class(task_html_content, "line-through")
|
|
else:
|
|
remove_class(task_html_content, "line-through")
|
|
|
|
new_task_content.clear()
|
|
task_html_check.element.onclick = check_task
|
|
|
|
|
|
def add_task_event(e):
|
|
if e.key == "Enter":
|
|
add_task()
|
|
|
|
|
|
new_task_content.element.onkeypress = add_task_event
|
|
</code>
|
|
</pre>
|
|
</div>
|
|
</section>
|
|
<script>
|
|
const viewCodeButton = document.getElementById("view-code-button");
|
|
const codeSection = document.getElementById("code-section");
|
|
const handleClick = () => {
|
|
if (codeSection.classList.contains("code-section-hidden")) {
|
|
codeSection.classList.remove("code-section-hidden");
|
|
codeSection.classList.add("code-section-visible");
|
|
} else {
|
|
codeSection.classList.remove("code-section-visible");
|
|
codeSection.classList.add("code-section-hidden");
|
|
}
|
|
}
|
|
viewCodeButton.addEventListener("click", handleClick)
|
|
viewCodeButton.addEventListener("keydown", (e) => {
|
|
if (e.key === " " || e.key === "Enter" || e.key === "Spacebar") {
|
|
handleClick();
|
|
}
|
|
})
|
|
</script>
|
|
</body>
|
|
</html>
|