Monorepo for Tangled tangled.org
2

Configure Feed

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

web/components/ui: add Avatar, Dropdown & Spinner stories

Signed-off-by: eti <eti@eti.tf>

author
eti
committer
dawn
date (Jul 23, 2026, 6:48 PM +0300) commit 2c0cba67 parent 0062df69 change-id ksurnrpy
+133
+30
web/src/lib/components/ui/Avatar.stories.svelte
··· 1 + <script module lang="ts"> 2 + import { defineMeta } from "@storybook/addon-svelte-csf"; 3 + import Avatar from "./Avatar.svelte"; 4 + 5 + const { Story } = defineMeta({ 6 + title: "UI/Avatar", 7 + component: Avatar, 8 + tags: ["autodocs"], 9 + argTypes: { 10 + src: { control: "text" }, 11 + handle: { control: "text" }, 12 + size: { control: "text" } 13 + }, 14 + args: { 15 + handle: "user.tld" 16 + } 17 + }); 18 + </script> 19 + 20 + <Story name="Fallback" /> 21 + <Story name="With image" args={{ src: "https://avatars.githubusercontent.com/u/1" }} /> 22 + <Story name="Broken image" args={{ src: "https://example.invalid/broken.png" }} /> 23 + 24 + <Story name="Sizes" asChild> 25 + <div class="flex items-center gap-3"> 26 + <Avatar handle="user.tld" size="size-6" /> 27 + <Avatar handle="user.tld" size="size-10" /> 28 + <Avatar handle="user.tld" size="size-16" /> 29 + </div> 30 + </Story>
+79
web/src/lib/components/ui/Dropdown.stories.svelte
··· 1 + <script module lang="ts"> 2 + import { defineMeta } from "@storybook/addon-svelte-csf"; 3 + import ChevronDown from "$icon/chevron-down"; 4 + import Pencil from "$icon/pencil"; 5 + import Settings from "$icon/settings"; 6 + import LogOut from "$icon/log-out"; 7 + import Dropdown from "./Dropdown.svelte"; 8 + import DropdownItem from "./DropdownItem.svelte"; 9 + 10 + const { Story } = defineMeta({ 11 + title: "UI/Dropdown", 12 + component: Dropdown, 13 + tags: ["autodocs"] 14 + }); 15 + </script> 16 + 17 + <!-- Dropdown's `trigger` and `children` are snippet props, so the trigger content is 18 + declared once below and passed by reference; every story renders via `asChild` rather 19 + than through the args table. Click the trigger in the canvas to open the menu — 20 + `open` is internal state, not a prop. --> 21 + 22 + {#snippet menuTrigger()} 23 + <span>Menu</span> 24 + <ChevronDown class="size-4" /> 25 + {/snippet} 26 + 27 + {#snippet actionsTrigger()} 28 + <span>Actions</span> 29 + <ChevronDown class="size-4" /> 30 + {/snippet} 31 + 32 + {#snippet firstTrigger()} 33 + <span>First</span> 34 + <ChevronDown class="size-4" /> 35 + {/snippet} 36 + 37 + {#snippet secondTrigger()} 38 + <span>Second</span> 39 + <ChevronDown class="size-4" /> 40 + {/snippet} 41 + 42 + <Story name="Plain trigger" asChild> 43 + <Dropdown trigger={menuTrigger} label="Menu"> 44 + <DropdownItem icon={Pencil}>Edit</DropdownItem> 45 + <DropdownItem icon={Settings}>Settings</DropdownItem> 46 + <DropdownItem icon={LogOut} danger>Log out</DropdownItem> 47 + </Dropdown> 48 + </Story> 49 + 50 + <Story name="Button trigger" asChild> 51 + <Dropdown variant="button" trigger={actionsTrigger} label="Actions"> 52 + <DropdownItem icon={Pencil}>Edit</DropdownItem> 53 + <DropdownItem icon={Settings}>Settings</DropdownItem> 54 + </Dropdown> 55 + </Story> 56 + 57 + <!-- DropdownItem states: plain text, with icon, as a link (href), and the danger variant. --> 58 + <Story name="Item states" asChild> 59 + <Dropdown trigger={menuTrigger} label="Menu"> 60 + <DropdownItem>Plain item</DropdownItem> 61 + <DropdownItem icon={Settings}>With icon</DropdownItem> 62 + <DropdownItem href="/">Link item</DropdownItem> 63 + <DropdownItem icon={LogOut} danger>Danger item</DropdownItem> 64 + </Dropdown> 65 + </Story> 66 + 67 + <!-- Dropdowns sharing a `group` close their siblings when one opens. --> 68 + <Story name="Grouped" asChild> 69 + <div class="flex gap-3"> 70 + <Dropdown group="story-group" trigger={firstTrigger} label="First"> 71 + <DropdownItem>One</DropdownItem> 72 + <DropdownItem>Two</DropdownItem> 73 + </Dropdown> 74 + <Dropdown group="story-group" trigger={secondTrigger} label="Second"> 75 + <DropdownItem>Three</DropdownItem> 76 + <DropdownItem>Four</DropdownItem> 77 + </Dropdown> 78 + </div> 79 + </Story>
+24
web/src/lib/components/ui/Spinner.stories.svelte
··· 1 + <script module lang="ts"> 2 + import { defineMeta } from "@storybook/addon-svelte-csf"; 3 + import Spinner from "./Spinner.svelte"; 4 + 5 + const { Story } = defineMeta({ 6 + title: "UI/Spinner", 7 + component: Spinner, 8 + tags: ["autodocs"], 9 + argTypes: { 10 + class: { control: "text" } 11 + } 12 + }); 13 + </script> 14 + 15 + <Story name="Default" /> 16 + 17 + <Story name="Sizes" asChild> 18 + <div class="flex items-center gap-3"> 19 + <Spinner class="size-3" /> 20 + <Spinner class="size-4" /> 21 + <Spinner class="size-6" /> 22 + <Spinner class="size-8" /> 23 + </div> 24 + </Story>