Skip to content

Commit

Permalink
fix(MultiSelect): Resolve infinite loop when using mode="immediate"
Browse files Browse the repository at this point in the history
… and Svelte 5. Resovles #507
  • Loading branch information
techniq committed Nov 3, 2024
1 parent 27e8dc8 commit 4b2360d
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .changeset/hot-stingrays-move.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte-ux': patch
---

fix(MultiSelect): Resolve infinite loop when using `mode="immediate"` and Svelte 5
23 changes: 13 additions & 10 deletions packages/svelte-ux/src/lib/components/MultiSelect.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
import { type ComponentProps, createEventDispatcher } from 'svelte';
import { flip } from 'svelte/animate';
import { get, partition } from 'lodash-es';
import { get } from 'svelte/store';
import { partition, isEqual } from 'lodash-es';
import { mdiMagnify } from '@mdi/js';
Expand All @@ -17,6 +18,7 @@
import selectionStore from '../stores/selectionStore.js';
import uniqueStore from '../stores/uniqueStore.js';
import { cls } from '../utils/styles.js';
import changeStore from '../stores/changeStore.js';
export let options: MenuOption<TValue>[];
export let value: TValue[] = [];
Expand Down Expand Up @@ -82,15 +84,11 @@
onChange();
}
$: if (mode === 'immediate' && $selection) {
applyChange();
}
export let searchText = '';
let applying = false;
// Partition options based on if they initially selected, which will be displayed at the top
$: [selectedOptions, unselectedOptions] = partition(options, (x) => value.includes(x.value));
$: [selectedOptions, unselectedOptions] = partition(options, (o) => value.includes(o.value));
// Filter by search text
function applyFilter(option: MenuOption<TValue>, searchText: string) {
Expand All @@ -105,10 +103,15 @@
$: filteredSelectedOptions = selectedOptions.filter((x) => applyFilter(x, searchText));
$: filteredUnselectedOptions = unselectedOptions.filter((x) => applyFilter(x, searchText));
$: selection = selectionStore({
initial: selectedOptions.map((x) => x.value),
max,
});
const selection = selectionStore({ max });
// Only "subscribe" to value changes (not `$selection`) to fix correct `value` / topological ordering. Should be simplified with Svelte 5
$: get(selection).setSelected(value);
// Immediately apply only if changed
const changed = changeStore(selection);
$: if (mode === 'immediate' && !isEqual($selection.selected, $changed.previous?.selected)) {
applyChange();
}
$: isSelectionDirty = dirtyStore(selection);
$: indeterminateStore = uniqueStore(indeterminateSelected);
Expand Down

0 comments on commit 4b2360d

Please sign in to comment.