improve local-app
This commit is contained in:
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user