Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798<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, { buttonIcon, 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; testid?: string; }
let { options, selected = $bindable(0), variant = "default", size, align = "right", menuLabel = "More options", disabled = false, loading = false, class: className, testid }: 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} data-testid={testid}> <Button data-testid="primaryAction" 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" square buttonVariant={variant} {size} {align} label={menuLabel} testid="options" > {#snippet trigger()} <ChevronDown class={buttonIcon({ size })} /> {/snippet} {#each options as option, index (option.label)} <DropdownItem data-testid="option" data-index={index} icon={option.icon} danger={option.danger} {size} onclick={() => (selected = index)} > {option.label} </DropdownItem> {/each} </Dropdown> </ButtonGroup>{/if}