--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <title>todo</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <link rel="stylesheet" type="text/css" href="./style.css">
+ <script src="./archived.js" defer></script>
+ </head>
+ <body>
+ <header>
+ <div>
+ <h3>todo</h3>
+ <nav>
+ <a href="./index.html">home</a>
+ <a href="./archived.html">archived</a>
+ <a href="./settings.html">settings</a>
+ </nav>
+ </div>
+ <hr>
+ </header>
+ <div id="main">
+ <div>
+ <div id="archived"></div>
+ </div>
+ </div>
+ <footer></footer>
+ </body>
+</html>
--- /dev/null
+var todos = {
+ uncompleted: [],
+ completed: []
+};
+var archived = [];
+var theme = {
+ colorScheme: "default",
+ css: ""
+};
+
+if (localStorage.getItem("todos")) {
+
+ todos = JSON.parse(localStorage.getItem("todos"));
+}
+
+if (localStorage.getItem("archived")) {
+
+ archived = JSON.parse(localStorage.getItem("archived"));
+}
+
+if (localStorage.getItem("theme")) {
+
+ theme = JSON.parse(localStorage.getItem("theme"));
+}
+
+const backupData = () => {
+
+ localStorage.setItem("todos", JSON.stringify(todos));
+ localStorage.setItem("archived", JSON.stringify(archived));
+ localStorage.setItem("theme", JSON.stringify(theme));
+}
+
+const archivedElement = document.getElementById("archived");
+
+const todoElement = (item, index) => {
+
+ return `
+ <todo id="todo-archived-${index}">
+ <div class="data">
+ <textarea id="todo-archived-${index}-description">${item.description}</textarea>
+ <div>
+ <p id="todo-archived-${index}-restore">restore</p>
+ <p id="todo-archived-${index}-delete">delete</p>
+ </div>
+ </div>
+ </todo>
+ `;
+}
+
+const resizeTextHeights = () => {
+
+ for (const element of document.querySelectorAll("todo textarea")) {
+
+ element.style.height = "0";
+ element.style.height = element.scrollHeight + "px";
+ }
+}
+
+const applyListeners = () => {
+
+ resizeTextHeights();
+
+ for (var i = 0; i < archived.length; i++) {
+
+ let index = i; // fixes scoping issue - what a disaster
+
+ document.getElementById(`todo-archived-${i}-description`).addEventListener("input", (event) => {
+
+ event.target.style.height = "0";
+ event.target.style.height = event.target.scrollHeight + "px";
+
+ archived[index].description = event.target.value;
+
+ backupData();
+ });
+
+ document.getElementById(`todo-archived-${i}-restore`).addEventListener("click", (event) => {
+
+ todos.uncompleted = [
+ archived[index]
+ ].concat(todos.uncompleted);
+ archived.splice(index, 1);
+
+ hydrateTodos();
+ backupData();
+ });
+
+ document.getElementById(`todo-archived-${i}-delete`).addEventListener("click", (event) => {
+
+ archived.splice(index, 1);
+
+ hydrateTodos();
+ backupData();
+ });
+ }
+}
+
+const hydrateTodos = () => {
+
+ archivedElement.innerHTML = archived
+ .map((x, index) => todoElement(x, index))
+ .join("");
+
+ applyListeners();
+};
+
+hydrateTodos();
<title>todo</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="./style.css">
- <script src="./script.js" defer></script>
+ <script src="./index.js" defer></script>
</head>
<body>
<header>
completed: []
};
var archived = [];
+var theme = {
+ colorScheme: "default",
+ css: ""
+};
if (localStorage.getItem("todos")) {
archived = JSON.parse(localStorage.getItem("archived"));
}
-// TODO: URL sharing data override here...
+if (localStorage.getItem("theme")) {
+
+ theme = JSON.parse(localStorage.getItem("theme"));
+}
+
+const backupData = () => {
+
+ localStorage.setItem("todos", JSON.stringify(todos));
+ localStorage.setItem("archived", JSON.stringify(archived));
+ localStorage.setItem("theme", JSON.stringify(theme));
+}
+
+if (window.location.hash.length > 0) {
+
+ try {
+
+ ({todos, archived, theme} = JSON.parse(atob(window.location.hash.substring(1)))); // using parenthesis around this is required because we're doing object literal destructuring without a declaration
+
+ backupData();
+ } catch (e) {
+
+ console.error(e);
+
+ window.alert("The import URL provided was not valid!");
+ }
+}
document.getElementById("createTodo").addEventListener("click", () => {
].concat(todos.uncompleted);
hydrateUncompleted();
+ applyListeners();
backupData();
});
resizeTextHeights();
});
-const backupData = () => {
-
- localStorage.setItem("todos", JSON.stringify(todos));
- localStorage.setItem("archived", JSON.stringify(archived));
-}
-
const todosElement = document.getElementById("todos");
const completedElement = document.getElementById("todos-completed");
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <title>todo</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <link rel="stylesheet" type="text/css" href="./style.css">
+ <script src="./settings.js" defer></script>
+ </head>
+ <body>
+ <header>
+ <div>
+ <h3>todo</h3>
+ <nav>
+ <a href="./index.html">home</a>
+ <a href="./archived.html">archived</a>
+ <a href="./settings.html">settings</a>
+ </nav>
+ </div>
+ <hr>
+ </header>
+ <div id="main">
+ <div>
+ <button id="exportData">
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path fill="currentColor" d="M6 22q-.825 0-1.412-.587T4 20V10q0-.825.588-1.412T6 8h2q.425 0 .713.288T9 9t-.288.713T8 10H6v10h12V10h-2q-.425 0-.712-.288T15 9t.288-.712T16 8h2q.825 0 1.413.588T20 10v10q0 .825-.587 1.413T18 22zm5.288-6.288Q11 15.425 11 15V4.825l-.9.9q-.3.3-.7.288T8.7 5.7q-.275-.3-.287-.7t.287-.7l2.6-2.6q.15-.15.325-.212T12 1.425t.375.063t.325.212l2.6 2.6q.275.275.275.688T15.3 5.7q-.3.3-.712.3t-.713-.3L13 4.825V15q0 .425-.288.713T12 16t-.712-.288" />
+ </svg>
+ <p id="exportData_text">export as link</p>
+ </button>
+ <h3>theming</h3>
+ <h3>override css</h3>
+ <textarea id="override_css"></textarea>
+ </div>
+ </div>
+ <footer></footer>
+ </body>
+</html>
--- /dev/null
+var todos = {
+ uncompleted: [],
+ completed: []
+};
+var archived = [];
+var theme = {
+ colorScheme: "default",
+ css: ""
+};
+
+if (localStorage.getItem("todos")) {
+
+ todos = JSON.parse(localStorage.getItem("todos"));
+}
+
+if (localStorage.getItem("archived")) {
+
+ archived = JSON.parse(localStorage.getItem("archived"));
+}
+
+if (localStorage.getItem("theme")) {
+
+ theme = JSON.parse(localStorage.getItem("theme"));
+}
+
+const exportDataText = document.getElementById("exportData_text");
+
+document.getElementById("exportData").addEventListener("click", async (event) => {
+
+ const dataExport = {
+ todos: todos,
+ archived: archived,
+ theme: theme
+ };
+
+ const exportURL = window.location.href.replace("settings.html", "") + "index.html#" + btoa(JSON.stringify(dataExport));
+
+ if (!window.navigator.userAgent.includes("Mozilla")) {
+
+ try {
+
+ await navigator.share({
+ title: "todo data export",
+ url: exportURL
+ });
+
+ exportDataText.innerHTML = "shared!";
+ setTimeout(() => {
+ exportDataText.innerHTML = "export as link";
+ }, 2000);
+ } catch (e) {
+
+ console.error(e);
+ }
+ } else {
+
+ navigator.clipboard.writeText(exportURL);
+
+ exportDataText.innerHTML = "link copied!";
+ setTimeout(() => {
+ exportDataText.innerHTML = "export as link";
+ }, 2000);
+ }
+});
+
+const override_css = document.getElementById("override_css");
+
+const update = () => {
+
+}
h3 {
margin: 0;
- user-select: none;
}
header {
display: flex;
width: 100%;
justify-content: center;
- & > div, #todos, #todos-completed {
+ & > div, #todos, #todos-completed, #archived {
width: 400px;
display: flex;
flex-direction: column;
justify-content: right;
gap: 0.5rem;
& > p {
- user-select: none;
+ /* user-select: none; */
font-size: 0.9rem;
margin: 0;
cursor: pointer;
cursor: pointer;
}
+#override_css {
+ resize: vertical;
+}
+
a, a:visited, a:active {
- user-select: none;
color: var(--text-primary);
background-color: var(--background-secondary);
text-decoration: none;