feat: refactor replacing types & moving search item to DropdownMenu component + add multiselect functionality

This commit is contained in:
Dayv1dDev
2026-02-13 13:24:55 +01:00
parent bbd8cc0c6e
commit cf58cd537a
3 changed files with 43 additions and 70 deletions
@@ -1,57 +1,43 @@
<script lang="ts">
// This component (Checkbox) is implemented in a parallel Pull Request, so it may not be available in the current branch
// import Checkbox from "./Checkbox.svelte";
import SearchIcon from "../../../svgs/SearchIcon.svelte";
import type {
DropdownItemModel,
DropdownItemPosition,
DropdownVariant,
} from "./dropdown.types";
let {
position = "middle",
id,
label,
position,
variant,
selectedIds = $bindable<string[]>([]),
...item
} = $props<
DropdownItemModel & {
position?: DropdownItemPosition;
variant: DropdownVariant;
selectedIds?: string[];
}
>();
} = $props<{
id: string;
label: string;
position?: DropdownItemPosition;
variant: DropdownVariant;
selectedIds?: string[];
}>();
let positionClass =
position === "start" ? "rounded-t-lg" : position === "end" ? "rounded-b-lg" : "";
</script>
{#if item.type === "search"}
<div class="border-grey rounded-t-lg border bg-white p-4">
<div
class="border-secondary group relative flex items-center justify-between rounded-3xl border bg-white p-4"
>
<input
class="max-h-6 w-full max-w-72 border-0 bg-white p-0 text-base/6 font-normal text-black ring-0 placeholder:opacity-48"
type="text"
placeholder="Search..."
/>
<SearchIcon class="absolute right-4" width={"32"} height={"32"} />
</div>
</div>
{:else if variant === "multiselect"}
{#if variant === "multiselect"}
<div
class="border-grey hover:bg-soft-purple hover:border-variant1 cursor-pointer border bg-white p-4 text-start {positionClass}"
>
<label class="flex cursor-pointer justify-between">
<span class="text-base text-black">{item.label}</span>
<span class="text-base text-black">{label}</span>
<input
type="checkbox"
checked={selectedIds.includes(item.id)}
checked={selectedIds.includes(id)}
onchange={(e) => {
if (e.currentTarget.checked) {
selectedIds = [...selectedIds, item.id];
selectedIds = [...selectedIds, id];
} else {
selectedIds = selectedIds.filter((id: string) => id !== item.id);
selectedIds = selectedIds.filter((x: string) => x !== id);
}
}}
class="text-primary border-secondary mt-1 size-5 shrink-0 rounded-sm border ring-0"
@@ -62,6 +48,6 @@
<div
class="border-grey hover:bg-soft-purple hover:border-variant1 cursor-pointer border bg-white p-4 text-start {positionClass}"
>
<button class="w-full cursor-pointer text-base text-black">{item.label}</button>
<button class="w-full cursor-pointer text-base text-black">{label}</button>
</div>
{/if}
@@ -1,49 +1,46 @@
<script lang="ts">
import SearchIcon from "../../../svgs/SearchIcon.svelte";
import DropdownItem from "./DropdownItem.svelte";
import type {
DropdownVariant,
DropdownBasicItem,
DropdownMultiSelectItem,
DropdownItemModel,
} from "./dropdown.types";
import type { DropdownItemPosition, DropdownItemType, DropdownVariant } from "./dropdown.types";
let {
variant,
items,
hasSearch = false,
selectedIds = $bindable<string[]>([]),
} = $props<
| {
variant: "basic";
items: DropdownBasicItem[];
hasSearch?: boolean;
}
| {
variant: "multiselect";
items: DropdownMultiSelectItem[];
hasSearch?: boolean;
selectedIds?: string[];
}
>();
} = $props<{
variant: DropdownVariant;
items: DropdownItemType[];
hasSearch?: boolean;
selectedIds?: string[];
}>();
const renderedItems = $derived<DropdownItemModel[]>([
...(hasSearch ? [{ id: "search", type: "search" } as const] : []),
...items.map((item: DropdownBasicItem | DropdownMultiSelectItem) => ({
...item,
type: variant,
})),
]);
const renderedItems = $derived<DropdownItemType[]>(items);
function getPosition(index: number, length: number) {
if (index === 0 && hasSearch) return "middle";
function getPosition(index: number, length: number): DropdownItemPosition {
if (index === 0 && !hasSearch) return "start";
if (index === length - 1) return "end";
return "start";
return "middle";
}
</script>
<div
class="flex w-full max-w-[416px] flex-col self-center rounded-lg bg-transparent shadow-[0_35px_10px_0_rgba(0,0,0,0),0_22px_9px_0_rgba(0,0,0,0.01),0_13px_8px_0_rgba(0,0,0,0.05),0_6px_6px_0_rgba(0,0,0,0.09),0_1px_3px_0_rgba(0,0,0,0.1)]"
>
{#if hasSearch}
<div class="border-grey rounded-t-lg border bg-white p-4">
<div
class="border-secondary group relative flex items-center justify-between rounded-3xl border bg-white p-4"
>
<input
class="max-h-6 w-full max-w-72 border-0 bg-white p-0 text-base/6 font-normal text-black ring-0 placeholder:opacity-48"
type="text"
placeholder="Search..."
/>
<SearchIcon class="absolute right-4" width={"32"} height={"32"} />
</div>
</div>
{/if}
{#each renderedItems as item, index}
<DropdownItem
{...item}
@@ -2,17 +2,7 @@ export type DropdownVariant = "basic" | "multiselect";
export type DropdownItemPosition = "start" | "middle" | "end";
export type DropdownBasicItem = {
export type DropdownItemType = {
id: string;
label: string;
};
export type DropdownMultiSelectItem = {
id: string;
label: string;
};
export type DropdownItemModel =
| { id: string; type: "search" }
| ({ type: "basic" } & DropdownBasicItem)
| ({ type: "multiselect" } & DropdownMultiSelectItem);