@nexub/patterns 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,28 @@
1
+ 'use client';
2
+
3
+ // src/components/InfoList/InfoList.tsx
4
+ import * as React from "react";
5
+ import { cn } from "@nexub/react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ var Root = React.forwardRef(
8
+ ({ className, children, ...props }, ref) => /* @__PURE__ */ jsx(
9
+ "dl",
10
+ {
11
+ ref,
12
+ className: cn("divide-y divide-[var(--color-border-default)]", className),
13
+ ...props,
14
+ children
15
+ }
16
+ )
17
+ );
18
+ Root.displayName = "InfoList.Root";
19
+ var Item = ({ label, value, className }) => /* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between py-3 gap-4", className), children: [
20
+ /* @__PURE__ */ jsx("dt", { className: "text-sm text-[var(--color-text-muted)] shrink-0", children: label }),
21
+ /* @__PURE__ */ jsx("dd", { className: "text-sm font-medium text-[var(--color-text-primary)] text-right", children: value })
22
+ ] });
23
+ Item.displayName = "InfoList.Item";
24
+ var InfoList = { Root, Item };
25
+
26
+ export {
27
+ InfoList
28
+ };
@@ -0,0 +1,60 @@
1
+ 'use client';
2
+
3
+ // src/components/StatCard/StatCard.tsx
4
+ import * as React from "react";
5
+ import { cn } from "@nexub/react";
6
+ import { TrendingUp, TrendingDown, Minus } from "@nexub/icons";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ var StatCard = React.forwardRef(
9
+ ({
10
+ label,
11
+ value,
12
+ delta,
13
+ deltaType = "neutral",
14
+ icon,
15
+ description,
16
+ className,
17
+ ...props
18
+ }, ref) => {
19
+ const deltaColor = deltaType === "increase" ? "var(--color-success-500)" : deltaType === "decrease" ? "var(--color-error-500)" : "var(--color-text-muted)";
20
+ const DeltaIcon = deltaType === "increase" ? TrendingUp : deltaType === "decrease" ? TrendingDown : Minus;
21
+ return /* @__PURE__ */ jsxs(
22
+ "div",
23
+ {
24
+ ref,
25
+ className: cn(
26
+ "rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-default)] p-6 flex flex-col gap-3",
27
+ className
28
+ ),
29
+ ...props,
30
+ children: [
31
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
32
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--color-text-muted)]", children: label }),
33
+ icon && /* @__PURE__ */ jsx("span", { className: "text-[var(--color-text-muted)]", children: icon })
34
+ ] }),
35
+ /* @__PURE__ */ jsx("div", { className: "text-3xl font-bold tracking-tight text-[var(--color-text-primary)]", children: value }),
36
+ (delta || description) && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
37
+ delta && /* @__PURE__ */ jsxs(
38
+ "span",
39
+ {
40
+ className: "inline-flex items-center gap-1 text-xs font-medium",
41
+ style: { color: deltaColor },
42
+ "data-delta-type": deltaType,
43
+ children: [
44
+ /* @__PURE__ */ jsx(DeltaIcon, { size: "xs", "aria-hidden": "true" }),
45
+ delta
46
+ ]
47
+ }
48
+ ),
49
+ description && /* @__PURE__ */ jsx("span", { className: "text-xs text-[var(--color-text-muted)]", children: description })
50
+ ] })
51
+ ]
52
+ }
53
+ );
54
+ }
55
+ );
56
+ StatCard.displayName = "StatCard";
57
+
58
+ export {
59
+ StatCard
60
+ };
@@ -0,0 +1,53 @@
1
+ 'use client';
2
+
3
+ // src/components/UserCard/UserCard.tsx
4
+ import * as React from "react";
5
+ import { cn, Avatar } from "@nexub/react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ var UserCard = React.forwardRef(
8
+ ({
9
+ name,
10
+ role,
11
+ email,
12
+ avatarSrc,
13
+ avatarFallback,
14
+ actions,
15
+ size = "md",
16
+ className,
17
+ ...props
18
+ }, ref) => {
19
+ return /* @__PURE__ */ jsxs(
20
+ "div",
21
+ {
22
+ ref,
23
+ className: cn(
24
+ "flex items-center gap-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-default)] p-4",
25
+ className
26
+ ),
27
+ ...props,
28
+ children: [
29
+ /* @__PURE__ */ jsx(
30
+ Avatar,
31
+ {
32
+ size,
33
+ src: avatarSrc,
34
+ name,
35
+ fallback: avatarFallback
36
+ }
37
+ ),
38
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0 flex flex-col gap-0.5", children: [
39
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-[var(--color-text-primary)] truncate", children: name }),
40
+ role && /* @__PURE__ */ jsx("span", { className: "text-xs text-[var(--color-text-muted)] truncate", children: role }),
41
+ email && /* @__PURE__ */ jsx("span", { className: "text-xs text-[var(--color-text-secondary)] truncate", children: email })
42
+ ] }),
43
+ actions && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 shrink-0", children: actions })
44
+ ]
45
+ }
46
+ );
47
+ }
48
+ );
49
+ UserCard.displayName = "UserCard";
50
+
51
+ export {
52
+ UserCard
53
+ };
@@ -0,0 +1,38 @@
1
+ 'use client';
2
+
3
+ // src/components/AuthCard/AuthCard.tsx
4
+ import "react";
5
+ import { cn } from "@nexub/react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ function AuthCard({
8
+ logo,
9
+ title,
10
+ description,
11
+ children,
12
+ footer,
13
+ className
14
+ }) {
15
+ return /* @__PURE__ */ jsxs(
16
+ "div",
17
+ {
18
+ className: cn(
19
+ "mx-auto w-full max-w-[400px] flex flex-col gap-6",
20
+ className
21
+ ),
22
+ children: [
23
+ logo && /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: logo }),
24
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 text-center", children: [
25
+ /* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold tracking-tight text-[var(--color-text-primary)]", children: title }),
26
+ description && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--color-text-muted)]", children: description })
27
+ ] }),
28
+ /* @__PURE__ */ jsx("div", { className: "rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-default)] p-6 flex flex-col gap-4", children }),
29
+ footer && /* @__PURE__ */ jsx("div", { className: "flex flex-col items-center gap-2 text-sm text-[var(--color-text-muted)]", children: footer })
30
+ ]
31
+ }
32
+ );
33
+ }
34
+ AuthCard.displayName = "AuthCard";
35
+
36
+ export {
37
+ AuthCard
38
+ };
@@ -0,0 +1,27 @@
1
+ 'use client';
2
+
3
+ // src/components/FilterBar/FilterBar.tsx
4
+ import * as React from "react";
5
+ import { cn } from "@nexub/react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ var FilterBar = React.forwardRef(
8
+ ({ searchPlaceholder, onSearch, searchValue, filters, actions, className, ...props }, ref) => /* @__PURE__ */ jsxs("div", { ref, className: cn("flex flex-wrap items-center gap-3", className), ...props, children: [
9
+ /* @__PURE__ */ jsx(
10
+ "input",
11
+ {
12
+ type: "search",
13
+ placeholder: searchPlaceholder ?? "Search\u2026",
14
+ value: searchValue,
15
+ onChange: (e) => onSearch?.(e.target.value),
16
+ className: "h-9 rounded-md border border-[var(--color-border-default)] bg-[var(--color-bg-default)] px-3 py-2 text-sm placeholder:text-[var(--color-text-muted)] focus:outline-none focus:ring-2 focus:ring-[var(--color-brand-primary-500)] w-full sm:w-64"
17
+ }
18
+ ),
19
+ filters && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2", children: filters }),
20
+ actions && /* @__PURE__ */ jsx("div", { className: "ml-auto flex items-center gap-2", children: actions })
21
+ ] })
22
+ );
23
+ FilterBar.displayName = "FilterBar";
24
+
25
+ export {
26
+ FilterBar
27
+ };
@@ -0,0 +1,43 @@
1
+ 'use client';
2
+
3
+ // src/components/StepIndicator/StepIndicator.tsx
4
+ import * as React from "react";
5
+ import { cn } from "@nexub/react";
6
+ import { Check } from "@nexub/icons";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ var StepIndicator = React.forwardRef(
9
+ ({ steps, currentStep, className, ...props }, ref) => /* @__PURE__ */ jsx("ol", { ref, className: cn("flex flex-col gap-0", className), ...props, children: steps.map((step, index) => {
10
+ const status = index < currentStep ? "complete" : index === currentStep ? "current" : "upcoming";
11
+ return /* @__PURE__ */ jsxs("li", { className: "relative flex gap-4 pb-6 last:pb-0", children: [
12
+ index < steps.length - 1 && /* @__PURE__ */ jsx(
13
+ "div",
14
+ {
15
+ className: "absolute left-4 top-8 h-full w-px bg-[var(--color-border-default)]",
16
+ "aria-hidden": "true"
17
+ }
18
+ ),
19
+ /* @__PURE__ */ jsx(
20
+ "div",
21
+ {
22
+ className: cn(
23
+ "relative z-10 flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-xs font-semibold",
24
+ status === "complete" && "bg-[var(--color-fill-primary)] text-white",
25
+ status === "current" && "border-2 border-[var(--color-brand-primary-500)] text-[var(--color-brand-primary-500)] bg-[var(--color-bg-default)]",
26
+ status === "upcoming" && "border-2 border-[var(--color-border-default)] text-[var(--color-text-muted)] bg-[var(--color-bg-default)]"
27
+ ),
28
+ "aria-current": status === "current" ? "step" : void 0,
29
+ children: status === "complete" ? /* @__PURE__ */ jsx(Check, { size: "xs", "aria-hidden": "true" }) : index + 1
30
+ }
31
+ ),
32
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5 pt-0.5", children: [
33
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-[var(--color-text-primary)]", children: step.label }),
34
+ step.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--color-text-muted)]", children: step.description })
35
+ ] })
36
+ ] }, step.label);
37
+ }) })
38
+ );
39
+ StepIndicator.displayName = "StepIndicator";
40
+
41
+ export {
42
+ StepIndicator
43
+ };
@@ -0,0 +1,23 @@
1
+ 'use client';
2
+
3
+ // src/components/SettingsSection/SettingsSection.tsx
4
+ import * as React from "react";
5
+ import { cn } from "@nexub/react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ var SettingsSection = React.forwardRef(
8
+ ({ title, description, actions, children, className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("flex flex-col gap-8", className), ...props, children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6 md:flex-row md:gap-8", children: [
9
+ /* @__PURE__ */ jsxs("div", { className: "md:w-1/3 flex flex-col gap-1", children: [
10
+ /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold text-[var(--color-text-primary)]", children: title }),
11
+ description && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--color-text-muted)]", children: description })
12
+ ] }),
13
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col gap-4", children: [
14
+ children,
15
+ actions && /* @__PURE__ */ jsx("div", { className: "flex justify-end gap-2", children: actions })
16
+ ] })
17
+ ] }) })
18
+ );
19
+ SettingsSection.displayName = "SettingsSection";
20
+
21
+ export {
22
+ SettingsSection
23
+ };
@@ -0,0 +1,69 @@
1
+ 'use client';
2
+
3
+ // src/components/PlanCard/PlanCard.tsx
4
+ import * as React from "react";
5
+ import { cn, Badge, Button } from "@nexub/react";
6
+ import { Check } from "@nexub/icons";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ var PlanCard = React.forwardRef(
9
+ ({
10
+ name,
11
+ price,
12
+ period,
13
+ description,
14
+ features,
15
+ cta,
16
+ onSelect,
17
+ highlighted = false,
18
+ badge,
19
+ className,
20
+ ...props
21
+ }, ref) => /* @__PURE__ */ jsxs(
22
+ "div",
23
+ {
24
+ ref,
25
+ className: cn(
26
+ "relative flex flex-col gap-6 rounded-xl border bg-[var(--color-bg-default)] p-6",
27
+ highlighted ? "border-[var(--color-brand-primary-500)] shadow-lg ring-1 ring-[var(--color-brand-primary-500)]" : "border-[var(--color-border-default)]",
28
+ className
29
+ ),
30
+ ...props,
31
+ children: [
32
+ badge && /* @__PURE__ */ jsx("div", { className: "absolute -top-3 left-1/2 -translate-x-1/2", children: /* @__PURE__ */ jsx(Badge, { tone: "info", children: badge }) }),
33
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
34
+ /* @__PURE__ */ jsx("p", { className: "text-base font-semibold text-[var(--color-text-primary)]", children: name }),
35
+ /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-1", children: [
36
+ /* @__PURE__ */ jsx("span", { className: "text-3xl font-bold text-[var(--color-text-primary)]", children: price }),
37
+ period && /* @__PURE__ */ jsx("span", { className: "text-sm text-[var(--color-text-muted)]", children: period })
38
+ ] }),
39
+ description && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--color-text-muted)]", children: description })
40
+ ] }),
41
+ /* @__PURE__ */ jsx("ul", { className: "flex flex-col gap-2", children: features.map((feature) => /* @__PURE__ */ jsxs(
42
+ "li",
43
+ {
44
+ className: "flex items-center gap-2 text-sm text-[var(--color-text-secondary)]",
45
+ children: [
46
+ /* @__PURE__ */ jsx(Check, { size: "xs", className: "text-[var(--color-success-500)] shrink-0", "aria-hidden": "true" }),
47
+ feature
48
+ ]
49
+ },
50
+ feature
51
+ )) }),
52
+ /* @__PURE__ */ jsx(
53
+ Button,
54
+ {
55
+ variant: highlighted ? "primary" : "outline",
56
+ className: "mt-auto w-full",
57
+ onClick: onSelect,
58
+ children: cta
59
+ }
60
+ )
61
+ ]
62
+ }
63
+ )
64
+ );
65
+ PlanCard.displayName = "PlanCard";
66
+
67
+ export {
68
+ PlanCard
69
+ };
@@ -0,0 +1,112 @@
1
+ 'use client';
2
+
3
+ // src/components/SettingsLayout/SettingsLayout.tsx
4
+ import * as React from "react";
5
+ import { Card, IconButton, Sheet, Sidebar, TopNav, cn } from "@nexub/react";
6
+ import { Menu } from "@nexub/icons";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ var isPlainClick = (e) => e.button === 0 && !e.metaKey && !e.ctrlKey && !e.shiftKey && !e.altKey;
9
+ function SectionNav({
10
+ items,
11
+ activeId,
12
+ onChoose,
13
+ label,
14
+ className
15
+ }) {
16
+ return /* @__PURE__ */ jsx(Sidebar.Root, { "aria-label": label, className, children: /* @__PURE__ */ jsx(Sidebar.Group, { children: items.map((item) => /* @__PURE__ */ jsx(
17
+ Sidebar.Item,
18
+ {
19
+ href: item.href,
20
+ icon: item.icon,
21
+ active: item.id === activeId,
22
+ onClick: (e) => onChoose(e, item),
23
+ children: item.label
24
+ },
25
+ item.id
26
+ )) }) });
27
+ }
28
+ var Root = React.forwardRef(
29
+ ({ items, activeId, onSelect, logo, actions, title, description, navLabel = "Settings", className, children, ...props }, ref) => {
30
+ const [menuOpen, setMenuOpen] = React.useState(false);
31
+ const onChoose = (e, item) => {
32
+ if (!isPlainClick(e)) return;
33
+ if (onSelect) {
34
+ e.preventDefault();
35
+ onSelect(item);
36
+ }
37
+ setMenuOpen(false);
38
+ };
39
+ return /* @__PURE__ */ jsxs("div", { ref, className: cn("flex min-h-screen flex-col bg-[var(--color-bg-default)]", className), ...props, children: [
40
+ /* @__PURE__ */ jsxs(TopNav.Root, { children: [
41
+ /* @__PURE__ */ jsx(IconButton, { label: `Open ${navLabel.toLowerCase()} menu`, className: "-ml-2 md:hidden", onClick: () => setMenuOpen(true), children: /* @__PURE__ */ jsx(Menu, { size: "md", "aria-hidden": "true" }) }),
42
+ logo && /* @__PURE__ */ jsx(TopNav.Logo, { className: "min-w-0", children: logo }),
43
+ actions && /* @__PURE__ */ jsx(TopNav.Actions, { children: actions })
44
+ ] }),
45
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-1", children: [
46
+ /* @__PURE__ */ jsx(
47
+ SectionNav,
48
+ {
49
+ items,
50
+ activeId,
51
+ onChoose,
52
+ label: navLabel,
53
+ className: "sticky top-[var(--size-nav-header,64px)] hidden h-[calc(100vh-var(--size-nav-header,64px))] shrink-0 md:flex"
54
+ }
55
+ ),
56
+ /* @__PURE__ */ jsx("main", { className: "min-w-0 flex-1 px-4 py-6 md:px-8 md:py-10", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto flex w-full max-w-3xl flex-col gap-6", children: [
57
+ (title || description) && /* @__PURE__ */ jsxs("header", { className: "flex flex-col gap-1", children: [
58
+ title && /* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold tracking-tight text-[var(--color-text-primary)]", children: title }),
59
+ description && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--color-text-muted)]", children: description })
60
+ ] }),
61
+ children
62
+ ] }) })
63
+ ] }),
64
+ /* @__PURE__ */ jsx(Sheet.Root, { open: menuOpen, onOpenChange: setMenuOpen, direction: "left", children: /* @__PURE__ */ jsxs(Sheet.Portal, { children: [
65
+ /* @__PURE__ */ jsx(Sheet.Overlay, { className: "md:hidden" }),
66
+ /* @__PURE__ */ jsxs(Sheet.Content, { side: "left", "aria-describedby": void 0, className: "md:hidden", children: [
67
+ /* @__PURE__ */ jsx(Sheet.Title, { className: "px-5 pb-2 pt-5 text-base", children: navLabel }),
68
+ /* @__PURE__ */ jsx(SectionNav, { items, activeId, onChoose, label: navLabel, className: "w-full border-r-0" })
69
+ ] })
70
+ ] }) })
71
+ ] });
72
+ }
73
+ );
74
+ Root.displayName = "SettingsLayout";
75
+ var Section = React.forwardRef(
76
+ ({ title, description, className, children, ...props }, ref) => /* @__PURE__ */ jsx(Card, { asChild: true, className: cn("px-5 py-1", className), children: /* @__PURE__ */ jsxs("section", { ref, ...props, children: [
77
+ (title || description) && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 border-b border-[var(--color-border-default)] pb-4 pt-4", children: [
78
+ title && /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold text-[var(--color-text-primary)]", children: title }),
79
+ description && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--color-text-muted)]", children: description })
80
+ ] }),
81
+ children
82
+ ] }) })
83
+ );
84
+ Section.displayName = "SettingsLayout.Section";
85
+ var SettingsRow = React.forwardRef(
86
+ ({ label, description, value, action, className, ...props }, ref) => /* @__PURE__ */ jsxs(
87
+ "div",
88
+ {
89
+ ref,
90
+ className: cn(
91
+ "flex flex-col gap-2 border-t border-[var(--color-border-default)] py-4 first:border-t-0 sm:flex-row sm:items-center sm:gap-4",
92
+ className
93
+ ),
94
+ ...props,
95
+ children: [
96
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5 sm:w-1/3 sm:shrink-0", children: [
97
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--color-text-primary)]", children: label }),
98
+ description && /* @__PURE__ */ jsx("span", { className: "text-xs text-[var(--color-text-muted)]", children: description })
99
+ ] }),
100
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 break-words text-sm text-[var(--color-text-secondary)]", children: value }),
101
+ action && /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2", children: action })
102
+ ]
103
+ }
104
+ )
105
+ );
106
+ SettingsRow.displayName = "SettingsRow";
107
+ var SettingsLayout = Object.assign(Root, { Section, Row: SettingsRow });
108
+
109
+ export {
110
+ SettingsRow,
111
+ SettingsLayout
112
+ };
@@ -0,0 +1,97 @@
1
+ 'use client';
2
+
3
+ // src/components/AppLauncher/AppLauncher.tsx
4
+ import * as React from "react";
5
+ import { Avatar, IconButton, Popover, cn } from "@nexub/react";
6
+ import { Apps } from "@nexub/icons";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ var COLUMNS = 3;
9
+ function AppIcon({ app }) {
10
+ const [failed, setFailed] = React.useState(false);
11
+ if (app.icon && !failed) {
12
+ return /* @__PURE__ */ jsx(
13
+ "img",
14
+ {
15
+ src: app.icon,
16
+ alt: "",
17
+ width: 40,
18
+ height: 40,
19
+ className: "h-10 w-10 rounded-xl object-cover",
20
+ onError: () => setFailed(true)
21
+ }
22
+ );
23
+ }
24
+ return /* @__PURE__ */ jsx(Avatar, { name: app.name, size: "md", className: "rounded-xl", "aria-hidden": "true" });
25
+ }
26
+ var AppLauncher = React.forwardRef(
27
+ ({ apps, onNavigate, surface = "solid", label = "Nexub apps", className }, ref) => {
28
+ const [open, setOpen] = React.useState(false);
29
+ const listRef = React.useRef(null);
30
+ if (apps.length === 0) return null;
31
+ const onOpenAutoFocus = (e) => {
32
+ e.preventDefault();
33
+ const list = listRef.current;
34
+ (list?.querySelector("a[aria-current]") ?? list?.querySelector("a"))?.focus();
35
+ };
36
+ const onKeyDown = (e) => {
37
+ const links = [...e.currentTarget.querySelectorAll("a")];
38
+ const i = links.indexOf(document.activeElement);
39
+ if (i < 0) return;
40
+ const step = {
41
+ ArrowRight: 1,
42
+ ArrowLeft: -1,
43
+ ArrowDown: COLUMNS,
44
+ ArrowUp: -COLUMNS,
45
+ Home: -i,
46
+ End: links.length - 1 - i
47
+ };
48
+ const delta = step[e.key];
49
+ if (delta === void 0) return;
50
+ e.preventDefault();
51
+ links[Math.min(links.length - 1, Math.max(0, i + delta))]?.focus();
52
+ };
53
+ const onClick = (e, app) => {
54
+ const plain = e.button === 0 && !e.metaKey && !e.ctrlKey && !e.shiftKey && !e.altKey;
55
+ if (onNavigate && plain) {
56
+ e.preventDefault();
57
+ onNavigate(app);
58
+ }
59
+ if (plain) setOpen(false);
60
+ };
61
+ return /* @__PURE__ */ jsxs(Popover.Root, { open, onOpenChange: setOpen, children: [
62
+ /* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(IconButton, { ref, label, className, children: /* @__PURE__ */ jsx(Apps, { size: "md", "aria-hidden": "true" }) }) }),
63
+ /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(
64
+ Popover.Content,
65
+ {
66
+ align: "end",
67
+ surface,
68
+ "aria-label": label,
69
+ onOpenAutoFocus,
70
+ className: "w-80 max-w-[calc(100vw-2rem)] p-2",
71
+ children: /* @__PURE__ */ jsx("ul", { ref: listRef, className: "grid grid-cols-3 gap-1", onKeyDown, children: apps.map((app) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
72
+ "a",
73
+ {
74
+ href: app.href,
75
+ "aria-current": app.current ? "page" : void 0,
76
+ onClick: (e) => onClick(e, app),
77
+ className: cn(
78
+ "flex flex-col items-center gap-1.5 rounded-lg px-1 py-3 text-center text-xs text-[var(--color-text-primary)] transition-colors",
79
+ "hover:bg-[var(--color-bg-subtle)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-ring-default)]",
80
+ app.current && "bg-[var(--color-bg-subtle)] font-semibold"
81
+ ),
82
+ children: [
83
+ /* @__PURE__ */ jsx(AppIcon, { app }),
84
+ /* @__PURE__ */ jsx("span", { className: "w-full truncate", children: app.name })
85
+ ]
86
+ }
87
+ ) }, app.id)) })
88
+ }
89
+ ) })
90
+ ] });
91
+ }
92
+ );
93
+ AppLauncher.displayName = "AppLauncher";
94
+
95
+ export {
96
+ AppLauncher
97
+ };
@@ -0,0 +1,50 @@
1
+ 'use client';
2
+
3
+ // src/components/NotificationItem/NotificationItem.tsx
4
+ import * as React from "react";
5
+ import { cn } from "@nexub/react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ var NotificationItem = React.forwardRef(
8
+ ({ title, description, timestamp, read = false, icon, onDismiss, className, ...props }, ref) => /* @__PURE__ */ jsxs(
9
+ "div",
10
+ {
11
+ ref,
12
+ className: cn(
13
+ "flex items-start gap-3 p-3 rounded-lg transition-colors hover:bg-[var(--color-bg-subtle)]",
14
+ !read && "bg-[var(--color-bg-subtle)]",
15
+ className
16
+ ),
17
+ ...props,
18
+ children: [
19
+ !read && /* @__PURE__ */ jsx(
20
+ "span",
21
+ {
22
+ className: "mt-1.5 h-2 w-2 shrink-0 rounded-full bg-[var(--color-brand-primary-500)]",
23
+ "aria-label": "Unread"
24
+ }
25
+ ),
26
+ icon && /* @__PURE__ */ jsx("div", { className: "shrink-0 mt-0.5", children: icon }),
27
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
28
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-[var(--color-text-primary)] truncate", children: title }),
29
+ description && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--color-text-muted)] mt-0.5 line-clamp-2", children: description }),
30
+ timestamp && /* @__PURE__ */ jsx("time", { className: "text-xs text-[var(--color-text-muted)] mt-1 block", children: timestamp })
31
+ ] }),
32
+ onDismiss && /* @__PURE__ */ jsx(
33
+ "button",
34
+ {
35
+ type: "button",
36
+ onClick: onDismiss,
37
+ "aria-label": "Dismiss notification",
38
+ className: "shrink-0 text-[var(--color-text-muted)] hover:text-[var(--color-text-primary)] p-1 rounded",
39
+ children: "\xD7"
40
+ }
41
+ )
42
+ ]
43
+ }
44
+ )
45
+ );
46
+ NotificationItem.displayName = "NotificationItem";
47
+
48
+ export {
49
+ NotificationItem
50
+ };
@@ -0,0 +1,34 @@
1
+ 'use client';
2
+
3
+ // src/components/ActivityFeed/ActivityFeed.tsx
4
+ import * as React from "react";
5
+ import { cn } from "@nexub/react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ var Root = React.forwardRef(
8
+ ({ className, children, ...props }, ref) => /* @__PURE__ */ jsx(
9
+ "ul",
10
+ {
11
+ ref,
12
+ role: "feed",
13
+ "aria-label": "Activity feed",
14
+ className: cn("flex flex-col divide-y divide-[var(--color-border-default)]", className),
15
+ ...props,
16
+ children
17
+ }
18
+ )
19
+ );
20
+ Root.displayName = "ActivityFeed.Root";
21
+ var Item = ({ avatar, title, description, timestamp, className }) => /* @__PURE__ */ jsxs("li", { className: cn("flex items-start gap-3 py-3", className), children: [
22
+ avatar && /* @__PURE__ */ jsx("div", { className: "shrink-0 mt-0.5", children: avatar }),
23
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
24
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-[var(--color-text-primary)]", children: title }),
25
+ description && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--color-text-muted)] mt-0.5", children: description })
26
+ ] }),
27
+ timestamp && /* @__PURE__ */ jsx("time", { className: "shrink-0 text-xs text-[var(--color-text-muted)]", children: timestamp })
28
+ ] });
29
+ Item.displayName = "ActivityFeed.Item";
30
+ var ActivityFeed = { Root, Item };
31
+
32
+ export {
33
+ ActivityFeed
34
+ };
@@ -0,0 +1,7 @@
1
+ 'use client';
2
+ import {
3
+ ActivityFeed
4
+ } from "../../chunk-YX7UO577.js";
5
+ export {
6
+ ActivityFeed
7
+ };
@@ -0,0 +1,7 @@
1
+ 'use client';
2
+ import {
3
+ AppLauncher
4
+ } from "../../chunk-WQX5FFVK.js";
5
+ export {
6
+ AppLauncher
7
+ };