@kala-ui/react-app 0.1.0-beta.0 → 0.1.0-beta.10
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 +83 -0
- package/README.md +9 -4
- package/dist/components/app-shell/app-shell.d.ts +8 -46
- package/dist/components/app-shell/app-shell.js +41 -42
- package/dist/components/app-shell/app-shell.types.d.ts +45 -0
- package/dist/components/app-shell/app-shell.types.js +2 -0
- package/dist/components/app-shell/index.d.ts +1 -0
- package/dist/components/charts/area-chart.d.ts +1 -1
- package/dist/components/charts/area-chart.js +4 -3
- package/dist/components/charts/bar-chart.d.ts +1 -1
- package/dist/components/charts/bar-chart.js +4 -3
- package/dist/components/charts/chart-skeleton.d.ts +0 -21
- package/dist/components/charts/chart-skeleton.js +2 -1
- package/dist/components/charts/chart-utils.d.ts +56 -0
- package/dist/components/charts/chart-utils.js +135 -0
- package/dist/components/charts/chart.d.ts +3 -1
- package/dist/components/charts/chart.js +14 -4
- package/dist/components/charts/chart.types.d.ts +3 -0
- package/dist/components/charts/donut-chart.d.ts +1 -1
- package/dist/components/charts/donut-chart.js +5 -4
- package/dist/components/charts/index.d.ts +1 -1
- package/dist/components/charts/index.js +1 -1
- package/dist/components/charts/line-chart.d.ts +1 -1
- package/dist/components/charts/line-chart.js +4 -3
- package/dist/components/charts/radial-bar-chart.d.ts +1 -1
- package/dist/components/charts/radial-bar-chart.js +4 -3
- 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 +6 -4
- package/dist/components/data-table/data-table.types.d.ts +2 -0
- package/dist/components/data-table/index.d.ts +1 -1
- package/dist/components/data-table/index.js +1 -1
- package/dist/components/data-table/pagination-nav.js +1 -1
- package/dist/components/data-table/use-table-state.d.ts +29 -0
- package/dist/components/data-table/use-table-state.js +312 -0
- package/dist/components/dnd/dnd.d.ts +16 -98
- package/dist/components/dnd/dnd.js +36 -8
- package/dist/components/dnd/dnd.types.d.ts +92 -0
- package/dist/components/dnd/dnd.types.js +7 -0
- package/dist/components/dnd/index.d.ts +2 -1
- package/dist/components/dnd/index.js +1 -1
- package/dist/components/footer/footer.d.ts +2 -37
- package/dist/components/footer/footer.js +18 -2
- package/dist/components/footer/footer.types.d.ts +25 -0
- package/dist/components/footer/footer.types.js +2 -0
- package/dist/components/footer/index.d.ts +1 -1
- package/dist/components/header/header-skeleton.js +1 -1
- package/dist/components/header/header.js +6 -12
- package/dist/components/metric-card/index.d.ts +2 -1
- package/dist/components/metric-card/metric-card-skeleton.js +2 -1
- package/dist/components/metric-card/metric-card.d.ts +2 -14
- package/dist/components/metric-card/metric-card.js +33 -29
- package/dist/components/metric-card/metric-card.types.d.ts +3 -0
- package/dist/components/nav-link/index.d.ts +2 -1
- package/dist/components/nav-link/index.js +1 -1
- package/dist/components/nav-link/nav-link.d.ts +2 -25
- package/dist/components/nav-link/nav-link.js +43 -5
- package/dist/components/nav-link/nav-link.types.d.ts +23 -0
- package/dist/components/nav-link/nav-link.types.js +2 -0
- package/dist/components/navigation/index.d.ts +1 -1
- package/dist/components/navigation/navigation-skeleton.js +1 -1
- package/dist/components/navigation/navigation.d.ts +1 -24
- package/dist/components/navigation/navigation.js +9 -19
- package/dist/components/navigation/navigation.types.d.ts +15 -0
- package/dist/components/navigation/navigation.types.js +2 -0
- package/dist/components/session-card/index.d.ts +2 -1
- package/dist/components/session-card/index.js +1 -1
- package/dist/components/session-card/session-card-skeleton.d.ts +3 -1
- package/dist/components/session-card/session-card-skeleton.js +3 -2
- package/dist/components/session-card/session-card.d.ts +3 -26
- package/dist/components/session-card/session-card.js +23 -8
- package/dist/components/session-card/session-card.types.d.ts +26 -0
- package/dist/components/session-card/session-card.types.js +2 -0
- package/dist/components/sidebar/sidebar-skeleton.js +1 -1
- package/dist/components/sidebar/sidebar.js +6 -12
- package/dist/components/social-login-button/index.d.ts +3 -1
- package/dist/components/social-login-button/index.js +2 -1
- package/dist/components/social-login-button/social-login-button.d.ts +2 -20
- package/dist/components/social-login-button/social-login-button.js +9 -11
- package/dist/components/social-login-button/social-login-button.types.d.ts +28 -0
- package/dist/components/social-login-button/social-login-button.types.js +2 -0
- package/dist/components/social-login-button/social-login-buttons.d.ts +4 -0
- package/dist/components/social-login-button/social-login-buttons.js +18 -0
- 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/index.d.ts +2 -1
- package/dist/components/user-menu-dropdown/index.js +1 -1
- package/dist/components/user-menu-dropdown/user-menu-dropdown.d.ts +2 -31
- package/dist/components/user-menu-dropdown/user-menu-dropdown.js +14 -2
- package/dist/components/user-menu-dropdown/user-menu-dropdown.types.d.ts +16 -0
- package/dist/components/user-menu-dropdown/user-menu-dropdown.types.js +2 -0
- package/dist/config/app-shell.d.ts +13 -0
- package/dist/config/app-shell.js +20 -0
- package/dist/config/charts.d.ts +23 -0
- package/dist/config/charts.js +23 -0
- package/dist/config/footer.d.ts +16 -0
- package/dist/config/footer.js +16 -0
- package/dist/config/index.d.ts +7 -0
- package/dist/config/index.js +7 -0
- package/dist/config/metric-card.d.ts +36 -0
- package/dist/config/metric-card.js +44 -0
- package/dist/config/session-card.d.ts +32 -0
- package/dist/config/session-card.js +32 -0
- package/dist/config/social-login-button.d.ts +15 -0
- package/dist/config/social-login-button.js +15 -0
- package/dist/config/user-menu-dropdown.d.ts +10 -0
- package/dist/config/user-menu-dropdown.js +10 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/styles/app.css +18 -53
- package/package.json +24 -28
|
@@ -14,6 +14,8 @@ export interface SessionCardSkeletonProps extends SessionCardSkeletonConfig {
|
|
|
14
14
|
* Test ID for querying the element
|
|
15
15
|
*/
|
|
16
16
|
"data-testid"?: string;
|
|
17
|
+
[dataKala: `data-${string}`]: string | undefined;
|
|
18
|
+
style?: React.CSSProperties;
|
|
17
19
|
}
|
|
18
20
|
/**
|
|
19
21
|
* Session Card skeleton component
|
|
@@ -25,7 +27,7 @@ export interface SessionCardSkeletonProps extends SessionCardSkeletonConfig {
|
|
|
25
27
|
* <SessionCardSkeleton showBadge={true} />
|
|
26
28
|
* ```
|
|
27
29
|
*/
|
|
28
|
-
export declare function SessionCardSkeleton({ showBadge, showRevokeButton, className, "data-testid": dataTestId, }: SessionCardSkeletonProps): import("react").JSX.Element;
|
|
30
|
+
export declare function SessionCardSkeleton({ showBadge, showRevokeButton, className, "data-testid": dataTestId, style, ...markerProps }: SessionCardSkeletonProps): import("react").JSX.Element;
|
|
29
31
|
export declare namespace SessionCardSkeleton {
|
|
30
32
|
export { SessionCardSkeleton as Skeleton };
|
|
31
33
|
}
|
|
@@ -8,6 +8,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
8
8
|
*/
|
|
9
9
|
import { Card, CardContent, CardHeader } from "@kala-ui/react/card";
|
|
10
10
|
import { cn } from "@kala-ui/react/lib/utils";
|
|
11
|
+
import { sessionCardSkeletonStyles, sessionCardStyles, } from "../../config/session-card.js";
|
|
11
12
|
import { Skeleton, SkeletonCircle } from "@kala-ui/react/skeleton";
|
|
12
13
|
/**
|
|
13
14
|
* Session Card skeleton component
|
|
@@ -19,8 +20,8 @@ import { Skeleton, SkeletonCircle } from "@kala-ui/react/skeleton";
|
|
|
19
20
|
* <SessionCardSkeleton showBadge={true} />
|
|
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(
|
|
23
|
+
export function SessionCardSkeleton({ showBadge = false, showRevokeButton = true, className, "data-testid": dataTestId, style, ...markerProps }) {
|
|
24
|
+
return (_jsxs(Card, { "data-kala-component": "session-card-skeleton", "data-testid": dataTestId || "session-card-skeleton", className: cn(sessionCardSkeletonStyles.root, className), style: style, ...markerProps, children: [_jsx(CardHeader, { className: sessionCardStyles.header, children: _jsxs("div", { className: sessionCardSkeletonStyles.headerRow, children: [_jsxs("div", { className: sessionCardSkeletonStyles.headerMain, children: [_jsx(SkeletonCircle, { size: "1.5rem", className: sessionCardSkeletonStyles.deviceIcon }), _jsxs("div", { className: sessionCardSkeletonStyles.titleBlock, children: [_jsx(Skeleton, { className: sessionCardSkeletonStyles.titleSkeleton }), _jsx(Skeleton, { className: sessionCardSkeletonStyles.subtitleSkeleton })] })] }), showBadge ? (_jsx(Skeleton, { className: sessionCardSkeletonStyles.badgeSkeleton })) : (showRevokeButton && (_jsx(Skeleton, { className: sessionCardSkeletonStyles.revokeSkeleton })))] }) }), _jsx(CardContent, { className: sessionCardStyles.content, children: _jsxs("div", { className: sessionCardSkeletonStyles.detailList, children: [_jsx(Skeleton, { className: sessionCardSkeletonStyles.detailRowMedium }), _jsx(Skeleton, { className: sessionCardSkeletonStyles.detailRowNarrow }), _jsx(Skeleton, { className: sessionCardSkeletonStyles.detailRowWide })] }) })] }));
|
|
24
25
|
}
|
|
25
26
|
/**
|
|
26
27
|
* Compound component: SessionCard.Skeleton
|
|
@@ -1,27 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Session Card Component
|
|
3
|
-
* Displays information about an active session with device, browser, location, and last activity
|
|
4
|
-
*/
|
|
5
|
-
import type { SessionCardSkeletonConfig } from "@kala-ui/react/skeleton";
|
|
6
1
|
import type * as React from "react";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
browser: string;
|
|
11
|
-
os: string;
|
|
12
|
-
location?: string;
|
|
13
|
-
ip: string;
|
|
14
|
-
lastActiveAt: string;
|
|
15
|
-
createdAt: string;
|
|
16
|
-
isCurrent: boolean;
|
|
17
|
-
}
|
|
18
|
-
export interface SessionCardProps extends React.ComponentProps<"div"> {
|
|
19
|
-
session: SessionData;
|
|
20
|
-
onRevoke?: (sessionId: string) => void | Promise<void>;
|
|
21
|
-
isRevoking?: boolean;
|
|
22
|
-
className?: string;
|
|
23
|
-
isLoading?: boolean;
|
|
24
|
-
skeletonConfig?: SessionCardSkeletonConfig;
|
|
25
|
-
skeleton?: React.ReactNode;
|
|
26
|
-
}
|
|
27
|
-
export declare function SessionCard({ session, onRevoke, isRevoking, className, isLoading, skeletonConfig, skeleton, ref, ...props }: SessionCardProps): React.JSX.Element;
|
|
2
|
+
import type { SessionCardProps } from "./session-card.types";
|
|
3
|
+
export declare function SessionCard({ session, onRevoke, isRevoking, className, style, slotStyles: slotStylesRaw, isLoading, skeletonConfig, skeleton, ref, ...props }: SessionCardProps): React.JSX.Element;
|
|
4
|
+
export type { SessionData } from "./session-card.types";
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
/**
|
|
4
|
-
* Session Card Component
|
|
5
|
-
* Displays information about an active session with device, browser, location, and last activity
|
|
6
|
-
*/
|
|
7
3
|
import { Button } from "@kala-ui/react/button";
|
|
8
4
|
import { Card, CardContent, CardHeader } from "@kala-ui/react/card";
|
|
5
|
+
import { useSlotStyles } from "@kala-ui/react/kala-provider";
|
|
6
|
+
import { applySlot } from "@kala-ui/react/lib/slot-styles";
|
|
9
7
|
import { cn } from "@kala-ui/react/lib/utils";
|
|
10
8
|
import { Clock, HelpCircle, MapPin, Monitor, Smartphone, Tablet, Wifi, } from "lucide-react";
|
|
9
|
+
import { sessionCardStyles } from "../../config/session-card.js";
|
|
11
10
|
import { SessionCardSkeleton } from "./session-card-skeleton.js";
|
|
12
11
|
function getDeviceIcon(device) {
|
|
13
12
|
const deviceLower = device.toLowerCase();
|
|
@@ -49,12 +48,14 @@ function formatTimestamp(timestamp) {
|
|
|
49
48
|
hour12: true,
|
|
50
49
|
});
|
|
51
50
|
}
|
|
52
|
-
export function SessionCard({ session, onRevoke, isRevoking, className, isLoading = false, skeletonConfig, skeleton, ref, ...props }) {
|
|
51
|
+
export function SessionCard({ session, onRevoke, isRevoking, className, style, slotStyles: slotStylesRaw, isLoading = false, skeletonConfig, skeleton, ref, ...props }) {
|
|
52
|
+
const slotStyles = useSlotStyles("session-card", slotStylesRaw);
|
|
53
|
+
const root = applySlot(cn(sessionCardStyles.root, className), slotStyles?.root);
|
|
53
54
|
if (isLoading) {
|
|
54
55
|
if (skeleton) {
|
|
55
|
-
return (_jsx(Card, { "data-
|
|
56
|
+
return (_jsx(Card, { "data-kala-component": "session-card", ref: ref, className: root.className, style: root.style, ...props, children: skeleton }));
|
|
56
57
|
}
|
|
57
|
-
return _jsx(SessionCardSkeleton, { className: className, ...skeletonConfig });
|
|
58
|
+
return (_jsx(SessionCardSkeleton, { "data-kala-component": "session-card", className: root.className, ...skeletonConfig }));
|
|
58
59
|
}
|
|
59
60
|
const DeviceIcon = getDeviceIcon(session.device);
|
|
60
61
|
const lastActive = formatTimestamp(session.lastActiveAt);
|
|
@@ -63,5 +64,19 @@ export function SessionCard({ session, onRevoke, isRevoking, className, isLoadin
|
|
|
63
64
|
void onRevoke(session.id);
|
|
64
65
|
}
|
|
65
66
|
};
|
|
66
|
-
|
|
67
|
+
const header = applySlot(sessionCardStyles.header, slotStyles?.header);
|
|
68
|
+
const headerRow = applySlot(sessionCardStyles.headerRow, slotStyles?.headerRow);
|
|
69
|
+
const headerMain = applySlot(sessionCardStyles.headerMain, slotStyles?.headerMain);
|
|
70
|
+
const deviceIcon = applySlot(sessionCardStyles.deviceIcon, slotStyles?.deviceIcon);
|
|
71
|
+
const titleBlock = applySlot(sessionCardStyles.titleBlock, slotStyles?.titleBlock);
|
|
72
|
+
const titleRow = applySlot(sessionCardStyles.titleRow, slotStyles?.titleRow);
|
|
73
|
+
const title = applySlot(sessionCardStyles.title, slotStyles?.title);
|
|
74
|
+
const subtitle = applySlot(sessionCardStyles.subtitle, slotStyles?.subtitle);
|
|
75
|
+
const currentBadge = applySlot(sessionCardStyles.currentBadge, slotStyles?.currentBadge);
|
|
76
|
+
const revokeButton = applySlot(sessionCardStyles.revokeButton, slotStyles?.revokeButton);
|
|
77
|
+
const content = applySlot(sessionCardStyles.content, slotStyles?.content);
|
|
78
|
+
const detailList = applySlot(sessionCardStyles.detailList, slotStyles?.detailList);
|
|
79
|
+
const detailRow = applySlot(sessionCardStyles.detailRow, slotStyles?.detailRow);
|
|
80
|
+
const detailIcon = applySlot(sessionCardStyles.detailIcon, slotStyles?.detailIcon);
|
|
81
|
+
return (_jsxs(Card, { "data-kala-component": "session-card", ref: ref, className: root.className, style: root.style, ...props, children: [_jsx(CardHeader, { className: header.className, style: header.style, children: _jsxs("div", { className: headerRow.className, style: headerRow.style, children: [_jsxs("div", { className: headerMain.className, style: headerMain.style, children: [_jsx(DeviceIcon, { className: deviceIcon.className, style: deviceIcon.style, "aria-hidden": "true" }), _jsxs("div", { className: titleBlock.className, style: titleBlock.style, children: [_jsx("div", { className: titleRow.className, style: titleRow.style, children: _jsx("h4", { className: title.className, style: title.style, children: session.browser }) }), _jsxs("p", { className: subtitle.className, style: subtitle.style, children: [session.os, " \u2022 ", session.device] })] })] }), session.isCurrent ? (_jsx("div", { className: currentBadge.className, style: currentBadge.style, children: "Current Session" })) : (onRevoke && (_jsx(Button, { variant: "ghost", size: "sm", onClick: handleRevoke, disabled: isRevoking, className: revokeButton.className, style: revokeButton.style, "aria-label": "Revoke session", children: isRevoking ? "Revoking..." : "Revoke" })))] }) }), _jsx(CardContent, { className: content.className, style: content.style, children: _jsxs("div", { className: detailList.className, style: detailList.style, children: [session.location && (_jsxs("div", { className: detailRow.className, style: detailRow.style, children: [_jsx(MapPin, { className: detailIcon.className, "aria-hidden": "true" }), _jsx("span", { children: session.location })] })), _jsxs("div", { className: detailRow.className, style: detailRow.style, children: [_jsx(Wifi, { className: detailIcon.className, "aria-hidden": "true" }), _jsx("span", { children: session.ip })] }), _jsxs("div", { className: detailRow.className, style: detailRow.style, children: [_jsx(Clock, { className: detailIcon.className, "aria-hidden": "true" }), _jsxs("span", { children: ["Last active ", lastActive] })] })] }) })] }));
|
|
67
82
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { SlotStyles } from "@kala-ui/react/lib/slot-styles";
|
|
2
|
+
import type { SessionCardSkeletonConfig } from "@kala-ui/react/skeleton";
|
|
3
|
+
import type * as React from "react";
|
|
4
|
+
export interface SessionData {
|
|
5
|
+
id: string;
|
|
6
|
+
device: string;
|
|
7
|
+
browser: string;
|
|
8
|
+
os: string;
|
|
9
|
+
location?: string;
|
|
10
|
+
ip: string;
|
|
11
|
+
lastActiveAt: string;
|
|
12
|
+
createdAt: string;
|
|
13
|
+
isCurrent: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface SessionCardProps extends React.ComponentProps<"div"> {
|
|
16
|
+
session: SessionData;
|
|
17
|
+
onRevoke?: (sessionId: string) => void | Promise<void>;
|
|
18
|
+
isRevoking?: boolean;
|
|
19
|
+
className?: string;
|
|
20
|
+
isLoading?: boolean;
|
|
21
|
+
skeletonConfig?: SessionCardSkeletonConfig;
|
|
22
|
+
skeleton?: React.ReactNode;
|
|
23
|
+
/** Per-part overrides: root, header, headerRow, deviceIcon, title, subtitle,
|
|
24
|
+
* currentBadge, revokeButton, content, detailList, detailRow, detailIcon. */
|
|
25
|
+
slotStyles?: SlotStyles;
|
|
26
|
+
}
|
|
@@ -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
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Button } from "@kala-ui/react/button";
|
|
4
4
|
import { cn } from "@kala-ui/react/lib/utils";
|
|
5
|
-
import { useFocusTrap, useMediaQuery, useMergedRef, useScrollLock, } from "@kala-ui/react-hooks";
|
|
5
|
+
import { useFocusTrap, useMediaQuery, useMergedRef, useScrollLock, useWindowEvent, } from "@kala-ui/react-hooks";
|
|
6
6
|
import { ChevronDown } from "lucide-react";
|
|
7
7
|
import { useEffect, useId, useState } from "react";
|
|
8
8
|
import { isActivePath } from "../../lib/active-path.js";
|
|
@@ -17,16 +17,10 @@ export function Sidebar({ logo, navSections = [], pathname = "", isOpen = true,
|
|
|
17
17
|
useEffect(() => {
|
|
18
18
|
setScrollLocked(overlayActive);
|
|
19
19
|
}, [overlayActive, setScrollLocked]);
|
|
20
|
-
|
|
21
|
-
if (
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (event.key === "Escape")
|
|
25
|
-
onClose?.();
|
|
26
|
-
};
|
|
27
|
-
window.addEventListener("keydown", handleKeyDown);
|
|
28
|
-
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
29
|
-
}, [overlayActive, onClose]);
|
|
20
|
+
useWindowEvent("keydown", (event) => {
|
|
21
|
+
if (event.key === "Escape" && overlayActive)
|
|
22
|
+
onClose?.();
|
|
23
|
+
});
|
|
30
24
|
// Track which sections are expanded
|
|
31
25
|
const [expandedSections, setExpandedSections] = useState(new Set(navSections
|
|
32
26
|
.map((section, index) => (section.defaultOpen !== false ? index : -1))
|
|
@@ -65,5 +59,5 @@ export function Sidebar({ logo, navSections = [], pathname = "", isOpen = true,
|
|
|
65
59
|
// switches with the viewport.
|
|
66
60
|
const shellRef = useMergedRef(ref, trapRef);
|
|
67
61
|
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-
|
|
62
|
+
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
63
|
}
|
|
@@ -1 +1,3 @@
|
|
|
1
|
-
export { SocialLoginButton
|
|
1
|
+
export { SocialLoginButton } from "./social-login-button";
|
|
2
|
+
export type { SocialLoginButtonProps, SocialLoginButtonsProps, } from "./social-login-button.types";
|
|
3
|
+
export { SocialLoginButtons } from "./social-login-buttons";
|
|
@@ -1,20 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* Individual OAuth provider button with loading state and brand colors from design tokens
|
|
4
|
-
*/
|
|
5
|
-
import { type ButtonProps } from "@kala-ui/react/button";
|
|
6
|
-
export interface SocialLoginButtonProps extends Omit<ButtonProps, "variant"> {
|
|
7
|
-
/**
|
|
8
|
-
* OAuth provider name (e.g., 'google', 'github')
|
|
9
|
-
*/
|
|
10
|
-
provider: "google" | "github" | "facebook" | "twitter" | "x" | "linkedin";
|
|
11
|
-
/**
|
|
12
|
-
* Loading state during OAuth redirect
|
|
13
|
-
*/
|
|
14
|
-
isLoading?: boolean;
|
|
15
|
-
/**
|
|
16
|
-
* Optional custom label (defaults to provider name, e.g., "Google")
|
|
17
|
-
*/
|
|
18
|
-
label?: string;
|
|
19
|
-
}
|
|
20
|
-
export declare function SocialLoginButton({ ref, provider, isLoading, label, className, disabled, ...props }: SocialLoginButtonProps): import("react").JSX.Element;
|
|
1
|
+
import type { SocialLoginButtonProps } from "./social-login-button.types";
|
|
2
|
+
export declare function SocialLoginButton({ ref, provider, isLoading, label, className, style, slotStyles: slotStylesRaw, disabled, ...props }: SocialLoginButtonProps): import("react").JSX.Element;
|
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
/**
|
|
4
|
-
* SocialLoginButton Component
|
|
5
|
-
* Individual OAuth provider button with loading state and brand colors from design tokens
|
|
6
|
-
*/
|
|
7
3
|
import { Button } from "@kala-ui/react/button";
|
|
4
|
+
import { useSlotStyles } from "@kala-ui/react/kala-provider";
|
|
5
|
+
import { applySlot } from "@kala-ui/react/lib/slot-styles";
|
|
8
6
|
import { cn } from "@kala-ui/react/lib/utils";
|
|
9
7
|
import { Loader2 } from "lucide-react";
|
|
10
8
|
import { siFacebook, siGithub, siGoogle, siX } from "simple-icons";
|
|
11
|
-
|
|
12
|
-
* Provider configuration with proper brand colors
|
|
13
|
-
* Using solid hover colors instead of opacity to fix white background issue
|
|
14
|
-
*/
|
|
9
|
+
import { socialLoginButtonStyles } from "../../config/social-login-button.js";
|
|
15
10
|
// Custom LinkedIn icon (removed from simple-icons due to Microsoft legal requirements)
|
|
16
11
|
// See: https://github.com/simple-icons/simple-icons/issues/11372
|
|
17
|
-
const LinkedInIcon = ({ className }) => (_jsxs("svg", { role: "img", viewBox: "0 0 24 24", className: className, fill: "currentColor", xmlns: "http://www.w3.org/2000/svg", children: [_jsx("title", { children: "LinkedIn" }), _jsx("path", { d: "M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.
|
|
12
|
+
const LinkedInIcon = ({ className }) => (_jsxs("svg", { role: "img", viewBox: "0 0 24 24", className: className, fill: "currentColor", xmlns: "http://www.w3.org/2000/svg", children: [_jsx("title", { children: "LinkedIn" }), _jsx("path", { d: "M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.224 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" })] }));
|
|
18
13
|
// Wrapper component to convert SimpleIcon to React component
|
|
19
14
|
const SimpleIconWrapper = ({ icon, className, label, }) => (_jsxs("svg", { role: "img", viewBox: "0 0 24 24", className: className, fill: "currentColor", xmlns: "http://www.w3.org/2000/svg", children: [_jsx("title", { children: label }), _jsx("path", { d: icon.path })] }));
|
|
20
15
|
const providerConfig = {
|
|
@@ -49,9 +44,12 @@ const providerConfig = {
|
|
|
49
44
|
color: "hover:bg-brand-linkedin hover:text-white hover:border-brand-linkedin",
|
|
50
45
|
},
|
|
51
46
|
};
|
|
52
|
-
export function SocialLoginButton({ ref, provider, isLoading = false, label, className, disabled, ...props }) {
|
|
47
|
+
export function SocialLoginButton({ ref, provider, isLoading = false, label, className, style, slotStyles: slotStylesRaw, disabled, ...props }) {
|
|
48
|
+
const slotStyles = useSlotStyles("social-login-button", slotStylesRaw);
|
|
53
49
|
const config = providerConfig[provider];
|
|
54
50
|
const displayLabel = label ?? config.label;
|
|
55
51
|
const icon = config.icon;
|
|
56
|
-
|
|
52
|
+
const root = applySlot(cn(socialLoginButtonStyles.root, config.color, className), slotStyles?.root);
|
|
53
|
+
const iconSlot = applySlot(socialLoginButtonStyles.icon, slotStyles?.icon);
|
|
54
|
+
return (_jsxs(Button, { "data-kala-component": "social-login-button", ref: ref, type: "button", variant: "outline", className: root.className, style: root.style, disabled: disabled || isLoading, ...props, children: [isLoading ? (_jsx(Loader2, { className: applySlot(socialLoginButtonStyles.spinner, slotStyles?.icon).className })) : provider === "linkedin" ? (_jsx(LinkedInIcon, { className: iconSlot.className })) : icon ? (_jsx(SimpleIconWrapper, { icon: icon, className: iconSlot.className, label: displayLabel })) : null, _jsx("span", { children: isLoading ? "Redirecting..." : displayLabel })] }));
|
|
57
55
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ButtonProps } from "@kala-ui/react/button";
|
|
2
|
+
import type { SlotStyles } from "@kala-ui/react/lib/slot-styles";
|
|
3
|
+
export interface SocialLoginButtonProps extends Omit<ButtonProps, "variant"> {
|
|
4
|
+
/** OAuth provider name (e.g., 'google', 'github') */
|
|
5
|
+
provider: "google" | "github" | "facebook" | "twitter" | "x" | "linkedin";
|
|
6
|
+
/** Loading state during OAuth redirect */
|
|
7
|
+
isLoading?: boolean;
|
|
8
|
+
/** Optional custom label (defaults to provider name, e.g., "Google") */
|
|
9
|
+
label?: string;
|
|
10
|
+
/** Per-part overrides: `root` (button), `icon` (provider glyph). */
|
|
11
|
+
slotStyles?: SlotStyles;
|
|
12
|
+
}
|
|
13
|
+
export interface SocialLoginButtonsProps {
|
|
14
|
+
/** Callback when any provider button is clicked */
|
|
15
|
+
onProviderClick: (provider: SocialLoginButtonProps["provider"]) => void;
|
|
16
|
+
/** Currently loading provider (if any) */
|
|
17
|
+
loadingProvider?: SocialLoginButtonProps["provider"];
|
|
18
|
+
/** Optional list of providers to display (defaults to all 5) */
|
|
19
|
+
providers?: SocialLoginButtonProps["provider"][];
|
|
20
|
+
/** Optional custom class name for the container */
|
|
21
|
+
className?: string;
|
|
22
|
+
/** Show divider with "Or continue with" text */
|
|
23
|
+
showDivider?: boolean;
|
|
24
|
+
/** Custom text for the divider; defaults to "Or sign in with" */
|
|
25
|
+
dividerText?: string;
|
|
26
|
+
/** Per-part overrides: root, divider, dividerLine, dividerMask, dividerLabel, dividerRule, list. */
|
|
27
|
+
slotStyles?: SlotStyles;
|
|
28
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
import type { SocialLoginButtonsProps } from "./social-login-button.types";
|
|
3
|
+
export declare function SocialLoginButtons({ onProviderClick, loadingProvider, providers, className, showDivider, dividerText, slotStyles: slotStylesRaw, }: SocialLoginButtonsProps): React.JSX.Element;
|
|
4
|
+
export type { SocialLoginButtonProps } from "./social-login-button.types";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useSlotStyles } from "@kala-ui/react/kala-provider";
|
|
4
|
+
import { applySlot } from "@kala-ui/react/lib/slot-styles";
|
|
5
|
+
import { cn } from "@kala-ui/react/lib/utils";
|
|
6
|
+
import { socialLoginButtonsStyles } from "../../config/social-login-button.js";
|
|
7
|
+
import { SocialLoginButton } from "./social-login-button.js";
|
|
8
|
+
export function SocialLoginButtons({ onProviderClick, loadingProvider, providers = ["google", "github", "facebook", "x", "linkedin"], className, showDivider = true, dividerText = "Or sign in with", slotStyles: slotStylesRaw, }) {
|
|
9
|
+
const slotStyles = useSlotStyles("social-login-buttons", slotStylesRaw);
|
|
10
|
+
const root = applySlot(cn(socialLoginButtonsStyles.root, className), slotStyles?.root);
|
|
11
|
+
const divider = applySlot(socialLoginButtonsStyles.divider, slotStyles?.divider);
|
|
12
|
+
const dividerLine = applySlot(socialLoginButtonsStyles.dividerLine, slotStyles?.dividerLine);
|
|
13
|
+
const dividerMask = applySlot(socialLoginButtonsStyles.dividerMask, slotStyles?.dividerMask);
|
|
14
|
+
const dividerLabel = applySlot(socialLoginButtonsStyles.dividerLabel, slotStyles?.dividerLabel);
|
|
15
|
+
const dividerRule = applySlot(socialLoginButtonsStyles.dividerRule, slotStyles?.dividerRule);
|
|
16
|
+
const list = applySlot(socialLoginButtonsStyles.list, slotStyles?.list);
|
|
17
|
+
return (_jsxs("div", { "data-kala-component": "social-login-buttons", className: root.className, children: [showDivider && (_jsxs("div", { className: divider.className, children: [_jsx("div", { className: dividerLine.className, children: _jsx("span", { className: dividerRule.className }) }), _jsx("div", { className: dividerMask.className, children: _jsx("span", { className: dividerLabel.className, children: dividerText }) })] })), _jsx("div", { className: list.className, children: providers.map((provider) => (_jsx(SocialLoginButton, { provider: provider, isLoading: loadingProvider === provider, onClick: () => onProviderClick(provider), disabled: loadingProvider !== undefined && loadingProvider !== provider }, provider))) })] }));
|
|
18
|
+
}
|
|
@@ -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
|
}
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export { UserMenuDropdown
|
|
1
|
+
export { UserMenuDropdown } from "./user-menu-dropdown";
|
|
2
|
+
export type { UserMenuDropdownProps } from "./user-menu-dropdown.types";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
export { UserMenuDropdown
|
|
2
|
+
export { UserMenuDropdown } from "./user-menu-dropdown.js";
|
|
@@ -1,31 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* User information to display
|
|
4
|
-
*/
|
|
5
|
-
user: {
|
|
6
|
-
name?: string;
|
|
7
|
-
email?: string;
|
|
8
|
-
avatar?: string;
|
|
9
|
-
};
|
|
10
|
-
/**
|
|
11
|
-
* Whether the dropdown is currently open
|
|
12
|
-
*/
|
|
13
|
-
isOpen?: boolean;
|
|
14
|
-
/**
|
|
15
|
-
* Callback when dropdown open state changes
|
|
16
|
-
*/
|
|
17
|
-
onOpenChange?: (open: boolean) => void;
|
|
18
|
-
/**
|
|
19
|
-
* Callback when logout is clicked
|
|
20
|
-
*/
|
|
21
|
-
onLogout?: () => void;
|
|
22
|
-
/**
|
|
23
|
-
* Base URL for navigation links (default: '/admin')
|
|
24
|
-
*/
|
|
25
|
-
baseUrl?: string;
|
|
26
|
-
/**
|
|
27
|
-
* Additional CSS classes for the trigger button
|
|
28
|
-
*/
|
|
29
|
-
className?: string;
|
|
30
|
-
}
|
|
31
|
-
export declare function UserMenuDropdown({ user, isOpen, onOpenChange, onLogout, baseUrl, className, ref, ...props }: UserMenuDropdownProps): import("react").JSX.Element;
|
|
1
|
+
import type { UserMenuDropdownProps } from "./user-menu-dropdown.types";
|
|
2
|
+
export declare function UserMenuDropdown({ user, isOpen, onOpenChange, onLogout, baseUrl, className, style, slotStyles: slotStylesRaw, ref, ...props }: UserMenuDropdownProps): import("react").JSX.Element;
|
|
@@ -3,9 +3,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { Avatar, AvatarFallback, AvatarImage } from "@kala-ui/react/avatar";
|
|
4
4
|
import { Button } from "@kala-ui/react/button";
|
|
5
5
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@kala-ui/react/dropdown-menu";
|
|
6
|
+
import { useSlotStyles } from "@kala-ui/react/kala-provider";
|
|
7
|
+
import { applySlot } from "@kala-ui/react/lib/slot-styles";
|
|
6
8
|
import { cn } from "@kala-ui/react/lib/utils";
|
|
7
9
|
import { LogOut, Settings, User } from "lucide-react";
|
|
8
|
-
|
|
10
|
+
import { userMenuDropdownStyles } from "../../config/user-menu-dropdown.js";
|
|
11
|
+
export function UserMenuDropdown({ user, isOpen, onOpenChange, onLogout, baseUrl = "/admin", className, style, slotStyles: slotStylesRaw, ref, ...props }) {
|
|
12
|
+
const slotStyles = useSlotStyles("user-menu-dropdown", slotStylesRaw);
|
|
9
13
|
const initials = user.name
|
|
10
14
|
? user.name
|
|
11
15
|
.split(" ")
|
|
@@ -23,5 +27,13 @@ export function UserMenuDropdown({ user, isOpen, onOpenChange, onLogout, baseUrl
|
|
|
23
27
|
const handleLogoutClick = () => {
|
|
24
28
|
onLogout?.();
|
|
25
29
|
};
|
|
26
|
-
|
|
30
|
+
const trigger = applySlot(cn(userMenuDropdownStyles.trigger, className), slotStyles?.trigger);
|
|
31
|
+
const avatar = applySlot(userMenuDropdownStyles.avatar, slotStyles?.avatar);
|
|
32
|
+
const avatarFallback = applySlot(userMenuDropdownStyles.avatarFallback, slotStyles?.avatarFallback);
|
|
33
|
+
const content = applySlot(userMenuDropdownStyles.content, slotStyles?.content);
|
|
34
|
+
const label = applySlot(userMenuDropdownStyles.label, slotStyles?.label);
|
|
35
|
+
const name = applySlot(userMenuDropdownStyles.name, slotStyles?.name);
|
|
36
|
+
const email = applySlot(userMenuDropdownStyles.email, slotStyles?.email);
|
|
37
|
+
const itemIcon = applySlot(userMenuDropdownStyles.itemIcon, slotStyles?.itemIcon);
|
|
38
|
+
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: trigger.className, style: trigger.style, "aria-label": "User menu", ...props, children: _jsxs(Avatar, { className: avatar.className, style: avatar.style, children: [user.avatar && (_jsx(AvatarImage, { src: user.avatar, alt: user.name ?? "User" })), _jsx(AvatarFallback, { className: avatarFallback.className, style: avatarFallback.style, children: initials })] }) }) }), _jsxs(DropdownMenuContent, { align: "end", className: content.className, style: content.style, children: [_jsx(DropdownMenuLabel, { children: _jsxs("div", { className: label.className, style: label.style, children: [user.name && (_jsx("p", { className: name.className, style: name.style, children: user.name })), user.email && (_jsx("p", { className: email.className, style: email.style, children: user.email }))] }) }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { onClick: handleProfileClick, children: [_jsx(User, { className: itemIcon.className }), "Profile"] }), _jsxs(DropdownMenuItem, { onClick: handleSettingsClick, children: [_jsx(Settings, { className: itemIcon.className }), "Settings"] }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { onClick: handleLogoutClick, color: "destructive", children: [_jsx(LogOut, { className: itemIcon.className }), "Logout"] })] })] }));
|
|
27
39
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { SlotStyles } from "@kala-ui/react/lib/slot-styles";
|
|
2
|
+
import type * as React from "react";
|
|
3
|
+
export interface UserMenuDropdownProps extends Omit<React.ComponentProps<"button">, "color"> {
|
|
4
|
+
user: {
|
|
5
|
+
name?: string;
|
|
6
|
+
email?: string;
|
|
7
|
+
avatar?: string;
|
|
8
|
+
};
|
|
9
|
+
isOpen?: boolean;
|
|
10
|
+
onOpenChange?: (open: boolean) => void;
|
|
11
|
+
onLogout?: () => void;
|
|
12
|
+
baseUrl?: string;
|
|
13
|
+
className?: string;
|
|
14
|
+
/** Per-part overrides: trigger, avatar, avatarFallback, content, label, name, email, itemIcon. */
|
|
15
|
+
slotStyles?: SlotStyles;
|
|
16
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const appShellStyles: {
|
|
2
|
+
base: string;
|
|
3
|
+
header: string;
|
|
4
|
+
headerBorder: string;
|
|
5
|
+
navbar: string;
|
|
6
|
+
navbarBorder: string;
|
|
7
|
+
aside: string;
|
|
8
|
+
asideBorder: string;
|
|
9
|
+
main: string;
|
|
10
|
+
footer: string;
|
|
11
|
+
footerBorder: string;
|
|
12
|
+
padding: Record<string, string>;
|
|
13
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const appShellStyles = {
|
|
2
|
+
base: "flex min-h-screen flex-col bg-background text-foreground",
|
|
3
|
+
header: "fixed top-0 left-0 right-0 z-50 flex items-center bg-background px-4",
|
|
4
|
+
headerBorder: "border-b",
|
|
5
|
+
navbar: "fixed left-0 z-40 flex flex-col bg-background transition-transform duration-300 ease-in-out",
|
|
6
|
+
navbarBorder: "border-r",
|
|
7
|
+
aside: "fixed right-0 z-40 flex flex-col bg-background transition-transform duration-300 ease-in-out",
|
|
8
|
+
asideBorder: "border-l",
|
|
9
|
+
main: "flex-1 transition-all duration-300 ease-in-out",
|
|
10
|
+
footer: "fixed bottom-0 left-0 right-0 z-50 flex items-center bg-background px-4",
|
|
11
|
+
footerBorder: "border-t",
|
|
12
|
+
padding: {
|
|
13
|
+
none: "p-0",
|
|
14
|
+
xs: "p-2",
|
|
15
|
+
sm: "p-3",
|
|
16
|
+
md: "p-4",
|
|
17
|
+
lg: "p-6",
|
|
18
|
+
xl: "p-8",
|
|
19
|
+
},
|
|
20
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export declare const chartSkeletonStyles: {
|
|
2
|
+
root: string;
|
|
3
|
+
frame: string;
|
|
4
|
+
frameInner: string;
|
|
5
|
+
frameFill: string;
|
|
6
|
+
legendRow: string;
|
|
7
|
+
legendItem: string;
|
|
8
|
+
legendDot: string;
|
|
9
|
+
legendLabel: string;
|
|
10
|
+
tableWrap: string;
|
|
11
|
+
tableHead: string;
|
|
12
|
+
tableRow: string;
|
|
13
|
+
tableCell: string;
|
|
14
|
+
};
|
|
15
|
+
export declare const chartStyles: {
|
|
16
|
+
root: string;
|
|
17
|
+
empty: string;
|
|
18
|
+
emptyIcon: string;
|
|
19
|
+
emptyText: string;
|
|
20
|
+
titleBlock: string;
|
|
21
|
+
title: string;
|
|
22
|
+
subtitle: string;
|
|
23
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const chartSkeletonStyles = {
|
|
2
|
+
root: "w-full space-y-4",
|
|
3
|
+
frame: "w-full rounded-lg border bg-card kala-surface-card",
|
|
4
|
+
frameInner: "flex items-center justify-center h-full",
|
|
5
|
+
frameFill: "h-full w-full",
|
|
6
|
+
legendRow: "flex gap-4 justify-center mt-4",
|
|
7
|
+
legendItem: "flex items-center gap-2",
|
|
8
|
+
legendDot: "h-3 w-3 rounded-full",
|
|
9
|
+
legendLabel: "h-4 w-16",
|
|
10
|
+
tableWrap: "mt-6",
|
|
11
|
+
tableHead: "h-4 w-24",
|
|
12
|
+
tableRow: "border-0",
|
|
13
|
+
tableCell: "h-4 w-20",
|
|
14
|
+
};
|
|
15
|
+
export const chartStyles = {
|
|
16
|
+
root: "w-full",
|
|
17
|
+
empty: "w-full flex flex-col items-center justify-center text-center rounded-lg border bg-card text-muted-foreground",
|
|
18
|
+
emptyIcon: "mb-3 h-10 w-10 opacity-40",
|
|
19
|
+
emptyText: "text-sm",
|
|
20
|
+
titleBlock: "mb-1.5 text-left",
|
|
21
|
+
title: "text-lg font-semibold text-foreground",
|
|
22
|
+
subtitle: "text-sm text-muted-foreground",
|
|
23
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare const footerStyles: {
|
|
2
|
+
root: string;
|
|
3
|
+
inner: string;
|
|
4
|
+
centeredContent: string;
|
|
5
|
+
grid: string;
|
|
6
|
+
sectionTitle: string;
|
|
7
|
+
sectionList: string;
|
|
8
|
+
sectionLink: string;
|
|
9
|
+
customContent: string;
|
|
10
|
+
bottomRow: string;
|
|
11
|
+
bottomRowSide: string;
|
|
12
|
+
bottomRowCentered: string;
|
|
13
|
+
copyright: string;
|
|
14
|
+
socialList: string;
|
|
15
|
+
socialLink: string;
|
|
16
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const footerStyles = {
|
|
2
|
+
root: "w-full bg-muted text-muted-foreground border-t",
|
|
3
|
+
inner: "container mx-auto px-4 py-8 md:py-12",
|
|
4
|
+
centeredContent: "mb-12 flex flex-col items-center text-center",
|
|
5
|
+
grid: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8 mb-8",
|
|
6
|
+
sectionTitle: "text-foreground font-semibold text-sm uppercase tracking-wider mb-4",
|
|
7
|
+
sectionList: "space-y-2",
|
|
8
|
+
sectionLink: "text-muted-foreground hover:text-primary transition-colors text-sm",
|
|
9
|
+
customContent: "md:col-span-2 lg:col-span-1",
|
|
10
|
+
bottomRow: "border-t pt-8 flex flex-col gap-4",
|
|
11
|
+
bottomRowSide: "md:flex-row md:justify-between md:items-center",
|
|
12
|
+
bottomRowCentered: "items-center",
|
|
13
|
+
copyright: "text-sm text-muted-foreground order-2 md:order-1",
|
|
14
|
+
socialList: "flex items-center gap-4 order-1 md:order-2",
|
|
15
|
+
socialLink: "text-muted-foreground hover:text-primary transition-colors",
|
|
16
|
+
};
|