feat: searching formats

This commit is contained in:
JovannMC 2025-05-28 14:48:48 +03:00
parent cc6a08eda9
commit f18af921ec
No known key found for this signature in database
1 changed files with 100 additions and 80 deletions

View File

@ -21,22 +21,77 @@
disabled, disabled,
settingsStyle, settingsStyle,
}: Props = $props(); }: Props = $props();
let open = $state(false); let open = $state(false);
let hover = $state(false); let hover = $state(false);
let isUp = $state(false);
let dropdown = $state<HTMLDivElement>(); let dropdown = $state<HTMLDivElement>();
let currentCategory = $state<string | null>(); let currentCategory = $state<string | null>();
let shownCategories = $state<string[]>(Object.keys(categories)); let searchQuery = $state("");
// initialize current category
$effect(() => {
if (!currentCategory) {
if (selected) {
const foundCat = Object.keys(categories).find((cat) =>
categories[cat].formats.includes(selected),
);
currentCategory =
foundCat || Object.keys(categories)[0] || null;
} else {
// find category based on file types
const fileFormats = files.files.map((f) => f.from);
const foundCat = Object.keys(categories).find((cat) =>
fileFormats.some((format) =>
categories[cat].formats.includes(format),
),
);
currentCategory =
foundCat || Object.keys(categories)[0] || null;
}
}
});
// other available categories based on current category (e.g. converting between video and audio)
const availableCategories = $derived.by(() => {
if (!currentCategory) return Object.keys(categories);
return Object.keys(categories).filter(
(cat) =>
cat === currentCategory ||
categories[cat].canConvertTo?.includes(currentCategory || ""),
);
});
const filteredData = $derived.by(() => {
if (!searchQuery) {
return {
categories: availableCategories,
formats: currentCategory
? categories[currentCategory].formats
: [],
};
}
// filter formats across all available categories
const allFormats = availableCategories.flatMap((cat) =>
categories[cat].formats.filter((format) =>
format.toLowerCase().includes(searchQuery.toLowerCase()),
),
);
// filter categories that have matching formats
const matchingCategories = availableCategories.filter((cat) =>
categories[cat].formats.some((format) =>
format.toLowerCase().includes(searchQuery.toLowerCase()),
),
);
return {
categories: matchingCategories,
formats: allFormats,
};
});
const selectOption = (option: string) => { const selectOption = (option: string) => {
const oldIndex =
currentCategory &&
categories[currentCategory]?.formats.indexOf(selected || "");
const newIndex =
currentCategory &&
categories[currentCategory]?.formats.indexOf(option);
isUp = (oldIndex ?? 0) > (newIndex ?? 0);
selected = option; selected = option;
open = false; open = false;
onselect?.(option); onselect?.(option);
@ -45,74 +100,39 @@
const selectCategory = (category: string) => { const selectCategory = (category: string) => {
if (categories[category]) { if (categories[category]) {
currentCategory = category; currentCategory = category;
console.log(`Selected category: ${category}`); // clear search when switching categories
console.log(`Formats: ${categories[category].formats.join(", ")}`); searchQuery = "";
} }
}; };
const search = (event: Event) => { const handleSearch = (event: Event) => {
const query = (event.target as HTMLInputElement).value; searchQuery = (event.target as HTMLInputElement).value;
console.log(`Searching for: ${query}`);
// TODO: search logic
}; };
onMount(() => { onMount(() => {
const click = (e: MouseEvent) => { const handleClickOutside = (e: MouseEvent) => {
if (dropdown && !dropdown.contains(e.target as Node)) { if (dropdown && !dropdown.contains(e.target as Node)) {
open = false; open = false;
} }
}; };
window.addEventListener("click", click); window.addEventListener("click", handleClickOutside);
return () => window.removeEventListener("click", handleClickOutside);
});
// depending on selected, find category // initialize selected format if none chosen
if (selected) { $effect(() => {
currentCategory = Object.keys(categories).find((cat) => if (
categories[cat].formats.includes(selected), !selected &&
currentCategory &&
categories[currentCategory]?.formats?.length > 0
) {
const from = files.files[0]?.from;
const firstDiff = categories[currentCategory].formats.find(
(f) => f !== from,
); );
if (!currentCategory) { selected = firstDiff || categories[currentCategory].formats[0];
currentCategory = Object.keys(categories)[0] || null;
}
shownCategories = Object.keys(categories).filter(
(cat) =>
cat === currentCategory ||
categories[cat].canConvertTo?.includes(
currentCategory || "",
),
);
} else {
// find current category based on files
const fileCategories = [
...new Set(
files.files
.map(f =>
Object.keys(categories).find(cat =>
categories[cat].formats.includes(f.from),
),
)
.filter(Boolean),
),
];
currentCategory = fileCategories[0] || Object.keys(categories)[0] || null;
// only show categories that can convert to current category / itself
shownCategories = Object.keys(categories).filter(
cat =>
cat === currentCategory ||
categories[cat].canConvertTo?.includes(currentCategory || ""),
);
// if no selected format, select first format of current category
if (
!selected &&
currentCategory &&
categories[currentCategory].formats.length > 0
)
selected = categories[currentCategory].formats[0];
} }
return () => window.removeEventListener("click", click);
}); });
</script> </script>
@ -192,7 +212,8 @@
type="text" type="text"
placeholder="Search format" placeholder="Search format"
class="flex-grow w-full !pl-11 !pr-3 rounded-lg bg-panel text-foreground" class="flex-grow w-full !pl-11 !pr-3 rounded-lg bg-panel text-foreground"
oninput={search} bind:value={searchQuery}
oninput={handleSearch}
/> />
<span <span
class="absolute left-4 top-1/2 -translate-y-1/2 flex items-center" class="absolute left-4 top-1/2 -translate-y-1/2 flex items-center"
@ -202,9 +223,9 @@
</div> </div>
</div> </div>
<!-- categories and formats --> <!-- available categories -->
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
{#each shownCategories as category} {#each filteredData.categories as category}
<button <button
class="flex-grow text-lg text-muted hover:text-muted/20 border-b-[1px] pb-2 capitalize class="flex-grow text-lg text-muted hover:text-muted/20 border-b-[1px] pb-2 capitalize
{currentCategory === category {currentCategory === category
@ -217,20 +238,19 @@
{/each} {/each}
</div> </div>
<!-- available formats -->
<div class="max-h-80 overflow-y-auto grid grid-cols-3 gap-2 p-2"> <div class="max-h-80 overflow-y-auto grid grid-cols-3 gap-2 p-2">
{#if currentCategory} {#each filteredData.formats as format}
{#each categories[currentCategory].formats as option} <button
<button class="w-full p-2 text-center rounded-xl
class="w-full p-2 text-center rounded-xl {format === selected
{option === selected ? 'bg-accent text-black'
? 'bg-accent text-black' : 'hover:bg-panel'}"
: 'hover:bg-panel'}" onclick={() => selectOption(format)}
onclick={() => selectOption(option)} >
> {format}
{option} </button>
</button> {/each}
{/each}
{/if}
</div> </div>
</div> </div>
{/if} {/if}