state för importsidan, snyggare dashboard och förbered global hotkeys

This commit is contained in:
botvid johansson 2025-02-08 22:25:49 +01:00
parent c03226a610
commit 7373f30ddc
16 changed files with 253 additions and 52 deletions

View File

@ -1,4 +1,6 @@
<script> <script>
import { goto } from '$app/navigation';
import { importState } from '$lib/stores.svelte';
import * as Accordion from '$lib/components/ui/accordion'; import * as Accordion from '$lib/components/ui/accordion';
import AccordionItem from '$lib/components/ui/accordion/accordion-item.svelte'; import AccordionItem from '$lib/components/ui/accordion/accordion-item.svelte';
import { Button } from '$lib/components/ui/button'; import { Button } from '$lib/components/ui/button';
@ -7,12 +9,25 @@
import { Label } from '$lib/components/ui/label'; import { Label } from '$lib/components/ui/label';
import * as m from '$lib/paraglide/messages.js'; import * as m from '$lib/paraglide/messages.js';
import { Badge } from '$lib/components/ui/badge';
import { Separator } from '$lib/components/ui/separator';
import Import from './import/import.svelte'; import Import from './import/import.svelte';
export let open; export let open = true;
export let data = { export let data = {
form: {} form: {}
}; };
import default_shortforms from '../db/shortforms.se.json';
const importDefaultList = () => {
importState.data = '';
importState.errors = [];
default_shortforms.shortforms.forEach((sf) => {
importState.data += sf.sf + '=' + sf.p + '\n';
});
console.log('goto import');
goto('/import');
};
</script> </script>
<Dialog.Root bind:open closeOnOutsideClick={false}> <Dialog.Root bind:open closeOnOutsideClick={false}>
@ -29,7 +44,28 @@
<Accordion.Root class="w-full"> <Accordion.Root class="w-full">
<Accordion.Item value="import"> <Accordion.Item value="import">
<Accordion.Trigger>Importera förkortningar</Accordion.Trigger> <Accordion.Trigger>Importera förkortningar</Accordion.Trigger>
<Accordion.Content><Import data={data.form} /></Accordion.Content> <Accordion.Content>
<p class="leading-7 [&:not(:first-child)]:mt-6">
Skrivert kan bara importera och exportera förkortningslistor i läsbart textformat.<br />
Det finns ett verktyg för att konvertera listor och export-filer mellan olika format här:
<br />
<a href="https://qwertyist.se/tools/shortforms/" target="_blank"
><Badge variant="secondary">qwertyist.se/förkortingar/</Badge></a
>
</p>
<Separator class="my-4" />
<p class="leading-7 [&:not(:first-child)]:mt-6">
Vill du inte importera en egen lista så kan du använda programmets baslista som i den
här versionen av programmet har {default_shortforms.shortforms.length} förkortningar.<br
/>
<a href="/import" on:click={importDefaultList}
><Badge variant="secondary">Importera baslista</Badge></a
>
</p>
<p class="leading-7 [&:not(:first-child)]:mt-6">
<a href="/import"><Badge>Importera förkortningar</Badge></a>
</p>
</Accordion.Content>
</Accordion.Item> </Accordion.Item>
<Accordion.Item value="settings"> <Accordion.Item value="settings">
<Accordion.Trigger>Ändra inställningar</Accordion.Trigger> <Accordion.Trigger>Ändra inställningar</Accordion.Trigger>
@ -39,8 +75,12 @@
<Accordion.Trigger>Ge feedback eller rapportera fel</Accordion.Trigger> <Accordion.Trigger>Ge feedback eller rapportera fel</Accordion.Trigger>
<Accordion.Content>Här ger man feedback eller rapporterar fel</Accordion.Content> <Accordion.Content>Här ger man feedback eller rapporterar fel</Accordion.Content>
</Accordion.Item> </Accordion.Item>
<Accordion.Item value="connect">
<Accordion.Trigger>Koppla upp mot tolkanvändare</Accordion.Trigger>
<Accordion.Content></Accordion.Content>
</Accordion.Item>
<Accordion.Item value="interpret"> <Accordion.Item value="interpret">
<Accordion.Trigger>Börja skriva</Accordion.Trigger> <Accordion.Trigger>Börja skrivtolka</Accordion.Trigger>
<Accordion.Content>Man för börja skriva på en gång om man vill.</Accordion.Content> <Accordion.Content>Man för börja skriva på en gång om man vill.</Accordion.Content>
</Accordion.Item> </Accordion.Item>
</Accordion.Root> </Accordion.Root>

View File

@ -1,4 +1,8 @@
<script lang="ts"> <script lang="ts">
import { goto } from '$app/navigation';
import { onMount } from 'svelte';
import { importState } from '$lib/stores.svelte';
import * as Card from '$lib/components/ui/card/index.js';
import { Input } from '$lib/components/ui/input'; import { Input } from '$lib/components/ui/input';
import * as Select from '$lib/components/ui/select'; import * as Select from '$lib/components/ui/select';
import { Label } from '$lib/components/ui/label'; import { Label } from '$lib/components/ui/label';
@ -17,7 +21,6 @@
name: '', name: '',
type: false type: false
}; };
let errors = '...';
let importing = false; let importing = false;
let progress = 0; let progress = 0;
const handleImport = () => { const handleImport = () => {
@ -31,51 +34,56 @@
} }
}); });
}; };
const cancelImport = () => {
importState.data = '';
importState.errors = [];
goto('/');
};
const importDefaultList = () => { const importDefaultList = () => {
importState.data = '';
importState.errors = [];
default_shortforms.shortforms.forEach((sf) => { default_shortforms.shortforms.forEach((sf) => {
form.textarea += sf.sf + '=' + sf.p + '\n'; importState.data += sf.sf + '=' + sf.p + '\n';
}); });
}; };
onMount(() => {
form.textarea = importState.data;
});
</script> </script>
<p class="leading-7 [&:not(:first-child)]:mt-6"> <Card.Root class="w-[490px]">
Skrivert kan bara importera och exportera förkortningslistor i läsbart textformat.<br /> <Card.Header>
Det finns ett verktyg för att konvertera listor och export-filer mellan olika format här: <br /> <Card.Title>Importera förkortningar</Card.Title>
<a href="https://qwertyist.se/tools/shortforms/" target="_blank" <Card.Description>Nån annan hjälptext.</Card.Description>
><Badge>qwertyist.se/förkortingar/</Badge></a </Card.Header>
> <Card.Content>
</p> <form class="grid gap-4 py-4" method="post" enctype="multipart/form-data">
<Separator class="my-4" /> <div class="grid grid-cols-4 items-center gap-4">
<p class="leading-7 [&:not(:first-child)]:mt-6"> <Label>Förkortningar</Label>
Vill du inte importera en egen lista så kan du använda programmets baslista som i den här <textarea
versionen av programmet har {default_shortforms.shortforms.length} förkortningar.<br /> class="block w-[290px] rounded-lg border border-gray-200 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500"
<a href="#" on:click={importDefaultList}><Badge>Importera baslista</Badge></a> bind:value={form.textarea}
</p> placeholder="Skriv/klistra in dina förkortingar i formatet
<Separator class="my-4" />
<form class="grid gap-4 py-4" method="post" enctype="multipart/form-data">
<div class="grid grid-cols-4 items-center gap-4">
<Label>Förkortningar</Label>
<textarea
class="block w-[290px] rounded-lg border border-gray-200 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500"
bind:value={form.textarea}
placeholder="Skriv/klistra in dina förkortingar i formatet
förk=förkortning förk=förkortning
förkr=förkortningar förkr=förkortningar
förkn=förkortningen förkn=förkortningen
..." ..."
rows="5" rows="5"
></textarea> ></textarea>
</div> </div>
<div class="grid grid-cols-4 items-center gap-4"> <div class="grid grid-cols-4 items-center gap-4">
<Label>Namn</Label> <Label>Namn</Label>
<Input bind:value={form.name} placeholder="Döp förkortningslistan" class="w-[290px]" /> <Input bind:value={form.name} placeholder="Döp förkortningslistan" class="w-[290px]" />
</div> </div>
<div class="grid grid-cols-4 items-center gap-4"> <div class="grid grid-cols-4 items-center gap-4">
<Label>Prioritera lista</Label> <Label>Prioritera lista</Label>
<Switch bind:checked={form.type} /> <Switch bind:checked={form.type} />
</div> </div>
<p class="leading-7 [&:not(:first-child)]:mt-2">{errors}</p> <p class="leading-7 [&:not(:first-child)]:mt-2">{importState.errors}</p>
<Button on:click={handleImport}>Importera</Button> </form>
</form> </Card.Content>
<Card.Footer class="flex justify-between">
{#if importing}<Progress value={progress} />{/if} <Button on:click={cancelImport} variant="outline">Avbryt</Button>
<Button on:click={handleImport}>Importera</Button>
</Card.Footer>
</Card.Root>

View File

@ -1,4 +1,5 @@
<script lang="ts"> <script lang="ts">
import { shortforms } from '$lib/stores.svelte';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { browser } from '$app/environment'; import { browser } from '$app/environment';
@ -8,8 +9,6 @@
import type { Shortform } from '../db/main'; import type { Shortform } from '../db/main';
import AccordionTrigger from '$lib/components/ui/accordion/accordion-trigger.svelte'; import AccordionTrigger from '$lib/components/ui/accordion/accordion-trigger.svelte';
export let activeShortforms: Array<Shortform>;
let shortforms: Map<string, any>;
let state = { let state = {
capitalizeNext: true, capitalizeNext: true,
currentWord: '' currentWord: ''
@ -19,7 +18,7 @@
let text = ''; let text = '';
onMount(() => { onMount(() => {
shortforms = cacheShortforms([890324]); shortforms.cache = cacheShortforms([890324]);
if (browser) { if (browser) {
let t = document.getElementById('doc') as HTMLTextAreaElement; let t = document.getElementById('doc') as HTMLTextAreaElement;
if (t != null) { if (t != null) {
@ -38,7 +37,7 @@
if (defaultExpanders.has(expander)) { if (defaultExpanders.has(expander)) {
const expanderRules = defaultExpanders.get(expander)!; const expanderRules = defaultExpanders.get(expander)!;
const currentWord = getCurrentWord(textarea).trim(); const currentWord = getCurrentWord(textarea).trim();
const expandedPhrase = expandShortform(shortforms, currentWord); const expandedPhrase = expandShortform(shortforms.cache, currentWord);
if (expanderRules.fullstop) { if (expanderRules.fullstop) {
// console.log("should capitalize next"); // console.log("should capitalize next");
state.capitalizeNext = true; state.capitalizeNext = true;

View File

@ -0,0 +1,13 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn } from "$lib/utils.js";
type $$Props = HTMLAttributes<HTMLDivElement>;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<div class={cn("p-6", className)} {...$$restProps}>
<slot />
</div>

View File

@ -0,0 +1,13 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn } from "$lib/utils.js";
type $$Props = HTMLAttributes<HTMLParagraphElement>;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<p class={cn("text-muted-foreground text-sm", className)} {...$$restProps}>
<slot />
</p>

View File

@ -0,0 +1,13 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn } from "$lib/utils.js";
type $$Props = HTMLAttributes<HTMLDivElement>;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<div class={cn("flex items-center p-6 pt-0", className)} {...$$restProps}>
<slot />
</div>

View File

@ -0,0 +1,13 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn } from "$lib/utils.js";
type $$Props = HTMLAttributes<HTMLDivElement>;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<div class={cn("flex flex-col space-y-1.5 p-6 pb-0", className)} {...$$restProps}>
<slot />
</div>

View File

@ -0,0 +1,21 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import type { HeadingLevel } from "./index.js";
import { cn } from "$lib/utils.js";
type $$Props = HTMLAttributes<HTMLHeadingElement> & {
tag?: HeadingLevel;
};
let className: $$Props["class"] = undefined;
export let tag: $$Props["tag"] = "h3";
export { className as class };
</script>
<svelte:element
this={tag}
class={cn("font-semibold leading-none tracking-tight", className)}
{...$$restProps}
>
<slot />
</svelte:element>

View File

@ -0,0 +1,22 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn } from "$lib/utils.js";
type $$Props = HTMLAttributes<HTMLDivElement>;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
class={cn("bg-card text-card-foreground rounded-xl border shadow", className)}
{...$$restProps}
on:click
on:focusin
on:focusout
on:mouseenter
on:mouseleave
>
<slot />
</div>

View File

@ -0,0 +1,24 @@
import Root from "./card.svelte";
import Content from "./card-content.svelte";
import Description from "./card-description.svelte";
import Footer from "./card-footer.svelte";
import Header from "./card-header.svelte";
import Title from "./card-title.svelte";
export {
Root,
Content,
Description,
Footer,
Header,
Title,
//
Root as Card,
Content as CardContent,
Description as CardDescription,
Footer as CardFooter,
Header as CardHeader,
Title as CardTitle,
};
export type HeadingLevel = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";

2
src/lib/stores.svelte.ts Normal file
View File

@ -0,0 +1,2 @@
export const shortforms = $state({ standardList: "", subjectLists: [], cache: new Map() })
export const importState = $state({ data: "", errors: [] })

View File

@ -1,6 +1,8 @@
import { goto } from '$app/navigation';
import type { ExpanderType } from "./index.d.ts"; import type { ExpanderType } from "./index.d.ts";
export const defaultExpanders: Map<string, ExpanderType> = new Map(); export const defaultExpanders: Map<string, ExpanderType> = new Map();
export const hotkeys: Map<string, any> = new Map();
defaultExpanders.set(" ", { defaultExpanders.set(" ", {
key: { keyCode: 190, shiftKey: false }, key: { keyCode: 190, shiftKey: false },
@ -72,3 +74,18 @@ defaultExpanders.set(";", {
symbol: ":", symbol: ":",
fullstop: false, fullstop: false,
}); });
hotkeys.set("F1", {
action: (e) => {
e.preventDefault()
console.log("Open help page")
}
})
hotkeys.set("F12", {
action: (e) => {
e.preventDefault()
console.log("Open import page")
goto("/import")
}
})

View File

@ -1,6 +1,6 @@
import { db } from "../db/main" import { db } from "../db/main"
import { type Shortform } from "../db/main" import { type Shortform } from "../db/main"
export function cacheShortforms(lists: Array<string> = []) { export function cacheShortforms(lists: Array<number> = []) {
let shortforms = new Map(); let shortforms = new Map();
// console.log("Caching shortforms with lists:", lists); // console.log("Caching shortforms with lists:", lists);

View File

@ -5,6 +5,8 @@
let { children } = $props(); let { children } = $props();
</script> </script>
<ParaglideJS {i18n}> <div role="presentation">
{@render children()} <ParaglideJS {i18n}>
</ParaglideJS> {@render children()}
</ParaglideJS>
</div>

View File

@ -6,7 +6,9 @@
import { Button } from '$lib/components/ui/button'; import { Button } from '$lib/components/ui/button';
import { hotkeys } from '../modules/keyboard';
let cache: Array<Shortform>; let cache: Array<Shortform>;
let showDashboard: boolean = true;
db.shortforms db.shortforms
.toArray() .toArray()
.then((shortforms) => { .then((shortforms) => {
@ -20,11 +22,16 @@
deleteShortformList(890324); deleteShortformList(890324);
cache = []; cache = [];
}; };
const handleHotkeys = (e: KeyboardEvent) => {
hotkeys.get(e.key)?.action(e);
};
</script> </script>
<svelte:window on:keydown={handleHotkeys} />
<div class="h-full w-full" role="application"> <div class="h-full w-full" role="application">
<Textarea activeShortforms={cache} /> <Textarea />
<Dashboard open="true" /> <Dashboard open={showDashboard} />
<!-- <Button variant="destructive" on:click={deleteDefaultShortforms}>Ta bort standardlista</Button>--> <!-- <Button variant="destructive" on:click={deleteDefaultShortforms}>Ta bort standardlista</Button>-->
</div> </div>

View File

@ -0,0 +1,7 @@
<script>
import Import from '../../components/import/import.svelte';
</script>
<div class="flex items-center justify-center p-24">
<Import />
</div>