Add todo page

This commit is contained in:
I Made Setia Baruna 2020-11-09 02:57:56 +07:00
parent abe600373f
commit a2f942c1eb
19 changed files with 1648 additions and 37 deletions

View file

@ -37,6 +37,7 @@
"rollup-plugin-terser": "^7.0.0",
"sapper": "^0.28.0",
"svelte": "^3.17.3",
"svelte-masonry": "^0.0.17",
"svelte-preprocess": "^4.5.1",
"svelte-simple-modal": "^0.6.1",
"tailwindcss": "^1.9.5"

View file

@ -1,10 +1,40 @@
<script>
export let className;
export let className = '';
export let disabled = false;
export let size = 'md';
export let color = 'blue';
export let rounded = true;
let px = 4;
let py = 2;
let textColor = 'white';
let borderColor = 'white';
$: switch (color) {
case 'blue':
textColor = 'white';
borderColor = 'primary';
break;
case 'red':
textColor = 'red-400';
borderColor = 'red-400';
break;
}
$: switch (size) {
case 'md':
px = 4;
py = 2;
break;
case 'sm':
px = 1;
py = 1;
break;
}
</script>
<button
{disabled}
class={`text-white border-2 border-white border-opacity-25 rounded-xl px-4 py-2 transition duration-100
hover:border-primary focus:outline-none focus:border-primary disabled:opacity-50 disabled:border-gray-600 ${className}`}
class={`text-${textColor} border-2 border-white border-opacity-25 ${rounded ? 'rounded-xl' : 'rounded-none'} px-${px} py-${py} transition duration-100
hover:border-${borderColor} focus:outline-none focus:border-${borderColor} disabled:opacity-50 disabled:border-gray-600 ${className}`}
on:click><slot /></button>

View file

@ -1,6 +1,6 @@
<script>
export let checked = false;
export let disabled;
export let disabled = false;
</script>
<style>

View file

@ -2,10 +2,10 @@
import Icon from './Icon.svelte';
export let icon = null;
export let placeholder;
export let placeholder = '';
export let type = 'text';
export let min;
export let max;
export let min = Math.min();
export let max = Math.max();
export let value = '';

View file

@ -65,7 +65,7 @@
<SidebarItem
on:clicked={close}
active={segment === 'todo'}
image="/images/settings.png"
image="/images/todos.png"
label="Todo List"
href="/todo" />
<SidebarItem

View file

@ -30,7 +30,9 @@
<a on:click={clicked} class={`w-full rounded-xl ease-in duration-150 ${active ? 'active' : ''}`} {href}>
<div class="group w-full py-3 flex items-center px-6 cursor-pointer transition-colors">
<img class="w-8 h-8 mr-3 opacity-75 group-hover:opacity-100 ease-in duration-150" src={image} alt={label} />
<div class="h-8 w-8 flex justify-center mr-3 opacity-75 group-hover:opacity-100 ease-in duration-150">
<img class="h-full" src={image} alt={label} />
</div>
<span
class="font-body font-semibold text-lg leading-none text-gray-500 group-hover:text-white ease-in duration-150">{label}</span>
</div>

View file

@ -0,0 +1,40 @@
<script>
import { todos, loading } from '../stores/todo';
import { readSave, updateSave, fromRemote } from '../stores/saveManager';
import { onMount } from 'svelte';
let unsubscribe = null;
let firstLoad = true;
$: if ($fromRemote) {
readLocalData();
}
onMount(() => {
readLocalData();
});
function readLocalData() {
loading.set(true);
firstLoad = true;
if (unsubscribe) unsubscribe();
console.log('todo read local');
const data = readSave('todos');
if (data !== null) {
const todoList = JSON.parse(data);
todos.set(todoList);
}
unsubscribe = todos.subscribe((val) => {
if (firstLoad) return;
console.log('todos changed', val);
updateSave('todos', JSON.stringify(val));
});
firstLoad = false;
loading.set(false);
}
</script>

View file

@ -0,0 +1,36 @@
<script>
import Button from './Button.svelte';
export let todo;
export let deleteTodo;
export let cancel;
</script>
<div>
<p class="text-white font-bold mb-4 text-lg">Delete this todo?</p>
<div class="flex items-center mb-4 text-white">
{#if todo.type === 'weapon'}
<img
class="h-8 inline-block mr-2"
src={`/images/weapons/${todo.weapon ? todo.weapon.id : 'any_weapon_1'}.png`}
alt={todo.weapon ? todo.weapon.name : `Weapon Level ${todo.level.from}-${todo.level.to}`} />
<div class="flex-1">
<p class="font-bold">{todo.weapon ? todo.weapon.name : 'Weapon'}</p>
<p class="text-gray-500">Level {`${todo.level.from}-${todo.level.to}`}</p>
</div>
{:else if todo.type === 'character'}
<img
class="h-8 inline-block mr-2"
src={`/images/characters/${todo.character ? todo.character.id : 'characters'}.png`}
alt={todo.character ? todo.character.name : `Character Level ${todo.level.from}-${todo.level.to}`} />
<div class="flex-1">
<p class="font-bold">{todo.character ? todo.character.name : 'Character'}</p>
<p class="text-gray-500">Level {`${todo.level.from}-${todo.level.to}`}</p>
</div>
{/if}
</div>
<div class="flex justify-end gap-2">
<Button on:click={cancel}>Cancel</Button>
<Button on:click={deleteTodo} color="red">Delete</Button>
</div>
</div>

View file

@ -358,4 +358,8 @@ export const itemList = {
any_weapon_1: { id: 'any_weapon_1', name: '1 Star Weapon' },
any_weapon_2: { id: 'any_weapon_2', name: '2 Star Weapon' },
any_weapon_3: { id: 'any_weapon_3', name: '3 Star Weapon' },
mora: { id: 'mora', name: 'Mora' },
heros_wit: { id: 'heros_wit', name: "Hero's Wit" },
adventurers_experience: { id: 'adventurers_experience', name: "Adventurer's Experience" },
wanderes_advice: { id: 'wanderes_advice', name: "Wanderer's Advice" },
};

View file

@ -9,6 +9,7 @@
import { showSidebar } from '../stores/sidebar';
import { checkLocalSave } from '../stores/saveManager';
import TodoData from '../components/TodoData.svelte';
export let segment;
@ -31,6 +32,7 @@
{/if}
<Modal>
<DataSync>
<TodoData />
<main style="flex: 1 0 auto;">
<slot />
</main>

View file

@ -1,6 +1,6 @@
<script>
import { fade } from 'svelte/transition';
import { mdiClose, mdiInformationOutline } from '@mdi/js';
import { mdiCheckCircleOutline, mdiClose, mdiInformationOutline } from '@mdi/js';
import Input from '../../components/Input.svelte';
import AscensionSelector from '../../components/AscensionSelector.svelte';
@ -11,14 +11,23 @@
import Icon from '../../components/Icon.svelte';
import { characterExp } from '../../data/characterExp';
import { addTodo } from '../../stores/todo';
let resources = [
{ selected: true, disabled: false, image: '/images/items/heros_wit.png', label: "Hero's Wit", value: '20000' },
{
selected: true,
disabled: false,
image: '/images/items/heros_wit.png',
label: "Hero's Wit",
id: 'heros_wit',
value: '20000',
},
{
selected: true,
disabled: false,
image: '/images/items/adventurers_experience.png',
label: "Adventurer's Experience",
id: 'adventurers_experience',
value: '5000',
},
{
@ -26,10 +35,13 @@
disabled: false,
image: '/images/items/wanderes_advice.png',
label: "Wanderer's Advice",
id: 'wanderes_advice',
value: '1000',
},
];
let addedToTodo = false;
let withAscension = true;
let selectedCharacter = null;
@ -243,6 +255,40 @@
changed = false;
}
function addToTodo() {
const levelRes = usedResource.reduce((prev, item, i) => {
if (currentMax.usage[i] > 0) {
prev[item.id] = currentMax.usage[i];
}
return prev;
}, {});
const ascensionRes = Object.keys(ascensionResouce).reduce((prev, item) => {
if (ascensionResouce[item].amount > 0) {
prev[item] = ascensionResouce[item].amount;
}
return prev;
}, {});
addTodo({
type: 'character',
character: withAscension ? selectedCharacter : null,
level: { from: currentLevel, to: intendedLevel },
resources: {
mora: moraNeeded,
...levelRes,
...ascensionRes,
},
});
addedToTodo = true;
setTimeout(() => {
addedToTodo = false;
}, 2000);
}
</script>
<div class="bg-item rounded-xl p-4">
@ -388,6 +434,14 @@
</tr>
{/if}
</table>
<Button className="mt-2 w-full" on:click={addedToTodo ? () => {} : addToTodo}>
{#if addedToTodo}
<span class="text-green-400" in:fade={{ duration: 100 }}>
<Icon path={mdiCheckCircleOutline} size={0.8} />
Added to Todo List
</span>
{:else}<span in:fade={{ duration: 100 }}>Add to Todo List </span>{/if}
</Button>
</div>
{/if}
</div>

View file

@ -307,7 +307,7 @@
addTodo({
type: 'weapon',
weapon: selectedWeapon,
weapon: withAscension ? selectedWeapon : null,
level: { from: currentLevel, to: intendedLevel },
resources: {
mora: moraNeeded,
@ -473,7 +473,7 @@
</tr>
{/if}
</table>
<Button className="mt-2 w-full" on:click={addToTodo}>
<Button className="mt-2 w-full" on:click={addedToTodo ? () => {} : addToTodo}>
{#if addedToTodo}
<span class="text-green-400" in:fade={{ duration: 100 }}>
<Icon path={mdiCheckCircleOutline} size={0.8} />

View file

@ -1,6 +1,209 @@
<script>
import { getContext, tick } from 'svelte';
import { mdiChevronLeft, mdiChevronRight, mdiClose, mdiLoading } from '@mdi/js';
import { todos, loading } from '../stores/todo';
import { itemList } from '../data/itemList';
import Masonry from 'svelte-masonry/Masonry.svelte';
import Icon from '../components/Icon.svelte';
import Button from '../components/Button.svelte';
import TodoDeleteModal from '../components/TodoDeleteModal.svelte';
const { open: openModal, close: closeModal } = getContext('simple-modal');
let refreshLayout;
let numberFormat = Intl.NumberFormat();
let adding = false;
async function reorder(index, pos) {
if ((index === 0 && pos === -1) || (index === $todos.length - 1 && pos === 1)) return;
todos.update((val) => {
val.splice(index + pos, 0, val.splice(index, 1)[0]);
return val;
});
await tick();
refreshLayout();
}
async function deleteTodo(index) {
todos.update((val) => {
val.splice(index, 1);
return val;
});
await tick();
refreshLayout();
closeModal();
}
async function askDeleteTodo(index) {
if (index < 0 && index > $todos.length) return;
openModal(
TodoDeleteModal,
{
todo: $todos[index],
deleteTodo: () => deleteTodo(index),
cancel: closeModal,
},
{
closeButton: false,
styleWindow: { background: '#25294A', width: '400px' },
},
);
}
function decrease(key, val) {
todos.update((n) => {
let i = 0;
let leftover = val;
for (const current of n) {
const remaining = current.resources[key];
if (remaining !== undefined && remaining > 0) {
const reducedBy = Math.min(val, leftover, remaining);
n[i].resources[key] -= reducedBy;
leftover -= reducedBy;
if (leftover === 0) break;
}
i++;
}
return n;
});
}
$: summary = $todos.reduce((prev, current) => {
for (const [id, amount] of Object.entries(current.resources)) {
if (prev[id] === undefined) {
prev[id] = 0;
}
prev[id] += amount;
}
return prev;
}, {});
</script>
<svelte:head>
<title>Todo List - Paimon.moe</title>
</svelte:head>
<div class="lg:ml-64 pt-20 px-8 lg:pt-8">
<h1 class="font-display font-black text-5xl text-white">Todo List</h1>
<Masonry stretchFirst={true} bind:refreshLayout>
<h1 class="font-display font-black text-3xl lg:text-left lg:text-5xl text-white">Todo List</h1>
<div class="bg-item rounded-xl p-4 text-white">
{#if $loading}
<Icon path={mdiLoading} color="white" spin />
{:else if $todos.length > 0}
<p class="font-bold text-xl mb-4">Summary</p>
{:else}
<p class="font-bold text-xl">Nothing to do yet 😀<br />Add some here or from the Calculator!</p>
{/if}
<table class="w-full">
{#each Object.entries(summary) as [id, amount], i}
<tr>
<td class="text-right border-b border-gray-700 py-1">
<span class={`${amount === 0 ? 'line-through text-gray-600' : 'text-white'} mr-2 whitespace-no-wrap`}>
{numberFormat.format(amount)}
<Icon size={0.5} path={mdiClose} /></span>
</td>
<td class="border-b border-gray-700 py-1">
<span class={`${amount === 0 ? 'line-through text-gray-600' : 'text-white'} block mb-1`}>
<span class="w-6 inline-block">
<img class="h-6 inline-block mr-1" src={`/images/items/${id}.png`} alt={itemList[id].name} />
</span>
{itemList[id].name}
</span>
{#if id === 'mora'}
<Button size="sm" disabled={amount === 0 && !adding} on:click={() => decrease(id, 1000)}>
{adding ? '+' : '-'}1000
</Button>
<Button size="sm" disabled={amount === 0 && !adding} on:click={() => decrease(id, 10000)}>
{adding ? '+' : '-'}10000
</Button>
<Button size="sm" disabled={amount === 0 && !adding} on:click={() => decrease(id, 50000)}>
{adding ? '+' : '-'}50000
</Button>
{:else}
<Button size="sm" disabled={amount === 0 && !adding} className="w-10" on:click={() => decrease(id, 1)}>
{adding ? '+' : '-'}1
</Button>
<Button size="sm" disabled={amount === 0 && !adding} className="w-10" on:click={() => decrease(id, 5)}>
{adding ? '+' : '-'}5
</Button>
<Button size="sm" disabled={amount === 0 && !adding} className="w-10" on:click={() => decrease(id, 10)}>
{adding ? '+' : '-'}10
</Button>
{/if}
</td>
</tr>
{/each}
</table>
</div>
{#each $todos as todo, i}
<div class="bg-item rounded-xl p-4 text-white">
<div class="flex items-center mb-2">
{#if todo.type === 'weapon'}
<img
class="h-8 inline-block mr-2"
src={`/images/weapons/${todo.weapon ? todo.weapon.id : 'any_weapon_1'}.png`}
alt={todo.weapon ? todo.weapon.name : `Weapon Level ${todo.level.from}-${todo.level.to}`} />
<div class="flex-1">
<p class="font-bold">{todo.weapon ? todo.weapon.name : 'Weapon'}</p>
<p class="text-gray-500">Level {`${todo.level.from}-${todo.level.to}`}</p>
</div>
{:else if todo.type === 'character'}
<img
class="h-8 inline-block mr-2"
src={`/images/characters/${todo.character ? todo.character.id : 'characters'}.png`}
alt={todo.character ? todo.character.name : `Character Level ${todo.level.from}-${todo.level.to}`} />
<div class="flex-1">
<p class="font-bold">{todo.character ? todo.character.name : 'Character'}</p>
<p class="text-gray-500">Level {`${todo.level.from}-${todo.level.to}`}</p>
</div>
{/if}
<Button disabled={i === 0} on:click={() => reorder(i, -1)} rounded={false} size="sm" className="rounded-l-xl">
<Icon path={mdiChevronLeft} color="white" />
</Button>
<Button
disabled={i === $todos.length - 1}
on:click={() => reorder(i, 1)}
rounded={false}
size="sm"
className="rounded-r-xl">
<Icon path={mdiChevronRight} color="white" />
</Button>
</div>
<table class="w-full">
{#each Object.entries(todo.resources) as [id, amount]}
<tr>
<td class="text-right border-b border-gray-700 py-1">
<span class={`${amount === 0 ? 'line-through text-gray-600' : 'text-white'} mr-2 whitespace-no-wrap`}>
{numberFormat.format(amount)}
<Icon size={0.5} path={mdiClose} /></span>
</td>
<td class="border-b border-gray-700 py-1">
<span class={amount === 0 ? 'line-through text-gray-600' : 'text-white'}>
<span class="w-6 inline-block">
<img class="h-6 inline-block mr-1" src={`/images/items/${id}.png`} alt={itemList[id].name} />
</span>
{itemList[id].name}
</span>
</td>
</tr>
{/each}
</table>
<div class="flex mt-2 items-end">
<p class="flex-1 text-gray-400"># {i + 1}</p>
<Button on:click={() => askDeleteTodo(i)} size="sm" className="px-2">
<Icon path={mdiClose} color="white" size={0.8} />
Delete
</Button>
</div>
</div>
{/each}
</Masonry>
</div>

View file

@ -1,5 +1,6 @@
import { writable } from 'svelte/store';
export const loading = writable(true);
export const todos = writable([]);
export function addTodo(data) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
static/images/todos.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 942 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

1284
yarn.lock

File diff suppressed because it is too large Load diff