@broberg/app-shell 0.18.0 → 0.20.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 +27 -0
- package/css/app-shell.css +17 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js.map +1 -1
- package/dist/preact.cjs +151 -91
- package/dist/preact.cjs.map +1 -1
- package/dist/preact.d.cts +43 -1
- package/dist/preact.d.ts +43 -1
- package/dist/preact.js +153 -94
- package/dist/preact.js.map +1 -1
- package/package.json +1 -1
package/dist/preact.d.cts
CHANGED
|
@@ -73,6 +73,18 @@ interface NavGroup<Icon = unknown> {
|
|
|
73
73
|
* group is always open, even with a stored fold for this label. Default true.
|
|
74
74
|
*/
|
|
75
75
|
collapsible?: boolean;
|
|
76
|
+
/**
|
|
77
|
+
* F092.28 — a small icon button at the right of the heading row (e.g. «Filtrér og
|
|
78
|
+
* sortér»). The app opens its own menu from `onClick(anchor)`. Clicking it never
|
|
79
|
+
* folds the group. Not shown with `hideLabel` (there is no heading row).
|
|
80
|
+
*/
|
|
81
|
+
action?: {
|
|
82
|
+
icon: Icon;
|
|
83
|
+
label: string;
|
|
84
|
+
expanded?: boolean;
|
|
85
|
+
onClick: (anchor: HTMLElement) => void;
|
|
86
|
+
testid?: string;
|
|
87
|
+
};
|
|
76
88
|
}
|
|
77
89
|
/**
|
|
78
90
|
* Is this item the current page? Exact match for "/", otherwise the path is the
|
|
@@ -447,6 +459,34 @@ type PreactNavGroup = NavGroup<Icon>;
|
|
|
447
459
|
type Navigate = (href: string) => void;
|
|
448
460
|
/** True below 768px. Follows the window as it resizes. */
|
|
449
461
|
declare function useIsMobile(): boolean;
|
|
462
|
+
interface ItemActionsMenuProps {
|
|
463
|
+
/** The menu's items — the same NavAction[] a sidebar row takes (kbd, submenu, input, confirm, danger). */
|
|
464
|
+
actions: NavAction[];
|
|
465
|
+
/** What the menu opens from: positioned from it, and focus returns to it on close. */
|
|
466
|
+
anchor: HTMLElement;
|
|
467
|
+
/** An action was chosen (value = the text, for an input action). The menu has closed. */
|
|
468
|
+
onAction: (actionId: string, value?: string) => void;
|
|
469
|
+
/** The menu closed — by a pick, Escape, a click outside, scroll or resize. Unmount it here. */
|
|
470
|
+
onClose: () => void;
|
|
471
|
+
lang: Lang;
|
|
472
|
+
/** The menu's accessible name, e.g. the conversation's title. */
|
|
473
|
+
label: string;
|
|
474
|
+
/** "auto" (default): below the anchor if it fits, else above. "down": always below. */
|
|
475
|
+
placement?: "auto" | "down";
|
|
476
|
+
/** testids: `<prefix>-menu`, `<prefix>-action-<id>`, `<prefix>-back`, `<prefix>-input`, `<prefix>-save`. Default "item-actions". */
|
|
477
|
+
testidPrefix?: string;
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* F092.29 — the row-action menu on its own, so a title «⌄» and a sidebar row «…»
|
|
481
|
+
* open THE SAME menu (scout #2745). The sidebar's row menu is this component: one
|
|
482
|
+
* implementation, so keyboard, confirm, input and submenu cannot drift apart.
|
|
483
|
+
* Mount it while open; it closes itself through onClose.
|
|
484
|
+
*
|
|
485
|
+
* Keys (F092.21/.22): focus moves into the menu when it opens; arrows move real
|
|
486
|
+
* focus; Enter/Space act on the FOCUSED item; a kbd letter picks; ←/Backspace goes
|
|
487
|
+
* back from a submenu; Escape closes and returns focus to the anchor.
|
|
488
|
+
*/
|
|
489
|
+
declare function ItemActionsMenu(props: ItemActionsMenuProps): JSX.Element;
|
|
450
490
|
interface SidebarProps {
|
|
451
491
|
groups: PreactNavGroup[];
|
|
452
492
|
footer?: PreactNavItem[];
|
|
@@ -500,6 +540,8 @@ interface ShellUser {
|
|
|
500
540
|
picture?: string;
|
|
501
541
|
/** F092.15 — a line under the name, e.g. «Administrator · Enhed Nord». */
|
|
502
542
|
subtitle?: string;
|
|
543
|
+
/** F092.27 — the organisation, e.g. «broberg.ai». Shown ONLY in the menu's top, with a building icon. */
|
|
544
|
+
org?: string;
|
|
503
545
|
}
|
|
504
546
|
interface UserMenuItem {
|
|
505
547
|
id: string;
|
|
@@ -760,4 +802,4 @@ interface ConfirmDialogProps {
|
|
|
760
802
|
}
|
|
761
803
|
declare function ConfirmDialog(p: ConfirmDialogProps): JSX.Element | null;
|
|
762
804
|
|
|
763
|
-
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 };
|
|
805
|
+
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, ItemActionsMenu, type ItemActionsMenuProps, 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
|
@@ -73,6 +73,18 @@ interface NavGroup<Icon = unknown> {
|
|
|
73
73
|
* group is always open, even with a stored fold for this label. Default true.
|
|
74
74
|
*/
|
|
75
75
|
collapsible?: boolean;
|
|
76
|
+
/**
|
|
77
|
+
* F092.28 — a small icon button at the right of the heading row (e.g. «Filtrér og
|
|
78
|
+
* sortér»). The app opens its own menu from `onClick(anchor)`. Clicking it never
|
|
79
|
+
* folds the group. Not shown with `hideLabel` (there is no heading row).
|
|
80
|
+
*/
|
|
81
|
+
action?: {
|
|
82
|
+
icon: Icon;
|
|
83
|
+
label: string;
|
|
84
|
+
expanded?: boolean;
|
|
85
|
+
onClick: (anchor: HTMLElement) => void;
|
|
86
|
+
testid?: string;
|
|
87
|
+
};
|
|
76
88
|
}
|
|
77
89
|
/**
|
|
78
90
|
* Is this item the current page? Exact match for "/", otherwise the path is the
|
|
@@ -447,6 +459,34 @@ type PreactNavGroup = NavGroup<Icon>;
|
|
|
447
459
|
type Navigate = (href: string) => void;
|
|
448
460
|
/** True below 768px. Follows the window as it resizes. */
|
|
449
461
|
declare function useIsMobile(): boolean;
|
|
462
|
+
interface ItemActionsMenuProps {
|
|
463
|
+
/** The menu's items — the same NavAction[] a sidebar row takes (kbd, submenu, input, confirm, danger). */
|
|
464
|
+
actions: NavAction[];
|
|
465
|
+
/** What the menu opens from: positioned from it, and focus returns to it on close. */
|
|
466
|
+
anchor: HTMLElement;
|
|
467
|
+
/** An action was chosen (value = the text, for an input action). The menu has closed. */
|
|
468
|
+
onAction: (actionId: string, value?: string) => void;
|
|
469
|
+
/** The menu closed — by a pick, Escape, a click outside, scroll or resize. Unmount it here. */
|
|
470
|
+
onClose: () => void;
|
|
471
|
+
lang: Lang;
|
|
472
|
+
/** The menu's accessible name, e.g. the conversation's title. */
|
|
473
|
+
label: string;
|
|
474
|
+
/** "auto" (default): below the anchor if it fits, else above. "down": always below. */
|
|
475
|
+
placement?: "auto" | "down";
|
|
476
|
+
/** testids: `<prefix>-menu`, `<prefix>-action-<id>`, `<prefix>-back`, `<prefix>-input`, `<prefix>-save`. Default "item-actions". */
|
|
477
|
+
testidPrefix?: string;
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* F092.29 — the row-action menu on its own, so a title «⌄» and a sidebar row «…»
|
|
481
|
+
* open THE SAME menu (scout #2745). The sidebar's row menu is this component: one
|
|
482
|
+
* implementation, so keyboard, confirm, input and submenu cannot drift apart.
|
|
483
|
+
* Mount it while open; it closes itself through onClose.
|
|
484
|
+
*
|
|
485
|
+
* Keys (F092.21/.22): focus moves into the menu when it opens; arrows move real
|
|
486
|
+
* focus; Enter/Space act on the FOCUSED item; a kbd letter picks; ←/Backspace goes
|
|
487
|
+
* back from a submenu; Escape closes and returns focus to the anchor.
|
|
488
|
+
*/
|
|
489
|
+
declare function ItemActionsMenu(props: ItemActionsMenuProps): JSX.Element;
|
|
450
490
|
interface SidebarProps {
|
|
451
491
|
groups: PreactNavGroup[];
|
|
452
492
|
footer?: PreactNavItem[];
|
|
@@ -500,6 +540,8 @@ interface ShellUser {
|
|
|
500
540
|
picture?: string;
|
|
501
541
|
/** F092.15 — a line under the name, e.g. «Administrator · Enhed Nord». */
|
|
502
542
|
subtitle?: string;
|
|
543
|
+
/** F092.27 — the organisation, e.g. «broberg.ai». Shown ONLY in the menu's top, with a building icon. */
|
|
544
|
+
org?: string;
|
|
503
545
|
}
|
|
504
546
|
interface UserMenuItem {
|
|
505
547
|
id: string;
|
|
@@ -760,4 +802,4 @@ interface ConfirmDialogProps {
|
|
|
760
802
|
}
|
|
761
803
|
declare function ConfirmDialog(p: ConfirmDialogProps): JSX.Element | null;
|
|
762
804
|
|
|
763
|
-
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 };
|
|
805
|
+
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, ItemActionsMenu, type ItemActionsMenuProps, 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
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { useState, useEffect, useRef,
|
|
1
|
+
import { useState, useEffect, useRef, useLayoutEffect, useMemo } from 'preact/hooks';
|
|
2
2
|
import { createBellShell } from '@broberg/notifications/shell';
|
|
3
3
|
import { selectKeyReducer } from '@broberg/ui-controls-core';
|
|
4
4
|
import { getPreference, getPalette, getSurfaces, getBackdrop, onThemeChange, onPaletteChange, onSurfacesChange, onBackdropChange, setPreference, setPalette, PALETTES, PALETTE_LABELS, setSurfaces, setBackdrop } from '@broberg/theme';
|
|
5
|
-
import { jsxs,
|
|
5
|
+
import { jsxs, jsx, Fragment } from 'preact/jsx-runtime';
|
|
6
6
|
|
|
7
7
|
// src/preact.tsx
|
|
8
8
|
|
|
@@ -509,12 +509,22 @@ var IconUser = () => svg(/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
|
509
509
|
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "8", r: "4" }),
|
|
510
510
|
/* @__PURE__ */ jsx("path", { d: "M4 21a8 8 0 0 1 16 0" })
|
|
511
511
|
] }), 14);
|
|
512
|
-
|
|
512
|
+
var IconBuilding = () => svg(/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
513
|
+
/* @__PURE__ */ jsx("rect", { width: "16", height: "20", x: "4", y: "2", rx: "2" }),
|
|
514
|
+
/* @__PURE__ */ jsx("path", { d: "M9 22v-4h6v4" }),
|
|
515
|
+
/* @__PURE__ */ jsx("path", { d: "M8 6h.01M16 6h.01M12 6h.01M12 10h.01M12 14h.01M16 10h.01M16 14h.01M8 10h.01M8 14h.01" })
|
|
516
|
+
] }), 15);
|
|
517
|
+
function ItemActionsMenu(props) {
|
|
513
518
|
const t = TEXT[props.lang];
|
|
514
|
-
const
|
|
515
|
-
const btn = useRef(null);
|
|
519
|
+
const pre = props.testidPrefix ?? "item-actions";
|
|
516
520
|
const menu = useRef(null);
|
|
517
|
-
const [
|
|
521
|
+
const first = [{ kind: "list", actions: props.actions }];
|
|
522
|
+
const [stack, setStack] = useState(first);
|
|
523
|
+
const stackRef = useRef(first);
|
|
524
|
+
const setStackNow = (next) => {
|
|
525
|
+
stackRef.current = next;
|
|
526
|
+
setStack(next);
|
|
527
|
+
};
|
|
518
528
|
const [hi, setHiState] = useState(0);
|
|
519
529
|
const hiRef = useRef(0);
|
|
520
530
|
const setHi = (n) => {
|
|
@@ -533,16 +543,12 @@ function RowActions(props) {
|
|
|
533
543
|
const [value, setValue] = useState("");
|
|
534
544
|
const [pos, setPos] = useState(void 0);
|
|
535
545
|
const view = stack[stack.length - 1];
|
|
536
|
-
const
|
|
537
|
-
const stackRef = useRef([]);
|
|
538
|
-
const setStackNow = (next) => {
|
|
539
|
-
stackRef.current = next;
|
|
540
|
-
setStack(next);
|
|
541
|
-
};
|
|
546
|
+
const closed = useRef(false);
|
|
542
547
|
const close = (refocus = true) => {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
if (refocus)
|
|
548
|
+
if (closed.current) return;
|
|
549
|
+
closed.current = true;
|
|
550
|
+
if (refocus) props.anchor.focus();
|
|
551
|
+
props.onClose();
|
|
546
552
|
};
|
|
547
553
|
const show = (v) => {
|
|
548
554
|
setStackNow([...stackRef.current, v]);
|
|
@@ -567,25 +573,24 @@ function RowActions(props) {
|
|
|
567
573
|
props.onAction(a.id, value);
|
|
568
574
|
};
|
|
569
575
|
useLayoutEffect(() => {
|
|
570
|
-
|
|
571
|
-
const r = btn.current.getBoundingClientRect();
|
|
576
|
+
const r = props.anchor.getBoundingClientRect();
|
|
572
577
|
const vw = window.innerWidth, vh = window.innerHeight, m = 8;
|
|
573
578
|
const w = Math.min(240, vw - 2 * m);
|
|
574
579
|
const h = menu.current?.offsetHeight ?? 0;
|
|
575
580
|
const left = Math.max(m, Math.min(r.right - w, vw - w - m));
|
|
576
|
-
const
|
|
581
|
+
const fitsBelow = r.bottom + 4 + h <= vh - m;
|
|
582
|
+
const top = props.placement === "down" || fitsBelow || r.top - 4 - h < m ? Math.min(r.bottom + 4, vh - m - h) : r.top - 4 - h;
|
|
577
583
|
setPos({ position: "fixed", left: `${left}px`, top: `${Math.max(m, top)}px`, width: `${w}px` });
|
|
578
|
-
}, [
|
|
584
|
+
}, [stack.length]);
|
|
579
585
|
useLayoutEffect(() => {
|
|
580
|
-
if (!
|
|
581
|
-
if (view
|
|
586
|
+
if (!pos) return;
|
|
587
|
+
if (view.kind === "input") menu.current?.querySelector("input")?.focus();
|
|
582
588
|
else items.current[hiRef.current]?.focus();
|
|
583
|
-
}, [
|
|
589
|
+
}, [pos, hi, stack.length]);
|
|
584
590
|
useLayoutEffect(() => {
|
|
585
|
-
if (!open) return;
|
|
586
591
|
const outside = (e) => {
|
|
587
592
|
const n = e.target instanceof Node ? e.target : null;
|
|
588
|
-
if (n && (menu.current?.contains(n) ||
|
|
593
|
+
if (n && (menu.current?.contains(n) || props.anchor.contains(n))) return;
|
|
589
594
|
close(false);
|
|
590
595
|
};
|
|
591
596
|
const early = (e) => {
|
|
@@ -604,8 +609,9 @@ function RowActions(props) {
|
|
|
604
609
|
window.removeEventListener("scroll", outside, true);
|
|
605
610
|
window.removeEventListener("resize", outside);
|
|
606
611
|
};
|
|
607
|
-
}, [
|
|
612
|
+
}, []);
|
|
608
613
|
const onKeyDown = (e) => {
|
|
614
|
+
if (closed.current) return;
|
|
609
615
|
const stack2 = stackRef.current;
|
|
610
616
|
const view2 = stack2[stack2.length - 1];
|
|
611
617
|
if (!view2) return;
|
|
@@ -648,6 +654,82 @@ function RowActions(props) {
|
|
|
648
654
|
}
|
|
649
655
|
};
|
|
650
656
|
onKeyDownRef.current = onKeyDown;
|
|
657
|
+
return /* @__PURE__ */ jsxs(
|
|
658
|
+
"div",
|
|
659
|
+
{
|
|
660
|
+
ref: menu,
|
|
661
|
+
class: "bas-rowmenu",
|
|
662
|
+
role: "menu",
|
|
663
|
+
tabIndex: -1,
|
|
664
|
+
"aria-label": view.kind === "list" && view.title ? view.title : props.label,
|
|
665
|
+
"data-testid": `${pre}-menu`,
|
|
666
|
+
style: pos ?? { position: "fixed", visibility: "hidden" },
|
|
667
|
+
onKeyDown,
|
|
668
|
+
children: [
|
|
669
|
+
stack.length > 1 ? /* @__PURE__ */ jsxs("button", { type: "button", class: "bas-rowmenu__back", "data-testid": `${pre}-back`, "aria-label": t.back, onClick: back, children: [
|
|
670
|
+
/* @__PURE__ */ jsx(IconChevronLeft, {}),
|
|
671
|
+
/* @__PURE__ */ jsx("span", { children: view.kind === "list" ? view.title : view.action.label })
|
|
672
|
+
] }) : null,
|
|
673
|
+
view.kind === "list" ? view.actions.map((a, i) => /* @__PURE__ */ jsxs(
|
|
674
|
+
"button",
|
|
675
|
+
{
|
|
676
|
+
type: "button",
|
|
677
|
+
role: "menuitem",
|
|
678
|
+
ref: (el) => {
|
|
679
|
+
items.current[i] = el;
|
|
680
|
+
},
|
|
681
|
+
tabIndex: i === hi ? 0 : -1,
|
|
682
|
+
class: "bas-rowmenu__item" + (i === hi ? " is-active" : "") + (a.danger ? " is-danger" : "") + (armed === a.id ? " is-armed" : ""),
|
|
683
|
+
"data-testid": `${pre}-action-${a.id}`,
|
|
684
|
+
onMouseEnter: () => setHi(i),
|
|
685
|
+
onFocus: () => setHi(i),
|
|
686
|
+
onClick: () => pick(a),
|
|
687
|
+
children: [
|
|
688
|
+
a.icon ? /* @__PURE__ */ jsx("span", { class: "bas-rowmenu__icon", children: a.icon }) : null,
|
|
689
|
+
/* @__PURE__ */ jsx("span", { class: "bas-rowmenu__label", children: armed === a.id && a.confirm ? a.confirm.label : a.label }),
|
|
690
|
+
a.submenu?.length ? /* @__PURE__ */ jsx(IconChevronRight, {}) : a.kbd ? /* @__PURE__ */ jsx("kbd", { class: "bas-rowmenu__kbd", children: a.kbd.toUpperCase() }) : null
|
|
691
|
+
]
|
|
692
|
+
},
|
|
693
|
+
a.id
|
|
694
|
+
)) : /* @__PURE__ */ jsxs(
|
|
695
|
+
"form",
|
|
696
|
+
{
|
|
697
|
+
class: "bas-rowmenu__form",
|
|
698
|
+
"data-testid": `${pre}-form`,
|
|
699
|
+
onSubmit: (e) => {
|
|
700
|
+
e.preventDefault();
|
|
701
|
+
save(view.action);
|
|
702
|
+
},
|
|
703
|
+
children: [
|
|
704
|
+
/* @__PURE__ */ jsx(
|
|
705
|
+
"input",
|
|
706
|
+
{
|
|
707
|
+
class: "bas-rowmenu__input",
|
|
708
|
+
"data-testid": `${pre}-input`,
|
|
709
|
+
"aria-label": view.action.label,
|
|
710
|
+
value,
|
|
711
|
+
placeholder: view.action.input.placeholder,
|
|
712
|
+
onInput: (e) => setValue(e.currentTarget.value)
|
|
713
|
+
}
|
|
714
|
+
),
|
|
715
|
+
/* @__PURE__ */ jsx("button", { type: "submit", class: "bas-rowmenu__save", "data-testid": `${pre}-save`, children: view.action.input.saveLabel ?? t.save })
|
|
716
|
+
]
|
|
717
|
+
}
|
|
718
|
+
)
|
|
719
|
+
]
|
|
720
|
+
}
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
function RowActions(props) {
|
|
724
|
+
const t = TEXT[props.lang];
|
|
725
|
+
const id = props.item.id;
|
|
726
|
+
const btn = useRef(null);
|
|
727
|
+
const [open, setOpenState] = useState(false);
|
|
728
|
+
const openRef = useRef(false);
|
|
729
|
+
const setOpen = (v) => {
|
|
730
|
+
openRef.current = v;
|
|
731
|
+
setOpenState(v);
|
|
732
|
+
};
|
|
651
733
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
652
734
|
/* @__PURE__ */ jsx(
|
|
653
735
|
"button",
|
|
@@ -659,73 +741,24 @@ function RowActions(props) {
|
|
|
659
741
|
"aria-label": `${t.moreFor} ${props.item.label}`,
|
|
660
742
|
"aria-haspopup": "menu",
|
|
661
743
|
"aria-expanded": open,
|
|
662
|
-
onClick: () =>
|
|
744
|
+
onClick: () => {
|
|
745
|
+
if (!openRef.current) return setOpen(true);
|
|
746
|
+
setOpen(false);
|
|
747
|
+
btn.current?.focus();
|
|
748
|
+
},
|
|
663
749
|
children: /* @__PURE__ */ jsx(IconMore, {})
|
|
664
750
|
}
|
|
665
751
|
),
|
|
666
|
-
|
|
667
|
-
|
|
752
|
+
open && btn.current ? /* @__PURE__ */ jsx(
|
|
753
|
+
ItemActionsMenu,
|
|
668
754
|
{
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
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
|
-
]
|
|
755
|
+
actions: props.item.actions,
|
|
756
|
+
anchor: btn.current,
|
|
757
|
+
lang: props.lang,
|
|
758
|
+
label: props.item.label,
|
|
759
|
+
testidPrefix: `sidebar-item-${id}`,
|
|
760
|
+
onAction: props.onAction,
|
|
761
|
+
onClose: () => setOpen(false)
|
|
729
762
|
}
|
|
730
763
|
) : null
|
|
731
764
|
] });
|
|
@@ -910,10 +943,32 @@ function Sidebar(p) {
|
|
|
910
943
|
"aria-label": g.hideLabel ? g.label : void 0,
|
|
911
944
|
"data-testid": g.hideLabel ? `sidebar-group-${slug}-unlabelled` : void 0,
|
|
912
945
|
children: [
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
/* @__PURE__ */ jsx("span", { children: g.label })
|
|
916
|
-
|
|
946
|
+
(() => {
|
|
947
|
+
if (g.hideLabel) return null;
|
|
948
|
+
const heading = g.collapsible === false ? /* @__PURE__ */ jsx("div", { class: "bas-group__label bas-group__label--static", "data-testid": `sidebar-group-${slug}`, children: /* @__PURE__ */ jsx("span", { children: g.label }) }) : /* @__PURE__ */ jsxs("button", { type: "button", class: "bas-group__label", "data-testid": `sidebar-group-${slug}`, "aria-expanded": open, onClick: () => toggleGroup(g.label), children: [
|
|
949
|
+
/* @__PURE__ */ jsx(IconChevron, { open }),
|
|
950
|
+
/* @__PURE__ */ jsx("span", { children: g.label })
|
|
951
|
+
] });
|
|
952
|
+
if (!g.action) return heading;
|
|
953
|
+
const a = g.action;
|
|
954
|
+
return /* @__PURE__ */ jsxs("div", { class: "bas-group__head", children: [
|
|
955
|
+
heading,
|
|
956
|
+
/* @__PURE__ */ jsx(
|
|
957
|
+
"button",
|
|
958
|
+
{
|
|
959
|
+
type: "button",
|
|
960
|
+
class: "bas-group__action",
|
|
961
|
+
"data-testid": a.testid ?? `sidebar-group-${slug}-action`,
|
|
962
|
+
"aria-label": a.label,
|
|
963
|
+
title: a.label,
|
|
964
|
+
"aria-haspopup": "menu",
|
|
965
|
+
"aria-expanded": a.expanded ?? false,
|
|
966
|
+
onClick: (e) => a.onClick(e.currentTarget),
|
|
967
|
+
children: a.icon
|
|
968
|
+
}
|
|
969
|
+
)
|
|
970
|
+
] });
|
|
971
|
+
})(),
|
|
917
972
|
open ? g.items.map(item) : null,
|
|
918
973
|
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: [
|
|
919
974
|
/* @__PURE__ */ jsx("div", { class: "bas-section__label", children: sec.label }),
|
|
@@ -1273,7 +1328,11 @@ function UserMenu(p) {
|
|
|
1273
1328
|
/* @__PURE__ */ jsxs("div", { class: "bas-menu__whotext", children: [
|
|
1274
1329
|
p.user.name ? /* @__PURE__ */ jsx("div", { class: "bas-menu__name", children: p.user.name }) : null,
|
|
1275
1330
|
p.user.subtitle ? /* @__PURE__ */ jsx("div", { class: "bas-menu__sub", children: p.user.subtitle }) : null,
|
|
1276
|
-
p.user.email ? /* @__PURE__ */ jsx("div", { class: "bas-menu__email", children: p.user.email }) : null
|
|
1331
|
+
p.user.email ? /* @__PURE__ */ jsx("div", { class: "bas-menu__email", children: p.user.email }) : null,
|
|
1332
|
+
p.user.org ? /* @__PURE__ */ jsxs("div", { class: "bas-menu__org", "data-testid": "user-menu-org", children: [
|
|
1333
|
+
/* @__PURE__ */ jsx(IconBuilding, {}),
|
|
1334
|
+
/* @__PURE__ */ jsx("span", { children: p.user.org })
|
|
1335
|
+
] }) : null
|
|
1277
1336
|
] })
|
|
1278
1337
|
] }),
|
|
1279
1338
|
/* @__PURE__ */ jsx("div", { class: "bas-sep" }),
|
|
@@ -1995,6 +2054,6 @@ function ConfirmDialog(p) {
|
|
|
1995
2054
|
);
|
|
1996
2055
|
}
|
|
1997
2056
|
|
|
1998
|
-
export { AVATAR_MAX_BYTES, AVATAR_TYPES, AccountError, AccountPage, AnalyticsError, AnalyticsPage, AppShell, Avatar, ConfirmDialog, NotificationBell, PageHeader, PageTabs, Sidebar, TEXT, TenantLoadError, TenantSwitcher, TopBar, UserMenu, accountErrorKind, activeNavLabel, analyticsErrorKind, badgeLabel, changePct, checkAvatarFile, copenhagenDay, createFetchAccountAdapter, createFetchAnalyticsAdapter, createFetchNotificationSource, createFetchTenantAdapter, createMemoryNotificationSource, defaultReauthHref, fillDays, initials, isActivePath, isExternal, readFlag, readSet, relativeTime, safeNavigate, useIsMobile, writeFlag, writeSet };
|
|
2057
|
+
export { AVATAR_MAX_BYTES, AVATAR_TYPES, AccountError, AccountPage, AnalyticsError, AnalyticsPage, AppShell, Avatar, ConfirmDialog, ItemActionsMenu, NotificationBell, PageHeader, PageTabs, Sidebar, TEXT, TenantLoadError, TenantSwitcher, TopBar, UserMenu, accountErrorKind, activeNavLabel, analyticsErrorKind, badgeLabel, changePct, checkAvatarFile, copenhagenDay, createFetchAccountAdapter, createFetchAnalyticsAdapter, createFetchNotificationSource, createFetchTenantAdapter, createMemoryNotificationSource, defaultReauthHref, fillDays, initials, isActivePath, isExternal, readFlag, readSet, relativeTime, safeNavigate, useIsMobile, writeFlag, writeSet };
|
|
1999
2058
|
//# sourceMappingURL=preact.js.map
|
|
2000
2059
|
//# sourceMappingURL=preact.js.map
|