From e231b5e6062d6fd0f48ff2c550bcab234f16da8a Mon Sep 17 00:00:00 2001 From: YAMAMOTO Yuji Date: Tue, 23 Apr 2024 11:23:57 +0900 Subject: [PATCH] fix: dynamically setting options sets undefined to select value Approach ==== Without this change, the `Select` component doesn't know the `Options` (items in the internal `List` component) after `onMount`. So it can't tell which `Option`s are added/removed. To fix the root cause, I created a new events to `List`: `SMUIList:mountItem` and `SMUIList:unmountItem`, which tells the parent when some of its children are added or removed. Then, I implemented an event handler of `Select` for the new events to update the internal list and call `layoutOptions`. `layoutOptions` should always be called whenever the options are updated Another Option ==== The change might be simpler adding `bind:accessor={list}` to the `List`. But I didn't choose it because `List` seems to want to hide its `accessor` as the implementation details. I'll rewrite if you prefer. Ref: https://github.com/hperrin/svelte-material-ui/issues/538. Ref: https://discord.com/channels/833139170703704115/1228040959670091787 --- packages/list/src/List.svelte | 5 ++++- packages/select/src/Select.svelte | 16 +++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/list/src/List.svelte b/packages/list/src/List.svelte index 6d0edb7e5..448df7a83 100644 --- a/packages/list/src/List.svelte +++ b/packages/list/src/List.svelte @@ -110,6 +110,7 @@ let element: SvelteComponent; let instance: MDCListFoundation; + let accessor: SMUIListAccessor; let items: SMUIListItemAccessor[] = []; let role = getContext('SMUI:list:role'); let nav = getContext('SMUI:list:nav'); @@ -232,7 +233,7 @@ }, }); - const accessor: SMUIListAccessor = { + accessor = { get element() { return getElement(); }, @@ -282,6 +283,7 @@ selectedIndex = getListItemIndex(event.detail.element); } event.stopPropagation(); + dispatch(getElement(), 'SMUIList:mountItem', accessor); } function handleItemUnmount(event: CustomEvent) { @@ -292,6 +294,7 @@ itemAccessorMap.delete(event.detail.element); } event.stopPropagation(); + dispatch(getElement(), 'SMUIList:unmountItem', accessor); } function handleKeydown(event: KeyboardEvent) { diff --git a/packages/select/src/Select.svelte b/packages/select/src/Select.svelte index 465049845..3f0eef84e 100644 --- a/packages/select/src/Select.svelte +++ b/packages/select/src/Select.svelte @@ -199,6 +199,8 @@ {wrapFocus} bind:selectedIndex on:SMUIList:mount={(event) => (list = event.detail)} + on:SMUIList:mountItem={handleItemMountUnmount} + on:SMUIList:unmountItem={handleItemMountUnmount} {...prefixFilter($$restProps, 'list$')}> @@ -473,10 +475,13 @@ }, getSelectedIndex: () => selectedIndex, setSelectedIndex: (index) => { - // Don't update the instance again. previousSelectedIndex = index; - selectedIndex = index; - value = getMenuItemValues()[selectedIndex]; + selectedIndex = index === -1 ? 0 : index; + const menuItems = getMenuItemValues(); + // Avoid setting undefined to the value + if (selectedIndex < menuItems.length) { + value = menuItems[selectedIndex]; + } }, focusMenuItemAtIndex: (index) => { list.focusItemAtIndex(index); @@ -659,4 +664,9 @@ export function getElement() { return element; } + + function handleItemMountUnmount(event: CustomEvent): void { + list = event.detail; + instance && instance.layoutOptions(); + }