Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mobile Tabs #97

Open
quangdaon opened this issue Aug 11, 2024 · 0 comments
Open

Mobile Tabs #97

quangdaon opened this issue Aug 11, 2024 · 0 comments

Comments

@quangdaon
Copy link

quangdaon commented Aug 11, 2024

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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant