@the-portland-company/shell 0.4.0 → 0.5.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/native.cjs +124 -88
- package/dist/native.cjs.map +1 -1
- package/dist/native.d.cts +16 -1
- package/dist/native.d.ts +16 -1
- package/dist/native.js +125 -89
- package/dist/native.js.map +1 -1
- package/package.json +1 -1
package/dist/native.cjs
CHANGED
|
@@ -15,6 +15,7 @@ function envChipClasses(label) {
|
|
|
15
15
|
if (l === "staging") return "bg-[rgba(242,201,76,0.18)] text-[#a6791f]";
|
|
16
16
|
return "bg-[rgba(0,166,81,0.14)] text-[color:var(--color-green-flag)]";
|
|
17
17
|
}
|
|
18
|
+
var ORG_PILL_DEFAULT = "#2d2f36";
|
|
18
19
|
function NavSidebar({
|
|
19
20
|
pathname,
|
|
20
21
|
brand,
|
|
@@ -27,102 +28,131 @@ function NavSidebar({
|
|
|
27
28
|
const Link = linkComponent ?? DefaultLink;
|
|
28
29
|
const activeFn = nav.isActive ?? isNavItemActive;
|
|
29
30
|
const ModeIcon = mode?.icon;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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",
|
|
31
|
+
const orgBg = org?.brandColor || ORG_PILL_DEFAULT;
|
|
32
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("aside", { className: "fixed inset-y-0 left-0 z-30 hidden w-[240px] flex-col border-r border-[color:var(--line)] bg-[color:var(--panel-elevated)] lg:flex", children: [
|
|
33
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
34
|
+
Link,
|
|
40
35
|
{
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
36
|
+
href: brand.homeHref ?? "/",
|
|
37
|
+
className: "flex h-[64px] flex-shrink-0 items-center gap-2.5 px-6",
|
|
38
|
+
children: brand.logoSrc ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
39
|
+
"img",
|
|
40
|
+
{
|
|
41
|
+
src: brand.logoSrc,
|
|
42
|
+
alt: brand.logoAlt ?? brand.title,
|
|
43
|
+
draggable: false,
|
|
44
|
+
className: "h-10 w-auto object-contain"
|
|
45
|
+
}
|
|
46
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
47
|
+
/* @__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 }),
|
|
48
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex flex-col leading-tight", children: [
|
|
49
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-extrabold tracking-[-0.01em] text-[color:var(--color-race-red)]", children: brand.title }),
|
|
50
|
+
brand.subtitle ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-[color:var(--muted)]", children: brand.subtitle }) : null
|
|
51
|
+
] })
|
|
52
|
+
] })
|
|
51
53
|
}
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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,
|
|
54
|
+
),
|
|
55
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col overflow-y-auto px-4 pb-2", children: [
|
|
56
|
+
org ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-3", children: [
|
|
57
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 px-1 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Organization" }),
|
|
58
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
59
|
+
"button",
|
|
64
60
|
{
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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(" "),
|
|
61
|
+
type: "button",
|
|
62
|
+
onClick: org.onClick,
|
|
63
|
+
style: { backgroundColor: orgBg },
|
|
64
|
+
className: "flex w-full items-center justify-between gap-2 rounded-md px-2.5 py-2 text-left text-white shadow-sm transition hover:opacity-95",
|
|
71
65
|
children: [
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
{
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
) : null,
|
|
79
|
-
item.label
|
|
66
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-center gap-2.5", children: [
|
|
67
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-7 w-7 shrink-0 items-center justify-center overflow-hidden rounded-full bg-white/20 text-[11px] font-bold text-white", children: org.logoUrl ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: org.logoUrl, alt: "", className: "h-full w-full object-cover" }) : (org.name ?? "Org").slice(0, 2).toUpperCase() }),
|
|
68
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-sm font-semibold", children: org.name ?? "No organizations" })
|
|
69
|
+
] }),
|
|
70
|
+
/* @__PURE__ */ jsxRuntime.jsx(ChevronDown, {})
|
|
80
71
|
]
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
] }) : null,
|
|
75
|
+
mode ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-4", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
76
|
+
"button",
|
|
77
|
+
{
|
|
78
|
+
type: "button",
|
|
79
|
+
className: "flex w-full items-center justify-between gap-2 rounded-md bg-[color:var(--color-race-red)] px-2.5 py-2 text-white shadow-sm",
|
|
80
|
+
children: [
|
|
81
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-2.5", children: [
|
|
82
|
+
ModeIcon ? /* @__PURE__ */ jsxRuntime.jsx(ModeIcon, { size: 16, className: "shrink-0" }) : null,
|
|
83
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold tracking-[-0.01em]", children: mode.label })
|
|
84
|
+
] }),
|
|
85
|
+
/* @__PURE__ */ jsxRuntime.jsx(ChevronDown, {})
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
) }) : null,
|
|
89
|
+
/* @__PURE__ */ jsxRuntime.jsxs("nav", { className: "flex flex-col gap-1", children: [
|
|
90
|
+
nav.groupLabel ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-2 pb-1 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: nav.groupLabel }) : null,
|
|
91
|
+
nav.items.map((item) => {
|
|
92
|
+
const active = activeFn(pathname, item.href);
|
|
93
|
+
const Icon = item.icon;
|
|
94
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
95
|
+
Link,
|
|
96
|
+
{
|
|
97
|
+
href: item.href,
|
|
98
|
+
"aria-current": active ? "page" : void 0,
|
|
99
|
+
className: [
|
|
100
|
+
"group flex items-center gap-3 rounded-md px-3 py-2 text-sm font-semibold transition",
|
|
101
|
+
active ? "bg-[color:var(--color-race-red)] text-white shadow-sm" : "text-[color:var(--muted)] hover:bg-[color:var(--panel)] hover:text-[color:var(--foreground)]"
|
|
102
|
+
].join(" "),
|
|
103
|
+
children: [
|
|
104
|
+
Icon ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
105
|
+
Icon,
|
|
106
|
+
{
|
|
107
|
+
size: 18,
|
|
108
|
+
className: active ? "text-white" : "text-current"
|
|
109
|
+
}
|
|
110
|
+
) : null,
|
|
111
|
+
item.label
|
|
112
|
+
]
|
|
113
|
+
},
|
|
114
|
+
item.href
|
|
115
|
+
);
|
|
116
|
+
})
|
|
117
|
+
] })
|
|
85
118
|
] }),
|
|
86
|
-
footer ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "
|
|
87
|
-
/* @__PURE__ */ jsxRuntime.
|
|
119
|
+
footer ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-shrink-0 border-t border-[color:var(--line)] px-4 py-3", children: [
|
|
120
|
+
footer.links && footer.links.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-center gap-1.5 text-xs", children: footer.links.map((l, idx) => /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1.5", children: [
|
|
121
|
+
idx > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[color:var(--muted)]", children: "|" }) : null,
|
|
88
122
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
89
|
-
"
|
|
123
|
+
"a",
|
|
90
124
|
{
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
children: footer.envLabel
|
|
125
|
+
href: l.href,
|
|
126
|
+
className: "font-medium text-[color:var(--color-race-red)] hover:underline",
|
|
127
|
+
children: l.label
|
|
95
128
|
}
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
129
|
+
)
|
|
130
|
+
] }, l.label)) }) : null,
|
|
131
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-1.5 font-[family-name:var(--font-plex-mono)] text-[10px] text-[color:var(--muted)]", children: [
|
|
132
|
+
footer.envLabel,
|
|
133
|
+
" \xB7 v",
|
|
134
|
+
footer.version,
|
|
135
|
+
" \xB7 ",
|
|
136
|
+
footer.commit.slice(0, 7)
|
|
137
|
+
] })
|
|
105
138
|
] }) : null
|
|
106
139
|
] });
|
|
107
140
|
}
|
|
108
|
-
function
|
|
109
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
141
|
+
function ChevronDown() {
|
|
142
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
110
143
|
"svg",
|
|
111
144
|
{
|
|
112
|
-
width:
|
|
113
|
-
height:
|
|
145
|
+
width: 16,
|
|
146
|
+
height: 16,
|
|
114
147
|
viewBox: "0 0 24 24",
|
|
115
148
|
fill: "none",
|
|
116
149
|
stroke: "currentColor",
|
|
117
150
|
strokeWidth: 2,
|
|
118
151
|
strokeLinecap: "round",
|
|
119
152
|
strokeLinejoin: "round",
|
|
120
|
-
className: "shrink-0
|
|
153
|
+
className: "shrink-0 opacity-80",
|
|
121
154
|
"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
|
-
]
|
|
155
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m6 9 6 6 6-6" })
|
|
126
156
|
}
|
|
127
157
|
);
|
|
128
158
|
}
|
|
@@ -135,12 +165,13 @@ function NavTopBar({
|
|
|
135
165
|
themeToggle
|
|
136
166
|
}) {
|
|
137
167
|
const initials = (user?.name ?? "Politogy").slice(0, 2).toUpperCase();
|
|
138
|
-
|
|
168
|
+
const secondary = user?.email || [user?.tier, user?.role].filter(Boolean).join(" \xB7 ") || "member";
|
|
169
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "sticky top-0 z-20 flex h-20 items-center justify-between gap-4 border-b border-[color:var(--line)] bg-[color:var(--panel)] px-4 backdrop-blur-xl", children: [
|
|
139
170
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
|
|
140
171
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
141
172
|
"h1",
|
|
142
173
|
{
|
|
143
|
-
className: "truncate text-
|
|
174
|
+
className: "truncate text-lg font-bold tracking-[-0.01em] text-[color:var(--foreground)]",
|
|
144
175
|
"data-display": "true",
|
|
145
176
|
children: title
|
|
146
177
|
}
|
|
@@ -150,22 +181,27 @@ function NavTopBar({
|
|
|
150
181
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
151
182
|
actions,
|
|
152
183
|
themeToggle,
|
|
153
|
-
/* @__PURE__ */ jsxRuntime.jsxs("
|
|
154
|
-
/* @__PURE__ */ jsxRuntime.
|
|
155
|
-
|
|
156
|
-
/* @__PURE__ */ jsxRuntime.
|
|
157
|
-
|
|
184
|
+
/* @__PURE__ */ jsxRuntime.jsxs("details", { className: "group relative", children: [
|
|
185
|
+
/* @__PURE__ */ jsxRuntime.jsxs("summary", { className: "flex cursor-pointer list-none items-center gap-2.5 rounded-full py-1 pl-1 pr-2 transition hover:bg-[color:var(--panel-elevated)] [&::-webkit-details-marker]:hidden", children: [
|
|
186
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-9 w-9 items-center justify-center overflow-hidden rounded-full bg-[color:var(--color-race-red)] text-xs font-bold text-white", children: user?.avatarUrl ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: user.avatarUrl, alt: "", className: "h-full w-full object-cover" }) : initials }),
|
|
187
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "hidden flex-col leading-tight md:flex", children: [
|
|
188
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold text-[color:var(--foreground)]", children: user?.name ?? "Signed in" }),
|
|
189
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-[color:var(--muted)]", children: secondary })
|
|
190
|
+
] }),
|
|
191
|
+
/* @__PURE__ */ jsxRuntime.jsx(ChevronDown, {})
|
|
158
192
|
] }),
|
|
159
|
-
onSignOut ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
193
|
+
onSignOut ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-0 z-30 mt-2 w-44 overflow-hidden rounded-xl border border-[color:var(--line)] bg-[color:var(--panel-elevated)] py-1 shadow-[var(--shadow-soft)]", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
160
194
|
"button",
|
|
161
195
|
{
|
|
162
196
|
type: "button",
|
|
163
197
|
onClick: onSignOut,
|
|
164
|
-
"
|
|
165
|
-
|
|
166
|
-
|
|
198
|
+
className: "flex w-full items-center gap-2.5 px-4 py-2 text-left text-sm font-medium text-[color:var(--foreground)] hover:bg-[color:var(--panel)] hover:text-[color:var(--color-race-red)]",
|
|
199
|
+
children: [
|
|
200
|
+
/* @__PURE__ */ jsxRuntime.jsx(SignOutIcon, {}),
|
|
201
|
+
"Sign out"
|
|
202
|
+
]
|
|
167
203
|
}
|
|
168
|
-
) : null
|
|
204
|
+
) }) : null
|
|
169
205
|
] })
|
|
170
206
|
] })
|
|
171
207
|
] });
|
|
@@ -194,7 +230,7 @@ function SignOutIcon() {
|
|
|
194
230
|
function NavAppShell({ sidebar, topBar, children }) {
|
|
195
231
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-h-screen bg-[color:var(--background)] text-[color:var(--foreground)]", children: [
|
|
196
232
|
/* @__PURE__ */ jsxRuntime.jsx(NavSidebar, { ...sidebar }),
|
|
197
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lg:pl-[
|
|
233
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lg:pl-[240px]", children: [
|
|
198
234
|
/* @__PURE__ */ jsxRuntime.jsx(NavTopBar, { ...topBar }),
|
|
199
235
|
/* @__PURE__ */ jsxRuntime.jsx("main", { className: "px-6 py-7", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto max-w-7xl", children }) })
|
|
200
236
|
] })
|
package/dist/native.cjs.map
CHANGED
|
@@ -1 +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"]}
|
|
1
|
+
{"version":3,"sources":["../src/native/index.tsx"],"names":["jsx","jsxs","Fragment"],"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// It is a FAITHFUL VISUAL TWIN of the Chakra chrome (Sidebar.tsx / Header.tsx /\n// OrgPill / ModePill / NavItem / UserMenu): same 240px sidebar, brand-logo\n// slot, \"ORGANIZATION\" org pill, mode pill, solid active nav rows, pipe-\n// separated footer links + commit string, and an avatar+name+email+chevron user\n// menu — so an app rendering this looks identical to the main VRM app.\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 /**\n * Brand logo image src. When supplied this renders as the wordmark logo\n * (matching the main app's BrandLogo), and `badgeLabel`/`title`/`subtitle`\n * are used only as the no-image fallback.\n */\n logoSrc?: string\n logoAlt?: string\n /** Short label shown inside the brand badge fallback (e.g. \"P\"). */\n badgeLabel: string\n title: string\n subtitle?: string\n}\n\nexport type NavOrg = {\n name: string | null\n /** Optional org logo for the pill badge; falls back to initials. */\n logoUrl?: string | null\n /** Solid pill background (the org's brand color). Defaults to a dark slate. */\n brandColor?: 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; external?: boolean }\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\n// Default org-pill background — a dark slate that mirrors the main app's pill\n// when an org has no explicit brand color.\nconst ORG_PILL_DEFAULT = '#2d2f36'\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 const orgBg = org?.brandColor || ORG_PILL_DEFAULT\n\n return (\n <aside className=\"fixed inset-y-0 left-0 z-30 hidden w-[240px] flex-col border-r border-[color:var(--line)] bg-[color:var(--panel-elevated)] lg:flex\">\n {/* Brand — logo image (preferred) or badge fallback */}\n <Link\n href={brand.homeHref ?? '/'}\n className=\"flex h-[64px] flex-shrink-0 items-center gap-2.5 px-6\"\n >\n {brand.logoSrc ? (\n <img\n src={brand.logoSrc}\n alt={brand.logoAlt ?? brand.title}\n draggable={false}\n className=\"h-10 w-auto object-contain\"\n />\n ) : (\n <>\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-base font-extrabold tracking-[-0.01em] text-[color:var(--color-race-red)]\">\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 </>\n )}\n </Link>\n\n {/* Scroll region: org pill, mode pill, nav */}\n <div className=\"flex flex-1 flex-col overflow-y-auto px-4 pb-2\">\n {/* Organization switcher */}\n {org ? (\n <div className=\"mb-3\">\n <p className=\"mb-2 px-1 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]\">\n Organization\n </p>\n <button\n type=\"button\"\n onClick={org.onClick}\n style={{ backgroundColor: orgBg }}\n className=\"flex w-full items-center justify-between gap-2 rounded-md px-2.5 py-2 text-left text-white shadow-sm transition hover:opacity-95\"\n >\n <span className=\"flex min-w-0 items-center gap-2.5\">\n <span className=\"flex h-7 w-7 shrink-0 items-center justify-center overflow-hidden rounded-full bg-white/20 text-[11px] font-bold text-white\">\n {org.logoUrl ? (\n <img src={org.logoUrl} alt=\"\" className=\"h-full w-full object-cover\" />\n ) : (\n (org.name ?? 'Org').slice(0, 2).toUpperCase()\n )}\n </span>\n <span className=\"truncate text-sm font-semibold\">\n {org.name ?? 'No organizations'}\n </span>\n </span>\n <ChevronDown />\n </button>\n </div>\n ) : null}\n\n {/* Mode pill */}\n {mode ? (\n <div className=\"mb-4\">\n <button\n type=\"button\"\n className=\"flex w-full items-center justify-between gap-2 rounded-md bg-[color:var(--color-race-red)] px-2.5 py-2 text-white shadow-sm\"\n >\n <span className=\"flex items-center gap-2.5\">\n {ModeIcon ? <ModeIcon size={16} className=\"shrink-0\" /> : null}\n <span className=\"text-sm font-semibold tracking-[-0.01em]\">{mode.label}</span>\n </span>\n <ChevronDown />\n </button>\n </div>\n ) : null}\n\n {/* Nav list */}\n <nav className=\"flex flex-col gap-1\">\n {nav.groupLabel ? (\n <p className=\"px-2 pb-1 text-[11px] font-semibold uppercase tracking-[0.08em] 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-md px-3 py-2 text-sm font-semibold transition',\n active\n ? 'bg-[color:var(--color-race-red)] text-white shadow-sm'\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={active ? 'text-white' : 'text-current'}\n />\n ) : null}\n {item.label}\n </Link>\n )\n })}\n </nav>\n </div>\n\n {/* Footer — pipe-separated links + commit/version string */}\n {footer ? (\n <div className=\"flex-shrink-0 border-t border-[color:var(--line)] px-4 py-3\">\n {footer.links && footer.links.length > 0 ? (\n <div className=\"flex flex-wrap items-center gap-1.5 text-xs\">\n {footer.links.map((l, idx) => (\n <span key={l.label} className=\"inline-flex items-center gap-1.5\">\n {idx > 0 ? <span className=\"text-[color:var(--muted)]\">|</span> : null}\n <a\n href={l.href}\n className=\"font-medium text-[color:var(--color-race-red)] hover:underline\"\n >\n {l.label}\n </a>\n </span>\n ))}\n </div>\n ) : null}\n <p className=\"mt-1.5 font-[family-name:var(--font-plex-mono)] text-[10px] text-[color:var(--muted)]\">\n {footer.envLabel} · v{footer.version} · {footer.commit.slice(0, 7)}\n </p>\n </div>\n ) : null}\n </aside>\n )\n}\n\n// Inline chevron-down (mirrors lucide `ChevronDown`) — no icon dependency.\nfunction ChevronDown() {\n return (\n <svg\n width={16}\n height={16}\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 opacity-80\"\n aria-hidden=\"true\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n )\n}\n\n// ---------------------------------------------------------------------------\n\nexport type NavUser = {\n name: string | null\n email?: string | null\n /** Avatar image URL; falls back to initials on the brand color. */\n avatarUrl?: 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 const secondary = user?.email || [user?.tier, user?.role].filter(Boolean).join(' · ') || 'member'\n return (\n <header className=\"sticky top-0 z-20 flex h-20 items-center justify-between gap-4 border-b border-[color:var(--line)] bg-[color:var(--panel)] px-4 backdrop-blur-xl\">\n <div className=\"min-w-0\">\n <h1\n className=\"truncate text-lg font-bold tracking-[-0.01em] 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 — avatar + name/email + chevron; sign-out in a native\n <details> popover so the package needs no JS state. */}\n <details className=\"group relative\">\n <summary className=\"flex cursor-pointer list-none items-center gap-2.5 rounded-full py-1 pl-1 pr-2 transition hover:bg-[color:var(--panel-elevated)] [&::-webkit-details-marker]:hidden\">\n <span className=\"flex h-9 w-9 items-center justify-center overflow-hidden rounded-full bg-[color:var(--color-race-red)] text-xs font-bold text-white\">\n {user?.avatarUrl ? (\n <img src={user.avatarUrl} alt=\"\" className=\"h-full w-full object-cover\" />\n ) : (\n initials\n )}\n </span>\n <span className=\"hidden flex-col leading-tight md:flex\">\n <span className=\"text-sm font-semibold text-[color:var(--foreground)]\">\n {user?.name ?? 'Signed in'}\n </span>\n <span className=\"text-xs text-[color:var(--muted)]\">{secondary}</span>\n </span>\n <ChevronDown />\n </summary>\n {onSignOut ? (\n <div className=\"absolute right-0 z-30 mt-2 w-44 overflow-hidden rounded-xl border border-[color:var(--line)] bg-[color:var(--panel-elevated)] py-1 shadow-[var(--shadow-soft)]\">\n <button\n type=\"button\"\n onClick={onSignOut}\n className=\"flex w-full items-center gap-2.5 px-4 py-2 text-left text-sm font-medium text-[color:var(--foreground)] hover:bg-[color:var(--panel)] hover:text-[color:var(--color-race-red)]\"\n >\n <SignOutIcon />\n Sign out\n </button>\n </div>\n ) : null}\n </details>\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-[240px]\">\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"]}
|
package/dist/native.d.cts
CHANGED
|
@@ -24,13 +24,24 @@ declare function envChipClasses(label: string): string;
|
|
|
24
24
|
type NavBrand = {
|
|
25
25
|
/** Where the logo links to. Defaults to "/". */
|
|
26
26
|
homeHref?: string;
|
|
27
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Brand logo image src. When supplied this renders as the wordmark logo
|
|
29
|
+
* (matching the main app's BrandLogo), and `badgeLabel`/`title`/`subtitle`
|
|
30
|
+
* are used only as the no-image fallback.
|
|
31
|
+
*/
|
|
32
|
+
logoSrc?: string;
|
|
33
|
+
logoAlt?: string;
|
|
34
|
+
/** Short label shown inside the brand badge fallback (e.g. "P"). */
|
|
28
35
|
badgeLabel: string;
|
|
29
36
|
title: string;
|
|
30
37
|
subtitle?: string;
|
|
31
38
|
};
|
|
32
39
|
type NavOrg = {
|
|
33
40
|
name: string | null;
|
|
41
|
+
/** Optional org logo for the pill badge; falls back to initials. */
|
|
42
|
+
logoUrl?: string | null;
|
|
43
|
+
/** Solid pill background (the org's brand color). Defaults to a dark slate. */
|
|
44
|
+
brandColor?: string | null;
|
|
34
45
|
onClick?: () => void;
|
|
35
46
|
};
|
|
36
47
|
type NavMode = {
|
|
@@ -40,6 +51,7 @@ type NavMode = {
|
|
|
40
51
|
type NavFooterLink = {
|
|
41
52
|
label: string;
|
|
42
53
|
href: string;
|
|
54
|
+
external?: boolean;
|
|
43
55
|
};
|
|
44
56
|
type NavFooter = {
|
|
45
57
|
envLabel: string;
|
|
@@ -63,6 +75,9 @@ type NavSidebarProps = {
|
|
|
63
75
|
declare function NavSidebar({ pathname, brand, nav, org, mode, footer, linkComponent, }: NavSidebarProps): react_jsx_runtime.JSX.Element;
|
|
64
76
|
type NavUser = {
|
|
65
77
|
name: string | null;
|
|
78
|
+
email?: string | null;
|
|
79
|
+
/** Avatar image URL; falls back to initials on the brand color. */
|
|
80
|
+
avatarUrl?: string | null;
|
|
66
81
|
tier?: string | null;
|
|
67
82
|
role?: string | null;
|
|
68
83
|
};
|
package/dist/native.d.ts
CHANGED
|
@@ -24,13 +24,24 @@ declare function envChipClasses(label: string): string;
|
|
|
24
24
|
type NavBrand = {
|
|
25
25
|
/** Where the logo links to. Defaults to "/". */
|
|
26
26
|
homeHref?: string;
|
|
27
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Brand logo image src. When supplied this renders as the wordmark logo
|
|
29
|
+
* (matching the main app's BrandLogo), and `badgeLabel`/`title`/`subtitle`
|
|
30
|
+
* are used only as the no-image fallback.
|
|
31
|
+
*/
|
|
32
|
+
logoSrc?: string;
|
|
33
|
+
logoAlt?: string;
|
|
34
|
+
/** Short label shown inside the brand badge fallback (e.g. "P"). */
|
|
28
35
|
badgeLabel: string;
|
|
29
36
|
title: string;
|
|
30
37
|
subtitle?: string;
|
|
31
38
|
};
|
|
32
39
|
type NavOrg = {
|
|
33
40
|
name: string | null;
|
|
41
|
+
/** Optional org logo for the pill badge; falls back to initials. */
|
|
42
|
+
logoUrl?: string | null;
|
|
43
|
+
/** Solid pill background (the org's brand color). Defaults to a dark slate. */
|
|
44
|
+
brandColor?: string | null;
|
|
34
45
|
onClick?: () => void;
|
|
35
46
|
};
|
|
36
47
|
type NavMode = {
|
|
@@ -40,6 +51,7 @@ type NavMode = {
|
|
|
40
51
|
type NavFooterLink = {
|
|
41
52
|
label: string;
|
|
42
53
|
href: string;
|
|
54
|
+
external?: boolean;
|
|
43
55
|
};
|
|
44
56
|
type NavFooter = {
|
|
45
57
|
envLabel: string;
|
|
@@ -63,6 +75,9 @@ type NavSidebarProps = {
|
|
|
63
75
|
declare function NavSidebar({ pathname, brand, nav, org, mode, footer, linkComponent, }: NavSidebarProps): react_jsx_runtime.JSX.Element;
|
|
64
76
|
type NavUser = {
|
|
65
77
|
name: string | null;
|
|
78
|
+
email?: string | null;
|
|
79
|
+
/** Avatar image URL; falls back to initials on the brand color. */
|
|
80
|
+
avatarUrl?: string | null;
|
|
66
81
|
tier?: string | null;
|
|
67
82
|
role?: string | null;
|
|
68
83
|
};
|
package/dist/native.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
1
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
|
|
3
3
|
// src/native/index.tsx
|
|
4
4
|
var DefaultLink = ({ href, children, ...rest }) => /* @__PURE__ */ jsx("a", { href, ...rest, children });
|
|
@@ -13,6 +13,7 @@ function envChipClasses(label) {
|
|
|
13
13
|
if (l === "staging") return "bg-[rgba(242,201,76,0.18)] text-[#a6791f]";
|
|
14
14
|
return "bg-[rgba(0,166,81,0.14)] text-[color:var(--color-green-flag)]";
|
|
15
15
|
}
|
|
16
|
+
var ORG_PILL_DEFAULT = "#2d2f36";
|
|
16
17
|
function NavSidebar({
|
|
17
18
|
pathname,
|
|
18
19
|
brand,
|
|
@@ -25,102 +26,131 @@ function NavSidebar({
|
|
|
25
26
|
const Link = linkComponent ?? DefaultLink;
|
|
26
27
|
const activeFn = nav.isActive ?? isNavItemActive;
|
|
27
28
|
const ModeIcon = mode?.icon;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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",
|
|
29
|
+
const orgBg = org?.brandColor || ORG_PILL_DEFAULT;
|
|
30
|
+
return /* @__PURE__ */ jsxs("aside", { className: "fixed inset-y-0 left-0 z-30 hidden w-[240px] flex-col border-r border-[color:var(--line)] bg-[color:var(--panel-elevated)] lg:flex", children: [
|
|
31
|
+
/* @__PURE__ */ jsx(
|
|
32
|
+
Link,
|
|
38
33
|
{
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
34
|
+
href: brand.homeHref ?? "/",
|
|
35
|
+
className: "flex h-[64px] flex-shrink-0 items-center gap-2.5 px-6",
|
|
36
|
+
children: brand.logoSrc ? /* @__PURE__ */ jsx(
|
|
37
|
+
"img",
|
|
38
|
+
{
|
|
39
|
+
src: brand.logoSrc,
|
|
40
|
+
alt: brand.logoAlt ?? brand.title,
|
|
41
|
+
draggable: false,
|
|
42
|
+
className: "h-10 w-auto object-contain"
|
|
43
|
+
}
|
|
44
|
+
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
45
|
+
/* @__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 }),
|
|
46
|
+
/* @__PURE__ */ jsxs("span", { className: "flex flex-col leading-tight", children: [
|
|
47
|
+
/* @__PURE__ */ jsx("span", { className: "text-base font-extrabold tracking-[-0.01em] text-[color:var(--color-race-red)]", children: brand.title }),
|
|
48
|
+
brand.subtitle ? /* @__PURE__ */ jsx("span", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-[color:var(--muted)]", children: brand.subtitle }) : null
|
|
49
|
+
] })
|
|
50
|
+
] })
|
|
49
51
|
}
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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,
|
|
52
|
+
),
|
|
53
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col overflow-y-auto px-4 pb-2", children: [
|
|
54
|
+
org ? /* @__PURE__ */ jsxs("div", { className: "mb-3", children: [
|
|
55
|
+
/* @__PURE__ */ jsx("p", { className: "mb-2 px-1 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Organization" }),
|
|
56
|
+
/* @__PURE__ */ jsxs(
|
|
57
|
+
"button",
|
|
62
58
|
{
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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(" "),
|
|
59
|
+
type: "button",
|
|
60
|
+
onClick: org.onClick,
|
|
61
|
+
style: { backgroundColor: orgBg },
|
|
62
|
+
className: "flex w-full items-center justify-between gap-2 rounded-md px-2.5 py-2 text-left text-white shadow-sm transition hover:opacity-95",
|
|
69
63
|
children: [
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
{
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
) : null,
|
|
77
|
-
item.label
|
|
64
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-2.5", children: [
|
|
65
|
+
/* @__PURE__ */ jsx("span", { className: "flex h-7 w-7 shrink-0 items-center justify-center overflow-hidden rounded-full bg-white/20 text-[11px] font-bold text-white", children: org.logoUrl ? /* @__PURE__ */ jsx("img", { src: org.logoUrl, alt: "", className: "h-full w-full object-cover" }) : (org.name ?? "Org").slice(0, 2).toUpperCase() }),
|
|
66
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-sm font-semibold", children: org.name ?? "No organizations" })
|
|
67
|
+
] }),
|
|
68
|
+
/* @__PURE__ */ jsx(ChevronDown, {})
|
|
78
69
|
]
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
] }) : null,
|
|
73
|
+
mode ? /* @__PURE__ */ jsx("div", { className: "mb-4", children: /* @__PURE__ */ jsxs(
|
|
74
|
+
"button",
|
|
75
|
+
{
|
|
76
|
+
type: "button",
|
|
77
|
+
className: "flex w-full items-center justify-between gap-2 rounded-md bg-[color:var(--color-race-red)] px-2.5 py-2 text-white shadow-sm",
|
|
78
|
+
children: [
|
|
79
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2.5", children: [
|
|
80
|
+
ModeIcon ? /* @__PURE__ */ jsx(ModeIcon, { size: 16, className: "shrink-0" }) : null,
|
|
81
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold tracking-[-0.01em]", children: mode.label })
|
|
82
|
+
] }),
|
|
83
|
+
/* @__PURE__ */ jsx(ChevronDown, {})
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
) }) : null,
|
|
87
|
+
/* @__PURE__ */ jsxs("nav", { className: "flex flex-col gap-1", children: [
|
|
88
|
+
nav.groupLabel ? /* @__PURE__ */ jsx("p", { className: "px-2 pb-1 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: nav.groupLabel }) : null,
|
|
89
|
+
nav.items.map((item) => {
|
|
90
|
+
const active = activeFn(pathname, item.href);
|
|
91
|
+
const Icon = item.icon;
|
|
92
|
+
return /* @__PURE__ */ jsxs(
|
|
93
|
+
Link,
|
|
94
|
+
{
|
|
95
|
+
href: item.href,
|
|
96
|
+
"aria-current": active ? "page" : void 0,
|
|
97
|
+
className: [
|
|
98
|
+
"group flex items-center gap-3 rounded-md px-3 py-2 text-sm font-semibold transition",
|
|
99
|
+
active ? "bg-[color:var(--color-race-red)] text-white shadow-sm" : "text-[color:var(--muted)] hover:bg-[color:var(--panel)] hover:text-[color:var(--foreground)]"
|
|
100
|
+
].join(" "),
|
|
101
|
+
children: [
|
|
102
|
+
Icon ? /* @__PURE__ */ jsx(
|
|
103
|
+
Icon,
|
|
104
|
+
{
|
|
105
|
+
size: 18,
|
|
106
|
+
className: active ? "text-white" : "text-current"
|
|
107
|
+
}
|
|
108
|
+
) : null,
|
|
109
|
+
item.label
|
|
110
|
+
]
|
|
111
|
+
},
|
|
112
|
+
item.href
|
|
113
|
+
);
|
|
114
|
+
})
|
|
115
|
+
] })
|
|
83
116
|
] }),
|
|
84
|
-
footer ? /* @__PURE__ */ jsxs("div", { className: "
|
|
85
|
-
/* @__PURE__ */
|
|
117
|
+
footer ? /* @__PURE__ */ jsxs("div", { className: "flex-shrink-0 border-t border-[color:var(--line)] px-4 py-3", children: [
|
|
118
|
+
footer.links && footer.links.length > 0 ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-1.5 text-xs", children: footer.links.map((l, idx) => /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5", children: [
|
|
119
|
+
idx > 0 ? /* @__PURE__ */ jsx("span", { className: "text-[color:var(--muted)]", children: "|" }) : null,
|
|
86
120
|
/* @__PURE__ */ jsx(
|
|
87
|
-
"
|
|
121
|
+
"a",
|
|
88
122
|
{
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
children: footer.envLabel
|
|
123
|
+
href: l.href,
|
|
124
|
+
className: "font-medium text-[color:var(--color-race-red)] hover:underline",
|
|
125
|
+
children: l.label
|
|
93
126
|
}
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
127
|
+
)
|
|
128
|
+
] }, l.label)) }) : null,
|
|
129
|
+
/* @__PURE__ */ jsxs("p", { className: "mt-1.5 font-[family-name:var(--font-plex-mono)] text-[10px] text-[color:var(--muted)]", children: [
|
|
130
|
+
footer.envLabel,
|
|
131
|
+
" \xB7 v",
|
|
132
|
+
footer.version,
|
|
133
|
+
" \xB7 ",
|
|
134
|
+
footer.commit.slice(0, 7)
|
|
135
|
+
] })
|
|
103
136
|
] }) : null
|
|
104
137
|
] });
|
|
105
138
|
}
|
|
106
|
-
function
|
|
107
|
-
return /* @__PURE__ */
|
|
139
|
+
function ChevronDown() {
|
|
140
|
+
return /* @__PURE__ */ jsx(
|
|
108
141
|
"svg",
|
|
109
142
|
{
|
|
110
|
-
width:
|
|
111
|
-
height:
|
|
143
|
+
width: 16,
|
|
144
|
+
height: 16,
|
|
112
145
|
viewBox: "0 0 24 24",
|
|
113
146
|
fill: "none",
|
|
114
147
|
stroke: "currentColor",
|
|
115
148
|
strokeWidth: 2,
|
|
116
149
|
strokeLinecap: "round",
|
|
117
150
|
strokeLinejoin: "round",
|
|
118
|
-
className: "shrink-0
|
|
151
|
+
className: "shrink-0 opacity-80",
|
|
119
152
|
"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
|
-
]
|
|
153
|
+
children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
|
|
124
154
|
}
|
|
125
155
|
);
|
|
126
156
|
}
|
|
@@ -133,12 +163,13 @@ function NavTopBar({
|
|
|
133
163
|
themeToggle
|
|
134
164
|
}) {
|
|
135
165
|
const initials = (user?.name ?? "Politogy").slice(0, 2).toUpperCase();
|
|
136
|
-
|
|
166
|
+
const secondary = user?.email || [user?.tier, user?.role].filter(Boolean).join(" \xB7 ") || "member";
|
|
167
|
+
return /* @__PURE__ */ jsxs("header", { className: "sticky top-0 z-20 flex h-20 items-center justify-between gap-4 border-b border-[color:var(--line)] bg-[color:var(--panel)] px-4 backdrop-blur-xl", children: [
|
|
137
168
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
138
169
|
/* @__PURE__ */ jsx(
|
|
139
170
|
"h1",
|
|
140
171
|
{
|
|
141
|
-
className: "truncate text-
|
|
172
|
+
className: "truncate text-lg font-bold tracking-[-0.01em] text-[color:var(--foreground)]",
|
|
142
173
|
"data-display": "true",
|
|
143
174
|
children: title
|
|
144
175
|
}
|
|
@@ -148,22 +179,27 @@ function NavTopBar({
|
|
|
148
179
|
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
149
180
|
actions,
|
|
150
181
|
themeToggle,
|
|
151
|
-
/* @__PURE__ */ jsxs("
|
|
152
|
-
/* @__PURE__ */
|
|
153
|
-
|
|
154
|
-
/* @__PURE__ */
|
|
155
|
-
|
|
182
|
+
/* @__PURE__ */ jsxs("details", { className: "group relative", children: [
|
|
183
|
+
/* @__PURE__ */ jsxs("summary", { className: "flex cursor-pointer list-none items-center gap-2.5 rounded-full py-1 pl-1 pr-2 transition hover:bg-[color:var(--panel-elevated)] [&::-webkit-details-marker]:hidden", children: [
|
|
184
|
+
/* @__PURE__ */ jsx("span", { className: "flex h-9 w-9 items-center justify-center overflow-hidden rounded-full bg-[color:var(--color-race-red)] text-xs font-bold text-white", children: user?.avatarUrl ? /* @__PURE__ */ jsx("img", { src: user.avatarUrl, alt: "", className: "h-full w-full object-cover" }) : initials }),
|
|
185
|
+
/* @__PURE__ */ jsxs("span", { className: "hidden flex-col leading-tight md:flex", children: [
|
|
186
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-[color:var(--foreground)]", children: user?.name ?? "Signed in" }),
|
|
187
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-[color:var(--muted)]", children: secondary })
|
|
188
|
+
] }),
|
|
189
|
+
/* @__PURE__ */ jsx(ChevronDown, {})
|
|
156
190
|
] }),
|
|
157
|
-
onSignOut ? /* @__PURE__ */ jsx(
|
|
191
|
+
onSignOut ? /* @__PURE__ */ jsx("div", { className: "absolute right-0 z-30 mt-2 w-44 overflow-hidden rounded-xl border border-[color:var(--line)] bg-[color:var(--panel-elevated)] py-1 shadow-[var(--shadow-soft)]", children: /* @__PURE__ */ jsxs(
|
|
158
192
|
"button",
|
|
159
193
|
{
|
|
160
194
|
type: "button",
|
|
161
195
|
onClick: onSignOut,
|
|
162
|
-
"
|
|
163
|
-
|
|
164
|
-
|
|
196
|
+
className: "flex w-full items-center gap-2.5 px-4 py-2 text-left text-sm font-medium text-[color:var(--foreground)] hover:bg-[color:var(--panel)] hover:text-[color:var(--color-race-red)]",
|
|
197
|
+
children: [
|
|
198
|
+
/* @__PURE__ */ jsx(SignOutIcon, {}),
|
|
199
|
+
"Sign out"
|
|
200
|
+
]
|
|
165
201
|
}
|
|
166
|
-
) : null
|
|
202
|
+
) }) : null
|
|
167
203
|
] })
|
|
168
204
|
] })
|
|
169
205
|
] });
|
|
@@ -192,7 +228,7 @@ function SignOutIcon() {
|
|
|
192
228
|
function NavAppShell({ sidebar, topBar, children }) {
|
|
193
229
|
return /* @__PURE__ */ jsxs("div", { className: "min-h-screen bg-[color:var(--background)] text-[color:var(--foreground)]", children: [
|
|
194
230
|
/* @__PURE__ */ jsx(NavSidebar, { ...sidebar }),
|
|
195
|
-
/* @__PURE__ */ jsxs("div", { className: "lg:pl-[
|
|
231
|
+
/* @__PURE__ */ jsxs("div", { className: "lg:pl-[240px]", children: [
|
|
196
232
|
/* @__PURE__ */ jsx(NavTopBar, { ...topBar }),
|
|
197
233
|
/* @__PURE__ */ jsx("main", { className: "px-6 py-7", children: /* @__PURE__ */ jsx("div", { className: "mx-auto max-w-7xl", children }) })
|
|
198
234
|
] })
|
package/dist/native.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/native/index.tsx"],"names":[],"mappings":"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","file":"native.js","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"]}
|
|
1
|
+
{"version":3,"sources":["../src/native/index.tsx"],"names":[],"mappings":"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","file":"native.js","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// It is a FAITHFUL VISUAL TWIN of the Chakra chrome (Sidebar.tsx / Header.tsx /\n// OrgPill / ModePill / NavItem / UserMenu): same 240px sidebar, brand-logo\n// slot, \"ORGANIZATION\" org pill, mode pill, solid active nav rows, pipe-\n// separated footer links + commit string, and an avatar+name+email+chevron user\n// menu — so an app rendering this looks identical to the main VRM app.\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 /**\n * Brand logo image src. When supplied this renders as the wordmark logo\n * (matching the main app's BrandLogo), and `badgeLabel`/`title`/`subtitle`\n * are used only as the no-image fallback.\n */\n logoSrc?: string\n logoAlt?: string\n /** Short label shown inside the brand badge fallback (e.g. \"P\"). */\n badgeLabel: string\n title: string\n subtitle?: string\n}\n\nexport type NavOrg = {\n name: string | null\n /** Optional org logo for the pill badge; falls back to initials. */\n logoUrl?: string | null\n /** Solid pill background (the org's brand color). Defaults to a dark slate. */\n brandColor?: 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; external?: boolean }\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\n// Default org-pill background — a dark slate that mirrors the main app's pill\n// when an org has no explicit brand color.\nconst ORG_PILL_DEFAULT = '#2d2f36'\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 const orgBg = org?.brandColor || ORG_PILL_DEFAULT\n\n return (\n <aside className=\"fixed inset-y-0 left-0 z-30 hidden w-[240px] flex-col border-r border-[color:var(--line)] bg-[color:var(--panel-elevated)] lg:flex\">\n {/* Brand — logo image (preferred) or badge fallback */}\n <Link\n href={brand.homeHref ?? '/'}\n className=\"flex h-[64px] flex-shrink-0 items-center gap-2.5 px-6\"\n >\n {brand.logoSrc ? (\n <img\n src={brand.logoSrc}\n alt={brand.logoAlt ?? brand.title}\n draggable={false}\n className=\"h-10 w-auto object-contain\"\n />\n ) : (\n <>\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-base font-extrabold tracking-[-0.01em] text-[color:var(--color-race-red)]\">\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 </>\n )}\n </Link>\n\n {/* Scroll region: org pill, mode pill, nav */}\n <div className=\"flex flex-1 flex-col overflow-y-auto px-4 pb-2\">\n {/* Organization switcher */}\n {org ? (\n <div className=\"mb-3\">\n <p className=\"mb-2 px-1 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]\">\n Organization\n </p>\n <button\n type=\"button\"\n onClick={org.onClick}\n style={{ backgroundColor: orgBg }}\n className=\"flex w-full items-center justify-between gap-2 rounded-md px-2.5 py-2 text-left text-white shadow-sm transition hover:opacity-95\"\n >\n <span className=\"flex min-w-0 items-center gap-2.5\">\n <span className=\"flex h-7 w-7 shrink-0 items-center justify-center overflow-hidden rounded-full bg-white/20 text-[11px] font-bold text-white\">\n {org.logoUrl ? (\n <img src={org.logoUrl} alt=\"\" className=\"h-full w-full object-cover\" />\n ) : (\n (org.name ?? 'Org').slice(0, 2).toUpperCase()\n )}\n </span>\n <span className=\"truncate text-sm font-semibold\">\n {org.name ?? 'No organizations'}\n </span>\n </span>\n <ChevronDown />\n </button>\n </div>\n ) : null}\n\n {/* Mode pill */}\n {mode ? (\n <div className=\"mb-4\">\n <button\n type=\"button\"\n className=\"flex w-full items-center justify-between gap-2 rounded-md bg-[color:var(--color-race-red)] px-2.5 py-2 text-white shadow-sm\"\n >\n <span className=\"flex items-center gap-2.5\">\n {ModeIcon ? <ModeIcon size={16} className=\"shrink-0\" /> : null}\n <span className=\"text-sm font-semibold tracking-[-0.01em]\">{mode.label}</span>\n </span>\n <ChevronDown />\n </button>\n </div>\n ) : null}\n\n {/* Nav list */}\n <nav className=\"flex flex-col gap-1\">\n {nav.groupLabel ? (\n <p className=\"px-2 pb-1 text-[11px] font-semibold uppercase tracking-[0.08em] 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-md px-3 py-2 text-sm font-semibold transition',\n active\n ? 'bg-[color:var(--color-race-red)] text-white shadow-sm'\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={active ? 'text-white' : 'text-current'}\n />\n ) : null}\n {item.label}\n </Link>\n )\n })}\n </nav>\n </div>\n\n {/* Footer — pipe-separated links + commit/version string */}\n {footer ? (\n <div className=\"flex-shrink-0 border-t border-[color:var(--line)] px-4 py-3\">\n {footer.links && footer.links.length > 0 ? (\n <div className=\"flex flex-wrap items-center gap-1.5 text-xs\">\n {footer.links.map((l, idx) => (\n <span key={l.label} className=\"inline-flex items-center gap-1.5\">\n {idx > 0 ? <span className=\"text-[color:var(--muted)]\">|</span> : null}\n <a\n href={l.href}\n className=\"font-medium text-[color:var(--color-race-red)] hover:underline\"\n >\n {l.label}\n </a>\n </span>\n ))}\n </div>\n ) : null}\n <p className=\"mt-1.5 font-[family-name:var(--font-plex-mono)] text-[10px] text-[color:var(--muted)]\">\n {footer.envLabel} · v{footer.version} · {footer.commit.slice(0, 7)}\n </p>\n </div>\n ) : null}\n </aside>\n )\n}\n\n// Inline chevron-down (mirrors lucide `ChevronDown`) — no icon dependency.\nfunction ChevronDown() {\n return (\n <svg\n width={16}\n height={16}\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 opacity-80\"\n aria-hidden=\"true\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n )\n}\n\n// ---------------------------------------------------------------------------\n\nexport type NavUser = {\n name: string | null\n email?: string | null\n /** Avatar image URL; falls back to initials on the brand color. */\n avatarUrl?: 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 const secondary = user?.email || [user?.tier, user?.role].filter(Boolean).join(' · ') || 'member'\n return (\n <header className=\"sticky top-0 z-20 flex h-20 items-center justify-between gap-4 border-b border-[color:var(--line)] bg-[color:var(--panel)] px-4 backdrop-blur-xl\">\n <div className=\"min-w-0\">\n <h1\n className=\"truncate text-lg font-bold tracking-[-0.01em] 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 — avatar + name/email + chevron; sign-out in a native\n <details> popover so the package needs no JS state. */}\n <details className=\"group relative\">\n <summary className=\"flex cursor-pointer list-none items-center gap-2.5 rounded-full py-1 pl-1 pr-2 transition hover:bg-[color:var(--panel-elevated)] [&::-webkit-details-marker]:hidden\">\n <span className=\"flex h-9 w-9 items-center justify-center overflow-hidden rounded-full bg-[color:var(--color-race-red)] text-xs font-bold text-white\">\n {user?.avatarUrl ? (\n <img src={user.avatarUrl} alt=\"\" className=\"h-full w-full object-cover\" />\n ) : (\n initials\n )}\n </span>\n <span className=\"hidden flex-col leading-tight md:flex\">\n <span className=\"text-sm font-semibold text-[color:var(--foreground)]\">\n {user?.name ?? 'Signed in'}\n </span>\n <span className=\"text-xs text-[color:var(--muted)]\">{secondary}</span>\n </span>\n <ChevronDown />\n </summary>\n {onSignOut ? (\n <div className=\"absolute right-0 z-30 mt-2 w-44 overflow-hidden rounded-xl border border-[color:var(--line)] bg-[color:var(--panel-elevated)] py-1 shadow-[var(--shadow-soft)]\">\n <button\n type=\"button\"\n onClick={onSignOut}\n className=\"flex w-full items-center gap-2.5 px-4 py-2 text-left text-sm font-medium text-[color:var(--foreground)] hover:bg-[color:var(--panel)] hover:text-[color:var(--color-race-red)]\"\n >\n <SignOutIcon />\n Sign out\n </button>\n </div>\n ) : null}\n </details>\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-[240px]\">\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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@the-portland-company/shell",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Shared chrome (header, sidebar, footer, theme, auth) for politogy apps. Drop into any Vite SPA to inherit the politogy look and signed-in user.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"private": false,
|