@nexub/patterns 1.9.0 → 2.0.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.
- package/dist/{chunk-6PV4NM55.js → chunk-2WKGBINE.js} +1 -1
- package/dist/chunk-BDCVWAH7.js +123 -0
- package/dist/components/AppHeader/index.js +2 -2
- package/dist/components/AppLauncher/index.js +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +17 -17
- package/package.json +7 -7
- package/dist/chunk-H5UTW5ML.js +0 -97
|
@@ -0,0 +1,123 @@
|
|
|
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 groupApps(apps) {
|
|
10
|
+
const sections = [];
|
|
11
|
+
for (const app of apps) {
|
|
12
|
+
const s = sections.find((x) => x.group === app.group);
|
|
13
|
+
if (s) s.items.push(app);
|
|
14
|
+
else sections.push({ group: app.group, items: [app], firstRow: 0 });
|
|
15
|
+
}
|
|
16
|
+
let row = 0;
|
|
17
|
+
for (const s of sections) {
|
|
18
|
+
s.firstRow = row;
|
|
19
|
+
row += Math.ceil(s.items.length / COLUMNS);
|
|
20
|
+
}
|
|
21
|
+
return sections;
|
|
22
|
+
}
|
|
23
|
+
function AppIcon({ app }) {
|
|
24
|
+
const [failed, setFailed] = React.useState(false);
|
|
25
|
+
if (app.icon && !failed) {
|
|
26
|
+
return /* @__PURE__ */ jsx(
|
|
27
|
+
"img",
|
|
28
|
+
{
|
|
29
|
+
src: app.icon,
|
|
30
|
+
alt: "",
|
|
31
|
+
width: 40,
|
|
32
|
+
height: 40,
|
|
33
|
+
className: "h-10 w-10 rounded-[12px] object-cover",
|
|
34
|
+
onError: () => setFailed(true)
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
return /* @__PURE__ */ jsx(Avatar, { name: app.name, size: "md", className: "rounded-[12px]", "aria-hidden": "true" });
|
|
39
|
+
}
|
|
40
|
+
var AppLauncher = React.forwardRef(
|
|
41
|
+
({ apps, onNavigate, surface = "solid", label = "Nexub apps", className }, ref) => {
|
|
42
|
+
const [open, setOpen] = React.useState(false);
|
|
43
|
+
const listRef = React.useRef(null);
|
|
44
|
+
if (apps.length === 0) return null;
|
|
45
|
+
const sections = groupApps(apps);
|
|
46
|
+
const onOpenAutoFocus = (e) => {
|
|
47
|
+
e.preventDefault();
|
|
48
|
+
const list = listRef.current;
|
|
49
|
+
(list?.querySelector("a[aria-current]") ?? list?.querySelector("a"))?.focus();
|
|
50
|
+
};
|
|
51
|
+
const onKeyDown = (e) => {
|
|
52
|
+
const links = [...e.currentTarget.querySelectorAll("a")];
|
|
53
|
+
const i = links.indexOf(document.activeElement);
|
|
54
|
+
if (i < 0) return;
|
|
55
|
+
const at = (a) => [Number(a.dataset.row), Number(a.dataset.col)];
|
|
56
|
+
const vertical = (dir) => {
|
|
57
|
+
const [row, col] = at(links[i]);
|
|
58
|
+
const next = links.filter((a) => at(a)[0] === row + dir);
|
|
59
|
+
return next.length ? links.indexOf(next[Math.min(col, next.length - 1)]) : i;
|
|
60
|
+
};
|
|
61
|
+
const target = {
|
|
62
|
+
ArrowRight: () => i + 1,
|
|
63
|
+
ArrowLeft: () => i - 1,
|
|
64
|
+
ArrowDown: () => vertical(1),
|
|
65
|
+
ArrowUp: () => vertical(-1),
|
|
66
|
+
Home: () => 0,
|
|
67
|
+
End: () => links.length - 1
|
|
68
|
+
};
|
|
69
|
+
const go = target[e.key];
|
|
70
|
+
if (!go) return;
|
|
71
|
+
e.preventDefault();
|
|
72
|
+
links[Math.min(links.length - 1, Math.max(0, go()))]?.focus();
|
|
73
|
+
};
|
|
74
|
+
const onClick = (e, app) => {
|
|
75
|
+
const plain = e.button === 0 && !e.metaKey && !e.ctrlKey && !e.shiftKey && !e.altKey;
|
|
76
|
+
if (onNavigate && plain) {
|
|
77
|
+
e.preventDefault();
|
|
78
|
+
onNavigate(app);
|
|
79
|
+
}
|
|
80
|
+
if (plain) setOpen(false);
|
|
81
|
+
};
|
|
82
|
+
return /* @__PURE__ */ jsxs(Popover.Root, { open, onOpenChange: setOpen, children: [
|
|
83
|
+
/* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(IconButton, { ref, label, className, children: /* @__PURE__ */ jsx(Apps, { size: "md", "aria-hidden": "true" }) }) }),
|
|
84
|
+
/* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(
|
|
85
|
+
Popover.Content,
|
|
86
|
+
{
|
|
87
|
+
align: "end",
|
|
88
|
+
surface,
|
|
89
|
+
"aria-label": label,
|
|
90
|
+
onOpenAutoFocus,
|
|
91
|
+
className: "w-80 max-w-[calc(100vw-2rem)] p-2",
|
|
92
|
+
children: /* @__PURE__ */ jsx("div", { ref: listRef, className: "flex flex-col gap-2", onKeyDown, children: sections.map(({ group, items, firstRow }) => /* @__PURE__ */ jsxs("div", { children: [
|
|
93
|
+
group && /* @__PURE__ */ jsx("h3", { className: "px-2 py-1 text-label-sm text-[var(--color-on-surface-variant)]", children: group }),
|
|
94
|
+
/* @__PURE__ */ jsx("ul", { className: "grid grid-cols-3 gap-1", "aria-label": group, children: items.map((app, n) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
|
|
95
|
+
"a",
|
|
96
|
+
{
|
|
97
|
+
href: app.href,
|
|
98
|
+
"aria-current": app.current ? "page" : void 0,
|
|
99
|
+
"data-row": firstRow + Math.floor(n / COLUMNS),
|
|
100
|
+
"data-col": n % COLUMNS,
|
|
101
|
+
onClick: (e) => onClick(e, app),
|
|
102
|
+
className: cn(
|
|
103
|
+
"flex flex-col items-center gap-1.5 rounded-[8px] px-1 py-3 text-center text-xs text-[var(--color-on-surface)] transition-colors",
|
|
104
|
+
"hover:bg-[color-mix(in_srgb,var(--color-on-surface)_8%,transparent)]",
|
|
105
|
+
app.current && "bg-[var(--color-surface-container-low)] font-semibold"
|
|
106
|
+
),
|
|
107
|
+
children: [
|
|
108
|
+
/* @__PURE__ */ jsx(AppIcon, { app }),
|
|
109
|
+
/* @__PURE__ */ jsx("span", { className: "w-full truncate", children: app.name })
|
|
110
|
+
]
|
|
111
|
+
}
|
|
112
|
+
) }, app.id)) })
|
|
113
|
+
] }, group ?? "")) })
|
|
114
|
+
}
|
|
115
|
+
) })
|
|
116
|
+
] });
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
AppLauncher.displayName = "AppLauncher";
|
|
120
|
+
|
|
121
|
+
export {
|
|
122
|
+
AppLauncher
|
|
123
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -156,6 +156,8 @@ interface AppLauncherApp {
|
|
|
156
156
|
href: string;
|
|
157
157
|
/** Tile image URL (e.g. `@nexub/brand/app-icon-account.svg`). Falls back to initials. */
|
|
158
158
|
icon?: string;
|
|
159
|
+
/** Section heading, e.g. "People & HR". Apps with the same group sit together, in order of first appearance. */
|
|
160
|
+
group?: string;
|
|
159
161
|
/** The app the user is in now. */
|
|
160
162
|
current?: boolean;
|
|
161
163
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
'use client';
|
|
2
|
+
import {
|
|
3
|
+
StepIndicator
|
|
4
|
+
} from "./chunk-L456OB3Q.js";
|
|
5
|
+
import {
|
|
6
|
+
UserCard
|
|
7
|
+
} from "./chunk-7NYGYETG.js";
|
|
8
|
+
import {
|
|
9
|
+
FilterBar
|
|
10
|
+
} from "./chunk-Z6VMCMSW.js";
|
|
2
11
|
import {
|
|
3
12
|
InfoList
|
|
4
13
|
} from "./chunk-WPNT5JOU.js";
|
|
5
14
|
import {
|
|
6
15
|
ListDetail
|
|
7
16
|
} from "./chunk-5JU6HUVJ.js";
|
|
8
|
-
import {
|
|
9
|
-
FilterBar
|
|
10
|
-
} from "./chunk-Z6VMCMSW.js";
|
|
11
17
|
import {
|
|
12
18
|
NotificationItem
|
|
13
19
|
} from "./chunk-5IORYY2L.js";
|
|
@@ -24,36 +30,30 @@ import {
|
|
|
24
30
|
import {
|
|
25
31
|
StatCard
|
|
26
32
|
} from "./chunk-INKX2GYA.js";
|
|
27
|
-
import {
|
|
28
|
-
StepIndicator
|
|
29
|
-
} from "./chunk-L456OB3Q.js";
|
|
30
|
-
import {
|
|
31
|
-
UserCard
|
|
32
|
-
} from "./chunk-7NYGYETG.js";
|
|
33
33
|
import {
|
|
34
34
|
ActivityFeed
|
|
35
35
|
} from "./chunk-HYC5N5MO.js";
|
|
36
36
|
import {
|
|
37
37
|
AppHeader
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-2WKGBINE.js";
|
|
39
39
|
import "./chunk-VYMTTYPR.js";
|
|
40
40
|
import {
|
|
41
41
|
AppLauncher
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-BDCVWAH7.js";
|
|
43
43
|
import {
|
|
44
44
|
AuthCard
|
|
45
45
|
} from "./chunk-HMJRSNJD.js";
|
|
46
46
|
import {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
} from "./chunk-
|
|
47
|
+
BentoGrid,
|
|
48
|
+
BentoTile
|
|
49
|
+
} from "./chunk-WZTDCJYP.js";
|
|
50
50
|
import {
|
|
51
51
|
Conversation
|
|
52
52
|
} from "./chunk-WRP73TXG.js";
|
|
53
53
|
import {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
} from "./chunk-
|
|
54
|
+
ErrorSummary,
|
|
55
|
+
useFormErrors
|
|
56
|
+
} from "./chunk-RWPFCNMJ.js";
|
|
57
57
|
export {
|
|
58
58
|
ActivityFeed,
|
|
59
59
|
AppHeader,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nexub/patterns",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Composite UI patterns for Nexub Design System — pre-built, production-ready layouts and widgets built on @nexub/react.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"react": ">=18 <20",
|
|
38
38
|
"react-dom": ">=18 <20",
|
|
39
|
-
"@nexub/icons": "^
|
|
40
|
-
"@nexub/react": "^
|
|
41
|
-
"@nexub/tokens": "^
|
|
39
|
+
"@nexub/icons": "^2.0.0",
|
|
40
|
+
"@nexub/react": "^2.0.0",
|
|
41
|
+
"@nexub/tokens": "^2.0.0"
|
|
42
42
|
},
|
|
43
43
|
"peerDependenciesMeta": {
|
|
44
44
|
"@nexub/icons": {
|
|
@@ -59,9 +59,9 @@
|
|
|
59
59
|
"typescript": "^5.9.3",
|
|
60
60
|
"vite": "^7.3.7",
|
|
61
61
|
"vitest": "^5.0.5",
|
|
62
|
-
"@nexub/icons": "
|
|
63
|
-
"@nexub/react": "
|
|
64
|
-
"@nexub/tokens": "
|
|
62
|
+
"@nexub/icons": "2.0.0",
|
|
63
|
+
"@nexub/react": "2.0.0",
|
|
64
|
+
"@nexub/tokens": "2.0.0"
|
|
65
65
|
},
|
|
66
66
|
"scripts": {
|
|
67
67
|
"build": "tsup",
|
package/dist/chunk-H5UTW5ML.js
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
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-[12px] object-cover",
|
|
20
|
-
onError: () => setFailed(true)
|
|
21
|
-
}
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
return /* @__PURE__ */ jsx(Avatar, { name: app.name, size: "md", className: "rounded-[12px]", "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-[8px] px-1 py-3 text-center text-xs text-[var(--color-on-surface)] transition-colors",
|
|
79
|
-
"hover:bg-[color-mix(in_srgb,var(--color-on-surface)_8%,transparent)]",
|
|
80
|
-
app.current && "bg-[var(--color-surface-container-low)] 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
|
-
};
|