@broberg/app-shell 0.11.0 → 0.12.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,10 +16,23 @@ 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";
21
+ /** F092.20 — shows a pin icon after the label. */
22
+ pinned?: boolean;
23
+ }
24
+ interface NavSection<Icon = unknown> {
25
+ /** A sub-heading inside one group, e.g. "I dag" / "I går". */
26
+ label: string;
27
+ items: NavItem<Icon>[];
19
28
  }
20
29
  interface NavGroup<Icon = unknown> {
21
30
  label: string;
22
31
  items: NavItem<Icon>[];
32
+ /** F092.20 — "plain": the group label in normal text (not uppercase/mono). */
33
+ variant?: "default" | "plain";
34
+ /** F092.20 — sub-headed lists inside this group, shown after `items`. */
35
+ sections?: NavSection<Icon>[];
23
36
  }
24
37
  /**
25
38
  * Is this item the current page? Exact match for "/", otherwise the path is the
@@ -236,6 +249,8 @@ declare const TEXT: {
236
249
  readonly openMenu: "Åbn menu";
237
250
  readonly closeMenu: "Luk menu";
238
251
  readonly home: "Forside";
252
+ readonly working: "Arbejder";
253
+ readonly pinned: "Fastgjort";
239
254
  readonly language: "Sprog";
240
255
  readonly zoom: "Zoom";
241
256
  readonly zoomOut: "Zoom ud";
@@ -307,6 +322,8 @@ declare const TEXT: {
307
322
  readonly openMenu: "Open menu";
308
323
  readonly closeMenu: "Close menu";
309
324
  readonly home: "Home";
325
+ readonly working: "Working";
326
+ readonly pinned: "Pinned";
310
327
  readonly language: "Language";
311
328
  readonly zoom: "Zoom";
312
329
  readonly zoomOut: "Zoom out";
@@ -689,4 +706,4 @@ interface ConfirmDialogProps {
689
706
  }
690
707
  declare function ConfirmDialog(p: ConfirmDialogProps): JSX.Element | null;
691
708
 
692
- export { AVATAR_MAX_BYTES, AVATAR_TYPES, type AccountAdapter, AccountError, AccountPage, type AccountPageProps, type AccountProfile, type AnalyticsAdapter, type AnalyticsData, type AnalyticsDay, AnalyticsError, AnalyticsPage, type AnalyticsPageProps, AppShell, type AppShellProps, Avatar, ConfirmDialog, type ConfirmDialogProps, type Lang, type NavGroup, type NavItem, type Navigate, NotificationBell, type NotificationBellProps, type NotificationSource, PageHeader, type PageTab, PageTabs, type PreactNavGroup, type PreactNavItem, type ShellUser, Sidebar, type SidebarProps, TEXT, type TenantAdapter, TenantLoadError, type TenantMembershipRow, TenantSwitcher, type TenantSwitcherProps, TopBar, type TopBarProps, UserMenu, type UserMenuItem, type UserMenuProps, type UserMenuSection, accountErrorKind, activeNavLabel, analyticsErrorKind, badgeLabel, changePct, checkAvatarFile, copenhagenDay, createFetchAccountAdapter, createFetchAnalyticsAdapter, createFetchNotificationSource, createFetchTenantAdapter, createMemoryNotificationSource, defaultReauthHref, fillDays, initials, isActivePath, isExternal, readFlag, readSet, relativeTime, safeNavigate, useIsMobile, writeFlag, writeSet };
709
+ export { AVATAR_MAX_BYTES, AVATAR_TYPES, type AccountAdapter, AccountError, AccountPage, type AccountPageProps, type AccountProfile, type AnalyticsAdapter, type AnalyticsData, type AnalyticsDay, AnalyticsError, AnalyticsPage, type AnalyticsPageProps, AppShell, type AppShellProps, Avatar, ConfirmDialog, type ConfirmDialogProps, type Lang, type NavGroup, type NavItem, type NavSection, type Navigate, NotificationBell, type NotificationBellProps, type NotificationSource, PageHeader, type PageTab, PageTabs, type PreactNavGroup, type PreactNavItem, type ShellUser, Sidebar, type SidebarProps, TEXT, type TenantAdapter, TenantLoadError, type TenantMembershipRow, TenantSwitcher, type TenantSwitcherProps, TopBar, type TopBarProps, UserMenu, type UserMenuItem, type UserMenuProps, type UserMenuSection, accountErrorKind, activeNavLabel, analyticsErrorKind, badgeLabel, changePct, checkAvatarFile, copenhagenDay, createFetchAccountAdapter, createFetchAnalyticsAdapter, createFetchNotificationSource, createFetchTenantAdapter, createMemoryNotificationSource, defaultReauthHref, fillDays, initials, isActivePath, isExternal, readFlag, readSet, relativeTime, safeNavigate, useIsMobile, writeFlag, writeSet };
package/dist/preact.d.ts CHANGED
@@ -16,10 +16,23 @@ 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";
21
+ /** F092.20 — shows a pin icon after the label. */
22
+ pinned?: boolean;
23
+ }
24
+ interface NavSection<Icon = unknown> {
25
+ /** A sub-heading inside one group, e.g. "I dag" / "I går". */
26
+ label: string;
27
+ items: NavItem<Icon>[];
19
28
  }
20
29
  interface NavGroup<Icon = unknown> {
21
30
  label: string;
22
31
  items: NavItem<Icon>[];
32
+ /** F092.20 — "plain": the group label in normal text (not uppercase/mono). */
33
+ variant?: "default" | "plain";
34
+ /** F092.20 — sub-headed lists inside this group, shown after `items`. */
35
+ sections?: NavSection<Icon>[];
23
36
  }
24
37
  /**
25
38
  * Is this item the current page? Exact match for "/", otherwise the path is the
@@ -236,6 +249,8 @@ declare const TEXT: {
236
249
  readonly openMenu: "Åbn menu";
237
250
  readonly closeMenu: "Luk menu";
238
251
  readonly home: "Forside";
252
+ readonly working: "Arbejder";
253
+ readonly pinned: "Fastgjort";
239
254
  readonly language: "Sprog";
240
255
  readonly zoom: "Zoom";
241
256
  readonly zoomOut: "Zoom ud";
@@ -307,6 +322,8 @@ declare const TEXT: {
307
322
  readonly openMenu: "Open menu";
308
323
  readonly closeMenu: "Close menu";
309
324
  readonly home: "Home";
325
+ readonly working: "Working";
326
+ readonly pinned: "Pinned";
310
327
  readonly language: "Language";
311
328
  readonly zoom: "Zoom";
312
329
  readonly zoomOut: "Zoom out";
@@ -689,4 +706,4 @@ interface ConfirmDialogProps {
689
706
  }
690
707
  declare function ConfirmDialog(p: ConfirmDialogProps): JSX.Element | null;
691
708
 
692
- export { AVATAR_MAX_BYTES, AVATAR_TYPES, type AccountAdapter, AccountError, AccountPage, type AccountPageProps, type AccountProfile, type AnalyticsAdapter, type AnalyticsData, type AnalyticsDay, AnalyticsError, AnalyticsPage, type AnalyticsPageProps, AppShell, type AppShellProps, Avatar, ConfirmDialog, type ConfirmDialogProps, type Lang, type NavGroup, type NavItem, type Navigate, NotificationBell, type NotificationBellProps, type NotificationSource, PageHeader, type PageTab, PageTabs, type PreactNavGroup, type PreactNavItem, type ShellUser, Sidebar, type SidebarProps, TEXT, type TenantAdapter, TenantLoadError, type TenantMembershipRow, TenantSwitcher, type TenantSwitcherProps, TopBar, type TopBarProps, UserMenu, type UserMenuItem, type UserMenuProps, type UserMenuSection, accountErrorKind, activeNavLabel, analyticsErrorKind, badgeLabel, changePct, checkAvatarFile, copenhagenDay, createFetchAccountAdapter, createFetchAnalyticsAdapter, createFetchNotificationSource, createFetchTenantAdapter, createMemoryNotificationSource, defaultReauthHref, fillDays, initials, isActivePath, isExternal, readFlag, readSet, relativeTime, safeNavigate, useIsMobile, writeFlag, writeSet };
709
+ export { AVATAR_MAX_BYTES, AVATAR_TYPES, type AccountAdapter, AccountError, AccountPage, type AccountPageProps, type AccountProfile, type AnalyticsAdapter, type AnalyticsData, type AnalyticsDay, AnalyticsError, AnalyticsPage, type AnalyticsPageProps, AppShell, type AppShellProps, Avatar, ConfirmDialog, type ConfirmDialogProps, type Lang, type NavGroup, type NavItem, type NavSection, type Navigate, NotificationBell, type NotificationBellProps, type NotificationSource, PageHeader, type PageTab, PageTabs, type PreactNavGroup, type PreactNavItem, type ShellUser, Sidebar, type SidebarProps, TEXT, type TenantAdapter, TenantLoadError, type TenantMembershipRow, TenantSwitcher, type TenantSwitcherProps, TopBar, type TopBarProps, UserMenu, type UserMenuItem, type UserMenuProps, type UserMenuSection, accountErrorKind, activeNavLabel, analyticsErrorKind, badgeLabel, changePct, checkAvatarFile, copenhagenDay, createFetchAccountAdapter, createFetchAnalyticsAdapter, createFetchNotificationSource, createFetchTenantAdapter, createMemoryNotificationSource, defaultReauthHref, fillDays, initials, isActivePath, isExternal, readFlag, readSet, relativeTime, safeNavigate, useIsMobile, writeFlag, writeSet };
package/dist/preact.js CHANGED
@@ -304,6 +304,8 @@ var TEXT = {
304
304
  openMenu: "\xC5bn menu",
305
305
  closeMenu: "Luk menu",
306
306
  home: "Forside",
307
+ working: "Arbejder",
308
+ pinned: "Fastgjort",
307
309
  language: "Sprog",
308
310
  zoom: "Zoom",
309
311
  zoomOut: "Zoom ud",
@@ -375,6 +377,8 @@ var TEXT = {
375
377
  openMenu: "Open menu",
376
378
  closeMenu: "Close menu",
377
379
  home: "Home",
380
+ working: "Working",
381
+ pinned: "Pinned",
378
382
  language: "Language",
379
383
  zoom: "Zoom",
380
384
  zoomOut: "Zoom out",
@@ -493,6 +497,10 @@ var IconLogOut = () => svg(/* @__PURE__ */ jsxs(Fragment, { children: [
493
497
  /* @__PURE__ */ jsx("path", { d: "m16 17 5-5-5-5" }),
494
498
  /* @__PURE__ */ jsx("path", { d: "M21 12H9" })
495
499
  ] }), 14);
500
+ var IconPin = () => svg(/* @__PURE__ */ jsxs(Fragment, { children: [
501
+ /* @__PURE__ */ jsx("path", { d: "M12 17v5" }),
502
+ /* @__PURE__ */ jsx("path", { d: "M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z" })
503
+ ] }), 12);
496
504
  var IconUser = () => svg(/* @__PURE__ */ jsxs(Fragment, { children: [
497
505
  /* @__PURE__ */ jsx("circle", { cx: "12", cy: "8", r: "4" }),
498
506
  /* @__PURE__ */ jsx("path", { d: "M4 21a8 8 0 0 1 16 0" })
@@ -568,7 +576,9 @@ function Sidebar(p) {
568
576
  children: [
569
577
  active && !sub ? /* @__PURE__ */ jsx("span", { class: "bas-item__bar" }) : null,
570
578
  it.icon ? /* @__PURE__ */ jsx("span", { class: "bas-item__icon", children: it.icon }) : null,
579
+ 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,
571
580
  /* @__PURE__ */ jsx("span", { class: "bas-item__label", children: it.label }),
581
+ 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,
572
582
  badge ? /* @__PURE__ */ jsx("span", { class: "bas-item__count", "data-testid": `sidebar-badge-${it.id}`, children: badge }) : null
573
583
  ]
574
584
  },
@@ -654,12 +664,16 @@ function Sidebar(p) {
654
664
  /* @__PURE__ */ jsx("nav", { class: "bas-sidebar__nav", children: p.groups.map((g) => {
655
665
  const open = railed || !folded.has(g.label);
656
666
  const slug = g.label.toLowerCase().replace(/[^a-z0-9]+/g, "-");
657
- return /* @__PURE__ */ jsxs("div", { class: "bas-group", children: [
667
+ return /* @__PURE__ */ jsxs("div", { class: "bas-group" + (g.variant === "plain" ? " bas-group--plain" : ""), children: [
658
668
  /* @__PURE__ */ jsxs("button", { type: "button", class: "bas-group__label", "data-testid": `sidebar-group-${slug}`, "aria-expanded": open, onClick: () => toggleGroup(g.label), children: [
659
669
  /* @__PURE__ */ jsx(IconChevron, { open }),
660
670
  /* @__PURE__ */ jsx("span", { children: g.label })
661
671
  ] }),
662
- open ? g.items.map(item) : null
672
+ open ? g.items.map(item) : null,
673
+ 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: [
674
+ /* @__PURE__ */ jsx("div", { class: "bas-section__label", children: sec.label }),
675
+ sec.items.map(item)
676
+ ] }, sec.label)) : null
663
677
  ] }, g.label);
664
678
  }) }),
665
679
  p.footer?.length ? /* @__PURE__ */ jsx("div", { class: "bas-sidebar__foot", children: p.footer.map(item) }) : null,
@@ -845,6 +859,33 @@ function Segmented(props) {
845
859
  }
846
860
  function Dropdown(props) {
847
861
  const [state, setState] = useState({ open: false, highlighted: -1 });
862
+ const btnRef = useRef(null);
863
+ const listRef = useRef(null);
864
+ const [pos, setPos] = useState(void 0);
865
+ useLayoutEffect(() => {
866
+ if (!state.open || !btnRef.current) return setPos(void 0);
867
+ const r = btnRef.current.getBoundingClientRect();
868
+ const vw = window.innerWidth, vh = window.innerHeight, m = 8;
869
+ const h = listRef.current?.offsetHeight ?? 0;
870
+ const width = Math.min(Math.max(r.width, 160), vw - 2 * m);
871
+ const left = Math.max(m, Math.min(r.right - width, vw - width - m));
872
+ const below = r.bottom + 4;
873
+ const top = below + h <= vh - m || r.top - 4 - h < m ? Math.min(below, vh - m - h) : r.top - 4 - h;
874
+ setPos({ position: "fixed", left: `${left}px`, top: `${Math.max(m, top)}px`, width: `${width}px` });
875
+ }, [state.open]);
876
+ useEffect(() => {
877
+ if (!state.open) return;
878
+ const close = (e) => {
879
+ if (e.target instanceof Node && listRef.current?.contains(e.target)) return;
880
+ setState((st) => ({ ...st, open: false }));
881
+ };
882
+ window.addEventListener("scroll", close, true);
883
+ window.addEventListener("resize", close);
884
+ return () => {
885
+ window.removeEventListener("scroll", close, true);
886
+ window.removeEventListener("resize", close);
887
+ };
888
+ }, [state.open]);
848
889
  const current = Math.max(0, props.options.findIndex(([v]) => v === props.value));
849
890
  const choose = (i) => {
850
891
  setState({ open: false, highlighted: i });
@@ -865,6 +906,7 @@ function Dropdown(props) {
865
906
  /* @__PURE__ */ jsxs(
866
907
  "button",
867
908
  {
909
+ ref: btnRef,
868
910
  type: "button",
869
911
  class: "bas-dd__btn",
870
912
  "data-testid": props.testid,
@@ -880,7 +922,7 @@ function Dropdown(props) {
880
922
  ]
881
923
  }
882
924
  ),
883
- state.open ? /* @__PURE__ */ jsx("ul", { class: "bas-dd__list", role: "listbox", "aria-label": props.label, "data-testid": `${props.testid}-list`, children: props.options.map(([v, label], i) => /* @__PURE__ */ jsxs(
925
+ state.open ? /* @__PURE__ */ jsx("ul", { ref: listRef, class: "bas-dd__list", style: pos ?? { position: "fixed", visibility: "hidden" }, role: "listbox", "aria-label": props.label, "data-testid": `${props.testid}-list`, children: props.options.map(([v, label], i) => /* @__PURE__ */ jsxs(
884
926
  "li",
885
927
  {
886
928
  id: optId(v),