svelteuidev / svelteui

SvelteUI Monorepo
https://svelteui.dev
MIT License
1.29k stars 63 forks source link

Dynamic Tab creation doesn't work #474

Open nj-vs-vh opened 11 months ago

nj-vs-vh commented 11 months ago

What package has an issue

@svelteuidev/core

A clear and concise description of what the bug is

Trying to create Tabs dynamically doesn't work as expected. In particular, the newly added tab can't be selected with a mouse click, and navigating into it with arrow keys renders tab's content in the header.

In which browser(s) did the problem occur?

Brave (Chromium), Firefox

Steps To Reproduce

Here's the MRE:

<script lang="ts">
  import { Tabs, Stack, Button } from "@svelteuidev/core";

  let tabsContent: number[] = [1, 2, 3];
</script>

<Stack>
  <Button on:click={() => (tabsContent = [...tabsContent, tabsContent.length + 1])}>Add tab</Button>
  <Tabs>
    {#each tabsContent as tab}
      <Tabs.Tab label={tab}>Content: {tab}</Tabs.Tab>
    {/each}
  </Tabs>
</Stack>

When the code above is rendered, the first 3 tabs created initially are working OK, I can switch between them and see Content: N. However, while pressing the Add tab button renders the 4th added tab as expected, I can't click on it. If I navigate into it with arrow keys, the tab's content is rendered inside the tab label (see screenshot)

image

Do you know how to fix the issue

No

Are you willing to participate in fixing this issue and create a pull request with the fix

Yes

Relevant Assets

No response

nj-vs-vh commented 11 months ago

Looking through the code, this may not be easily fixable with the current approach (lookup child nodes on first component mount and install relevant DOM event handlers). Still worth putting it out there I guess.

nj-vs-vh commented 11 months ago

Ok so I made a crude workaround using svelte's {#key} directive to rerender the whole Tabs component whenever tabsContent change.

<script lang="ts">
  import { Tabs, Stack, Button } from "@svelteuidev/core";

  let tabsContent: number[] = [1, 2, 3];
  let active: number = 0;
</script>

<Stack>
  <Button on:click={() => (tabsContent = [...tabsContent, tabsContent.length + 1])}>Add tab</Button>
  {#key tabsContent}
    <Tabs {active} on:change={(e) => (active = e.detail.index)}>
      {#each tabsContent as tab}
        <Tabs.Tab label={tab}>Content: {tab}</Tabs.Tab>
      {/each}
    </Tabs>
  {/key}
</Stack>

Note that direct binding <Tabs bind:active> doesn't work, probably because the component re-initializes it to 0 on re-render.

Not sure if this is helpful for enhancing the library, but if anyone stumbles on this issue and is looking for a quick-and-dirty workaround.

BeeMargarida commented 11 months ago

Not a priority for right now, but definitely something to be improved, thank you!

lorypelli commented 1 month ago

thanks for your solution