@broberg/app-shell 0.1.1 → 0.2.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/README.md +37 -4
- package/css/app-shell.css +55 -24
- package/dist/index.cjs +18 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -1
- package/dist/index.d.ts +17 -1
- package/dist/index.js +18 -1
- package/dist/index.js.map +1 -1
- package/dist/preact.cjs +176 -19
- package/dist/preact.cjs.map +1 -1
- package/dist/preact.d.cts +54 -5
- package/dist/preact.d.ts +54 -5
- package/dist/preact.js +176 -20
- package/dist/preact.js.map +1 -1
- package/package.json +1 -1
package/dist/preact.cjs
CHANGED
|
@@ -25,6 +25,19 @@ function safeNavigate(target) {
|
|
|
25
25
|
return null;
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
+
function activeNavLabel(groups, footer, currentPath) {
|
|
29
|
+
let best;
|
|
30
|
+
const visit = (it) => {
|
|
31
|
+
if (it.href && isActivePath(it.href, currentPath)) {
|
|
32
|
+
const len = it.href.length;
|
|
33
|
+
if (!best || len > best.len) best = { label: it.label, len };
|
|
34
|
+
}
|
|
35
|
+
it.children?.forEach(visit);
|
|
36
|
+
};
|
|
37
|
+
for (const g of groups) g.items.forEach(visit);
|
|
38
|
+
footer?.forEach(visit);
|
|
39
|
+
return best?.label;
|
|
40
|
+
}
|
|
28
41
|
function badgeLabel(n) {
|
|
29
42
|
if (!n || n <= 0) return null;
|
|
30
43
|
return n > 99 ? "99+" : String(n);
|
|
@@ -159,6 +172,8 @@ function relativeTime(ts, lang, now = Date.now()) {
|
|
|
159
172
|
var TEXT = {
|
|
160
173
|
da: {
|
|
161
174
|
openMenu: "\xC5bn menu",
|
|
175
|
+
closeMenu: "Luk menu",
|
|
176
|
+
home: "Forside",
|
|
162
177
|
collapse: "Skjul sidemenu",
|
|
163
178
|
expand: "Vis sidemenu",
|
|
164
179
|
notifications: "Notifikationer",
|
|
@@ -183,6 +198,8 @@ var TEXT = {
|
|
|
183
198
|
},
|
|
184
199
|
en: {
|
|
185
200
|
openMenu: "Open menu",
|
|
201
|
+
closeMenu: "Close menu",
|
|
202
|
+
home: "Home",
|
|
186
203
|
collapse: "Hide sidebar",
|
|
187
204
|
expand: "Show sidebar",
|
|
188
205
|
notifications: "Notifications",
|
|
@@ -240,8 +257,10 @@ function NavLink(props) {
|
|
|
240
257
|
var svg = (d, size = 18) => /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", width: size, height: size, fill: "none", stroke: "currentColor", "stroke-width": "1.9", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: d });
|
|
241
258
|
var IconMenu = () => svg(/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 6h16M4 12h16M4 18h16" }), 20);
|
|
242
259
|
var IconChevron = ({ open }) => /* @__PURE__ */ jsxRuntime.jsx("span", { class: "bas-chev" + (open ? " is-open" : ""), children: svg(/* @__PURE__ */ jsxRuntime.jsx("path", { d: "m9 18 6-6-6-6" }), 12) });
|
|
243
|
-
var
|
|
244
|
-
|
|
260
|
+
var IconPanelLeft = () => svg(/* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
261
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }),
|
|
262
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 3v18" })
|
|
263
|
+
] }), 16);
|
|
245
264
|
var IconBell = () => svg(/* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
246
265
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" }),
|
|
247
266
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M10.3 21a1.94 1.94 0 0 0 3.4 0" })
|
|
@@ -256,8 +275,12 @@ var IconUser = () => svg(/* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
|
256
275
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 21a8 8 0 0 1 16 0" })
|
|
257
276
|
] }), 14);
|
|
258
277
|
function Sidebar(p) {
|
|
259
|
-
const t = TEXT[p.lang];
|
|
260
278
|
const key = p.groupsKey ?? "broberg-app-shell.groups";
|
|
279
|
+
const itemsKey = p.itemsKey ?? "broberg-app-shell.items";
|
|
280
|
+
const mode = p.mobile ?? "drawer";
|
|
281
|
+
const collapseMode = p.collapse ?? (mode === "rail" ? "icon" : "offcanvas");
|
|
282
|
+
const isMobile = useIsMobile();
|
|
283
|
+
const railed = isMobile ? mode === "rail" : !!p.collapsed && collapseMode === "icon";
|
|
261
284
|
const [folded, setFolded] = hooks.useState(() => readSet(key));
|
|
262
285
|
const toggleGroup = (label) => {
|
|
263
286
|
const next = new Set(folded);
|
|
@@ -265,9 +288,47 @@ function Sidebar(p) {
|
|
|
265
288
|
setFolded(next);
|
|
266
289
|
writeSet(key, next);
|
|
267
290
|
};
|
|
268
|
-
const
|
|
269
|
-
const
|
|
270
|
-
const
|
|
291
|
+
const all = [...p.groups.flatMap((g) => g.items), ...p.footer ?? []];
|
|
292
|
+
const childActive = (it) => !!it.children?.some((c) => isActivePath(c.href, p.currentPath));
|
|
293
|
+
const [openItems, setOpenItems] = hooks.useState(() => {
|
|
294
|
+
const s = readSet(itemsKey);
|
|
295
|
+
for (const it of all) if (childActive(it)) s.add(it.id);
|
|
296
|
+
return s;
|
|
297
|
+
});
|
|
298
|
+
const toggleItem = (id) => {
|
|
299
|
+
const next = new Set(openItems);
|
|
300
|
+
next.has(id) ? next.delete(id) : next.add(id);
|
|
301
|
+
setOpenItems(next);
|
|
302
|
+
writeSet(itemsKey, next);
|
|
303
|
+
};
|
|
304
|
+
const [flyout, setFlyout] = hooks.useState(null);
|
|
305
|
+
const flyoutRef = hooks.useRef(null);
|
|
306
|
+
hooks.useEffect(() => {
|
|
307
|
+
if (!flyout) return;
|
|
308
|
+
const onKey = (e) => {
|
|
309
|
+
if (e.key === "Escape") setFlyout(null);
|
|
310
|
+
};
|
|
311
|
+
const onDown = (e) => {
|
|
312
|
+
const t = e.target;
|
|
313
|
+
if (flyoutRef.current && t && flyoutRef.current.contains(t)) return;
|
|
314
|
+
if (t instanceof Element && t.closest(`[data-flyout-for="${flyout.id}"]`)) return;
|
|
315
|
+
setFlyout(null);
|
|
316
|
+
};
|
|
317
|
+
document.addEventListener("keydown", onKey);
|
|
318
|
+
document.addEventListener("mousedown", onDown);
|
|
319
|
+
return () => {
|
|
320
|
+
document.removeEventListener("keydown", onKey);
|
|
321
|
+
document.removeEventListener("mousedown", onDown);
|
|
322
|
+
};
|
|
323
|
+
}, [flyout]);
|
|
324
|
+
hooks.useEffect(() => {
|
|
325
|
+
if (!railed) setFlyout(null);
|
|
326
|
+
}, [railed]);
|
|
327
|
+
const close = () => {
|
|
328
|
+
setFlyout(null);
|
|
329
|
+
p.onClose?.();
|
|
330
|
+
};
|
|
331
|
+
const leaf = (it, sub = false) => {
|
|
271
332
|
const active = isActivePath(it.href, p.currentPath);
|
|
272
333
|
const badge = badgeLabel(it.badge);
|
|
273
334
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -276,12 +337,13 @@ function Sidebar(p) {
|
|
|
276
337
|
href: it.href,
|
|
277
338
|
onNavigate: p.onNavigate,
|
|
278
339
|
onAfter: close,
|
|
279
|
-
class: "bas-item" + (active ? " is-active" : ""),
|
|
340
|
+
class: "bas-item" + (sub ? " bas-item--sub" : "") + (active ? " is-active" : ""),
|
|
280
341
|
"data-testid": `sidebar-item-${it.id}`,
|
|
281
342
|
"aria-current": active ? "page" : void 0,
|
|
343
|
+
"aria-label": railed && !sub ? it.label : void 0,
|
|
282
344
|
title: it.label,
|
|
283
345
|
children: [
|
|
284
|
-
active ? /* @__PURE__ */ jsxRuntime.jsx("span", { class: "bas-item__bar" }) : null,
|
|
346
|
+
active && !sub ? /* @__PURE__ */ jsxRuntime.jsx("span", { class: "bas-item__bar" }) : null,
|
|
285
347
|
it.icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { class: "bas-item__icon", children: it.icon }) : null,
|
|
286
348
|
/* @__PURE__ */ jsxRuntime.jsx("span", { class: "bas-item__label", children: it.label }),
|
|
287
349
|
badge ? /* @__PURE__ */ jsxRuntime.jsx("span", { class: "bas-item__count", "data-testid": `sidebar-badge-${it.id}`, children: badge }) : null
|
|
@@ -290,22 +352,84 @@ function Sidebar(p) {
|
|
|
290
352
|
it.id
|
|
291
353
|
);
|
|
292
354
|
};
|
|
355
|
+
const parent = (it) => {
|
|
356
|
+
const children = it.children;
|
|
357
|
+
const onPage = childActive(it) || isActivePath(it.href, p.currentPath);
|
|
358
|
+
const open = railed ? flyout?.id === it.id : openItems.has(it.id);
|
|
359
|
+
const onClick = (e) => {
|
|
360
|
+
if (!railed) return toggleItem(it.id);
|
|
361
|
+
if (flyout?.id === it.id) return setFlyout(null);
|
|
362
|
+
const r = e.currentTarget.getBoundingClientRect();
|
|
363
|
+
setFlyout({ id: it.id, top: r.top, left: r.right + 6 });
|
|
364
|
+
};
|
|
365
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { class: "bas-itemgroup", children: [
|
|
366
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
367
|
+
"button",
|
|
368
|
+
{
|
|
369
|
+
type: "button",
|
|
370
|
+
class: "bas-item bas-item--parent" + (onPage ? " is-parent-active" : ""),
|
|
371
|
+
"data-testid": `sidebar-item-${it.id}-toggle`,
|
|
372
|
+
"data-flyout-for": it.id,
|
|
373
|
+
"aria-expanded": open,
|
|
374
|
+
"aria-label": railed ? it.label : void 0,
|
|
375
|
+
title: it.label,
|
|
376
|
+
onClick,
|
|
377
|
+
children: [
|
|
378
|
+
onPage && railed ? /* @__PURE__ */ jsxRuntime.jsx("span", { class: "bas-item__bar" }) : null,
|
|
379
|
+
it.icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { class: "bas-item__icon", children: it.icon }) : null,
|
|
380
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { class: "bas-item__label", children: it.label }),
|
|
381
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { class: "bas-item__chev", children: /* @__PURE__ */ jsxRuntime.jsx(IconChevron, { open }) })
|
|
382
|
+
]
|
|
383
|
+
}
|
|
384
|
+
),
|
|
385
|
+
!railed && open ? /* @__PURE__ */ jsxRuntime.jsx("div", { class: "bas-sub", children: children.map((c) => leaf(c, true)) }) : null,
|
|
386
|
+
railed && flyout?.id === it.id ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
387
|
+
"div",
|
|
388
|
+
{
|
|
389
|
+
ref: flyoutRef,
|
|
390
|
+
class: "bas-flyout",
|
|
391
|
+
"data-testid": `sidebar-flyout-${it.id}`,
|
|
392
|
+
role: "menu",
|
|
393
|
+
"aria-label": it.label,
|
|
394
|
+
style: { top: `${flyout.top}px`, left: `${flyout.left}px` },
|
|
395
|
+
children: [
|
|
396
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { class: "bas-flyout__title", children: it.label }),
|
|
397
|
+
children.map((c) => leaf(c, true))
|
|
398
|
+
]
|
|
399
|
+
}
|
|
400
|
+
) : null
|
|
401
|
+
] }, it.id);
|
|
402
|
+
};
|
|
403
|
+
const item = (it) => it.children?.length ? parent(it) : leaf(it);
|
|
293
404
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
294
405
|
mode === "drawer" ? /* @__PURE__ */ jsxRuntime.jsx("div", { class: "bas-backdrop" + (p.mobileOpen ? " is-open" : ""), "data-testid": "sidebar-backdrop", onClick: close }) : null,
|
|
295
406
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
296
407
|
"aside",
|
|
297
408
|
{
|
|
298
|
-
class: `bas-sidebar is-mobile-${mode}` + (p.mobileOpen ? " is-open" : "") + (p.collapsed ? " is-collapsed" : ""),
|
|
409
|
+
class: `bas-sidebar is-mobile-${mode}` + (p.mobileOpen ? " is-open" : "") + (p.collapsed ? " is-collapsed" : "") + (railed ? " is-railed" : ""),
|
|
299
410
|
"data-testid": "sidebar-root",
|
|
300
411
|
"data-mobile": mode,
|
|
412
|
+
"data-collapse": collapseMode,
|
|
301
413
|
"aria-label": "Navigation",
|
|
302
414
|
children: [
|
|
303
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { class: "bas-sidebar__head", children: [
|
|
304
|
-
p.header ? /* @__PURE__ */ jsxRuntime.jsx("div", { class: "bas-sidebar__brand", children: p.
|
|
305
|
-
p.
|
|
306
|
-
|
|
415
|
+
p.header || p.brandMark ? /* @__PURE__ */ jsxRuntime.jsxs("div", { class: "bas-sidebar__head", children: [
|
|
416
|
+
p.header ? /* @__PURE__ */ jsxRuntime.jsx("div", { class: "bas-sidebar__brand", children: p.homeHref ? /* @__PURE__ */ jsxRuntime.jsx(NavLink, { href: p.homeHref, onNavigate: p.onNavigate, onAfter: close, class: "bas-home", "data-testid": "sidebar-brand-home", children: p.header }) : p.header }) : null,
|
|
417
|
+
p.brandMark ? /* @__PURE__ */ jsxRuntime.jsx("div", { class: "bas-sidebar__mark", "data-testid": "sidebar-brand-mark", children: p.homeHref ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
418
|
+
NavLink,
|
|
419
|
+
{
|
|
420
|
+
href: p.homeHref,
|
|
421
|
+
onNavigate: p.onNavigate,
|
|
422
|
+
onAfter: close,
|
|
423
|
+
class: "bas-home",
|
|
424
|
+
"data-testid": "brand-mark-home",
|
|
425
|
+
"aria-label": p.homeLabel ?? TEXT[p.lang].home,
|
|
426
|
+
title: p.homeLabel ?? TEXT[p.lang].home,
|
|
427
|
+
children: p.brandMark
|
|
428
|
+
}
|
|
429
|
+
) : p.brandMark }) : null
|
|
430
|
+
] }) : null,
|
|
307
431
|
/* @__PURE__ */ jsxRuntime.jsx("nav", { class: "bas-sidebar__nav", children: p.groups.map((g) => {
|
|
308
|
-
const open = !folded.has(g.label);
|
|
432
|
+
const open = railed || !folded.has(g.label);
|
|
309
433
|
const slug = g.label.toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
|
310
434
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { class: "bas-group", children: [
|
|
311
435
|
/* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", class: "bas-group__label", "data-testid": `sidebar-group-${slug}`, "aria-expanded": open, onClick: () => toggleGroup(g.label), children: [
|
|
@@ -593,7 +717,7 @@ function TopBar(p) {
|
|
|
593
717
|
const t = TEXT[p.lang];
|
|
594
718
|
return /* @__PURE__ */ jsxRuntime.jsx("header", { class: "bas-topbar", "data-testid": "topbar-root", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { class: "bas-topbar__inner", children: [
|
|
595
719
|
p.onOpenMenu ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", class: "bas-iconbtn bas-hamburger", "data-testid": "sidebar-toggle", "aria-label": t.openMenu, onClick: p.onOpenMenu, children: /* @__PURE__ */ jsxRuntime.jsx(IconMenu, {}) }) : null,
|
|
596
|
-
p.brand ? /* @__PURE__ */ jsxRuntime.jsx("div", { class: "bas-topbar__brand", children: p.brand }) : null,
|
|
720
|
+
p.brand ? /* @__PURE__ */ jsxRuntime.jsx("div", { class: "bas-topbar__brand", children: p.homeHref ? /* @__PURE__ */ jsxRuntime.jsx(NavLink, { href: p.homeHref, onNavigate: p.onNavigate, onAfter: p.onHome, class: "bas-home", "data-testid": "brand-home", children: p.brand }) : p.brand }) : null,
|
|
597
721
|
p.actions ? /* @__PURE__ */ jsxRuntime.jsx("div", { class: "bas-topbar__actions", children: p.actions }) : null,
|
|
598
722
|
/* @__PURE__ */ jsxRuntime.jsx("div", { class: "bas-spacer" }),
|
|
599
723
|
/* @__PURE__ */ jsxRuntime.jsx(NotificationBell, { source: p.notifications, onNavigate: p.onNavigate, lang: p.lang }),
|
|
@@ -628,21 +752,29 @@ function PageTabs(p) {
|
|
|
628
752
|
function AppShell(p) {
|
|
629
753
|
const prefix = p.storageKey ?? "broberg-app-shell";
|
|
630
754
|
const mode = p.mobile ?? "drawer";
|
|
755
|
+
const collapse = p.collapse ?? (mode === "rail" ? "icon" : "offcanvas");
|
|
631
756
|
const [collapsed, setCollapsed] = hooks.useState(() => readFlag(`${prefix}.collapsed`, false));
|
|
632
757
|
const [drawer, setDrawer] = hooks.useState(false);
|
|
758
|
+
const isMobile = useIsMobile();
|
|
633
759
|
const t = TEXT[p.lang];
|
|
634
760
|
const toggle = () => {
|
|
635
761
|
const next = !collapsed;
|
|
636
762
|
setCollapsed(next);
|
|
637
763
|
writeFlag(`${prefix}.collapsed`, next);
|
|
638
764
|
};
|
|
765
|
+
const drawerMode = isMobile && mode === "drawer";
|
|
766
|
+
const onTrigger = () => drawerMode ? setDrawer(!drawer) : toggle();
|
|
767
|
+
const expanded = drawerMode ? drawer : !collapsed;
|
|
768
|
+
const triggerLabel = drawerMode ? drawer ? t.closeMenu : t.openMenu : collapsed ? t.expand : t.collapse;
|
|
769
|
+
const heading = p.title ?? activeNavLabel(p.groups, p.footer, p.currentPath);
|
|
639
770
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { class: "bas-root", "data-testid": "app-shell", "data-mobile": mode, children: [
|
|
640
771
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
641
772
|
TopBar,
|
|
642
773
|
{
|
|
643
774
|
brand: p.brand,
|
|
775
|
+
homeHref: p.homeHref,
|
|
776
|
+
onHome: () => setDrawer(false),
|
|
644
777
|
actions: p.actions,
|
|
645
|
-
onOpenMenu: mode === "drawer" ? () => setDrawer(true) : void 0,
|
|
646
778
|
notifications: p.notifications,
|
|
647
779
|
user: p.user,
|
|
648
780
|
userMenu: p.userMenu,
|
|
@@ -659,17 +791,41 @@ function AppShell(p) {
|
|
|
659
791
|
currentPath: p.currentPath,
|
|
660
792
|
onNavigate: p.onNavigate,
|
|
661
793
|
collapsed,
|
|
662
|
-
|
|
794
|
+
collapse,
|
|
795
|
+
brandMark: p.brandMark,
|
|
796
|
+
homeHref: p.homeHref,
|
|
797
|
+
homeLabel: p.homeLabel,
|
|
663
798
|
mobileOpen: drawer,
|
|
664
799
|
onClose: () => setDrawer(false),
|
|
665
800
|
mobile: mode,
|
|
666
801
|
header: mode === "drawer" ? p.brand : void 0,
|
|
667
802
|
groupsKey: `${prefix}.groups`,
|
|
803
|
+
itemsKey: `${prefix}.items`,
|
|
668
804
|
lang: p.lang
|
|
669
805
|
}
|
|
670
806
|
),
|
|
671
|
-
|
|
672
|
-
|
|
807
|
+
/* @__PURE__ */ jsxRuntime.jsxs("main", { class: "bas-main", "data-testid": "app-content", children: [
|
|
808
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { class: "bas-contenthead", "data-testid": "content-header", children: [
|
|
809
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
810
|
+
"button",
|
|
811
|
+
{
|
|
812
|
+
type: "button",
|
|
813
|
+
class: "bas-iconbtn bas-trigger",
|
|
814
|
+
"data-testid": "sidebar-trigger",
|
|
815
|
+
"aria-label": triggerLabel,
|
|
816
|
+
"aria-expanded": expanded,
|
|
817
|
+
title: triggerLabel,
|
|
818
|
+
onClick: onTrigger,
|
|
819
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(IconPanelLeft, {})
|
|
820
|
+
}
|
|
821
|
+
),
|
|
822
|
+
heading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
823
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { class: "bas-contenthead__sep", "data-testid": "content-header-separator", "aria-hidden": "true" }),
|
|
824
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { class: "bas-contenthead__title", "data-testid": "content-header-title", children: heading })
|
|
825
|
+
] }) : null
|
|
826
|
+
] }),
|
|
827
|
+
p.children
|
|
828
|
+
] })
|
|
673
829
|
] })
|
|
674
830
|
] });
|
|
675
831
|
}
|
|
@@ -683,6 +839,7 @@ exports.Sidebar = Sidebar;
|
|
|
683
839
|
exports.TEXT = TEXT;
|
|
684
840
|
exports.TopBar = TopBar;
|
|
685
841
|
exports.UserMenu = UserMenu;
|
|
842
|
+
exports.activeNavLabel = activeNavLabel;
|
|
686
843
|
exports.badgeLabel = badgeLabel;
|
|
687
844
|
exports.createFetchNotificationSource = createFetchNotificationSource;
|
|
688
845
|
exports.createMemoryNotificationSource = createMemoryNotificationSource;
|