Open
Description
Is there a recommended approach to dynamically switching the selector style across multiple breakpoints? Specifically, I'd like to use the "Tabs" component while in desktop but switch to a "Listbox" on mobile.
I originally used both createListbox
and createTabs
to manage separate components, and used a writable store to try to synchronize the selected value, but that didn't work. I also tried to keep the selected value synchronized through event listeners, which worked for updating the listbox when the tabs is changed, but not for the reverse. What ultimately ended up working was using a single createListbox
to control both elements and just styling the desktop view to look like tabs, but this seems hacky.
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import { createListbox } from 'svelte-headlessui';
const dispatch = createEventDispatcher();
const tabOptions: Record<string, string> = {
option1: 'Option 1',
option2: 'Option 2',
option3: 'Option 3'
};
const keys = Object.keys(tabOptions);
const listbox = createListbox({ label: 'Post Type', selected: keys[0] });
const handleListboxChange = (e: Event) => {
dispatch('changed', (e as CustomEvent).detail.selected);
};
const select = (value: string) => {
listbox.set({ selected: value });
};
</script>
<div class="hidden md:flex w-full flex-col">
<div
use:listbox.button
class="flex w-dull rounded-md bg-white divide-x divide-gray-200 overflow-clip"
>
{#each keys as value}
{@const active = $listbox.active === value}
{@const selected = $listbox.selected === value}
<button
use:listbox.item={{ value }}
on:click={() => select(value)}
class="w-full font-medium m-0 focus:outline-none"
>
<span
class="block py-4 text-sm border-x-0 border-b-4 border-transparent {selected
? 'border-b-blue-500 font-bold'
: active
? ''
: 'hover:border-b-gray-200 hover:bg-gray-50'}"
>
{tabOptions[value]}
</span>
</button>
{/each}
</div>
</div>
<div class="block relative md:hidden px-4">
<button
use:listbox.button
on:change={handleListboxChange}
class="relative w-full flex justify-between items-center cursor-default rounded-md bg-white py-2 px-3 text-left text-sm shadow-sm focus:outline-none"
>
<span class="block truncate">{tabOptions[$listbox.selected]}</span>
</button>
{#if $listbox.expanded}
<ul
use:listbox.items
class="absolute mt-1 left-4 right-4 overflow-auto rounded-md bg-white py-1 text-sm shadow-sm focus:outline-none"
>
{#each keys as value}
{@const selected = $listbox.selected === value}
<li
class="relative cursor-default select-none py-2 px-4 {selected ? 'bg-gray-50' : ''}"
use:listbox.item={{ value }}
>
<span class="block truncate {selected ? 'font-bold' : 'font-normal'}">
{tabOptions[value]}
</span>
</li>
{/each}
</ul>
{/if}
</div>
Metadata
Metadata
Assignees
Labels
No labels