summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorivarlovlie <git@ivarlovlie.no>2022-06-05 21:50:26 +0200
committerivarlovlie <git@ivarlovlie.no>2022-06-05 21:50:26 +0200
commit44a95927edb532f8982cf24c03d9fdd129016bd6 (patch)
treecd0efffc182f9429f8ec2bfe9622e6d36446f112 /apps
parent54b63daf1b5cefb54a3c616a4fee3ef57a671936 (diff)
downloadgreatoffice-44a95927edb532f8982cf24c03d9fdd129016bd6.tar.xz
greatoffice-44a95927edb532f8982cf24c03d9fdd129016bd6.zip
feat: Implement new theme switcher component and backend
The theme is now shared between the domain returned by base_domain()
Diffstat (limited to 'apps')
-rw-r--r--apps/frontpage/src/app.html15
-rw-r--r--apps/portal/src/app/index.scss1
-rw-r--r--apps/portal/src/app/pages/_layout.svelte84
-rw-r--r--apps/portal/src/app/pages/forgot.svelte2
-rw-r--r--apps/portal/src/app/pages/login.svelte2
-rw-r--r--apps/portal/src/app/pages/reset-password.svelte2
-rw-r--r--apps/portal/src/app/pages/sign-up.svelte7
-rw-r--r--apps/portal/src/index.html15
-rw-r--r--apps/projects/src/index.html11
-rw-r--r--apps/web-shared/src/components/theme-switcher.svelte425
-rw-r--r--apps/web-shared/src/lib/configuration.ts26
-rw-r--r--apps/web-shared/src/lib/helpers.ts26
-rw-r--r--apps/web-shared/src/styles/components/adv-custom-select.scss79
-rw-r--r--apps/web-shared/src/styles/components/light-dark-switch.scss102
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;
+ }
+ }
+}