Compare commits

...

3 Commits

8 changed files with 359 additions and 190 deletions

View File

@ -12,23 +12,23 @@
import { Badge } from '$lib/components/ui/badge'; import { Badge } from '$lib/components/ui/badge';
import { Separator } from '$lib/components/ui/separator'; import { Separator } from '$lib/components/ui/separator';
import { db, createShortform, type List, type Shortform } from '../db/main'; import { db, createShortform, type List, type Shortform } from '../db/main';
import { cacheShortform } from "../modules/shortforms"; import { cacheShortform } from '../modules/shortforms';
import { liveQuery } from "dexie"; import { liveQuery } from 'dexie';
import { ScrollArea } from "$lib/components/ui/scroll-area/index.js"; import { ScrollArea } from '$lib/components/ui/scroll-area/index.js';
$: abbForm = { $: abbForm = {
shortform: "", shortform: '',
phrase: "", phrase: '',
target: "", target: '',
errors: [], errors: []
}; };
let importing = false; let importing = false;
let progress = 0; let progress = 0;
const handleCreate = async (e) => { const handleCreate = async (e) => {
// abbForm.errors = [] // abbForm.errors = []
e.preventDefault() e.preventDefault();
/* /*
id?: number; id?: number;
@ -42,55 +42,59 @@
*/ */
const sf = { const sf = {
listid: abbForm.target.value, listid: abbForm.target.value,
shortform: abbForm.shortform.replace(/\s/g,"").toLowerCase(), shortform: abbForm.shortform.replace(/\s/g, '').toLowerCase(),
phrase: abbForm.phrase.trim(), phrase: abbForm.phrase.trim(),
last_used: new Date(0), last_used: new Date(0),
uses: 0, uses: 0,
remind: 0, remind: 0,
updated: Date.now(), updated: Date.now()
} };
//appState.open = "" //appState.open = ""
db.shortforms.where("[listid+shortform]").equals([sf.listid, sf.shortform]).first() db.shortforms
.then(existing => { .where('[listid+shortform]')
sf.id = existing.id .equals([sf.listid, sf.shortform])
db.shortforms.put(sf).then(result => { .first()
.then((existing) => {
sf.id = existing.id;
db.shortforms.put(sf).then((result) => {
cacheShortform(sf); cacheShortform(sf);
appState.open = "" appState.open = '';
}) });
}) })
.catch(() => { .catch(() => {
db.shortforms.put(sf).then(result => { db.shortforms.put(sf).then((result) => {
cacheShortform(sf); cacheShortform(sf);
appState.open = "" appState.open = '';
}) });
}) });
}; };
const cancelCreate = () => { const cancelCreate = () => {
abbForm.shortform = "" abbForm.shortform = '';
abbForm.phrase = "" abbForm.phrase = '';
abbForm.target = {} abbForm.target = {};
abbForm.errors = [] abbForm.errors = [];
appState.open = "" appState.open = '';
}; };
const onSelectedChange = (v) => { const onSelectedChange = (v) => {
console.log(v) console.log(v);
abbForm.target = v abbForm.target = v;
} };
let selectedLists = [] let selectedLists = [];
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 });
abbForm.target = { value: l.id, label: l.name } abbForm.target = { value: l.id, label: l.name };
}) });
console.log("fokusera på input och mata med medhavd data") console.log('fokusera på input och mata med medhavd data');
const el = document.getElementById("shortformEl") const el = document.getElementById('shortformEl');
el.focus() if (el) {
setTimeout(() => {
el.focus();
}, 15);
}
}); });
</script> </script>
<Card.Root class="mx-auto w-[490px]"> <Card.Root class="mx-auto w-[490px]">
@ -99,10 +103,22 @@
<Card.Description></Card.Description> <Card.Description></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" on:submit={handleCreate} autocomplete="off"> <form
class="grid gap-4 py-4"
method="post"
enctype="multipart/form-data"
on:submit={handleCreate}
autocomplete="off"
>
<div class="grid grid-cols-4 items-center gap-4"> <div class="grid grid-cols-4 items-center gap-4">
<Label>Förkortning</Label> <Label>Förkortning</Label>
<Input id="shortformEl" bind:value={abbForm.shortform} focus placeholder="" class="w-[290px]" /> <Input
id="shortformEl"
bind:value={abbForm.shortform}
focus
placeholder=""
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>Text</Label> <Label>Text</Label>
@ -110,24 +126,21 @@
</div> </div>
<div class="grid grid-cols-4 items-center gap-4"> <div class="grid grid-cols-4 items-center gap-4">
<Label>Mål</Label> <Label>Mål</Label>
<Select.Root selected={abbForm.target} onSelectedChange={onSelectedChange}> <Select.Root selected={abbForm.target} {onSelectedChange}>
<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>
{#each selectedLists as list} {#each selectedLists as list}
<Select.Item <Select.Item value={list.id} label={list.name}>{list.name}</Select.Item>
value={list.id}
label={list.name}
>{list.name}</Select.Item>
{/each} {/each}
</Select.Content> </Select.Content>
</Select.Root> </Select.Root>
</div> </div>
{#if abbForm.errors.length > 0} {#if abbForm.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 abbForms.errors as error} {#each abbForms.errors as error}
@ -147,4 +160,3 @@
</Card.Footer> </Card.Footer>
--> -->
</Card.Root> </Card.Root>

View File

@ -21,7 +21,7 @@
$: form = { $: form = {
textarea: '', textarea: '',
name: '', name: '',
type: false type: true
}; };
let importing = false; let importing = false;
let progress = 0; let progress = 0;
@ -92,7 +92,7 @@ förkn=förkortningen
<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>Skapa ämneslista</Label>
<Switch bind:checked={form.type} /> <Switch bind:checked={form.type} />
</div> </div>

View File

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

View File

@ -1,13 +1,21 @@
<script> <script>
import { appState } from "$lib/stores.svelte" import { appState } from '$lib/stores.svelte';
import * as DropdownMenu from "$lib/components/ui/dropdown-menu/index"; import * as DropdownMenu from '$lib/components/ui/dropdown-menu/index';
import { Button } from '$lib/components/ui/button'; import { Button } from '$lib/components/ui/button';
import Gear from "svelte-radix/Gear.svelte"; import Gear from 'svelte-radix/Gear.svelte';
let closeFocus = 'textarea#doc.textarea';
</script> </script>
<DropdownMenu.Root open={appState.menuOpen} onOpenChange={(v) => {appState.menuOpen = v}} closeFocus="textarea#doc.textarea"> <DropdownMenu.Root
open={appState.menuOpen}
onOpenChange={(v) => {
appState.menuOpen = v;
}}
{closeFocus}
>
<DropdownMenu.Trigger asChild let:builder> <DropdownMenu.Trigger asChild let:builder>
<Button builders={[builder]} variant="ghost" class="absolute top-2 right-2" size="icon"> <Button builders={[builder]} variant="ghost" class="absolute right-2 top-2" size="icon">
<Gear class="h-4 w-4" /> <Gear class="h-4 w-4" />
</Button> </Button>
</DropdownMenu.Trigger> </DropdownMenu.Trigger>
@ -15,7 +23,11 @@
<DropdownMenu.Label>Inställningar</DropdownMenu.Label> <DropdownMenu.Label>Inställningar</DropdownMenu.Label>
<DropdownMenu.Separator /> <DropdownMenu.Separator />
<DropdownMenu.Group> <DropdownMenu.Group>
<DropdownMenu.Item on:click={() => {appState.open="create"}}> <DropdownMenu.Item
on:click={() => {
appState.open = 'create';
}}
>
Lägg till förkortning Lägg till förkortning
<DropdownMenu.Shortcut>F2</DropdownMenu.Shortcut> <DropdownMenu.Shortcut>F2</DropdownMenu.Shortcut>
</DropdownMenu.Item> </DropdownMenu.Item>
@ -26,6 +38,14 @@
</DropdownMenu.Group> </DropdownMenu.Group>
<DropdownMenu.Separator /> <DropdownMenu.Separator />
<DropdownMenu.Group> <DropdownMenu.Group>
<DropdownMenu.Item
on:click={() => {
appState.open = 'selectLists';
}}
>
Välj listor
<DropdownMenu.Shortcut>F12</DropdownMenu.Shortcut>
</DropdownMenu.Item>
<DropdownMenu.Item> <DropdownMenu.Item>
Förkortningar Förkortningar
<DropdownMenu.Shortcut>F12</DropdownMenu.Shortcut> <DropdownMenu.Shortcut>F12</DropdownMenu.Shortcut>
@ -64,10 +84,12 @@
Hjälp Hjälp
<DropdownMenu.Shortcut>F12</DropdownMenu.Shortcut> <DropdownMenu.Shortcut>F12</DropdownMenu.Shortcut>
</DropdownMenu.Item> </DropdownMenu.Item>
<DropdownMenu.Item>Feedback <DropdownMenu.Item
>Feedback
<DropdownMenu.Shortcut>F12</DropdownMenu.Shortcut> <DropdownMenu.Shortcut>F12</DropdownMenu.Shortcut>
</DropdownMenu.Item> </DropdownMenu.Item>
<DropdownMenu.Item>Användarkonto <DropdownMenu.Item
>Användarkonto
<DropdownMenu.Shortcut>F12</DropdownMenu.Shortcut> <DropdownMenu.Shortcut>F12</DropdownMenu.Shortcut>
</DropdownMenu.Item> </DropdownMenu.Item>
<DropdownMenu.Separator /> <DropdownMenu.Separator />

View File

@ -32,7 +32,7 @@ const colorThemes = [
let selectedColor = 0; let selectedColor = 0;
export const shortforms = $state({ standardList: "", subjectLists: [], cache: new Map() }) export const shortforms = $state({ standardList: "", subjectList: "", cache: new Map() })
export const importState = $state({ data: "", errors: [] }) export const importState = $state({ data: "", errors: [] })
export const textSettings = $state({ font: "Arial", size: 30, colors: colorSetting, padding: 0, lineheight: 1 }) export const textSettings = $state({ font: "Arial", size: 30, colors: colorSetting, padding: 0, lineheight: 1 })
export const appState = $state({ text: "", menuOpen: false, open: "" }) export const appState = $state({ text: "", menuOpen: false, open: "" })
@ -58,10 +58,15 @@ export const selectNextColor = () => {
textSettings.colors = colorThemes[(selectedColor % 6)] textSettings.colors = colorThemes[(selectedColor % 6)]
} }
export const toggleMenuOpen = () => { export const toggleMenuOpen = (force = undefined) => {
if (force == undefined) {
appState.menuOpen = !appState.menuOpen appState.menuOpen = !appState.menuOpen
} else {
appState.menuOpen = force
}
} }
export const incTextSize = () => { export const incTextSize = () => {
console.log(textSettings.size) console.log(textSettings.size)
if (textSettings.size < 200) { if (textSettings.size < 200) {

View File

@ -9,7 +9,7 @@ export const defaultExpanders: Map<string, ExpanderType> = new Map();
export const hotkeys: Map<string, any> = new Map(); export const hotkeys: Map<string, any> = new Map();
defaultExpanders.set("\n", { defaultExpanders.set("\n", {
key: { keyCode: 13, shiftKey: false }, key: { keyCode: 188, shiftKey: false },
symbol: "\n", symbol: "\n",
fullstop: true, fullstop: true,
}); });
@ -136,6 +136,7 @@ hotkeys.set("F8", {
hotkeys.set("F2", { hotkeys.set("F2", {
action: (e) => { action: (e) => {
e.preventDefault() e.preventDefault()
toggleMenuOpen(false)
appState.open = "create" appState.open = "create"
} }
}) })

View File

@ -49,6 +49,8 @@ export function cacheShortforms(lists: Array<number> = []) {
}) })
} }
shortforms.cache = cache
return cache; return cache;
} }
export function expandShortform( export function expandShortform(

View File

@ -2,46 +2,55 @@
import Textarea from '../components/textarea.svelte'; import Textarea from '../components/textarea.svelte';
import Import from '../components/import/import.svelte'; import Import from '../components/import/import.svelte';
import Create from '../components/create.svelte'; import Create from '../components/create.svelte';
import ListSelector from '../components/listselector.svelte';
import Dashboard from '../components/dashboard.svelte'; import Dashboard from '../components/dashboard.svelte';
import { db, deleteShortformList, type Shortform } from '../db/main'; import { db, deleteShortformList, type Shortform } from '../db/main';
import Menu from "../components/menu.svelte"; import Menu from '../components/menu.svelte';
import { Button } from '$lib/components/ui/button'; import { Button } from '$lib/components/ui/button';
import { appState } from '$lib/stores.svelte' import { appState } from '$lib/stores.svelte';
let loaded = false let loaded = false;
$effect(() => { $effect(() => {
appState.open appState.open;
if (loaded) { if (loaded) {
if (appState.open == "") { if (appState.open == '') {
textarea.focus() textarea.focus();
} }
if (appState.open == "dashboard") { if (appState.open == 'dashboard') {
console.log("should open dashboard") console.log('should open dashboard');
showDashboard = true showDashboard = true;
} }
} }
loaded = true loaded = true;
});
})
import { hotkeys } from '../modules/keyboard'; import { hotkeys } from '../modules/keyboard';
let showDashboard: boolean = true; let showDashboard: boolean = true;
import { Toaster } from "$lib/components/ui/sonner"; import { Toaster } from '$lib/components/ui/sonner';
const handleHotkeys = (e: KeyboardEvent) => { const handleHotkeys = (e: KeyboardEvent) => {
hotkeys.get(e.key)?.action(e); hotkeys.get(e.key)?.action(e);
}; };
let textarea = null let textarea = null;
</script> </script>
<svelte:head> <svelte:head>
<title>SKRIVERT</title> <title>SKRIVERT</title>
</svelte:head> </svelte:head>
<svelte:window on:keydown={handleHotkeys} /> <svelte:window on:keydown={handleHotkeys} />
<div class="h-dvh w-full overflow-hidden" role="application"> <div class="h-dvh w-full overflow-hidden" role="application">
{#if appState.open == "create"} {#if appState.open == 'create'}
<div class="fixed flex top-0 right-0 left-0 z-50 justify-center items-center align-middle w-full md:inset-0 h-[calc(100%-1rem)] max-h-full"> <div
class="fixed left-0 right-0 top-0 z-50 flex h-[calc(100%-1rem)] max-h-full w-full items-center justify-center align-middle md:inset-0"
>
<Create /> <Create />
</div> </div>
{/if} {/if}
{#if appState.open == 'selectLists'}
<div
class="fixed left-0 right-0 top-0 z-50 flex h-[calc(100%-1rem)] max-h-full w-full items-center justify-center align-middle md:inset-0"
>
<ListSelector />
</div>
{/if}
<div class="h-full"> <div class="h-full">
<Menu /> <Menu />
<Textarea bind:ref={textarea} /> <Textarea bind:ref={textarea} />