@lobehub/ui 5.48.2 → 5.49.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 (203) hide show
  1. package/es/Highlighter/FullFeatured.mjs +1 -1
  2. package/es/Highlighter/FullFeatured.mjs.map +1 -1
  3. package/es/Highlighter/LangSelect.mjs +1 -1
  4. package/es/Highlighter/LangSelect.mjs.map +1 -1
  5. package/es/Highlighter/style.mjs +14 -11
  6. package/es/Highlighter/style.mjs.map +1 -1
  7. package/es/Mermaid/FullFeatured.mjs +1 -1
  8. package/es/Mermaid/FullFeatured.mjs.map +1 -1
  9. package/es/ThemeProvider/ThemeProvider.mjs +2 -2
  10. package/es/ThemeProvider/ThemeProvider.mjs.map +1 -1
  11. package/es/awesome/AgentSkillCard/AgentSkillCard.d.mts +6 -0
  12. package/es/awesome/AgentSkillCard/AgentSkillCard.mjs +83 -0
  13. package/es/awesome/AgentSkillCard/AgentSkillCard.mjs.map +1 -0
  14. package/es/awesome/AgentSkillCard/agents.d.mts +6 -0
  15. package/es/awesome/AgentSkillCard/agents.mjs +40 -0
  16. package/es/awesome/AgentSkillCard/agents.mjs.map +1 -0
  17. package/es/awesome/AgentSkillCard/index.d.mts +4 -0
  18. package/es/awesome/AgentSkillCard/style.mjs +85 -0
  19. package/es/awesome/AgentSkillCard/style.mjs.map +1 -0
  20. package/es/awesome/AgentSkillCard/type.d.mts +41 -0
  21. package/es/awesome/BentoGrid/BentoCard.d.mts +6 -0
  22. package/es/awesome/BentoGrid/BentoCard.mjs +39 -0
  23. package/es/awesome/BentoGrid/BentoCard.mjs.map +1 -0
  24. package/es/awesome/BentoGrid/BentoGrid.d.mts +6 -0
  25. package/es/awesome/BentoGrid/BentoGrid.mjs +20 -0
  26. package/es/awesome/BentoGrid/BentoGrid.mjs.map +1 -0
  27. package/es/awesome/BentoGrid/index.d.mts +4 -0
  28. package/es/awesome/BentoGrid/style.mjs +102 -0
  29. package/es/awesome/BentoGrid/style.mjs.map +1 -0
  30. package/es/awesome/BentoGrid/type.d.mts +40 -0
  31. package/es/awesome/CodeShowcase/CodeShowcase.d.mts +6 -0
  32. package/es/awesome/CodeShowcase/CodeShowcase.mjs +51 -0
  33. package/es/awesome/CodeShowcase/CodeShowcase.mjs.map +1 -0
  34. package/es/awesome/CodeShowcase/index.d.mts +3 -0
  35. package/es/awesome/CodeShowcase/style.mjs +50 -0
  36. package/es/awesome/CodeShowcase/style.mjs.map +1 -0
  37. package/es/awesome/CodeShowcase/type.d.mts +27 -0
  38. package/es/awesome/FeatureGrid/FeatureGrid.d.mts +6 -0
  39. package/es/awesome/FeatureGrid/FeatureGrid.mjs +48 -0
  40. package/es/awesome/FeatureGrid/FeatureGrid.mjs.map +1 -0
  41. package/es/awesome/FeatureGrid/index.d.mts +3 -0
  42. package/es/awesome/FeatureGrid/style.mjs +75 -0
  43. package/es/awesome/FeatureGrid/style.mjs.map +1 -0
  44. package/es/awesome/FeatureGrid/type.d.mts +25 -0
  45. package/es/awesome/FluidGradient/FluidGradient.d.mts +6 -0
  46. package/es/awesome/FluidGradient/FluidGradient.mjs +293 -0
  47. package/es/awesome/FluidGradient/FluidGradient.mjs.map +1 -0
  48. package/es/awesome/FluidGradient/index.d.mts +3 -0
  49. package/es/awesome/FluidGradient/style.mjs +52 -0
  50. package/es/awesome/FluidGradient/style.mjs.map +1 -0
  51. package/es/awesome/FluidGradient/type.d.mts +22 -0
  52. package/es/awesome/InstallBanner/InstallBanner.d.mts +6 -0
  53. package/es/awesome/InstallBanner/InstallBanner.mjs +35 -0
  54. package/es/awesome/InstallBanner/InstallBanner.mjs.map +1 -0
  55. package/es/awesome/InstallBanner/index.d.mts +3 -0
  56. package/es/awesome/InstallBanner/style.mjs +46 -0
  57. package/es/awesome/InstallBanner/style.mjs.map +1 -0
  58. package/es/awesome/InstallBanner/type.d.mts +18 -0
  59. package/es/awesome/LandingHero/LandingHero.d.mts +6 -0
  60. package/es/awesome/LandingHero/LandingHero.mjs +55 -0
  61. package/es/awesome/LandingHero/LandingHero.mjs.map +1 -0
  62. package/es/awesome/LandingHero/index.d.mts +3 -0
  63. package/es/awesome/LandingHero/style.mjs +155 -0
  64. package/es/awesome/LandingHero/style.mjs.map +1 -0
  65. package/es/awesome/LandingHero/type.d.mts +29 -0
  66. package/es/awesome/LandingSection/LandingSection.d.mts +6 -0
  67. package/es/awesome/LandingSection/LandingSection.mjs +61 -0
  68. package/es/awesome/LandingSection/LandingSection.mjs.map +1 -0
  69. package/es/awesome/LandingSection/index.d.mts +3 -0
  70. package/es/awesome/LandingSection/style.mjs +88 -0
  71. package/es/awesome/LandingSection/style.mjs.map +1 -0
  72. package/es/awesome/LandingSection/type.d.mts +40 -0
  73. package/es/awesome/LogoMarquee/LogoMarquee.d.mts +6 -0
  74. package/es/awesome/LogoMarquee/LogoMarquee.mjs +52 -0
  75. package/es/awesome/LogoMarquee/LogoMarquee.mjs.map +1 -0
  76. package/es/awesome/LogoMarquee/index.d.mts +3 -0
  77. package/es/awesome/LogoMarquee/style.mjs +70 -0
  78. package/es/awesome/LogoMarquee/style.mjs.map +1 -0
  79. package/es/awesome/LogoMarquee/type.d.mts +40 -0
  80. package/es/awesome/index.d.mts +33 -1
  81. package/es/awesome/index.mjs +13 -1
  82. package/es/awesome/landingActions.d.mts +20 -0
  83. package/es/awesome/landingActions.mjs +123 -0
  84. package/es/awesome/landingActions.mjs.map +1 -0
  85. package/es/awesome/landingIcon.mjs +13 -0
  86. package/es/awesome/landingIcon.mjs.map +1 -0
  87. package/es/awesome/landingLink.d.mts +12 -0
  88. package/es/awesome/landingLink.mjs +23 -0
  89. package/es/awesome/landingLink.mjs.map +1 -0
  90. package/es/awesome/landingTokens.d.mts +10 -0
  91. package/es/awesome/landingTokens.mjs +19 -0
  92. package/es/awesome/landingTokens.mjs.map +1 -0
  93. package/es/base-ui/Input/style.d.mts +1 -1
  94. package/es/base-ui/Segmented/atoms.mjs +1 -0
  95. package/es/base-ui/Segmented/atoms.mjs.map +1 -1
  96. package/es/base-ui/Segmented/style.mjs +127 -125
  97. package/es/base-ui/Segmented/style.mjs.map +1 -1
  98. package/es/base-ui/index.mjs +4 -4
  99. package/es/dashboard/AuthLayout/AuthLayout.d.mts +9 -0
  100. package/es/dashboard/AuthLayout/AuthLayout.mjs +54 -0
  101. package/es/dashboard/AuthLayout/AuthLayout.mjs.map +1 -0
  102. package/es/dashboard/AuthLayout/index.d.mts +3 -0
  103. package/es/dashboard/AuthLayout/style.mjs +120 -0
  104. package/es/dashboard/AuthLayout/style.mjs.map +1 -0
  105. package/es/dashboard/AuthLayout/type.d.mts +16 -0
  106. package/es/dashboard/Breadcrumb/Breadcrumb.d.mts +9 -0
  107. package/es/dashboard/Breadcrumb/Breadcrumb.mjs +55 -0
  108. package/es/dashboard/Breadcrumb/Breadcrumb.mjs.map +1 -0
  109. package/es/dashboard/Breadcrumb/index.d.mts +3 -0
  110. package/es/dashboard/Breadcrumb/style.mjs +66 -0
  111. package/es/dashboard/Breadcrumb/style.mjs.map +1 -0
  112. package/es/dashboard/Breadcrumb/type.d.mts +25 -0
  113. package/es/dashboard/ConsoleNav/ConsoleNav.d.mts +9 -0
  114. package/es/dashboard/ConsoleNav/ConsoleNav.mjs +275 -0
  115. package/es/dashboard/ConsoleNav/ConsoleNav.mjs.map +1 -0
  116. package/es/dashboard/ConsoleNav/active.d.mts +7 -0
  117. package/es/dashboard/ConsoleNav/active.mjs +29 -0
  118. package/es/dashboard/ConsoleNav/active.mjs.map +1 -0
  119. package/es/dashboard/ConsoleNav/index.d.mts +4 -0
  120. package/es/dashboard/ConsoleNav/style.mjs +283 -0
  121. package/es/dashboard/ConsoleNav/style.mjs.map +1 -0
  122. package/es/dashboard/ConsoleNav/type.d.mts +71 -0
  123. package/es/dashboard/ConsoleShell/ConsoleBrand.d.mts +9 -0
  124. package/es/dashboard/ConsoleShell/ConsoleBrand.mjs +37 -0
  125. package/es/dashboard/ConsoleShell/ConsoleBrand.mjs.map +1 -0
  126. package/es/dashboard/ConsoleShell/ConsoleShell.d.mts +9 -0
  127. package/es/dashboard/ConsoleShell/ConsoleShell.mjs +149 -0
  128. package/es/dashboard/ConsoleShell/ConsoleShell.mjs.map +1 -0
  129. package/es/dashboard/ConsoleShell/context.d.mts +6 -0
  130. package/es/dashboard/ConsoleShell/context.mjs +16 -0
  131. package/es/dashboard/ConsoleShell/context.mjs.map +1 -0
  132. package/es/dashboard/ConsoleShell/index.d.mts +5 -0
  133. package/es/dashboard/ConsoleShell/style.mjs +209 -0
  134. package/es/dashboard/ConsoleShell/style.mjs.map +1 -0
  135. package/es/dashboard/ConsoleShell/type.d.mts +67 -0
  136. package/es/dashboard/FilterSidebar/FilterSidebar.d.mts +17 -0
  137. package/es/dashboard/FilterSidebar/FilterSidebar.mjs +151 -0
  138. package/es/dashboard/FilterSidebar/FilterSidebar.mjs.map +1 -0
  139. package/es/dashboard/FilterSidebar/context.d.mts +7 -0
  140. package/es/dashboard/FilterSidebar/context.mjs +19 -0
  141. package/es/dashboard/FilterSidebar/context.mjs.map +1 -0
  142. package/es/dashboard/FilterSidebar/index.d.mts +4 -0
  143. package/es/dashboard/FilterSidebar/style.mjs +155 -0
  144. package/es/dashboard/FilterSidebar/style.mjs.map +1 -0
  145. package/es/dashboard/FilterSidebar/type.d.mts +44 -0
  146. package/es/dashboard/FilterSidebar/width.mjs +18 -0
  147. package/es/dashboard/FilterSidebar/width.mjs.map +1 -0
  148. package/es/dashboard/PageHeader/PageHeader.d.mts +9 -0
  149. package/es/dashboard/PageHeader/PageHeader.mjs +37 -0
  150. package/es/dashboard/PageHeader/PageHeader.mjs.map +1 -0
  151. package/es/dashboard/PageHeader/index.d.mts +3 -0
  152. package/es/dashboard/PageHeader/style.mjs +47 -0
  153. package/es/dashboard/PageHeader/style.mjs.map +1 -0
  154. package/es/dashboard/PageHeader/type.d.mts +14 -0
  155. package/es/dashboard/PageSkeleton/PageSkeleton.d.mts +25 -0
  156. package/es/dashboard/PageSkeleton/PageSkeleton.mjs +136 -0
  157. package/es/dashboard/PageSkeleton/PageSkeleton.mjs.map +1 -0
  158. package/es/dashboard/PageSkeleton/style.mjs +122 -0
  159. package/es/dashboard/PageSkeleton/style.mjs.map +1 -0
  160. package/es/dashboard/PageSkeleton/type.d.mts +22 -0
  161. package/es/dashboard/PageTabs/PageTabs.d.mts +9 -0
  162. package/es/dashboard/PageTabs/PageTabs.mjs +37 -0
  163. package/es/dashboard/PageTabs/PageTabs.mjs.map +1 -0
  164. package/es/dashboard/PageTabs/index.d.mts +3 -0
  165. package/es/dashboard/PageTabs/style.mjs +57 -0
  166. package/es/dashboard/PageTabs/style.mjs.map +1 -0
  167. package/es/dashboard/PageTabs/type.d.mts +17 -0
  168. package/es/dashboard/RouteProgress/RouteProgress.d.mts +9 -0
  169. package/es/dashboard/RouteProgress/RouteProgress.mjs +44 -0
  170. package/es/dashboard/RouteProgress/RouteProgress.mjs.map +1 -0
  171. package/es/dashboard/RouteProgress/style.mjs +19 -0
  172. package/es/dashboard/RouteProgress/style.mjs.map +1 -0
  173. package/es/dashboard/RouteProgress/type.d.mts +8 -0
  174. package/es/dashboard/StatCard/StatCard.d.mts +9 -0
  175. package/es/dashboard/StatCard/StatCard.mjs +84 -0
  176. package/es/dashboard/StatCard/StatCard.mjs.map +1 -0
  177. package/es/dashboard/StatCard/index.d.mts +3 -0
  178. package/es/dashboard/StatCard/style.mjs +135 -0
  179. package/es/dashboard/StatCard/style.mjs.map +1 -0
  180. package/es/dashboard/StatCard/type.d.mts +26 -0
  181. package/es/dashboard/Surface/Surface.d.mts +13 -0
  182. package/es/dashboard/Surface/Surface.mjs +20 -0
  183. package/es/dashboard/Surface/Surface.mjs.map +1 -0
  184. package/es/dashboard/Surface/index.d.mts +4 -0
  185. package/es/dashboard/Surface/style.d.mts +14 -0
  186. package/es/dashboard/Surface/style.mjs +22 -0
  187. package/es/dashboard/Surface/style.mjs.map +1 -0
  188. package/es/dashboard/Surface/type.d.mts +8 -0
  189. package/es/dashboard/hooks.mjs +49 -0
  190. package/es/dashboard/hooks.mjs.map +1 -0
  191. package/es/dashboard/index.d.mts +37 -0
  192. package/es/dashboard/index.d.ts +1 -0
  193. package/es/dashboard/index.js +1 -0
  194. package/es/dashboard/index.mjs +17 -0
  195. package/es/static-css/registry.mjs +2 -2
  196. package/es/static-css/registry.mjs.map +1 -1
  197. package/es/storybook/StoryBook/index.mjs +4 -0
  198. package/es/storybook/StoryBook/index.mjs.map +1 -1
  199. package/es/storybook/StoryBook/style.mjs +7 -2
  200. package/es/storybook/StoryBook/style.mjs.map +1 -1
  201. package/es/styles/theme/token/base.mjs +2 -2
  202. package/es/styles/theme/token/base.mjs.map +1 -1
  203. package/package.json +6 -2
@@ -0,0 +1,275 @@
1
+ "use client";
2
+ import Icon from "../../Icon/Icon.mjs";
3
+ import { Tooltip } from "../../base-ui/Tooltip/Tooltip.mjs";
4
+ import ScrollShadow from "../../ScrollShadow/ScrollShadow.mjs";
5
+ import { collectNavItems, findActiveBranch, resolveActiveHref } from "./active.mjs";
6
+ import { useConsoleShellState } from "../ConsoleShell/context.mjs";
7
+ import { useLocalStorage, usePrefersReducedMotion } from "../hooks.mjs";
8
+ import { styles } from "./style.mjs";
9
+ import { useCallback, useEffect, useId, useRef, useState } from "react";
10
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
11
+ import { ChevronDown, Play } from "lucide-react";
12
+ //#region src/dashboard/ConsoleNav/ConsoleNav.tsx
13
+ const PANEL_TRANSITION_MS = 160;
14
+ /** Older builds stored the closed group keys as an array. */
15
+ function readOverrides(value) {
16
+ if (Array.isArray(value)) return Object.fromEntries(value.map((key) => [String(key), false]));
17
+ return value && typeof value === "object" ? value : {};
18
+ }
19
+ function scrollActiveIntoView(container) {
20
+ const link = container.querySelector("[aria-current=\"page\"]") ?? [...container.querySelectorAll("button[data-active=\"true\"]")].at(-1);
21
+ if (!link) return;
22
+ const containerRect = container.getBoundingClientRect();
23
+ const linkRect = link.getBoundingClientRect();
24
+ if (linkRect.top >= containerRect.top && linkRect.bottom <= containerRect.bottom) return;
25
+ container.scrollTop += linkRect.top - containerRect.top - (containerRect.height - linkRect.height) / 2;
26
+ }
27
+ function NavLink({ active, collapsed, context, href, icon, indent = 0, item, name }) {
28
+ const shell = useConsoleShellState();
29
+ const label = item?.label ?? name;
30
+ const follow = () => {
31
+ context.onNavigate?.(href);
32
+ shell?.closeNavigation();
33
+ };
34
+ const linkProps = {
35
+ "aria-current": active ? "page" : void 0,
36
+ "aria-label": collapsed ? name : void 0,
37
+ "children": /* @__PURE__ */ jsxs(Fragment$1, { children: [
38
+ icon ? /* @__PURE__ */ jsx(Icon, {
39
+ icon,
40
+ size: 18
41
+ }) : null,
42
+ collapsed ? null : /* @__PURE__ */ jsx("span", {
43
+ className: styles.itemLabel,
44
+ children: label
45
+ }),
46
+ !collapsed && item?.badge ? /* @__PURE__ */ jsx("span", {
47
+ className: styles.badge,
48
+ children: item.badge
49
+ }) : null,
50
+ collapsed && item?.badge ? /* @__PURE__ */ jsx("span", {
51
+ "aria-hidden": true,
52
+ className: styles.dot
53
+ }) : null
54
+ ] }),
55
+ "className": styles.item,
56
+ "data-active": active,
57
+ "data-collapsed": collapsed,
58
+ "data-indent": indent,
59
+ "external": item?.external,
60
+ href,
61
+ "onClick": follow,
62
+ "title": collapsed ? void 0 : label
63
+ };
64
+ const link = context.renderLink ? context.renderLink(linkProps) : /* @__PURE__ */ jsx("a", {
65
+ "aria-current": linkProps["aria-current"],
66
+ "aria-label": linkProps["aria-label"],
67
+ className: linkProps.className,
68
+ "data-active": active,
69
+ "data-collapsed": collapsed,
70
+ "data-indent": indent,
71
+ href,
72
+ rel: item?.external ? "noreferrer" : void 0,
73
+ target: item?.external ? "_blank" : void 0,
74
+ title: linkProps.title,
75
+ onClick: (event) => {
76
+ if (context.onNavigate && !item?.external) event.preventDefault();
77
+ follow();
78
+ },
79
+ children: linkProps.children
80
+ });
81
+ if (!collapsed) return link;
82
+ return /* @__PURE__ */ jsx(Tooltip, {
83
+ placement: "right",
84
+ title: name,
85
+ children: link
86
+ });
87
+ }
88
+ function ConsoleNav({ collapsed: collapsedProp, defaultExpanded = "all", groups = [], items = [], label = "Primary", onNavigate, pathname, renderLink, storageKey }) {
89
+ const id = useId();
90
+ const navRef = useRef(null);
91
+ const shell = useConsoleShellState();
92
+ const collapsed = collapsedProp ?? shell?.collapsed ?? false;
93
+ const reducedMotion = usePrefersReducedMotion();
94
+ const [storedOverrides, setStoredOverrides] = useLocalStorage(storageKey, {});
95
+ const overrides = readOverrides(storedOverrides);
96
+ const context = {
97
+ onNavigate,
98
+ renderLink
99
+ };
100
+ const activeHref = resolveActiveHref(pathname, [...items, ...collectNavItems(groups)]);
101
+ const activeBranch = findActiveBranch(pathname, groups, activeHref);
102
+ const [trackedPathname, setTrackedPathname] = useState(pathname);
103
+ if (trackedPathname !== pathname) {
104
+ setTrackedPathname(pathname);
105
+ if (activeBranch.some((key) => overrides[key] === false)) {
106
+ const next = { ...overrides };
107
+ for (const key of activeBranch) delete next[key];
108
+ setStoredOverrides(next);
109
+ }
110
+ }
111
+ const isExpanded = (group) => overrides[group.key] ?? (activeBranch.includes(group.key) || (group.defaultExpanded ?? defaultExpanded === "all"));
112
+ const toggleGroup = useCallback((key, expanded) => {
113
+ setStoredOverrides((current) => ({
114
+ ...readOverrides(current),
115
+ [key]: !expanded
116
+ }));
117
+ }, [setStoredOverrides]);
118
+ useEffect(() => {
119
+ const container = navRef.current;
120
+ if (!container) return;
121
+ scrollActiveIntoView(container);
122
+ if (reducedMotion) return;
123
+ const timer = window.setTimeout(() => scrollActiveIntoView(container), PANEL_TRANSITION_MS);
124
+ return () => window.clearTimeout(timer);
125
+ }, [
126
+ activeHref,
127
+ collapsed,
128
+ pathname,
129
+ reducedMotion
130
+ ]);
131
+ const renderItems = (list, indent, groupLabel) => list.map((item) => /* @__PURE__ */ jsx(NavLink, {
132
+ active: item.href === activeHref,
133
+ collapsed: false,
134
+ context,
135
+ href: item.href,
136
+ icon: item.icon,
137
+ indent: item.icon ? Math.max(0, indent - 1) : indent,
138
+ item,
139
+ name: groupLabel ? `${groupLabel} / ${item.label}` : item.label
140
+ }, item.href));
141
+ if (collapsed) {
142
+ const railItems = (list, groupLabel) => list.filter((item) => item.icon).map((item) => /* @__PURE__ */ jsx(NavLink, {
143
+ collapsed: true,
144
+ active: item.href === activeHref,
145
+ context,
146
+ href: item.href,
147
+ icon: item.icon,
148
+ item,
149
+ name: groupLabel ? `${groupLabel} / ${item.label}` : item.label
150
+ }, item.href));
151
+ const topRail = railItems(items);
152
+ const iconGroups = groups.filter((group) => group.icon);
153
+ return /* @__PURE__ */ jsxs(ScrollShadow, {
154
+ "aria-label": label,
155
+ as: "nav",
156
+ className: styles.nav,
157
+ "data-collapsed": "",
158
+ orientation: "vertical",
159
+ ref: navRef,
160
+ size: 8,
161
+ children: [
162
+ topRail.length > 0 ? /* @__PURE__ */ jsx("div", {
163
+ className: styles.railTop,
164
+ children: topRail
165
+ }) : null,
166
+ iconGroups.length > 0 ? /* @__PURE__ */ jsx("div", {
167
+ className: styles.railGroup,
168
+ "data-first": topRail.length === 0,
169
+ role: "group",
170
+ children: iconGroups.map((group) => {
171
+ const href = group.href ?? collectNavItems([group])[0]?.href;
172
+ if (!href) return null;
173
+ return /* @__PURE__ */ jsx(NavLink, {
174
+ collapsed: true,
175
+ active: activeBranch[0] === group.key,
176
+ context,
177
+ href,
178
+ icon: group.icon,
179
+ name: group.label
180
+ }, group.key);
181
+ })
182
+ }) : null,
183
+ groups.filter((group) => !group.icon).map((group, index) => {
184
+ const rail = railItems(collectNavItems([group]), group.label);
185
+ if (rail.length === 0) return null;
186
+ return /* @__PURE__ */ jsxs("div", {
187
+ className: styles.railGroup,
188
+ "data-first": topRail.length === 0 && iconGroups.length === 0 && index === 0,
189
+ role: "group",
190
+ children: [/* @__PURE__ */ jsx("h2", {
191
+ className: styles.srOnly,
192
+ children: group.label
193
+ }), rail]
194
+ }, group.key);
195
+ })
196
+ ]
197
+ });
198
+ }
199
+ const renderGroup = (group, level, indent) => {
200
+ const expanded = isExpanded(group);
201
+ const panelId = `${id}-${group.key}`;
202
+ const childIndent = indent + (group.icon ? 1 : 0);
203
+ const active = activeBranch.includes(group.key);
204
+ return /* @__PURE__ */ jsxs("div", {
205
+ className: styles.group,
206
+ "data-icon": Boolean(group.icon),
207
+ "data-level": level,
208
+ children: [/* @__PURE__ */ jsxs("button", {
209
+ "aria-controls": panelId,
210
+ "aria-expanded": expanded,
211
+ className: styles.groupHeader,
212
+ "data-active": active,
213
+ "data-icon": Boolean(group.icon),
214
+ "data-indent": indent,
215
+ "data-level": level,
216
+ type: "button",
217
+ onClick: () => toggleGroup(group.key, expanded),
218
+ children: [
219
+ group.icon ? /* @__PURE__ */ jsx(Icon, {
220
+ icon: group.icon,
221
+ size: 16
222
+ }) : null,
223
+ /* @__PURE__ */ jsx("span", {
224
+ className: styles.itemLabel,
225
+ "data-label": "",
226
+ children: group.label
227
+ }),
228
+ level === 0 ? /* @__PURE__ */ jsx(Icon, {
229
+ className: styles.chevron,
230
+ "data-expanded": expanded,
231
+ icon: ChevronDown,
232
+ size: 14
233
+ }) : /* @__PURE__ */ jsx("span", {
234
+ className: styles.indicator,
235
+ "data-expanded": expanded,
236
+ children: /* @__PURE__ */ jsx(Play, {
237
+ fill: "currentColor",
238
+ size: 7,
239
+ strokeWidth: 1
240
+ })
241
+ })
242
+ ]
243
+ }), /* @__PURE__ */ jsx("div", {
244
+ "aria-hidden": !expanded,
245
+ className: styles.groupPanel,
246
+ "data-expanded": expanded,
247
+ "data-instant": reducedMotion,
248
+ id: panelId,
249
+ inert: expanded ? void 0 : true,
250
+ children: /* @__PURE__ */ jsxs("div", {
251
+ className: styles.groupItems,
252
+ children: [renderItems(group.items ?? [], childIndent, group.label), (group.groups ?? []).map((child) => renderGroup(child, level + 1, childIndent))]
253
+ })
254
+ })]
255
+ }, group.key);
256
+ };
257
+ return /* @__PURE__ */ jsxs(ScrollShadow, {
258
+ "aria-label": label,
259
+ as: "nav",
260
+ className: styles.nav,
261
+ orientation: "vertical",
262
+ ref: navRef,
263
+ size: 4,
264
+ children: [items.length > 0 ? /* @__PURE__ */ jsx("div", {
265
+ className: styles.topItems,
266
+ "data-divided": groups.length > 0,
267
+ children: renderItems(items, 0)
268
+ }) : null, groups.map((group) => renderGroup(group, 0, 0))]
269
+ });
270
+ }
271
+ ConsoleNav.displayName = "ConsoleNav";
272
+ //#endregion
273
+ export { ConsoleNav as default };
274
+
275
+ //# sourceMappingURL=ConsoleNav.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConsoleNav.mjs","names":[],"sources":["../../../src/dashboard/ConsoleNav/ConsoleNav.tsx"],"sourcesContent":["'use client';\n\nimport { ChevronDown, Play } from 'lucide-react';\nimport { useCallback, useEffect, useId, useRef, useState } from 'react';\n\nimport Tooltip from '@/base-ui/Tooltip';\nimport Icon from '@/Icon';\nimport ScrollShadow from '@/ScrollShadow';\n\nimport { useConsoleShellState } from '../ConsoleShell/context';\nimport { useLocalStorage, usePrefersReducedMotion } from '../hooks';\nimport { collectNavItems, findActiveBranch, resolveActiveHref } from './active';\nimport { styles } from './style';\nimport type { ConsoleNavGroup, ConsoleNavItem, ConsoleNavLinkProps, ConsoleNavProps } from './type';\n\nconst PANEL_TRANSITION_MS = 160;\n\ntype ExpandedOverrides = Record<string, boolean>;\n\n/** Older builds stored the closed group keys as an array. */\nfunction readOverrides(value: unknown): ExpandedOverrides {\n if (Array.isArray(value)) {\n return Object.fromEntries(value.map((key) => [String(key), false]));\n }\n return value && typeof value === 'object' ? (value as ExpandedOverrides) : {};\n}\n\nfunction scrollActiveIntoView(container: HTMLElement) {\n const link =\n container.querySelector<HTMLElement>('[aria-current=\"page\"]') ??\n [...container.querySelectorAll<HTMLElement>('button[data-active=\"true\"]')].at(-1);\n if (!link) return;\n const containerRect = container.getBoundingClientRect();\n const linkRect = link.getBoundingClientRect();\n if (linkRect.top >= containerRect.top && linkRect.bottom <= containerRect.bottom) return;\n container.scrollTop +=\n linkRect.top - containerRect.top - (containerRect.height - linkRect.height) / 2;\n}\n\ninterface LinkContext {\n onNavigate?: (href: string) => void;\n renderLink?: ConsoleNavProps['renderLink'];\n}\n\nfunction NavLink({\n active,\n collapsed,\n context,\n href,\n icon,\n indent = 0,\n item,\n name,\n}: {\n active: boolean;\n collapsed: boolean;\n context: LinkContext;\n href: string;\n icon?: ConsoleNavItem['icon'];\n indent?: number;\n item?: ConsoleNavItem;\n name: string;\n}) {\n const shell = useConsoleShellState();\n const label = item?.label ?? name;\n const follow = () => {\n context.onNavigate?.(href);\n shell?.closeNavigation();\n };\n const linkProps: ConsoleNavLinkProps = {\n 'aria-current': active ? 'page' : undefined,\n 'aria-label': collapsed ? name : undefined,\n 'children': (\n <>\n {icon ? <Icon icon={icon} size={18} /> : null}\n {collapsed ? null : <span className={styles.itemLabel}>{label}</span>}\n {!collapsed && item?.badge ? <span className={styles.badge}>{item.badge}</span> : null}\n {collapsed && item?.badge ? <span aria-hidden className={styles.dot} /> : null}\n </>\n ),\n 'className': styles.item,\n 'data-active': active,\n 'data-collapsed': collapsed,\n 'data-indent': indent,\n 'external': item?.external,\n href,\n 'onClick': follow,\n 'title': collapsed ? undefined : label,\n };\n\n const link = context.renderLink ? (\n context.renderLink(linkProps)\n ) : (\n <a\n aria-current={linkProps['aria-current']}\n aria-label={linkProps['aria-label']}\n className={linkProps.className}\n data-active={active}\n data-collapsed={collapsed}\n data-indent={indent}\n href={href}\n rel={item?.external ? 'noreferrer' : undefined}\n target={item?.external ? '_blank' : undefined}\n title={linkProps.title}\n onClick={(event) => {\n if (context.onNavigate && !item?.external) event.preventDefault();\n follow();\n }}\n >\n {linkProps.children}\n </a>\n );\n\n if (!collapsed) return link;\n return (\n <Tooltip placement=\"right\" title={name}>\n {link}\n </Tooltip>\n );\n}\n\nfunction ConsoleNav({\n collapsed: collapsedProp,\n defaultExpanded = 'all',\n groups = [],\n items = [],\n label = 'Primary',\n onNavigate,\n pathname,\n renderLink,\n storageKey,\n}: ConsoleNavProps) {\n const id = useId();\n const navRef = useRef<HTMLDivElement>(null);\n const shell = useConsoleShellState();\n const collapsed = collapsedProp ?? shell?.collapsed ?? false;\n const reducedMotion = usePrefersReducedMotion();\n const [storedOverrides, setStoredOverrides] = useLocalStorage<unknown>(storageKey, {});\n const overrides = readOverrides(storedOverrides);\n const context: LinkContext = { onNavigate, renderLink };\n\n const activeHref = resolveActiveHref(pathname, [...items, ...collectNavItems(groups)]);\n const activeBranch = findActiveBranch(pathname, groups, activeHref);\n\n // Arriving on a page reopens its branch even if it was closed by hand earlier.\n const [trackedPathname, setTrackedPathname] = useState(pathname);\n if (trackedPathname !== pathname) {\n setTrackedPathname(pathname);\n if (activeBranch.some((key) => overrides[key] === false)) {\n const next = { ...overrides };\n for (const key of activeBranch) delete next[key];\n setStoredOverrides(next);\n }\n }\n\n const isExpanded = (group: ConsoleNavGroup) =>\n overrides[group.key] ??\n (activeBranch.includes(group.key) || (group.defaultExpanded ?? defaultExpanded === 'all'));\n\n const toggleGroup = useCallback(\n (key: string, expanded: boolean) => {\n setStoredOverrides((current: unknown) => ({ ...readOverrides(current), [key]: !expanded }));\n },\n [setStoredOverrides],\n );\n\n useEffect(() => {\n const container = navRef.current;\n if (!container) return;\n scrollActiveIntoView(container);\n if (reducedMotion) return;\n const timer = window.setTimeout(() => scrollActiveIntoView(container), PANEL_TRANSITION_MS);\n return () => window.clearTimeout(timer);\n }, [activeHref, collapsed, pathname, reducedMotion]);\n\n const renderItems = (list: ConsoleNavItem[], indent: number, groupLabel?: string) =>\n list.map((item) => (\n <NavLink\n active={item.href === activeHref}\n collapsed={false}\n context={context}\n href={item.href}\n icon={item.icon}\n indent={item.icon ? Math.max(0, indent - 1) : indent}\n item={item}\n key={item.href}\n name={groupLabel ? `${groupLabel} / ${item.label}` : item.label}\n />\n ));\n\n if (collapsed) {\n const railItems = (list: ConsoleNavItem[], groupLabel?: string) =>\n list\n .filter((item) => item.icon)\n .map((item) => (\n <NavLink\n collapsed\n active={item.href === activeHref}\n context={context}\n href={item.href}\n icon={item.icon}\n item={item}\n key={item.href}\n name={groupLabel ? `${groupLabel} / ${item.label}` : item.label}\n />\n ));\n const topRail = railItems(items);\n const iconGroups = groups.filter((group) => group.icon);\n\n return (\n <ScrollShadow\n aria-label={label}\n as={'nav'}\n className={styles.nav}\n data-collapsed=\"\"\n orientation={'vertical'}\n ref={navRef}\n size={8}\n >\n {topRail.length > 0 ? <div className={styles.railTop}>{topRail}</div> : null}\n {iconGroups.length > 0 ? (\n <div className={styles.railGroup} data-first={topRail.length === 0} role=\"group\">\n {iconGroups.map((group) => {\n const href = group.href ?? collectNavItems([group])[0]?.href;\n if (!href) return null;\n return (\n <NavLink\n collapsed\n active={activeBranch[0] === group.key}\n context={context}\n href={href}\n icon={group.icon}\n key={group.key}\n name={group.label}\n />\n );\n })}\n </div>\n ) : null}\n {groups\n .filter((group) => !group.icon)\n .map((group, index) => {\n const rail = railItems(collectNavItems([group]), group.label);\n if (rail.length === 0) return null;\n return (\n <div\n className={styles.railGroup}\n data-first={topRail.length === 0 && iconGroups.length === 0 && index === 0}\n key={group.key}\n role=\"group\"\n >\n <h2 className={styles.srOnly}>{group.label}</h2>\n {rail}\n </div>\n );\n })}\n </ScrollShadow>\n );\n }\n\n const renderGroup = (group: ConsoleNavGroup, level: number, indent: number) => {\n const expanded = isExpanded(group);\n const panelId = `${id}-${group.key}`;\n // Nested groups are headings over their items, so the items keep the same indent.\n const childIndent = indent + (group.icon ? 1 : 0);\n const active = activeBranch.includes(group.key);\n\n return (\n <div\n className={styles.group}\n data-icon={Boolean(group.icon)}\n data-level={level}\n key={group.key}\n >\n <button\n aria-controls={panelId}\n aria-expanded={expanded}\n className={styles.groupHeader}\n data-active={active}\n data-icon={Boolean(group.icon)}\n data-indent={indent}\n data-level={level}\n type=\"button\"\n onClick={() => toggleGroup(group.key, expanded)}\n >\n {group.icon ? <Icon icon={group.icon} size={16} /> : null}\n <span className={styles.itemLabel} data-label=\"\">\n {group.label}\n </span>\n {level === 0 ? (\n <Icon\n className={styles.chevron}\n data-expanded={expanded}\n icon={ChevronDown}\n size={14}\n />\n ) : (\n <span className={styles.indicator} data-expanded={expanded}>\n <Play fill={'currentColor'} size={7} strokeWidth={1} />\n </span>\n )}\n </button>\n <div\n aria-hidden={!expanded}\n className={styles.groupPanel}\n data-expanded={expanded}\n data-instant={reducedMotion}\n id={panelId}\n inert={expanded ? undefined : true}\n >\n <div className={styles.groupItems}>\n {renderItems(group.items ?? [], childIndent, group.label)}\n {(group.groups ?? []).map((child) => renderGroup(child, level + 1, childIndent))}\n </div>\n </div>\n </div>\n );\n };\n\n return (\n <ScrollShadow\n aria-label={label}\n as={'nav'}\n className={styles.nav}\n orientation={'vertical'}\n ref={navRef}\n size={4}\n >\n {items.length > 0 ? (\n <div className={styles.topItems} data-divided={groups.length > 0}>\n {renderItems(items, 0)}\n </div>\n ) : null}\n {groups.map((group) => renderGroup(group, 0, 0))}\n </ScrollShadow>\n );\n}\n\nConsoleNav.displayName = 'ConsoleNav';\n\nexport default ConsoleNav;\n"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { ConsoleNavItem } from "./type.mjs";
2
+ //#region src/dashboard/ConsoleNav/active.d.ts
3
+ /** Longest matching href wins, so `/resources/new` does not also light up `/`. */
4
+ declare function resolveActiveHref(pathname: string, items: readonly ConsoleNavItem[]): string;
5
+ //#endregion
6
+ export { resolveActiveHref };
7
+ //# sourceMappingURL=active.d.mts.map
@@ -0,0 +1,29 @@
1
+ //#region src/dashboard/ConsoleNav/active.ts
2
+ function matchesNavPath(pathname, item) {
3
+ if (item.end || item.href === "/") return pathname === item.href;
4
+ return pathname === item.href || pathname.startsWith(`${item.href}/`);
5
+ }
6
+ /** Longest matching href wins, so `/resources/new` does not also light up `/`. */
7
+ function resolveActiveHref(pathname, items) {
8
+ return items.filter((item) => matchesNavPath(pathname, item)).sort((left, right) => right.href.length - left.href.length)[0]?.href;
9
+ }
10
+ function collectNavItems(groups) {
11
+ return groups.flatMap((group) => [...group.items ?? [], ...collectNavItems(group.groups ?? [])]);
12
+ }
13
+ /**
14
+ * Keys of the groups on the path to the active page, outermost first. A group
15
+ * overview page (`href`) counts as being inside that group.
16
+ */
17
+ function findActiveBranch(pathname, groups, activeHref) {
18
+ for (const group of groups) {
19
+ if (group.href && pathname === group.href) return [group.key];
20
+ const nested = findActiveBranch(pathname, group.groups ?? [], activeHref);
21
+ if (nested.length > 0) return [group.key, ...nested];
22
+ if (activeHref && group.items?.some((item) => item.href === activeHref)) return [group.key];
23
+ }
24
+ return [];
25
+ }
26
+ //#endregion
27
+ export { collectNavItems, findActiveBranch, matchesNavPath, resolveActiveHref };
28
+
29
+ //# sourceMappingURL=active.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"active.mjs","names":[],"sources":["../../../src/dashboard/ConsoleNav/active.ts"],"sourcesContent":["import type { ConsoleNavGroup, ConsoleNavItem } from './type';\n\nexport function matchesNavPath(pathname: string, item: Pick<ConsoleNavItem, 'end' | 'href'>) {\n if (item.end || item.href === '/') return pathname === item.href;\n return pathname === item.href || pathname.startsWith(`${item.href}/`);\n}\n\n/** Longest matching href wins, so `/resources/new` does not also light up `/`. */\nexport function resolveActiveHref(pathname: string, items: readonly ConsoleNavItem[]) {\n return items\n .filter((item) => matchesNavPath(pathname, item))\n .sort((left, right) => right.href.length - left.href.length)[0]?.href;\n}\n\nexport function collectNavItems(groups: readonly ConsoleNavGroup[]): ConsoleNavItem[] {\n return groups.flatMap((group) => [\n ...(group.items ?? []),\n ...collectNavItems(group.groups ?? []),\n ]);\n}\n\n/**\n * Keys of the groups on the path to the active page, outermost first. A group\n * overview page (`href`) counts as being inside that group.\n */\nexport function findActiveBranch(\n pathname: string,\n groups: readonly ConsoleNavGroup[],\n activeHref: string | undefined,\n): string[] {\n for (const group of groups) {\n if (group.href && pathname === group.href) return [group.key];\n const nested = findActiveBranch(pathname, group.groups ?? [], activeHref);\n if (nested.length > 0) return [group.key, ...nested];\n if (activeHref && group.items?.some((item) => item.href === activeHref)) return [group.key];\n }\n return [];\n}\n"],"mappings":";AAEA,SAAgB,eAAe,UAAkB,MAA4C;CAC3F,IAAI,KAAK,OAAO,KAAK,SAAS,KAAK,OAAO,aAAa,KAAK;CAC5D,OAAO,aAAa,KAAK,QAAQ,SAAS,WAAW,GAAG,KAAK,KAAK,EAAE;AACtE;;AAGA,SAAgB,kBAAkB,UAAkB,OAAkC;CACpF,OAAO,MACJ,QAAQ,SAAS,eAAe,UAAU,IAAI,CAAC,CAAC,CAChD,MAAM,MAAM,UAAU,MAAM,KAAK,SAAS,KAAK,KAAK,MAAM,CAAC,CAAC,EAAE,EAAE;AACrE;AAEA,SAAgB,gBAAgB,QAAsD;CACpF,OAAO,OAAO,SAAS,UAAU,CAC/B,GAAI,MAAM,SAAS,CAAC,GACpB,GAAG,gBAAgB,MAAM,UAAU,CAAC,CAAC,CACvC,CAAC;AACH;;;;;AAMA,SAAgB,iBACd,UACA,QACA,YACU;CACV,KAAK,MAAM,SAAS,QAAQ;EAC1B,IAAI,MAAM,QAAQ,aAAa,MAAM,MAAM,OAAO,CAAC,MAAM,GAAG;EAC5D,MAAM,SAAS,iBAAiB,UAAU,MAAM,UAAU,CAAC,GAAG,UAAU;EACxE,IAAI,OAAO,SAAS,GAAG,OAAO,CAAC,MAAM,KAAK,GAAG,MAAM;EACnD,IAAI,cAAc,MAAM,OAAO,MAAM,SAAS,KAAK,SAAS,UAAU,GAAG,OAAO,CAAC,MAAM,GAAG;CAC5F;CACA,OAAO,CAAC;AACV"}
@@ -0,0 +1,4 @@
1
+ import { ConsoleNavGroup, ConsoleNavItem, ConsoleNavLinkProps, ConsoleNavProps } from "./type.mjs";
2
+ import { resolveActiveHref } from "./active.mjs";
3
+ import ConsoleNav from "./ConsoleNav.mjs";
4
+ export { type ConsoleNavGroup, type ConsoleNavItem, type ConsoleNavProps, ConsoleNav as default, resolveActiveHref };
@@ -0,0 +1,283 @@
1
+ import { createStaticStyles } from "antd-style";
2
+ //#region src/dashboard/ConsoleNav/style.ts
3
+ const styles = createStaticStyles(({ css, cssVar, responsive }) => ({
4
+ badge: css`
5
+ flex: none;
6
+
7
+ min-inline-size: 20px;
8
+ padding-inline: 6px;
9
+ border-radius: 999px;
10
+
11
+ font-size: ${cssVar.fontSizeSM};
12
+ font-variant-numeric: tabular-nums;
13
+ color: ${cssVar.colorError};
14
+ text-align: center;
15
+
16
+ background: color-mix(in srgb, ${cssVar.colorError} 12%, transparent);
17
+ `,
18
+ chevron: css`
19
+ flex: none;
20
+ transition: transform 160ms ease;
21
+
22
+ &[data-expanded='false'] {
23
+ transform: rotate(-90deg);
24
+ }
25
+ `,
26
+ indicator: css`
27
+ display: flex;
28
+ flex: none;
29
+ align-items: center;
30
+ justify-content: center;
31
+
32
+ width: 18px;
33
+ height: 18px;
34
+ margin-inline-start: -6px;
35
+
36
+ color: ${cssVar.colorTextDescription};
37
+
38
+ transition: transform 160ms ease;
39
+
40
+ &[data-expanded='true'] {
41
+ transform: rotate(90deg);
42
+ }
43
+ `,
44
+ dot: css`
45
+ position: absolute;
46
+ inset-block-start: 7px;
47
+ inset-inline-end: 9px;
48
+
49
+ inline-size: 7px;
50
+ block-size: 7px;
51
+ border-radius: 999px;
52
+
53
+ background: ${cssVar.colorError};
54
+ `,
55
+ group: css`
56
+ display: flex;
57
+ flex-direction: column;
58
+ margin-block-start: 12px;
59
+
60
+ &[data-level='0'][data-icon='true'] {
61
+ margin-block-start: 2px;
62
+ }
63
+
64
+ &:not([data-level='0']) {
65
+ margin-block-start: 6px;
66
+ }
67
+ `,
68
+ groupHeader: css`
69
+ cursor: pointer;
70
+
71
+ display: flex;
72
+ gap: 8px;
73
+ align-items: center;
74
+ justify-content: space-between;
75
+
76
+ min-block-size: 32px;
77
+ padding-inline: 10px;
78
+ border: none;
79
+ border-radius: ${cssVar.borderRadius};
80
+
81
+ font-size: ${cssVar.fontSizeSM};
82
+ font-weight: 500;
83
+ color: ${cssVar.colorTextSecondary};
84
+ text-align: start;
85
+
86
+ background: none;
87
+
88
+ &:hover,
89
+ &[data-active='true'] {
90
+ color: ${cssVar.colorText};
91
+ }
92
+
93
+ &[data-icon='true'] {
94
+ gap: 12px;
95
+ justify-content: flex-start;
96
+ min-block-size: 36px;
97
+ font-size: ${cssVar.fontSize};
98
+ }
99
+
100
+ &[data-level='0'][data-icon='true']:hover {
101
+ background: ${cssVar.colorFillTertiary};
102
+ }
103
+
104
+ /* Nested groups read as quiet subheadings with the indicator right after the label. */
105
+ &:not([data-level='0']) {
106
+ gap: 8px;
107
+ justify-content: flex-start;
108
+
109
+ min-block-size: 26px;
110
+
111
+ font-size: ${cssVar.fontSizeSM};
112
+ font-weight: 400;
113
+ color: ${cssVar.colorTextDescription};
114
+
115
+ > [data-label] {
116
+ flex: 0 1 auto;
117
+ }
118
+
119
+ &:hover,
120
+ &[data-active='true'] {
121
+ color: ${cssVar.colorTextSecondary};
122
+ }
123
+ }
124
+
125
+ &[data-indent='1'] {
126
+ padding-inline-start: 38px;
127
+ }
128
+
129
+ &[data-indent='2'] {
130
+ padding-inline-start: 56px;
131
+ }
132
+
133
+ > [role='img'] {
134
+ display: flex;
135
+ flex: none;
136
+ }
137
+ `,
138
+ groupItems: css`
139
+ display: flex;
140
+ flex-direction: column;
141
+ gap: 2px;
142
+ min-block-size: 0;
143
+ `,
144
+ groupPanel: css`
145
+ display: grid;
146
+ grid-template-rows: 1fr;
147
+ transition: grid-template-rows 160ms ease;
148
+
149
+ > div {
150
+ overflow: hidden;
151
+ }
152
+
153
+ &[data-expanded='false'] {
154
+ grid-template-rows: 0fr;
155
+ }
156
+
157
+ &[data-instant='true'] {
158
+ transition: none;
159
+ }
160
+ `,
161
+ item: css`
162
+ position: relative;
163
+
164
+ display: flex;
165
+ gap: 10px;
166
+ align-items: center;
167
+
168
+ min-block-size: 36px;
169
+ padding-inline: 10px;
170
+ border-radius: ${cssVar.borderRadius};
171
+
172
+ color: ${cssVar.colorTextSecondary};
173
+ text-decoration: none;
174
+
175
+ transition:
176
+ background 120ms ease,
177
+ color 120ms ease;
178
+
179
+ &:hover {
180
+ color: ${cssVar.colorText};
181
+ background: ${cssVar.colorFillTertiary};
182
+ }
183
+
184
+ &[data-active='true'] {
185
+ font-weight: 500;
186
+ color: ${cssVar.colorText};
187
+ background: ${cssVar.colorFillSecondary};
188
+ }
189
+
190
+ &[data-indent='1'],
191
+ &[data-indent='2'] {
192
+ min-block-size: 32px;
193
+ color: ${cssVar.colorText};
194
+ }
195
+
196
+ &[data-indent='1'] {
197
+ padding-inline-start: 38px;
198
+ }
199
+
200
+ &[data-indent='2'] {
201
+ padding-inline-start: 56px;
202
+ }
203
+
204
+ > [role='img'] {
205
+ display: flex;
206
+ flex: none;
207
+ }
208
+
209
+ &[data-collapsed='true'] {
210
+ justify-content: center;
211
+ min-block-size: 40px;
212
+ padding-inline: 0;
213
+ }
214
+
215
+ ${responsive.mobile} {
216
+ min-block-size: 44px;
217
+ }
218
+ `,
219
+ itemLabel: css`
220
+ overflow: hidden;
221
+ flex: 1;
222
+ text-overflow: ellipsis;
223
+ white-space: nowrap;
224
+ `,
225
+ nav: css`
226
+ scrollbar-width: thin;
227
+
228
+ display: flex;
229
+ flex: 1;
230
+ flex-direction: column;
231
+ gap: 2px;
232
+
233
+ min-block-size: 0;
234
+ padding-block-end: 12px;
235
+ padding-inline: 8px;
236
+ `,
237
+ railGroup: css`
238
+ display: flex;
239
+ flex-direction: column;
240
+ gap: 2px;
241
+
242
+ margin-block-start: 6px;
243
+ padding-block-start: 6px;
244
+ border-block-start: 1px solid ${cssVar.colorBorderSecondary};
245
+
246
+ &[data-first='true'] {
247
+ margin-block-start: 0;
248
+ border-block-start: none;
249
+ }
250
+ `,
251
+ railTop: css`
252
+ display: flex;
253
+ flex-direction: column;
254
+ gap: 2px;
255
+ `,
256
+ topItems: css`
257
+ display: flex;
258
+ flex-direction: column;
259
+ gap: 2px;
260
+
261
+ &[data-divided='true'] {
262
+ margin-block-end: 6px;
263
+ padding-block-end: 8px;
264
+ border-block-end: 1px solid ${cssVar.colorBorderSecondary};
265
+ }
266
+ `,
267
+ srOnly: css`
268
+ position: absolute;
269
+
270
+ overflow: hidden;
271
+
272
+ inline-size: 1px;
273
+ block-size: 1px;
274
+
275
+ white-space: nowrap;
276
+
277
+ clip-path: inset(50%);
278
+ `
279
+ }));
280
+ //#endregion
281
+ export { styles };
282
+
283
+ //# sourceMappingURL=style.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style.mjs","names":[],"sources":["../../../src/dashboard/ConsoleNav/style.ts"],"sourcesContent":["import { createStaticStyles } from 'antd-style';\n\nexport const styles = createStaticStyles(({ css, cssVar, responsive }) => ({\n badge: css`\n flex: none;\n\n min-inline-size: 20px;\n padding-inline: 6px;\n border-radius: 999px;\n\n font-size: ${cssVar.fontSizeSM};\n font-variant-numeric: tabular-nums;\n color: ${cssVar.colorError};\n text-align: center;\n\n background: color-mix(in srgb, ${cssVar.colorError} 12%, transparent);\n `,\n chevron: css`\n flex: none;\n transition: transform 160ms ease;\n\n &[data-expanded='false'] {\n transform: rotate(-90deg);\n }\n `,\n indicator: css`\n display: flex;\n flex: none;\n align-items: center;\n justify-content: center;\n\n width: 18px;\n height: 18px;\n margin-inline-start: -6px;\n\n color: ${cssVar.colorTextDescription};\n\n transition: transform 160ms ease;\n\n &[data-expanded='true'] {\n transform: rotate(90deg);\n }\n `,\n dot: css`\n position: absolute;\n inset-block-start: 7px;\n inset-inline-end: 9px;\n\n inline-size: 7px;\n block-size: 7px;\n border-radius: 999px;\n\n background: ${cssVar.colorError};\n `,\n group: css`\n display: flex;\n flex-direction: column;\n margin-block-start: 12px;\n\n &[data-level='0'][data-icon='true'] {\n margin-block-start: 2px;\n }\n\n &:not([data-level='0']) {\n margin-block-start: 6px;\n }\n `,\n groupHeader: css`\n cursor: pointer;\n\n display: flex;\n gap: 8px;\n align-items: center;\n justify-content: space-between;\n\n min-block-size: 32px;\n padding-inline: 10px;\n border: none;\n border-radius: ${cssVar.borderRadius};\n\n font-size: ${cssVar.fontSizeSM};\n font-weight: 500;\n color: ${cssVar.colorTextSecondary};\n text-align: start;\n\n background: none;\n\n &:hover,\n &[data-active='true'] {\n color: ${cssVar.colorText};\n }\n\n &[data-icon='true'] {\n gap: 12px;\n justify-content: flex-start;\n min-block-size: 36px;\n font-size: ${cssVar.fontSize};\n }\n\n &[data-level='0'][data-icon='true']:hover {\n background: ${cssVar.colorFillTertiary};\n }\n\n /* Nested groups read as quiet subheadings with the indicator right after the label. */\n &:not([data-level='0']) {\n gap: 8px;\n justify-content: flex-start;\n\n min-block-size: 26px;\n\n font-size: ${cssVar.fontSizeSM};\n font-weight: 400;\n color: ${cssVar.colorTextDescription};\n\n > [data-label] {\n flex: 0 1 auto;\n }\n\n &:hover,\n &[data-active='true'] {\n color: ${cssVar.colorTextSecondary};\n }\n }\n\n &[data-indent='1'] {\n padding-inline-start: 38px;\n }\n\n &[data-indent='2'] {\n padding-inline-start: 56px;\n }\n\n > [role='img'] {\n display: flex;\n flex: none;\n }\n `,\n groupItems: css`\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-block-size: 0;\n `,\n groupPanel: css`\n display: grid;\n grid-template-rows: 1fr;\n transition: grid-template-rows 160ms ease;\n\n > div {\n overflow: hidden;\n }\n\n &[data-expanded='false'] {\n grid-template-rows: 0fr;\n }\n\n &[data-instant='true'] {\n transition: none;\n }\n `,\n item: css`\n position: relative;\n\n display: flex;\n gap: 10px;\n align-items: center;\n\n min-block-size: 36px;\n padding-inline: 10px;\n border-radius: ${cssVar.borderRadius};\n\n color: ${cssVar.colorTextSecondary};\n text-decoration: none;\n\n transition:\n background 120ms ease,\n color 120ms ease;\n\n &:hover {\n color: ${cssVar.colorText};\n background: ${cssVar.colorFillTertiary};\n }\n\n &[data-active='true'] {\n font-weight: 500;\n color: ${cssVar.colorText};\n background: ${cssVar.colorFillSecondary};\n }\n\n &[data-indent='1'],\n &[data-indent='2'] {\n min-block-size: 32px;\n color: ${cssVar.colorText};\n }\n\n &[data-indent='1'] {\n padding-inline-start: 38px;\n }\n\n &[data-indent='2'] {\n padding-inline-start: 56px;\n }\n\n > [role='img'] {\n display: flex;\n flex: none;\n }\n\n &[data-collapsed='true'] {\n justify-content: center;\n min-block-size: 40px;\n padding-inline: 0;\n }\n\n ${responsive.mobile} {\n min-block-size: 44px;\n }\n `,\n itemLabel: css`\n overflow: hidden;\n flex: 1;\n text-overflow: ellipsis;\n white-space: nowrap;\n `,\n nav: css`\n scrollbar-width: thin;\n\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: 2px;\n\n min-block-size: 0;\n padding-block-end: 12px;\n padding-inline: 8px;\n `,\n railGroup: css`\n display: flex;\n flex-direction: column;\n gap: 2px;\n\n margin-block-start: 6px;\n padding-block-start: 6px;\n border-block-start: 1px solid ${cssVar.colorBorderSecondary};\n\n &[data-first='true'] {\n margin-block-start: 0;\n border-block-start: none;\n }\n `,\n railTop: css`\n display: flex;\n flex-direction: column;\n gap: 2px;\n `,\n topItems: css`\n display: flex;\n flex-direction: column;\n gap: 2px;\n\n &[data-divided='true'] {\n margin-block-end: 6px;\n padding-block-end: 8px;\n border-block-end: 1px solid ${cssVar.colorBorderSecondary};\n }\n `,\n srOnly: css`\n position: absolute;\n\n overflow: hidden;\n\n inline-size: 1px;\n block-size: 1px;\n\n white-space: nowrap;\n\n clip-path: inset(50%);\n `,\n}));\n"],"mappings":";;AAEA,MAAa,SAAS,oBAAoB,EAAE,KAAK,QAAQ,kBAAkB;CACzE,OAAO,GAAG;;;;;;;iBAOK,OAAO,WAAW;;aAEtB,OAAO,WAAW;;;qCAGM,OAAO,WAAW;;CAErD,SAAS,GAAG;;;;;;;;CAQZ,WAAW,GAAG;;;;;;;;;;aAUH,OAAO,qBAAqB;;;;;;;;CAQvC,KAAK,GAAG;;;;;;;;;kBASQ,OAAO,WAAW;;CAElC,OAAO,GAAG;;;;;;;;;;;;;CAaV,aAAa,GAAG;;;;;;;;;;;qBAWG,OAAO,aAAa;;iBAExB,OAAO,WAAW;;aAEtB,OAAO,mBAAmB;;;;;;;eAOxB,OAAO,UAAU;;;;;;;mBAOb,OAAO,SAAS;;;;oBAIf,OAAO,kBAAkB;;;;;;;;;;mBAU1B,OAAO,WAAW;;eAEtB,OAAO,qBAAqB;;;;;;;;iBAQ1B,OAAO,mBAAmB;;;;;;;;;;;;;;;;;CAiBzC,YAAY,GAAG;;;;;;CAMf,YAAY,GAAG;;;;;;;;;;;;;;;;;CAiBf,MAAM,GAAG;;;;;;;;;qBASU,OAAO,aAAa;;aAE5B,OAAO,mBAAmB;;;;;;;;eAQxB,OAAO,UAAU;oBACZ,OAAO,kBAAkB;;;;;eAK9B,OAAO,UAAU;oBACZ,OAAO,mBAAmB;;;;;;eAM/B,OAAO,UAAU;;;;;;;;;;;;;;;;;;;;;;MAsB1B,WAAW,OAAO;;;;CAItB,WAAW,GAAG;;;;;;CAMd,KAAK,GAAG;;;;;;;;;;;;CAYR,WAAW,GAAG;;;;;;;oCAOoB,OAAO,qBAAqB;;;;;;;CAO9D,SAAS,GAAG;;;;;CAKZ,UAAU,GAAG;;;;;;;;oCAQqB,OAAO,qBAAqB;;;CAG9D,QAAQ,GAAG;;;;;;;;;;;;AAYb,EAAE"}