Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485<script module lang="ts"> import type { Component } from "svelte"; import type { SvelteHTMLElements } from "svelte/elements";
export interface SplitButtonOption { /** shown in the menu, where the options are read side by side and need to disambiguate */ label: string; /** shown on the button once armed; falls back to `label` when the two can be the same */ buttonLabel?: string; icon?: Component<SvelteHTMLElements["svg"]>; onclick?: () => void; danger?: boolean; }</script>
<script lang="ts"> import ChevronDown from "$icon/chevron-down"; import Button, { type ButtonVariants } from "$lib/components/ui/Button.svelte"; import ButtonGroup from "$lib/components/ui/ButtonGroup.svelte"; import Dropdown, { type DropdownVariants } from "$lib/components/ui/Dropdown.svelte"; import DropdownItem from "$lib/components/ui/DropdownItem.svelte";
interface Props { options: SplitButtonOption[]; /** index of the armed option; two-way, so a caller can react to the choice */ selected?: number; variant?: ButtonVariants["variant"]; size?: ButtonVariants["size"]; align?: DropdownVariants["align"]; menuLabel?: string; disabled?: boolean; loading?: boolean; class?: string; }
let { options, selected = $bindable(0), variant = "default", size, align = "right", menuLabel = "More options", disabled = false, loading = false, class: className }: Props = $props();
// options can be built from a filtered list, so neither an empty array nor a // stale index may reach options[selected] const current = $derived(options[Math.min(Math.max(selected, 0), options.length - 1)]);</script>
{#if current} <ButtonGroup class={className}> <Button class="flex-1" {variant} {size} {disabled} {loading} icon={current.icon} onclick={current.onclick} > <span>{current.buttonLabel ?? current.label}</span> </Button> <!-- the menu stays open even when the armed option is disabled, so a caller can gate one option (a conflicting merge, say) without trapping the user --> <Dropdown variant="button" buttonVariant={variant} {size} {align} label={menuLabel}> {#snippet trigger()} <ChevronDown class="size-4" /> {/snippet} {#each options as option, index (option.label)} <DropdownItem icon={option.icon} danger={option.danger} {size} onclick={() => (selected = index)} > {option.label} </DropdownItem> {/each} </Dropdown> </ButtonGroup>{/if}