359 lines
11 KiB
HTML
359 lines
11 KiB
HTML
<!DOCTYPE html>
|
|
<html
|
|
lang="en"
|
|
x-data
|
|
x-bind:data-theme="$store.darkMode.on ? 'dark' : 'light'"
|
|
>
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>Minimalist Tasks</title>
|
|
<!-- Alpine.js -->
|
|
<script
|
|
defer
|
|
src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"
|
|
></script>
|
|
<!-- PicoCSS -->
|
|
<link
|
|
rel="stylesheet"
|
|
href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css"
|
|
/>
|
|
<!-- Icons -->
|
|
<link href="https://css.gg/trash.css" rel="stylesheet" />
|
|
<link href="https://css.gg/sun.css" rel="stylesheet" />
|
|
<link href="https://css.gg/moon.css" rel="stylesheet" />
|
|
<link href="https://css.gg/code-slash.css" rel="stylesheet" />
|
|
</head>
|
|
|
|
<body
|
|
style="
|
|
display: flex;
|
|
align-items: center;
|
|
text-align: center;
|
|
min-width: 100vw;
|
|
min-height: 100vh;
|
|
"
|
|
>
|
|
<main style="max-width: 25rem">
|
|
<section>
|
|
<hgroup>
|
|
<h1>Minimalist Tasks</h1>
|
|
<p>Just the necessary</p>
|
|
</hgroup>
|
|
|
|
<template x-if="$store.page == 'tasks'">
|
|
<article
|
|
x-data="{newTaskText: ''}"
|
|
id="tasks"
|
|
style="min-height: 23rem"
|
|
>
|
|
<ul style="text-align: start; padding-left: 0">
|
|
<template x-for="task in $store.tasks">
|
|
<li
|
|
style="
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
"
|
|
>
|
|
<label style="max-width: 21rem">
|
|
<input
|
|
type="checkbox"
|
|
x-model="task.checked"
|
|
:checked="task.checked"
|
|
@click="changeTaskState(task.id, !task.checked)"
|
|
/>
|
|
<span
|
|
x-text="task.message"
|
|
:style="task.checked ? 'text-decoration: line-through;' : ''"
|
|
></span>
|
|
</label>
|
|
<i
|
|
style="cursor: pointer"
|
|
class="gg-trash"
|
|
@click="removeTask(task.id)"
|
|
></i>
|
|
</li>
|
|
</template>
|
|
</ul>
|
|
<form @submit.prevent="addTask(newTaskText)">
|
|
<input
|
|
type="text"
|
|
name="task-name"
|
|
id="task-input"
|
|
x-model="newTaskText"
|
|
placeholder="Whats needs to be added?"
|
|
/>
|
|
<button type="submit">Add task</button>
|
|
</form>
|
|
</article>
|
|
</template>
|
|
<template x-if="$store.page == 'about'">
|
|
<article
|
|
id="#about"
|
|
style="min-height: 23rem; text-align: justify; padding: 3rem"
|
|
>
|
|
<section>
|
|
<p>
|
|
This is a small experiment and learning project, with the
|
|
intention of making a working
|
|
<abbr data-tooltip="Single Page Application"> SPA </abbr>
|
|
with just a single HTML file. <i>"Just the necessary"</i>
|
|
</p>
|
|
<!-- <p style="opacity: 0.25;">
|
|
<s>
|
|
<small>
|
|
And simply out of curiosity to learn and try Alpine.js and
|
|
PicoCSS.
|
|
</small>
|
|
</s>
|
|
</p> -->
|
|
</section>
|
|
<section>
|
|
<p style="margin: 0"><small>Tech Stack:</small></p>
|
|
<ul style="display: flex; padding: 0">
|
|
<li
|
|
style="list-style: none; margin: 0.5rem; border: 0"
|
|
data-tooltip="AlpineJS"
|
|
>
|
|
<a href="https://alpinejs.dev" target="_blank">
|
|
<img
|
|
src="https://alpinejs.dev/favicon.png"
|
|
alt="AlpineJS Icon"
|
|
/>
|
|
</a>
|
|
</li>
|
|
<li
|
|
style="list-style: none; margin: 0.5rem; border: 0"
|
|
data-tooltip="PicoCSS"
|
|
>
|
|
<a href="https://picocss.com" target="_blank">
|
|
<img
|
|
src="https://picocss.com/img/favicon-32x32.png"
|
|
alt="PicoCSS Icon"
|
|
/>
|
|
</a>
|
|
</li>
|
|
<li
|
|
style="list-style: none; margin: 0.5rem; border: 0"
|
|
data-tooltip="css.gg"
|
|
>
|
|
<a href="https://css.gg" target="_blank">
|
|
<img
|
|
src="https://css.gg/fav/favicon-32x32.png"
|
|
alt="css.gg Icon"
|
|
/>
|
|
</a>
|
|
</li>
|
|
<li
|
|
style="list-style: none; margin: 0.5rem; border: 0"
|
|
data-tooltip="Vercel"
|
|
>
|
|
<a href="https://vercel.com" target="_blank">
|
|
<svg
|
|
width="32"
|
|
height="32"
|
|
viewBox="0 0 76 65"
|
|
fill="none"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
>
|
|
<path
|
|
d="M37.5274 0L75.0548 65H0L37.5274 0Z"
|
|
:fill="$store.darkMode.on ? '#fff' : '#000'"
|
|
/>
|
|
</svg>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
<div
|
|
style="
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
"
|
|
>
|
|
<p style="margin: 0">
|
|
Created by
|
|
<a href="https://guz.vercel.app" target="_blank">Guz</a>
|
|
</p>
|
|
<a
|
|
href="https://codeberg.org/Guz013/MinimalistTasks"
|
|
target="_blank"
|
|
data-tooltip="Source Code"
|
|
>
|
|
<i class="gg-code-slash"></i>
|
|
</a>
|
|
</div>
|
|
</section>
|
|
</article>
|
|
</template>
|
|
</section>
|
|
<footer>
|
|
<section
|
|
style="
|
|
display: flex;
|
|
width: 6rem;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin: auto;
|
|
"
|
|
>
|
|
<i class="gg-sun"></i>
|
|
<input
|
|
x-data
|
|
type="checkbox"
|
|
name="changeTheme"
|
|
role="switch"
|
|
x-model="$store.darkMode.on"
|
|
@click="$store.darkMode.toggle()"
|
|
/>
|
|
<i class="gg-moon"></i>
|
|
</section>
|
|
<section style="margin-top: 1rem; opacity: 0.25">
|
|
<template x-if="$store.page == 'tasks'">
|
|
<a
|
|
href="#about"
|
|
@click="$store.page = 'about'; localStorage.setItem('page', 'about')"
|
|
><small>About</small></a
|
|
>
|
|
</template>
|
|
<template x-if="$store.page == 'about'">
|
|
<a
|
|
href="#tasks"
|
|
@click="$store.page = 'tasks'; localStorage.setItem('page', 'tasks')"
|
|
>
|
|
<small>Tasks</small></a
|
|
>
|
|
</template>
|
|
</section>
|
|
</footer>
|
|
</main>
|
|
|
|
<script>
|
|
const icons = {};
|
|
|
|
function addTask(text) {
|
|
Alpine.store("tasks").push({
|
|
message: text,
|
|
checked: false,
|
|
id: Math.random(),
|
|
});
|
|
localStorage.setItem("tasks", JSON.stringify(Alpine.store("tasks")));
|
|
}
|
|
function removeTask(id) {
|
|
const index = Alpine.store("tasks").findIndex((t) => t.id === id);
|
|
if (index > -1) Alpine.store("tasks").splice(index, 1);
|
|
localStorage.setItem("tasks", JSON.stringify(Alpine.store("tasks")));
|
|
}
|
|
function changeTaskState(id, state) {
|
|
const index = Alpine.store("tasks").findIndex((t) => t.id === id);
|
|
if (index > -1) Alpine.store("tasks")[index].checked = state;
|
|
localStorage.setItem("tasks", JSON.stringify(Alpine.store("tasks")));
|
|
}
|
|
|
|
document.addEventListener("alpine:init", () => {
|
|
const tasks = JSON.parse(localStorage.getItem("tasks")) || [
|
|
{
|
|
message: "Create HTML document",
|
|
checked: true,
|
|
id: Math.random(),
|
|
},
|
|
{
|
|
message: "Finish application",
|
|
checked: true,
|
|
id: Math.random(),
|
|
},
|
|
{
|
|
message: "Publish to the world",
|
|
checked: true,
|
|
id: Math.random(),
|
|
},
|
|
{
|
|
message: "Rewrite with AlpineJS Persist",
|
|
checked: false,
|
|
id: Math.random(),
|
|
},
|
|
{
|
|
message: "Add animations?",
|
|
checked: false,
|
|
id: Math.random(),
|
|
},
|
|
];
|
|
Alpine.store("tasks", tasks);
|
|
|
|
Alpine.store("darkMode", {
|
|
init() {
|
|
this.on =
|
|
window.matchMedia("(prefers-color-scheme: dark)").matches ||
|
|
JSON.parse(localStorage.getItem("darkMode"));
|
|
},
|
|
on: false,
|
|
toggle() {
|
|
localStorage.setItem("darkMode", JSON.stringify(!this.on));
|
|
this.on = !this.on;
|
|
},
|
|
});
|
|
|
|
const page = localStorage.getItem("page") || "tasks";
|
|
Alpine.store("page", page);
|
|
|
|
Alpine.store("tools", [
|
|
{
|
|
name: "AlpineJS",
|
|
url: "https://alpinejs.dev",
|
|
icon: "https://alpinejs.dev/favicon.png",
|
|
},
|
|
{
|
|
name: "PicoCSS",
|
|
url: "https://picocss.com",
|
|
icon: "https://picocss.com/img/favicon-32x32.png",
|
|
},
|
|
]);
|
|
});
|
|
</script>
|
|
|
|
<style>
|
|
/* PicoCSS customization */
|
|
|
|
/* Blue-grey Light scheme (Default) */
|
|
/* Can be forced with data-theme="light" */
|
|
[data-theme="light"],
|
|
:root:not([data-theme="dark"]) {
|
|
--primary: #546e7a;
|
|
--primary-hover: #455a64;
|
|
--primary-focus: rgba(84, 110, 122, 0.125);
|
|
--primary-inverse: #fff;
|
|
}
|
|
|
|
/* Blue-grey Dark scheme (Auto) */
|
|
/* Automatically enabled if user has Dark mode enabled */
|
|
@media only screen and (prefers-color-scheme: dark) {
|
|
:root:not([data-theme="light"]) {
|
|
--primary: #546e7a;
|
|
--primary-hover: #607d8b;
|
|
--primary-focus: rgba(84, 110, 122, 0.25);
|
|
--primary-inverse: #fff;
|
|
}
|
|
}
|
|
|
|
/* Blue-grey Dark scheme (Forced) */
|
|
/* Enabled if forced with data-theme="dark" */
|
|
[data-theme="dark"] {
|
|
--primary: #546e7a;
|
|
--primary-hover: #607d8b;
|
|
--primary-focus: rgba(84, 110, 122, 0.25);
|
|
--primary-inverse: #fff;
|
|
}
|
|
|
|
/* Blue-grey (Common styles) */
|
|
:root {
|
|
--form-element-active-border-color: var(--primary);
|
|
--form-element-focus-color: var(--primary-focus);
|
|
--switch-color: var(--primary-inverse);
|
|
--switch-checked-background-color: var(--primary);
|
|
}
|
|
</style>
|
|
</body>
|
|
</html>
|