Skip to content

Commit 71d41a0

Browse files
committed
feat: add keyword search function
1 parent f5bab0a commit 71d41a0

File tree

2 files changed

+41
-4
lines changed

2 files changed

+41
-4
lines changed

frontend/src/lib/components/FeedsSelect.svelte

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
1111
export let data: Feed[];
1212
export let selected: number;
13+
export let className = '';
1314
let open = false;
1415
1516
let optionAll = { value: '-1', label: 'All' };
@@ -38,7 +39,7 @@
3839
variant="outline"
3940
role="combobox"
4041
aria-expanded={open}
41-
class="w-[200px] justify-between"
42+
class="w-[200px] justify-between {className}"
4243
>
4344
{feeds.find((f) => f.value === String(selected))?.label ?? 'Select a feed...'}
4445
<ChevronsUpDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />

frontend/src/lib/components/ItemList.svelte

+39-3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import { page } from '$app/stores';
1313
import { goto, invalidateAll } from '$app/navigation';
1414
import FeedsSelect from './FeedsSelect.svelte';
15+
import { Input } from './ui/input';
1516
1617
export let data: { feeds: Feed[]; items: { total: number; data: Item[] } };
1718
let filter = parseURLtoFilter($page.url.searchParams);
@@ -73,6 +74,26 @@
7374
toast.error((e as Error).message);
7475
}
7576
}
77+
function debounce(func: Function, wait: number): EventListener {
78+
let timeout: ReturnType<typeof setTimeout>;
79+
80+
return function (this: HTMLElement, event: Event) {
81+
const context = this;
82+
83+
const later = () => {
84+
func.apply(context, event);
85+
};
86+
87+
clearTimeout(timeout);
88+
timeout = setTimeout(later, wait);
89+
};
90+
}
91+
92+
const handleSearchInput = debounce(function (e: Event) {
93+
if (e.target instanceof HTMLInputElement) {
94+
filter.keyword = e.target.value;
95+
}
96+
}, 1000);
7697
7798
function fromNow(d: Date) {
7899
d = new Date(d);
@@ -91,16 +112,31 @@
91112
];
92113
</script>
93114

94-
<div class="flex justify-between items-center w-full">
95-
<FeedsSelect data={data.feeds} bind:selected={selectedFeed} />
115+
<div class="flex flex-col md:flex-row md:justify-between md:items-center w-full gap-2">
116+
<div class="flex flex-col md:flex-row gap-2">
117+
<FeedsSelect data={data.feeds} bind:selected={selectedFeed} className="w-full md:w-[200px]" />
118+
<Input
119+
type="text"
120+
placeholder="Search in title and content..."
121+
class="w-full md:w-[400px]"
122+
on:input={handleSearchInput}
123+
/>
124+
</div>
96125

97126
{#if data.items.data.length > 0}
98127
<div>
99128
{#each actions as action}
100129
<Tooltip.Root>
101130
<Tooltip.Trigger asChild let:builder>
102-
<Button builders={[builder]} on:click={action.handler} variant="outline" size="icon">
131+
<Button
132+
builders={[builder]}
133+
on:click={action.handler}
134+
variant="outline"
135+
size="icon"
136+
class="w-full md:w-[40px]"
137+
>
103138
<svelte:component this={action.icon} size="20" />
139+
<span class="ml-1 md:hidden">{action.tooltip}</span>
104140
</Button>
105141
</Tooltip.Trigger>
106142
<Tooltip.Content>

0 commit comments

Comments
 (0)