@datum-cloud/datum-ui 3.0.0 → 3.2.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.
@@ -1,3 +1,3 @@
1
1
  import { C as SidebarProvider, S as SidebarGroupLabel, _ as SidebarMenuSubButton, a as SidebarInput, b as SidebarGroupAction, c as SidebarSeparator, d as SidebarMenuAction, f as SidebarMenuBadge, g as SidebarMenuSub, h as SidebarMenuSkeleton, i as SidebarHeader, l as SidebarTrigger, m as SidebarMenuItem, n as SidebarContent, o as SidebarInset, p as SidebarMenuButton, r as SidebarFooter, s as SidebarRail, t as Sidebar, u as SidebarMenu, v as SidebarMenuSubItem, w as useSidebar, x as SidebarGroupContent, y as SidebarGroup } from "../sidebar-BeWEEu7P.mjs";
2
- import { a as hasActiveDescendant, i as findItemByHref, n as NavMenu, o as isNavItemActive, r as NavMenuItem, s as normalizePath, t as AppNavigation } from "../app-navigation-D52ikGN-.mjs";
2
+ import { a as hasActiveDescendant, i as findItemByHref, n as NavMenu, o as isNavItemActive, r as NavMenuItem, s as normalizePath, t as AppNavigation } from "../app-navigation-DEqa2_TC.mjs";
3
3
  export { AppNavigation, NavMenu, NavMenuItem, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, findItemByHref, hasActiveDescendant, isNavItemActive, normalizePath, useSidebar };
@@ -99,13 +99,22 @@ function useNavMenuContext() {
99
99
  * Uses type + title + href + level (not href alone) so that collapsible groups
100
100
  * with href:null can still be toggled and duplicate hrefs never collide.
101
101
  */
102
+ /**
103
+ * Depth at which an item's children render. A `group` is only a section label,
104
+ * so its children render at the group's own depth and look like top-level rows.
105
+ */
106
+ function childLevel(item, level) {
107
+ return item.type === "group" ? level : level + 1;
108
+ }
102
109
  function getNavItemKey(item, level) {
103
110
  return `${item.type}:${item.title}:${item.href ?? ""}:${level}`;
104
111
  }
105
112
  //#endregion
106
113
  //#region src/components/features/app-navigation/nav-menu/parts.tsx
107
114
  const NAV_STYLES = {
108
- menuButton: "rounded-xl h-8 font-normal text-xs transition-all px-2 py-1 data-[active=true]:bg-sidebar data-[active=true]:text-foreground data-[active=true]:text-sidebar-primary data-[active=true]:[&>svg]:text-primary hover:bg-sidebar hover:text-sidebar-primary hover:[&>svg]:text-sidebar-primary hover:bg-sidebar-accent data-[active=true]:bg-sidebar-accent hover:font-semibold data-[active=true]:hover:[&>svg]:text-sidebar-primary transition-colors duration-300 gap-2.5 text-foreground [&>svg]:text-icon-primary",
115
+ /** Side inset of every row. Drops back to px-2 so a 32px button fits the 48px icon rail. */
116
+ rowInset: "px-3 group-data-[collapsible=icon]:px-2",
117
+ menuButton: "rounded-xl h-8 font-normal text-xs transition-all px-3 py-1 data-[active=true]:bg-sidebar data-[active=true]:text-foreground data-[active=true]:text-sidebar-primary data-[active=true]:[&>svg]:text-primary hover:bg-sidebar hover:text-sidebar-primary hover:[&>svg]:text-sidebar-primary hover:bg-sidebar-accent data-[active=true]:bg-sidebar-accent hover:font-semibold data-[active=true]:hover:[&>svg]:text-sidebar-primary transition-colors duration-300 gap-2.5 text-foreground [&>svg]:text-icon-primary",
109
118
  disabled: "pointer-events-none opacity-50",
110
119
  icon: "duration-300 transition-all",
111
120
  iconSmall: "size-4 duration-300 transition-all"
@@ -157,6 +166,13 @@ const COLLAPSIBLE_CHILD_VARIANTS = {
157
166
  }
158
167
  }
159
168
  };
169
+ /**
170
+ * Nested rows already sit inside their parent's inset; adding another would push
171
+ * their labels past the parent's label instead of lining up under it.
172
+ */
173
+ function rowInsetFor(level) {
174
+ return level >= 1 ? "px-0" : NAV_STYLES.rowInset;
175
+ }
160
176
  /** Stable React key for a rendered nav item at a given depth. */
161
177
  function itemKeyOf(item, level) {
162
178
  return `${item.title}-${item.href || ""}-${level}`;
@@ -179,7 +195,8 @@ function NavBadge({ badge }) {
179
195
  * Memoized so unchanged subtrees skip re-rendering when their props are stable.
180
196
  *
181
197
  * Items with children use {@link NavCollapsibleItem}. Section headers use
182
- * {@link NavGroup} (always open, no dropdown).
198
+ * {@link NavGroup} (always open, no dropdown); their children render at the
199
+ * header's own level, so they look like top-level rows.
183
200
  */
184
201
  const NavMenuItem = memo(({ item, level = 0 }) => {
185
202
  if (item.hidden) return null;
@@ -197,32 +214,21 @@ const NavMenuItem = memo(({ item, level = 0 }) => {
197
214
  });
198
215
  });
199
216
  NavMenuItem.displayName = "NavMenuItem";
200
- /** A labelled group of nav items (always open — Vercel-style section headers). */
217
+ /**
218
+ * A labelled group of nav items (always open — Vercel-style section headers).
219
+ * The same tree renders in the icon rail with the header hidden, so the rail
220
+ * shows every item's icon and collapsing the sidebar never remounts the items.
221
+ */
201
222
  function NavGroup({ item, level }) {
202
223
  const ctx = useNavMenuContext();
203
- const { pathname, isIconRail, isMobile } = ctx;
224
+ const { pathname } = ctx;
204
225
  const hasActiveChild = item.children?.some((child) => hasActiveDescendant(child, pathname)) ?? false;
205
- if (isIconRail && !isMobile) return /* @__PURE__ */ jsxs(Fragment$1, { children: [
206
- item.showSeparatorAbove && /* @__PURE__ */ jsx(SidebarSeparator, { className: "my-1" }),
207
- /* @__PURE__ */ jsx(SidebarMenu, {
208
- className: "px-2",
209
- children: /* @__PURE__ */ jsx(NavSidebarMenuButton, {
210
- item,
211
- isActive: hasActiveChild,
212
- disableTooltip: ctx.disableTooltip,
213
- className: cn(ctx.itemClassName, hasActiveChild && "[&>svg:first-of-type]:text-primary"),
214
- activeClassName: ctx.activeItemClassName,
215
- onClick: () => ctx.setOpen(true)
216
- })
217
- }),
218
- item.showSeparatorBelow && /* @__PURE__ */ jsx(SidebarSeparator, { className: "my-2" })
219
- ] });
220
226
  return /* @__PURE__ */ jsxs(Fragment$1, { children: [
221
227
  item.showSeparatorAbove && /* @__PURE__ */ jsx(SidebarSeparator, { className: "my-2" }),
222
228
  /* @__PURE__ */ jsxs(SidebarGroup, {
223
- className: "mb-1 p-0! px-2",
229
+ className: cn("mb-1 p-0!", !item.showSeparatorAbove && "mt-3"),
224
230
  children: [item.title && /* @__PURE__ */ jsx(SidebarMenu, {
225
- className: "w-full",
231
+ className: cn("w-full group-data-[collapsible=icon]:hidden", NAV_STYLES.rowInset),
226
232
  children: /* @__PURE__ */ jsx(NavSidebarMenuButton, {
227
233
  item,
228
234
  isActive: hasActiveChild,
@@ -238,8 +244,8 @@ function NavGroup({ item, level }) {
238
244
  className: "flex flex-col gap-0.5",
239
245
  children: (item.children || []).map((child) => /* @__PURE__ */ jsx(NavMenuItem, {
240
246
  item: child,
241
- level: level + 1
242
- }, itemKeyOf(child, level + 1)))
247
+ level: childLevel(item, level)
248
+ }, itemKeyOf(child, childLevel(item, level))))
243
249
  })]
244
250
  }),
245
251
  item.showSeparatorBelow && /* @__PURE__ */ jsx(SidebarSeparator, { className: "my-2" })
@@ -263,7 +269,7 @@ function NavCollapsibleItem({ item, level }) {
263
269
  return /* @__PURE__ */ jsxs(Fragment$1, { children: [
264
270
  item.showSeparatorAbove && /* @__PURE__ */ jsx(SidebarSeparator, { className: "my-2" }),
265
271
  /* @__PURE__ */ jsx(SidebarMenu, {
266
- className: "px-2",
272
+ className: rowInsetFor(level),
267
273
  children: /* @__PURE__ */ jsx(Collapsible, {
268
274
  asChild: true,
269
275
  open: panelOpen,
@@ -347,7 +353,7 @@ function NavLeafItem({ item, level }) {
347
353
  return /* @__PURE__ */ jsxs(Fragment$1, { children: [
348
354
  item.showSeparatorAbove && /* @__PURE__ */ jsx(SidebarSeparator, { className: "my-2" }),
349
355
  /* @__PURE__ */ jsx(SidebarMenu, {
350
- className: cn(`level_${level} px-2`),
356
+ className: cn(`level_${level}`, rowInsetFor(level)),
351
357
  children: /* @__PURE__ */ jsx(SidebarMenuItem, {
352
358
  className: "[&>*:first-child]:w-full",
353
359
  children: /* @__PURE__ */ jsx(NavSidebarMenuButton, {
@@ -431,7 +437,7 @@ function NavMenu({ ref, className, items, currentPath, linkComponent: LinkComp =
431
437
  navItems.forEach((navItem) => {
432
438
  const key = getNavItemKey(navItem, level);
433
439
  if (prev[key] && hasActiveDescendant(navItem, pathname)) newOpenItems[key] = true;
434
- if (navItem.children) keepActive(navItem.children, level + 1);
440
+ if (navItem.children) keepActive(navItem.children, childLevel(navItem, level));
435
441
  });
436
442
  };
437
443
  keepActive(items, 0);
@@ -510,7 +516,7 @@ function NavSkeleton() {
510
516
  "data-sidebar": "menu",
511
517
  children: [
512
518
  /* @__PURE__ */ jsx(SidebarMenu, {
513
- className: "px-2",
519
+ className: NAV_STYLES.rowInset,
514
520
  children: /* @__PURE__ */ jsx(SidebarMenuItem, {
515
521
  className: "[&>*:first-child]:w-full",
516
522
  children: /* @__PURE__ */ jsx(SidebarMenuSkeleton, {
@@ -528,7 +534,7 @@ function NavSkeleton() {
528
534
  5,
529
535
  6
530
536
  ].map((i) => /* @__PURE__ */ jsx(SidebarMenu, {
531
- className: "px-2",
537
+ className: NAV_STYLES.rowInset,
532
538
  children: /* @__PURE__ */ jsx(SidebarMenuItem, {
533
539
  className: "[&>*:first-child]:w-full",
534
540
  children: /* @__PURE__ */ jsx(SidebarMenuSkeleton, {
@@ -539,7 +545,7 @@ function NavSkeleton() {
539
545
  }, i)),
540
546
  /* @__PURE__ */ jsx(SidebarSeparator, { className: "my-2" }),
541
547
  /* @__PURE__ */ jsx(SidebarMenu, {
542
- className: "px-2",
548
+ className: NAV_STYLES.rowInset,
543
549
  children: /* @__PURE__ */ jsx(SidebarMenuItem, {
544
550
  className: "[&>*:first-child]:w-full",
545
551
  children: /* @__PURE__ */ jsx(SidebarMenuSkeleton, {
@@ -1,7 +1,7 @@
1
1
  import type { VariantProps } from 'class-variance-authority';
2
2
  import * as React from 'react';
3
3
  declare const titleVariants: (props?: ({
4
- level?: 1 | 2 | 3 | 4 | 5 | 6 | null | undefined;
4
+ level?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | null | undefined;
5
5
  weight?: "bold" | "normal" | "medium" | "semibold" | "extrabold" | null | undefined;
6
6
  textColor?: "default" | "secondary" | "destructive" | "success" | "info" | "warning" | "primary" | "muted" | null | undefined;
7
7
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
@@ -300,7 +300,13 @@ declare function Title({ className, level, weight, textColor, as, children, ...p
300
300
  className: string;
301
301
  }, HTMLElement>;
302
302
  interface TextProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'color'>, VariantProps<typeof textVariants> {
303
- as?: 'span' | 'p' | 'div';
303
+ /**
304
+ * Headings are allowed because the scale's display steps start at 16px
305
+ * (`Title` level 6), so a smaller heading — a card or section label at 13
306
+ * or 14px — has no `Title` level. Render it as `<Text as="h4">` rather than
307
+ * dropping to a raw size class.
308
+ */
309
+ as?: 'span' | 'p' | 'div' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
304
310
  copyable?: boolean;
305
311
  ellipsis?: boolean;
306
312
  mark?: boolean;
@@ -44,6 +44,13 @@ export declare function useNavMenuContext(): NavMenuContextValue;
44
44
  * Uses type + title + href + level (not href alone) so that collapsible groups
45
45
  * with href:null can still be toggled and duplicate hrefs never collide.
46
46
  */
47
+ /**
48
+ * Depth at which an item's children render. A `group` is only a section label,
49
+ * so its children render at the group's own depth and look like top-level rows.
50
+ */
51
+ export declare function childLevel(item: {
52
+ type: string;
53
+ }, level: number): number;
47
54
  export declare function getNavItemKey(item: {
48
55
  type: string;
49
56
  title: string;
@@ -7,7 +7,8 @@ declare function itemKeyOf(item: NavItem, level: number): string;
7
7
  * Memoized so unchanged subtrees skip re-rendering when their props are stable.
8
8
  *
9
9
  * Items with children use {@link NavCollapsibleItem}. Section headers use
10
- * {@link NavGroup} (always open, no dropdown).
10
+ * {@link NavGroup} (always open, no dropdown); their children render at the
11
+ * header's own level, so they look like top-level rows.
11
12
  */
12
13
  export declare const NavMenuItem: import("react").MemoExoticComponent<({ item, level }: {
13
14
  item: NavItem;
@@ -3,7 +3,9 @@ import type { ComponentProps } from 'react';
3
3
  import type { NavItem } from './types';
4
4
  import { SidebarMenuButton } from '../../../base/sidebar';
5
5
  export declare const NAV_STYLES: {
6
- readonly menuButton: "rounded-xl h-8 font-normal text-xs transition-all px-2 py-1 data-[active=true]:bg-sidebar data-[active=true]:text-foreground data-[active=true]:text-sidebar-primary data-[active=true]:[&>svg]:text-primary hover:bg-sidebar hover:text-sidebar-primary hover:[&>svg]:text-sidebar-primary hover:bg-sidebar-accent data-[active=true]:bg-sidebar-accent hover:font-semibold data-[active=true]:hover:[&>svg]:text-sidebar-primary transition-colors duration-300 gap-2.5 text-foreground [&>svg]:text-icon-primary";
6
+ /** Side inset of every row. Drops back to px-2 so a 32px button fits the 48px icon rail. */
7
+ readonly rowInset: "px-3 group-data-[collapsible=icon]:px-2";
8
+ readonly menuButton: "rounded-xl h-8 font-normal text-xs transition-all px-3 py-1 data-[active=true]:bg-sidebar data-[active=true]:text-foreground data-[active=true]:text-sidebar-primary data-[active=true]:[&>svg]:text-primary hover:bg-sidebar hover:text-sidebar-primary hover:[&>svg]:text-sidebar-primary hover:bg-sidebar-accent data-[active=true]:bg-sidebar-accent hover:font-semibold data-[active=true]:hover:[&>svg]:text-sidebar-primary transition-colors duration-300 gap-2.5 text-foreground [&>svg]:text-icon-primary";
7
9
  readonly disabled: "pointer-events-none opacity-50";
8
10
  readonly icon: "duration-300 transition-all";
9
11
  readonly iconSmall: "size-4 duration-300 transition-all";
package/dist/index.mjs CHANGED
@@ -41,9 +41,9 @@ import { t as Switch } from "./switch-BaR-Vna4.mjs";
41
41
  import { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow } from "./table/index.mjs";
42
42
  import { Tabs, TabsContent, TabsLinkTrigger, TabsList, TabsTrigger } from "./tabs/index.mjs";
43
43
  import { t as Textarea } from "./textarea-DiSGxCBO.mjs";
44
- import { a as ListItem, c as Title, d as titleVariants, i as List, l as paragraphVariants, n as Code, o as Paragraph, r as Link, s as Text, t as Blockquote, u as textVariants } from "./typography-DNQXFq1I.mjs";
44
+ import { a as ListItem, c as Title, d as titleVariants, i as List, l as paragraphVariants, n as Code, o as Paragraph, r as Link, s as Text, t as Blockquote, u as textVariants } from "./typography-q71iTG72.mjs";
45
45
  import { t as VisuallyHidden } from "./visuallyhidden-D7L_qs8b.mjs";
46
- import { a as hasActiveDescendant, i as findItemByHref, n as NavMenu, o as isNavItemActive, r as NavMenuItem, s as normalizePath, t as AppNavigation } from "./app-navigation-D52ikGN-.mjs";
46
+ import { a as hasActiveDescendant, i as findItemByHref, n as NavMenu, o as isNavItemActive, r as NavMenuItem, s as normalizePath, t as AppNavigation } from "./app-navigation-DEqa2_TC.mjs";
47
47
  import { n as EASE, t as DURATION } from "./motion-DvGRWdsm.mjs";
48
48
  import { t as LoaderOverlay } from "./loader-overlay-DD94azZX.mjs";
49
49
  import { t as Autocomplete } from "./autocomplete-BxtzndxZ.mjs";
@@ -1,2 +1,2 @@
1
- import { a as ListItem, c as Title, d as titleVariants, i as List, l as paragraphVariants, n as Code, o as Paragraph, r as Link, s as Text, t as Blockquote, u as textVariants } from "../typography-DNQXFq1I.mjs";
1
+ import { a as ListItem, c as Title, d as titleVariants, i as List, l as paragraphVariants, n as Code, o as Paragraph, r as Link, s as Text, t as Blockquote, u as textVariants } from "../typography-q71iTG72.mjs";
2
2
  export { Blockquote, Code, Link, List, ListItem, Paragraph, Text, Title, paragraphVariants, textVariants, titleVariants };
@@ -12,7 +12,8 @@ const titleVariants = cva("font-semibold leading-tight tracking-tight", {
12
12
  3: "text-2xl",
13
13
  4: "text-xl",
14
14
  5: "text-lg",
15
- 6: "text-base"
15
+ 6: "text-base",
16
+ 7: "text-sm"
16
17
  },
17
18
  weight: {
18
19
  normal: "font-normal",
@@ -115,7 +116,7 @@ const paragraphVariants = cva("leading-relaxed", {
115
116
  }
116
117
  });
117
118
  function Title({ className, level, weight, textColor, as, children, ...props }) {
118
- const Component = as ?? `h${level ?? 4}`;
119
+ const Component = as ?? `h${Math.min(level ?? 4, 6)}`;
119
120
  return React.createElement(Component, {
120
121
  className: cn(titleVariants({
121
122
  level,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@datum-cloud/datum-ui",
3
3
  "type": "module",
4
- "version": "3.0.0",
4
+ "version": "3.2.0",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "url": "https://github.com/datum-cloud/datum-ui"