aboutsummaryrefslogtreecommitdiff
path: root/backup
diff options
context:
space:
mode:
authorFuwn <[email protected]>2022-09-21 18:29:52 -0700
committerFuwn <[email protected]>2022-09-21 18:29:52 -0700
commit7051213e0fdbd47ab1c0e6e8a67bda4f9cfba323 (patch)
treea0a59d1bf0b800635eb988a8c030cc0b0ed7c7fe /backup
downloadcapybara-markets-7051213e0fdbd47ab1c0e6e8a67bda4f9cfba323.tar.xz
capybara-markets-7051213e0fdbd47ab1c0e6e8a67bda4f9cfba323.zip
feat: initial commit
Diffstat (limited to 'backup')
-rw-r--r--backup/todos/+page.server.ts61
-rw-r--r--backup/todos/+page.svelte183
-rw-r--r--backup/todos/api.ts20
3 files changed, 264 insertions, 0 deletions
diff --git a/backup/todos/+page.server.ts b/backup/todos/+page.server.ts
new file mode 100644
index 0000000..6444b59
--- /dev/null
+++ b/backup/todos/+page.server.ts
@@ -0,0 +1,61 @@
+import { error } from '@sveltejs/kit';
+import { api } from './api';
+import type { PageServerLoad, Actions } from './$types';
+
+type Todo = {
+ uid: string;
+ created_at: Date;
+ text: string;
+ done: boolean;
+ pending_delete: boolean;
+};
+
+export const load: PageServerLoad = async ({ locals }) => {
+ // locals.userid comes from src/hooks.js
+ const response = await api('GET', `todos/${locals.userid}`);
+
+ if (response.status === 404) {
+ // user hasn't created a todo list.
+ // start with an empty array
+ return {
+ todos: [] as Todo[]
+ };
+ }
+
+ if (response.status === 200) {
+ return {
+ todos: (await response.json()) as Todo[]
+ };
+ }
+
+ throw error(response.status);
+};
+
+export const actions: Actions = {
+ add: async ({ request, locals }) => {
+ const form = await request.formData();
+
+ await api('POST', `todos/${locals.userid}`, {
+ text: form.get('text')
+ });
+ },
+ edit: async ({ request, locals }) => {
+ const form = await request.formData();
+
+ await api('PATCH', `todos/${locals.userid}/${form.get('uid')}`, {
+ text: form.get('text')
+ });
+ },
+ toggle: async ({ request, locals }) => {
+ const form = await request.formData();
+
+ await api('PATCH', `todos/${locals.userid}/${form.get('uid')}`, {
+ done: !!form.get('done')
+ });
+ },
+ delete: async ({ request, locals }) => {
+ const form = await request.formData();
+
+ await api('DELETE', `todos/${locals.userid}/${form.get('uid')}`);
+ }
+};
diff --git a/backup/todos/+page.svelte b/backup/todos/+page.svelte
new file mode 100644
index 0000000..17549a6
--- /dev/null
+++ b/backup/todos/+page.svelte
@@ -0,0 +1,183 @@
+<script lang="ts">
+ import { enhance } from '$app/forms';
+ import { invalidateAll } from '$app/navigation';
+ import { scale } from 'svelte/transition';
+ import { flip } from 'svelte/animate';
+ import type { PageData } from '../../.svelte-kit/types/src/routes/todos/$types';
+
+ export let data: PageData;
+ $: todos = data.todos;
+</script>
+
+<svelte:head>
+ <title>Todos</title>
+ <meta name="description" content="A todo list app" />
+</svelte:head>
+
+<div class="todos">
+ <h1>Todos</h1>
+
+ <form
+ class="new"
+ action="/todos?/add"
+ method="post"
+ use:enhance={() => {
+ return ({ form, result }) => {
+ if (result.type === 'success') {
+ form.reset();
+ invalidateAll();
+ }
+ };
+ }}
+ >
+ <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?/toggle"
+ method="post"
+ use:enhance={({ 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?/edit" 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?/delete"
+ method="post"
+ use:enhance={() => {
+ 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(0 0 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/backup/todos/api.ts b/backup/todos/api.ts
new file mode 100644
index 0000000..8344488
--- /dev/null
+++ b/backup/todos/api.ts
@@ -0,0 +1,20 @@
+/*
+ This module is used by the /todos endpoint to
+ make calls to api.svelte.dev, which stores todos
+ for each user.
+
+ (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 function api(method: string, resource: string, data?: Record<string, unknown>) {
+ return fetch(`${base}/${resource}`, {
+ method,
+ headers: {
+ 'content-type': 'application/json'
+ },
+ body: data && JSON.stringify(data)
+ });
+}