@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 +59 -71
- package/dist/animated-icons/shield.js +20 -8
- package/dist/button.js +1 -1
- package/dist/copy-button.d.ts +2 -2
- package/dist/copy-button.js +6 -3
- package/dist/index.d.ts +1 -1
- package/dist/layout.d.ts +23 -6
- package/dist/layout.js +72 -8
- package/dist/section-nav.d.ts +1 -0
- package/dist/section-nav.js +77 -11
- package/dist/select.js +26 -3
- package/dist/table.d.ts +3 -1
- package/dist/table.js +9 -6
- package/package.json +5 -161
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
|
-
|
|
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
|
|
48
|
+
import {
|
|
27
49
|
neaDesignSystemContent,
|
|
28
|
-
|
|
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
|
-
|
|
62
|
+
export default config;
|
|
63
|
+
```
|
|
41
64
|
|
|
42
|
-
|
|
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
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
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
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
-
|
|
125
|
-
-
|
|
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: () =>
|
|
23
|
-
stopAnimation: () =>
|
|
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
|
-
|
|
43
|
+
startControls("animate");
|
|
32
44
|
}
|
|
33
|
-
}, [
|
|
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
|
-
|
|
51
|
+
startControls("normal");
|
|
40
52
|
}
|
|
41
|
-
}, [
|
|
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
|
|
78
|
+
return React.Children.map(node, (child) => attachAnimatedIconRefs(child, refs));
|
|
79
79
|
}
|
|
80
80
|
if (!React.isValidElement(node)) {
|
|
81
81
|
return node;
|
package/dist/copy-button.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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 };
|
package/dist/copy-button.js
CHANGED
|
@@ -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
|
-
|
|
36
|
+
interface DashboardSidebarNavItemBase {
|
|
37
37
|
id: string;
|
|
38
38
|
label: string;
|
|
39
|
-
icon
|
|
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
|
-
|
|
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
|
|
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
|
|
133
|
-
const
|
|
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:
|
|
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();
|
package/dist/section-nav.d.ts
CHANGED
|
@@ -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;
|
package/dist/section-nav.js
CHANGED
|
@@ -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(
|
|
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
|
-
|
|
75
|
-
|
|
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
|
-
}, [
|
|
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-
|
|
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
|
|
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),
|
|
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.
|
|
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
|
-
"./
|
|
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
|
}
|