rob-balfre / svelte-select

Svelte Select. A select component for Svelte
https://svelte-select-examples.vercel.app
Other
1.25k stars 175 forks source link

This is really weird behavior #527

Closed frederikhors closed 1 year ago

frederikhors commented 1 year ago

REPL: https://svelte.dev/repl/569d7b12b85a418d818fe40a439f99f5?version=3.55.1.

As you can see when you start writing for example "bob" it doesn't show the "bob" element in list (which becomes empty).

If you then click outside and click again on the select "bob" appears!

issue

<script>
  import Select from 'svelte-select@5.1.3';

    let default_items = [
        {id: "1", firstname: "john"},
        {id: "2", firstname: "sill"},
        {id: "3", firstname: "sick"}
    ];
    let value = null;
    let filterText = '';
    let timeout;

    function updateFilterText (filterText) {
        if (filterText.length == 0) return;

        items = [{id: "123", firstname: "bob"}];
    };

    $: updateFilterText(filterText);

    $: items = default_items;
</script>

items: {JSON.stringify(items)}<br><br>

<Select {items} bind:filterText>
    <svelte:fragment slot="item" let:item>
        {item.firstname}
    </svelte:fragment>
</Select>
rob-balfre commented 1 year ago

You've bound filterText to a reactive statement that updates items. You've also not passed in itemId. I'd argue it's more a "Really weird setup".

https://svelte.dev/repl/a36f6735163e48ed8a63d4eded60e914?version=3.55.1

frederikhors commented 1 year ago

I used your code adding just the slot and it doesn't work (if you search for "bob" No options message remains, click outside, click again and it appears).

Can you please re-open?

REPL: https://svelte.dev/repl/934d51c5b72d443fa09ebf2e256a91a4?version=3.55.1

<script>
  import Select from 'svelte-select@5.1.3';

  let default_items = [
    {id: "1", firstname: "john"},
    {id: "2", firstname: "sill"},
    {id: "3", firstname: "sick"}
  ];
  let value = null;
  let filterText = '';
  let timeout;

  function updateFilterText (filterText) {
    if (!filterText || filterText.length === 0) return;

    items = [{id: "123", firstname: "bob"}];
  };

  $: updateFilterText(filterText);

  $: items = default_items;
</script>

items: {JSON.stringify(items)}<br><br>

<Select {items} bind:filterText itemId="id">
  <svelte:fragment slot="item" let:item>
    {item.firstname}
  </svelte:fragment>
</Select>
frederikhors commented 1 year ago

@rob-balfre if you fix the slot issue I think we can also fix somewhat #529.

rob-balfre commented 1 year ago

needs label="firstname"

https://svelte.dev/repl/1fd91d30c951474d8909c315cff1c8d2?version=3.55.1

frederikhors commented 1 year ago

I need a composite label. So I cannot use label="firstname".

rob-balfre commented 1 year ago

@frederikhors you can do whatever you want in the slot .... https://svelte.dev/repl/4fd91607a3cd432f98de1a573202a429?version=3.55.1

frederikhors commented 1 year ago

You're right, but why do we need label="firstname" prop?

rob-balfre commented 1 year ago

to tell select which object keys to filter by

frederikhors commented 1 year ago

I do not understand.

Take this: https://svelte.dev/repl/c3161ebfd0a54546b8c1dd5bce1a1e57?version=3.55.1.

If you search by "zzz" it doesn't show Bob! And this is wrong behavior.

I don't need to filter items, my items are already filtered. I just need to show label according to my needs.

Again, I think the slot="item" should work even without label prop on Select.

rob-balfre commented 1 year ago

@frederikhors

function updateFilterText (filterText) {
    if (!filterText || filterText.length === 0) return;

    items = [{id: "123", firstname: "bob", lastname: "zzzz"}];
};
  1. Above you're setting items to a single item array if filterText is not null and greater than 0.
  2. Select by default only uses label to search through items.... so essentially is only sees this: [{firstname: "bob"}].
  3. Your filterText is zzz so the filter finds 0 items.

Clearer?

If you wanted to have a more robust filter function then try passing your own:

Here's the default one...

export let itemFilter = (label, filterText, option) => label.toLowerCase().includes(filterText.toLowerCase());
frederikhors commented 1 year ago

Ok, so if I use this:

<Select
  items={items.map(item => ({id: item.id, label: `${item.firstname} ${item.lastname}`, value: item}))}
  bind:filterText
  itemId="id"
/>

it works. If I use slot instead I cannot use filterText to filter this way anymore, is this correct?

rob-balfre commented 1 year ago

The slot is only for display purposes, it doesn't affect the filter or internals at all.

<Select 
  items={items.map(item => ({id: item.id, label: `${item.firstname} ${item.lastname}`, value: item}))}
  bind:filterText
  itemId="id">
  <svelte:fragment slot="item" let:item>
    {item.label} or {item.value.firstname} or whatever
  </svelte:fragment>
</Select>

https://svelte.dev/repl/23415905af774808802ad54bb77a596b?version=3.55.1