aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFuwn <[email protected]>2024-01-31 18:01:16 -0800
committerFuwn <[email protected]>2024-01-31 18:01:16 -0800
commit25e25ead288af9d3cfd7843ef7cbfc773b96f6f0 (patch)
tree6e1918807a016e21ba53bb702a28835c18bd8a6a /src
parentdeps(bun): update lockfile (diff)
downloaddue.moe-25e25ead288af9d3cfd7843ef7cbfc773b96f6f0.tar.xz
due.moe-25e25ead288af9d3cfd7843ef7cbfc773b96f6f0.zip
refactor(layout): dropdown component
Diffstat (limited to 'src')
-rw-r--r--src/lib/Dropdown.svelte105
-rw-r--r--src/routes/+layout.svelte89
2 files changed, 114 insertions, 80 deletions
diff --git a/src/lib/Dropdown.svelte b/src/lib/Dropdown.svelte
new file mode 100644
index 00000000..efbafa1a
--- /dev/null
+++ b/src/lib/Dropdown.svelte
@@ -0,0 +1,105 @@
+<script lang="ts">
+ interface Item {
+ name: string;
+ url: string;
+ }
+
+ export let items: Item[] = [];
+ export let title: string;
+
+ let open = false;
+
+ const handleClickOutside = (event: any) => {
+ if (!event.target.closest('.dropdown')) open = false;
+ };
+</script>
+
+<svelte:window on:click={handleClickOutside} />
+
+<div class="dropdown" id="dropdown">
+ <span
+ class="header-item dropdown-toggle"
+ id="dropdown-toggle"
+ on:click|preventDefault={() => (open = !open)}
+ on:keydown={() => {}}
+ role="button"
+ tabindex="0"
+ >
+ {title}
+ </span>
+
+ <div class={`dropdown-content card card-small ${open ? 'dropdown-open' : ''}`}>
+ {#each items as item}
+ <a href={item.url} class="header-item">
+ {item.name}
+ </a>
+ {/each}
+ </div>
+</div>
+
+<style lang="scss">
+ a {
+ color: var(--base06);
+ }
+
+ .header-item {
+ margin: 0 0.5rem;
+ }
+
+ .header-item:hover {
+ text-decoration: none;
+ }
+
+ .header-item:active {
+ outline: none;
+ }
+
+ .dropdown {
+ position: relative;
+ display: inline-block;
+ }
+
+ .dropdown-content {
+ display: block;
+ position: absolute;
+ min-width: max-content;
+ padding: 0.5em 0;
+ opacity: 0;
+ transform: translateY(-20px);
+ visibility: hidden;
+ $delay: 0.25s;
+ transition: opacity $delay ease, transform $delay ease, visibility 0s linear $delay;
+ left: 50%;
+ transform: translateX(-50%);
+ }
+
+ .dropdown-open {
+ opacity: 1;
+ transform: translateY(0);
+ visibility: visible;
+ transition-delay: 0s, 0s, 0s;
+ left: 50%;
+ transform: translateX(-50%);
+ }
+
+ .dropdown:hover .dropdown-content {
+ opacity: 1;
+ transform: translateY(0);
+ visibility: visible;
+ transition-delay: 0s, 0s, 0s;
+ left: 50%;
+ transform: translateX(-50%);
+ }
+
+ .dropdown-content a {
+ padding: 0.5em 0.75em;
+ text-decoration: none;
+ display: block;
+ }
+
+ .dropdown-content a:hover {
+ border-radius: 8px;
+ backdrop-filter: blur(160px);
+ background-color: var(--base01);
+ }
+</style>
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index a29529d7..7fb24a50 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -20,11 +20,10 @@
import locale from '$stores/locale';
import Skeleton from '$lib/Loading/Skeleton.svelte';
import subsPlease from '$stores/subsPlease';
+ import Dropdown from '$lib/Dropdown.svelte';
export let data;
- let dropdownOpen = false;
-
addMessages('en', english as unknown as LocaleDictionary);
addMessages('ja', japanese as unknown as LocaleDictionary);
init({ fallbackLocale: 'en', initialLocale: $settings.displayLanguage });
@@ -73,12 +72,6 @@
});
});
- const handleClickOutside = (event: any) => {
- if (!event.target.closest('.dropdown')) {
- dropdownOpen = false;
- }
- };
-
$: {
if ((data.url === '/' || data.url === '/completed') && !$subsPlease)
fetch(root(`/api/subsplease?tz=${Intl.DateTimeFormat().resolvedOptions().timeZone}`))
@@ -87,8 +80,6 @@
}
</script>
-<svelte:window on:click={handleClickOutside} />
-
<HeadTitle />
<div id="container">
@@ -100,27 +91,14 @@
>
{$locale().navigation.completed}
</a>
- <div class="dropdown" id="dropdown">
- <span
- class="header-item dropdown-toggle"
- id="dropdown-toggle"
- on:click|preventDefault={() => (dropdownOpen = !dropdownOpen)}
- on:keydown={() => {}}
- role="button"
- tabindex="0"
- >
- {$locale().navigation.schedule}
- </span>
-
- <div class={`dropdown-content card card-small ${dropdownOpen ? 'dropdown-open' : ''}`}>
- <a href={root('/schedule')} class="header-item">{$locale().navigation.subtitleSchedule}</a
- >
- <a href={root('/updates')} class="header-item">{$locale().navigation.newReleases}</a>
- <a href={root('/birthdays')} class="header-item">
- {$locale().tools.tool.characterBirthdays.short}
- </a>
- </div>
- </div>
+ <Dropdown
+ items={[
+ { name: $locale().navigation.subtitleSchedule, url: root('/schedule') },
+ { name: $locale().navigation.newReleases, url: root('/updates') },
+ { name: $locale().tools.tool.characterBirthdays.short, url: root('/birthdays') }
+ ]}
+ title={$locale().navigation.schedule}
+ />
<a href={root('/tools')} class="header-item">{$locale().navigation.tools}</a>
<a href={root('/settings')} class="header-item">{$locale().navigation.settings}</a>
@@ -266,53 +244,4 @@
border-radius: 8px;
box-shadow: 0 1.5px 9px var(--base01), 0 0 0 4px var(--base0E), 0 4px 30px var(--base01);
}
-
- .dropdown {
- position: relative;
- display: inline-block;
- }
-
- .dropdown-content {
- display: block;
- position: absolute;
- min-width: max-content;
- padding: 0.5em 0;
- opacity: 0;
- transform: translateY(-20px);
- visibility: hidden;
- $delay: 0.25s;
- transition: opacity $delay ease, transform $delay ease, visibility 0s linear $delay;
- left: 50%;
- transform: translateX(-50%);
- }
-
- .dropdown-open {
- opacity: 1;
- transform: translateY(0);
- visibility: visible;
- transition-delay: 0s, 0s, 0s;
- left: 50%;
- transform: translateX(-50%);
- }
-
- .dropdown:hover .dropdown-content {
- opacity: 1;
- transform: translateY(0);
- visibility: visible;
- transition-delay: 0s, 0s, 0s;
- left: 50%;
- transform: translateX(-50%);
- }
-
- .dropdown-content a {
- padding: 0.5em 0.75em;
- text-decoration: none;
- display: block;
- }
-
- .dropdown-content a:hover {
- border-radius: 8px;
- backdrop-filter: blur(160px);
- background-color: var(--base01);
- }
</style>