Skip to content

Commit

Permalink
Merge pull request #171 from danielbruni/feature/save-and-load-builds
Browse files Browse the repository at this point in the history
Feature: save and load builds
  • Loading branch information
marcoaaguiar authored Dec 5, 2024
2 parents e4f8afd + 6fcccb6 commit 172625b
Show file tree
Hide file tree
Showing 10 changed files with 286 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/lib/components/save-and-load/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as SaveAndLoadModal } from './modal.svelte';
export { default as LoadModalBtn } from './load-modal-btn.svelte';
export { default as SaveModalBtn } from './save-modal-btn.svelte';
12 changes: 12 additions & 0 deletions src/lib/components/save-and-load/load-modal-btn.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<script lang="ts">
import { showModal, modalAction } from './store.svelte';
</script>

<button
class="hover:bg-slate-700 px-4 py-1 border rounded hover:border-white border-solid border-transparent"
onclick={() => {
modalAction.set('load');
showModal.set(true);
}}
>Load
</button>
109 changes: 109 additions & 0 deletions src/lib/components/save-and-load/load.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<script lang="ts">
import { onMount } from 'svelte';
import type { SaveData } from './types';
import { goto } from '$app/navigation';
import { showModal } from './store.svelte';
let savedData = $state<SaveData[]>([]);
onMount(() => {
const data = JSON.parse(localStorage.getItem('savedData') || '[]');
savedData = data;
});
function deleteEntry(selectedIndex: number) {
if (confirm('Are you sure you want to delete this entry?')) {
savedData = savedData.filter((_, index) => index !== selectedIndex);
localStorage.setItem('savedData', JSON.stringify(savedData));
}
}
function handleClick(selectedIndex: number) {
console.log('Selected index:', selectedIndex);
const selectedBuild = savedData[selectedIndex];
if (selectedBuild) {
showModal.set(false);
const params = new URLSearchParams();
params.set('a', selectedBuild.ascendancy);
params.set('p', selectedBuild.passivesCompressed);
if (!selectedBuild?.ascendancy || !selectedBuild.passivesCompressed || !selectedBuild.title) {
console.error('Invalid build data');
return;
}
// TODO: switch to sveltekit's goto when +page.svelte is switched to runes.
// To many issues without runes and $effect.
window.location.href = `/?${params.toString()}`;
}
}
</script>

<div class="rounded-md bg-[#1a1a1a] text-white w-full p-8 space-y-2">
<h2 class="font-bold my-4 text-2xl">Load Build</h2>
<table class="table-auto border-collapse text-sm w-full">
<thead>
<tr class="bg-slate-700">
<th class="border-b border-slate-700 px-4 pt-8 pl-8 pb-3 text-left font-bold">Title</th>
<th class="border-b border-slate-700 px-4 pt-8 pb-3 text-left font-bold">Description</th>
<th class="border-b border-slate-700 px-4 pt-8 pr-8 pb-3 text-left font-bold"> </th>
</tr>
</thead>
<tbody class="bg-[#1a1a1a]">
{#each savedData as entry, index}
<tr>
<td class="border-b border-slate-700 p-4 pl-8">{entry.title}</td>
<td class="border-b border-slate-700 p-4">{entry.description}</td>
<td class="border-b border-slate-700 p-4 pr-8 flex items-center justify-center gap-6">
<button
onclick={() => handleClick(index)}
class="flex justify-center items-center gap-2 hover:text-green-500"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="size-5"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 12 12 16.5m0 0L7.5 12m4.5 4.5V3"
/>
</svg>
<span>Load</span>
</button>
<button
onclick={() => {
deleteEntry(index);
}}
class="flex justify-center items-center gap-2 hover:text-red-500"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="size-5"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
/>
</svg>

<span>Delete</span>
</button>
</td>
</tr>
{/each}
</tbody>
</table>
</div>
39 changes: 39 additions & 0 deletions src/lib/components/save-and-load/modal.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<script lang="ts">
import Load from './load.svelte';
import Save from './save.svelte';
import { showModal, modalAction } from './store.svelte';
import { onDestroy } from 'svelte';
let dialog: HTMLDialogElement | undefined = $state();
// Subscribe to showModal store
const unsubscribe = showModal.subscribe((value) => {
if (value && dialog) {
dialog.showModal();
} else if (dialog) {
dialog.close();
}
});
onDestroy(() => {
unsubscribe();
});
</script>

<!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_noninteractive_element_interactions -->
<dialog
bind:this={dialog}
onclose={() => showModal.set(false)}
onclick={(e) => {
if (e.target === dialog) {
dialog.close();
}
}}
class="rounded-md shadow-lg w-2/3 lg:w-1/3"
>
{#if $modalAction === 'save'}
<Save />
{:else if $modalAction === 'load'}
<Load />
{/if}
</dialog>
13 changes: 13 additions & 0 deletions src/lib/components/save-and-load/save-modal-btn.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script lang="ts">
import { showModal, modalAction } from './store.svelte';
</script>

<button
class="hover:bg-slate-700 px-4 py-1 border rounded hover:border-white border-solid border-transparent"
onclick={() => {
modalAction.set('save');
showModal.set(true);
}}
>
Save
</button>
94 changes: 94 additions & 0 deletions src/lib/components/save-and-load/save.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<script lang="ts">
import { onMount } from 'svelte';
import type { SaveData } from './types';
import { showModal } from './store.svelte';
let savedData = $state<SaveData[]>([]);
let title = $state<string>('');
let description = $state<string>('');
let ascendancy = $state<string>('');
let passivesCompressed = $state<string>('');
let selectedIndex = $state<number>(-1);
onMount(() => {
const data = JSON.parse(localStorage.getItem('savedData') || '[]');
savedData = data;
const params = new URLSearchParams(window.location.search);
ascendancy = params.get('a') || '';
passivesCompressed = params.get('p') || '';
});
const saveData = (e: Event) => {
e.preventDefault();
const data = { title, description, ascendancy, passivesCompressed };
if (selectedIndex >= 0) {
savedData[selectedIndex] = data;
} else {
savedData = [...savedData, data];
}
localStorage.setItem('savedData', JSON.stringify(savedData));
resetForm();
showModal.set(false);
};
const resetForm = () => {
title = '';
description = '';
selectedIndex = -1;
};
</script>

<div class="p-4 bg-[#1a1a1a] text-white rounded-md">
<h2 class="font-bold my-4 text-2xl">Save Build</h2>
<form onsubmit={saveData} class="space-y-6">
<div>
<label for="saveslot" class="text-sm leading-none block">Select Build</label>
<select
class="text-black mt-4 px-3 rounded-md h-10"
id="saveslot"
bind:value={selectedIndex}
onchange={() => {
if (selectedIndex >= 0) {
const selectedBuild = savedData[selectedIndex];
title = selectedBuild.title;
description = selectedBuild.description;
}
}}
>
<option value={-1}>New</option>
{#each savedData as entry, index}
<option value={index}>{entry.title}</option>
{/each}
</select>
</div>

<div>
<label for="title" class="text-sm leading-none">Title</label>
<input
type="text"
id="title"
bind:value={title}
class="px-3 py-2 text-sm h-10 w-full rounded-md text-black mt-2"
placeholder="Title"
required
/>
</div>

<div>
<label for="description">Description:</label>
<textarea
id="description"
bind:value={description}
class="px-3 py-2 text-sm w-full rounded-md text-black"
placeholder="Description"
rows="3"
></textarea>
</div>

<button type="submit" class="border border-white rounded md px-4 py-1">Save</button>
</form>
</div>
4 changes: 4 additions & 0 deletions src/lib/components/save-and-load/store.svelte.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { writable } from 'svelte/store';

export const showModal = writable<boolean>(false);
export const modalAction = writable<null | 'save' | 'load'>(null);
6 changes: 6 additions & 0 deletions src/lib/components/save-and-load/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface SaveData {
title: string;
description: string;
ascendancy: string;
passivesCompressed: string;
}
3 changes: 3 additions & 0 deletions src/lib/components/ui/header/header.svelte
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script lang="ts">
import { LoadModalBtn, SaveModalBtn } from '$lib/components/save-and-load';
</script>

<div class="flex flex-row gap-4 items-center w-full bg-[#1a1a1a] p-2 h-16">
Expand All @@ -9,6 +10,8 @@
<!-- Middle Part -->
<nav class="hidden md:flex flex-row items-center gap-4 flex-1">
<!-- Something here? -->
<SaveModalBtn />
<LoadModalBtn />
</nav>
<!-- GitHub -->
<nav class="flex flex-row items-center gap-4">
Expand Down
3 changes: 3 additions & 0 deletions src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import TreeNode from '$lib/components/ui/tree-node/tree-node.svelte';
import NodeListItem from '$lib/components/ui/node-list-item/node-list-item.svelte';
import LZString from 'lz-string';
import { SaveAndLoadModal } from '$lib/components/save-and-load';
let { nodes } = loadData();
Expand Down Expand Up @@ -751,4 +752,6 @@
</div>
</div>
</div>

<SaveAndLoadModal />
</div>

0 comments on commit 172625b

Please sign in to comment.