improve local-app

This commit is contained in:
Anton Bracke
2022-02-22 10:05:21 +01:00
parent 6b208ceb0c
commit ac18aab773
30 changed files with 841 additions and 105 deletions
@@ -0,0 +1,16 @@
<script lang="ts">
export let title: string;
export let id: string;
export let description: string = "";
</script>
<div class="mb-6">
<label class="block text-gray-200 text-lg font-bold mb-1" for={id}>
{title}
</label>
<slot />
<!-- <input class="shadow appearance-none border border-red-500 rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline" id="password" type="password" placeholder="******************"> -->
{#if description.length > 0}
<p class="text-gray-200 text-xs mt-1 italic">{description}</p>
{/if}
</div>
+178
View File
@@ -0,0 +1,178 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
export let id: string;
export let value: string = "";
const dispatch = createEventDispatcher<{
change: string;
}>();
// https://www.electronjs.org/de/docs/latest/api/accelerator
function keyCodeToElectron(key: string): string {
if (key.match(/^Key[A-Z]/)) {
return key.replace(/^Key/, "").toLocaleUpperCase();
}
if (key.match(/^Digit[0-9]/)) {
return key.replace(/^Digit/, "").toLowerCase();
}
if (key.match(/^Numpad[0-9]/)) {
return key.replace(/^Numpad/, "num").toLowerCase();
}
switch (key) {
// Modifiers
case "ControlLeft":
return "CmdOrCtrl";
case "ControlRight":
return "CmdOrCtrl";
case "AltLeft":
return "Alt";
case "AltRight":
return "AltGr";
case "ScrollLock":
return "Scrolllock";
case "ShiftLeft":
return "Shift";
case "ShiftRight":
return "Shift";
// Specialchars
case "Period":
return ".";
case "Comma":
return ",";
case "Slash":
return "/";
case "Backslash":
return "\\";
case "Minus":
return "-";
case "Equal":
return "=";
case "BracketLeft":
return "[";
case "BracketRight":
return "]";
case "Quote":
return "'";
case "Semicolon":
return ";";
case "IntlBackslash":
return "\\";
case "Backquote":
return "`";
// Numpad
case "NumpadDecimal":
return "numdec";
case "NumpadAdd":
return "numadd";
case "NumpadSubtract":
return "numsub";
case "NumpadMultiply":
return "nummult";
case "NumpadDivide":
return "numdiv";
default:
return key;
}
}
let shortCut: string[] = [];
let recording = false;
let recordingTimeout: NodeJS.Timeout;
let keyInputTimeout: NodeJS.Timeout;
function camelPad(str: string) {
return (
str
// Look for long acronyms and filter out the last letter
.replace(/([A-Z]+)([A-Z][a-z])/g, " $1 $2")
// Look for lower-case letters followed by upper-case letters
.replace(/([a-z\d])([A-Z])/g, "$1 $2")
// Look for lower-case letters followed by numbers
// .replace(/([a-zA-Z])(\d)/g, "$1 $2")
.replace(/^./, (str) => str.toUpperCase())
// Remove any white space left around the word
.trim()
);
}
function resetRecording() {
recording = false;
shortCut = [];
value = "";
}
function stopRecording() {
clearTimeout(recordingTimeout);
recording = false;
value = shortCut.map(keyCodeToElectron).join(" + ");
dispatch("change", value);
}
function startRecording() {
if (recording) {
return;
}
recording = true;
value = "";
shortCut = [];
recordingTimeout = setTimeout(() => {
stopRecording();
}, 1000 * 5);
}
function keyUp(event: KeyboardEvent) {
if (!recording) {
return;
}
shortCut = [...shortCut, event.code];
if (!keyInputTimeout) {
keyInputTimeout = setTimeout(() => {
stopRecording();
keyInputTimeout = undefined;
}, 300);
}
}
</script>
<div
class={`flex items-center w-full h-8 border-1 rounded-md overflow-hidden text-gray-200 text-xs appearance-none focus:outline-none ${
recording ? "border-red-500" : "border-gray-400"
}`}
on:keyup={keyUp}
on:click={startRecording}
tabindex="0"
>
<input
{id}
type="text"
class="flex-grow h-full border-none mx-2 bg-transparent appearance-none focus:outline-none"
disabled
{value}
/>
{#if value.length > 0}
<span
class="flex items-center justify-center w-4 h-4 p-2 mr-1 rounded-full cursor-pointer bg-gray-500 hover:bg-gray-400"
on:click|stopPropagation={resetRecording}>x</span
>
{/if}
<div
class={`flex h-6 items-center px-2 m-0.5 rounded-sm w-28 justify-center cursor-pointer ${
recording ? "bg-red-500" : "hover:bg-gray-400"
}`}
on:click={recording ? stopRecording : startRecording}
>
{#if recording}
<span>recording</span>
{:else}
<span>record</span>
{/if}
</div>
</div>
+25 -25
View File
@@ -1,35 +1,35 @@
<script>
import { onMount } from "svelte";
import { onMount } from "svelte";
export let component;
export let delayMs = null;
export let component;
export let delayMs = null;
let loadedComponent = null;
let timeout;
let showFallback = !delayMs;
let loadedComponent = null;
let timeout;
let showFallback = !delayMs;
let props;
$: {
// eslint-disable-next-line no-shadow
const { component, delayMs, ...restProps } = $$props;
props = restProps;
}
let props;
$: {
// eslint-disable-next-line no-shadow
const { component, delayMs, ...restProps } = $$props;
props = restProps;
}
onMount(() => {
if (delayMs) {
timeout = setTimeout(() => {
showFallback = true;
}, delayMs);
}
component().then(module => {
loadedComponent = module.default;
});
return () => clearTimeout(timeout);
});
onMount(() => {
if (delayMs) {
timeout = setTimeout(() => {
showFallback = true;
}, delayMs);
}
component().then((module) => {
loadedComponent = module.default;
});
return () => clearTimeout(timeout);
});
</script>
{#if loadedComponent}
<svelte:component this={loadedComponent} {...props} />
<svelte:component this={loadedComponent} {...props} />
{:else if showFallback}
<slot />
<slot />
{/if}
+14 -14
View File
@@ -1,20 +1,20 @@
<script>
import { Route } from "svelte-navigator";
import Lazy from "./Lazy.svelte";
import { Route } from "svelte-navigator";
import Lazy from "~/lib/Lazy.svelte";
export let component;
export let delayMs = null;
export let component;
export let delayMs = null;
let props;
$: {
// eslint-disable-next-line no-shadow
const { component, ...restProps } = $$props;
props = restProps;
}
let props;
$: {
// eslint-disable-next-line no-shadow
const { component, ...restProps } = $$props;
props = restProps;
}
</script>
<Route {...props}>
<Lazy {component} {delayMs}>
<slot />
</Lazy>
</Route>
<Lazy {component} {delayMs}>
<slot />
</Lazy>
</Route>
+11 -12
View File
@@ -1,18 +1,17 @@
<script>
import { navigate } from "svelte-navigator";
import { navigate } from "svelte-navigator";
let to = '/';
let clazz = '';
let to = "/";
let clazz = "";
export { clazz as class, to };
export { clazz as class, to };
async function click() {
await window?.WorkAdventureDesktopApi?.showLocalApp();
navigate(to);
}
async function click() {
await window?.WorkAdventureDesktopApi?.showLocalApp();
navigate(to);
}
</script>
<div on:click="{ click }" class="{`${clazz}`}">
<slot />
</div>
<div on:click={click} class={`${clazz}`}>
<slot />
</div>
+35 -14
View File
@@ -1,16 +1,29 @@
<script lang="ts">
import { onMount } from "svelte";
import Link from "./Link.svelte";
import { servers, selectedServer, selectServer, loadServers } from "../store";
import Link from "~/lib/Link.svelte";
import { servers, selectedServer, selectServer, loadServers, Server } from "../store";
import CogIcon from "~/assets/nes.icons/cog.svg";
let isDevelopment = false;
function getServerColor(i: number) {
const serverColors = ['bg-red-400', 'bg-yellow-500', 'bg-green-500', 'bg-blue-500', 'bg-indigo-500', 'bg-purple-500'];
const serverColors = [
"bg-red-400",
"bg-yellow-500",
"bg-green-500",
"bg-blue-500",
"bg-indigo-500",
"bg-purple-500",
];
return serverColors[i % serverColors.length];
}
selectedServer.subscribe((e) => {
console.log("selected server changed", e);
});
onMount(async () => {
await loadServers();
isDevelopment = await window?.WorkAdventureDesktopApi?.isDevelopment();
@@ -20,29 +33,37 @@
<aside class="flex flex-col bg-gray-700 items-center">
<div class="flex flex-col mt-4 space-y-4 overflow-y-auto pb-4">
{#each $servers as server, i}
<div class="flex flex-col items-center justify-center text-gray-400">
<div class="flex flex-col items-center justify-center ">
<div
class={`w-12 h-12 rounded-lg flex cursor-pointer text-center items-center justify-center text-light-50 ${getServerColor(i)} ${
$selectedServer === server._id ? "" : ""
}`}
class={`w-16 h-16 p-1 rounded-md flex cursor-pointer text-light-50 border-4 border-transparent text-gray-200 hover:text-gray-500`}
class:border-gray-400={$selectedServer === server._id}
on:click={() => selectServer(server)}
>
{server.name.slice(0, 2).toLocaleUpperCase()}
<div class={`flex w-full h-full text-center items-center justify-center rounded-md ${getServerColor(i)}`}>
{server.name.slice(0,2).toLocaleUpperCase()}
</div>
</div>
</div>
{/each}
<Link
to="/server/add"
class="flex justify-center items-center text-4xl no-underline text-gray-400 cursor-pointer">+</Link
>
</div>
<Link
to="/server/add"
class="flex justify-center items-center text-4xl no-underline text-gray-200 cursor-pointer hover:text-gray-500"
>+</Link
>
<Link
to="/settings"
class="flex mt-auto justify-center items-center text-4xl no-underline cursor-pointer"
>
<CogIcon width="30" height="30" class="fill-gray-200 hover:fill-gray-500" />
</Link>
{#if isDevelopment}
<button class="mt-auto" on:click={() => location.reload()}>Refresh</button>
<button class="text-8px text-red-500 mt-8 mb-4" on:click={() => location.reload()}>Refresh</button>
{/if}
</aside>
<style>
aside {
width: 70px;
width: 80px;
}
</style>
@@ -0,0 +1,27 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
export let id: string;
export let value: string = "";
export let required: boolean = false;
const dispatch = createEventDispatcher<{
change: string;
}>();
</script>
<div
class={`flex items-center w-full h-10 border-1 rounded-md overflow-hidden text-gray-200 text-md appearance-none focus:outline-none`}
>
<input
{id}
type="text"
class="flex-grow h-full border-none mx-2 bg-transparent appearance-none focus:outline-none"
{value}
{required}
on:change={(e) => {
value = e.target.value;
dispatch("change", { value });
}}
/>
</div>
@@ -0,0 +1,25 @@
<script lang="ts">
export let id: string;
export let value: boolean;
export let title: string = null;
</script>
<div class="flex w-full my-2">
<label for={id} class="flex items-center cursor-pointer">
<div class="relative">
<input {id} type="checkbox" class="sr-only" checked={value} />
<div class="w-10 h-4 bg-gray-400 rounded-full shadow-inner" />
<div class="dot absolute w-6 h-6 bg-gray-500 rounded-full shadow -left-1 -top-1 transition" />
</div>
{#if title}
<div class="ml-4 text-gray-200 text-sm">{title}</div>
{/if}
</label>
</div>
<style>
input:checked ~ .dot {
transform: translateX(100%);
background-color: #0369a1;
}
</style>