From 44a95927edb532f8982cf24c03d9fdd129016bd6 Mon Sep 17 00:00:00 2001 From: ivarlovlie Date: Sun, 5 Jun 2022 21:50:26 +0200 Subject: feat: Implement new theme switcher component and backend The theme is now shared between the domain returned by base_domain() --- .../src/components/theme-switcher.svelte | 425 +++++++++++++++++++++ apps/web-shared/src/lib/configuration.ts | 26 +- apps/web-shared/src/lib/helpers.ts | 26 +- .../src/styles/components/adv-custom-select.scss | 79 ++++ .../src/styles/components/light-dark-switch.scss | 102 +++++ 5 files changed, 642 insertions(+), 16 deletions(-) create mode 100644 apps/web-shared/src/components/theme-switcher.svelte create mode 100644 apps/web-shared/src/styles/components/adv-custom-select.scss create mode 100644 apps/web-shared/src/styles/components/light-dark-switch.scss (limited to 'apps/web-shared/src') 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 @@ + + +
+ +
+ +
+
+
+ Appearance +
+
+
+
change("dark")}> + + + + + + + + + + +
+
Dark
+
+
+
change("light")}> + + + + + + + + + + +
+
Light
+
+
+
change("system")}> + + + + + + + + + + + + + + + + +
+
System
+
+
+
+
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 + 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; + } + } +} -- cgit v1.3