From f5de892268b1aaaa4f6875c814b6f18dcd5c6583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Malinowski?= Date: Sat, 9 Nov 2024 14:53:33 +0100 Subject: [PATCH] fix(multiselect): Svelte 5 compatibility Refactor the MultiSelect component to remove circular dependencies which would cause infinite loops in Svelte 5 (#1986). --- src/MultiSelect/MultiSelect.svelte | 48 ++++++++++++++---------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/src/MultiSelect/MultiSelect.svelte b/src/MultiSelect/MultiSelect.svelte index 032dde90..057757b6 100644 --- a/src/MultiSelect/MultiSelect.svelte +++ b/src/MultiSelect/MultiSelect.svelte @@ -168,7 +168,7 @@ */ export let highlightedId = null; - import { afterUpdate, createEventDispatcher, setContext } from "svelte"; + import { afterUpdate, createEventDispatcher, setContext, tick } from "svelte"; import WarningFilled from "../icons/WarningFilled.svelte"; import WarningAltFilled from "../icons/WarningAltFilled.svelte"; import Checkbox from "../Checkbox/Checkbox.svelte"; @@ -237,7 +237,7 @@ afterUpdate(() => { if (checked.length !== prevChecked.length) { if (selectionFeedback === "top") { - sortedItems = sort(); + items = sort(); } prevChecked = checked; selectedIds = checked.map(({ id }) => id); @@ -247,33 +247,29 @@ unselected: unchecked, }); } + }); - if (!open) { - if (!initialSorted || selectionFeedback !== "fixed") { - sortedItems = sort(); + $: if (!open) { + if (!initialSorted || selectionFeedback !== "fixed") { + tick().then(() => { + items = sort(); initialSorted = true; - } - - highlightedIndex = -1; - value = ""; + }); } - items = sortedItems; - }); + highlightedIndex = -1; + value = ""; + } $: menuId = `menu-${id}`; $: inline = type === "inline"; $: ariaLabel = $$props["aria-label"] || "Choose an item"; - $: sortedItems = items.map((item) => ({ - ...item, - checked: selectedIds.includes(item.id), - })); - $: checked = sortedItems.filter(({ checked }) => checked); - $: unchecked = sortedItems.filter(({ checked }) => !checked); - $: filteredItems = sortedItems.filter((item) => filterItem(item, value)); + $: checked = items.filter(({ checked }) => checked); + $: unchecked = items.filter(({ checked }) => !checked); + $: filteredItems = items.filter((item) => filterItem(item, value)); $: highlightedId = highlightedIndex > -1 - ? (filterable ? filteredItems : sortedItems)[highlightedIndex]?.id ?? null + ? (filterable ? filteredItems : items)[highlightedIndex]?.id ?? null : null; @@ -366,7 +362,7 @@ change(-1); } else if (key === 'Enter') { if (highlightedIndex > -1) { - sortedItems = sortedItems.map((item, i) => { + items = items.map((item, i) => { if (i !== highlightedIndex) return item; return { ...item, checked: !item.checked }; }); @@ -394,7 +390,7 @@ on:clear on:clear="{() => { selectedIds = []; - sortedItems = sortedItems.map((item) => ({ + items = items.map((item) => ({ ...item, checked: false, })); @@ -424,10 +420,10 @@ on:keydown|stopPropagation="{({ key }) => { if (key === 'Enter') { if (highlightedId) { - const filteredItemIndex = sortedItems.findIndex( + const filteredItemIndex = items.findIndex( (item) => item.id === highlightedId ); - sortedItems = sortedItems.map((item, i) => { + items = items.map((item, i) => { if (i !== filteredItemIndex) return item; return { ...item, checked: !item.checked }; }); @@ -489,7 +485,7 @@ id="{id}" aria-multiselectable="true" > - {#each filterable ? filteredItems : sortedItems as item, i (item.id)} + {#each filterable ? filteredItems : items as item, i (item.id)} + + items = items.map((_) => _.id === item.id ? { ..._, checked: !_.checked } : _ ); + fieldRef.focus(); }}" on:mouseenter="{() => {