Adding toggle component and local storage using carbon components as base.

This commit is contained in:
Bradley Shellnut 2022-05-03 23:27:06 -07:00
parent 888b045581
commit e84f212772
6 changed files with 277 additions and 179 deletions

71
package-lock.json generated
View file

@ -8,7 +8,7 @@
"name": "boredgame", "name": "boredgame",
"version": "0.0.1", "version": "0.0.1",
"dependencies": { "dependencies": {
"@fontsource/fira-mono": "^4.5.7", "@fontsource/fira-mono": "^4.5.8",
"@lukeed/uuid": "^2.0.0", "@lukeed/uuid": "^2.0.0",
"cookie": "^0.5.0", "cookie": "^0.5.0",
"node-sass": "^7.0.1", "node-sass": "^7.0.1",
@ -16,15 +16,16 @@
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.21.1", "@playwright/test": "^1.21.1",
"@rgossiaux/svelte-headlessui": "1.0.0-beta.12",
"@sveltejs/adapter-auto": "next", "@sveltejs/adapter-auto": "next",
"@sveltejs/kit": "next", "@sveltejs/kit": "next",
"@types/cookie": "^0.5.0", "@types/cookie": "^0.5.1",
"@types/node": "^17.0.25", "@types/node": "^17.0.31",
"@typescript-eslint/eslint-plugin": "^5.20.0", "@typescript-eslint/eslint-plugin": "^5.21.0",
"@typescript-eslint/parser": "^5.20.0", "@typescript-eslint/parser": "^5.21.0",
"carbon-components-svelte": "^0.63.1", "carbon-components-svelte": "^0.63.4",
"carbon-icons-svelte": "^11.0.1", "carbon-icons-svelte": "^11.0.1",
"eslint": "^8.13.0", "eslint": "^8.14.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.5.0",
"eslint-plugin-svelte3": "^3.4.1", "eslint-plugin-svelte3": "^3.4.1",
"prettier": "^2.6.2", "prettier": "^2.6.2",
@ -32,8 +33,8 @@
"svelte": "^3.47.0", "svelte": "^3.47.0",
"svelte-check": "^2.7.0", "svelte-check": "^2.7.0",
"svelte-preprocess": "^4.10.6", "svelte-preprocess": "^4.10.6",
"tslib": "^2.3.1", "tslib": "^2.4.0",
"typescript": "^4.6.3" "typescript": "^4.6.4"
} }
}, },
"node_modules/@babel/code-frame": { "node_modules/@babel/code-frame": {
@ -1115,6 +1116,15 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true "dev": true
}, },
"node_modules/@rgossiaux/svelte-headlessui": {
"version": "1.0.0-beta.12",
"resolved": "https://registry.npmjs.org/@rgossiaux/svelte-headlessui/-/svelte-headlessui-1.0.0-beta.12.tgz",
"integrity": "sha512-UgLV9PwlwkJYObYSzxm5kh5HbyEps3oMbihL1BY7CpZ26j0q6O6WaG4d95eiMilhzyHfRYRm/VLyNPJISBFqAA==",
"dev": true,
"peerDependencies": {
"svelte": "^3.44.0"
}
},
"node_modules/@rollup/pluginutils": { "node_modules/@rollup/pluginutils": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.1.2.tgz", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.1.2.tgz",
@ -1264,9 +1274,9 @@
"integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==" "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ=="
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "17.0.30", "version": "17.0.31",
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.30.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.31.tgz",
"integrity": "sha512-oNBIZjIqyHYP8VCNAV9uEytXVeXG2oR0w9lgAXro20eugRQfY002qr3CUl6BAe+Yf/z3CRjPdz27Pu6WWtuSRw==", "integrity": "sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==",
"dev": true "dev": true
}, },
"node_modules/@types/normalize-package-data": { "node_modules/@types/normalize-package-data": {
@ -1890,9 +1900,9 @@
] ]
}, },
"node_modules/carbon-components-svelte": { "node_modules/carbon-components-svelte": {
"version": "0.63.3", "version": "0.63.4",
"resolved": "https://registry.npmjs.org/carbon-components-svelte/-/carbon-components-svelte-0.63.3.tgz", "resolved": "https://registry.npmjs.org/carbon-components-svelte/-/carbon-components-svelte-0.63.4.tgz",
"integrity": "sha512-a3HX73QryT0vO1YzQsFLbuzWclCBcqXtIvW8VwlAteiQ9xe/59uGFv50SwdiTvRNUauVdzOExUvaJO4FwMvoDg==", "integrity": "sha512-ZDEgPF6XJzHOMt10JDmzvu78gy5TapHXPBg3jA38HD6acfRG5AWYcGAH55/qIxPkBd7VTBixOst2DQ1sMH0kwA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"flatpickr": "4.6.9" "flatpickr": "4.6.9"
@ -5684,9 +5694,9 @@
} }
}, },
"node_modules/tslib": { "node_modules/tslib": {
"version": "2.3.1", "version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
"dev": true "dev": true
}, },
"node_modules/tsutils": { "node_modules/tsutils": {
@ -6815,6 +6825,13 @@
} }
} }
}, },
"@rgossiaux/svelte-headlessui": {
"version": "1.0.0-beta.12",
"resolved": "https://registry.npmjs.org/@rgossiaux/svelte-headlessui/-/svelte-headlessui-1.0.0-beta.12.tgz",
"integrity": "sha512-UgLV9PwlwkJYObYSzxm5kh5HbyEps3oMbihL1BY7CpZ26j0q6O6WaG4d95eiMilhzyHfRYRm/VLyNPJISBFqAA==",
"dev": true,
"requires": {}
},
"@rollup/pluginutils": { "@rollup/pluginutils": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.1.2.tgz", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.1.2.tgz",
@ -6936,9 +6953,9 @@
"integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==" "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ=="
}, },
"@types/node": { "@types/node": {
"version": "17.0.30", "version": "17.0.31",
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.30.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.31.tgz",
"integrity": "sha512-oNBIZjIqyHYP8VCNAV9uEytXVeXG2oR0w9lgAXro20eugRQfY002qr3CUl6BAe+Yf/z3CRjPdz27Pu6WWtuSRw==", "integrity": "sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==",
"dev": true "dev": true
}, },
"@types/normalize-package-data": { "@types/normalize-package-data": {
@ -7365,9 +7382,9 @@
"dev": true "dev": true
}, },
"carbon-components-svelte": { "carbon-components-svelte": {
"version": "0.63.3", "version": "0.63.4",
"resolved": "https://registry.npmjs.org/carbon-components-svelte/-/carbon-components-svelte-0.63.3.tgz", "resolved": "https://registry.npmjs.org/carbon-components-svelte/-/carbon-components-svelte-0.63.4.tgz",
"integrity": "sha512-a3HX73QryT0vO1YzQsFLbuzWclCBcqXtIvW8VwlAteiQ9xe/59uGFv50SwdiTvRNUauVdzOExUvaJO4FwMvoDg==", "integrity": "sha512-ZDEgPF6XJzHOMt10JDmzvu78gy5TapHXPBg3jA38HD6acfRG5AWYcGAH55/qIxPkBd7VTBixOst2DQ1sMH0kwA==",
"dev": true, "dev": true,
"requires": { "requires": {
"flatpickr": "4.6.9" "flatpickr": "4.6.9"
@ -10131,9 +10148,9 @@
} }
}, },
"tslib": { "tslib": {
"version": "2.3.1", "version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
"dev": true "dev": true
}, },
"tsutils": { "tsutils": {

View file

@ -0,0 +1,66 @@
<script>
import { Switch } from '@rgossiaux/svelte-headlessui';
let enabled = false;
</script>
<Switch
checked={enabled}
on:change={(e) => (enabled = e.detail)}
class={enabled ? 'switch switch-enabled' : 'switch switch-disabled'}
>
<span class="sr-only">Dark Mode</span>
<span class="toggle" class:toggle-on={enabled} class:toggle-off={!enabled} />
</Switch>
<style>
:global(.switch) {
position: relative;
display: inline-flex;
align-items: center;
border-radius: 1rem;
border: 0;
height: 1.25rem;
width: 2.5rem;
}
:global(.switch-enabled) {
/* Blue */
background-color: hsla(0, 0%, 0%, 1);
}
:global(.switch-disabled) {
/* Gray */
background-color: hsla(0, 0%, 100%, 0.5);
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
.toggle {
display: inline-block;
width: 1rem;
height: 1rem;
background-color: hsla(0, 0%, 100%, 1);
border-radius: 1rem;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
transition-property: transform;
}
.toggle-on {
transform: translateX(1.4rem);
}
.toggle-off {
transform: translateX(0.1rem);
}
</style>

View file

@ -0,0 +1,71 @@
<script>
/**
* @event {null} save
* @event {{ prevValue: any; value: any; }} update
*/
/**
* Specify the local storage key
*/
export let key = 'local-storage-key';
/**
* Provide a value to persist
* @type {any}
*/
export let value = '';
/**
* Remove the persisted key value from the browser's local storage
* @type {() => void}
*/
export function clearItem() {
localStorage.removeItem(key);
}
/**
* Clear all key values from the browser's local storage
* @type {() => void}
*/
export function clearAll() {
localStorage.clear();
}
import { onMount, afterUpdate, createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
let prevValue = value;
function setItem() {
if (typeof value === 'object') {
localStorage.setItem(key, JSON.stringify(value));
} else {
localStorage.setItem(key, value);
}
}
onMount(() => {
const item = localStorage.getItem(key);
if (item != null) {
try {
value = JSON.parse(item);
} catch (e) {
value = item;
}
} else {
setItem(value);
dispatch('save');
}
});
afterUpdate(() => {
if (prevValue !== value) {
setItem(value);
dispatch('update', { prevValue, value });
}
prevValue = value;
});
</script>

View file

@ -1,27 +1,26 @@
<script lang="ts"> <script lang="ts">
// import { // import {
// Theme, // Theme,
// RadioButtonGroup, // RadioButtonGroup,
// RadioButton, // RadioButton,
// } from "carbon-components-svelte"; // } from "carbon-components-svelte";
// import type { CarbonTheme } from "carbon-components-svelte/types/Theme/Theme.svelte"; // import type { CarbonTheme } from "carbon-components-svelte/types/Theme/Theme.svelte";
import { Switch } from '@rgossiaux/svelte-headlessui'; import { page } from '$app/stores';
import { page } from '$app/stores'; import Toggle from '$root/components/toggle.svelte';
import logo from './svelte-logo.svg'; import logo from './svelte-logo.svg';
// let theme: CarbonTheme = "white"; // let theme: CarbonTheme = "white";
let enabled = false;
</script> </script>
<header> <header>
<div class="corner"> <div class="corner">
<a href="https://kit.svelte.dev"> <a href="https://kit.svelte.dev">
<img src={logo} alt="SvelteKit" /> <img src={logo} alt="SvelteKit" />
</a> </a>
</div> </div>
<nav> <nav>
<!-- <Theme <!-- <Theme
render="toggle" render="toggle"
toggle={{ toggle={{
themes: ['white','g100'], themes: ['white','g100'],
@ -32,162 +31,105 @@
persist persist
persistKey="__carbon-theme" persistKey="__carbon-theme"
/> --> /> -->
<div> <div>
<Switch <Toggle />
checked={enabled} </div>
on:change={(e) => (enabled = e.detail)} <ul>
class={enabled ? "switch switch-enabled" : "switch switch-disabled"}> <li class:active={$page.url.pathname === '/'}><a sveltekit:prefetch href="/">Home</a></li>
<span class="sr-only">Enable notifications</span> <li class:active={$page.url.pathname === '/about'}>
<span class="toggle" class:toggle-on={enabled} class:toggle-off={!enabled} /> <a sveltekit:prefetch href="/about">About</a>
</Switch> </li>
</div> </ul>
<ul> </nav>
<li class:active={$page.url.pathname === '/'}><a sveltekit:prefetch href="/">Home</a></li>
<li class:active={$page.url.pathname === '/about'}>
<a sveltekit:prefetch href="/about">About</a>
</li>
</ul>
</nav>
</header> </header>
<style> <style>
header { header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
} }
.corner { .corner {
width: 3em; width: 3em;
height: 3em; height: 3em;
} }
.corner a { .corner a {
display: flex; display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.corner img {
width: 2em;
height: 2em;
object-fit: contain;
}
nav {
display: flex;
justify-content: center;
align-items: center;
--background: rgba(255, 255, 255, 0.7);
}
svg {
width: 2em;
height: 3em;
display: block;
}
path {
fill: var(--background);
}
ul {
position: relative;
padding: 0;
margin: 0;
height: 3em;
display: flex;
justify-content: center;
align-items: center;
list-style: none;
background: var(--background);
background-size: contain;
}
li {
position: relative;
height: 100%;
}
li.active::before {
--size: 6px;
content: '';
width: 0;
height: 0;
position: absolute;
top: 0;
left: calc(50% - var(--size));
border: var(--size) solid transparent;
border-top: var(--size) solid var(--accent-color);
}
nav a {
display: flex;
height: 100%;
align-items: center;
padding: 0 1em;
color: var(--heading-color);
font-weight: 700;
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.1em;
text-decoration: none;
transition: color 0.2s linear;
}
a:hover {
color: var(--accent-color);
}
:global(.switch) {
position: relative;
display: inline-flex;
align-items: center; align-items: center;
border-radius: 1rem; justify-content: center;
border: 0; width: 100%;
height: 1.25rem; height: 100%;
width: 2.5rem;
} }
:global(.switch-enabled) { .corner img {
/* Blue */ width: 2em;
background-color: rgb(37 99 235); height: 2em;
object-fit: contain;
} }
:global(.switch-disabled) { nav {
/* Gray */ display: flex;
background-color: rgb(229 231 235); justify-content: center;
align-items: center;
--background: rgba(255, 255, 255, 0.7);
} }
.sr-only { svg {
position: absolute; width: 2em;
width: 1px; height: 3em;
height: 1px; display: block;
}
path {
fill: var(--background);
}
ul {
position: relative;
padding: 0; padding: 0;
margin: -1px; margin: 0;
overflow: hidden; height: 3em;
clip: rect(0, 0, 0, 0); display: flex;
white-space: nowrap; justify-content: center;
border-width: 0; align-items: center;
list-style: none;
background: var(--background);
background-size: contain;
} }
.toggle { li {
display: inline-block; position: relative;
width: 1rem; height: 100%;
height: 1rem;
background-color: rgb(255 255 255);
border-radius: 1rem;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
transition-property: transform;
} }
.toggle-on { li.active::before {
transform: translateX(1.4rem); --size: 6px;
content: '';
width: 0;
height: 0;
position: absolute;
top: 0;
left: calc(50% - var(--size));
border: var(--size) solid transparent;
border-top: var(--size) solid var(--accent-color);
} }
.toggle-off { nav a {
transform: translateX(0.1rem); display: flex;
height: 100%;
align-items: center;
padding: 0 1em;
color: var(--heading-color);
font-weight: 700;
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.1em;
text-decoration: none;
transition: color 0.2s linear;
}
a:hover {
color: var(--accent-color);
} }
</style> </style>

1
static/moon.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-moon"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>

After

Width:  |  Height:  |  Size: 281 B

1
static/sun.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-sun"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>

After

Width:  |  Height:  |  Size: 650 B