@cl-light-ds/ui 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -45
- package/package.json +1 -1
- package/src/components/accordion/Accordion.astro +248 -248
- package/src/components/accordion/AccordionContent.astro +26 -26
- package/src/components/accordion/AccordionItem.astro +36 -36
- package/src/components/accordion/AccordionTrigger.astro +118 -118
- package/src/components/accordion/CHANGELOG.md +41 -41
- package/src/components/accordion/index.ts +4 -4
- package/src/components/alert/Alert.astro +167 -167
- package/src/components/alert/AlertDescription.astro +24 -24
- package/src/components/alert/AlertTitle.astro +24 -24
- package/src/components/alert/CHANGELOG.md +5 -5
- package/src/components/alert/index.ts +4 -4
- package/src/components/avatar/Avatar.astro +197 -197
- package/src/components/avatar/AvatarBadge.astro +44 -44
- package/src/components/avatar/AvatarFallback.astro +26 -26
- package/src/components/avatar/AvatarGroup.astro +26 -26
- package/src/components/avatar/AvatarGroupCount.astro +30 -30
- package/src/components/avatar/AvatarImage.astro +46 -46
- package/src/components/avatar/CHANGELOG.md +44 -44
- package/src/components/avatar/index.ts +6 -6
- package/src/components/badge/Badge.astro +167 -167
- package/src/components/badge/CHANGELOG.md +49 -49
- package/src/components/badge/index.ts +2 -2
- package/src/components/breadcrumb/Breadcrumb.astro +76 -76
- package/src/components/breadcrumb/BreadcrumbItem.astro +32 -32
- package/src/components/breadcrumb/BreadcrumbList.astro +26 -26
- package/src/components/breadcrumb/BreadcrumbSeparator.astro +26 -26
- package/src/components/breadcrumb/CHANGELOG.md +24 -24
- package/src/components/breadcrumb/index.ts +5 -5
- package/src/components/button/Button.astro +233 -233
- package/src/components/button/CHANGELOG.md +41 -41
- package/src/components/button/index.ts +2 -2
- package/src/components/button-group/ButtonGroup.astro +130 -130
- package/src/components/button-group/ButtonGroupSeparator.astro +30 -30
- package/src/components/button-group/ButtonGroupText.astro +21 -21
- package/src/components/button-group/CHANGELOG.md +34 -34
- package/src/components/button-group/index.ts +3 -3
- package/src/components/card/CHANGELOG.md +162 -119
- package/src/components/card/Card.astro +197 -182
- package/src/components/card/CardAction.astro +26 -26
- package/src/components/card/CardContent.astro +28 -28
- package/src/components/card/CardDescription.astro +28 -28
- package/src/components/card/CardFooter.astro +24 -24
- package/src/components/card/CardHeader.astro +30 -30
- package/src/components/card/CardMedia.astro +27 -27
- package/src/components/card/CardMediaActions.astro +21 -0
- package/src/components/card/CardMediaSelect.astro +21 -0
- package/src/components/card/CardOverlay.astro +32 -32
- package/src/components/card/CardTitle.astro +21 -21
- package/src/components/card/index.ts +12 -10
- package/src/components/carousel/CHANGELOG.md +5 -5
- package/src/components/carousel/Carousel.astro +106 -106
- package/src/components/carousel/CarouselSlide.astro +22 -22
- package/src/components/carousel/index.ts +3 -3
- package/src/components/checkbox/CHANGELOG.md +58 -58
- package/src/components/checkbox/Checkbox.astro +159 -159
- package/src/components/checkbox/index.ts +1 -1
- package/src/components/collapsible/CHANGELOG.md +5 -5
- package/src/components/collapsible/Collapsible.astro +202 -202
- package/src/components/collapsible/CollapsibleContent.astro +36 -36
- package/src/components/collapsible/CollapsibleTrigger.astro +34 -34
- package/src/components/collapsible/index.ts +4 -4
- package/src/components/combobox/CHANGELOG.md +27 -27
- package/src/components/combobox/Combobox.astro +365 -365
- package/src/components/combobox/ComboboxContent.astro +37 -37
- package/src/components/combobox/ComboboxEmpty.astro +18 -18
- package/src/components/combobox/ComboboxGroup.astro +19 -19
- package/src/components/combobox/ComboboxInput.astro +74 -74
- package/src/components/combobox/ComboboxItem.astro +39 -39
- package/src/components/combobox/ComboboxLabel.astro +18 -18
- package/src/components/combobox/ComboboxList.astro +21 -21
- package/src/components/combobox/ComboboxSeparator.astro +17 -17
- package/src/components/combobox/ComboboxTrigger.astro +30 -30
- package/src/components/combobox/ComboboxValue.astro +22 -22
- package/src/components/combobox/index.ts +11 -11
- package/src/components/date/CHANGELOG.md +18 -18
- package/src/components/date/Date.astro +137 -137
- package/src/components/date/index.ts +2 -2
- package/src/components/dialog/CHANGELOG.md +39 -39
- package/src/components/dialog/Dialog.astro +196 -196
- package/src/components/dialog/DialogClose.astro +23 -23
- package/src/components/dialog/DialogContent.astro +42 -42
- package/src/components/dialog/DialogDescription.astro +18 -18
- package/src/components/dialog/DialogFooter.astro +18 -18
- package/src/components/dialog/DialogHeader.astro +18 -18
- package/src/components/dialog/DialogOverlay.astro +19 -19
- package/src/components/dialog/DialogTitle.astro +18 -18
- package/src/components/dialog/DialogTrigger.astro +37 -37
- package/src/components/dialog/index.ts +9 -9
- package/src/components/dropdown-menu/CHANGELOG.md +194 -194
- package/src/components/dropdown-menu/DropdownMenu.astro +226 -226
- package/src/components/dropdown-menu/DropdownMenuCheckboxItem.astro +114 -114
- package/src/components/dropdown-menu/DropdownMenuContent.astro +42 -42
- package/src/components/dropdown-menu/DropdownMenuGroup.astro +19 -19
- package/src/components/dropdown-menu/DropdownMenuItem.astro +220 -220
- package/src/components/dropdown-menu/DropdownMenuItemContent.astro +79 -79
- package/src/components/dropdown-menu/DropdownMenuLabel.astro +20 -20
- package/src/components/dropdown-menu/DropdownMenuPortal.astro +13 -13
- package/src/components/dropdown-menu/DropdownMenuRadioGroup.astro +13 -13
- package/src/components/dropdown-menu/DropdownMenuRadioItem.astro +101 -101
- package/src/components/dropdown-menu/DropdownMenuSeparator.astro +18 -18
- package/src/components/dropdown-menu/DropdownMenuShortcut.astro +18 -18
- package/src/components/dropdown-menu/DropdownMenuTrigger.astro +49 -49
- package/src/components/dropdown-menu/dropdown-menu-item-variants.ts +24 -24
- package/src/components/dropdown-menu/index.ts +12 -12
- package/src/components/field/CHANGELOG.md +33 -33
- package/src/components/field/Field.astro +664 -664
- package/src/components/field/FieldContent.astro +20 -20
- package/src/components/field/FieldDescription.astro +22 -22
- package/src/components/field/FieldError.astro +51 -51
- package/src/components/field/FieldGroup.astro +20 -20
- package/src/components/field/FieldLabel.astro +22 -22
- package/src/components/field/FieldLegend.astro +23 -23
- package/src/components/field/FieldSeparator.astro +30 -30
- package/src/components/field/FieldSet.astro +20 -20
- package/src/components/field/FieldTitle.astro +20 -20
- package/src/components/field/index.ts +10 -10
- package/src/components/hover-card/CHANGELOG.md +20 -20
- package/src/components/hover-card/HoverCard.astro +235 -235
- package/src/components/hover-card/HoverCardContent.astro +54 -54
- package/src/components/hover-card/HoverCardTrigger.astro +38 -38
- package/src/components/hover-card/index.ts +3 -3
- package/src/components/input/CHANGELOG.md +29 -29
- package/src/components/input/Input.astro +130 -130
- package/src/components/input/index.ts +2 -2
- package/src/components/input-group/CHANGELOG.md +49 -49
- package/src/components/input-group/InputGroup.astro +135 -135
- package/src/components/input-group/InputGroupAddon.astro +49 -49
- package/src/components/input-group/InputGroupButton.astro +58 -58
- package/src/components/input-group/InputGroupInput.astro +18 -18
- package/src/components/input-group/InputGroupText.astro +21 -21
- package/src/components/input-group/InputGroupTextarea.astro +17 -17
- package/src/components/input-group/controlClasses.ts +2 -2
- package/src/components/input-group/index.ts +6 -6
- package/src/components/item/CHANGELOG.md +27 -27
- package/src/components/item/Item.astro +122 -122
- package/src/components/item/ItemActions.astro +21 -21
- package/src/components/item/ItemContent.astro +25 -25
- package/src/components/item/ItemDescription.astro +25 -25
- package/src/components/item/ItemFooter.astro +24 -24
- package/src/components/item/ItemGroup.astro +24 -24
- package/src/components/item/ItemHeader.astro +24 -24
- package/src/components/item/ItemMedia.astro +47 -47
- package/src/components/item/ItemSeparator.astro +21 -21
- package/src/components/item/ItemTitle.astro +25 -25
- package/src/components/item/index.ts +10 -10
- package/src/components/kbd/CHANGELOG.md +5 -5
- package/src/components/kbd/Kbd.astro +145 -145
- package/src/components/kbd/KbdGroup.astro +55 -55
- package/src/components/kbd/index.ts +2 -2
- package/src/components/label/CHANGELOG.md +28 -28
- package/src/components/label/Label.astro +97 -97
- package/src/components/label/index.ts +2 -2
- package/src/components/link-group/CHANGELOG.md +75 -75
- package/src/components/link-group/LinkGroup.astro +67 -67
- package/src/components/link-group/LinkGroupItem.astro +32 -32
- package/src/components/link-group/LinkGroupList.astro +25 -25
- package/src/components/link-group/LinkGroupTitle.astro +22 -22
- package/src/components/link-group/index.ts +5 -5
- package/src/components/link-group/link-group-list-variants.ts +15 -15
- package/src/components/marquee/CHANGELOG.md +5 -5
- package/src/components/marquee/Marquee.astro +119 -119
- package/src/components/marquee/index.ts +2 -2
- package/src/components/native-select/CHANGELOG.md +5 -5
- package/src/components/native-select/NativeSelect.astro +125 -125
- package/src/components/native-select/NativeSelectOptGroup.astro +14 -14
- package/src/components/native-select/NativeSelectOption.astro +9 -9
- package/src/components/native-select/index.ts +3 -3
- package/src/components/navigation-menu/CHANGELOG.md +63 -63
- package/src/components/navigation-menu/NavigationMenu.astro +414 -414
- package/src/components/navigation-menu/NavigationMenuContent.astro +37 -37
- package/src/components/navigation-menu/NavigationMenuIndicator.astro +24 -24
- package/src/components/navigation-menu/NavigationMenuItem.astro +22 -22
- package/src/components/navigation-menu/NavigationMenuLink.astro +18 -18
- package/src/components/navigation-menu/NavigationMenuList.astro +21 -21
- package/src/components/navigation-menu/NavigationMenuPositioner.astro +44 -44
- package/src/components/navigation-menu/NavigationMenuTrigger.astro +30 -30
- package/src/components/navigation-menu/NavigationMenuViewport.astro +19 -19
- package/src/components/navigation-menu/index.ts +10 -10
- package/src/components/organisms/lightning-sidebar-nav/CHANGELOG.md +50 -0
- package/src/components/organisms/lightning-sidebar-nav/LightningSidebarNav.astro +176 -0
- package/src/components/organisms/lightning-sidebar-nav/SidebarNav.astro +50 -0
- package/src/components/organisms/lightning-sidebar-nav/SidebarNavGroup.astro +74 -0
- package/src/components/organisms/lightning-sidebar-nav/SidebarNavItem.astro +94 -0
- package/src/components/organisms/lightning-sidebar-nav/SidebarNavProBadge.astro +26 -0
- package/src/components/organisms/lightning-sidebar-nav/SidebarNavSection.astro +71 -0
- package/src/components/organisms/lightning-sidebar-nav/SidebarNavSkeleton.astro +67 -0
- package/src/components/organisms/lightning-sidebar-nav/SidebarNavSubItem.astro +69 -0
- package/src/components/organisms/lightning-sidebar-nav/SidebarNavUsage.astro +78 -0
- package/src/components/organisms/lightning-sidebar-nav/SidebarNavWorkspaceAvatar.astro +46 -0
- package/src/components/organisms/lightning-sidebar-nav/SidebarNavWorkspaceSwitcher.astro +130 -0
- package/src/components/organisms/lightning-sidebar-nav/icons.ts +46 -0
- package/src/components/organisms/lightning-sidebar-nav/index.ts +18 -0
- package/src/components/organisms/lightning-sidebar-nav/types.ts +77 -0
- package/src/components/popover/CHANGELOG.md +20 -20
- package/src/components/popover/Popover.astro +211 -211
- package/src/components/popover/PopoverClose.astro +18 -18
- package/src/components/popover/PopoverContent.astro +50 -50
- package/src/components/popover/PopoverDescription.astro +18 -18
- package/src/components/popover/PopoverHeader.astro +18 -18
- package/src/components/popover/PopoverTitle.astro +18 -18
- package/src/components/popover/PopoverTrigger.astro +35 -35
- package/src/components/popover/index.ts +7 -7
- package/src/components/radio-group/CHANGELOG.md +31 -31
- package/src/components/radio-group/RadioGroup.astro +81 -81
- package/src/components/radio-group/RadioGroupItem.astro +51 -51
- package/src/components/radio-group/index.ts +3 -3
- package/src/components/select/CHANGELOG.md +39 -39
- package/src/components/select/Select.astro +373 -373
- package/src/components/select/SelectContent.astro +91 -91
- package/src/components/select/SelectGroup.astro +19 -19
- package/src/components/select/SelectItem.astro +48 -48
- package/src/components/select/SelectLabel.astro +18 -18
- package/src/components/select/SelectSeparator.astro +17 -17
- package/src/components/select/SelectTrigger.astro +34 -34
- package/src/components/select/SelectValue.astro +22 -22
- package/src/components/select/index.ts +8 -8
- package/src/components/separator/CHANGELOG.md +5 -5
- package/src/components/separator/Separator.astro +60 -60
- package/src/components/separator/index.ts +2 -2
- package/src/components/skeleton/CHANGELOG.md +5 -5
- package/src/components/skeleton/Skeleton.astro +121 -121
- package/src/components/skeleton/index.ts +1 -1
- package/src/components/slider/CHANGELOG.md +5 -5
- package/src/components/slider/Slider.astro +198 -198
- package/src/components/slider/index.ts +1 -1
- package/src/components/spinner/CHANGELOG.md +18 -18
- package/src/components/spinner/Spinner.astro +135 -135
- package/src/components/spinner/index.ts +1 -1
- package/src/components/sticky-surface/CHANGELOG.md +5 -5
- package/src/components/sticky-surface/StickySurface.astro +152 -152
- package/src/components/sticky-surface/index.ts +2 -2
- package/src/components/switch/CHANGELOG.md +29 -29
- package/src/components/switch/Switch.astro +106 -106
- package/src/components/switch/index.ts +1 -1
- package/src/components/table/CHANGELOG.md +24 -24
- package/src/components/table/Table.astro +358 -251
- package/src/components/table/TableBody.astro +17 -18
- package/src/components/table/TableBulkToolbar.astro +37 -0
- package/src/components/table/TableCaption.astro +17 -17
- package/src/components/table/TableCell.astro +45 -21
- package/src/components/table/TableCellText.astro +17 -0
- package/src/components/table/TableColumnMenu.astro +50 -0
- package/src/components/table/TableEditableCell.astro +287 -0
- package/src/components/table/TableEmpty.astro +17 -0
- package/src/components/table/TableFolderCell.astro +38 -0
- package/src/components/table/TableFooter.astro +21 -21
- package/src/components/table/TableHead.astro +90 -21
- package/src/components/table/TableHeader.astro +17 -17
- package/src/components/table/TablePagination.astro +57 -0
- package/src/components/table/TableRow.astro +52 -21
- package/src/components/table/TableSelectAllBanner.astro +34 -0
- package/src/components/table/TableSelectCell.astro +50 -0
- package/src/components/table/TableSkeletonRow.astro +21 -0
- package/src/components/table/index.ts +19 -9
- package/src/components/table/table-cell-variants.ts +29 -0
- package/src/components/tabs/CHANGELOG.md +5 -5
- package/src/components/tabs/Tabs.astro +306 -306
- package/src/components/tabs/TabsContent.astro +47 -47
- package/src/components/tabs/TabsList.astro +69 -69
- package/src/components/tabs/TabsTrigger.astro +40 -40
- package/src/components/tabs/index.ts +6 -6
- package/src/components/textarea/CHANGELOG.md +22 -22
- package/src/components/textarea/Textarea.astro +58 -58
- package/src/components/textarea/index.ts +2 -2
- package/src/components/toggle/CHANGELOG.md +23 -23
- package/src/components/toggle/Toggle.astro +150 -150
- package/src/components/toggle/index.ts +1 -1
- package/src/components/toggle-group/CHANGELOG.md +18 -18
- package/src/components/toggle-group/ToggleGroup.astro +236 -236
- package/src/components/toggle-group/ToggleGroupItem.astro +48 -48
- package/src/components/toggle-group/index.ts +2 -2
- package/src/components/tooltip/CHANGELOG.md +22 -22
- package/src/components/tooltip/Tooltip.astro +357 -357
- package/src/components/tooltip/TooltipContent.astro +85 -85
- package/src/components/tooltip/TooltipTrigger.astro +34 -34
- package/src/components/tooltip/index.ts +3 -3
- package/src/lib/table-controller.ts +1090 -0
- package/src/lib/toggle-group-controller.ts +113 -113
- package/src/lib/toggle-shared.ts +27 -27
- package/src/lib/utils.ts +6 -6
- package/src/styles/globals.css +197 -177
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
---
|
|
2
|
-
import { cn } from "@cl-light-ds/ui/lib/utils";
|
|
3
|
-
import type { HTMLAttributes } from "astro/types";
|
|
4
|
-
|
|
5
|
-
interface Props extends HTMLAttributes<"div"> {
|
|
6
|
-
class?: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const { class: className = "", ...rest } = Astro.props as Props;
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
<div
|
|
13
|
-
hidden
|
|
14
|
-
data-slot="dialog-overlay"
|
|
15
|
-
class={cn("data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 isolate z-50", className)}
|
|
16
|
-
{...rest}
|
|
17
|
-
>
|
|
18
|
-
<slot />
|
|
19
|
-
</div>
|
|
1
|
+
---
|
|
2
|
+
import { cn } from "@cl-light-ds/ui/lib/utils";
|
|
3
|
+
import type { HTMLAttributes } from "astro/types";
|
|
4
|
+
|
|
5
|
+
interface Props extends HTMLAttributes<"div"> {
|
|
6
|
+
class?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const { class: className = "", ...rest } = Astro.props as Props;
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<div
|
|
13
|
+
hidden
|
|
14
|
+
data-slot="dialog-overlay"
|
|
15
|
+
class={cn("data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 isolate z-50", className)}
|
|
16
|
+
{...rest}
|
|
17
|
+
>
|
|
18
|
+
<slot />
|
|
19
|
+
</div>
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
---
|
|
2
|
-
import { cn } from "@cl-light-ds/ui/lib/utils";
|
|
3
|
-
import type { HTMLAttributes } from "astro/types";
|
|
4
|
-
|
|
5
|
-
interface Props extends HTMLAttributes<"div"> {
|
|
6
|
-
class?: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const { class: className = "", ...rest } = Astro.props as Props;
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
<div
|
|
13
|
-
{...rest}
|
|
14
|
-
data-slot="dialog-title"
|
|
15
|
-
class={cn("text-xl leading-6 font-semibold cn-font-heading", className)}
|
|
16
|
-
>
|
|
17
|
-
<slot />
|
|
18
|
-
</div>
|
|
1
|
+
---
|
|
2
|
+
import { cn } from "@cl-light-ds/ui/lib/utils";
|
|
3
|
+
import type { HTMLAttributes } from "astro/types";
|
|
4
|
+
|
|
5
|
+
interface Props extends HTMLAttributes<"div"> {
|
|
6
|
+
class?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const { class: className = "", ...rest } = Astro.props as Props;
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<div
|
|
13
|
+
{...rest}
|
|
14
|
+
data-slot="dialog-title"
|
|
15
|
+
class={cn("text-xl leading-6 font-semibold cn-font-heading", className)}
|
|
16
|
+
>
|
|
17
|
+
<slot />
|
|
18
|
+
</div>
|
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
---
|
|
2
|
-
import { cn } from "@cl-light-ds/ui/lib/utils";
|
|
3
|
-
import type { HTMLAttributes } from "astro/types";
|
|
4
|
-
|
|
5
|
-
interface Props extends HTMLAttributes<"button"> {
|
|
6
|
-
class?: string;
|
|
7
|
-
/** Radix-like: render wrapper so the child can be the interactive element */
|
|
8
|
-
asChild?: boolean;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const {
|
|
12
|
-
class: className = "",
|
|
13
|
-
asChild = false,
|
|
14
|
-
type = "button",
|
|
15
|
-
...rest
|
|
16
|
-
} = Astro.props as Props;
|
|
17
|
-
|
|
18
|
-
const defaultClasses =
|
|
19
|
-
"cursor-pointer select-none outline-none group-focus-within:pointer-events-none inline-flex items-center gap-1";
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
{
|
|
23
|
-
asChild ? (
|
|
24
|
-
<div data-slot="dialog-trigger" data-as-child class={cn("", defaultClasses, className)}>
|
|
25
|
-
<slot />
|
|
26
|
-
</div>
|
|
27
|
-
) : (
|
|
28
|
-
<button
|
|
29
|
-
{...rest}
|
|
30
|
-
type={type}
|
|
31
|
-
data-slot="dialog-trigger"
|
|
32
|
-
class={cn("", defaultClasses, className)}
|
|
33
|
-
>
|
|
34
|
-
<slot />
|
|
35
|
-
</button>
|
|
36
|
-
)
|
|
37
|
-
}
|
|
1
|
+
---
|
|
2
|
+
import { cn } from "@cl-light-ds/ui/lib/utils";
|
|
3
|
+
import type { HTMLAttributes } from "astro/types";
|
|
4
|
+
|
|
5
|
+
interface Props extends HTMLAttributes<"button"> {
|
|
6
|
+
class?: string;
|
|
7
|
+
/** Radix-like: render wrapper so the child can be the interactive element */
|
|
8
|
+
asChild?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
class: className = "",
|
|
13
|
+
asChild = false,
|
|
14
|
+
type = "button",
|
|
15
|
+
...rest
|
|
16
|
+
} = Astro.props as Props;
|
|
17
|
+
|
|
18
|
+
const defaultClasses =
|
|
19
|
+
"cursor-pointer select-none outline-none group-focus-within:pointer-events-none inline-flex items-center gap-1";
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
{
|
|
23
|
+
asChild ? (
|
|
24
|
+
<div data-slot="dialog-trigger" data-as-child class={cn("", defaultClasses, className)}>
|
|
25
|
+
<slot />
|
|
26
|
+
</div>
|
|
27
|
+
) : (
|
|
28
|
+
<button
|
|
29
|
+
{...rest}
|
|
30
|
+
type={type}
|
|
31
|
+
data-slot="dialog-trigger"
|
|
32
|
+
class={cn("", defaultClasses, className)}
|
|
33
|
+
>
|
|
34
|
+
<slot />
|
|
35
|
+
</button>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export { default as Dialog } from "./Dialog.astro";
|
|
2
|
-
export { default as DialogTitle } from "./DialogTitle.astro";
|
|
3
|
-
export { default as DialogDescription } from "./DialogDescription.astro";
|
|
4
|
-
export { default as DialogContent } from "./DialogContent.astro";
|
|
5
|
-
export { default as DialogFooter } from "./DialogFooter.astro";
|
|
6
|
-
export { default as DialogClose } from "./DialogClose.astro";
|
|
7
|
-
export { default as DialogHeader } from "./DialogHeader.astro";
|
|
8
|
-
export { default as DialogTrigger } from "./DialogTrigger.astro";
|
|
9
|
-
export { default as DialogOverlay } from "./DialogOverlay.astro";
|
|
1
|
+
export { default as Dialog } from "./Dialog.astro";
|
|
2
|
+
export { default as DialogTitle } from "./DialogTitle.astro";
|
|
3
|
+
export { default as DialogDescription } from "./DialogDescription.astro";
|
|
4
|
+
export { default as DialogContent } from "./DialogContent.astro";
|
|
5
|
+
export { default as DialogFooter } from "./DialogFooter.astro";
|
|
6
|
+
export { default as DialogClose } from "./DialogClose.astro";
|
|
7
|
+
export { default as DialogHeader } from "./DialogHeader.astro";
|
|
8
|
+
export { default as DialogTrigger } from "./DialogTrigger.astro";
|
|
9
|
+
export { default as DialogOverlay } from "./DialogOverlay.astro";
|
|
@@ -1,194 +1,194 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to the `DropdownMenu` component will be documented in this file.
|
|
4
|
-
|
|
5
|
-
## Date 10th Sep 2026
|
|
6
|
-
## Figma alignment (node 2329-6521, prop table 7392-10142)
|
|
7
|
-
|
|
8
|
-
### Variants
|
|
9
|
-
|
|
10
|
-
- Added a `category` variant to `DropdownMenuItem` (`variant: "default" | "destructive" | "category"`, was `"default" | "destructive"`), matching Figma's `.item` component set which documents `Category` as a third item variant. Renders as a non-interactive (`role="presentation"`, `tabindex={-1}`, `pointer-events-none`), muted (`text-muted-foreground font-medium`) row — the visual is intentionally close to the existing `DropdownMenuLabel`, but this variant lives on `DropdownMenuItem` itself so it can sit inline in a list without an extra import, per Figma's own prop table contract. It does not render a separator automatically — pair it with the existing `<DropdownMenuSeparator />` if a divider is wanted, same as any other item.
|
|
11
|
-
- Converted `DropdownMenuItem` from hardcoded `data-[variant=...]:` Tailwind selectors to `class-variance-authority` (`cva`), matching the pattern already used by `Card`/`Button`/`Badge`/`Avatar`.
|
|
12
|
-
|
|
13
|
-
### Sizing & Spacing
|
|
14
|
-
|
|
15
|
-
- **Radius:** `rounded-sm` (4px) → `rounded-lg` (8px) on `DropdownMenuItem`, `DropdownMenuCheckboxItem`, and `DropdownMenuRadioItem` — Figma's item box is consistently `rounded-[8px]`.
|
|
16
|
-
- **Padding:** `py-1.5` (6px) → `py-1` (4px) on all three item components — Figma's item is `py-[4px]` (`spacing/1`), horizontal `px-2` (`spacing/2`, 8px) was already correct.
|
|
17
|
-
- **Typography:** item label moved from unstyled `text-sm` (14px, regular) → `text-xs font-semibold` (12px/600) on all three item components, matching Figma's `text/sm/sm-semibold` text style for the item label. Supportive text (new, see below) is `text-xs font-normal text-muted-foreground`.
|
|
18
|
-
- **Popover chrome** (`DropdownMenuContent`): `ring-1 ring-foreground/10` → `border border-border` (Figma uses a real 1px border, not a ring); `shadow-md` → Figma's actual 3-layer `shadow-lg` drop-shadow (`0.3px 2px/-2.2 spread #1F003B05, 0.5px 3.2px/-1.4 #1F003B08, 0.7px 4px/-1.8 #1F003B0F`); `p-1` (4px) → `p-2` (8px); added an explicit `gap-1.5` (6px) between items, matching Figma's item-to-item gap (previously no gap was set at all, relying on nothing — items had no separation beyond their own padding).
|
|
19
|
-
|
|
20
|
-
### New Props (`DropdownMenuItem` only)
|
|
21
|
-
|
|
22
|
-
Following the Button/Badge "boolean prop, auto-detected from a named slot via `Astro.slots.has(...)`" convention:
|
|
23
|
-
|
|
24
|
-
- `checked?: boolean` (default `false`) — presentational checked state, only visible when `showCheckbox` is also set.
|
|
25
|
-
- `showCheckbox?: boolean` (default `false`) — reserves a 20px leading slot for a checkmark; **kept the existing indicator pattern** (no visible box at rest, a checkmark fades in when `checked`), matching `DropdownMenuCheckboxItem`'s existing visual language rather than inventing Figma's filled-square glyph, which has no documented unchecked state in the file. `DropdownMenuCheckboxItem` itself is unchanged — this is a separate, presentation-only affordance for a plain `DropdownMenuItem`.
|
|
26
|
-
- `leadingIcon?: boolean` (default: has a `leading-icon` slot) — renders `<slot name="leading-icon" />` at 20px.
|
|
27
|
-
- `avatar?: boolean` (default: has an `avatar` slot) — renders `<slot name="avatar" />`. Use `<Avatar size="sm">` (24px) — it already matches Figma's item avatar size exactly, no new Avatar sizing needed.
|
|
28
|
-
- `showBadge?: boolean` (default: has a `badge` slot) — renders `<slot name="badge" />`. Use `<Badge size="sm">`.
|
|
29
|
-
- `showSubmenuIcon?: boolean` (default `false`) — renders a static 20px chevron-right. **Cosmetic only** — there is no nested-submenu infrastructure (flyout positioning, keyboard handling) anywhere in `DropdownMenu` yet. See "Flagged for the designer" below.
|
|
30
|
-
- `supportiveText?: boolean` (default: has a `supportive-text` slot) — renders a second, muted line under the item label via `<slot name="supportive-text" />`.
|
|
31
|
-
|
|
32
|
-
`leadingIcon`, `avatar`, and `showCheckbox` are independent booleans (can combine), per Figma's literal prop table — not consolidated into a single `leading` enum.
|
|
33
|
-
|
|
34
|
-
### Trigger
|
|
35
|
-
|
|
36
|
-
- `DropdownMenuTrigger` now defaults `variant="outline"` (was inheriting `Button`'s own default `"default"`/primary-blue variant) — Figma's default/closed trigger state is a white/bordered button, not primary. Still fully overridable via the existing `variant` prop.
|
|
37
|
-
- `DropdownMenuTrigger` now renders a default trailing chevron (`ChevronDown` from `@lucide/astro`) when the consumer doesn't slot their own `tail-icon` content — Figma's trigger always shows a chevron as core anatomy, not an opt-in add-on. It rotates 180° when the menu is open via a new `group/dropdown-menu` marker added to `DropdownMenu.astro`'s root (`data-state` lands on that root element, confirmed in `@data-slot/dropdown-menu`'s runtime). Pass `tailIcon={false}` to opt out entirely, or your own `<Icon slot="tail-icon" />` to override.
|
|
38
|
-
|
|
39
|
-
### Tokens (`globals.css`)
|
|
40
|
-
|
|
41
|
-
- No new tokens needed. Every color/radius used above (`background`, `background-hover`, `muted`, `muted-foreground`, `foreground`, `border`, `ring`, `chart-1`, `destructive`, `popover`, `primary`, `primary-foreground`) already existed — this was a binding/usage fix, not a token-addition pass.
|
|
42
|
-
|
|
43
|
-
### Storybook & Docs
|
|
44
|
-
|
|
45
|
-
- `apps/web/src/pages/storybook/_sections/dropdown-menu.astro`: added the seven new `DropdownMenuItem` prop rows to `itemProps`, updated the `variant` row to include `"category"`, and added preview blocks for leading icon / avatar / badge / supportive text / category / checkbox.
|
|
46
|
-
- `DropdownMenuStory.astro` / `DropdownMenu.stories.ts`: added stories covering the new item variants/props.
|
|
47
|
-
|
|
48
|
-
### Not changed
|
|
49
|
-
|
|
50
|
-
- `DropdownMenuLabel.astro` — spotted a pre-existing bug while auditing (its class list sets both `text-xs` and `text-sm`, with `text-sm` winning via `tailwind-merge`, so labels render at 14px instead of the intended 12px) but left as-is since it wasn't part of this pass's scope; worth a follow-up fix.
|
|
51
|
-
- `DropdownMenuGroup.astro`, `DropdownMenuSeparator.astro`, `DropdownMenuPortal.astro`, `DropdownMenuRadioGroup.astro`, `DropdownMenuShortcut.astro` — no Figma delta identified.
|
|
52
|
-
|
|
53
|
-
### Flagged for the designer / engineering (not addressed in this pass)
|
|
54
|
-
|
|
55
|
-
- **Real nested submenus:** Figma's `.item` prop table documents `showSubmenuIcon`, implying a submenu affordance, but no submenu flyout, positioning, or keyboard-navigation behavior exists anywhere in `DropdownMenu`'s runtime (`@data-slot/dropdown-menu`). This pass ships the chevron as a static, cosmetic icon only. Building real nested submenus is a separate, larger feature.
|
|
56
|
-
- **Checkbox unchecked state:** Figma's item file only shows a filled/checked 20px checkbox square in its Left Content — there's no authored unchecked-square state to match. `showCheckbox`/`checked` on `DropdownMenuItem` therefore reuses the existing checkmark-only indicator pattern instead; worth designing a real unchecked square in Figma if a literal checkbox control (vs. a checked-indicator) is wanted here later.
|
|
57
|
-
- **`DropdownMenuLabel` vs. `DropdownMenuItem variant="category"` overlap:** these are now two different ways to render a muted section header (one a dedicated component, one an item variant). Worth a follow-up design decision on whether `DropdownMenuLabel` should be deprecated in favor of `variant="category"`, or the two are meant to serve genuinely different call sites.
|
|
58
|
-
|
|
59
|
-
## Date 10th Sep 2026 (follow-up)
|
|
60
|
-
## Fix: menuitemcheckbox/menuitemradio alignment + item padding (node 2329-6521, prop table 7392-10142)
|
|
61
|
-
|
|
62
|
-
The designer updated Figma again after the first pass above: the `DropdownMenuItem` prop table now
|
|
63
|
-
explicitly ties `showCheckbox` to *"menuitemcheckbox"* and adds a new `showRadio` → *"menuitemradio"*,
|
|
64
|
-
and the item component's padding/radio glyph changed. This entry reconciles that with the framework's
|
|
65
|
-
existing three-component structure (`DropdownMenuItem` / `DropdownMenuCheckboxItem` / `DropdownMenuRadioItem`)
|
|
66
|
-
rather than collapsing them into one — see "Architecture" below for why.
|
|
67
|
-
|
|
68
|
-
### Architecture
|
|
69
|
-
|
|
70
|
-
- Extracted the shared item chrome into two new **internal, non-exported** files (not added to `index.ts`,
|
|
71
|
-
same precedent as `DropdownMenuStory.astro`): `dropdown-menu-item-variants.ts` (the single `cva` box-class
|
|
72
|
-
builder) and `DropdownMenuItemContent.astro` (the shared Left Content / label / Right Content layout).
|
|
73
|
-
`DropdownMenuItem`, `DropdownMenuCheckboxItem`, and `DropdownMenuRadioItem` all now render through these,
|
|
74
|
-
instead of each duplicating the same padding/typography classes.
|
|
75
|
-
- **Did not consolidate into one role-switching component.** Figma's single-component mental model
|
|
76
|
-
(`DropdownMenuItem` + a boolean that "becomes" a checkbox/radio) doesn't map cleanly onto this
|
|
77
|
-
framework: `@data-slot/dropdown-menu`'s runtime detects real interactive item type purely from each
|
|
78
|
-
element's `data-slot` value and manages `role`/`tabindex`/`aria-checked`/checked-state tracking
|
|
79
|
-
automatically for `[data-slot="dropdown-menu-checkbox-item"]` / `[data-slot="dropdown-menu-radio-item"]`.
|
|
80
|
-
Collapsing three ARIA-correct, already-working components into one conditionally-shaped component would
|
|
81
|
-
have been a much larger, riskier change for no framework-side benefit — the shared-chrome extraction above
|
|
82
|
-
gets the same "one visual spec, one place" outcome without touching the runtime contract.
|
|
83
|
-
|
|
84
|
-
### Sizing & Spacing
|
|
85
|
-
|
|
86
|
-
- **Padding correction:** Figma's item box is now uniform `p-2` (8px on all sides) for `default`/`destructive`
|
|
87
|
-
— was `px-2 py-1` last pass (a padding fix I hadn't fully caught the first time). Row height grows
|
|
88
|
-
24px → 32px accordingly, on `DropdownMenuItem`, `DropdownMenuCheckboxItem`, and `DropdownMenuRadioItem`.
|
|
89
|
-
- **`category` has its own distinct, tighter padding** (`pt-1 px-1 pb-0 gap-1`, no `p-2`) — this was already
|
|
90
|
-
structurally different in Figma and wasn't captured correctly last pass, where it incorrectly shared the
|
|
91
|
-
interactive variants' row padding.
|
|
92
|
-
- `DropdownMenuCheckboxItem`/`DropdownMenuRadioItem` **dropped their old asymmetric `pr-8 pl-2` padding**
|
|
93
|
-
entirely — no longer needed now that the checked/selected indicator lives in Left Content instead of
|
|
94
|
-
floating absolute-right (see below), so they use the exact same `p-2` box as a plain `DropdownMenuItem`.
|
|
95
|
-
|
|
96
|
-
### Checkbox / Radio glyph — switched to Figma's literal look
|
|
97
|
-
|
|
98
|
-
- `DropdownMenuCheckboxItem`/`DropdownMenuRadioItem` moved from the old shadcn-style indicator (invisible
|
|
99
|
-
until checked, a bare checkmark fading in at `absolute right-2`) to Figma's literal glyph: an
|
|
100
|
-
always-reserved 20px box (`rounded-sm`, checkbox) or circle (`rounded-full`, radio) in Left Content,
|
|
101
|
-
outlined (`border-input`) when unchecked and `bg-primary border-primary` + a white check icon / centered
|
|
102
|
-
dot when checked. Visual language borrowed directly from the existing `Checkbox.astro`/`RadioGroupItem.astro`
|
|
103
|
-
components (same token usage, scaled to the item's 20px glyph size) rather than inventing new colors.
|
|
104
|
-
- This is driven purely by CSS off the `data-checked` attribute the runtime **already sets** on these exact
|
|
105
|
-
elements (confirmed in `@data-slot/dropdown-menu`'s source) via `group-data-checked/dropdown-menu-item:` —
|
|
106
|
-
no new JavaScript needed, no change to the runtime's checked-state tracking or click/keyboard handling.
|
|
107
|
-
- `DropdownMenuItem`'s own presentational `showCheckbox` also switched from the old checkmark-only pattern to
|
|
108
|
-
the same 20px box glyph (driven by its static `checked` prop, not runtime state), for visual consistency
|
|
109
|
-
with the real components. New presentational `showRadio` (see below) uses the matching circle glyph.
|
|
110
|
-
- **Resolves last pass's "Checkbox unchecked state" flag** — Figma still doesn't author a literal unchecked
|
|
111
|
-
square, but reusing `Checkbox`'s existing unchecked visual (bordered, no fill) is a defensible, low-risk
|
|
112
|
-
default that's already established elsewhere in the design system.
|
|
113
|
-
|
|
114
|
-
### New Props
|
|
115
|
-
|
|
116
|
-
- `DropdownMenuItem` gains `showRadio?: boolean` (default `false`), mirroring `showCheckbox` — presentational
|
|
117
|
-
only, reuses the shared `checked` prop. Per Figma's prop table: `showRadio` → *"Show radiobutton which is
|
|
118
|
-
menuitemradio"*.
|
|
119
|
-
- `DropdownMenuCheckboxItem` and `DropdownMenuRadioItem` both gain the same extra prop surface
|
|
120
|
-
`DropdownMenuItem` got last pass: `leadingIcon`, `avatar`, `showBadge`, `showSubmenuIcon`, `supportiveText`
|
|
121
|
-
(same `Astro.slots.has(...)`-default convention). Sharing chrome made this nearly free, and Figma's own
|
|
122
|
-
item variants already mix a checkbox with other leading/trailing content in the same row.
|
|
123
|
-
- `value`, `disabled`, `inset`, `defaultChecked`, `role="menuitemcheckbox"`/`"menuitemradio"`,
|
|
124
|
-
`data-slot="dropdown-menu-checkbox-item"`/`"dropdown-menu-radio-item"` are all **unchanged** — this is the
|
|
125
|
-
ARIA/runtime contract and was not touched.
|
|
126
|
-
|
|
127
|
-
### Fixed (implementation bug, not a Figma delta)
|
|
128
|
-
|
|
129
|
-
- While building the shared chrome, caught a real bug in the first draft: forwarding a
|
|
130
|
-
`<Fragment slot="leading-icon">` etc. from each root component into the shared `DropdownMenuItemContent`
|
|
131
|
-
made `Astro.slots.has("leading-icon")` always return `true` inside that shared partial (it saw the
|
|
132
|
-
pass-through Fragment, not whether the real end-consumer supplied content), so empty leading-icon/avatar/
|
|
133
|
-
badge wrapper `<span>`s rendered on every single item regardless of props. Fixed by resolving
|
|
134
|
-
`Astro.slots.has(...)` defaults in each outer component (`DropdownMenuItem`/`CheckboxItem`/`RadioItem`,
|
|
135
|
-
against their *own* callers) and passing explicit resolved booleans down — the shared partial no longer
|
|
136
|
-
does any slot introspection of its own. Verified via built story output before/after (span count per item
|
|
137
|
-
went from unconditional-always to matching only the items that actually set the prop).
|
|
138
|
-
|
|
139
|
-
### Storybook & Docs
|
|
140
|
-
|
|
141
|
-
- `apps/web/src/pages/storybook/_sections/dropdown-menu.astro`: added a `showRadio` row to `itemProps`;
|
|
142
|
-
updated `leadingIcon`/`avatar`/`showBadge`/`showSubmenuIcon`/`supportiveText` descriptions to note they now
|
|
143
|
-
apply to all three item components, not just `DropdownMenuItem`; enriched the Single Select/Multi Select
|
|
144
|
-
preview blocks to exercise the new prop parity (`supportiveText` on a radio item, `showBadge` on a checkbox
|
|
145
|
-
item); added `showRadio` to the "Item content" preview.
|
|
146
|
-
- `DropdownMenuStory.astro`/`DropdownMenu.stories.ts`: added a new `CheckboxRadioContent` story
|
|
147
|
-
(`showCheckboxRadio` prop) covering presentational checked/unchecked checkbox and radio glyphs side by side
|
|
148
|
-
with the real, runtime-tracked `DropdownMenuCheckboxItem`/`DropdownMenuRadioItem` (one with `avatar`, one
|
|
149
|
-
with `showBadge`) so both visual systems are directly comparable.
|
|
150
|
-
|
|
151
|
-
### Flagged for the designer / engineering (not addressed in this pass)
|
|
152
|
-
|
|
153
|
-
- **`showCheckbox`/`showRadio` on plain `DropdownMenuItem` are cosmetic only** — they draw the glyph via a
|
|
154
|
-
static prop and don't manage checked/selected state or emit events. Don't reach for them expecting real
|
|
155
|
-
interactivity; that's what `DropdownMenuCheckboxItem`/`DropdownMenuRadioItem` are for. Worth a naming pass
|
|
156
|
-
if this keeps causing confusion (e.g. renaming the presentational props to make the distinction more
|
|
157
|
-
obvious in autocomplete).
|
|
158
|
-
- Carried over from last pass, still open: real nested submenus (`showSubmenuIcon` is still cosmetic-only),
|
|
159
|
-
and the `DropdownMenuLabel` vs. `variant="category"` overlap.
|
|
160
|
-
|
|
161
|
-
## Date 10th Sep 2026 (follow-up)
|
|
162
|
-
## Fix: distinct Hover vs. Focus states (node 2529-2338, item states)
|
|
163
|
-
|
|
164
|
-
While adding a states preview to the Storybook docs, caught that `DropdownMenuItem` (and by extension
|
|
165
|
-
`DropdownMenuCheckboxItem`/`DropdownMenuRadioItem`, which share its `cva`) only ever had **one** interactive
|
|
166
|
-
style — `focus:bg-background-hover` — with no real `:hover` class at all. Figma documents Hover and Focused
|
|
167
|
-
as two visually distinct states (Hover: grey background only; Focused: a blue border + ring, no background
|
|
168
|
-
change), which wasn't implemented.
|
|
169
|
-
|
|
170
|
-
### Fixed
|
|
171
|
-
|
|
172
|
-
- `dropdown-menu-item-variants.ts`: added a real `hover:bg-background-hover` (per variant, `destructive` gets
|
|
173
|
-
`hover:text-destructive dark:hover:bg-destructive/20`) so mouse hover now works with plain CSS — no JS
|
|
174
|
-
required, matching Figma's Hover state.
|
|
175
|
-
- Replaced `focus:bg-background-hover` with `focus-visible:border-ring focus-visible:ring-2
|
|
176
|
-
focus-visible:ring-chart-1`, matching Figma's Focused effect (`border-ring` + a 2px ring) and the same
|
|
177
|
-
`focus-visible:border-ring focus-visible:ring-*` pattern already used by `Button`/`Card`. Added
|
|
178
|
-
`border border-transparent` to the shared base so the border can swap color on focus without any layout
|
|
179
|
-
shift (same technique `Button`/`Card` already use).
|
|
180
|
-
- Using `:focus-visible` instead of plain `:focus` is deliberate: the dropdown runtime calls a real
|
|
181
|
-
`element.focus()` on pointer hover (so highlight tracks the mouse for accessibility purposes) — with
|
|
182
|
-
`:focus-visible`, browsers' own heuristics correctly suppress the keyboard-style ring for that
|
|
183
|
-
pointer-triggered focus (real `:hover` covers the visual instead) while still showing the ring for genuine
|
|
184
|
-
keyboard navigation (Tab/Arrow keys). Net effect: mouse hover shows grey background only, keyboard
|
|
185
|
-
navigation shows the blue ring — exactly Figma's distinction, with no behavior change to the runtime itself.
|
|
186
|
-
|
|
187
|
-
### Storybook & Docs
|
|
188
|
-
|
|
189
|
-
- `apps/web/src/pages/storybook/_sections/dropdown-menu.astro`: added a new "DropdownMenuItem states" preview
|
|
190
|
-
(Default/Hover/Focus/Disabled) above "DropdownMenuItem variants" — Hover/Focus rows use a forced `class`
|
|
191
|
-
override for an always-visible reference, with a note that the same states are live (hover/Tab to see them
|
|
192
|
-
for real anywhere in the gallery).
|
|
193
|
-
|
|
194
|
-
## Unreleased
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the `DropdownMenu` component will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## Date 10th Sep 2026
|
|
6
|
+
## Figma alignment (node 2329-6521, prop table 7392-10142)
|
|
7
|
+
|
|
8
|
+
### Variants
|
|
9
|
+
|
|
10
|
+
- Added a `category` variant to `DropdownMenuItem` (`variant: "default" | "destructive" | "category"`, was `"default" | "destructive"`), matching Figma's `.item` component set which documents `Category` as a third item variant. Renders as a non-interactive (`role="presentation"`, `tabindex={-1}`, `pointer-events-none`), muted (`text-muted-foreground font-medium`) row — the visual is intentionally close to the existing `DropdownMenuLabel`, but this variant lives on `DropdownMenuItem` itself so it can sit inline in a list without an extra import, per Figma's own prop table contract. It does not render a separator automatically — pair it with the existing `<DropdownMenuSeparator />` if a divider is wanted, same as any other item.
|
|
11
|
+
- Converted `DropdownMenuItem` from hardcoded `data-[variant=...]:` Tailwind selectors to `class-variance-authority` (`cva`), matching the pattern already used by `Card`/`Button`/`Badge`/`Avatar`.
|
|
12
|
+
|
|
13
|
+
### Sizing & Spacing
|
|
14
|
+
|
|
15
|
+
- **Radius:** `rounded-sm` (4px) → `rounded-lg` (8px) on `DropdownMenuItem`, `DropdownMenuCheckboxItem`, and `DropdownMenuRadioItem` — Figma's item box is consistently `rounded-[8px]`.
|
|
16
|
+
- **Padding:** `py-1.5` (6px) → `py-1` (4px) on all three item components — Figma's item is `py-[4px]` (`spacing/1`), horizontal `px-2` (`spacing/2`, 8px) was already correct.
|
|
17
|
+
- **Typography:** item label moved from unstyled `text-sm` (14px, regular) → `text-xs font-semibold` (12px/600) on all three item components, matching Figma's `text/sm/sm-semibold` text style for the item label. Supportive text (new, see below) is `text-xs font-normal text-muted-foreground`.
|
|
18
|
+
- **Popover chrome** (`DropdownMenuContent`): `ring-1 ring-foreground/10` → `border border-border` (Figma uses a real 1px border, not a ring); `shadow-md` → Figma's actual 3-layer `shadow-lg` drop-shadow (`0.3px 2px/-2.2 spread #1F003B05, 0.5px 3.2px/-1.4 #1F003B08, 0.7px 4px/-1.8 #1F003B0F`); `p-1` (4px) → `p-2` (8px); added an explicit `gap-1.5` (6px) between items, matching Figma's item-to-item gap (previously no gap was set at all, relying on nothing — items had no separation beyond their own padding).
|
|
19
|
+
|
|
20
|
+
### New Props (`DropdownMenuItem` only)
|
|
21
|
+
|
|
22
|
+
Following the Button/Badge "boolean prop, auto-detected from a named slot via `Astro.slots.has(...)`" convention:
|
|
23
|
+
|
|
24
|
+
- `checked?: boolean` (default `false`) — presentational checked state, only visible when `showCheckbox` is also set.
|
|
25
|
+
- `showCheckbox?: boolean` (default `false`) — reserves a 20px leading slot for a checkmark; **kept the existing indicator pattern** (no visible box at rest, a checkmark fades in when `checked`), matching `DropdownMenuCheckboxItem`'s existing visual language rather than inventing Figma's filled-square glyph, which has no documented unchecked state in the file. `DropdownMenuCheckboxItem` itself is unchanged — this is a separate, presentation-only affordance for a plain `DropdownMenuItem`.
|
|
26
|
+
- `leadingIcon?: boolean` (default: has a `leading-icon` slot) — renders `<slot name="leading-icon" />` at 20px.
|
|
27
|
+
- `avatar?: boolean` (default: has an `avatar` slot) — renders `<slot name="avatar" />`. Use `<Avatar size="sm">` (24px) — it already matches Figma's item avatar size exactly, no new Avatar sizing needed.
|
|
28
|
+
- `showBadge?: boolean` (default: has a `badge` slot) — renders `<slot name="badge" />`. Use `<Badge size="sm">`.
|
|
29
|
+
- `showSubmenuIcon?: boolean` (default `false`) — renders a static 20px chevron-right. **Cosmetic only** — there is no nested-submenu infrastructure (flyout positioning, keyboard handling) anywhere in `DropdownMenu` yet. See "Flagged for the designer" below.
|
|
30
|
+
- `supportiveText?: boolean` (default: has a `supportive-text` slot) — renders a second, muted line under the item label via `<slot name="supportive-text" />`.
|
|
31
|
+
|
|
32
|
+
`leadingIcon`, `avatar`, and `showCheckbox` are independent booleans (can combine), per Figma's literal prop table — not consolidated into a single `leading` enum.
|
|
33
|
+
|
|
34
|
+
### Trigger
|
|
35
|
+
|
|
36
|
+
- `DropdownMenuTrigger` now defaults `variant="outline"` (was inheriting `Button`'s own default `"default"`/primary-blue variant) — Figma's default/closed trigger state is a white/bordered button, not primary. Still fully overridable via the existing `variant` prop.
|
|
37
|
+
- `DropdownMenuTrigger` now renders a default trailing chevron (`ChevronDown` from `@lucide/astro`) when the consumer doesn't slot their own `tail-icon` content — Figma's trigger always shows a chevron as core anatomy, not an opt-in add-on. It rotates 180° when the menu is open via a new `group/dropdown-menu` marker added to `DropdownMenu.astro`'s root (`data-state` lands on that root element, confirmed in `@data-slot/dropdown-menu`'s runtime). Pass `tailIcon={false}` to opt out entirely, or your own `<Icon slot="tail-icon" />` to override.
|
|
38
|
+
|
|
39
|
+
### Tokens (`globals.css`)
|
|
40
|
+
|
|
41
|
+
- No new tokens needed. Every color/radius used above (`background`, `background-hover`, `muted`, `muted-foreground`, `foreground`, `border`, `ring`, `chart-1`, `destructive`, `popover`, `primary`, `primary-foreground`) already existed — this was a binding/usage fix, not a token-addition pass.
|
|
42
|
+
|
|
43
|
+
### Storybook & Docs
|
|
44
|
+
|
|
45
|
+
- `apps/web/src/pages/storybook/_sections/dropdown-menu.astro`: added the seven new `DropdownMenuItem` prop rows to `itemProps`, updated the `variant` row to include `"category"`, and added preview blocks for leading icon / avatar / badge / supportive text / category / checkbox.
|
|
46
|
+
- `DropdownMenuStory.astro` / `DropdownMenu.stories.ts`: added stories covering the new item variants/props.
|
|
47
|
+
|
|
48
|
+
### Not changed
|
|
49
|
+
|
|
50
|
+
- `DropdownMenuLabel.astro` — spotted a pre-existing bug while auditing (its class list sets both `text-xs` and `text-sm`, with `text-sm` winning via `tailwind-merge`, so labels render at 14px instead of the intended 12px) but left as-is since it wasn't part of this pass's scope; worth a follow-up fix.
|
|
51
|
+
- `DropdownMenuGroup.astro`, `DropdownMenuSeparator.astro`, `DropdownMenuPortal.astro`, `DropdownMenuRadioGroup.astro`, `DropdownMenuShortcut.astro` — no Figma delta identified.
|
|
52
|
+
|
|
53
|
+
### Flagged for the designer / engineering (not addressed in this pass)
|
|
54
|
+
|
|
55
|
+
- **Real nested submenus:** Figma's `.item` prop table documents `showSubmenuIcon`, implying a submenu affordance, but no submenu flyout, positioning, or keyboard-navigation behavior exists anywhere in `DropdownMenu`'s runtime (`@data-slot/dropdown-menu`). This pass ships the chevron as a static, cosmetic icon only. Building real nested submenus is a separate, larger feature.
|
|
56
|
+
- **Checkbox unchecked state:** Figma's item file only shows a filled/checked 20px checkbox square in its Left Content — there's no authored unchecked-square state to match. `showCheckbox`/`checked` on `DropdownMenuItem` therefore reuses the existing checkmark-only indicator pattern instead; worth designing a real unchecked square in Figma if a literal checkbox control (vs. a checked-indicator) is wanted here later.
|
|
57
|
+
- **`DropdownMenuLabel` vs. `DropdownMenuItem variant="category"` overlap:** these are now two different ways to render a muted section header (one a dedicated component, one an item variant). Worth a follow-up design decision on whether `DropdownMenuLabel` should be deprecated in favor of `variant="category"`, or the two are meant to serve genuinely different call sites.
|
|
58
|
+
|
|
59
|
+
## Date 10th Sep 2026 (follow-up)
|
|
60
|
+
## Fix: menuitemcheckbox/menuitemradio alignment + item padding (node 2329-6521, prop table 7392-10142)
|
|
61
|
+
|
|
62
|
+
The designer updated Figma again after the first pass above: the `DropdownMenuItem` prop table now
|
|
63
|
+
explicitly ties `showCheckbox` to *"menuitemcheckbox"* and adds a new `showRadio` → *"menuitemradio"*,
|
|
64
|
+
and the item component's padding/radio glyph changed. This entry reconciles that with the framework's
|
|
65
|
+
existing three-component structure (`DropdownMenuItem` / `DropdownMenuCheckboxItem` / `DropdownMenuRadioItem`)
|
|
66
|
+
rather than collapsing them into one — see "Architecture" below for why.
|
|
67
|
+
|
|
68
|
+
### Architecture
|
|
69
|
+
|
|
70
|
+
- Extracted the shared item chrome into two new **internal, non-exported** files (not added to `index.ts`,
|
|
71
|
+
same precedent as `DropdownMenuStory.astro`): `dropdown-menu-item-variants.ts` (the single `cva` box-class
|
|
72
|
+
builder) and `DropdownMenuItemContent.astro` (the shared Left Content / label / Right Content layout).
|
|
73
|
+
`DropdownMenuItem`, `DropdownMenuCheckboxItem`, and `DropdownMenuRadioItem` all now render through these,
|
|
74
|
+
instead of each duplicating the same padding/typography classes.
|
|
75
|
+
- **Did not consolidate into one role-switching component.** Figma's single-component mental model
|
|
76
|
+
(`DropdownMenuItem` + a boolean that "becomes" a checkbox/radio) doesn't map cleanly onto this
|
|
77
|
+
framework: `@data-slot/dropdown-menu`'s runtime detects real interactive item type purely from each
|
|
78
|
+
element's `data-slot` value and manages `role`/`tabindex`/`aria-checked`/checked-state tracking
|
|
79
|
+
automatically for `[data-slot="dropdown-menu-checkbox-item"]` / `[data-slot="dropdown-menu-radio-item"]`.
|
|
80
|
+
Collapsing three ARIA-correct, already-working components into one conditionally-shaped component would
|
|
81
|
+
have been a much larger, riskier change for no framework-side benefit — the shared-chrome extraction above
|
|
82
|
+
gets the same "one visual spec, one place" outcome without touching the runtime contract.
|
|
83
|
+
|
|
84
|
+
### Sizing & Spacing
|
|
85
|
+
|
|
86
|
+
- **Padding correction:** Figma's item box is now uniform `p-2` (8px on all sides) for `default`/`destructive`
|
|
87
|
+
— was `px-2 py-1` last pass (a padding fix I hadn't fully caught the first time). Row height grows
|
|
88
|
+
24px → 32px accordingly, on `DropdownMenuItem`, `DropdownMenuCheckboxItem`, and `DropdownMenuRadioItem`.
|
|
89
|
+
- **`category` has its own distinct, tighter padding** (`pt-1 px-1 pb-0 gap-1`, no `p-2`) — this was already
|
|
90
|
+
structurally different in Figma and wasn't captured correctly last pass, where it incorrectly shared the
|
|
91
|
+
interactive variants' row padding.
|
|
92
|
+
- `DropdownMenuCheckboxItem`/`DropdownMenuRadioItem` **dropped their old asymmetric `pr-8 pl-2` padding**
|
|
93
|
+
entirely — no longer needed now that the checked/selected indicator lives in Left Content instead of
|
|
94
|
+
floating absolute-right (see below), so they use the exact same `p-2` box as a plain `DropdownMenuItem`.
|
|
95
|
+
|
|
96
|
+
### Checkbox / Radio glyph — switched to Figma's literal look
|
|
97
|
+
|
|
98
|
+
- `DropdownMenuCheckboxItem`/`DropdownMenuRadioItem` moved from the old shadcn-style indicator (invisible
|
|
99
|
+
until checked, a bare checkmark fading in at `absolute right-2`) to Figma's literal glyph: an
|
|
100
|
+
always-reserved 20px box (`rounded-sm`, checkbox) or circle (`rounded-full`, radio) in Left Content,
|
|
101
|
+
outlined (`border-input`) when unchecked and `bg-primary border-primary` + a white check icon / centered
|
|
102
|
+
dot when checked. Visual language borrowed directly from the existing `Checkbox.astro`/`RadioGroupItem.astro`
|
|
103
|
+
components (same token usage, scaled to the item's 20px glyph size) rather than inventing new colors.
|
|
104
|
+
- This is driven purely by CSS off the `data-checked` attribute the runtime **already sets** on these exact
|
|
105
|
+
elements (confirmed in `@data-slot/dropdown-menu`'s source) via `group-data-checked/dropdown-menu-item:` —
|
|
106
|
+
no new JavaScript needed, no change to the runtime's checked-state tracking or click/keyboard handling.
|
|
107
|
+
- `DropdownMenuItem`'s own presentational `showCheckbox` also switched from the old checkmark-only pattern to
|
|
108
|
+
the same 20px box glyph (driven by its static `checked` prop, not runtime state), for visual consistency
|
|
109
|
+
with the real components. New presentational `showRadio` (see below) uses the matching circle glyph.
|
|
110
|
+
- **Resolves last pass's "Checkbox unchecked state" flag** — Figma still doesn't author a literal unchecked
|
|
111
|
+
square, but reusing `Checkbox`'s existing unchecked visual (bordered, no fill) is a defensible, low-risk
|
|
112
|
+
default that's already established elsewhere in the design system.
|
|
113
|
+
|
|
114
|
+
### New Props
|
|
115
|
+
|
|
116
|
+
- `DropdownMenuItem` gains `showRadio?: boolean` (default `false`), mirroring `showCheckbox` — presentational
|
|
117
|
+
only, reuses the shared `checked` prop. Per Figma's prop table: `showRadio` → *"Show radiobutton which is
|
|
118
|
+
menuitemradio"*.
|
|
119
|
+
- `DropdownMenuCheckboxItem` and `DropdownMenuRadioItem` both gain the same extra prop surface
|
|
120
|
+
`DropdownMenuItem` got last pass: `leadingIcon`, `avatar`, `showBadge`, `showSubmenuIcon`, `supportiveText`
|
|
121
|
+
(same `Astro.slots.has(...)`-default convention). Sharing chrome made this nearly free, and Figma's own
|
|
122
|
+
item variants already mix a checkbox with other leading/trailing content in the same row.
|
|
123
|
+
- `value`, `disabled`, `inset`, `defaultChecked`, `role="menuitemcheckbox"`/`"menuitemradio"`,
|
|
124
|
+
`data-slot="dropdown-menu-checkbox-item"`/`"dropdown-menu-radio-item"` are all **unchanged** — this is the
|
|
125
|
+
ARIA/runtime contract and was not touched.
|
|
126
|
+
|
|
127
|
+
### Fixed (implementation bug, not a Figma delta)
|
|
128
|
+
|
|
129
|
+
- While building the shared chrome, caught a real bug in the first draft: forwarding a
|
|
130
|
+
`<Fragment slot="leading-icon">` etc. from each root component into the shared `DropdownMenuItemContent`
|
|
131
|
+
made `Astro.slots.has("leading-icon")` always return `true` inside that shared partial (it saw the
|
|
132
|
+
pass-through Fragment, not whether the real end-consumer supplied content), so empty leading-icon/avatar/
|
|
133
|
+
badge wrapper `<span>`s rendered on every single item regardless of props. Fixed by resolving
|
|
134
|
+
`Astro.slots.has(...)` defaults in each outer component (`DropdownMenuItem`/`CheckboxItem`/`RadioItem`,
|
|
135
|
+
against their *own* callers) and passing explicit resolved booleans down — the shared partial no longer
|
|
136
|
+
does any slot introspection of its own. Verified via built story output before/after (span count per item
|
|
137
|
+
went from unconditional-always to matching only the items that actually set the prop).
|
|
138
|
+
|
|
139
|
+
### Storybook & Docs
|
|
140
|
+
|
|
141
|
+
- `apps/web/src/pages/storybook/_sections/dropdown-menu.astro`: added a `showRadio` row to `itemProps`;
|
|
142
|
+
updated `leadingIcon`/`avatar`/`showBadge`/`showSubmenuIcon`/`supportiveText` descriptions to note they now
|
|
143
|
+
apply to all three item components, not just `DropdownMenuItem`; enriched the Single Select/Multi Select
|
|
144
|
+
preview blocks to exercise the new prop parity (`supportiveText` on a radio item, `showBadge` on a checkbox
|
|
145
|
+
item); added `showRadio` to the "Item content" preview.
|
|
146
|
+
- `DropdownMenuStory.astro`/`DropdownMenu.stories.ts`: added a new `CheckboxRadioContent` story
|
|
147
|
+
(`showCheckboxRadio` prop) covering presentational checked/unchecked checkbox and radio glyphs side by side
|
|
148
|
+
with the real, runtime-tracked `DropdownMenuCheckboxItem`/`DropdownMenuRadioItem` (one with `avatar`, one
|
|
149
|
+
with `showBadge`) so both visual systems are directly comparable.
|
|
150
|
+
|
|
151
|
+
### Flagged for the designer / engineering (not addressed in this pass)
|
|
152
|
+
|
|
153
|
+
- **`showCheckbox`/`showRadio` on plain `DropdownMenuItem` are cosmetic only** — they draw the glyph via a
|
|
154
|
+
static prop and don't manage checked/selected state or emit events. Don't reach for them expecting real
|
|
155
|
+
interactivity; that's what `DropdownMenuCheckboxItem`/`DropdownMenuRadioItem` are for. Worth a naming pass
|
|
156
|
+
if this keeps causing confusion (e.g. renaming the presentational props to make the distinction more
|
|
157
|
+
obvious in autocomplete).
|
|
158
|
+
- Carried over from last pass, still open: real nested submenus (`showSubmenuIcon` is still cosmetic-only),
|
|
159
|
+
and the `DropdownMenuLabel` vs. `variant="category"` overlap.
|
|
160
|
+
|
|
161
|
+
## Date 10th Sep 2026 (follow-up)
|
|
162
|
+
## Fix: distinct Hover vs. Focus states (node 2529-2338, item states)
|
|
163
|
+
|
|
164
|
+
While adding a states preview to the Storybook docs, caught that `DropdownMenuItem` (and by extension
|
|
165
|
+
`DropdownMenuCheckboxItem`/`DropdownMenuRadioItem`, which share its `cva`) only ever had **one** interactive
|
|
166
|
+
style — `focus:bg-background-hover` — with no real `:hover` class at all. Figma documents Hover and Focused
|
|
167
|
+
as two visually distinct states (Hover: grey background only; Focused: a blue border + ring, no background
|
|
168
|
+
change), which wasn't implemented.
|
|
169
|
+
|
|
170
|
+
### Fixed
|
|
171
|
+
|
|
172
|
+
- `dropdown-menu-item-variants.ts`: added a real `hover:bg-background-hover` (per variant, `destructive` gets
|
|
173
|
+
`hover:text-destructive dark:hover:bg-destructive/20`) so mouse hover now works with plain CSS — no JS
|
|
174
|
+
required, matching Figma's Hover state.
|
|
175
|
+
- Replaced `focus:bg-background-hover` with `focus-visible:border-ring focus-visible:ring-2
|
|
176
|
+
focus-visible:ring-chart-1`, matching Figma's Focused effect (`border-ring` + a 2px ring) and the same
|
|
177
|
+
`focus-visible:border-ring focus-visible:ring-*` pattern already used by `Button`/`Card`. Added
|
|
178
|
+
`border border-transparent` to the shared base so the border can swap color on focus without any layout
|
|
179
|
+
shift (same technique `Button`/`Card` already use).
|
|
180
|
+
- Using `:focus-visible` instead of plain `:focus` is deliberate: the dropdown runtime calls a real
|
|
181
|
+
`element.focus()` on pointer hover (so highlight tracks the mouse for accessibility purposes) — with
|
|
182
|
+
`:focus-visible`, browsers' own heuristics correctly suppress the keyboard-style ring for that
|
|
183
|
+
pointer-triggered focus (real `:hover` covers the visual instead) while still showing the ring for genuine
|
|
184
|
+
keyboard navigation (Tab/Arrow keys). Net effect: mouse hover shows grey background only, keyboard
|
|
185
|
+
navigation shows the blue ring — exactly Figma's distinction, with no behavior change to the runtime itself.
|
|
186
|
+
|
|
187
|
+
### Storybook & Docs
|
|
188
|
+
|
|
189
|
+
- `apps/web/src/pages/storybook/_sections/dropdown-menu.astro`: added a new "DropdownMenuItem states" preview
|
|
190
|
+
(Default/Hover/Focus/Disabled) above "DropdownMenuItem variants" — Hover/Focus rows use a forced `class`
|
|
191
|
+
override for an always-visible reference, with a note that the same states are live (hover/Tab to see them
|
|
192
|
+
for real anywhere in the gallery).
|
|
193
|
+
|
|
194
|
+
## Unreleased
|