@the-portland-company/shell 0.3.4 → 0.4.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/CHANGELOG.md +7 -0
- package/dist/chrome.css +235 -73
- package/dist/index.cjs +26 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -2
- package/dist/index.d.ts +9 -2
- package/dist/index.js +26 -32
- package/dist/index.js.map +1 -1
- package/dist/native.cjs +210 -0
- package/dist/native.cjs.map +1 -0
- package/dist/native.d.cts +91 -0
- package/dist/native.d.ts +91 -0
- package/dist/native.js +204 -0
- package/dist/native.js.map +1 -0
- package/package.json +9 -3
package/dist/native.cjs
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
|
|
5
|
+
// src/native/index.tsx
|
|
6
|
+
var DefaultLink = ({ href, children, ...rest }) => /* @__PURE__ */ jsxRuntime.jsx("a", { href, ...rest, children });
|
|
7
|
+
function isNavItemActive(pathname, href) {
|
|
8
|
+
if (href === "/") return pathname === "/";
|
|
9
|
+
return pathname === href || pathname.startsWith(`${href}/`);
|
|
10
|
+
}
|
|
11
|
+
function envChipClasses(label) {
|
|
12
|
+
const l = label.toLowerCase();
|
|
13
|
+
if (l === "production")
|
|
14
|
+
return "bg-[rgba(230,57,70,0.12)] text-[color:var(--color-race-red)]";
|
|
15
|
+
if (l === "staging") return "bg-[rgba(242,201,76,0.18)] text-[#a6791f]";
|
|
16
|
+
return "bg-[rgba(0,166,81,0.14)] text-[color:var(--color-green-flag)]";
|
|
17
|
+
}
|
|
18
|
+
function NavSidebar({
|
|
19
|
+
pathname,
|
|
20
|
+
brand,
|
|
21
|
+
nav,
|
|
22
|
+
org,
|
|
23
|
+
mode,
|
|
24
|
+
footer,
|
|
25
|
+
linkComponent
|
|
26
|
+
}) {
|
|
27
|
+
const Link = linkComponent ?? DefaultLink;
|
|
28
|
+
const activeFn = nav.isActive ?? isNavItemActive;
|
|
29
|
+
const ModeIcon = mode?.icon;
|
|
30
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("aside", { className: "fixed inset-y-0 left-0 z-30 hidden w-[264px] flex-col border-r border-[color:var(--line)] bg-[color:var(--panel-elevated)] px-4 py-5 lg:flex", children: [
|
|
31
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Link, { href: brand.homeHref ?? "/", className: "flex items-center gap-3 px-1 py-1", children: [
|
|
32
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-9 w-9 items-center justify-center rounded-xl bg-[color:var(--color-race-red)] text-base font-black text-white shadow-sm", children: brand.badgeLabel }),
|
|
33
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex flex-col leading-tight", children: [
|
|
34
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-bold tracking-[-0.01em] text-[color:var(--foreground)]", children: brand.title }),
|
|
35
|
+
brand.subtitle ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-[color:var(--muted)]", children: brand.subtitle }) : null
|
|
36
|
+
] })
|
|
37
|
+
] }),
|
|
38
|
+
org ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
39
|
+
"button",
|
|
40
|
+
{
|
|
41
|
+
type: "button",
|
|
42
|
+
onClick: org.onClick,
|
|
43
|
+
className: "mt-5 flex w-full items-center justify-between rounded-xl border border-[color:var(--line)] bg-[color:var(--panel)] px-3 py-2.5 text-left transition hover:border-[color:var(--color-race-red)]/40",
|
|
44
|
+
children: [
|
|
45
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-2.5 overflow-hidden", children: [
|
|
46
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-lg bg-[color:var(--accent-muted)] text-xs font-bold text-[color:var(--color-race-red)]", children: (org.name ?? "Org").slice(0, 2).toUpperCase() }),
|
|
47
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-sm font-semibold text-[color:var(--foreground)]", children: org.name ?? "Account" })
|
|
48
|
+
] }),
|
|
49
|
+
/* @__PURE__ */ jsxRuntime.jsx(ChevronDownUp, {})
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
) : null,
|
|
53
|
+
mode ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2.5 flex items-center gap-2.5 rounded-xl bg-[color:var(--color-race-red)] px-3 py-2.5 text-white shadow-sm", children: [
|
|
54
|
+
ModeIcon ? /* @__PURE__ */ jsxRuntime.jsx(ModeIcon, { size: 16, className: "shrink-0" }) : null,
|
|
55
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold tracking-[-0.01em]", children: mode.label })
|
|
56
|
+
] }) : null,
|
|
57
|
+
/* @__PURE__ */ jsxRuntime.jsxs("nav", { className: "mt-5 flex flex-1 flex-col gap-1 overflow-y-auto", children: [
|
|
58
|
+
nav.groupLabel ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-3 pb-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-[color:var(--muted)]", children: nav.groupLabel }) : null,
|
|
59
|
+
nav.items.map((item) => {
|
|
60
|
+
const active = activeFn(pathname, item.href);
|
|
61
|
+
const Icon = item.icon;
|
|
62
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
63
|
+
Link,
|
|
64
|
+
{
|
|
65
|
+
href: item.href,
|
|
66
|
+
"aria-current": active ? "page" : void 0,
|
|
67
|
+
className: [
|
|
68
|
+
"group flex items-center gap-3 rounded-xl px-3 py-2.5 text-sm font-semibold transition",
|
|
69
|
+
active ? "bg-[color:var(--accent-muted)] text-[color:var(--color-race-red)]" : "text-[color:var(--muted)] hover:bg-[color:var(--panel)] hover:text-[color:var(--foreground)]"
|
|
70
|
+
].join(" "),
|
|
71
|
+
children: [
|
|
72
|
+
Icon ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
73
|
+
Icon,
|
|
74
|
+
{
|
|
75
|
+
size: 18,
|
|
76
|
+
className: active ? "text-[color:var(--color-race-red)]" : "text-[color:var(--muted)] group-hover:text-[color:var(--foreground)]"
|
|
77
|
+
}
|
|
78
|
+
) : null,
|
|
79
|
+
item.label
|
|
80
|
+
]
|
|
81
|
+
},
|
|
82
|
+
item.href
|
|
83
|
+
);
|
|
84
|
+
})
|
|
85
|
+
] }),
|
|
86
|
+
footer ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 border-t border-[color:var(--line)] pt-3", children: [
|
|
87
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
88
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
89
|
+
"span",
|
|
90
|
+
{
|
|
91
|
+
className: `rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-[0.12em] ${envChipClasses(
|
|
92
|
+
footer.envLabel
|
|
93
|
+
)}`,
|
|
94
|
+
children: footer.envLabel
|
|
95
|
+
}
|
|
96
|
+
),
|
|
97
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-[family-name:var(--font-plex-mono)] text-[10px] text-[color:var(--muted)]", children: [
|
|
98
|
+
"v",
|
|
99
|
+
footer.version,
|
|
100
|
+
" \xB7 ",
|
|
101
|
+
footer.commit.slice(0, 7)
|
|
102
|
+
] })
|
|
103
|
+
] }),
|
|
104
|
+
footer.links && footer.links.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 flex gap-3 text-[11px] font-medium text-[color:var(--muted)]", children: footer.links.map((l) => /* @__PURE__ */ jsxRuntime.jsx("a", { href: l.href, className: "hover:text-[color:var(--foreground)]", children: l.label }, l.label)) }) : null
|
|
105
|
+
] }) : null
|
|
106
|
+
] });
|
|
107
|
+
}
|
|
108
|
+
function ChevronDownUp() {
|
|
109
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
110
|
+
"svg",
|
|
111
|
+
{
|
|
112
|
+
width: 15,
|
|
113
|
+
height: 15,
|
|
114
|
+
viewBox: "0 0 24 24",
|
|
115
|
+
fill: "none",
|
|
116
|
+
stroke: "currentColor",
|
|
117
|
+
strokeWidth: 2,
|
|
118
|
+
strokeLinecap: "round",
|
|
119
|
+
strokeLinejoin: "round",
|
|
120
|
+
className: "shrink-0 text-[color:var(--muted)]",
|
|
121
|
+
"aria-hidden": "true",
|
|
122
|
+
children: [
|
|
123
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "m7 15 5 5 5-5" }),
|
|
124
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "m7 9 5-5 5 5" })
|
|
125
|
+
]
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
function NavTopBar({
|
|
130
|
+
title,
|
|
131
|
+
subtitle,
|
|
132
|
+
user,
|
|
133
|
+
onSignOut,
|
|
134
|
+
actions,
|
|
135
|
+
themeToggle
|
|
136
|
+
}) {
|
|
137
|
+
const initials = (user?.name ?? "Politogy").slice(0, 2).toUpperCase();
|
|
138
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "sticky top-0 z-20 flex items-center justify-between gap-4 border-b border-[color:var(--line)] bg-[color:var(--panel)] px-6 py-3 backdrop-blur-xl", children: [
|
|
139
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
|
|
140
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
141
|
+
"h1",
|
|
142
|
+
{
|
|
143
|
+
className: "truncate text-xl font-semibold tracking-[-0.02em] text-[color:var(--foreground)]",
|
|
144
|
+
"data-display": "true",
|
|
145
|
+
children: title
|
|
146
|
+
}
|
|
147
|
+
),
|
|
148
|
+
subtitle ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate text-sm text-[color:var(--muted)]", children: subtitle }) : null
|
|
149
|
+
] }),
|
|
150
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
151
|
+
actions,
|
|
152
|
+
themeToggle,
|
|
153
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5 rounded-full border border-[color:var(--line)] bg-[color:var(--panel-elevated)] py-1 pl-1 pr-2.5", children: [
|
|
154
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-8 w-8 items-center justify-center rounded-full bg-[color:var(--color-race-red)] text-xs font-bold text-white", children: initials }),
|
|
155
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "hidden flex-col leading-tight md:flex", children: [
|
|
156
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold text-[color:var(--foreground)]", children: user?.name ?? "Signed in" }),
|
|
157
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-medium uppercase tracking-[0.08em] text-[color:var(--muted)]", children: [user?.tier, user?.role].filter(Boolean).join(" \xB7 ") || "member" })
|
|
158
|
+
] }),
|
|
159
|
+
onSignOut ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
160
|
+
"button",
|
|
161
|
+
{
|
|
162
|
+
type: "button",
|
|
163
|
+
onClick: onSignOut,
|
|
164
|
+
"aria-label": "Sign out",
|
|
165
|
+
className: "ml-1 flex h-7 w-7 items-center justify-center rounded-full text-[color:var(--muted)] hover:text-[color:var(--color-race-red)]",
|
|
166
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(SignOutIcon, {})
|
|
167
|
+
}
|
|
168
|
+
) : null
|
|
169
|
+
] })
|
|
170
|
+
] })
|
|
171
|
+
] });
|
|
172
|
+
}
|
|
173
|
+
function SignOutIcon() {
|
|
174
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
175
|
+
"svg",
|
|
176
|
+
{
|
|
177
|
+
width: 15,
|
|
178
|
+
height: 15,
|
|
179
|
+
viewBox: "0 0 24 24",
|
|
180
|
+
fill: "none",
|
|
181
|
+
stroke: "currentColor",
|
|
182
|
+
strokeWidth: 2,
|
|
183
|
+
strokeLinecap: "round",
|
|
184
|
+
strokeLinejoin: "round",
|
|
185
|
+
"aria-hidden": "true",
|
|
186
|
+
children: [
|
|
187
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" }),
|
|
188
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "16 17 21 12 16 7" }),
|
|
189
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "21", x2: "9", y1: "12", y2: "12" })
|
|
190
|
+
]
|
|
191
|
+
}
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
function NavAppShell({ sidebar, topBar, children }) {
|
|
195
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-h-screen bg-[color:var(--background)] text-[color:var(--foreground)]", children: [
|
|
196
|
+
/* @__PURE__ */ jsxRuntime.jsx(NavSidebar, { ...sidebar }),
|
|
197
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lg:pl-[264px]", children: [
|
|
198
|
+
/* @__PURE__ */ jsxRuntime.jsx(NavTopBar, { ...topBar }),
|
|
199
|
+
/* @__PURE__ */ jsxRuntime.jsx("main", { className: "px-6 py-7", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto max-w-7xl", children }) })
|
|
200
|
+
] })
|
|
201
|
+
] });
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
exports.NavAppShell = NavAppShell;
|
|
205
|
+
exports.NavSidebar = NavSidebar;
|
|
206
|
+
exports.NavTopBar = NavTopBar;
|
|
207
|
+
exports.envChipClasses = envChipClasses;
|
|
208
|
+
exports.isNavItemActive = isNavItemActive;
|
|
209
|
+
//# sourceMappingURL=native.cjs.map
|
|
210
|
+
//# sourceMappingURL=native.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/native/index.tsx"],"names":["jsx","jsxs"],"mappings":"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","file":"native.cjs","sourcesContent":["// ---------------------------------------------------------------------------\n// Framework-agnostic chrome (sidebar + top navbar) for @the-portland-company/shell.\n//\n// The other chrome in this package (ShellChrome, Sidebar, Header, …) is built\n// on Chakra UI v2 + react-router and targets Vite SPAs. That stack cannot be\n// imported into a Next.js / React 19 / Tailwind app. This \"native\" module is a\n// dependency-light alternative: pure React (no Chakra, no emotion, no\n// react-router, no icon library) whose styling is driven entirely by the CSS\n// design tokens shipped in `@the-portland-company/shell/chrome.css`.\n//\n// Every app concern (nav items, org, mode, user, links, icons, the routing\n// Link component, the theme toggle) is injected via props, so both the Contacts\n// app (Next.js) and the VRM app could consume the same components.\n// ---------------------------------------------------------------------------\n\nimport type { ComponentType, ReactNode } from 'react'\n\n// An icon is any component that accepts an optional size + className. This\n// matches both `lucide-react` and `react-icons` icon signatures, so consuming\n// apps pass their own icon set without this package depending on one.\nexport type NavIcon = ComponentType<{ size?: number; className?: string }>\n\nexport type NavItem = {\n href: string\n label: string\n icon?: NavIcon\n}\n\n// The routing primitive is injected so each app supplies its own (Next.js\n// `<Link>`, react-router `<Link>`, or a plain `<a>`). Defaults to an anchor.\nexport type NavLinkProps = {\n href: string\n className?: string\n 'aria-current'?: 'page' | undefined\n children: ReactNode\n}\nexport type NavLinkComponent = ComponentType<NavLinkProps>\n\nconst DefaultLink: NavLinkComponent = ({ href, children, ...rest }) => (\n <a href={href} {...rest}>\n {children}\n </a>\n)\n\n/** Default active-route test: exact match for \"/\", prefix match otherwise. */\nexport function isNavItemActive(pathname: string, href: string): boolean {\n if (href === '/') return pathname === '/'\n return pathname === href || pathname.startsWith(`${href}/`)\n}\n\n/** Token-driven tone classes for the sidebar environment chip. */\nexport function envChipClasses(label: string): string {\n const l = label.toLowerCase()\n if (l === 'production')\n return 'bg-[rgba(230,57,70,0.12)] text-[color:var(--color-race-red)]'\n if (l === 'staging') return 'bg-[rgba(242,201,76,0.18)] text-[#a6791f]'\n return 'bg-[rgba(0,166,81,0.14)] text-[color:var(--color-green-flag)]'\n}\n\n// ---------------------------------------------------------------------------\n\nexport type NavBrand = {\n /** Where the logo links to. Defaults to \"/\". */\n homeHref?: string\n /** Short label shown inside the brand badge (e.g. \"P\"). */\n badgeLabel: string\n title: string\n subtitle?: string\n}\n\nexport type NavOrg = {\n name: string | null\n onClick?: () => void\n}\n\nexport type NavMode = {\n label: string\n icon?: NavIcon\n}\n\nexport type NavFooterLink = { label: string; href: string }\n\nexport type NavFooter = {\n envLabel: string\n version: string\n commit: string\n links?: NavFooterLink[]\n}\n\nexport type NavSidebarProps = {\n pathname: string\n brand: NavBrand\n nav: {\n groupLabel?: string\n items: NavItem[]\n isActive?: (pathname: string, href: string) => boolean\n }\n org?: NavOrg | null\n mode?: NavMode | null\n footer?: NavFooter | null\n linkComponent?: NavLinkComponent\n}\n\nexport function NavSidebar({\n pathname,\n brand,\n nav,\n org,\n mode,\n footer,\n linkComponent,\n}: NavSidebarProps) {\n const Link = linkComponent ?? DefaultLink\n const activeFn = nav.isActive ?? isNavItemActive\n const ModeIcon = mode?.icon\n\n return (\n <aside className=\"fixed inset-y-0 left-0 z-30 hidden w-[264px] flex-col border-r border-[color:var(--line)] bg-[color:var(--panel-elevated)] px-4 py-5 lg:flex\">\n {/* Brand */}\n <Link href={brand.homeHref ?? '/'} className=\"flex items-center gap-3 px-1 py-1\">\n <span className=\"flex h-9 w-9 items-center justify-center rounded-xl bg-[color:var(--color-race-red)] text-base font-black text-white shadow-sm\">\n {brand.badgeLabel}\n </span>\n <span className=\"flex flex-col leading-tight\">\n <span className=\"text-sm font-bold tracking-[-0.01em] text-[color:var(--foreground)]\">\n {brand.title}\n </span>\n {brand.subtitle ? (\n <span className=\"text-[11px] font-semibold uppercase tracking-[0.16em] text-[color:var(--muted)]\">\n {brand.subtitle}\n </span>\n ) : null}\n </span>\n </Link>\n\n {/* Organization switcher pill */}\n {org ? (\n <button\n type=\"button\"\n onClick={org.onClick}\n className=\"mt-5 flex w-full items-center justify-between rounded-xl border border-[color:var(--line)] bg-[color:var(--panel)] px-3 py-2.5 text-left transition hover:border-[color:var(--color-race-red)]/40\"\n >\n <span className=\"flex items-center gap-2.5 overflow-hidden\">\n <span className=\"flex h-7 w-7 shrink-0 items-center justify-center rounded-lg bg-[color:var(--accent-muted)] text-xs font-bold text-[color:var(--color-race-red)]\">\n {(org.name ?? 'Org').slice(0, 2).toUpperCase()}\n </span>\n <span className=\"truncate text-sm font-semibold text-[color:var(--foreground)]\">\n {org.name ?? 'Account'}\n </span>\n </span>\n <ChevronDownUp />\n </button>\n ) : null}\n\n {/* Mode pill */}\n {mode ? (\n <div className=\"mt-2.5 flex items-center gap-2.5 rounded-xl bg-[color:var(--color-race-red)] px-3 py-2.5 text-white shadow-sm\">\n {ModeIcon ? <ModeIcon size={16} className=\"shrink-0\" /> : null}\n <span className=\"text-sm font-semibold tracking-[-0.01em]\">{mode.label}</span>\n </div>\n ) : null}\n\n {/* Nav list */}\n <nav className=\"mt-5 flex flex-1 flex-col gap-1 overflow-y-auto\">\n {nav.groupLabel ? (\n <p className=\"px-3 pb-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-[color:var(--muted)]\">\n {nav.groupLabel}\n </p>\n ) : null}\n {nav.items.map((item) => {\n const active = activeFn(pathname, item.href)\n const Icon = item.icon\n return (\n <Link\n key={item.href}\n href={item.href}\n aria-current={active ? 'page' : undefined}\n className={[\n 'group flex items-center gap-3 rounded-xl px-3 py-2.5 text-sm font-semibold transition',\n active\n ? 'bg-[color:var(--accent-muted)] text-[color:var(--color-race-red)]'\n : 'text-[color:var(--muted)] hover:bg-[color:var(--panel)] hover:text-[color:var(--foreground)]',\n ].join(' ')}\n >\n {Icon ? (\n <Icon\n size={18}\n className={\n active\n ? 'text-[color:var(--color-race-red)]'\n : 'text-[color:var(--muted)] group-hover:text-[color:var(--foreground)]'\n }\n />\n ) : null}\n {item.label}\n </Link>\n )\n })}\n </nav>\n\n {/* Footer — env chip + commit/version */}\n {footer ? (\n <div className=\"mt-4 border-t border-[color:var(--line)] pt-3\">\n <div className=\"flex items-center justify-between\">\n <span\n className={`rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-[0.12em] ${envChipClasses(\n footer.envLabel,\n )}`}\n >\n {footer.envLabel}\n </span>\n <span className=\"font-[family-name:var(--font-plex-mono)] text-[10px] text-[color:var(--muted)]\">\n v{footer.version} · {footer.commit.slice(0, 7)}\n </span>\n </div>\n {footer.links && footer.links.length > 0 ? (\n <div className=\"mt-2 flex gap-3 text-[11px] font-medium text-[color:var(--muted)]\">\n {footer.links.map((l) => (\n <a key={l.label} href={l.href} className=\"hover:text-[color:var(--foreground)]\">\n {l.label}\n </a>\n ))}\n </div>\n ) : null}\n </div>\n ) : null}\n </aside>\n )\n}\n\n// Small inline chevron so this module needs no icon dependency. Mirrors the\n// lucide `ChevronsUpDown` glyph used by the org pill.\nfunction ChevronDownUp() {\n return (\n <svg\n width={15}\n height={15}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"shrink-0 text-[color:var(--muted)]\"\n aria-hidden=\"true\"\n >\n <path d=\"m7 15 5 5 5-5\" />\n <path d=\"m7 9 5-5 5 5\" />\n </svg>\n )\n}\n\n// ---------------------------------------------------------------------------\n\nexport type NavUser = {\n name: string | null\n tier?: string | null\n role?: string | null\n}\n\nexport type NavTopBarProps = {\n title: string\n subtitle?: string\n user?: NavUser | null\n onSignOut?: () => void\n /** App-specific action cluster rendered left of the theme toggle. */\n actions?: ReactNode\n /** Theme toggle slot (injected so this package needs no theme dependency). */\n themeToggle?: ReactNode\n}\n\nexport function NavTopBar({\n title,\n subtitle,\n user,\n onSignOut,\n actions,\n themeToggle,\n}: NavTopBarProps) {\n const initials = (user?.name ?? 'Politogy').slice(0, 2).toUpperCase()\n return (\n <header className=\"sticky top-0 z-20 flex items-center justify-between gap-4 border-b border-[color:var(--line)] bg-[color:var(--panel)] px-6 py-3 backdrop-blur-xl\">\n <div className=\"min-w-0\">\n <h1\n className=\"truncate text-xl font-semibold tracking-[-0.02em] text-[color:var(--foreground)]\"\n data-display=\"true\"\n >\n {title}\n </h1>\n {subtitle ? (\n <p className=\"truncate text-sm text-[color:var(--muted)]\">{subtitle}</p>\n ) : null}\n </div>\n\n <div className=\"flex shrink-0 items-center gap-2\">\n {actions}\n {themeToggle}\n\n {/* User menu */}\n <div className=\"flex items-center gap-2.5 rounded-full border border-[color:var(--line)] bg-[color:var(--panel-elevated)] py-1 pl-1 pr-2.5\">\n <span className=\"flex h-8 w-8 items-center justify-center rounded-full bg-[color:var(--color-race-red)] text-xs font-bold text-white\">\n {initials}\n </span>\n <span className=\"hidden flex-col leading-tight md:flex\">\n <span className=\"text-xs font-semibold text-[color:var(--foreground)]\">\n {user?.name ?? 'Signed in'}\n </span>\n <span className=\"text-[10px] font-medium uppercase tracking-[0.08em] text-[color:var(--muted)]\">\n {[user?.tier, user?.role].filter(Boolean).join(' · ') || 'member'}\n </span>\n </span>\n {onSignOut ? (\n <button\n type=\"button\"\n onClick={onSignOut}\n aria-label=\"Sign out\"\n className=\"ml-1 flex h-7 w-7 items-center justify-center rounded-full text-[color:var(--muted)] hover:text-[color:var(--color-race-red)]\"\n >\n <SignOutIcon />\n </button>\n ) : null}\n </div>\n </div>\n </header>\n )\n}\n\n// Inline log-out glyph (mirrors lucide `LogOut`) to avoid an icon dependency.\nfunction SignOutIcon() {\n return (\n <svg\n width={15}\n height={15}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4\" />\n <polyline points=\"16 17 21 12 16 7\" />\n <line x1=\"21\" x2=\"9\" y1=\"12\" y2=\"12\" />\n </svg>\n )\n}\n\n// ---------------------------------------------------------------------------\n\nexport type NavAppShellProps = {\n sidebar: NavSidebarProps\n topBar: NavTopBarProps\n children: ReactNode\n}\n\n/**\n * Full chrome: branded left sidebar + sticky top navbar + scrollable content\n * region. Presentational only — wrap it in app-level auth/gating as needed.\n */\nexport function NavAppShell({ sidebar, topBar, children }: NavAppShellProps) {\n return (\n <div className=\"min-h-screen bg-[color:var(--background)] text-[color:var(--foreground)]\">\n <NavSidebar {...sidebar} />\n <div className=\"lg:pl-[264px]\">\n <NavTopBar {...topBar} />\n <main className=\"px-6 py-7\">\n <div className=\"mx-auto max-w-7xl\">{children}</div>\n </main>\n </div>\n </div>\n )\n}\n"]}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ComponentType, ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
type NavIcon = ComponentType<{
|
|
5
|
+
size?: number;
|
|
6
|
+
className?: string;
|
|
7
|
+
}>;
|
|
8
|
+
type NavItem = {
|
|
9
|
+
href: string;
|
|
10
|
+
label: string;
|
|
11
|
+
icon?: NavIcon;
|
|
12
|
+
};
|
|
13
|
+
type NavLinkProps = {
|
|
14
|
+
href: string;
|
|
15
|
+
className?: string;
|
|
16
|
+
'aria-current'?: 'page' | undefined;
|
|
17
|
+
children: ReactNode;
|
|
18
|
+
};
|
|
19
|
+
type NavLinkComponent = ComponentType<NavLinkProps>;
|
|
20
|
+
/** Default active-route test: exact match for "/", prefix match otherwise. */
|
|
21
|
+
declare function isNavItemActive(pathname: string, href: string): boolean;
|
|
22
|
+
/** Token-driven tone classes for the sidebar environment chip. */
|
|
23
|
+
declare function envChipClasses(label: string): string;
|
|
24
|
+
type NavBrand = {
|
|
25
|
+
/** Where the logo links to. Defaults to "/". */
|
|
26
|
+
homeHref?: string;
|
|
27
|
+
/** Short label shown inside the brand badge (e.g. "P"). */
|
|
28
|
+
badgeLabel: string;
|
|
29
|
+
title: string;
|
|
30
|
+
subtitle?: string;
|
|
31
|
+
};
|
|
32
|
+
type NavOrg = {
|
|
33
|
+
name: string | null;
|
|
34
|
+
onClick?: () => void;
|
|
35
|
+
};
|
|
36
|
+
type NavMode = {
|
|
37
|
+
label: string;
|
|
38
|
+
icon?: NavIcon;
|
|
39
|
+
};
|
|
40
|
+
type NavFooterLink = {
|
|
41
|
+
label: string;
|
|
42
|
+
href: string;
|
|
43
|
+
};
|
|
44
|
+
type NavFooter = {
|
|
45
|
+
envLabel: string;
|
|
46
|
+
version: string;
|
|
47
|
+
commit: string;
|
|
48
|
+
links?: NavFooterLink[];
|
|
49
|
+
};
|
|
50
|
+
type NavSidebarProps = {
|
|
51
|
+
pathname: string;
|
|
52
|
+
brand: NavBrand;
|
|
53
|
+
nav: {
|
|
54
|
+
groupLabel?: string;
|
|
55
|
+
items: NavItem[];
|
|
56
|
+
isActive?: (pathname: string, href: string) => boolean;
|
|
57
|
+
};
|
|
58
|
+
org?: NavOrg | null;
|
|
59
|
+
mode?: NavMode | null;
|
|
60
|
+
footer?: NavFooter | null;
|
|
61
|
+
linkComponent?: NavLinkComponent;
|
|
62
|
+
};
|
|
63
|
+
declare function NavSidebar({ pathname, brand, nav, org, mode, footer, linkComponent, }: NavSidebarProps): react_jsx_runtime.JSX.Element;
|
|
64
|
+
type NavUser = {
|
|
65
|
+
name: string | null;
|
|
66
|
+
tier?: string | null;
|
|
67
|
+
role?: string | null;
|
|
68
|
+
};
|
|
69
|
+
type NavTopBarProps = {
|
|
70
|
+
title: string;
|
|
71
|
+
subtitle?: string;
|
|
72
|
+
user?: NavUser | null;
|
|
73
|
+
onSignOut?: () => void;
|
|
74
|
+
/** App-specific action cluster rendered left of the theme toggle. */
|
|
75
|
+
actions?: ReactNode;
|
|
76
|
+
/** Theme toggle slot (injected so this package needs no theme dependency). */
|
|
77
|
+
themeToggle?: ReactNode;
|
|
78
|
+
};
|
|
79
|
+
declare function NavTopBar({ title, subtitle, user, onSignOut, actions, themeToggle, }: NavTopBarProps): react_jsx_runtime.JSX.Element;
|
|
80
|
+
type NavAppShellProps = {
|
|
81
|
+
sidebar: NavSidebarProps;
|
|
82
|
+
topBar: NavTopBarProps;
|
|
83
|
+
children: ReactNode;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Full chrome: branded left sidebar + sticky top navbar + scrollable content
|
|
87
|
+
* region. Presentational only — wrap it in app-level auth/gating as needed.
|
|
88
|
+
*/
|
|
89
|
+
declare function NavAppShell({ sidebar, topBar, children }: NavAppShellProps): react_jsx_runtime.JSX.Element;
|
|
90
|
+
|
|
91
|
+
export { NavAppShell, type NavAppShellProps, type NavBrand, type NavFooter, type NavFooterLink, type NavIcon, type NavItem, type NavLinkComponent, type NavLinkProps, type NavMode, type NavOrg, NavSidebar, type NavSidebarProps, NavTopBar, type NavTopBarProps, type NavUser, envChipClasses, isNavItemActive };
|
package/dist/native.d.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ComponentType, ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
type NavIcon = ComponentType<{
|
|
5
|
+
size?: number;
|
|
6
|
+
className?: string;
|
|
7
|
+
}>;
|
|
8
|
+
type NavItem = {
|
|
9
|
+
href: string;
|
|
10
|
+
label: string;
|
|
11
|
+
icon?: NavIcon;
|
|
12
|
+
};
|
|
13
|
+
type NavLinkProps = {
|
|
14
|
+
href: string;
|
|
15
|
+
className?: string;
|
|
16
|
+
'aria-current'?: 'page' | undefined;
|
|
17
|
+
children: ReactNode;
|
|
18
|
+
};
|
|
19
|
+
type NavLinkComponent = ComponentType<NavLinkProps>;
|
|
20
|
+
/** Default active-route test: exact match for "/", prefix match otherwise. */
|
|
21
|
+
declare function isNavItemActive(pathname: string, href: string): boolean;
|
|
22
|
+
/** Token-driven tone classes for the sidebar environment chip. */
|
|
23
|
+
declare function envChipClasses(label: string): string;
|
|
24
|
+
type NavBrand = {
|
|
25
|
+
/** Where the logo links to. Defaults to "/". */
|
|
26
|
+
homeHref?: string;
|
|
27
|
+
/** Short label shown inside the brand badge (e.g. "P"). */
|
|
28
|
+
badgeLabel: string;
|
|
29
|
+
title: string;
|
|
30
|
+
subtitle?: string;
|
|
31
|
+
};
|
|
32
|
+
type NavOrg = {
|
|
33
|
+
name: string | null;
|
|
34
|
+
onClick?: () => void;
|
|
35
|
+
};
|
|
36
|
+
type NavMode = {
|
|
37
|
+
label: string;
|
|
38
|
+
icon?: NavIcon;
|
|
39
|
+
};
|
|
40
|
+
type NavFooterLink = {
|
|
41
|
+
label: string;
|
|
42
|
+
href: string;
|
|
43
|
+
};
|
|
44
|
+
type NavFooter = {
|
|
45
|
+
envLabel: string;
|
|
46
|
+
version: string;
|
|
47
|
+
commit: string;
|
|
48
|
+
links?: NavFooterLink[];
|
|
49
|
+
};
|
|
50
|
+
type NavSidebarProps = {
|
|
51
|
+
pathname: string;
|
|
52
|
+
brand: NavBrand;
|
|
53
|
+
nav: {
|
|
54
|
+
groupLabel?: string;
|
|
55
|
+
items: NavItem[];
|
|
56
|
+
isActive?: (pathname: string, href: string) => boolean;
|
|
57
|
+
};
|
|
58
|
+
org?: NavOrg | null;
|
|
59
|
+
mode?: NavMode | null;
|
|
60
|
+
footer?: NavFooter | null;
|
|
61
|
+
linkComponent?: NavLinkComponent;
|
|
62
|
+
};
|
|
63
|
+
declare function NavSidebar({ pathname, brand, nav, org, mode, footer, linkComponent, }: NavSidebarProps): react_jsx_runtime.JSX.Element;
|
|
64
|
+
type NavUser = {
|
|
65
|
+
name: string | null;
|
|
66
|
+
tier?: string | null;
|
|
67
|
+
role?: string | null;
|
|
68
|
+
};
|
|
69
|
+
type NavTopBarProps = {
|
|
70
|
+
title: string;
|
|
71
|
+
subtitle?: string;
|
|
72
|
+
user?: NavUser | null;
|
|
73
|
+
onSignOut?: () => void;
|
|
74
|
+
/** App-specific action cluster rendered left of the theme toggle. */
|
|
75
|
+
actions?: ReactNode;
|
|
76
|
+
/** Theme toggle slot (injected so this package needs no theme dependency). */
|
|
77
|
+
themeToggle?: ReactNode;
|
|
78
|
+
};
|
|
79
|
+
declare function NavTopBar({ title, subtitle, user, onSignOut, actions, themeToggle, }: NavTopBarProps): react_jsx_runtime.JSX.Element;
|
|
80
|
+
type NavAppShellProps = {
|
|
81
|
+
sidebar: NavSidebarProps;
|
|
82
|
+
topBar: NavTopBarProps;
|
|
83
|
+
children: ReactNode;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Full chrome: branded left sidebar + sticky top navbar + scrollable content
|
|
87
|
+
* region. Presentational only — wrap it in app-level auth/gating as needed.
|
|
88
|
+
*/
|
|
89
|
+
declare function NavAppShell({ sidebar, topBar, children }: NavAppShellProps): react_jsx_runtime.JSX.Element;
|
|
90
|
+
|
|
91
|
+
export { NavAppShell, type NavAppShellProps, type NavBrand, type NavFooter, type NavFooterLink, type NavIcon, type NavItem, type NavLinkComponent, type NavLinkProps, type NavMode, type NavOrg, NavSidebar, type NavSidebarProps, NavTopBar, type NavTopBarProps, type NavUser, envChipClasses, isNavItemActive };
|
package/dist/native.js
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
// src/native/index.tsx
|
|
4
|
+
var DefaultLink = ({ href, children, ...rest }) => /* @__PURE__ */ jsx("a", { href, ...rest, children });
|
|
5
|
+
function isNavItemActive(pathname, href) {
|
|
6
|
+
if (href === "/") return pathname === "/";
|
|
7
|
+
return pathname === href || pathname.startsWith(`${href}/`);
|
|
8
|
+
}
|
|
9
|
+
function envChipClasses(label) {
|
|
10
|
+
const l = label.toLowerCase();
|
|
11
|
+
if (l === "production")
|
|
12
|
+
return "bg-[rgba(230,57,70,0.12)] text-[color:var(--color-race-red)]";
|
|
13
|
+
if (l === "staging") return "bg-[rgba(242,201,76,0.18)] text-[#a6791f]";
|
|
14
|
+
return "bg-[rgba(0,166,81,0.14)] text-[color:var(--color-green-flag)]";
|
|
15
|
+
}
|
|
16
|
+
function NavSidebar({
|
|
17
|
+
pathname,
|
|
18
|
+
brand,
|
|
19
|
+
nav,
|
|
20
|
+
org,
|
|
21
|
+
mode,
|
|
22
|
+
footer,
|
|
23
|
+
linkComponent
|
|
24
|
+
}) {
|
|
25
|
+
const Link = linkComponent ?? DefaultLink;
|
|
26
|
+
const activeFn = nav.isActive ?? isNavItemActive;
|
|
27
|
+
const ModeIcon = mode?.icon;
|
|
28
|
+
return /* @__PURE__ */ jsxs("aside", { className: "fixed inset-y-0 left-0 z-30 hidden w-[264px] flex-col border-r border-[color:var(--line)] bg-[color:var(--panel-elevated)] px-4 py-5 lg:flex", children: [
|
|
29
|
+
/* @__PURE__ */ jsxs(Link, { href: brand.homeHref ?? "/", className: "flex items-center gap-3 px-1 py-1", children: [
|
|
30
|
+
/* @__PURE__ */ jsx("span", { className: "flex h-9 w-9 items-center justify-center rounded-xl bg-[color:var(--color-race-red)] text-base font-black text-white shadow-sm", children: brand.badgeLabel }),
|
|
31
|
+
/* @__PURE__ */ jsxs("span", { className: "flex flex-col leading-tight", children: [
|
|
32
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-bold tracking-[-0.01em] text-[color:var(--foreground)]", children: brand.title }),
|
|
33
|
+
brand.subtitle ? /* @__PURE__ */ jsx("span", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-[color:var(--muted)]", children: brand.subtitle }) : null
|
|
34
|
+
] })
|
|
35
|
+
] }),
|
|
36
|
+
org ? /* @__PURE__ */ jsxs(
|
|
37
|
+
"button",
|
|
38
|
+
{
|
|
39
|
+
type: "button",
|
|
40
|
+
onClick: org.onClick,
|
|
41
|
+
className: "mt-5 flex w-full items-center justify-between rounded-xl border border-[color:var(--line)] bg-[color:var(--panel)] px-3 py-2.5 text-left transition hover:border-[color:var(--color-race-red)]/40",
|
|
42
|
+
children: [
|
|
43
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2.5 overflow-hidden", children: [
|
|
44
|
+
/* @__PURE__ */ jsx("span", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-lg bg-[color:var(--accent-muted)] text-xs font-bold text-[color:var(--color-race-red)]", children: (org.name ?? "Org").slice(0, 2).toUpperCase() }),
|
|
45
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-sm font-semibold text-[color:var(--foreground)]", children: org.name ?? "Account" })
|
|
46
|
+
] }),
|
|
47
|
+
/* @__PURE__ */ jsx(ChevronDownUp, {})
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
) : null,
|
|
51
|
+
mode ? /* @__PURE__ */ jsxs("div", { className: "mt-2.5 flex items-center gap-2.5 rounded-xl bg-[color:var(--color-race-red)] px-3 py-2.5 text-white shadow-sm", children: [
|
|
52
|
+
ModeIcon ? /* @__PURE__ */ jsx(ModeIcon, { size: 16, className: "shrink-0" }) : null,
|
|
53
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold tracking-[-0.01em]", children: mode.label })
|
|
54
|
+
] }) : null,
|
|
55
|
+
/* @__PURE__ */ jsxs("nav", { className: "mt-5 flex flex-1 flex-col gap-1 overflow-y-auto", children: [
|
|
56
|
+
nav.groupLabel ? /* @__PURE__ */ jsx("p", { className: "px-3 pb-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-[color:var(--muted)]", children: nav.groupLabel }) : null,
|
|
57
|
+
nav.items.map((item) => {
|
|
58
|
+
const active = activeFn(pathname, item.href);
|
|
59
|
+
const Icon = item.icon;
|
|
60
|
+
return /* @__PURE__ */ jsxs(
|
|
61
|
+
Link,
|
|
62
|
+
{
|
|
63
|
+
href: item.href,
|
|
64
|
+
"aria-current": active ? "page" : void 0,
|
|
65
|
+
className: [
|
|
66
|
+
"group flex items-center gap-3 rounded-xl px-3 py-2.5 text-sm font-semibold transition",
|
|
67
|
+
active ? "bg-[color:var(--accent-muted)] text-[color:var(--color-race-red)]" : "text-[color:var(--muted)] hover:bg-[color:var(--panel)] hover:text-[color:var(--foreground)]"
|
|
68
|
+
].join(" "),
|
|
69
|
+
children: [
|
|
70
|
+
Icon ? /* @__PURE__ */ jsx(
|
|
71
|
+
Icon,
|
|
72
|
+
{
|
|
73
|
+
size: 18,
|
|
74
|
+
className: active ? "text-[color:var(--color-race-red)]" : "text-[color:var(--muted)] group-hover:text-[color:var(--foreground)]"
|
|
75
|
+
}
|
|
76
|
+
) : null,
|
|
77
|
+
item.label
|
|
78
|
+
]
|
|
79
|
+
},
|
|
80
|
+
item.href
|
|
81
|
+
);
|
|
82
|
+
})
|
|
83
|
+
] }),
|
|
84
|
+
footer ? /* @__PURE__ */ jsxs("div", { className: "mt-4 border-t border-[color:var(--line)] pt-3", children: [
|
|
85
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
86
|
+
/* @__PURE__ */ jsx(
|
|
87
|
+
"span",
|
|
88
|
+
{
|
|
89
|
+
className: `rounded-full px-2 py-0.5 text-[10px] font-bold uppercase tracking-[0.12em] ${envChipClasses(
|
|
90
|
+
footer.envLabel
|
|
91
|
+
)}`,
|
|
92
|
+
children: footer.envLabel
|
|
93
|
+
}
|
|
94
|
+
),
|
|
95
|
+
/* @__PURE__ */ jsxs("span", { className: "font-[family-name:var(--font-plex-mono)] text-[10px] text-[color:var(--muted)]", children: [
|
|
96
|
+
"v",
|
|
97
|
+
footer.version,
|
|
98
|
+
" \xB7 ",
|
|
99
|
+
footer.commit.slice(0, 7)
|
|
100
|
+
] })
|
|
101
|
+
] }),
|
|
102
|
+
footer.links && footer.links.length > 0 ? /* @__PURE__ */ jsx("div", { className: "mt-2 flex gap-3 text-[11px] font-medium text-[color:var(--muted)]", children: footer.links.map((l) => /* @__PURE__ */ jsx("a", { href: l.href, className: "hover:text-[color:var(--foreground)]", children: l.label }, l.label)) }) : null
|
|
103
|
+
] }) : null
|
|
104
|
+
] });
|
|
105
|
+
}
|
|
106
|
+
function ChevronDownUp() {
|
|
107
|
+
return /* @__PURE__ */ jsxs(
|
|
108
|
+
"svg",
|
|
109
|
+
{
|
|
110
|
+
width: 15,
|
|
111
|
+
height: 15,
|
|
112
|
+
viewBox: "0 0 24 24",
|
|
113
|
+
fill: "none",
|
|
114
|
+
stroke: "currentColor",
|
|
115
|
+
strokeWidth: 2,
|
|
116
|
+
strokeLinecap: "round",
|
|
117
|
+
strokeLinejoin: "round",
|
|
118
|
+
className: "shrink-0 text-[color:var(--muted)]",
|
|
119
|
+
"aria-hidden": "true",
|
|
120
|
+
children: [
|
|
121
|
+
/* @__PURE__ */ jsx("path", { d: "m7 15 5 5 5-5" }),
|
|
122
|
+
/* @__PURE__ */ jsx("path", { d: "m7 9 5-5 5 5" })
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
function NavTopBar({
|
|
128
|
+
title,
|
|
129
|
+
subtitle,
|
|
130
|
+
user,
|
|
131
|
+
onSignOut,
|
|
132
|
+
actions,
|
|
133
|
+
themeToggle
|
|
134
|
+
}) {
|
|
135
|
+
const initials = (user?.name ?? "Politogy").slice(0, 2).toUpperCase();
|
|
136
|
+
return /* @__PURE__ */ jsxs("header", { className: "sticky top-0 z-20 flex items-center justify-between gap-4 border-b border-[color:var(--line)] bg-[color:var(--panel)] px-6 py-3 backdrop-blur-xl", children: [
|
|
137
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
138
|
+
/* @__PURE__ */ jsx(
|
|
139
|
+
"h1",
|
|
140
|
+
{
|
|
141
|
+
className: "truncate text-xl font-semibold tracking-[-0.02em] text-[color:var(--foreground)]",
|
|
142
|
+
"data-display": "true",
|
|
143
|
+
children: title
|
|
144
|
+
}
|
|
145
|
+
),
|
|
146
|
+
subtitle ? /* @__PURE__ */ jsx("p", { className: "truncate text-sm text-[color:var(--muted)]", children: subtitle }) : null
|
|
147
|
+
] }),
|
|
148
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
149
|
+
actions,
|
|
150
|
+
themeToggle,
|
|
151
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 rounded-full border border-[color:var(--line)] bg-[color:var(--panel-elevated)] py-1 pl-1 pr-2.5", children: [
|
|
152
|
+
/* @__PURE__ */ jsx("span", { className: "flex h-8 w-8 items-center justify-center rounded-full bg-[color:var(--color-race-red)] text-xs font-bold text-white", children: initials }),
|
|
153
|
+
/* @__PURE__ */ jsxs("span", { className: "hidden flex-col leading-tight md:flex", children: [
|
|
154
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-[color:var(--foreground)]", children: user?.name ?? "Signed in" }),
|
|
155
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium uppercase tracking-[0.08em] text-[color:var(--muted)]", children: [user?.tier, user?.role].filter(Boolean).join(" \xB7 ") || "member" })
|
|
156
|
+
] }),
|
|
157
|
+
onSignOut ? /* @__PURE__ */ jsx(
|
|
158
|
+
"button",
|
|
159
|
+
{
|
|
160
|
+
type: "button",
|
|
161
|
+
onClick: onSignOut,
|
|
162
|
+
"aria-label": "Sign out",
|
|
163
|
+
className: "ml-1 flex h-7 w-7 items-center justify-center rounded-full text-[color:var(--muted)] hover:text-[color:var(--color-race-red)]",
|
|
164
|
+
children: /* @__PURE__ */ jsx(SignOutIcon, {})
|
|
165
|
+
}
|
|
166
|
+
) : null
|
|
167
|
+
] })
|
|
168
|
+
] })
|
|
169
|
+
] });
|
|
170
|
+
}
|
|
171
|
+
function SignOutIcon() {
|
|
172
|
+
return /* @__PURE__ */ jsxs(
|
|
173
|
+
"svg",
|
|
174
|
+
{
|
|
175
|
+
width: 15,
|
|
176
|
+
height: 15,
|
|
177
|
+
viewBox: "0 0 24 24",
|
|
178
|
+
fill: "none",
|
|
179
|
+
stroke: "currentColor",
|
|
180
|
+
strokeWidth: 2,
|
|
181
|
+
strokeLinecap: "round",
|
|
182
|
+
strokeLinejoin: "round",
|
|
183
|
+
"aria-hidden": "true",
|
|
184
|
+
children: [
|
|
185
|
+
/* @__PURE__ */ jsx("path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" }),
|
|
186
|
+
/* @__PURE__ */ jsx("polyline", { points: "16 17 21 12 16 7" }),
|
|
187
|
+
/* @__PURE__ */ jsx("line", { x1: "21", x2: "9", y1: "12", y2: "12" })
|
|
188
|
+
]
|
|
189
|
+
}
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
function NavAppShell({ sidebar, topBar, children }) {
|
|
193
|
+
return /* @__PURE__ */ jsxs("div", { className: "min-h-screen bg-[color:var(--background)] text-[color:var(--foreground)]", children: [
|
|
194
|
+
/* @__PURE__ */ jsx(NavSidebar, { ...sidebar }),
|
|
195
|
+
/* @__PURE__ */ jsxs("div", { className: "lg:pl-[264px]", children: [
|
|
196
|
+
/* @__PURE__ */ jsx(NavTopBar, { ...topBar }),
|
|
197
|
+
/* @__PURE__ */ jsx("main", { className: "px-6 py-7", children: /* @__PURE__ */ jsx("div", { className: "mx-auto max-w-7xl", children }) })
|
|
198
|
+
] })
|
|
199
|
+
] });
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export { NavAppShell, NavSidebar, NavTopBar, envChipClasses, isNavItemActive };
|
|
203
|
+
//# sourceMappingURL=native.js.map
|
|
204
|
+
//# sourceMappingURL=native.js.map
|