@broberg/app-shell 0.14.0 → 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/README.md +29 -0
- package/css/app-shell.css +72 -65
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -2
- package/dist/index.d.ts +11 -2
- package/dist/index.js.map +1 -1
- package/dist/preact.cjs +99 -79
- package/dist/preact.cjs.map +1 -1
- package/dist/preact.d.cts +17 -2
- package/dist/preact.d.ts +17 -2
- package/dist/preact.js +99 -79
- package/dist/preact.js.map +1 -1
- package/package.json +1 -1
package/dist/preact.d.cts
CHANGED
|
@@ -16,8 +16,11 @@ interface NavItem<Icon = unknown> {
|
|
|
16
16
|
* it is on the current path.
|
|
17
17
|
*/
|
|
18
18
|
children?: NavItem<Icon>[];
|
|
19
|
-
/**
|
|
20
|
-
|
|
19
|
+
/**
|
|
20
|
+
* F092.20 — a status marker before the label; "working" = a run in progress.
|
|
21
|
+
* F092.23 — "idle" = the same ring in grey (finished), so every row lines up.
|
|
22
|
+
*/
|
|
23
|
+
marker?: "working" | "idle";
|
|
21
24
|
/** F092.20 — shows a pin icon after the label. */
|
|
22
25
|
pinned?: boolean;
|
|
23
26
|
/** F092.21 — row actions: a «…» button opens a menu with these. */
|
|
@@ -59,6 +62,12 @@ interface NavGroup<Icon = unknown> {
|
|
|
59
62
|
variant?: "default" | "plain";
|
|
60
63
|
/** F092.20 — sub-headed lists inside this group, shown after `items`. */
|
|
61
64
|
sections?: NavSection<Icon>[];
|
|
65
|
+
/**
|
|
66
|
+
* F092.23 — no heading row: the items show directly, always open (it cannot be
|
|
67
|
+
* folded, so a stored fold for this label is ignored). `label` stays required —
|
|
68
|
+
* it is the group's key and its accessible name.
|
|
69
|
+
*/
|
|
70
|
+
hideLabel?: boolean;
|
|
62
71
|
}
|
|
63
72
|
/**
|
|
64
73
|
* Is this item the current page? Exact match for "/", otherwise the path is the
|
|
@@ -601,6 +610,12 @@ declare function PageTabs(p: {
|
|
|
601
610
|
onNavigate?: Navigate;
|
|
602
611
|
}): JSX.Element;
|
|
603
612
|
interface AppShellProps {
|
|
613
|
+
/**
|
|
614
|
+
* F092.24 — the app's default text size: 1 = as designed, 1.2 = every shell text
|
|
615
|
+
* 20 % larger. Sets --bas-font-scale on the root. The APP's choice; the user's own
|
|
616
|
+
* Zoom (userMenu.zoom) is separate and applies on top.
|
|
617
|
+
*/
|
|
618
|
+
fontScale?: number;
|
|
604
619
|
/** F092.21 — passed to the sidebar: a row action was chosen. */
|
|
605
620
|
onItemAction?: SidebarProps["onItemAction"];
|
|
606
621
|
brand?: ComponentChildren;
|
package/dist/preact.d.ts
CHANGED
|
@@ -16,8 +16,11 @@ interface NavItem<Icon = unknown> {
|
|
|
16
16
|
* it is on the current path.
|
|
17
17
|
*/
|
|
18
18
|
children?: NavItem<Icon>[];
|
|
19
|
-
/**
|
|
20
|
-
|
|
19
|
+
/**
|
|
20
|
+
* F092.20 — a status marker before the label; "working" = a run in progress.
|
|
21
|
+
* F092.23 — "idle" = the same ring in grey (finished), so every row lines up.
|
|
22
|
+
*/
|
|
23
|
+
marker?: "working" | "idle";
|
|
21
24
|
/** F092.20 — shows a pin icon after the label. */
|
|
22
25
|
pinned?: boolean;
|
|
23
26
|
/** F092.21 — row actions: a «…» button opens a menu with these. */
|
|
@@ -59,6 +62,12 @@ interface NavGroup<Icon = unknown> {
|
|
|
59
62
|
variant?: "default" | "plain";
|
|
60
63
|
/** F092.20 — sub-headed lists inside this group, shown after `items`. */
|
|
61
64
|
sections?: NavSection<Icon>[];
|
|
65
|
+
/**
|
|
66
|
+
* F092.23 — no heading row: the items show directly, always open (it cannot be
|
|
67
|
+
* folded, so a stored fold for this label is ignored). `label` stays required —
|
|
68
|
+
* it is the group's key and its accessible name.
|
|
69
|
+
*/
|
|
70
|
+
hideLabel?: boolean;
|
|
62
71
|
}
|
|
63
72
|
/**
|
|
64
73
|
* Is this item the current page? Exact match for "/", otherwise the path is the
|
|
@@ -601,6 +610,12 @@ declare function PageTabs(p: {
|
|
|
601
610
|
onNavigate?: Navigate;
|
|
602
611
|
}): JSX.Element;
|
|
603
612
|
interface AppShellProps {
|
|
613
|
+
/**
|
|
614
|
+
* F092.24 — the app's default text size: 1 = as designed, 1.2 = every shell text
|
|
615
|
+
* 20 % larger. Sets --bas-font-scale on the root. The APP's choice; the user's own
|
|
616
|
+
* Zoom (userMenu.zoom) is separate and applies on top.
|
|
617
|
+
*/
|
|
618
|
+
fontScale?: number;
|
|
604
619
|
/** F092.21 — passed to the sidebar: a row action was chosen. */
|
|
605
620
|
onItemAction?: SidebarProps["onItemAction"];
|
|
606
621
|
brand?: ComponentChildren;
|
package/dist/preact.js
CHANGED
|
@@ -808,7 +808,7 @@ function Sidebar(p) {
|
|
|
808
808
|
children: [
|
|
809
809
|
active && !sub ? /* @__PURE__ */ jsx("span", { class: "bas-item__bar" }) : null,
|
|
810
810
|
it.icon ? /* @__PURE__ */ jsx("span", { class: "bas-item__icon", children: it.icon }) : null,
|
|
811
|
-
it.marker === "working" ? /* @__PURE__ */ jsx("span", { class: "bas-item__marker", "data-testid": `sidebar-item-${it.id}-marker`, role: "img", "aria-label": TEXT[p.lang].working }) : null,
|
|
811
|
+
it.marker === "working" ? /* @__PURE__ */ jsx("span", { class: "bas-item__marker", "data-testid": `sidebar-item-${it.id}-marker`, role: "img", "aria-label": TEXT[p.lang].working }) : it.marker === "idle" ? /* @__PURE__ */ jsx("span", { class: "bas-item__marker bas-item__marker--idle", "data-testid": `sidebar-item-${it.id}-marker`, "aria-hidden": "true" }) : null,
|
|
812
812
|
/* @__PURE__ */ jsx("span", { class: "bas-item__label", children: it.label }),
|
|
813
813
|
it.pinned ? /* @__PURE__ */ jsx("span", { class: "bas-item__pin", "data-testid": `sidebar-item-${it.id}-pinned`, role: "img", "aria-label": TEXT[p.lang].pinned, children: /* @__PURE__ */ jsx(IconPin, {}) }) : null,
|
|
814
814
|
badge ? /* @__PURE__ */ jsx("span", { class: "bas-item__count", "data-testid": `sidebar-badge-${it.id}`, children: badge }) : null
|
|
@@ -899,19 +899,29 @@ function Sidebar(p) {
|
|
|
899
899
|
) : p.brandMark }) : null
|
|
900
900
|
] }) : null,
|
|
901
901
|
/* @__PURE__ */ jsx("nav", { class: "bas-sidebar__nav", children: p.groups.map((g) => {
|
|
902
|
-
const open = railed || !folded.has(g.label);
|
|
902
|
+
const open = railed || g.hideLabel || !folded.has(g.label);
|
|
903
903
|
const slug = g.label.toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
|
904
|
-
return /* @__PURE__ */ jsxs(
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
904
|
+
return /* @__PURE__ */ jsxs(
|
|
905
|
+
"div",
|
|
906
|
+
{
|
|
907
|
+
class: "bas-group" + (g.variant === "plain" ? " bas-group--plain" : ""),
|
|
908
|
+
role: g.hideLabel ? "group" : void 0,
|
|
909
|
+
"aria-label": g.hideLabel ? g.label : void 0,
|
|
910
|
+
"data-testid": g.hideLabel ? `sidebar-group-${slug}-unlabelled` : void 0,
|
|
911
|
+
children: [
|
|
912
|
+
g.hideLabel ? null : /* @__PURE__ */ jsxs("button", { type: "button", class: "bas-group__label", "data-testid": `sidebar-group-${slug}`, "aria-expanded": open, onClick: () => toggleGroup(g.label), children: [
|
|
913
|
+
/* @__PURE__ */ jsx(IconChevron, { open }),
|
|
914
|
+
/* @__PURE__ */ jsx("span", { children: g.label })
|
|
915
|
+
] }),
|
|
916
|
+
open ? g.items.map(item) : null,
|
|
917
|
+
open ? g.sections?.map((sec) => /* @__PURE__ */ jsxs("div", { class: "bas-section", "data-testid": `sidebar-section-${slug}-${sec.label.toLowerCase().replace(/[^a-z0-9æøå]+/g, "-")}`, children: [
|
|
918
|
+
/* @__PURE__ */ jsx("div", { class: "bas-section__label", children: sec.label }),
|
|
919
|
+
sec.items.map(item)
|
|
920
|
+
] }, sec.label)) : null
|
|
921
|
+
]
|
|
922
|
+
},
|
|
923
|
+
g.label
|
|
924
|
+
);
|
|
915
925
|
}) }),
|
|
916
926
|
p.footer?.length ? /* @__PURE__ */ jsx("div", { class: "bas-sidebar__foot", children: p.footer.map(item) }) : null,
|
|
917
927
|
p.bottom ? /* @__PURE__ */ jsx("div", { class: "bas-sidebar__user", "data-testid": "sidebar-user", children: p.bottom }) : null
|
|
@@ -1471,76 +1481,86 @@ function AppShell(p) {
|
|
|
1471
1481
|
const heading = p.title ?? activeNavLabel(p.groups, p.footer, p.currentPath);
|
|
1472
1482
|
const userInSidebar = p.userMenuPlacement === "sidebar-footer";
|
|
1473
1483
|
const sidebarUser = userInSidebar && p.user ? /* @__PURE__ */ jsx(UserMenu, { user: p.user, ...p.userMenu, placement: "sidebar-footer", onNavigate: p.onNavigate, lang: p.lang }) : null;
|
|
1474
|
-
return /* @__PURE__ */ jsxs(
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
collapsed,
|
|
1499
|
-
collapse,
|
|
1500
|
-
brandMark: p.brandMark,
|
|
1501
|
-
homeHref: p.homeHref,
|
|
1502
|
-
homeLabel: p.homeLabel,
|
|
1503
|
-
mobileOpen: drawer,
|
|
1504
|
-
onClose: () => setDrawer(false),
|
|
1505
|
-
mobile: mode,
|
|
1506
|
-
header: inset || mode === "drawer" ? p.brand : void 0,
|
|
1507
|
-
brandTestId: inset ? "brand-home" : void 0,
|
|
1508
|
-
groupsKey: `${prefix}.groups`,
|
|
1509
|
-
itemsKey: `${prefix}.items`,
|
|
1510
|
-
bottom: sidebarUser,
|
|
1511
|
-
lang: p.lang
|
|
1512
|
-
}
|
|
1513
|
-
),
|
|
1514
|
-
/* @__PURE__ */ jsxs("main", { class: "bas-main", "data-testid": "app-content", children: [
|
|
1515
|
-
/* @__PURE__ */ jsxs("div", { class: "bas-contenthead", "data-testid": "content-header", children: [
|
|
1484
|
+
return /* @__PURE__ */ jsxs(
|
|
1485
|
+
"div",
|
|
1486
|
+
{
|
|
1487
|
+
class: "bas-root",
|
|
1488
|
+
"data-testid": "app-shell",
|
|
1489
|
+
"data-mobile": mode,
|
|
1490
|
+
"data-layout": layout,
|
|
1491
|
+
style: p.fontScale !== void 0 ? { "--bas-font-scale": String(p.fontScale) } : void 0,
|
|
1492
|
+
children: [
|
|
1493
|
+
inset ? null : /* @__PURE__ */ jsx(
|
|
1494
|
+
TopBar,
|
|
1495
|
+
{
|
|
1496
|
+
brand: p.brand,
|
|
1497
|
+
homeHref: p.homeHref,
|
|
1498
|
+
onHome: () => setDrawer(false),
|
|
1499
|
+
actions: p.actions,
|
|
1500
|
+
notifications: p.notifications,
|
|
1501
|
+
user: userInSidebar ? void 0 : p.user,
|
|
1502
|
+
userMenu: p.userMenu,
|
|
1503
|
+
onNavigate: p.onNavigate,
|
|
1504
|
+
lang: p.lang
|
|
1505
|
+
}
|
|
1506
|
+
),
|
|
1507
|
+
/* @__PURE__ */ jsxs("div", { class: "bas-row", children: [
|
|
1516
1508
|
/* @__PURE__ */ jsx(
|
|
1517
|
-
|
|
1509
|
+
Sidebar,
|
|
1518
1510
|
{
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1511
|
+
groups: p.groups,
|
|
1512
|
+
footer: p.footer,
|
|
1513
|
+
onItemAction: p.onItemAction,
|
|
1514
|
+
currentPath: p.currentPath,
|
|
1515
|
+
onNavigate: p.onNavigate,
|
|
1516
|
+
collapsed,
|
|
1517
|
+
collapse,
|
|
1518
|
+
brandMark: p.brandMark,
|
|
1519
|
+
homeHref: p.homeHref,
|
|
1520
|
+
homeLabel: p.homeLabel,
|
|
1521
|
+
mobileOpen: drawer,
|
|
1522
|
+
onClose: () => setDrawer(false),
|
|
1523
|
+
mobile: mode,
|
|
1524
|
+
header: inset || mode === "drawer" ? p.brand : void 0,
|
|
1525
|
+
brandTestId: inset ? "brand-home" : void 0,
|
|
1526
|
+
groupsKey: `${prefix}.groups`,
|
|
1527
|
+
itemsKey: `${prefix}.items`,
|
|
1528
|
+
bottom: sidebarUser,
|
|
1529
|
+
lang: p.lang
|
|
1527
1530
|
}
|
|
1528
1531
|
),
|
|
1529
|
-
|
|
1530
|
-
/* @__PURE__ */
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1532
|
+
/* @__PURE__ */ jsxs("main", { class: "bas-main", "data-testid": "app-content", children: [
|
|
1533
|
+
/* @__PURE__ */ jsxs("div", { class: "bas-contenthead", "data-testid": "content-header", children: [
|
|
1534
|
+
/* @__PURE__ */ jsx(
|
|
1535
|
+
"button",
|
|
1536
|
+
{
|
|
1537
|
+
type: "button",
|
|
1538
|
+
class: "bas-iconbtn bas-trigger",
|
|
1539
|
+
"data-testid": "sidebar-trigger",
|
|
1540
|
+
"aria-label": triggerLabel,
|
|
1541
|
+
"aria-expanded": expanded,
|
|
1542
|
+
title: triggerLabel,
|
|
1543
|
+
onClick: onTrigger,
|
|
1544
|
+
children: /* @__PURE__ */ jsx(IconPanelLeft, {})
|
|
1545
|
+
}
|
|
1546
|
+
),
|
|
1547
|
+
heading ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1548
|
+
/* @__PURE__ */ jsx("span", { class: "bas-contenthead__sep", "data-testid": "content-header-separator", "aria-hidden": "true" }),
|
|
1549
|
+
/* @__PURE__ */ jsx("h1", { class: "bas-contenthead__title", "data-testid": "content-header-title", children: heading })
|
|
1550
|
+
] }) : null,
|
|
1551
|
+
inset ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1552
|
+
/* @__PURE__ */ jsx("div", { class: "bas-spacer" }),
|
|
1553
|
+
p.actions ? /* @__PURE__ */ jsx("div", { class: "bas-contenthead__actions", children: p.actions }) : null,
|
|
1554
|
+
/* @__PURE__ */ jsx(NotificationBell, { source: p.notifications, onNavigate: p.onNavigate, lang: p.lang }),
|
|
1555
|
+
p.user && !userInSidebar ? /* @__PURE__ */ jsx(UserMenu, { user: p.user, ...p.userMenu, onNavigate: p.onNavigate, lang: p.lang }) : null
|
|
1556
|
+
] }) : null
|
|
1557
|
+
] }),
|
|
1558
|
+
p.children
|
|
1559
|
+
] })
|
|
1560
|
+
] })
|
|
1561
|
+
]
|
|
1562
|
+
}
|
|
1563
|
+
);
|
|
1544
1564
|
}
|
|
1545
1565
|
function defaultReauthHref() {
|
|
1546
1566
|
const here = typeof location === "undefined" ? "/" : location.pathname + location.search;
|