@broberg/app-shell 0.12.0 → 0.14.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
@@ -20,6 +20,32 @@ interface NavItem<Icon = unknown> {
20
20
  marker?: "working";
21
21
  /** F092.20 — shows a pin icon after the label. */
22
22
  pinned?: boolean;
23
+ /** F092.21 — row actions: a «…» button opens a menu with these. */
24
+ actions?: NavAction[];
25
+ }
26
+ /**
27
+ * F092.21 — one row action. `submenu` switches the menu to a sub-list (with a
28
+ * back arrow); `input` switches it to a field + save (e.g. rename). A plain
29
+ * action, a submenu leaf and a saved input all call `onItemAction`.
30
+ */
31
+ interface NavAction {
32
+ id: string;
33
+ label: string;
34
+ icon?: unknown;
35
+ /** A single letter that picks this action while the menu is open (e.g. "P"). */
36
+ kbd?: string;
37
+ /** Shown in the danger colour (delete). */
38
+ danger?: boolean;
39
+ submenu?: NavAction[];
40
+ input?: {
41
+ value: string;
42
+ placeholder?: string;
43
+ saveLabel?: string;
44
+ };
45
+ /** F092.22 — ask twice: the first pick shows `label` («Tryk igen for at slette») and keeps the menu open; the second runs it. */
46
+ confirm?: {
47
+ label: string;
48
+ };
23
49
  }
24
50
  interface NavSection<Icon = unknown> {
25
51
  /** A sub-heading inside one group, e.g. "I dag" / "I går". */
@@ -250,6 +276,8 @@ declare const TEXT: {
250
276
  readonly closeMenu: "Luk menu";
251
277
  readonly home: "Forside";
252
278
  readonly working: "Arbejder";
279
+ readonly moreFor: "Mere for";
280
+ readonly back: "Tilbage";
253
281
  readonly pinned: "Fastgjort";
254
282
  readonly language: "Sprog";
255
283
  readonly zoom: "Zoom";
@@ -323,6 +351,8 @@ declare const TEXT: {
323
351
  readonly closeMenu: "Close menu";
324
352
  readonly home: "Home";
325
353
  readonly working: "Working";
354
+ readonly moreFor: "More for";
355
+ readonly back: "Back";
326
356
  readonly pinned: "Pinned";
327
357
  readonly language: "Language";
328
358
  readonly zoom: "Zoom";
@@ -437,6 +467,8 @@ interface SidebarProps {
437
467
  lang: Lang;
438
468
  /** F092.15 — rendered at the very bottom of the sidebar (the user menu in "sidebar-footer"). */
439
469
  bottom?: ComponentChildren;
470
+ /** F092.21 — a row action was chosen (value: the saved text of an `input` action). */
471
+ onItemAction?: (navItemId: string, actionId: string, value?: string) => void;
440
472
  }
441
473
  declare function Sidebar(p: SidebarProps): JSX.Element;
442
474
  interface NotificationBellProps {
@@ -569,6 +601,8 @@ declare function PageTabs(p: {
569
601
  onNavigate?: Navigate;
570
602
  }): JSX.Element;
571
603
  interface AppShellProps {
604
+ /** F092.21 — passed to the sidebar: a row action was chosen. */
605
+ onItemAction?: SidebarProps["onItemAction"];
572
606
  brand?: ComponentChildren;
573
607
  actions?: ComponentChildren;
574
608
  groups: PreactNavGroup[];
@@ -706,4 +740,4 @@ interface ConfirmDialogProps {
706
740
  }
707
741
  declare function ConfirmDialog(p: ConfirmDialogProps): JSX.Element | null;
708
742
 
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 };
743
+ 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 NavAction, 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
@@ -20,6 +20,32 @@ interface NavItem<Icon = unknown> {
20
20
  marker?: "working";
21
21
  /** F092.20 — shows a pin icon after the label. */
22
22
  pinned?: boolean;
23
+ /** F092.21 — row actions: a «…» button opens a menu with these. */
24
+ actions?: NavAction[];
25
+ }
26
+ /**
27
+ * F092.21 — one row action. `submenu` switches the menu to a sub-list (with a
28
+ * back arrow); `input` switches it to a field + save (e.g. rename). A plain
29
+ * action, a submenu leaf and a saved input all call `onItemAction`.
30
+ */
31
+ interface NavAction {
32
+ id: string;
33
+ label: string;
34
+ icon?: unknown;
35
+ /** A single letter that picks this action while the menu is open (e.g. "P"). */
36
+ kbd?: string;
37
+ /** Shown in the danger colour (delete). */
38
+ danger?: boolean;
39
+ submenu?: NavAction[];
40
+ input?: {
41
+ value: string;
42
+ placeholder?: string;
43
+ saveLabel?: string;
44
+ };
45
+ /** F092.22 — ask twice: the first pick shows `label` («Tryk igen for at slette») and keeps the menu open; the second runs it. */
46
+ confirm?: {
47
+ label: string;
48
+ };
23
49
  }
24
50
  interface NavSection<Icon = unknown> {
25
51
  /** A sub-heading inside one group, e.g. "I dag" / "I går". */
@@ -250,6 +276,8 @@ declare const TEXT: {
250
276
  readonly closeMenu: "Luk menu";
251
277
  readonly home: "Forside";
252
278
  readonly working: "Arbejder";
279
+ readonly moreFor: "Mere for";
280
+ readonly back: "Tilbage";
253
281
  readonly pinned: "Fastgjort";
254
282
  readonly language: "Sprog";
255
283
  readonly zoom: "Zoom";
@@ -323,6 +351,8 @@ declare const TEXT: {
323
351
  readonly closeMenu: "Close menu";
324
352
  readonly home: "Home";
325
353
  readonly working: "Working";
354
+ readonly moreFor: "More for";
355
+ readonly back: "Back";
326
356
  readonly pinned: "Pinned";
327
357
  readonly language: "Language";
328
358
  readonly zoom: "Zoom";
@@ -437,6 +467,8 @@ interface SidebarProps {
437
467
  lang: Lang;
438
468
  /** F092.15 — rendered at the very bottom of the sidebar (the user menu in "sidebar-footer"). */
439
469
  bottom?: ComponentChildren;
470
+ /** F092.21 — a row action was chosen (value: the saved text of an `input` action). */
471
+ onItemAction?: (navItemId: string, actionId: string, value?: string) => void;
440
472
  }
441
473
  declare function Sidebar(p: SidebarProps): JSX.Element;
442
474
  interface NotificationBellProps {
@@ -569,6 +601,8 @@ declare function PageTabs(p: {
569
601
  onNavigate?: Navigate;
570
602
  }): JSX.Element;
571
603
  interface AppShellProps {
604
+ /** F092.21 — passed to the sidebar: a row action was chosen. */
605
+ onItemAction?: SidebarProps["onItemAction"];
572
606
  brand?: ComponentChildren;
573
607
  actions?: ComponentChildren;
574
608
  groups: PreactNavGroup[];
@@ -706,4 +740,4 @@ interface ConfirmDialogProps {
706
740
  }
707
741
  declare function ConfirmDialog(p: ConfirmDialogProps): JSX.Element | null;
708
742
 
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 };
743
+ 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 NavAction, 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
@@ -305,6 +305,8 @@ var TEXT = {
305
305
  closeMenu: "Luk menu",
306
306
  home: "Forside",
307
307
  working: "Arbejder",
308
+ moreFor: "Mere for",
309
+ back: "Tilbage",
308
310
  pinned: "Fastgjort",
309
311
  language: "Sprog",
310
312
  zoom: "Zoom",
@@ -378,6 +380,8 @@ var TEXT = {
378
380
  closeMenu: "Close menu",
379
381
  home: "Home",
380
382
  working: "Working",
383
+ moreFor: "More for",
384
+ back: "Back",
381
385
  pinned: "Pinned",
382
386
  language: "Language",
383
387
  zoom: "Zoom",
@@ -505,6 +509,234 @@ var IconUser = () => svg(/* @__PURE__ */ jsxs(Fragment, { children: [
505
509
  /* @__PURE__ */ jsx("circle", { cx: "12", cy: "8", r: "4" }),
506
510
  /* @__PURE__ */ jsx("path", { d: "M4 21a8 8 0 0 1 16 0" })
507
511
  ] }), 14);
512
+ function RowActions(props) {
513
+ const t = TEXT[props.lang];
514
+ const id = props.item.id;
515
+ const btn = useRef(null);
516
+ const menu = useRef(null);
517
+ const [stack, setStack] = useState([]);
518
+ const [hi, setHiState] = useState(0);
519
+ const hiRef = useRef(0);
520
+ const setHi = (n) => {
521
+ hiRef.current = n;
522
+ setHiState(n);
523
+ };
524
+ const [armed, setArmedState] = useState(null);
525
+ const armedRef = useRef(null);
526
+ const setArmed = (v) => {
527
+ armedRef.current = v;
528
+ setArmedState(v);
529
+ };
530
+ const items = useRef([]);
531
+ const onKeyDownRef = useRef(() => {
532
+ });
533
+ const [value, setValue] = useState("");
534
+ const [pos, setPos] = useState(void 0);
535
+ const view = stack[stack.length - 1];
536
+ const open = !!view;
537
+ const stackRef = useRef([]);
538
+ const setStackNow = (next) => {
539
+ stackRef.current = next;
540
+ setStack(next);
541
+ };
542
+ const close = (refocus = true) => {
543
+ setStackNow([]);
544
+ setArmed(null);
545
+ if (refocus) btn.current?.focus();
546
+ };
547
+ const show = (v) => {
548
+ setStackNow([...stackRef.current, v]);
549
+ setHi(0);
550
+ setArmed(null);
551
+ if (v.kind === "input") setValue(v.action.input.value);
552
+ };
553
+ const back = () => {
554
+ setStackNow(stackRef.current.slice(0, -1));
555
+ setHi(0);
556
+ setArmed(null);
557
+ };
558
+ const pick = (a) => {
559
+ if (a.submenu?.length) return show({ kind: "list", actions: a.submenu, title: a.label });
560
+ if (a.input) return show({ kind: "input", action: a });
561
+ if (a.confirm && armedRef.current !== a.id) return setArmed(a.id);
562
+ close();
563
+ props.onAction(a.id);
564
+ };
565
+ const save = (a) => {
566
+ close();
567
+ props.onAction(a.id, value);
568
+ };
569
+ useLayoutEffect(() => {
570
+ if (!open || !btn.current) return setPos(void 0);
571
+ const r = btn.current.getBoundingClientRect();
572
+ const vw = window.innerWidth, vh = window.innerHeight, m = 8;
573
+ const w = Math.min(240, vw - 2 * m);
574
+ const h = menu.current?.offsetHeight ?? 0;
575
+ const left = Math.max(m, Math.min(r.right - w, vw - w - m));
576
+ const top = r.bottom + 4 + h <= vh - m || r.top - 4 - h < m ? Math.min(r.bottom + 4, vh - m - h) : r.top - 4 - h;
577
+ setPos({ position: "fixed", left: `${left}px`, top: `${Math.max(m, top)}px`, width: `${w}px` });
578
+ }, [open, stack.length]);
579
+ useLayoutEffect(() => {
580
+ if (!open || !pos) return;
581
+ if (view?.kind === "input") menu.current?.querySelector("input")?.focus();
582
+ else items.current[hiRef.current]?.focus();
583
+ }, [open, pos, hi, stack.length]);
584
+ useLayoutEffect(() => {
585
+ if (!open) return;
586
+ const outside = (e) => {
587
+ const n = e.target instanceof Node ? e.target : null;
588
+ if (n && (menu.current?.contains(n) || btn.current?.contains(n))) return;
589
+ close(false);
590
+ };
591
+ const early = (e) => {
592
+ const n = e.target instanceof Node ? e.target : null;
593
+ if (n && menu.current?.contains(n)) return;
594
+ if (e.key === "Enter" || e.key === " " || e.key === "Spacebar" || e.key === "Tab") return;
595
+ onKeyDownRef.current(e);
596
+ };
597
+ document.addEventListener("keydown", early, true);
598
+ document.addEventListener("mousedown", outside);
599
+ window.addEventListener("scroll", outside, true);
600
+ window.addEventListener("resize", outside);
601
+ return () => {
602
+ document.removeEventListener("keydown", early, true);
603
+ document.removeEventListener("mousedown", outside);
604
+ window.removeEventListener("scroll", outside, true);
605
+ window.removeEventListener("resize", outside);
606
+ };
607
+ }, [open]);
608
+ const onKeyDown = (e) => {
609
+ const stack2 = stackRef.current;
610
+ const view2 = stack2[stack2.length - 1];
611
+ if (!view2) return;
612
+ e.stopPropagation();
613
+ if (e.key === "Escape") {
614
+ e.preventDefault();
615
+ return close();
616
+ }
617
+ if (view2.kind === "input") {
618
+ if (e.key === "Enter") {
619
+ e.preventDefault();
620
+ save(view2.action);
621
+ }
622
+ return;
623
+ }
624
+ if ((e.key === "ArrowLeft" || e.key === "Backspace") && stack2.length > 1) {
625
+ e.preventDefault();
626
+ return back();
627
+ }
628
+ const list = view2.actions;
629
+ if (e.key === "Enter" || e.key === " " || e.key === "Spacebar") {
630
+ e.preventDefault();
631
+ const at = items.current.findIndex((b) => b === document.activeElement);
632
+ const idx = at >= 0 ? at : hiRef.current;
633
+ return list[idx] ? pick(list[idx]) : void 0;
634
+ }
635
+ const { state, intent } = selectKeyReducer({ open: true, highlighted: hiRef.current }, e.key, list.length);
636
+ if (intent.type === "select") {
637
+ e.preventDefault();
638
+ return pick(list[intent.index]);
639
+ }
640
+ if (state.highlighted !== hiRef.current) {
641
+ e.preventDefault();
642
+ return setHi(state.highlighted);
643
+ }
644
+ const byKbd = e.key.length === 1 ? list.find((a) => a.kbd?.toLowerCase() === e.key.toLowerCase()) : void 0;
645
+ if (byKbd) {
646
+ e.preventDefault();
647
+ pick(byKbd);
648
+ }
649
+ };
650
+ onKeyDownRef.current = onKeyDown;
651
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
652
+ /* @__PURE__ */ jsx(
653
+ "button",
654
+ {
655
+ ref: btn,
656
+ type: "button",
657
+ class: "bas-item__more" + (open ? " is-open" : ""),
658
+ "data-testid": `sidebar-item-${id}-more`,
659
+ "aria-label": `${t.moreFor} ${props.item.label}`,
660
+ "aria-haspopup": "menu",
661
+ "aria-expanded": open,
662
+ onClick: () => stackRef.current.length ? close() : show({ kind: "list", actions: props.item.actions }),
663
+ children: /* @__PURE__ */ jsx(IconMore, {})
664
+ }
665
+ ),
666
+ view ? /* @__PURE__ */ jsxs(
667
+ "div",
668
+ {
669
+ ref: menu,
670
+ class: "bas-rowmenu",
671
+ role: "menu",
672
+ tabIndex: -1,
673
+ "aria-label": view.kind === "list" && view.title ? view.title : props.item.label,
674
+ "data-testid": `sidebar-item-${id}-menu`,
675
+ style: pos ?? { position: "fixed", visibility: "hidden" },
676
+ onKeyDown,
677
+ children: [
678
+ stack.length > 1 ? /* @__PURE__ */ jsxs("button", { type: "button", class: "bas-rowmenu__back", "data-testid": `sidebar-item-${id}-back`, "aria-label": t.back, onClick: back, children: [
679
+ /* @__PURE__ */ jsx(IconChevronLeft, {}),
680
+ /* @__PURE__ */ jsx("span", { children: view.kind === "list" ? view.title : view.action.label })
681
+ ] }) : null,
682
+ view.kind === "list" ? view.actions.map((a, i) => /* @__PURE__ */ jsxs(
683
+ "button",
684
+ {
685
+ type: "button",
686
+ role: "menuitem",
687
+ ref: (el) => {
688
+ items.current[i] = el;
689
+ },
690
+ tabIndex: i === hi ? 0 : -1,
691
+ class: "bas-rowmenu__item" + (i === hi ? " is-active" : "") + (a.danger ? " is-danger" : "") + (armed === a.id ? " is-armed" : ""),
692
+ "data-testid": `sidebar-item-${id}-action-${a.id}`,
693
+ onMouseEnter: () => setHi(i),
694
+ onFocus: () => setHi(i),
695
+ onClick: () => pick(a),
696
+ children: [
697
+ a.icon ? /* @__PURE__ */ jsx("span", { class: "bas-rowmenu__icon", children: a.icon }) : null,
698
+ /* @__PURE__ */ jsx("span", { class: "bas-rowmenu__label", children: armed === a.id && a.confirm ? a.confirm.label : a.label }),
699
+ a.submenu?.length ? /* @__PURE__ */ jsx(IconChevronRight, {}) : a.kbd ? /* @__PURE__ */ jsx("kbd", { class: "bas-rowmenu__kbd", children: a.kbd.toUpperCase() }) : null
700
+ ]
701
+ },
702
+ a.id
703
+ )) : /* @__PURE__ */ jsxs(
704
+ "form",
705
+ {
706
+ class: "bas-rowmenu__form",
707
+ "data-testid": `sidebar-item-${id}-form`,
708
+ onSubmit: (e) => {
709
+ e.preventDefault();
710
+ save(view.action);
711
+ },
712
+ children: [
713
+ /* @__PURE__ */ jsx(
714
+ "input",
715
+ {
716
+ class: "bas-rowmenu__input",
717
+ "data-testid": `sidebar-item-${id}-input`,
718
+ "aria-label": view.action.label,
719
+ value,
720
+ placeholder: view.action.input.placeholder,
721
+ onInput: (e) => setValue(e.currentTarget.value)
722
+ }
723
+ ),
724
+ /* @__PURE__ */ jsx("button", { type: "submit", class: "bas-rowmenu__save", "data-testid": `sidebar-item-${id}-save`, children: view.action.input.saveLabel ?? t.save })
725
+ ]
726
+ }
727
+ )
728
+ ]
729
+ }
730
+ ) : null
731
+ ] });
732
+ }
733
+ var IconMore = () => svg(/* @__PURE__ */ jsxs(Fragment, { children: [
734
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "1" }),
735
+ /* @__PURE__ */ jsx("circle", { cx: "19", cy: "12", r: "1" }),
736
+ /* @__PURE__ */ jsx("circle", { cx: "5", cy: "12", r: "1" })
737
+ ] }), 16);
738
+ var IconChevronLeft = () => svg(/* @__PURE__ */ jsx("path", { d: "m15 18-6-6 6-6" }), 14);
739
+ var IconChevronRight = () => svg(/* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" }), 14);
508
740
  function Sidebar(p) {
509
741
  const key = p.groupsKey ?? "broberg-app-shell.groups";
510
742
  const itemsKey = p.itemsKey ?? "broberg-app-shell.items";
@@ -562,7 +794,7 @@ function Sidebar(p) {
562
794
  const leaf = (it, sub = false) => {
563
795
  const active = isActivePath(it.href, p.currentPath);
564
796
  const badge = badgeLabel(it.badge);
565
- return /* @__PURE__ */ jsxs(
797
+ const link = /* @__PURE__ */ jsxs(
566
798
  NavLink,
567
799
  {
568
800
  href: it.href,
@@ -584,6 +816,11 @@ function Sidebar(p) {
584
816
  },
585
817
  it.id
586
818
  );
819
+ if (!it.actions?.length || railed) return link;
820
+ return /* @__PURE__ */ jsxs("div", { class: "bas-itemrow", children: [
821
+ link,
822
+ /* @__PURE__ */ jsx(RowActions, { item: it, lang: p.lang, onAction: (a, v) => v === void 0 ? p.onItemAction?.(it.id, a) : p.onItemAction?.(it.id, a, v) })
823
+ ] }, it.id);
587
824
  };
588
825
  const parent = (it) => {
589
826
  const children = it.children;
@@ -1255,6 +1492,7 @@ function AppShell(p) {
1255
1492
  {
1256
1493
  groups: p.groups,
1257
1494
  footer: p.footer,
1495
+ onItemAction: p.onItemAction,
1258
1496
  currentPath: p.currentPath,
1259
1497
  onNavigate: p.onNavigate,
1260
1498
  collapsed,