aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFuwn <[email protected]>2025-05-14 01:43:10 -0700
committerFuwn <[email protected]>2025-05-14 01:43:10 -0700
commit93901232b9836a6add3e75c7c7ec745371d44cec (patch)
tree6753d192536ae5f1174fe3bc043317bbeaf1eb34 /src
parentfeat(CommandPalette): Global toggle fading (diff)
downloaddue.moe-93901232b9836a6add3e75c7c7ec745371d44cec.tar.xz
due.moe-93901232b9836a6add3e75c7c7ec745371d44cec.zip
feat(CommandPalette): Animate search results
Diffstat (limited to 'src')
-rw-r--r--src/lib/CommandPalette/CommandPalette.svelte65
1 files changed, 48 insertions, 17 deletions
diff --git a/src/lib/CommandPalette/CommandPalette.svelte b/src/lib/CommandPalette/CommandPalette.svelte
index 56542270..538eea98 100644
--- a/src/lib/CommandPalette/CommandPalette.svelte
+++ b/src/lib/CommandPalette/CommandPalette.svelte
@@ -1,5 +1,7 @@
<script lang="ts">
import { onMount } from 'svelte';
+ import { fly, fade } from 'svelte/transition';
+ import { flip } from 'svelte/animate';
interface CommandPaletteItem {
name: string;
@@ -17,10 +19,17 @@
let inputRef: HTMLInputElement;
let isVisible = false;
let timeoutID: ReturnType<typeof setTimeout> | null = null;
+ let itemIDs = new Map<string, number>();
- $: filtered = items
- .filter((item) => item.name.toLowerCase().includes(search.toLowerCase()))
- .slice(0, 10);
+ $: {
+ items.forEach((item, index) => {
+ if (!itemIDs.has(item.url)) itemIDs.set(item.url, index);
+ });
+
+ filtered = items
+ .filter((item) => item.name.toLowerCase().includes(search.toLowerCase()))
+ .slice(0, 10);
+ }
$: if (selectedIndex >= filtered.length) selectedIndex = filtered.length - 1;
$: if (selectedIndex < 0 && filtered.length > 0) selectedIndex = 0;
@@ -103,6 +112,7 @@
<div
class="command-palette-overlay {open ? 'fade-in' : 'fade-out'}"
on:click={() => (open = false)}
+ transition:fade={{ duration: 150 }}
/>
<div class="dropdown {open ? 'fade-in' : 'fade-out'}">
@@ -115,20 +125,29 @@
on:keydown={handleKey}
/>
- {#each filtered as item, i}
- <a
- href={item.url}
- class="header-item {selectedIndex === i ? 'selected' : ''}"
- on:click={(e) => {
- if (item.preventDefault) e.preventDefault();
- if (item.onClick) item.onClick();
-
- open = false;
- }}
- >
- {item.name}
- </a>
- {/each}
+ <div class="results-container">
+ {#each filtered as item, i (item.url)}
+ <a
+ href={item.url}
+ class="header-item {selectedIndex === i ? 'selected' : ''}"
+ in:fly={{ y: 20, duration: 150, delay: i * 30 }}
+ out:fly={{ y: -20, duration: 150 }}
+ animate:flip={{ duration: 200 }}
+ on:click={(e) => {
+ if (item.preventDefault) e.preventDefault();
+ if (item.onClick) item.onClick();
+
+ open = false;
+ }}
+ >
+ {item.name}
+ </a>
+ {/each}
+
+ {#if filtered.length === 0 && search !== ''}
+ <div class="no-results opaque" in:fade={{ duration: 150 }}>No results found</div>
+ {/if}
+ </div>
</div>
</div>
{/if}
@@ -150,6 +169,17 @@
outline: none;
}
+ .no-results {
+ padding: 0.75em;
+ text-align: center;
+ }
+
+ .results-container {
+ max-height: 60vh;
+ overflow-y: auto;
+ overflow-x: hidden;
+ }
+
.command-palette-overlay {
position: fixed;
top: 0;
@@ -261,6 +291,7 @@
margin: 0.5em 0.75em;
backdrop-filter: blur(0px);
font-weight: 450;
+ transform-origin: center left;
}
.dropdown-content a:hover,