diff options
42 files changed, 2468 insertions, 1251 deletions
diff --git a/apps/frontpage/src/routes/__layout.svelte b/apps/frontpage/src/routes/__layout.svelte index ea37fe7..aa70e51 100644 --- a/apps/frontpage/src/routes/__layout.svelte +++ b/apps/frontpage/src/routes/__layout.svelte @@ -1,6 +1,6 @@ <script> import {portal_base} from "$shared/lib/configuration"; - import ThemeSwitcher from "$shared/components/theme-switcher.svelte"; + import BlowoutToolbelt from "$shared/components/blowout-toolbelt.svelte"; import "./app.scss"; import {afterNavigate} from "$app/navigation"; import {page} from "$app/stores"; @@ -22,7 +22,7 @@ background: hsla(var(--color-contrast-higher-h), var(--color-contrast-higher-s), var(--color-contrast-higher-l), 0.075) } </style> - +<BlowoutToolbelt/> <div class="flex@sm"> <aside class="sidebar {showSidebar ? 'sidebar--is-visible': ''}"> <div class="sidebar__panel"> @@ -74,12 +74,7 @@ </div> </div> </aside> - <main class="position-relative padding-sm z-index-1 flex-grow"> <slot></slot> </main> - - <div class="position-fixed right-0 top-0 margin-md z-index-2"> - <ThemeSwitcher/> - </div> </div> diff --git a/apps/portal/src/app/pages/_layout.svelte b/apps/portal/src/app/pages/_layout.svelte index 7cb7b2a..8c75cb9 100644 --- a/apps/portal/src/app/pages/_layout.svelte +++ b/apps/portal/src/app/pages/_layout.svelte @@ -1,6 +1,5 @@ <script> - import ThemeSwitcher from "$shared/components/theme-switcher.svelte"; - import {frontpage_base} from "$shared/lib/configuration"; + import BlowoutToolbelt from "$shared/components/blowout-toolbelt.svelte"; </script> <style> @@ -27,18 +26,15 @@ height: auto; } </style> - +<BlowoutToolbelt/> + <main class="container-fluid padding-x-xs padding-x-xxl@xs padding-y-md padding-y-lg@md max-width-sm"> - <a href="{frontpage_base()}" class="block margin-bottom-xs">Go to {frontpage_base()}</a> <div class="z-index-2 position-relative"> <slot/> </div> - - <div class="position-fixed right-0 top-0 margin-md z-index-2"> - <ThemeSwitcher/> - </div> <figure id="decoration" + class="z-index-1" aria-hidden="true"> <svg class="color-contrast-higher opacity-10%" viewBox="0 0 1920 450" diff --git a/apps/portal/src/app/pages/_layout@loggedin.svelte b/apps/portal/src/app/pages/_layout@loggedin.svelte index eb092a1..ea56f73 100644 --- a/apps/portal/src/app/pages/_layout@loggedin.svelte +++ b/apps/portal/src/app/pages/_layout@loggedin.svelte @@ -1,5 +1,5 @@ <script> - import ThemeSwitcher from "$shared/components/theme-switcher.svelte"; + import BlowoutToolbelt from "$shared/components/blowout-toolbelt.svelte"; import UserMenu from "$app/components/user-menu.svelte"; import {get_session_data} from "$shared/lib/session"; @@ -30,18 +30,19 @@ height: auto; } </style> - + +<BlowoutToolbelt/> <main class="container max-width-xl padding-x-xs padding-x-xxl@xs padding-y-md padding-y-lg@md"> <div class="z-index-2 position-relative"> <slot/> </div> - + <div class="flex flex-row gap-xs position-fixed right-0 top-0 margin-md z-index-2"> - <UserMenu name="{session.profile?.username}"/> - <ThemeSwitcher/> + <UserMenu name="{session?.profile?.username}"/> </div> <figure id="decoration" + class="z-index-1" aria-hidden="true"> <svg class="color-contrast-higher opacity-10%" viewBox="0 0 1920 450" diff --git a/apps/portal/src/app/pages/home.svelte b/apps/portal/src/app/pages/home.svelte index b9b9829..b1859d2 100644 --- a/apps/portal/src/app/pages/home.svelte +++ b/apps/portal/src/app/pages/home.svelte @@ -4,7 +4,7 @@ import {push} from "svelte-spa-router"; import Layout from "./_layout@loggedin.svelte"; import LinkCard from "$shared/components/link-card.svelte"; - import {UserIcon, UsersIcon, WatchIcon} from "svelte-feather-icons"; + import {UserIcon, UsersIcon, WatchIcon, SendIcon, ListIcon} from "svelte-feather-icons"; let showUsers = true; const session = get_session_data(); @@ -21,7 +21,7 @@ <div class="grid-auto-md gap-sm"> <LinkCard name="Projects" description="The home for your projects" - text="Open" + text="Open in a new tab" target="_blank" title="Open Projects in a new tab" href="{projects_base()}"> @@ -33,13 +33,43 @@ </div> </figure> </LinkCard> + <LinkCard name="Tickets" + description="The home for your tickets" + class="c-disabled user-select-none" + text="Coming soon" + target="_blank" + title="Open Tickets in a new tab" + href="{projects_base()}"> + <figure slot="icon"> + <div class="bg-primary bg-opacity-10% padding-xs border-left border-primary border-2"> + <SendIcon size="42" + class="color-primary" + strokeWidth="1.2"/> + </div> + </figure> + </LinkCard> + <LinkCard name="Todo" + description="The home for your todos" + class="c-disabled user-select-none" + text="Coming soon" + target="_blank" + title="Open Todo in a new tab" + href="{projects_base()}"> + <figure slot="icon"> + <div class="bg-primary bg-opacity-10% padding-xs border-left border-primary border-2"> + <ListIcon size="42" + class="color-primary" + strokeWidth="1.2"/> + </div> + </figure> + </LinkCard> </div> </div> <div class="row"> <h2 class="margin-bottom-xs">Manage</h2> <div class="grid-auto-md gap-sm"> <LinkCard name="Profile" - description="Manage your profile information" + description="Manage your profile" text="Open" title="Go to your profile management page" on:click={() => push("/profile")}> diff --git a/apps/portal/src/app/pages/login.svelte b/apps/portal/src/app/pages/login.svelte index b842463..900c5ac 100644 --- a/apps/portal/src/app/pages/login.svelte +++ b/apps/portal/src/app/pages/login.svelte @@ -14,6 +14,7 @@ values: { username: "", password: "", + persist: true }, alert: { title: "", @@ -92,6 +93,7 @@ </script> <Layout> + <a href="{frontpage_base()}" class="block margin-bottom-xs">Go to {frontpage_base()}</a> <Tile> <form on:submit|preventDefault={loginForm.submit_form} class="max-width-xxs"> diff --git a/apps/portal/src/app/pages/sign-up.svelte b/apps/portal/src/app/pages/sign-up.svelte index 509d33a..3bcab6d 100644 --- a/apps/portal/src/app/pages/sign-up.svelte +++ b/apps/portal/src/app/pages/sign-up.svelte @@ -1,5 +1,6 @@ <script> import {create_account} from "$shared/lib/api/user"; + import {frontpage_base} from "$shared/lib/configuration"; import {is_email} from "$shared/lib/helpers"; import Alert from "$shared/components/alert.svelte"; import Button from "$shared/components/button.svelte"; @@ -79,6 +80,8 @@ </script> <Layout> + <a href="{frontpage_base()}" + class="block margin-bottom-xs">Go to {frontpage_base()}</a> <Tile> <form on:submit|preventDefault={signupForm.submit_form} class="max-width-xxs"> diff --git a/apps/projects/src/.typesafe-i18n.json b/apps/projects/src/.typesafe-i18n.json new file mode 100644 index 0000000..cfb94b6 --- /dev/null +++ b/apps/projects/src/.typesafe-i18n.json @@ -0,0 +1,5 @@ +{ + "adapter": "svelte", + "$schema": "https://unpkg.com/typesafe-i18n@5.5.2/schema/typesafe-i18n.json", + "outputPath": "app/lib/i18n" +} diff --git a/apps/projects/src/app/index.scss b/apps/projects/src/app/index.scss index 4794787..0892d63 100644 --- a/apps/projects/src/app/index.scss +++ b/apps/projects/src/app/index.scss @@ -36,3 +36,4 @@ @use '../../web-shared/src/styles/components/custom-checkbox'; @use '../../web-shared/src/styles/components/menu'; @use '../../web-shared/src/styles/components/user-menu'; +@use '../../web-shared/src/styles/components/light-dark-switch'; diff --git a/apps/projects/src/app/index.svelte b/apps/projects/src/app/index.svelte index 77d290d..e397de3 100644 --- a/apps/projects/src/app/index.svelte +++ b/apps/projects/src/app/index.svelte @@ -1,53 +1,86 @@ <svelte:options immutable={true}/> <svelte:window bind:online={online}/> -<script> - import {logout_user} from "$app/lib/services/user-service"; - import Router from "svelte-spa-router"; - import {wrap} from "svelte-spa-router/wrap"; - import {QueryClient, QueryClientProvider} from "@sveltestack/svelte-query"; - import {is_active} from "$shared/lib/session"; - import UiWorkbench from "$app/pages/ui-workbench.svelte"; - import NotFound from "$app/pages/not-found.svelte"; - import Home from "$app/pages/home.svelte"; - import Settings from "$app/pages/settings.svelte"; - import Data from "$app/pages/data.svelte"; - import PreHeader from "$shared/components/pre-header.svelte"; +<script lang="ts"> + import {logout_user} from "$app/lib/services/user-service"; + import {currentLocale, preffered_or_default} from "$app/lib/stores/locale"; + import {CookieNames} from "$shared/lib/configuration"; + import {get_cookie} from "$shared/lib/helpers"; + import {Temporal} from "@js-temporal/polyfill"; + import {onMount} from "svelte"; + import Router from "svelte-spa-router"; + import {wrap} from "svelte-spa-router/wrap"; + import {QueryClient, QueryClientProvider} from "@sveltestack/svelte-query"; + import {is_active} from "$shared/lib/session"; + import UiWorkbench from "$app/pages/ui-workbench.svelte"; + import NotFound from "$app/pages/not-found.svelte"; + import Home from "$app/pages/home.svelte"; + import Settings from "$app/pages/settings.svelte"; + import Data from "$app/pages/data.svelte"; + import PreHeader from "$shared/components/pre-header.svelte"; + import {setLocale} from "$app/lib/i18n/i18n-svelte"; + import {loadLocaleAsync} from "$app/lib/i18n/i18n-util.async"; + import {i18nObject} from "$app/lib/i18n/i18n-util"; - let online = true; + let online = true; + let notOnlineText; + let LL; - async function user_is_logged_in() { - if (!await is_active()) { - await logout_user("expired"); - } - return true; - } + console.log("Projects Startup Report", { + prefferedLocale: navigator.language, + timeZone: Temporal.Now.timeZone().id, + go_theme: get_cookie(CookieNames.theme), + go_locale: get_cookie(CookieNames.locale), + prefersColorScheme: window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" + }); - const queryClient = new QueryClient(); - - const routes = { - "/home": wrap({ - component: Home, - conditions: [user_is_logged_in], - }), - "/": wrap({ - component: Home, - conditions: [user_is_logged_in], - }), - "/settings": wrap({ - component: Settings, - conditions: [user_is_logged_in], - }), - "/data": wrap({ - component: Data, - conditions: [user_is_logged_in], - }), - "/ui-workbench": UiWorkbench, - "*": NotFound, - }; + currentLocale.subscribe(async locale => { + locale = locale === "preffered" ? preffered_or_default() : locale; + await loadLocaleAsync(locale); + LL = i18nObject(locale); + setLocale(locale); + }); + + onMount(async () => { + const locale = $currentLocale === "preffered" ? preffered_or_default() : $currentLocale; + await loadLocaleAsync(locale); + LL = i18nObject(locale); + setLocale(locale); + notOnlineText = LL.messages.noInternet(); + }); + + async function user_is_logged_in() { + if (!await is_active()) { + await logout_user("expired"); + } + return true; + } + + const queryClient = new QueryClient(); + + const routes = { + "/home": wrap({ + component: Home, + conditions: [user_is_logged_in], + }), + "/": wrap({ + component: Home, + conditions: [user_is_logged_in], + }), + "/settings": wrap({ + component: Settings, + conditions: [user_is_logged_in], + }), + "/data": wrap({ + component: Data, + conditions: [user_is_logged_in], + }), + "/ui-workbench": UiWorkbench, + "*": NotFound, + }; </script> -<PreHeader show="{!online}">You seem to be offline, please check your internet connection.</PreHeader> +<PreHeader show="{!online}">{notOnlineText}</PreHeader> <QueryClientProvider client={queryClient}> <Router diff --git a/apps/projects/src/app/lib/i18n/en/index.ts b/apps/projects/src/app/lib/i18n/en/index.ts new file mode 100644 index 0000000..9d74481 --- /dev/null +++ b/apps/projects/src/app/lib/i18n/en/index.ts @@ -0,0 +1,127 @@ +import type {BaseTranslation} from "../i18n-types"; + +const en: BaseTranslation = { + nav: { + home: "Home", + data: "Data", + settings: "Settings", + usermenu: { + logout: "Log out", + logoutTitle: "Log out of your profile", + profile: "Profile", + profileTitle: "Administrate your profile", + toggleTitle: "Toggle user menu", + } + }, + views: { + dataTablePaginator: { + goToPrevPage: "Go to previous page", + goToNextPage: "Go to next page", + of: "of", + }, + categoryForm: { + name: "Name", + color: "Color", + defaultLabels: "Default labels", + labelsPlaceholder: "Search or create" + }, + settingsCategoriesTile: { + deleteAllConfirm: "Are you sure you want to delete this category?\nThis will delete all relating entries!", + active: "Active", + archived: "Archived", + name: "Name", + color: "Color", + editEntry: "Edit entry", + deleteEntry: "Delete entry", + noCategories: "No categories", + categories: "Categories" + }, + settingsLabelsTile: { + deleteAllConfirm: "Are you sure you want to delete this label?\nIt will be removed from all related entries!", + active: "Active", + archived: "Archived", + name: "Name", + color: "Color", + editEntry: "Edit label", + deleteEntry: "Delete label", + noLabels: "No labels", + labels: "Labels" + }, + entryForm: { + entryUpdateError: "An error occured while updating the entry, try again soon.", + entryCreateError: "An error occured while creating the entry, try again soon.", + errDescriptionReq: "Description is required", + reset: "Reset", + description: "Description", + save: "Save", + create: "Create", + category: { + category: "Category", + placeholder: "Search or create", + noResults: "No categories available (Create a new one by searching for it)", + errisRequired: "Category is required", + _logReset: "Reset category section" + }, + labels: { + placeholder: "Search or create", + noResults: "No labels available (Create a new one by searching for it)", + labels: "Labels", + _logReset: "Reset labels section" + }, + dateTime: { + errDateIsRequired: "Date is required", + errFromIsRequired: "From is required", + errFromAfterTo: "From can not be after To", + errFromEqTo: "From and To can not be equal", + errToIsRequired: "To is required", + errToBeforeFrom: "To can not be before From", + from: "From", + to: "To", + date: "Date", + _logReset: "Reset date time section" + } + } + }, + data: { + durationSummary: "Showing {entryCountString:string}, totalling in {totalHourMin:string}", + hourSingleChar: "h", + minSingleChar: "m", + entry: "entry", + entries: "entries", + confirmDeleteEntry: "Are you sure you want to delete this entry?", + editEntry: "Edit entry", + date: "Date", + from: "From", + duration: "Duration", + category: "Category", + description: "Description", + loading: "Loading", + noEntries: "No entries", + to: "to", + use: "Use", + }, + home: { + hourSingleChar: "h", + minSingleChar: "m", + confirmDeleteEntry: "Are you sure you want to delete this entry?", + newEntry: "New entry", + editEntry: "Edit entry", + deleteEntry: "Delete entry", + loggedTimeToday: "Logged time today", + currentTime: "Current time", + loading: "Loading", + stopwatch: "Stopwatch", + todayEntries: "Today's entries", + noEntriesToday: "No entries today", + refreshTodayEntries: "Refresh today's entries", + category: "Category", + timespan: "Timespan", + }, + messages: { + pageNotFound: "Page not found", + goToFrontpage: "Go to frontpage", + noInternet: "It seems like your device does not have a internet connection, please check your connection." + } +}; + +export default en; diff --git a/apps/projects/src/app/lib/i18n/formatters.ts b/apps/projects/src/app/lib/i18n/formatters.ts new file mode 100644 index 0000000..78734f9 --- /dev/null +++ b/apps/projects/src/app/lib/i18n/formatters.ts @@ -0,0 +1,11 @@ +import type { FormattersInitializer } from 'typesafe-i18n' +import type { Locales, Formatters } from './i18n-types' + +export const initFormatters: FormattersInitializer<Locales, Formatters> = (locale: Locales) => { + + const formatters: Formatters = { + // add your formatter functions here + } + + return formatters +} diff --git a/apps/projects/src/app/lib/i18n/i18n-svelte.ts b/apps/projects/src/app/lib/i18n/i18n-svelte.ts new file mode 100644 index 0000000..6cdffb3 --- /dev/null +++ b/apps/projects/src/app/lib/i18n/i18n-svelte.ts @@ -0,0 +1,12 @@ +// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. +/* eslint-disable */ + +import { initI18nSvelte } from 'typesafe-i18n/svelte' +import type { Formatters, Locales, TranslationFunctions, Translations } from './i18n-types' +import { loadedFormatters, loadedLocales } from './i18n-util' + +const { locale, LL, setLocale } = initI18nSvelte<Locales, Translations, TranslationFunctions, Formatters>(loadedLocales, loadedFormatters) + +export { locale, LL, setLocale } + +export default LL diff --git a/apps/projects/src/app/lib/i18n/i18n-types.ts b/apps/projects/src/app/lib/i18n/i18n-types.ts new file mode 100644 index 0000000..f9fd9cc --- /dev/null +++ b/apps/projects/src/app/lib/i18n/i18n-types.ts @@ -0,0 +1,812 @@ +// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. +/* eslint-disable */ +import type { BaseTranslation as BaseTranslationType, LocalizedString, RequiredParams } from 'typesafe-i18n' + +export type BaseTranslation = BaseTranslationType +export type BaseLocale = 'en' + +export type Locales = + | 'en' + | 'nb' + +export type Translation = RootTranslation + +export type Translations = RootTranslation + +type RootTranslation = { + nav: { + /** + * Home + */ + home: string + /** + * Data + */ + data: string + /** + * Settings + */ + settings: string + usermenu: { + /** + * Log out + */ + logout: string + /** + * Log out of your profile + */ + logoutTitle: string + /** + * Profile + */ + profile: string + /** + * Administrate your profile + */ + profileTitle: string + /** + * Toggle user menu + */ + toggleTitle: string + } + } + views: { + dataTablePaginator: { + /** + * Go to previous page + */ + goToPrevPage: string + /** + * Go to next page + */ + goToNextPage: string + /** + * of + */ + of: string + } + categoryForm: { + /** + * Name + */ + name: string + /** + * Color + */ + color: string + /** + * Default labels + */ + defaultLabels: string + /** + * Search or create + */ + labelsPlaceholder: string + } + settingsCategoriesTile: { + /** + * Are you sure you want to delete this category? + This will delete all relating entries! + */ + deleteAllConfirm: string + /** + * Active + */ + active: string + /** + * Archived + */ + archived: string + /** + * Name + */ + name: string + /** + * Color + */ + color: string + /** + * Edit entry + */ + editEntry: string + /** + * Delete entry + */ + deleteEntry: string + /** + * No categories + */ + noCategories: string + } + settingsLabelsTile: { + /** + * Are you sure you want to delete this label? + It will be removed from all related entries! + */ + deleteAllConfirm: string + /** + * Active + */ + active: string + /** + * Archived + */ + archived: string + /** + * Name + */ + name: string + /** + * Color + */ + color: string + /** + * Edit label + */ + editEntry: string + /** + * Delete label + */ + deleteEntry: string + /** + * No labels + */ + noLabels: string + } + entryForm: { + /** + * An error occured while updating the entry, try again soon. + */ + entryUpdateError: string + /** + * An error occured while creating the entry, try again soon. + */ + entryCreateError: string + /** + * Description is required + */ + errDescriptionReq: string + /** + * Reset + */ + reset: string + /** + * Description + */ + description: string + /** + * Save + */ + save: string + /** + * Create + */ + create: string + category: { + /** + * Category + */ + category: string + /** + * Search or create + */ + placeholder: string + /** + * No categories available (Create a new one by searching for it) + */ + noResults: string + /** + * Category is required + */ + errisRequired: string + /** + * Reset category section + */ + _logReset: string + } + labels: { + /** + * Search or create + */ + placeholder: string + /** + * No labels available (Create a new one by searching for it) + */ + noResults: string + /** + * Labels + */ + labels: string + /** + * Reset labels section + */ + _logReset: string + } + dateTime: { + /** + * Date is required + */ + errDateIsRequired: string + /** + * From is required + */ + errFromIsRequired: string + /** + * From can not be after To + */ + errFromAfterTo: string + /** + * From and To can not be equal + */ + errFromEqTo: string + /** + * To is required + */ + errToIsRequired: string + /** + * To can not be before From + */ + errToBeforeFrom: string + /** + * From + */ + from: string + /** + * To + */ + to: string + /** + * Date + */ + date: string + /** + * Reset date time section + */ + _logReset: string + } + } + } + data: { + /** + * Showing {entryCountString}, totalling in {totalHourMin} + * @param {string} entryCountString + * @param {string} totalHourMin + */ + durationSummary: RequiredParams<'entryCountString' | 'totalHourMin'> + /** + * h + */ + hourSingleChar: string + /** + * m + */ + minSingleChar: string + /** + * entry + */ + entry: string + /** + * entries + */ + entries: string + /** + * Are you sure you want to delete this entry? + */ + confirmDeleteEntry: string + /** + * Edit entry + */ + editEntry: string + /** + * Date + */ + date: string + /** + * From + */ + from: string + /** + * Duration + */ + duration: string + /** + * Category + */ + category: string + /** + * Description + */ + description: string + /** + * Loading + */ + loading: string + /** + * No entries + */ + noEntries: string + /** + * to + */ + to: string + /** + * Use + */ + use: string + } + home: { + /** + * h + */ + hourSingleChar: string + /** + * m + */ + minSingleChar: string + /** + * Are you sure you want to delete this entry? + */ + confirmDeleteEntry: string + /** + * New entry + */ + newEntry: string + /** + * Edit entry + */ + editEntry: string + /** + * Delete entry + */ + deleteEntry: string + /** + * Logged time today + */ + loggedTimeToday: string + /** + * Current time + */ + currentTime: string + /** + * Loading + */ + loading: string + /** + * Stopwatch + */ + stopwatch: string + /** + * Today's entries + */ + todayEntries: string + /** + * No entries today + */ + noEntriesToday: string + /** + * Refresh today's entries + */ + refreshTodayEntries: string + /** + * Category + */ + category: string + /** + * Timespan + */ + timespan: string + } + messages: { + /** + * Page not found + */ + pageNotFound: string + /** + * Go to frontpage + */ + goToFrontpage: string + /** + * It seems like your device does not have a internet connection, please check your connection. + */ + noInternet: string + } +} + +export type TranslationFunctions = { + nav: { + /** + * Home + */ + home: () => LocalizedString + /** + * Data + */ + data: () => LocalizedString + /** + * Settings + */ + settings: () => LocalizedString + usermenu: { + /** + * Log out + */ + logout: () => LocalizedString + /** + * Log out of your profile + */ + logoutTitle: () => LocalizedString + /** + * Profile + */ + profile: () => LocalizedString + /** + * Administrate your profile + */ + profileTitle: () => LocalizedString + /** + * Toggle user menu + */ + toggleTitle: () => LocalizedString + } + } + views: { + dataTablePaginator: { + /** + * Go to previous page + */ + goToPrevPage: () => LocalizedString + /** + * Go to next page + */ + goToNextPage: () => LocalizedString + /** + * of + */ + of: () => LocalizedString + } + categoryForm: { + /** + * Name + */ + name: () => LocalizedString + /** + * Color + */ + color: () => LocalizedString + /** + * Default labels + */ + defaultLabels: () => LocalizedString + /** + * Search or create + */ + labelsPlaceholder: () => LocalizedString + } + settingsCategoriesTile: { + /** + * Are you sure you want to delete this category? + This will delete all relating entries! + */ + deleteAllConfirm: () => LocalizedString + /** + * Active + */ + active: () => LocalizedString + /** + * Archived + */ + archived: () => LocalizedString + /** + * Name + */ + name: () => LocalizedString + /** + * Color + */ + color: () => LocalizedString + /** + * Edit entry + */ + editEntry: () => LocalizedString + /** + * Delete entry + */ + deleteEntry: () => LocalizedString + /** + * No categories + */ + noCategories: () => LocalizedString + } + settingsLabelsTile: { + /** + * Are you sure you want to delete this label? + It will be removed from all related entries! + */ + deleteAllConfirm: () => LocalizedString + /** + * Active + */ + active: () => LocalizedString + /** + * Archived + */ + archived: () => LocalizedString + /** + * Name + */ + name: () => LocalizedString + /** + * Color + */ + color: () => LocalizedString + /** + * Edit label + */ + editEntry: () => LocalizedString + /** + * Delete label + */ + deleteEntry: () => LocalizedString + /** + * No labels + */ + noLabels: () => LocalizedString + } + entryForm: { + /** + * An error occured while updating the entry, try again soon. + */ + entryUpdateError: () => LocalizedString + /** + * An error occured while creating the entry, try again soon. + */ + entryCreateError: () => LocalizedString + /** + * Description is required + */ + errDescriptionReq: () => LocalizedString + /** + * Reset + */ + reset: () => LocalizedString + /** + * Description + */ + description: () => LocalizedString + /** + * Save + */ + save: () => LocalizedString + /** + * Create + */ + create: () => LocalizedString + category: { + /** + * Category + */ + category: () => LocalizedString + /** + * Search or create + */ + placeholder: () => LocalizedString + /** + * No categories available (Create a new one by searching for it) + */ + noResults: () => LocalizedString + /** + * Category is required + */ + errisRequired: () => LocalizedString + /** + * Reset category section + */ + _logReset: () => LocalizedString + } + labels: { + /** + * Search or create + */ + placeholder: () => LocalizedString + /** + * No labels available (Create a new one by searching for it) + */ + noResults: () => LocalizedString + /** + * Labels + */ + labels: () => LocalizedString + /** + * Reset labels section + */ + _logReset: () => LocalizedString + } + dateTime: { + /** + * Date is required + */ + errDateIsRequired: () => LocalizedString + /** + * From is required + */ + errFromIsRequired: () => LocalizedString + /** + * From can not be after To + */ + errFromAfterTo: () => LocalizedString + /** + * From and To can not be equal + */ + errFromEqTo: () => LocalizedString + /** + * To is required + */ + errToIsRequired: () => LocalizedString + /** + * To can not be before From + */ + errToBeforeFrom: () => LocalizedString + /** + * From + */ + from: () => LocalizedString + /** + * To + */ + to: () => LocalizedString + /** + * Date + */ + date: () => LocalizedString + /** + * Reset date time section + */ + _logReset: () => LocalizedString + } + } + } + data: { + /** + * Showing {entryCountString}, totalling in {totalHourMin} + */ + durationSummary: (arg: { entryCountString: string, totalHourMin: string }) => LocalizedString + /** + * h + */ + hourSingleChar: () => LocalizedString + /** + * m + */ + minSingleChar: () => LocalizedString + /** + * entry + */ + entry: () => LocalizedString + /** + * entries + */ + entries: () => LocalizedString + /** + * Are you sure you want to delete this entry? + */ + confirmDeleteEntry: () => LocalizedString + /** + * Edit entry + */ + editEntry: () => LocalizedString + /** + * Date + */ + date: () => LocalizedString + /** + * From + */ + from: () => LocalizedString + /** + * Duration + */ + duration: () => LocalizedString + /** + * Category + */ + category: () => LocalizedString + /** + * Description + */ + description: () => LocalizedString + /** + * Loading + */ + loading: () => LocalizedString + /** + * No entries + */ + noEntries: () => LocalizedString + /** + * to + */ + to: () => LocalizedString + /** + * Use + */ + use: () => LocalizedString + } + home: { + /** + * h + */ + hourSingleChar: () => LocalizedString + /** + * m + */ + minSingleChar: () => LocalizedString + /** + * Are you sure you want to delete this entry? + */ + confirmDeleteEntry: () => LocalizedString + /** + * New entry + */ + newEntry: () => LocalizedString + /** + * Edit entry + */ + editEntry: () => LocalizedString + /** + * Delete entry + */ + deleteEntry: () => LocalizedString + /** + * Logged time today + */ + loggedTimeToday: () => LocalizedString + /** + * Current time + */ + currentTime: () => LocalizedString + /** + * Loading + */ + loading: () => LocalizedString + /** + * Stopwatch + */ + stopwatch: () => LocalizedString + /** + * Today's entries + */ + todayEntries: () => LocalizedString + /** + * No entries today + */ + noEntriesToday: () => LocalizedString + /** + * Refresh today's entries + */ + refreshTodayEntries: () => LocalizedString + /** + * Category + */ + category: () => LocalizedString + /** + * Timespan + */ + timespan: () => LocalizedString + } + messages: { + /** + * Page not found + */ + pageNotFound: () => LocalizedString + /** + * Go to frontpage + */ + goToFrontpage: () => LocalizedString + /** + * It seems like your device does not have a internet connection, please check your connection. + */ + noInternet: () => LocalizedString + } +} + +export type Formatters = {} diff --git a/apps/projects/src/app/lib/i18n/i18n-util.async.ts b/apps/projects/src/app/lib/i18n/i18n-util.async.ts new file mode 100644 index 0000000..75b90c9 --- /dev/null +++ b/apps/projects/src/app/lib/i18n/i18n-util.async.ts @@ -0,0 +1,27 @@ +// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. +/* eslint-disable */ + +import { initFormatters } from './formatters' +import type { Locales, Translations } from './i18n-types' +import { loadedFormatters, loadedLocales, locales } from './i18n-util' + +const localeTranslationLoaders = { + en: () => import('./en'), + nb: () => import('./nb'), +} + +const updateDictionary = (locale: Locales, dictionary: Partial<Translations>) => + loadedLocales[locale] = { ...loadedLocales[locale], ...dictionary } + +export const loadLocaleAsync = async (locale: Locales): Promise<void> => { + updateDictionary( + locale, + (await localeTranslationLoaders[locale]()).default as unknown as Translations + ) + loadFormatters(locale) +} + +export const loadAllLocalesAsync = (): Promise<void[]> => Promise.all(locales.map(loadLocaleAsync)) + +export const loadFormatters = (locale: Locales): void => + void (loadedFormatters[locale] = initFormatters(locale)) diff --git a/apps/projects/src/app/lib/i18n/i18n-util.sync.ts b/apps/projects/src/app/lib/i18n/i18n-util.sync.ts new file mode 100644 index 0000000..7a1d51e --- /dev/null +++ b/apps/projects/src/app/lib/i18n/i18n-util.sync.ts @@ -0,0 +1,27 @@ +// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. +/* eslint-disable */ + +import { initFormatters } from './formatters' +import type { Locales, Translations } from './i18n-types' +import { loadedFormatters, loadedLocales, locales } from './i18n-util' + +import en from './en' +import nb from './nb' + +const localeTranslations = { + en, + nb, +} + +export const loadLocale = (locale: Locales): void => { + if (loadedLocales[locale]) return + + loadedLocales[locale] = localeTranslations[locale] as unknown as Translations + loadFormatters(locale) +} + +export const loadAllLocales = (): void => locales.forEach(loadLocale) + +export const loadFormatters = (locale: Locales): void => { + loadedFormatters[locale] = initFormatters(locale) +} diff --git a/apps/projects/src/app/lib/i18n/i18n-util.ts b/apps/projects/src/app/lib/i18n/i18n-util.ts new file mode 100644 index 0000000..cad1e7a --- /dev/null +++ b/apps/projects/src/app/lib/i18n/i18n-util.ts @@ -0,0 +1,31 @@ +// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. +/* eslint-disable */ + +import { i18n as initI18n, i18nObject as initI18nObject, i18nString as initI18nString } from 'typesafe-i18n' +import type { LocaleDetector } from 'typesafe-i18n/detectors' +import { detectLocale as detectLocaleFn } from 'typesafe-i18n/detectors' +import type { Formatters, Locales, Translations, TranslationFunctions } from './i18n-types' + +export const baseLocale: Locales = 'en' + +export const locales: Locales[] = [ + 'en', + 'nb' +] + +export const loadedLocales = {} as Record<Locales, Translations> + +export const loadedFormatters = {} as Record<Locales, Formatters> + +export const i18nString = (locale: Locales) => initI18nString<Locales, Formatters>(locale, loadedFormatters[locale]) + +export const i18nObject = (locale: Locales) => + initI18nObject<Locales, Translations, TranslationFunctions, Formatters>( + locale, + loadedLocales[locale], + loadedFormatters[locale] + ) + +export const i18n = () => initI18n<Locales, Translations, TranslationFunctions, Formatters>(loadedLocales, loadedFormatters) + +export const detectLocale = (...detectors: LocaleDetector[]) => detectLocaleFn<Locales>(baseLocale, locales, ...detectors) diff --git a/apps/projects/src/app/lib/i18n/nb/index.ts b/apps/projects/src/app/lib/i18n/nb/index.ts new file mode 100644 index 0000000..af3a487 --- /dev/null +++ b/apps/projects/src/app/lib/i18n/nb/index.ts @@ -0,0 +1,127 @@ +import type {Translation} from "../i18n-types"; + +const nb: Translation = { + nav: { + home: "Hjem", + data: "Data", + settings: "Innstillinger", + usermenu: { + logout: "Logg ut", + logoutTitle: "Logg ut av din profil", + profile: "Profil", + profileTitle: "Administrer din profil", + toggleTitle: "Vis brukermeny" + } + }, + views: { + categoryForm: { + name: "Navn", + color: "Farge", + defaultLabels: "Standard merknader", + labelsPlaceholder: "Søk eller opprett" + }, + dataTablePaginator: { + goToPrevPage: "Gå til forrige side", + goToNextPage: "Gå til neste side", + of: "av", + }, + settingsCategoriesTile: { + deleteAllConfirm: "Er du sikker på at du vil slette denne kategorien?\nDette vil slette alle tilhørende rader!", + active: "Aktive", + archived: "Arkiverte", + name: "Navn", + color: "Farge", + editEntry: "Rediger kategori", + deleteEntry: "Slett kategori", + noCategories: "Ingen kategorier", + categories: "Kategorier" + }, + settingsLabelsTile: { + deleteAllConfirm: "Er du sikker på at du vil slette denne merknaden?\nDen vil bli slette fra alle relaterte rader!", + active: "Aktive", + archived: "Arkiverte", + name: "Navn", + color: "Farge", + editEntry: "Rediger merknad", + deleteEntry: "Slett merknad", + noLabels: "Ingen merknader", + labels: "Merknader" + }, + entryForm: { + entryUpdateError: "En feil oppstod med lagringen av din rad, prøv igjen snart.", + entryCreateError: "En feil oppstod med opprettelsen av din rad, prøv igjen snart.", + errDescriptionReq: "Beskrivelse er påkrevd", + reset: "Tilbakestill", + description: "Beskrivelse", + save: "Lagre", + create: "Opprett", + category: { + category: "Kategori", + placeholder: "Søk eller opprett", + noResults: "Ingen kategorier tilgjengelig (Opprett en ny ved å skrive navnet i søkefeltet).", + errisRequired: "Kategori er påkrevd", + _logReset: "Tilbakestilte kategori-seksjonen" + }, + labels: { + placeholder: "Søk eller opprett", + noResults: "Ingen merkander tilgjengelig (Opprett en ny ved å skrive navnet i søkefeltet).", + labels: "Merknader", + _logReset: "Tilbakestilte merknader-seksjonen" + }, + dateTime: { + errDateIsRequired: "Dato er påkrevd", + errFromIsRequired: "Fra er påkrevd", + errFromAfterTo: "Fra kan ikke være etter Til", + errFromEqTo: "Fra og Til kan ikke ha lik verdi", + errToIsRequired: "Til er påkrevd", + errToBeforeFrom: "Til kan ikke være før Fra", + from: "Fra", + to: "Til", + date: "Dato", + _logReset: "Tilbakestilte dato-seksjonen" + } + } + }, + data: { + durationSummary: "Viser {entryCountString:string}, Tilsammen {totalHourMin:string}", + hourSingleChar: "t", + minSingleChar: "m", + entry: "rad", + entries: "rader", + confirmDeleteEntry: "Er du sikker på at du vil slette denne raden?", + editEntry: "Rediger rad", + date: "Dato", + from: "Fra", + duration: "Tidsrom", + category: "Kategori", + description: "Beskrivelse", + loading: "Laster", + noEntries: "Ingen rader", + to: "til", + use: "Bruk", + }, + home: { + hourSingleChar: "t", + minSingleChar: "m", + confirmDeleteEntry: "Er du sikker på at du vil slette denne raden?", + newEntry: "Ny rad", + editEntry: "Rediger rad", + deleteEntry: "Slett rad", + loggedTimeToday: "Registrert tid hittil idag", + currentTime: "Klokken", + loading: "Laster", + stopwatch: "Stoppeklokke", + todayEntries: "Dagens rader", + noEntriesToday: "Ingen rader i dag", + refreshTodayEntries: "Last inn dagens rader på nytt", + category: "Kategori", + timespan: "Tidsrom", + }, + messages: { + pageNotFound: "Fant ikke siden", + goToFrontpage: "Gå til forsiden", + noInternet: "Det ser ut som at du er uten internettilgang, vennligst sjekk tilkoblingen din." + } +}; + +export default nb; diff --git a/apps/projects/src/app/lib/stores/locale.ts b/apps/projects/src/app/lib/stores/locale.ts new file mode 100644 index 0000000..1215c20 --- /dev/null +++ b/apps/projects/src/app/lib/stores/locale.ts @@ -0,0 +1,21 @@ +import {base_domain, CookieNames} from "$shared/lib/configuration"; +import {get_cookie, set_cookie} from "$shared/lib/helpers"; +import {writable} from "svelte/store"; +import type {Locales} from "$app/lib/i18n/i18n-types"; + +export function preffered_or_default(): Locales { + if (/^en\b/i.test(navigator.language)) { + return "en"; + } + if (/^nb\b/i.test(navigator.language) || /^nn\b/i.test(navigator.language)) { + return "nb"; + } + return "en"; +} + +export const currentLocale = writable<Locales>((get_cookie(CookieNames.locale) ?? preffered_or_default()) as Locales); +currentLocale.subscribe(locale => { + //@ts-ignore + if (locale === "preffered") set_cookie(CookieNames.locale, preffered_or_default(), base_domain()); + set_cookie(CookieNames.locale, locale, base_domain()); +}); diff --git a/apps/projects/src/app/pages/_layout.svelte b/apps/projects/src/app/pages/_layout.svelte index 3d632ae..fb34593 100644 --- a/apps/projects/src/app/pages/_layout.svelte +++ b/apps/projects/src/app/pages/_layout.svelte @@ -2,18 +2,27 @@ import {onMount} from "svelte"; import {location, link} from "svelte-spa-router"; import {logout_user} from "$app/lib/services/user-service"; - import {random_string, switch_theme} from "$shared/lib/helpers"; + import {random_string} from "$shared/lib/helpers"; import {get_session_data} from "$shared/lib/session"; import ProfileModal from "$app/pages/views/profile-modal.svelte"; import {Menu, MenuItem, MenuItemSeparator} from "$shared/components/menu"; import Button from "$shared/components/button.svelte"; import {IconNames} from "$shared/lib/configuration"; + import LL from "$app/lib/i18n/i18n-svelte"; + import BlowoutToolbelt from "$shared/components/blowout-toolbelt.svelte"; + import {currentLocale} from "$app/lib/stores/locale"; let ProfileModalFunctions = {}; let showUserMenu = false; let userMenuTriggerNode; const userMenuId = "__menu_" + random_string(3); - const username = get_session_data().profile.username; + const username = get_session_data()?.profile.username; + + function toolbelt_change(event) { + if (event.detail.name === "locale") { + currentLocale.set(event.detail.value); + } + } onMount(() => { userMenuTriggerNode = document.getElementById("open-user-menu"); @@ -21,6 +30,7 @@ </script> <ProfileModal bind:functions={ProfileModalFunctions}/> +<BlowoutToolbelt on:change={toolbelt_change}/> <nav class="container max-width-xl@md width-fit-content@md width-100% max-width-none margin-y-xs@md margin-bottom-xs block@md position-relative@md position-absolute bottom-unset@md bottom-0"> <div class="tabs-nav-v2 justify-between"> @@ -28,17 +38,17 @@ <div class="tab-v2"> <a href="/home" use:link - class="tabs-nav-v2__item {($location === '/' || $location.startsWith('/home')) ? 'tabs-nav-v2__item--selected' : ''}">Home</a> + class="tabs-nav-v2__item {($location === '/' || $location.startsWith('/home')) ? 'tabs-nav-v2__item--selected' : ''}">{$LL.nav.home()}</a> </div> <div class="tab-v2"> <a href="/data" use:link - class="tabs-nav-v2__item {$location.startsWith('/data') ? 'tabs-nav-v2__item--selected' : ''}">Data</a> + class="tabs-nav-v2__item {$location.startsWith('/data') ? 'tabs-nav-v2__item--selected' : ''}">{$LL.nav.data()}</a> </div> <div class="tab-v2"> <a href="/settings" use:link - class="tabs-nav-v2__item {$location.startsWith('/settings') ? 'tabs-nav-v2__item--selected' : ''}">Settings</a> + class="tabs-nav-v2__item {$location.startsWith('/settings') ? 'tabs-nav-v2__item--selected' : ''}">{$LL.nav.settings()}</a> </div> </div> <div class="tab-v2 padding-x-sm"> @@ -51,7 +61,7 @@ icon_width="2rem" icon_height="2rem" icon_right_aligned="true" - title="Toggle user menu" + title="{$LL.nav.usermenu.toggleTitle()}" aria-controls="{userMenuId}" /> <Menu bind:show="{showUserMenu}" @@ -59,14 +69,12 @@ id="{userMenuId}"> <div slot="options"> <MenuItem on:click={() => ProfileModalFunctions.open()}> - <span title="Administrate your profile">Profile</span> - </MenuItem> - <MenuItem on:click={() => switch_theme()}> - <span title="Change between a dark and light theme">Switch theme</span> + <span title="{$LL.nav.usermenu.profileTitle()}">{$LL.nav.usermenu.profile()}</span> </MenuItem> <MenuItemSeparator/> - <MenuItem danger="true" on:click={() => logout_user()}> - <span title="Log out of your profile">Log out</span> + <MenuItem danger="true" + on:click={() => logout_user()}> + <span title="{$LL.nav.usermenu.logoutTitle()}">{$LL.nav.usermenu.logout()}</span> </MenuItem> </div> </Menu> diff --git a/apps/projects/src/app/pages/data.svelte b/apps/projects/src/app/pages/data.svelte index 070b98b..190c641 100644 --- a/apps/projects/src/app/pages/data.svelte +++ b/apps/projects/src/app/pages/data.svelte @@ -12,6 +12,7 @@ import {delete_time_entry, get_time_entries, get_time_entry} from "$shared/lib/api/time-entry"; import {seconds_to_hour_minute_string, is_guid, move_focus, unwrap_date_time_from_entry} from "$shared/lib/helpers"; import Button from "$shared/components/button.svelte"; + import LL from "$app/lib/i18n/i18n-svelte"; let pageCount = 1; let page = 1; @@ -41,7 +42,10 @@ function set_duration_summary_string() { if (entries.length > 0) { - durationSummary = `Showing ${entries.length} ${entries.length === 1 ? "entry" : "entries"}, totalling in ${seconds_to_hour_minute_string(secondsLogged)}`; + durationSummary = $LL.data.durationSummary({ + entryCountString: `${entries.length} ${entries.length === 1 ? $LL.data.entry() : $LL.data.entries()}`, + totalHourMin: seconds_to_hour_minute_string(secondsLogged) + }); } else { durationSummary = ""; } @@ -61,7 +65,7 @@ date: date_time.start_date, start: date_time.start_time, stop: date_time.stop_time, - durationString: date_time.duration.hours + "h" + date_time.duration.minutes + "m", + durationString: date_time.duration.hours + $LL.data.hourSingleChar() + date_time.duration.minutes + $LL.data.minSingleChar(), seconds: seconds, category: entry.category, labels: entry.labels, @@ -126,7 +130,7 @@ } async function handle_delete_entry_button_click(e, entryId) { - if (confirm("Are you sure you want to delete this entry?")) { + if (confirm($LL.data.confirmDeleteEntry())) { const response = await delete_time_entry(entryId); if (response.ok) { const indexOfEntry = entries.findIndex((c) => c.id === entryId); @@ -180,7 +184,7 @@ }); </script> -<Modal title="Edit entry" +<Modal title="{$LL.data.editEntry()}" bind:functions={EditEntryModal} on:closed={() => EditEntryForm.reset()}> <EntryForm bind:functions={EditEntryForm} @@ -216,7 +220,7 @@ {#if currentTimespanFilter === TimeEntryQueryDuration.SPECIFIC_DATE} <div class="flex items-baseline margin-bottom-xxxxs justify-between"> - <span class="text-sm color-contrast-medium margin-right-xs">Date:</span> + <span class="text-sm color-contrast-medium margin-right-xs">{$LL.data.date()}:</span> <span class="text-sm"> <input type="date" class="border-none padding-0 color-inherit bg-transparent" @@ -227,7 +231,7 @@ {#if currentTimespanFilter === TimeEntryQueryDuration.DATE_RANGE} <div class="flex items-baseline margin-bottom-xxxxs justify-between"> - <span class="text-sm color-contrast-medium margin-right-xs">From:</span> + <span class="text-sm color-contrast-medium margin-right-xs">{$LL.data.from()}:</span> <span class="text-sm"> <input type="date" class="border-none padding-0 color-inherit bg-transparent" @@ -236,7 +240,7 @@ </div> <div class="flex items-baseline margin-bottom-xxxxs justify-between"> - <span class="text-sm color-contrast-medium margin-right-xs">To:</span> + <span class="text-sm color-contrast-medium margin-right-xs">{$LL.data.to()}:</span> <span class="text-sm"> <input type="date" class="border-none padding-0 color-inherit bg-transparent" @@ -249,13 +253,13 @@ <Button variant="subtle" on:click={() => load_entries_with_filter(page)} class="text-sm" - text="Save"/> + text="{$LL.data.use()}"/> </div> </div> <Layout> <Tile class="{isLoading ? 'c-disabled loading' : ''}"> - <nav class="s-tabs text-sm"> + <nav class="s-tabs text-sm hide"> <ul class="s-tabs__list"> <li><span class="s-tabs__link s-tabs__link--current">All (21)</span></li> <li><span class="s-tabs__link">Published (19)</span></li> @@ -280,7 +284,7 @@ <TCell type="th" style="width: 100px"> <div class="flex items-center justify-between"> - <span>Date</span> + <span>{$LL.data.date()}</span> <div class="date_filter_box_el cursor-pointer" on:click={toggle_date_filter_box}> <Icon name="{IconNames.funnel}"/> @@ -291,21 +295,21 @@ <TCell type="th" style="width: 100px"> <div class="flex items-center"> - <span>Duration</span> + <span>{$LL.data.duration()}</span> </div> </TCell> <TCell type="th" style="width: 100px;"> <div class="flex items-center"> - <span>Category</span> + <span>{$LL.data.category()}</span> </div> </TCell> <TCell type="th" style="width: 300px;"> <div class="flex items-center"> - <span>Description</span> + <span>{$LL.data.description()}</span> </div> </TCell> <TCell type="th" @@ -366,7 +370,7 @@ <TCell type="th" thScope="row" colspan="7"> - {isLoading ? "Loading..." : "No entries"} + {isLoading ? $LL.data.loading() + "..." : $LL.data.noEntries()} </TCell> </TRow> {/if} @@ -378,7 +382,7 @@ {#if durationSummary} <small class={isLoading ? "c-disabled loading" : ""}>{durationSummary}</small> {:else} - <small class={isLoading ? "c-disabled loading" : ""}>No entries</small> + <small class={isLoading ? "c-disabled loading" : ""}>{$LL.data.noEntries()}</small> {/if} </p> diff --git a/apps/projects/src/app/pages/home.svelte b/apps/projects/src/app/pages/home.svelte index 84d6728..33bb0d8 100644 --- a/apps/projects/src/app/pages/home.svelte +++ b/apps/projects/src/app/pages/home.svelte @@ -1,4 +1,5 @@ <script lang="ts"> + import LL from "$app/lib/i18n/i18n-svelte"; import {delete_time_entry, get_time_entries, get_time_entry, update_time_entry} from "$shared/lib/api/time-entry"; import {IconNames, QueryKeys} from "$shared/lib/configuration"; import {TimeEntryDto} from "$shared/lib/models/TimeEntryDto"; @@ -18,7 +19,7 @@ let isLoading = false; let EditEntryForm: any; let timeEntries = [] as Array<TimeEntryDto>; - let timeLoggedTodayString = "0h0m"; + let timeLoggedTodayString = "0" + $LL.home.hourSingleChar() + "0" + $LL.home.minSingleChar(); const queryClient = useQueryClient(); const queryResult = useQuery(QueryKeys.entries, async () => await get_time_entries({ @@ -49,7 +50,7 @@ } async function on_delete_entry_button_click(event, entryId: string) { - if (confirm("Are you sure you want to delete this entry?")) { + if (confirm($LL.home.confirmDeleteEntry())) { $delete_entry_mutation.mutate(entryId); } } @@ -88,34 +89,34 @@ <Layout> <div class="grid gap-md margin-top-xs flex-row@md items-start flex-column-reverse"> <Tile class="col"> - <h3 class="text-md padding-bottom-xxxs">New entry</h3> + <h3 class="text-md padding-bottom-xxxs">{$LL.home.newEntry()}</h3> <EntryFrom bind:functions={EditEntryForm}/> </Tile> <div class="col grid gap-sm"> <Tile class="col-6@md col-12"> <p class="text-xxl">{timeLoggedTodayString}</p> - <p class="text-xs margin-bottom-xxs">Logged time today</p> + <p class="text-xs margin-bottom-xxs">{$LL.home.loggedTimeToday()}</p> <pre class="text-xxl">{currentTime}</pre> - <p class="text-xs">Current time</p> + <p class="text-xs">{$LL.home.currentTime()}</p> </Tile> <Tile class="col-6@md col-12"> <Stopwatch on:create={on_create_from_stopwatch}> <h3 slot="header" - class="text-md">Stopwatch</h3> + class="text-md">{$LL.home.stopwatch()}</h3> </Stopwatch> </Tile> <Tile class="col-12"> - <h3 class="text-md padding-bottom-xxxs">Today's entries</h3> + <h3 class="text-md padding-bottom-xxxs">{$LL.home.todayEntries()}</h3> <div class="max-width-100% overflow-auto"> <Table class="width-100% text-sm"> <THead> <TCell type="th" class="text-left"> - <span>Category</span> + <span>{$LL.home.category()}</span> </TCell> <TCell type="th" class="text-left"> - <span>Timespan</span> + <span>{$LL.home.timespan()}</span> </TCell> <TCell type="th" class="text-right"> @@ -123,7 +124,7 @@ variant="reset" icon_width="1.2rem" icon_height="1.2rem" - title="Refresh today's entries" + title="{$LL.home.refreshTodayEntries()}" on:click={() => queryClient.invalidateQueries(QueryKeys.entries)}/> </TCell> </THead> @@ -148,13 +149,13 @@ icon_width="1.2rem" icon_height="1.2rem" on:click={(e) => on_edit_entry_button_click(e, entry.id)} - title="Edit entry"/> + title="{$LL.home.editEntry()}"/> <Button icon="{IconNames.trash}" variant="reset" icon_width="1.2rem" icon_height="1.2rem" on:click={(e) => on_delete_entry_button_click(e, entry.id)} - title="Delete entry"/> + title="{$LL.home.deleteEntry()}"/> </TCell> </TRow> {/each} @@ -163,7 +164,7 @@ <TCell type="th" thScope="row" colspan="7"> - {isLoading ? "Loading..." : "No entries today"} + {isLoading ? $LL.home.loading() + "..." : $LL.home.noEntriesToday()} </TCell> </TRow> {/if} diff --git a/apps/projects/src/app/pages/not-found.svelte b/apps/projects/src/app/pages/not-found.svelte index 46d0d1d..8822e0e 100644 --- a/apps/projects/src/app/pages/not-found.svelte +++ b/apps/projects/src/app/pages/not-found.svelte @@ -1,4 +1,5 @@ <script> + import LL from "$app/lib/i18n/i18n-svelte"; import {link} from "svelte-spa-router"; </script> @@ -18,7 +19,7 @@ <main> <header>404</header> - <p>Page not found!</p> + <p>{$LL.messages.pageNotFound()}</p> <a use:link - href="/">Go to front</a> + href="/">{$LL.messages.goToFrontpage()}</a> </main> diff --git a/apps/projects/src/app/pages/views/category-form/index.svelte b/apps/projects/src/app/pages/views/category-form/index.svelte index e8c0f94..21024c3 100644 --- a/apps/projects/src/app/pages/views/category-form/index.svelte +++ b/apps/projects/src/app/pages/views/category-form/index.svelte @@ -1,9 +1,9 @@ <script lang="ts"> import Alert from "$shared/components/alert.svelte"; import Dropdown from "$shared/components/dropdown.svelte"; - import labels, {reload_labels, create_label_async} from "$app/lib/stores/labels"; + import labels, {create_label_async} from "$app/lib/stores/labels"; import {generate_random_hex_color} from "$shared/lib/colors"; - import {get} from "svelte/store"; + import LL from "$app/lib/i18n/i18n-svelte"; let LabelsDropdown; @@ -106,7 +106,7 @@ <div class="grid gap-x-xs margin-bottom-sm"> <div class="col-10"> <label for="name" - class="form-label margin-bottom-xxs">Name</label> + class="form-label margin-bottom-xxs">{$LL.views.categoryForm.name()}</label> <input type="text" class="form-control width-100%" id="name" @@ -117,7 +117,7 @@ </div> <div class="col-2"> <label for="color" - class="form-label margin-bottom-xxs">Color</label> + class="form-label margin-bottom-xxs">{$LL.views.categoryForm.color()}</label> <input type="color" class="form-control width-100%" id="color" @@ -130,10 +130,10 @@ </div> <div class="margin-bottom-sm"> <label for="labels" - class="form-label margin-bottom-xxs">Default labels</label> + class="form-label margin-bottom-xxs">{$LL.views.categoryForm.defaultLabels()}</label> <Dropdown id="labels" createable={true} - placeholder="Search or create" + placeholder="{$LL.views.categoryForm.labelsPlaceholder()}" entries={$labels} multiple={true} on_create_async={(name) => dough.fields.labels.create({name})}/> diff --git a/apps/projects/src/app/pages/views/data-table-paginator.svelte b/apps/projects/src/app/pages/views/data-table-paginator.svelte index 3d9834a..b2649eb 100644 --- a/apps/projects/src/app/pages/views/data-table-paginator.svelte +++ b/apps/projects/src/app/pages/views/data-table-paginator.svelte @@ -1,4 +1,5 @@ <script> + import LL from "$app/lib/i18n/i18n-svelte"; import {createEventDispatcher, onMount} from "svelte"; import {restrict_input_to_numbers} from "$shared/lib/helpers"; @@ -52,8 +53,8 @@ <button on:click={decrement} class="reset pagination__item {canDecrement ? '' : 'c-disabled'}"> <svg class="icon icon--xs flip-x" - viewBox="0 0 16 16" - ><title>Go to previous page</title> + viewBox="0 0 16 16"> + <title>{$LL.views.dataTablePaginator.goToPrevPage()}</title> <polyline points="6 2 12 8 6 14" fill="none" @@ -68,26 +69,23 @@ <li> <span class="pagination__jumper flex items-center"> - <input - aria-label="Page number" - class="form-control" - id="curr-page" - type="text" - on:change={handle_change} - value={page} + <input aria-label="Page number" + class="form-control" + id="curr-page" + type="text" + on:change={handle_change} + value={page} /> - <em>of {pageCount}</em> + <em>{$LL.views.dataTablePaginator.of()} {pageCount}</em> </span> </li> <li> - <button - on:click={increment} - class="reset pagination__item {canIncrement ? '' : 'c-disabled'}" - > + <button on:click={increment} + class="reset pagination__item {canIncrement ? '' : 'c-disabled'}"> <svg class="icon icon--xs" - viewBox="0 0 16 16" - ><title>Go to next page</title> + viewBox="0 0 16 16"> + <title>{$LL.views.dataTablePaginator.goToNextPage()}</title> <polyline points="6 2 12 8 6 14" fill="none" diff --git a/apps/projects/src/app/pages/views/entry-form/index.svelte b/apps/projects/src/app/pages/views/entry-form/index.svelte index cb974ed..cf3d173 100644 --- a/apps/projects/src/app/pages/views/entry-form/index.svelte +++ b/apps/projects/src/app/pages/views/entry-form/index.svelte @@ -1,4 +1,5 @@ <script lang="ts"> + import LL from "$app/lib/i18n/i18n-svelte"; import {TimeEntryDto} from "$shared/lib/models/TimeEntryDto"; import {Temporal} from "@js-temporal/polyfill"; import {createEventDispatcher, onMount, onDestroy} from "svelte"; @@ -48,7 +49,7 @@ function description_is_valid() { if (!description) { - descriptionError = "Description is required"; + descriptionError = $LL.views.entryForm.errDescriptionReq(); } else { descriptionError = ""; } @@ -97,7 +98,7 @@ functions.reset(); dispatch("updated", response.data); } else { - formError = "An error occured while updating the entry, try again soon"; + formError = $LL.views.entryForm.entryUpdateError(); formIsLoading = false; } } else { @@ -106,7 +107,7 @@ functions.reset(); dispatch("created"); } else { - formError = "An error occured while creating the entry, try again soon"; + formError = $LL.views.entryForm.entryCreateError(); formIsLoading = false; } } @@ -175,14 +176,14 @@ <div class="margin-bottom-sm"> <Textarea class="width-100%" id="description" - label="Description" + label="{$LL.views.entryForm.description()}" errorText="{descriptionError}" bind:value={description}></Textarea> </div> <div class="flex flex-row justify-end gap-x-xs"> {#if entryId} - <Button text="Reset" + <Button text="{$LL.views.entryForm.reset()}" on:click={() => functions.reset()} variant="subtle" /> @@ -190,7 +191,7 @@ <Button loading={formIsLoading} type="submit" variant="primary" - text={entryId ? "Save" : "Create"} + text={entryId ? $LL.views.entryForm.save() : $LL.views.entryForm.create()} /> </div> </form> diff --git a/apps/projects/src/app/pages/views/entry-form/sections/category.svelte b/apps/projects/src/app/pages/views/entry-form/sections/category.svelte index aac84be..f7af382 100644 --- a/apps/projects/src/app/pages/views/entry-form/sections/category.svelte +++ b/apps/projects/src/app/pages/views/entry-form/sections/category.svelte @@ -3,6 +3,7 @@ import Dropdown from "$shared/components/dropdown.svelte"; import {is_guid, move_focus} from "$shared/lib/helpers"; import categories, {reload_categories, create_category_async} from "$app/lib/stores/categories"; + import LL from "$app/lib/i18n/i18n-svelte" let categoriesError = ""; let loading = false; @@ -12,7 +13,7 @@ function reset() { DropdownExports.reset(); categoriesError = ""; - console.log("Reset category-part"); + console.log($LL.views.entryForm.category._logReset()); } async function on_create({name}) { @@ -40,7 +41,7 @@ let isValid = true; const category = get_selected(); if (!is_guid(category?.id)) { - categoriesError = "Category is required"; + categoriesError = $LL.views.entryForm.category.errisRequired(); isValid = false; move_focus(document.getElementById("category-dropdown")); } else { @@ -60,15 +61,15 @@ <Dropdown entries={$categories} - label="Category" + label="{$LL.views.entryForm.category.category()}" maxlength="50" createable={true} - placeholder="Search or create" + placeholder="{$LL.views.entryForm.category.placeholder()}" id="category-dropdown" loading={loading} name="category-dropdown" on_create_async={on_create} - noResultsText="No categories available (Create a new one by searching for it)" + noResultsText="{$LL.views.entryForm.category.noResults()}" errorText="{categoriesError}" bind:this={DropdownExports} /> diff --git a/apps/projects/src/app/pages/views/entry-form/sections/date-time.svelte b/apps/projects/src/app/pages/views/entry-form/sections/date-time.svelte index c91e014..47b06e3 100644 --- a/apps/projects/src/app/pages/views/entry-form/sections/date-time.svelte +++ b/apps/projects/src/app/pages/views/entry-form/sections/date-time.svelte @@ -1,131 +1,134 @@ <script lang="ts"> - import {Temporal} from "@js-temporal/polyfill"; + import LL from "$app/lib/i18n/i18n-svelte"; + import {Temporal} from "@js-temporal/polyfill"; - // TIME - let fromTimeValue = ""; - let fromTimeError = ""; - let toTimeValue = ""; - let toTimeError = ""; + // TIME + let fromTimeValue = ""; + let fromTimeError = ""; + let toTimeValue = ""; + let toTimeError = ""; - function handle_from_time_changed(e) { - fromTimeValue = e.target.value; - if (fromTimeValue) { - fromTimeError = ""; - } - } + function handle_from_time_changed(e) { + fromTimeValue = e.target.value; + if (fromTimeValue) { + fromTimeError = ""; + } + } - function handle_to_time_changed(e) { - toTimeValue = e.target.value; - if (toTimeValue) { - toTimeError = ""; - } - } + function handle_to_time_changed(e) { + toTimeValue = e.target.value; + if (toTimeValue) { + toTimeError = ""; + } + } - // DATE - let date = Temporal.Now.plainDateTimeISO().toString().substring(0, 10); - let dateError = ""; + // DATE + let date = Temporal.Now.plainDateTimeISO().toString().substring(0, 10); + let dateError = ""; - function is_valid() { - let isValid = true; - let focusIsSet = false; - if (!date) { - dateError = "Date is required"; - isValid = false; - if (!focusIsSet) { - document.getElementById("date")?.focus(); - focusIsSet = true; - } - } else { - dateError = ""; - } + function is_valid() { + let isValid = true; + let focusIsSet = false; + if (!date) { + dateError = $LL.views.entryForm.dateTime.errDateIsRequired(); + isValid = false; + if (!focusIsSet) { + document.getElementById("date")?.focus(); + focusIsSet = true; + } + } else { + dateError = ""; + } - if (!fromTimeValue) { - fromTimeError = "From is required"; - isValid = false; - if (!focusIsSet) { - document.getElementById("from")?.focus(); - focusIsSet = true; - } - } else if (toTimeValue && fromTimeValue > toTimeValue) { - fromTimeError = "From can not be after To"; - isValid = false; - if (!focusIsSet) { - document.getElementById("from")?.focus(); - focusIsSet = true; - } - } else if (fromTimeValue === toTimeValue) { - fromTimeError = "From and To can not be equal"; - isValid = false; - if (!focusIsSet) { - document.getElementById("from")?.focus(); - focusIsSet = true; - } - } else { - fromTimeError = ""; - } + if (!fromTimeValue) { + fromTimeError = $LL.views.entryForm.dateTime.errFromIsRequired(); + isValid = false; + if (!focusIsSet) { + document.getElementById("from")?.focus(); + focusIsSet = true; + } + } else if (toTimeValue && fromTimeValue > toTimeValue) { + fromTimeError = $LL.views.entryForm.dateTime.errFromAfterTo(); + isValid = false; + if (!focusIsSet) { + document.getElementById("from")?.focus(); + focusIsSet = true; + } + } else if (fromTimeValue === toTimeValue) { + fromTimeError = $LL.views.entryForm.dateTime.errFromEqTo(); - if (!toTimeValue) { - toTimeError = "To is required"; - isValid = false; - if (!focusIsSet) { - document.getElementById("to")?.focus(); - focusIsSet = true; - } - } else if (fromTimeValue && toTimeValue < fromTimeValue) { - toTimeError = "To can not be before From"; - isValid = false; - if (!focusIsSet) { - document.getElementById("to")?.focus(); - focusIsSet = true; - } - } else { - toTimeError = ""; - } + isValid = false; + if (!focusIsSet) { + document.getElementById("from")?.focus(); + focusIsSet = true; + } + } else { + fromTimeError = ""; + } - return isValid; - } + if (!toTimeValue) { + toTimeError = $LL.views.entryForm.dateTime.errToIsRequired(); + isValid = false; + if (!focusIsSet) { + document.getElementById("to")?.focus(); + focusIsSet = true; + } + } else if (fromTimeValue && toTimeValue < fromTimeValue) { + toTimeError = $LL.views.entryForm.dateTime.errToBeforeFrom(); + isValid = false; + if (!focusIsSet) { + document.getElementById("to")?.focus(); + focusIsSet = true; + } + } else { + toTimeError = ""; + } - export const functions = { - get_from_time_value() { - return fromTimeValue; - }, - get_to_time_value() { - return toTimeValue; - }, - get_date() { - return date; - }, - is_valid, - reset(focusDate = false) { - fromTimeValue = ""; - toTimeValue = ""; - if (focusDate) { - document.getElementById("date")?.focus(); - } - }, - set_times(value) { - console.log(value); - fromTimeValue = value.from.toString().substring(0, 5); - toTimeValue = value.to.toString().substring(0, 5); - }, - set_date(new_date: Temporal.PlainDate) { - date = new_date.toString(); - }, - set_values(values) { - const currentTimeZone = Temporal.Now.timeZone().id; - const startDate = Temporal.Instant.from(values.start); - const stopDate = Temporal.Instant.from(values.stop); - fromTimeValue = startDate.toZonedDateTimeISO(currentTimeZone).toPlainTime().toString().substring(0, 5); - toTimeValue = stopDate.toZonedDateTimeISO(currentTimeZone).toPlainTime().toString().substring(0, 5); - date = startDate.toZonedDateTimeISO(currentTimeZone).toPlainDate().toString(); - } - }; + return isValid; + } + + export const functions = { + get_from_time_value() { + return fromTimeValue; + }, + get_to_time_value() { + return toTimeValue; + }, + get_date() { + return date; + }, + is_valid, + reset(focusDate = false) { + fromTimeValue = ""; + toTimeValue = ""; + if (focusDate) { + document.getElementById("date")?.focus(); + } + console.log($LL.views.entryForm.dateTime._logReset()); + }, + set_times(value) { + console.log(value); + fromTimeValue = value.from.toString().substring(0, 5); + toTimeValue = value.to.toString().substring(0, 5); + }, + set_date(new_date: Temporal.PlainDate) { + date = new_date.toString(); + }, + set_values(values) { + const currentTimeZone = Temporal.Now.timeZone().id; + const startDate = Temporal.Instant.from(values.start); + const stopDate = Temporal.Instant.from(values.stop); + fromTimeValue = startDate.toZonedDateTimeISO(currentTimeZone).toPlainTime().toString().substring(0, 5); + toTimeValue = stopDate.toZonedDateTimeISO(currentTimeZone).toPlainTime().toString().substring(0, 5); + date = startDate.toZonedDateTimeISO(currentTimeZone).toPlainDate().toString(); + } + }; </script> <div class="grid gap-xs"> <div class="col-4"> <label for="date" - class="form-label margin-bottom-xxs">Date</label> + class="form-label margin-bottom-xxs">{$LL.views.entryForm.dateTime.date()}</label> <input type="date" id="date" class="form-control width-100%" @@ -136,7 +139,7 @@ </div> <div class="col-4"> <label for="from" - class="form-label margin-bottom-xxs">From</label> + class="form-label margin-bottom-xxs">{$LL.views.entryForm.dateTime.from()}</label> <input id="from" class="form-control width-100%" pattern="[0-9][0-9]:[0-9][0-9]" @@ -150,7 +153,7 @@ </div> <div class="col-4"> <label for="to" - class="form-label margin-bottom-xxs">To</label> + class="form-label margin-bottom-xxs">{$LL.views.entryForm.dateTime.to()}</label> <input id="to" class="form-control width-100%" pattern="[0-9][0-9]:[0-9][0-9]" diff --git a/apps/projects/src/app/pages/views/entry-form/sections/labels.svelte b/apps/projects/src/app/pages/views/entry-form/sections/labels.svelte index f0853cc..a6f324b 100644 --- a/apps/projects/src/app/pages/views/entry-form/sections/labels.svelte +++ b/apps/projects/src/app/pages/views/entry-form/sections/labels.svelte @@ -1,4 +1,5 @@ <script> + import LL from "$app/lib/i18n/i18n-svelte"; import {generate_random_hex_color} from "$shared/lib/colors"; import labels, {reload_labels, create_label_async} from "$app/lib/stores/labels"; import Dropdown from "$shared/components/dropdown.svelte"; @@ -9,7 +10,7 @@ function reset() { DropdownExports.reset(); - console.log("Reset labels-part"); + console.log($LL.views.entryForm.labels._logReset()); } function get_selected() { @@ -50,15 +51,15 @@ <Dropdown entries={$labels} - label="Labels" + label="{$LL.views.entryForm.labels.labels()}" maxlength="50" createable={true} - placeholder="Search or create" + placeholder="{$LL.views.entryForm.labels.placeholder()}" multiple="{true}" id="labels-search" name="labels-search" on_create_async={on_create} - noResultsText="No labels available (Create a new one by searching for it)" + noResultsText="{$LL.views.entryForm.labels.placeholder()}" errorText="{labelsError}" bind:this={DropdownExports} {loading} diff --git a/apps/projects/src/app/pages/views/settings-categories-tile.svelte b/apps/projects/src/app/pages/views/settings-categories-tile.svelte index 890609a..8d2480f 100644 --- a/apps/projects/src/app/pages/views/settings-categories-tile.svelte +++ b/apps/projects/src/app/pages/views/settings-categories-tile.svelte @@ -8,6 +8,7 @@ import Button from "$shared/components/button.svelte"; import Tile from "$shared/components/tile.svelte"; import {Table, THead, TBody, TCell, TRow} from "$shared/components/table"; + import LL from "$app/lib/i18n/i18n-svelte"; let is_loading = true; let categories = []; @@ -38,9 +39,7 @@ if ( row && row.dataset.id && - confirm( - "Are you sure you want to delete this category?\nThis will delete all relating entries!" - ) + confirm($LL.views.settingsCategoriesTile.deleteAllConfirm()) ) { const response = await delete_time_category(row.dataset.id); if (response.ok) { @@ -56,14 +55,14 @@ </script> <Tile class="col-6@md col-12 {is_loading ? 'c-disabled loading' : ''}"> - <h2 class="margin-bottom-xxs">Categories</h2> + <h2 class="margin-bottom-xxs">{$LL.views.settingsCategoriesTile.categories()}</h2> {#if active_categories.length > 0 && archived_categories.length > 0} <nav class="s-tabs text-sm"> <ul class="s-tabs__list"> <li><a class="s-tabs__link s-tabs__link--current" - href="#0">Active ({active_categories.length})</a></li> + href="#0">{$LL.views.settingsCategoriesTile.active()} ({active_categories.length})</a></li> <li><a class="s-tabs__link" - href="#0">Archived ({archived_categories.length})</a></li> + href="#0">{$LL.views.settingsCategoriesTile.archived()} ({archived_categories.length})</a></li> </ul> </nav> {/if} @@ -72,11 +71,11 @@ <THead class="text-left"> <TCell type="th" thScope="col"> - Name + {$LL.views.settingsCategoriesTile.name()} </TCell> <TCell type="th" thScope="col"> - Color + {$LL.views.settingsCategoriesTile.color()} </TCell> <TCell type="th" thScope="col" @@ -102,13 +101,13 @@ class="hide" icon_height="1.2rem" on:click={handle_edit_category_click} - title="Edit entry"/> + title="{$LL.views.settingsCategoriesTile.editEntry()}"/> <Button icon="{IconNames.trash}" variant="reset" icon_width="1.2rem" icon_height="1.2rem" on:click={handle_delete_category_click} - title="Delete entry"/> + title="{$LL.views.settingsCategoriesTile.deleteEntry()}"/> </TCell> </TRow> @@ -117,7 +116,7 @@ <TRow> <TCell type="th" thScope="3"> - No categories + {$LL.views.settingsCategoriesTile.noCategories()} </TCell> </TRow> {/if} diff --git a/apps/projects/src/app/pages/views/settings-labels-tile.svelte b/apps/projects/src/app/pages/views/settings-labels-tile.svelte index f59e233..59b5e30 100644 --- a/apps/projects/src/app/pages/views/settings-labels-tile.svelte +++ b/apps/projects/src/app/pages/views/settings-labels-tile.svelte @@ -5,6 +5,7 @@ import Button from "$shared/components/button.svelte"; import Tile from "$shared/components/tile.svelte"; import {Table, THead, TBody, TCell, TRow} from "$shared/components/table"; + import LL from "$app/lib/i18n/i18n-svelte"; let is_loading = true; @@ -25,9 +26,7 @@ if ( row && row.dataset.id && - confirm( - "Are you sure you want to delete this label?\nIt will be removed from all related entries!" - ) + confirm($LL.views.settingsLabelsTile.deleteAllConfirm()) ) { await delete_label_async({id: row.dataset.id}); row.classList.add("d-none"); @@ -40,14 +39,14 @@ </script> <Tile class="col-6@md col-12 {is_loading ? 'c-disabled loading' : ''}"> - <h2 class="margin-bottom-xxs">Labels</h2> + <h2 class="margin-bottom-xxs">{$LL.views.settingsLabelsTile.labels()}</h2> {#if active_labels.length > 0 && archived_labels.length > 0} <nav class="s-tabs text-sm"> <ul class="s-tabs__list"> <li><a class="s-tabs__link s-tabs__link--current" - href="#0">Active ({active_labels.length})</a></li> + href="#0">{$LL.views.settingsLabelsTile.active()} ({active_labels.length})</a></li> <li><a class="s-tabs__link" - href="#0">Archived ({archived_labels.length})</a></li> + href="#0">{$LL.views.settingsLabelsTile.archived()} ({archived_labels.length})</a></li> </ul> </nav> {/if} @@ -56,11 +55,11 @@ <THead class="text-left"> <TCell type="th" thScope="row"> - Name + {$LL.views.settingsLabelsTile.name()} </TCell> <TCell type="th" thScope="row"> - Color + {$LL.views.settingsLabelsTile.color()} </TCell> <TCell type="th" thScope="row" @@ -87,13 +86,13 @@ class="hide" icon_height="1.2rem" on:click={handle_edit_label_click} - title="Edit entry"/> + title="{$LL.views.settingsLabelsTile.editEntry()}"/> <Button icon="{IconNames.trash}" variant="reset" icon_width="1.2rem" icon_height="1.2rem" on:click={handle_delete_label_click} - title="Delete entry"/> + title="{$LL.views.settingsLabelsTile.deleteEntry()}"/> </TCell> </TRow> {/each} diff --git a/apps/projects/src/package.json b/apps/projects/src/package.json index 636df49..d7b41a8 100644 --- a/apps/projects/src/package.json +++ b/apps/projects/src/package.json @@ -3,13 +3,16 @@ "version": "0.0.1", "private": "true", "scripts": { - "dev": "vite", + "dev": "npm-run-all --parallel vite typesafe-i18n", + "vite": "vite", + "typesafe-i18n": "typesafe-i18n", "build": "vite build" }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "1.0.0-next.43", "@sveltestack/svelte-query": "^1.6.0", "broadcast-channel": "^4.13.0", + "npm-run-all": "^4.1.5", "sass": "^1.51.0", "svelte": "^3.48.0", "svelte-preprocess": "^4.10.6", @@ -19,6 +22,7 @@ }, "dependencies": { "@js-temporal/polyfill": "^0.4.1", - "fuzzysort": "^1.9.0" + "fuzzysort": "^1.9.0", + "typesafe-i18n": "^5.5.2" } } diff --git a/apps/projects/src/pnpm-lock.yaml b/apps/projects/src/pnpm-lock.yaml index aae0331..094c1be 100644 --- a/apps/projects/src/pnpm-lock.yaml +++ b/apps/projects/src/pnpm-lock.yaml @@ -6,21 +6,25 @@ specifiers: '@sveltestack/svelte-query': ^1.6.0 broadcast-channel: ^4.13.0 fuzzysort: ^1.9.0 + npm-run-all: ^4.1.5 sass: ^1.51.0 svelte: ^3.48.0 svelte-preprocess: ^4.10.6 svelte-spa-router: ^3.2.0 + typesafe-i18n: ^5.5.2 typescript: 4.6.4 vite: ^2.9.8 dependencies: '@js-temporal/polyfill': 0.4.1 fuzzysort: 1.9.0 + typesafe-i18n: 5.5.2_typescript@4.6.4 devDependencies: '@sveltejs/vite-plugin-svelte': 1.0.0-next.43_svelte@3.48.0+vite@2.9.8 '@sveltestack/svelte-query': 1.6.0_broadcast-channel@4.13.0 broadcast-channel: 4.13.0 + npm-run-all: 4.1.5 sass: 1.51.0 svelte: 3.48.0 svelte-preprocess: 4.10.6_24ezlekk4ocevlsjgs2qnqmjum @@ -101,6 +105,13 @@ packages: '@types/node': 17.0.31 dev: true + /ansi-styles/3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + /anymatch/3.1.2: resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==} engines: {node: '>= 8'} @@ -148,6 +159,22 @@ packages: resolution: {integrity: sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=} dev: true + /call-bind/1.0.2: + resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + dependencies: + function-bind: 1.1.1 + get-intrinsic: 1.1.1 + dev: true + + /chalk/2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + /chokidar/3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} @@ -163,10 +190,31 @@ packages: fsevents: 2.3.2 dev: true + /color-convert/1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + + /color-name/1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true + /concat-map/0.0.1: resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} dev: true + /cross-spawn/6.0.5: + resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==} + engines: {node: '>=4.8'} + dependencies: + nice-try: 1.0.5 + path-key: 2.0.1 + semver: 5.7.1 + shebang-command: 1.2.0 + which: 1.3.1 + dev: true + /debug/4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -184,6 +232,14 @@ packages: engines: {node: '>=0.10.0'} dev: true + /define-properties/1.1.4: + resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==} + engines: {node: '>= 0.4'} + dependencies: + has-property-descriptors: 1.0.0 + object-keys: 1.1.1 + dev: true + /detect-indent/6.1.0: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} @@ -193,6 +249,50 @@ packages: resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} dev: true + /error-ex/1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + dependencies: + is-arrayish: 0.2.1 + dev: true + + /es-abstract/1.20.1: + resolution: {integrity: sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + es-to-primitive: 1.2.1 + function-bind: 1.1.1 + function.prototype.name: 1.1.5 + get-intrinsic: 1.1.1 + get-symbol-description: 1.0.0 + has: 1.0.3 + has-property-descriptors: 1.0.0 + has-symbols: 1.0.3 + internal-slot: 1.0.3 + is-callable: 1.2.4 + is-negative-zero: 2.0.2 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.2 + is-string: 1.0.7 + is-weakref: 1.0.2 + object-inspect: 1.12.2 + object-keys: 1.1.1 + object.assign: 4.1.2 + regexp.prototype.flags: 1.4.3 + string.prototype.trimend: 1.0.5 + string.prototype.trimstart: 1.0.5 + unbox-primitive: 1.0.2 + dev: true + + /es-to-primitive/1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.4 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + dev: true + /es6-promise/3.3.1: resolution: {integrity: sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=} dev: true @@ -405,6 +505,11 @@ packages: esbuild-windows-arm64: 0.14.38 dev: true + /escape-string-regexp/1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + /estree-walker/2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} dev: true @@ -436,10 +541,40 @@ packages: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: true + /function.prototype.name/1.1.5: + resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.1 + functions-have-names: 1.2.3 + dev: true + + /functions-have-names/1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: true + /fuzzysort/1.9.0: resolution: {integrity: sha512-MOxCT0qLTwLqmEwc7UtU045RKef7mc8Qz8eR4r2bLNEq9dy/c3ZKMEFp6IEst69otkQdFZ4FfgH2dmZD+ddX1g==} dev: false + /get-intrinsic/1.1.1: + resolution: {integrity: sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==} + dependencies: + function-bind: 1.1.1 + has: 1.0.3 + has-symbols: 1.0.3 + dev: true + + /get-symbol-description/1.0.0: + resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.1.1 + dev: true + /glob-parent/5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -462,6 +597,33 @@ packages: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} dev: true + /has-bigints/1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + dev: true + + /has-flag/3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: true + + /has-property-descriptors/1.0.0: + resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} + dependencies: + get-intrinsic: 1.1.1 + dev: true + + /has-symbols/1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: true + + /has-tostringtag/1.0.0: + resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + /has/1.0.3: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} @@ -469,6 +631,10 @@ packages: function-bind: 1.1.1 dev: true + /hosted-git-info/2.8.9: + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + dev: true + /immutable/4.0.0: resolution: {integrity: sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==} dev: true @@ -484,6 +650,25 @@ packages: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} dev: true + /internal-slot/1.0.3: + resolution: {integrity: sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.1.1 + has: 1.0.3 + side-channel: 1.0.4 + dev: true + + /is-arrayish/0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + dev: true + + /is-bigint/1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + dependencies: + has-bigints: 1.0.2 + dev: true + /is-binary-path/2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -491,12 +676,32 @@ packages: binary-extensions: 2.2.0 dev: true + /is-boolean-object/1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-callable/1.2.4: + resolution: {integrity: sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==} + engines: {node: '>= 0.4'} + dev: true + /is-core-module/2.9.0: resolution: {integrity: sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==} dependencies: has: 1.0.3 dev: true + /is-date-object/1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + /is-extglob/2.1.1: resolution: {integrity: sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=} engines: {node: '>=0.10.0'} @@ -509,20 +714,84 @@ packages: is-extglob: 2.1.1 dev: true + /is-negative-zero/2.0.2: + resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} + engines: {node: '>= 0.4'} + dev: true + + /is-number-object/1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + /is-number/7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} dev: true + /is-regex/1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-shared-array-buffer/1.0.2: + resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} + dependencies: + call-bind: 1.0.2 + dev: true + + /is-string/1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-symbol/1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /is-weakref/1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + dependencies: + call-bind: 1.0.2 + dev: true + + /isexe/2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + /jsbi/4.3.0: resolution: {integrity: sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g==} dev: false + /json-parse-better-errors/1.0.2: + resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} + dev: true + /kleur/4.1.4: resolution: {integrity: sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==} engines: {node: '>=6'} dev: true + /load-json-file/4.0.0: + resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} + engines: {node: '>=4'} + dependencies: + graceful-fs: 4.2.10 + parse-json: 4.0.0 + pify: 3.0.0 + strip-bom: 3.0.0 + dev: true + /magic-string/0.25.9: resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} dependencies: @@ -536,6 +805,11 @@ packages: sourcemap-codec: 1.4.8 dev: true + /memorystream/0.3.1: + resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} + engines: {node: '>= 0.10.0'} + dev: true + /microtime/3.0.0: resolution: {integrity: sha512-SirJr7ZL4ow2iWcb54bekS4aWyBQNVcEDBiwAz9D/sTgY59A+uE8UJU15cp5wyZmPBwg/3zf8lyCJ5NUe1nVlQ==} engines: {node: '>= 4.0.0'} @@ -577,6 +851,10 @@ packages: hasBin: true dev: true + /nice-try/1.0.5: + resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} + dev: true + /node-addon-api/1.7.2: resolution: {integrity: sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==} dev: true @@ -586,11 +864,55 @@ packages: hasBin: true dev: true + /normalize-package-data/2.5.0: + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + dependencies: + hosted-git-info: 2.8.9 + resolve: 1.22.0 + semver: 5.7.1 + validate-npm-package-license: 3.0.4 + dev: true + /normalize-path/3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} dev: true + /npm-run-all/4.1.5: + resolution: {integrity: sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==} + engines: {node: '>= 4'} + hasBin: true + dependencies: + ansi-styles: 3.2.1 + chalk: 2.4.2 + cross-spawn: 6.0.5 + memorystream: 0.3.1 + minimatch: 3.1.2 + pidtree: 0.3.1 + read-pkg: 3.0.0 + shell-quote: 1.7.3 + string.prototype.padend: 3.1.3 + dev: true + + /object-inspect/1.12.2: + resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==} + dev: true + + /object-keys/1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + dev: true + + /object.assign/4.1.2: + resolution: {integrity: sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + has-symbols: 1.0.3 + object-keys: 1.1.1 + dev: true + /oblivious-set/1.1.1: resolution: {integrity: sha512-Oh+8fK09mgGmAshFdH6hSVco6KZmd1tTwNFWj35OvzdmJTMZtAkbn05zar2iG3v6sDs1JLEtOiBGNb6BHwkb2w==} dev: true @@ -621,15 +943,35 @@ packages: p-finally: 1.0.0 dev: true + /parse-json/4.0.0: + resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} + engines: {node: '>=4'} + dependencies: + error-ex: 1.3.2 + json-parse-better-errors: 1.0.2 + dev: true + /path-is-absolute/1.0.1: resolution: {integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18=} engines: {node: '>=0.10.0'} dev: true + /path-key/2.0.1: + resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} + engines: {node: '>=4'} + dev: true + /path-parse/1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true + /path-type/3.0.0: + resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} + engines: {node: '>=4'} + dependencies: + pify: 3.0.0 + dev: true + /picocolors/1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} dev: true @@ -639,6 +981,17 @@ packages: engines: {node: '>=8.6'} dev: true + /pidtree/0.3.1: + resolution: {integrity: sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==} + engines: {node: '>=0.10'} + hasBin: true + dev: true + + /pify/3.0.0: + resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} + engines: {node: '>=4'} + dev: true + /postcss/8.4.13: resolution: {integrity: sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA==} engines: {node: ^10 || ^12 || >=14} @@ -648,6 +1001,15 @@ packages: source-map-js: 1.0.2 dev: true + /read-pkg/3.0.0: + resolution: {integrity: sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=} + engines: {node: '>=4'} + dependencies: + load-json-file: 4.0.0 + normalize-package-data: 2.5.0 + path-type: 3.0.0 + dev: true + /readdirp/3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -659,6 +1021,15 @@ packages: resolution: {integrity: sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==} dev: true + /regexp.prototype.flags/1.4.3: + resolution: {integrity: sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + functions-have-names: 1.2.3 + dev: true + /regexparam/2.0.0: resolution: {integrity: sha512-gJKwd2MVPWHAIFLsaYDZfyKzHNS4o7E/v8YmNf44vmeV2e4YfVoDToTOKTvE7ab68cRJ++kLuEXJBaEeJVt5ow==} engines: {node: '>=8'} @@ -714,6 +1085,35 @@ packages: source-map-js: 1.0.2 dev: true + /semver/5.7.1: + resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} + hasBin: true + dev: true + + /shebang-command/1.2.0: + resolution: {integrity: sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=} + engines: {node: '>=0.10.0'} + dependencies: + shebang-regex: 1.0.0 + dev: true + + /shebang-regex/1.0.0: + resolution: {integrity: sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=} + engines: {node: '>=0.10.0'} + dev: true + + /shell-quote/1.7.3: + resolution: {integrity: sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==} + dev: true + + /side-channel/1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.1.1 + object-inspect: 1.12.2 + dev: true + /sorcery/0.10.0: resolution: {integrity: sha1-iukK19fLBfxZ8asMY3hF1cFaUrc=} hasBin: true @@ -733,6 +1133,58 @@ packages: resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} dev: true + /spdx-correct/3.1.1: + resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==} + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.11 + dev: true + + /spdx-exceptions/2.3.0: + resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==} + dev: true + + /spdx-expression-parse/3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + dependencies: + spdx-exceptions: 2.3.0 + spdx-license-ids: 3.0.11 + dev: true + + /spdx-license-ids/3.0.11: + resolution: {integrity: sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==} + dev: true + + /string.prototype.padend/3.1.3: + resolution: {integrity: sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.1 + dev: true + + /string.prototype.trimend/1.0.5: + resolution: {integrity: sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.1 + dev: true + + /string.prototype.trimstart/1.0.5: + resolution: {integrity: sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.4 + es-abstract: 1.20.1 + dev: true + + /strip-bom/3.0.0: + resolution: {integrity: sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=} + engines: {node: '>=4'} + dev: true + /strip-indent/3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} @@ -740,6 +1192,13 @@ packages: min-indent: 1.0.1 dev: true + /supports-color/5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + /supports-preserve-symlinks-flag/1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -828,10 +1287,27 @@ packages: resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} dev: false + /typesafe-i18n/5.5.2_typescript@4.6.4: + resolution: {integrity: sha512-EcqEHyiSujfIIxqTHuWvhd36Dmbp0rngYp+Z0IilCzRoZXSWGm/CdS5/jhkFySNGUEqtfgdD0gw1EnHbp6/54g==} + hasBin: true + peerDependencies: + typescript: '>=3.5.1' + dependencies: + typescript: 4.6.4 + dev: false + /typescript/4.6.4: resolution: {integrity: sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==} engines: {node: '>=4.2.0'} hasBin: true + + /unbox-primitive/1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + dependencies: + call-bind: 1.0.2 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 dev: true /unload/2.3.1: @@ -841,6 +1317,13 @@ packages: detect-node: 2.1.0 dev: true + /validate-npm-package-license/3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + dependencies: + spdx-correct: 3.1.1 + spdx-expression-parse: 3.0.1 + dev: true + /vite/2.9.8_sass@1.51.0: resolution: {integrity: sha512-zsBGwn5UT3YS0NLSJ7hnR54+vUKfgzMUh/Z9CxF1YKEBVIe213+63jrFLmZphgGI5zXwQCSmqIdbPuE8NJywPw==} engines: {node: '>=12.2.0'} @@ -866,6 +1349,23 @@ packages: fsevents: 2.3.2 dev: true + /which-boxed-primitive/1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + dev: true + + /which/1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + /wrappy/1.0.2: resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=} dev: true diff --git a/apps/web-shared/package.json b/apps/web-shared/package.json index e30d0ba..fb14b4b 100644 --- a/apps/web-shared/package.json +++ b/apps/web-shared/package.json @@ -3,10 +3,11 @@ "version": "0.0.1", "private": "true", "devDependencies": { + "@js-temporal/polyfill": "^0.4.1", + "fuzzysort": "^1.9.0", "svelte": "^3.48.0", + "svelte-feather-icons": "^4.0.0", "svelte-spa-router": "^3.2.0", - "typescript": "4.6.4", - "@js-temporal/polyfill": "^0.4.1", - "fuzzysort": "^1.9.0" + "typescript": "4.6.4" } } diff --git a/apps/web-shared/pnpm-lock.yaml b/apps/web-shared/pnpm-lock.yaml index 3b56115..88ebee5 100644 --- a/apps/web-shared/pnpm-lock.yaml +++ b/apps/web-shared/pnpm-lock.yaml @@ -2,27 +2,19 @@ lockfileVersion: 5.4 specifiers: '@js-temporal/polyfill': ^0.4.1 - '@sveltejs/vite-plugin-svelte': 1.0.0-next.43 fuzzysort: ^1.9.0 - sass: ^1.51.0 svelte: ^3.48.0 - svelte-preprocess: ^4.10.6 + svelte-feather-icons: ^4.0.0 svelte-spa-router: ^3.2.0 typescript: 4.6.4 - vite: ^2.9.8 -dependencies: +devDependencies: '@js-temporal/polyfill': 0.4.1 fuzzysort: 1.9.0 - -devDependencies: - '@sveltejs/vite-plugin-svelte': 1.0.0-next.43_svelte@3.48.0+vite@2.9.8 - sass: 1.51.0 svelte: 3.48.0 - svelte-preprocess: 4.10.6_24ezlekk4ocevlsjgs2qnqmjum + svelte-feather-icons: 4.0.0 svelte-spa-router: 3.2.0 typescript: 4.6.4 - vite: 2.9.8_sass@1.51.0 packages: @@ -32,543 +24,14 @@ packages: dependencies: jsbi: 4.3.0 tslib: 2.4.0 - dev: false - - /@rollup/pluginutils/4.2.1: - resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} - engines: {node: '>= 8.0.0'} - dependencies: - estree-walker: 2.0.2 - picomatch: 2.3.1 - dev: true - - /@sveltejs/vite-plugin-svelte/1.0.0-next.43_svelte@3.48.0+vite@2.9.8: - resolution: {integrity: sha512-MzeczqGrnDmbAldw/LfXV/dhpLC2bdUzuMhcx0C2j79V2uNzQERHDinxXnG2AVTCTjSpbQxzQwMMmYflnI7W1g==} - engines: {node: ^14.13.1 || >= 16} - peerDependencies: - diff-match-patch: ^1.0.5 - svelte: ^3.44.0 - vite: ^2.9.0 - peerDependenciesMeta: - diff-match-patch: - optional: true - dependencies: - '@rollup/pluginutils': 4.2.1 - debug: 4.3.4 - deepmerge: 4.2.2 - kleur: 4.1.4 - magic-string: 0.26.1 - svelte: 3.48.0 - svelte-hmr: 0.14.11_svelte@3.48.0 - vite: 2.9.8_sass@1.51.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@types/node/17.0.31: - resolution: {integrity: sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==} - dev: true - - /@types/pug/2.0.6: - resolution: {integrity: sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==} - dev: true - - /@types/sass/1.43.1: - resolution: {integrity: sha512-BPdoIt1lfJ6B7rw35ncdwBZrAssjcwzI5LByIrYs+tpXlj/CAkuVdRsgZDdP4lq5EjyWzwxZCqAoFyHKFwp32g==} - dependencies: - '@types/node': 17.0.31 - dev: true - - /anymatch/3.1.2: - resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==} - engines: {node: '>= 8'} - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - dev: true - - /balanced-match/1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - dev: true - - /binary-extensions/2.2.0: - resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} - engines: {node: '>=8'} - dev: true - - /brace-expansion/1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - dev: true - - /braces/3.0.2: - resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} - engines: {node: '>=8'} - dependencies: - fill-range: 7.0.1 - dev: true - - /buffer-crc32/0.2.13: - resolution: {integrity: sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=} - dev: true - - /chokidar/3.5.3: - resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} - engines: {node: '>= 8.10.0'} - dependencies: - anymatch: 3.1.2 - braces: 3.0.2 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.2 - dev: true - - /concat-map/0.0.1: - resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} - dev: true - - /debug/4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.2 - dev: true - - /deepmerge/4.2.2: - resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==} - engines: {node: '>=0.10.0'} - dev: true - - /detect-indent/6.1.0: - resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} - engines: {node: '>=8'} - dev: true - - /es6-promise/3.3.1: - resolution: {integrity: sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=} - dev: true - - /esbuild-android-64/0.14.38: - resolution: {integrity: sha512-aRFxR3scRKkbmNuGAK+Gee3+yFxkTJO/cx83Dkyzo4CnQl/2zVSurtG6+G86EQIZ+w+VYngVyK7P3HyTBKu3nw==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - requiresBuild: true - dev: true - optional: true - - /esbuild-android-arm64/0.14.38: - resolution: {integrity: sha512-L2NgQRWuHFI89IIZIlpAcINy9FvBk6xFVZ7xGdOwIm8VyhX1vNCEqUJO3DPSSy945Gzdg98cxtNt8Grv1CsyhA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true - optional: true - - /esbuild-darwin-64/0.14.38: - resolution: {integrity: sha512-5JJvgXkX87Pd1Og0u/NJuO7TSqAikAcQQ74gyJ87bqWRVeouky84ICoV4sN6VV53aTW+NE87qLdGY4QA2S7KNA==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /esbuild-darwin-arm64/0.14.38: - resolution: {integrity: sha512-eqF+OejMI3mC5Dlo9Kdq/Ilbki9sQBw3QlHW3wjLmsLh+quNfHmGMp3Ly1eWm981iGBMdbtSS9+LRvR2T8B3eQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /esbuild-freebsd-64/0.14.38: - resolution: {integrity: sha512-epnPbhZUt93xV5cgeY36ZxPXDsQeO55DppzsIgWM8vgiG/Rz+qYDLmh5ts3e+Ln1wA9dQ+nZmVHw+RjaW3I5Ig==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /esbuild-freebsd-arm64/0.14.38: - resolution: {integrity: sha512-/9icXUYJWherhk+y5fjPI5yNUdFPtXHQlwP7/K/zg8t8lQdHVj20SqU9/udQmeUo5pDFHMYzcEFfJqgOVeKNNQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-32/0.14.38: - resolution: {integrity: sha512-QfgfeNHRFvr2XeHFzP8kOZVnal3QvST3A0cgq32ZrHjSMFTdgXhMhmWdKzRXP/PKcfv3e2OW9tT9PpcjNvaq6g==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-64/0.14.38: - resolution: {integrity: sha512-uuZHNmqcs+Bj1qiW9k/HZU3FtIHmYiuxZ/6Aa+/KHb/pFKr7R3aVqvxlAudYI9Fw3St0VCPfv7QBpUITSmBR1Q==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-arm/0.14.38: - resolution: {integrity: sha512-FiFvQe8J3VKTDXG01JbvoVRXQ0x6UZwyrU4IaLBZeq39Bsbatd94Fuc3F1RGqPF5RbIWW7RvkVQjn79ejzysnA==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-arm64/0.14.38: - resolution: {integrity: sha512-HlMGZTEsBrXrivr64eZ/EO0NQM8H8DuSENRok9d+Jtvq8hOLzrxfsAT9U94K3KOGk2XgCmkaI2KD8hX7F97lvA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-mips64le/0.14.38: - resolution: {integrity: sha512-qd1dLf2v7QBiI5wwfil9j0HG/5YMFBAmMVmdeokbNAMbcg49p25t6IlJFXAeLzogv1AvgaXRXvgFNhScYEUXGQ==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-ppc64le/0.14.38: - resolution: {integrity: sha512-mnbEm7o69gTl60jSuK+nn+pRsRHGtDPfzhrqEUXyCl7CTOCLtWN2bhK8bgsdp6J/2NyS/wHBjs1x8aBWwP2X9Q==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-riscv64/0.14.38: - resolution: {integrity: sha512-+p6YKYbuV72uikChRk14FSyNJZ4WfYkffj6Af0/Tw63/6TJX6TnIKE+6D3xtEc7DeDth1fjUOEqm+ApKFXbbVQ==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-s390x/0.14.38: - resolution: {integrity: sha512-0zUsiDkGJiMHxBQ7JDU8jbaanUY975CdOW1YDrurjrM0vWHfjv9tLQsW9GSyEb/heSK1L5gaweRjzfUVBFoybQ==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-netbsd-64/0.14.38: - resolution: {integrity: sha512-cljBAApVwkpnJZfnRVThpRBGzCi+a+V9Ofb1fVkKhtrPLDYlHLrSYGtmnoTVWDQdU516qYI8+wOgcGZ4XIZh0Q==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - requiresBuild: true - dev: true - optional: true - - /esbuild-openbsd-64/0.14.38: - resolution: {integrity: sha512-CDswYr2PWPGEPpLDUO50mL3WO/07EMjnZDNKpmaxUPsrW+kVM3LoAqr/CE8UbzugpEiflYqJsGPLirThRB18IQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - requiresBuild: true - dev: true - optional: true - - /esbuild-sunos-64/0.14.38: - resolution: {integrity: sha512-2mfIoYW58gKcC3bck0j7lD3RZkqYA7MmujFYmSn9l6TiIcAMpuEvqksO+ntBgbLep/eyjpgdplF7b+4T9VJGOA==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - requiresBuild: true - dev: true - optional: true - - /esbuild-windows-32/0.14.38: - resolution: {integrity: sha512-L2BmEeFZATAvU+FJzJiRLFUP+d9RHN+QXpgaOrs2klshoAm1AE6Us4X6fS9k33Uy5SzScn2TpcgecbqJza1Hjw==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /esbuild-windows-64/0.14.38: - resolution: {integrity: sha512-Khy4wVmebnzue8aeSXLC+6clo/hRYeNIm0DyikoEqX+3w3rcvrhzpoix0S+MF9vzh6JFskkIGD7Zx47ODJNyCw==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /esbuild-windows-arm64/0.14.38: - resolution: {integrity: sha512-k3FGCNmHBkqdJXuJszdWciAH77PukEyDsdIryEHn9cKLQFxzhT39dSumeTuggaQcXY57UlmLGIkklWZo2qzHpw==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /esbuild/0.14.38: - resolution: {integrity: sha512-12fzJ0fsm7gVZX1YQ1InkOE5f9Tl7cgf6JPYXRJtPIoE0zkWAbHdPHVPPaLi9tYAcEBqheGzqLn/3RdTOyBfcA==} - engines: {node: '>=12'} - hasBin: true - requiresBuild: true - optionalDependencies: - esbuild-android-64: 0.14.38 - esbuild-android-arm64: 0.14.38 - esbuild-darwin-64: 0.14.38 - esbuild-darwin-arm64: 0.14.38 - esbuild-freebsd-64: 0.14.38 - esbuild-freebsd-arm64: 0.14.38 - esbuild-linux-32: 0.14.38 - esbuild-linux-64: 0.14.38 - esbuild-linux-arm: 0.14.38 - esbuild-linux-arm64: 0.14.38 - esbuild-linux-mips64le: 0.14.38 - esbuild-linux-ppc64le: 0.14.38 - esbuild-linux-riscv64: 0.14.38 - esbuild-linux-s390x: 0.14.38 - esbuild-netbsd-64: 0.14.38 - esbuild-openbsd-64: 0.14.38 - esbuild-sunos-64: 0.14.38 - esbuild-windows-32: 0.14.38 - esbuild-windows-64: 0.14.38 - esbuild-windows-arm64: 0.14.38 - dev: true - - /estree-walker/2.0.2: - resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - dev: true - - /fill-range/7.0.1: - resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} - engines: {node: '>=8'} - dependencies: - to-regex-range: 5.0.1 - dev: true - - /fs.realpath/1.0.0: - resolution: {integrity: sha1-FQStJSMVjKpA20onh8sBQRmU6k8=} - dev: true - - /fsevents/2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /function-bind/1.1.1: - resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: true /fuzzysort/1.9.0: resolution: {integrity: sha512-MOxCT0qLTwLqmEwc7UtU045RKef7mc8Qz8eR4r2bLNEq9dy/c3ZKMEFp6IEst69otkQdFZ4FfgH2dmZD+ddX1g==} - dev: false - - /glob-parent/5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - dependencies: - is-glob: 4.0.3 - dev: true - - /glob/7.2.0: - resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - dev: true - - /graceful-fs/4.2.10: - resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} - dev: true - - /has/1.0.3: - resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} - engines: {node: '>= 0.4.0'} - dependencies: - function-bind: 1.1.1 - dev: true - - /immutable/4.0.0: - resolution: {integrity: sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==} - dev: true - - /inflight/1.0.6: - resolution: {integrity: sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=} - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - dev: true - - /inherits/2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - dev: true - - /is-binary-path/2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - dependencies: - binary-extensions: 2.2.0 - dev: true - - /is-core-module/2.9.0: - resolution: {integrity: sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==} - dependencies: - has: 1.0.3 - dev: true - - /is-extglob/2.1.1: - resolution: {integrity: sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=} - engines: {node: '>=0.10.0'} - dev: true - - /is-glob/4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - dependencies: - is-extglob: 2.1.1 - dev: true - - /is-number/7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} dev: true /jsbi/4.3.0: resolution: {integrity: sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g==} - dev: false - - /kleur/4.1.4: - resolution: {integrity: sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==} - engines: {node: '>=6'} - dev: true - - /magic-string/0.25.9: - resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} - dependencies: - sourcemap-codec: 1.4.8 - dev: true - - /magic-string/0.26.1: - resolution: {integrity: sha512-ndThHmvgtieXe8J/VGPjG+Apu7v7ItcD5mhEIvOscWjPF/ccOiLxHaSuCAS2G+3x4GKsAbT8u7zdyamupui8Tg==} - engines: {node: '>=12'} - dependencies: - sourcemap-codec: 1.4.8 - dev: true - - /min-indent/1.0.1: - resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} - engines: {node: '>=4'} - dev: true - - /minimatch/3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - dependencies: - brace-expansion: 1.1.11 - dev: true - - /minimist/1.2.6: - resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==} - dev: true - - /mkdirp/0.5.6: - resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true - dependencies: - minimist: 1.2.6 - dev: true - - /ms/2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - dev: true - - /nanoid/3.3.4: - resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - dev: true - - /normalize-path/3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - dev: true - - /once/1.4.0: - resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=} - dependencies: - wrappy: 1.0.2 - dev: true - - /path-is-absolute/1.0.1: - resolution: {integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18=} - engines: {node: '>=0.10.0'} - dev: true - - /path-parse/1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - dev: true - - /picocolors/1.0.0: - resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} - dev: true - - /picomatch/2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - dev: true - - /postcss/8.4.13: - resolution: {integrity: sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA==} - engines: {node: ^10 || ^12 || >=14} - dependencies: - nanoid: 3.3.4 - picocolors: 1.0.0 - source-map-js: 1.0.2 - dev: true - - /readdirp/3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - dependencies: - picomatch: 2.3.1 dev: true /regexparam/2.0.0: @@ -576,139 +39,10 @@ packages: engines: {node: '>=8'} dev: true - /resolve/1.22.0: - resolution: {integrity: sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==} - hasBin: true + /svelte-feather-icons/4.0.0: + resolution: {integrity: sha512-4ieUsjp+VYa1r6y80jDt9zRiRUZyJNbESpRdHdJJhiBubyuXX96A7f1UZSK4olxzP6Qsg5ZAuyZlnmvD+/swAA==} dependencies: - is-core-module: 2.9.0 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - dev: true - - /rimraf/2.7.1: - resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} - hasBin: true - dependencies: - glob: 7.2.0 - dev: true - - /rollup/2.72.1: - resolution: {integrity: sha512-NTc5UGy/NWFGpSqF1lFY8z9Adri6uhyMLI6LvPAXdBKoPRFhIIiBUpt+Qg2awixqO3xvzSijjhnb4+QEZwJmxA==} - engines: {node: '>=10.0.0'} - hasBin: true - optionalDependencies: - fsevents: 2.3.2 - dev: true - - /sander/0.5.1: - resolution: {integrity: sha1-dB4kXiMfB8r7b98PEzrfohalAq0=} - dependencies: - es6-promise: 3.3.1 - graceful-fs: 4.2.10 - mkdirp: 0.5.6 - rimraf: 2.7.1 - dev: true - - /sass/1.51.0: - resolution: {integrity: sha512-haGdpTgywJTvHC2b91GSq+clTKGbtkkZmVAb82jZQN/wTy6qs8DdFm2lhEQbEwrY0QDRgSQ3xDurqM977C3noA==} - engines: {node: '>=12.0.0'} - hasBin: true - dependencies: - chokidar: 3.5.3 - immutable: 4.0.0 - source-map-js: 1.0.2 - dev: true - - /sorcery/0.10.0: - resolution: {integrity: sha1-iukK19fLBfxZ8asMY3hF1cFaUrc=} - hasBin: true - dependencies: - buffer-crc32: 0.2.13 - minimist: 1.2.6 - sander: 0.5.1 - sourcemap-codec: 1.4.8 - dev: true - - /source-map-js/1.0.2: - resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} - engines: {node: '>=0.10.0'} - dev: true - - /sourcemap-codec/1.4.8: - resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} - dev: true - - /strip-indent/3.0.0: - resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} - engines: {node: '>=8'} - dependencies: - min-indent: 1.0.1 - dev: true - - /supports-preserve-symlinks-flag/1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - dev: true - - /svelte-hmr/0.14.11_svelte@3.48.0: - resolution: {integrity: sha512-R9CVfX6DXxW1Kn45Jtmx+yUe+sPhrbYSUp7TkzbW0jI5fVPn6lsNG9NEs5dFg5qRhFNAoVdRw5qQDLALNKhwbQ==} - engines: {node: ^12.20 || ^14.13.1 || >= 16} - peerDependencies: - svelte: '>=3.19.0' - dependencies: - svelte: 3.48.0 - dev: true - - /svelte-preprocess/4.10.6_24ezlekk4ocevlsjgs2qnqmjum: - resolution: {integrity: sha512-I2SV1w/AveMvgIQlUF/ZOO3PYVnhxfcpNyGt8pxpUVhPfyfL/CZBkkw/KPfuFix5FJ9TnnNYMhACK3DtSaYVVQ==} - engines: {node: '>= 9.11.2'} - requiresBuild: true - peerDependencies: - '@babel/core': ^7.10.2 - coffeescript: ^2.5.1 - less: ^3.11.3 || ^4.0.0 - node-sass: '*' - postcss: ^7 || ^8 - postcss-load-config: ^2.1.0 || ^3.0.0 - pug: ^3.0.0 - sass: ^1.26.8 - stylus: ^0.55.0 - sugarss: ^2.0.0 - svelte: ^3.23.0 - typescript: ^3.9.5 || ^4.0.0 - peerDependenciesMeta: - '@babel/core': - optional: true - coffeescript: - optional: true - less: - optional: true - node-sass: - optional: true - postcss: - optional: true - postcss-load-config: - optional: true - pug: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - typescript: - optional: true - dependencies: - '@types/pug': 2.0.6 - '@types/sass': 1.43.1 - detect-indent: 6.1.0 - magic-string: 0.25.9 - sass: 1.51.0 - sorcery: 0.10.0 - strip-indent: 3.0.0 svelte: 3.48.0 - typescript: 4.6.4 dev: true /svelte-spa-router/3.2.0: @@ -722,48 +56,12 @@ packages: engines: {node: '>= 8'} dev: true - /to-regex-range/5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - dependencies: - is-number: 7.0.0 - dev: true - /tslib/2.4.0: resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} - dev: false + dev: true /typescript/4.6.4: resolution: {integrity: sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==} engines: {node: '>=4.2.0'} hasBin: true dev: true - - /vite/2.9.8_sass@1.51.0: - resolution: {integrity: sha512-zsBGwn5UT3YS0NLSJ7hnR54+vUKfgzMUh/Z9CxF1YKEBVIe213+63jrFLmZphgGI5zXwQCSmqIdbPuE8NJywPw==} - engines: {node: '>=12.2.0'} - hasBin: true - peerDependencies: - less: '*' - sass: '*' - stylus: '*' - peerDependenciesMeta: - less: - optional: true - sass: - optional: true - stylus: - optional: true - dependencies: - esbuild: 0.14.38 - postcss: 8.4.13 - resolve: 1.22.0 - rollup: 2.72.1 - sass: 1.51.0 - optionalDependencies: - fsevents: 2.3.2 - dev: true - - /wrappy/1.0.2: - resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=} - dev: true diff --git a/apps/web-shared/src/components/blowout-toolbelt.svelte b/apps/web-shared/src/components/blowout-toolbelt.svelte new file mode 100644 index 0000000..69e9902 --- /dev/null +++ b/apps/web-shared/src/components/blowout-toolbelt.svelte @@ -0,0 +1,60 @@ +<script> + import {createEventDispatcher} from "svelte"; + import ThemeSwitcher from "./theme-switcher.svelte"; + import ThemeSwitcherIcon from "./theme-switcher-icon.svelte"; + import LocaleSwitcher from "./locale-switcher.svelte"; + import LocaleSwitcherIcon from "./locale-switcher-icon.svelte"; + import {ChevronsRightIcon, ChevronsLeftIcon} from "svelte-feather-icons"; + + const dispatch = createEventDispatcher(); + + function locale_change(event) { + dispatch("change", {name: "locale", value: event.detail}) + } + + let expanded = false; + const localeSwitcher = { + show: false, + selection: "preffered" + }; + + const themeSwitcher = { + show: false, + selection: "system" + }; +</script> +<style> + .blowout { + right: -80px; + } + + .expanded { + right: 0 !important; + } +</style> +<aside class="blowout position-fixed bg-light inner-glow shadow-xs padding-xxs bottom-50% right-0 z-index-2 {expanded ? 'expanded' : ''}"> + <LocaleSwitcher bind:show="{localeSwitcher.show}" + glow="{false}" + on:change={locale_change} + bind:selection="{localeSwitcher.selection}"/> + <ThemeSwitcher bind:show="{themeSwitcher.show}" + glow="{false}" + bind:selection="{themeSwitcher.selection}"/> + <div class="flex flex-row gap-sm justify-end"> + {#if !expanded} + <div class="flex ld-switch-btn" + on:click={() => expanded = true}> + <ChevronsLeftIcon/> + </div> + {:else} + <div class="flex ld-switch-btn" + on:click={() => expanded = false}> + <ChevronsRightIcon/> + </div> + {/if} + <LocaleSwitcherIcon bind:show="{localeSwitcher.show}" + bind:selection="{localeSwitcher.selection}"/> + <ThemeSwitcherIcon bind:show="{themeSwitcher.show}" + bind:selection="{themeSwitcher.selection}"/> + </div> +</aside> diff --git a/apps/web-shared/src/components/link-card.svelte b/apps/web-shared/src/components/link-card.svelte index 0c15a53..85738c7 100644 --- a/apps/web-shared/src/components/link-card.svelte +++ b/apps/web-shared/src/components/link-card.svelte @@ -7,13 +7,13 @@ export let title = null; </script> -<a class="link-card flex flex-column bg-light cursor-pointer radius-md {$$restProps.class??''}" +<a class="link-card flex flex-column bg-light cursor-pointer radius-sm inner-glow {$$restProps.class??''}" {href} {target} {title} on:click aria-label="Link label"> - <div class="padding-md"> + <div class="padding-sm"> <div class="flex flex-wrap gap-xs items-center"> <slot name="icon"></slot> <div class="line-height-xs"> diff --git a/apps/web-shared/src/components/locale-switcher-icon.svelte b/apps/web-shared/src/components/locale-switcher-icon.svelte new file mode 100644 index 0000000..d2776a1 --- /dev/null +++ b/apps/web-shared/src/components/locale-switcher-icon.svelte @@ -0,0 +1,16 @@ +<script> + import {GlobeIcon} from "svelte-feather-icons"; + + export let show = false; + export let selection = "preffered"; +</script> +<div data-locale-switcher-element + class="ld-switch flex"> + <button class="reset ld-switch-btn" + on:click={() => (show = !show)}> + <div class="ld-switch-btn__icon-wrapper ld-switch-btn__icon-wrapper--in" + aria-hidden="true"> + <GlobeIcon/> + </div> + </button> +</div> diff --git a/apps/web-shared/src/components/locale-switcher.svelte b/apps/web-shared/src/components/locale-switcher.svelte new file mode 100644 index 0000000..2dae026 --- /dev/null +++ b/apps/web-shared/src/components/locale-switcher.svelte @@ -0,0 +1,64 @@ +<script> + import {base_domain, CookieNames} from "$shared/lib/configuration"; + import {get_cookie, set_cookie} from "$shared/lib/helpers"; + import {createEventDispatcher, onMount} from "svelte"; + + const dispatch = createEventDispatcher(); + + export let glow = false; + export let show = false; + export let selection = "preffered"; + export let size; + + function change(to) { + selection = to; + set_cookie(CookieNames.locale, selection, base_domain()); + dispatch("change", selection); + } + + onMount(() => { + selection = get_cookie(CookieNames.locale); + document.addEventListener("keydown", (e) => { + if (e.code === "Esc") { + show = false; + } + }); + document.addEventListener("click", (e) => { + if (e.target.closest("[data-locale-switcher-element]") === null) { + show = false; + } + }); + }); +</script> + +<div class="bg-light padding-x-xs padding-bottom-xs padding-top-xxxs radius-md {glow ? 'inner-glow shadow-xs':''}" + data-locale-switcher-element + class:is-hidden={!show} + role="listbox"> + <div class="flex flex-wrap flex-column" + role="group"> + <div class="margin-bottom-xs flex-grow"> + <span class="text-xs color-contrast-medium">Language</span> + </div> + <div class="flex gap-xs flex-row"> + <div class="ld-switch-popover__option" + aria-selected="{selection === 'en' ? 'true' : 'false'}" + on:click={() => change("en")} + role="option"> + <div class="text-xs margin-top-xxxs padding-x-xxxxs">English</div> + </div> + <div class="ld-switch-popover__option" + aria-selected="{selection === 'nb' ? 'true' : 'false'}" + on:click={() => change("nb")} + role="option"> + <div class="text-xs margin-top-xxxs padding-x-xxxxs">Norsk</div> + </div> + <div class="ld-switch-popover__option" + aria-selected="{selection === 'preffered' ? 'true' : 'false'}" + on:click={() => change("preffered")} + role="option"> + <div class="text-xs margin-top-xxxs padding-x-xxxxs">Default</div> + </div> + </div> + </div> +</div> diff --git a/apps/web-shared/src/components/theme-switcher-icon.svelte b/apps/web-shared/src/components/theme-switcher-icon.svelte new file mode 100644 index 0000000..1531ab2 --- /dev/null +++ b/apps/web-shared/src/components/theme-switcher-icon.svelte @@ -0,0 +1,248 @@ +<script> + export let show = false; + export let selection = ""; +</script> + +<div class="ld-switch flex" + data-theme-switcher-element> + <button class="reset ld-switch-btn" + on:click={() => show =!show}> + <span class="sr-only">{selection}</span> + <div class="ld-switch-btn__icon-wrapper ld-switch-btn__icon-wrapper--in" + aria-hidden="true"> + {#if selection === "dark"} + <svg class="icon ld-switch-btn__icon" + viewBox="0 0 20 20"> + <title>dark</title> + <g fill="currentColor"> + <path d="M11.964 3.284c.021.237.036.474.036.716a8 8 0 0 1-8 8c-.242 0-.479-.015-.716-.036a7 7 0 1 0 8.68-8.68z" + fill-opacity=".2" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2"></path> + <path d="M7 4a.979.979 0 0 1-1-1 1 1 0 0 0-2 0 .979.979 0 0 1-1 1 1 1 0 0 0 0 2 .979.979 0 0 1 1 1 1 1 0 0 0 2 0 .979.979 0 0 1 1-1 1 1 0 0 0 0-2z"></path> + </g> + </svg> + {:else if selection === "light"} + <svg class="icon ld-switch-btn__icon" + viewBox="0 0 20 20"><title>light</title> + <g fill="currentColor"> + <circle cx="10" + cy="10" + r="4" + fill-opacity=".2" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2"></circle> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M10 1v1.5"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M16.364 3.636l-1.061 1.061"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M19 10h-1.5"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M16.364 16.364l-1.061-1.061"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M10 19v-1.5"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M3.636 16.364l1.061-1.061"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M1 10h1.5"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M3.636 3.636l1.061 1.061"></path> + </g> + </svg> + {:else } + <svg class="icon ld-switch-btn__icon" + viewBox="0 0 20 20"><title>light-auto</title> + <g fill="currentColor"> + <path d="M10 14a4 4 0 1 1 3.465-6" + fill-opacity=".2" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M12 18l2.5-7h1l2.5 7"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M12.714 16h4.572"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M10 1v1.5"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M16.364 3.636l-1.061 1.061"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M3.636 16.364l1.061-1.061"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M1 10h1.5"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M3.636 3.636l1.061 1.061"></path> + </g> + </svg> + {/if} + </div> + + <div class="ld-switch-btn__icon-wrapper js-ld-switch-icon" + aria-hidden="true"> + {#if selection === "dark"} + <svg class="icon ld-switch-btn__icon" + viewBox="0 0 20 20"> + <title>dark</title> + <g fill="currentColor"> + <path d="M11.964 3.284c.021.237.036.474.036.716a8 8 0 0 1-8 8c-.242 0-.479-.015-.716-.036a7 7 0 1 0 8.68-8.68z" + fill-opacity=".2" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2"></path> + <path d="M7 4a.979.979 0 0 1-1-1 1 1 0 0 0-2 0 .979.979 0 0 1-1 1 1 1 0 0 0 0 2 .979.979 0 0 1 1 1 1 1 0 0 0 2 0 .979.979 0 0 1 1-1 1 1 0 0 0 0-2z"></path> + </g> + </svg> + {:else if selection === "light"} + <svg class="icon ld-switch-btn__icon" + viewBox="0 0 20 20"> + <title>light-auto</title> + <g fill="currentColor"> + <path d="M10 14a4 4 0 1 1 3.465-6" + fill-opacity=".2" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M12 18l2.5-7h1l2.5 7"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M12.714 16h4.572"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M10 1v1.5"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M16.364 3.636l-1.061 1.061"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M3.636 16.364l1.061-1.061"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M1 10h1.5"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M3.636 3.636l1.061 1.061"></path> + </g> + </svg> + {:else } + <svg class="icon ld-switch-btn__icon" + viewBox="0 0 20 20"> + <title>dark-auto</title> + <g fill="currentColor"> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M12 18l2.5-7h1l2.5 7"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M12.714 16h4.572"></path> + <path d="M12.146 10.159A2.5 2.5 0 0 1 14.5 8.5h1a2.5 2.5 0 0 1 1.412.441 7 7 0 0 0-4.948-5.657c.021.237.036.474.036.716a8 8 0 0 1-8 8c-.242 0-.479-.015-.716-.036a6.99 6.99 0 0 0 6.427 5.012z" + fill-opacity=".2"></path> + <path d="M16.71 8a7.015 7.015 0 0 0-4.746-4.716c.021.237.036.474.036.716a8 8 0 0 1-8 8c-.242 0-.479-.015-.716-.036A7.006 7.006 0 0 0 9 16.929" + fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2"></path> + <path d="M7 4a.979.979 0 0 1-1-1 1 1 0 0 0-2 0 .979.979 0 0 1-1 1 1 1 0 0 0 0 2 .979.979 0 0 1 1 1 1 1 0 0 0 2 0 .979.979 0 0 1 1-1 1 1 0 0 0 0-2z"></path> + </g> + </svg> + {/if} + </div> + </button> +</div> diff --git a/apps/web-shared/src/components/theme-switcher.svelte b/apps/web-shared/src/components/theme-switcher.svelte index fd14059..397bad4 100644 --- a/apps/web-shared/src/components/theme-switcher.svelte +++ b/apps/web-shared/src/components/theme-switcher.svelte @@ -6,6 +6,7 @@ type theme = "system"|"dark"|"light"; export let show = false; + export let glow = false; export let selection: theme = "system"; export let size; let prefers = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; @@ -42,253 +43,8 @@ } </script> -<div class="ld-switch" - data-theme-switcher-element> - <button class="reset ld-switch-btn" - on:click={() => show =!show}> - <span class="sr-only">{selection}</span> - <div class="ld-switch-btn__icon-wrapper ld-switch-btn__icon-wrapper--in" - aria-hidden="true"> - {#if selection === "dark"} - <svg class="icon ld-switch-btn__icon" - viewBox="0 0 20 20"> - <title>dark</title> - <g fill="currentColor"> - <path d="M11.964 3.284c.021.237.036.474.036.716a8 8 0 0 1-8 8c-.242 0-.479-.015-.716-.036a7 7 0 1 0 8.68-8.68z" - fill-opacity=".2" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2"></path> - <path d="M7 4a.979.979 0 0 1-1-1 1 1 0 0 0-2 0 .979.979 0 0 1-1 1 1 1 0 0 0 0 2 .979.979 0 0 1 1 1 1 1 0 0 0 2 0 .979.979 0 0 1 1-1 1 1 0 0 0 0-2z"></path> - </g> - </svg> - {:else if selection === "light"} - <svg class="icon ld-switch-btn__icon" - viewBox="0 0 20 20"><title>light</title> - <g fill="currentColor"> - <circle cx="10" - cy="10" - r="4" - fill-opacity=".2" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2"></circle> - <path fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M10 1v1.5"></path> - <path fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M16.364 3.636l-1.061 1.061"></path> - <path fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M19 10h-1.5"></path> - <path fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M16.364 16.364l-1.061-1.061"></path> - <path fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M10 19v-1.5"></path> - <path fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M3.636 16.364l1.061-1.061"></path> - <path fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M1 10h1.5"></path> - <path fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M3.636 3.636l1.061 1.061"></path> - </g> - </svg> - {:else } - <svg class="icon ld-switch-btn__icon" - viewBox="0 0 20 20"><title>light-auto</title> - <g fill="currentColor"> - <path d="M10 14a4 4 0 1 1 3.465-6" - fill-opacity=".2" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2"></path> - <path fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M12 18l2.5-7h1l2.5 7"></path> - <path fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M12.714 16h4.572"></path> - <path fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M10 1v1.5"></path> - <path fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M16.364 3.636l-1.061 1.061"></path> - <path fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M3.636 16.364l1.061-1.061"></path> - <path fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M1 10h1.5"></path> - <path fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M3.636 3.636l1.061 1.061"></path> - </g> - </svg> - {/if} - </div> - - <div class="ld-switch-btn__icon-wrapper js-ld-switch-icon" - aria-hidden="true"> - {#if selection === "dark"} - <svg class="icon ld-switch-btn__icon" - viewBox="0 0 20 20"> - <title>dark</title> - <g fill="currentColor"> - <path d="M11.964 3.284c.021.237.036.474.036.716a8 8 0 0 1-8 8c-.242 0-.479-.015-.716-.036a7 7 0 1 0 8.68-8.68z" - fill-opacity=".2" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2"></path> - <path d="M7 4a.979.979 0 0 1-1-1 1 1 0 0 0-2 0 .979.979 0 0 1-1 1 1 1 0 0 0 0 2 .979.979 0 0 1 1 1 1 1 0 0 0 2 0 .979.979 0 0 1 1-1 1 1 0 0 0 0-2z"></path> - </g> - </svg> - {:else if selection === "light"} - <svg class="icon ld-switch-btn__icon" - viewBox="0 0 20 20"> - <title>light-auto</title> - <g fill="currentColor"> - <path d="M10 14a4 4 0 1 1 3.465-6" - fill-opacity=".2" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2"></path> - <path fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M12 18l2.5-7h1l2.5 7"></path> - <path fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M12.714 16h4.572"></path> - <path fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M10 1v1.5"></path> - <path fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M16.364 3.636l-1.061 1.061"></path> - <path fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M3.636 16.364l1.061-1.061"></path> - <path fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M1 10h1.5"></path> - <path fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M3.636 3.636l1.061 1.061"></path> - </g> - </svg> - {:else } - <svg class="icon ld-switch-btn__icon" - viewBox="0 0 20 20"> - <title>dark-auto</title> - <g fill="currentColor"> - <path fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M12 18l2.5-7h1l2.5 7"></path> - <path fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - d="M12.714 16h4.572"></path> - <path d="M12.146 10.159A2.5 2.5 0 0 1 14.5 8.5h1a2.5 2.5 0 0 1 1.412.441 7 7 0 0 0-4.948-5.657c.021.237.036.474.036.716a8 8 0 0 1-8 8c-.242 0-.479-.015-.716-.036a6.99 6.99 0 0 0 6.427 5.012z" - fill-opacity=".2"></path> - <path d="M16.71 8a7.015 7.015 0 0 0-4.746-4.716c.021.237.036.474.036.716a8 8 0 0 1-8 8c-.242 0-.479-.015-.716-.036A7.006 7.006 0 0 0 9 16.929" - fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2"></path> - <path d="M7 4a.979.979 0 0 1-1-1 1 1 0 0 0-2 0 .979.979 0 0 1-1 1 1 1 0 0 0 0 2 .979.979 0 0 1 1 1 1 1 0 0 0 2 0 .979.979 0 0 1 1-1 1 1 0 0 0 0-2z"></path> - </g> - </svg> - {/if} - </div> - </button> -</div> - -<div class="bg-light position-fixed margin-top-xxs padding-x-xs padding-bottom-xs padding-top-xxxs radius-md inner-glow shadow-xs" +<div class="bg-light padding-x-xs padding-bottom-xs padding-top-xxxs radius-md {glow ? 'inner-glow shadow-xs' : ''}" class:is-hidden={!show} - style="right: 15px" data-theme-switcher-element role="listbox"> <div class="flex flex-wrap flex-column" diff --git a/apps/web-shared/src/lib/configuration.ts b/apps/web-shared/src/lib/configuration.ts index cb08d21..5c79089 100644 --- a/apps/web-shared/src/lib/configuration.ts +++ b/apps/web-shared/src/lib/configuration.ts @@ -40,7 +40,8 @@ export function is_debug(): boolean { } export const CookieNames = { - theme: "go_theme" + theme: "go_theme", + locale: "go_locale" }; export const QueryKeys = { diff --git a/apps/web-shared/src/lib/helpers.ts b/apps/web-shared/src/lib/helpers.ts index 4da8254..1cf94f4 100644 --- a/apps/web-shared/src/lib/helpers.ts +++ b/apps/web-shared/src/lib/helpers.ts @@ -1,4 +1,4 @@ -import {base_domain, CookieNames, StorageKeys} from "$shared/lib/configuration"; +import {base_domain, CookieNames} from "$shared/lib/configuration"; import {TimeEntryDto} from "$shared/lib/models/TimeEntryDto"; import {UnwrappedEntryDateTime} from "$shared/lib/models/UnwrappedEntryDateTime"; import {Temporal} from "@js-temporal/polyfill"; @@ -482,11 +482,3 @@ export function get_hash_code(value: string): number|undefined { } return hash; } - -export function $(selector: string): HTMLElement|null { - return document.querySelector(selector); -} - -export function $$(selector: string): NodeListOf<Element> { - return document.querySelectorAll(selector); -} |
