@neasg/design-system 0.4.6 → 0.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,6 +4,12 @@ Shared NEA UI primitives, theme tokens, and Storybook documentation.
4
4
 
5
5
  ## Local Development
6
6
 
7
+ Use Node 22 for local development:
8
+
9
+ ```bash
10
+ nvm use
11
+ ```
12
+
7
13
  ```bash
8
14
  npm install
9
15
  npm test
@@ -13,19 +19,36 @@ npm run storybook
13
19
 
14
20
  ## Package Usage
15
21
 
22
+ Import the stylesheet once at your app root before rendering package
23
+ components:
24
+
16
25
  ```ts
17
26
  import "@neasg/design-system/styles.css";
18
- import { Badge, Button, Typography } from "@neasg/design-system";
19
27
  ```
20
28
 
21
- For Tailwind-based apps, prefer the exported preset so you inherit the shared colors, font tokens, radius tokens, container sizing, and motion defaults:
29
+ Use the root package import by default:
30
+
31
+ ```ts
32
+ import {
33
+ Badge,
34
+ Button,
35
+ Card,
36
+ PageHeader,
37
+ Table,
38
+ Typography,
39
+ } from "@neasg/design-system";
40
+ ```
41
+
42
+ For Tailwind-based apps, use the exported preset so you inherit the shared
43
+ colors, font tokens, radius tokens, spacing, and motion defaults:
22
44
 
23
45
  ```ts
24
46
  // tailwind.config.ts
25
47
  import type { Config } from "tailwindcss";
26
- import neaTailwindPreset, {
48
+ import {
27
49
  neaDesignSystemContent,
28
- } from "@neasg/design-system/tailwind-preset";
50
+ neaTailwindPreset,
51
+ } from "@neasg/design-system";
29
52
 
30
53
  const config: Config = {
31
54
  presets: [neaTailwindPreset],
@@ -35,11 +58,13 @@ const config: Config = {
35
58
  ...neaDesignSystemContent,
36
59
  ],
37
60
  };
38
- ```
39
61
 
40
- Theme tokens are provided through the exported stylesheet. Import `@neasg/design-system/styles.css` once at your app root before rendering package components.
62
+ export default config;
63
+ ```
41
64
 
42
- If you want to change the package base font size, override `--font-size-root` after importing the stylesheet:
65
+ Theme tokens are provided through the exported stylesheet. If you want to
66
+ change the package base font size, override `--font-size-root` after importing
67
+ the stylesheet:
43
68
 
44
69
  ```css
45
70
  :root {
@@ -47,7 +72,8 @@ If you want to change the package base font size, override `--font-size-root` af
47
72
  }
48
73
  ```
49
74
 
50
- If you want the package font token to resolve to the actual Inter font in Next.js, load it into `--font-sans`:
75
+ If you want the package font token to resolve to the actual Inter font in
76
+ Next.js, load it into `--font-sans`:
51
77
 
52
78
  ```ts
53
79
  import { Inter } from "next/font/google";
@@ -58,37 +84,28 @@ const inter = Inter({
58
84
  });
59
85
  ```
60
86
 
61
- The package currently includes:
62
-
63
- - theme tokens for colors, typography, font weights, border radius, shared spacing, and dashboard layout sizing/padding
64
- - typography primitives for titles, headings, body copy, labels, and captions
65
- - dashboard layout primitives for sidebar + content
66
- - page header with title, subtitle, and breadcrumbs
67
- - draggable tabs for overview/case tab strips
68
- - prescriptive table and pagination components with built-in sorting, loading skeletons, and table-level pagination wiring
69
-
70
- ## Package Rules
71
-
72
- - Use `@neasg/design-system` as the source of truth for shared UI.
73
- - Prefer package components before creating app-local duplicates.
74
- - If a reusable pattern is missing, extract it into the design system instead of leaving it app-local.
75
- - This design system is light-theme only. Do not add dark theme tokens, dark variants, or a theme toggle unless product direction changes.
87
+ Use package components before creating app-local UI copies. If a reusable
88
+ pattern is missing, extract it into the design system instead of duplicating it
89
+ locally. This design system is light-theme only.
76
90
 
77
91
  ## AI Usage
78
92
 
79
- ### MCP Server (recommended)
80
-
81
- This design system includes the [Storybook MCP addon](https://storybook.js.org/docs/next/ai/mcp/overview/), which gives AI coding assistants direct access to component documentation, props, stories, and usage examples.
93
+ This design system includes the
94
+ [Storybook MCP addon](https://storybook.js.org/docs/ai/mcp/overview), which
95
+ lets AI coding assistants read component documentation, props, stories, and
96
+ usage examples from Storybook.
82
97
 
83
- **Setup:**
98
+ 1. Start Storybook in this repo:
84
99
 
85
- 1. Start the Storybook dev server in this repo:
86
100
  ```bash
87
101
  npm run storybook
88
102
  ```
89
- This starts on `http://localhost:6007` with the MCP endpoint at `/mcp`.
90
103
 
91
- 2. In your consuming project, add a `.mcp.json` at the project root:
104
+ Storybook runs on `http://localhost:6007`, with the MCP endpoint at
105
+ `http://localhost:6007/mcp`.
106
+
107
+ 2. In the consuming project, add a `.mcp.json` at the project root:
108
+
92
109
  ```json
93
110
  {
94
111
  "mcpServers": {
@@ -100,56 +117,27 @@ This design system includes the [Storybook MCP addon](https://storybook.js.org/d
100
117
  }
101
118
  ```
102
119
 
103
- 3. Restart Claude Code (or your MCP-compatible AI tool) in the consuming project. The following tools become available:
104
- - **list-all-documentation** discover all components and their story IDs
105
- - **get-documentation** get full component docs, props, and code examples
106
- - **get-documentation-for-story** — get details for a specific story variant
107
- - **preview-stories** — generate preview URLs for stories
108
- - **get-storybook-story-instructions** — get guidance on writing stories
109
-
110
- The MCP server requires the Storybook dev server to be running. Start it before opening your AI tool.
120
+ 3. Restart your MCP-compatible AI tool. The docs toolset can list all
121
+ documentation, fetch documentation for a component, and fetch documentation
122
+ for a specific story. The development toolset can preview stories and fetch
123
+ Storybook story-writing instructions.
111
124
 
112
- ### Manual instructions
113
-
114
- If you are not using the MCP server, give your AI coding assistant these instructions explicitly:
125
+ If you are not using MCP, give your AI coding assistant these instructions:
115
126
 
116
127
  ```text
117
128
  Use @neasg/design-system as the source of truth for shared UI.
118
129
 
130
+ - Use the Storybook MCP server to discover available components and their APIs before writing UI code.
119
131
  - Prefer components from @neasg/design-system before creating app-local UI.
120
- - Import package components directly when they already exist instead of building app-local copies.
121
- - Prefer this import pattern:
122
- import { Button, Input, PageHeader, Table, Typography } from "@neasg/design-system";
132
+ - Import components from the root package:
133
+ import { Button, Input, Card, Table, Typography } from "@neasg/design-system";
134
+ - Use the documented props-driven API. Do not build custom compositions from internal primitives.
123
135
  - Use the package theme tokens and Tailwind preset instead of inventing ad hoc colors, spacing, or radius values.
124
- - Do not introduce dark theme support. This design system is light-theme only.
125
- - If a needed shared pattern is missing, extract it into @neasg/design-system rather than building a duplicate local component.
136
+ - Use Typography for headings, body copy, labels, and captions.
137
+ - This design system is light-theme only. Do not generate dark-mode styles, tokens, or examples.
138
+ - If a needed shared pattern is missing, extract it into @neasg/design-system rather than duplicating it locally.
126
139
  ```
127
140
 
128
- ## Common Exports
129
-
130
- - `@neasg/design-system`
131
- - `@neasg/design-system/button`
132
- - `@neasg/design-system/badge`
133
- - `@neasg/design-system/breadcrumb`
134
- - `@neasg/design-system/email-input`
135
- - `@neasg/design-system/input`
136
- - `@neasg/design-system/otp-input`
137
- - `@neasg/design-system/phone-input`
138
- - `@neasg/design-system/textarea`
139
- - `@neasg/design-system/select`
140
- - `@neasg/design-system/draggable-tabs`
141
- - `@neasg/design-system/layout`
142
- - `@neasg/design-system/page-header`
143
- - `@neasg/design-system/popover`
144
- - `@neasg/design-system/pagination`
145
- - `@neasg/design-system/table`
146
- - `@neasg/design-system/tailwind-preset`
147
- - `@neasg/design-system/tabs`
148
- - `@neasg/design-system/typography`
149
- - `@neasg/design-system/tooltip`
150
- - `@neasg/design-system/theme`
151
- - `@neasg/design-system/styles.css`
152
-
153
141
  ## Publishing
154
142
 
155
143
  This package is built to `dist/` before publish. The public package entry points are the compiled files in `dist`, not the raw `src` files.
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { motion, useAnimation } from "motion/react";
4
- import { forwardRef, useCallback, useImperativeHandle, useRef } from "react";
4
+ import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, } from "react";
5
5
  import { cn } from "../lib/utils";
6
6
  const SVG_VARIANTS = {
7
7
  normal: { scale: 1 },
@@ -16,29 +16,41 @@ const SVG_VARIANTS = {
16
16
  const ShieldIcon = forwardRef(({ onMouseEnter, onMouseLeave, className, size = 24, ...props }, ref) => {
17
17
  const controls = useAnimation();
18
18
  const isControlledRef = useRef(false);
19
+ const isMountedRef = useRef(false);
20
+ useEffect(() => {
21
+ isMountedRef.current = true;
22
+ return () => {
23
+ isMountedRef.current = false;
24
+ };
25
+ }, []);
26
+ const startControls = useCallback((variant) => {
27
+ if (isMountedRef.current) {
28
+ controls.start(variant);
29
+ }
30
+ }, [controls]);
19
31
  useImperativeHandle(ref, () => {
20
32
  isControlledRef.current = true;
21
33
  return {
22
- startAnimation: () => controls.start("animate"),
23
- stopAnimation: () => controls.start("normal"),
34
+ startAnimation: () => startControls("animate"),
35
+ stopAnimation: () => startControls("normal"),
24
36
  };
25
- });
37
+ }, [startControls]);
26
38
  const handleMouseEnter = useCallback((e) => {
27
39
  if (isControlledRef.current) {
28
40
  onMouseEnter === null || onMouseEnter === void 0 ? void 0 : onMouseEnter(e);
29
41
  }
30
42
  else {
31
- controls.start("animate");
43
+ startControls("animate");
32
44
  }
33
- }, [controls, onMouseEnter]);
45
+ }, [onMouseEnter, startControls]);
34
46
  const handleMouseLeave = useCallback((e) => {
35
47
  if (isControlledRef.current) {
36
48
  onMouseLeave === null || onMouseLeave === void 0 ? void 0 : onMouseLeave(e);
37
49
  }
38
50
  else {
39
- controls.start("normal");
51
+ startControls("normal");
40
52
  }
41
- }, [controls, onMouseLeave]);
53
+ }, [onMouseLeave, startControls]);
42
54
  return (_jsx("div", { className: cn(className), onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, ...props, children: _jsx(motion.svg, { animate: controls, fill: "none", height: size, stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", variants: SVG_VARIANTS, viewBox: "0 0 24 24", width: size, xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" }) }) }));
43
55
  });
44
56
  ShieldIcon.displayName = "ShieldIcon";
package/dist/button.js CHANGED
@@ -75,7 +75,7 @@ function attachAnimatedIconRefs(node, refs) {
75
75
  return node;
76
76
  }
77
77
  if (Array.isArray(node)) {
78
- return node.map((child) => attachAnimatedIconRefs(child, refs));
78
+ return React.Children.map(node, (child) => attachAnimatedIconRefs(child, refs));
79
79
  }
80
80
  if (!React.isValidElement(node)) {
81
81
  return node;
@@ -12,8 +12,8 @@ export interface CopyButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLBut
12
12
  onError?: (error: unknown) => void;
13
13
  /** Accessible label for the icon-only button. Defaults to "Copy". */
14
14
  copyLabel?: string;
15
- /** Accessible label announced after copy. Defaults to "Copied". */
15
+ /** Feedback label after copying. Defaults to "Copied". */
16
16
  copiedLabel?: string;
17
17
  }
18
- declare function CopyButton({ value, feedbackDuration, children, onCopy, onError, copyLabel, copiedLabel, className, type, ...props }: CopyButtonProps): import("react/jsx-runtime").JSX.Element;
18
+ declare function CopyButton({ value, feedbackDuration, children, onCopy, onError, onClick, copyLabel, copiedLabel, className, type, ...props }: CopyButtonProps): import("react/jsx-runtime").JSX.Element;
19
19
  export { CopyButton };
@@ -4,14 +4,17 @@ import * as React from "react";
4
4
  import { Copy } from "lucide-react";
5
5
  import { CheckIcon } from "./animated-icons/check";
6
6
  import { cn } from "./lib/utils";
7
- function CopyButton({ value, feedbackDuration = 1500, children, onCopy, onError, copyLabel = "Copy", copiedLabel = "Copied", className, type = "button", ...props }) {
7
+ function CopyButton({ value, feedbackDuration = 1500, children, onCopy, onError, onClick, copyLabel = "Copy", copiedLabel = "Copied", className, type = "button", ...props }) {
8
8
  const [copied, setCopied] = React.useState(false);
9
9
  const timeoutRef = React.useRef(null);
10
10
  React.useEffect(() => () => {
11
11
  if (timeoutRef.current)
12
12
  clearTimeout(timeoutRef.current);
13
13
  }, []);
14
- const handleCopy = React.useCallback(async () => {
14
+ const handleCopy = React.useCallback(async (event) => {
15
+ onClick === null || onClick === void 0 ? void 0 : onClick(event);
16
+ if (event.defaultPrevented)
17
+ return;
15
18
  try {
16
19
  await navigator.clipboard.writeText(value);
17
20
  setCopied(true);
@@ -23,7 +26,7 @@ function CopyButton({ value, feedbackDuration = 1500, children, onCopy, onError,
23
26
  catch (error) {
24
27
  onError === null || onError === void 0 ? void 0 : onError(error);
25
28
  }
26
- }, [value, feedbackDuration, onCopy, onError]);
29
+ }, [value, feedbackDuration, onCopy, onClick, onError]);
27
30
  const iconOnly = children === undefined;
28
31
  const label = copied ? copiedLabel : copyLabel;
29
32
  return (_jsxs(_Fragment, { children: [_jsxs("button", { type: type, onClick: handleCopy, "aria-label": iconOnly ? label : undefined, className: cn("inline-flex cursor-pointer items-center gap-1.5 rounded text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50", !iconOnly && "text-sm font-medium", className), ...props, children: [copied ? (_jsx(CheckIcon, { size: 16, className: "text-green-600" })) : (_jsx(Copy, { className: "h-4 w-4", "aria-hidden": "true" })), iconOnly ? null : _jsx("span", { children: copied ? copiedLabel : children })] }), _jsx("span", { className: "sr-only", "aria-live": "polite", children: copied ? copiedLabel : "" })] }));
package/dist/index.d.ts CHANGED
@@ -78,7 +78,7 @@ export { DraggableTabs, type DraggableTabItem, type DraggableTabsProps, } from "
78
78
  export { Drawer, } from "./drawer";
79
79
  export type { DrawerAction, DrawerProps, DrawerSide } from "./drawer";
80
80
  export { DashboardLayout, DashboardSidebar, useSidebar, } from "./layout";
81
- export type { DashboardLayoutProps, DashboardSidebarNavGroup, DashboardSidebarNavItem, } from "./layout";
81
+ export type { DashboardLayoutProps, DashboardSidebarNavDropdownItem, DashboardSidebarNavGroup, DashboardSidebarNavItem, DashboardSidebarNavLinkItem, } from "./layout";
82
82
  export { PageHeader } from "./page-header";
83
83
  export { PageSection, PageSectionGroup } from "./page-section";
84
84
  export type { PageSectionGroupProps, PageSectionProps } from "./page-section";
package/dist/layout.d.ts CHANGED
@@ -33,13 +33,10 @@ export interface DashboardSidebarProps extends React.HTMLAttributes<HTMLElement>
33
33
  }) => React.ReactNode);
34
34
  toggleAriaLabel?: string;
35
35
  }
36
- export interface DashboardSidebarNavItem {
36
+ interface DashboardSidebarNavItemBase {
37
37
  id: string;
38
38
  label: string;
39
- icon: React.ElementType;
40
- href?: string;
41
- target?: string;
42
- rel?: string;
39
+ icon?: React.ElementType;
43
40
  active?: boolean;
44
41
  disabled?: boolean;
45
42
  wip?: boolean;
@@ -47,10 +44,30 @@ export interface DashboardSidebarNavItem {
47
44
  badgeVariant?: CountBadgeProps["variant"];
48
45
  badgePulse?: boolean;
49
46
  trailing?: React.ReactNode;
50
- onClick?: () => void;
51
47
  onHoverStart?: () => void;
52
48
  onHoverEnd?: () => void;
53
49
  }
50
+ export interface DashboardSidebarNavLinkItem extends DashboardSidebarNavItemBase {
51
+ href?: string;
52
+ target?: string;
53
+ rel?: string;
54
+ onClick?: () => void;
55
+ items?: never;
56
+ defaultExpanded?: never;
57
+ expanded?: never;
58
+ onExpandedChange?: never;
59
+ }
60
+ export interface DashboardSidebarNavDropdownItem extends DashboardSidebarNavItemBase {
61
+ items: DashboardSidebarNavItem[];
62
+ defaultExpanded?: boolean;
63
+ expanded?: boolean;
64
+ onExpandedChange?: (expanded: boolean) => void;
65
+ href?: never;
66
+ target?: never;
67
+ rel?: never;
68
+ onClick?: never;
69
+ }
70
+ export type DashboardSidebarNavItem = DashboardSidebarNavLinkItem | DashboardSidebarNavDropdownItem;
54
71
  export interface DashboardSidebarNavGroup {
55
72
  label?: string;
56
73
  items: DashboardSidebarNavItem[];
package/dist/layout.js CHANGED
@@ -1,6 +1,8 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
+ import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
5
+ import { ChevronDownIcon } from "./animated-icons/chevron-down";
4
6
  import { ChevronLeftIcon } from "./animated-icons/chevron-left";
5
7
  import { ChevronRightIcon } from "./animated-icons/chevron-right";
6
8
  import { Badge } from "./badge";
@@ -80,7 +82,7 @@ function SidebarHeader({ className, children, }) {
80
82
  return _jsx("div", { className: cn("shrink-0", className), children: children });
81
83
  }
82
84
  function SidebarContent({ className, children, }) {
83
- return _jsx("div", { className: cn("flex-1 overflow-y-auto", className), children: children });
85
+ return (_jsx("div", { className: cn("flex-1 overflow-y-auto", className), children: children }));
84
86
  }
85
87
  function SidebarFooter({ className, children, }) {
86
88
  return _jsx("div", { className: cn("shrink-0", className), children: children });
@@ -97,12 +99,25 @@ function SidebarMenu({ className, children, }) {
97
99
  function SidebarMenuItem({ className, children, }) {
98
100
  return _jsx("li", { className: className, children: children });
99
101
  }
100
- function SidebarMenuButton({ active = false, collapsed = false, disabled = false, className, asChild = false, children, ...props }) {
101
- return (_jsx(Button, { asChild: asChild, variant: "ghost", disabled: disabled, className: cn("h-control w-full justify-start gap-3 rounded-lg text-sm font-medium shadow-none", collapsed ? "justify-center px-3" : "px-3", disabled
102
+ const SidebarMenuButton = React.forwardRef(({ active = false, collapsed = false, disabled = false, className, asChild = false, children, ...props }, ref) => {
103
+ return (_jsx(Button, { ref: ref, asChild: asChild, variant: "ghost", disabled: disabled, className: cn("h-control w-full justify-start gap-3 rounded-lg text-sm font-medium shadow-none", collapsed ? "justify-center px-3" : "px-3", disabled
102
104
  ? "text-muted-foreground/50 hover:bg-transparent hover:text-muted-foreground/50"
103
105
  : active
104
106
  ? "bg-white text-foreground hover:bg-white hover:text-foreground"
105
107
  : "text-muted-foreground hover:bg-white hover:text-foreground", className), ...props, children: children }));
108
+ });
109
+ SidebarMenuButton.displayName = "SidebarMenuButton";
110
+ function isDropdownSidebarItem(item) {
111
+ return Array.isArray(item.items);
112
+ }
113
+ function sidebarItemIsActive(item) {
114
+ if (item.active) {
115
+ return true;
116
+ }
117
+ if (isDropdownSidebarItem(item)) {
118
+ return item.items.some(sidebarItemIsActive);
119
+ }
120
+ return false;
106
121
  }
107
122
  function renderSidebarSlot(slot, isCollapsed) {
108
123
  if (typeof slot === "function") {
@@ -110,10 +125,11 @@ function renderSidebarSlot(slot, isCollapsed) {
110
125
  }
111
126
  return slot !== null && slot !== void 0 ? slot : null;
112
127
  }
113
- function DashboardSidebarItem({ item, isCollapsed, }) {
128
+ function DashboardSidebarItemButton({ item, isCollapsed, active, expanded, depth = 0, onClick, ariaExpanded, }) {
114
129
  const Icon = item.icon;
115
130
  const iconRef = React.useRef(null);
116
131
  const showWipBadge = item.disabled || item.wip;
132
+ const hasDropdown = isDropdownSidebarItem(item);
117
133
  const handleMouseEnter = () => {
118
134
  var _a, _b, _c;
119
135
  if (!item.disabled) {
@@ -128,14 +144,62 @@ function DashboardSidebarItem({ item, isCollapsed, }) {
128
144
  (_c = item.onHoverEnd) === null || _c === void 0 ? void 0 : _c.call(item);
129
145
  }
130
146
  };
131
- const content = (_jsxs(_Fragment, { children: [_jsxs("div", { className: "relative flex h-5 w-5 shrink-0 items-center justify-center", children: [_jsx(Icon, { ref: iconRef, size: 16 }), isCollapsed && item.badgeCount !== undefined ? (_jsx(CountBadge, { count: item.badgeCount, variant: item.badgeVariant, size: "sm", pulse: item.badgePulse, className: "absolute -right-2 -top-2" })) : null] }), !isCollapsed ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "flex-1 truncate text-left", children: item.label }), showWipBadge ? _jsx(Badge, { variant: "muted", children: "WIP" }) : null, item.trailing, item.badgeCount !== undefined ? (_jsx(CountBadge, { count: item.badgeCount, variant: item.badgeVariant, size: "md", pulse: item.badgePulse })) : null] })) : null] }));
132
- const button = (_jsx(SidebarMenuButton, { onClick: item.onClick, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onFocus: handleMouseEnter, onBlur: handleMouseLeave, disabled: item.disabled, active: item.active, collapsed: isCollapsed, "aria-current": item.active ? "page" : undefined, children: content }));
133
- const interactive = item.href && !item.disabled ? (_jsx(SidebarMenuButton, { asChild: true, onClick: item.onClick, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onFocus: handleMouseEnter, onBlur: handleMouseLeave, active: item.active, collapsed: isCollapsed, "aria-current": item.active ? "page" : undefined, children: _jsx("a", { href: item.href, target: item.target, rel: item.rel, children: content }) })) : (button);
147
+ const content = (_jsxs(_Fragment, { children: [Icon ? (_jsxs("div", { className: "relative flex h-5 w-5 shrink-0 items-center justify-center", children: [_jsx(Icon, { ref: iconRef, size: 16 }), isCollapsed && item.badgeCount !== undefined ? (_jsx(CountBadge, { count: item.badgeCount, variant: item.badgeVariant, size: "sm", pulse: item.badgePulse, className: "absolute -right-2 -top-2" })) : null] })) : (_jsxs("div", { "aria-hidden": "true", className: "relative flex h-5 w-5 shrink-0 items-center justify-center", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-current/35" }), isCollapsed && item.badgeCount !== undefined ? (_jsx(CountBadge, { count: item.badgeCount, variant: item.badgeVariant, size: "sm", pulse: item.badgePulse, className: "absolute -right-2 -top-2" })) : null] })), !isCollapsed ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "flex-1 truncate text-left", children: item.label }), showWipBadge ? _jsx(Badge, { variant: "muted", children: "WIP" }) : null, item.trailing, item.badgeCount !== undefined ? (_jsx(CountBadge, { count: item.badgeCount, variant: item.badgeVariant, size: "md", pulse: item.badgePulse })) : null, hasDropdown ? (_jsx(ChevronDownIcon, { "aria-hidden": "true", className: cn("shrink-0 text-muted-foreground transition-transform duration-200", expanded && "rotate-180 text-foreground"), size: 16 })) : null] })) : null] }));
148
+ const tooltipLabel = `${item.label}${showWipBadge ? " (WIP)" : ""}`;
149
+ const button = (_jsx(SidebarMenuButton, { onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onFocus: handleMouseEnter, onBlur: handleMouseLeave, onClick: onClick, disabled: item.disabled, active: active, collapsed: isCollapsed, "aria-current": active && !hasDropdown ? "page" : undefined, "aria-expanded": ariaExpanded, "aria-label": isCollapsed ? tooltipLabel : undefined, children: content }));
150
+ const interactive = !hasDropdown && item.href && !item.disabled ? (_jsx(SidebarMenuButton, { asChild: true, onClick: item.onClick, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onFocus: handleMouseEnter, onBlur: handleMouseLeave, active: active, collapsed: isCollapsed, "aria-current": active ? "page" : undefined, "aria-label": isCollapsed ? tooltipLabel : undefined, children: _jsx("a", { href: item.href, target: item.target, rel: item.rel, children: content }) })) : (button);
134
151
  if (isCollapsed) {
135
- return (_jsx(Tooltip, { side: "right", content: _jsxs("p", { children: [item.label, showWipBadge ? " (WIP)" : ""] }), children: interactive }));
152
+ return (_jsx(Tooltip, { side: "right", content: _jsx("p", { children: tooltipLabel }), children: item.disabled ? (_jsx("span", { className: "block w-full cursor-not-allowed", children: interactive })) : (interactive) }));
136
153
  }
137
154
  return interactive;
138
155
  }
156
+ function DashboardSidebarLeafItem({ item, isCollapsed, depth = 0, }) {
157
+ return (_jsx(DashboardSidebarItemButton, { item: item, isCollapsed: isCollapsed, active: sidebarItemIsActive(item), depth: depth, onClick: item.onClick }));
158
+ }
159
+ function DashboardSidebarDropdownItem({ item, isCollapsed, depth = 0, }) {
160
+ var _a;
161
+ const { toggle } = useSidebar();
162
+ const hasActiveDescendant = item.items.some(sidebarItemIsActive);
163
+ const isControlled = item.expanded !== undefined;
164
+ const [uncontrolledExpanded, setUncontrolledExpanded] = React.useState((_a = item.defaultExpanded) !== null && _a !== void 0 ? _a : Boolean(item.active || hasActiveDescendant));
165
+ const expanded = isControlled ? item.expanded : uncontrolledExpanded;
166
+ React.useEffect(() => {
167
+ if (!isControlled && (item.active || hasActiveDescendant)) {
168
+ setUncontrolledExpanded(true);
169
+ }
170
+ }, [hasActiveDescendant, isControlled, item.active]);
171
+ const setExpanded = React.useCallback((next) => {
172
+ var _a;
173
+ if (!isControlled) {
174
+ setUncontrolledExpanded(next);
175
+ }
176
+ (_a = item.onExpandedChange) === null || _a === void 0 ? void 0 : _a.call(item, next);
177
+ }, [isControlled, item]);
178
+ const handleOpenChange = React.useCallback((next) => {
179
+ if (item.disabled) {
180
+ return;
181
+ }
182
+ setExpanded(next);
183
+ }, [item.disabled, setExpanded]);
184
+ const handleClick = React.useCallback(() => {
185
+ if (item.disabled) {
186
+ return;
187
+ }
188
+ if (isCollapsed) {
189
+ setExpanded(true);
190
+ toggle();
191
+ return;
192
+ }
193
+ handleOpenChange(!expanded);
194
+ }, [expanded, handleOpenChange, isCollapsed, item.disabled, setExpanded, toggle]);
195
+ return (_jsxs(CollapsiblePrimitive.Root, { open: expanded, onOpenChange: handleOpenChange, children: [_jsx(DashboardSidebarItemButton, { item: item, isCollapsed: isCollapsed, active: item.active || hasActiveDescendant, expanded: expanded, depth: depth, onClick: handleClick, ariaExpanded: isCollapsed ? undefined : expanded }), !isCollapsed ? (_jsx(CollapsiblePrimitive.Content, { className: "overflow-hidden", children: _jsx("div", { className: "ml-5 mt-1 border-l border-border/70 pl-2", children: _jsx(SidebarMenu, { className: "space-y-1", children: item.items.map((child) => (_jsx(SidebarMenuItem, { children: _jsx(DashboardSidebarItem, { item: child, isCollapsed: false, depth: depth + 1 }) }, child.id))) }) }) })) : null] }));
196
+ }
197
+ function DashboardSidebarItem({ item, isCollapsed, depth = 0, }) {
198
+ if (isDropdownSidebarItem(item)) {
199
+ return (_jsx(DashboardSidebarDropdownItem, { item: item, isCollapsed: isCollapsed, depth: depth }));
200
+ }
201
+ return (_jsx(DashboardSidebarLeafItem, { item: item, isCollapsed: isCollapsed, depth: depth }));
202
+ }
139
203
  function DashboardSidebar({ className, children, logo, search, navGroups, footerItems, footer, toggleAriaLabel = "Toggle sidebar", ...props }) {
140
204
  var _a;
141
205
  const { isCollapsed, toggle } = useSidebar();
@@ -13,6 +13,7 @@ export interface SectionNavItem {
13
13
  disabled?: boolean;
14
14
  trailing?: React.ReactNode;
15
15
  onClick?: () => void;
16
+ items?: SectionNavItem[];
16
17
  }
17
18
  export interface SectionNavProps extends Omit<React.HTMLAttributes<HTMLElement>, "title"> {
18
19
  title?: React.ReactNode;
@@ -1,25 +1,91 @@
1
+ "use client";
1
2
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  import * as React from "react";
4
+ import { Check } from "lucide-react";
5
+ import { ChevronRightIcon } from "./animated-icons/chevron-right";
3
6
  import { Button } from "./button";
4
7
  import { cn } from "./lib/utils";
5
8
  import { Link } from "./link";
9
+ import { Popover } from "./popover";
6
10
  import { Typography } from "./typography";
11
+ function hasSectionNavChildren(item) {
12
+ return Array.isArray(item.items) && item.items.length > 0;
13
+ }
14
+ function sectionNavItemIsActive(item) {
15
+ if (item.active) {
16
+ return true;
17
+ }
18
+ if (hasSectionNavChildren(item)) {
19
+ return item.items.some(sectionNavItemIsActive);
20
+ }
21
+ return false;
22
+ }
23
+ function renderSectionNavItemContent({ item, active, surface, hasChildren, }) {
24
+ const Icon = item.icon;
25
+ return (_jsxs(_Fragment, { children: [Icon ? _jsx(Icon, { className: "h-4 w-4 shrink-0" }) : null, _jsx("span", { className: "min-w-0 flex-1 truncate text-left", children: item.label }), item.trailing ? _jsx("span", { className: "shrink-0", children: item.trailing }) : null, surface === "menu" && active && !hasChildren ? (_jsx(Check, { "aria-hidden": "true", className: "h-4 w-4 shrink-0" })) : null, hasChildren ? (_jsx(ChevronRightIcon, { "aria-hidden": "true", size: 14, className: "shrink-0 text-muted-foreground" })) : null] }));
26
+ }
27
+ const SectionNavRow = React.forwardRef(({ item, active, surface, hasChildren = false, onSelect, ariaExpanded, className: triggerClassName, onClick: externalOnClick, ...buttonProps }, ref) => {
28
+ const isLink = Boolean(item.href) && !item.disabled && !hasChildren;
29
+ const className = cn(surface === "list"
30
+ ? "w-full justify-start gap-3 rounded-lg px-3 text-sm font-medium shadow-none"
31
+ : "h-auto min-h-control w-full justify-start gap-3 rounded-sm px-2 py-2 text-left text-sm font-normal shadow-none whitespace-normal", active && !item.disabled
32
+ ? surface === "list"
33
+ ? "bg-white hover:bg-white"
34
+ : "bg-accent text-accent-foreground hover:bg-accent hover:text-accent-foreground"
35
+ : surface === "list"
36
+ ? "hover:bg-white"
37
+ : "hover:bg-accent hover:text-accent-foreground");
38
+ const content = renderSectionNavItemContent({
39
+ item,
40
+ active,
41
+ surface,
42
+ hasChildren,
43
+ });
44
+ if (isLink) {
45
+ return (_jsx(Button, { ref: ref, asChild: true, variant: "ghost", disabled: item.disabled, className: cn(className, triggerClassName), "aria-current": active ? "page" : undefined, ...buttonProps, children: _jsx(Link, { variant: "inherit", href: item.href, target: item.target, rel: item.rel, onClick: (event) => {
46
+ var _a;
47
+ externalOnClick === null || externalOnClick === void 0 ? void 0 : externalOnClick(event);
48
+ onSelect === null || onSelect === void 0 ? void 0 : onSelect();
49
+ (_a = item.onClick) === null || _a === void 0 ? void 0 : _a.call(item);
50
+ }, children: content }) }));
51
+ }
52
+ return (_jsx(Button, { ref: ref, type: "button", variant: "ghost", disabled: item.disabled, className: cn(className, triggerClassName), "aria-current": active && !hasChildren ? "page" : undefined, "aria-expanded": hasChildren ? ariaExpanded : undefined, "aria-haspopup": hasChildren ? "menu" : undefined, ...buttonProps, onClick: hasChildren
53
+ ? externalOnClick
54
+ : (event) => {
55
+ var _a;
56
+ externalOnClick === null || externalOnClick === void 0 ? void 0 : externalOnClick(event);
57
+ onSelect === null || onSelect === void 0 ? void 0 : onSelect();
58
+ (_a = item.onClick) === null || _a === void 0 ? void 0 : _a.call(item);
59
+ }, children: content }));
60
+ });
61
+ SectionNavRow.displayName = "SectionNavRow";
62
+ function SectionNavMenu({ items, surface, onItemSelect }) {
63
+ const renderedItems = items.map((item) => {
64
+ const content = hasSectionNavChildren(item) ? (_jsx(SectionNavSubmenuItem, { item: item, surface: surface, onItemSelect: onItemSelect })) : (_jsx(SectionNavRow, { item: item, active: sectionNavItemIsActive(item), surface: surface, onSelect: onItemSelect }));
65
+ if (surface === "list") {
66
+ return _jsx("li", { children: content }, item.id);
67
+ }
68
+ return _jsx("div", { children: content }, item.id);
69
+ });
70
+ if (surface === "list") {
71
+ return _jsx("ul", { className: "space-y-1.5", children: renderedItems });
72
+ }
73
+ return _jsx("div", { className: "space-y-1", children: renderedItems });
74
+ }
75
+ function SectionNavSubmenuItem({ item, surface, onItemSelect, }) {
76
+ const active = sectionNavItemIsActive(item);
77
+ const [open, setOpen] = React.useState(false);
78
+ return (_jsx(Popover, { open: open, onOpenChange: setOpen, surface: "menu", align: "start", side: "right", sideOffset: surface === "list" ? 8 : 6, contentClassName: "min-w-[14rem]", trigger: _jsx(SectionNavRow, { item: item, active: active, surface: surface, hasChildren: true, ariaExpanded: open }), children: _jsx("div", { children: _jsx(SectionNavMenu, { items: item.items, surface: "menu", onItemSelect: () => {
79
+ setOpen(false);
80
+ onItemSelect === null || onItemSelect === void 0 ? void 0 : onItemSelect();
81
+ } }) }) }));
82
+ }
7
83
  function SectionNav({ title, items, className, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ...props }) {
8
84
  const titleId = React.useId();
9
85
  const resolvedLabelledBy = ariaLabelledBy !== null && ariaLabelledBy !== void 0 ? ariaLabelledBy : (!ariaLabel && title != null && !React.isValidElement(title)
10
86
  ? titleId
11
87
  : undefined);
12
88
  const renderedTitle = title == null ? null : React.isValidElement(title) ? (title) : (_jsx(Typography, { id: titleId, as: "h2", variant: "bodySm", className: "font-medium text-muted-foreground", children: title }));
13
- return (_jsxs("nav", { className: cn("w-full space-y-3 rounded-xl bg-muted/40 p-3", className), "aria-label": ariaLabel, "aria-labelledby": resolvedLabelledBy, ...props, children: [renderedTitle, _jsx("ul", { className: "space-y-1.5", children: items.map((item) => {
14
- const Icon = item.icon;
15
- const itemClasses = cn("w-full justify-start", item.active &&
16
- !item.disabled &&
17
- "bg-white hover:bg-white");
18
- const content = (_jsxs(_Fragment, { children: [Icon ? _jsx(Icon, {}) : null, _jsx("span", { className: "min-w-0 flex-1 truncate text-left", children: item.label }), item.trailing ? (_jsx("span", { className: "shrink-0", children: item.trailing })) : null] }));
19
- const isLink = Boolean(item.href) && !item.disabled;
20
- return (_jsx("li", { children: _jsx(Button, { asChild: isLink, variant: "ghost", disabled: item.disabled, className: itemClasses, "aria-current": item.active ? "page" : undefined, ...(isLink
21
- ? {}
22
- : { type: "button", onClick: item.onClick }), children: isLink ? (_jsx(Link, { variant: "inherit", href: item.href, target: item.target, rel: item.rel, onClick: item.onClick, children: content })) : (content) }) }, item.id));
23
- }) })] }));
89
+ return (_jsxs("nav", { className: cn("w-full space-y-3 rounded-xl bg-muted/40 p-3", className), "aria-label": ariaLabel, "aria-labelledby": resolvedLabelledBy, ...props, children: [renderedTitle, _jsx(SectionNavMenu, { items: items, surface: "list" })] }));
24
90
  }
25
91
  export { SectionNav };
package/dist/select.js CHANGED
@@ -62,6 +62,13 @@ function Select({ label, options, placeholder = "Select an option", description,
62
62
  : options;
63
63
  const optionGroups = groupOptions(filteredOptions);
64
64
  const hasVisibleOptions = filteredOptions.length > 0;
65
+ const focusSearchInput = React.useCallback(() => {
66
+ const input = searchInputRef.current;
67
+ if (!input || document.activeElement === input) {
68
+ return;
69
+ }
70
+ input.focus({ preventScroll: true });
71
+ }, []);
65
72
  React.useEffect(() => {
66
73
  if (!isOpen) {
67
74
  setSearchQuery("");
@@ -71,13 +78,29 @@ function Select({ label, options, placeholder = "Select an option", description,
71
78
  return;
72
79
  }
73
80
  const animationFrame = window.requestAnimationFrame(() => {
74
- var _a;
75
- (_a = searchInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
81
+ focusSearchInput();
82
+ });
83
+ return () => {
84
+ window.cancelAnimationFrame(animationFrame);
85
+ };
86
+ }, [focusSearchInput, isOpen, searchable]);
87
+ React.useEffect(() => {
88
+ if (!isOpen || !searchable || !searchQuery) {
89
+ return;
90
+ }
91
+ const animationFrame = window.requestAnimationFrame(() => {
92
+ focusSearchInput();
76
93
  });
77
94
  return () => {
78
95
  window.cancelAnimationFrame(animationFrame);
79
96
  };
80
- }, [isOpen, searchable]);
97
+ }, [
98
+ filteredOptions.length,
99
+ focusSearchInput,
100
+ isOpen,
101
+ searchable,
102
+ searchQuery,
103
+ ]);
81
104
  const handleOpenChange = React.useCallback((nextOpen) => {
82
105
  if (open === undefined) {
83
106
  setUncontrolledOpen(nextOpen);
package/dist/table.d.ts CHANGED
@@ -100,6 +100,8 @@ export interface TableProps<Row> extends Omit<React.HTMLAttributes<HTMLDivElemen
100
100
  containerClassName?: string;
101
101
  tableClassName?: string;
102
102
  rowClassName?: string | ((row: Row, index: number) => string | undefined);
103
+ /** Constrains the table container height so the table body scrolls instead of the surrounding surface. */
104
+ maxHeight?: React.CSSProperties["maxHeight"];
103
105
  sorting?: TableSortingProps;
104
106
  pagination?: Omit<PaginationProps, "className">;
105
107
  loading?: boolean;
@@ -109,5 +111,5 @@ export interface TableProps<Row> extends Omit<React.HTMLAttributes<HTMLDivElemen
109
111
  /** When set, rows become interactive: cursor-pointer, hover highlight, and click handling. */
110
112
  onRowClick?: (row: Row, index: number) => void;
111
113
  }
112
- declare function Table<Row>({ columns, rows, getRowKey, emptyState, toolbar, caption, className, containerClassName, tableClassName, rowClassName, sorting, pagination, loading, loadingRows, striped, onRowClick, ...props }: TableProps<Row>): import("react/jsx-runtime").JSX.Element;
114
+ declare function Table<Row>({ columns, rows, getRowKey, emptyState, toolbar, caption, className, containerClassName, tableClassName, rowClassName, maxHeight, sorting, pagination, loading, loadingRows, striped, onRowClick, style, ...props }: TableProps<Row>): import("react/jsx-runtime").JSX.Element;
113
115
  export { Table, TableRoot, TableContainer, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption, SortableTableHead, TableRowSkeleton, };
package/dist/table.js CHANGED
@@ -9,7 +9,7 @@ import { Skeleton } from "./skeleton";
9
9
  import { cn } from "./lib/utils";
10
10
  const TableRoot = React.forwardRef(({ className, wrapperClassName: _wrapperClassName, ...props }, ref) => (_jsx("table", { ref: ref, className: cn("w-full caption-bottom text-sm", className), ...props })));
11
11
  TableRoot.displayName = "TableRoot";
12
- const TableContainer = React.forwardRef(({ className, ...props }, ref) => (_jsx("div", { ref: ref, className: cn("overflow-x-auto rounded-lg border bg-card", className), ...props })));
12
+ const TableContainer = React.forwardRef(({ className, ...props }, ref) => (_jsx("div", { ref: ref, className: cn("min-h-0 overflow-auto rounded-lg border bg-card", className), ...props })));
13
13
  TableContainer.displayName = "TableContainer";
14
14
  const TableHeader = React.forwardRef(({ className, ...props }, ref) => (_jsx("thead", { ref: ref, className: cn("sticky top-0 z-10 bg-muted [&_tr]:border-b [&>tr]:bg-inherit", className), ...props })));
15
15
  TableHeader.displayName = "TableHeader";
@@ -17,7 +17,7 @@ const TableBody = React.forwardRef(({ className, ...props }, ref) => (_jsx("tbod
17
17
  TableBody.displayName = "TableBody";
18
18
  const TableFooter = React.forwardRef(({ className, ...props }, ref) => (_jsx("tfoot", { ref: ref, className: cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className), ...props })));
19
19
  TableFooter.displayName = "TableFooter";
20
- const TableRow = React.forwardRef(({ className, ...props }, ref) => (_jsx("tr", { ref: ref, className: cn("border-b bg-card transition-colors cursor-pointer hover:bg-muted data-[state=selected]:bg-muted", className), ...props })));
20
+ const TableRow = React.forwardRef(({ className, ...props }, ref) => (_jsx("tr", { ref: ref, className: cn("border-b bg-card transition-colors data-[state=selected]:bg-muted", className), ...props })));
21
21
  TableRow.displayName = "TableRow";
22
22
  const TableHead = React.forwardRef(({ className, style, width, minWidth, maxWidth, wrap = false, ...props }, ref) => (_jsx("th", { ref: ref, className: cn("h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0", wrap ? "whitespace-normal" : "whitespace-nowrap", className), style: {
23
23
  ...style,
@@ -115,18 +115,21 @@ function getDefaultSkeleton(column) {
115
115
  height: "1rem",
116
116
  };
117
117
  }
118
- function Table({ columns, rows, getRowKey, emptyState = (_jsx(EmptyState, { message: "No data available", description: "There are no rows to display in this table yet.", className: "border-0 bg-transparent px-0 py-6" })), toolbar, caption, className, containerClassName, tableClassName, rowClassName, sorting, pagination, loading = false, loadingRows = 5, striped = false, onRowClick, ...props }) {
118
+ function Table({ columns, rows, getRowKey, emptyState = (_jsx(EmptyState, { message: "No data available", description: "There are no rows to display in this table yet.", className: "border-0 bg-transparent px-0 py-6" })), toolbar, caption, className, containerClassName, tableClassName, rowClassName, maxHeight, sorting, pagination, loading = false, loadingRows = 5, striped = false, onRowClick, style, ...props }) {
119
119
  // Use auto layout so the browser can grow a column to contain its (nowrap)
120
120
  // header. Caller can opt back into fixed layout via `tableClassName="table-fixed"`.
121
121
  const shouldUseFixedLayout = false;
122
- const container = (_jsx(TableContainer, { className: cn("w-full", loading && "pointer-events-none select-none", className, containerClassName), ...props, children: _jsxs(TableRoot, { className: cn(shouldUseFixedLayout && "table-fixed", tableClassName), children: [caption ? _jsx(TableCaption, { children: caption }) : null, _jsx(TableHeader, { children: _jsx(TableRow, { children: columns.map((column) => {
122
+ const container = (_jsx(TableContainer, { className: cn("w-full", loading && "pointer-events-none select-none", className, containerClassName), style: {
123
+ ...style,
124
+ ...(maxHeight !== undefined ? { maxHeight } : {}),
125
+ }, ...props, children: _jsxs(TableRoot, { className: cn(shouldUseFixedLayout && "table-fixed", tableClassName), children: [caption ? _jsx(TableCaption, { children: caption }) : null, _jsx(TableHeader, { children: _jsx(TableRow, { children: columns.map((column) => {
123
126
  var _a;
124
127
  const sortField = (_a = column.sortKey) !== null && _a !== void 0 ? _a : column.key;
125
128
  const isSortable = column.sortable && sorting;
126
129
  return (isSortable ? (_jsx(SortableTableHead, { field: sortField, currentSortField: sorting.field, currentSortDirection: sorting.direction, onSort: sorting.onSort, className: cn(stickyClassName(column.sticky, "head"), column.headerClassName), allowWrap: column.wrap, width: column.width, minWidth: column.minWidth, maxWidth: column.maxWidth, align: column.align, sortLabel: column.sortLabel, children: column.header }, column.key)) : (_jsx(TableHead, { width: column.width, minWidth: column.minWidth, maxWidth: column.maxWidth, wrap: column.wrap, className: cn(alignClassName(column.align), stickyClassName(column.sticky, "head"), column.headerClassName), children: column.header }, column.key)));
127
- }) }) }), _jsx(TableBody, { className: cn(striped && "[&>tr:nth-child(even)]:bg-muted/60"), children: loading ? (_jsx(TableRowSkeleton, { rows: loadingRows, columns: columns.map((column) => { var _a; return (_a = column.skeleton) !== null && _a !== void 0 ? _a : getDefaultSkeleton(column); }) })) : rows.length ? (rows.map((row, rowIndex) => (_jsx(TableRow, { onClick: onRowClick ? () => onRowClick(row, rowIndex) : undefined, className: typeof rowClassName === "function"
130
+ }) }) }), _jsx(TableBody, { className: cn(striped && "[&>tr:nth-child(even)]:bg-muted/60"), children: loading ? (_jsx(TableRowSkeleton, { rows: loadingRows, columns: columns.map((column) => { var _a; return (_a = column.skeleton) !== null && _a !== void 0 ? _a : getDefaultSkeleton(column); }) })) : rows.length ? (rows.map((row, rowIndex) => (_jsx(TableRow, { onClick: onRowClick ? () => onRowClick(row, rowIndex) : undefined, className: cn(onRowClick && "cursor-pointer hover:bg-muted", typeof rowClassName === "function"
128
131
  ? rowClassName(row, rowIndex)
129
- : rowClassName, children: columns.map((column) => {
132
+ : rowClassName), children: columns.map((column) => {
130
133
  var _a, _b;
131
134
  const value = (_b = (_a = column.cell) === null || _a === void 0 ? void 0 : _a.call(column, row, rowIndex)) !== null && _b !== void 0 ? _b : (column.accessorKey !== undefined
132
135
  ? row[column.accessorKey]
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "@neasg/design-system",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
4
4
  "description": "NEA shared design system primitives, theme tokens, and Storybook docs.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
7
+ "engines": {
8
+ "node": ">=22 <23"
9
+ },
7
10
  "main": "./dist/index.js",
8
11
  "module": "./dist/index.js",
9
12
  "types": "./dist/index.d.ts",
@@ -82,166 +85,7 @@
82
85
  "types": "./dist/index.d.ts",
83
86
  "import": "./dist/index.js"
84
87
  },
85
- "./avatar": {
86
- "types": "./dist/avatar.d.ts",
87
- "import": "./dist/avatar.js"
88
- },
89
- "./animated-icons": {
90
- "types": "./dist/animated-icons/index.d.ts",
91
- "import": "./dist/animated-icons/index.js"
92
- },
93
- "./badge": {
94
- "types": "./dist/badge.d.ts",
95
- "import": "./dist/badge.js"
96
- },
97
- "./back-button": {
98
- "types": "./dist/back-button.d.ts",
99
- "import": "./dist/back-button.js"
100
- },
101
- "./button": {
102
- "types": "./dist/button.d.ts",
103
- "import": "./dist/button.js"
104
- },
105
- "./dialog": {
106
- "types": "./dist/dialog.d.ts",
107
- "import": "./dist/dialog.js"
108
- },
109
- "./command": {
110
- "types": "./dist/command.d.ts",
111
- "import": "./dist/command.js"
112
- },
113
- "./count-badge": {
114
- "types": "./dist/count-badge.d.ts",
115
- "import": "./dist/count-badge.js"
116
- },
117
- "./command-search": {
118
- "types": "./dist/command-search.d.ts",
119
- "import": "./dist/command-search.js"
120
- },
121
- "./empty-state": {
122
- "types": "./dist/empty-state.d.ts",
123
- "import": "./dist/empty-state.js"
124
- },
125
- "./editable-table": {
126
- "types": "./dist/editable-table.d.ts",
127
- "import": "./dist/editable-table.js"
128
- },
129
- "./email-input": {
130
- "types": "./dist/email-input.d.ts",
131
- "import": "./dist/email-input.js"
132
- },
133
- "./input": {
134
- "types": "./dist/input.d.ts",
135
- "import": "./dist/input.js"
136
- },
137
- "./phone-input": {
138
- "types": "./dist/phone-input.d.ts",
139
- "import": "./dist/phone-input.js"
140
- },
141
- "./otp-input": {
142
- "types": "./dist/otp-input.d.ts",
143
- "import": "./dist/otp-input.js"
144
- },
145
- "./select": {
146
- "types": "./dist/select.d.ts",
147
- "import": "./dist/select.js"
148
- },
149
- "./label-value": {
150
- "types": "./dist/label-value.d.ts",
151
- "import": "./dist/label-value.js"
152
- },
153
- "./label-value-grid": {
154
- "types": "./dist/label-value-grid.d.ts",
155
- "import": "./dist/label-value-grid.js"
156
- },
157
- "./breadcrumb": {
158
- "types": "./dist/breadcrumb.d.ts",
159
- "import": "./dist/breadcrumb.js"
160
- },
161
- "./draggable-tabs": {
162
- "types": "./dist/draggable-tabs.d.ts",
163
- "import": "./dist/draggable-tabs.js"
164
- },
165
- "./drawer": {
166
- "types": "./dist/drawer.d.ts",
167
- "import": "./dist/drawer.js"
168
- },
169
- "./right-drawer": {
170
- "types": "./dist/right-drawer.d.ts",
171
- "import": "./dist/right-drawer.js"
172
- },
173
- "./layout": {
174
- "types": "./dist/layout.d.ts",
175
- "import": "./dist/layout.js"
176
- },
177
- "./page-header": {
178
- "types": "./dist/page-header.d.ts",
179
- "import": "./dist/page-header.js"
180
- },
181
- "./page-section": {
182
- "types": "./dist/page-section.d.ts",
183
- "import": "./dist/page-section.js"
184
- },
185
- "./popover": {
186
- "types": "./dist/popover.d.ts",
187
- "import": "./dist/popover.js"
188
- },
189
- "./pagination": {
190
- "types": "./dist/pagination.d.ts",
191
- "import": "./dist/pagination.js"
192
- },
193
- "./search-input": {
194
- "types": "./dist/search-input.d.ts",
195
- "import": "./dist/search-input.js"
196
- },
197
- "./section-nav": {
198
- "types": "./dist/section-nav.d.ts",
199
- "import": "./dist/section-nav.js"
200
- },
201
88
  "./styles.css": "./dist/styles.css",
202
- "./table": {
203
- "types": "./dist/table.d.ts",
204
- "import": "./dist/table.js"
205
- },
206
- "./table-toolbar": {
207
- "types": "./dist/table-toolbar.d.ts",
208
- "import": "./dist/table-toolbar.js"
209
- },
210
- "./tailwind-preset": {
211
- "types": "./dist/tailwind-preset.d.ts",
212
- "import": "./dist/tailwind-preset.js"
213
- },
214
- "./tabs": {
215
- "types": "./dist/tabs.d.ts",
216
- "import": "./dist/tabs.js"
217
- },
218
- "./textarea": {
219
- "types": "./dist/textarea.d.ts",
220
- "import": "./dist/textarea.js"
221
- },
222
- "./typography": {
223
- "types": "./dist/typography.d.ts",
224
- "import": "./dist/typography.js"
225
- },
226
- "./theme": {
227
- "types": "./dist/theme.d.ts",
228
- "import": "./dist/theme.js"
229
- },
230
- "./tooltip": {
231
- "types": "./dist/tooltip.d.ts",
232
- "import": "./dist/tooltip.js"
233
- },
234
- "./separator": {
235
- "types": "./dist/separator.d.ts",
236
- "import": "./dist/separator.js"
237
- },
238
- "./collapsible-section": {
239
- "types": "./dist/collapsible-section.d.ts",
240
- "import": "./dist/collapsible-section.js"
241
- },
242
- "./lib/utils": {
243
- "types": "./dist/lib/utils.d.ts",
244
- "import": "./dist/lib/utils.js"
245
- }
89
+ "./package.json": "./package.json"
246
90
  }
247
91
  }