@the-portland-company/shell 0.5.0 → 0.7.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 +30 -0
- package/dist/native.cjs +339 -36
- package/dist/native.cjs.map +1 -1
- package/dist/native.d.cts +83 -5
- package/dist/native.d.ts +83 -5
- package/dist/native.js +339 -37
- package/dist/native.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.0 — 2026-07-01
|
|
4
|
+
|
|
5
|
+
### Added (native chrome — all backward-compatible)
|
|
6
|
+
|
|
7
|
+
- **Expandable nav groups**: `NavItem` gains optional `children?: NavItem[]`. A parent with children renders as an accordion group header — clicking toggles (never navigates, even with `href`; the href only feeds active detection), a right-aligned chevron rotates 180° when open, the sidebar keeps a single group open at a time, and the group containing the active route auto-expands on route change. Sub-items render as smaller nav rows on a lighter panel through the same injected `LinkComponent` (with `onClick` button support), with the main app's exact treatments: parent solid race-red when itself-or-a-child is active, active sub-item on elevated panel with race-red text, inactive rows keep the orange→red hover gradient sweep. Flat menus are byte-identical to before.
|
|
8
|
+
- **`standardPolitogyNav`** (new export, with `StandardNavOptions` / `StandardNavIconKey`): the canonical Politogy VRM menu — Dashboard, Contacts, Email Broadcast, SMS/MMS Blast, Calls to Action, Forms & Surveys, Support — encoded once in the shell with core-relative routes absolutized against `coreBaseUrl`. Apps map icon slots to their own icon library, and an app that owns an item nests its local pages via `contactsItem: { href, children }` (used verbatim, not absolutized) while everything else links out to VRM core. `supportItem` supplies the Support handler (`onClick`/`href`) or omits the row.
|
|
9
|
+
|
|
10
|
+
### Tests
|
|
11
|
+
|
|
12
|
+
- Group behavior: render-when-expanded, toggle, auto-expand on active child, single-open accordion (6 tests).
|
|
13
|
+
- `standardPolitogyNav`: order/wording, absolutization, href-less CTA/Forms parents, default vs local Contacts children, Support variants, icon mapping (7 tests).
|
|
14
|
+
|
|
15
|
+
## 0.6.0 — 2026-07-01
|
|
16
|
+
|
|
17
|
+
### Added (native chrome — all backward-compatible)
|
|
18
|
+
|
|
19
|
+
Chrome-parity upgrades to the `./native` module (`NavSidebar`/`NavTopBar`/`NavAppShell`) so the dependency-light chrome is a faithful twin of the main VRM app's Chakra chrome. No Chakra components touched.
|
|
20
|
+
|
|
21
|
+
- **`NavFooter`** gains optional `commitDate?: string` and `commitAuthor?: string`. The footer now renders the main app's two-row build block: row 1 is `[env chip] | {commit last-4} | v{version}` (env chip is a color-coded capsule via the extended `envChipClasses`), row 2 (only when date/author present) is `| {Mon D} | {firstName} |` in mono.
|
|
22
|
+
- **`NavOrg`** gains optional `id?: string | null`. When present, the "Organization" label row shows a right-aligned `...{last-8}` copy button that writes the full id to the clipboard (`navigator.clipboard.writeText`) with a brief "Copied" affordance.
|
|
23
|
+
- **`NavItem`** gains optional `onClick?: () => void` (`href` is now optional). onClick items render as a `<button>` styled identically to the link rows — used for a sidebar "Support" action.
|
|
24
|
+
- **`NavTopBarProps`** gains optional `actionsList?: NavTopBarAction[]` (new exported `NavTopBarAction` type). Each renders as a 32×32 circular icon button (20px icon) with an optional red count badge (`badge > 0`), `warning`/`danger` tones coloring the icon red, and `href` support (external links open in a new tab). Rendered before the existing `actions` ReactNode slot (kept for back-compat).
|
|
25
|
+
- **Nav hover gradient sweep**: inactive sidebar nav rows (links and buttons) animate a left→right `linear-gradient(90deg,#ea6a3a,#E63946)` width 0→100% over 300ms with white text on hover; active rows keep solid race-red. No layout shift.
|
|
26
|
+
- **Org pill default color** changed from dark slate `#2d2f36` to race red (`var(--color-race-red)`, `#E63946`), matching the main app's fallback.
|
|
27
|
+
- `envChipClasses` extended: production red, staging orange, local/dev green, unknown gray.
|
|
28
|
+
|
|
29
|
+
### Tests
|
|
30
|
+
|
|
31
|
+
- Added `__tests__/native.test.tsx` covering `actionsList` badge/href rendering, org-id copy, onClick nav items, footer rows, and `envChipClasses` tones (6 tests; suite now 49).
|
|
32
|
+
|
|
3
33
|
## 0.3.5 — 2026-05-14
|
|
4
34
|
|
|
5
35
|
### Fixed
|
package/dist/native.cjs
CHANGED
|
@@ -1,21 +1,111 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var react = require('react');
|
|
3
4
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
5
|
|
|
5
6
|
// src/native/index.tsx
|
|
7
|
+
|
|
8
|
+
// src/native/standardNav.ts
|
|
9
|
+
var CONTACTS_DEFAULT_CHILDREN = [
|
|
10
|
+
{ label: "Dashboard", icon: "grid", path: "/relationship/contacts/dashboard" },
|
|
11
|
+
{ label: "All Contacts", icon: "list", path: "/relationship/contacts/all" },
|
|
12
|
+
{ label: "Tribes", icon: "users", path: "/relationship/contacts/groups" },
|
|
13
|
+
{ label: "Import", icon: "upload", path: "/relationship/contacts/import" },
|
|
14
|
+
{ label: "Import History", icon: "fileText", path: "/relationship/contacts/import-history" },
|
|
15
|
+
{ label: "Merge Rules", icon: "layout", path: "/relationship/contacts/merge-rules" }
|
|
16
|
+
];
|
|
17
|
+
var EMAIL_CHILDREN = [
|
|
18
|
+
{ label: "Dashboard", icon: "grid", path: "/relationship/email/dashboard" },
|
|
19
|
+
{ label: "Create & Edit", icon: "edit", path: "/relationship/email/create-and-edit" },
|
|
20
|
+
{ label: "Sender Information", icon: "mail", path: "/relationship/email/inbox" },
|
|
21
|
+
{ label: "Recipients", icon: "user", path: "/relationship/email/send-to" },
|
|
22
|
+
{ label: "Send & Automate", icon: "clock", path: "/relationship/email/send-and-automate" },
|
|
23
|
+
{ label: "Statistics", icon: "barChart", path: "/relationship/email/statistics" }
|
|
24
|
+
];
|
|
25
|
+
var SMS_CHILDREN = [
|
|
26
|
+
{ label: "Dashboard", icon: "grid", path: "/relationship/sms-mms-blast/dashboard" },
|
|
27
|
+
{ label: "Compose & Send", icon: "send", path: "/relationship/sms-mms-blast/compose-and-send" },
|
|
28
|
+
{ label: "Conversations", icon: "messageSquare", path: "/relationship/sms-mms-blast/conversations" }
|
|
29
|
+
];
|
|
30
|
+
var CTA_CHILDREN = [
|
|
31
|
+
{ label: "Dashboard", icon: "grid", path: "/relationship/cta-database/dashboard" },
|
|
32
|
+
{ label: "Create & Edit", icon: "edit", path: "/relationship/cta-creator/create-and-edit" },
|
|
33
|
+
{ label: "Submissions", icon: "fileText", path: "/relationship/cta-submissions/submissions" }
|
|
34
|
+
];
|
|
35
|
+
var FORMS_CHILDREN = [
|
|
36
|
+
{ label: "Dashboard", icon: "grid", path: "/relationship/forms" },
|
|
37
|
+
{ label: "Create & Edit", icon: "edit", path: "/relationship/forms/create-and-edit" },
|
|
38
|
+
{ label: "Submissions", icon: "fileText", path: "/relationship/forms/submissions" }
|
|
39
|
+
];
|
|
40
|
+
function standardPolitogyNav(opts) {
|
|
41
|
+
const base = opts.coreBaseUrl.replace(/\/+$/, "");
|
|
42
|
+
const abs = (path) => `${base}${path.startsWith("/") ? path : `/${path}`}`;
|
|
43
|
+
const icon = (key) => opts.icons?.[key];
|
|
44
|
+
const leaves = (children) => children.map((c) => ({ label: c.label, icon: icon(c.icon), href: abs(c.path) }));
|
|
45
|
+
const contacts = opts.contactsItem ? {
|
|
46
|
+
label: "Contacts",
|
|
47
|
+
icon: icon("users"),
|
|
48
|
+
href: opts.contactsItem.href,
|
|
49
|
+
children: opts.contactsItem.children
|
|
50
|
+
} : {
|
|
51
|
+
label: "Contacts",
|
|
52
|
+
icon: icon("users"),
|
|
53
|
+
href: abs("/relationship/contacts/dashboard"),
|
|
54
|
+
children: leaves(CONTACTS_DEFAULT_CHILDREN)
|
|
55
|
+
};
|
|
56
|
+
const items = [
|
|
57
|
+
{ label: "Dashboard", icon: icon("home"), href: abs("/relationship/dashboard") },
|
|
58
|
+
contacts,
|
|
59
|
+
{
|
|
60
|
+
label: "Email Broadcast",
|
|
61
|
+
icon: icon("mail"),
|
|
62
|
+
href: abs("/relationship/email/dashboard"),
|
|
63
|
+
children: leaves(EMAIL_CHILDREN)
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
label: "SMS/MMS Blast",
|
|
67
|
+
icon: icon("messageSquare"),
|
|
68
|
+
href: abs("/relationship/sms-mms-blast/dashboard"),
|
|
69
|
+
children: leaves(SMS_CHILDREN)
|
|
70
|
+
},
|
|
71
|
+
// These two carry no top-level href in VRM core (parent row only toggles).
|
|
72
|
+
{ label: "Calls to Action", icon: icon("layout"), children: leaves(CTA_CHILDREN) },
|
|
73
|
+
{ label: "Forms & Surveys", icon: icon("activity"), children: leaves(FORMS_CHILDREN) }
|
|
74
|
+
];
|
|
75
|
+
if (opts.supportItem) {
|
|
76
|
+
items.push({
|
|
77
|
+
label: "Support",
|
|
78
|
+
icon: icon("lifeBuoy"),
|
|
79
|
+
href: opts.supportItem.href,
|
|
80
|
+
onClick: opts.supportItem.onClick
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return items;
|
|
84
|
+
}
|
|
6
85
|
var DefaultLink = ({ href, children, ...rest }) => /* @__PURE__ */ jsxRuntime.jsx("a", { href, ...rest, children });
|
|
7
86
|
function isNavItemActive(pathname, href) {
|
|
8
87
|
if (href === "/") return pathname === "/";
|
|
9
88
|
return pathname === href || pathname.startsWith(`${href}/`);
|
|
10
89
|
}
|
|
90
|
+
function groupKeyOf(item, index) {
|
|
91
|
+
return item.href ?? `nav-group-${index}-${item.label}`;
|
|
92
|
+
}
|
|
93
|
+
function hasActiveChild(item, pathname, activeFn) {
|
|
94
|
+
return (item.children ?? []).some(
|
|
95
|
+
(child) => child.href ? activeFn(pathname, child.href) : false
|
|
96
|
+
);
|
|
97
|
+
}
|
|
11
98
|
function envChipClasses(label) {
|
|
12
99
|
const l = label.toLowerCase();
|
|
13
|
-
if (l === "production")
|
|
14
|
-
return "bg-[
|
|
15
|
-
if (l === "staging"
|
|
16
|
-
|
|
100
|
+
if (l === "production" || l === "prod")
|
|
101
|
+
return "bg-[#fee2e2] text-[#ef4444]";
|
|
102
|
+
if (l === "staging" || l === "stage")
|
|
103
|
+
return "bg-[#ffedd5] text-[#f97316]";
|
|
104
|
+
if (l === "local" || l === "development" || l === "dev")
|
|
105
|
+
return "bg-[#dcfce7] text-[#22c55e]";
|
|
106
|
+
return "bg-[rgba(107,114,128,0.16)] text-[#6b7280]";
|
|
17
107
|
}
|
|
18
|
-
var ORG_PILL_DEFAULT = "
|
|
108
|
+
var ORG_PILL_DEFAULT = "var(--color-race-red)";
|
|
19
109
|
function NavSidebar({
|
|
20
110
|
pathname,
|
|
21
111
|
brand,
|
|
@@ -29,6 +119,20 @@ function NavSidebar({
|
|
|
29
119
|
const activeFn = nav.isActive ?? isNavItemActive;
|
|
30
120
|
const ModeIcon = mode?.icon;
|
|
31
121
|
const orgBg = org?.brandColor || ORG_PILL_DEFAULT;
|
|
122
|
+
const activeGroupKey = react.useMemo(() => {
|
|
123
|
+
for (let i = 0; i < nav.items.length; i++) {
|
|
124
|
+
const item = nav.items[i];
|
|
125
|
+
if (item.children && item.children.length > 0 && hasActiveChild(item, pathname, activeFn)) {
|
|
126
|
+
return groupKeyOf(item, i);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}, [nav.items, pathname, activeFn]);
|
|
131
|
+
const [openGroup, setOpenGroup] = react.useState(activeGroupKey);
|
|
132
|
+
react.useEffect(() => {
|
|
133
|
+
setOpenGroup(activeGroupKey);
|
|
134
|
+
}, [activeGroupKey]);
|
|
135
|
+
const toggleGroup = (key) => setOpenGroup((prev) => prev === key ? null : key);
|
|
32
136
|
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
137
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
34
138
|
Link,
|
|
@@ -54,7 +158,10 @@ function NavSidebar({
|
|
|
54
158
|
),
|
|
55
159
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col overflow-y-auto px-4 pb-2", children: [
|
|
56
160
|
org ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-3", children: [
|
|
57
|
-
/* @__PURE__ */ jsxRuntime.
|
|
161
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2 flex items-center justify-between px-1", children: [
|
|
162
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Organization" }),
|
|
163
|
+
org.id ? /* @__PURE__ */ jsxRuntime.jsx(OrgIdCopyButton, { id: org.id }) : null
|
|
164
|
+
] }),
|
|
58
165
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
59
166
|
"button",
|
|
60
167
|
{
|
|
@@ -88,30 +195,34 @@ function NavSidebar({
|
|
|
88
195
|
) }) : null,
|
|
89
196
|
/* @__PURE__ */ jsxRuntime.jsxs("nav", { className: "flex flex-col gap-1", children: [
|
|
90
197
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
198
|
+
nav.items.map((item, idx) => {
|
|
199
|
+
if (item.children && item.children.length > 0) {
|
|
200
|
+
const key = groupKeyOf(item, idx);
|
|
201
|
+
const selfActive = item.href ? activeFn(pathname, item.href) : false;
|
|
202
|
+
const parentActive = selfActive || hasActiveChild(item, pathname, activeFn);
|
|
203
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
204
|
+
NavGroup,
|
|
205
|
+
{
|
|
206
|
+
item,
|
|
207
|
+
active: parentActive,
|
|
208
|
+
open: openGroup === key,
|
|
209
|
+
onToggle: () => toggleGroup(key),
|
|
210
|
+
pathname,
|
|
211
|
+
activeFn,
|
|
212
|
+
Link
|
|
213
|
+
},
|
|
214
|
+
key
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
const active = item.href ? activeFn(pathname, item.href) : false;
|
|
218
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
219
|
+
NavRow,
|
|
96
220
|
{
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
]
|
|
221
|
+
item,
|
|
222
|
+
active,
|
|
223
|
+
Link
|
|
113
224
|
},
|
|
114
|
-
item.href
|
|
225
|
+
item.href ?? `nav-${idx}-${item.label}`
|
|
115
226
|
);
|
|
116
227
|
})
|
|
117
228
|
] })
|
|
@@ -128,17 +239,41 @@ function NavSidebar({
|
|
|
128
239
|
}
|
|
129
240
|
)
|
|
130
241
|
] }, l.label)) }) : null,
|
|
131
|
-
/* @__PURE__ */ jsxRuntime.jsxs("
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
242
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1.5 flex flex-col items-center gap-0.5 font-[family-name:var(--font-plex-mono)] text-[10px] text-[color:var(--muted)]", children: [
|
|
243
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
244
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
245
|
+
"span",
|
|
246
|
+
{
|
|
247
|
+
className: [
|
|
248
|
+
"rounded-md px-1.5 py-0.5 text-[10px] font-semibold",
|
|
249
|
+
envChipClasses(footer.envLabel)
|
|
250
|
+
].join(" "),
|
|
251
|
+
children: footer.envLabel
|
|
252
|
+
}
|
|
253
|
+
),
|
|
254
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[color:var(--muted)]", children: "|" }),
|
|
255
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: footer.commit.slice(-4) }),
|
|
256
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[color:var(--muted)]", children: "|" }),
|
|
257
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
258
|
+
"v",
|
|
259
|
+
footer.version
|
|
260
|
+
] })
|
|
261
|
+
] }),
|
|
262
|
+
footer.commitDate || footer.commitAuthor ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
263
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[color:var(--muted)]", children: "|" }),
|
|
264
|
+
footer.commitDate ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: new Date(footer.commitDate).toLocaleDateString(void 0, {
|
|
265
|
+
month: "short",
|
|
266
|
+
day: "numeric"
|
|
267
|
+
}) }) : null,
|
|
268
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[color:var(--muted)]", children: "|" }),
|
|
269
|
+
footer.commitAuthor ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: footer.commitAuthor.split(/\s+/)[0] }) : null,
|
|
270
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[color:var(--muted)]", children: "|" })
|
|
271
|
+
] }) : null
|
|
137
272
|
] })
|
|
138
273
|
] }) : null
|
|
139
274
|
] });
|
|
140
275
|
}
|
|
141
|
-
function ChevronDown() {
|
|
276
|
+
function ChevronDown({ className } = {}) {
|
|
142
277
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
143
278
|
"svg",
|
|
144
279
|
{
|
|
@@ -150,17 +285,145 @@ function ChevronDown() {
|
|
|
150
285
|
strokeWidth: 2,
|
|
151
286
|
strokeLinecap: "round",
|
|
152
287
|
strokeLinejoin: "round",
|
|
153
|
-
className: "shrink-0 opacity-80",
|
|
288
|
+
className: className ?? "shrink-0 opacity-80",
|
|
154
289
|
"aria-hidden": "true",
|
|
155
290
|
children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m6 9 6 6 6-6" })
|
|
156
291
|
}
|
|
157
292
|
);
|
|
158
293
|
}
|
|
294
|
+
var NAV_HOVER_GRADIENT = "linear-gradient(90deg,#ea6a3a 0%,#E63946 100%)";
|
|
295
|
+
function NavRow({
|
|
296
|
+
item,
|
|
297
|
+
active,
|
|
298
|
+
Link,
|
|
299
|
+
variant = "top"
|
|
300
|
+
}) {
|
|
301
|
+
const Icon = item.icon;
|
|
302
|
+
const isSub = variant === "sub";
|
|
303
|
+
const activeClasses = isSub ? "bg-[color:var(--panel-elevated)] text-[color:var(--color-race-red)] shadow-sm" : "bg-[color:var(--color-race-red)] text-white shadow-sm";
|
|
304
|
+
const wrapperClasses = [
|
|
305
|
+
"group relative block overflow-hidden rounded-md transition",
|
|
306
|
+
active ? activeClasses : "text-[color:var(--muted)] hover:text-white"
|
|
307
|
+
].join(" ");
|
|
308
|
+
const rowPadding = isSub ? "px-3 py-1.5 text-[13px]" : "px-3 py-2 text-sm";
|
|
309
|
+
const iconSize = isSub ? 16 : 18;
|
|
310
|
+
const iconClass = active && !isSub ? "text-white" : "text-current";
|
|
311
|
+
const inner = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
312
|
+
!active ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
313
|
+
"span",
|
|
314
|
+
{
|
|
315
|
+
"aria-hidden": "true",
|
|
316
|
+
className: "pointer-events-none absolute inset-y-0 left-0 w-0 transition-[width] duration-300 group-hover:w-full",
|
|
317
|
+
style: { background: NAV_HOVER_GRADIENT }
|
|
318
|
+
}
|
|
319
|
+
) : null,
|
|
320
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: `relative z-10 flex items-center gap-3 font-semibold ${rowPadding}`, children: [
|
|
321
|
+
Icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { size: iconSize, className: iconClass }) : null,
|
|
322
|
+
item.label
|
|
323
|
+
] })
|
|
324
|
+
] });
|
|
325
|
+
if (item.onClick && !item.href) {
|
|
326
|
+
return /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: item.onClick, className: `w-full text-left ${wrapperClasses}`, children: inner });
|
|
327
|
+
}
|
|
328
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
329
|
+
Link,
|
|
330
|
+
{
|
|
331
|
+
href: item.href ?? "#",
|
|
332
|
+
"aria-current": active ? "page" : void 0,
|
|
333
|
+
className: wrapperClasses,
|
|
334
|
+
children: inner
|
|
335
|
+
}
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
function NavGroup({
|
|
339
|
+
item,
|
|
340
|
+
active,
|
|
341
|
+
open,
|
|
342
|
+
onToggle,
|
|
343
|
+
pathname,
|
|
344
|
+
activeFn,
|
|
345
|
+
Link
|
|
346
|
+
}) {
|
|
347
|
+
const Icon = item.icon;
|
|
348
|
+
const headerClasses = [
|
|
349
|
+
"group relative block w-full overflow-hidden text-left transition",
|
|
350
|
+
open ? "rounded-t-md" : "rounded-md",
|
|
351
|
+
active ? "bg-[color:var(--color-race-red)] text-white shadow-sm" : open ? "bg-[color:var(--panel)] text-[color:var(--foreground)]" : "text-[color:var(--muted)] hover:text-white"
|
|
352
|
+
].join(" ");
|
|
353
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
354
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
355
|
+
"button",
|
|
356
|
+
{
|
|
357
|
+
type: "button",
|
|
358
|
+
onClick: onToggle,
|
|
359
|
+
"aria-expanded": open,
|
|
360
|
+
className: headerClasses,
|
|
361
|
+
children: [
|
|
362
|
+
!active ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
363
|
+
"span",
|
|
364
|
+
{
|
|
365
|
+
"aria-hidden": "true",
|
|
366
|
+
className: "pointer-events-none absolute inset-y-0 left-0 w-0 transition-[width] duration-300 group-hover:w-full",
|
|
367
|
+
style: { background: NAV_HOVER_GRADIENT }
|
|
368
|
+
}
|
|
369
|
+
) : null,
|
|
370
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative z-10 flex items-center gap-3 px-3 py-2 text-sm font-semibold", children: [
|
|
371
|
+
Icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { size: 18, className: active ? "text-white" : "text-current" }) : null,
|
|
372
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1", children: item.label }),
|
|
373
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
374
|
+
ChevronDown,
|
|
375
|
+
{
|
|
376
|
+
className: [
|
|
377
|
+
"shrink-0 opacity-80 transition-transform duration-200",
|
|
378
|
+
open ? "rotate-180" : ""
|
|
379
|
+
].join(" ")
|
|
380
|
+
}
|
|
381
|
+
)
|
|
382
|
+
] })
|
|
383
|
+
]
|
|
384
|
+
}
|
|
385
|
+
),
|
|
386
|
+
open ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-hidden rounded-b-md bg-[color:var(--panel)] p-1", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-0.5", children: item.children.map((child, i) => {
|
|
387
|
+
const childActive = child.href ? activeFn(pathname, child.href) : false;
|
|
388
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
389
|
+
NavRow,
|
|
390
|
+
{
|
|
391
|
+
item: child,
|
|
392
|
+
active: childActive,
|
|
393
|
+
Link,
|
|
394
|
+
variant: "sub"
|
|
395
|
+
},
|
|
396
|
+
child.href ?? `sub-${i}-${child.label}`
|
|
397
|
+
);
|
|
398
|
+
}) }) }) : null
|
|
399
|
+
] });
|
|
400
|
+
}
|
|
401
|
+
function OrgIdCopyButton({ id }) {
|
|
402
|
+
const [copied, setCopied] = react.useState(false);
|
|
403
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
404
|
+
"button",
|
|
405
|
+
{
|
|
406
|
+
type: "button",
|
|
407
|
+
onClick: () => {
|
|
408
|
+
try {
|
|
409
|
+
void navigator.clipboard?.writeText(id);
|
|
410
|
+
} catch {
|
|
411
|
+
}
|
|
412
|
+
setCopied(true);
|
|
413
|
+
setTimeout(() => setCopied(false), 1500);
|
|
414
|
+
},
|
|
415
|
+
title: "Copy organization id",
|
|
416
|
+
className: "rounded px-1 text-[11px] text-[color:var(--muted)] hover:bg-[color:var(--panel)]",
|
|
417
|
+
children: copied ? "Copied" : `...${id.slice(-8)}`
|
|
418
|
+
}
|
|
419
|
+
);
|
|
420
|
+
}
|
|
159
421
|
function NavTopBar({
|
|
160
422
|
title,
|
|
161
423
|
subtitle,
|
|
162
424
|
user,
|
|
163
425
|
onSignOut,
|
|
426
|
+
actionsList,
|
|
164
427
|
actions,
|
|
165
428
|
themeToggle
|
|
166
429
|
}) {
|
|
@@ -179,6 +442,7 @@ function NavTopBar({
|
|
|
179
442
|
subtitle ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate text-sm text-[color:var(--muted)]", children: subtitle }) : null
|
|
180
443
|
] }),
|
|
181
444
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
445
|
+
actionsList && actionsList.length > 0 ? actionsList.map((action) => /* @__PURE__ */ jsxRuntime.jsx(NavTopBarActionButton, { action }, action.key)) : null,
|
|
182
446
|
actions,
|
|
183
447
|
themeToggle,
|
|
184
448
|
/* @__PURE__ */ jsxRuntime.jsxs("details", { className: "group relative", children: [
|
|
@@ -227,6 +491,44 @@ function SignOutIcon() {
|
|
|
227
491
|
}
|
|
228
492
|
);
|
|
229
493
|
}
|
|
494
|
+
function NavTopBarActionButton({ action }) {
|
|
495
|
+
const Icon = action.icon;
|
|
496
|
+
const tone = action.tone ?? "default";
|
|
497
|
+
const toneClass = tone === "danger" || tone === "warning" ? "text-[color:var(--color-race-red)] hover:text-[color:var(--color-race-red)]" : "text-[color:var(--muted)] hover:text-[color:var(--foreground)]";
|
|
498
|
+
const className = [
|
|
499
|
+
"relative flex h-8 w-8 items-center justify-center rounded-full border border-[color:var(--line)] transition",
|
|
500
|
+
toneClass
|
|
501
|
+
].join(" ");
|
|
502
|
+
const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
503
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon, { size: 20, className: "text-current" }),
|
|
504
|
+
typeof action.badge === "number" && action.badge > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -top-1 -right-1 flex h-4 min-w-4 items-center justify-center rounded-full bg-[color:var(--color-race-red)] px-1 text-[10px] text-white", children: action.badge }) : null
|
|
505
|
+
] });
|
|
506
|
+
const isExternal = action.href ? /^https?:\/\//.test(action.href) : false;
|
|
507
|
+
if (action.href) {
|
|
508
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
509
|
+
"a",
|
|
510
|
+
{
|
|
511
|
+
href: action.href,
|
|
512
|
+
"aria-label": action.label,
|
|
513
|
+
title: action.label,
|
|
514
|
+
className,
|
|
515
|
+
...isExternal ? { target: "_blank", rel: "noreferrer" } : {},
|
|
516
|
+
children: content
|
|
517
|
+
}
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
521
|
+
"button",
|
|
522
|
+
{
|
|
523
|
+
type: "button",
|
|
524
|
+
onClick: action.onClick,
|
|
525
|
+
"aria-label": action.label,
|
|
526
|
+
title: action.label,
|
|
527
|
+
className,
|
|
528
|
+
children: content
|
|
529
|
+
}
|
|
530
|
+
);
|
|
531
|
+
}
|
|
230
532
|
function NavAppShell({ sidebar, topBar, children }) {
|
|
231
533
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-h-screen bg-[color:var(--background)] text-[color:var(--foreground)]", children: [
|
|
232
534
|
/* @__PURE__ */ jsxRuntime.jsx(NavSidebar, { ...sidebar }),
|
|
@@ -242,5 +544,6 @@ exports.NavSidebar = NavSidebar;
|
|
|
242
544
|
exports.NavTopBar = NavTopBar;
|
|
243
545
|
exports.envChipClasses = envChipClasses;
|
|
244
546
|
exports.isNavItemActive = isNavItemActive;
|
|
547
|
+
exports.standardPolitogyNav = standardPolitogyNav;
|
|
245
548
|
//# sourceMappingURL=native.cjs.map
|
|
246
549
|
//# sourceMappingURL=native.cjs.map
|