@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,176 @@
1
+ ---
2
+ /**
3
+ * @component LightningSidebarNav
4
+ * @title Lightning Sidebar Nav
5
+ * @description Organism. The full workspace sidebar rendered from JSON-serialisable backend data: workspace switcher, navigation sections, footer links, usage and upgrade CTA.
6
+ * @figmaUrl https://www.figma.com/design/qXkzdqZ3QPQqgckU3AwIAH/ClickLearn-Lightning-Design-Library?node-id=7513-60
7
+ *
8
+ * @usage
9
+ *
10
+ * ```astro nocollapse
11
+ * ---
12
+ * import { LightningSidebarNav } from '@cl-light-ds/ui/components/organisms/lightning-sidebar-nav';
13
+ * const nav = await fetch('/api/navigation').then((r) => r.json());
14
+ * ---
15
+ *
16
+ * <LightningSidebarNav {...nav} currentPath={Astro.url.pathname} />
17
+ * ```
18
+ *
19
+ * Active item: `activeId` wins; otherwise the link whose `href` is the longest
20
+ * segment-prefix of `currentPath` (defaults to `Astro.url.pathname`).
21
+ * Groups containing the active link open automatically.
22
+ */
23
+
24
+ import type { HTMLAttributes } from "astro/types";
25
+ import { Separator } from "../../separator";
26
+ import {
27
+ isSidebarNavGroup,
28
+ type SidebarNavLink,
29
+ type SidebarNavSectionData,
30
+ type SidebarNavSubLink,
31
+ type SidebarNavUpgrade,
32
+ type SidebarNavUsageMetric,
33
+ type SidebarNavWorkspace,
34
+ } from "./types";
35
+ import SidebarNav from "./SidebarNav.astro";
36
+ import SidebarNavGroup from "./SidebarNavGroup.astro";
37
+ import SidebarNavItem from "./SidebarNavItem.astro";
38
+ import SidebarNavSection from "./SidebarNavSection.astro";
39
+ import SidebarNavSkeleton from "./SidebarNavSkeleton.astro";
40
+ import SidebarNavSubItem from "./SidebarNavSubItem.astro";
41
+ import SidebarNavUsage from "./SidebarNavUsage.astro";
42
+ import SidebarNavWorkspaceSwitcher from "./SidebarNavWorkspaceSwitcher.astro";
43
+
44
+ interface Props extends HTMLAttributes<"nav"> {
45
+ workspace: SidebarNavWorkspace;
46
+ workspaces?: SidebarNavWorkspace[];
47
+ sections: SidebarNavSectionData[];
48
+ footerItems?: SidebarNavLink[];
49
+ currentPath?: string;
50
+ activeId?: string;
51
+ usage?: SidebarNavUsageMetric[];
52
+ upgrade?: SidebarNavUpgrade | null;
53
+ loading?: boolean;
54
+ class?: string;
55
+ }
56
+
57
+ const {
58
+ workspace,
59
+ workspaces = [],
60
+ sections,
61
+ footerItems = [],
62
+ currentPath = Astro.url.pathname,
63
+ activeId: activeIdProp,
64
+ usage = [],
65
+ upgrade = null,
66
+ loading = false,
67
+ ...rest
68
+ } = Astro.props as Props;
69
+
70
+ const allLinks: (SidebarNavLink | SidebarNavSubLink)[] = [
71
+ ...sections.flatMap((section) =>
72
+ section.items.flatMap((entry) =>
73
+ isSidebarNavGroup(entry) ? entry.children : [entry],
74
+ ),
75
+ ),
76
+ ...footerItems,
77
+ ];
78
+
79
+ const stripPath = (value: string) =>
80
+ value.split(/[?#]/)[0]!.replace(/(.)\/+$/, "$1");
81
+
82
+ const matches = (href: string, path: string) => {
83
+ const target = stripPath(href);
84
+ if (target === path) return true;
85
+ return target !== "/" && path.startsWith(`${target}/`);
86
+ };
87
+
88
+ const resolveActiveId = () => {
89
+ if (activeIdProp) return activeIdProp;
90
+ const path = stripPath(currentPath);
91
+ return allLinks
92
+ .filter((link) => matches(link.href, path))
93
+ .sort((a, b) => stripPath(b.href).length - stripPath(a.href).length)[0]?.id;
94
+ };
95
+
96
+ const activeId = resolveActiveId();
97
+ ---
98
+
99
+ <SidebarNav {...rest} aria-busy={loading ? "true" : undefined}>
100
+ <SidebarNavWorkspaceSwitcher
101
+ slot="header"
102
+ workspace={workspace}
103
+ workspaces={workspaces}
104
+ />
105
+
106
+ {
107
+ loading ? (
108
+ <SidebarNavSkeleton usage={usage.length > 0 || Boolean(upgrade)} />
109
+ ) : (
110
+ <Fragment>
111
+ <div class="flex flex-col gap-3">
112
+ {sections.map((section) => (
113
+ <SidebarNavSection label={section.label}>
114
+ {section.items.map((entry) =>
115
+ isSidebarNavGroup(entry) ? (
116
+ <SidebarNavGroup
117
+ label={entry.label}
118
+ icon={entry.icon}
119
+ pro={entry.pro}
120
+ open={
121
+ entry.defaultOpen ??
122
+ entry.children.some((child) => child.id === activeId)
123
+ }
124
+ >
125
+ {entry.children.map((child) => (
126
+ <SidebarNavSubItem
127
+ href={child.href}
128
+ label={child.label}
129
+ pro={child.pro}
130
+ active={child.id === activeId}
131
+ data-nav-id={child.id}
132
+ />
133
+ ))}
134
+ </SidebarNavGroup>
135
+ ) : (
136
+ <SidebarNavItem
137
+ href={entry.href}
138
+ label={entry.label}
139
+ icon={entry.icon}
140
+ pro={entry.pro}
141
+ disabled={entry.disabled}
142
+ active={entry.id === activeId}
143
+ data-nav-id={entry.id}
144
+ />
145
+ ),
146
+ )}
147
+ </SidebarNavSection>
148
+ ))}
149
+ <Separator class="my-2" />
150
+ </div>
151
+
152
+ {footerItems.length > 0 && (
153
+ <SidebarNavSection density="compact">
154
+ {footerItems.map((item) => (
155
+ <SidebarNavItem
156
+ href={item.href}
157
+ label={item.label}
158
+ icon={item.icon}
159
+ pro={item.pro}
160
+ disabled={item.disabled}
161
+ active={item.id === activeId}
162
+ data-nav-id={item.id}
163
+ />
164
+ ))}
165
+ </SidebarNavSection>
166
+ )}
167
+ </Fragment>
168
+ )
169
+ }
170
+
171
+ {
172
+ !loading && (
173
+ <SidebarNavUsage slot="footer" usage={usage} upgrade={upgrade} />
174
+ )
175
+ }
176
+ </SidebarNav>
@@ -0,0 +1,50 @@
1
+ ---
2
+ /**
3
+ * @component SidebarNav
4
+ * @title Sidebar Nav
5
+ * @description The 270px sidebar shell. `header` slot (workspace switcher), a scrollable default slot (sections) and a `footer` slot (usage / upgrade).
6
+ * @figmaUrl https://www.figma.com/design/qXkzdqZ3QPQqgckU3AwIAH/ClickLearn-Lightning-Design-Library?node-id=7514-711
7
+ *
8
+ * @usage
9
+ *
10
+ * ```astro nocollapse
11
+ * <SidebarNav aria-label="Workspace navigation">
12
+ * <SidebarNavWorkspaceSwitcher slot="header" workspace={workspace} />
13
+ * <SidebarNavSection>…</SidebarNavSection>
14
+ * <SidebarNavUsage slot="footer" usage={usage} />
15
+ * </SidebarNav>
16
+ * ```
17
+ */
18
+
19
+ import type { HTMLAttributes } from "astro/types";
20
+ import { cn } from "@cl-light-ds/ui/lib/utils";
21
+
22
+ interface Props extends HTMLAttributes<"nav"> {
23
+ class?: string;
24
+ }
25
+
26
+ const {
27
+ class: className = "",
28
+ "aria-label": ariaLabel = "Workspace navigation",
29
+ ...rest
30
+ } = Astro.props as Props;
31
+ ---
32
+
33
+ <nav
34
+ {...rest}
35
+ aria-label={ariaLabel}
36
+ data-slot="sidebar-nav"
37
+ class={cn(
38
+ "bg-sidebar text-sidebar-foreground border-sidebar-border flex h-full w-[270px] shrink-0 flex-col overflow-hidden border-r p-4",
39
+ className,
40
+ )}
41
+ >
42
+ <slot name="header" />
43
+ <div
44
+ data-slot="sidebar-nav-body"
45
+ class="flex min-h-0 flex-1 flex-col justify-between overflow-y-auto"
46
+ >
47
+ <slot />
48
+ </div>
49
+ <slot name="footer" />
50
+ </nav>
@@ -0,0 +1,74 @@
1
+ ---
2
+ /**
3
+ * @component SidebarNavGroup
4
+ * @title Sidebar Nav Group
5
+ * @description An expandable parent row (`<li>`) built on Collapsible. Holds SidebarNavSubItem children; the chevron flips when open.
6
+ * @figmaUrl https://www.figma.com/design/qXkzdqZ3QPQqgckU3AwIAH/ClickLearn-Lightning-Design-Library?node-id=7514-258
7
+ *
8
+ * @usage
9
+ *
10
+ * ```astro nocollapse
11
+ * <SidebarNavGroup label="Customization" icon="palette" open>
12
+ * <SidebarNavSubItem href="/customization/templates" label="Templates" />
13
+ * <SidebarNavSubItem href="/customization/branding" label="Branding" active />
14
+ * </SidebarNavGroup>
15
+ * ```
16
+ */
17
+
18
+ import { ChevronDown } from "@lucide/astro";
19
+ import { cn } from "@cl-light-ds/ui/lib/utils";
20
+ import {
21
+ Collapsible,
22
+ CollapsibleContent,
23
+ CollapsibleTrigger,
24
+ } from "../../collapsible";
25
+ import { resolveSidebarNavIcon } from "./icons";
26
+ import type { SidebarNavIcon } from "./types";
27
+ import SidebarNavProBadge from "./SidebarNavProBadge.astro";
28
+
29
+ interface Props {
30
+ label: string;
31
+ icon?: SidebarNavIcon;
32
+ /** Initial open state. */
33
+ open?: boolean;
34
+ pro?: boolean;
35
+ class?: string;
36
+ }
37
+
38
+ const {
39
+ label,
40
+ icon,
41
+ open = false,
42
+ pro = false,
43
+ class: className = "",
44
+ } = Astro.props as Props;
45
+
46
+ const Icon = resolveSidebarNavIcon(icon);
47
+ ---
48
+
49
+ <li data-slot="sidebar-nav-group" class={cn("flex flex-col", className)}>
50
+ <Collapsible open={open} class="flex flex-col">
51
+ <CollapsibleTrigger
52
+ class={cn(
53
+ "flex w-full items-center gap-2 rounded-lg p-2 text-left text-sm leading-5 font-medium text-muted-foreground outline-none transition-colors",
54
+ "group-data-[state=open]/collapsible:text-foreground",
55
+ "hover:bg-muted hover:font-semibold hover:text-foreground",
56
+ "focus-visible:text-foreground focus-visible:ring-1 focus-visible:ring-sidebar-ring focus-visible:ring-inset",
57
+ "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg]:size-4",
58
+ )}
59
+ >
60
+ {Icon && <Icon aria-hidden="true" />}
61
+ <span class="min-w-0 flex-1 truncate">{label}</span>
62
+ {pro && <SidebarNavProBadge />}
63
+ <ChevronDown
64
+ aria-hidden="true"
65
+ class="transition-transform duration-200 group-data-[state=open]/collapsible:rotate-180 motion-reduce:transition-none"
66
+ />
67
+ </CollapsibleTrigger>
68
+ <CollapsibleContent>
69
+ <ul data-slot="sidebar-nav-group-content" class="flex flex-col px-2 pb-2">
70
+ <slot />
71
+ </ul>
72
+ </CollapsibleContent>
73
+ </Collapsible>
74
+ </li>
@@ -0,0 +1,94 @@
1
+ ---
2
+ /**
3
+ * @component SidebarNavItem
4
+ * @title Sidebar Nav Item
5
+ * @description One row (`<li>`) of the workspace sidebar: a 16px Lucide icon, a label and an optional Pro badge. Active is carried by an accent fill AND a semibold label, never by colour alone.
6
+ * @figmaUrl https://www.figma.com/design/qXkzdqZ3QPQqgckU3AwIAH/ClickLearn-Lightning-Design-Library?node-id=7514-237
7
+ *
8
+ * @usage
9
+ *
10
+ * ```astro nocollapse
11
+ * ---
12
+ * import { SidebarNavItem } from '@cl-light-ds/ui/components/organisms/lightning-sidebar-nav';
13
+ * ---
14
+ *
15
+ * <SidebarNavItem href="/recordings" icon="video" label="Recordings" active />
16
+ * ```
17
+ */
18
+
19
+ import type { HTMLAttributes } from "astro/types";
20
+ import { cva } from "class-variance-authority";
21
+ import { cn } from "@cl-light-ds/ui/lib/utils";
22
+ import { resolveSidebarNavIcon } from "./icons";
23
+ import type { SidebarNavIcon } from "./types";
24
+ import SidebarNavProBadge from "./SidebarNavProBadge.astro";
25
+
26
+ type Props = Omit<HTMLAttributes<"a">, "href"> & {
27
+ /** Renders an `<a>` when set, otherwise a `<button>`. */
28
+ href?: string;
29
+ label: string;
30
+ icon?: SidebarNavIcon;
31
+ active?: boolean;
32
+ pro?: boolean;
33
+ disabled?: boolean;
34
+ class?: string;
35
+ };
36
+
37
+ const {
38
+ href,
39
+ label,
40
+ icon,
41
+ active = false,
42
+ pro = false,
43
+ disabled = false,
44
+ class: className = "",
45
+ ...rest
46
+ } = Astro.props as Props;
47
+
48
+ export const sidebarNavItemVariants = cva(
49
+ [
50
+ "group/sidebar-nav-item flex w-full items-center gap-2 rounded-lg p-2 text-left text-sm leading-5 font-medium outline-none transition-colors",
51
+ "focus-visible:ring-1 focus-visible:ring-sidebar-ring focus-visible:ring-inset",
52
+ "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&>svg]:size-4",
53
+ ],
54
+ {
55
+ variants: {
56
+ active: {
57
+ true: "bg-sidebar-accent font-semibold text-sidebar-accent-foreground",
58
+ false:
59
+ "text-muted-foreground hover:bg-muted hover:font-semibold hover:text-foreground focus-visible:text-foreground",
60
+ },
61
+ disabled: {
62
+ true: "pointer-events-none opacity-50",
63
+ false: "",
64
+ },
65
+ },
66
+ defaultVariants: { active: false, disabled: false },
67
+ },
68
+ );
69
+
70
+ const Icon = resolveSidebarNavIcon(icon);
71
+ const Tag = href && !disabled ? "a" : "button";
72
+ const classes = cn(sidebarNavItemVariants({ active, disabled }), className);
73
+ ---
74
+
75
+ <li data-slot="sidebar-nav-item-wrapper" class="flex">
76
+ <Tag
77
+ {...rest}
78
+ data-slot="sidebar-nav-item"
79
+ data-active={active ? "" : undefined}
80
+ href={Tag === "a" ? href : undefined}
81
+ type={Tag === "button" ? "button" : undefined}
82
+ aria-current={active ? "page" : undefined}
83
+ aria-disabled={disabled ? "true" : undefined}
84
+ disabled={Tag === "button" && disabled ? true : undefined}
85
+ class={classes}
86
+ >
87
+ {Icon && <Icon aria-hidden="true" />}
88
+ <span data-slot="sidebar-nav-item-label" class="min-w-0 flex-1 truncate"
89
+ >{label}</span
90
+ >
91
+ {pro && <SidebarNavProBadge />}
92
+ <slot />
93
+ </Tag>
94
+ </li>
@@ -0,0 +1,26 @@
1
+ ---
2
+ /**
3
+ * @component SidebarNavProBadge
4
+ * @description The Accent/Small "Pro" badge with a zap tail icon, shown on Pro-gated destinations.
5
+ * @figmaUrl https://www.figma.com/design/qXkzdqZ3QPQqgckU3AwIAH/ClickLearn-Lightning-Design-Library?node-id=7514-237
6
+ */
7
+
8
+ import { Zap } from "@lucide/astro";
9
+ import { Badge } from "../../badge";
10
+
11
+ interface Props {
12
+ class?: string;
13
+ }
14
+
15
+ const { class: className = "" } = Astro.props as Props;
16
+ ---
17
+
18
+ <Badge
19
+ variant="accent"
20
+ size="sm"
21
+ data-slot="sidebar-nav-pro-badge"
22
+ class={className}
23
+ >
24
+ Pro
25
+ <Zap slot="tail-icon" aria-hidden="true" />
26
+ </Badge>
@@ -0,0 +1,71 @@
1
+ ---
2
+ /**
3
+ * @component SidebarNavSection
4
+ * @title Sidebar Nav Section
5
+ * @description A list of SidebarNavItem / SidebarNavGroup rows, optionally headed by an uppercase category label.
6
+ * @figmaUrl https://www.figma.com/design/qXkzdqZ3QPQqgckU3AwIAH/ClickLearn-Lightning-Design-Library?node-id=7514-242
7
+ *
8
+ * @usage
9
+ *
10
+ * ```astro nocollapse
11
+ * <SidebarNavSection label="Customization">
12
+ * <SidebarNavItem href="/templates" icon="file-text" label="Templates" />
13
+ * </SidebarNavSection>
14
+ * ```
15
+ */
16
+
17
+ import type { HTMLAttributes } from "astro/types";
18
+ import { cva } from "class-variance-authority";
19
+ import { cn } from "@cl-light-ds/ui/lib/utils";
20
+
21
+ interface Props extends HTMLAttributes<"div"> {
22
+ /** Category heading (Figma: Group Item / Variant=Category). */
23
+ label?: string;
24
+ /** `default` = 12px between rows (primary nav); `compact` = 2px (footer links). */
25
+ density?: "default" | "compact";
26
+ class?: string;
27
+ }
28
+
29
+ const {
30
+ label,
31
+ density = "default",
32
+ class: className = "",
33
+ ...rest
34
+ } = Astro.props as Props;
35
+
36
+ const listVariants = cva("flex flex-col", {
37
+ variants: {
38
+ density: {
39
+ default: "gap-3",
40
+ compact: "gap-0.5",
41
+ },
42
+ },
43
+ defaultVariants: { density: "default" },
44
+ });
45
+
46
+ // Unique per render so several sidebars can share a page.
47
+ const headingId = label
48
+ ? `sidebar-nav-section-${Math.random().toString(36).slice(2, 10)}`
49
+ : undefined;
50
+ ---
51
+
52
+ <div
53
+ {...rest}
54
+ data-slot="sidebar-nav-section"
55
+ class={cn("flex flex-col gap-2", className)}
56
+ >
57
+ {
58
+ label && (
59
+ <p
60
+ id={headingId}
61
+ data-slot="sidebar-nav-section-label"
62
+ class="text-muted-foreground truncate px-2 text-xs leading-none font-semibold uppercase"
63
+ >
64
+ {label}
65
+ </p>
66
+ )
67
+ }
68
+ <ul aria-labelledby={headingId} class={listVariants({ density })}>
69
+ <slot />
70
+ </ul>
71
+ </div>
@@ -0,0 +1,67 @@
1
+ ---
2
+ /**
3
+ * @component SidebarNavSkeleton
4
+ * @title Sidebar Nav Skeleton
5
+ * @description Loading placeholders for the sidebar body (Figma: Sidebar / State=Skeleton). The workspace switcher stays real while navigation loads.
6
+ * @figmaUrl https://www.figma.com/design/qXkzdqZ3QPQqgckU3AwIAH/ClickLearn-Lightning-Design-Library?node-id=7514-710
7
+ */
8
+
9
+ import { Separator } from "../../separator";
10
+ import { Skeleton } from "../../skeleton";
11
+
12
+ interface Props {
13
+ /** Placeholder rows in the primary list. */
14
+ items?: number;
15
+ /** Placeholder rows in the footer list. */
16
+ footerItems?: number;
17
+ /** Render usage/upgrade placeholders. */
18
+ usage?: boolean;
19
+ }
20
+
21
+ const { items = 5, footerItems = 2, usage = true } = Astro.props as Props;
22
+
23
+ const row = (count: number) => Array.from({ length: count }, (_, i) => i);
24
+ ---
25
+
26
+ <div
27
+ data-slot="sidebar-nav-skeleton"
28
+ aria-hidden="true"
29
+ class="flex min-h-0 flex-1 flex-col"
30
+ >
31
+ <div class="flex min-h-0 flex-1 flex-col justify-between">
32
+ <div class="flex flex-col gap-3">
33
+ {
34
+ row(items).map(() => (
35
+ <div class="flex items-center gap-2 p-2">
36
+ <Skeleton class="size-4 rounded-[3px]" />
37
+ <Skeleton class="h-3.5 w-20 rounded-sm" />
38
+ </div>
39
+ ))
40
+ }
41
+ <Separator class="my-2" />
42
+ </div>
43
+ <div class="flex flex-col gap-0.5">
44
+ {
45
+ row(footerItems).map(() => (
46
+ <div class="flex items-center gap-2 p-2">
47
+ <Skeleton class="size-4 rounded-[3px]" />
48
+ <Skeleton class="h-3.5 w-20 rounded-sm" />
49
+ </div>
50
+ ))
51
+ }
52
+ </div>
53
+ </div>
54
+ {
55
+ usage && (
56
+ <div class="flex flex-col gap-3">
57
+ <Separator class="my-2" />
58
+ <div class="flex flex-col gap-2">
59
+ <Skeleton class="h-3.5 w-20 rounded-sm" />
60
+ <Skeleton class="h-3.5 w-20 rounded-sm" />
61
+ </div>
62
+ <Skeleton class="h-3.5 w-20 rounded-sm" />
63
+ </div>
64
+ )
65
+ }
66
+ </div>
67
+ <span class="sr-only" role="status">Loading navigation</span>
@@ -0,0 +1,69 @@
1
+ ---
2
+ /**
3
+ * @component SidebarNavSubItem
4
+ * @title Sidebar Nav Sub Item
5
+ * @description A destination nested inside a SidebarNavGroup, drawn against a vertical guide rail. The active item darkens its segment of the rail.
6
+ * @figmaUrl https://www.figma.com/design/qXkzdqZ3QPQqgckU3AwIAH/ClickLearn-Lightning-Design-Library?node-id=7514-323
7
+ *
8
+ * @usage
9
+ *
10
+ * ```astro nocollapse
11
+ * <SidebarNavSubItem href="/customization/branding" label="Branding" active />
12
+ * ```
13
+ */
14
+
15
+ import type { HTMLAttributes } from "astro/types";
16
+ import { cn } from "@cl-light-ds/ui/lib/utils";
17
+ import SidebarNavProBadge from "./SidebarNavProBadge.astro";
18
+
19
+ interface Props extends HTMLAttributes<"a"> {
20
+ href: string;
21
+ label: string;
22
+ active?: boolean;
23
+ pro?: boolean;
24
+ class?: string;
25
+ }
26
+
27
+ const {
28
+ href,
29
+ label,
30
+ active = false,
31
+ pro = false,
32
+ class: className = "",
33
+ ...rest
34
+ } = Astro.props as Props;
35
+ ---
36
+
37
+ <li
38
+ data-slot="sidebar-nav-sub-item"
39
+ class={cn("relative flex pl-[22px]", className)}
40
+ >
41
+ <span
42
+ aria-hidden="true"
43
+ class="bg-sidebar-rail absolute top-1/2 left-[7.5px] h-8 w-px -translate-y-1/2"
44
+ ></span>
45
+ {
46
+ active && (
47
+ <span
48
+ aria-hidden="true"
49
+ class="bg-sidebar-accent-foreground absolute top-1/2 left-[7.5px] h-7 w-px -translate-y-1/2"
50
+ />
51
+ )
52
+ }
53
+ <a
54
+ {...rest}
55
+ href={href}
56
+ data-active={active ? "" : undefined}
57
+ aria-current={active ? "page" : undefined}
58
+ class={cn(
59
+ "flex min-w-0 flex-1 items-center justify-between gap-2 rounded-sm px-2 py-0.5 text-xs leading-6 font-medium outline-none transition-colors",
60
+ "hover:bg-muted focus-visible:ring-1 focus-visible:ring-sidebar-ring focus-visible:ring-inset",
61
+ active
62
+ ? "text-sidebar-accent-foreground"
63
+ : "text-muted-foreground hover:text-zinc-800 focus-visible:text-zinc-800 dark:hover:text-foreground dark:focus-visible:text-foreground",
64
+ )}
65
+ >
66
+ <span class="min-w-0 flex-1 truncate">{label}</span>
67
+ {pro && <SidebarNavProBadge />}
68
+ </a>
69
+ </li>