Better transitions
Rename app in package.json Set up for purely static builds Accept cluster_deleted messages Delete unused pages
This commit is contained in:
parent
bc1ad5e45a
commit
91cca57992
|
@ -1,11 +1,11 @@
|
||||||
{
|
{
|
||||||
"name": "my-app",
|
"name": "kafka-dance-frontend",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "my-app",
|
"name": "kafka-dance-frontend",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/fira-mono": "^4.5.0",
|
"@fontsource/fira-mono": "^4.5.0",
|
||||||
|
@ -16,6 +16,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-auto": "next",
|
"@sveltejs/adapter-auto": "next",
|
||||||
|
"@sveltejs/adapter-static": "^1.0.0-next.39",
|
||||||
"@sveltejs/kit": "next",
|
"@sveltejs/kit": "next",
|
||||||
"@types/cookie": "^0.5.1",
|
"@types/cookie": "^0.5.1",
|
||||||
"eslint": "^8.22.0",
|
"eslint": "^8.22.0",
|
||||||
|
@ -265,6 +266,12 @@
|
||||||
"tiny-glob": "^0.2.9"
|
"tiny-glob": "^0.2.9"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@sveltejs/adapter-static": {
|
||||||
|
"version": "1.0.0-next.39",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-1.0.0-next.39.tgz",
|
||||||
|
"integrity": "sha512-EeD39H6iEe0UEKnKxLFTZFZpi/FcX5xfbAvsMQ+B09aDZccpQmkJBSIo+4kq1JsQGSjwi/+J3aE9bR67R6CIyQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@sveltejs/adapter-vercel": {
|
"node_modules/@sveltejs/adapter-vercel": {
|
||||||
"version": "1.0.0-next.66",
|
"version": "1.0.0-next.66",
|
||||||
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-vercel/-/adapter-vercel-1.0.0-next.66.tgz",
|
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-vercel/-/adapter-vercel-1.0.0-next.66.tgz",
|
||||||
|
@ -3412,6 +3419,12 @@
|
||||||
"tiny-glob": "^0.2.9"
|
"tiny-glob": "^0.2.9"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@sveltejs/adapter-static": {
|
||||||
|
"version": "1.0.0-next.39",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-1.0.0-next.39.tgz",
|
||||||
|
"integrity": "sha512-EeD39H6iEe0UEKnKxLFTZFZpi/FcX5xfbAvsMQ+B09aDZccpQmkJBSIo+4kq1JsQGSjwi/+J3aE9bR67R6CIyQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@sveltejs/adapter-vercel": {
|
"@sveltejs/adapter-vercel": {
|
||||||
"version": "1.0.0-next.66",
|
"version": "1.0.0-next.66",
|
||||||
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-vercel/-/adapter-vercel-1.0.0-next.66.tgz",
|
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-vercel/-/adapter-vercel-1.0.0-next.66.tgz",
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-auto": "next",
|
"@sveltejs/adapter-auto": "next",
|
||||||
|
"@sveltejs/adapter-static": "^1.0.0-next.39",
|
||||||
"@sveltejs/kit": "next",
|
"@sveltejs/kit": "next",
|
||||||
"@types/cookie": "^0.5.1",
|
"@types/cookie": "^0.5.1",
|
||||||
"eslint": "^8.22.0",
|
"eslint": "^8.22.0",
|
||||||
|
@ -27,8 +28,8 @@
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@zerodevx/svelte-json-view": "^0.2.1",
|
|
||||||
"@fontsource/fira-mono": "^4.5.0",
|
"@fontsource/fira-mono": "^4.5.0",
|
||||||
|
"@zerodevx/svelte-json-view": "^0.2.1",
|
||||||
"cookie": "^0.4.1",
|
"cookie": "^0.4.1",
|
||||||
"highlight.js": "^11.6.0",
|
"highlight.js": "^11.6.0",
|
||||||
"svelte-highlight": "^6.2.1"
|
"svelte-highlight": "^6.2.1"
|
||||||
|
|
22
src/app.css
22
src/app.css
|
@ -24,7 +24,7 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
select, button {
|
select, button, input {
|
||||||
font-family: Roboto, Arial, -apple-system, BlinkMacSystemFont, 'Segoe UI', Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
font-family: Roboto, Arial, -apple-system, BlinkMacSystemFont, 'Segoe UI', Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||||
font-size: 90%;
|
font-size: 90%;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
|
@ -34,6 +34,18 @@ select, button {
|
||||||
padding: 0.2em;
|
padding: 0.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
select, input {
|
||||||
|
padding: 0.3em;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
select option {
|
||||||
|
padding: 0.3em;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
padding: 0.4em 1.0em;
|
padding: 0.4em 1.0em;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
@ -82,6 +94,13 @@ button.danger:hover {
|
||||||
border-color: black;
|
border-color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.collapsed-hint {
|
||||||
|
width: 100%;
|
||||||
|
text-align: right;
|
||||||
|
font-size: 200%;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
font-family: 'Liberation Sans', sans-serif;
|
font-family: 'Liberation Sans', sans-serif;
|
||||||
}
|
}
|
||||||
|
@ -209,6 +228,7 @@ button {
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-color: #b3ddef;
|
border-color: #b3ddef;
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
button:focus:not(:focus-visible) {
|
button:focus:not(:focus-visible) {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
export let big
|
import { slide } from 'svelte/transition'
|
||||||
|
export let big = false
|
||||||
export let jsFilter
|
export let jsFilter
|
||||||
export let mutableObjects
|
export let mutableObjects
|
||||||
export let queryCode
|
export let queryCode
|
||||||
|
@ -12,52 +13,65 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if jsFilter}
|
<div class={"query-input-display" + (big ? ' big' : '')}>
|
||||||
<div class="query-input-display">
|
{#if jsFilter}
|
||||||
<div title="If enabled, mutations made by the below code will be displayed in the result data.">
|
<div class="query-input-hideable" transition:slide|local>
|
||||||
Allow JavaScript to mutate objects
|
<div title="If enabled, mutations made by the below code will be displayed in the result data.">
|
||||||
<input type=checkbox bind:checked={mutableObjects}>
|
Allow JavaScript to mutate objects
|
||||||
|
<input type=checkbox bind:checked={mutableObjects}>
|
||||||
|
</div>
|
||||||
|
<textarea class='query-input' bind:value={queryCode}></textarea>
|
||||||
</div>
|
</div>
|
||||||
<textarea class={'query-input' + (big ? ' big' : '')} bind:value={queryCode}></textarea>
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
|
||||||
|
|
||||||
<!--suppress CssUnusedSymbol -->
|
<!--suppress CssUnusedSymbol -->
|
||||||
<style>
|
<style>
|
||||||
.query-input-header {
|
.query-input-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
.query-input-display {
|
|
||||||
margin-top: 1em;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.query-input {
|
|
||||||
min-height: 5vw;
|
|
||||||
resize: vertical;
|
|
||||||
}
|
|
||||||
.query-input.big {
|
|
||||||
height: 60vh;
|
|
||||||
width: 60vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
.query-input-display {
|
||||||
margin-top: 1em;
|
display: flex;
|
||||||
margin-bottom: 0;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
}
|
||||||
z-index: 1;
|
|
||||||
margin-left: 0;
|
|
||||||
|
|
||||||
background-color: #222222;
|
.query-input-hideable {
|
||||||
color: #d0dde9;
|
margin-top: 1em;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
/* For some reason, using flex-grow: 1; broke the slide in transition */
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
border: none;
|
.query-input-display.big {
|
||||||
font-size: 14px;
|
height: 60vh;
|
||||||
font-family: var(--font-mono);
|
width: 60vw;
|
||||||
padding: 1em;
|
}
|
||||||
overflow-x: auto;
|
|
||||||
min-width: 20vw;
|
.query-input {
|
||||||
}
|
min-height: 5vw;
|
||||||
|
resize: vertical;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 1;
|
||||||
|
margin-left: 0;
|
||||||
|
|
||||||
|
background-color: #222222;
|
||||||
|
color: #d0dde9;
|
||||||
|
|
||||||
|
border: none;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
padding: 1em;
|
||||||
|
overflow-x: auto;
|
||||||
|
min-width: 20vw;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
|
@ -13,14 +13,6 @@
|
||||||
<li class:active={$page.url.pathname === '/settings'}>
|
<li class:active={$page.url.pathname === '/settings'}>
|
||||||
<a sveltekit:prefetch href="/settings">Settings</a>
|
<a sveltekit:prefetch href="/settings">Settings</a>
|
||||||
</li>
|
</li>
|
||||||
<!--
|
|
||||||
<li class:active={$page.url.pathname === '/about'}>
|
|
||||||
<a sveltekit:prefetch href="/about">About</a>
|
|
||||||
</li>
|
|
||||||
<li class:active={$page.url.pathname === '/todos'}>
|
|
||||||
<a sveltekit:prefetch href="/todos">Todos</a>
|
|
||||||
</li>
|
|
||||||
-->
|
|
||||||
</ul>
|
</ul>
|
||||||
<svg viewBox="0 0 2 3" aria-hidden="true">
|
<svg viewBox="0 0 2 3" aria-hidden="true">
|
||||||
<path d="M0,0 L0,3 C0.5,3 0.5,3 1,2 L2,0 Z" />
|
<path d="M0,0 L0,3 C0.5,3 0.5,3 1,2 L2,0 Z" />
|
||||||
|
|
|
@ -34,7 +34,7 @@ const mockItems = [
|
||||||
},
|
},
|
||||||
].map(addMetadata)
|
].map(addMetadata)
|
||||||
|
|
||||||
export const testMode = false
|
export const testMode = true
|
||||||
|
|
||||||
export const state = writable({
|
export const state = writable({
|
||||||
items: [],
|
items: [],
|
||||||
|
@ -161,6 +161,13 @@ export const connect = () => {
|
||||||
try {
|
try {
|
||||||
data = JSON.parse(message.data)
|
data = JSON.parse(message.data)
|
||||||
switch (data?.type.toLowerCase()) {
|
switch (data?.type.toLowerCase()) {
|
||||||
|
case 'cluster_deleted':
|
||||||
|
updateClearError(s => ({
|
||||||
|
...s,
|
||||||
|
error: `The cluster '${data.message}' has been deleted from the Kafka Dance Server`,
|
||||||
|
errorDetails: 'Your running search has been canceled.'
|
||||||
|
}))
|
||||||
|
break
|
||||||
case 'complete':
|
case 'complete':
|
||||||
updateClearError(s => ({
|
updateClearError(s => ({
|
||||||
...s,
|
...s,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
|
import { slide, fly, scale } from 'svelte/transition'
|
||||||
import { JsonView } from '@zerodevx/svelte-json-view'
|
import { JsonView } from '@zerodevx/svelte-json-view'
|
||||||
import QueryInput from '$lib/QueryInput.svelte'
|
import QueryInput from '$lib/QueryInput.svelte'
|
||||||
import { state, connect } from '$lib/state'
|
import { state, connect } from '$lib/state'
|
||||||
|
@ -13,6 +14,7 @@
|
||||||
startQuery,
|
startQuery,
|
||||||
stopQuery
|
stopQuery
|
||||||
} from './+page.js';
|
} from './+page.js';
|
||||||
|
export let errors, data
|
||||||
|
|
||||||
let showQueryModal = false
|
let showQueryModal = false
|
||||||
let queryRunning = false
|
let queryRunning = false
|
||||||
|
@ -116,7 +118,6 @@
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="settings-option">
|
<div class="settings-option">
|
||||||
<label>
|
<label>
|
||||||
Maximum Results
|
Maximum Results
|
||||||
|
@ -147,6 +148,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Previous 'Start' style
|
||||||
<div class="settings-option query-type">
|
<div class="settings-option query-type">
|
||||||
<span>Query Type:</span>
|
<span>Query Type:</span>
|
||||||
<span>
|
<span>
|
||||||
|
@ -169,6 +171,23 @@
|
||||||
<button class="colored" on:click={() => queryRunning = startQuery(querySettings)}>Start Query</button>
|
<button class="colored" on:click={() => queryRunning = startQuery(querySettings)}>Start Query</button>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<div class="query-button">
|
||||||
|
{#if queryRunning}
|
||||||
|
<button transition:slide class="danger" on:click={() => queryRunning = stopQuery(querySettings)}>Stop Query</button>
|
||||||
|
{:else}
|
||||||
|
<button transition:scale class="colored"
|
||||||
|
on:click={() => queryRunning = startQuery({...querySettings, mode: queryMode.ONE_SHOT})}>
|
||||||
|
Start One-Shot
|
||||||
|
</button>
|
||||||
|
<div class="separator"></div>
|
||||||
|
<button transition:slide class="colored"
|
||||||
|
on:click={() => queryRunning = startQuery({...querySettings, mode: queryMode.REAL_TIME})}>
|
||||||
|
Start Real-Time
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="data-view-container">
|
<div class="data-view-container">
|
||||||
|
@ -308,6 +327,18 @@
|
||||||
text-align: right;
|
text-align: right;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
border-style: none;
|
border-style: none;
|
||||||
|
/* New button CSS */
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.separator {
|
||||||
|
width: 1em;
|
||||||
|
}
|
||||||
|
.query-button button {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
.query-button button:hover {
|
||||||
|
flex-grow: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.state-error {
|
.state-error {
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
import { browser, dev } from '$app/env';
|
|
||||||
|
|
||||||
// we don't need any JS on this page, though we'll load
|
|
||||||
// it in dev so that we get hot module replacement...
|
|
||||||
export const hydrate = dev;
|
|
||||||
|
|
||||||
// ...but if the client-side router is already loaded
|
|
||||||
// (i.e. we came here from elsewhere in the app), use it
|
|
||||||
export const router = browser;
|
|
||||||
|
|
||||||
// since there's no dynamic data here, we can prerender
|
|
||||||
// it so that it gets served as a static asset in prod
|
|
||||||
export const prerender = true;
|
|
|
@ -1,52 +0,0 @@
|
||||||
<script context="module">
|
|
||||||
throw new Error("@migration task: Check code was safely removed (https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292722)");
|
|
||||||
|
|
||||||
// import { browser, dev } from '$app/env';
|
|
||||||
|
|
||||||
// // we don't need any JS on this page, though we'll load
|
|
||||||
// // it in dev so that we get hot module replacement...
|
|
||||||
// export const hydrate = dev;
|
|
||||||
|
|
||||||
// // ...but if the client-side router is already loaded
|
|
||||||
// // (i.e. we came here from elsewhere in the app), use it
|
|
||||||
// export const router = browser;
|
|
||||||
|
|
||||||
// // since there's no dynamic data here, we can prerender
|
|
||||||
// // it so that it gets served as a static asset in prod
|
|
||||||
// export const prerender = true;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>About</title>
|
|
||||||
<meta name="description" content="About this app" />
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<div class="content">
|
|
||||||
<h1>About this app</h1>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
This is a <a href="https://kit.svelte.dev">SvelteKit</a> app. You can make your own by typing the
|
|
||||||
following into your command line and following the prompts:
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<pre>npm create svelte@latest</pre>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
The page you're looking at is purely static HTML, with no client-side interactivity needed.
|
|
||||||
Because of that, we don't need to load any JavaScript. Try viewing the page's source, or opening
|
|
||||||
the devtools network panel and reloading.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
The <a href="/todos">TODOs</a> page illustrates SvelteKit's data loading and form handling. Try using
|
|
||||||
it with JavaScript disabled!
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.content {
|
|
||||||
width: 100%;
|
|
||||||
max-width: var(--column-width);
|
|
||||||
margin: var(--column-margin-top) auto 0 auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,70 +0,0 @@
|
||||||
import { api } from './_api';
|
|
||||||
|
|
||||||
/** @type {import('./$types').PageServerLoad} */
|
|
||||||
export const load = 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: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.status === 200) {
|
|
||||||
return {
|
|
||||||
todos: await response.json()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error("@migration task: Migrate this return statement (https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292699)");
|
|
||||||
return {
|
|
||||||
status: response.status
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/** @type {import('./$types').Action} */
|
|
||||||
export const POST = async ({ request, locals }) => {
|
|
||||||
const form = await request.formData();
|
|
||||||
|
|
||||||
await api('POST', `todos/${locals.userid}`, {
|
|
||||||
text: form.get('text')
|
|
||||||
});
|
|
||||||
|
|
||||||
throw new Error("@migration task: Migrate this return statement (https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292699)");
|
|
||||||
return {};
|
|
||||||
};
|
|
||||||
|
|
||||||
// If the user has JavaScript disabled, the URL will change to
|
|
||||||
// include the method override unless we redirect back to /todos
|
|
||||||
const redirect = {
|
|
||||||
status: 303,
|
|
||||||
headers: {
|
|
||||||
location: '/todos'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** @type {import('./$types').Action} */
|
|
||||||
export const PATCH = async ({ request, locals }) => {
|
|
||||||
const form = await request.formData();
|
|
||||||
|
|
||||||
await api('PATCH', `todos/${locals.userid}/${form.get('uid')}`, {
|
|
||||||
text: form.has('text') ? form.get('text') : undefined,
|
|
||||||
done: form.has('done') ? !!form.get('done') : undefined
|
|
||||||
});
|
|
||||||
|
|
||||||
throw new Error("@migration task: Migrate this return statement (https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292699)");
|
|
||||||
return redirect;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** @type {import('./$types').Action} */
|
|
||||||
export const DELETE = async ({ request, locals }) => {
|
|
||||||
const form = await request.formData();
|
|
||||||
|
|
||||||
await api('DELETE', `todos/${locals.userid}/${form.get('uid')}`);
|
|
||||||
|
|
||||||
throw new Error("@migration task: Migrate this return statement (https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292699)");
|
|
||||||
return redirect;
|
|
||||||
};
|
|
|
@ -1,192 +0,0 @@
|
||||||
<script>
|
|
||||||
throw new Error("@migration task: Add data prop (https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292707)");
|
|
||||||
|
|
||||||
import { enhance } from '$lib/form';
|
|
||||||
import { scale } from 'svelte/transition';
|
|
||||||
import { flip } from 'svelte/animate';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {{
|
|
||||||
* uid: string;
|
|
||||||
* created_at: Date;
|
|
||||||
* text: string;
|
|
||||||
* done: boolean;
|
|
||||||
* pending_delete: boolean;
|
|
||||||
* }} Todo
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @type {Todo[]} */
|
|
||||||
export let 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"
|
|
||||||
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(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>
|
|
|
@ -1,27 +0,0 @@
|
||||||
/*
|
|
||||||
This module is used by the /todos endpoint 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';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} method
|
|
||||||
* @param {string} resource
|
|
||||||
* @param {Record<string, unknown>} [data]
|
|
||||||
*/
|
|
||||||
export function api(method, resource, data) {
|
|
||||||
return fetch(`${base}/${resource}`, {
|
|
||||||
method,
|
|
||||||
headers: {
|
|
||||||
'content-type': 'application/json'
|
|
||||||
},
|
|
||||||
body: data && JSON.stringify(data)
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,9 +1,12 @@
|
||||||
// import adapter from '@sveltejs/adapter-auto';
|
import adapter from '@sveltejs/adapter-static';
|
||||||
|
|
||||||
/** @type {import('@sveltejs/kit').Config} */
|
/** @type {import('@sveltejs/kit').Config} */
|
||||||
const config = {
|
const config = {
|
||||||
kit: {
|
kit: {
|
||||||
//adapter: adapter(),
|
adapter: adapter(),
|
||||||
|
prerender: {
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
|
||||||
// Override http methods in the Todo forms
|
// Override http methods in the Todo forms
|
||||||
methodOverride: {
|
methodOverride: {
|
||||||
|
|
Loading…
Reference in New Issue