diff --git a/src/components/AccordionFilter.svelte b/src/components/AccordionFilter.svelte
new file mode 100644
index 0000000..48658c7
--- /dev/null
+++ b/src/components/AccordionFilter.svelte
@@ -0,0 +1,22 @@
+
+
+
+
+
e.key === "Escape" && (value = "")} />
+ {#if value}
+
+ {/if}
+
diff --git a/src/components/destinations/BookmarksDestination.svelte b/src/components/destinations/BookmarksDestination.svelte
index 147af35..32813d2 100644
--- a/src/components/destinations/BookmarksDestination.svelte
+++ b/src/components/destinations/BookmarksDestination.svelte
@@ -2,6 +2,8 @@
import kipclipLogo from "../../assets/kipclip-logo.png?url";
import sillLogo from "../../assets/sill-logo.png?url";
import marginLogo from "../../assets/margin-logo.png?url";
+ import AccordionFilter from "../AccordionFilter.svelte";
+ import { ACCORDION_FILTER_THRESHOLD } from "../../lib/constants";
let {
tags,
@@ -13,12 +15,19 @@
checkedTags: string[];
} = $props();
- function onTagChange() {
- if (checkedTags.length > 0) checked = true;
- }
+ let filter = $state("");
+
+ const filteredTags = $derived(
+ filter
+ ? tags.filter((t) => t.toLowerCase().includes(filter.toLowerCase()))
+ : tags
+ );
function onCheckedChange() {
- if (!checked) checkedTags = [];
+ if (!checked) {
+ checkedTags = [];
+ filter = "";
+ }
}
@@ -34,12 +43,32 @@
{#if tags.length === 0}
No tags found.
{:else}
- {#each tags as tag (tag)}
-
- {/each}
+ {#if tags.length >= ACCORDION_FILTER_THRESHOLD}
+
+ {/if}
+
{/if}
diff --git a/src/components/destinations/CollectionsDestination.svelte b/src/components/destinations/CollectionsDestination.svelte
index fd0c8ad..f26cdab 100644
--- a/src/components/destinations/CollectionsDestination.svelte
+++ b/src/components/destinations/CollectionsDestination.svelte
@@ -2,6 +2,8 @@
import sembleLogo from "../../assets/semble-logo.png?url";
import marginLogo from "../../assets/margin-logo.png?url";
import rabbitholeLogo from "../../assets/rabbithole-logo.png?url";
+ import AccordionFilter from "../AccordionFilter.svelte";
+ import { ACCORDION_FILTER_THRESHOLD } from "../../lib/constants";
interface Collection {
uri: string;
@@ -19,10 +21,18 @@
let open = $state(false);
let loaded = $state(false);
+ let filter = $state("");
+
+ const filtered = $derived(
+ filter
+ ? collections.filter((c) => c.name.toLowerCase().includes(filter.toLowerCase()))
+ : collections
+ );
function toggle() {
open = !open;
if (open) loaded = true;
+ else filter = "";
}
@@ -44,12 +54,31 @@
{#if collections.length === 0}
No collections found.
{:else}
- {#each collections as collection (collection.uri)}
-
- {/each}
+ {#if collections.length >= ACCORDION_FILTER_THRESHOLD}
+
+ {/if}
+
{/if}
{/if}
diff --git a/src/lib/constants.ts b/src/lib/constants.ts
index 86154ff..46f7b13 100644
--- a/src/lib/constants.ts
+++ b/src/lib/constants.ts
@@ -1,2 +1,4 @@
+export const ACCORDION_FILTER_THRESHOLD = 5;
+
export const OAUTH_SCOPE =
"atproto include:app.bsky.authCreatePosts include:at.margin.authFull include:network.cosmik.authFull blob:image/* repo:com.kipclip.annotation repo:com.kipclip.tag repo:com.kipclip.preferences repo:at.margin.bookmark repo:community.lexicon.bookmarks.bookmark";
diff --git a/src/styles/global.css b/src/styles/global.css
index 14d9d30..d174856 100644
--- a/src/styles/global.css
+++ b/src/styles/global.css
@@ -286,3 +286,53 @@ button[type="submit"]:disabled { opacity: 0.35; cursor: not-allowed; }
color: var(--muted);
border-top: 1px solid var(--border);
}
+
+.accordion-filter {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.6rem 1rem;
+ border-top: 1px solid var(--border);
+ color: var(--muted);
+}
+
+.accordion-filter input[type="text"] {
+ flex: 1;
+ width: auto;
+ border: none;
+ background: transparent;
+ padding: 0;
+ font-size: 1rem;
+ min-width: 0;
+}
+
+.accordion-filter input[type="text"]:focus {
+ border-color: transparent;
+ background: transparent;
+}
+
+.accordion-filter-clear {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: none;
+ border: none;
+ padding: 2px;
+ cursor: pointer;
+ color: var(--muted);
+ border-radius: 50%;
+ flex-shrink: 0;
+ transition: color 0.15s;
+}
+.accordion-filter-clear:hover { color: var(--text); }
+
+.accordion-scroll {
+ max-height: calc(4.5 * 2.5rem);
+ overflow-y: auto;
+ scrollbar-width: thin;
+ scrollbar-color: var(--border) transparent;
+}
+
+.accordion-scroll::-webkit-scrollbar { width: 4px; }
+.accordion-scroll::-webkit-scrollbar-track { background: transparent; }
+.accordion-scroll::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }