diff options
Diffstat (limited to 'src/routes/todos')
-rw-r--r-- | src/routes/todos/_api.ts | 22 | ||||
-rw-r--r-- | src/routes/todos/index.svelte | 186 | ||||
-rw-r--r-- | src/routes/todos/index.ts | 52 |
3 files changed, 0 insertions, 260 deletions
diff --git a/src/routes/todos/_api.ts b/src/routes/todos/_api.ts deleted file mode 100644 index f8bcf73..0000000 --- a/src/routes/todos/_api.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - This module is used by the /todos and /todos/[uid] - endpoints to make calls to api.svelte.dev, which stores todos - for each user. The leading underscore indicates that this is - a private module, _not_ an endpoint — visiting /todos/_api - will net you a 404 response. - - (The data on the todo app will expire periodically; no - guarantees are made. Don't use it to organise your life.) -*/ - -const base = 'https://api.svelte.dev'; - -export async function api(request: Request, resource: string, data?: Record<string, unknown>) { - return fetch(`${base}/${resource}`, { - method: request.method, - headers: { - 'content-type': 'application/json' - }, - body: data && JSON.stringify(data) - }); -} diff --git a/src/routes/todos/index.svelte b/src/routes/todos/index.svelte deleted file mode 100644 index e23c1a1..0000000 --- a/src/routes/todos/index.svelte +++ /dev/null @@ -1,186 +0,0 @@ -<script lang="ts"> - import { enhance } from '$lib/form' - import { scale } from 'svelte/transition' - import { flip } from 'svelte/animate' - - type Todo = { - uid: string - created_at: Date - text: string - done: boolean - pending_delete: boolean - } - - export let todos: Todo[] -</script> - -<svelte:head> - <title>Todos</title> -</svelte:head> - -<div class="todos"> - <h1>Todos</h1> - - <form - class="new" - action="/todos" - method="post" - use:enhance={{ - result: async ({ form }) => { - form.reset() - }, - }} - > - <input name="text" aria-label="Add todo" placeholder="+ tap to add a todo" /> - </form> - - {#each todos as todo (todo.uid)} - <div - class="todo" - class:done={todo.done} - transition:scale|local={{ start: 0.7 }} - animate:flip={{ duration: 200 }} - > - <form - action="/todos?_method=PATCH" - method="post" - use:enhance={{ - pending: ({ data }) => { - todo.done = !!data.get('done') - }, - }} - > - <input type="hidden" name="uid" value={todo.uid} /> - <input type="hidden" name="done" value={todo.done ? '' : 'true'} /> - <button class="toggle" aria-label="Mark todo as {todo.done ? 'not done' : 'done'}" /> - </form> - - <form class="text" action="/todos?_method=PATCH" method="post" use:enhance> - <input type="hidden" name="uid" value={todo.uid} /> - <input aria-label="Edit todo" type="text" name="text" value={todo.text} /> - <button class="save" aria-label="Save todo" /> - </form> - - <form - action="/todos?_method=DELETE" - method="post" - use:enhance={{ - pending: () => (todo.pending_delete = true), - }} - > - <input type="hidden" name="uid" value={todo.uid} /> - <button class="delete" aria-label="Delete todo" disabled={todo.pending_delete} /> - </form> - </div> - {/each} -</div> - -<style> - .todos { - width: 100%; - max-width: var(--column-width); - margin: var(--column-margin-top) auto 0 auto; - line-height: 1; - } - - .new { - margin: 0 0 0.5rem 0; - } - - input { - border: 1px solid transparent; - } - - input:focus-visible { - box-shadow: inset 1px 1px 6px rgba(0, 0, 0, 0.1); - border: 1px solid #ff3e00 !important; - outline: none; - } - - .new input { - font-size: 28px; - width: 100%; - padding: 0.5em 1em 0.3em 1em; - box-sizing: border-box; - background: rgba(255, 255, 255, 0.05); - border-radius: 8px; - text-align: center; - } - - .todo { - display: grid; - grid-template-columns: 2rem 1fr 2rem; - grid-gap: 0.5rem; - align-items: center; - margin: 0 0 0.5rem 0; - padding: 0.5rem; - background-color: white; - border-radius: 8px; - filter: drop-shadow(2px 4px 6px rgba(0, 0, 0, 0.1)); - transform: translate(-1px, -1px); - transition: filter 0.2s, transform 0.2s; - } - - .done { - transform: none; - opacity: 0.4; - filter: drop-shadow(0px 0px 1px rgba(0, 0, 0, 0.1)); - } - - form.text { - position: relative; - display: flex; - align-items: center; - flex: 1; - } - - .todo input { - flex: 1; - padding: 0.5em 2em 0.5em 0.8em; - border-radius: 3px; - } - - .todo button { - width: 2em; - height: 2em; - border: none; - background-color: transparent; - background-position: 50% 50%; - background-repeat: no-repeat; - } - - button.toggle { - border: 1px solid rgba(0, 0, 0, 0.2); - border-radius: 50%; - box-sizing: border-box; - background-size: 1em auto; - } - - .done .toggle { - background-image: url("data:image/svg+xml,%3Csvg width='22' height='16' viewBox='0 0 22 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M20.5 1.5L7.4375 14.5L1.5 8.5909' stroke='%23676778' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); - } - - .delete { - background-image: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M4.5 5V22H19.5V5H4.5Z' fill='%23676778' stroke='%23676778' stroke-width='1.5' stroke-linejoin='round'/%3E%3Cpath d='M10 10V16.5' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M14 10V16.5' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M2 5H22' stroke='%23676778' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M8 5L9.6445 2H14.3885L16 5H8Z' fill='%23676778' stroke='%23676778' stroke-width='1.5' stroke-linejoin='round'/%3E%3C/svg%3E%0A"); - opacity: 0.2; - } - - .delete:hover, - .delete:focus { - transition: opacity 0.2s; - opacity: 1; - } - - .save { - position: absolute; - right: 0; - opacity: 0; - background-image: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M20.5 2H3.5C2.67158 2 2 2.67157 2 3.5V20.5C2 21.3284 2.67158 22 3.5 22H20.5C21.3284 22 22 21.3284 22 20.5V3.5C22 2.67157 21.3284 2 20.5 2Z' fill='%23676778' stroke='%23676778' stroke-width='1.5' stroke-linejoin='round'/%3E%3Cpath d='M17 2V11H7.5V2H17Z' fill='white' stroke='white' stroke-width='1.5' stroke-linejoin='round'/%3E%3Cpath d='M13.5 5.5V7.5' stroke='%23676778' stroke-width='1.5' stroke-linecap='round'/%3E%3Cpath d='M5.99844 2H18.4992' stroke='%23676778' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E%0A"); - } - - .todo input:focus + .save, - .save:focus { - transition: opacity 0.2s; - opacity: 1; - } -</style> diff --git a/src/routes/todos/index.ts b/src/routes/todos/index.ts deleted file mode 100644 index 129b60a..0000000 --- a/src/routes/todos/index.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { api } from './_api'; -import type { RequestHandler } from '@sveltejs/kit'; - -export const get: RequestHandler = async ({ request, locals }) => { - // locals.userid comes from src/hooks.js - const response = await api(request, `todos/${locals.userid}`); - - if (response.status === 404) { - // user hasn't created a todo list. - // start with an empty array - return { - body: { - todos: [] - } - }; - } - - if (response.ok) { - return { - body: { - todos: await response.json() - } - }; - } - - return { - status: response.status - }; -}; - -export const post: RequestHandler = async ({ request, locals }) => { - const form = await request.formData(); - - return api(request, `todos/${locals.userid}`, { - text: form.get('text') - }); -}; - -export const patch: RequestHandler = async ({ request, locals }) => { - const form = await request.formData(); - - return api(request, `todos/${locals.userid}/${form.get('uid')}`, { - text: form.has('text') ? form.get('text') : undefined, - done: form.has('done') ? !!form.get('done') : undefined - }); -}; - -export const del: RequestHandler = async ({ request, locals }) => { - const form = await request.formData(); - - return api(request, `todos/${locals.userid}/${form.get('uid')}`); -}; |