Adding settings pages with default text, about page that is static, and a form action fully typed used in all form elements. If JS exists then use fetch otherwise default to regular HTML form ways.

This commit is contained in:
Bradley Shellnut 2022-04-13 14:20:06 -07:00
parent 9d13216fea
commit 4324a70a7a
10 changed files with 253 additions and 3 deletions

View file

@ -1,4 +1,6 @@
<script lang="ts">
import { enhance } from '$root/lib/form'
let tweet = ''
let maxCharacters = 140
@ -8,7 +10,14 @@
<div class="compose">
<img src="/profile/matia/avatar.webp" alt="Avatar">
<form action="/home" method="post" autocomplete="off">
<form
action="/home"
method="post"
autocomplete="off"
use:enhance={{
result: ({ form }) => form.reset()
}}
>
<input
aria-label="Enter your tweet"
bind:value={tweet}

View file

@ -1,6 +1,7 @@
<script lang="ts">
import { fade, fly } from 'svelte/transition'
import { enhance } from '$root/lib/form'
import Icon from '$root/components/icon.svelte'
import type { TweetType } from '$root/types'
@ -32,7 +33,11 @@
</div>
<div class="actions">
<form action="/home/like" method="post">
<form
action="/home/like"
method="post"
use:enhance
>
<input type="hidden" name="id" value={tweet.id} />
<button
class="btn like"
@ -72,7 +77,11 @@
</div>
</a>
<form action="/home?_method=delete" method="post">
<form
action="/home?_method=delete"
method="post"
use:enhance
>
<input type="hidden" name="id" value={tweet.id} />
<button
aria-label="Remove tweet"

48
src/lib/form.ts Normal file
View file

@ -0,0 +1,48 @@
import { invalidate } from "$app/navigation"
import { page } from "$app/stores"
type Parameters = {
result?: ({ form }: { form: HTMLFormElement }) => void
}
type Destroy = { destroy: () => void }
type Enhance = (
form?: HTMLFormElement,
{ result }?: Parameters
) => Destroy
export const enhance: Enhance = (form, { result } = {}) => {
let invalidatePath: URL
page.subscribe((path) => {
invalidatePath = path.url
})
async function handleSubmit(event: Event) {
event.preventDefault()
const response = await fetch(form.action, {
method: form.method,
headers: { accept: 'application/json' },
body: new FormData(form)
})
if (!response.ok) {
console.error(await response.text());
}
const url = new URL(invalidatePath)
url.search = ''
url.hash = ''
invalidate(url.href)
if (result) {
result({ form })
}
}
form.addEventListener('submit', handleSubmit)
return {
destroy() {
form.removeEventListener('submit', handleSubmit)
}
}
}

View file

@ -0,0 +1,38 @@
<script context="module" lang="ts">
import { dev } from '$app/env'
export const hydrate = dev
export const prerender = true
</script>
<svelte:head>
<title>About</title>
</svelte:head>
<div class="container">
<h1>About</h1>
<div class="content">
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Molestiae mollitia, dolores consequatur quos hic commodi eum eius ex et laudantium quia dolore quidem aliquid tempore sit. A natus dolor sed!</p>
</div>
</div>
<style>
h1 {
font-size: var(--font-24);
}
p {
color: var(--color-text-muted);
}
.container {
display: grid;
gap: var(--spacing-8);
padding: var(--spacing-16);
}
.content {
display: grid;
gap: var(--spacing-16);
}
</style>

View file

@ -0,0 +1,123 @@
<script lang="ts">
import { page } from '$app/stores';
import Navigation from '$root/components/navigation.svelte';
$: path = $page.url.pathname
</script>
<div class="container">
<Navigation />
<main class="settings">
<h1 class="title">Settings</h1>
<section class="privacy">
<h2>Privacy</h2>
<a
class:active={
path === '/home/settings/personalization'
}
href="/home/settings/personalization"
>
Personalization and data
</a>
<a
class:active={
path === '/home/settings/data'
}
href="/home/settings/data"
>
Your Twitttr data
</a>
<a
class:active={
path === '/home/settings/cookies'
}
href="/home/settings/cookies"
>
Cookie preferences
</a>
</section>
<section class="general">
<h2>General</h2>
<a
class:active={
path === '/home/settings/resources'
}
href="/home/settings/resources"
>
Additional resources
</a>
</section>
</main>
<aside>
<slot />
</aside>
</div>
<style>
.container {
height: 100vh;
max-width: min-content;
margin: 0 auto;
display: grid;
grid-template-columns: min-content 50ch;
}
.settings {
display: grid;
grid-auto-rows: min-content;
gap: var(--spacing-24);
border: 1px solid var(--color-border-primary);
border-top: none;
border-bottom: none;
}
.privacy,
.general {
display: grid;
}
h1 {
font-size: var(--font-18);
margin-top: var(--spacing-24);
padding: 0 var(--spacing-16);
}
h2 {
font-size: var(--font-24);
padding: 0 var(--spacing-16);
}
h2 {
margin-bottom: var(--spacing-16);
}
a {
padding: 1.4rem var(--spacing-16);
}
.active {
background: var(--color-link-hover);
}
aside {
display: none;
padding: var(--spacing-16);
}
@media (min-width: 1024px) {
.container {
max-width: 1240px;
margin: 0 auto;
grid-template-columns: 1fr 50ch 1fr;
}
aside {
width: 400px;
display: block;
}
}
</style>

View file

@ -0,0 +1,5 @@
<svelte:head>
<title>Settings | Cookies</title>
</svelte:head>
<h3>Cookie preferences</h3>

View file

@ -0,0 +1,5 @@
<svelte:head>
<title>Settings | Data</title>
</svelte:head>
<h3>Your Twitter data</h3>

View file

@ -0,0 +1,3 @@
<svelte:head>
<title>Settings</title>
</svelte:head>

View file

@ -0,0 +1,5 @@
<svelte:head>
<title>Settings | Personalization</title>
</svelte:head>
<h3>Personalization and data</h3>

View file

@ -0,0 +1,5 @@
<svelte:head>
<title>Settings | Resources</title>
</svelte:head>
<h3>Additional resources</h3>