mirror of
https://github.com/polaroi8d/cactoide.git
synced 2026-03-22 06:05:28 +00:00
feat: add fuse.js search module
This commit is contained in:
33
package-lock.json
generated
33
package-lock.json
generated
@@ -10,13 +10,13 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sveltejs/adapter-node": "^5.3.1",
|
"@sveltejs/adapter-node": "^5.3.1",
|
||||||
"drizzle-orm": "^0.44.5",
|
"drizzle-orm": "^0.44.5",
|
||||||
|
"fuse.js": "^7.1.0",
|
||||||
"postgres": "^3.4.7"
|
"postgres": "^3.4.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/compat": "^1.2.5",
|
"@eslint/compat": "^1.2.5",
|
||||||
"@eslint/js": "^9.18.0",
|
"@eslint/js": "^9.18.0",
|
||||||
"@sveltejs/adapter-auto": "^6.0.0",
|
"@sveltejs/adapter-auto": "^6.0.0",
|
||||||
"@sveltejs/adapter-netlify": "^5.2.2",
|
|
||||||
"@sveltejs/kit": "^2.22.0",
|
"@sveltejs/kit": "^2.22.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^6.0.0",
|
"@sveltejs/vite-plugin-svelte": "^6.0.0",
|
||||||
"@tailwindcss/forms": "^0.5.9",
|
"@tailwindcss/forms": "^0.5.9",
|
||||||
@@ -1135,13 +1135,6 @@
|
|||||||
"url": "https://github.com/sponsors/nzakas"
|
"url": "https://github.com/sponsors/nzakas"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@iarna/toml": {
|
|
||||||
"version": "2.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz",
|
|
||||||
"integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC"
|
|
||||||
},
|
|
||||||
"node_modules/@isaacs/fs-minipass": {
|
"node_modules/@isaacs/fs-minipass": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
|
||||||
@@ -1630,21 +1623,6 @@
|
|||||||
"@sveltejs/kit": "^2.0.0"
|
"@sveltejs/kit": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sveltejs/adapter-netlify": {
|
|
||||||
"version": "5.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-netlify/-/adapter-netlify-5.2.2.tgz",
|
|
||||||
"integrity": "sha512-pFWB/lxG8NuJLEYOcPxD059v5QQDFX1vxpBbVobHjgJDCpSDLySGMi4ipDKNPfysqIA9TEG+rwdexz0iaIAocg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@iarna/toml": "^2.2.5",
|
|
||||||
"esbuild": "^0.25.4",
|
|
||||||
"set-cookie-parser": "^2.6.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@sveltejs/kit": "^2.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@sveltejs/adapter-node": {
|
"node_modules/@sveltejs/adapter-node": {
|
||||||
"version": "5.3.1",
|
"version": "5.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-5.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-5.3.1.tgz",
|
||||||
@@ -3242,6 +3220,15 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fuse.js": {
|
||||||
|
"version": "7.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.1.0.tgz",
|
||||||
|
"integrity": "sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/get-tsconfig": {
|
"node_modules/get-tsconfig": {
|
||||||
"version": "4.10.1",
|
"version": "4.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz",
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sveltejs/adapter-node": "^5.3.1",
|
"@sveltejs/adapter-node": "^5.3.1",
|
||||||
"drizzle-orm": "^0.44.5",
|
"drizzle-orm": "^0.44.5",
|
||||||
|
"fuse.js": "^7.1.0",
|
||||||
"postgres": "^3.4.7"
|
"postgres": "^3.4.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,51 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Event } from '$lib/types';
|
import type { Event, EventType } from '$lib/types';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import type { PageData } from '../$types';
|
import type { PageData } from '../$types';
|
||||||
import { formatTime, formatDate } from '$lib/dateFormatter';
|
import { formatTime, formatDate } from '$lib/dateFormatter';
|
||||||
|
import Fuse from 'fuse.js';
|
||||||
|
|
||||||
let publicEvents: Event[] = [];
|
let publicEvents: Event[] = [];
|
||||||
let error = '';
|
let error = '';
|
||||||
let searchQuery = '';
|
let searchQuery = '';
|
||||||
|
let selectedEventType: EventType | 'all' = 'all';
|
||||||
|
let fuse: Fuse<Event>;
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
// Use the server-side data
|
// Use the server-side data
|
||||||
$: publicEvents = data.events;
|
$: publicEvents = data.events;
|
||||||
|
|
||||||
// Filter events based on search query
|
// Initialize Fuse.js with search options
|
||||||
$: filteredEvents =
|
$: fuse = new Fuse(publicEvents, {
|
||||||
searchQuery.trim() === ''
|
keys: [
|
||||||
? publicEvents
|
{ name: 'name', weight: 0.7 },
|
||||||
: publicEvents.filter((event) =>
|
{ name: 'location', weight: 0.3 }
|
||||||
event.name.toLowerCase().includes(searchQuery.toLowerCase())
|
],
|
||||||
);
|
threshold: 0.3, // Lower threshold = more strict matching
|
||||||
|
includeScore: true,
|
||||||
|
includeMatches: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Filter events based on search query and event type using Fuse.js
|
||||||
|
$: filteredEvents = (() => {
|
||||||
|
let events = publicEvents;
|
||||||
|
|
||||||
|
// First filter by event type
|
||||||
|
if (selectedEventType !== 'all') {
|
||||||
|
events = events.filter((event) => event.type === selectedEventType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then apply search query
|
||||||
|
if (searchQuery.trim() !== '') {
|
||||||
|
events = fuse.search(searchQuery).map((result) => result.item);
|
||||||
|
// Re-apply type filter after search
|
||||||
|
if (selectedEventType !== 'all') {
|
||||||
|
events = events.filter((event) => event.type === selectedEventType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return events;
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
@@ -61,9 +88,11 @@
|
|||||||
<p class="text-slate-500">Discover events created by the community</p>
|
<p class="text-slate-500">Discover events created by the community</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Search Bar -->
|
<!-- Search and Filter Section -->
|
||||||
<div class="mb-8">
|
<div class="mb-8">
|
||||||
<div class="relative mx-auto max-w-md">
|
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-center">
|
||||||
|
<!-- Search Bar -->
|
||||||
|
<div class="relative mx-auto max-w-md sm:mx-0">
|
||||||
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
|
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
|
||||||
<svg
|
<svg
|
||||||
class="h-5 w-5 text-slate-400"
|
class="h-5 w-5 text-slate-400"
|
||||||
@@ -82,7 +111,7 @@
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
bind:value={searchQuery}
|
bind:value={searchQuery}
|
||||||
placeholder="Search events by title..."
|
placeholder="Search events by name, location..."
|
||||||
class="w-full rounded-lg border border-slate-600 bg-slate-800 px-4 py-3 pl-10 text-white placeholder-slate-400 focus:border-violet-500 focus:ring-2 focus:ring-violet-500/20 focus:outline-none"
|
class="w-full rounded-lg border border-slate-600 bg-slate-800 px-4 py-3 pl-10 text-white placeholder-slate-400 focus:border-violet-500 focus:ring-2 focus:ring-violet-500/20 focus:outline-none"
|
||||||
/>
|
/>
|
||||||
{#if searchQuery}
|
{#if searchQuery}
|
||||||
@@ -101,6 +130,38 @@
|
|||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Event Type Filter -->
|
||||||
|
<div class="flex items-center justify-center gap-2">
|
||||||
|
<button
|
||||||
|
on:click={() => (selectedEventType = 'all')}
|
||||||
|
class="rounded-sm border px-3 py-2 text-sm font-medium transition-colors {selectedEventType ===
|
||||||
|
'all'
|
||||||
|
? 'border-violet-500 bg-violet-500/20 text-violet-400'
|
||||||
|
: 'border-slate-600 text-slate-400 hover:border-slate-500 hover:text-slate-300'}"
|
||||||
|
>
|
||||||
|
All
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
on:click={() => (selectedEventType = 'limited')}
|
||||||
|
class="rounded-sm border px-3 py-2 text-sm font-medium transition-colors {selectedEventType ===
|
||||||
|
'limited'
|
||||||
|
? 'border-amber-600 bg-amber-600/20 text-amber-600'
|
||||||
|
: 'border-slate-600 text-slate-400 hover:border-slate-500 hover:text-slate-300'}"
|
||||||
|
>
|
||||||
|
Limited
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
on:click={() => (selectedEventType = 'unlimited')}
|
||||||
|
class="rounded-sm border px-3 py-2 text-sm font-medium transition-colors {selectedEventType ===
|
||||||
|
'unlimited'
|
||||||
|
? 'border-teal-500 bg-teal-500/20 text-teal-500'
|
||||||
|
: 'border-slate-600 text-slate-400 hover:border-slate-500 hover:text-slate-300'}"
|
||||||
|
>
|
||||||
|
Unlimited
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||||
|
|||||||
Reference in New Issue
Block a user