Skip to content

Commit

Permalink
Homepage (#319)
Browse files Browse the repository at this point in the history
* feat: created homepage

* refactor: moved buttons to card footer

* feat: added more cards

* feat: setup masonry layout for cards

* fix: fixed masonry layout (for now)

* fix: Run prettier format

* fix: fixed temp info in cards
fix: added temp link to homepage in root

* fix: fixed formatting

* refactor: Changed all text to use Text component

* feat: Pity widget (uncompleted)
chore: added more temp data to reminder
feat: started work on Achievement widget

* fix: fixed formatting, commented unused code

* feat: added progress component (shadcn)
fix: fixed colors in progress component
feat: Achievement widget (almost complete)

* fix: Run Prettier formatter

* fix: Text component now doesnt have margin by default, has margin property instead

* fix: Run formatter

* fix: fixed achievement widget
fix: fixed progress component colors to work with new color system

* feat: added a bunch of components for future development

* fix: added dependencies

* refactor: removed unneeded comments
refactor: using tuples instead of arrays

* feat: removed dragdrop pkg, took svelte file and modified it
feat: added Edit menu

(ts is angry at dragdrop svelte, because there's no type declaration)

* refactor: commented unused code

* fix: fixed dragdrop to work with ts type system

* fix: formatter

* feat: added more widgets and a welcome dialog
fix: dragdrop now works
chore: ran prettier

* chore: ran prettier

* feat: changed most values to variables instead of plain text
feat: resin tracker now almost fully working (lacks realtime resin regeneration)
feat: display is coming together
feat: wish buttons now work

* feat: added underline when hovering and focusing on resin tracker input
refactor: total wishes now take info directly from the userProfile
feat: moved and changed total wishes in wishing stats widget
feat: moved up wishing page link in pity widget and swapped buttons in wishing stats widget
fix: text colors are now using tailwind theme colors
fix: dark theme now uses brighter color for five stars
fix: resin now wont overflow 2000 when increasing with button
refactor: fixed misspell "standart" in variable names

* refactor: total wishes counter is now readable
feat: wrote code for getting some achievements data from userProfile

Feat is commented for now until we have achievement data in userProfile

* feat: added localization for the dashboard
chore: added .vscode to gitignore

* refactor: moved the homepage to the root

* chore: regenerate lockfile

---------

Co-authored-by: Alexander König <[email protected]>
Co-authored-by: Ludovic Malot <[email protected]>
  • Loading branch information
3 people authored Aug 13, 2024
1 parent 628e239 commit df7e2a7
Show file tree
Hide file tree
Showing 15 changed files with 1,812 additions and 35 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ vite.config.js.timestamp-*
vite.config.ts.timestamp-*
.idea
.vscode
/.npm-only-allow
/.npm-only-allow
295 changes: 295 additions & 0 deletions src/lib/components/ui/dragdrop/DragDropList.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
<script lang="ts">
import { flip } from 'svelte/animate';
import { Label } from '$lib/components/ui/label';
import { Checkbox } from '$lib/components/ui/checkbox';
import Text from '$lib/components/typography/Text.svelte';
class DatumType {
id!: number;
check?: string;
checked?: boolean;
html?: string;
text?: string;
}
export let data: DatumType[] = [];
export let removesItems = false;
let ghost: HTMLElement;
let grabbed: HTMLElement | null;
let lastTarget: Element;
let mouseY = 0; // pointer y coordinate within client
let offsetY = 0; // y distance from top of grabbed element to pointer
let layerY = 0; // distance from top of list to top of client
function grab(clientY: number, element: HTMLElement) {
// modify grabbed element
grabbed = element;
grabbed.dataset.grabY = String(clientY);
// modify ghost element (which is actually dragged)
ghost.innerHTML = grabbed.innerHTML;
// record offset from cursor to top of element
// (used for positioning ghost)
offsetY = grabbed.getBoundingClientRect().y - clientY;
drag(clientY);
}
// drag handler updates cursor position
function drag(clientY: number) {
if (grabbed) {
mouseY = clientY;
layerY = ghost.parentElement!.getBoundingClientRect().y;
}
}
// touchEnter handler emulates the mouseenter event for touch input
// (more or less)
function touchEnter(ev: Touch) {
drag(ev.clientY);
// trigger dragEnter the first time the cursor moves over a list item
let target = document
.elementFromPoint(ev.clientX, ev.clientY)!
.closest('.item') as HTMLElement;
if (target && target != lastTarget) {
lastTarget = target;
dragEnter(target);
}
}
function dragEnter(target: EventTarget) {
if (!(target instanceof HTMLElement)) return;
// swap items in data
if (grabbed && target != grabbed && target.classList.contains('item')) {
moveDatum(parseInt(grabbed.dataset.index!), parseInt(target.dataset.index!));
}
}
// does the actual moving of items in data
function moveDatum(from: number, to: number) {
let temp = data[from];
data = [...data.slice(0, from), ...data.slice(from + 1)];
data = [...data.slice(0, to), temp, ...data.slice(to)];
}
function release() {
grabbed = null;
}
function removeDatum(index: number) {
data = [...data.slice(0, index), ...data.slice(index + 1)];
}
function check(index: number) {
data[index].checked = !data[index].checked;
}
</script>

<!-- All the documentation has to go up here, sorry.
(otherwise it conflicts with the HTML or svelte/animate)
The .list has handlers for pointer movement and pointer up/release/end.
Each .item has a handler for pointer down/click/start, which assigns that
element as the item currently being "grabbed". They also have a handler
for pointer enter (the touchmove handler has extra logic to behave like the
no longer extant 'touchenter'), which swaps the entered element with the
grabbed element when triggered.
You'll also find reactive styling below, which keeps it from being directly
part of the imperative javascript handlers. -->
<main class="basis-[100%]">
<div
bind:this={ghost}
id="ghost"
class={grabbed ? 'item haunting' : 'item'}
style={'top: ' + (mouseY + offsetY - layerY) + 'px'}
>
<p></p>
</div>
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
class="list"
on:mousemove={function (ev) {
ev.stopPropagation();
drag(ev.clientY);
}}
on:touchmove={function (ev) {
ev.stopPropagation();
drag(ev.touches[0].clientY);
}}
on:mouseup={function (ev) {
ev.stopPropagation();
release();
}}
on:touchend={function (ev) {
ev.stopPropagation();
release();
}}
>
{#each data as datum, i (datum.id ? datum.id : JSON.stringify(datum))}
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
id={grabbed && (datum.id ? datum.id : JSON.stringify(datum)) == grabbed.dataset.id
? 'grabbed'
: ''}
class="item"
data-index={i}
data-id={datum.id}
data-grabY="0"
on:mousedown={function (ev) {
grab(ev.clientY, ev.currentTarget);
}}
on:touchstart={function (ev) {
grab(ev.touches[0].clientY, ev.currentTarget);
}}
on:mouseenter={function (ev) {
ev.stopPropagation();
dragEnter(ev.currentTarget);
}}
on:touchmove={function (ev) {
ev.stopPropagation();
ev.preventDefault();
touchEnter(ev.touches[0]);
}}
animate:flip={{ duration: 200 }}
>
<div class="buttons">
<button
class="up"
style={'visibility: ' + (i > 0 ? '' : 'hidden') + ';'}
on:click={function (ev) {
moveDatum(i, i - 1);
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16px"
height="16px"
>
<path d="M0 0h24v24H0V0z" fill="none" />
<path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6 1.41 1.41z" />
</svg>
</button>
<button
class="down"
style={'visibility: ' + (i < data.length - 1 ? '' : 'hidden') + ';'}
on:click={function (ev) {
moveDatum(i, i + 1);
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16px"
height="16px"
>
<path d="M0 0h24v24H0V0z" fill="none" />
<path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z" />
</svg>
</button>
</div>

<div class="content m-0 w-full">
{#if datum.html}
{@html datum.html}
{:else if datum.text}
<p>{datum.text}</p>
{:else if datum.check}
<div class="w-full h-full flex flex-row items-center justify-between px-16">
<Label
for={datum.check
.replace(/[^a-zA-Z]/g, '')
.replace(/(?:^\w|[A-Z]|\b\w)/g, (match, index) =>
index === 0 ? match.toLowerCase() : match.toUpperCase()
)
.concat('Check')}
>
<Text type="h4">
{datum.check}
</Text>
</Label>
<Checkbox
on:click={(e) => check(i)}
class=""
id={datum.check
.replace(/[^a-zA-Z]/g, '')
.replace(/(?:^\w|[A-Z]|\b\w)/g, (match, index) =>
index === 0 ? match.toLowerCase() : match.toUpperCase()
)
.concat('Check')}
checked={data[i].checked}
/>
</div>
{:else}
<p>{datum}</p>
{/if}
</div>

<div class="buttons delete">
{#if removesItems}
<button
on:click={function (ev) {
removeDatum(i);
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
height="16"
viewBox="0 0 24 24"
width="16"
>
<path d="M0 0h24v24H0z" fill="none" />
<path
d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"
/>
</svg>
</button>
{/if}
</div>
</div>
{/each}
</div>
</main>

<style>
main {
position: relative;
}
.list {
@apply cursor-grab z-[5] flex flex-col shrink;
}
.item {
@apply bg-foreground box-border inline-flex w-full min-h-[3em] border select-none mb-[0.5em] rounded-sm border-solid border-black;
}
.item:last-child {
@apply mb-0;
}
.item:not(#grabbed):not(#ghost) {
@apply z-10;
}
.buttons {
@apply w-8 min-w-[32px] flex flex-col mx-0 my-auto;
}
.buttons button {
@apply cursor-pointer w-[18px] h-[18px] border bg-inherit mx-auto my-0 p-0 border-solid border-[rgba(0,0,0,0)] focus:border focus:border-solid focus:border-[black];
@apply invert;
}
.delete {
@apply w-8;
}
#grabbed {
@apply opacity-[0.0];
}
#ghost {
@apply pointer-events-none z-[-5] absolute opacity-[0.0] left-0 top-0;
}
#ghost * {
@apply pointer-events-none;
}
#ghost.haunting {
@apply z-20 opacity-[1.0];
}
</style>
47 changes: 47 additions & 0 deletions src/lib/locales/DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"action.yes": "Ja",
"action.no": "Nein",
"action.confirm": "Bestätigen",
"action.cancel": "Cancel",
"action.sort_by": "Sortieren nach: {{sortFN}}",
"action.filter_by": "Filtern nach",
"navigation.category.collection": "Deine Sammlung",
Expand All @@ -18,6 +19,52 @@
"navigation.logout": "Abmelden",
"error.title": "Fehler",
"dashboard.title": "Startseite",
"dashboard.welcome.title": "Welcome to Dval.in!",
"dashboard.welcome.message": "Your best Genshin Impact companion! Dval.in helps you plan what to farm with an ascension calculator, and it also tracks your progress with a todo list and a wish counter.",
"dashboard.edit_layout.button": "Edit",
"dashboard.edit_layout.title": "Edit the layout",
"dashboard.edit_layout.description": "Edit your homepage layout to fit your needs.",
"dashboard.widget.changelog.title": "Changelog",
"dashboard.widget.reminder.title": "Reminder",
"dashboard.widget.reminder.daily": "Daily",
"dashboard.widget.reminder.daily.teapot_currency": "Teapot currency and friendship",
"dashboard.widget.reminder.daily.commissions": "Daily commissions",
"dashboard.widget.reminder.daily.resin": "Spend resin",
"dashboard.widget.reminder.daily.welkin": "Welkin moon",
"dashboard.widget.reminder.two_day.title": "Every 2 days",
"dashboard.widget.reminder.two_day.local_specialties": "Local specialties",
"dashboard.widget.reminder.two_day.ores": "Ores",
"dashboard.widget.reminder.three_day.title": "Every 3 days",
"dashboard.widget.reminder.three_day.crystals": "Crystal chunks",
"dashboard.widget.reminder.three_day.teapot_farm": "Teapot farms",
"dashboard.widget.reminder.three_day.fishing": "Fishing",
"dashboard.widget.reminder.weekly.title": "Weekly",
"dashboard.widget.reminder.weekly.parametric_transformer": "Parametric transformer",
"dashboard.widget.reminder.weekly.crystalfly_trap": "Crystalfly trap",
"dashboard.widget.reminder.weekly.bosses": "Weekly bosses",
"dashboard.widget.reminder.weekly.reputation": "Reputation bounties",
"dashboard.widget.events.title": "Current Events",
"dashboard.widget.events.version": "For version",
"dashboard.widget.events.timeline.button": "View timeline",
"dashboard.widget.todo.title": "To-do List",
"dashboard.widget.domain.title": "Domain Rotation",
"dashboard.widget.achievements.title": "Achievements",
"dashboard.widget.achievements.progress": "Overall progress",
"dashboard.widget.achievements.latest": "Latest achievements",
"dashboard.widget.achievements.more": "View more achievements",
"dashboard.widget.pity.title": "Pity",
"dashboard.widget.wishing.title": "Wishing stats",
"dashboard.widget.wishing.pity": "Pity",
"dashboard.widget.wishing.total": "Total wishes",
"dashboard.widget.wishing.latest": "Latest pulls",
"dashboard.widget.display.title": "Display",
"dashboard.widget.global_wishing.title": "Global wishing stats",
"dashboard.widget.global_wishing.percentage": "of all",
"dashboard.widget.global_wishing.pity_average": "Pity average",
"dashboard.widget.global_wishing.based_on": "based on {{number}} submissions",
"dashboard.widget.resin.title": "Resin tracker",
"dashboard.widget.resin.hour": "h",
"dashboard.widget.resin.minute": "m",
"wish.detailed.title.WeaponEvent": "Verlauf des Waffengebets",
"wish.detailed.title.Standard": "Verlauf des Standartgebets",
"wish.detailed.title.CharacterEvent": "Verlauf des Charaktergebets",
Expand Down
Loading

0 comments on commit df7e2a7

Please sign in to comment.