diff options
Diffstat (limited to 'apps/kit/src')
21 files changed, 511 insertions, 434 deletions
diff --git a/apps/kit/src/lib/components/alert.svelte b/apps/kit/src/lib/components/alert.svelte index fa36a63..31e7357 100644 --- a/apps/kit/src/lib/components/alert.svelte +++ b/apps/kit/src/lib/components/alert.svelte @@ -3,13 +3,7 @@ import { createEventDispatcher } from "svelte"; import { onMount } from "svelte"; import { Temporal } from "temporal-polyfill"; - import { - ExclamationTriangle, - CheckCircle, - InformationCircle, - XCircle, - XMark, - } from "./icons"; + import { ExclamationTriangleIcon, CheckCircleIcon, InformationCircleIcon, XCircleIcon, XMarkIcon } from "./icons"; const dispatch = createEventDispatcher(); const noCooldownSetting = "no-cooldown"; @@ -58,46 +52,40 @@ * Text is the button text * Color is the optional tailwind color to used, the value is used in classes like bg-$color-50. */ - export let actions: Array<{ id: string; text: string; color?: string }> = - []; + export let actions: Array<{ id: string; text: string; color?: string }> = []; /** * This value is set on a plain anchor tag without any svelte routing, * listen to the on:rightLinkClick if you want to intercept the click without navigating */ export let rightLinkHref = "javascript:void(0)"; $: cooldownEnabled = - id.indexOf(noCooldownSetting) === -1 && - closeable && - (closeableCooldown === "~" || parseInt(closeableCooldown) > 0); + id.indexOf(noCooldownSetting) === -1 && closeable && (closeableCooldown === "~" || parseInt(closeableCooldown) > 0); /** * Sets this alerts visibility state, when this is false it is removed from the dom using svelte a {#if} block. */ - export let visible = - closeableCooldown === "~" || parseInt(closeableCooldown) > 0 - ? false - : true; + export let visible = closeableCooldown === "~" || parseInt(closeableCooldown) > 0 ? false : true; const cooldownStorageKey = "lastseen--" + id; $: switch (type) { case "info": { colorClassPart = "blue"; - iconComponent = InformationCircle; + iconComponent = InformationCircleIcon; break; } case "warning": { colorClassPart = "yellow"; - iconComponent = ExclamationTriangle; + iconComponent = ExclamationTriangleIcon; break; } case "error": { colorClassPart = "red"; - iconComponent = XCircle; + iconComponent = XCircleIcon; break; } case "success": { colorClassPart = "green"; - iconComponent = CheckCircle; + iconComponent = CheckCircleIcon; break; } } @@ -105,15 +93,8 @@ function close() { visible = false; if (cooldownEnabled) { - console.log( - "Cooldown enabled for " + id + ", " + closeableCooldown === "~" - ? "with an endless cooldown" - : "" - ); - localStorage.setItem( - cooldownStorageKey, - String(Temporal.Now.instant().epochSeconds) - ); + console.log("Cooldown enabled for " + id + ", " + closeableCooldown === "~" ? "with an endless cooldown" : ""); + localStorage.setItem(cooldownStorageKey, String(Temporal.Now.instant().epochSeconds)); } } @@ -148,15 +129,8 @@ return; } - const lastSeen = Temporal.Instant.fromEpochSeconds( - parseInt(localStorage.getItem(cooldownStorageKey) ?? "-1") - ); - if ( - Temporal.Instant.compare( - Temporal.Now.instant(), - lastSeen.add({ seconds: parseInt(closeableCooldown) }) - ) === 1 - ) { + const lastSeen = Temporal.Instant.fromEpochSeconds(parseInt(localStorage.getItem(cooldownStorageKey) ?? "-1")); + if (Temporal.Instant.compare(Temporal.Now.instant(), lastSeen.add({ seconds: parseInt(closeableCooldown) })) === 1) { console.log( "Alert " + id + @@ -177,31 +151,19 @@ run_cooldown(); } - if ( - closeable && - closeableCooldown && - id.indexOf(noCooldownSetting) !== -1 - ) { + if (closeable && closeableCooldown && id.indexOf(noCooldownSetting) !== -1) { // TODO: This prints twice before shutting up as it should, in this example look at the only alert with closeableCooldown in alertsbook. // Looks like svelte mounts three times and that my id is only set on the third. Not sure it does at all after logging the id onMount. - console.error( - "Alert cooldown does not work without specifying a unique id, related id: " + - id - ); + console.error("Alert cooldown does not work without specifying a unique id, related id: " + id); } }); </script> {#if visible} - <div - class="rounded-md bg-{colorClassPart}-50 p-4 {$$restProps.class ?? ''}" - > + <div class="rounded-md bg-{colorClassPart}-50 p-4 {$$restProps.class ?? ''}"> <div class="flex"> <div class="flex-shrink-0"> - <svelte:component - this={iconComponent} - class="text-{colorClassPart}-400" - /> + <svelte:component this={iconComponent} class="text-{colorClassPart}-400" /> </div> <div class="ml-3 text-sm w-full"> {#if !rightLinkText} @@ -211,20 +173,14 @@ </h3> {/if} {#if message} - <div - class="{title - ? 'mt-2' - : ''} text-{colorClassPart}-700 justify-start" - > + <div class="{title ? 'mt-2' : ''} text-{colorClassPart}-700 justify-start"> <p> {@html message} </p> </div> {/if} {#if listItems?.length ?? 0} - <ul - class="list-disc space-y-1 pl-5 text-{colorClassPart}-700" - > + <ul class="list-disc space-y-1 pl-5 text-{colorClassPart}-700"> {#each listItems as listItem} <li>{listItem}</li> {/each} @@ -234,27 +190,19 @@ <div class="flex-1 md:flex md:justify-between"> <div> {#if title} - <h3 - class="font-medium text-{colorClassPart}-800" - > + <h3 class="font-medium text-{colorClassPart}-800"> {title} </h3> {/if} {#if message} - <div - class="{title - ? 'mt-2' - : ''} text-{colorClassPart}-700 justify-start" - > + <div class="{title ? 'mt-2' : ''} text-{colorClassPart}-700 justify-start"> <p> {@html message} </p> </div> {/if} {#if listItems?.length ?? 0} - <ul - class="list-disc space-y-1 pl-5 text-{colorClassPart}-700" - > + <ul class="list-disc space-y-1 pl-5 text-{colorClassPart}-700"> {#each listItems as listItem} <li>{listItem}</li> {/each} @@ -307,7 +255,7 @@ class="inline-flex rounded-md bg-{colorClassPart}-50 p-1.5 text-{colorClassPart}-500 hover:bg-{colorClassPart}-100 focus:outline-none focus:ring-2 focus:ring-{colorClassPart}-600 focus:ring-offset-2 focus:ring-offset-{colorClassPart}-50" > <span class="sr-only">Dismiss</span> - <XMark /> + <XMarkIcon /> </button> </div> </div> diff --git a/apps/kit/src/lib/components/button.svelte b/apps/kit/src/lib/components/button.svelte index 323c7ad..d345b37 100644 --- a/apps/kit/src/lib/components/button.svelte +++ b/apps/kit/src/lib/components/button.svelte @@ -4,7 +4,7 @@ </script> <script lang="ts"> - import { Spinner } from "./icons"; + import { SpinnerIcon } from "./icons"; export let kind = "primary" as ButtonKind; export let size = "md" as ButtonSize; @@ -54,18 +54,15 @@ $: switch (kind) { case "secondary": - kindClasses = - "border-transparent text-teal-800 bg-teal-100 hover:bg-teal-200 focus:ring-teal-500"; + kindClasses = "border-transparent text-teal-800 bg-teal-100 hover:bg-teal-200 focus:ring-teal-500"; spinnerTextClasses = "teal-800"; break; case "primary": - kindClasses = - "border-transparent text-teal-900 bg-teal-300 hover:bg-teal-400 focus:ring-teal-500"; + kindClasses = "border-transparent text-teal-900 bg-teal-300 hover:bg-teal-400 focus:ring-teal-500"; spinnerTextClasses = "text-teal-900"; break; case "white": - kindClasses = - "border-gray-300 text-gray-700 bg-white hover:bg-gray-50 focus:ring-gray-400"; + kindClasses = "border-gray-300 text-gray-700 bg-white hover:bg-gray-50 focus:ring-gray-400"; spinnerTextClasses = "text-gray-700"; break; } @@ -76,14 +73,12 @@ {...shared_props} {href} on:click - class="{sizeClasses} {kindClasses} {loading - ? 'disabled:' - : ''} {$$restProps.class ?? ''} {fullWidth + class="{sizeClasses} {kindClasses} {loading ? 'disabled:' : ''} {$$restProps.class ?? ''} {fullWidth ? 'w-full justify-center' : ''} inline-flex items-center border font-medium rounded shadow-sm focus:outline-none focus:ring-2" > {#if loading} - <Spinner class={spinnerTextClasses + " " + spinnerMarginClasses} /> + <SpinnerIcon class={spinnerTextClasses + " " + spinnerMarginClasses} /> {/if} {text} </a> @@ -95,7 +90,7 @@ : ''} inline-flex items-center border font-medium rounded shadow-sm focus:outline-none focus:ring-2" > {#if loading} - <Spinner class={spinnerTextClasses + " " + spinnerMarginClasses} /> + <SpinnerIcon class={spinnerTextClasses + " " + spinnerMarginClasses} /> {/if} {text} </button> diff --git a/apps/kit/src/lib/components/icons/bars-3-center-left.svelte b/apps/kit/src/lib/components/icons/bars-3-center-left.svelte new file mode 100644 index 0000000..785ece3 --- /dev/null +++ b/apps/kit/src/lib/components/icons/bars-3-center-left.svelte @@ -0,0 +1,15 @@ +<svg + class="h-6 w-6 {$$restProps.class ?? ''}" + xmlns="http://www.w3.org/2000/svg" + fill="none" + viewBox="0 0 24 24" + stroke-width="1.5" + stroke="currentColor" + aria-hidden="true" +> + <path + stroke-linecap="round" + stroke-linejoin="round" + d="M3.75 6.75h16.5M3.75 12H12m-8.25 5.25h16.5" + /> +</svg> diff --git a/apps/kit/src/lib/components/icons/calendar.svelte b/apps/kit/src/lib/components/icons/calendar.svelte new file mode 100644 index 0000000..e0053ee --- /dev/null +++ b/apps/kit/src/lib/components/icons/calendar.svelte @@ -0,0 +1,14 @@ +<svg + xmlns="http://www.w3.org/2000/svg" + fill="none" + viewBox="0 0 24 24" + stroke-width="1.5" + stroke="currentColor" + class="w-6 h-6 {$$restProps.class ?? ''}" +> + <path + stroke-linecap="round" + stroke-linejoin="round" + d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 012.25-2.25h13.5A2.25 2.25 0 0121 7.5v11.25m-18 0A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75m-18 0v-7.5A2.25 2.25 0 015.25 9h13.5A2.25 2.25 0 0121 11.25v7.5m-9-6h.008v.008H12v-.008zM12 15h.008v.008H12V15zm0 2.25h.008v.008H12v-.008zM9.75 15h.008v.008H9.75V15zm0 2.25h.008v.008H9.75v-.008zM7.5 15h.008v.008H7.5V15zm0 2.25h.008v.008H7.5v-.008zm6.75-4.5h.008v.008h-.008v-.008zm0 2.25h.008v.008h-.008V15zm0 2.25h.008v.008h-.008v-.008zm2.25-4.5h.008v.008H16.5v-.008zm0 2.25h.008v.008H16.5V15z" + /> +</svg> diff --git a/apps/kit/src/lib/components/icons/chevron-up-down.svelte b/apps/kit/src/lib/components/icons/chevron-up-down.svelte new file mode 100644 index 0000000..c07aed5 --- /dev/null +++ b/apps/kit/src/lib/components/icons/chevron-up-down.svelte @@ -0,0 +1,13 @@ +<svg + class="h-5 w-5 {$$restProps.class ?? ''}" + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 20 20" + fill="currentColor" + aria-hidden="true" +> + <path + fill-rule="evenodd" + d="M10 3a.75.75 0 01.55.24l3.25 3.5a.75.75 0 11-1.1 1.02L10 4.852 7.3 7.76a.75.75 0 01-1.1-1.02l3.25-3.5A.75.75 0 0110 3zm-3.76 9.2a.75.75 0 011.06.04l2.7 2.908 2.7-2.908a.75.75 0 111.1 1.02l-3.25 3.5a.75.75 0 01-1.1 0l-3.25-3.5a.75.75 0 01.04-1.06z" + clip-rule="evenodd" + /> +</svg> diff --git a/apps/kit/src/lib/components/icons/folder-open.svelte b/apps/kit/src/lib/components/icons/folder-open.svelte new file mode 100644 index 0000000..409c8e2 --- /dev/null +++ b/apps/kit/src/lib/components/icons/folder-open.svelte @@ -0,0 +1,14 @@ +<svg + xmlns="http://www.w3.org/2000/svg" + fill="none" + viewBox="0 0 24 24" + stroke-width="1.5" + stroke="currentColor" + class="w-6 h-6 {$$restProps.class ?? ''}" +> + <path + stroke-linecap="round" + stroke-linejoin="round" + d="M3.75 9.776c.112-.017.227-.026.344-.026h15.812c.117 0 .232.009.344.026m-16.5 0a2.25 2.25 0 00-1.883 2.542l.857 6a2.25 2.25 0 002.227 1.932H19.05a2.25 2.25 0 002.227-1.932l.857-6a2.25 2.25 0 00-1.883-2.542m-16.5 0V6A2.25 2.25 0 016 3.75h3.879a1.5 1.5 0 011.06.44l2.122 2.12a1.5 1.5 0 001.06.44H18A2.25 2.25 0 0120.25 9v.776" + /> +</svg> diff --git a/apps/kit/src/lib/components/icons/index.ts b/apps/kit/src/lib/components/icons/index.ts index e846dc4..8c24873 100644 --- a/apps/kit/src/lib/components/icons/index.ts +++ b/apps/kit/src/lib/components/icons/index.ts @@ -3,25 +3,39 @@ import MenuIcon from "./menu.svelte"; import AdjustmentsIcon from "./adjustments.svelte"; import DatabaseIcon from "./database.svelte"; import HomeIcon from "./home.svelte"; -import InformationCircle from "./information-circle.svelte"; -import ExclamationTriangle from "./exclamation-triangle.svelte"; -import XCircle from "./x-circle.svelte"; -import CheckCircle from "./check-circle.svelte"; -import XMark from "./x-mark.svelte"; -import Spinner from "./spinner.svelte"; -import ExclamationCircle from "./exclamation-circle.svelte"; +import InformationCircleIcon from "./information-circle.svelte"; +import ExclamationTriangleIcon from "./exclamation-triangle.svelte"; +import XCircleIcon from "./x-circle.svelte"; +import CheckCircleIcon from "./check-circle.svelte"; +import XMarkIcon from "./x-mark.svelte"; +import SpinnerIcon from "./spinner.svelte"; +import ExclamationCircleIcon from "./exclamation-circle.svelte"; +import ChevronUpDownIcon from "./chevron-up-down.svelte"; +import MagnifyingGlassIcon from "./magnifying-glass.svelte"; +import Bars3CenterLeftIcon from "./bars-3-center-left.svelte"; +import CalendarIcon from "./calendar.svelte"; +import FolderOpenIcon from "./folder-open.svelte"; +import MegaphoneIcon from "./megaphone.svelte"; +import QueueListIcon from "./queue-list.svelte"; export { + QueueListIcon, + FolderOpenIcon, + MegaphoneIcon, + CalendarIcon, + Bars3CenterLeftIcon, + MagnifyingGlassIcon, + ChevronUpDownIcon, XIcon, MenuIcon, HomeIcon, DatabaseIcon, AdjustmentsIcon, - InformationCircle, - ExclamationTriangle, - ExclamationCircle, - XCircle, - CheckCircle, - XMark, - Spinner + InformationCircleIcon, + ExclamationTriangleIcon, + ExclamationCircleIcon, + XCircleIcon, + CheckCircleIcon, + XMarkIcon, + SpinnerIcon }
\ No newline at end of file diff --git a/apps/kit/src/lib/components/icons/magnifying-glass.svelte b/apps/kit/src/lib/components/icons/magnifying-glass.svelte new file mode 100644 index 0000000..f8fdb6e --- /dev/null +++ b/apps/kit/src/lib/components/icons/magnifying-glass.svelte @@ -0,0 +1,13 @@ +<svg + class="h-5 w-5 {$$restProps.class ?? ''}" + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 20 20" + fill="currentColor" + aria-hidden="true" +> + <path + fill-rule="evenodd" + d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z" + clip-rule="evenodd" + /> +</svg> diff --git a/apps/kit/src/lib/components/icons/megaphone.svelte b/apps/kit/src/lib/components/icons/megaphone.svelte new file mode 100644 index 0000000..7ada5f3 --- /dev/null +++ b/apps/kit/src/lib/components/icons/megaphone.svelte @@ -0,0 +1,14 @@ +<svg + xmlns="http://www.w3.org/2000/svg" + fill="none" + viewBox="0 0 24 24" + stroke-width="1.5" + stroke="currentColor" + class="w-6 h-6 {$$restProps.class ?? ''}" +> + <path + stroke-linecap="round" + stroke-linejoin="round" + d="M10.34 15.84c-.688-.06-1.386-.09-2.09-.09H7.5a4.5 4.5 0 110-9h.75c.704 0 1.402-.03 2.09-.09m0 9.18c.253.962.584 1.892.985 2.783.247.55.06 1.21-.463 1.511l-.657.38c-.551.318-1.26.117-1.527-.461a20.845 20.845 0 01-1.44-4.282m3.102.069a18.03 18.03 0 01-.59-4.59c0-1.586.205-3.124.59-4.59m0 9.18a23.848 23.848 0 018.835 2.535M10.34 6.66a23.847 23.847 0 008.835-2.535m0 0A23.74 23.74 0 0018.795 3m.38 1.125a23.91 23.91 0 011.014 5.395m-1.014 8.855c-.118.38-.245.754-.38 1.125m.38-1.125a23.91 23.91 0 001.014-5.395m0-3.46c.495.413.811 1.035.811 1.73 0 .695-.316 1.317-.811 1.73m0-3.46a24.347 24.347 0 010 3.46" + /> +</svg> diff --git a/apps/kit/src/lib/components/icons/queue-list.svelte b/apps/kit/src/lib/components/icons/queue-list.svelte new file mode 100644 index 0000000..6148394 --- /dev/null +++ b/apps/kit/src/lib/components/icons/queue-list.svelte @@ -0,0 +1,14 @@ +<svg + xmlns="http://www.w3.org/2000/svg" + fill="none" + viewBox="0 0 24 24" + stroke-width="1.5" + stroke="currentColor" + class="w-6 h-6 {$$restProps.class ?? ''}" +> + <path + stroke-linecap="round" + stroke-linejoin="round" + d="M3.75 12h16.5m-16.5 3.75h16.5M3.75 19.5h16.5M5.625 4.5h12.75a1.875 1.875 0 010 3.75H5.625a1.875 1.875 0 010-3.75z" + /> +</svg> diff --git a/apps/kit/src/lib/components/input.svelte b/apps/kit/src/lib/components/input.svelte index 6063fe4..999c565 100644 --- a/apps/kit/src/lib/components/input.svelte +++ b/apps/kit/src/lib/components/input.svelte @@ -1,6 +1,6 @@ <script lang="ts"> import { random_string } from "$lib/helpers"; - import { ExclamationCircle } from "./icons"; + import { ExclamationCircleIcon } from "./icons"; export let label: string | undefined = undefined; export let type: string = "text"; @@ -28,12 +28,10 @@ required: required || null, } as any; $: hasBling = icon || addon || errorText; - const defaultColorClass = - "border-gray-300 focus:border-teal-500 focus:ring-teal-500"; + const defaultColorClass = "border-gray-300 focus:border-teal-500 focus:ring-teal-500"; let colorClass = defaultColorClass; $: if (errorText) { - colorClass = - "placeholder-red-300 focus:border-red-500 focus:outline-none focus:ring-red-500 text-red-900 pr-10 border-red-300"; + colorClass = "placeholder-red-300 focus:border-red-500 focus:outline-none focus:ring-red-500 text-red-900 pr-10 border-red-300"; } else { colorClass = defaultColorClass; } @@ -45,23 +43,13 @@ <div class={wrapperClass}> {#if label && !cornerHint && !hideLabel} - <label - for={id} - class={hideLabel - ? "sr-only" - : "block text-sm font-medium text-gray-700"} - > + <label for={id} class={hideLabel ? "sr-only" : "block text-sm font-medium text-gray-700"}> {label} </label> {:else if cornerHint && !hideLabel} <div class="flex justify-between"> {#if label} - <label - for={id} - class={hideLabel - ? "sr-only" - : "block text-sm font-medium text-gray-700"} - > + <label for={id} class={hideLabel ? "sr-only" : "block text-sm font-medium text-gray-700"}> {label} </label> {/if} @@ -70,24 +58,13 @@ </span> </div> {/if} - <div - class="mt-1 {hasBling ? 'relative rounded-md' : ''} {addon - ? 'flex' - : ''}" - > + <div class="mt-1 {hasBling ? 'relative rounded-md' : ''} {addon ? 'flex' : ''}"> {#if icon} - <div - class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3" - > - <svelte:component - this={icon} - class={errorText ? "text-red-500" : "text-gray-400"} - /> + <div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3"> + <svelte:component this={icon} class={errorText ? "text-red-500" : "text-gray-400"} /> </div> {:else if addon} - <div - class="inline-flex items-center rounded-l-md border border-r-0 border-gray-300 bg-gray-50 px-3 text-gray-500 sm:text-sm" - > + <div class="inline-flex items-center rounded-l-md border border-r-0 border-gray-300 bg-gray-50 px-3 text-gray-500 sm:text-sm"> <span class="text-gray-500 sm:text-sm">{addon}</span> </div> {/if} @@ -97,19 +74,16 @@ {id} {...attributes} bind:value - class="block w-full rounded-md shadow-sm sm:text-sm {colorClass} {disabled - ? 'disabled:cursor-not-allowed disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500' - : ''} - {addon ? 'min-w-0 flex-1 rounded-none rounded-r-md' : ''} {icon - ? 'pl-10' - : ''}" + class="block w-full rounded-md shadow-sm sm:text-sm + {colorClass} + {disabled ? 'disabled:cursor-not-allowed disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500' : ''} + {addon ? 'min-w-0 flex-1 rounded-none rounded-r-md' : ''} + {icon ? 'pl-10' : ''}" {placeholder} /> {#if errorText} - <div - class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3" - > - <ExclamationCircle class="text-red-500" /> + <div class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3"> + <ExclamationCircleIcon class="text-red-500" /> </div> {/if} </div> diff --git a/apps/kit/src/lib/components/locale-switcher.svelte b/apps/kit/src/lib/components/locale-switcher.svelte index 39d6168..1f9eb37 100644 --- a/apps/kit/src/lib/components/locale-switcher.svelte +++ b/apps/kit/src/lib/components/locale-switcher.svelte @@ -1,51 +1,32 @@ <script lang="ts"> - import {browser} from "$app/environment"; - import {page} from "$app/stores"; - import {setLocale, locale} from "$lib/i18n/i18n-svelte"; - import type {Locales} from "$lib/i18n/i18n-types"; - import {locales} from "$lib/i18n/i18n-util"; - import {loadLocaleAsync} from "$lib/i18n/i18n-util.async"; + import { browser } from "$app/environment"; + import { page } from "$app/stores"; + import { setLocale, locale } from "$lib/i18n/i18n-svelte"; + import type { Locales } from "$lib/i18n/i18n-types"; + import { locales } from "$lib/i18n/i18n-util"; + import { loadLocaleAsync } from "$lib/i18n/i18n-util.async"; - const switchLocale = async ( - newLocale: Locales, - updateHistoryState = true, - ) => { + const switchLocale = async (newLocale: Locales) => { if (!newLocale || $locale === newLocale) return; - - // load new dictionary from server await loadLocaleAsync(newLocale); - - // select locale setLocale(newLocale); - - // update `lang` attribute document.querySelector("html")?.setAttribute("lang", newLocale); - - //TODO set cookie that persists the locale }; - // update locale when navigating via browser back/forward buttons - const handlePopStateEvent = async ({state}: PopStateEvent) => - switchLocale(state.locale, false); - - // update locale when page store changes $: if (browser) { - const lang = $page.params.lang as Locales; - switchLocale(lang, false); + switchLocale($page.params.lang as Locales); } </script> -<svelte:window on:popstate={handlePopStateEvent}/> - <ul> - {#each locales as l} + {#each locales as aLocale} <li> <button - type="button" - class:active={l === $locale} - on:click={() => switchLocale(l)} + type="button" + class:active={aLocale === $locale} + on:click={() => switchLocale(aLocale)} > - {l} + {aLocale} </button> </li> {/each} diff --git a/apps/kit/src/routes/(main)/(app)/+layout.svelte b/apps/kit/src/routes/(main)/(app)/+layout.svelte index b69341c..6660c53 100644 --- a/apps/kit/src/routes/(main)/(app)/+layout.svelte +++ b/apps/kit/src/routes/(main)/(app)/+layout.svelte @@ -1,283 +1,297 @@ <script lang="ts"> - import LL from "$lib/i18n/i18n-svelte"; - import { - Dialog, - TransitionChild, - TransitionRoot, - } from "@rgossiaux/svelte-headlessui"; - import { - MenuIcon, - HomeIcon, - DatabaseIcon, - AdjustmentsIcon, - } from "$lib/components/icons"; + import { + ChevronUpDownIcon, + MagnifyingGlassIcon, + Bars3CenterLeftIcon, + XMarkIcon, + HomeIcon, + MegaphoneIcon, + FolderOpenIcon, + QueueListIcon, + CalendarIcon, + } from "$lib/components/icons"; + import { Dialog, Menu, MenuButton, MenuItem, MenuItems, Transition, TransitionChild, TransitionRoot } from "@rgossiaux/svelte-headlessui"; + import { DialogPanel } from "@developermuch/dev-svelte-headlessui"; + import type { ISession } from "$lib/models/ISession"; + import { Input } from "$lib/components"; + import { end_session } from "$lib/session"; + import { goto } from "$app/navigation"; + import { page } from "$app/stores"; - let online = true; - let sidebarIsOpen = false; - const username = "dumb"; + const session = { + profile: { + username: "Brukernavn", + displayName: "epost@adresse.no", + }, + } as ISession; - const navigations = [ - { - name: "Home", - icon: HomeIcon, - }, - { - name: "Data", - icon: DatabaseIcon, - }, - { - name: "Settings", - icon: AdjustmentsIcon, - }, - ]; + let sidebarOpen = false; + let sidebarSearchValue: string | undefined; + + function signOut() { + end_session(() => goto("/sign-in")); + } + + const navigationItems = [ + { + href: "/home", + name: "Home", + icon: HomeIcon, + }, + { + href: "/projects", + name: "Projects", + icon: CalendarIcon, + }, + { + href: "/tickets", + name: "Tickets", + icon: MegaphoneIcon, + }, + { + href: "/todo", + name: "Todo", + icon: QueueListIcon, + }, + { + href: "/wiki", + name: "Wiki", + icon: FolderOpenIcon, + }, + ]; </script> -<svelte:window bind:online /> -{#if !online} - <div class="bg-yellow-50 border-l-4 border-yellow-400 p-4"> - <div class="flex"> - <div class="flex-shrink-0"> - <svg - class="h-5 w-5 text-yellow-400" - xmlns="http://www.w3.org/2000/svg" - viewBox="0 0 20 20" - fill="currentColor" - aria-hidden="true" - > - <path - fill-rule="evenodd" - d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" - clip-rule="evenodd" - /> - </svg> - </div> - <div class="ml-3"> - <p class="text-sm text-yellow-700"> - You seem to be offline, please check your internet - connection. - </p> - </div> - </div> - </div> -{/if} -<div class="h-full flex"> - <TransitionRoot show={sidebarIsOpen}> - <Dialog - class="relative z-40 lg:hidden" - on:close={() => (sidebarIsOpen = !sidebarIsOpen)} +<div class="min-h-full"> + <!-- Mobile sidebar --> + <TransitionRoot show={sidebarOpen}> + <Dialog as="div" class="relative z-40 lg:hidden" on:close={() => (sidebarOpen = false)}> + <TransitionChild + as="div" + enter="transition-opacity ease-linear duration-300" + enterFrom="opacity-0" + enterTo="opacity-100" + leave="transition-opacity ease-linear duration-300" + leaveFrom="opacity-100" + leaveTo="opacity-0" + > + <div class="fixed inset-0 bg-gray-600 bg-opacity-75" /> + </TransitionChild> + + <div class="fixed inset-0 z-40 flex"> + <TransitionChild + as="div" + enter="transition ease-in-out duration-300 transform" + enterFrom="-translate-x-full" + enterTo="translate-x-0" + leave="transition ease-in-out duration-300 transform" + leaveFrom="translate-x-0" + leaveTo="-translate-x-full" > + <DialogPanel class="relative flex w-full max-w-xs flex-1 flex-col bg-white pt-5 pb-4"> <TransitionChild - enter="transition-opacity ease-linear duration-300" - enterFrom="opacity-0" - enterTo="opacity-100" - leave="transition-opacity ease-linear duration-300" - leaveFrom="opacity-100" - leaveTo="opacity-0" + as="div" + enter="ease-in-out duration-300" + enterFrom="opacity-0" + enterTo="opacity-100" + leave="ease-in-out duration-300" + leaveFrom="opacity-100" + leaveTo="opacity-0" > - <div class="fixed inset-0 bg-gray-600 bg-opacity-75" /> - </TransitionChild> - - <div class="fixed inset-0 flex z-40"> - <TransitionChild - enter="transition ease-in-out duration-300 transform" - enterFrom="-translate-x-full" - enterTo="translate-x-0" - leave="transition ease-in-out duration-300 transform" - leaveFrom="translate-x-0" - leaveTo="-translate-x-full" + <div class="absolute top-0 right-0 -mr-12 pt-2"> + <button + type="button" + class="ml-1 flex h-10 w-10 items-center justify-center rounded-full focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white" + on:click={() => (sidebarOpen = false)} > - <!-- <DialogPanel - class="relative flex-1 flex flex-col max-w-xs w-full bg-white focus:outline-none" + <span class="sr-only">Close sidebar</span> + <XMarkIcon class="text-white" aria-hidden="true" /> + </button> + </div> + </TransitionChild> + <div class="mt-5 h-0 flex-1 overflow-y-auto"> + <nav class="px-2"> + <div class="space-y-1"> + {#each navigationItems as item} + {@const current = $page.url.pathname.startsWith(item.href)} + <a + href={item.href} + aria-current={current ? "page" : undefined} + class="group flex items-center px-2 py-2 text-base leading-5 font-medium rounded-md + {current ? 'bg-gray-100 text-gray-900' : 'text-gray-600 hover:text-gray-900 hover:bg-gray-50'}" > - <TransitionChild - enter="ease-in-out duration-300" - enterFrom="opacity-0" - enterTo="opacity-100" - leave="ease-in-out duration-300" - leaveFrom="opacity-100" - leaveTo="opacity-0" - > - <div class="absolute top-0 right-0 -mr-12 pt-2"> - <button - type="button" - class="ml-1 flex items-center justify-center h-10 w-10 rounded-full focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white" - on:click={() => (sidebarIsOpen = false)} - > - <span class="sr-only">Close sidebar</span> - <XIcon - class="text-white" - aria-hidden="true" - /> - </button> - </div> - </TransitionChild> - <div class="flex-1 h-0 pt-5 pb-4 overflow-y-auto"> - <div class="flex-shrink-0 flex items-center px-4"> - <img - class="h-8 w-auto" - src="https://tailwindui.com/img/logos/workflow-mark.svg?color=indigo&shade=600" - alt="Workflow" - /> - </div> - <nav aria-label="Sidebar" class="mt-5"> - <div class="px-2 space-y-1"> - {#each navigations as item (item.name)} - <a - href={item.href} - class="{item.current - ? 'bg-gray-100 text-gray-900' - : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'} group flex items-center px-2 py-2 text-base font-medium rounded-md" - > - <svelte:component - this={item.icon} - class="{item.current - ? 'text-gray-500' - : 'text-gray-400 group-hover:text-gray-500'} mr-4 h-6 w-6" - aria-hidden="true" - /> - {item.name} - </a> - {/each} - </div> - </nav> - </div> - <div - class="flex-shrink-0 flex border-t border-gray-200 p-4" - > - <a href="#" class="flex-shrink-0 group block"> - <div class="flex items-center"> - <div> - <img - class="inline-block h-10 w-10 rounded-full" - src="https://images.unsplash.com/photo-1517365830460-955ce3ccd263?ixlib=rb-=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=8&w=256&h=256&q=80" - alt="" - /> - </div> - <div class="ml-3"> - <p - class="text-base font-medium text-gray-700 group-hover:text-gray-900" - > - {username} - </p> - <p - class="text-sm font-medium text-gray-500 group-hover:text-gray-700" - > - {$LL.nav.usermenu.profileTitle()} - </p> - </div> - </div> - </a> - </div> - </DialogPanel> --> - </TransitionChild> - <div class="flex-shrink-0 w-14" aria-hidden="true"> - <!--{/* Force sidebar to shrink to fit close icon */}--> + <svelte:component + this={item.icon} + class="mr-3 flex-shrink-0 h-6 w-6 {current ? 'text-gray-500' : 'text-gray-400 group-hover:text-gray-500'}" + aria-hidden="true" + /> + {item.name} + </a> + {/each} </div> + </nav> </div> - </Dialog> - </TransitionRoot> + </DialogPanel> + </TransitionChild> + <div class="w-14 flex-shrink-0" aria-hidden="true"> + <!-- Dummy element to force sidebar to shrink to fit close icon --> + </div> + </div> + </Dialog> + </TransitionRoot> - <!--{/* Static sidebar for desktop */}--> - <div class="hidden lg:flex lg:flex-shrink-0"> - <div class="flex flex-col w-64"> - <!--{/* Sidebar component, swap this element with another sidebar if you like */}--> - <div - class="flex-1 flex flex-col min-h-0 border-r border-gray-200 bg-gray-100" - > - <div class="flex-1 flex flex-col pt-5 pb-4 overflow-y-auto"> - <div class="flex items-center flex-shrink-0 px-4"> - <img - class="h-8 w-auto" - src="https://tailwindui.com/img/logos/workflow-mark.svg?color=indigo&shade=600" - alt="Workflow" - /> - </div> - <nav class="mt-5 flex-1" aria-label="Sidebar"> - <div class="px-2 space-y-1"> - {#each navigations as item (item.name)} - <a - key={item.name} - href={item.href} - class="{item.current - ? 'bg-gray-200 text-gray-900' - : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'} group flex items-center px-2 py-2 text-sm font-medium rounded-md" - > - <svelte:component - this={item.icon} - class="{item.current - ? 'text-gray-500' - : 'text-gray-400 group-hover:text-gray-500'} mr-3 h-6 w-6" - aria-hidden="true" - /> - {item.name} - </a> - {/each} - </div> - </nav> - </div> - <div class="flex-shrink-0 flex border-t border-gray-200 p-4"> - <a href="#" class="flex-shrink-0 w-full group block"> - <div class="flex items-center"> - <div> - <img - class="inline-block h-9 w-9 rounded-full" - src="https://images.unsplash.com/photo-1517365830460-955ce3ccd263?ixlib=rb-=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=8&w=256&h=256&q=80" - alt="" - /> - </div> - <div class="ml-3"> - <p - class="text-base font-medium text-gray-700 group-hover:text-gray-900" - > - {username} - </p> - <p - class="text-sm font-medium text-gray-500 group-hover:text-gray-700" - > - {$LL.nav.usermenu.profileTitle()} - </p> - </div> - </div> - </a> - </div> + <!-- Static sidebar for desktop --> + <div class="hidden lg:fixed lg:inset-y-0 lg:flex lg:w-64 lg:flex-col lg:border-r lg:border-gray-200 lg:bg-gray-100 lg:pb-4"> + <div class="flex h-0 flex-1 p-3 flex-col overflow-y-auto"> + <!-- User account dropdown --> + <Menu class="relative inline-block text-left"> + <MenuButton + class="group w-full rounded-md bg-gray-100 px-3.5 py-2 text-left text-sm font-medium text-gray-700 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-teal-500 focus:ring-offset-2 focus:ring-offset-gray-100" + > + <span class="flex w-full items-center justify-between"> + <span class="flex min-w-0 items-center justify-between space-x-3"> + <span class="flex min-w-0 flex-1 flex-col"> + <span class="truncate text-sm font-medium text-gray-900"> + {session.profile.username} + </span> + <span class="truncate text-sm text-gray-500">{session.profile.displayName}</span> + </span> + </span> + <ChevronUpDownIcon class="flex-shrink-0 text-gray-400 group-hover:text-gray-500" aria-hidden="true" /> + </span> + </MenuButton> + <Transition + leave="transition ease-in duration-75" + enter="transition ease-out duration-100" + enterFrom="transform opacity-0 scale-95" + enterTo="transform opacity-100 scale-100" + leaveFrom="transform opacity-100 scale-100" + leaveTo="transform opacity-0 scale-95" + as="div" + > + <MenuItems + class="absolute right-0 left-0 z-10 mt-1 origin-top divide-y divide-gray-200 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none" + > + <div class="py-1"> + <MenuItem> + <a href="/profile" class="text-gray-700 block px-4 py-2 text-sm hover:text-gray-900 hover:bg-gray-100"> View profile </a> + </MenuItem> + <MenuItem> + <a href="/settings" class="text-gray-700 block px-4 py-2 text-sm hover:text-gray-900 hover:bg-gray-100"> Settings </a> + </MenuItem> </div> + <div class="py-1"> + <MenuItem> + <span + on:click={() => signOut()} + class="text-gray-700 block px-4 py-2 text-sm hover:bg-red-200 hover:text-red-900 cursor-pointer" + > + Sign out + </span> + </MenuItem> + </div> + </MenuItems> + </Transition> + </Menu> + <!-- Sidebar Search --> + <div class="mt-3"> + <label for="search" class="sr-only">Search</label> + <div class="relative mt-1 rounded-md shadow-sm"> + <Input type="search" name="search" icon={MagnifyingGlassIcon} placeholder="Search" bind:value={sidebarSearchValue} /> </div> - </div> - <div class="flex flex-col min-w-0 flex-1 overflow-hidden"> - <div class="lg:hidden"> - <div - class="flex items-center justify-between bg-gray-50 border-b border-gray-200 px-4 py-1.5" + </div> + <!-- Navigation --> + <nav class="mt-5"> + <div class="space-y-1"> + {#each navigationItems as item} + {@const current = $page.url.pathname.startsWith(item.href)} + <a + href={item.href} + aria-current={current ? "page" : undefined} + class="group flex items-center px-2 py-2 text-base leading-5 font-medium rounded-md + {current ? 'bg-gray-200 text-gray-900' : 'text-gray-700 hover:text-gray-900 hover:bg-gray-50'}" > - <div> - <button - type="button" - class="-mr-3 h-12 w-12 inline-flex items-center justify-center rounded-md text-gray-500 hover:text-gray-900" - on:click={() => (sidebarIsOpen = true)} - > - <span class="sr-only">Open sidebar</span> - <MenuIcon aria-hidden="true" /> - </button> - </div> + <svelte:component + this={item.icon} + class="mr-3 flex-shrink-0 h-6 w-6 {current ? 'text-gray-500' : 'text-gray-400 group-hover:text-gray-500'}" + aria-hidden="true" + /> + {item.name} + </a> + {/each} + </div> + </nav> + </div> + </div> + + <!-- Main column --> + <div class="flex flex-col lg:pl-64"> + <!-- Search header --> + <div class="sticky top-0 z-10 flex h-16 flex-shrink-0 border-b border-gray-200 bg-white lg:hidden"> + <button + type="button" + class="border-r border-gray-200 px-4 text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-teal-500 lg:hidden" + on:click={() => (sidebarOpen = true)} + > + <span class="sr-only">Open sidebar</span> + <Bars3CenterLeftIcon aria-hidden="true" /> + </button> + <div class="flex flex-1 justify-between px-4 sm:px-6 lg:px-8"> + <div class="flex flex-1"> + <form class="flex w-full md:ml-0" action="#" method="GET"> + <label for="search-field" class="sr-only">Search</label> + <div class="relative w-full text-gray-400 focus-within:text-gray-600"> + <Input + bind:value={sidebarSearchValue} + icon={MagnifyingGlassIcon} + id="search-field" + name="search-field" + placeholder="Search" + type="search" + /> </div> + </form> </div> - <div class="flex-1 relative z-0 flex overflow-hidden"> - <main - class="flex-1 relative z-0 overflow-y-auto focus:outline-none" - > - <!-- - MAIN CONTENT - --> - <slot /> - </main> - <aside - class="hidden relative xl:flex xl:flex-col flex-shrink-0 w-96 border-l border-gray-200 overflow-y-auto" + <div class="flex items-center"> + <!-- Profile dropdown --> + <Menu as="div" class="relative ml-3"> + <div> + <MenuButton + class="flex max-w-xs items-center rounded-full bg-white text-sm focus:outline-none focus:ring-2 focus:ring-teal-500 focus:ring-offset-2" + > + <span class="sr-only">Open user menu</span> + </MenuButton> + </div> + <Transition + enterFrom="transform opacity-0 scale-95" + enterTo="transform opacity-100 scale-100" + leaveFrom="transform opacity-100 scale-100" + leaveTo="transform opacity-0 scale-95" + as="div" > - <!--{/* Start secondary column (hidden on smaller screens) */} - <div class="absolute inset-0 py-6 px-4 sm:px-6 lg:px-8"> - <div class="h-full border-2 border-gray-200 border-dashed rounded-lg" /> + <MenuItems + class="absolute right-0 z-10 mt-2 w-48 origin-top-right divide-y divide-gray-200 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none" + > + <div class="py-1"> + <MenuItem> + <a href="/profile" class="text-gray-700 block px-4 py-2 text-sm"> View profile </a> + </MenuItem> + <MenuItem> + <a href="/settings" class="text-gray-700 block px-4 py-2 text-sm hover:text-gray-900 hover:bg-gray-100"> Settings </a> + </MenuItem> + <div class="py-1"> + <MenuItem> + <span on:click={() => signOut()} class="text-gray-700 block px-4 py-2 text-sm"> Sign out </span> + </MenuItem> + </div> </div> - {/* End secondary column */}--> - </aside> + </MenuItems> + </Transition> + </Menu> </div> + </div> </div> + <main class="flex-1"> + <slot /> + </main> + </div> </div> diff --git a/apps/kit/src/routes/(main)/(app)/org/+page.svelte b/apps/kit/src/routes/(main)/(app)/org/+page.svelte new file mode 100644 index 0000000..429ec25 --- /dev/null +++ b/apps/kit/src/routes/(main)/(app)/org/+page.svelte @@ -0,0 +1,4 @@ +<script lang="ts"> +</script> + +<h1>$ORGNAME</h1> diff --git a/apps/kit/src/routes/(main)/(app)/profile/+page.svelte b/apps/kit/src/routes/(main)/(app)/profile/+page.svelte new file mode 100644 index 0000000..7c6eb3e --- /dev/null +++ b/apps/kit/src/routes/(main)/(app)/profile/+page.svelte @@ -0,0 +1,4 @@ +<script lang="ts"> +</script> + +<h1>Hi, Ivar</h1> diff --git a/apps/kit/src/routes/(main)/(app)/projects/+page.svelte b/apps/kit/src/routes/(main)/(app)/projects/+page.svelte new file mode 100644 index 0000000..6413e1d --- /dev/null +++ b/apps/kit/src/routes/(main)/(app)/projects/+page.svelte @@ -0,0 +1,4 @@ +<script lang="ts"> +</script> + +<h1>Projects</h1> diff --git a/apps/kit/src/routes/(main)/(app)/settings/+page.svelte b/apps/kit/src/routes/(main)/(app)/settings/+page.svelte new file mode 100644 index 0000000..ae6d403 --- /dev/null +++ b/apps/kit/src/routes/(main)/(app)/settings/+page.svelte @@ -0,0 +1,4 @@ +<script lang="ts"> +</script> + +<h1>Settings</h1> diff --git a/apps/kit/src/routes/(main)/(app)/tickets/+page.svelte b/apps/kit/src/routes/(main)/(app)/tickets/+page.svelte new file mode 100644 index 0000000..2a4792b --- /dev/null +++ b/apps/kit/src/routes/(main)/(app)/tickets/+page.svelte @@ -0,0 +1,4 @@ +<script lang="ts"> +</script> + +<h1>Tickets</h1> diff --git a/apps/kit/src/routes/(main)/(app)/todo/+page.svelte b/apps/kit/src/routes/(main)/(app)/todo/+page.svelte new file mode 100644 index 0000000..e29f263 --- /dev/null +++ b/apps/kit/src/routes/(main)/(app)/todo/+page.svelte @@ -0,0 +1,4 @@ +<script lang="ts"> +</script> + +<h1>Todo</h1> diff --git a/apps/kit/src/routes/(main)/(app)/wiki/+page.svelte b/apps/kit/src/routes/(main)/(app)/wiki/+page.svelte new file mode 100644 index 0000000..1762d43 --- /dev/null +++ b/apps/kit/src/routes/(main)/(app)/wiki/+page.svelte @@ -0,0 +1,4 @@ +<script lang="ts"> +</script> + +<h1>Wiki</h1> diff --git a/apps/kit/src/routes/(main)/+layout.svelte b/apps/kit/src/routes/(main)/+layout.svelte index 9787e17..5354f02 100644 --- a/apps/kit/src/routes/(main)/+layout.svelte +++ b/apps/kit/src/routes/(main)/+layout.svelte @@ -3,9 +3,35 @@ import { setLocale } from "$lib/i18n/i18n-svelte"; import type { LayoutData } from "./$types"; + let online = true; export let data: LayoutData; setLocale(data.locale); </script> +<svelte:window bind:online /> +{#if !online} + <div class="bg-yellow-50 relative z-50 border-yellow-400 p-4"> + <div class="flex"> + <div class="flex-shrink-0"> + <svg + class="h-5 w-5 text-yellow-400" + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 20 20" + fill="currentColor" + aria-hidden="true" + > + <path + fill-rule="evenodd" + d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" + clip-rule="evenodd" + /> + </svg> + </div> + <div class="ml-3"> + <p class="text-sm text-yellow-700">You seem to be offline, please check your internet connection.</p> + </div> + </div> + </div> +{/if} <slot /> |
