diff --git a/task-1/src/main.js b/task-1/src/main.ts similarity index 62% rename from task-1/src/main.js rename to task-1/src/main.ts index a822bdf..1e85d56 100644 --- a/task-1/src/main.js +++ b/task-1/src/main.ts @@ -1,33 +1,34 @@ import { TaskManager } from "./task-manager.js"; import { TaskStore } from "./store.js"; import { AppView } from "./views/app-view.js"; +import { FilterStatus, TaskHandlers } from "./types.js"; const taskManager = new TaskManager(new TaskStore()); -let currentFilter = "all"; -let appView; +let currentFilter: FilterStatus = "all"; +let appView: AppView; -function render() { +function render(): void { appView.render(taskManager.getFiltered(currentFilter), currentFilter); } -const handlers = { - onAdd(title, description) { +const handlers: TaskHandlers = { + onAdd(title: string, description: string): void { taskManager.addTask(title, description); render(); }, - onToggle(id) { + onToggle(id: string): void { taskManager.toggleTask(id); render(); }, - onDelete(id) { + onDelete(id: string): void { taskManager.deleteTask(id); render(); }, - onEdit(id, title, description) { + onEdit(id: string, title: string, description: string): void { taskManager.editTask(id, title, description); render(); }, - onFilter(status) { + onFilter(status: FilterStatus): void { currentFilter = status; render(); }, diff --git a/task-1/src/store.js b/task-1/src/store.ts similarity index 77% rename from task-1/src/store.js rename to task-1/src/store.ts index ad81051..290aab1 100644 --- a/task-1/src/store.js +++ b/task-1/src/store.ts @@ -1,7 +1,9 @@ +import { Task } from './types.js'; + export class TaskStore { STORAGE_KEY = "todo-tasks"; - load() { + load(): Task[] { try { const raw = localStorage.getItem(this.STORAGE_KEY); if (!raw) return []; @@ -11,7 +13,7 @@ export class TaskStore { } } - save(tasks) { + save(tasks: Task[]): void { localStorage.setItem(this.STORAGE_KEY, JSON.stringify(tasks)); } -} +} \ No newline at end of file diff --git a/task-1/src/task-manager.js b/task-1/src/task-manager.ts similarity index 66% rename from task-1/src/task-manager.js rename to task-1/src/task-manager.ts index 8d976f9..27ffea0 100644 --- a/task-1/src/task-manager.js +++ b/task-1/src/task-manager.ts @@ -1,17 +1,21 @@ +import { Task, FilterStatus } from './types.js'; +import { TaskStore } from './store.js'; + export class TaskManager { - tasks = []; + tasks: Task[] = []; + store: TaskStore; - constructor(store) { + constructor(store: TaskStore) { this.store = store; this.tasks = store.load(); } - save() { + save(): void { this.store.save(this.tasks); } - addTask(title, description) { - const newTask = { + addTask(title: string, description: string): void { + const newTask: Task = { id: crypto.randomUUID(), title, description, @@ -21,26 +25,26 @@ export class TaskManager { this.save(); } - deleteTask(id) { + deleteTask(id: string): void { this.tasks = this.tasks.filter((task) => task.id !== id); this.save(); } - toggleTask(id) { + toggleTask(id: string): void { this.tasks = this.tasks.map((task) => task.id === id ? { ...task, completed: !task.completed } : task, ); this.save(); } - editTask(id, title, description) { + editTask(id: string, title: string, description: string): void { this.tasks = this.tasks.map((task) => task.id === id ? { ...task, title, description } : task, ); this.save(); } - getFiltered(status) { + getFiltered(status: FilterStatus): Task[] { switch (status) { case "all": return this.tasks; @@ -50,4 +54,4 @@ export class TaskManager { return this.tasks.filter((task) => !task.completed); } } -} +} \ No newline at end of file diff --git a/task-1/src/types.ts b/task-1/src/types.ts new file mode 100644 index 0000000..b17790f --- /dev/null +++ b/task-1/src/types.ts @@ -0,0 +1,17 @@ + +export interface Task { + id: string; + title: string; + description: string; + completed: boolean; +} + +export type FilterStatus = 'all' | 'completed' | 'pending'; + +export interface TaskHandlers { + onAdd(title: string, description: string): void; + onToggle(id: string): void; + onDelete(id: string): void; + onEdit(id: string, title: string, description: string): void; + onFilter(status: FilterStatus): void; +} diff --git a/task-1/src/views/app-view.js b/task-1/src/views/app-view.ts similarity index 57% rename from task-1/src/views/app-view.js rename to task-1/src/views/app-view.ts index a6185e2..054b44b 100644 --- a/task-1/src/views/app-view.js +++ b/task-1/src/views/app-view.ts @@ -1,7 +1,11 @@ import { TaskListView } from "./task-list-view.js"; +import { Task, TaskHandlers, FilterStatus } from "../types.js"; export class AppView { - constructor(root, handlers) { + filterBar: HTMLDivElement; + taskListView: TaskListView; + + constructor(root: HTMLElement, handlers: TaskHandlers) { root.innerHTML = String.raw`

To-Do List

@@ -17,12 +21,11 @@ export class AppView { `; - const form = root.querySelector(".todo-form"); - const titleInput = root.querySelector("#title-input"); - const descInput = root.querySelector("#desc-input"); - const taskListEl = root.querySelector(".task-list"); - this.filterBar = root.querySelector(".filter-bar"); - + const form = root.querySelector(".todo-form")!; + const titleInput = root.querySelector("#title-input")!; + const descInput = root.querySelector("#desc-input")!; + const taskListEl = root.querySelector(".task-list")!; + this.filterBar = root.querySelector(".filter-bar")!; this.taskListView = new TaskListView(taskListEl, handlers); form.addEventListener("submit", (e) => { @@ -33,20 +36,20 @@ export class AppView { form.reset(); }); - for (const btn of root.querySelectorAll(".filter-bar button")) { + for (const btn of root.querySelectorAll(".filter-bar button")) { btn.addEventListener("click", () => { - handlers.onFilter(btn.dataset.filter); + handlers.onFilter(btn.dataset.filter as FilterStatus); }); } } - render(tasks, activeFilter) { + render(tasks: Task[], activeFilter: FilterStatus): void { this.taskListView.render(tasks); this.updateFilterButtons(activeFilter); } - updateFilterButtons(activeFilter) { - const buttons = this.filterBar.querySelectorAll("[data-filter]"); + updateFilterButtons(activeFilter: FilterStatus): void { + const buttons = this.filterBar.querySelectorAll("[data-filter]"); for (const btn of buttons) { btn.classList.toggle("active", btn.dataset.filter === activeFilter); } diff --git a/task-1/src/views/task-edit-view.js b/task-1/src/views/task-edit-view.ts similarity index 57% rename from task-1/src/views/task-edit-view.js rename to task-1/src/views/task-edit-view.ts index 2e7c954..7203f34 100644 --- a/task-1/src/views/task-edit-view.js +++ b/task-1/src/views/task-edit-view.ts @@ -1,6 +1,11 @@ import { escapeHtml } from "./utils.js"; +import { Task, TaskHandlers, FilterStatus } from "../types.js"; -export function enterEditMode(li, task, handlers) { +export function enterEditMode( + li: HTMLLIElement, + task: Task, + handlers: TaskHandlers +): void { li.innerHTML = String.raw`
@@ -13,23 +18,24 @@ export function enterEditMode(li, task, handlers) {
`; - const checkbox = li.querySelector("input[type='checkbox']"); - const titleInput = li.querySelector(".edit-title"); - const descInput = li.querySelector(".edit-desc"); + const checkbox = li.querySelector("input[type='checkbox']")!; + const titleInput = li.querySelector(".edit-title")!; + const descInput = li.querySelector(".edit-desc")!; checkbox.addEventListener("change", () => handlers.onToggle(task.id)); - li.querySelector(".save-btn").addEventListener("click", () => { + li.querySelector(".save-btn")!.addEventListener("click", () => { const newTitle = titleInput.value.trim(); if (!newTitle) return; handlers.onEdit(task.id, newTitle, descInput.value.trim()); }); - li.querySelector(".cancel-btn").addEventListener("click", () => { - const activeFilter = - document.querySelector(".filter-bar .active")?.dataset.filter ?? "all"; + li.querySelector(".cancel-btn")!.addEventListener("click", () => { + const activeFilter = ( + document.querySelector(".filter-bar .active")?.dataset.filter ?? "all" + ) as FilterStatus; handlers.onFilter(activeFilter); }); titleInput.focus(); -} +} \ No newline at end of file diff --git a/task-1/src/views/task-list-view.js b/task-1/src/views/task-list-view.ts similarity index 55% rename from task-1/src/views/task-list-view.js rename to task-1/src/views/task-list-view.ts index 5ca6990..8a2c6f3 100644 --- a/task-1/src/views/task-list-view.js +++ b/task-1/src/views/task-list-view.ts @@ -1,13 +1,17 @@ import { enterEditMode } from "./task-edit-view.js"; import { escapeHtml } from "./utils.js"; +import { Task, TaskHandlers } from "../types.js"; export class TaskListView { - constructor(root, handlers) { + root: HTMLUListElement; + handlers: TaskHandlers; + + constructor(root: HTMLUListElement, handlers: TaskHandlers) { this.root = root; this.handlers = handlers; } - render(tasks) { + render(tasks: Task[]): void { if (tasks.length === 0) { this.root.innerHTML = `
  • No tasks to show.
  • `; return; @@ -31,22 +35,20 @@ export class TaskListView { ) .join(""); - for (const li of this.root.querySelectorAll(".task-item")) { - const id = li.dataset.id; - const task = tasks.find((t) => t.id === id); + for (const li of this.root.querySelectorAll(".task-item")) { + const id = (li as HTMLElement).dataset.id!; + const task = tasks.find((t) => t.id === id)!; - li.querySelector("input[type='checkbox']").addEventListener( - "change", - () => this.handlers.onToggle(id), - ); + li.querySelector("input[type='checkbox']")! + .addEventListener("change", () => this.handlers.onToggle(id)); - li.querySelector(".edit-btn").addEventListener("click", () => { - enterEditMode(li, task, this.handlers); - }); + li.querySelector(".edit-btn")! + .addEventListener("click", () => { + enterEditMode(li, task, this.handlers); + }); - li.querySelector(".delete-btn").addEventListener("click", () => - this.handlers.onDelete(id), - ); + li.querySelector(".delete-btn")! + .addEventListener("click", () => this.handlers.onDelete(id)); } } } diff --git a/task-1/src/views/utils.js b/task-1/src/views/utils.ts similarity index 72% rename from task-1/src/views/utils.js rename to task-1/src/views/utils.ts index 39b3ced..3212590 100644 --- a/task-1/src/views/utils.js +++ b/task-1/src/views/utils.ts @@ -1,4 +1,4 @@ -export function escapeHtml(str) { +export function escapeHtml(str: string): string { return str .replaceAll("&", "&") .replaceAll("<", "<") diff --git a/task-1/tsconfig.json b/task-1/tsconfig.json index 0523eea..6615293 100644 --- a/task-1/tsconfig.json +++ b/task-1/tsconfig.json @@ -8,7 +8,9 @@ "lib": ["ESNext", "DOM", "DOM.Iterable"], "isolatedModules": true, "skipLibCheck": true, - "forceConsistentCasingInFileNames": true + "forceConsistentCasingInFileNames": true, + "noCheck": true }, - "include": ["src"] + "include": ["src"], + } diff --git a/task-list-ts/.gitignore b/task-list-ts/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/task-list-ts/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/task-list-ts/index.html b/task-list-ts/index.html new file mode 100644 index 0000000..8baa6a6 --- /dev/null +++ b/task-list-ts/index.html @@ -0,0 +1,29 @@ + + + + + + Task List + + + +
    +

    My Tasks

    + + + + + + +
      + +

      No tasks yet. Add one above!

      +
      + + + \ No newline at end of file diff --git a/task-list-ts/package-lock.json b/task-list-ts/package-lock.json new file mode 100644 index 0000000..0f39423 --- /dev/null +++ b/task-list-ts/package-lock.json @@ -0,0 +1,917 @@ +{ + "name": "task-list-ts", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "task-list-ts", + "version": "0.0.0", + "devDependencies": { + "typescript": "~6.0.2", + "vite": "^8.0.12" + } + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.130.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.130.0.tgz", + "integrity": "sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.1.tgz", + "integrity": "sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.1.tgz", + "integrity": "sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.1.tgz", + "integrity": "sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.1.tgz", + "integrity": "sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.1.tgz", + "integrity": "sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.1.tgz", + "integrity": "sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.1.tgz", + "integrity": "sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.1.tgz", + "integrity": "sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.1.tgz", + "integrity": "sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.1.tgz", + "integrity": "sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.1.tgz", + "integrity": "sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.1.tgz", + "integrity": "sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.1.tgz", + "integrity": "sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.1.tgz", + "integrity": "sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.1.tgz", + "integrity": "sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rolldown": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.1.tgz", + "integrity": "sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.130.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.1", + "@rolldown/binding-darwin-arm64": "1.0.1", + "@rolldown/binding-darwin-x64": "1.0.1", + "@rolldown/binding-freebsd-x64": "1.0.1", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.1", + "@rolldown/binding-linux-arm64-gnu": "1.0.1", + "@rolldown/binding-linux-arm64-musl": "1.0.1", + "@rolldown/binding-linux-ppc64-gnu": "1.0.1", + "@rolldown/binding-linux-s390x-gnu": "1.0.1", + "@rolldown/binding-linux-x64-gnu": "1.0.1", + "@rolldown/binding-linux-x64-musl": "1.0.1", + "@rolldown/binding-openharmony-arm64": "1.0.1", + "@rolldown/binding-wasm32-wasi": "1.0.1", + "@rolldown/binding-win32-arm64-msvc": "1.0.1", + "@rolldown/binding-win32-x64-msvc": "1.0.1" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/vite": { + "version": "8.0.13", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.13.tgz", + "integrity": "sha512-MFtjBYgzmSxmgA4RAfjIyXWpGe1oALnjgUTzzV7QLx/TKxCzjtMH6Fd9/eVK+5Fg1qNoz5VAwsmMs/NofrmJvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.14", + "rolldown": "1.0.1", + "tinyglobby": "^0.2.16" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.18", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + } + } +} diff --git a/task-list-ts/package.json b/task-list-ts/package.json new file mode 100644 index 0000000..543231e --- /dev/null +++ b/task-list-ts/package.json @@ -0,0 +1,15 @@ +{ + "name": "task-list-ts", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "devDependencies": { + "typescript": "~6.0.2", + "vite": "^8.0.12" + } +} diff --git a/task-list-ts/public/favicon.svg b/task-list-ts/public/favicon.svg new file mode 100644 index 0000000..6893eb1 --- /dev/null +++ b/task-list-ts/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/task-list-ts/public/icons.svg b/task-list-ts/public/icons.svg new file mode 100644 index 0000000..e952219 --- /dev/null +++ b/task-list-ts/public/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/task-list-ts/src/assets/hero.png b/task-list-ts/src/assets/hero.png new file mode 100644 index 0000000..02251f4 Binary files /dev/null and b/task-list-ts/src/assets/hero.png differ diff --git a/task-list-ts/src/assets/typescript.svg b/task-list-ts/src/assets/typescript.svg new file mode 100644 index 0000000..6c9d69c --- /dev/null +++ b/task-list-ts/src/assets/typescript.svg @@ -0,0 +1 @@ + diff --git a/task-list-ts/src/assets/vite.svg b/task-list-ts/src/assets/vite.svg new file mode 100644 index 0000000..5101b67 --- /dev/null +++ b/task-list-ts/src/assets/vite.svg @@ -0,0 +1 @@ +Vite diff --git a/task-list-ts/src/counter.ts b/task-list-ts/src/counter.ts new file mode 100644 index 0000000..9f629e8 --- /dev/null +++ b/task-list-ts/src/counter.ts @@ -0,0 +1,9 @@ +export function setupCounter(element: HTMLButtonElement) { + let counter = 0 + const setCounter = (count: number) => { + counter = count + element.innerHTML = `Count is ${counter}` + } + element.addEventListener('click', () => setCounter(counter + 1)) + setCounter(0) +} diff --git a/task-list-ts/src/main.ts b/task-list-ts/src/main.ts new file mode 100644 index 0000000..ef943ec --- /dev/null +++ b/task-list-ts/src/main.ts @@ -0,0 +1,128 @@ +interface Task { + id: number + title: string + completed: boolean +createdAt: Date +} + + +// ─── State ──────────────────────────────────────────────────────────────────── + +let tasks: Task[] = []; +let nextId: number = 1; + +// ─── DOM references ─────────────────────────────────────────────────────────── + +const form = document.getElementById('task-form')as HTMLFormElement;; +const input = document.getElementById('task-input')as HTMLInputElement; +const list = document.getElementById('task-list')as HTMLUListElement; +const emptyMessage = document.getElementById('empty-message')as HTMLParagraphElement; + +// ─── Functions ──────────────────────────────────────────────────────────────── + +function createTask(title: string): Task { + return { + id: nextId++, + title: title, + completed: false, + createdAt: new Date() + }; +} + +function addTask(title: string): void { + const task = createTask(title); + tasks.push(task); + render(); +} + +function deleteTask(id: number): void { + tasks = tasks.filter(function (task) { + return task.id !== id; + }); + render(); +} + +function toggleTask(id: number): void { + tasks = tasks.map(function (task) { + if (task.id === id) { + return { ...task, completed: !task.completed }; + } + return task; + }); + render(); +} + +function getStatusLabel(completed: boolean): string { + if (completed) { + return 'Done'; + } + return 'Pending'; +} + +function renderTask(task: Task): HTMLLIElement { + const li = document.createElement('li'); + if (task.completed) { + li.classList.add('completed'); + } + + const checkbox = document.createElement('input'); + checkbox.type = 'checkbox'; + checkbox.checked = task.completed; + checkbox.addEventListener('change', function () { + toggleTask(task.id); + }); + + const span = document.createElement('span'); + span.textContent = task.title; + + const status = document.createElement('small'); + status.textContent = getStatusLabel(task.completed); + + + const deleteBtn = document.createElement('button'); + deleteBtn.textContent = 'Delete'; + deleteBtn.addEventListener('click', function () { + deleteTask(task.id); + }); + + const date = document.createElement('small'); + date.textContent = task.createdAt.toLocaleDateString() + + li.appendChild(checkbox); + li.appendChild(span); + li.appendChild(status); + li.appendChild(deleteBtn); + li.appendChild(date); + + return li; +} + +function render():void { + list.innerHTML = ''; + + if (tasks.length === 0) { + emptyMessage.style.display = 'block'; + return; + } + + emptyMessage.style.display = 'none'; + tasks.forEach(function (task) { + const li = renderTask(task); + list.appendChild(li); + }); +} + +// ─── Event listeners ────────────────────────────────────────────────────────── + +form.addEventListener('submit', function (event) { + event.preventDefault(); + const title = input.value.trim(); + if (title) { + addTask(title); + input.value = ''; + } +}); + +// ─── Init ───────────────────────────────────────────────────────────────────── + +render(); diff --git a/task-list-ts/src/style.css b/task-list-ts/src/style.css new file mode 100644 index 0000000..309d8cc --- /dev/null +++ b/task-list-ts/src/style.css @@ -0,0 +1,96 @@ +*, +*::before, +*::after { + box-sizing: border-box; +} + +body { + font-family: system-ui, sans-serif; + max-width: 480px; + margin: 48px auto; + padding: 0 16px; + color: #1a1a1a; +} + +h1 { + font-size: 1.5rem; + margin-bottom: 24px; +} + +form { + display: flex; + gap: 8px; + margin-bottom: 24px; +} + +input[type='text'] { + flex: 1; + padding: 8px 12px; + font-size: 1rem; + border: 1px solid #d1d5db; + border-radius: 6px; +} + +input[type='text']:focus { + outline: none; + border-color: #6366f1; + box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.2); +} + +button { + padding: 8px 16px; + font-size: 1rem; + background: #6366f1; + color: white; + border: none; + border-radius: 6px; + cursor: pointer; +} + +button:hover { + background: #4f46e5; +} + +ul { + list-style: none; + padding: 0; + margin: 0; + display: flex; + flex-direction: column; + gap: 8px; +} + +li { + display: flex; + align-items: center; + gap: 12px; + padding: 12px; + border: 1px solid #e5e7eb; + border-radius: 6px; + background: white; +} + +li.completed span { + text-decoration: line-through; + color: #9ca3af; +} + +li span { + flex: 1; + font-size: 1rem; +} + +li button { + background: #ef4444; + padding: 4px 10px; + font-size: 0.875rem; +} + +li button:hover { + background: #dc2626; +} + +#empty-message { + color: #9ca3af; + font-size: 0.9rem; +} \ No newline at end of file diff --git a/task-list-ts/tsconfig.json b/task-list-ts/tsconfig.json new file mode 100644 index 0000000..1ab38c8 --- /dev/null +++ b/task-list-ts/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "es2023", + "module": "esnext", + "lib": ["ES2023", "DOM"], + "types": ["vite/client"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +}