feat: layout editing (sorta)

This commit is contained in:
2023-09-22 20:27:15 +02:00
parent f03b4d586b
commit e7a52221d2
21 changed files with 500 additions and 505 deletions

View File

@@ -1,6 +1,6 @@
<script>
import {page} from "$app/stores"
import LL from "../../i18n/i18n-svelte"
import LL from "../i18n/i18n-svelte"
$: paths = [
{href: "/config/chords/", title: $LL.configure.chords.TITLE(), icon: "piano"},

View File

@@ -0,0 +1,85 @@
<script lang="ts">
import LL from "../i18n/i18n-svelte"
import {changes} from "$lib/serial/connection"
import type {Change} from "$lib/serial/connection"
import {fly} from "svelte/transition"
function undo() {
redoQueue = [$changes.pop()!, ...redoQueue]
changes.update(it => it)
}
function redo() {
const [change, ...queue] = redoQueue
changes.update(it => {
it.push(change)
return it
})
redoQueue = queue
}
let redoQueue: Change[] = []
function apply() {
// TODO
}
</script>
<button title={$LL.saveActions.UNDO()} class="icon" disabled={$changes.length === 0} on:click={undo}
>undo</button
>
<button title={$LL.saveActions.REDO()} class="icon" disabled={redoQueue.length === 0} on:click={redo}
>redo</button
>
<div class="separator" />
<button title={$LL.saveActions.SAVE()} class="icon">save</button>
{#if $changes.length !== 0}
<button class="click-me" transition:fly={{x: 8}}
><span class="icon">bolt</span>{$LL.saveActions.APPLY()}</button
>
{/if}
<style lang="scss">
button {
cursor: pointer;
padding: 0;
color: currentcolor;
background: none;
border: none;
transition: all 250ms ease;
}
:disabled {
pointer-events: none;
opacity: 0.5;
}
.click-me {
display: flex;
align-items: center;
justify-content: center;
margin-inline: 8px;
padding-block: 2px;
padding-inline-start: 4px;
padding-inline-end: 8px;
font-family: inherit;
font-weight: bold;
color: var(--md-sys-color-primary);
border: 2px solid var(--md-sys-color-primary);
border-radius: 18px;
outline: 2px dashed var(--md-sys-color-primary);
outline-offset: 2px;
}
.separator {
width: 1px;
height: 24px;
background: var(--md-sys-color-outline-variant);
}
</style>

View File

@@ -20,6 +20,7 @@
position: absolute;
bottom: 0;
left: 0;
opacity: 0.4;
}
ul {

View File

@@ -1,6 +1,5 @@
<script lang="ts">
import {serialPort, syncStatus, unsavedChanges} from "$lib/serial/connection"
import {page} from "$app/stores"
import {slide, fly} from "svelte/transition"
import {canShare, triggerShare} from "$lib/share"
import {popup} from "$lib/popup"
@@ -11,17 +10,8 @@
import {userPreferences} from "$lib/preferences"
import LL from "../i18n/i18n-svelte"
import Profile from "./Profile.svelte"
const training = [
{slug: "cpm", title: "CPM - Characters Per Minute", icon: "music_note"},
{slug: "chords", title: "ChM - Chords Mastered", icon: "piano"},
{slug: "avg-wpm", title: "aWPM - Average Words Per Minute", icon: "avg_pace"},
{slug: "sentences", title: "StM - Sentences Mastered", icon: "lyrics"},
{slug: "top-wpm", title: "tWPM - Top Words Per Minute", icon: "speed"},
{slug: "cm", title: "CM - Concepts Mastered", icon: "cognition"},
]
let placeboProgress = false
import ConfigTabs from "./ConfigTabs.svelte"
import EditActions from "./EditActions.svelte"
async function flashChanges() {
$syncStatus = "uploading"
@@ -49,34 +39,32 @@
</script>
<nav>
<a href="/" class="title">{$LL.TITLE()}</a>
<div class="steps">
{#each training as {slug, title, icon}}
<a
href="/train/{slug}/"
{title}
class="icon train {slug}"
class:active={$page.url.pathname === `/train/${slug}/`}>{icon}</a
>
{/each}
<div class="actions">
<EditActions />
</div>
<ConfigTabs />
<div class="actions">
{#if $canShare}
<button transition:fly={{x: -8}} class="icon" on:click={triggerShare}>share</button>
<div transition:slide class="separator"/>
<div transition:slide class="separator" />
{/if}
{#if import.meta.env.TAURI_FAMILY === undefined}
{#await import("$lib/components/PwaStatus.svelte") then {default: PwaStatus}}
<PwaStatus/>
{#await import("$lib/components/PwaStatus.svelte") then { default: PwaStatus }}
<PwaStatus />
{/await}
{/if}
{#if $unsavedChanges.size > 0}
<button disabled={$syncStatus === 'uploading'} on:click={flashChanges} transition:fly={{x: -8}}
title={$LL.deviceManager.APPLY_SETTINGS()} class="icon">save
<button
disabled={$syncStatus === "uploading"}
on:click={flashChanges}
transition:fly={{x: -8}}
title={$LL.deviceManager.APPLY_SETTINGS()}
class="icon"
>save
</button>
<div transition:slide class="separator"/>
<div transition:slide class="separator" />
{/if}
{#if $serialPort}
<button title={$LL.backup.TITLE()} use:popup={BackupPopup} class="icon {$syncStatus}">
@@ -92,11 +80,11 @@
</button>
{/if}
<button
bind:this={connectButton}
title="Devices"
use:popup={ConnectionPopup}
class="icon connect"
class:error={$serialPort === undefined}
bind:this={connectButton}
title="Devices"
use:popup={ConnectionPopup}
class="icon connect"
class:error={$serialPort === undefined}
>
cable
</button>
@@ -167,16 +155,20 @@
}
nav {
display: flex;
display: grid;
grid-template-columns: 1fr auto 1fr;
gap: 4px;
align-items: center;
justify-content: space-between;
width: calc(min(100%, 28cm));
margin-block: 8px;
margin-inline: 16px;
margin-inline: auto;
padding-inline: 16px;
}
.title {
display: flex;
align-items: center;
margin-block: 0;
font-size: 1.5rem;
@@ -195,7 +187,7 @@
justify-content: center;
aspect-ratio: 1;
padding: 4px;
padding: 2px;
color: inherit;
text-decoration: none;
@@ -210,49 +202,16 @@
color: var(--md-sys-color-on-error);
background: var(--md-sys-color-error);
}
&.active,
&:active {
color: var(--md-sys-color-on-primary);
background: var(--md-sys-color-primary);
}
}
.steps {
position: absolute;
left: 50%;
translate: -50% 0;
display: flex;
> a.icon {
aspect-ratio: unset;
margin-inline: -4px;
padding-inline: 16px;
font-size: 24px;
color: var(--md-sys-on-surface-variant);
background: var(--md-sys-color-surface-variant);
clip-path: polygon(25% 50%, 0% 0%, 75% 0%, 100% 50%, 75% 100%, 0% 100%);
border-radius: 0;
&.active,
&:active {
color: var(--md-sys-color-on-tertiary);
background: var(--md-sys-color-tertiary);
&,
~ * {
translate: 8px 0;
}
}
}
}
.actions {
display: flex;
gap: 8px;
align-items: center;
&:last-child {
justify-content: flex-end;
}
}
.icon.account {
@@ -260,7 +219,7 @@
color: var(--md-sys-color-on-secondary-container);
background: var(--md-sys-color-secondary-container);
}
:disabled {
pointer-events: none;
opacity: 0.5;

View File

@@ -2,10 +2,8 @@
import {chords} from "$lib/serial/connection"
import {KEYMAP_CODES} from "$lib/serial/keymap-codes"
import Index from "flexsearch"
import {tick} from "svelte"
import type {Chord} from "$lib/serial/chord"
import LL from "../../../i18n/i18n-svelte"
import {actionAutocomplete} from "$lib/action-autocomplete"
$: searchIndex = $chords?.length > 0 ? buildIndex($chords) : undefined
@@ -20,11 +18,8 @@
let searchFilter: number[] | undefined
function search(event: Event) {
document.startViewTransition(async () => {
const query = (event.target as HTMLInputElement).value
searchFilter = query && searchIndex ? searchIndex.search(query) : undefined
await tick()
})
const query = (event.target as HTMLInputElement).value
searchFilter = query && searchIndex ? searchIndex.search(query) : undefined
}
$: items = searchFilter?.map(it => [$chords[it], it] as const) ?? $chords.map((it, i) => [it, i] as const)
@@ -38,7 +33,7 @@
<input
type="search"
placeholder={$LL.configure.chords.search.PLACEHOLDER($chords.length)}
use:actionAutocomplete
on:input={search}
/>
</div>

View File

@@ -1,6 +0,0 @@
import {redirect} from "@sveltejs/kit"
import type {PageLoad} from "./$types"
export const load = (() => {
throw redirect(302, "/train/cpm/")
}) satisfies PageLoad

View File

@@ -1,5 +0,0 @@
<script>
import TypingInput from "$lib/components/TypingInput.svelte"
</script>
<TypingInput />