klick utanför ruta stänger den

This commit is contained in:
botvid johansson 2025-02-14 14:33:16 +01:00
parent 9da2421b31
commit be98f2b189
7 changed files with 201 additions and 151 deletions

7
package-lock.json generated
View File

@ -14,6 +14,7 @@
"@oslojs/encoding": "^1.1.0", "@oslojs/encoding": "^1.1.0",
"@tauri-apps/api": "^2.2.0", "@tauri-apps/api": "^2.2.0",
"dexie": "^4.0.11", "dexie": "^4.0.11",
"svelte-outside": "^0.0.3",
"svelte-radix": "^2.0.1" "svelte-radix": "^2.0.1"
}, },
"devDependencies": { "devDependencies": {
@ -6845,6 +6846,12 @@
"url": "https://opencollective.com/eslint" "url": "https://opencollective.com/eslint"
} }
}, },
"node_modules/svelte-outside": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/svelte-outside/-/svelte-outside-0.0.3.tgz",
"integrity": "sha512-4mJttaDRXkBBL+8JqjfA1P5Ny64qmkZL3x5zELAW3tqehic3LLHvPNDcGAk8PVRooM2qUym9Oz3TfA9lO4OctA==",
"license": "MIT"
},
"node_modules/svelte-radix": { "node_modules/svelte-radix": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/svelte-radix/-/svelte-radix-2.0.1.tgz", "resolved": "https://registry.npmjs.org/svelte-radix/-/svelte-radix-2.0.1.tgz",

View File

@ -58,6 +58,7 @@
"@oslojs/encoding": "^1.1.0", "@oslojs/encoding": "^1.1.0",
"@tauri-apps/api": "^2.2.0", "@tauri-apps/api": "^2.2.0",
"dexie": "^4.0.11", "dexie": "^4.0.11",
"svelte-outside": "^0.0.3",
"svelte-radix": "^2.0.1" "svelte-radix": "^2.0.1"
} }
} }

View File

@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { clickOutside } from 'svelte-outside';
import { appState, shortforms, importState } from '$lib/stores.svelte'; import { appState, shortforms, importState } from '$lib/stores.svelte';
import * as Card from '$lib/components/ui/card/index.js'; import * as Card from '$lib/components/ui/card/index.js';
import { Input } from '$lib/components/ui/input'; import { Input } from '$lib/components/ui/input';
@ -69,11 +70,14 @@
}); });
}; };
const cancelCreate = () => { const cancelCreate = () => {
abbForm.shortform = ''; if (show) {
abbForm.phrase = ''; abbForm.shortform = '';
abbForm.target = {}; abbForm.phrase = '';
abbForm.errors = []; abbForm.target = {};
appState.open = ''; abbForm.errors = [];
appState.open = '';
show = false;
}
}; };
const onSelectedChange = (v) => { const onSelectedChange = (v) => {
@ -81,7 +85,7 @@
abbForm.target = v; abbForm.target = v;
}; };
let selectedLists = []; let selectedLists = [];
let show = false;
onMount(() => { onMount(() => {
db.lists.get(shortforms.standardList).then((l) => { db.lists.get(shortforms.standardList).then((l) => {
selectedLists.push({ value: l.id, label: l.name }); selectedLists.push({ value: l.id, label: l.name });
@ -91,72 +95,78 @@
const el = document.getElementById('shortformEl'); const el = document.getElementById('shortformEl');
if (el) { if (el) {
setTimeout(() => { setTimeout(() => {
show = true;
el.focus(); el.focus();
}, 15); }, 15);
} }
}); });
</script> </script>
<Card.Root class="mx-auto w-[490px]"> <div use:clickOutside={cancelCreate}>
<Card.Header> <Card.Root class="mx-auto w-[490px]">
<Card.Title>Lägg till förkortning</Card.Title> <Card.Header>
<Card.Description></Card.Description> <Card.Title>Lägg till förkortning</Card.Title>
</Card.Header> <Card.Description></Card.Description>
<Card.Content class="mx-auto"> </Card.Header>
<form <Card.Content class="mx-auto">
class="grid gap-4 py-4" <form
method="post" class="grid gap-4 py-4"
enctype="multipart/form-data" method="post"
on:submit={handleCreate} enctype="multipart/form-data"
autocomplete="off" on:submit={handleCreate}
> autocomplete="off"
<div class="grid grid-cols-4 items-center gap-4"> >
<Label>Förkortning</Label> <div class="grid grid-cols-4 items-center gap-4">
<Input <Label>Förkortning</Label>
id="shortformEl" <Input
bind:value={abbForm.shortform} id="shortformEl"
focus bind:value={abbForm.shortform}
placeholder="" focus
class="w-[290px]" placeholder=""
/> class="w-[290px]"
</div> />
<div class="grid grid-cols-4 items-center gap-4"> </div>
<Label>Text</Label> <div class="grid grid-cols-4 items-center gap-4">
<Input id="phraseEl" bind:value={abbForm.phrase} placeholder="" class="w-[290px]" /> <Label>Text</Label>
</div> <Input id="phraseEl" bind:value={abbForm.phrase} placeholder="" class="w-[290px]" />
<div class="grid grid-cols-4 items-center gap-4"> </div>
<Label>Mål</Label> <div class="grid grid-cols-4 items-center gap-4">
<Select.Root selected={abbForm.target} {onSelectedChange}> <Label>Mål</Label>
<Select.Trigger class="w-[290px]"> <Select.Root selected={abbForm.target} {onSelectedChange}>
<Select.Value placeholder="Ingen lista vald" /> <Select.Trigger class="w-[290px]">
</Select.Trigger> <Select.Value placeholder="Ingen lista vald" />
</Select.Trigger>
<Select.Content> <Select.Content>
{#each selectedLists as list} {#each selectedLists as list}
<Select.Item value={list.id} label={list.name}>{list.name}</Select.Item> <Select.Item value={list.id} label={list.name}>{list.name}</Select.Item>
{/each}
</Select.Content>
</Select.Root>
</div>
{#if abbForm.errors.length > 0}
<ScrollArea class="max-h-48 rounded-md">
<div class="border-l-4 border-orange-500 bg-orange-100 p-4 text-orange-700" role="alert">
<p class="font-bold">Fel vid import</p>
<ul class="p-6">
{#each abbForms.errors as error}
<li class="list-disc">{error}</li>
{/each} {/each}
</ul> </Select.Content>
</div> </Select.Root>
</ScrollArea> </div>
{/if} {#if abbForm.errors.length > 0}
<button type="submit" class="hidden" /> <ScrollArea class="max-h-48 rounded-md">
</form> <div
</Card.Content> class="border-l-4 border-orange-500 bg-orange-100 p-4 text-orange-700"
<!-- role="alert"
>
<p class="font-bold">Fel vid import</p>
<ul class="p-6">
{#each abbForms.errors as error}
<li class="list-disc">{error}</li>
{/each}
</ul>
</div>
</ScrollArea>
{/if}
<button type="submit" class="hidden" />
</form>
</Card.Content>
<!--
<Card.Footer class="flex justify-between"> <Card.Footer class="flex justify-between">
<Button on:click={cancelCreate} variant="outline">Avbryt</Button> <Button on:click={cancelCreate} variant="outline">Avbryt</Button>
<Button on:click={handleCreate}>Importera</Button> <Button on:click={handleCreate}>Importera</Button>
</Card.Footer> </Card.Footer>
--> -->
</Card.Root> </Card.Root>
</div>

View File

@ -16,7 +16,7 @@
import { importDefaultShortforms, importShortforms } from '../../db/import'; import { importDefaultShortforms, importShortforms } from '../../db/import';
import { createShortformList, type List } from '../../db/main'; import { createShortformList, type List } from '../../db/main';
import { ScrollArea } from "$lib/components/ui/scroll-area/index.js"; import { ScrollArea } from '$lib/components/ui/scroll-area/index.js';
$: form = { $: form = {
textarea: '', textarea: '',
@ -26,35 +26,48 @@
let importing = false; let importing = false;
let progress = 0; let progress = 0;
const handleImport = async () => { const handleImport = async () => {
importState.errors = [] importState.errors = [];
if (form.textarea == "") { if (form.textarea == '') {
console.error("no name error") console.error('no name error');
importState.errors.push("Importfältet är tomt") importState.errors.push('Importfältet är tomt');
return return;
} }
if (form.name == "") { if (form.name == '') {
console.error("no name error") console.error('no name error');
importState.errors.push("Du måste döpa förkortningslistan till något") importState.errors.push('Du måste döpa förkortningslistan till något');
return return;
} }
importing = true; importing = true;
const l: List = { name: form.name, type: form.type ? 1 : 0, updated: new Date() }; const l: List = { name: form.name, type: form.type ? 1 : 0, updated: new Date() };
const listid = await createShortformList(l); const listid = await createShortformList(l);
let sfs = [] let sfs = [];
form.textarea.split('\n').forEach((line) => { const lines = form.textarea.split('\n');
const fields = line.split("=") for (let i = 0; i < lines.length; i += 1) {
const fields = lines[i].split('=');
if (fields.length == 2) { if (fields.length == 2) {
sfs.push({shortform: fields[0], phrase: fields[1], used: 0, listid: listid}) sfs.push({ shortform: fields[0], phrase: fields[1], used: 0, listid: listid });
setTimeout(() => {
progress = ((i + 1) / lines.length) * 100;
}, 1);
} }
}); }
importShortforms(sfs) importShortforms(sfs);
goto('/');
}; };
const cancelImport = () => { const cancelImport = () => {
importState.data = ''; importState.data = '';
importState.errors = []; importState.errors = [];
importState.name = ""; importState.name = '';
appState.open = "" appState.open = '';
}; };
const onsubmit = (e) => {
e.preventDefault();
console.log('yooo');
handleImport();
};
const importDefaultList = () => { const importDefaultList = () => {
importState.data = ''; importState.data = '';
importState.errors = []; importState.errors = [];
@ -62,6 +75,7 @@
importState.data += sf.sf + '=' + sf.p + '\n'; importState.data += sf.sf + '=' + sf.p + '\n';
}); });
}; };
onMount(() => { onMount(() => {
form.textarea = importState.data; form.textarea = importState.data;
}); });
@ -73,7 +87,7 @@
<Card.Description>Nån annan hjälptext.</Card.Description> <Card.Description>Nån annan hjälptext.</Card.Description>
</Card.Header> </Card.Header>
<Card.Content class="mx-auto"> <Card.Content class="mx-auto">
<form class="grid gap-4 py-4" method="post" enctype="multipart/form-data"> <form class="grid gap-4 py-4" {onsubmit}>
<div class="grid grid-cols-4 items-center gap-4"> <div class="grid grid-cols-4 items-center gap-4">
<Label>Förkortningar</Label> <Label>Förkortningar</Label>
<textarea <textarea
@ -97,21 +111,25 @@ förkn=förkortningen
</div> </div>
{#if importState.errors.length > 0} {#if importState.errors.length > 0}
<ScrollArea class="max-h-48 rounded-md"> <ScrollArea class="max-h-48 rounded-md">
<div class="bg-orange-100 border-l-4 border-orange-500 text-orange-700 p-4" role="alert"> <div class="border-l-4 border-orange-500 bg-orange-100 p-4 text-orange-700" role="alert">
<p class="font-bold">Fel vid import</p> <p class="font-bold">Fel vid import</p>
<ul class="p-6"> <ul class="p-6">
{#each importState.errors as error} {#each importState.errors as error}
<li class="list-disc">{error}</li> <li class="list-disc">{error}</li>
{/each} {/each}
</ul> </ul>
</div> </div>
</ScrollArea> </ScrollArea>
{/if} {/if}
</form> </form>
</Card.Content> </Card.Content>
<Card.Footer class="flex justify-between"> <Card.Footer class="flex justify-between">
<Button on:click={cancelImport} variant="outline">Avbryt</Button> {#if importing}
<Button on:click={handleImport}>Importera</Button> <Progress value={progress} />
{:else}
<Button on:click={cancelImport} variant="outline">Avbryt</Button>
<Button on:click={onsubmit}>Importera</Button>
{/if}
</Card.Footer> </Card.Footer>
</Card.Root> </Card.Root>

View File

@ -1,12 +1,12 @@
<script> <script>
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { clickOutside } from 'svelte-outside';
import * as Card from '$lib/components/ui/card/index'; import * as Card from '$lib/components/ui/card/index';
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';
import { db } from '../db/main'; import { db } from '../db/main';
import { liveQuery } from 'dexie'; import { liveQuery } from 'dexie';
import { shortforms } from '$lib/stores.svelte'; import { appState, shortforms } from '$lib/stores.svelte';
import { cacheShortforms } from '../modules/shortforms'; import { cacheShortforms } from '../modules/shortforms';
const onSelectedStandardListChange = (e) => { const onSelectedStandardListChange = (e) => {
@ -43,7 +43,8 @@
let standardLists = []; let standardLists = [];
let subjectLists = []; let subjectLists = [];
onMount(() => { let show = false;
onMount(async () => {
//console.log($state.snapshot(shortforms)); //console.log($state.snapshot(shortforms));
db.lists.toArray().then((lists) => { db.lists.toArray().then((lists) => {
standardLists = lists.filter((l) => l.type == 0); standardLists = lists.filter((l) => l.type == 0);
@ -54,65 +55,78 @@
listSelectorForm.subject = { value: subject.id, label: subject.name }; listSelectorForm.subject = { value: subject.id, label: subject.name };
console.log(listSelectorForm); console.log(listSelectorForm);
}); });
setTimeout(() => {
show = true;
}, 15);
}); });
const close = () => {
if (show == true) {
appState.open = '';
}
};
</script> </script>
<Card.Root class="mx-auto w-[490px]"> {#if show}
<Card.Header> <div use:clickOutside={close}>
<Card.Title>Välj förkortningslistor</Card.Title> <Card.Root class="mx-auto w-[490px]">
<Card.Description></Card.Description> <Card.Header>
</Card.Header> <Card.Title>Välj förkortningslistor</Card.Title>
<Card.Content class="mx-auto"> <Card.Description></Card.Description>
<form </Card.Header>
class="grid gap-4 py-4" <Card.Content class="mx-auto">
method="post" <form
enctype="multipart/form-data" class="grid gap-4 py-4"
onsubmit={onSubmit} method="post"
autocomplete="off" enctype="multipart/form-data"
> onsubmit={onSubmit}
<div class="grid grid-cols-4 items-center gap-4"> autocomplete="off"
<Label>Standardlista</Label>
<Select.Root
selected={listSelectorForm.standard}
onSelectedChange={onSelectedStandardListChange}
> >
<Select.Trigger class="w-[290px]"> <div class="grid grid-cols-4 items-center gap-4">
<Select.Value placeholder="Ingen lista vald" /> <Label>Standardlista</Label>
</Select.Trigger> <Select.Root
selected={listSelectorForm.standard}
onSelectedChange={onSelectedStandardListChange}
>
<Select.Trigger class="w-[290px]">
<Select.Value placeholder="Ingen lista vald" />
</Select.Trigger>
<Select.Content> <Select.Content>
{#each standardLists as list} {#each standardLists as list}
<Select.Item value={list.id} label={list.name}>{list.name}</Select.Item> <Select.Item value={list.id} label={list.name}>{list.name}</Select.Item>
{/each} {/each}
</Select.Content> </Select.Content>
</Select.Root> </Select.Root>
</div> </div>
<div class="grid grid-cols-4 items-center gap-4"> <div class="grid grid-cols-4 items-center gap-4">
<Label>Ämneslista</Label> <Label>Ämneslista</Label>
<Select.Root <Select.Root
selected={listSelectorForm.subject} selected={listSelectorForm.subject}
onSelectedChange={onSelectedSubjectListChange} onSelectedChange={onSelectedSubjectListChange}
disabled={subjectLists.length < 1} disabled={subjectLists.length < 1}
> >
<Select.Trigger class="w-[290px]"> <Select.Trigger class="w-[290px]">
<Select.Value placeholder="Ingen lista vald" /> <Select.Value placeholder="Ingen lista vald" />
</Select.Trigger> </Select.Trigger>
<Select.Content> <Select.Content>
<Select.Item value={0} label="Ingen lista vald">Ingen lista vald</Select.Item> <Select.Item value={0} label="Ingen lista vald">Ingen lista vald</Select.Item>
{#each subjectLists as list} {#each subjectLists as list}
<Select.Item value={list.id} label={list.name}>{list.name}</Select.Item> <Select.Item value={list.id} label={list.name}>{list.name}</Select.Item>
{/each} {/each}
</Select.Content> </Select.Content>
</Select.Root> </Select.Root>
</div> </div>
<button type="submit" class="hidden" /> <button type="submit" class="hidden" />
</form> </form>
</Card.Content> </Card.Content>
<!-- <!--
<Card.Footer class="flex justify-between"> <Card.Footer class="flex justify-between">
<Button on:click={cancelCreate} variant="outline">Avbryt</Button> <Button on:click={cancelCreate} variant="outline">Avbryt</Button>
<Button on:click={handleCreate}>Importera</Button> <Button on:click={handleCreate}>Importera</Button>
</Card.Footer> </Card.Footer>
--> -->
</Card.Root> </Card.Root>
</div>
{/if}

View File

@ -114,8 +114,8 @@
" "
class="textarea h-full flex-grow" class="textarea h-full flex-grow"
id="doc" id="doc"
on:keyup={change} onkeyup={change}
on:beforeinput={input} onbeforeinput={input}
bind:value={appState.text} bind:value={appState.text}
spellcheck="false" spellcheck="false"
></textarea> ></textarea>

View File

@ -51,8 +51,8 @@
<ListSelector /> <ListSelector />
</div> </div>
{/if} {/if}
<Menu />
<div class="h-full"> <div class="h-full">
<Menu />
<Textarea bind:ref={textarea} /> <Textarea bind:ref={textarea} />
<!-- <Button variant="destructive" on:click={deleteDefaultShortforms}>Ta bort standardlista</Button>--> <!-- <Button variant="destructive" on:click={deleteDefaultShortforms}>Ta bort standardlista</Button>-->
<!-- <Dashboard open={showDashboard} />--> <!-- <Dashboard open={showDashboard} />-->