diff options
Diffstat (limited to 'apps')
| -rw-r--r-- | apps/frontpage/src/app.html | 15 | ||||
| -rw-r--r-- | apps/portal/src/app/index.scss | 1 | ||||
| -rw-r--r-- | apps/portal/src/app/pages/_layout.svelte | 84 | ||||
| -rw-r--r-- | apps/portal/src/app/pages/forgot.svelte | 2 | ||||
| -rw-r--r-- | apps/portal/src/app/pages/login.svelte | 2 | ||||
| -rw-r--r-- | apps/portal/src/app/pages/reset-password.svelte | 2 | ||||
| -rw-r--r-- | apps/portal/src/app/pages/sign-up.svelte | 7 | ||||
| -rw-r--r-- | apps/portal/src/index.html | 15 | ||||
| -rw-r--r-- | apps/projects/src/index.html | 11 | ||||
| -rw-r--r-- | apps/web-shared/src/components/theme-switcher.svelte | 425 | ||||
| -rw-r--r-- | apps/web-shared/src/lib/configuration.ts | 26 | ||||
| -rw-r--r-- | apps/web-shared/src/lib/helpers.ts | 26 | ||||
| -rw-r--r-- | apps/web-shared/src/styles/components/adv-custom-select.scss | 79 | ||||
| -rw-r--r-- | apps/web-shared/src/styles/components/light-dark-switch.scss | 102 |
14 files changed, 711 insertions, 86 deletions
diff --git a/apps/frontpage/src/app.html b/apps/frontpage/src/app.html index 917c2aa..5893110 100644 --- a/apps/frontpage/src/app.html +++ b/apps/frontpage/src/app.html @@ -4,6 +4,21 @@ <meta charset="utf-8"/> <link rel="icon" href="%sveltekit.assets%/favicon.png"/> + <script> + const value = `; ${document.cookie}`; + const parts = value.split(`; go_theme=`); + let currentTheme = "system"; + if (parts.length === 2) { + currentTheme = parts.pop().split(";").shift(); + } + if (currentTheme === "light") { + document.querySelector("html").dataset.theme = "light"; + } else if (currentTheme === "dark") { + document.querySelector("html").dataset.theme = "dark"; + } else { + document.querySelector("html").dataset.theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; + } + </script> <meta name="viewport" content="width=device-width, initial-scale=1"/> %sveltekit.head% diff --git a/apps/portal/src/app/index.scss b/apps/portal/src/app/index.scss index 56ac1c0..dd4ddb6 100644 --- a/apps/portal/src/app/index.scss +++ b/apps/portal/src/app/index.scss @@ -19,3 +19,4 @@ @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/light-dark-switch'; diff --git a/apps/portal/src/app/pages/_layout.svelte b/apps/portal/src/app/pages/_layout.svelte index ec4bd11..d5af444 100644 --- a/apps/portal/src/app/pages/_layout.svelte +++ b/apps/portal/src/app/pages/_layout.svelte @@ -1,7 +1,5 @@ <script> - import Button from "$shared/components/button.svelte"; - import Tile from "$shared/components/tile.svelte"; - import {switch_theme} from "$shared/lib/helpers"; + import ThemeSwitcher from "$shared/components/theme-switcher.svelte"; </script> <style> @@ -13,6 +11,7 @@ width: 100%; height: 100%; overflow: hidden; + z-index: 1; } #decoration svg { @@ -29,13 +28,13 @@ </style> <main class="container-fluid padding-x-xs padding-x-xxl@xs padding-y-md padding-y-lg@md max-width-sm"> - <slot/> - - <Tile class="margin-top-sm"> - <Button on:click={() => switch_theme()} - text="Switch theme" - variant="secondary"/> - </Tile> + <div class="z-index-2 position-relative"> + <slot/> + </div> + + <div class="position-fixed right-0 top-0 margin-md z-index-2"> + <ThemeSwitcher/> + </div> <figure id="decoration" aria-hidden="true"> @@ -43,55 +42,22 @@ viewBox="0 0 1920 450" fill="none"> <g stroke="currentColor" - stroke-width="2"> - <rect x="1286" - y="64" - width="128" - height="128"/> - <circle cx="1350" - cy="128" - r="64"/> - <path d="M1286 64L1414 192"/> - <circle cx="1478" - cy="128" - r="64"/> - <rect x="1414" - y="192" - width="128" - height="128"/> - <circle cx="1478" - cy="256" - r="64"/> - <path d="M1414 192L1542 320"/> - <circle cx="1606" - cy="256" - r="64"/> - <rect x="1542" - y="320" - width="128" - height="128"/> - <circle cx="1606" - cy="384" - r="64"/> - <path d="M1542 320L1670 448"/> - <rect x="1690" - y="192" - width="128" - height="128"/> - <circle cx="1754" - cy="256" - r="64"/> - <path d="M1690 192L1818 320"/> - <rect x="1542" - y="64" - width="128" - height="128"/> - <circle cx="1606" - cy="128" - r="64"/> - <path d="M1542 64L1670 192"/> - <circle cx="1478" - r="64"/> + stroke-width="2" + stroke-linejoin="round" + stroke-linecap="round"> + <path d="M1449 94.9993V3L1354 48.9995L1259 3V94.9993L1354 140.999L1449 94.9993Z"/> + <path d="M1639 94.9993V3L1544 48.9995L1449 3V94.9993L1544 140.999L1639 94.9993Z"/> + <path d="M1354 49.0002V141"/> + <path d="M1544 49.0002V141"/> + <path d="M1449 94.9995L1544 140.999L1449 186.999L1354 140.999L1449 94.9995Z"/> + <path d="M1544 141V232.999L1449 278.999L1354 232.999V141"/> + <path d="M1449 187V279"/> + <path d="M1544 264L1639 310L1544 355.999L1449 310L1544 264Z"/> + <path d="M1639 310V402L1544 447.999L1449 402V310"/> + <path d="M1544 356.001V448"/> + <path d="M1639 94.9995L1734 140.999L1639 186.999L1544 140.999L1639 94.9995Z"/> + <path d="M1734 141V232.999L1639 278.999L1544 232.999V141"/> + <path d="M1639 187V279"/> </g> </svg> </figure> diff --git a/apps/portal/src/app/pages/forgot.svelte b/apps/portal/src/app/pages/forgot.svelte index 5ffac23..156deab 100644 --- a/apps/portal/src/app/pages/forgot.svelte +++ b/apps/portal/src/app/pages/forgot.svelte @@ -71,7 +71,7 @@ class="max-width-xxs"> <fieldset> <legend class="form-legend"> - <span class="margin-bottom-xs">Send reset link</span> <br/> + <span class="margin-bottom-xs text-xl">Send reset link</span> <br/> <span class="text-sm">... or <a href="/login" use:link>log in</a></span> </legend> diff --git a/apps/portal/src/app/pages/login.svelte b/apps/portal/src/app/pages/login.svelte index df8d537..2822be0 100644 --- a/apps/portal/src/app/pages/login.svelte +++ b/apps/portal/src/app/pages/login.svelte @@ -98,7 +98,7 @@ class="max-width-xxs"> <fieldset> <legend class="form-legend"> - <span class="margin-bottom-xs">Log into your account</span> + <span class="margin-bottom-xs text-xl">Log into your account</span> <br/> <span class="text-sm">... or <a href="/signup" use:link>create a new one</a></span> diff --git a/apps/portal/src/app/pages/reset-password.svelte b/apps/portal/src/app/pages/reset-password.svelte index 1b26903..dabf5c9 100644 --- a/apps/portal/src/app/pages/reset-password.svelte +++ b/apps/portal/src/app/pages/reset-password.svelte @@ -84,7 +84,7 @@ {#if isActive === true} <fieldset> <legend class="form-legend"> - <span class="margin-bottom-xs">Set your new password</span> <br/> + <span class="margin-bottom-xs text-xl">Set your new password</span> <br/> <span class="text-sm"> ... or <a href="/login" diff --git a/apps/portal/src/app/pages/sign-up.svelte b/apps/portal/src/app/pages/sign-up.svelte index 31ae55b..509d33a 100644 --- a/apps/portal/src/app/pages/sign-up.svelte +++ b/apps/portal/src/app/pages/sign-up.svelte @@ -84,12 +84,15 @@ class="max-width-xxs"> <fieldset> <legend class="form-legend"> - <span class="margin-bottom-xs">Create your account</span> <br/> + <span class="margin-bottom-xs text-xl">Create your account</span> <br/> <span class="text-sm" >... or <a href="/login" use:link>log in</a></span > </legend> + <div class="margin-bottom-xs"> + <p>Provide an email and password to get immediate access to your new environment (30 days full access, no billing details required, no promotion emails).</p> + </div> <div class="margin-bottom-xxs max-width-xxs"> <Alert visible={signupForm.alert.isVisible} title={signupForm.alert.title} @@ -114,7 +117,7 @@ </div> <div class="flex justify-end"> <Button class="margin-bottom-xs" - text="Submit" + text="Create account" type="primary" loading={signupForm.loading} /> diff --git a/apps/portal/src/index.html b/apps/portal/src/index.html index 0bf1fb1..25de7d3 100644 --- a/apps/portal/src/index.html +++ b/apps/portal/src/index.html @@ -26,16 +26,23 @@ <link rel="icon" href="./_assets/pwa/favicon.svg"> <script> - const currentTheme = localStorage.getItem("theme"); + const value = `; ${document.cookie}`; + const parts = value.split(`; go_theme=`); + let currentTheme = "system"; + if (parts.length === 2) { + currentTheme = parts.pop().split(";").shift(); + } if (currentTheme === "light") { document.querySelector("html").dataset.theme = "light"; - } else { + } else if (currentTheme === "dark") { document.querySelector("html").dataset.theme = "dark"; + } else { + document.querySelector("html").dataset.theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; } </script> <link rel="stylesheet" href="./_assets/pre.css"> - <title>Projects - Greatoffice</title> + <title>Portal - Greatoffice</title> </head> <body> @@ -47,7 +54,7 @@ <div class="fill-loader fill-loader--v4" id="loader" role="alert"> - <p class="fill-loader__label">Loading Projects...</p> + <p class="fill-loader__label">Loading Portal...</p> <div aria-hidden="true"> <div class="fill-loader__base"></div> <div class="fill-loader__fill"></div> diff --git a/apps/projects/src/index.html b/apps/projects/src/index.html index 985b62b..22dba4f 100644 --- a/apps/projects/src/index.html +++ b/apps/projects/src/index.html @@ -26,11 +26,18 @@ <link rel="icon" href="./_assets/pwa/favicon.svg"> <script> - const currentTheme = localStorage.getItem("theme"); + const value = `; ${document.cookie}`; + const parts = value.split(`; go_theme=`); + let currentTheme = "system"; + if (parts.length === 2) { + currentTheme = parts.pop().split(";").shift(); + } if (currentTheme === "light") { document.querySelector("html").dataset.theme = "light"; - } else { + } else if (currentTheme === "dark") { document.querySelector("html").dataset.theme = "dark"; + } else { + document.querySelector("html").dataset.theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; } </script> <link rel="stylesheet" diff --git a/apps/web-shared/src/components/theme-switcher.svelte b/apps/web-shared/src/components/theme-switcher.svelte new file mode 100644 index 0000000..26ae507 --- /dev/null +++ b/apps/web-shared/src/components/theme-switcher.svelte @@ -0,0 +1,425 @@ +<script lang="ts"> + import {base_domain, CookieNames} from "$shared/lib/configuration"; + import {get_cookie, set_cookie} from "$shared/lib/helpers"; + import {onMount} from "svelte"; + + type theme = "system"|"dark"|"light"; + + export let show = false; + export let selection: theme = "system"; + export let size; + let prefers = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; + onMount(() => { + selection = get_cookie(CookieNames.theme) as theme; + document.addEventListener("keydown", (e) => { + if (e.code === "Esc") show = false; + }); + document.addEventListener("click", (e: any) => { + if (e.target.closest("[data-theme-switcher-element]") === null) show = false; + }); + }); + let html = document.querySelector("html"); + + window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (event) => { + prefers = event.matches ? "dark" : "light"; + }); + + $: switch (selection || prefers) { + case "system": + html.dataset.theme = prefers; + break; + case "light": + html.dataset.theme = "light"; + break; + case "dark": + html.dataset.theme = "dark"; + break; + } + + function change(to: theme) { + selection = to; + set_cookie(CookieNames.theme, selection, base_domain()); + } +</script> + +<div class="ld-switch" data-theme-switcher-element> + <button class="reset ld-switch-btn" + on:click={() => show =!show}> + <span class="sr-only">{selection}</span> + <div class="ld-switch-btn__icon-wrapper ld-switch-btn__icon-wrapper--in" + aria-hidden="true"> + {#if selection === "dark"} + <svg class="icon ld-switch-btn__icon" + viewBox="0 0 20 20"> + <title>dark</title> + <g fill="currentColor"> + <path d="M11.964 3.284c.021.237.036.474.036.716a8 8 0 0 1-8 8c-.242 0-.479-.015-.716-.036a7 7 0 1 0 8.68-8.68z" + fill-opacity=".2" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2"></path> + <path d="M7 4a.979.979 0 0 1-1-1 1 1 0 0 0-2 0 .979.979 0 0 1-1 1 1 1 0 0 0 0 2 .979.979 0 0 1 1 1 1 1 0 0 0 2 0 .979.979 0 0 1 1-1 1 1 0 0 0 0-2z"></path> + </g> + </svg> + {:else if selection === "light"} + <svg class="icon ld-switch-btn__icon" + viewBox="0 0 20 20"> + <title>light-auto</title> + <g fill="currentColor"> + <path d="M10 14a4 4 0 1 1 3.465-6" + fill-opacity=".2" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M12 18l2.5-7h1l2.5 7"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M12.714 16h4.572"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M10 1v1.5"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M16.364 3.636l-1.061 1.061"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M3.636 16.364l1.061-1.061"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M1 10h1.5"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M3.636 3.636l1.061 1.061"></path> + </g> + </svg> + {:else } + <svg class="icon ld-switch-btn__icon" + viewBox="0 0 20 20"> + <title>dark-auto</title> + <g fill="currentColor"> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M12 18l2.5-7h1l2.5 7"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M12.714 16h4.572"></path> + <path d="M12.146 10.159A2.5 2.5 0 0 1 14.5 8.5h1a2.5 2.5 0 0 1 1.412.441 7 7 0 0 0-4.948-5.657c.021.237.036.474.036.716a8 8 0 0 1-8 8c-.242 0-.479-.015-.716-.036a6.99 6.99 0 0 0 6.427 5.012z" + fill-opacity=".2"></path> + <path d="M16.71 8a7.015 7.015 0 0 0-4.746-4.716c.021.237.036.474.036.716a8 8 0 0 1-8 8c-.242 0-.479-.015-.716-.036A7.006 7.006 0 0 0 9 16.929" + fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2"></path> + <path d="M7 4a.979.979 0 0 1-1-1 1 1 0 0 0-2 0 .979.979 0 0 1-1 1 1 1 0 0 0 0 2 .979.979 0 0 1 1 1 1 1 0 0 0 2 0 .979.979 0 0 1 1-1 1 1 0 0 0 0-2z"></path> + </g> + </svg> + {/if} + </div> + + <div class="ld-switch-btn__icon-wrapper js-ld-switch-icon" + aria-hidden="true"> + {#if selection === "dark"} + <svg class="icon ld-switch-btn__icon" + viewBox="0 0 20 20"> + <title>dark</title> + <g fill="currentColor"> + <path d="M11.964 3.284c.021.237.036.474.036.716a8 8 0 0 1-8 8c-.242 0-.479-.015-.716-.036a7 7 0 1 0 8.68-8.68z" + fill-opacity=".2" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2"></path> + <path d="M7 4a.979.979 0 0 1-1-1 1 1 0 0 0-2 0 .979.979 0 0 1-1 1 1 1 0 0 0 0 2 .979.979 0 0 1 1 1 1 1 0 0 0 2 0 .979.979 0 0 1 1-1 1 1 0 0 0 0-2z"></path> + </g> + </svg> + {:else if selection === "light"} + <svg class="icon ld-switch-btn__icon" + viewBox="0 0 20 20"> + <title>light-auto</title> + <g fill="currentColor"> + <path d="M10 14a4 4 0 1 1 3.465-6" + fill-opacity=".2" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M12 18l2.5-7h1l2.5 7"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M12.714 16h4.572"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M10 1v1.5"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M16.364 3.636l-1.061 1.061"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M3.636 16.364l1.061-1.061"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M1 10h1.5"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M3.636 3.636l1.061 1.061"></path> + </g> + </svg> + {:else } + <svg class="icon ld-switch-btn__icon" + viewBox="0 0 20 20"> + <title>dark-auto</title> + <g fill="currentColor"> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M12 18l2.5-7h1l2.5 7"></path> + <path fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2" + d="M12.714 16h4.572"></path> + <path d="M12.146 10.159A2.5 2.5 0 0 1 14.5 8.5h1a2.5 2.5 0 0 1 1.412.441 7 7 0 0 0-4.948-5.657c.021.237.036.474.036.716a8 8 0 0 1-8 8c-.242 0-.479-.015-.716-.036a6.99 6.99 0 0 0 6.427 5.012z" + fill-opacity=".2"></path> + <path d="M16.71 8a7.015 7.015 0 0 0-4.746-4.716c.021.237.036.474.036.716a8 8 0 0 1-8 8c-.242 0-.479-.015-.716-.036A7.006 7.006 0 0 0 9 16.929" + fill="none" + stroke="currentColor" + stroke-linecap="round" + stroke-linejoin="round" + stroke-width="2"></path> + <path d="M7 4a.979.979 0 0 1-1-1 1 1 0 0 0-2 0 .979.979 0 0 1-1 1 1 1 0 0 0 0 2 .979.979 0 0 1 1 1 1 1 0 0 0 2 0 .979.979 0 0 1 1-1 1 1 0 0 0 0-2z"></path> + </g> + </svg> + {/if} + </div> + </button> +</div> + +<div class="bg-light position-fixed margin-top-xxs padding-x-xs padding-bottom-xs padding-top-xxxs radius-md inner-glow shadow-xs" + class:is-hidden={!show} + style="right: 15px" + data-theme-switcher-element + role="listbox"> + <div class="flex flex-wrap flex-column" + role="group"> + <div class="margin-bottom-xs flex-grow"> + <span class="text-xs color-contrast-medium">Appearance</span> + </div> + <div class="flex gap-xs flex-row"> + <div class="ld-switch-popover__option" + aria-selected="{selection === 'dark' ? 'true' : 'false'}" + role="option"> + <figure class="radius-md inner-glow" + on:click={() => change("dark")}> + <svg id="Layer_1" + class="block radius-inherit" + data-name="Layer 1" + xmlns="http://www.w3.org/2000/svg" + width="70" + height="50" + viewBox="0 0 70 50"> + <rect width="70" + height="50" + fill="#22232a"/> + <path d="M14,10H70V50H10V14A4,4,0,0,1,14,10Z" + fill="#5a5c63"/> + <circle cx="18" + cy="18" + r="3" + fill="#22232a"/> + <circle cx="27" + cy="18" + r="3" + fill="#22232a"/> + <circle cx="36" + cy="18" + r="3" + fill="#22232a"/> + <rect x="17" + y="28" + width="46" + height="3" + rx="1" + fill="#fafaff"/> + <rect x="17" + y="34" + width="46" + height="3" + rx="1" + fill="#fafaff"/> + <rect x="17" + y="40" + width="30" + height="3" + rx="1" + fill="#fafaff"/> + </svg> + </figure> + <div class="text-xs margin-top-xxxs padding-x-xxxxs">Dark</div> + </div> + <div class="ld-switch-popover__option" + aria-selected="{selection === 'light' ? 'true' : 'false'}" + role="option"> + <figure class="radius-md inner-glow" + on:click={() => change("light")}> + <svg id="Layer_1" + class="block radius-inherit" + data-name="Layer 1" + xmlns="http://www.w3.org/2000/svg" + width="70" + height="50" + viewBox="0 0 70 50"> + <rect width="70" + height="50" + fill="#e5e5e6"/> + <path d="M14,10H70a0,0,0,0,1,0,0V50a0,0,0,0,1,0,0H10a0,0,0,0,1,0,0V14A4,4,0,0,1,14,10Z" + fill="#fff"/> + <circle cx="18" + cy="18" + r="3" + fill="#e5e5e6"/> + <circle cx="27" + cy="18" + r="3" + fill="#e5e5e6"/> + <circle cx="36" + cy="18" + r="3" + fill="#e5e5e6"/> + <rect x="17" + y="28" + width="46" + height="3" + rx="1" + fill="#38393e"/> + <rect x="17" + y="34" + width="46" + height="3" + rx="1" + fill="#38393e"/> + <rect x="17" + y="40" + width="30" + height="3" + rx="1" + fill="#38393e"/> + </svg> + </figure> + <div class="text-xs margin-top-xxxs padding-x-xxxxs">Light</div> + </div> + <div class="ld-switch-popover__option" + aria-selected="{selection === 'system' ? 'true' : 'false'}" + role="option"> + <figure class="radius-md inner-glow" + on:click={() => change("system")}> + <svg id="Layer_1" + class="block radius-inherit" + data-name="Layer 1" + xmlns="http://www.w3.org/2000/svg" + width="70" + height="50" + viewBox="0 0 70 50"> + <rect width="35" + height="50" + fill="#e5e5e6"/> + <path d="M14,10H35a0,0,0,0,1,0,0V50a0,0,0,0,1,0,0H10a0,0,0,0,1,0,0V14A4,4,0,0,1,14,10Z" + fill="#fff"/> + <circle cx="18" + cy="18" + r="3" + fill="#e5e5e6"/> + <circle cx="27" + cy="18" + r="3" + fill="#e5e5e6"/> + <path d="M18,28H35a0,0,0,0,1,0,0v3a0,0,0,0,1,0,0H18a1,1,0,0,1-1-1V29A1,1,0,0,1,18,28Z" + fill="#38393e"/> + <path d="M18,34H35a0,0,0,0,1,0,0v3a0,0,0,0,1,0,0H18a1,1,0,0,1-1-1V35A1,1,0,0,1,18,34Z" + fill="#38393e"/> + <path d="M18,40H35a0,0,0,0,1,0,0v3a0,0,0,0,1,0,0H18a1,1,0,0,1-1-1V41A1,1,0,0,1,18,40Z" + fill="#38393e"/> + <rect x="35" + width="35" + height="50" + fill="#22232a"/> + <path d="M49,10H70V50H45V14A4,4,0,0,1,49,10Z" + fill="#5a5c63"/> + <circle cx="53" + cy="18" + r="3" + fill="#22232a"/> + <circle cx="62" + cy="18" + r="3" + fill="#22232a"/> + <path d="M53,28H70a0,0,0,0,1,0,0v3a0,0,0,0,1,0,0H53a1,1,0,0,1-1-1V29A1,1,0,0,1,53,28Z" + fill="#fafaff"/> + <path d="M53,34H70a0,0,0,0,1,0,0v3a0,0,0,0,1,0,0H53a1,1,0,0,1-1-1V35A1,1,0,0,1,53,34Z" + fill="#fafaff"/> + <path d="M53,40H70a0,0,0,0,1,0,0v3a0,0,0,0,1,0,0H53a1,1,0,0,1-1-1V41A1,1,0,0,1,53,40Z" + fill="#fafaff"/> + </svg> + </figure> + <div class="text-xs margin-top-xxxs padding-x-xxxxs">System</div> + </div> + </div> + </div> +</div> diff --git a/apps/web-shared/src/lib/configuration.ts b/apps/web-shared/src/lib/configuration.ts index 188d615..0ed5cd4 100644 --- a/apps/web-shared/src/lib/configuration.ts +++ b/apps/web-shared/src/lib/configuration.ts @@ -1,7 +1,9 @@ -export const API_ADDRESS = "https://api.dev.greatoffice.life"; -export const PROJECTS_ADDRESS = "https://projects.dev.greatoffice.life"; -export const PORTAL_ADDRESS = "https://portal.dev.greatoffice.life"; -export const FRONTPAGE_ADDRESS = "https://greatoffice.life"; +export const BASE_DOMAIN = "greatoffice.life"; +export const DEV_BASE_DOMAIN = "localhost"; +export const API_ADDRESS = "https://api.dev." + BASE_DOMAIN; +export const PROJECTS_ADDRESS = "https://projects.dev." + BASE_DOMAIN; +export const PORTAL_ADDRESS = "https://portal.dev." + BASE_DOMAIN; +export const FRONTPAGE_ADDRESS = "https://" + BASE_DOMAIN; export const DEV_PORTAL_ADDRESS = "http://localhost:3001"; export const DEV_FRONTPAGE_ADDRESS = "http://localhost:3002"; export const DEV_API_ADDRESS = "http://localhost:5000"; @@ -9,19 +11,23 @@ export const DEV_PROJECTS_ADDRESS = "http://localhost:3000"; export const SECONDS_BETWEEN_SESSION_CHECK = 600; export function projects_base(path: string = ""): string { - return (is_development() ? DEV_PROJECTS_ADDRESS : PROJECTS_ADDRESS) + (path ? "/" + path : "/"); + return (is_development() ? DEV_PROJECTS_ADDRESS : PROJECTS_ADDRESS) + (path !== "" ? "/" + path : ""); +} + +export function base_domain(path: string = ""): string { + return (is_development() ? DEV_BASE_DOMAIN : BASE_DOMAIN) + (path !== "" ? "/" + path : ""); } export function frontpage_base(path: string = ""): string { - return (is_development() ? DEV_FRONTPAGE_ADDRESS : FRONTPAGE_ADDRESS) + (path ? "/" + path : "/"); + return (is_development() ? DEV_FRONTPAGE_ADDRESS : FRONTPAGE_ADDRESS) + (path !== "" ? "/" + path : ""); } export function portal_base(path: string = ""): string { - return (is_development() ? DEV_PORTAL_ADDRESS : PORTAL_ADDRESS) + (path ? "/" + path : "/"); + return (is_development() ? DEV_PORTAL_ADDRESS : PORTAL_ADDRESS) + (path !== "" ? "/" + path : ""); } export function api_base(path: string = ""): string { - return (is_development() ? DEV_API_ADDRESS : API_ADDRESS) + (path ? "/" + path : "/"); + return (is_development() ? DEV_API_ADDRESS : API_ADDRESS) + (path !== "" ? "/" + path : ""); } export function is_development(): boolean { @@ -33,6 +39,10 @@ export function is_debug(): boolean { return localStorage.getItem(StorageKeys.debug) !== "true"; } +export const CookieNames = { + theme: "go_theme" +}; + export const QueryKeys = { labels: "labels", categories: "categories", diff --git a/apps/web-shared/src/lib/helpers.ts b/apps/web-shared/src/lib/helpers.ts index acf74d7..f2d0cca 100644 --- a/apps/web-shared/src/lib/helpers.ts +++ b/apps/web-shared/src/lib/helpers.ts @@ -1,4 +1,4 @@ -import {StorageKeys} from "$shared/lib/configuration"; +import {base_domain, CookieNames, StorageKeys} from "$shared/lib/configuration"; import {TimeEntryDto} from "$shared/lib/models/TimeEntryDto"; import {UnwrappedEntryDateTime} from "$shared/lib/models/UnwrappedEntryDateTime"; import {Temporal} from "@js-temporal/polyfill"; @@ -36,14 +36,24 @@ export function is_norwegian_phone_number(value: string): boolean { export function switch_theme() { const html = document.querySelector("html"); - if (html) { - if (html.dataset.theme === "dark") { - html.dataset.theme = "light"; - } else { - html.dataset.theme = "dark"; - } - localStorage.setItem(StorageKeys.theme, html.dataset.theme); + if (html.dataset.theme === "dark") { + html.dataset.theme = "light"; + } else { + html.dataset.theme = "dark"; } + set_cookie(CookieNames.theme, html.dataset.theme, base_domain()); +} + +export function get_cookie(name) { + const value = `; ${document.cookie}`; + const parts = value.split(`; ${name}=`); + if (parts.length === 2) return parts.pop().split(";").shift(); +} + +export function set_cookie(name, value, baseDomain = window.location.host) { + let asdf = name + "=" + encodeURIComponent(value) + (baseDomain ? ";domain=" + baseDomain : ""); + console.log(asdf); + document.cookie = asdf; } export function unwrap_date_time_from_entry(entry: TimeEntryDto): UnwrappedEntryDateTime { diff --git a/apps/web-shared/src/styles/components/adv-custom-select.scss b/apps/web-shared/src/styles/components/adv-custom-select.scss new file mode 100644 index 0000000..bd28247 --- /dev/null +++ b/apps/web-shared/src/styles/components/adv-custom-select.scss @@ -0,0 +1,79 @@ +@use '../base' as *; +@use 'popover.scss' as *; + +/* -------------------------------- + +File#: _2_adv-custom-select +Title: Advanced Custom Select +Descr: Custom select with advanced structure options +Usage: codyhouse.co/license + +-------------------------------- */ + +.adv-select { +} + +.adv-select__control { +} + +.adv-select-popover { + // use rem units + @include spaceUnit(1rem); + @include textUnit(1rem); + + &.popover { // popover component - dependency + --popover-width: 250px; + --popover-control-gap: 4px; // ⚠️ use px units - vertical gap between the popover and its control + --popover-viewport-gap: 20px; // ⚠️ use px units - vertical gap between the popover and the viewport - visible if popover height > viewport height + --popover-transition-duration: 0.2s; + + @include breakpoint(md) { + --popover-width: 320px; + } + } +} + +.adv-select-popover__optgroup:not(:first-of-type) { // custom <optgroup> + padding-top: var(--space-xxs); +} + +.adv-select-popover__optgroup:not(:last-of-type) { + border-bottom: 1px solid alpha(var(--color-contrast-higher), 0.1); + padding-bottom: var(--space-xxs); +} + +.adv-select-popover__check { + display: none; +} + +.adv-select-popover__label { +} + +.adv-select-popover__option { + position: relative; + cursor: pointer; + @include fontSmooth; + transition: .2s; + + &:hover { + background-color: alpha(var(--color-contrast-higher), 0.075); + } + + &:focus { + outline: none; + background-color: alpha(var(--color-primary), 0.15); + } + + &[aria-selected=true] { // selected option + background-color: var(--color-primary); + color: var(--color-white); + + .adv-select-popover__check { + display: block; + } + + &:focus { + box-shadow: inset 0 0 0 2px var(--color-primary-dark); + } + } +} diff --git a/apps/web-shared/src/styles/components/light-dark-switch.scss b/apps/web-shared/src/styles/components/light-dark-switch.scss new file mode 100644 index 0000000..1554010 --- /dev/null +++ b/apps/web-shared/src/styles/components/light-dark-switch.scss @@ -0,0 +1,102 @@ +@use '../base' as *; +@use 'popover.scss' as *; +@use 'adv-custom-select.scss' as *; + +/* -------------------------------- + +File#: _3_light-dark-switch +Title: Light/Dark Switch +Descr: Color theme switcher +Usage: codyhouse.co/license + +-------------------------------- */ + +.ld-switch {} + +.ld-switch-btn { + position: relative; + width: 24px; + height: 24px; + overflow: hidden; + display: inline-block; + + transition: opacity 0.2s, color 0.2s; + + &:hover { + cursor: pointer; + opacity: 0.8; + } + + &:focus { + outline: none; + color: var(--color-primary); + } +} + +.ld-switch-btn.popover-control--active { + /* class added to the control button when the dropdown is visible */ + color: var(--color-primary); +} + +.ld-switch-btn__icon-wrapper { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + display: flex; + justify-content: center; + align-items: center; + opacity: 0; + transform: translateY(100%) rotate(35deg) scale(0.5); +} + +.ld-switch-btn__icon-wrapper--in { + opacity: 1; + transform: translateY(0) rotate(0) scale(1); +} + +.ld-switch-btn__icon-wrapper--out { + opacity: 0; + transform: translateY(-100%) rotate(-35deg) scale(0.5); +} + +.ld-switch-btn__icon-wrapper--in, +.ld-switch-btn__icon-wrapper--out { + transition: transform 0.3s var(--ease-in-out), opacity 0.3s; +} + +.ld-switch-btn__icon { + margin: auto; + --size: 20px; /* icon size */ +} + +.popover.ld-switch-popover { + --popover-width: 250px; +} + +.ld-switch-popover__option { + user-select: none; + transition: opacity 0.2s; + + &:hover { + cursor: pointer; + opacity: 0.85; + } + + &:focus { + outline: none; + + figure { + box-shadow: 0 0 0 1px var(--color-bg-light), 0 0 0 3px var(--color-contrast-higher); + } + } + + &[aria-selected=true] { + color: var(--color-primary); + + figure { + box-shadow: 0 0 0 1px var(--color-bg-light), 0 0 0 3px currentColor; + } + } +} |
