@cl-light-ds/ui 0.1.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.
Files changed (148) hide show
  1. package/package.json +1 -1
  2. package/src/components/accordion/CHANGELOG.md +41 -41
  3. package/src/components/accordion/index.ts +4 -4
  4. package/src/components/alert/CHANGELOG.md +5 -5
  5. package/src/components/alert/index.ts +4 -4
  6. package/src/components/avatar/CHANGELOG.md +44 -44
  7. package/src/components/avatar/index.ts +6 -6
  8. package/src/components/badge/CHANGELOG.md +49 -49
  9. package/src/components/badge/index.ts +2 -2
  10. package/src/components/breadcrumb/CHANGELOG.md +24 -24
  11. package/src/components/breadcrumb/index.ts +5 -5
  12. package/src/components/button/CHANGELOG.md +41 -41
  13. package/src/components/button/index.ts +2 -2
  14. package/src/components/button-group/CHANGELOG.md +34 -34
  15. package/src/components/button-group/index.ts +3 -3
  16. package/src/components/card/CHANGELOG.md +162 -119
  17. package/src/components/card/Card.astro +23 -8
  18. package/src/components/card/CardContent.astro +1 -1
  19. package/src/components/card/CardDescription.astro +1 -1
  20. package/src/components/card/CardMedia.astro +1 -1
  21. package/src/components/card/CardMediaActions.astro +21 -0
  22. package/src/components/card/CardMediaSelect.astro +21 -0
  23. package/src/components/card/index.ts +12 -10
  24. package/src/components/carousel/CHANGELOG.md +5 -5
  25. package/src/components/carousel/index.ts +3 -3
  26. package/src/components/checkbox/CHANGELOG.md +58 -58
  27. package/src/components/checkbox/Checkbox.astro +2 -2
  28. package/src/components/checkbox/index.ts +1 -1
  29. package/src/components/collapsible/CHANGELOG.md +5 -5
  30. package/src/components/collapsible/index.ts +4 -4
  31. package/src/components/combobox/CHANGELOG.md +27 -27
  32. package/src/components/combobox/index.ts +11 -11
  33. package/src/components/date/CHANGELOG.md +18 -18
  34. package/src/components/date/index.ts +2 -2
  35. package/src/components/dialog/CHANGELOG.md +39 -39
  36. package/src/components/dialog/index.ts +9 -9
  37. package/src/components/dropdown-menu/CHANGELOG.md +194 -194
  38. package/src/components/dropdown-menu/DropdownMenuItemContent.astro +79 -79
  39. package/src/components/dropdown-menu/DropdownMenuPortal.astro +13 -13
  40. package/src/components/dropdown-menu/DropdownMenuRadioGroup.astro +13 -13
  41. package/src/components/dropdown-menu/dropdown-menu-item-variants.ts +24 -24
  42. package/src/components/dropdown-menu/index.ts +12 -12
  43. package/src/components/field/CHANGELOG.md +33 -33
  44. package/src/components/field/index.ts +10 -10
  45. package/src/components/hover-card/CHANGELOG.md +20 -20
  46. package/src/components/hover-card/index.ts +3 -3
  47. package/src/components/input/CHANGELOG.md +29 -29
  48. package/src/components/input/index.ts +2 -2
  49. package/src/components/input-group/CHANGELOG.md +49 -49
  50. package/src/components/input-group/controlClasses.ts +2 -2
  51. package/src/components/input-group/index.ts +6 -6
  52. package/src/components/item/CHANGELOG.md +27 -27
  53. package/src/components/item/index.ts +10 -10
  54. package/src/components/kbd/CHANGELOG.md +5 -5
  55. package/src/components/kbd/index.ts +2 -2
  56. package/src/components/label/CHANGELOG.md +28 -28
  57. package/src/components/label/index.ts +2 -2
  58. package/src/components/link-group/CHANGELOG.md +75 -75
  59. package/src/components/link-group/index.ts +5 -5
  60. package/src/components/link-group/link-group-list-variants.ts +15 -15
  61. package/src/components/marquee/CHANGELOG.md +5 -5
  62. package/src/components/marquee/index.ts +2 -2
  63. package/src/components/native-select/CHANGELOG.md +5 -5
  64. package/src/components/native-select/NativeSelectOption.astro +9 -9
  65. package/src/components/native-select/index.ts +3 -3
  66. package/src/components/navigation-menu/CHANGELOG.md +63 -63
  67. package/src/components/navigation-menu/index.ts +10 -10
  68. package/src/components/organisms/lightning-sidebar-nav/CHANGELOG.md +50 -0
  69. package/src/components/organisms/lightning-sidebar-nav/LightningSidebarNav.astro +176 -0
  70. package/src/components/organisms/lightning-sidebar-nav/SidebarNav.astro +50 -0
  71. package/src/components/organisms/lightning-sidebar-nav/SidebarNavGroup.astro +74 -0
  72. package/src/components/organisms/lightning-sidebar-nav/SidebarNavItem.astro +94 -0
  73. package/src/components/organisms/lightning-sidebar-nav/SidebarNavProBadge.astro +26 -0
  74. package/src/components/organisms/lightning-sidebar-nav/SidebarNavSection.astro +71 -0
  75. package/src/components/organisms/lightning-sidebar-nav/SidebarNavSkeleton.astro +67 -0
  76. package/src/components/organisms/lightning-sidebar-nav/SidebarNavSubItem.astro +69 -0
  77. package/src/components/organisms/lightning-sidebar-nav/SidebarNavUsage.astro +78 -0
  78. package/src/components/organisms/lightning-sidebar-nav/SidebarNavWorkspaceAvatar.astro +46 -0
  79. package/src/components/organisms/lightning-sidebar-nav/SidebarNavWorkspaceSwitcher.astro +130 -0
  80. package/src/components/organisms/lightning-sidebar-nav/icons.ts +46 -0
  81. package/src/components/organisms/lightning-sidebar-nav/index.ts +18 -0
  82. package/src/components/organisms/lightning-sidebar-nav/types.ts +77 -0
  83. package/src/components/popover/CHANGELOG.md +20 -5
  84. package/src/components/popover/Popover.astro +1 -0
  85. package/src/components/popover/PopoverContent.astro +6 -5
  86. package/src/components/popover/PopoverHeader.astro +1 -1
  87. package/src/components/popover/PopoverTrigger.astro +7 -1
  88. package/src/components/popover/index.ts +7 -7
  89. package/src/components/radio-group/CHANGELOG.md +31 -9
  90. package/src/components/radio-group/RadioGroup.astro +1 -1
  91. package/src/components/radio-group/RadioGroupItem.astro +12 -3
  92. package/src/components/radio-group/index.ts +3 -3
  93. package/src/components/select/CHANGELOG.md +39 -39
  94. package/src/components/select/index.ts +8 -8
  95. package/src/components/separator/CHANGELOG.md +5 -5
  96. package/src/components/separator/index.ts +2 -2
  97. package/src/components/skeleton/CHANGELOG.md +5 -5
  98. package/src/components/skeleton/index.ts +1 -1
  99. package/src/components/slider/CHANGELOG.md +5 -5
  100. package/src/components/slider/index.ts +1 -1
  101. package/src/components/spinner/CHANGELOG.md +18 -5
  102. package/src/components/spinner/Spinner.astro +3 -2
  103. package/src/components/spinner/index.ts +1 -1
  104. package/src/components/sticky-surface/CHANGELOG.md +5 -5
  105. package/src/components/sticky-surface/index.ts +2 -2
  106. package/src/components/switch/CHANGELOG.md +29 -9
  107. package/src/components/switch/Switch.astro +4 -4
  108. package/src/components/switch/index.ts +1 -1
  109. package/src/components/table/CHANGELOG.md +24 -5
  110. package/src/components/table/Table.astro +120 -12
  111. package/src/components/table/TableBody.astro +1 -2
  112. package/src/components/table/TableBulkToolbar.astro +37 -0
  113. package/src/components/table/TableCell.astro +25 -1
  114. package/src/components/table/TableCellText.astro +17 -0
  115. package/src/components/table/TableColumnMenu.astro +50 -0
  116. package/src/components/table/TableEditableCell.astro +287 -0
  117. package/src/components/table/TableEmpty.astro +17 -0
  118. package/src/components/table/TableFolderCell.astro +38 -0
  119. package/src/components/table/TableFooter.astro +1 -1
  120. package/src/components/table/TableHead.astro +71 -2
  121. package/src/components/table/TableHeader.astro +1 -1
  122. package/src/components/table/TablePagination.astro +57 -0
  123. package/src/components/table/TableRow.astro +32 -1
  124. package/src/components/table/TableSelectAllBanner.astro +34 -0
  125. package/src/components/table/TableSelectCell.astro +50 -0
  126. package/src/components/table/TableSkeletonRow.astro +21 -0
  127. package/src/components/table/index.ts +19 -9
  128. package/src/components/table/table-cell-variants.ts +29 -0
  129. package/src/components/tabs/CHANGELOG.md +5 -5
  130. package/src/components/tabs/index.ts +6 -6
  131. package/src/components/textarea/CHANGELOG.md +22 -5
  132. package/src/components/textarea/Textarea.astro +25 -3
  133. package/src/components/textarea/index.ts +2 -2
  134. package/src/components/toggle/CHANGELOG.md +23 -5
  135. package/src/components/toggle/Toggle.astro +13 -1
  136. package/src/components/toggle/index.ts +1 -1
  137. package/src/components/toggle-group/CHANGELOG.md +18 -5
  138. package/src/components/toggle-group/ToggleGroup.astro +14 -2
  139. package/src/components/toggle-group/index.ts +2 -2
  140. package/src/components/tooltip/CHANGELOG.md +22 -5
  141. package/src/components/tooltip/Tooltip.astro +26 -0
  142. package/src/components/tooltip/TooltipContent.astro +34 -9
  143. package/src/components/tooltip/index.ts +3 -3
  144. package/src/lib/table-controller.ts +1090 -0
  145. package/src/lib/toggle-group-controller.ts +113 -113
  146. package/src/lib/toggle-shared.ts +27 -26
  147. package/src/lib/utils.ts +6 -6
  148. package/src/styles/globals.css +197 -177
@@ -0,0 +1,78 @@
1
+ ---
2
+ /**
3
+ * @component SidebarNavUsage
4
+ * @title Sidebar Nav Usage
5
+ * @description Plan usage rows (label … used / limit) and an optional full-width upgrade CTA, pinned to the bottom of the sidebar.
6
+ * @figmaUrl https://www.figma.com/design/qXkzdqZ3QPQqgckU3AwIAH/ClickLearn-Lightning-Design-Library?node-id=7514-198
7
+ *
8
+ * @usage
9
+ *
10
+ * ```astro nocollapse
11
+ * <SidebarNavUsage
12
+ * usage={[{ id: "recordings", label: "Recordings created", used: 5, limit: 5 }]}
13
+ * upgrade={{ label: "Upgrade to Pro", href: "/billing" }}
14
+ * />
15
+ * ```
16
+ */
17
+
18
+ import { cn } from "@cl-light-ds/ui/lib/utils";
19
+ import { Button } from "../../button";
20
+ import { Separator } from "../../separator";
21
+ import type { SidebarNavUpgrade, SidebarNavUsageMetric } from "./types";
22
+
23
+ interface Props {
24
+ usage?: SidebarNavUsageMetric[];
25
+ upgrade?: SidebarNavUpgrade | null;
26
+ class?: string;
27
+ }
28
+
29
+ const {
30
+ usage = [],
31
+ upgrade = null,
32
+ class: className = "",
33
+ } = Astro.props as Props;
34
+
35
+ const formatValue = ({ used, limit }: SidebarNavUsageMetric) =>
36
+ limit === null ? `${used} / Unlimited` : `${used} / ${limit}`;
37
+ ---
38
+
39
+ {
40
+ (usage.length > 0 || upgrade) && (
41
+ <div
42
+ data-slot="sidebar-nav-usage"
43
+ class={cn("flex flex-col gap-3", className)}
44
+ >
45
+ <Separator class="my-2" />
46
+ {usage.length > 0 && (
47
+ <dl class="flex flex-col gap-2 text-xs leading-4">
48
+ {usage.map((metric) => (
49
+ <div
50
+ data-slot="sidebar-nav-usage-metric"
51
+ data-at-limit={
52
+ metric.limit !== null && metric.used >= metric.limit
53
+ ? ""
54
+ : undefined
55
+ }
56
+ class="flex items-baseline justify-between gap-2"
57
+ >
58
+ <dt class="text-muted-foreground truncate">{metric.label}</dt>
59
+ <dd class="text-foreground shrink-0 font-semibold whitespace-nowrap">
60
+ {formatValue(metric)}
61
+ </dd>
62
+ </div>
63
+ ))}
64
+ </dl>
65
+ )}
66
+ {upgrade && (
67
+ <Button
68
+ as="a"
69
+ href={upgrade.href}
70
+ size="sm"
71
+ class="w-full font-semibold"
72
+ >
73
+ {upgrade.label}
74
+ </Button>
75
+ )}
76
+ </div>
77
+ )
78
+ }
@@ -0,0 +1,46 @@
1
+ ---
2
+ /**
3
+ * @component SidebarNavWorkspaceAvatar
4
+ * @description 24px workspace avatar: image when `avatarUrl` is set, else initials, else the user-round icon on primary.
5
+ */
6
+
7
+ import { UserRound } from "@lucide/astro";
8
+ import { cn } from "@cl-light-ds/ui/lib/utils";
9
+ import { Avatar, AvatarFallback, AvatarImage } from "../../avatar";
10
+ import type { SidebarNavWorkspace } from "./types";
11
+
12
+ interface Props {
13
+ workspace: SidebarNavWorkspace;
14
+ class?: string;
15
+ slot?: string;
16
+ }
17
+
18
+ const { workspace, class: className = "", ...rest } = Astro.props as Props;
19
+
20
+ const variant = workspace.avatarUrl
21
+ ? "image"
22
+ : workspace.initials
23
+ ? "initials"
24
+ : "icon";
25
+ ---
26
+
27
+ <Avatar
28
+ {...rest}
29
+ size="sm"
30
+ variant={variant}
31
+ icon={variant === "icon" ? UserRound : undefined}
32
+ class={cn(
33
+ variant === "icon" &&
34
+ "bg-primary [&_[data-slot=avatar-icon]]:text-primary-foreground",
35
+ className,
36
+ )}
37
+ >
38
+ {workspace.avatarUrl && <AvatarImage src={workspace.avatarUrl} alt="" />}
39
+ {
40
+ !workspace.avatarUrl && workspace.initials && (
41
+ <AvatarFallback class="bg-primary text-primary-foreground">
42
+ {workspace.initials}
43
+ </AvatarFallback>
44
+ )
45
+ }
46
+ </Avatar>
@@ -0,0 +1,130 @@
1
+ ---
2
+ /**
3
+ * @component SidebarNavWorkspaceSwitcher
4
+ * @title Sidebar Nav Workspace Switcher
5
+ * @description Current workspace (avatar + name) at the top of the sidebar. With `workspaces`, it opens a DropdownMenu to switch.
6
+ * @figmaUrl https://www.figma.com/design/qXkzdqZ3QPQqgckU3AwIAH/ClickLearn-Lightning-Design-Library?node-id=7514-183
7
+ *
8
+ * @usage
9
+ *
10
+ * ```astro nocollapse
11
+ * <SidebarNavWorkspaceSwitcher
12
+ * workspace={{ id: "nw", name: "Northwind Group" }}
13
+ * workspaces={[{ id: "nw", name: "Northwind Group" }, { id: "ct", name: "Contoso", href: "/w/ct" }]}
14
+ * />
15
+ * ```
16
+ *
17
+ * ### Events
18
+ *
19
+ * | Event | Detail | Description |
20
+ * |-------|--------|-------------|
21
+ * | `sidebar-nav:workspace-change` | `{ id: string }` | Bubbles, cancelable. Fired when a workspace is picked. If the workspace has an `href` and the event isn't cancelled, the browser navigates there. |
22
+ */
23
+
24
+ import { ChevronsUpDown } from "@lucide/astro";
25
+ import { cn } from "@cl-light-ds/ui/lib/utils";
26
+ import {
27
+ DropdownMenu,
28
+ DropdownMenuContent,
29
+ DropdownMenuLabel,
30
+ DropdownMenuRadioGroup,
31
+ DropdownMenuRadioItem,
32
+ DropdownMenuTrigger,
33
+ } from "../../dropdown-menu";
34
+ import { Separator } from "../../separator";
35
+ import type { SidebarNavWorkspace } from "./types";
36
+ import SidebarNavWorkspaceAvatar from "./SidebarNavWorkspaceAvatar.astro";
37
+
38
+ interface Props {
39
+ workspace: SidebarNavWorkspace;
40
+ /** Options in the switcher menu. Empty = static display, no menu. */
41
+ workspaces?: SidebarNavWorkspace[];
42
+ /** Heading shown at the top of the menu. */
43
+ menuLabel?: string;
44
+ class?: string;
45
+ }
46
+
47
+ const {
48
+ workspace,
49
+ workspaces = [],
50
+ menuLabel = "Workspaces",
51
+ class: className = "",
52
+ } = Astro.props as Props;
53
+
54
+ const hasMenu = workspaces.length > 0;
55
+ const rowClass =
56
+ "flex w-full items-center gap-2 overflow-hidden rounded-lg p-2 text-left text-sm leading-5 font-medium text-foreground";
57
+ ---
58
+
59
+ <div data-slot="sidebar-nav-workspace" class={cn("flex flex-col", className)}>
60
+ {
61
+ hasMenu ? (
62
+ <DropdownMenu
63
+ class="block w-full"
64
+ defaultValue={workspace.id}
65
+ highlightItemOnHover
66
+ >
67
+ <DropdownMenuTrigger asChild class="flex w-full">
68
+ <button
69
+ type="button"
70
+ aria-label={`Switch workspace, current: ${workspace.name}`}
71
+ class={cn(
72
+ rowClass,
73
+ "outline-none transition-colors hover:bg-muted focus-visible:ring-1 focus-visible:ring-sidebar-ring focus-visible:ring-inset",
74
+ )}
75
+ >
76
+ <SidebarNavWorkspaceAvatar workspace={workspace} />
77
+ <span class="min-w-0 flex-1 truncate">{workspace.name}</span>
78
+ <ChevronsUpDown
79
+ aria-hidden="true"
80
+ class="text-foreground size-4 shrink-0"
81
+ />
82
+ </button>
83
+ </DropdownMenuTrigger>
84
+ {/* --anchor-width isn't set by the runtime; match the 238px inner width of the 270px shell. */}
85
+ <DropdownMenuContent align="start" class="w-[238px]">
86
+ <DropdownMenuLabel>{menuLabel}</DropdownMenuLabel>
87
+ <DropdownMenuRadioGroup>
88
+ {workspaces.map((option) => (
89
+ <DropdownMenuRadioItem
90
+ value={option.id}
91
+ data-href={option.href}
92
+ avatar
93
+ >
94
+ <SidebarNavWorkspaceAvatar slot="avatar" workspace={option} />
95
+ {option.name}
96
+ </DropdownMenuRadioItem>
97
+ ))}
98
+ </DropdownMenuRadioGroup>
99
+ </DropdownMenuContent>
100
+ </DropdownMenu>
101
+ ) : (
102
+ <div class={rowClass}>
103
+ <SidebarNavWorkspaceAvatar workspace={workspace} />
104
+ <span class="min-w-0 flex-1 truncate">{workspace.name}</span>
105
+ </div>
106
+ )
107
+ }
108
+ <Separator class="my-2" />
109
+ </div>
110
+
111
+ <script>
112
+ document
113
+ .querySelectorAll<HTMLElement>('[data-slot="sidebar-nav-workspace"]')
114
+ .forEach((root) => {
115
+ root.addEventListener("dropdown-menu:select", (event) => {
116
+ const { value, item } = (
117
+ event as CustomEvent<{ value: string; item: HTMLElement | null }>
118
+ ).detail;
119
+ const href = item?.dataset.href;
120
+ const proceed = root.dispatchEvent(
121
+ new CustomEvent("sidebar-nav:workspace-change", {
122
+ bubbles: true,
123
+ cancelable: true,
124
+ detail: { id: value },
125
+ }),
126
+ );
127
+ if (proceed && href) window.location.assign(href);
128
+ });
129
+ });
130
+ </script>
@@ -0,0 +1,46 @@
1
+ import {
2
+ BookOpen,
3
+ ChartColumn,
4
+ CircleHelp,
5
+ FileText,
6
+ Folder,
7
+ House,
8
+ Library,
9
+ Palette,
10
+ Settings,
11
+ Trash2,
12
+ UserRound,
13
+ Users,
14
+ Video,
15
+ type AstroComponent,
16
+ } from "@lucide/astro";
17
+
18
+ /**
19
+ * String → Lucide icon registry, so backend data can name icons without
20
+ * importing components. Extend this map when a new destination needs an icon.
21
+ */
22
+ export const sidebarNavIcons = {
23
+ "book-open": BookOpen,
24
+ "chart-column": ChartColumn,
25
+ "circle-help": CircleHelp,
26
+ "file-text": FileText,
27
+ folder: Folder,
28
+ house: House,
29
+ library: Library,
30
+ palette: Palette,
31
+ settings: Settings,
32
+ "trash-2": Trash2,
33
+ "user-round": UserRound,
34
+ users: Users,
35
+ video: Video,
36
+ } satisfies Record<string, AstroComponent>;
37
+
38
+ export type SidebarNavIconName = keyof typeof sidebarNavIcons;
39
+
40
+ export const resolveSidebarNavIcon = (
41
+ icon: SidebarNavIconName | AstroComponent | undefined,
42
+ ): AstroComponent | undefined => {
43
+ if (!icon) return undefined;
44
+ if (typeof icon === "string") return sidebarNavIcons[icon];
45
+ return icon;
46
+ };
@@ -0,0 +1,18 @@
1
+ export { default as LightningSidebarNav } from "./LightningSidebarNav.astro";
2
+ export { default as SidebarNav } from "./SidebarNav.astro";
3
+ export { default as SidebarNavGroup } from "./SidebarNavGroup.astro";
4
+ export { default as SidebarNavItem } from "./SidebarNavItem.astro";
5
+ export { sidebarNavItemVariants } from "./SidebarNavItem.astro";
6
+ export { default as SidebarNavProBadge } from "./SidebarNavProBadge.astro";
7
+ export { default as SidebarNavSection } from "./SidebarNavSection.astro";
8
+ export { default as SidebarNavSkeleton } from "./SidebarNavSkeleton.astro";
9
+ export { default as SidebarNavSubItem } from "./SidebarNavSubItem.astro";
10
+ export { default as SidebarNavUsage } from "./SidebarNavUsage.astro";
11
+ export { default as SidebarNavWorkspaceAvatar } from "./SidebarNavWorkspaceAvatar.astro";
12
+ export { default as SidebarNavWorkspaceSwitcher } from "./SidebarNavWorkspaceSwitcher.astro";
13
+ export {
14
+ resolveSidebarNavIcon,
15
+ sidebarNavIcons,
16
+ type SidebarNavIconName,
17
+ } from "./icons";
18
+ export * from "./types";
@@ -0,0 +1,77 @@
1
+ import type { AstroComponent } from "@lucide/astro";
2
+ import type { SidebarNavIconName } from "./icons";
3
+
4
+ /**
5
+ * Backend contract for the Lightning Sidebar Nav.
6
+ * Everything except `AstroComponent` icons is JSON-serialisable, so a page can
7
+ * pass an API response straight into `<LightningSidebarNav />`.
8
+ */
9
+
10
+ /** Registry key (from backend data) or a Lucide component (hand-authored markup). */
11
+ export type SidebarNavIcon = SidebarNavIconName | AstroComponent;
12
+
13
+ /** A top-level destination (Figma: Group Item / Variant=Default). */
14
+ export interface SidebarNavLink {
15
+ id: string;
16
+ label: string;
17
+ href: string;
18
+ icon?: SidebarNavIcon;
19
+ /** Shows the Accent/Small "Pro" badge. The link still navigates. */
20
+ pro?: boolean;
21
+ disabled?: boolean;
22
+ }
23
+
24
+ /** A destination nested under a group (Figma: Group item / .Nav Item). */
25
+ export interface SidebarNavSubLink {
26
+ id: string;
27
+ label: string;
28
+ href: string;
29
+ pro?: boolean;
30
+ }
31
+
32
+ /** An expandable parent (Figma: Group Item / Variant=Collapsed | Expanded). */
33
+ export interface SidebarNavGroup {
34
+ id: string;
35
+ label: string;
36
+ icon?: SidebarNavIcon;
37
+ pro?: boolean;
38
+ children: SidebarNavSubLink[];
39
+ /** Force the initial open state. Groups with an active child open automatically. */
40
+ defaultOpen?: boolean;
41
+ }
42
+
43
+ export type SidebarNavEntry = SidebarNavLink | SidebarNavGroup;
44
+
45
+ /** A block of entries, optionally headed by a category label (Figma: Variant=Category). */
46
+ export interface SidebarNavSectionData {
47
+ id: string;
48
+ label?: string;
49
+ items: SidebarNavEntry[];
50
+ }
51
+
52
+ export interface SidebarNavWorkspace {
53
+ id: string;
54
+ name: string;
55
+ avatarUrl?: string;
56
+ initials?: string;
57
+ /** When set, choosing this workspace navigates here. Otherwise `sidebar-nav:workspace-change` fires. */
58
+ href?: string;
59
+ }
60
+
61
+ export interface SidebarNavUsageMetric {
62
+ id: string;
63
+ label: string;
64
+ used: number;
65
+ /** `null` means unlimited. */
66
+ limit: number | null;
67
+ }
68
+
69
+ export interface SidebarNavUpgrade {
70
+ label: string;
71
+ href: string;
72
+ }
73
+
74
+ export const isSidebarNavGroup = (
75
+ entry: SidebarNavEntry,
76
+ ): entry is SidebarNavGroup =>
77
+ Array.isArray((entry as SidebarNavGroup).children);
@@ -1,5 +1,20 @@
1
- # Changelog
2
-
3
- All notable changes to the `Popover` component will be documented in this file.
4
-
5
- ## Unreleased
1
+ # Changelog
2
+
3
+ All notable changes to the `Popover` component family (`Popover`, `PopoverTrigger`, `PopoverContent`, `PopoverHeader`, `PopoverTitle`, `PopoverDescription`, `PopoverClose`) are documented in this file.
4
+
5
+ ## 2026-09-18 — Figma alignment (ClickLearn Lightning Design Library, node `5548-11795`)
6
+
7
+ Figma documents Popover across two pages: Items (the trigger button's Default/Hover/Pressed/Focus/Disabled states) and the expanded popover (12 Side × Alignment combinations, all sharing the same panel styling).
8
+
9
+ ### Fixed
10
+
11
+ - Added the missing `@figmaUrl` (this component never had one).
12
+ - `PopoverContent`: replaced the `ring-1 ring-foreground/10` border approximation with a real `border border-border`, and `shadow-md` → `shadow-lg` — matching Figma's tokens (`border` = `#e4e4e7`, a named `shadow-lg`), same category of fix already made to `DialogContent`/`HoverCardContent`.
13
+ - `PopoverContent`: removed a dead, redundant raw class string duplicating most of `contentVariants()` — same class of bug already fixed on `HoverCardContent`. It also carried two genuinely dead selectors, `data-[side=inline-start]:...`/`data-[side=inline-end]:...`, which the underlying `@data-slot/popover` behavior library never actually sets (confirmed by checking its source — it only ever sets physical `top`/`bottom`/`left`/`right`, matching the component's own `side` prop type). Also removed the dead `rounded-md` (a later `rounded-lg` always won anyway) and a `duration-100`/`duration-150` conflict (150ms was already winning).
14
+ - `PopoverHeader`: `gap-1` (4px) → `gap-0.5` (2px) between `PopoverTitle`/`PopoverDescription`, matching Figma's measured 2px gap.
15
+ - `PopoverTrigger`: now defaults to `variant="outline"` instead of inheriting `Button`'s own `default` (solid primary) variant — Figma's Default trigger state is a bordered/light button, matching outline exactly, and every existing usage example already passed `variant="outline"` explicitly. Visually a no-op for existing usages; makes new usages match the documented default without needing to repeat the prop.
16
+
17
+ ### Notes
18
+
19
+ - The trigger's Hover/Pressed/Focus/Disabled states are entirely `Button`'s own responsibility (`PopoverTrigger` just renders a `Button`) — already verified correct in earlier passes, no changes needed here.
20
+ - `bg-popover`/`text-popover-foreground`, `p-4`, and `rounded-lg` already matched Figma's tokens exactly.
@@ -3,6 +3,7 @@
3
3
  * @component Popover
4
4
  * @title Popover
5
5
  * @description A floating panel that appears on trigger click, useful for displaying rich content like forms or additional information.
6
+ * @figmaUrl https://www.figma.com/design/qXkzdqZ3QPQqgckU3AwIAH/ClickLearn-Lightning-Design-Library?node-id=5548-11795
6
7
  *
7
8
  * @preview
8
9
  * <Popover>
@@ -21,11 +21,16 @@ const {
21
21
  } = Astro.props as Props;
22
22
 
23
23
  const contentVariants = cva([
24
+ // visibility
24
25
  "z-50 duration-150 ease transition-discrete outline-hidden flex flex-col",
26
+ // animations
25
27
  "data-[state=open]:animate-in data-[state=closed]:animate-out",
26
28
  "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
27
29
  "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
28
30
  "data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2",
31
+ // styles
32
+ "bg-popover text-popover-foreground gap-4 rounded-lg border border-border p-4 text-sm shadow-lg",
33
+ // origin from positioner
29
34
  "origin-(--transform-origin) [--tw-animation-fill-mode:both]",
30
35
  ]);
31
36
  ---
@@ -39,11 +44,7 @@ const contentVariants = cva([
39
44
  data-side={side}
40
45
  data-side-offset={sideOffset}
41
46
  style={`--side-offset: ${sideOffset}px`}
42
- class={cn(
43
- "bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 flex flex-col gap-4 rounded-md p-4 text-sm shadow-md ring-1 duration-100 rounded-lg data-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2",
44
- contentVariants(),
45
- className,
46
- )}
47
+ class={cn(contentVariants(), className)}
47
48
  >
48
49
  <slot />
49
50
  </div>
@@ -12,7 +12,7 @@ const { class: className = "", ...rest } = Astro.props as Props;
12
12
  <div
13
13
  {...rest}
14
14
  data-slot="popover-header"
15
- class={cn("flex flex-col gap-1 text-sm", className)}
15
+ class={cn("flex flex-col gap-0.5 text-sm", className)}
16
16
  >
17
17
  <slot />
18
18
  </div>
@@ -9,7 +9,12 @@ type Props = ButtonProps & {
9
9
  asChild?: boolean;
10
10
  };
11
11
 
12
- const { class: className = "", asChild = false, ...rest } = Astro.props as Props;
12
+ const {
13
+ class: className = "",
14
+ asChild = false,
15
+ variant = "outline",
16
+ ...rest
17
+ } = Astro.props as Props;
13
18
  ---
14
19
 
15
20
  {
@@ -20,6 +25,7 @@ const { class: className = "", asChild = false, ...rest } = Astro.props as Props
20
25
  ) : (
21
26
  <Button
22
27
  data-slot="popover-trigger"
28
+ variant={variant}
23
29
  class={cn("", "inline-flex items-center", className)}
24
30
  {...rest as any}
25
31
  >
@@ -1,7 +1,7 @@
1
- export { default as Popover } from "./Popover.astro";
2
- export { default as PopoverClose } from "./PopoverClose.astro";
3
- export { default as PopoverContent } from "./PopoverContent.astro";
4
- export { default as PopoverDescription } from "./PopoverDescription.astro";
5
- export { default as PopoverHeader } from "./PopoverHeader.astro";
6
- export { default as PopoverTitle } from "./PopoverTitle.astro";
7
- export { default as PopoverTrigger } from "./PopoverTrigger.astro";
1
+ export { default as Popover } from "./Popover.astro";
2
+ export { default as PopoverClose } from "./PopoverClose.astro";
3
+ export { default as PopoverContent } from "./PopoverContent.astro";
4
+ export { default as PopoverDescription } from "./PopoverDescription.astro";
5
+ export { default as PopoverHeader } from "./PopoverHeader.astro";
6
+ export { default as PopoverTitle } from "./PopoverTitle.astro";
7
+ export { default as PopoverTrigger } from "./PopoverTrigger.astro";
@@ -1,9 +1,31 @@
1
- # Changelog
2
-
3
- All notable changes to the `RadioGroup` component will be documented in this file.
4
-
5
- ## Unreleased
6
-
7
- ### Fixed
8
-
9
- - Restored interactivity: `@data-slot/radio-group` (already the correct import in `RadioGroup.astro`) was never added as a dependency and was aliased to a no-op stub so builds wouldn't crash, leaving RadioGroup fully static. Added the real package as a dependency and removed the stub alias.
1
+ # Changelog
2
+
3
+ All notable changes to the `RadioGroup` component family (`RadioGroup`, `RadioGroupItem`) are documented in this file.
4
+
5
+ ## Unreleased
6
+
7
+ ### Fixed
8
+
9
+ - Restored interactivity: `@data-slot/radio-group` (already the correct import in `RadioGroup.astro`) was never added as a dependency and was aliased to a no-op stub so builds wouldn't crash, leaving RadioGroup fully static. Added the real package as a dependency and removed the stub alias.
10
+
11
+ ## 2026-09-18 — Figma alignment (ClickLearn Lightning Design Library, node `50-3604`)
12
+
13
+ Figma documents Radio across four pages: Variants (Default, Checked), States (Default, Hover, Focus, Disabled), Sizes (Small 16px, Large 20px), and Contents (Clean, Label, Label and Caption).
14
+
15
+ ### Fixed
16
+
17
+ - Updated `@figmaUrl` from the old Bejamas file to the ClickLearn Lightning Design Library link.
18
+ - **Checked-state color was backwards** — the real, visually significant bug here. The code filled the whole circle with `bg-primary` + a white (`bg-primary-foreground`) center dot when checked. Figma shows the opposite: the circle stays white (`card`) with a `border-primary` ring, and only the small center dot is `bg-primary` — a standard bordered radio with a colored dot, not a filled disc. Fixed by removing `data-checked:bg-primary`/`data-checked:text-primary-foreground` and changing the indicator dot from `bg-primary-foreground` to `bg-primary`.
19
+ - Background: added an explicit, unconditional `bg-card` — previously the root only had `dark:bg-input/30` with nothing for light mode, same category of fix already made to `Input`/`InputGroup`/`DialogContent`.
20
+ - Added `size?: "default" | "lg"` to `RadioGroupItem` (16px / 20px, matching Figma's Small/Large) — previously hard-coded to `size-4` (16px) only. The indicator dot scales proportionally (8px / 10px, matching Figma's exact 0.5 dot-to-circle ratio at both sizes measured directly from the Figma node).
21
+
22
+ ### Added
23
+
24
+ - Storybook examples for Sizes, "Label and caption" (via `Field`/`FieldLabel`/`FieldDescription`, matching Figma's third content variant — `RadioGroupItem` already carries the `data-slot="radio-group-item"` marker that `Field`'s alignment selector targets, from the earlier Checkbox alignment work, so no additional alignment fix was needed here), and Disabled — none of these were previously demonstrated.
25
+
26
+ ### Notes
27
+
28
+ - Focus (`focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50`) and error/`aria-invalid` styling already matched Figma's tokens exactly (`ring` = `#5f9de6`) — no changes needed.
29
+ - Hover isn't implemented, per the standing project direction to skip hover states.
30
+ - Disabled's exact color treatment in Figma is ambiguous from the extracted tokens (bound to what look like incidental, non-muted swatch names at reduced opacity rather than a clear flat color swap like Checkbox/Input had) — left as the existing `disabled:opacity-50` fade rather than guessing a specific flat-color replacement.
31
+ - "Clean" and "Label" content variants were already fully supported (a bare `RadioGroupItem`, or paired with a sibling `Label`) — no changes needed.
@@ -3,7 +3,7 @@
3
3
  * @component RadioGroup
4
4
  * @title Radio Group
5
5
  * @description A group of options where users can select exactly one choice.
6
- * @figmaUrl https://www.figma.com/design/koxai7zw5vIuBzuVxe5T2l/bejamas-ui?node-id=2549-18817&t=YFStJ3V8fXEO8QD8-4
6
+ * @figmaUrl https://www.figma.com/design/qXkzdqZ3QPQqgckU3AwIAH/ClickLearn-Lightning-Design-Library?node-id=50-3604
7
7
  *
8
8
  * @preview
9
9
  *
@@ -4,6 +4,7 @@ import type { HTMLAttributes } from "astro/types";
4
4
 
5
5
  interface Props extends HTMLAttributes<"span"> {
6
6
  value: string;
7
+ size?: "default" | "lg";
7
8
  class?: string;
8
9
  disabled?: boolean;
9
10
  id?: string;
@@ -14,6 +15,7 @@ const {
14
15
  class: className = "",
15
16
  id = "",
16
17
  value,
18
+ size = "default",
17
19
  disabled = false,
18
20
  "aria-invalid": ariaInvalid,
19
21
  ...rest
@@ -25,18 +27,25 @@ const {
25
27
  id={id}
26
28
  data-slot="radio-group-item"
27
29
  data-value={value}
30
+ data-size={size}
28
31
  data-disabled={disabled ? "true" : undefined}
29
32
  aria-invalid={ariaInvalid}
30
33
  class={cn(
31
- "border-input dark:bg-input/30 data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary data-checked:border-primary aria-invalid:aria-checked:border-primary aria-invalid:border-destructive focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 dark:aria-invalid:border-destructive/50 flex size-4 rounded-full focus-visible:ring-3 aria-invalid:ring-3 group/radio-group-item peer relative aspect-square shrink-0 border outline-none after:absolute after:-inset-x-3 after:-inset-y-2 disabled:cursor-not-allowed disabled:opacity-50",
34
+ "border-input bg-card data-checked:border-primary aria-invalid:aria-checked:border-primary aria-invalid:border-destructive focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 dark:aria-invalid:border-destructive/50 flex rounded-full focus-visible:ring-3 aria-invalid:ring-3 group/radio-group-item peer relative aspect-square shrink-0 border outline-none after:absolute after:-inset-x-3 after:-inset-y-2 disabled:cursor-not-allowed disabled:opacity-50",
35
+ size === "lg" ? "size-5" : "size-4",
32
36
  className,
33
37
  )}
34
38
  >
35
39
  <span
36
40
  aria-hidden="true"
37
41
  data-slot="radio-group-indicator"
38
- class="flex size-4 items-center justify-center data-unchecked:hidden"
42
+ class={cn("flex items-center justify-center data-unchecked:hidden", size === "lg" ? "size-5" : "size-4")}
39
43
  >
40
- <span class="bg-primary-foreground absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rounded-full"></span>
44
+ <span
45
+ class={cn(
46
+ "bg-primary absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 rounded-full",
47
+ size === "lg" ? "size-2.5" : "size-2",
48
+ )}
49
+ ></span>
41
50
  </span>
42
51
  </span>
@@ -1,3 +1,3 @@
1
- export { default as RadioGroup } from "./RadioGroup.astro";
2
- export { default as RadioGroupItem } from "./RadioGroupItem.astro";
3
-
1
+ export { default as RadioGroup } from "./RadioGroup.astro";
2
+ export { default as RadioGroupItem } from "./RadioGroupItem.astro";
3
+