diff options
Diffstat (limited to 'old-apps/projects/src/app')
44 files changed, 0 insertions, 5065 deletions
diff --git a/old-apps/projects/src/app/index.d.ts b/old-apps/projects/src/app/index.d.ts deleted file mode 100644 index c044583..0000000 --- a/old-apps/projects/src/app/index.d.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* Use this file to declare any custom file extensions for importing */ -/* Use this folder to also add/extend a package d.ts file, if needed. */ - -/* CSS MODULES */ -declare module "*.module.css" { - const classes: { [key: string]: string }; - export default classes; -} -declare module "*.module.scss" { - const classes: { [key: string]: string }; - export default classes; -} - -/* CSS */ -declare module "*.css"; -declare module "*.scss"; - -/* IMAGES */ -declare module "*.svg" { - const ref: string; - export default ref; -} -declare module "*.bmp" { - const ref: string; - export default ref; -} -declare module "*.gif" { - const ref: string; - export default ref; -} -declare module "*.jpg" { - const ref: string; - export default ref; -} -declare module "*.jpeg" { - const ref: string; - export default ref; -} -declare module "*.png" { - const ref: string; - export default ref; -} - -/* CUSTOM: ADD YOUR OWN HERE */ -declare module "*.svelte" { - const value: any; - export default value; -} diff --git a/old-apps/projects/src/app/index.html b/old-apps/projects/src/app/index.html deleted file mode 100644 index 7e0b0e1..0000000 --- a/old-apps/projects/src/app/index.html +++ /dev/null @@ -1,63 +0,0 @@ -<!doctype html> -<html lang="en"> - -<head> - <meta charset="UTF-8"> - <meta name="viewport" - content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> - <link rel="apple-touch-icon" - sizes="180x180" - href="../_assets/pwa/apple-touch-icon.png"> - <link rel="icon" - type="image/png" - sizes="32x32" - href="../_assets/pwa/favicon-32x32.png"> - <link rel="icon" - type="image/png" - sizes="16x16" - href="../_assets/pwa/favicon-16x16.png"> - <link rel="manifest" - href="../_assets/pwa/manifest.json"> - <link rel="mask-icon" - href="../_assets/pwa/safari-pinned-tab.svg" - color="#5bbad5"> - <meta name="msapplication-TileColor" - content="#da532c"> - <link rel="icon" - href="../_assets/pwa/favicon.svg"> - <script> - const currentTheme = localStorage.getItem("theme"); - if (currentTheme === "light") { - document.querySelector("html").dataset.theme = "light"; - } else { - document.querySelector("html").dataset.theme = "dark"; - } - </script> - <link rel="stylesheet" - href="../_assets/pre.css"> - <title>Time Tracker</title> -</head> - -<body> - -<noscript> - This page is built with javascript. Allow it and try again. -</noscript> - -<div class="fill-loader fill-loader--v4" - id="loader" - role="alert"> - <p class="fill-loader__label">Loading Time Tracker...</p> - <div aria-hidden="true"> - <div class="fill-loader__base"></div> - <div class="fill-loader__fill"></div> - </div> -</div> - -<div id="root"></div> - -<script type="module" - src="./index.ts"></script> -</body> - -</html> diff --git a/old-apps/projects/src/app/index.scss b/old-apps/projects/src/app/index.scss deleted file mode 100644 index f83b1a1..0000000 --- a/old-apps/projects/src/app/index.scss +++ /dev/null @@ -1,40 +0,0 @@ -@use '../../web-shared/src/styles/base'as * with ($breakpoints: ('xs': "768px", - 'sm': "768px", - 'md': "1200px", - 'lg': "1200px", - 'xl': "1600px", - ), - $grid-columns: 12); - -@use '../../web-shared/src/styles/custom-style/colors'; -@use '../../web-shared/src/styles/custom-style/spacing'; -@use '../../web-shared/src/styles/custom-style/shared-styles'; -@use '../../web-shared/src/styles/custom-style/typography'; -@use '../../web-shared/src/styles/custom-style/icons'; -@use '../../web-shared/src/styles/custom-style/buttons'; -@use '../../web-shared/src/styles/custom-style/forms'; -@use '../../web-shared/src/styles/custom-style/util'; - -@use '../../web-shared/src/styles/components/radios-checkboxes'; -@use '../../web-shared/src/styles/components/circle-loader'; -@use '../../web-shared/src/styles/components/list'; -@use '../../web-shared/src/styles/components/form-validator'; -@use '../../web-shared/src/styles/components/btn-states'; -@use '../../web-shared/src/styles/components/alert'; -@use '../../web-shared/src/styles/components/details'; -@use '../../web-shared/src/styles/components/tabbed-navigation'; -@use '../../web-shared/src/styles/components/dropdown'; -@use '../../web-shared/src/styles/components/modal'; -@use '../../web-shared/src/styles/components/chip'; -@use '../../web-shared/src/styles/components/autocomplete'; -@use '../../web-shared/src/styles/components/select-autocomplete'; -@use '../../web-shared/src/styles/components/interactive-table'; -@use '../../web-shared/src/styles/components/pagination'; -@use '../../web-shared/src/styles/components/custom-select'; -@use '../../web-shared/src/styles/components/pre-header'; -@use '../../web-shared/src/styles/components/table'; -@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'; -@use '../../web-shared/src/styles/components/side-navigation-v4'; diff --git a/old-apps/projects/src/app/index.svelte b/old-apps/projects/src/app/index.svelte deleted file mode 100644 index c121a32..0000000 --- a/old-apps/projects/src/app/index.svelte +++ /dev/null @@ -1,96 +0,0 @@ -<svelte:options immutable={true}/> -<svelte:window bind:online={online}/> - -<script lang="ts"> - import { Locales } from "$app/lib/i18n/i18n-types"; - import { logout_user } from "$app/lib/services/user-service"; - import { currentLocale, preffered_or_default } from "$shared/lib/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 notOnlineText; - let LL; - - console.log("Projects Startup Report", { - prefferedLocale: navigator.language, - timeZone: Temporal.Now.timeZone().id, - themeCookie: {name: CookieNames.theme, value: get_cookie(CookieNames.theme)}, - localeCookie: {name: CookieNames.locale, value: get_cookie(CookieNames.locale)}, - prefersColorScheme: window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" - }); - - currentLocale.subscribe(async locale => { - if (locale === "preffered") locale = preffered_or_default(); - await loadLocaleAsync(locale as Locales); - LL = i18nObject(locale as Locales); - setLocale(locale as Locales); - }); - - onMount(async () => { - await loadLocaleAsync($currentLocale); - LL = i18nObject($currentLocale); - setLocale($currentLocale); - 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}">{notOnlineText}</PreHeader> - -<QueryClientProvider client={queryClient}> - <Router - {routes} - restoreScrollState={true} - on:routeLoading={() => { - document.getElementById("loader").style.display = "inline-block"; - }} - on:routeLoaded={() => { - document.getElementById("loader").style.display = "none"; - }} - /> -</QueryClientProvider> diff --git a/old-apps/projects/src/app/index.ts b/old-apps/projects/src/app/index.ts deleted file mode 100644 index febb583..0000000 --- a/old-apps/projects/src/app/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -// @ts-ignore -import App from "./index.svelte"; -import "./index.scss"; -import {is_debug, is_development} from "$shared/lib/configuration"; -import {noop} from "$shared/lib/helpers"; - -if (is_development() || is_debug()) { - console.log("%c Debug", "background-color:yellow;color:black;font-size:18px;"); -} else { - console.log("%c Production; Suppressing logs", "background-color:yellow;color:black;font-size:18px;"); - console.log = noop; -} - -export default new App({ - target: document.getElementById("root"), -}); diff --git a/old-apps/projects/src/app/lib/i18n/en/index.ts b/old-apps/projects/src/app/lib/i18n/en/index.ts deleted file mode 100644 index a85af7b..0000000 --- a/old-apps/projects/src/app/lib/i18n/en/index.ts +++ /dev/null @@ -1,126 +0,0 @@ -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: { - confirmDeleteEntry: "Are you sure you want to delete this entry?", - newEntry: "New entry", - editEntry: "Edit entry", - deleteEntry: "Delete entry", - loggedTimeToday: "Logged time today", - loggedTimeTodayString: "{hours}h{minutes}m", - 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/old-apps/projects/src/app/lib/i18n/formatters.ts b/old-apps/projects/src/app/lib/i18n/formatters.ts deleted file mode 100644 index 78734f9..0000000 --- a/old-apps/projects/src/app/lib/i18n/formatters.ts +++ /dev/null @@ -1,11 +0,0 @@ -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/old-apps/projects/src/app/lib/i18n/i18n-svelte.ts b/old-apps/projects/src/app/lib/i18n/i18n-svelte.ts deleted file mode 100644 index 6cdffb3..0000000 --- a/old-apps/projects/src/app/lib/i18n/i18n-svelte.ts +++ /dev/null @@ -1,12 +0,0 @@ -// 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/old-apps/projects/src/app/lib/i18n/i18n-types.ts b/old-apps/projects/src/app/lib/i18n/i18n-types.ts deleted file mode 100644 index acba223..0000000 --- a/old-apps/projects/src/app/lib/i18n/i18n-types.ts +++ /dev/null @@ -1,822 +0,0 @@ -// 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 - /** - * Categories - */ - categories: 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 - /** - * Labels - */ - labels: 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: { - /** - * 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 - /** - * {hours}h{minutes}m - * @param {unknown} hours - * @param {unknown} minutes - */ - loggedTimeTodayString: RequiredParams<'hours' | 'minutes'> - /** - * 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 - /** - * Categories - */ - categories: () => 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 - /** - * Labels - */ - labels: () => 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: { - /** - * 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 - /** - * {hours}h{minutes}m - */ - loggedTimeTodayString: (arg: { hours: unknown, minutes: unknown }) => 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/old-apps/projects/src/app/lib/i18n/i18n-util.async.ts b/old-apps/projects/src/app/lib/i18n/i18n-util.async.ts deleted file mode 100644 index 3ccef5f..0000000 --- a/old-apps/projects/src/app/lib/i18n/i18n-util.async.ts +++ /dev/null @@ -1,27 +0,0 @@ -// 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 importLocaleAsync = async (locale: Locales) => - (await localeTranslationLoaders[locale]()).default as unknown as Translations - -export const loadLocaleAsync = async (locale: Locales): Promise<void> => { - updateDictionary(locale, await importLocaleAsync(locale)) - 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/old-apps/projects/src/app/lib/i18n/i18n-util.sync.ts b/old-apps/projects/src/app/lib/i18n/i18n-util.sync.ts deleted file mode 100644 index f1a8e9e..0000000 --- a/old-apps/projects/src/app/lib/i18n/i18n-util.sync.ts +++ /dev/null @@ -1,26 +0,0 @@ -// 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 => - void (loadedFormatters[locale] = initFormatters(locale)) diff --git a/old-apps/projects/src/app/lib/i18n/i18n-util.ts b/old-apps/projects/src/app/lib/i18n/i18n-util.ts deleted file mode 100644 index cad1e7a..0000000 --- a/old-apps/projects/src/app/lib/i18n/i18n-util.ts +++ /dev/null @@ -1,31 +0,0 @@ -// 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/old-apps/projects/src/app/lib/i18n/nb/index.ts b/old-apps/projects/src/app/lib/i18n/nb/index.ts deleted file mode 100644 index 1638345..0000000 --- a/old-apps/projects/src/app/lib/i18n/nb/index.ts +++ /dev/null @@ -1,126 +0,0 @@ -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: { - loggedTimeTodayString: "{hours}t{minutes}m", - confirmDeleteEntry: "Er du sikker på at du vil slette denne raden?", - newEntry: "Ny tidsoppføring", - editEntry: "Rediger rad", - deleteEntry: "Slett rad", - loggedTimeToday: "Registrert tid hittil idag", - currentTime: "Klokken", - loading: "Laster", - stopwatch: "Stoppeklokke", - todayEntries: "Dagens tidsoppføringer", - noEntriesToday: "Ingen oppføringer i dag", - refreshTodayEntries: "Last inn dagens tidsoppføringer 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/old-apps/projects/src/app/lib/services/user-service.ts b/old-apps/projects/src/app/lib/services/user-service.ts deleted file mode 100644 index 4155819..0000000 --- a/old-apps/projects/src/app/lib/services/user-service.ts +++ /dev/null @@ -1,14 +0,0 @@ -import {portal_base} from "$shared/lib/configuration"; -import {end_session} from "$shared/lib/session"; -import {clear_categories} from "$app/lib/stores/categories"; -import {clear_entries} from "$app/lib/stores/entries"; -import {clear_labels} from "$app/lib/stores/labels"; - -export async function logout_user(reason: string = "") { - await end_session(() => { - clear_categories(); - clear_labels(); - clear_entries(); - location.replace(portal_base("#/login" + (reason ? "?" + reason : ""))); - }); -} diff --git a/old-apps/projects/src/app/lib/stores/categories.ts b/old-apps/projects/src/app/lib/stores/categories.ts deleted file mode 100644 index 2a63c42..0000000 --- a/old-apps/projects/src/app/lib/stores/categories.ts +++ /dev/null @@ -1,44 +0,0 @@ -import {writable, get} from "svelte/store"; -import {create_time_category, delete_time_category, get_time_categories} from "$shared/lib/api/time-entry"; -import type {TimeCategoryDto} from "$shared/lib/models/TimeCategoryDto"; -import type {IInternalFetchResponse} from "$shared/lib/models/IInternalFetchResponse"; - -const categories = writable<Array<TimeCategoryDto>>([]); - -export async function reload_categories() { - const get_categories_response = await get_time_categories(); - if (!get_categories_response.ok) { - clear_categories(); - return; - } - categories.set(get_categories_response.data ?? []); -} - -export function clear_categories() { - categories.set([]); -} - -export async function create_category_async(request: TimeCategoryDto): Promise<IInternalFetchResponse> { - const create_entry_response = await create_time_category(request); - if (create_entry_response.ok) { - const stored_entries = get(categories); - stored_entries.push(create_entry_response.data); - categories.set(stored_entries); - } - return create_entry_response; -} - -export async function edit_category_async(entry: TimeCategoryDto) { - if (!entry.id) return; -} - -export async function delete_category_async(entry: TimeCategoryDto) { - if (!entry.id) return; - const http_request = await delete_time_category(entry.id); - if (http_request.ok) { - const stored_entries = get(categories); - categories.set(stored_entries.filter(e => e.id !== entry.id)); - } -} - -export default categories; diff --git a/old-apps/projects/src/app/lib/stores/entries.ts b/old-apps/projects/src/app/lib/stores/entries.ts deleted file mode 100644 index e933568..0000000 --- a/old-apps/projects/src/app/lib/stores/entries.ts +++ /dev/null @@ -1,74 +0,0 @@ -import {Temporal} from "@js-temporal/polyfill"; -import {writable, get} from "svelte/store"; -import {get_time_entries, create_time_entry, delete_time_entry, update_time_entry} from "$shared/lib/api/time-entry"; -import type {TimeEntryDto} from "$shared/lib/models/TimeEntryDto"; -import type {IInternalFetchResponse} from "$shared/lib/models/IInternalFetchResponse"; -import type {TimeEntryQuery} from "$shared/lib/models/TimeEntryQuery"; - -const entries = writable<Array<TimeEntryDto>>([]); - -export function get_time_entry(id: string): TimeEntryDto { - return get(entries).find(c => c.id === id); -} - -export async function reload_entries(query: TimeEntryQuery): Promise<void> { - const get_entries_response = await get_time_entries(query); - if (!get_entries_response.ok) { - clear_entries(); - return; - } - entries.set(get_default_sorted(get_entries_response.data?.results ?? [])); -} - -export function clear_entries() { - entries.set([]); -} - -function get_default_sorted(unsorted: Array<TimeEntryDto>): Array<TimeEntryDto> { - if (unsorted.length < 1) return unsorted; - const byStart = unsorted.sort((a, b) => { - return Temporal.Instant.compare(Temporal.Instant.from(b.start), Temporal.Instant.from(a.start)); - }); - - return byStart.sort((a, b) => { - return Temporal.Instant.compare(Temporal.Instant.from(b.stop), Temporal.Instant.from(a.stop)); - }); -} - -export async function create_entry_async(request: TimeEntryDto): Promise<IInternalFetchResponse> { - const create_entry_response = await create_time_entry(request); - if (create_entry_response.ok) { - const stored_entries = get(entries) ?? []; - stored_entries.push(create_entry_response.data); - entries.set(get_default_sorted(stored_entries)); - } - return create_entry_response; -} - -export async function edit_entry_async(request: TimeEntryDto): Promise<IInternalFetchResponse> { - if (!request.id) return; - const edit_entry_response = await update_time_entry(request); - if (edit_entry_response.ok) { - const stored_entries = get(entries) ?? []; - const index = stored_entries.findIndex(c => c.id === request.id); - if (index === -1) { - stored_entries.push(edit_entry_response.data); - } else { - stored_entries[index] = edit_entry_response.data; - } - entries.set(get_default_sorted(stored_entries)); - } - return edit_entry_response; -} - -export async function delete_entry_async(entry_id: string): Promise<void> { - if (!entry_id) throw new Error("No id was supplied when deleting query"); - const delete_entry_response = await delete_time_entry(entry_id); - if (delete_entry_response.ok) { - const stored_entries = get(entries) ?? []; - entries.set(get_default_sorted(stored_entries.filter((e) => e.id !== entry_id) ?? [])); - } -} - - -export default entries; diff --git a/old-apps/projects/src/app/lib/stores/labels.ts b/old-apps/projects/src/app/lib/stores/labels.ts deleted file mode 100644 index d5ffaa9..0000000 --- a/old-apps/projects/src/app/lib/stores/labels.ts +++ /dev/null @@ -1,44 +0,0 @@ -import {writable, get} from "svelte/store"; -import {create_time_label, delete_time_label, get_time_labels} from "$shared/lib/api/time-entry"; -import type {IInternalFetchResponse} from "$shared/lib/models/IInternalFetchResponse"; -import type {TimeLabelDto} from "$shared/lib/models/TimeLabelDto"; - -const labels = writable<Array<TimeLabelDto>>([]); - -export async function reload_labels() { - const get_labels_response = await get_time_labels(); - if (!get_labels_response.ok) { - clear_labels(); - return; - } - labels.set(get_labels_response.data ?? []); -} - -export function clear_labels() { - labels.set([]); -} - -export async function create_label_async(request: TimeLabelDto): Promise<IInternalFetchResponse> { - const create_label_response = await create_time_label(request); - if (create_label_response.ok) { - const stored_entries = get(labels) ?? []; - stored_entries.push(create_label_response.data); - labels.set(stored_entries); - } - return create_label_response; -} - -export async function edit_label_async(entry: TimeLabelDto) { - if (!entry.id) throw new Error("Label id is required"); -} - -export async function delete_label_async(entry: TimeLabelDto) { - if (!entry.id) return; - const http_request = await delete_time_label(entry.id); - if (http_request.ok) { - const stored_entries = get(labels) ?? []; - labels.set(stored_entries.filter(e => e.id !== entry.id)); - } -} - -export default labels; diff --git a/old-apps/projects/src/app/pages/_layout.svelte b/old-apps/projects/src/app/pages/_layout.svelte deleted file mode 100644 index 07a4a25..0000000 --- a/old-apps/projects/src/app/pages/_layout.svelte +++ /dev/null @@ -1,66 +0,0 @@ -<script> - import {onMount} from "svelte"; - import {location, link} from "svelte-spa-router"; - import {logout_user} from "$app/lib/services/user-service"; - 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 {NavWrapper, NavItem} from "./nav"; - - let ProfileModalFunctions = {}; - let showUserMenu = false; - let userMenuTriggerNode; - const userMenuId = "__menu_" + random_string(3); - const username = get_session_data()?.profile.username; - - onMount(() => { - userMenuTriggerNode = document.getElementById("open-user-menu"); - }); -</script> - -<ProfileModal bind:functions={ProfileModalFunctions}/> -<BlowoutToolbelt/> - -<NavWrapper> - <ul slot="navigation-items"> - <NavItem to="/home" text="{$LL.nav.home()}"/> - <NavItem to="/data" text="{$LL.nav.data()}"/> - <NavItem to="/settings" text="{$LL.nav.settings()}"/> - </ul> - <div slot="navigation-footer" class="tabs-nav-v2 justify-between"> - <div class="tab-v2 padding-x-sm"> - <Button class="user-menu-control" - variant="reset" - id="open-user-menu" - on:click={() => showUserMenu = !showUserMenu} - text={username} - icon={IconNames.chevronDown} - icon_width="2rem" - icon_height="2rem" - icon_right_aligned="true" - title="{$LL.nav.usermenu.toggleTitle()}" - aria-controls="{userMenuId}" - /> - <Menu bind:show="{showUserMenu}" - trigger={userMenuTriggerNode} - id="{userMenuId}"> - <div slot="options"> - <MenuItem on:click={() => ProfileModalFunctions.open()}> - <span title="{$LL.nav.usermenu.profileTitle()}">{$LL.nav.usermenu.profile()}</span> - </MenuItem> - <MenuItemSeparator/> - <MenuItem danger="true" - on:click={() => logout_user()}> - <span title="{$LL.nav.usermenu.logoutTitle()}">{$LL.nav.usermenu.logout()}</span> - </MenuItem> - </div> - </Menu> - </div> - </div> - <slot slot="main-content"/> -</NavWrapper>
\ No newline at end of file diff --git a/old-apps/projects/src/app/pages/data.svelte b/old-apps/projects/src/app/pages/data.svelte deleted file mode 100644 index 190c641..0000000 --- a/old-apps/projects/src/app/pages/data.svelte +++ /dev/null @@ -1,396 +0,0 @@ -<script> - import {IconNames} from "$shared/lib/configuration"; - import {onMount} from "svelte"; - import {Temporal} from "@js-temporal/polyfill"; - import Layout from "./_layout.svelte"; - import Modal from "$shared/components/modal.svelte"; - import Tile from "$shared/components/tile.svelte"; - import Icon from "$shared/components/icon.svelte"; - import EntryForm from "$app/pages/views/entry-form/index.svelte"; - import {Table, THead, TBody, TCell, TRow, TablePaginator} from "$shared/components/table"; - import {TimeEntryQueryDuration} from "$shared/lib/models/TimeEntryQuery"; - 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; - - const defaultQuery = { - duration: TimeEntryQueryDuration.THIS_YEAR, - categories: [], - labels: [], - page: page, - pageSize: 50, - }; - - let isLoading; - let categories = []; - let labels = []; - let entries = []; - let durationSummary = false; - let EditEntryModal; - let EditEntryForm; - let currentTimespanFilter = TimeEntryQueryDuration.THIS_YEAR; - let currentSpecificDateFilter = Temporal.Now.plainDateTimeISO().subtract({days: 1}).toString().substring(0, 10); - let currentDateRangeFilter = {}; - let currentCategoryFilter = "all"; - let currentLabelFilter = "all"; - let showDateFilterOptions = false; - let secondsLogged = 0; - - function set_duration_summary_string() { - if (entries.length > 0) { - 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 = ""; - } - } - - async function load_entries(query = defaultQuery) { - isLoading = true; - const response = await get_time_entries(query); - if (response.status === 200) { - const responseEntries = []; - secondsLogged = 0; - for (const entry of response.data.results) { - const date_time = unwrap_date_time_from_entry(entry); - const seconds = (date_time.duration.hours * 60 * 60) + (date_time.duration.minutes * 60); - responseEntries.push({ - id: entry.id, - date: date_time.start_date, - start: date_time.start_time, - stop: date_time.stop_time, - durationString: date_time.duration.hours + $LL.data.hourSingleChar() + date_time.duration.minutes + $LL.data.minSingleChar(), - seconds: seconds, - category: entry.category, - labels: entry.labels, - description: entry.description, - }); - secondsLogged += seconds; - } - entries = responseEntries; - page = response.data.page; - pageCount = response.data.totalPageCount; - } else { - entries = []; - page = 0; - pageCount = 0; - } - isLoading = false; - set_duration_summary_string(); - } - - function load_entries_with_filter(page = 1) { - let query = defaultQuery; - query.duration = currentTimespanFilter; - query.labels = []; - query.categories = []; - query.page = page; - - if (currentTimespanFilter === TimeEntryQueryDuration.SPECIFIC_DATE) { - query.specificDate = currentSpecificDateFilter; - } else { - delete query.specificDate; - } - - if (currentTimespanFilter === TimeEntryQueryDuration.DATE_RANGE) { - query.dateRange = currentDateRangeFilter; - } else { - delete query.dateRange; - } - - if ((currentCategoryFilter !== "all" && currentCategoryFilter?.length > 0) ?? false) { - for (const chosenCategoryId of currentCategoryFilter) { - if (chosenCategoryId === "all") { - continue; - } - query.categories.push({ - id: chosenCategoryId, - }); - } - } - - if ((currentLabelFilter !== "all" && currentLabelFilter?.length > 0) ?? false) { - for (const chosenLabelId of currentLabelFilter) { - if (chosenLabelId === "all") { - continue; - } - query.labels.push({ - id: chosenLabelId, - }); - } - } - - load_entries(query); - } - - async function handle_delete_entry_button_click(e, entryId) { - if (confirm($LL.data.confirmDeleteEntry())) { - const response = await delete_time_entry(entryId); - if (response.ok) { - const indexOfEntry = entries.findIndex((c) => c.id === entryId); - if (indexOfEntry !== -1) { - secondsLogged -= entries[indexOfEntry].seconds; - entries.splice(indexOfEntry, 1); - entries = entries; - set_duration_summary_string(); - } - } - } - } - - function handle_edit_entry_form_updated() { - load_entries_with_filter(page); - EditEntryModal.close(); - } - - async function handle_edit_entry_button_click(event, entryId) { - const response = await get_time_entry(entryId); - if (response.status === 200) { - if (is_guid(response.data.id)) { - EditEntryForm.set_values(response.data); - EditEntryModal.open(); - move_focus(document.querySelector("input[id='date']")); - } - } - } - - function close_date_filter_box(event) { - if (!event.target.closest(".date_filter_box_el")) { - showDateFilterOptions = false; - window.removeEventListener("click", close_date_filter_box); - } - } - - function toggle_date_filter_box(event) { - const box = document.getElementById("date_filter_box"); - const rect = event.target.getBoundingClientRect(); - box.style.top = rect.y + "px"; - box.style.left = rect.x - 50 + "px"; - showDateFilterOptions = true; - window.addEventListener("click", close_date_filter_box); - } - - onMount(() => { - isLoading = true; - Promise.all([load_entries()]).then(() => { - isLoading = false; - }); - }); -</script> - -<Modal title="{$LL.data.editEntry()}" - bind:functions={EditEntryModal} - on:closed={() => EditEntryForm.reset()}> - <EntryForm bind:functions={EditEntryForm} - on:updated={handle_edit_entry_form_updated}/> -</Modal> - -<div id="date_filter_box" - style="margin-top:25px" - class="padding-xs z-index-overlay bg shadow-sm position-absolute date_filter_box_el border {showDateFilterOptions ? '' : 'hide'}"> - <div class="flex items-baseline margin-bottom-xxxxs"> - <label class="text-sm color-contrast-medium margin-right-xs" - for="durationSelect">Timespan:</label> - <div class="select inline-block js-select"> - <select name="durationSelect" - bind:value={currentTimespanFilter} - id="durationSelect"> - <option value={TimeEntryQueryDuration.TODAY} - selected> Today - </option> - <option value={TimeEntryQueryDuration.THIS_WEEK}>This week</option> - <option value={TimeEntryQueryDuration.THIS_MONTH}>This month</option> - <option value={TimeEntryQueryDuration.THIS_YEAR}>This year</option> - <option value={TimeEntryQueryDuration.SPECIFIC_DATE}>Spesific date</option> - <option value={TimeEntryQueryDuration.DATE_RANGE}>Date range</option> - </select> - - <svg class="icon icon--xxxs margin-left-xxs" - viewBox="0 0 8 8"> - <path d="M7.934,1.251A.5.5,0,0,0,7.5,1H.5a.5.5,0,0,0-.432.752l3.5,6a.5.5,0,0,0,.864,0l3.5-6A.5.5,0,0,0,7.934,1.251Z"/> - </svg> - </div> - </div> - - {#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">{$LL.data.date()}:</span> - <span class="text-sm"> - <input type="date" - class="border-none padding-0 color-inherit bg-transparent" - bind:value={currentSpecificDateFilter}/> - </span> - </div> - {/if} - - {#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">{$LL.data.from()}:</span> - <span class="text-sm"> - <input type="date" - class="border-none padding-0 color-inherit bg-transparent" - on:change={(e) => (currentDateRangeFilter.from = e.target.value)}/> - </span> - </div> - - <div class="flex items-baseline margin-bottom-xxxxs justify-between"> - <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" - on:change={(e) => (currentDateRangeFilter.to = e.target.value)}/> - </span> - </div> - {/if} - - <div class="flex items-baseline justify-end"> - <Button variant="subtle" - on:click={() => load_entries_with_filter(page)} - class="text-sm" - text="{$LL.data.use()}"/> - </div> -</div> - -<Layout> - <Tile class="{isLoading ? 'c-disabled loading' : ''}"> - <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> - <li><span class="s-tabs__link">Draft (2)</span></li> - </ul> - </nav> - <div class="max-width-100% overflow-auto" - style="max-height: 82.5vh"> - <Table class="text-sm width-100% int-table--sticky-header"> - <THead> - <TCell type="th" - style="width: 30px;"> - <div class="custom-checkbox int-table__checkbox"> - <input class="custom-checkbox__input" - type="checkbox" - aria-label="Select all rows"/> - <div class="custom-checkbox__control" - aria-hidden="true"></div> - </div> - </TCell> - - <TCell type="th" - style="width: 100px"> - <div class="flex items-center justify-between"> - <span>{$LL.data.date()}</span> - <div class="date_filter_box_el cursor-pointer" - on:click={toggle_date_filter_box}> - <Icon name="{IconNames.funnel}"/> - </div> - </div> - </TCell> - - <TCell type="th" - style="width: 100px"> - <div class="flex items-center"> - <span>{$LL.data.duration()}</span> - </div> - </TCell> - - <TCell type="th" - style="width: 100px;"> - <div class="flex items-center"> - <span>{$LL.data.category()}</span> - </div> - </TCell> - - <TCell type="th" - style="width: 300px;"> - <div class="flex items-center"> - <span>{$LL.data.description()}</span> - </div> - </TCell> - <TCell type="th" - style="width: 50px"></TCell> - </THead> - <TBody> - {#if entries.length > 0} - {#each entries as entry} - <TRow class="text-nowrap" - data-id={entry.id}> - <TCell type="th" - thScope="row"> - <div class="custom-checkbox int-table__checkbox"> - <input class="custom-checkbox__input" - type="checkbox" - aria-label="Select this row"/> - <div class="custom-checkbox__control" - aria-hidden="true"></div> - </div> - </TCell> - <TCell> - <pre>{entry.date.toLocaleString()}</pre> - </TCell> - <TCell> - <pre class="flex justify-between"> - <div class="flex justify-between"> - <span>{entry.start.toLocaleString(undefined, {timeStyle: "short"})}</span> - <span> - </span> - <span>{entry.stop.toLocaleString(undefined, {timeStyle: "short"})}</span> - </div> - </pre> - </TCell> - <TCell> - <span data-id={entry.category.id}>{entry.category.name}</span> - </TCell> - <TCell class="text-truncate max-width-xxxxs" - title="{entry.description}"> - {entry.description ?? ""} - </TCell> - <TCell class="flex flex-row justify-end items-center"> - <Button icon="{IconNames.pencilSquare}" - variant="reset" - icon_width="1.2rem" - icon_height="1.2rem" - on:click={(e) => handle_edit_entry_button_click(e, entry.id)} - title="Edit entry"/> - <Button icon="{IconNames.trash}" - variant="reset" - icon_width="1.2rem" - icon_height="1.2rem" - on:click={(e) => handle_delete_entry_button_click(e, entry.id)} - title="Delete entry"/> - </TCell> - </TRow> - {/each} - {:else} - <TRow class="text-nowrap"> - <TCell type="th" - thScope="row" - colspan="7"> - {isLoading ? $LL.data.loading() + "..." : $LL.data.noEntries()} - </TCell> - </TRow> - {/if} - </TBody> - </Table> - </div> - <div class="flex items-center justify-between"> - <p class="text-sm"> - {#if durationSummary} - <small class={isLoading ? "c-disabled loading" : ""}>{durationSummary}</small> - {:else} - <small class={isLoading ? "c-disabled loading" : ""}>{$LL.data.noEntries()}</small> - {/if} - </p> - - <nav class="grid padding-y-sm {isLoading ? 'c-disabled loading' : ''}"> - <TablePaginator {page} - on:value_change={(e) => load_entries_with_filter(e.detail.newValue)} - {pageCount}/> - </nav> - </div> - </Tile> -</Layout> diff --git a/old-apps/projects/src/app/pages/home.svelte b/old-apps/projects/src/app/pages/home.svelte deleted file mode 100644 index 1f398b5..0000000 --- a/old-apps/projects/src/app/pages/home.svelte +++ /dev/null @@ -1,178 +0,0 @@ -<script lang="ts"> - import LL from "$app/lib/i18n/i18n-svelte"; - import { delete_time_entry, get_time_entries, get_time_entry } from "$shared/lib/api/time-entry"; - import { IconNames, QueryKeys } from "$shared/lib/configuration"; - import { TimeEntryDto } from "$shared/lib/models/TimeEntryDto"; - import { Temporal } from "@js-temporal/polyfill"; - import { useMutation, useQuery, useQueryClient } from "@sveltestack/svelte-query"; - import { onMount } from "svelte"; - import Tile from "$shared/components/tile.svelte"; - import Button from "$shared/components/button.svelte"; - import Stopwatch from "$shared/components/stopwatch.svelte"; - import { Table, THead, TBody, TCell, TRow } from "$shared/components/table"; - import Layout from "./_layout.svelte"; - import EntryFrom from "$app/pages/views/entry-form/index.svelte"; - import { seconds_to_hour_minute, unwrap_date_time_from_entry } from "$shared/lib/helpers"; - import { TimeEntryQueryDuration } from "$shared/lib/models/TimeEntryQuery"; - - let currentTime = ""; - let isLoading = false; - let EditEntryForm: any; - let timeEntries = [] as Array<TimeEntryDto>; - let timeLoggedTodayString = $LL.home.loggedTimeTodayString({hours: 0, minutes: 0}); - let loggedSecondsToday = 0; - - const queryClient = useQueryClient(); - const queryResult = useQuery(QueryKeys.entries, async () => await get_time_entries({ - duration: TimeEntryQueryDuration.TODAY, - page: 1, - pageSize: 100, - })?.data ?? [] - ); - - function set_current_time() { - currentTime = Temporal.Now.plainTimeISO().toLocaleString(undefined, { - timeStyle: "short", - }); - } - - const delete_entry_mutation = useMutation(delete_time_entry, { - onSuccess: (data) => { - queryClient.invalidateQueries([QueryKeys.entries, data.data.id]); - }, - }); - - async function on_edit_entry_button_click(event, entryId: string) { - const response = useQuery([QueryKeys.entries, entryId], () => { - return get_time_entry(entryId); - }); - - EditEntryForm.set_values(response); - } - - async function on_delete_entry_button_click(event, entryId: string) { - if (confirm($LL.home.confirmDeleteEntry())) { - $delete_entry_mutation.mutate(entryId); - } - } - - function on_create_from_stopwatch(event) { - EditEntryForm.set_time({to: event.detail.to, from: event.detail.from}); - if (event.detail.description) { - EditEntryForm.set_description(event.detail.description); - } - } - - onMount(async () => { - set_current_time(); - setInterval(() => { - set_current_time(); - }, 1e4); - queryResult.subscribe((result) => { - const newEntries = []; - loggedSecondsToday = 0; - for (const entry of result.data?.results ?? []) { - const date_time = unwrap_date_time_from_entry(entry); - newEntries.push({ - id: entry.id, - start: date_time.start_time, - stop: date_time.stop_time, - category: entry.category, - }); - loggedSecondsToday += (date_time.duration.hours * 60 * 60) + (date_time.duration.minutes * 60); - } - timeLoggedTodayString = $LL.home.loggedTimeTodayString(seconds_to_hour_minute(loggedSecondsToday)); - timeEntries = newEntries; - }); - }); -</script> - -<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">{$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">{$LL.home.loggedTimeToday()}</p> - <pre class="text-xxl">{currentTime}</pre> - <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">{$LL.home.stopwatch()}</h3> - </Stopwatch> - </Tile> - <Tile class="col-12"> - <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>{$LL.home.category()}</span> - </TCell> - <TCell type="th" - class="text-left"> - <span>{$LL.home.timespan()}</span> - </TCell> - <TCell type="th" - class="text-right"> - <Button icon="{IconNames.refresh}" - variant="reset" - icon_width="1.2rem" - icon_height="1.2rem" - title="{$LL.home.refreshTodayEntries()}" - on:click={() => queryClient.invalidateQueries(QueryKeys.entries)}/> - </TCell> - </THead> - <TBody> - {#if timeEntries.length > 0} - {#each timeEntries as entry} - <TRow class="text-nowrap text-left" - data-id={entry.id}> - <TCell> - <span data-id={entry.category?.id}> - {entry.category?.name} - </span> - </TCell> - <TCell> - {entry.start.toLocaleString(undefined, {timeStyle: "short"})} - <span>-</span> - {entry.stop.toLocaleString(undefined, {timeStyle: "short"})} - </TCell> - <TCell class="flex flex-row justify-end items-center"> - <Button icon="{IconNames.pencilSquare}" - variant="reset" - icon_width="1.2rem" - icon_height="1.2rem" - on:click={(e) => on_edit_entry_button_click(e, entry.id)} - 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="{$LL.home.deleteEntry()}"/> - </TCell> - </TRow> - {/each} - {:else} - <TRow class="text-nowrap"> - <TCell type="th" - thScope="row" - colspan="7"> - {isLoading ? $LL.home.loading() + "..." : $LL.home.noEntriesToday()} - </TCell> - </TRow> - {/if} - </TBody> - </Table> - </div> - </Tile> - </div> - </div> -</Layout> diff --git a/old-apps/projects/src/app/pages/nav/css/1_responsive-sidebar.css b/old-apps/projects/src/app/pages/nav/css/1_responsive-sidebar.css deleted file mode 100644 index 515a9f2..0000000 --- a/old-apps/projects/src/app/pages/nav/css/1_responsive-sidebar.css +++ /dev/null @@ -1,179 +0,0 @@ -/* -------------------------------- - -File#: _1_responsive-sidebar -Title: Responsive Sidebar -Descr: Responsive sidebar container -Usage: codyhouse.co/license - --------------------------------- */ -/* mobile version only (--default) 👇 */ -.sidebar:not(.sidebar--static) { - position: fixed; - top: 0; - left: 0; - z-index: var(--z-index-fixed-element, 10); - width: 100%; - height: 100%; - visibility: hidden; - transition: visibility 0s 0.3s; -} -.sidebar:not(.sidebar--static)::after { - /* overlay layer */ - content: ""; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: hsla(var(--color-black-h), var(--color-black-s), var(--color-black-l), 0); - transition: background-color 0.3s; - z-index: 1; -} -.sidebar:not(.sidebar--static) .sidebar__panel { - /* content */ - position: absolute; - top: 0; - left: 0; - z-index: 2; - width: 100%; - max-width: 380px; - height: 100%; - overflow: auto; - -webkit-overflow-scrolling: touch; - background-color: var(--color-bg); - -webkit-transform: translateX(-100%); - transform: translateX(-100%); - transition: box-shadow 0.3s, -webkit-transform 0.3s; - transition: box-shadow 0.3s, transform 0.3s; - transition: box-shadow 0.3s, transform 0.3s, -webkit-transform 0.3s; -} -.sidebar:not(.sidebar--static).sidebar--right-on-mobile .sidebar__panel { - left: auto; - right: 0; - -webkit-transform: translateX(100%); - transform: translateX(100%); -} -.sidebar:not(.sidebar--static).sidebar--is-visible { - visibility: visible; - transition: none; -} -.sidebar:not(.sidebar--static).sidebar--is-visible::after { - background-color: hsla(var(--color-black-h), var(--color-black-s), var(--color-black-l), 0.85); -} -.sidebar:not(.sidebar--static).sidebar--is-visible .sidebar__panel { - -webkit-transform: translateX(0); - transform: translateX(0); - box-shadow: var(--shadow-md); -} - -/* end mobile version */ -.sidebar__header { - display: flex; - align-items: center; - justify-content: space-between; - position: -webkit-sticky; - position: sticky; - top: 0; -} - -.sidebar__close-btn { - --size: 32px; - width: var(--size); - height: var(--size); - display: flex; - border-radius: 50%; - background-color: var(--color-bg-light); - box-shadow: var(--inner-glow), var(--shadow-sm); - transition: 0.2s; - flex-shrink: 0; -} -.sidebar__close-btn .icon { - display: block; - margin: auto; -} -.sidebar__close-btn:hover { - background-color: var(--color-bg-lighter); - box-shadow: var(--inner-glow), var(--shadow-md); -} - -/* desktop version only (--static) 👇 */ -.sidebar--static { - flex-shrink: 0; - flex-grow: 1; -} -.sidebar--static .sidebar__header { - display: none; -} - -.sidebar--sticky-on-desktop { - position: -webkit-sticky; - position: sticky; - top: var(--space-sm); - max-height: calc(100vh - var(--space-sm)); - overflow: auto; - -webkit-overflow-scrolling: touch; -} - -/* end desktop version */ -.sidebar, .sidebar-loaded\:show { - opacity: 0; - /* hide sidebar - or other elements using the .sidebar-loaded:show class - while it is initialized in JS */ -} - -.sidebar--loaded { - opacity: 1; -} - -/* detect when the sidebar needs to switch from the mobile layout to a static one - used in JS */ -[class*=sidebar--static]::before { - display: none; -} - -.sidebar--static::before { - content: "static"; -} - -.sidebar--static\@xs::before { - content: "mobile"; -} -@media (min-width: 32rem) { - .sidebar--static\@xs::before { - content: "static"; - } -} - -.sidebar--static\@sm::before { - content: "mobile"; -} -@media (min-width: 48rem) { - .sidebar--static\@sm::before { - content: "static"; - } -} - -.sidebar--static\@md::before { - content: "mobile"; -} -@media (min-width: 64rem) { - .sidebar--static\@md::before { - content: "static"; - } -} - -.sidebar--static\@lg::before { - content: "mobile"; -} -@media (min-width: 80rem) { - .sidebar--static\@lg::before { - content: "static"; - } -} - -.sidebar--static\@xl::before { - content: "mobile"; -} -@media (min-width: 90rem) { - .sidebar--static\@xl::before { - content: "static"; - } -}
\ No newline at end of file diff --git a/old-apps/projects/src/app/pages/nav/css/2_side-navigation-v4.css b/old-apps/projects/src/app/pages/nav/css/2_side-navigation-v4.css deleted file mode 100644 index ec5fcdf..0000000 --- a/old-apps/projects/src/app/pages/nav/css/2_side-navigation-v4.css +++ /dev/null @@ -1,213 +0,0 @@ -/* -------------------------------- - -File#: _2_side-navigation-v4 -Title: Side Navigation v4 -Descr: Main, side navigation -Usage: codyhouse.co/license - --------------------------------- */ -.sidenav-v4 { - --sidenav-v4-icon-size: 20px; - --sidenav-v4-icon-margin-right: var(--space-xxs); -} - -.sidenav-v4__item { - position: relative; -} - -.sidenav-v4__link, -.sidenav-v4__sub-link, -.sidenav-v4__separator { - padding: var(--space-sm); -} - -.sidenav-v4__link, .sidenav-v4__sub-link { - display: flex; - align-items: center; - width: 100%; - border-radius: var(--radius-md); - text-decoration: none; - color: inherit; - line-height: 1; - font-size: var(--text-md); - transition: 0.2s; -} -.sidenav-v4__link:hover, .sidenav-v4__sub-link:hover { - color: var(--color-primary); - background-color: hsla(var(--color-contrast-higher-h), var(--color-contrast-higher-s), var(--color-contrast-higher-l), 0.075); -} -.sidenav-v4__link[aria-current=page], .sidenav-v4__sub-link[aria-current=page] { - color: var(--color-primary); -} - -.sidenav-v4__sub-link { - position: relative; - color: var(--color-contrast-medium); - /* dot indicator */ -} -.sidenav-v4__sub-link::before { - content: ""; - display: block; - --size: 6px; - width: var(--size); - height: var(--size); - background: currentColor; - border-radius: 50%; - margin-left: calc(var(--sidenav-v4-icon-size)/2 - var(--size)/2); - margin-right: calc(var(--sidenav-v4-icon-size)/2 - var(--size)/2 + var(--sidenav-v4-icon-margin-right)); - opacity: 0; - /* visible only if current */ -} -.sidenav-v4__sub-link[aria-current=page]::before { - /* show dot indicator */ - opacity: 1; -} - -.sidenav-v4__notification-marker { - margin-left: auto; - background-color: var(--color-accent); - border-radius: var(--radius-md); - height: 16px; - line-height: 16px; - padding: 0 4px; - color: var(--color-white); - font-size: 12px; - /* hide - visible only on desktop */ - display: none; -} - -/* label icon */ -.sidenav-v4__icon { - --size: var(--sidenav-v4-icon-size); - margin-right: var(--sidenav-v4-icon-margin-right); -} - -/* arrow icon - visible on mobile if item is expandable */ -.sidenav-v4__arrow-icon { - --size: 20px; - /* hide icon for links - show only for buttons created in JS */ -} -.sidenav-v4__arrow-icon .icon__group { - will-change: transform; - -webkit-transform-origin: 50% 50%; - transform-origin: 50% 50%; - -webkit-transform: rotate(-90deg); - transform: rotate(-90deg); - transition: -webkit-transform 0.3s var(--ease-out); - transition: transform 0.3s var(--ease-out); - transition: transform 0.3s var(--ease-out), -webkit-transform 0.3s var(--ease-out); -} -.sidenav-v4__arrow-icon .icon__group > * { - -webkit-transform-origin: 50% 50%; - transform-origin: 50% 50%; - stroke-dasharray: 20; - stroke-dashoffset: 0; - -webkit-transform: translateY(0px); - transform: translateY(0px); - transition: stroke-dashoffset 0.3s, -webkit-transform 0.3s; - transition: transform 0.3s, stroke-dashoffset 0.3s; - transition: transform 0.3s, stroke-dashoffset 0.3s, -webkit-transform 0.3s; - transition-timing-function: var(--ease-out); -} -.sidenav-v4__item--collapsed .sidenav-v4__arrow-icon .icon__group { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); -} -.sidenav-v4__item--collapsed .sidenav-v4__arrow-icon .icon__group > * { - -webkit-transform: translateY(4px); - transform: translateY(4px); -} -.sidenav-v4__item--collapsed .sidenav-v4__arrow-icon .icon__group > *:first-child { - stroke-dashoffset: 10.15; -} -.sidenav-v4__item--collapsed .sidenav-v4__arrow-icon .icon__group > *:last-child { - stroke-dashoffset: 10.15; -} -.sidenav-v4__link--href .sidenav-v4__arrow-icon { - display: none; -} - -/* current item */ -.sidenav-v4__item--current .sidenav-v4__sub-list { - display: block; - /* show sublist */ -} - -/* separator */ -.sidenav-v4__separator span { - display: block; - width: var(--sidenav-v4-icon-size); - height: 1px; - background-color: var(--color-contrast-lower); -} - -/* mobile only */ -@media not all and (min-width: 64rem) { - .sidenav-v4__item--collapsed .sidenav-v4__sub-list { - display: none; - } - - .sidenav-v4__link--href { - display: none; - /* hide link -> show button */ - } -} -/* desktop */ -@media (min-width: 64rem) { - .sidenav-v4__sub-list { - display: none; - } - - .sidenav-v4__link, -.sidenav-v4__sub-link, -.sidenav-v4__separator { - padding: var(--space-xs); - } - - .sidenav-v4__link, -.sidenav-v4__sub-link { - font-size: var(--text-sm); - } - - .sidenav-v4__link--btn { - display: none; - /* hide button -> show link */ - } - - /* tooltip */ - .sidenav-v4__item:not(.sidenav-v4__item--current) .sidenav-v4__sub-list { - width: 220px; - position: absolute; - z-index: var(--z-index-overlay); - left: 100%; - top: 0; - background-color: var(--color-bg-light); - box-shadow: var(--inner-glow), var(--shadow-md); - border-radius: var(--radius-md); - overflow: hidden; - } - .sidenav-v4__item:not(.sidenav-v4__item--current) .sidenav-v4__sub-link { - border-radius: 0; - color: var(--color-contrast-high); - } - .sidenav-v4__item:not(.sidenav-v4__item--current) .sidenav-v4__sub-link::before { - display: none; - /* remove dot indicator */ - } - .sidenav-v4__item:not(.sidenav-v4__item--current) .sidenav-v4__sub-link:hover { - color: var(--color-primary); - } - .sidenav-v4__item:not(.sidenav-v4__item--current).sidenav-v4__item--hover .sidenav-v4__sub-list, .sidenav-v4__item:not(.sidenav-v4__item--current):focus-within .sidenav-v4__sub-list { - display: block; - } - .sidenav-v4__item:not(.sidenav-v4__item--current):hover .sidenav-v4__link { - /* highlight main link if tooltip is visible */ - color: var(--color-primary); - background-color: hsla(var(--color-contrast-higher-h), var(--color-contrast-higher-s), var(--color-contrast-higher-l), 0.075); - } - - /* notification marker */ - .sidenav-v4__notification-marker { - display: block; - } -}
\ No newline at end of file diff --git a/old-apps/projects/src/app/pages/nav/html/side-navigation-v4.html b/old-apps/projects/src/app/pages/nav/html/side-navigation-v4.html deleted file mode 100644 index 1131b4d..0000000 --- a/old-apps/projects/src/app/pages/nav/html/side-navigation-v4.html +++ /dev/null @@ -1,211 +0,0 @@ -<div class="padding-component hide@md no-js:is-hidden"> - <button class="btn btn--primary" aria-controls="sidenav-v4">Show sidebar</button> -</div> - -<div class="flex@md"> - <aside id="sidenav-v4" class="sidebar sidebar--static@md js-sidebar" data-static-class="position-relative z-index-2 bg width-100% max-width-xxxxs shadow-sm"> - <div class="sidebar__panel"> - <!-- 👇 header visible only on mobile --> - <header class="sidebar__header bg padding-y-sm padding-left-md padding-right-sm border-bottom z-index-2"> - <h1 class="text-md text-truncate" id="sidebar-title">Menu</h1> - - <button class="reset sidebar__close-btn js-sidebar__close-btn js-tab-focus"> - <svg class="icon icon--xs" viewBox="0 0 16 16"><title>Close panel</title><g stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10"><line x1="13.5" y1="2.5" x2="2.5" y2="13.5"></line><line x1="2.5" y1="2.5" x2="13.5" y2="13.5"></line></g></svg> - </button> - </header> - - <div class="position-relative z-index-1"> - <nav class="sidenav-v4 padding-xs js-sidenav-v4"> - <ul> - <li class="sidenav-v4__item"> - <a class="sidenav-v4__link js-sidenav-v4__link" href="#0"> - <svg class="sidenav-v4__icon icon" viewBox="0 0 20 20"> - <g fill="currentColor"> - <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 12v7H4v-7"></path> - <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 10l9-9 9 9"></path> - <path d="M10 14a2 2 0 0 1 2 2v2H8v-2a2 2 0 0 1 2-2z"></path> - </g> - </svg> - - <span>Overview</span> - - <svg class="sidenav-v4__arrow-icon icon margin-left-auto" viewBox="0 0 20 20"> - <g class="icon__group" fill="none" stroke="currentColor" stroke-width="2px" stroke-linecap="round" stroke-linejoin="round"> - <line x1="3" y1="3" x2="17" y2="17" /> - <line x1="17" y1="3" x2="3" y2="17" /> - </g> - </svg> - </a> - - <ul class="sidenav-v4__sub-list"> - <li> - <a class="sidenav-v4__sub-link" href="#0">All Data</a> - </li> - - <li> - <a class="sidenav-v4__sub-link" href="#0">Category 1</a> - </li> - - <li> - <a class="sidenav-v4__sub-link" href="#0">Category 2</a> - </li> - </ul> - </li> - - <li class="sidenav-v4__item sidenav-v4__item--current"> - <a class="sidenav-v4__link js-sidenav-v4__link" href="#0"> - <svg class="sidenav-v4__icon icon" viewBox="0 0 20 20"> - <g fill="currentColor"> - <path d="M10 20a2 2 0 0 1-2-2h4a2 2 0 0 1-2 2z"></path> - <path d="M19 15a3 3 0 0 1-3-3V7a6 6 0 0 0-6-6 6 6 0 0 0-6 6v5a3 3 0 0 1-3 3h18z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path> - </g> - </svg> - - <span>Notifications</span> - - <span class="sidenav-v4__notification-marker">8 <i class="sr-only">notifications</i></span> - - <svg class="sidenav-v4__arrow-icon icon margin-left-auto" viewBox="0 0 20 20"> - <g class="icon__group" fill="none" stroke="currentColor" stroke-width="2px" stroke-linecap="round" stroke-linejoin="round"> - <line x1="3" y1="3" x2="17" y2="17" /> - <line x1="17" y1="3" x2="3" y2="17" /> - </g> - </svg> - </a> - - <ul class="sidenav-v4__sub-list"> - <li> - <a class="sidenav-v4__sub-link" href="#0">All Notifications</a> - </li> - - <li> - <a class="sidenav-v4__sub-link" href="#0" aria-current="page">Friends</a> - </li> - - <li> - <a class="sidenav-v4__sub-link" href="#0">Other</a> - </li> - </ul> - </li> - - <li class="sidenav-v4__item"> - <a class="sidenav-v4__link js-sidenav-v4__link" href="#0"> - <svg class="sidenav-v4__icon icon" viewBox="0 0 20 20"> - <g fill="currentColor"> - <path d="M17 2H3a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h4l3 4 3-4h4a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path> - </g> - </svg> - - <span>Comments</span> - - <svg class="sidenav-v4__arrow-icon icon margin-left-auto" viewBox="0 0 20 20"> - <g class="icon__group" fill="none" stroke="currentColor" stroke-width="2px" stroke-linecap="round" stroke-linejoin="round"> - <line x1="3" y1="3" x2="17" y2="17" /> - <line x1="17" y1="3" x2="3" y2="17" /> - </g> - </svg> - </a> - - <ul class="sidenav-v4__sub-list"> - <li> - <a class="sidenav-v4__sub-link" href="#0">All Comments</a> - </li> - - <li> - <a class="sidenav-v4__sub-link" href="#0">+ New Comment</a> - </li> - - <li> - <a class="sidenav-v4__sub-link" href="#0">Spam</a> - </li> - </ul> - </li> - - <li class="sidenav-v4__separator" role="presentation"><span></span></li> - - <li class="sidenav-v4__item"> - <a class="sidenav-v4__link js-sidenav-v4__link" href="#0"> - <svg class="sidenav-v4__icon icon" viewBox="0 0 20 20"> - <g fill="currentColor"> - <rect x="2" y="2" width="16" height="16" rx="2" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></rect> - <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 14l6-6 2 6H6z"></path><circle cx="6.5" cy="6.5" r="1.5"></circle> - </g> - </svg> - - <span>Assets</span> - - <svg class="sidenav-v4__arrow-icon icon margin-left-auto" viewBox="0 0 20 20"> - <g class="icon__group" fill="none" stroke="currentColor" stroke-width="2px" stroke-linecap="round" stroke-linejoin="round"> - <line x1="3" y1="3" x2="17" y2="17" /> - <line x1="17" y1="3" x2="3" y2="17" /> - </g> - </svg> - </a> - - <ul class="sidenav-v4__sub-list"> - <li> - <a class="sidenav-v4__sub-link" href="#0">All Assets</a> - </li> - - <li> - <a class="sidenav-v4__sub-link" href="#0">Upload</a> - </li> - </ul> - </li> - - <li class="sidenav-v4__item"> - <a class="sidenav-v4__link js-sidenav-v4__link" href="#0"> - <svg class="sidenav-v4__icon icon" viewBox="0 0 20 20"> - <g fill="currentColor"> - <circle cx="10" cy="4" r="3" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></circle> - <path d="M10 11a8 8 0 0 0-7.562 5.383A2 2 0 0 0 4.347 19h11.306a2 2 0 0 0 1.909-2.617A8 8 0 0 0 10 11z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path> - </g> - </svg> - - <span>Users</span> - - <svg class="sidenav-v4__arrow-icon icon margin-left-auto" viewBox="0 0 20 20"> - <g class="icon__group" fill="none" stroke="currentColor" stroke-width="2px" stroke-linecap="round" stroke-linejoin="round"> - <line x1="3" y1="3" x2="17" y2="17" /> - <line x1="17" y1="3" x2="3" y2="17" /> - </g> - </svg> - </a> - - <ul class="sidenav-v4__sub-list"> - <li> - <a class="sidenav-v4__sub-link" href="#0">All Users</a> - </li> - - <li> - <a class="sidenav-v4__sub-link" href="#0">+ New User</a> - </li> - </ul> - </li> - - <li class="sidenav-v4__item"> - <a class="sidenav-v4__link js-sidenav-v4__link" href="#0"> - <svg class="sidenav-v4__icon icon" viewBox="0 0 20 20"> - <g fill="currentColor"> - <path d="M11 16l-1.55 1.55a4.95 4.95 0 0 1-7 0 4.95 4.95 0 0 1 0-7l2.192-2.192a4.95 4.95 0 0 1 7 0A4.907 4.907 0 0 1 12.731 10" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path> - <path d="M9 4l1.55-1.55a4.95 4.95 0 0 1 7 0 4.95 4.95 0 0 1 0 7l-2.192 2.192a4.95 4.95 0 0 1-7 0A4.907 4.907 0 0 1 7.269 10" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path> - </g> - </svg> - - <span>Link</span> - </a> - </li> - </ul> - </nav> - </div> - </div> - </aside> - - <main class="position-relative z-index-1 flex-grow height-100vh sidebar-loaded:show"> - <!-- start main content --> - <div class="text-component padding-md"> - <p>Main content.</p> - </div> - <!-- end main content --> - </main> -</div>
\ No newline at end of file diff --git a/old-apps/projects/src/app/pages/nav/index.ts b/old-apps/projects/src/app/pages/nav/index.ts deleted file mode 100644 index ca91c20..0000000 --- a/old-apps/projects/src/app/pages/nav/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import NavWrapper from "./nav-wrapper.svelte"; -import NavItem from "./nav-item.svelte"; -export { - NavWrapper, - NavItem -}
\ No newline at end of file diff --git a/old-apps/projects/src/app/pages/nav/js/_1_diagonal-movement.js b/old-apps/projects/src/app/pages/nav/js/_1_diagonal-movement.js deleted file mode 100644 index ed4a47d..0000000 --- a/old-apps/projects/src/app/pages/nav/js/_1_diagonal-movement.js +++ /dev/null @@ -1,296 +0,0 @@ -// File#: _1_diagonal-movement -// Usage: codyhouse.co/license -/* - Modified version of the jQuery-menu-aim plugin - https://github.com/kamens/jQuery-menu-aim - - Replaced jQuery with Vanilla JS - - Minor changes -*/ -(function() { - var menuAim = function(opts) { - init(opts); - }; - - window.menuAim = menuAim; - - function init(opts) { - var activeRow = null, - mouseLocs = [], - lastDelayLoc = null, - timeoutId = null, - options = Util.extend({ - menu: '', - rows: false, //if false, get direct children - otherwise pass nodes list - submenuSelector: "*", - submenuDirection: "right", - tolerance: 75, // bigger = more forgivey when entering submenu - enter: function(){}, - exit: function(){}, - activate: function(){}, - deactivate: function(){}, - exitMenu: function(){} - }, opts), - menu = options.menu; - - var MOUSE_LOCS_TRACKED = 3, // number of past mouse locations to track - DELAY = 300; // ms delay when user appears to be entering submenu - - /** - * Keep track of the last few locations of the mouse. - */ - var mouseMoveFallback = function(event) { - (!window.requestAnimationFrame) ? mousemoveDocument(event) : window.requestAnimationFrame(function(){mousemoveDocument(event);}); - }; - - var mousemoveDocument = function(e) { - mouseLocs.push({x: e.pageX, y: e.pageY}); - - if (mouseLocs.length > MOUSE_LOCS_TRACKED) { - mouseLocs.shift(); - } - }; - - /** - * Cancel possible row activations when leaving the menu entirely - */ - var mouseleaveMenu = function() { - if (timeoutId) { - clearTimeout(timeoutId); - } - - // If exitMenu is supplied and returns true, deactivate the - // currently active row on menu exit. - if (options.exitMenu(this)) { - if (activeRow) { - options.deactivate(activeRow); - } - - activeRow = null; - } - }; - - /** - * Trigger a possible row activation whenever entering a new row. - */ - var mouseenterRow = function() { - if (timeoutId) { - // Cancel any previous activation delays - clearTimeout(timeoutId); - } - - options.enter(this); - possiblyActivate(this); - }, - mouseleaveRow = function() { - options.exit(this); - }; - - /* - * Immediately activate a row if the user clicks on it. - */ - var clickRow = function() { - activate(this); - }; - - /** - * Activate a menu row. - */ - var activate = function(row) { - if (row == activeRow) { - return; - } - - if (activeRow) { - options.deactivate(activeRow); - } - - options.activate(row); - activeRow = row; - }; - - /** - * Possibly activate a menu row. If mouse movement indicates that we - * shouldn't activate yet because user may be trying to enter - * a submenu's content, then delay and check again later. - */ - var possiblyActivate = function(row) { - var delay = activationDelay(); - - if (delay) { - timeoutId = setTimeout(function() { - possiblyActivate(row); - }, delay); - } else { - activate(row); - } - }; - - /** - * Return the amount of time that should be used as a delay before the - * currently hovered row is activated. - * - * Returns 0 if the activation should happen immediately. Otherwise, - * returns the number of milliseconds that should be delayed before - * checking again to see if the row should be activated. - */ - var activationDelay = function() { - if (!activeRow || !Util.is(activeRow, options.submenuSelector)) { - // If there is no other submenu row already active, then - // go ahead and activate immediately. - return 0; - } - - function getOffset(element) { - var rect = element.getBoundingClientRect(); - return { top: rect.top + window.pageYOffset, left: rect.left + window.pageXOffset }; - }; - - var offset = getOffset(menu), - upperLeft = { - x: offset.left, - y: offset.top - options.tolerance - }, - upperRight = { - x: offset.left + menu.offsetWidth, - y: upperLeft.y - }, - lowerLeft = { - x: offset.left, - y: offset.top + menu.offsetHeight + options.tolerance - }, - lowerRight = { - x: offset.left + menu.offsetWidth, - y: lowerLeft.y - }, - loc = mouseLocs[mouseLocs.length - 1], - prevLoc = mouseLocs[0]; - - if (!loc) { - return 0; - } - - if (!prevLoc) { - prevLoc = loc; - } - - if (prevLoc.x < offset.left || prevLoc.x > lowerRight.x || prevLoc.y < offset.top || prevLoc.y > lowerRight.y) { - // If the previous mouse location was outside of the entire - // menu's bounds, immediately activate. - return 0; - } - - if (lastDelayLoc && loc.x == lastDelayLoc.x && loc.y == lastDelayLoc.y) { - // If the mouse hasn't moved since the last time we checked - // for activation status, immediately activate. - return 0; - } - - // Detect if the user is moving towards the currently activated - // submenu. - // - // If the mouse is heading relatively clearly towards - // the submenu's content, we should wait and give the user more - // time before activating a new row. If the mouse is heading - // elsewhere, we can immediately activate a new row. - // - // We detect this by calculating the slope formed between the - // current mouse location and the upper/lower right points of - // the menu. We do the same for the previous mouse location. - // If the current mouse location's slopes are - // increasing/decreasing appropriately compared to the - // previous's, we know the user is moving toward the submenu. - // - // Note that since the y-axis increases as the cursor moves - // down the screen, we are looking for the slope between the - // cursor and the upper right corner to decrease over time, not - // increase (somewhat counterintuitively). - function slope(a, b) { - return (b.y - a.y) / (b.x - a.x); - }; - - var decreasingCorner = upperRight, - increasingCorner = lowerRight; - - // Our expectations for decreasing or increasing slope values - // depends on which direction the submenu opens relative to the - // main menu. By default, if the menu opens on the right, we - // expect the slope between the cursor and the upper right - // corner to decrease over time, as explained above. If the - // submenu opens in a different direction, we change our slope - // expectations. - if (options.submenuDirection == "left") { - decreasingCorner = lowerLeft; - increasingCorner = upperLeft; - } else if (options.submenuDirection == "below") { - decreasingCorner = lowerRight; - increasingCorner = lowerLeft; - } else if (options.submenuDirection == "above") { - decreasingCorner = upperLeft; - increasingCorner = upperRight; - } - - var decreasingSlope = slope(loc, decreasingCorner), - increasingSlope = slope(loc, increasingCorner), - prevDecreasingSlope = slope(prevLoc, decreasingCorner), - prevIncreasingSlope = slope(prevLoc, increasingCorner); - - if (decreasingSlope < prevDecreasingSlope && increasingSlope > prevIncreasingSlope) { - // Mouse is moving from previous location towards the - // currently activated submenu. Delay before activating a - // new menu row, because user may be moving into submenu. - lastDelayLoc = loc; - return DELAY; - } - - lastDelayLoc = null; - return 0; - }; - - var reset = function(triggerDeactivate) { - if (timeoutId) { - clearTimeout(timeoutId); - } - - if (activeRow && triggerDeactivate) { - options.deactivate(activeRow); - } - - activeRow = null; - }; - - var destroyInstance = function() { - menu.removeEventListener('mouseleave', mouseleaveMenu); - document.removeEventListener('mousemove', mouseMoveFallback); - if(rows.length > 0) { - for(var i = 0; i < rows.length; i++) { - rows[i].removeEventListener('mouseenter', mouseenterRow); - rows[i].removeEventListener('mouseleave', mouseleaveRow); - rows[i].removeEventListener('click', clickRow); - } - } - - }; - - /** - * Hook up initial menu events - */ - menu.addEventListener('mouseleave', mouseleaveMenu); - var rows = (options.rows) ? options.rows : menu.children; - if(rows.length > 0) { - for(var i = 0; i < rows.length; i++) {(function(i){ - rows[i].addEventListener('mouseenter', mouseenterRow); - rows[i].addEventListener('mouseleave', mouseleaveRow); - rows[i].addEventListener('click', clickRow); - })(i);} - } - - document.addEventListener('mousemove', mouseMoveFallback); - - /* Reset/destroy menu */ - menu.addEventListener('reset', function(event){ - reset(event.detail); - }); - menu.addEventListener('destroy', destroyInstance); - }; -}()); - diff --git a/old-apps/projects/src/app/pages/nav/js/_1_responsive-sidebar.js b/old-apps/projects/src/app/pages/nav/js/_1_responsive-sidebar.js deleted file mode 100644 index f9599d8..0000000 --- a/old-apps/projects/src/app/pages/nav/js/_1_responsive-sidebar.js +++ /dev/null @@ -1,215 +0,0 @@ -// File#: _1_responsive-sidebar -// Usage: codyhouse.co/license -(function() { - var Sidebar = function(element) { - this.element = element; - this.triggers = document.querySelectorAll('[aria-controls="'+this.element.getAttribute('id')+'"]'); - this.firstFocusable = null; - this.lastFocusable = null; - this.selectedTrigger = null; - this.showClass = "sidebar--is-visible"; - this.staticClass = "sidebar--static"; - this.customStaticClass = ""; - this.readyClass = "sidebar--loaded"; - this.contentReadyClass = "sidebar-loaded:show"; - this.layout = false; // this will be static or mobile - this.preventScrollEl = getPreventScrollEl(this); - getCustomStaticClass(this); // custom classes for static version - initSidebar(this); - }; - - function getPreventScrollEl(element) { - var scrollEl = false; - var querySelector = element.element.getAttribute('data-sidebar-prevent-scroll'); - if(querySelector) scrollEl = document.querySelector(querySelector); - return scrollEl; - }; - - function getCustomStaticClass(element) { - var customClasses = element.element.getAttribute('data-static-class'); - if(customClasses) element.customStaticClass = ' '+customClasses; - }; - - function initSidebar(sidebar) { - initSidebarResize(sidebar); // handle changes in layout -> mobile to static and viceversa - - if ( sidebar.triggers ) { // open sidebar when clicking on trigger buttons - mobile layout only - for(var i = 0; i < sidebar.triggers.length; i++) { - sidebar.triggers[i].addEventListener('click', function(event) { - event.preventDefault(); - toggleSidebar(sidebar, event.target); - }); - } - } - - // use the 'openSidebar' event to trigger the sidebar - sidebar.element.addEventListener('openSidebar', function(event) { - toggleSidebar(sidebar, event.detail); - }); - }; - - function toggleSidebar(sidebar, target) { - if(Util.hasClass(sidebar.element, sidebar.showClass)) { - sidebar.selectedTrigger = target; - closeSidebar(sidebar); - return; - } - sidebar.selectedTrigger = target; - showSidebar(sidebar); - initSidebarEvents(sidebar); - }; - - function showSidebar(sidebar) { // mobile layout only - Util.addClass(sidebar.element, sidebar.showClass); - getFocusableElements(sidebar); - Util.moveFocus(sidebar.element); - // change the overflow of the preventScrollEl - if(sidebar.preventScrollEl) sidebar.preventScrollEl.style.overflow = 'hidden'; - }; - - function closeSidebar(sidebar) { // mobile layout only - Util.removeClass(sidebar.element, sidebar.showClass); - sidebar.firstFocusable = null; - sidebar.lastFocusable = null; - if(sidebar.selectedTrigger) sidebar.selectedTrigger.focus(); - sidebar.element.removeAttribute('tabindex'); - //remove listeners - cancelSidebarEvents(sidebar); - // change the overflow of the preventScrollEl - if(sidebar.preventScrollEl) sidebar.preventScrollEl.style.overflow = ''; - }; - - function initSidebarEvents(sidebar) { // mobile layout only - //add event listeners - sidebar.element.addEventListener('keydown', handleEvent.bind(sidebar)); - sidebar.element.addEventListener('click', handleEvent.bind(sidebar)); - }; - - function cancelSidebarEvents(sidebar) { // mobile layout only - //remove event listeners - sidebar.element.removeEventListener('keydown', handleEvent.bind(sidebar)); - sidebar.element.removeEventListener('click', handleEvent.bind(sidebar)); - }; - - function handleEvent(event) { // mobile layout only - switch(event.type) { - case 'click': { - initClick(this, event); - } - case 'keydown': { - initKeyDown(this, event); - } - } - }; - - function initKeyDown(sidebar, event) { // mobile layout only - if( event.keyCode && event.keyCode == 27 || event.key && event.key == 'Escape' ) { - //close sidebar window on esc - closeSidebar(sidebar); - } else if( event.keyCode && event.keyCode == 9 || event.key && event.key == 'Tab' ) { - //trap focus inside sidebar - trapFocus(sidebar, event); - } - }; - - function initClick(sidebar, event) { // mobile layout only - //close sidebar when clicking on close button or sidebar bg layer - if( !event.target.closest('.js-sidebar__close-btn') && !Util.hasClass(event.target, 'js-sidebar') ) return; - event.preventDefault(); - closeSidebar(sidebar); - }; - - function trapFocus(sidebar, event) { // mobile layout only - if( sidebar.firstFocusable == document.activeElement && event.shiftKey) { - //on Shift+Tab -> focus last focusable element when focus moves out of sidebar - event.preventDefault(); - sidebar.lastFocusable.focus(); - } - if( sidebar.lastFocusable == document.activeElement && !event.shiftKey) { - //on Tab -> focus first focusable element when focus moves out of sidebar - event.preventDefault(); - sidebar.firstFocusable.focus(); - } - }; - - function initSidebarResize(sidebar) { - // custom event emitted when window is resized - detect only if the sidebar--static@{breakpoint} class was added - var beforeContent = getComputedStyle(sidebar.element, ':before').getPropertyValue('content'); - if(beforeContent && beforeContent !='' && beforeContent !='none') { - checkSidebarLayout(sidebar); - - sidebar.element.addEventListener('update-sidebar', function(event){ - checkSidebarLayout(sidebar); - }); - } - // check if there a main element to show - var mainContent = document.getElementsByClassName(sidebar.contentReadyClass); - if(mainContent.length > 0) Util.removeClass(mainContent[0], sidebar.contentReadyClass); - Util.addClass(sidebar.element, sidebar.readyClass); - }; - - function checkSidebarLayout(sidebar) { - var layout = getComputedStyle(sidebar.element, ':before').getPropertyValue('content').replace(/\'|"/g, ''); - if(layout == sidebar.layout) return; - sidebar.layout = layout; - if(layout != 'static') Util.addClass(sidebar.element, 'is-hidden'); - Util.toggleClass(sidebar.element, sidebar.staticClass + sidebar.customStaticClass, layout == 'static'); - if(layout != 'static') setTimeout(function(){Util.removeClass(sidebar.element, 'is-hidden')}); - // reset element role - (layout == 'static') ? sidebar.element.removeAttribute('role', 'alertdialog') : sidebar.element.setAttribute('role', 'alertdialog'); - // reset mobile behaviour - if(layout == 'static' && Util.hasClass(sidebar.element, sidebar.showClass)) closeSidebar(sidebar); - }; - - function getFocusableElements(sidebar) { - //get all focusable elements inside the drawer - var allFocusable = sidebar.element.querySelectorAll('[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex]:not([tabindex="-1"]), [contenteditable], audio[controls], video[controls], summary'); - getFirstVisible(sidebar, allFocusable); - getLastVisible(sidebar, allFocusable); - }; - - function getFirstVisible(sidebar, elements) { - //get first visible focusable element inside the sidebar - for(var i = 0; i < elements.length; i++) { - if( elements[i].offsetWidth || elements[i].offsetHeight || elements[i].getClientRects().length ) { - sidebar.firstFocusable = elements[i]; - return true; - } - } - }; - - function getLastVisible(sidebar, elements) { - //get last visible focusable element inside the sidebar - for(var i = elements.length - 1; i >= 0; i--) { - if( elements[i].offsetWidth || elements[i].offsetHeight || elements[i].getClientRects().length ) { - sidebar.lastFocusable = elements[i]; - return true; - } - } - }; - - window.Sidebar = Sidebar; - - //initialize the Sidebar objects - var sidebar = document.getElementsByClassName('js-sidebar'); - if( sidebar.length > 0 ) { - for( var i = 0; i < sidebar.length; i++) { - (function(i){new Sidebar(sidebar[i]);})(i); - } - // switch from mobile to static layout - var customEvent = new CustomEvent('update-sidebar'); - window.addEventListener('resize', function(event){ - (!window.requestAnimationFrame) ? setTimeout(function(){resetLayout();}, 250) : window.requestAnimationFrame(resetLayout); - }); - - (window.requestAnimationFrame) // init sidebar layout - ? window.requestAnimationFrame(resetLayout) - : resetLayout(); - - function resetLayout() { - for( var i = 0; i < sidebar.length; i++) { - (function(i){sidebar[i].dispatchEvent(customEvent)})(i); - }; - }; - } -}());
\ No newline at end of file diff --git a/old-apps/projects/src/app/pages/nav/js/_2_side-navigation-v4.js b/old-apps/projects/src/app/pages/nav/js/_2_side-navigation-v4.js deleted file mode 100644 index 63ef9c4..0000000 --- a/old-apps/projects/src/app/pages/nav/js/_2_side-navigation-v4.js +++ /dev/null @@ -1,73 +0,0 @@ -// File#: _2_side-navigation-v4 -// Usage: codyhouse.co/license -(function() { - function initSideNav(nav) { - // create btns - visible on mobile only - createBtns(nav); - // toggle sublists on mobile when clicking on buttons - toggleSubLists(nav); - // init diagonal movement - initDiagonalMove(nav); - }; - - function createBtns(nav) { - // on mobile -> create a <button> element for each link with a submenu - var expandableLinks = nav.getElementsByClassName('js-sidenav-v4__link'); - for(var i = 0; i < expandableLinks.length; i++) { - createSingleBtn(expandableLinks[i]); - } - }; - - function createSingleBtn(link) { - if(!hasSubList(link)) return; - // create btn and insert it into the DOM - var btnClasses = link.getAttribute('class').replace('js-sidenav-v4__link', 'js-sidenav-v4__btn'); - btnClasses = btnClasses +' sidenav-v4__link--btn'; - var btnHtml = '<button class="reset '+btnClasses+'">'+link.innerHTML+'</button>'; - link.insertAdjacentHTML('afterend', btnHtml); - // add class to link element - Util.addClass(link, 'sidenav-v4__link--href'); - // check if we need to add the collpsed class to the <li> element - var listItem = link.parentElement; - if(!Util.hasClass(listItem, 'sidenav-v4__item--current')) Util.addClass(listItem, 'sidenav-v4__item--collapsed'); - }; - - function hasSubList(link) { - // check if link has submenu - var sublist = link.nextElementSibling; - if(!sublist) return false; - return Util.hasClass(sublist, 'sidenav-v4__sub-list'); - }; - - function toggleSubLists(nav) { - // open/close sublist on mobile - nav.addEventListener('click', function(event){ - var btn = event.target.closest('.js-sidenav-v4__btn'); - if(!btn) return; - Util.toggleClass(btn.parentElement, 'sidenav-v4__item--collapsed', !Util.hasClass(btn.parentElement, 'sidenav-v4__item--collapsed')); - }); - }; - - function initDiagonalMove(nav) { - // improve dropdown navigation - new menuAim({ - menu: nav.querySelector('ul'), - activate: function(row) { - Util.addClass(row, 'sidenav-v4__item--hover'); - }, - deactivate: function(row) { - Util.removeClass(row, 'sidenav-v4__item--hover'); - }, - exitMenu: function() { - return true; - }, - }); - }; - - var sideNavs = document.getElementsByClassName('js-sidenav-v4'); - if( sideNavs.length > 0 ) { - for( var i = 0; i < sideNavs.length; i++) { - (function(i){initSideNav(sideNavs[i]);})(i); - } - } -}());
\ No newline at end of file diff --git a/old-apps/projects/src/app/pages/nav/nav-item.svelte b/old-apps/projects/src/app/pages/nav/nav-item.svelte deleted file mode 100644 index 335cbbb..0000000 --- a/old-apps/projects/src/app/pages/nav/nav-item.svelte +++ /dev/null @@ -1,18 +0,0 @@ -<script lang="ts"> - import {link} from "svelte-spa-router"; - import Icon from "$shared/components/icon.svelte"; - - export let external = ""; - export let to = ""; - export let text; - export let icon; -</script> - -<li class="sidenav-v4__item"> - <a class="sidenav-v4__link" href={to ?? external} use:link={external === ""}> - {#if icon} - <Icon class="sidenav-v4__icon icon" name="{icon}" /> - {/if} - <span>{text}</span> - </a> -</li> diff --git a/old-apps/projects/src/app/pages/nav/nav-wrapper.svelte b/old-apps/projects/src/app/pages/nav/nav-wrapper.svelte deleted file mode 100644 index 8321544..0000000 --- a/old-apps/projects/src/app/pages/nav/nav-wrapper.svelte +++ /dev/null @@ -1,20 +0,0 @@ -<script lang="ts"> - import {random_string} from "$shared/lib/helpers"; - - export let id = "nav__" + random_string(4); - const staticClasses = "position-relative z-index-2 bg width-100% max-width-xxxxs shadow-sm" -</script> -<div class="flex@md"> - <aside id="{id}" class="sidebar sidebar--static@md {staticClasses}"> - <div class="sidebar__panel"> - <div class="position-relative z-index-1"> - <nav class="sidenav-v4 padding-xs"> - <slot name="navigation-items"></slot> - </nav> - </div> - </div> - </aside> - <main class="container max-width-xl position-relative z-index-1 flex-grow min-height-100vh position-sticky@md top-0@md height-100vh@md overflow-auto@m"> - <slot name="main-content"></slot> - </main> -</div>
\ No newline at end of file diff --git a/old-apps/projects/src/app/pages/nav/scss/_1_responsive-sidebar.scss b/old-apps/projects/src/app/pages/nav/scss/_1_responsive-sidebar.scss deleted file mode 100644 index e4304f1..0000000 --- a/old-apps/projects/src/app/pages/nav/scss/_1_responsive-sidebar.scss +++ /dev/null @@ -1,147 +0,0 @@ -@use '../base' as *; - -/* -------------------------------- - -File#: _1_responsive-sidebar -Title: Responsive Sidebar -Descr: Responsive sidebar container -Usage: codyhouse.co/license - --------------------------------- */ - -/* mobile version only (--default) 👇 */ -.sidebar:not(.sidebar--static) { - position: fixed; - top: 0; - left: 0; - z-index: var(--z-index-fixed-element, 10); - width: 100%; - height: 100%; - visibility: hidden; - transition: visibility 0s 0.3s; - - &::after { /* overlay layer */ - content: ''; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: alpha(var(--color-black), 0); - transition: background-color .3s; - z-index: 1; - } - - .sidebar__panel { /* content */ - position: absolute; - top: 0; - left: 0; - z-index: 2; - width: 100%; - max-width: 380px; - height: 100%; - overflow: auto; - -webkit-overflow-scrolling: touch; - background-color: var(--color-bg); - transform: translateX(-100%); - transition: box-shadow 0.3s,transform 0.3s; - } - - &.sidebar--right-on-mobile { - .sidebar__panel { - left: auto; - right: 0; - transform: translateX(100%); - } - } - - &.sidebar--is-visible { - visibility: visible; - transition: none; - - &::after { - background-color: alpha(var(--color-black), 0.85); - } - - .sidebar__panel { - transform: translateX(0); - box-shadow: var(--shadow-md); - } - } -} -/* end mobile version */ - -.sidebar__header { - display: flex; - align-items: center; - justify-content: space-between; - position: sticky; - top: 0; -} - -.sidebar__close-btn { - --size: 32px; - width: var(--size); - height: var(--size); - display: flex; - border-radius: 50%; - background-color: var(--color-bg-light); - box-shadow: var(--inner-glow), var(--shadow-sm); - transition: .2s; - flex-shrink: 0; - - .icon { - display: block; - margin: auto; - } - - &:hover { - background-color: var(--color-bg-lighter); - box-shadow: var(--inner-glow), var(--shadow-md); - } -} - -/* desktop version only (--static) 👇 */ -.sidebar--static { - flex-shrink: 0; - flex-grow: 1; - - .sidebar__header { - display: none; - } -} - -.sidebar--sticky-on-desktop { - position: sticky; - top: var(--space-sm); - max-height: calc(100vh - var(--space-sm)); - overflow: auto; - -webkit-overflow-scrolling: touch; -} -/* end desktop version */ - -.sidebar, .sidebar-loaded\:show { - opacity: 0; /* hide sidebar - or other elements using the .sidebar-loaded:show class - while it is initialized in JS */ -} - -.sidebar--loaded { - opacity: 1; -} - -/* detect when the sidebar needs to switch from the mobile layout to a static one - used in JS */ -[class*="sidebar--static"]::before { - display: none; -} - -.sidebar--static::before { - content: 'static'; -} - -@each $breakpoint, $value in $breakpoints { - .sidebar--static\@#{$breakpoint}::before { - content: 'mobile'; - @include breakpoint(#{$breakpoint}) { - content: 'static'; - } - } -}
\ No newline at end of file diff --git a/old-apps/projects/src/app/pages/nav/scss/_2_side-navigation-v4.scss b/old-apps/projects/src/app/pages/nav/scss/_2_side-navigation-v4.scss deleted file mode 100644 index 2b421df..0000000 --- a/old-apps/projects/src/app/pages/nav/scss/_2_side-navigation-v4.scss +++ /dev/null @@ -1,237 +0,0 @@ -@use '../base' as *; -@use '_1_responsive-sidebar.scss' as *; - -/* -------------------------------- - -File#: _2_side-navigation-v4 -Title: Side Navigation v4 -Descr: Main, side navigation -Usage: codyhouse.co/license - --------------------------------- */ - -.sidenav-v4 { - --sidenav-v4-icon-size: 20px; - --sidenav-v4-icon-margin-right: var(--space-xxs); -} - -.sidenav-v4__item { - position: relative; -} - -.sidenav-v4__link, -.sidenav-v4__sub-link, -.sidenav-v4__separator { - padding: var(--space-sm); -} - -.sidenav-v4__link, .sidenav-v4__sub-link { - display: flex; - align-items: center; - - width: 100%; - border-radius: var(--radius-md); - - text-decoration: none; - color: inherit; - line-height: 1; - font-size: var(--text-md); - - transition: .2s; - - &:hover { - color: var(--color-primary); - background-color: alpha(var(--color-contrast-higher), 0.075); - } - - &[aria-current="page"] { - color: var(--color-primary); - } -} - -.sidenav-v4__sub-link { - position: relative; - color: var(--color-contrast-medium); - - /* dot indicator */ - &::before { - content: ''; - display: block; - --size: 6px; - width: var(--size); - height: var(--size); - background: currentColor; - border-radius: 50%; - margin-left: calc(var(--sidenav-v4-icon-size)/2 - var(--size)/2); - margin-right: calc(var(--sidenav-v4-icon-size)/2 - var(--size)/2 + var(--sidenav-v4-icon-margin-right)); - - opacity: 0; /* visible only if current */ - } - - &[aria-current="page"] { - &::before { /* show dot indicator */ - opacity: 1; - } - } -} - -.sidenav-v4__notification-marker { - margin-left: auto; - background-color: var(--color-accent); - border-radius: var(--radius-md); - - height: 16px; - line-height: 16px; - padding: 0 4px; - color: var(--color-white); - font-size: 12px; - - /* hide - visible only on desktop */ - display: none; -} - -/* label icon */ -.sidenav-v4__icon { - --size: var(--sidenav-v4-icon-size); - margin-right: var(--sidenav-v4-icon-margin-right); -} - -/* arrow icon - visible on mobile if item is expandable */ -.sidenav-v4__arrow-icon { - --size: 20px; - - .icon__group { - will-change: transform; - transform-origin: 50% 50%; - transform: rotate(-90deg); - transition: transform .3s var(--ease-out); - - > * { - transform-origin: 50% 50%; - stroke-dasharray: 20; - stroke-dashoffset: 0; - transform: translateY(0px); - transition: transform .3s, stroke-dashoffset .3s; - transition-timing-function: var(--ease-out); - } - - .sidenav-v4__item--collapsed & { - transform: rotate(0deg); - - > * { - transform: translateY(4px); - } - - > *:first-child { - stroke-dashoffset: 10.15; - } - - > *:last-child { - stroke-dashoffset: 10.15; - } - } - } - - /* hide icon for links - show only for buttons created in JS */ - .sidenav-v4__link--href & { - display: none; - } -} - -/* current item */ -.sidenav-v4__item--current { - .sidenav-v4__sub-list { - display: block; /* show sublist */ - } -} - -/* separator */ -.sidenav-v4__separator { - span { - display: block; - width: var(--sidenav-v4-icon-size); - height: 1px; - background-color: var(--color-contrast-lower); - } -} - -/* mobile only */ -@include breakpoint(md, "not all") { - .sidenav-v4__item--collapsed { - .sidenav-v4__sub-list { - display: none; - } - } - - .sidenav-v4__link--href { - display: none; /* hide link -> show button */ - } -} - -/* desktop */ -@include breakpoint(md) { - .sidenav-v4__sub-list { - display: none; - } - - .sidenav-v4__link, - .sidenav-v4__sub-link, - .sidenav-v4__separator { - padding: var(--space-xs); - } - - .sidenav-v4__link, - .sidenav-v4__sub-link { - font-size: var(--text-sm); - } - - .sidenav-v4__link--btn { - display: none; /* hide button -> show link */ - } - - /* tooltip */ - .sidenav-v4__item:not(.sidenav-v4__item--current) { - .sidenav-v4__sub-list { - width: 220px; - position: absolute; - z-index: var(--z-index-overlay); - left: 100%; - top: 0; - - background-color: var(--color-bg-light); - box-shadow: var(--inner-glow), var(--shadow-md); - border-radius: var(--radius-md); - - overflow: hidden; - } - - .sidenav-v4__sub-link { - border-radius: 0; - color: var(--color-contrast-high); - - &::before { - display: none; /* remove dot indicator */ - } - - &:hover { - color: var(--color-primary); - } - } - - &.sidenav-v4__item--hover, &:focus-within { - .sidenav-v4__sub-list { - display: block; - } - } - - &:hover .sidenav-v4__link { /* highlight main link if tooltip is visible */ - color: var(--color-primary); - background-color: alpha(var(--color-contrast-higher), 0.075); - } - } - - /* notification marker */ - .sidenav-v4__notification-marker { - display: block; - } -}
\ No newline at end of file diff --git a/old-apps/projects/src/app/pages/nav/side-navigation-v4.zip b/old-apps/projects/src/app/pages/nav/side-navigation-v4.zip Binary files differdeleted file mode 100644 index d034eaf..0000000 --- a/old-apps/projects/src/app/pages/nav/side-navigation-v4.zip +++ /dev/null diff --git a/old-apps/projects/src/app/pages/not-found.svelte b/old-apps/projects/src/app/pages/not-found.svelte deleted file mode 100644 index 8822e0e..0000000 --- a/old-apps/projects/src/app/pages/not-found.svelte +++ /dev/null @@ -1,25 +0,0 @@ -<script> - import LL from "$app/lib/i18n/i18n-svelte"; - import {link} from "svelte-spa-router"; -</script> - -<style> - header { - font-size: 12rem; - } - - main { - position: absolute; - left: 50%; - top: 50%; - transform: translate(-50%, -50%); - text-align: center; - } -</style> - -<main> - <header>404</header> - <p>{$LL.messages.pageNotFound()}</p> - <a use:link - href="/">{$LL.messages.goToFrontpage()}</a> -</main> diff --git a/old-apps/projects/src/app/pages/settings.svelte b/old-apps/projects/src/app/pages/settings.svelte deleted file mode 100644 index ca9fd47..0000000 --- a/old-apps/projects/src/app/pages/settings.svelte +++ /dev/null @@ -1,12 +0,0 @@ -<script> - import Layout from "./_layout.svelte"; - import CategoriesTile from "$app/pages/views/settings-categories-tile.svelte"; - import LabelsTile from "$app/pages/views/settings-labels-tile.svelte"; -</script> - -<Layout> - <section class="grid gap-md"> - <CategoriesTile/> - <LabelsTile/> - </section> -</Layout> diff --git a/old-apps/projects/src/app/pages/ui-workbench.svelte b/old-apps/projects/src/app/pages/ui-workbench.svelte deleted file mode 100644 index ff2b058..0000000 --- a/old-apps/projects/src/app/pages/ui-workbench.svelte +++ /dev/null @@ -1,7 +0,0 @@ -<script> - import {NavWrapper} from "./nav/index"; -</script> - -<NavWrapper> - -</NavWrapper>
\ No newline at end of file diff --git a/old-apps/projects/src/app/pages/views/category-form/index.svelte b/old-apps/projects/src/app/pages/views/category-form/index.svelte deleted file mode 100644 index 21024c3..0000000 --- a/old-apps/projects/src/app/pages/views/category-form/index.svelte +++ /dev/null @@ -1,144 +0,0 @@ -<script lang="ts"> - import Alert from "$shared/components/alert.svelte"; - import Dropdown from "$shared/components/dropdown.svelte"; - import labels, {create_label_async} from "$app/lib/stores/labels"; - import {generate_random_hex_color} from "$shared/lib/colors"; - import LL from "$app/lib/i18n/i18n-svelte"; - - let LabelsDropdown; - - const dough = { - error: "", - fields: { - name: { - value: "", - error: "", - validate() { - return false; - } - }, - color: { - value: "", - error: "", - validate() { - return true; - } - }, - labels: { - loading: false, - value: [], - error: "", - validate() { - return true; - }, - async create({name}) { - dough.fields.labels.loading = true; - const response = await create_label_async({ - name: name, - color: generate_random_hex_color(), - }); - dough.fields.labels.loading = false; - if (response.ok) { - // Small pause to allow loading state to update everywhere. - setTimeout(() => LabelsDropdown.select_entry(response.data.id), 50); - } - } - }, - archived: { - value: false, - error: "", - validate() { - return true; - } - } - }, - bake() { - // labels.filter((c) => Object.hasOwn(c, "selected") && c.selected === true); - return { - labels: dough.fields.labels.value, - name: dough.fields.name.value, - color: dough.fields.color.value, - }; - }, - submit(event) { - const bread = dough.bake(); - console.log(bread); - console.log("Submitted"); - } - }; - - const functions = { - set(values) { - functions.set_archived(values.archived); - functions.set_labels(values.labels); - functions.set_color(values.color); - functions.set_name(values.name); - }, - is_valid() { - let isValid = true; - if (!dough.fields.labels.validate()) isValid = false; - if (!dough.fields.color.validate()) isValid = false; - if (!dough.fields.name.validate()) isValid = false; - if (!dough.fields.archived.validate()) isValid = false; - return isValid; - }, - set_archived(value) { - dough.fields.archived.value = value; - }, - set_labels(value) { - dough.fields.labels.value = value; - }, - set_color(value) { - dough.fields.color.value = value; - }, - set_name(value) { - dough.fields.name.value = value; - }, - }; -</script> - -<form on:submit|preventDefault={dough.submit}> - <div class="margin-y-sm"> - <Alert visible={dough.error !== ""} - message={dough.error} - type="error"/> - </div> - <div class="grid gap-x-xs margin-bottom-sm"> - <div class="col-10"> - <label for="name" - class="form-label margin-bottom-xxs">{$LL.views.categoryForm.name()}</label> - <input type="text" - class="form-control width-100%" - id="name" - bind:value={dough.fields.name.value}/> - {#if dough.fields.name.error} - <small class="color-error">{dough.fields.name.error}</small> - {/if} - </div> - <div class="col-2"> - <label for="color" - class="form-label margin-bottom-xxs">{$LL.views.categoryForm.color()}</label> - <input type="color" - class="form-control width-100%" - id="color" - style="height: 41px" - bind:value={dough.fields.color.value}/> - {#if dough.fields.color.error} - <small class="color-error">{dough.fields.color.error}</small> - {/if} - </div> - </div> - <div class="margin-bottom-sm"> - <label for="labels" - class="form-label margin-bottom-xxs">{$LL.views.categoryForm.defaultLabels()}</label> - <Dropdown id="labels" - createable={true} - placeholder="{$LL.views.categoryForm.labelsPlaceholder()}" - entries={$labels} - multiple={true} - on_create_async={(name) => dough.fields.labels.create({name})}/> - {#if dough.fields.labels.error} - <small class="color-error">{dough.fields.labels.error}</small> - {/if} - </div> -</form> diff --git a/old-apps/projects/src/app/pages/views/data-table-paginator.svelte b/old-apps/projects/src/app/pages/views/data-table-paginator.svelte deleted file mode 100644 index b2649eb..0000000 --- a/old-apps/projects/src/app/pages/views/data-table-paginator.svelte +++ /dev/null @@ -1,101 +0,0 @@ -<script> - import LL from "$app/lib/i18n/i18n-svelte"; - import {createEventDispatcher, onMount} from "svelte"; - import {restrict_input_to_numbers} from "$shared/lib/helpers"; - - const dispatch = createEventDispatcher(); - export let page = 1; - export let pageCount = 1; - let prevCount = page; - let canIncrement = false; - let canDecrement = false; - $: canIncrement = page < pageCount; - $: canDecrement = page > 1; - - onMount(() => { - restrict_input_to_numbers(document.querySelector("#curr-page")); - }); - - function increment() { - if (canIncrement) { - page++; - } - } - - function decrement() { - if (canDecrement) { - page--; - } - } - - $: if (page) { - handle_change(); - } - - function handle_change() { - if (page === prevCount) { - return; - } - prevCount = page; - if (page > pageCount) { - page = pageCount; - } - dispatch("value_change", { - newValue: page, - }); - } -</script> - -<nav class="pagination" - aria-label="Pagination"> - <ul class="pagination__list flex flex-wrap gap-xxxs justify-center justify-end@md"> - <li> - <button on:click={decrement} - class="reset pagination__item {canDecrement ? '' : 'c-disabled'}"> - <svg class="icon icon--xs flip-x" - viewBox="0 0 16 16"> - <title>{$LL.views.dataTablePaginator.goToPrevPage()}</title> - <polyline - points="6 2 12 8 6 14" - fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - /> - </svg> - </button> - </li> - - <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} - /> - <em>{$LL.views.dataTablePaginator.of()} {pageCount}</em> - </span> - </li> - - <li> - <button on:click={increment} - class="reset pagination__item {canIncrement ? '' : 'c-disabled'}"> - <svg class="icon icon--xs" - viewBox="0 0 16 16"> - <title>{$LL.views.dataTablePaginator.goToNextPage()}</title> - <polyline - points="6 2 12 8 6 14" - fill="none" - stroke="currentColor" - stroke-linecap="round" - stroke-linejoin="round" - stroke-width="2" - /> - </svg> - </button> - </li> - </ul> -</nav> diff --git a/old-apps/projects/src/app/pages/views/entry-form/index.svelte b/old-apps/projects/src/app/pages/views/entry-form/index.svelte deleted file mode 100644 index e43d2a9..0000000 --- a/old-apps/projects/src/app/pages/views/entry-form/index.svelte +++ /dev/null @@ -1,199 +0,0 @@ -<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"; - import DateTimePart from "./sections/date-time.svelte"; - import LabelsPart from "./sections/labels.svelte"; - import CategoryPart from "./sections/category.svelte"; - import Button from "$shared/components/button.svelte"; - import {Textarea} from "$shared/components/form"; - import Alert from "$shared/components/alert.svelte"; - import {is_guid} from "$shared/lib/helpers"; - import {create_entry_async, edit_entry_async} from "$app/lib/stores/entries"; - - const dispatch = createEventDispatcher(); - - let formError = ""; - let formIsLoading = false; - let isSubmitting = false; - let description = ""; - let descriptionError = ""; - let dateTimePart; - let labelsPart; - let categoryPart; - let entryId; - - onMount(() => { - formIsLoading = true; - - Promise.all([categoryPart.load_categories(), labelsPart.load_labels()]).then(() => { - formIsLoading = false; - }); - - window.addEventListener("keydown", handle_window_keydown); - }); - - onDestroy(() => { - window.removeEventListener("keydown", handle_window_keydown); - }); - - function handle_window_keydown(event) { - if (event.ctrlKey && event.code === "Enter") { - submit_form(); - } - } - - function validate_form() { - return dateTimePart.is_valid() && categoryPart.is_valid() && description_is_valid(); - } - - function description_is_valid() { - if (!description) { - descriptionError = $LL.views.entryForm.errDescriptionReq(); - } else { - descriptionError = ""; - } - - return description; - } - - function get_payload() { - const response = {} as TimeEntryDto; - const values = get_values(); - if (!is_guid(values.id)) { - delete values.id; - } else { - response.id = values.id; - } - - const currentTimeZone = Temporal.Now.zonedDateTimeISO().offset; - response.start = values.date + "T" + values.fromTimeValue + currentTimeZone.toString(); - response.stop = values.date + "T" + values.toTimeValue + currentTimeZone.toString(); - - response.category = { - id: values.category.id, - }; - - const selectedLabels = values.labels; - if (selectedLabels?.length > 0 ?? false) { - response.labels = selectedLabels; - } - - const descriptionContent = description?.trim(); - if (descriptionContent?.length > 0 ?? false) { - response.description = descriptionContent; - } - - return response; - } - - async function submit_form() { - formError = ""; - if (validate_form()) { - const payload = get_payload() as TimeEntryDto; - isSubmitting = true; - if (is_guid(payload.id)) { - const response = await edit_entry_async(payload); - if (response.ok) { - functions.reset(); - dispatch("updated", response.data); - } else { - formError = $LL.views.entryForm.entryUpdateError(); - isSubmitting = false; - } - } else { - const response = await create_entry_async(payload); - if (response.ok) { - functions.reset(); - dispatch("created"); - } else { - formError = $LL.views.entryForm.entryCreateError(); - isSubmitting = false; - } - } - } - } - - function get_values() { - return { - id: entryId, - toTimeValue: dateTimePart.get_to_time_value(), - fromTimeValue: dateTimePart.get_from_time_value(), - date: dateTimePart.get_date(), - category: categoryPart.get_selected(), - labels: labelsPart.get_selected(), - description: description, - }; - } - - export const functions = { - set_values(values) { - entryId = values.id; - dateTimePart.set_values(values); - labelsPart.select_labels(values?.labels.map((c) => c.id) ?? []); - categoryPart.select_category(values?.category?.id); - description = values.description; - }, - set_time(value: {to: Temporal.PlainTime, from: Temporal.PlainTime}) { - dateTimePart.set_times(value); - }, - set_description(value: string) { - if (description) description = description + "\n\n" + value; - else description = value; - }, - reset() { - isSubmitting = false; - formIsLoading = false; - entryId = ""; - labelsPart.reset(); - categoryPart.reset(); - dateTimePart.reset(true); - description = ""; - formError = ""; - }, - }; -</script> - -<form on:submit|preventDefault={submit_form} - on:reset={() => functions.reset()}> - <div class="margin-y-sm"> - <Alert visible={formError !== ""} - message={formError} - type="error"/> - </div> - - <div class="margin-bottom-sm"> - <DateTimePart bind:functions={dateTimePart}/> - </div> - - <div class="margin-bottom-sm"> - <CategoryPart bind:functions={categoryPart}/> - </div> - - <div class="margin-bottom-sm"> - <LabelsPart bind:functions={labelsPart}/> - </div> - - <div class="margin-bottom-sm"> - <Textarea class="width-100%" - id="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="{$LL.views.entryForm.reset()}" - on:click={() => functions.reset()} - variant="subtle" - /> - {/if} - <Button loading={isSubmitting} - type="submit" - variant="primary" - text={entryId ? $LL.views.entryForm.save() : $LL.views.entryForm.create()} - /> - </div> -</form> diff --git a/old-apps/projects/src/app/pages/views/entry-form/sections/category.svelte b/old-apps/projects/src/app/pages/views/entry-form/sections/category.svelte deleted file mode 100644 index f7af382..0000000 --- a/old-apps/projects/src/app/pages/views/entry-form/sections/category.svelte +++ /dev/null @@ -1,76 +0,0 @@ -<script> - import {generate_random_hex_color} from "$shared/lib/colors"; - 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; - - let DropdownExports; - - function reset() { - DropdownExports.reset(); - categoriesError = ""; - console.log($LL.views.entryForm.category._logReset()); - } - - async function on_create({name}) { - loading = true; - const response = await create_category_async({ - name: name, - color: generate_random_hex_color(), - }); - loading = false; - if (response.ok) { - // Small pause to allow loading state to update everywhere. - setTimeout(() => select_category(response.data.id), 50); - } - } - - function get_selected() { - return $categories.find((c) => c.selected === true); - } - - function select_category(id) { - DropdownExports.select(id); - } - - function is_valid() { - let isValid = true; - const category = get_selected(); - if (!is_guid(category?.id)) { - categoriesError = $LL.views.entryForm.category.errisRequired(); - isValid = false; - move_focus(document.getElementById("category-dropdown")); - } else { - categoriesError = ""; - } - return isValid; - } - - export const functions = { - get_selected, - reset, - is_valid, - select_category, - load_categories: reload_categories, - }; -</script> - -<Dropdown - entries={$categories} - label="{$LL.views.entryForm.category.category()}" - maxlength="50" - createable={true} - placeholder="{$LL.views.entryForm.category.placeholder()}" - id="category-dropdown" - loading={loading} - name="category-dropdown" - on_create_async={on_create} - noResultsText="{$LL.views.entryForm.category.noResults()}" - errorText="{categoriesError}" - bind:this={DropdownExports} -/> - diff --git a/old-apps/projects/src/app/pages/views/entry-form/sections/date-time.svelte b/old-apps/projects/src/app/pages/views/entry-form/sections/date-time.svelte deleted file mode 100644 index b91f1a4..0000000 --- a/old-apps/projects/src/app/pages/views/entry-form/sections/date-time.svelte +++ /dev/null @@ -1,167 +0,0 @@ -<script lang="ts"> - import LL from "$app/lib/i18n/i18n-svelte"; - import { Temporal } from "@js-temporal/polyfill"; - - // TIME - let fromTimeValue = ""; - let fromTimeError = ""; - let toTimeValue = ""; - let toTimeError = ""; - - 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 = ""; - } - } - - // DATE - let date = Temporal.Now.plainDateTimeISO().toString().substring(0, 10); - let 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 = $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(); - - isValid = false; - if (!focusIsSet) { - document.getElementById("from")?.focus(); - focusIsSet = true; - } - } else { - fromTimeError = ""; - } - - 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 = ""; - } - - 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) { - fromTimeValue = value.from.toPlainTime().toString().substring(0, 5); - toTimeValue = value.to.toPlainTime().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">{$LL.views.entryForm.dateTime.date()}</label> - <input type="date" - id="date" - class="form-control width-100%" - bind:value={date}> - {#if dateError} - <small class="color-error">{dateError}</small> - {/if} - </div> - <div class="col-4"> - <label for="from" - 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]" - type="time" - bind:value={fromTimeValue} - on:input={handle_from_time_changed} - /> - {#if fromTimeError} - <small class="color-error">{fromTimeError}</small> - {/if} - </div> - <div class="col-4"> - <label for="to" - 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]" - type="time" - bind:value={toTimeValue} - on:input={handle_to_time_changed} - /> - {#if toTimeError} - <small class="color-error">{toTimeError}</small> - {/if} - </div> -</div> diff --git a/old-apps/projects/src/app/pages/views/entry-form/sections/labels.svelte b/old-apps/projects/src/app/pages/views/entry-form/sections/labels.svelte deleted file mode 100644 index a6f324b..0000000 --- a/old-apps/projects/src/app/pages/views/entry-form/sections/labels.svelte +++ /dev/null @@ -1,66 +0,0 @@ -<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"; - - let labelsError = ""; - let loading = false; - let DropdownExports; - - function reset() { - DropdownExports.reset(); - console.log($LL.views.entryForm.labels._logReset()); - } - - function get_selected() { - return $labels.filter((c) => Object.hasOwn(c, "selected") && c.selected === true); - } - - function select_label(id) { - DropdownExports.select(id); - } - - function select_labels(ids) { - for (const id of ids) { - DropdownExports.select(id); - } - } - - async function on_create({name}) { - loading = true; - const response = await create_label_async({ - name: name, - color: generate_random_hex_color(), - }); - loading = false; - if (response.ok) { - // Small pause to allow loading state to update everywhere. - setTimeout(() => select_label(response.data.id), 50); - } - } - - export const functions = { - get_selected, - reset, - load_labels: reload_labels, - select_labels, - select_label, - }; -</script> - -<Dropdown - entries={$labels} - label="{$LL.views.entryForm.labels.labels()}" - maxlength="50" - createable={true} - placeholder="{$LL.views.entryForm.labels.placeholder()}" - multiple="{true}" - id="labels-search" - name="labels-search" - on_create_async={on_create} - noResultsText="{$LL.views.entryForm.labels.placeholder()}" - errorText="{labelsError}" - bind:this={DropdownExports} - {loading} -/> diff --git a/old-apps/projects/src/app/pages/views/profile-modal.svelte b/old-apps/projects/src/app/pages/views/profile-modal.svelte deleted file mode 100644 index 7560175..0000000 --- a/old-apps/projects/src/app/pages/views/profile-modal.svelte +++ /dev/null @@ -1,156 +0,0 @@ -<script> - import {update_profile} from "$shared/lib/api/user"; - import Modal from "$shared/components/modal.svelte"; - import Alert from "$shared/components/alert.svelte"; - import Button from "$shared/components/button.svelte"; - import {is_email} from "$shared/lib/helpers"; - import {api_base} from "$shared/lib/configuration"; - import {get_session_data} from "$shared/lib/session"; - - const archiveLink = api_base("_/api/account/archive"); - - let modal; - let understands = false; - - let formIsLoading = false; - let formError; - - let username = get_session_data()?.profile.username; - let usernameFieldMessage; - let usernameFieldMessageClass = "color-error"; - - let password; - let passwordFieldMessage; - let passwordFieldMessageClass = "color-error"; - - async function submit_form(e) { - e.preventDefault(); - if (!username && !password) { - console.error("Not submitting becuase both values is empty"); - return; - } - - usernameFieldMessage = ""; - passwordFieldMessage = ""; - - if (username && !is_email(username)) { - usernameFieldMessage = "Username has to be a valid email"; - return; - } - - if (password && password?.length < 6) { - passwordFieldMessage = "The new password must contain at least 6 characters"; - return; - } - - formIsLoading = true; - - const response = await update_profile({ - username, - password, - }); - - formIsLoading = false; - - if (response.ok) { - if (password) { - passwordFieldMessage = "Successfully updated"; - passwordFieldMessageClass = "color-success"; - password = ""; - } - if (username) { - usernameFieldMessage = "Successfully updated"; - usernameFieldMessageClass = "color-success"; - password = ""; - } - } else { - formError = response.data.title ?? "An unknown error occured"; - } - } - - async function handle_delete_account_button_click() { - alert("Not implemented"); - return; - if (understands && confirm("Are you absolutely sure that you want to delete your account?")) { - } - } - - export const functions = { - open() { - modal.open(); - }, - close() { - // modal.close(); - }, - }; -</script> - -<Modal title="Profile" - bind:functions={modal}> - <section class="margin-bottom-md"> - <p class="text-md margin-bottom-sm">Update your information</p> - <form on:submit={submit_form} - autocomplete="new-password"> - {#if formError} - <small class="color-danger">{formError}</small> - {/if} - <div class="margin-bottom-sm"> - <label for="email" - class="form-label margin-bottom-xxs">New username</label> - <input type="email" - class="form-control width-100%" - id="email" - placeholder={username} - bind:value={username}/> - {#if usernameFieldMessage} - <small class={usernameFieldMessageClass}>{usernameFieldMessage}</small> - {/if} - </div> - <div class="margin-bottom-sm"> - <label for="password" - class="form-label margin-bottom-xxs">New password</label> - <input type="password" - class="form-control width-100%" - id="password" - bind:value={password}/> - {#if passwordFieldMessage} - <small class={passwordFieldMessageClass}>{passwordFieldMessage}</small> - {/if} - </div> - <div class="flex justify-end"> - <Button text="Save" - on:click={submit_form} - variant="primary" - loading={formIsLoading}/> - </div> - </form> - </section> - <section class="margin-bottom-md"> - <p class="text-md margin-bottom-sm">Download your data</p> - <a class="btn btn--subtle" - href={archiveLink} - download>Click here to download your data</a> - </section> - <section> - <p class="text-md margin-bottom-sm">Delete account</p> - <div class="margin-bottom-sm"> - <Alert - message="Deleting your account and data means that all of your data (entries, categories, etc.) will be unrecoverable forever.<br>You should probably download your data before continuing." - type="info" - /> - </div> - <div class="form-check margin-bottom-sm"> - <input type="checkbox" - class="checkbox" - id="the-consequences" - bind:checked={understands}/> - <label for="the-consequences">I understand the consequences of deleting my account and data.</label> - </div> - <div class="flex justify-end"> - <Button text="Delete everything" - variant="accent" - disabled={!understands} - on:click={handle_delete_account_button_click}/> - </div> - </section> -</Modal> diff --git a/old-apps/projects/src/app/pages/views/settings-categories-tile.svelte b/old-apps/projects/src/app/pages/views/settings-categories-tile.svelte deleted file mode 100644 index 8d2480f..0000000 --- a/old-apps/projects/src/app/pages/views/settings-categories-tile.svelte +++ /dev/null @@ -1,126 +0,0 @@ -<script> - import {IconNames} from "$shared/lib/configuration"; - import {onMount} from "svelte"; - import { - delete_time_category, - get_time_categories, - } from "$shared/lib/api/time-entry"; - 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 = []; - - $: active_categories = categories.filter(c => !c.archived); - $: archived_categories = categories.filter(c => c.archived); - - async function load_categories() { - is_loading = true; - const response = await get_time_categories(); - if (response.status === 200) { - categories = response.data; - } else if (response.status === 204) { - categories = []; - console.log("Empty response when getting time categories"); - } else { - categories = []; - console.error("Error when getting time categories"); - } - is_loading = false; - } - - async function handle_edit_category_click(event) { - } - - async function handle_delete_category_click(event) { - const row = event.target.closest("tr"); - if ( - row && - row.dataset.id && - confirm($LL.views.settingsCategoriesTile.deleteAllConfirm()) - ) { - const response = await delete_time_category(row.dataset.id); - if (response.ok) { - // svelte errors if we remove the row. - row.classList.add("d-none"); - } - } - } - - onMount(() => { - load_categories(); - }); -</script> - -<Tile class="col-6@md col-12 {is_loading ? 'c-disabled loading' : ''}"> - <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">{$LL.views.settingsCategoriesTile.active()} ({active_categories.length})</a></li> - <li><a class="s-tabs__link" - href="#0">{$LL.views.settingsCategoriesTile.archived()} ({archived_categories.length})</a></li> - </ul> - </nav> - {/if} - <div class="max-width-100% overflow-auto"> - <Table class="text-sm width-100%"> - <THead class="text-left"> - <TCell type="th" - thScope="col"> - {$LL.views.settingsCategoriesTile.name()} - </TCell> - <TCell type="th" - thScope="col"> - {$LL.views.settingsCategoriesTile.color()} - </TCell> - <TCell type="th" - thScope="col" - style="width:50px"></TCell> - </THead> - <TBody class="text-left"> - {#if categories.length > 0} - {#each categories as category} - <TRow class="text-nowrap" - data-id={category.id}> - <TCell> - {category.name} - </TCell> - <TCell> - <span style="border-left: 3px solid {category.color}; background-color:{category.color}25;"> - {category.color} - </span> - </TCell> - <TCell> - <Button icon="{IconNames.pencilSquare}" - variant="reset" - icon_width="1.2rem" - class="hide" - icon_height="1.2rem" - on:click={handle_edit_category_click} - 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="{$LL.views.settingsCategoriesTile.deleteEntry()}"/> - - </TCell> - </TRow> - {/each} - {:else} - <TRow> - <TCell type="th" - thScope="3"> - {$LL.views.settingsCategoriesTile.noCategories()} - </TCell> - </TRow> - {/if} - </TBody> - </Table> - </div> -</Tile> diff --git a/old-apps/projects/src/app/pages/views/settings-labels-tile.svelte b/old-apps/projects/src/app/pages/views/settings-labels-tile.svelte deleted file mode 100644 index 3d5a567..0000000 --- a/old-apps/projects/src/app/pages/views/settings-labels-tile.svelte +++ /dev/null @@ -1,111 +0,0 @@ -<script> - import {IconNames} from "$shared/lib/configuration"; - import {onMount} from "svelte"; - import labels, {reload_labels, delete_label_async} from "$app/lib/stores/labels"; - 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 isLoadingLabels = true; - - $: active_labels = $labels.filter(c => !c.archived); - $: archived_labels = $labels.filter(c => c.archived); - - async function load_labels() { - isLoadingLabels = true; - await reload_labels(); - isLoadingLabels = false; - } - - async function handle_edit_label_click(event) { - } - - async function handle_delete_label_click(event) { - const row = event.target.closest("tr"); - if ( - row && - row.dataset.id && - confirm($LL.views.settingsLabelsTile.deleteAllConfirm()) - ) { - await delete_label_async({id: row.dataset.id}); - row.classList.add("d-none"); - } - } - - onMount(() => { - load_labels(); - }); -</script> - -<Tile class="col-6@md col-12 {isLoadingLabels ? 'c-disabled loading' : ''}"> - <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">{$LL.views.settingsLabelsTile.active()} ({active_labels.length})</a></li> - <li><a class="s-tabs__link" - href="#0">{$LL.views.settingsLabelsTile.archived()} ({archived_labels.length})</a></li> - </ul> - </nav> - {/if} - <div class="max-width-100% overflow-auto"> - <Table class="text-sm width-100%"> - <THead class="text-left"> - <TCell type="th" - thScope="row"> - {$LL.views.settingsLabelsTile.name()} - </TCell> - <TCell type="th" - thScope="row"> - {$LL.views.settingsLabelsTile.color()} - </TCell> - <TCell type="th" - thScope="row" - style="width: 50px;"> - </TCell> - </THead> - <TBody class="text-left"> - {#if $labels.length > 0} - {#each $labels as label} - <TRow class="text-nowrap" - dataId={label.id}> - <TCell> - {label.name} - </TCell> - <TCell> - <span style="border-left: 3px solid {label.color}; background-color:{label.color}25;"> - {label.color} - </span> - </TCell> - <TCell> - <Button icon="{IconNames.pencilSquare}" - variant="reset" - icon_width="1.2rem" - class="hide" - icon_height="1.2rem" - on:click={handle_edit_label_click} - 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="{$LL.views.settingsLabelsTile.deleteEntry()}"/> - </TCell> - </TRow> - {/each} - {:else} - <TRow> - <TCell type="th" - thScope="row" - colspan="3"> - {$LL.views.settingsLabelsTile.noLabels()} - </TCell> - </TRow> - {/if} - </TBody> - </Table> - </div> -</Tile> |
