Functioning Game and Group Dropdowns with svelte storage
This commit is contained in:
parent
f5a2d32adf
commit
1c92cb8418
8 changed files with 314 additions and 16 deletions
66
src/lib/components/DropDown.svelte
Normal file
66
src/lib/components/DropDown.svelte
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<script lang="ts">
|
||||||
|
export let showDropdown = false;
|
||||||
|
export let dropdownTitle = 'Dropdown';
|
||||||
|
export let dropdownId: string;
|
||||||
|
|
||||||
|
const toggleDropdown = () => {
|
||||||
|
showDropdown = !showDropdown;
|
||||||
|
document.body.addEventListener('click', handleMenuClose);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMenuClose = (e: MouseEvent) => {
|
||||||
|
let self = false;
|
||||||
|
if (e) {
|
||||||
|
const target = e.target as HTMLElement;
|
||||||
|
if (target.dataset.dropdown == dropdownId) {
|
||||||
|
self = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self) {
|
||||||
|
showDropdown = false;
|
||||||
|
document.body.removeEventListener('click', handleMenuClose);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div data-dropdown={dropdownId} class="dropdown">
|
||||||
|
<button data-dropdown={dropdownId} class="dropdown-button" on:click|self={toggleDropdown}>{dropdownTitle}</button>
|
||||||
|
{#if showDropdown}
|
||||||
|
<ul data-dropdown={dropdownId} class="dropdown-container">
|
||||||
|
<slot />
|
||||||
|
</ul>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.dropdown {
|
||||||
|
position: relative;
|
||||||
|
display: grid;
|
||||||
|
justify-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-button {
|
||||||
|
background-color: var(--background);
|
||||||
|
color: var(--color-text);
|
||||||
|
border: none;
|
||||||
|
height: var(--height-m);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-button:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--color-active-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-container {
|
||||||
|
list-style-type: none;
|
||||||
|
width: max-content;
|
||||||
|
height: fit-content;
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.8);
|
||||||
|
border-radius: 10px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
</style>
|
19
src/lib/components/DropDownElement.svelte
Normal file
19
src/lib/components/DropDownElement.svelte
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<script lang="ts">
|
||||||
|
export let dropdownId: string;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<li class="dropdown-element" data-dropdown={dropdownId}>
|
||||||
|
<slot />
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.dropdown-element {
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
max-width: 100%;
|
||||||
|
min-width: fit-content;
|
||||||
|
margin: 10% auto;
|
||||||
|
margin-left: 0;
|
||||||
|
background: #3f3f3f;
|
||||||
|
}
|
||||||
|
</style>
|
5
src/lib/stores/GameFilter.ts
Normal file
5
src/lib/stores/GameFilter.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { writable, type Writable } from "svelte/store";
|
||||||
|
|
||||||
|
const SelectedGameStore: Writable<string> = writable("");
|
||||||
|
|
||||||
|
export default SelectedGameStore;
|
5
src/lib/stores/GameGroupFilter.ts
Normal file
5
src/lib/stores/GameGroupFilter.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { writable, type Writable } from "svelte/store";
|
||||||
|
|
||||||
|
const SelectedGameGroupsStore: Writable<Map<string, Array<string>>> = writable(new Map());
|
||||||
|
|
||||||
|
export default SelectedGameGroupsStore;
|
99
src/routes/graphs/GameGroupSelection.svelte
Normal file
99
src/routes/graphs/GameGroupSelection.svelte
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import DropDown from '$lib/components/DropDown.svelte';
|
||||||
|
import DropDownElement from '$lib/components/DropDownElement.svelte';
|
||||||
|
import SelectedGameGroupsStore from '$lib/stores/GameGroupFilter';
|
||||||
|
import SelectedGameStore from '$lib/stores/GameFilter';
|
||||||
|
|
||||||
|
let selectedGame: string = "";
|
||||||
|
let selectedGameGroups: Array<string> = [];
|
||||||
|
let selectedGameGroupsMap: Map<string, Array<string>> = new Map();
|
||||||
|
let store: string | null;
|
||||||
|
|
||||||
|
SelectedGameStore.subscribe((selection) => {
|
||||||
|
selectedGame = selection;
|
||||||
|
if (selectedGameGroupsMap.size != 0) {
|
||||||
|
const tmp = selectedGameGroupsMap.get(selectedGame);
|
||||||
|
if (typeof tmp !== "undefined") {
|
||||||
|
selectedGameGroups = [...tmp.values()];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (typeof localStorage !== 'undefined') {
|
||||||
|
store = localStorage.getItem('gameGroupSelection');
|
||||||
|
|
||||||
|
if (typeof store == 'string') {
|
||||||
|
let selectedGameGroupsMap = new Map<string, Array<string>>(JSON.parse(store));
|
||||||
|
const tmp = selectedGameGroupsMap.get(selectedGame);
|
||||||
|
if (typeof tmp !== "undefined") {
|
||||||
|
selectedGameGroups = [...tmp.values()];
|
||||||
|
}
|
||||||
|
SelectedGameGroupsStore.set(selectedGameGroupsMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectedGameGroupsStore.subscribe((selection) => {
|
||||||
|
selectedGameGroupsMap = selection;
|
||||||
|
const tmp = selection.get(selectedGame);
|
||||||
|
if (typeof tmp !== "undefined") {
|
||||||
|
selectedGameGroups = [...tmp.values()];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof localStorage !== 'undefined') {
|
||||||
|
localStorage.setItem('gameGroupSelection', JSON.stringify(Array.from(selection.entries())));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const updateGameGroupSelection = () => {
|
||||||
|
SelectedGameGroupsStore.update((selection) => selection.set(selectedGame, selectedGameGroups));
|
||||||
|
};
|
||||||
|
|
||||||
|
const dropdownId = crypto.randomUUID();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DropDown dropdownId={dropdownId} dropdownTitle="Group Selection">
|
||||||
|
<DropDownElement dropdownId={dropdownId}>
|
||||||
|
<input
|
||||||
|
id="checkboxGameGroupA"
|
||||||
|
data-dropdown={dropdownId}
|
||||||
|
type="checkbox"
|
||||||
|
bind:group={selectedGameGroups}
|
||||||
|
on:change={updateGameGroupSelection}
|
||||||
|
value="A"
|
||||||
|
/>
|
||||||
|
<label for="checkboxGameGroupA" data-dropdown={dropdownId}>Group A</label>
|
||||||
|
</DropDownElement>
|
||||||
|
<DropDownElement dropdownId={dropdownId}>
|
||||||
|
<input
|
||||||
|
id="checkboxGameGroupB"
|
||||||
|
data-dropdown={dropdownId}
|
||||||
|
type="checkbox"
|
||||||
|
bind:group={selectedGameGroups}
|
||||||
|
on:change={updateGameGroupSelection}
|
||||||
|
value="B"
|
||||||
|
/>
|
||||||
|
<label for="checkboxGameGroupB" data-dropdown={dropdownId}>Group B</label>
|
||||||
|
</DropDownElement>
|
||||||
|
<DropDownElement dropdownId={dropdownId}>
|
||||||
|
<input
|
||||||
|
id="checkboxGameGroupNA"
|
||||||
|
data-dropdown={dropdownId}
|
||||||
|
type="checkbox"
|
||||||
|
bind:group={selectedGameGroups}
|
||||||
|
on:change={updateGameGroupSelection}
|
||||||
|
value="N/A"
|
||||||
|
/>
|
||||||
|
<label for="checkboxGameGroupNA" data-dropdown={dropdownId}>Ungrouped</label>
|
||||||
|
</DropDownElement>
|
||||||
|
</DropDown>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:checked + label {
|
||||||
|
color: var(--color-active-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
:hover {
|
||||||
|
color: var(--color-active-1) !important;
|
||||||
|
}
|
||||||
|
</style>
|
63
src/routes/graphs/GameSelection.svelte
Normal file
63
src/routes/graphs/GameSelection.svelte
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import DropDown from '$lib/components/DropDown.svelte';
|
||||||
|
import DropDownElement from '$lib/components/DropDownElement.svelte';
|
||||||
|
import SelectedGameStore from '$lib/stores/GameFilter';
|
||||||
|
|
||||||
|
let selectedGame: string = "";
|
||||||
|
let store: string | null;
|
||||||
|
|
||||||
|
if (typeof localStorage !== 'undefined') {
|
||||||
|
store = localStorage.getItem('gameSelection');
|
||||||
|
|
||||||
|
if (typeof store == 'string') {
|
||||||
|
selectedGame = JSON.parse(store);
|
||||||
|
SelectedGameStore.set(selectedGame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectedGameStore.subscribe((selection) => {
|
||||||
|
selectedGame = selection;
|
||||||
|
|
||||||
|
if (typeof localStorage !== 'undefined') {
|
||||||
|
localStorage.setItem('gameSelection', JSON.stringify(selectedGame));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateGameSelection = () => {
|
||||||
|
SelectedGameStore.update(() => selectedGame);
|
||||||
|
};
|
||||||
|
|
||||||
|
const dropdownId = crypto.randomUUID();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DropDown dropdownId={dropdownId} dropdownTitle="Game Selection">
|
||||||
|
<DropDownElement dropdownId={dropdownId}>
|
||||||
|
<input
|
||||||
|
id="checkboxGameGroupA"
|
||||||
|
data-dropdown={dropdownId}
|
||||||
|
type="radio"
|
||||||
|
bind:group={selectedGame}
|
||||||
|
on:change={updateGameSelection}
|
||||||
|
value="16"
|
||||||
|
/>
|
||||||
|
<label for="checkboxGameGroupA" data-dropdown={dropdownId}>Game 16</label>
|
||||||
|
</DropDownElement>
|
||||||
|
<DropDownElement dropdownId={dropdownId}>
|
||||||
|
<input
|
||||||
|
id="checkboxGameGroupB"
|
||||||
|
data-dropdown={dropdownId}
|
||||||
|
type="radio"
|
||||||
|
bind:group={selectedGame}
|
||||||
|
on:change={updateGameSelection}
|
||||||
|
value="17"
|
||||||
|
/>
|
||||||
|
<label for="checkboxGameGroupB" data-dropdown={dropdownId}>Game 17</label>
|
||||||
|
</DropDownElement>
|
||||||
|
</DropDown>
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
label:hover {
|
||||||
|
color: var(--color-active-1);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,34 +1,35 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import discord from '$lib/images/discord.svg';
|
import GameGroupSelection from './GameGroupSelection.svelte';
|
||||||
|
import GameSelection from './GameSelection.svelte';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
<div class="corner">
|
<div class="corner" />
|
||||||
</div>
|
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<svg viewBox="0 0 2 3" aria-hidden="true">
|
<svg viewBox="0 0 2 3" aria-hidden="true">
|
||||||
<path d="M0,0 L1,2 C1.5,3 1.5,3 2,3 L2,0 Z" />
|
<path d="M0,0 L1,2 C1.5,3 1.5,3 2,3 L2,0 Z" />
|
||||||
</svg>
|
</svg>
|
||||||
<ul>
|
<ul>
|
||||||
<li aria-current={$page.url.pathname === '/graphs' ? 'page' : undefined}>
|
|
||||||
<a href="/graphs">Home</a>
|
|
||||||
</li>
|
|
||||||
<li aria-current={$page.url.pathname === '/graphs/tab' ? 'page' : undefined}>
|
<li aria-current={$page.url.pathname === '/graphs/tab' ? 'page' : undefined}>
|
||||||
<a href="/graphs/tab">Tab 1</a>
|
<a href="/graphs/tab">Tab 1</a>
|
||||||
</li>
|
</li>
|
||||||
<li aria-current={$page.url.pathname === '/graphs/tab2' ? 'page' : undefined}>
|
<li aria-current={$page.url.pathname === '/graphs/tab2' ? 'page' : undefined}>
|
||||||
<a href="/graphs/tab2">Tab 2</a>
|
<a href="/graphs/tab2">Tab 2</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li aria-current={$page.url.pathname === '/graphs/tab3' ? 'page' : undefined}>
|
||||||
|
<a href="/graphs/tab3">Tab 3</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<GameSelection />
|
||||||
|
<GameGroupSelection />
|
||||||
<svg viewBox="0 0 2 3" aria-hidden="true">
|
<svg viewBox="0 0 2 3" aria-hidden="true">
|
||||||
<path d="M0,0 L0,3 C0.5,3 0.5,3 1,2 L2,0 Z" />
|
<path d="M0,0 L0,3 C0.5,3 0.5,3 1,2 L2,0 Z" />
|
||||||
</svg>
|
</svg>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="corner">
|
<div class="corner" />
|
||||||
</div>
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -38,8 +39,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.corner {
|
.corner {
|
||||||
width: 3em;
|
width: var(--height-m);
|
||||||
height: 3em;
|
height: var(--height-m);
|
||||||
}
|
}
|
||||||
|
|
||||||
.corner a {
|
.corner a {
|
||||||
|
@ -64,7 +65,7 @@
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
width: 2em;
|
width: 2em;
|
||||||
height: 3em;
|
height: var(--height-m);
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +77,7 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
height: 3em;
|
height: var(--height-m);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -99,7 +100,7 @@
|
||||||
top: 0;
|
top: 0;
|
||||||
left: calc(50% - var(--size));
|
left: calc(50% - var(--size));
|
||||||
border: var(--size) solid transparent;
|
border: var(--size) solid transparent;
|
||||||
border-top: var(--size) solid var(--color-theme-1);
|
border-top: var(--size) solid var(--color-active-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
nav a {
|
nav a {
|
||||||
|
@ -116,7 +117,11 @@
|
||||||
transition: color 0.2s linear;
|
transition: color 0.2s linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nav button {
|
||||||
|
height: var(--height-m);
|
||||||
|
}
|
||||||
|
|
||||||
a:hover {
|
a:hover {
|
||||||
color: var(--color-theme-1);
|
color: var(--color-active-1);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1 +1,37 @@
|
||||||
<h1>Example Tab</h1>
|
<script lang="ts">
|
||||||
|
import SelectedGameGroupsStore from '$lib/stores/GameGroupFilter';
|
||||||
|
import SelectedGameStore from '$lib/stores/GameFilter';
|
||||||
|
|
||||||
|
let selectedGameGroups: Array<string> = [];
|
||||||
|
let selectedGameGroupsMap: Map<string, Array<string>> = new Map();
|
||||||
|
let selectedGame: string = "";
|
||||||
|
|
||||||
|
SelectedGameStore.subscribe((selection) => {
|
||||||
|
selectedGame = selection;
|
||||||
|
console.log(selectedGameGroupsMap.size);
|
||||||
|
if (selectedGameGroupsMap.size != 0) {
|
||||||
|
const tmp = selectedGameGroupsMap.get(selectedGame);
|
||||||
|
if (typeof tmp !== "undefined") {
|
||||||
|
selectedGameGroups = [...tmp.values()];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
SelectedGameGroupsStore.subscribe((selection) => {
|
||||||
|
selectedGameGroupsMap = selection;
|
||||||
|
const tmp = selection.get(selectedGame);
|
||||||
|
if (typeof tmp !== 'undefined') {
|
||||||
|
selectedGameGroups = [...tmp.values()];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Graphs</title>
|
||||||
|
<meta name="description" content="Chellaris Sign-Up Graphs" />
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<h1>Example Tab</h1>
|
||||||
|
|
||||||
|
<p>{selectedGameGroups.length > 1 ? "Groups" : "Group"} {selectedGameGroups.join(", ")} {selectedGameGroups.length > 1 ? "are" : "is"} selected</p>
|
||||||
|
|
Reference in a new issue