Menu
The menu
component provides a context menu for navigating a list of options.
Example
- New tab
- New window
- History
- Downloads
- Bookmarks
- Settings
- Exit
Sources
+page.svelte
<script lang="ts">
import Menu from './Menu.svelte'
import MenuItem from './MenuItem.svelte'
import MenuDivider from './MenuDivider.svelte'
import MenuSubmenu from './MenuSubmenu.svelte'
</script>
<Menu>
<MenuItem href="#">New tab</MenuItem>
<MenuItem href="#">New window</MenuItem>
<MenuDivider />
<MenuItem>
History
<MenuSubmenu slot="submenu">
<MenuItem href="#">Show full history</MenuItem>
<MenuItem href="#">History by site</MenuItem>
<MenuItem href="#">Recently closed</MenuItem>
<MenuItem href="#">Tabs from other devices</MenuItem>
</MenuSubmenu>
</MenuItem>
<MenuItem href="#">Downloads</MenuItem>
<MenuItem>
Bookmarks
<MenuSubmenu slot="submenu">
<MenuItem href="#">
Show all bookmarks
<MenuSubmenu slot="submenu">
<MenuItem href="#">Bookmarks bar</MenuItem>
<MenuItem href="#">Other bookmarks</MenuItem>
</MenuSubmenu>
</MenuItem>
<MenuItem href="#">Bookmark manager</MenuItem>
<MenuItem href="#">Add bookmark</MenuItem>
</MenuSubmenu>
</MenuItem>
<MenuDivider />
<MenuItem href="#">Settings</MenuItem>
<MenuItem href="#">Exit</MenuItem>
</Menu>
Menu.svelte
<script lang="ts">
import { createMenu } from 'louisette'
import { setContext } from 'svelte'
const { menuAttrs, ...menuContext } = createMenu()
setContext('menu', menuContext)
</script>
<ul
{...$menuAttrs}
class="flex w-max min-w-[12rem] flex-col gap-1 rounded bg-white p-2 shadow-lg dark:bg-neutral-900"
>
<slot />
</ul>
MenuDivider.svelte
<li
role="separator"
class="my-1 border-t border-neutral-200 dark:border-neutral-800"
/>
MenuItem.svelte
<script lang="ts">
import { createKey, type Menu } from 'louisette'
import { getContext, setContext } from 'svelte'
import { ChevronRight } from 'lucide-svelte'
export let href: string = ''
// Generate a random key for this menu item
const key = createKey()
// Check if this menu item has a submenu
const hasSubmenu = $$slots.submenu !== undefined
// Get the menu item attributes
const { itemAttrs, triggerAttrs } = getContext<Menu>('menu')
// Set the key of the submenu if this menu item has one
setContext('submenu', hasSubmenu ? key : null)
</script>
<li class="relative">
{#if hasSubmenu}
<span
{...$triggerAttrs(key)}
class="flex items-center justify-between rounded-sm px-4 py-1 text-sm hover:bg-neutral-200 dark:hover:bg-neutral-700"
>
<slot />
<ChevronRight class="ml-2 h-4 w-4" />
</span>
<slot name="submenu" />
{:else}
<a
{...$itemAttrs(key)}
{href}
class="flex items-center rounded-sm px-4 py-1 text-sm hover:bg-neutral-200 dark:hover:bg-neutral-700"
>
<slot />
</a>
{/if}
</li>
MenuSubmenu.svelte
<script lang="ts">
import type { Menu } from 'louisette'
import { getContext } from 'svelte'
const key: string = getContext('submenu')
if (!key) {
throw new Error('Submenu must be used inside a Menu item component')
}
const { submenuAttrs, activePath } = getContext<Menu>('menu')
</script>
{#if key}
<div
class="absolute left-full top-0 z-10 -mt-2 pl-2"
class:hidden={!$activePath.includes(key)}
>
<ul
{...$submenuAttrs(key)}
class="flex w-max min-w-[12rem] flex-col gap-1 rounded bg-white p-2 shadow-lg dark:bg-neutral-900"
>
<slot />
</ul>
</div>
{/if}