This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / web / src / lib / components / ui / ButtonGroup.svelte
2.3 kB 67 lines
1<script module lang="ts"> 2 import { tv, type VariantProps } from "tailwind-variants"; 3 import type { ButtonVariants } from "./Button.svelte"; 4 5 export const buttonGroup = tv({ 6 base: "inline-flex items-center", 7 variants: { 8 // joined groups are the segmented control from Figma: the group draws the outer 9 // border and the recessed surface, and each segment's own 1px border is pulled 10 // onto it (-m-px) so an unselected segment shows the group's surface while a 11 // selected one covers it and reads as a raised card. 12 spaced: { 13 true: "gap-2", 14 false: [ 15 "gap-0 rounded border border-border-default bg-background-subtle", 16 "*:relative *:-my-px", 17 "[&>*:first-child]:-ml-px [&>*:last-child]:-mr-px", 18 "[&>*:not(:first-child)]:-ml-px [&>*:not(:first-child)]:[--btn-radius-l:0px]", 19 "[&>*:not(:last-child)]:[--btn-radius-r:0px]", 20 "[&>*:focus-visible]:z-10 [&>*:hover]:z-10" 21 ] 22 } 23 }, 24 defaultVariants: { 25 spaced: false 26 } 27 }); 28 29 export type ButtonGroupVariants = VariantProps<typeof buttonGroup>; 30 31 /** 32 * Recipe for using ButtonGroup as a tab/segment switcher (e.g. Open/Closed issue filters, 33 * Write/Preview editor tabs). Per Figma, the selected segment reads as a raised, lighter 34 * surface (`default` + its inset shadow) while unselected segments are flat `ghost` 35 * buttons — never the plain `default` variant, which reads as the *hovered* state of an 36 * unselected segment. Figma also gives an unselected segment the same foreground as a 37 * selected one, where `ghost` on its own is muted, so the recipe overrides the colour 38 * here rather than changing `ghost` everywhere it's used. 39 */ 40 export function segmentProps(selected: boolean): { 41 variant: ButtonVariants["variant"]; 42 insetShadow: boolean; 43 class?: string; 44 } { 45 return selected 46 ? { variant: "default", insetShadow: true } 47 : { variant: "ghost", insetShadow: false, class: "text-foreground-default" }; 48 } 49</script> 50 51<script lang="ts"> 52 import type { Snippet } from "svelte"; 53 54 interface Props { 55 spaced?: ButtonGroupVariants["spaced"]; 56 class?: string; 57 children: Snippet; 58 } 59 60 let { spaced = false, class: className, children }: Props = $props(); 61 62 const classes = $derived(buttonGroup({ spaced, class: className })); 63</script> 64 65<div class={classes}> 66 {@render children()} 67</div>