@kala-ui/react-app 0.1.0-beta.0 → 0.1.0-beta.1
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/CHANGELOG.md +17 -0
- package/README.md +9 -4
- package/dist/components/app-shell/app-shell.js +6 -6
- package/dist/components/charts/area-chart.js +1 -1
- package/dist/components/charts/bar-chart.js +1 -1
- package/dist/components/charts/chart-skeleton.js +1 -1
- package/dist/components/charts/chart.js +3 -3
- package/dist/components/charts/donut-chart.js +2 -2
- package/dist/components/charts/line-chart.js +1 -1
- package/dist/components/charts/radial-bar-chart.js +1 -1
- package/dist/components/data-table/column-filters.js +1 -1
- package/dist/components/data-table/column-header-filter.js +1 -1
- package/dist/components/data-table/data-table-body-rows.js +1 -1
- package/dist/components/data-table/data-table-filter-chips.js +1 -1
- package/dist/components/data-table/data-table-header-row.js +1 -1
- package/dist/components/data-table/data-table-skeleton.js +1 -1
- package/dist/components/data-table/data-table-toolbar.js +1 -1
- package/dist/components/data-table/data-table.js +3 -3
- package/dist/components/data-table/pagination-nav.js +1 -1
- package/dist/components/dnd/dnd.js +7 -7
- package/dist/components/footer/footer.js +1 -1
- package/dist/components/header/header-skeleton.js +1 -1
- package/dist/components/header/header.js +1 -1
- package/dist/components/metric-card/metric-card-skeleton.js +1 -1
- package/dist/components/metric-card/metric-card.js +3 -3
- package/dist/components/nav-link/nav-link.js +1 -1
- package/dist/components/navigation/navigation-skeleton.js +1 -1
- package/dist/components/navigation/navigation.js +4 -4
- package/dist/components/session-card/session-card-skeleton.js +1 -1
- package/dist/components/session-card/session-card.js +3 -3
- package/dist/components/sidebar/sidebar-skeleton.js +1 -1
- package/dist/components/sidebar/sidebar.js +1 -1
- package/dist/components/social-login-button/social-login-button.js +1 -1
- package/dist/components/social-login-buttons/social-login-buttons.js +1 -1
- package/dist/components/sparkline-chart/sparkline-chart.js +1 -1
- package/dist/components/user-menu-dropdown/user-menu-dropdown.js +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @kala-ui/react-app
|
|
2
2
|
|
|
3
|
+
## 0.1.0-beta.1
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- b5cd997: Add stable `data-kala-component` identification attributes to every component root.
|
|
8
|
+
|
|
9
|
+
Every component (and each compound part, e.g. `dialog-content`, `card-header`, `data-table-toolbar`) now renders `data-kala-component="<kebab-name>"` on its root element. This is a guaranteed-stable public API for DevTools debugging, e2e selectors, and targeted consumer CSS overrides.
|
|
10
|
+
|
|
11
|
+
- No styling or behavior changes — the attribute is identification only.
|
|
12
|
+
- The ad-hoc `data-comp` markers previously used by a few components were migrated to `data-kala-component` (update any selectors that queried `[data-comp='...']`).
|
|
13
|
+
- shadcn-compatible `data-slot` attributes are unchanged.
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Updated dependencies [b5cd997]
|
|
18
|
+
- @kala-ui/react@0.1.0-beta.1
|
|
19
|
+
|
|
3
20
|
## 0.1.0-beta.0
|
|
4
21
|
|
|
5
22
|
### Added
|
package/README.md
CHANGED
|
@@ -4,18 +4,18 @@ App-level composite components for [Kala UI](https://github.com/krr2020/kala-ui)
|
|
|
4
4
|
|
|
5
5
|
## What's inside
|
|
6
6
|
|
|
7
|
-
- **Application chrome** — `AppShell`, `Header`, `Sidebar`, `Footer`, `Navigation`, `NavLink`
|
|
7
|
+
- **Application chrome** — `AppShell`, `Header`, `Sidebar`, `Footer`, `Navigation`, `NavLink` (plus the `isActivePath` routing helper from `@kala-ui/react-app/lib/active-path`)
|
|
8
8
|
- **Data-heavy composites** — `DataTable` (sorting / filtering / pagination / selection / server-side), `Dnd` (dnd-kit sortable lists and boards)
|
|
9
|
-
- **Charts** — `AreaChart`, `BarChart`, `LineChart`, `DonutChart`, `RadialBarChart`, `SparklineChart` (ApexCharts wrappers, theme-aware)
|
|
9
|
+
- **Charts** — `AreaChart`, `BarChart`, `LineChart`, `DonutChart`, `RadialBarChart`, `SparklineChart` (ApexCharts wrappers, theme-aware, lazy-loaded)
|
|
10
10
|
- **Dashboard & domain widgets** — `MetricCard`, `SessionCard`, `UserMenuDropdown`, `SocialLoginButton(s)`
|
|
11
11
|
|
|
12
12
|
## Installation
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
|
-
pnpm add @kala-ui/react-app @kala-ui/react
|
|
15
|
+
pnpm add @kala-ui/react-app@beta @kala-ui/react@beta
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
Import the core stylesheet first, then this package's utilities:
|
|
18
|
+
Requires React 19.2+. Import the core stylesheet first, then this package's utilities (theme, tokens, preflight and `kala-*` helpers ship once via the core):
|
|
19
19
|
|
|
20
20
|
```css
|
|
21
21
|
@import "@kala-ui/react/styles";
|
|
@@ -29,6 +29,11 @@ import { Button, ThemeProvider } from "@kala-ui/react";
|
|
|
29
29
|
import { AppShell, DataTable } from "@kala-ui/react-app";
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
Every component has a subpath export (`@kala-ui/react-app/<component>`) plus
|
|
33
|
+
the barrel. All component modules ship a `"use client"` banner, so they import
|
|
34
|
+
cleanly into React Server Component apps — charts lazy-load ApexCharts on the
|
|
35
|
+
client only.
|
|
36
|
+
|
|
32
37
|
See the repository README and [THEMING.md](https://github.com/krr2020/kala-ui/blob/main/THEMING.md) for theming, dark mode and framework integration.
|
|
33
38
|
|
|
34
39
|
## License
|
|
@@ -28,7 +28,7 @@ function AppShellBase({ children, className, header, navbar, aside, footer, padd
|
|
|
28
28
|
? `${aside.width}px`
|
|
29
29
|
: (aside?.width ?? "0px"),
|
|
30
30
|
};
|
|
31
|
-
return (_jsx(AppShellContext.Provider, { value: { header, navbar, aside, footer, padding }, children: _jsx(Box, { ref: ref, className: cn("flex min-h-screen flex-col bg-background text-foreground", className), style: cssVars, ...props, children: children }) }));
|
|
31
|
+
return (_jsx(AppShellContext.Provider, { "data-kala-component": "app-shell-base", value: { header, navbar, aside, footer, padding }, children: _jsx(Box, { ref: ref, className: cn("flex min-h-screen flex-col bg-background text-foreground", className), style: cssVars, ...props, children: children }) }));
|
|
32
32
|
}
|
|
33
33
|
export const AppShell = Object.assign(AppShellBase, {
|
|
34
34
|
Header: AppShellHeader,
|
|
@@ -41,7 +41,7 @@ function AppShellHeader({ ref, className, withBorder = true, ...props }) {
|
|
|
41
41
|
const { header } = React.useContext(AppShellContext);
|
|
42
42
|
if (!header)
|
|
43
43
|
return null;
|
|
44
|
-
return (_jsx(Box, { as: "header", ref: ref, className: cn("fixed top-0 left-0 right-0 z-50 flex items-center bg-background px-4", withBorder && "border-b", className), style: { height: "var(--app-shell-header-height)" }, ...props }));
|
|
44
|
+
return (_jsx(Box, { "data-kala-component": "app-shell-header", as: "header", ref: ref, className: cn("fixed top-0 left-0 right-0 z-50 flex items-center bg-background px-4", withBorder && "border-b", className), style: { height: "var(--app-shell-header-height)" }, ...props }));
|
|
45
45
|
}
|
|
46
46
|
function AppShellNavbar({ ref, className, withBorder = true, ...props }) {
|
|
47
47
|
const { navbar } = React.useContext(AppShellContext);
|
|
@@ -49,7 +49,7 @@ function AppShellNavbar({ ref, className, withBorder = true, ...props }) {
|
|
|
49
49
|
return null;
|
|
50
50
|
// Off-canvas below the configured breakpoint, docked from it upward.
|
|
51
51
|
const variant = `${navbar.breakpoint ?? "md"}:`;
|
|
52
|
-
return (_jsx(Box, { as: "nav", ref: ref, className: cn("fixed left-0 z-40 flex flex-col bg-background transition-transform duration-300 ease-in-out", withBorder && "border-r", "-translate-x-full", `${variant}translate-x-0`, className), style: {
|
|
52
|
+
return (_jsx(Box, { "data-kala-component": "app-shell-navbar", as: "nav", ref: ref, className: cn("fixed left-0 z-40 flex flex-col bg-background transition-transform duration-300 ease-in-out", withBorder && "border-r", "-translate-x-full", `${variant}translate-x-0`, className), style: {
|
|
53
53
|
width: "var(--app-shell-navbar-width)",
|
|
54
54
|
top: "var(--app-shell-header-height)",
|
|
55
55
|
height: "calc(100vh - var(--app-shell-header-height))",
|
|
@@ -60,7 +60,7 @@ function AppShellAside({ ref, className, withBorder = true, ...props }) {
|
|
|
60
60
|
if (!aside)
|
|
61
61
|
return null;
|
|
62
62
|
const variant = `${aside.breakpoint ?? "md"}:`;
|
|
63
|
-
return (_jsx(Box, { as: "aside", ref: ref, className: cn("fixed right-0 z-40 flex flex-col bg-background transition-transform duration-300 ease-in-out", withBorder && "border-l", "translate-x-full", `${variant}translate-x-0`, className), style: {
|
|
63
|
+
return (_jsx(Box, { "data-kala-component": "app-shell-aside", as: "aside", ref: ref, className: cn("fixed right-0 z-40 flex flex-col bg-background transition-transform duration-300 ease-in-out", withBorder && "border-l", "translate-x-full", `${variant}translate-x-0`, className), style: {
|
|
64
64
|
width: "var(--app-shell-aside-width)",
|
|
65
65
|
top: "var(--app-shell-header-height)",
|
|
66
66
|
height: "calc(100vh - var(--app-shell-header-height))",
|
|
@@ -76,7 +76,7 @@ function AppShellMain({ ref, className, ...props }) {
|
|
|
76
76
|
lg: "p-6",
|
|
77
77
|
xl: "p-8",
|
|
78
78
|
};
|
|
79
|
-
return (_jsx(Box, { as: "main", ref: ref, className: cn("flex-1 transition-all duration-300 ease-in-out", padding && paddingClasses[padding], header && "pt-[var(--app-shell-header-height)]", footer && "pb-[var(--app-shell-footer-height)]",
|
|
79
|
+
return (_jsx(Box, { "data-kala-component": "app-shell-main", as: "main", ref: ref, className: cn("flex-1 transition-all duration-300 ease-in-out", padding && paddingClasses[padding], header && "pt-[var(--app-shell-header-height)]", footer && "pb-[var(--app-shell-footer-height)]",
|
|
80
80
|
// Side offsets only reserve space while the fixed panels are
|
|
81
81
|
// actually docked (breakpoint and up).
|
|
82
82
|
navbar &&
|
|
@@ -87,5 +87,5 @@ function AppShellFooter({ ref, className, withBorder = true, ...props }) {
|
|
|
87
87
|
const { footer } = React.useContext(AppShellContext);
|
|
88
88
|
if (!footer)
|
|
89
89
|
return null;
|
|
90
|
-
return (_jsx(Box, { as: "footer", ref: ref, className: cn("fixed bottom-0 left-0 right-0 z-50 flex items-center bg-background px-4", withBorder && "border-t", className), style: { height: "var(--app-shell-footer-height)" }, ...props }));
|
|
90
|
+
return (_jsx(Box, { "data-kala-component": "app-shell-footer", as: "footer", ref: ref, className: cn("fixed bottom-0 left-0 right-0 z-50 flex items-center bg-background px-4", withBorder && "border-t", className), style: { height: "var(--app-shell-footer-height)" }, ...props }));
|
|
91
91
|
}
|
|
@@ -168,5 +168,5 @@ export function AreaChart({ series, categories, colors, height = 350, width = "1
|
|
|
168
168
|
referenceLines,
|
|
169
169
|
customOptions,
|
|
170
170
|
]);
|
|
171
|
-
return (_jsx(Chart, { className: cn("w-full", className), options: chartOptions, series: series, type: "area", height: height, width: width, isLoading: isLoading, isEmpty: isEmpty, emptyMessage: emptyMessage }));
|
|
171
|
+
return (_jsx(Chart, { "data-kala-component": "charts-area-chart", className: cn("w-full", className), options: chartOptions, series: series, type: "area", height: height, width: width, isLoading: isLoading, isEmpty: isEmpty, emptyMessage: emptyMessage }));
|
|
172
172
|
}
|
|
@@ -149,5 +149,5 @@ export function BarChart({ series, categories, colors, height = 350, width = "10
|
|
|
149
149
|
subtitle,
|
|
150
150
|
customOptions,
|
|
151
151
|
]);
|
|
152
|
-
return (_jsx(Chart, { className: cn("w-full", className), options: chartOptions, series: series, type: "bar", height: height, width: width, isLoading: isLoading, isEmpty: isEmpty, emptyMessage: emptyMessage }));
|
|
152
|
+
return (_jsx(Chart, { "data-kala-component": "charts-bar-chart", className: cn("w-full", className), options: chartOptions, series: series, type: "bar", height: height, width: width, isLoading: isLoading, isEmpty: isEmpty, emptyMessage: emptyMessage }));
|
|
153
153
|
}
|
|
@@ -9,5 +9,5 @@ import { cn } from "@kala-ui/react/lib/utils";
|
|
|
9
9
|
import { Skeleton } from "@kala-ui/react/skeleton";
|
|
10
10
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@kala-ui/react/table";
|
|
11
11
|
export function ChartSkeleton({ height = 350, showLegend = true, legendCount = 4, showTable = false, tableRows = 5, tableColumns = 3, className, }) {
|
|
12
|
-
return (_jsxs("div", { className: cn("w-full space-y-4", className), children: [_jsx("div", { className: "w-full rounded-lg border bg-card kala-surface-card", style: { height: `${height}px` }, children: _jsx("div", { className: "flex items-center justify-center h-full", children: _jsx(Skeleton, { className: "h-full w-full" }) }) }), showLegend && (_jsx("div", { className: "flex gap-4 justify-center mt-4", children: Array.from({ length: legendCount }).map((_, i) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Skeleton, { className: "h-3 w-3 rounded-full" }), _jsx(Skeleton, { className: "h-4 w-16" })] }, `legend-${i}`))) })), showTable && (_jsx("div", { className: "mt-6", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsx(TableRow, { children: Array.from({ length: tableColumns }).map((_, index) => (_jsx(TableHead, { children: _jsx(Skeleton, { className: "h-4 w-24" }) }, `header-${index}`))) }) }), _jsx(TableBody, { children: Array.from({ length: tableRows }).map((_, rowIndex) => (_jsx(TableRow, { className: "border-0", children: Array.from({ length: tableColumns }).map((_, colIndex) => (_jsx(TableCell, { children: _jsx(Skeleton, { className: "h-4 w-20" }) }, `cell-${rowIndex}-${colIndex}`))) }, `row-${rowIndex}`))) })] }) }))] }));
|
|
12
|
+
return (_jsxs("div", { "data-kala-component": "charts-chart-skeleton", className: cn("w-full space-y-4", className), children: [_jsx("div", { className: "w-full rounded-lg border bg-card kala-surface-card", style: { height: `${height}px` }, children: _jsx("div", { className: "flex items-center justify-center h-full", children: _jsx(Skeleton, { className: "h-full w-full" }) }) }), showLegend && (_jsx("div", { className: "flex gap-4 justify-center mt-4", children: Array.from({ length: legendCount }).map((_, i) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Skeleton, { className: "h-3 w-3 rounded-full" }), _jsx(Skeleton, { className: "h-4 w-16" })] }, `legend-${i}`))) })), showTable && (_jsx("div", { className: "mt-6", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsx(TableRow, { children: Array.from({ length: tableColumns }).map((_, index) => (_jsx(TableHead, { children: _jsx(Skeleton, { className: "h-4 w-24" }) }, `header-${index}`))) }) }), _jsx(TableBody, { children: Array.from({ length: tableRows }).map((_, rowIndex) => (_jsx(TableRow, { className: "border-0", children: Array.from({ length: tableColumns }).map((_, colIndex) => (_jsx(TableCell, { children: _jsx(Skeleton, { className: "h-4 w-20" }) }, `cell-${rowIndex}-${colIndex}`))) }, `row-${rowIndex}`))) })] }) }))] }));
|
|
13
13
|
}
|
|
@@ -30,7 +30,7 @@ export function Chart({ className, isLoading, isEmpty, emptyMessage = "No data a
|
|
|
30
30
|
if (skeleton) {
|
|
31
31
|
return _jsx(_Fragment, { children: skeleton });
|
|
32
32
|
}
|
|
33
|
-
return _jsx(ChartSkeleton, { ...(skeletonConfig || {}), className: className });
|
|
33
|
+
return (_jsx(ChartSkeleton, { "data-kala-component": "charts-chart", ...(skeletonConfig || {}), className: className }));
|
|
34
34
|
}
|
|
35
35
|
// Render empty state when no data
|
|
36
36
|
if (isEmpty) {
|
|
@@ -40,9 +40,9 @@ export function Chart({ className, isLoading, isEmpty, emptyMessage = "No data a
|
|
|
40
40
|
const height = typeof configuredHeight === "number"
|
|
41
41
|
? `${configuredHeight}px`
|
|
42
42
|
: configuredHeight;
|
|
43
|
-
return (_jsxs("div", { className: cn("w-full flex flex-col items-center justify-center text-center rounded-lg border bg-card text-muted-foreground", className), style: { height }, children: [_jsxs("svg", { className: "mb-3 h-10 w-10 opacity-40", fill: "none", role: "img", "aria-label": "Chart empty state", stroke: "currentColor", viewBox: "0 0 24 24", children: [_jsx("title", { children: "No chart data" }), _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M3 13h2v8H3zM9 9h2v12H9zM15 5h2v16h-2zM21 1h2v20h-2z" })] }), _jsx("p", { className: "text-sm", children: emptyMessage })] }));
|
|
43
|
+
return (_jsxs("div", { "data-kala-component": "charts-chart", className: cn("w-full flex flex-col items-center justify-center text-center rounded-lg border bg-card text-muted-foreground", className), style: { height }, children: [_jsxs("svg", { className: "mb-3 h-10 w-10 opacity-40", fill: "none", role: "img", "aria-label": "Chart empty state", stroke: "currentColor", viewBox: "0 0 24 24", children: [_jsx("title", { children: "No chart data" }), _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M3 13h2v8H3zM9 9h2v12H9zM15 5h2v16h-2zM21 1h2v20h-2z" })] }), _jsx("p", { className: "text-sm", children: emptyMessage })] }));
|
|
44
44
|
}
|
|
45
|
-
return (_jsx("div", { className: cn("w-full", className), children: _jsx(Suspense, { fallback: skeleton ?? (_jsx(ChartSkeleton, { ...(skeletonConfig || {}), className: className })), children: _jsx(ReactApexChart, { ...props }) }) }));
|
|
45
|
+
return (_jsx("div", { "data-kala-component": "charts-chart", className: cn("w-full", className), children: _jsx(Suspense, { fallback: skeleton ?? (_jsx(ChartSkeleton, { ...(skeletonConfig || {}), className: className })), children: _jsx(ReactApexChart, { ...props }) }) }));
|
|
46
46
|
}
|
|
47
47
|
export { ChartSkeleton } from "./chart-skeleton.js";
|
|
48
48
|
export { getTooltipTheme };
|
|
@@ -138,9 +138,9 @@ export function DonutChart({ series, labels, colors, height = 350, width = "100%
|
|
|
138
138
|
subtitle,
|
|
139
139
|
customOptions,
|
|
140
140
|
]);
|
|
141
|
-
return (_jsx(Chart, { className: cn("w-full", className), options: chartOptions, series: series, type: donut ? "donut" : "pie", height: height, width: width, isLoading: isLoading, isEmpty: isEmpty, emptyMessage: emptyMessage }));
|
|
141
|
+
return (_jsx(Chart, { "data-kala-component": "charts-donut-chart", className: cn("w-full", className), options: chartOptions, series: series, type: donut ? "donut" : "pie", height: height, width: width, isLoading: isLoading, isEmpty: isEmpty, emptyMessage: emptyMessage }));
|
|
142
142
|
}
|
|
143
143
|
// Convenience alias for Pie charts
|
|
144
144
|
export function PieChart(props) {
|
|
145
|
-
return _jsx(DonutChart, { ...props, donut: false });
|
|
145
|
+
return (_jsx(DonutChart, { "data-kala-component": "charts-pie-chart", ...props, donut: false }));
|
|
146
146
|
}
|
|
@@ -154,5 +154,5 @@ export function LineChart({ series, categories, colors, height = 350, width = "1
|
|
|
154
154
|
subtitle,
|
|
155
155
|
customOptions,
|
|
156
156
|
]);
|
|
157
|
-
return (_jsx(Chart, { className: cn("w-full", className), options: chartOptions, series: series, type: "line", height: height, width: width, isLoading: isLoading, isEmpty: isEmpty, emptyMessage: emptyMessage }));
|
|
157
|
+
return (_jsx(Chart, { "data-kala-component": "charts-line-chart", className: cn("w-full", className), options: chartOptions, series: series, type: "line", height: height, width: width, isLoading: isLoading, isEmpty: isEmpty, emptyMessage: emptyMessage }));
|
|
158
158
|
}
|
|
@@ -111,5 +111,5 @@ export function RadialBarChart({ series, labels, colors, height = 350, width = "
|
|
|
111
111
|
subtitle,
|
|
112
112
|
customOptions,
|
|
113
113
|
]);
|
|
114
|
-
return (_jsx(Chart, { className: cn("w-full", className), options: chartOptions, series: series, type: "radialBar", height: height, width: width, isLoading: isLoading, isEmpty: isEmpty, emptyMessage: emptyMessage }));
|
|
114
|
+
return (_jsx(Chart, { "data-kala-component": "charts-radial-bar-chart", className: cn("w-full", className), options: chartOptions, series: series, type: "radialBar", height: height, width: width, isLoading: isLoading, isEmpty: isEmpty, emptyMessage: emptyMessage }));
|
|
115
115
|
}
|
|
@@ -14,7 +14,7 @@ export function ColumnFilters({ filterableColumns, activeFilters, onFilterChange
|
|
|
14
14
|
return null;
|
|
15
15
|
}
|
|
16
16
|
const hasActiveFilters = activeFilters.length > 0;
|
|
17
|
-
return (_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [filterableColumns.map((column) => {
|
|
17
|
+
return (_jsxs("div", { "data-kala-component": "data-table-column-filters", className: "flex flex-wrap items-center gap-2", children: [filterableColumns.map((column) => {
|
|
18
18
|
const activeFilter = activeFilters.find((f) => f.key === column.key);
|
|
19
19
|
const filterValue = activeFilter?.value;
|
|
20
20
|
if (column.type === "select" && column.options) {
|
|
@@ -64,7 +64,7 @@ export function ColumnHeaderFilter({ column, activeFilters, onFilterChange, onFi
|
|
|
64
64
|
onFilterRemove(column.key);
|
|
65
65
|
setTextValue("");
|
|
66
66
|
};
|
|
67
|
-
return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", size: "sm", className: "h-8 w-8 p-0 hover:bg-muted relative", "aria-label": `Filter by ${column.label}`, children: [_jsx(Filter, { className: `h-4 w-4 ${hasActiveFilter ? "text-primary" : "text-muted-foreground"}` }), hasActiveFilter ? (_jsx(Badge, { className: "absolute -top-1 -right-1 h-4 w-4 p-0 flex items-center justify-center text-[10px]", children: _jsx(Text, { size: "xs", weight: "bold", className: "text-primary-foreground", children: column.type === "text" ? "1" : activeCount }) })) : null] }) }), _jsx(PopoverContent, { className: "w-64 p-3", align: "start", children: _jsxs(Stack, { gap: 3, children: [_jsxs(Flex, { align: "center", justify: "between", children: [_jsxs(Heading, { size: "h6", weight: "medium", className: "text-sm", children: ["Filter by ", column.label] }), hasActiveFilter ? (_jsxs(Button, { variant: "ghost", size: "sm", onClick: handleClear, className: "h-7 px-2 text-xs text-muted-foreground", children: [_jsx(X, { className: "h-3 w-3 mr-1" }), "Clear"] })) : null] }), column.type === "select" && column.options ? (_jsx(Stack, { gap: 2, className: "max-h-[300px] overflow-y-auto", children: column.options.map((option) => {
|
|
67
|
+
return (_jsxs(Popover, { "data-kala-component": "data-table-column-header-filter", open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", size: "sm", className: "h-8 w-8 p-0 hover:bg-muted relative", "aria-label": `Filter by ${column.label}`, children: [_jsx(Filter, { className: `h-4 w-4 ${hasActiveFilter ? "text-primary" : "text-muted-foreground"}` }), hasActiveFilter ? (_jsx(Badge, { className: "absolute -top-1 -right-1 h-4 w-4 p-0 flex items-center justify-center text-[10px]", children: _jsx(Text, { size: "xs", weight: "bold", className: "text-primary-foreground", children: column.type === "text" ? "1" : activeCount }) })) : null] }) }), _jsx(PopoverContent, { className: "w-64 p-3", align: "start", children: _jsxs(Stack, { gap: 3, children: [_jsxs(Flex, { align: "center", justify: "between", children: [_jsxs(Heading, { size: "h6", weight: "medium", className: "text-sm", children: ["Filter by ", column.label] }), hasActiveFilter ? (_jsxs(Button, { variant: "ghost", size: "sm", onClick: handleClear, className: "h-7 px-2 text-xs text-muted-foreground", children: [_jsx(X, { className: "h-3 w-3 mr-1" }), "Clear"] })) : null] }), column.type === "select" && column.options ? (_jsx(Stack, { gap: 2, className: "max-h-[300px] overflow-y-auto", children: column.options.map((option) => {
|
|
68
68
|
const isChecked = selectedValues.includes(option.value);
|
|
69
69
|
return (_jsxs(Flex, { align: "center", gap: 2, children: [_jsx(Checkbox, { id: `${String(column.key)}-${option.value}`, checked: isChecked, onCheckedChange: (checked) => handleCheckboxChange(option.value, checked === true) }), _jsx(Label, { htmlFor: `${String(column.key)}-${option.value}`, className: "text-sm font-normal cursor-pointer flex-1 text-foreground", children: option.label })] }, option.value));
|
|
70
70
|
}) })) : (_jsxs(Stack, { gap: 2, children: [_jsx(Input, { type: "text", placeholder: column.placeholder || `Filter by ${column.label}`, value: textValue, onChange: (e) => setTextValue(e.target.value), onKeyDown: (e) => {
|
|
@@ -9,7 +9,7 @@ import { Checkbox } from "@kala-ui/react/checkbox";
|
|
|
9
9
|
import { cn } from "@kala-ui/react/lib/utils";
|
|
10
10
|
import { TableBody, TableCell, TableRow } from "@kala-ui/react/table";
|
|
11
11
|
export function DataTableBodyRows({ displayData, columns, selection, selectedIds, onSelectRow, onRowClick, compact, striped, hoverable, getRowClassName, getRowAttributes, hasPagination, hasFooter, }) {
|
|
12
|
-
return (_jsx(TableBody, { children: displayData.map((row, rowIndex) => {
|
|
12
|
+
return (_jsx(TableBody, { "data-kala-component": "data-table-body-rows", children: displayData.map((row, rowIndex) => {
|
|
13
13
|
const isSelectable = !selection?.isRowSelectable || selection.isRowSelectable(row);
|
|
14
14
|
const rowId = selection?.getRowId(row);
|
|
15
15
|
const isSelected = rowId ? selectedIds.has(rowId) : false;
|
|
@@ -13,7 +13,7 @@ import { X } from "lucide-react";
|
|
|
13
13
|
export function DataTableFilterChips({ filterConfigs, filterableColumns, onSetFilter, onRemoveFilter, onClearFilters, }) {
|
|
14
14
|
if (filterConfigs.length === 0)
|
|
15
15
|
return null;
|
|
16
|
-
return (_jsxs(Flex, { align: "center", gap: 2, className: "mb-4 flex-wrap", children: [_jsx(Text, { size: "sm", className: "text-muted-foreground", children: "Active filters:" }), filterConfigs.map((filter) => {
|
|
16
|
+
return (_jsxs(Flex, { "data-kala-component": "data-table-filter-chips", align: "center", gap: 2, className: "mb-4 flex-wrap", children: [_jsx(Text, { size: "sm", className: "text-muted-foreground", children: "Active filters:" }), filterConfigs.map((filter) => {
|
|
17
17
|
const column = filterableColumns.find((fc) => fc.key === filter.key);
|
|
18
18
|
if (!column)
|
|
19
19
|
return null;
|
|
@@ -13,7 +13,7 @@ import { TableHead, TableHeader, TableRow } from "@kala-ui/react/table";
|
|
|
13
13
|
import { ChevronDown, ChevronUp } from "lucide-react";
|
|
14
14
|
import { ColumnHeaderFilter } from "./column-header-filter.js";
|
|
15
15
|
export function DataTableHeaderRow({ columns, selection, sortConfig, onToggleSort, filterableColumns, filterConfigs, onSetFilter, onRemoveFilter, isAllSelected, isSomeSelected, onSelectAll, stickyHeader, }) {
|
|
16
|
-
return (_jsx(TableHeader, { className: stickyHeader
|
|
16
|
+
return (_jsx(TableHeader, { "data-kala-component": "data-table-header-row", className: stickyHeader
|
|
17
17
|
? "sticky top-0 z-1 bg-muted/95 backdrop-blur-sm [&_tr]:border-0"
|
|
18
18
|
: "[&_tr]:border-0", children: _jsxs(TableRow, { className: "border-0", children: [selection?.enabled && (_jsx(TableHead, { className: stickyHeader
|
|
19
19
|
? "w-12 rounded-tl-lg border-b border-border-strong/50"
|
|
@@ -15,7 +15,7 @@ import { Select, SelectTrigger, SelectValue } from "@kala-ui/react/select";
|
|
|
15
15
|
import { Skeleton } from "@kala-ui/react/skeleton";
|
|
16
16
|
import { TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@kala-ui/react/table";
|
|
17
17
|
export function DataTableSkeleton({ rows = 5, columns, showSearch = false, showFilters = false, filterCount = 3, showPagination = false, showSelection = false, showBulkActions = false, stickyHeader = true, stickyFooter = false, }) {
|
|
18
|
-
return (_jsxs("div", { className: "flex flex-col", children: [showSearch && (_jsxs("div", { className: "flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 mb-4 shrink-0", children: [_jsx("div", { className: "flex-1 max-w-sm", children: _jsx(Input, { type: "search", placeholder: "Search...", disabled: true, className: "opacity-50" }) }), showBulkActions && (_jsxs("div", { className: "flex items-center gap-2 flex-wrap min-h-[36px]", children: [_jsx("span", { className: "text-sm text-muted-foreground", children: "0 selected" }), _jsx(Button, { variant: "outline", size: "sm", disabled: true, className: "opacity-50", children: "Edit" }), _jsx(Button, { variant: "outline", size: "sm", disabled: true, className: "opacity-50", children: "Delete" })] }))] })), showFilters && (_jsxs("div", { className: "flex flex-wrap items-center gap-2 mb-4 shrink-0", children: [_jsx("span", { className: "text-sm text-muted-foreground", children: "Active filters:" }), Array.from({ length: filterCount }).map((_, i) => (_jsxs(Badge, { color: "secondary", className: "gap-1.5 pl-2 pr-1 py-1 bg-primary/10 text-primary border-primary/20", children: [_jsx("span", { className: "text-xs font-medium", children: "Filter: Value" }), _jsx(Button, { variant: "ghost", size: "sm", disabled: true, className: "h-4 w-4 p-0 hover:bg-primary/20 rounded-sm", children: _jsx(Skeleton, { className: "h-3 w-3" }) })] }, `filter-${i}`))), _jsx(Button, { variant: "ghost", size: "sm", disabled: true, className: "h-7 px-2 text-xs text-muted-foreground hover:text-foreground", children: "Clear all" })] })), _jsx("div", { className: cn("border relative overflow-hidden kala-surface-card", showPagination ? "rounded-t-lg border-b-0" : "rounded-lg", stickyFooter ? "flex-1 min-h-0" : ""), children: _jsx("div", { className: cn("overflow-auto relative", showPagination ? "rounded-t-lg" : "rounded-lg", stickyFooter ? "flex-1 min-h-0 max-h-[70vh]" : "max-h-[600px]"), children: _jsxs("table", { className: "w-full border-separate border-spacing-0 caption-bottom text-sm", children: [_jsx(TableHeader, { className: cn(stickyHeader && "sticky top-0 z-1 bg-muted/95 backdrop-blur-sm", "[&_tr]:border-0"), children: _jsxs(TableRow, { className: "border-0", children: [showSelection && (_jsx(TableHead, { className: cn("w-12 rounded-tl-lg", stickyHeader
|
|
18
|
+
return (_jsxs("div", { "data-kala-component": "data-table-skeleton", className: "flex flex-col", children: [showSearch && (_jsxs("div", { className: "flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 mb-4 shrink-0", children: [_jsx("div", { className: "flex-1 max-w-sm", children: _jsx(Input, { type: "search", placeholder: "Search...", disabled: true, className: "opacity-50" }) }), showBulkActions && (_jsxs("div", { className: "flex items-center gap-2 flex-wrap min-h-[36px]", children: [_jsx("span", { className: "text-sm text-muted-foreground", children: "0 selected" }), _jsx(Button, { variant: "outline", size: "sm", disabled: true, className: "opacity-50", children: "Edit" }), _jsx(Button, { variant: "outline", size: "sm", disabled: true, className: "opacity-50", children: "Delete" })] }))] })), showFilters && (_jsxs("div", { className: "flex flex-wrap items-center gap-2 mb-4 shrink-0", children: [_jsx("span", { className: "text-sm text-muted-foreground", children: "Active filters:" }), Array.from({ length: filterCount }).map((_, i) => (_jsxs(Badge, { color: "secondary", className: "gap-1.5 pl-2 pr-1 py-1 bg-primary/10 text-primary border-primary/20", children: [_jsx("span", { className: "text-xs font-medium", children: "Filter: Value" }), _jsx(Button, { variant: "ghost", size: "sm", disabled: true, className: "h-4 w-4 p-0 hover:bg-primary/20 rounded-sm", children: _jsx(Skeleton, { className: "h-3 w-3" }) })] }, `filter-${i}`))), _jsx(Button, { variant: "ghost", size: "sm", disabled: true, className: "h-7 px-2 text-xs text-muted-foreground hover:text-foreground", children: "Clear all" })] })), _jsx("div", { className: cn("border relative overflow-hidden kala-surface-card", showPagination ? "rounded-t-lg border-b-0" : "rounded-lg", stickyFooter ? "flex-1 min-h-0" : ""), children: _jsx("div", { className: cn("overflow-auto relative", showPagination ? "rounded-t-lg" : "rounded-lg", stickyFooter ? "flex-1 min-h-0 max-h-[70vh]" : "max-h-[600px]"), children: _jsxs("table", { className: "w-full border-separate border-spacing-0 caption-bottom text-sm", children: [_jsx(TableHeader, { className: cn(stickyHeader && "sticky top-0 z-1 bg-muted/95 backdrop-blur-sm", "[&_tr]:border-0"), children: _jsxs(TableRow, { className: "border-0", children: [showSelection && (_jsx(TableHead, { className: cn("w-12 rounded-tl-lg", stickyHeader
|
|
19
19
|
? "border-b border-border-strong/50"
|
|
20
20
|
: "border-b"), children: _jsx(Checkbox, { disabled: true }) })), columns.map((column, index) => {
|
|
21
21
|
const isFirst = !showSelection && index === 0;
|
|
@@ -17,7 +17,7 @@ export function DataTableToolbar({ searchConfig, searchQuery, onSearchChange, bu
|
|
|
17
17
|
const showRight = toolbarContent || bulkActions;
|
|
18
18
|
if (!showSearch && !showRight)
|
|
19
19
|
return null;
|
|
20
|
-
return (_jsxs(Flex, { align: "center", justify: "between", gap: 4, className: cn("mb-4 flex-wrap", className), children: [showSearch && (_jsx(Box, { className: "flex-1 max-w-sm", children: _jsx(Input, { type: "text", placeholder: searchConfig?.placeholder, value: searchQuery, onChange: (e) => onSearchChange(e.target.value), prefixIcon: _jsx(Search, { className: "h-4 w-4" }), suffixIcon: searchQuery ? (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => onSearchChange(""), className: "p-1 text-muted-foreground hover:text-foreground h-auto", "aria-label": "Clear search", children: _jsx(X, { className: "h-4 w-4" }) })) : null, "aria-label": searchConfig?.ariaLabel }) })), showRight && (_jsxs(Flex, { align: "center", gap: 2, className: "flex-wrap", children: [toolbarContent, bulkActions && (_jsxs(Flex, { align: "center", gap: 2, className: "flex-wrap min-h-[36px]", children: [_jsxs(Text, { size: "sm", className: "text-muted-foreground", children: [selectedIdsCount, " selected"] }), bulkActions.map((action) => (_jsxs(Button, { variant: action.variant === "destructive"
|
|
20
|
+
return (_jsxs(Flex, { "data-kala-component": "data-table-toolbar", align: "center", justify: "between", gap: 4, className: cn("mb-4 flex-wrap", className), children: [showSearch && (_jsx(Box, { className: "flex-1 max-w-sm", children: _jsx(Input, { type: "text", placeholder: searchConfig?.placeholder, value: searchQuery, onChange: (e) => onSearchChange(e.target.value), prefixIcon: _jsx(Search, { className: "h-4 w-4" }), suffixIcon: searchQuery ? (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => onSearchChange(""), className: "p-1 text-muted-foreground hover:text-foreground h-auto", "aria-label": "Clear search", children: _jsx(X, { className: "h-4 w-4" }) })) : null, "aria-label": searchConfig?.ariaLabel }) })), showRight && (_jsxs(Flex, { align: "center", gap: 2, className: "flex-wrap", children: [toolbarContent, bulkActions && (_jsxs(Flex, { align: "center", gap: 2, className: "flex-wrap min-h-[36px]", children: [_jsxs(Text, { size: "sm", className: "text-muted-foreground", children: [selectedIdsCount, " selected"] }), bulkActions.map((action) => (_jsxs(Button, { variant: action.variant === "destructive"
|
|
21
21
|
? "solid"
|
|
22
22
|
: (action.variant ?? "outline"), color: action.variant === "destructive" ? "destructive" : undefined, size: "sm", onClick: () => action.onClick(selectedRows), disabled: selectedIdsCount === 0 || action.disabled, className: selectedIdsCount === 0
|
|
23
23
|
? "opacity-50 cursor-not-allowed"
|
|
@@ -212,7 +212,7 @@ export function DataTable({ data, columns, searchable, pagination, defaultSort,
|
|
|
212
212
|
// Render loading state
|
|
213
213
|
if (isLoading) {
|
|
214
214
|
if (skeleton) {
|
|
215
|
-
return (_jsx(Stack, { gap: 4, className: className, children: skeleton }));
|
|
215
|
+
return (_jsx(Stack, { "data-kala-component": "data-table", gap: 4, className: className, children: skeleton }));
|
|
216
216
|
}
|
|
217
217
|
const showSearch = !!searchConfig;
|
|
218
218
|
const showFilters = !!filterableColumns;
|
|
@@ -220,7 +220,7 @@ export function DataTable({ data, columns, searchable, pagination, defaultSort,
|
|
|
220
220
|
const showSelection = !!selection?.enabled;
|
|
221
221
|
const showBulkActions = !!bulkActions;
|
|
222
222
|
const skeletonRows = skeletonConfig?.rows ?? loadingConfig?.rows ?? 5;
|
|
223
|
-
return (_jsx(Stack, { gap: 4, className: className, children: _jsx(DataTableSkeleton, { rows: skeletonRows, columns: columns, showSearch: showSearch, showFilters: showFilters, showPagination: showPagination, showSelection: showSelection, showBulkActions: showBulkActions, stickyHeader: stickyHeader, stickyFooter: stickyFooter }) }));
|
|
223
|
+
return (_jsx(Stack, { "data-kala-component": "data-table", gap: 4, className: className, children: _jsx(DataTableSkeleton, { rows: skeletonRows, columns: columns, showSearch: showSearch, showFilters: showFilters, showPagination: showPagination, showSelection: showSelection, showBulkActions: showBulkActions, stickyHeader: stickyHeader, stickyFooter: stickyFooter }) }));
|
|
224
224
|
}
|
|
225
225
|
// Compute empty state config
|
|
226
226
|
const emptyConfig = {
|
|
@@ -230,7 +230,7 @@ export function DataTable({ data, columns, searchable, pagination, defaultSort,
|
|
|
230
230
|
: "There is no data to display at the moment.",
|
|
231
231
|
...emptyState,
|
|
232
232
|
};
|
|
233
|
-
return (_jsxs(Stack, { className: cn(stickyFooter ? "h-full min-h-0" : "", className), "aria-label": ariaLabel, role: ariaLabel ? "region" : undefined, gap: 0, children: [_jsx(DataTableToolbar, { searchConfig: searchConfig, searchQuery: searchQuery, onSearchChange: setSearchQuery, bulkActions: bulkActions, selectedRows: selectedRows, selectedIdsCount: selectedIds.size, toolbarContent: toolbarContent, hasData: data.length > 0, className: stickyFooter ? "shrink-0" : undefined }), filterableColumns && (_jsx("div", { className: stickyFooter ? "shrink-0" : undefined, children: _jsx(DataTableFilterChips, { filterConfigs: filterConfigs, filterableColumns: filterableColumns, onSetFilter: setFilter, onRemoveFilter: removeFilter, onClearFilters: clearFilters }) })), displayData.length === 0 && !filterConfigs.length && !searchQuery ? (_jsx(EmptyState, { title: emptyConfig.title, description: emptyConfig.description, ...(emptyState?.icon && { icon: emptyState.icon }), ...(emptyState?.action && { action: emptyState.action }) })) : (_jsxs(_Fragment, { children: [_jsxs(Box, { className: cn("border relative overflow-hidden kala-surface-card", stickyFooter && paginationConfig && displayData.length > 0
|
|
233
|
+
return (_jsxs(Stack, { "data-kala-component": "data-table", className: cn(stickyFooter ? "h-full min-h-0" : "", className), "aria-label": ariaLabel, role: ariaLabel ? "region" : undefined, gap: 0, children: [_jsx(DataTableToolbar, { searchConfig: searchConfig, searchQuery: searchQuery, onSearchChange: setSearchQuery, bulkActions: bulkActions, selectedRows: selectedRows, selectedIdsCount: selectedIds.size, toolbarContent: toolbarContent, hasData: data.length > 0, className: stickyFooter ? "shrink-0" : undefined }), filterableColumns && (_jsx("div", { className: stickyFooter ? "shrink-0" : undefined, children: _jsx(DataTableFilterChips, { filterConfigs: filterConfigs, filterableColumns: filterableColumns, onSetFilter: setFilter, onRemoveFilter: removeFilter, onClearFilters: clearFilters }) })), displayData.length === 0 && !filterConfigs.length && !searchQuery ? (_jsx(EmptyState, { title: emptyConfig.title, description: emptyConfig.description, ...(emptyState?.icon && { icon: emptyState.icon }), ...(emptyState?.action && { action: emptyState.action }) })) : (_jsxs(_Fragment, { children: [_jsxs(Box, { className: cn("border relative overflow-hidden kala-surface-card", stickyFooter && paginationConfig && displayData.length > 0
|
|
234
234
|
? "flex-1 min-h-0"
|
|
235
235
|
: "", bordered && "border-2", paginationConfig && displayData.length > 0
|
|
236
236
|
? "rounded-t-lg border-b-0"
|
|
@@ -19,5 +19,5 @@ export function PaginationNav({ currentPage, totalPages, hasPreviousPage, hasNex
|
|
|
19
19
|
boundaries: 1,
|
|
20
20
|
onPageChange,
|
|
21
21
|
});
|
|
22
|
-
return (_jsxs(Flex, { align: "center", justify: "center", wrap: "wrap", gap: 1, children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: onPreviousPage, disabled: !hasPreviousPage, className: "h-8 w-8 p-0", children: [_jsx(ChevronLeft, { className: "h-4 w-4" }), _jsx(Box, { as: "span", className: "sr-only", children: "Previous page" })] }), range.map((item, index) => item === DOTS ? (_jsx(Flex, { align: "center", justify: "center", className: "h-8 w-8", "aria-hidden": true, children: _jsx(MoreHorizontal, { className: "h-4 w-4 text-muted-foreground" }) }, `dots-${index}`)) : (_jsx(Button, { variant: item === currentPage ? "solid" : "outline", size: "sm", onClick: () => onPageChange(item), className: "h-8 w-8 p-0 text-xs", "aria-label": `Page ${item}`, "aria-current": item === currentPage ? "page" : undefined, children: item }, item))), _jsxs(Button, { variant: "outline", size: "sm", onClick: onNextPage, disabled: !hasNextPage, className: "h-8 w-8 p-0", children: [_jsx(ChevronRight, { className: "h-4 w-4" }), _jsx(Box, { as: "span", className: "sr-only", children: "Next page" })] })] }));
|
|
22
|
+
return (_jsxs(Flex, { "data-kala-component": "data-table-pagination-nav", align: "center", justify: "center", wrap: "wrap", gap: 1, children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: onPreviousPage, disabled: !hasPreviousPage, className: "h-8 w-8 p-0", children: [_jsx(ChevronLeft, { className: "h-4 w-4" }), _jsx(Box, { as: "span", className: "sr-only", children: "Previous page" })] }), range.map((item, index) => item === DOTS ? (_jsx(Flex, { align: "center", justify: "center", className: "h-8 w-8", "aria-hidden": true, children: _jsx(MoreHorizontal, { className: "h-4 w-4 text-muted-foreground" }) }, `dots-${index}`)) : (_jsx(Button, { variant: item === currentPage ? "solid" : "outline", size: "sm", onClick: () => onPageChange(item), className: "h-8 w-8 p-0 text-xs", "aria-label": `Page ${item}`, "aria-current": item === currentPage ? "page" : undefined, children: item }, item))), _jsxs(Button, { variant: "outline", size: "sm", onClick: onNextPage, disabled: !hasNextPage, className: "h-8 w-8 p-0", children: [_jsx(ChevronRight, { className: "h-4 w-4" }), _jsx(Box, { as: "span", className: "sr-only", children: "Next page" })] })] }));
|
|
23
23
|
}
|
|
@@ -54,7 +54,7 @@ function createDragDropSensors(options = {}) {
|
|
|
54
54
|
function DragDropContext({ children, sensors: sensorsProp, useSensors: useSensorsOptions, ...props }) {
|
|
55
55
|
const defaultSensors = createDragDropSensors(useSensorsOptions);
|
|
56
56
|
const sensors = sensorsProp ?? defaultSensors;
|
|
57
|
-
return (_jsx(DndKitContext, { sensors: sensors, ...props, children: children }));
|
|
57
|
+
return (_jsx(DndKitContext, { "data-kala-component": "dnd-drag-drop-context", sensors: sensors, ...props, children: children }));
|
|
58
58
|
}
|
|
59
59
|
function Droppable({ ref, children, className, id, disabled = false, ...props }) {
|
|
60
60
|
const { isOver, setNodeRef } = useDroppable({
|
|
@@ -64,7 +64,7 @@ function Droppable({ ref, children, className, id, disabled = false, ...props })
|
|
|
64
64
|
const content = typeof children === "function"
|
|
65
65
|
? children({ isOver, setNodeRef })
|
|
66
66
|
: children;
|
|
67
|
-
return (_jsx("div", { ref: useMergedRef(ref, setNodeRef), className: cn(className), ...props, children: content }));
|
|
67
|
+
return (_jsx("div", { "data-kala-component": "dnd-droppable", ref: useMergedRef(ref, setNodeRef), className: cn(className), ...props, children: content }));
|
|
68
68
|
}
|
|
69
69
|
function Draggable({ ref, children, className, id, disabled = false, ...props }) {
|
|
70
70
|
const { attributes, listeners, setNodeRef, transform, isDragging } = useDraggable({
|
|
@@ -79,11 +79,11 @@ function Draggable({ ref, children, className, id, disabled = false, ...props })
|
|
|
79
79
|
transform: CSS.Translate.toString(transform),
|
|
80
80
|
}
|
|
81
81
|
: undefined;
|
|
82
|
-
return (_jsx("div", { ref: useMergedRef(ref, setNodeRef), style: style, className: cn(className), ...attributes, ...listeners, ...props, children: content }));
|
|
82
|
+
return (_jsx("div", { "data-kala-component": "dnd-draggable", ref: useMergedRef(ref, setNodeRef), style: style, className: cn(className), ...attributes, ...listeners, ...props, children: content }));
|
|
83
83
|
}
|
|
84
84
|
function SortableContext({ children, items, strategy = verticalListSortingStrategy, }) {
|
|
85
85
|
const itemIds = React.useMemo(() => items.map((item) => typeof item === "object" && "id" in item ? item.id : item), [items]);
|
|
86
|
-
return (_jsx(SortableContextKit, { items: itemIds, strategy: strategy, children: children }));
|
|
86
|
+
return (_jsx(SortableContextKit, { "data-kala-component": "dnd-sortable-context", items: itemIds, strategy: strategy, children: children }));
|
|
87
87
|
}
|
|
88
88
|
const SortableHandleContext = React.createContext(null);
|
|
89
89
|
function SortableItem({ ref, children, className, id, disabled = false, handle = false, ...props }) {
|
|
@@ -108,7 +108,7 @@ function SortableItem({ ref, children, className, id, disabled = false, handle =
|
|
|
108
108
|
transition,
|
|
109
109
|
zIndex: isDragging ? 9999 : undefined,
|
|
110
110
|
};
|
|
111
|
-
return (_jsx(SortableHandleContext.Provider, { value: { attributes, listeners, setActivatorNodeRef }, children: _jsx("div", { ref: useMergedRef(ref, setNodeRef), style: style, className: cn(className), ...attributes, ...(handle ? {} : listeners), ...props, children: content }) }));
|
|
111
|
+
return (_jsx(SortableHandleContext.Provider, { "data-kala-component": "dnd-sortable-item", value: { attributes, listeners, setActivatorNodeRef }, children: _jsx("div", { ref: useMergedRef(ref, setNodeRef), style: style, className: cn(className), ...attributes, ...(handle ? {} : listeners), ...props, children: content }) }));
|
|
112
112
|
}
|
|
113
113
|
function SortableHandle({ ref, children, className, ...props }) {
|
|
114
114
|
const sortable = React.useContext(SortableHandleContext);
|
|
@@ -116,10 +116,10 @@ function SortableHandle({ ref, children, className, ...props }) {
|
|
|
116
116
|
console.warn("SortableHandle must be rendered inside a SortableItem to receive drag listeners.");
|
|
117
117
|
}
|
|
118
118
|
const content = typeof children === "function" ? children(sortable?.listeners) : children;
|
|
119
|
-
return (_jsx("div", { ref: useMergedRef(ref, sortable?.setActivatorNodeRef), className: cn("cursor-grab active:cursor-grabbing", className), ...(sortable?.attributes ?? {}), ...(sortable?.listeners ?? {}), ...props, children: content }));
|
|
119
|
+
return (_jsx("div", { "data-kala-component": "dnd-sortable-handle", ref: useMergedRef(ref, sortable?.setActivatorNodeRef), className: cn("cursor-grab active:cursor-grabbing", className), ...(sortable?.attributes ?? {}), ...(sortable?.listeners ?? {}), ...props, children: content }));
|
|
120
120
|
}
|
|
121
121
|
function DragOverlayComponent({ children, className, dropAnimation, ...props }) {
|
|
122
|
-
return (_jsx(DragOverlay, { dropAnimation: dropAnimation, ...props, children: children ? _jsx("div", { className: cn(className), children: children }) : null }));
|
|
122
|
+
return (_jsx(DragOverlay, { "data-kala-component": "dnd-drag-overlay-component", dropAnimation: dropAnimation, ...props, children: children ? _jsx("div", { className: cn(className), children: children }) : null }));
|
|
123
123
|
}
|
|
124
124
|
export { createDragDropSensors as useDragDropSensors, DragDropContext, Draggable, DragOverlayComponent, Droppable,
|
|
125
125
|
// Re-export sensors
|
|
@@ -4,5 +4,5 @@ import { cn } from "@kala-ui/react/lib/utils";
|
|
|
4
4
|
export function Footer({ ref, className, linkSections = [], socialLinks = [], copyright, children, centered = false, ...props }) {
|
|
5
5
|
const currentYear = new Date().getFullYear();
|
|
6
6
|
const copyrightText = copyright || `© ${currentYear} All rights reserved.`;
|
|
7
|
-
return (_jsx("footer", {
|
|
7
|
+
return (_jsx("footer", { "data-kala-component": "footer", ref: ref, className: cn("w-full bg-muted text-muted-foreground border-t", className), ...props, children: _jsxs("div", { className: "container mx-auto px-4 py-8 md:py-12", children: [centered && children && (_jsx("div", { className: "mb-12 flex flex-col items-center text-center", children: children })), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8 mb-8", children: [linkSections.map((section) => (_jsxs("nav", { "aria-label": `${section.title} links`, children: [_jsx("h3", { className: "text-foreground font-semibold text-sm uppercase tracking-wider mb-4", children: section.title }), _jsx("ul", { className: "space-y-2", children: section.links.map((link) => (_jsx("li", { children: _jsx("a", { href: link.href, className: "text-muted-foreground hover:text-primary transition-colors text-sm", children: link.label }) }, `${link.href}-${link.label}`))) })] }, section.title))), !centered && children && (_jsx("div", { className: "md:col-span-2 lg:col-span-1", children: children }))] }), _jsxs("div", { className: cn("border-t pt-8 flex flex-col gap-4", !centered && "md:flex-row md:justify-between md:items-center", centered && "items-center"), children: [_jsx("div", { className: "text-sm text-muted-foreground order-2 md:order-1", children: copyrightText }), socialLinks.length > 0 && (_jsx("div", { className: "flex items-center gap-4 order-1 md:order-2", children: socialLinks.map((social) => (_jsx("a", { href: social.href, target: "_blank", rel: "noopener noreferrer", className: "text-muted-foreground hover:text-primary transition-colors", "aria-label": social.name, children: social.icon }, social.href))) }))] })] }) }));
|
|
8
8
|
}
|
|
@@ -19,5 +19,5 @@ import { Skeleton } from "@kala-ui/react/skeleton";
|
|
|
19
19
|
* ```
|
|
20
20
|
*/
|
|
21
21
|
export function HeaderSkeleton({ showNavigation = true, navItemCount = 4, showSearch = false, showNotifications = false, showUserMenu = true, className, "data-testid": dataTestId, }) {
|
|
22
|
-
return (_jsxs("header", { "data-testid": dataTestId || "header-skeleton", className: cn("flex items-center justify-between p-4 border-b bg-background", className), children: [_jsx(Skeleton, { className: "h-8 w-32 flex-shrink-0" }), showNavigation && (_jsx("div", { className: "hidden md:flex gap-6 flex-1 mx-8", children: Array.from({ length: navItemCount }).map((_, i) => (_jsx(Skeleton, { className: "h-4 w-20" }, `nav-${i}`))) })), _jsxs("div", { className: "flex items-center gap-3 flex-shrink-0", children: [showSearch && (_jsx(Skeleton, { className: "hidden sm:block h-9 w-64 rounded-full" })), showNotifications && _jsx(Skeleton, { className: "h-9 w-9 rounded-full" }), showUserMenu && _jsx(Skeleton, { className: "h-9 w-9 rounded-full" }), _jsx(Skeleton, { className: "h-9 w-9 md:hidden" })] })] }));
|
|
22
|
+
return (_jsxs("header", { "data-kala-component": "header-skeleton", "data-testid": dataTestId || "header-skeleton", className: cn("flex items-center justify-between p-4 border-b bg-background", className), children: [_jsx(Skeleton, { className: "h-8 w-32 flex-shrink-0" }), showNavigation && (_jsx("div", { className: "hidden md:flex gap-6 flex-1 mx-8", children: Array.from({ length: navItemCount }).map((_, i) => (_jsx(Skeleton, { className: "h-4 w-20" }, `nav-${i}`))) })), _jsxs("div", { className: "flex items-center gap-3 flex-shrink-0", children: [showSearch && (_jsx(Skeleton, { className: "hidden sm:block h-9 w-64 rounded-full" })), showNotifications && _jsx(Skeleton, { className: "h-9 w-9 rounded-full" }), showUserMenu && _jsx(Skeleton, { className: "h-9 w-9 rounded-full" }), _jsx(Skeleton, { className: "h-9 w-9 md:hidden" })] })] }));
|
|
23
23
|
}
|
|
@@ -47,7 +47,7 @@ export function Header({ ref, className, logo, navLinks = [], userMenu, userProf
|
|
|
47
47
|
return next;
|
|
48
48
|
});
|
|
49
49
|
};
|
|
50
|
-
return (_jsxs(Box, {
|
|
50
|
+
return (_jsxs(Box, { "data-kala-component": "header", as: "header", ref: ref, className: cn("w-full bg-popover border-b sticky top-0 z-20 kala-surface-popover", className), ...props, children: [_jsx(Box, { className: "container mx-auto px-3 sm:px-4 lg:px-6 xl:px-8", children: _jsxs(Flex, { align: "center", justify: "between", className: "h-14 sm:h-16 gap-2 sm:gap-4", children: [_jsx(Box, { className: "shrink-0 min-w-0 max-w-[120px] sm:max-w-none", children: logo }), _jsx(Flex, { align: "center", gap: variant === "default" ? 3 : 6, className: cn("hidden lg:flex", variant === "default" ? "flex-1" : ""), children: _jsx(Box, { className: cn("flex", variant === "default" ? "mx-auto" : ""), children: _jsxs(NavigationMenu, { children: [_jsx(NavigationMenuList, { children: navLinks.map((link) => (_jsx(NavigationMenuItem, { children: link.children ? (_jsxs(_Fragment, { children: [_jsx(NavigationMenuTrigger, { className: cn("text-sm lg:text-base", link.active && "text-primary"), children: link.label }), _jsx(NavigationMenuContent, { children: _jsx(Box, { className: "py-2", children: _jsx(List, { divided: false, className: cn("bg-transparent border-none", link.children.length <= 3
|
|
51
51
|
? "w-[220px]"
|
|
52
52
|
: "w-[440px] grid grid-cols-2"), children: link.children.map((child) => (_jsx(Box, { as: "li", children: _jsx(NavigationMenuLink, { asChild: true, children: _jsx(Box, { as: "a", href: child.href, className: cn("block px-4 py-2.5 text-sm font-normal no-underline outline-none transition-colors", "text-foreground", "hover:bg-accent hover:text-accent-foreground", "focus-visible:bg-accent focus-visible:text-accent-foreground", child.active &&
|
|
53
53
|
"text-primary bg-primary/10"), children: child.label }) }) }, child.href))) }) }) })] })) : (_jsx(NavigationMenuLink, { href: link.href, className: cn("px-4 py-2 text-sm lg:text-base", link.active && "text-primary"), "aria-current": link.active ? "page" : undefined, children: link.label })) }, link.href))) }), _jsx(NavigationMenuViewport, {})] }) }) }), _jsxs(Flex, { align: "center", className: "gap-1.5 sm:gap-2 lg:gap-3 shrink-0", children: [searchBar && (_jsx(Box, { className: "hidden lg:block w-48 xl:w-64", children: searchBar })), languageSwitcher && (_jsx(Box, { className: "hidden lg:block", children: languageSwitcher })), themeSwitcher && (_jsx(Box, { className: "hidden lg:block", children: themeSwitcher })), notifications && (_jsx(Box, { className: "hidden lg:block", children: notifications })), userMenu && _jsx(Box, { className: "hidden lg:block", children: userMenu }), onMobileMenuToggle && (_jsx(Button, { variant: "ghost", size: "icon", className: "lg:hidden h-9 w-9 sm:h-10 sm:w-10", onClick: onMobileMenuToggle, "aria-label": "Toggle mobile menu", "aria-expanded": isMobileMenuOpen, children: isMobileMenuOpen ? (_jsx(X, { className: "w-5 h-5 sm:w-6 sm:h-6" })) : (_jsx(Menu, { className: "w-5 h-5 sm:w-6 sm:h-6" })) }))] })] }) }), isMobileMenuOpen &&
|
|
@@ -21,7 +21,7 @@ import * as React from "react";
|
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
23
|
export function MetricCardSkeleton({ variant = "default", showIcon = false, showChange = true, className, "data-testid": dataTestId, }) {
|
|
24
|
-
return (_jsx(Card, { ref: React.createRef(), "data-testid": dataTestId || "metric-card-skeleton", className: cn(className), children: _jsxs("div", { className: "p-6", children: [_jsxs("div", { className: "flex items-center justify-between mb-4", children: [_jsx(Skeleton, { className: "h-4 w-24" }), variant === "withIcon" || showIcon ? (_jsx(SkeletonCircle, { size: "1.5rem", className: "rounded-md" })) : null] }), _jsx("div", { className: "mb-3", children: _jsx(Skeleton, { className: "h-10 w-32" }) }), (variant === "default" || showChange) && (_jsx("div", { className: "text-sm", children: _jsx(Skeleton, { className: "h-4 w-32" }) }))] }) }));
|
|
24
|
+
return (_jsx(Card, { "data-kala-component": "metric-card-skeleton", ref: React.createRef(), "data-testid": dataTestId || "metric-card-skeleton", className: cn(className), children: _jsxs("div", { className: "p-6", children: [_jsxs("div", { className: "flex items-center justify-between mb-4", children: [_jsx(Skeleton, { className: "h-4 w-24" }), variant === "withIcon" || showIcon ? (_jsx(SkeletonCircle, { size: "1.5rem", className: "rounded-md" })) : null] }), _jsx("div", { className: "mb-3", children: _jsx(Skeleton, { className: "h-10 w-32" }) }), (variant === "default" || showChange) && (_jsx("div", { className: "text-sm", children: _jsx(Skeleton, { className: "h-4 w-32" }) }))] }) }));
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
27
|
* Compound component: MetricCard.Skeleton
|
|
@@ -6,9 +6,9 @@ import { MetricCardSkeleton } from "./metric-card-skeleton.js";
|
|
|
6
6
|
function MetricCard({ ref, title, value, icon, change, changeLabel, subtitle, className, color = "muted", isLoading = false, skeletonConfig, skeleton, ...props }) {
|
|
7
7
|
if (isLoading) {
|
|
8
8
|
if (skeleton) {
|
|
9
|
-
return (_jsx(Card, {
|
|
9
|
+
return (_jsx(Card, { "data-kala-component": "metric-card", ref: ref, className: cn(className), ...props, children: skeleton }));
|
|
10
10
|
}
|
|
11
|
-
return _jsx(MetricCardSkeleton, { className: className, ...skeletonConfig });
|
|
11
|
+
return (_jsx(MetricCardSkeleton, { "data-kala-component": "metric-card", className: className, ...skeletonConfig }));
|
|
12
12
|
}
|
|
13
13
|
const getChangeColor = (changeValue) => {
|
|
14
14
|
if (changeValue > 0)
|
|
@@ -50,6 +50,6 @@ function MetricCard({ ref, title, value, icon, change, changeLabel, subtitle, cl
|
|
|
50
50
|
}
|
|
51
51
|
};
|
|
52
52
|
const isColorful = color !== "muted" || className?.includes("bg-");
|
|
53
|
-
return (_jsx(Card, {
|
|
53
|
+
return (_jsx(Card, { "data-kala-component": "metric-card", ref: ref, className: cn(getColorStyles(), className), ...props, children: _jsxs("div", { className: "p-6", children: [_jsxs("div", { className: "flex items-center justify-between mb-4", children: [_jsx("h6", { className: cn("text-xs font-semibold uppercase tracking-wide", isColorful ? "opacity-90" : "text-muted-foreground"), children: title }), icon && (_jsx("div", { className: isColorful ? "opacity-70" : "text-muted-foreground", "aria-hidden": "true", children: icon }))] }), _jsx("div", { className: "mb-3", children: _jsxs("div", { className: cn("text-4xl font-bold leading-none", isColorful ? "" : "text-card-foreground"), children: [_jsxs("span", { className: "sr-only", children: [title, ":", " ", typeof value === "number" ? value.toLocaleString() : value] }), typeof value === "number" ? value.toLocaleString() : value] }) }), (change !== undefined || subtitle) && (_jsx("div", { className: "text-sm", children: change !== undefined ? (_jsxs("div", { className: cn("flex items-center gap-1", isColorful ? "opacity-90" : getChangeColor(change)), children: [_jsxs("span", { className: "sr-only", children: ["Change: ", getChangeText(change)] }), _jsx("span", { className: "inline-block", "aria-hidden": "true", children: getChangeIcon(change) }), _jsx("span", { children: getChangeText(change) })] })) : subtitle ? (_jsx("div", { className: cn(isColorful ? "opacity-80" : "text-muted-foreground"), children: subtitle })) : null }))] }) }));
|
|
54
54
|
}
|
|
55
55
|
export { MetricCard };
|
|
@@ -16,7 +16,7 @@ export function NavLink({ ref, className, label, description, icon, rightSection
|
|
|
16
16
|
}
|
|
17
17
|
onClick?.(e);
|
|
18
18
|
};
|
|
19
|
-
return (_jsxs(_Fragment, { children: [_jsxs("button", { ref: ref, className: cn("flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50", active ? "bg-accent text-accent-foreground" : "text-muted-foreground", className), onClick: handleToggle, ...props, children: [icon && (_jsx("span", { className: "flex items-center justify-center", children: icon })), _jsxs("div", { className: "flex flex-1 flex-col items-start overflow-hidden", children: [_jsx("span", { className: "truncate", children: label }), description && (_jsx("span", { className: "truncate text-xs text-muted-foreground font-normal", children: description }))] }), (rightSection || children) && (_jsx("span", { className: cn("flex items-center justify-center text-muted-foreground transition-transform duration-200", children &&
|
|
19
|
+
return (_jsxs(_Fragment, { children: [_jsxs("button", { "data-kala-component": "nav-link", ref: ref, className: cn("flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50", active ? "bg-accent text-accent-foreground" : "text-muted-foreground", className), onClick: handleToggle, ...props, children: [icon && (_jsx("span", { className: "flex items-center justify-center", children: icon })), _jsxs("div", { className: "flex flex-1 flex-col items-start overflow-hidden", children: [_jsx("span", { className: "truncate", children: label }), description && (_jsx("span", { className: "truncate text-xs text-muted-foreground font-normal", children: description }))] }), (rightSection || children) && (_jsx("span", { className: cn("flex items-center justify-center text-muted-foreground transition-transform duration-200", children &&
|
|
20
20
|
!disableRightSectionRotation &&
|
|
21
21
|
isOpened &&
|
|
22
22
|
"rotate-90"), children: rightSection || (children && _jsx(ChevronRight, { className: "h-4 w-4" })) }))] }), children && isOpened && (_jsx("div", { className: cn("flex flex-col gap-1", indent && "pl-4"), children: children }))] }));
|
|
@@ -19,5 +19,5 @@ import { Skeleton } from "@kala-ui/react/skeleton";
|
|
|
19
19
|
* ```
|
|
20
20
|
*/
|
|
21
21
|
export function NavigationSkeleton({ orientation = "horizontal", itemCount = 5, showIcons = false, showBadges = false, nested = false, className, "data-testid": dataTestId, }) {
|
|
22
|
-
return (_jsxs("nav", { "data-testid": dataTestId || "navigation-skeleton", className: cn("flex", orientation === "vertical" ? "flex-col gap-2" : "flex-row gap-6", className), "aria-label": "Loading navigation", "aria-busy": "true", children: [Array.from({ length: itemCount }).map((_, i) => (_jsxs("div", { className: cn("flex items-center", orientation === "vertical" ? "gap-3 px-3 py-2" : "gap-2"), children: [showIcons && (_jsx(Skeleton, { className: cn("flex-shrink-0", orientation === "vertical" ? "h-5 w-5" : "h-4 w-4") })), _jsx(Skeleton, { className: cn("h-4", orientation === "vertical" ? "w-24 flex-1" : "w-20") }), showBadges && (_jsx(Skeleton, { className: "h-4 w-4 rounded-full ml-auto flex-shrink-0" }))] }, `nav-${i}`))), nested && orientation === "vertical" && (_jsx("div", { className: "ml-8 space-y-2", children: Array.from({ length: 2 }).map((_, i) => (_jsxs("div", { className: "flex items-center gap-3 px-3 py-2", children: [showIcons && _jsx(Skeleton, { className: "h-4 w-4 flex-shrink-0" }), _jsx(Skeleton, { className: "h-4 w-20" })] }, `nested-${i}`))) }))] }));
|
|
22
|
+
return (_jsxs("nav", { "data-kala-component": "navigation-skeleton", "data-testid": dataTestId || "navigation-skeleton", className: cn("flex", orientation === "vertical" ? "flex-col gap-2" : "flex-row gap-6", className), "aria-label": "Loading navigation", "aria-busy": "true", children: [Array.from({ length: itemCount }).map((_, i) => (_jsxs("div", { className: cn("flex items-center", orientation === "vertical" ? "gap-3 px-3 py-2" : "gap-2"), children: [showIcons && (_jsx(Skeleton, { className: cn("flex-shrink-0", orientation === "vertical" ? "h-5 w-5" : "h-4 w-4") })), _jsx(Skeleton, { className: cn("h-4", orientation === "vertical" ? "w-24 flex-1" : "w-20") }), showBadges && (_jsx(Skeleton, { className: "h-4 w-4 rounded-full ml-auto flex-shrink-0" }))] }, `nav-${i}`))), nested && orientation === "vertical" && (_jsx("div", { className: "ml-8 space-y-2", children: Array.from({ length: 2 }).map((_, i) => (_jsxs("div", { className: "flex items-center gap-3 px-3 py-2", children: [showIcons && _jsx(Skeleton, { className: "h-4 w-4 flex-shrink-0" }), _jsx(Skeleton, { className: "h-4 w-20" })] }, `nested-${i}`))) }))] }));
|
|
23
23
|
}
|
|
@@ -22,7 +22,7 @@ export function Navigation({ ref, className, links, orientation = "horizontal",
|
|
|
22
22
|
}, [isMobileOpen]);
|
|
23
23
|
// Vertical desktop navigation (explicit opt-in via orientation)
|
|
24
24
|
if (orientation === "vertical") {
|
|
25
|
-
return (_jsx("nav", { "data-
|
|
25
|
+
return (_jsx("nav", { "data-kala-component": "navigation", className: cn(orientation === "vertical"
|
|
26
26
|
? "hidden md:flex md:flex-col gap-2"
|
|
27
27
|
: "flex md:hidden flex-col gap-2", className), "aria-label": "Navigation", ...props, ref: ref, children: links.map((link) => (_jsx("a", { href: link.href, className: cn("text-sm font-medium px-3 py-2 rounded-md transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring", isActive(link.href)
|
|
28
28
|
? "bg-accent text-accent-foreground"
|
|
@@ -30,15 +30,15 @@ export function Navigation({ ref, className, links, orientation = "horizontal",
|
|
|
30
30
|
}
|
|
31
31
|
// Horizontal on desktop + stacked vertical list on mobile
|
|
32
32
|
if (mobileLayout === "vertical") {
|
|
33
|
-
return (_jsxs(_Fragment, { children: [_jsx("nav", { "data-
|
|
33
|
+
return (_jsxs(_Fragment, { children: [_jsx("nav", { "data-kala-component": "navigation", className: cn("hidden md:flex flex-row items-center gap-6", className), "aria-label": "Main navigation", ...props, ref: ref, children: links.map((link) => (_jsx("a", { href: link.href, className: cn("text-sm font-medium transition-colors hover:text-primary outline-none focus-visible:underline", isActive(link.href) ? "text-primary" : "text-foreground"), "aria-current": isActive(link.href) ? "page" : undefined, children: link.label }, link.href))) }), _jsx("nav", { "data-kala-component": "navigation", className: cn("flex md:hidden flex-col gap-2", className), "aria-label": "Mobile navigation", children: links.map((link) => (_jsx("a", { href: link.href, className: cn("text-sm font-medium px-3 py-2 rounded-md transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring", isActive(link.href)
|
|
34
34
|
? "bg-accent text-accent-foreground"
|
|
35
35
|
: "text-foreground hover:bg-accent hover:text-accent-foreground"), "aria-current": isActive(link.href) ? "page" : undefined, children: link.label }, link.href))) })] }));
|
|
36
36
|
}
|
|
37
37
|
// Horizontal desktop + dropdown mobile
|
|
38
|
-
return (_jsxs(_Fragment, { children: [_jsx("nav", { "data-
|
|
38
|
+
return (_jsxs(_Fragment, { children: [_jsx("nav", { "data-kala-component": "navigation", className: cn("hidden md:flex flex-row items-center gap-6", className), "aria-label": "Main navigation", ...props, ref: ref, children: links.map((link) => (_jsx("a", { href: link.href, className: cn("text-sm font-medium transition-colors hover:text-primary outline-none focus-visible:underline", isActive(link.href) ? "text-primary" : "text-foreground"), "aria-current": isActive(link.href) ? "page" : undefined, children: link.label }, link.href))) }), _jsxs("div", { "data-kala-component": "navigation", className: "md:hidden", "data-mobile-nav": true, children: [_jsxs("button", { type: "button", onClick: (e) => {
|
|
39
39
|
e.stopPropagation();
|
|
40
40
|
setIsMobileOpen(!isMobileOpen);
|
|
41
|
-
}, className: "flex items-center gap-2 text-sm font-medium text-foreground hover:text-primary transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-md px-2 py-1", "aria-label": "Toggle mobile navigation", "aria-expanded": isMobileOpen, children: ["Menu", _jsx(ChevronDown, { className: cn("size-4 transition-transform", isMobileOpen && "rotate-180") })] }), isMobileOpen && (_jsx("nav", {
|
|
41
|
+
}, className: "flex items-center gap-2 text-sm font-medium text-foreground hover:text-primary transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-md px-2 py-1", "aria-label": "Toggle mobile navigation", "aria-expanded": isMobileOpen, children: ["Menu", _jsx(ChevronDown, { className: cn("size-4 transition-transform", isMobileOpen && "rotate-180") })] }), isMobileOpen && (_jsx("nav", { className: "mt-2 flex flex-col gap-2 bg-popover border rounded-md p-3 text-popover-foreground kala-surface-popover", "aria-label": "Mobile navigation", onClick: (e) => e.stopPropagation(), children: links.map((link) => (_jsx("a", { href: link.href, onClick: () => setIsMobileOpen(false), className: cn("text-sm font-medium px-3 py-2 rounded-md transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring", isActive(link.href)
|
|
42
42
|
? "bg-accent text-accent-foreground"
|
|
43
43
|
: "text-foreground hover:bg-accent hover:text-accent-foreground"), "aria-current": isActive(link.href) ? "page" : undefined, children: link.label }, link.href))) }))] })] }));
|
|
44
44
|
}
|
|
@@ -20,7 +20,7 @@ import { Skeleton, SkeletonCircle } from "@kala-ui/react/skeleton";
|
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
22
|
export function SessionCardSkeleton({ showBadge = false, showRevokeButton = true, className, "data-testid": dataTestId, }) {
|
|
23
|
-
return (_jsxs(Card, { "data-testid": dataTestId || "session-card-skeleton", className: cn("relative", className), children: [_jsx(CardHeader, { className: "pb-3", children: _jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "flex items-start gap-3 flex-1", children: [_jsx(SkeletonCircle, { size: "1.5rem", className: "rounded-md" }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx(Skeleton, { className: "h-4 w-32 mb-1" }), _jsx(Skeleton, { className: "h-3 w-48" })] })] }), showBadge ? (_jsx(Skeleton, { className: "h-5 w-24 rounded-md" })) : (showRevokeButton && _jsx(Skeleton, { className: "h-8 w-16 rounded-md" }))] }) }), _jsx(CardContent, { className: "pt-0", children: _jsxs("div", { className: "space-y-1.5", children: [_jsx(Skeleton, { className: "h-4 w-48" }), _jsx(Skeleton, { className: "h-4 w-40" }), _jsx(Skeleton, { className: "h-4 w-56" })] }) })] }));
|
|
23
|
+
return (_jsxs(Card, { "data-kala-component": "session-card-skeleton", "data-testid": dataTestId || "session-card-skeleton", className: cn("relative", className), children: [_jsx(CardHeader, { className: "pb-3", children: _jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "flex items-start gap-3 flex-1", children: [_jsx(SkeletonCircle, { size: "1.5rem", className: "rounded-md" }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx(Skeleton, { className: "h-4 w-32 mb-1" }), _jsx(Skeleton, { className: "h-3 w-48" })] })] }), showBadge ? (_jsx(Skeleton, { className: "h-5 w-24 rounded-md" })) : (showRevokeButton && _jsx(Skeleton, { className: "h-8 w-16 rounded-md" }))] }) }), _jsx(CardContent, { className: "pt-0", children: _jsxs("div", { className: "space-y-1.5", children: [_jsx(Skeleton, { className: "h-4 w-48" }), _jsx(Skeleton, { className: "h-4 w-40" }), _jsx(Skeleton, { className: "h-4 w-56" })] }) })] }));
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
26
|
* Compound component: SessionCard.Skeleton
|
|
@@ -52,9 +52,9 @@ function formatTimestamp(timestamp) {
|
|
|
52
52
|
export function SessionCard({ session, onRevoke, isRevoking, className, isLoading = false, skeletonConfig, skeleton, ref, ...props }) {
|
|
53
53
|
if (isLoading) {
|
|
54
54
|
if (skeleton) {
|
|
55
|
-
return (_jsx(Card, { "data-
|
|
55
|
+
return (_jsx(Card, { "data-kala-component": "session-card", ref: ref, className: cn("relative", className), ...props, children: skeleton }));
|
|
56
56
|
}
|
|
57
|
-
return _jsx(SessionCardSkeleton, { className: className, ...skeletonConfig });
|
|
57
|
+
return (_jsx(SessionCardSkeleton, { "data-kala-component": "session-card", className: className, ...skeletonConfig }));
|
|
58
58
|
}
|
|
59
59
|
const DeviceIcon = getDeviceIcon(session.device);
|
|
60
60
|
const lastActive = formatTimestamp(session.lastActiveAt);
|
|
@@ -63,5 +63,5 @@ export function SessionCard({ session, onRevoke, isRevoking, className, isLoadin
|
|
|
63
63
|
void onRevoke(session.id);
|
|
64
64
|
}
|
|
65
65
|
};
|
|
66
|
-
return (_jsxs(Card, { "data-
|
|
66
|
+
return (_jsxs(Card, { "data-kala-component": "session-card", ref: ref, className: cn("relative", className), ...props, children: [_jsx(CardHeader, { className: "pb-3", children: _jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "flex items-start gap-3 flex-1", children: [_jsx(DeviceIcon, { className: "size-6 text-muted-foreground mt-0.5", "aria-hidden": "true" }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("div", { className: "flex items-center gap-2 flex-wrap", children: _jsx("h4", { className: "font-semibold text-sm", children: session.browser }) }), _jsxs("p", { className: "text-xs text-muted-foreground mt-0.5", children: [session.os, " \u2022 ", session.device] })] })] }), session.isCurrent ? (_jsx("div", { className: "inline-flex items-center rounded-md bg-success/10 px-2 py-1 text-xs font-medium text-success border border-success/20", children: "Current Session" })) : (onRevoke && (_jsx(Button, { variant: "ghost", size: "sm", onClick: handleRevoke, disabled: isRevoking, className: "text-destructive hover:text-destructive hover:bg-destructive/10", "aria-label": "Revoke session", children: isRevoking ? "Revoking..." : "Revoke" })))] }) }), _jsx(CardContent, { className: "pt-0", children: _jsxs("div", { className: "space-y-1.5 text-xs text-muted-foreground", children: [session.location && (_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(MapPin, { className: "size-3.5", "aria-hidden": "true" }), _jsx("span", { children: session.location })] })), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(Wifi, { className: "size-3.5", "aria-hidden": "true" }), _jsx("span", { children: session.ip })] }), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(Clock, { className: "size-3.5", "aria-hidden": "true" }), _jsxs("span", { children: ["Last active ", lastActive] })] })] }) })] }));
|
|
67
67
|
}
|
|
@@ -19,5 +19,5 @@ import { Skeleton } from "@kala-ui/react/skeleton";
|
|
|
19
19
|
* ```
|
|
20
20
|
*/
|
|
21
21
|
export function SidebarSkeleton({ collapsed = false, showHeader = true, showFooter = true, sectionCount = 3, itemsPerSection = 4, className, "data-testid": dataTestId, }) {
|
|
22
|
-
return (_jsxs("aside", { "data-testid": dataTestId || "sidebar-skeleton", className: cn("flex flex-col h-full bg-popover border-r", collapsed ? "w-16" : "w-64", className), "aria-label": "Loading sidebar", "aria-busy": "true", children: [showHeader && (_jsx("div", { className: "flex items-center h-16 px-6 border-b", children: collapsed ? (_jsx(Skeleton, { className: "h-8 w-8 mx-auto" })) : (_jsx(Skeleton, { className: "h-8 w-32" })) })), _jsx("div", { className: "flex-1 overflow-y-auto py-4 px-3", children: Array.from({ length: sectionCount }).map((_, sectionIndex) => (_jsxs("div", { className: "mb-6 last:mb-0", children: [!collapsed && _jsx(Skeleton, { className: "h-3 w-20 mb-2 mx-3" }), _jsx("div", { className: "space-y-2", children: Array.from({ length: itemsPerSection }).map((_, itemIndex) => (_jsxs("div", { className: cn("flex items-center px-3 py-2", collapsed ? "justify-center" : "gap-3"), children: [_jsx(Skeleton, { className: "h-5 w-5 flex-shrink-0" }), !collapsed && _jsx(Skeleton, { className: "h-4 flex-1" }), !collapsed && itemIndex === 0 && (_jsx(Skeleton, { className: "h-4 w-4 rounded-full flex-shrink-0" }))] }, itemIndex))) })] }, sectionIndex))) }), showFooter && (_jsx("div", { className: "p-4 border-t", children: _jsxs("div", { className: cn("flex items-center", collapsed ? "justify-center" : "gap-3"), children: [_jsx(Skeleton, { className: "h-10 w-10 rounded-full flex-shrink-0" }), !collapsed && (_jsxs("div", { className: "flex-1 min-w-0", children: [_jsx(Skeleton, { className: "h-4 w-24 mb-1" }), _jsx(Skeleton, { className: "h-3 w-32" })] }))] }) }))] }));
|
|
22
|
+
return (_jsxs("aside", { "data-kala-component": "sidebar-skeleton", "data-testid": dataTestId || "sidebar-skeleton", className: cn("flex flex-col h-full bg-popover border-r", collapsed ? "w-16" : "w-64", className), "aria-label": "Loading sidebar", "aria-busy": "true", children: [showHeader && (_jsx("div", { className: "flex items-center h-16 px-6 border-b", children: collapsed ? (_jsx(Skeleton, { className: "h-8 w-8 mx-auto" })) : (_jsx(Skeleton, { className: "h-8 w-32" })) })), _jsx("div", { className: "flex-1 overflow-y-auto py-4 px-3", children: Array.from({ length: sectionCount }).map((_, sectionIndex) => (_jsxs("div", { className: "mb-6 last:mb-0", children: [!collapsed && _jsx(Skeleton, { className: "h-3 w-20 mb-2 mx-3" }), _jsx("div", { className: "space-y-2", children: Array.from({ length: itemsPerSection }).map((_, itemIndex) => (_jsxs("div", { className: cn("flex items-center px-3 py-2", collapsed ? "justify-center" : "gap-3"), children: [_jsx(Skeleton, { className: "h-5 w-5 flex-shrink-0" }), !collapsed && _jsx(Skeleton, { className: "h-4 flex-1" }), !collapsed && itemIndex === 0 && (_jsx(Skeleton, { className: "h-4 w-4 rounded-full flex-shrink-0" }))] }, itemIndex))) })] }, sectionIndex))) }), showFooter && (_jsx("div", { className: "p-4 border-t", children: _jsxs("div", { className: cn("flex items-center", collapsed ? "justify-center" : "gap-3"), children: [_jsx(Skeleton, { className: "h-10 w-10 rounded-full flex-shrink-0" }), !collapsed && (_jsxs("div", { className: "flex-1 min-w-0", children: [_jsx(Skeleton, { className: "h-4 w-24 mb-1" }), _jsx(Skeleton, { className: "h-3 w-32" })] }))] }) }))] }));
|
|
23
23
|
}
|
|
@@ -65,5 +65,5 @@ export function Sidebar({ logo, navSections = [], pathname = "", isOpen = true,
|
|
|
65
65
|
// switches with the viewport.
|
|
66
66
|
const shellRef = useMergedRef(ref, trapRef);
|
|
67
67
|
const shellClassName = cn("fixed left-0 top-0 z-30 h-full w-64 bg-popover border-r text-foreground transition-transform duration-300 ease-out flex flex-col kala-surface-card", "md:translate-x-0 md:z-10", isOpen ? "animate-slide-in-from-left" : "-translate-x-full", className);
|
|
68
|
-
return (_jsxs(_Fragment, { children: [isOpen && (_jsx("div", { className: "fixed inset-0 bg-overlay z-30 md:hidden animate-fade-in transition-opacity duration-200 ease-out", onClick: onClose, "aria-hidden": "true" })), overlayActive ? (_jsx("div", { ref: shellRef, "data-
|
|
68
|
+
return (_jsxs(_Fragment, { children: [isOpen && (_jsx("div", { className: "fixed inset-0 bg-overlay z-30 md:hidden animate-fade-in transition-opacity duration-200 ease-out", onClick: onClose, "aria-hidden": "true" })), overlayActive ? (_jsx("div", { ref: shellRef, "data-kala-component": "sidebar", role: "dialog", "aria-modal": "true", "aria-label": "Sidebar", className: shellClassName, ...props, children: sidebarContent })) : (_jsx("aside", { ref: shellRef, "data-kala-component": "sidebar", className: shellClassName, ...props, children: sidebarContent }))] }));
|
|
69
69
|
}
|
|
@@ -53,5 +53,5 @@ export function SocialLoginButton({ ref, provider, isLoading = false, label, cla
|
|
|
53
53
|
const config = providerConfig[provider];
|
|
54
54
|
const displayLabel = label ?? config.label;
|
|
55
55
|
const icon = config.icon;
|
|
56
|
-
return (_jsxs(Button, {
|
|
56
|
+
return (_jsxs(Button, { "data-kala-component": "social-login-button", ref: ref, type: "button", variant: "outline", className: cn("relative transition-colors duration-200 bg-transparent border-input text-foreground", config.color, className), disabled: disabled || isLoading, ...props, children: [isLoading ? (_jsx(Loader2, { className: "w-5 h-5 animate-spin mr-2" })) : provider === "linkedin" ? (_jsx(LinkedInIcon, { className: "w-5 h-5 mr-2" })) : icon ? (_jsx(SimpleIconWrapper, { icon: icon, className: "w-5 h-5 mr-2", label: displayLabel })) : null, _jsx("span", { children: isLoading ? "Redirecting..." : displayLabel })] }));
|
|
57
57
|
}
|
|
@@ -14,5 +14,5 @@ const defaultProviders = [
|
|
|
14
14
|
"linkedin",
|
|
15
15
|
];
|
|
16
16
|
export function SocialLoginButtons({ onProviderClick, loadingProvider, providers = defaultProviders, className, showDivider = true, dividerText = "Or sign in with", }) {
|
|
17
|
-
return (_jsxs("div", { "data-
|
|
17
|
+
return (_jsxs("div", { "data-kala-component": "social-login-buttons", className: cn("w-full space-y-3", className), children: [showDivider && (_jsxs("div", { className: "relative", children: [_jsx("div", { className: "absolute inset-0 flex items-center", children: _jsx("span", { className: "w-full border-t" }) }), _jsx("div", { className: "relative flex justify-center text-xs uppercase", children: _jsx("span", { className: "bg-background px-2 text-muted-foreground", children: dividerText }) })] })), _jsx("div", { className: "flex flex-wrap justify-center gap-2", children: providers.map((provider) => (_jsx(SocialLoginButton, { provider: provider, isLoading: loadingProvider === provider, onClick: () => onProviderClick(provider), disabled: loadingProvider !== undefined && loadingProvider !== provider }, provider))) })] }));
|
|
18
18
|
}
|
|
@@ -130,5 +130,5 @@ export function SparklineChart({ data, type = "line", color, width = "100%", hei
|
|
|
130
130
|
data: data,
|
|
131
131
|
},
|
|
132
132
|
];
|
|
133
|
-
return (_jsx("div", { className: cn("sparkline-chart", className), children: _jsx(Chart, { options: options, series: series, type: type, height: height, width: width }) }));
|
|
133
|
+
return (_jsx("div", { "data-kala-component": "sparkline-chart", className: cn("sparkline-chart", className), children: _jsx(Chart, { options: options, series: series, type: type, height: height, width: width }) }));
|
|
134
134
|
}
|
|
@@ -23,5 +23,5 @@ export function UserMenuDropdown({ user, isOpen, onOpenChange, onLogout, baseUrl
|
|
|
23
23
|
const handleLogoutClick = () => {
|
|
24
24
|
onLogout?.();
|
|
25
25
|
};
|
|
26
|
-
return (_jsxs(DropdownMenu, { ...(isOpen !== undefined && { open: isOpen }), ...(onOpenChange && { onOpenChange }), children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", ref: ref, className: cn("h-auto p-1 rounded-full hover:bg-accent", className), "aria-label": "User menu", ...props, children: _jsxs(Avatar, { className: "size-8", children: [user.avatar && (_jsx(AvatarImage, { src: user.avatar, alt: user.name ?? "User" })), _jsx(AvatarFallback, { className: "bg-primary text-primary-foreground text-sm font-medium", children: initials })] }) }) }), _jsxs(DropdownMenuContent, { align: "end", className: "w-56", children: [_jsx(DropdownMenuLabel, { children: _jsxs("div", { className: "flex flex-col space-y-1", children: [user.name && (_jsx("p", { className: "text-sm font-medium leading-none", children: user.name })), user.email && (_jsx("p", { className: "text-xs text-muted-foreground leading-none", children: user.email }))] }) }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { onClick: handleProfileClick, children: [_jsx(User, { className: "mr-2 size-4" }), "Profile"] }), _jsxs(DropdownMenuItem, { onClick: handleSettingsClick, children: [_jsx(Settings, { className: "mr-2 size-4" }), "Settings"] }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { onClick: handleLogoutClick, color: "destructive", children: [_jsx(LogOut, { className: "mr-2 size-4" }), "Logout"] })] })] }));
|
|
26
|
+
return (_jsxs(DropdownMenu, { "data-kala-component": "user-menu-dropdown", ...(isOpen !== undefined && { open: isOpen }), ...(onOpenChange && { onOpenChange }), children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", ref: ref, className: cn("h-auto p-1 rounded-full hover:bg-accent", className), "aria-label": "User menu", ...props, children: _jsxs(Avatar, { className: "size-8", children: [user.avatar && (_jsx(AvatarImage, { src: user.avatar, alt: user.name ?? "User" })), _jsx(AvatarFallback, { className: "bg-primary text-primary-foreground text-sm font-medium", children: initials })] }) }) }), _jsxs(DropdownMenuContent, { align: "end", className: "w-56", children: [_jsx(DropdownMenuLabel, { children: _jsxs("div", { className: "flex flex-col space-y-1", children: [user.name && (_jsx("p", { className: "text-sm font-medium leading-none", children: user.name })), user.email && (_jsx("p", { className: "text-xs text-muted-foreground leading-none", children: user.email }))] }) }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { onClick: handleProfileClick, children: [_jsx(User, { className: "mr-2 size-4" }), "Profile"] }), _jsxs(DropdownMenuItem, { onClick: handleSettingsClick, children: [_jsx(Settings, { className: "mr-2 size-4" }), "Settings"] }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { onClick: handleLogoutClick, color: "destructive", children: [_jsx(LogOut, { className: "mr-2 size-4" }), "Logout"] })] })] }));
|
|
27
27
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kala-ui/react-app",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": [
|
|
6
6
|
"**/*.css"
|
|
@@ -126,8 +126,8 @@
|
|
|
126
126
|
"lucide-react": "~1.34.0",
|
|
127
127
|
"react-apexcharts": "~2.1.1",
|
|
128
128
|
"simple-icons": "~16.28.0",
|
|
129
|
-
"@kala-ui/react": "0.1.0-beta.0",
|
|
130
|
-
"@kala-ui/react
|
|
129
|
+
"@kala-ui/react-hooks": "0.1.0-beta.0",
|
|
130
|
+
"@kala-ui/react": "0.1.0-beta.1"
|
|
131
131
|
},
|
|
132
132
|
"devDependencies": {
|
|
133
133
|
"@tailwindcss/postcss": "~4.3.3",
|