@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/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
- /** F092.20 — a status marker before the label; "working" = a run in progress. */
20
- marker?: "working";
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
- /** F092.20 — a status marker before the label; "working" = a run in progress. */
20
- marker?: "working";
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("div", { class: "bas-group" + (g.variant === "plain" ? " bas-group--plain" : ""), children: [
905
- /* @__PURE__ */ jsxs("button", { type: "button", class: "bas-group__label", "data-testid": `sidebar-group-${slug}`, "aria-expanded": open, onClick: () => toggleGroup(g.label), children: [
906
- /* @__PURE__ */ jsx(IconChevron, { open }),
907
- /* @__PURE__ */ jsx("span", { children: g.label })
908
- ] }),
909
- open ? g.items.map(item) : null,
910
- 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: [
911
- /* @__PURE__ */ jsx("div", { class: "bas-section__label", children: sec.label }),
912
- sec.items.map(item)
913
- ] }, sec.label)) : null
914
- ] }, g.label);
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("div", { class: "bas-root", "data-testid": "app-shell", "data-mobile": mode, "data-layout": layout, children: [
1475
- inset ? null : /* @__PURE__ */ jsx(
1476
- TopBar,
1477
- {
1478
- brand: p.brand,
1479
- homeHref: p.homeHref,
1480
- onHome: () => setDrawer(false),
1481
- actions: p.actions,
1482
- notifications: p.notifications,
1483
- user: userInSidebar ? void 0 : p.user,
1484
- userMenu: p.userMenu,
1485
- onNavigate: p.onNavigate,
1486
- lang: p.lang
1487
- }
1488
- ),
1489
- /* @__PURE__ */ jsxs("div", { class: "bas-row", children: [
1490
- /* @__PURE__ */ jsx(
1491
- Sidebar,
1492
- {
1493
- groups: p.groups,
1494
- footer: p.footer,
1495
- onItemAction: p.onItemAction,
1496
- currentPath: p.currentPath,
1497
- onNavigate: p.onNavigate,
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
- "button",
1509
+ Sidebar,
1518
1510
  {
1519
- type: "button",
1520
- class: "bas-iconbtn bas-trigger",
1521
- "data-testid": "sidebar-trigger",
1522
- "aria-label": triggerLabel,
1523
- "aria-expanded": expanded,
1524
- title: triggerLabel,
1525
- onClick: onTrigger,
1526
- children: /* @__PURE__ */ jsx(IconPanelLeft, {})
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
- heading ? /* @__PURE__ */ jsxs(Fragment, { children: [
1530
- /* @__PURE__ */ jsx("span", { class: "bas-contenthead__sep", "data-testid": "content-header-separator", "aria-hidden": "true" }),
1531
- /* @__PURE__ */ jsx("h1", { class: "bas-contenthead__title", "data-testid": "content-header-title", children: heading })
1532
- ] }) : null,
1533
- inset ? /* @__PURE__ */ jsxs(Fragment, { children: [
1534
- /* @__PURE__ */ jsx("div", { class: "bas-spacer" }),
1535
- p.actions ? /* @__PURE__ */ jsx("div", { class: "bas-contenthead__actions", children: p.actions }) : null,
1536
- /* @__PURE__ */ jsx(NotificationBell, { source: p.notifications, onNavigate: p.onNavigate, lang: p.lang }),
1537
- p.user && !userInSidebar ? /* @__PURE__ */ jsx(UserMenu, { user: p.user, ...p.userMenu, onNavigate: p.onNavigate, lang: p.lang }) : null
1538
- ] }) : null
1539
- ] }),
1540
- p.children
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;