@the-portland-company/shell 0.14.3 → 0.16.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/chrome.css +53 -0
- package/dist/native.cjs +758 -29
- package/dist/native.cjs.map +1 -1
- package/dist/native.d.cts +152 -3
- package/dist/native.d.ts +152 -3
- package/dist/native.js +751 -32
- package/dist/native.js.map +1 -1
- package/package.json +1 -1
package/dist/native.cjs
CHANGED
|
@@ -52,6 +52,534 @@ function resolveBrandGlyph(name) {
|
|
|
52
52
|
if (!name) return null;
|
|
53
53
|
return GLYPHS[name.trim().toLowerCase()] ?? null;
|
|
54
54
|
}
|
|
55
|
+
function topBarIconButtonClass(tone = "default") {
|
|
56
|
+
const toneClass = tone === "danger" || tone === "warning" ? "text-[color:var(--color-race-red)] hover:text-[color:var(--color-race-red)]" : "text-[color:var(--topbar-icon-color,var(--muted))] hover:text-[color:var(--topbar-icon-hover-color,var(--foreground))] hover:bg-[color:var(--topbar-icon-hover-bg,transparent)]";
|
|
57
|
+
return [
|
|
58
|
+
// `group/tbtip` powers the shared <TopBarTooltip> rendered inside the trigger.
|
|
59
|
+
"group/tbtip relative flex items-center justify-center rounded-full border border-[color:var(--line)] transition",
|
|
60
|
+
"h-[var(--topbar-icon-button-size,32px)] w-[var(--topbar-icon-button-size,32px)]",
|
|
61
|
+
toneClass
|
|
62
|
+
].join(" ");
|
|
63
|
+
}
|
|
64
|
+
function TopBarCountBadge({ count }) {
|
|
65
|
+
if (!(count > 0)) return null;
|
|
66
|
+
return /* @__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: count > 99 ? "99+" : count });
|
|
67
|
+
}
|
|
68
|
+
function TopBarTooltip({
|
|
69
|
+
title,
|
|
70
|
+
description
|
|
71
|
+
}) {
|
|
72
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
73
|
+
"span",
|
|
74
|
+
{
|
|
75
|
+
role: "tooltip",
|
|
76
|
+
className: "pointer-events-none absolute left-1/2 top-full z-50 mt-2 hidden w-max max-w-56 -translate-x-1/2 flex-col rounded-lg border border-[color:var(--line)] bg-[color:var(--panel-elevated)] px-3 py-2 text-left shadow-[var(--shadow-soft)] group-hover/tbtip:flex",
|
|
77
|
+
children: [
|
|
78
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
79
|
+
"span",
|
|
80
|
+
{
|
|
81
|
+
"aria-hidden": true,
|
|
82
|
+
className: "absolute -top-[5px] left-1/2 h-2.5 w-2.5 -translate-x-1/2 rotate-45 border-l border-t border-[color:var(--line)] bg-[color:var(--panel-elevated)]"
|
|
83
|
+
}
|
|
84
|
+
),
|
|
85
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold text-[color:var(--foreground)]", children: title }),
|
|
86
|
+
description ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-0.5 text-[11px] leading-snug text-[color:var(--muted)]", children: description }) : null
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
function TopBarWidgetSlot({
|
|
92
|
+
title,
|
|
93
|
+
description,
|
|
94
|
+
children
|
|
95
|
+
}) {
|
|
96
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "topbar-widget-slot group/tbtip relative inline-flex", children: [
|
|
97
|
+
children,
|
|
98
|
+
/* @__PURE__ */ jsxRuntime.jsx(TopBarTooltip, { title, description })
|
|
99
|
+
] });
|
|
100
|
+
}
|
|
101
|
+
var THEME_OPTIONS = [
|
|
102
|
+
{ value: "light", label: "Light" },
|
|
103
|
+
{ value: "system", label: "System" },
|
|
104
|
+
{ value: "dark", label: "Dark" }
|
|
105
|
+
];
|
|
106
|
+
function NavThemeControl({
|
|
107
|
+
value,
|
|
108
|
+
onChange
|
|
109
|
+
}) {
|
|
110
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-3", children: [
|
|
111
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Appearance" }),
|
|
112
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
113
|
+
"div",
|
|
114
|
+
{
|
|
115
|
+
role: "radiogroup",
|
|
116
|
+
"aria-label": "Appearance",
|
|
117
|
+
className: "flex overflow-hidden rounded-full border border-[color:var(--line)]",
|
|
118
|
+
children: THEME_OPTIONS.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
119
|
+
"button",
|
|
120
|
+
{
|
|
121
|
+
type: "button",
|
|
122
|
+
role: "radio",
|
|
123
|
+
"aria-checked": value === opt.value,
|
|
124
|
+
onClick: () => onChange(opt.value),
|
|
125
|
+
className: [
|
|
126
|
+
"flex-1 px-3 py-1.5 text-xs font-semibold transition",
|
|
127
|
+
value === opt.value ? "bg-[color:var(--color-race-red)] text-white" : "text-[color:var(--muted)] hover:bg-[color:var(--panel)] hover:text-[color:var(--foreground)]"
|
|
128
|
+
].join(" "),
|
|
129
|
+
children: opt.label
|
|
130
|
+
},
|
|
131
|
+
opt.value
|
|
132
|
+
))
|
|
133
|
+
}
|
|
134
|
+
)
|
|
135
|
+
] });
|
|
136
|
+
}
|
|
137
|
+
var NavChromeContext = react.createContext(null);
|
|
138
|
+
function NavChromeProvider({
|
|
139
|
+
fetchJson,
|
|
140
|
+
children
|
|
141
|
+
}) {
|
|
142
|
+
return /* @__PURE__ */ jsxRuntime.jsx(NavChromeContext.Provider, { value: { fetchJson }, children });
|
|
143
|
+
}
|
|
144
|
+
function useNavChrome(component) {
|
|
145
|
+
const ctx = react.useContext(NavChromeContext);
|
|
146
|
+
if (!ctx) {
|
|
147
|
+
throw new Error(
|
|
148
|
+
`${component} must be rendered inside <NavChromeProvider> (inject the chrome API fetcher there).`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
return ctx;
|
|
152
|
+
}
|
|
153
|
+
function useChromeOrgs() {
|
|
154
|
+
const { fetchJson } = useNavChrome("useChromeOrgs");
|
|
155
|
+
const [orgs, setOrgs] = react.useState(null);
|
|
156
|
+
const [loading, setLoading] = react.useState(true);
|
|
157
|
+
const [error, setError] = react.useState(false);
|
|
158
|
+
react.useEffect(() => {
|
|
159
|
+
let cancelled = false;
|
|
160
|
+
setLoading(true);
|
|
161
|
+
setError(false);
|
|
162
|
+
fetchJson("orgs").then((data) => {
|
|
163
|
+
if (cancelled) return;
|
|
164
|
+
const list = data?.orgs ?? [];
|
|
165
|
+
setOrgs(list);
|
|
166
|
+
}).catch(() => {
|
|
167
|
+
if (!cancelled) setError(true);
|
|
168
|
+
}).finally(() => {
|
|
169
|
+
if (!cancelled) setLoading(false);
|
|
170
|
+
});
|
|
171
|
+
return () => {
|
|
172
|
+
cancelled = true;
|
|
173
|
+
};
|
|
174
|
+
}, [fetchJson]);
|
|
175
|
+
return { orgs, loading, error };
|
|
176
|
+
}
|
|
177
|
+
function ValueSkeleton({ className }) {
|
|
178
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
179
|
+
"span",
|
|
180
|
+
{
|
|
181
|
+
"aria-hidden": true,
|
|
182
|
+
className: [
|
|
183
|
+
"inline-block animate-pulse rounded bg-[color:var(--panel-elevated)]",
|
|
184
|
+
className ?? "h-4 w-16"
|
|
185
|
+
].join(" ")
|
|
186
|
+
}
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
function SlotGlyph({
|
|
190
|
+
slot,
|
|
191
|
+
fallback: Fallback,
|
|
192
|
+
size = 20
|
|
193
|
+
}) {
|
|
194
|
+
const glyphName = useTopBarIconSlotGlyph(slot ?? "apps");
|
|
195
|
+
const BrandGlyph = slot ? resolveBrandGlyph(glyphName) : null;
|
|
196
|
+
const Glyph = BrandGlyph ?? Fallback;
|
|
197
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Glyph, { size, className: "text-current" });
|
|
198
|
+
}
|
|
199
|
+
function TopBarPopover({
|
|
200
|
+
label,
|
|
201
|
+
description,
|
|
202
|
+
slot,
|
|
203
|
+
fallbackIcon,
|
|
204
|
+
badge,
|
|
205
|
+
tone,
|
|
206
|
+
onOpen,
|
|
207
|
+
children,
|
|
208
|
+
panelClassName
|
|
209
|
+
}) {
|
|
210
|
+
const [open, setOpen] = react.useState(false);
|
|
211
|
+
const rootRef = react.useRef(null);
|
|
212
|
+
react.useEffect(() => {
|
|
213
|
+
if (!open) return;
|
|
214
|
+
const onDocClick = (e) => {
|
|
215
|
+
if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false);
|
|
216
|
+
};
|
|
217
|
+
const onKey = (e) => {
|
|
218
|
+
if (e.key === "Escape") setOpen(false);
|
|
219
|
+
};
|
|
220
|
+
document.addEventListener("mousedown", onDocClick);
|
|
221
|
+
document.addEventListener("keydown", onKey);
|
|
222
|
+
return () => {
|
|
223
|
+
document.removeEventListener("mousedown", onDocClick);
|
|
224
|
+
document.removeEventListener("keydown", onKey);
|
|
225
|
+
};
|
|
226
|
+
}, [open]);
|
|
227
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: rootRef, className: "relative", children: [
|
|
228
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
229
|
+
"button",
|
|
230
|
+
{
|
|
231
|
+
type: "button",
|
|
232
|
+
"aria-label": label,
|
|
233
|
+
"aria-expanded": open,
|
|
234
|
+
className: topBarIconButtonClass(tone),
|
|
235
|
+
onClick: () => {
|
|
236
|
+
setOpen((v) => {
|
|
237
|
+
const next = !v;
|
|
238
|
+
if (next) onOpen?.();
|
|
239
|
+
return next;
|
|
240
|
+
});
|
|
241
|
+
},
|
|
242
|
+
children: [
|
|
243
|
+
/* @__PURE__ */ jsxRuntime.jsx(SlotGlyph, { slot, fallback: fallbackIcon }),
|
|
244
|
+
typeof badge === "number" ? /* @__PURE__ */ jsxRuntime.jsx(TopBarCountBadge, { count: badge }) : null,
|
|
245
|
+
!open ? /* @__PURE__ */ jsxRuntime.jsx(TopBarTooltip, { title: label, description }) : null
|
|
246
|
+
]
|
|
247
|
+
}
|
|
248
|
+
),
|
|
249
|
+
open ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
250
|
+
"div",
|
|
251
|
+
{
|
|
252
|
+
className: [
|
|
253
|
+
"absolute right-0 z-40 mt-2 overflow-hidden rounded-xl border border-[color:var(--line)] bg-[color:var(--panel-elevated)] shadow-[var(--shadow-soft)]",
|
|
254
|
+
panelClassName ?? "w-80"
|
|
255
|
+
].join(" "),
|
|
256
|
+
children
|
|
257
|
+
}
|
|
258
|
+
) : null
|
|
259
|
+
] });
|
|
260
|
+
}
|
|
261
|
+
function BatteryGlyph({ size = 20, className }) {
|
|
262
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", className, "aria-hidden": true, children: [
|
|
263
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "1", y: "6", width: "18", height: "12", rx: "2", ry: "2" }),
|
|
264
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "23", y1: "10", x2: "23", y2: "14" }),
|
|
265
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "5", y1: "10", x2: "5", y2: "14" }),
|
|
266
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "9", y1: "10", x2: "9", y2: "14" })
|
|
267
|
+
] });
|
|
268
|
+
}
|
|
269
|
+
function BellGlyph({ size = 20, className }) {
|
|
270
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", className, "aria-hidden": true, children: [
|
|
271
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9" }),
|
|
272
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M13.73 21a2 2 0 0 1-3.46 0" })
|
|
273
|
+
] });
|
|
274
|
+
}
|
|
275
|
+
function LifeBuoyGlyph({ size = 20, className }) {
|
|
276
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", className, "aria-hidden": true, children: [
|
|
277
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
278
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "4" }),
|
|
279
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "4.93", y1: "4.93", x2: "9.17", y2: "9.17" }),
|
|
280
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "14.83", y1: "14.83", x2: "19.07", y2: "19.07" }),
|
|
281
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "14.83", y1: "9.17", x2: "19.07", y2: "4.93" }),
|
|
282
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "4.93", y1: "19.07", x2: "9.17", y2: "14.83" })
|
|
283
|
+
] });
|
|
284
|
+
}
|
|
285
|
+
function LayoutGlyph({ size = 20, className }) {
|
|
286
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", className, "aria-hidden": true, children: [
|
|
287
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
|
|
288
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "9", x2: "21", y2: "9" }),
|
|
289
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "9", y1: "21", x2: "9", y2: "9" })
|
|
290
|
+
] });
|
|
291
|
+
}
|
|
292
|
+
function FuelRow({
|
|
293
|
+
label,
|
|
294
|
+
channel,
|
|
295
|
+
loading
|
|
296
|
+
}) {
|
|
297
|
+
const remaining = channel?.remainingUnits ?? 0;
|
|
298
|
+
const total = channel ? Math.max(channel.rawBalanceUnits, channel.remainingUnits) : 0;
|
|
299
|
+
const ratio = total > 0 ? Math.min(100, Math.max(0, remaining / total * 100)) : 0;
|
|
300
|
+
const low = channel?.lowFuel ?? false;
|
|
301
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full", children: [
|
|
302
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-1 flex items-center justify-between", children: [
|
|
303
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold text-[color:var(--foreground)]", children: label }),
|
|
304
|
+
loading ? /* @__PURE__ */ jsxRuntime.jsx(ValueSkeleton, { className: "h-3.5 w-14" }) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
305
|
+
"span",
|
|
306
|
+
{
|
|
307
|
+
className: [
|
|
308
|
+
"text-xs font-semibold tabular-nums",
|
|
309
|
+
low ? "text-[color:var(--color-race-red)]" : "text-[color:var(--muted)]"
|
|
310
|
+
].join(" "),
|
|
311
|
+
children: [
|
|
312
|
+
remaining.toLocaleString(),
|
|
313
|
+
" left"
|
|
314
|
+
]
|
|
315
|
+
}
|
|
316
|
+
)
|
|
317
|
+
] }),
|
|
318
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-1.5 w-full overflow-hidden rounded-full bg-[color:var(--panel)]", children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full w-full animate-pulse bg-[color:var(--line)]" }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
319
|
+
"div",
|
|
320
|
+
{
|
|
321
|
+
className: [
|
|
322
|
+
"h-full rounded-full transition-[width]",
|
|
323
|
+
low ? "bg-[color:var(--color-race-red)]" : "bg-[color:var(--color-ok,#00A651)]"
|
|
324
|
+
].join(" "),
|
|
325
|
+
style: { width: `${ratio}%` }
|
|
326
|
+
}
|
|
327
|
+
) })
|
|
328
|
+
] });
|
|
329
|
+
}
|
|
330
|
+
function FuelAction({ orgId }) {
|
|
331
|
+
const { fetchJson } = useNavChrome("FuelAction");
|
|
332
|
+
const [fuel, setFuel] = react.useState(null);
|
|
333
|
+
const [loading, setLoading] = react.useState(true);
|
|
334
|
+
const [error, setError] = react.useState(false);
|
|
335
|
+
react.useEffect(() => {
|
|
336
|
+
let cancelled = false;
|
|
337
|
+
setLoading(true);
|
|
338
|
+
setError(false);
|
|
339
|
+
setFuel(null);
|
|
340
|
+
fetchJson("communications/fuel-status", {
|
|
341
|
+
headers: orgId ? { "x-contacts-org": orgId } : void 0
|
|
342
|
+
}).then((data) => {
|
|
343
|
+
if (cancelled) return;
|
|
344
|
+
setFuel(data?.fuel ?? null);
|
|
345
|
+
}).catch(() => {
|
|
346
|
+
if (!cancelled) setError(true);
|
|
347
|
+
}).finally(() => {
|
|
348
|
+
if (!cancelled) setLoading(false);
|
|
349
|
+
});
|
|
350
|
+
return () => {
|
|
351
|
+
cancelled = true;
|
|
352
|
+
};
|
|
353
|
+
}, [fetchJson, orgId]);
|
|
354
|
+
const low = !!fuel && (fuel.signal.lowFuel || fuel.broadcast.lowFuel);
|
|
355
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
356
|
+
TopBarPopover,
|
|
357
|
+
{
|
|
358
|
+
label: "Communications Fuel",
|
|
359
|
+
description: "Remaining SMS and email sending balance for the active organization.",
|
|
360
|
+
slot: "comms-fuel",
|
|
361
|
+
fallbackIcon: BatteryGlyph,
|
|
362
|
+
tone: low ? "warning" : "default",
|
|
363
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-3", children: [
|
|
364
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-3 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Communications Fuel" }),
|
|
365
|
+
error || !loading && !fuel ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-[color:var(--muted)]", children: "Fuel balance is unavailable right now." }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
366
|
+
/* @__PURE__ */ jsxRuntime.jsx(FuelRow, { label: "SMS Signal", channel: fuel?.signal ?? null, loading }),
|
|
367
|
+
/* @__PURE__ */ jsxRuntime.jsx(FuelRow, { label: "Email Broadcast", channel: fuel?.broadcast ?? null, loading })
|
|
368
|
+
] })
|
|
369
|
+
] })
|
|
370
|
+
}
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
function NotificationsAction({ orgId }) {
|
|
374
|
+
const { fetchJson } = useNavChrome("NotificationsAction");
|
|
375
|
+
const [payload, setPayload] = react.useState(null);
|
|
376
|
+
const [loading, setLoading] = react.useState(true);
|
|
377
|
+
const [error, setError] = react.useState(false);
|
|
378
|
+
react.useEffect(() => {
|
|
379
|
+
let cancelled = false;
|
|
380
|
+
setLoading(true);
|
|
381
|
+
setError(false);
|
|
382
|
+
setPayload(null);
|
|
383
|
+
fetchJson("notifications", {
|
|
384
|
+
headers: orgId ? { "x-contacts-org": orgId } : void 0
|
|
385
|
+
}).then((data) => {
|
|
386
|
+
if (!cancelled) setPayload(data);
|
|
387
|
+
}).catch(() => {
|
|
388
|
+
if (!cancelled) setError(true);
|
|
389
|
+
}).finally(() => {
|
|
390
|
+
if (!cancelled) setLoading(false);
|
|
391
|
+
});
|
|
392
|
+
return () => {
|
|
393
|
+
cancelled = true;
|
|
394
|
+
};
|
|
395
|
+
}, [fetchJson, orgId]);
|
|
396
|
+
const count = (payload?.alerts.length ?? 0) + (payload?.replies.length ?? 0);
|
|
397
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
398
|
+
TopBarPopover,
|
|
399
|
+
{
|
|
400
|
+
label: "Alerts & Notifications",
|
|
401
|
+
description: "System alerts and recent activity for the active organization.",
|
|
402
|
+
slot: "notifications",
|
|
403
|
+
fallbackIcon: BellGlyph,
|
|
404
|
+
badge: loading ? 0 : count,
|
|
405
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-3", children: [
|
|
406
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Notifications" }),
|
|
407
|
+
loading ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 py-1", children: [
|
|
408
|
+
/* @__PURE__ */ jsxRuntime.jsx(ValueSkeleton, { className: "h-4 w-full" }),
|
|
409
|
+
/* @__PURE__ */ jsxRuntime.jsx(ValueSkeleton, { className: "h-4 w-3/4" })
|
|
410
|
+
] }) : error ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-[color:var(--muted)]", children: "Notifications are unavailable right now." }) : count === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "py-1 text-sm text-[color:var(--muted)]", children: "You\u2019re all caught up." }) : /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "flex max-h-80 flex-col gap-1 overflow-y-auto", children: [
|
|
411
|
+
payload?.alerts.map((a) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
412
|
+
"a",
|
|
413
|
+
{
|
|
414
|
+
href: a.href ?? "#",
|
|
415
|
+
className: "block rounded-lg px-2 py-2 hover:bg-[color:var(--panel)]",
|
|
416
|
+
children: [
|
|
417
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-[color:var(--foreground)]", children: a.title }),
|
|
418
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-[color:var(--muted)]", children: a.description })
|
|
419
|
+
]
|
|
420
|
+
}
|
|
421
|
+
) }, a.id)),
|
|
422
|
+
payload?.replies.map((r) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "rounded-lg px-2 py-2 hover:bg-[color:var(--panel)]", children: [
|
|
423
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-[color:var(--foreground)]", children: r.name }),
|
|
424
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate text-xs text-[color:var(--muted)]", children: r.preview })
|
|
425
|
+
] }, r.id))
|
|
426
|
+
] })
|
|
427
|
+
] })
|
|
428
|
+
}
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
function SupportAction() {
|
|
432
|
+
const { fetchJson } = useNavChrome("SupportAction");
|
|
433
|
+
const [open, setOpen] = react.useState(false);
|
|
434
|
+
const [message, setMessage] = react.useState("");
|
|
435
|
+
const [state, setState] = react.useState("idle");
|
|
436
|
+
const close = react.useCallback(() => {
|
|
437
|
+
setOpen(false);
|
|
438
|
+
setState("idle");
|
|
439
|
+
setMessage("");
|
|
440
|
+
}, []);
|
|
441
|
+
react.useEffect(() => {
|
|
442
|
+
if (!open) return;
|
|
443
|
+
const onKey = (e) => {
|
|
444
|
+
if (e.key === "Escape") close();
|
|
445
|
+
};
|
|
446
|
+
document.addEventListener("keydown", onKey);
|
|
447
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
448
|
+
}, [open, close]);
|
|
449
|
+
const submit = async () => {
|
|
450
|
+
if (!message.trim() || state === "sending") return;
|
|
451
|
+
setState("sending");
|
|
452
|
+
try {
|
|
453
|
+
await fetchJson("support", {
|
|
454
|
+
method: "POST",
|
|
455
|
+
headers: { "Content-Type": "application/json" },
|
|
456
|
+
body: JSON.stringify({ message: message.trim() })
|
|
457
|
+
});
|
|
458
|
+
setState("sent");
|
|
459
|
+
} catch {
|
|
460
|
+
setState("error");
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
464
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
465
|
+
"button",
|
|
466
|
+
{
|
|
467
|
+
type: "button",
|
|
468
|
+
"aria-label": "Support",
|
|
469
|
+
className: topBarIconButtonClass(),
|
|
470
|
+
onClick: () => setOpen(true),
|
|
471
|
+
children: [
|
|
472
|
+
/* @__PURE__ */ jsxRuntime.jsx(SlotGlyph, { slot: "support", fallback: LifeBuoyGlyph }),
|
|
473
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
474
|
+
TopBarTooltip,
|
|
475
|
+
{
|
|
476
|
+
title: "Support",
|
|
477
|
+
description: "Send a support request to the Politogy team."
|
|
478
|
+
}
|
|
479
|
+
)
|
|
480
|
+
]
|
|
481
|
+
}
|
|
482
|
+
),
|
|
483
|
+
open ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
484
|
+
"div",
|
|
485
|
+
{
|
|
486
|
+
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4",
|
|
487
|
+
role: "dialog",
|
|
488
|
+
"aria-modal": "true",
|
|
489
|
+
"aria-label": "Support request",
|
|
490
|
+
onMouseDown: (e) => {
|
|
491
|
+
if (e.target === e.currentTarget) close();
|
|
492
|
+
},
|
|
493
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full max-w-md rounded-2xl border border-[color:var(--line)] bg-[color:var(--panel-elevated)] p-6 shadow-[var(--shadow-soft)]", children: [
|
|
494
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-bold tracking-[-0.01em] text-[color:var(--foreground)]", children: "Contact Support" }),
|
|
495
|
+
state === "sent" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
496
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-3 text-sm text-[color:var(--muted)]", children: "Your request was sent. The team will reply by email." }),
|
|
497
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-5 flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
498
|
+
"button",
|
|
499
|
+
{
|
|
500
|
+
type: "button",
|
|
501
|
+
onClick: close,
|
|
502
|
+
className: "rounded-full bg-[color:var(--color-race-red)] px-4 py-2 text-sm font-semibold text-white hover:opacity-90",
|
|
503
|
+
children: "Done"
|
|
504
|
+
}
|
|
505
|
+
) })
|
|
506
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
507
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-[color:var(--muted)]", children: "Describe the problem or question. Your account details ride along automatically." }),
|
|
508
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
509
|
+
"textarea",
|
|
510
|
+
{
|
|
511
|
+
value: message,
|
|
512
|
+
onChange: (e) => setMessage(e.target.value),
|
|
513
|
+
rows: 5,
|
|
514
|
+
placeholder: "How can we help?",
|
|
515
|
+
className: "mt-4 w-full resize-y rounded-lg border border-[color:var(--line)] bg-[color:var(--panel)] px-3 py-2 text-sm text-[color:var(--foreground)] placeholder:text-[color:var(--muted)] focus:outline-none focus:ring-2 focus:ring-[color:var(--color-race-red)]"
|
|
516
|
+
}
|
|
517
|
+
),
|
|
518
|
+
state === "error" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm text-[color:var(--color-race-red)]", children: "Sending failed \u2014 try again." }) : null,
|
|
519
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 flex justify-end gap-2", children: [
|
|
520
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
521
|
+
"button",
|
|
522
|
+
{
|
|
523
|
+
type: "button",
|
|
524
|
+
onClick: close,
|
|
525
|
+
className: "rounded-full border border-[color:var(--line)] px-4 py-2 text-sm font-semibold text-[color:var(--foreground)] hover:bg-[color:var(--panel)]",
|
|
526
|
+
children: "Cancel"
|
|
527
|
+
}
|
|
528
|
+
),
|
|
529
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
530
|
+
"button",
|
|
531
|
+
{
|
|
532
|
+
type: "button",
|
|
533
|
+
onClick: submit,
|
|
534
|
+
disabled: !message.trim() || state === "sending",
|
|
535
|
+
className: "rounded-full bg-[color:var(--color-race-red)] px-4 py-2 text-sm font-semibold text-white transition hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50",
|
|
536
|
+
children: state === "sending" ? "Sending\u2026" : "Send request"
|
|
537
|
+
}
|
|
538
|
+
)
|
|
539
|
+
] })
|
|
540
|
+
] })
|
|
541
|
+
] })
|
|
542
|
+
}
|
|
543
|
+
) : null
|
|
544
|
+
] });
|
|
545
|
+
}
|
|
546
|
+
var NAV_DISPLAY_OPTIONS = [
|
|
547
|
+
{ value: "menu", label: "Sub Menu" },
|
|
548
|
+
{ value: "tabs", label: "Tabs Only" },
|
|
549
|
+
{ value: "both", label: "Both" }
|
|
550
|
+
];
|
|
551
|
+
function NavDisplayAction({
|
|
552
|
+
value,
|
|
553
|
+
onChange
|
|
554
|
+
}) {
|
|
555
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
556
|
+
TopBarPopover,
|
|
557
|
+
{
|
|
558
|
+
label: "Navigation Display",
|
|
559
|
+
description: "Show navigation as a sub-menu, tabs, or both.",
|
|
560
|
+
fallbackIcon: LayoutGlyph,
|
|
561
|
+
panelClassName: "w-44",
|
|
562
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-1", role: "radiogroup", "aria-label": "Navigation display", children: NAV_DISPLAY_OPTIONS.map((opt) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
563
|
+
"button",
|
|
564
|
+
{
|
|
565
|
+
type: "button",
|
|
566
|
+
role: "radio",
|
|
567
|
+
"aria-checked": value === opt.value,
|
|
568
|
+
onClick: () => onChange(opt.value),
|
|
569
|
+
className: [
|
|
570
|
+
"flex w-full items-center justify-between px-4 py-2 text-left text-sm font-medium",
|
|
571
|
+
value === opt.value ? "text-[color:var(--color-race-red)]" : "text-[color:var(--foreground)] hover:bg-[color:var(--panel)]"
|
|
572
|
+
].join(" "),
|
|
573
|
+
children: [
|
|
574
|
+
opt.label,
|
|
575
|
+
value === opt.value ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, children: "\u2713" }) : null
|
|
576
|
+
]
|
|
577
|
+
},
|
|
578
|
+
opt.value
|
|
579
|
+
)) })
|
|
580
|
+
}
|
|
581
|
+
);
|
|
582
|
+
}
|
|
55
583
|
|
|
56
584
|
// src/native/standardNav.ts
|
|
57
585
|
var CONTACTS_DEFAULT_CHILDREN = [
|
|
@@ -211,16 +739,67 @@ function NavSidebar({
|
|
|
211
739
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Organization" }),
|
|
212
740
|
org.id ? /* @__PURE__ */ jsxRuntime.jsx(OrgIdCopyButton, { id: org.id }) : null
|
|
213
741
|
] }),
|
|
214
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
742
|
+
org.organizations && org.organizations.length > 0 && org.onSwitch ? /* @__PURE__ */ jsxRuntime.jsxs("details", { className: "group relative", children: [
|
|
743
|
+
/* @__PURE__ */ jsxRuntime.jsxs("summary", { className: "flex w-full cursor-pointer list-none items-center justify-between gap-2 rounded-md border border-[color:var(--line)] bg-[color:var(--panel-elevated)] px-2.5 py-2 text-left text-[color:var(--foreground)] shadow-sm transition hover:bg-[color:var(--panel)] [&::-webkit-details-marker]:hidden", children: [
|
|
744
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-center gap-2.5", children: [
|
|
745
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
746
|
+
"span",
|
|
747
|
+
{
|
|
748
|
+
style: { backgroundColor: orgBg },
|
|
749
|
+
className: "flex h-7 w-7 shrink-0 items-center justify-center overflow-hidden rounded-full text-[11px] font-bold text-white",
|
|
750
|
+
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()
|
|
751
|
+
}
|
|
752
|
+
),
|
|
753
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-sm font-semibold", children: org.name ?? "No organizations" })
|
|
754
|
+
] }),
|
|
755
|
+
/* @__PURE__ */ jsxRuntime.jsx(ChevronDown, {})
|
|
756
|
+
] }),
|
|
757
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-0 right-0 z-40 mt-1 max-h-72 overflow-y-auto rounded-xl border border-[color:var(--line)] bg-[color:var(--panel-elevated)] py-1 shadow-[var(--shadow-soft)]", children: org.organizations.map((o) => {
|
|
758
|
+
const selected = o.id === org.id;
|
|
759
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
760
|
+
"button",
|
|
761
|
+
{
|
|
762
|
+
type: "button",
|
|
763
|
+
onClick: (e) => {
|
|
764
|
+
e.currentTarget.closest("details")?.removeAttribute("open");
|
|
765
|
+
if (!selected) org.onSwitch?.(o.id);
|
|
766
|
+
},
|
|
767
|
+
"aria-current": selected ? "true" : void 0,
|
|
768
|
+
className: [
|
|
769
|
+
"flex w-full items-center gap-2.5 px-3 py-2 text-left text-sm",
|
|
770
|
+
selected ? "font-semibold text-[color:var(--color-race-red)]" : "font-medium text-[color:var(--foreground)] hover:bg-[color:var(--panel)]"
|
|
771
|
+
].join(" "),
|
|
772
|
+
children: [
|
|
773
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
774
|
+
"span",
|
|
775
|
+
{
|
|
776
|
+
style: { backgroundColor: o.brandColor || ORG_PILL_DEFAULT },
|
|
777
|
+
className: "flex h-6 w-6 shrink-0 items-center justify-center overflow-hidden rounded-full text-[10px] font-bold text-white",
|
|
778
|
+
children: o.logoUrl ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: o.logoUrl, alt: "", className: "h-full w-full object-cover" }) : (o.name ?? "Org").slice(0, 2).toUpperCase()
|
|
779
|
+
}
|
|
780
|
+
),
|
|
781
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: o.name ?? o.id.slice(0, 8) })
|
|
782
|
+
]
|
|
783
|
+
},
|
|
784
|
+
o.id
|
|
785
|
+
);
|
|
786
|
+
}) })
|
|
787
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
215
788
|
"button",
|
|
216
789
|
{
|
|
217
790
|
type: "button",
|
|
218
791
|
onClick: org.onClick,
|
|
219
|
-
|
|
220
|
-
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",
|
|
792
|
+
className: "flex w-full items-center justify-between gap-2 rounded-md border border-[color:var(--line)] bg-[color:var(--panel-elevated)] px-2.5 py-2 text-left text-[color:var(--foreground)] shadow-sm transition hover:bg-[color:var(--panel)]",
|
|
221
793
|
children: [
|
|
222
794
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-center gap-2.5", children: [
|
|
223
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
795
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
796
|
+
"span",
|
|
797
|
+
{
|
|
798
|
+
style: { backgroundColor: orgBg },
|
|
799
|
+
className: "flex h-7 w-7 shrink-0 items-center justify-center overflow-hidden rounded-full text-[11px] font-bold text-white",
|
|
800
|
+
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()
|
|
801
|
+
}
|
|
802
|
+
),
|
|
224
803
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-sm font-semibold", children: org.name ?? "No organizations" })
|
|
225
804
|
] }),
|
|
226
805
|
/* @__PURE__ */ jsxRuntime.jsx(ChevronDown, {})
|
|
@@ -471,6 +1050,7 @@ function NavTopBar({
|
|
|
471
1050
|
title,
|
|
472
1051
|
subtitle,
|
|
473
1052
|
user,
|
|
1053
|
+
userMenu,
|
|
474
1054
|
onSignOut,
|
|
475
1055
|
actionsList,
|
|
476
1056
|
actions,
|
|
@@ -494,31 +1074,176 @@ function NavTopBar({
|
|
|
494
1074
|
actionsList && actionsList.length > 0 ? actionsList.map((action) => /* @__PURE__ */ jsxRuntime.jsx(NavTopBarActionButton, { action }, action.key)) : null,
|
|
495
1075
|
actions,
|
|
496
1076
|
themeToggle,
|
|
497
|
-
/* @__PURE__ */ jsxRuntime.jsxs("details", { className: "group relative", children: [
|
|
498
|
-
/* @__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: [
|
|
1077
|
+
/* @__PURE__ */ jsxRuntime.jsxs("details", { className: "group/acct relative", children: [
|
|
1078
|
+
/* @__PURE__ */ jsxRuntime.jsxs("summary", { className: "group/tbtip relative 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: [
|
|
499
1079
|
/* @__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 }),
|
|
500
1080
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "hidden flex-col leading-tight md:flex", children: [
|
|
501
1081
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold text-[color:var(--foreground)]", children: user?.name ?? "Signed in" }),
|
|
502
1082
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-[color:var(--muted)]", children: secondary })
|
|
503
1083
|
] }),
|
|
504
|
-
/* @__PURE__ */ jsxRuntime.jsx(ChevronDown, {})
|
|
1084
|
+
/* @__PURE__ */ jsxRuntime.jsx(ChevronDown, {}),
|
|
1085
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "group-open/acct:hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1086
|
+
TopBarTooltip,
|
|
1087
|
+
{
|
|
1088
|
+
title: "My Account",
|
|
1089
|
+
description: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1090
|
+
user?.name ?? "Signed in",
|
|
1091
|
+
user?.email ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1092
|
+
/* @__PURE__ */ jsxRuntime.jsx("br", {}),
|
|
1093
|
+
user.email
|
|
1094
|
+
] }) : null,
|
|
1095
|
+
/* @__PURE__ */ jsxRuntime.jsx("br", {}),
|
|
1096
|
+
"Profile, organizations, appearance and sign out."
|
|
1097
|
+
] })
|
|
1098
|
+
}
|
|
1099
|
+
) })
|
|
505
1100
|
] }),
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
1101
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute right-0 z-30 mt-2 w-72 overflow-hidden rounded-xl border border-[color:var(--line)] bg-[color:var(--panel-elevated)] py-1 shadow-[var(--shadow-soft)]", children: [
|
|
1102
|
+
userMenu?.myProfileHref ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1103
|
+
"a",
|
|
1104
|
+
{
|
|
1105
|
+
href: safeHref(userMenu.myProfileHref),
|
|
1106
|
+
className: "flex w-full items-center gap-2.5 px-4 py-2.5 text-left text-sm font-medium text-[color:var(--foreground)] hover:bg-[color:var(--panel)]",
|
|
1107
|
+
children: [
|
|
1108
|
+
/* @__PURE__ */ jsxRuntime.jsx(ProfileIcon, {}),
|
|
1109
|
+
"My Profile"
|
|
1110
|
+
]
|
|
1111
|
+
}
|
|
1112
|
+
) : null,
|
|
1113
|
+
userMenu?.organizationSettingsHref ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1114
|
+
"a",
|
|
1115
|
+
{
|
|
1116
|
+
href: safeHref(userMenu.organizationSettingsHref),
|
|
1117
|
+
className: "flex w-full items-center gap-2.5 border-t border-[color:var(--line)] px-4 py-2.5 text-left text-sm font-medium text-[color:var(--foreground)] hover:bg-[color:var(--panel)]",
|
|
1118
|
+
children: [
|
|
1119
|
+
/* @__PURE__ */ jsxRuntime.jsx(BriefcaseIcon, {}),
|
|
1120
|
+
"Organization Settings"
|
|
1121
|
+
]
|
|
1122
|
+
}
|
|
1123
|
+
) : null,
|
|
1124
|
+
userMenu?.roles && userMenu.roles.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-[color:var(--line)] px-4 py-3", children: [
|
|
1125
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Roles & Organizations" }),
|
|
1126
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-col gap-1.5", children: userMenu.roles.map((r, i) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex flex-wrap items-center gap-1.5 text-sm", children: [
|
|
1127
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-md bg-[color:var(--panel)] px-2 py-0.5 text-xs font-bold uppercase tracking-wide text-[color:var(--foreground)]", children: r.role }),
|
|
1128
|
+
r.orgName ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[color:var(--muted)]", children: [
|
|
1129
|
+
"in ",
|
|
1130
|
+
r.orgName
|
|
1131
|
+
] }) : null
|
|
1132
|
+
] }, `${r.role}-${i}`)) })
|
|
1133
|
+
] }) : null,
|
|
1134
|
+
userMenu?.accounts && userMenu.accounts.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-[color:var(--line)] px-4 py-3", children: [
|
|
1135
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Accounts" }),
|
|
1136
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-col gap-1", children: userMenu.accounts.map((a) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-center gap-2.5", children: [
|
|
1137
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1138
|
+
"button",
|
|
1139
|
+
{
|
|
1140
|
+
type: "button",
|
|
1141
|
+
onClick: () => {
|
|
1142
|
+
if (!a.active) userMenu.onSwitchAccount?.(a.id);
|
|
1143
|
+
},
|
|
1144
|
+
className: "flex min-w-0 flex-1 items-center gap-2.5 rounded-lg px-1 py-1 text-left hover:bg-[color:var(--panel)]",
|
|
1145
|
+
children: [
|
|
1146
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-8 w-8 shrink-0 items-center justify-center overflow-hidden rounded-full bg-[color:var(--color-race-red)] text-[11px] font-bold text-white", children: a.avatarUrl ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: a.avatarUrl, alt: "", className: "h-full w-full object-cover" }) : (a.name ?? a.email ?? "?").slice(0, 2).toUpperCase() }),
|
|
1147
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-col leading-tight", children: [
|
|
1148
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1149
|
+
"span",
|
|
1150
|
+
{
|
|
1151
|
+
className: [
|
|
1152
|
+
"truncate text-sm font-semibold",
|
|
1153
|
+
a.active ? "text-[color:var(--color-race-red)]" : "text-[color:var(--foreground)]"
|
|
1154
|
+
].join(" "),
|
|
1155
|
+
children: a.name ?? a.email ?? a.id.slice(0, 8)
|
|
1156
|
+
}
|
|
1157
|
+
),
|
|
1158
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-xs text-[color:var(--muted)]", children: a.active ? "Active" : a.email ?? "" })
|
|
1159
|
+
] })
|
|
1160
|
+
]
|
|
1161
|
+
}
|
|
1162
|
+
),
|
|
1163
|
+
!a.active && userMenu.onRemoveAccount ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1164
|
+
"button",
|
|
1165
|
+
{
|
|
1166
|
+
type: "button",
|
|
1167
|
+
"aria-label": `Remove ${a.name ?? a.email ?? "account"}`,
|
|
1168
|
+
onClick: () => userMenu.onRemoveAccount?.(a.id),
|
|
1169
|
+
className: "shrink-0 rounded p-1 text-[color:var(--color-race-red)] hover:bg-[color:var(--panel)]",
|
|
1170
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(TrashIcon, {})
|
|
1171
|
+
}
|
|
1172
|
+
) : null
|
|
1173
|
+
] }, a.id)) }),
|
|
1174
|
+
userMenu.addAccountHref || userMenu.onAddAccount ? userMenu.addAccountHref ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1175
|
+
"a",
|
|
1176
|
+
{
|
|
1177
|
+
href: safeHref(userMenu.addAccountHref),
|
|
1178
|
+
className: "mt-1.5 flex items-center gap-2.5 rounded-lg px-1 py-1.5 text-sm font-semibold text-[color:var(--color-race-red)] hover:bg-[color:var(--panel)]",
|
|
1179
|
+
children: [
|
|
1180
|
+
/* @__PURE__ */ jsxRuntime.jsx(AddUserIcon, {}),
|
|
1181
|
+
"Add Account"
|
|
1182
|
+
]
|
|
1183
|
+
}
|
|
1184
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1185
|
+
"button",
|
|
1186
|
+
{
|
|
1187
|
+
type: "button",
|
|
1188
|
+
onClick: userMenu.onAddAccount,
|
|
1189
|
+
className: "mt-1.5 flex w-full items-center gap-2.5 rounded-lg px-1 py-1.5 text-left text-sm font-semibold text-[color:var(--color-race-red)] hover:bg-[color:var(--panel)]",
|
|
1190
|
+
children: [
|
|
1191
|
+
/* @__PURE__ */ jsxRuntime.jsx(AddUserIcon, {}),
|
|
1192
|
+
"Add Account"
|
|
1193
|
+
]
|
|
1194
|
+
}
|
|
1195
|
+
) : null
|
|
1196
|
+
] }) : null,
|
|
1197
|
+
userMenu?.theme ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-[color:var(--line)]", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1198
|
+
NavThemeControl,
|
|
1199
|
+
{
|
|
1200
|
+
value: userMenu.theme.value,
|
|
1201
|
+
onChange: userMenu.theme.onChange
|
|
1202
|
+
}
|
|
1203
|
+
) }) : null,
|
|
1204
|
+
onSignOut ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1205
|
+
"button",
|
|
1206
|
+
{
|
|
1207
|
+
type: "button",
|
|
1208
|
+
onClick: onSignOut,
|
|
1209
|
+
className: "flex w-full items-center gap-2.5 border-t border-[color:var(--line)] px-4 py-2.5 text-left text-sm font-medium text-[color:var(--foreground)] hover:bg-[color:var(--panel)] hover:text-[color:var(--color-race-red)]",
|
|
1210
|
+
children: [
|
|
1211
|
+
/* @__PURE__ */ jsxRuntime.jsx(SignOutIcon, {}),
|
|
1212
|
+
"Sign out"
|
|
1213
|
+
]
|
|
1214
|
+
}
|
|
1215
|
+
) : null
|
|
1216
|
+
] })
|
|
518
1217
|
] })
|
|
519
1218
|
] })
|
|
520
1219
|
] });
|
|
521
1220
|
}
|
|
1221
|
+
function ProfileIcon() {
|
|
1222
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: 16, height: 16, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
|
|
1223
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" }),
|
|
1224
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "7", r: "4" })
|
|
1225
|
+
] });
|
|
1226
|
+
}
|
|
1227
|
+
function BriefcaseIcon() {
|
|
1228
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: 16, height: 16, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
|
|
1229
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "2", y: "7", width: "20", height: "14", rx: "2", ry: "2" }),
|
|
1230
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16" })
|
|
1231
|
+
] });
|
|
1232
|
+
}
|
|
1233
|
+
function TrashIcon() {
|
|
1234
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: 14, height: 14, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
|
|
1235
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "3 6 5 6 21 6" }),
|
|
1236
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
|
|
1237
|
+
] });
|
|
1238
|
+
}
|
|
1239
|
+
function AddUserIcon() {
|
|
1240
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: 16, height: 16, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
|
|
1241
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" }),
|
|
1242
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "8.5", cy: "7", r: "4" }),
|
|
1243
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "20", y1: "8", x2: "20", y2: "14" }),
|
|
1244
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "23", y1: "11", x2: "17", y2: "11" })
|
|
1245
|
+
] });
|
|
1246
|
+
}
|
|
522
1247
|
function SignOutIcon() {
|
|
523
1248
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
524
1249
|
"svg",
|
|
@@ -542,19 +1267,14 @@ function SignOutIcon() {
|
|
|
542
1267
|
}
|
|
543
1268
|
function NavTopBarActionButton({ action }) {
|
|
544
1269
|
const Icon = action.icon;
|
|
545
|
-
const
|
|
546
|
-
const toneClass = tone === "danger" || tone === "warning" ? "text-[color:var(--color-race-red)] hover:text-[color:var(--color-race-red)]" : "text-[color:var(--topbar-icon-color,var(--muted))] hover:text-[color:var(--topbar-icon-hover-color,var(--foreground))] hover:bg-[color:var(--topbar-icon-hover-bg,transparent)]";
|
|
547
|
-
const className = [
|
|
548
|
-
"relative flex items-center justify-center rounded-full border border-[color:var(--line)] transition",
|
|
549
|
-
"h-[var(--topbar-icon-button-size,32px)] w-[var(--topbar-icon-button-size,32px)]",
|
|
550
|
-
toneClass
|
|
551
|
-
].join(" ");
|
|
1270
|
+
const className = topBarIconButtonClass(action.tone ?? "default");
|
|
552
1271
|
const glyphName = useTopBarIconSlotGlyph(action.slot ?? "apps");
|
|
553
1272
|
const BrandGlyph = action.slot ? resolveBrandGlyph(glyphName) : null;
|
|
554
1273
|
const Glyph = BrandGlyph ?? Icon;
|
|
555
1274
|
const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
556
1275
|
/* @__PURE__ */ jsxRuntime.jsx(Glyph, { size: 20, className: "text-current" }),
|
|
557
|
-
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
|
|
1276
|
+
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,
|
|
1277
|
+
/* @__PURE__ */ jsxRuntime.jsx(TopBarTooltip, { title: action.label, description: action.description })
|
|
558
1278
|
] });
|
|
559
1279
|
const isExternal = action.href ? /^https?:\/\//.test(action.href) : false;
|
|
560
1280
|
if (action.href) {
|
|
@@ -563,7 +1283,6 @@ function NavTopBarActionButton({ action }) {
|
|
|
563
1283
|
{
|
|
564
1284
|
href: safeHref(action.href),
|
|
565
1285
|
"aria-label": action.label,
|
|
566
|
-
title: action.label,
|
|
567
1286
|
className,
|
|
568
1287
|
...isExternal ? { target: "_blank", rel: "noreferrer" } : {},
|
|
569
1288
|
children: content
|
|
@@ -576,7 +1295,6 @@ function NavTopBarActionButton({ action }) {
|
|
|
576
1295
|
type: "button",
|
|
577
1296
|
onClick: action.onClick,
|
|
578
1297
|
"aria-label": action.label,
|
|
579
|
-
title: action.label,
|
|
580
1298
|
className,
|
|
581
1299
|
children: content
|
|
582
1300
|
}
|
|
@@ -690,12 +1408,23 @@ function NavAppShell({ sidebar, topBar, children }) {
|
|
|
690
1408
|
] });
|
|
691
1409
|
}
|
|
692
1410
|
|
|
1411
|
+
exports.FuelAction = FuelAction;
|
|
693
1412
|
exports.NavAppShell = NavAppShell;
|
|
1413
|
+
exports.NavChromeProvider = NavChromeProvider;
|
|
1414
|
+
exports.NavDisplayAction = NavDisplayAction;
|
|
694
1415
|
exports.NavSidebar = NavSidebar;
|
|
695
1416
|
exports.NavTabStrip = NavTabStrip;
|
|
1417
|
+
exports.NavThemeControl = NavThemeControl;
|
|
696
1418
|
exports.NavTopBar = NavTopBar;
|
|
1419
|
+
exports.NotificationsAction = NotificationsAction;
|
|
1420
|
+
exports.SupportAction = SupportAction;
|
|
1421
|
+
exports.TopBarCountBadge = TopBarCountBadge;
|
|
1422
|
+
exports.TopBarTooltip = TopBarTooltip;
|
|
1423
|
+
exports.TopBarWidgetSlot = TopBarWidgetSlot;
|
|
697
1424
|
exports.envChipClasses = envChipClasses;
|
|
698
1425
|
exports.isNavItemActive = isNavItemActive;
|
|
699
1426
|
exports.standardPolitogyNav = standardPolitogyNav;
|
|
1427
|
+
exports.topBarIconButtonClass = topBarIconButtonClass;
|
|
1428
|
+
exports.useChromeOrgs = useChromeOrgs;
|
|
700
1429
|
//# sourceMappingURL=native.cjs.map
|
|
701
1430
|
//# sourceMappingURL=native.cjs.map
|