@boost33/ui 0.8.0 → 0.9.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/CHANGELOG.md +214 -123
- package/dist/backdrop-icons.d.ts +53 -0
- package/dist/backdrop-icons.js +62 -0
- package/dist/backdrop-icons.js.map +1 -0
- package/dist/backdrop.css +1 -0
- package/dist/backdrop.d.ts +46 -0
- package/dist/backdrop.js +127 -0
- package/dist/backdrop.js.map +1 -0
- package/dist/components/AccountMenu/AccountMenu.d.ts +42 -32
- package/dist/components/AccountMenu/AccountMenu.js +66 -48
- package/dist/components/AccountMenu/AccountMenu.js.map +1 -1
- package/dist/components/AccountMenu/AccountMenu.module.css.js +44 -38
- package/dist/components/AccountMenu/AccountMenu.module.css.js.map +1 -1
- package/dist/components/AccountMenu/StandardAccountMenu.d.ts +87 -0
- package/dist/components/AccountMenu/StandardAccountMenu.js +86 -0
- package/dist/components/AccountMenu/StandardAccountMenu.js.map +1 -0
- package/dist/components/AccountMenu/icons.d.ts +2 -2
- package/dist/components/AccountMenu/icons.js +21 -8
- package/dist/components/AccountMenu/icons.js.map +1 -1
- package/dist/components/AccountMenu/initials.d.ts +11 -0
- package/dist/components/AccountMenu/initials.js +8 -0
- package/dist/components/AccountMenu/initials.js.map +1 -0
- package/dist/components/AccountMenu/labels.d.ts +64 -3
- package/dist/components/AccountMenu/labels.js +33 -5
- package/dist/components/AccountMenu/labels.js.map +1 -1
- package/dist/components/AccountMenu/use-menu.d.ts +34 -0
- package/dist/components/AccountMenu/use-menu.js +102 -0
- package/dist/components/AccountMenu/use-menu.js.map +1 -0
- package/dist/components/Backdrop/BackdropAppMenu.d.ts +55 -0
- package/dist/components/Backdrop/BackdropAppMenu.js +79 -0
- package/dist/components/Backdrop/BackdropAppMenu.js.map +1 -0
- package/dist/components/Backdrop/BackdropAppMenu.module.css.js +48 -0
- package/dist/components/Backdrop/BackdropAppMenu.module.css.js.map +1 -0
- package/dist/components/Backdrop/BackdropBasics.d.ts +129 -0
- package/dist/components/Backdrop/BackdropBasics.js +130 -0
- package/dist/components/Backdrop/BackdropBasics.js.map +1 -0
- package/dist/components/Backdrop/BackdropBasics.module.css.js +71 -0
- package/dist/components/Backdrop/BackdropBasics.module.css.js.map +1 -0
- package/dist/components/Backdrop/BackdropButton.d.ts +63 -0
- package/dist/components/Backdrop/BackdropButton.js +59 -0
- package/dist/components/Backdrop/BackdropButton.js.map +1 -0
- package/dist/components/Backdrop/BackdropButton.module.css.js +41 -0
- package/dist/components/Backdrop/BackdropButton.module.css.js.map +1 -0
- package/dist/components/Backdrop/BackdropCharts.d.ts +158 -0
- package/dist/components/Backdrop/BackdropCharts.js +222 -0
- package/dist/components/Backdrop/BackdropCharts.js.map +1 -0
- package/dist/components/Backdrop/BackdropCharts.module.css.js +109 -0
- package/dist/components/Backdrop/BackdropCharts.module.css.js.map +1 -0
- package/dist/components/Backdrop/BackdropChrome.d.ts +26 -0
- package/dist/components/Backdrop/BackdropChrome.js +35 -0
- package/dist/components/Backdrop/BackdropChrome.js.map +1 -0
- package/dist/components/Backdrop/BackdropConversation.d.ts +67 -0
- package/dist/components/Backdrop/BackdropConversation.js +76 -0
- package/dist/components/Backdrop/BackdropConversation.js.map +1 -0
- package/dist/components/Backdrop/BackdropConversation.module.css.js +44 -0
- package/dist/components/Backdrop/BackdropConversation.module.css.js.map +1 -0
- package/dist/components/Backdrop/BackdropData.d.ts +195 -0
- package/dist/components/Backdrop/BackdropData.js +204 -0
- package/dist/components/Backdrop/BackdropData.js.map +1 -0
- package/dist/components/Backdrop/BackdropData.module.css.js +135 -0
- package/dist/components/Backdrop/BackdropData.module.css.js.map +1 -0
- package/dist/components/Backdrop/BackdropDisplay.d.ts +122 -0
- package/dist/components/Backdrop/BackdropDisplay.js +80 -0
- package/dist/components/Backdrop/BackdropDisplay.js.map +1 -0
- package/dist/components/Backdrop/BackdropDisplay.module.css.js +80 -0
- package/dist/components/Backdrop/BackdropDisplay.module.css.js.map +1 -0
- package/dist/components/Backdrop/BackdropFields.d.ts +122 -0
- package/dist/components/Backdrop/BackdropFields.js +196 -0
- package/dist/components/Backdrop/BackdropFields.js.map +1 -0
- package/dist/components/Backdrop/BackdropFields.module.css.js +104 -0
- package/dist/components/Backdrop/BackdropFields.module.css.js.map +1 -0
- package/dist/components/Backdrop/BackdropFlows.d.ts +128 -0
- package/dist/components/Backdrop/BackdropFlows.js +156 -0
- package/dist/components/Backdrop/BackdropFlows.js.map +1 -0
- package/dist/components/Backdrop/BackdropFlows.module.css.js +80 -0
- package/dist/components/Backdrop/BackdropFlows.module.css.js.map +1 -0
- package/dist/components/Backdrop/BackdropFrame.d.ts +36 -0
- package/dist/components/Backdrop/BackdropFrame.js +15 -0
- package/dist/components/Backdrop/BackdropFrame.js.map +1 -0
- package/dist/components/Backdrop/BackdropFrame.module.css.js +14 -0
- package/dist/components/Backdrop/BackdropFrame.module.css.js.map +1 -0
- package/dist/components/Backdrop/BackdropGlobalPanels.d.ts +272 -0
- package/dist/components/Backdrop/BackdropGlobalPanels.js +264 -0
- package/dist/components/Backdrop/BackdropGlobalPanels.js.map +1 -0
- package/dist/components/Backdrop/BackdropGlobalPanels.module.css.js +201 -0
- package/dist/components/Backdrop/BackdropGlobalPanels.module.css.js.map +1 -0
- package/dist/components/Backdrop/BackdropHub.d.ts +80 -0
- package/dist/components/Backdrop/BackdropHub.js +92 -0
- package/dist/components/Backdrop/BackdropHub.js.map +1 -0
- package/dist/components/Backdrop/BackdropHub.module.css.js +40 -0
- package/dist/components/Backdrop/BackdropHub.module.css.js.map +1 -0
- package/dist/components/Backdrop/BackdropLists.d.ts +165 -0
- package/dist/components/Backdrop/BackdropLists.js +162 -0
- package/dist/components/Backdrop/BackdropLists.js.map +1 -0
- package/dist/components/Backdrop/BackdropLists.module.css.js +114 -0
- package/dist/components/Backdrop/BackdropLists.module.css.js.map +1 -0
- package/dist/components/Backdrop/BackdropOverlays.d.ts +175 -0
- package/dist/components/Backdrop/BackdropOverlays.js +252 -0
- package/dist/components/Backdrop/BackdropOverlays.js.map +1 -0
- package/dist/components/Backdrop/BackdropOverlays.module.css.js +104 -0
- package/dist/components/Backdrop/BackdropOverlays.module.css.js.map +1 -0
- package/dist/components/Backdrop/BackdropPage.d.ts +73 -0
- package/dist/components/Backdrop/BackdropPage.js +65 -0
- package/dist/components/Backdrop/BackdropPage.js.map +1 -0
- package/dist/components/Backdrop/BackdropPage.module.css.js +46 -0
- package/dist/components/Backdrop/BackdropPage.module.css.js.map +1 -0
- package/dist/components/Backdrop/BackdropPatterns.d.ts +224 -0
- package/dist/components/Backdrop/BackdropPatterns.js +202 -0
- package/dist/components/Backdrop/BackdropPatterns.js.map +1 -0
- package/dist/components/Backdrop/BackdropPatterns.module.css.js +114 -0
- package/dist/components/Backdrop/BackdropPatterns.module.css.js.map +1 -0
- package/dist/components/Backdrop/BackdropRail.d.ts +63 -0
- package/dist/components/Backdrop/BackdropRail.js +98 -0
- package/dist/components/Backdrop/BackdropRail.js.map +1 -0
- package/dist/components/Backdrop/BackdropRail.module.css.js +42 -0
- package/dist/components/Backdrop/BackdropRail.module.css.js.map +1 -0
- package/dist/components/Backdrop/BackdropSheets.d.ts +68 -0
- package/dist/components/Backdrop/BackdropSheets.js +75 -0
- package/dist/components/Backdrop/BackdropSheets.js.map +1 -0
- package/dist/components/Backdrop/BackdropSheets.module.css.js +42 -0
- package/dist/components/Backdrop/BackdropSheets.module.css.js.map +1 -0
- package/dist/components/Backdrop/BackdropTabs.d.ts +20 -0
- package/dist/components/Backdrop/BackdropTabs.js +31 -0
- package/dist/components/Backdrop/BackdropTabs.js.map +1 -0
- package/dist/components/Backdrop/BackdropTabs.module.css.js +16 -0
- package/dist/components/Backdrop/BackdropTabs.module.css.js.map +1 -0
- package/dist/components/Backdrop/BackdropTooltip.d.ts +21 -0
- package/dist/components/Backdrop/BackdropTooltip.js +15 -0
- package/dist/components/Backdrop/BackdropTooltip.js.map +1 -0
- package/dist/components/Backdrop/BackdropTooltip.module.css.js +18 -0
- package/dist/components/Backdrop/BackdropTooltip.module.css.js.map +1 -0
- package/dist/components/Backdrop/BackdropTopStrip.d.ts +65 -0
- package/dist/components/Backdrop/BackdropTopStrip.js +46 -0
- package/dist/components/Backdrop/BackdropTopStrip.js.map +1 -0
- package/dist/components/Backdrop/BackdropTopStrip.module.css.js +40 -0
- package/dist/components/Backdrop/BackdropTopStrip.module.css.js.map +1 -0
- package/dist/components/Backdrop/cx.d.ts +2 -0
- package/dist/components/Backdrop/cx.js +7 -0
- package/dist/components/Backdrop/cx.js.map +1 -0
- package/dist/components/Backdrop/icons.d.ts +70 -0
- package/dist/components/Backdrop/icons.js +103 -0
- package/dist/components/Backdrop/icons.js.map +1 -0
- package/dist/components/TopBar/TopBar.d.ts +8 -1
- package/dist/components/TopBar/TopBar.js +19 -16
- package/dist/components/TopBar/TopBar.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +40 -36
- package/dist/index.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +23 -4
|
@@ -6,13 +6,17 @@ import { AccountMenuLabels } from './labels';
|
|
|
6
6
|
* menu reads no session and fetches nothing (CLAUDE.md).
|
|
7
7
|
*/
|
|
8
8
|
export interface AccountMenuPerson {
|
|
9
|
-
/**
|
|
10
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Their name, e.g. "Sofia Marques". Optional: Identity may hold none. Without
|
|
11
|
+
* it the name line reads "You" (`labels.unnamed`) and the initials come from
|
|
12
|
+
* the email, as the Hub's do.
|
|
13
|
+
*/
|
|
14
|
+
name?: string;
|
|
11
15
|
/** Their email, on the line beneath. */
|
|
12
16
|
email: string;
|
|
13
17
|
/**
|
|
14
|
-
* An avatar image
|
|
15
|
-
* `--color-
|
|
18
|
+
* An avatar image, on the trigger and in the person block. Omit it and both
|
|
19
|
+
* circles carry the person's initials on `--color-neutral-300`, as the Hub's do.
|
|
16
20
|
*
|
|
17
21
|
* The image is decorative when it renders: it takes an empty `alt`, because the
|
|
18
22
|
* person's name is on the line beside it and a name here would make a screen
|
|
@@ -27,8 +31,11 @@ export interface AccountMenuPerson {
|
|
|
27
31
|
export interface AccountMenuOrg {
|
|
28
32
|
/** The organisation's name, e.g. "Acme Coaching". */
|
|
29
33
|
name: string;
|
|
30
|
-
/**
|
|
31
|
-
|
|
34
|
+
/**
|
|
35
|
+
* The initials on the tile beside it, e.g. "AC". Decorative -- the name is next
|
|
36
|
+
* to it. Omit them and they are taken from the name.
|
|
37
|
+
*/
|
|
38
|
+
initials?: string;
|
|
32
39
|
/**
|
|
33
40
|
* The role pill at the right of the strip, e.g. "Admin". Omit it and no pill is
|
|
34
41
|
* drawn -- it is Identity's word for what this person is in this organisation,
|
|
@@ -81,8 +88,13 @@ export interface AccountMenuGroup {
|
|
|
81
88
|
export interface AccountMenuProps {
|
|
82
89
|
/** The signed-in person. */
|
|
83
90
|
person: AccountMenuPerson;
|
|
84
|
-
/**
|
|
85
|
-
|
|
91
|
+
/**
|
|
92
|
+
* The organisation this person is acting in. Omit it and no strip is drawn: a
|
|
93
|
+
* person on no membership would otherwise get an empty dark tile where the
|
|
94
|
+
* company goes, which reads as a company whose name failed to load (the Hub's
|
|
95
|
+
* reasoning).
|
|
96
|
+
*/
|
|
97
|
+
org?: AccountMenuOrg;
|
|
86
98
|
/** The row groups, in order. A hairline is drawn above each one. */
|
|
87
99
|
groups: AccountMenuGroup[];
|
|
88
100
|
/**
|
|
@@ -92,6 +104,11 @@ export interface AccountMenuProps {
|
|
|
92
104
|
* looks like), keeps its default.
|
|
93
105
|
*/
|
|
94
106
|
labels?: Partial<AccountMenuLabels>;
|
|
107
|
+
/**
|
|
108
|
+
* Draw the menu open on first render, without moving focus. For the specimen
|
|
109
|
+
* and for screenshots; an app leaves it unset.
|
|
110
|
+
*/
|
|
111
|
+
defaultOpen?: boolean;
|
|
95
112
|
/**
|
|
96
113
|
* The element a row with an `href` renders through. Defaults to a plain `a`; a
|
|
97
114
|
* router-owning consumer passes its own so the rows route client-side with the
|
|
@@ -101,31 +118,24 @@ export interface AccountMenuProps {
|
|
|
101
118
|
linkComponent?: ElementType;
|
|
102
119
|
}
|
|
103
120
|
/**
|
|
104
|
-
* The account menu:
|
|
105
|
-
*
|
|
121
|
+
* The account menu: the avatar button in the top bar's end group, and the menu it
|
|
122
|
+
* opens -- who you are, which organisation you are in, the way into Profile,
|
|
123
|
+
* Security and Settings, Feedback and Docs, and the way out (B33-493).
|
|
106
124
|
*
|
|
107
|
-
* **
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
* and a halo behind the avatar; 103 draws none of the three) and about where the
|
|
114
|
-
* panel sits relative to it (004: 8px below, 16px right inset, overhanging the
|
|
115
|
-
* avatar by 4px; 103: 10px below, 20px inset, flush). CLAUDE.md puts that choice
|
|
116
|
-
* with the person at the fidelity gate, and says plainly that an agent picking
|
|
117
|
-
* one has invented a referent. So the anchor is not here, `TopBar` is untouched,
|
|
118
|
-
* and the specimen positions the panel in its own stylesheet.
|
|
125
|
+
* **Stage B, and the referent is the Hub's working menu** (boost33-hub
|
|
126
|
+
* src/app/account-menu.tsx), named by Jamal on 5 Nov 2026 so that every app's
|
|
127
|
+
* avatar opens the same menu. The panel is Stage A's, which was drawn from the
|
|
128
|
+
* same Paper artboard the Hub built from (103 · 11); the trigger and the placement
|
|
129
|
+
* are the Hub's: a 26px avatar, the panel 10px below it and aligned to its right
|
|
130
|
+
* edge.
|
|
119
131
|
*
|
|
120
|
-
* A
|
|
121
|
-
* role on
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
* arrive with the trigger in Stage B.
|
|
132
|
+
* A menu button, not a disclosure: `role="menu"` on the panel and
|
|
133
|
+
* `role="menuitem"` on every row, with the Hub's keyboard model (./use-menu.ts).
|
|
134
|
+
* Choosing a row closes the menu, so a client-side navigation does not leave it
|
|
135
|
+
* open over the page it just opened.
|
|
125
136
|
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
* into the spec are what binds, not Paper.
|
|
137
|
+
* Most apps want `StandardAccountMenu`, which builds the standard rows from an
|
|
138
|
+
* identity URL, a Hub URL and the person's role. This is the lower level: any
|
|
139
|
+
* rows, as groups.
|
|
130
140
|
*/
|
|
131
|
-
export declare function AccountMenu({ person, org, groups, labels, linkComponent }: AccountMenuProps): import("react").JSX.Element;
|
|
141
|
+
export declare function AccountMenu({ person, org, groups, labels, defaultOpen, linkComponent }: AccountMenuProps): import("react").JSX.Element;
|
|
@@ -1,66 +1,84 @@
|
|
|
1
|
-
|
|
2
|
-
import { Fragment as
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs as r, jsx as a, Fragment as b } from "react/jsx-runtime";
|
|
3
|
+
import { Fragment as w } from "react";
|
|
4
|
+
import { ACCOUNT_MENU_ICONS as x } from "./icons.js";
|
|
5
|
+
import { initials as m } from "./initials.js";
|
|
6
|
+
import { resolveAccountMenuLabels as U } from "./labels.js";
|
|
7
|
+
import { useMenu as y } from "./use-menu.js";
|
|
8
|
+
import e from "./AccountMenu.module.css.js";
|
|
9
|
+
function P({ person: i, org: l, groups: p, labels: h, defaultOpen: u, linkComponent: N }) {
|
|
10
|
+
const o = U(h), v = N ?? "a", s = y({ defaultOpen: u }), c = m(i.name ?? i.email);
|
|
11
|
+
function g(n) {
|
|
12
|
+
const t = n.description === void 0, f = n.icon === void 0 ? null : x[n.icon](e.icon), d = /* @__PURE__ */ r(b, { children: [
|
|
13
|
+
/* @__PURE__ */ a("span", { className: e.lane, "aria-hidden": "true", children: f }),
|
|
14
|
+
/* @__PURE__ */ r("span", { className: e.rowText, children: [
|
|
15
|
+
/* @__PURE__ */ a("span", { className: e.rowLabel, children: n.label }),
|
|
16
|
+
n.description !== void 0 && /* @__PURE__ */ a("span", { className: e.rowDescription, children: n.description })
|
|
14
17
|
] })
|
|
15
18
|
] });
|
|
16
|
-
return n.href === void 0 ? /* @__PURE__ */
|
|
19
|
+
return n.href === void 0 ? /* @__PURE__ */ a(
|
|
17
20
|
"button",
|
|
18
21
|
{
|
|
19
22
|
type: "button",
|
|
20
|
-
|
|
23
|
+
role: "menuitem",
|
|
24
|
+
tabIndex: -1,
|
|
25
|
+
className: e.row,
|
|
21
26
|
"data-item": n.id,
|
|
22
|
-
"data-single-line":
|
|
23
|
-
children:
|
|
27
|
+
"data-single-line": t ? "true" : void 0,
|
|
28
|
+
children: d
|
|
24
29
|
}
|
|
25
|
-
) : /* @__PURE__ */
|
|
26
|
-
|
|
30
|
+
) : /* @__PURE__ */ a(
|
|
31
|
+
v,
|
|
27
32
|
{
|
|
28
|
-
|
|
33
|
+
role: "menuitem",
|
|
34
|
+
tabIndex: -1,
|
|
35
|
+
className: e.row,
|
|
29
36
|
href: n.href,
|
|
30
37
|
"data-item": n.id,
|
|
31
|
-
"data-single-line":
|
|
32
|
-
children:
|
|
38
|
+
"data-single-line": t ? "true" : void 0,
|
|
39
|
+
children: d
|
|
33
40
|
}
|
|
34
41
|
);
|
|
35
42
|
}
|
|
36
|
-
return /* @__PURE__ */
|
|
37
|
-
/* @__PURE__ */
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
43
|
+
return /* @__PURE__ */ r("div", { className: e.root, ...s.rootProps, children: [
|
|
44
|
+
/* @__PURE__ */ a("button", { ...s.triggerProps, type: "button", className: e.trigger, "aria-label": o.trigger, children: i.avatarUrl === void 0 ? /* @__PURE__ */ a("span", { "aria-hidden": "true", children: c }) : /* @__PURE__ */ a("img", { className: e.avatarImage, src: i.avatarUrl, alt: "" }) }),
|
|
45
|
+
s.open && /* @__PURE__ */ r(
|
|
46
|
+
"div",
|
|
47
|
+
{
|
|
48
|
+
...s.menuProps,
|
|
49
|
+
className: e.panel,
|
|
50
|
+
"aria-label": o.panel,
|
|
51
|
+
onClick: (n) => {
|
|
52
|
+
n.target.closest("[role='menuitem']") && s.close(!1);
|
|
53
|
+
},
|
|
54
|
+
children: [
|
|
55
|
+
/* @__PURE__ */ r("div", { className: e.person, children: [
|
|
56
|
+
i.avatarUrl === void 0 ? /* @__PURE__ */ a("span", { className: e.personAvatar, "aria-hidden": "true", children: c }) : (
|
|
57
|
+
// Empty alt, not a name: the person's name is on the next line in.
|
|
58
|
+
/* @__PURE__ */ a("img", { className: e.personAvatar, src: i.avatarUrl, alt: "" })
|
|
59
|
+
),
|
|
60
|
+
/* @__PURE__ */ r("span", { className: e.personText, children: [
|
|
61
|
+
/* @__PURE__ */ a("span", { className: e.personName, children: i.name ?? o.unnamed }),
|
|
62
|
+
/* @__PURE__ */ a("span", { className: e.personEmail, children: i.email })
|
|
63
|
+
] })
|
|
64
|
+
] }),
|
|
65
|
+
l !== void 0 && /* @__PURE__ */ r("div", { className: e.org, children: [
|
|
66
|
+
/* @__PURE__ */ r("span", { className: e.orgIdentity, children: [
|
|
67
|
+
/* @__PURE__ */ a("span", { className: e.orgTile, "aria-hidden": "true", children: l.initials ?? m(l.name) }),
|
|
68
|
+
/* @__PURE__ */ a("span", { className: e.orgName, children: l.name })
|
|
69
|
+
] }),
|
|
70
|
+
l.role !== void 0 && /* @__PURE__ */ a("span", { className: e.orgRole, children: l.role })
|
|
71
|
+
] }),
|
|
72
|
+
p.map((n) => /* @__PURE__ */ r(w, { children: [
|
|
73
|
+
/* @__PURE__ */ a("hr", { className: e.divider }),
|
|
74
|
+
/* @__PURE__ */ a("ul", { className: e.rows, role: "none", children: n.items.map((t) => /* @__PURE__ */ a("li", { role: "none", children: g(t) }, t.id)) })
|
|
75
|
+
] }, n.id))
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
)
|
|
61
79
|
] });
|
|
62
80
|
}
|
|
63
81
|
export {
|
|
64
|
-
|
|
82
|
+
P as AccountMenu
|
|
65
83
|
};
|
|
66
84
|
//# sourceMappingURL=AccountMenu.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountMenu.js","sources":["../../../src/components/AccountMenu/AccountMenu.tsx"],"sourcesContent":["// No \"use client\", and that is Stage A's whole shape rather than an oversight.\n// This renders the panel and nothing else: no open/closed state, no handlers, no\n// useAnchoredOverlay, no keyboard handling. So it stays a Server Component, a\n// consumer pays no client bundle for it, and tests/guards.test.ts -- which pins\n// the built package's \"use client\" modules to exactly three -- stays green\n// without being edited. Stage B is where that changes, deliberately and with the\n// guard's expectation updated in the same change.\n\nimport { Fragment } from 'react'\nimport type { ElementType, ReactElement } from 'react'\nimport { ACCOUNT_MENU_ICONS, type AccountMenuIcon } from './icons'\nimport { resolveAccountMenuLabels, type AccountMenuLabels } from './labels'\nimport styles from './AccountMenu.module.css'\n\n/**\n * The signed-in person, at the top of the panel. Supplied by the consumer -- the\n * menu reads no session and fetches nothing (CLAUDE.md).\n */\nexport interface AccountMenuPerson {\n /** Their name, e.g. \"Sofia Marques\". */\n name: string\n /** Their email, on the line beneath. */\n email: string\n /**\n * An avatar image. Omit it -- the drawing does -- and the circle draws bare, in\n * `--color-border-strong`.\n *\n * The image is decorative when it renders: it takes an empty `alt`, because the\n * person's name is on the line beside it and a name here would make a screen\n * reader say who you are twice. Same reasoning as `ProductMark`'s default.\n */\n avatarUrl?: string\n}\n\n/**\n * The organisation strip under the person: which account you are acting in.\n * Identity's data, supplied per render, never looked up here (ADR-0003).\n */\nexport interface AccountMenuOrg {\n /** The organisation's name, e.g. \"Acme Coaching\". */\n name: string\n /** The initials on the tile beside it, e.g. \"AC\". Decorative -- the name is next to it. */\n initials: string\n /**\n * The role pill at the right of the strip, e.g. \"Admin\". Omit it and no pill is\n * drawn -- it is Identity's word for what this person is in this organisation,\n * so a consumer with nothing to say there says nothing rather than guessing.\n */\n role?: string\n}\n\n/**\n * One row in the panel.\n *\n * Whether it navigates is decided by `href` and nothing else, which is ADR-0011's\n * rule applied rather than re-decided: a row with an `href` is a link and renders\n * through `linkComponent`; a row without one is an action and stays a `<button>`.\n * That keeps the component out of the business of knowing which of Profile,\n * Settings, Switch account and Sign out are navigations in a given product --\n * four apps may answer differently, and the consumer already knows.\n */\nexport interface AccountMenuItem {\n /** Stable identity: the React key, and what a test addresses the row by. */\n id: string\n /** The row's name, e.g. \"Profile\". */\n label: string\n /**\n * The line beneath the name. Omit it and the row is a single line, set in the\n * quieter register the drawing gives its Sign out row -- see `.rowLabel` in the\n * stylesheet for why the two travel together.\n */\n description?: string\n /** Where the row goes. Omit it and the row is an action, rendered as a button. */\n href?: string\n /** Which glyph the row carries. Omit it and the glyph lane stays, empty. */\n icon?: AccountMenuIcon\n}\n\n/**\n * A run of rows drawn together. The panel's hairline is drawn above every group,\n * which is what separates the first group from the identity blocks above it as\n * well as each group from the last.\n *\n * Grouped rather than flat because the drawing puts a rule between its three\n * navigations and its one way out, and grouped rather than named (`items` plus a\n * `footer`) because \"footer\" would be this package inventing a meaning for the\n * second group. A consumer that wants one group passes one.\n */\nexport interface AccountMenuGroup {\n /** Stable identity: the React key. */\n id: string\n /** The rows, in the order they are drawn. */\n items: AccountMenuItem[]\n}\n\nexport interface AccountMenuProps {\n /** The signed-in person. */\n person: AccountMenuPerson\n /** The organisation this person is acting in. */\n org: AccountMenuOrg\n /** The row groups, in order. A hairline is drawn above each one. */\n groups: AccountMenuGroup[]\n /**\n * Every user-facing string that is not row data -- which is the panel's\n * accessible name, and today nothing else. Each field defaults to English, and\n * a field left out, or supplied as `undefined` (what a missed next-intl key\n * looks like), keeps its default.\n */\n labels?: Partial<AccountMenuLabels>\n /**\n * The element a row with an `href` renders through. Defaults to a plain `a`; a\n * router-owning consumer passes its own so the rows route client-side with the\n * locale prefix intact (ADR-0011, B33-847). Rows without an `href` are buttons\n * and ignore it.\n */\n linkComponent?: ElementType\n}\n\n/**\n * The account menu: who you are, which organisation you are in, and the way into\n * Settings and out of the session (B33-493).\n *\n * **This is Stage A, and this component is the panel alone.** It takes no\n * trigger, holds no open state and wires nothing. That is not a half-built\n * version of trigger-plus-panel -- it is the part of this component that the two\n * reference drawings agree about. Paper's 004 \"AccountMenu — popover\" and\n * Identity's 103 · 11 Account menu are identical in every property of the panel,\n * and they disagree about the anchor (004 draws a bell on a tile, an unread dot\n * and a halo behind the avatar; 103 draws none of the three) and about where the\n * panel sits relative to it (004: 8px below, 16px right inset, overhanging the\n * avatar by 4px; 103: 10px below, 20px inset, flush). CLAUDE.md puts that choice\n * with the person at the fidelity gate, and says plainly that an agent picking\n * one has invented a referent. So the anchor is not here, `TopBar` is untouched,\n * and the specimen positions the panel in its own stylesheet.\n *\n * A disclosure, like the switcher (ADR-0010): no widget role on the panel, no\n * role on the rows, every row the real `<a>` or `<button>` it already is, and Tab\n * the document's own order. Dismissal, focus restoration and the trigger's\n * `aria-expanded` are the shared anchored-overlay contract's (ADR-0009) and\n * arrive with the trigger in Stage B.\n *\n * Nothing here is authoritative. Every measurement in the stylesheet is read off\n * a drawing that no human has approved, and the gate is where it becomes a\n * referent -- at which point the approved baseline and the measurements written\n * into the spec are what binds, not Paper.\n */\nexport function AccountMenu({ person, org, groups, labels, linkComponent }: AccountMenuProps) {\n const t = resolveAccountMenuLabels(labels)\n const Link: ElementType = linkComponent ?? 'a'\n\n function renderRow(item: AccountMenuItem): ReactElement {\n const singleLine = item.description === undefined\n const glyph = item.icon === undefined ? null : ACCOUNT_MENU_ICONS[item.icon](styles.icon)\n\n // The lane is drawn whether or not the row has a glyph. A row that drops its\n // icon and lets the text slide left would break the column the four names\n // stand in, which is the one alignment a panel of stacked rows has to hold.\n // Same reasoning as NavItem's trailing slot (CONTEXT.md, \"Trailing slot\").\n const content = (\n <>\n <span className={styles.lane} aria-hidden=\"true\">\n {glyph}\n </span>\n <span className={styles.rowText}>\n <span className={styles.rowLabel}>{item.label}</span>\n {item.description !== undefined && (\n <span className={styles.rowDescription}>{item.description}</span>\n )}\n </span>\n </>\n )\n\n // No onClick on either branch: Stage A renders the element and wires nothing.\n // The action row is a real <button type=\"button\"> so that the shape Stage B\n // attaches a handler to is the shape a human approved here.\n return item.href === undefined ? (\n <button\n type=\"button\"\n className={styles.row}\n data-item={item.id}\n data-single-line={singleLine ? 'true' : undefined}\n >\n {content}\n </button>\n ) : (\n <Link\n className={styles.row}\n href={item.href}\n data-item={item.id}\n data-single-line={singleLine ? 'true' : undefined}\n >\n {content}\n </Link>\n )\n }\n\n return (\n <div className={styles.panel} aria-label={t.panel}>\n <div className={styles.person}>\n {person.avatarUrl === undefined ? (\n <span className={styles.personAvatar} aria-hidden=\"true\" />\n ) : (\n // Empty alt, not a name: the person's name is on the next line in. The\n // guard in tests/no-hardcoded-strings.test.ts reads `alt`, which is\n // the right thing to read -- an empty one is an absence of copy, not\n // untranslated copy.\n <img className={styles.personAvatar} src={person.avatarUrl} alt=\"\" />\n )}\n <span className={styles.personText}>\n <span className={styles.personName}>{person.name}</span>\n <span className={styles.personEmail}>{person.email}</span>\n </span>\n </div>\n\n <div className={styles.org}>\n <span className={styles.orgIdentity}>\n <span className={styles.orgTile} aria-hidden=\"true\">\n {org.initials}\n </span>\n <span className={styles.orgName}>{org.name}</span>\n </span>\n {org.role !== undefined && <span className={styles.orgRole}>{org.role}</span>}\n </div>\n\n {/* A Fragment rather than a wrapper element, so the hairline and the rows\n are direct children of the panel's flex column exactly as the drawing\n has them. A wrapper would be a block in between, and the hairline's\n block margins -- which are measured, see `.divider` -- would collapse\n through its edges and move the rule. */}\n {groups.map((group) => (\n <Fragment key={group.id}>\n <hr className={styles.divider} />\n <ul className={styles.rows}>\n {group.items.map((item) => (\n <li key={item.id}>{renderRow(item)}</li>\n ))}\n </ul>\n </Fragment>\n ))}\n </div>\n )\n}\n"],"names":["AccountMenu","person","org","groups","labels","linkComponent","t","resolveAccountMenuLabels","Link","renderRow","item","singleLine","glyph","ACCOUNT_MENU_ICONS","styles","content","jsxs","Fragment","jsx","group"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AccountMenu.js","sources":["../../../src/components/AccountMenu/AccountMenu.tsx"],"sourcesContent":["'use client'\n\n// A client component since Stage B (B33-493): the avatar button holds the menu's\n// open state and its keyboard behaviour (./use-menu.ts). Everything it takes is\n// plain data -- person, org, groups of rows with hrefs, labels -- so a Server\n// Component layout renders it directly and passes no function across the\n// boundary. `linkComponent` is the one exception and it is optional: a client\n// consumer may pass its router's link; a Server Component never needs to.\n// tests/guards.test.ts pins the built package's \"use client\" modules, and this\n// one was added to that list on purpose.\n\nimport { Fragment } from 'react'\nimport type { ElementType, ReactElement } from 'react'\nimport { ACCOUNT_MENU_ICONS, type AccountMenuIcon } from './icons'\nimport { initials } from './initials'\nimport { resolveAccountMenuLabels, type AccountMenuLabels } from './labels'\nimport { useMenu } from './use-menu'\nimport styles from './AccountMenu.module.css'\n\n/**\n * The signed-in person, at the top of the panel. Supplied by the consumer -- the\n * menu reads no session and fetches nothing (CLAUDE.md).\n */\nexport interface AccountMenuPerson {\n /**\n * Their name, e.g. \"Sofia Marques\". Optional: Identity may hold none. Without\n * it the name line reads \"You\" (`labels.unnamed`) and the initials come from\n * the email, as the Hub's do.\n */\n name?: string\n /** Their email, on the line beneath. */\n email: string\n /**\n * An avatar image, on the trigger and in the person block. Omit it and both\n * circles carry the person's initials on `--color-neutral-300`, as the Hub's do.\n *\n * The image is decorative when it renders: it takes an empty `alt`, because the\n * person's name is on the line beside it and a name here would make a screen\n * reader say who you are twice. Same reasoning as `ProductMark`'s default.\n */\n avatarUrl?: string\n}\n\n/**\n * The organisation strip under the person: which account you are acting in.\n * Identity's data, supplied per render, never looked up here (ADR-0003).\n */\nexport interface AccountMenuOrg {\n /** The organisation's name, e.g. \"Acme Coaching\". */\n name: string\n /**\n * The initials on the tile beside it, e.g. \"AC\". Decorative -- the name is next\n * to it. Omit them and they are taken from the name.\n */\n initials?: string\n /**\n * The role pill at the right of the strip, e.g. \"Admin\". Omit it and no pill is\n * drawn -- it is Identity's word for what this person is in this organisation,\n * so a consumer with nothing to say there says nothing rather than guessing.\n */\n role?: string\n}\n\n/**\n * One row in the panel.\n *\n * Whether it navigates is decided by `href` and nothing else, which is ADR-0011's\n * rule applied rather than re-decided: a row with an `href` is a link and renders\n * through `linkComponent`; a row without one is an action and stays a `<button>`.\n * That keeps the component out of the business of knowing which of Profile,\n * Settings, Switch account and Sign out are navigations in a given product --\n * four apps may answer differently, and the consumer already knows.\n */\nexport interface AccountMenuItem {\n /** Stable identity: the React key, and what a test addresses the row by. */\n id: string\n /** The row's name, e.g. \"Profile\". */\n label: string\n /**\n * The line beneath the name. Omit it and the row is a single line, set in the\n * quieter register the drawing gives its Sign out row -- see `.rowLabel` in the\n * stylesheet for why the two travel together.\n */\n description?: string\n /** Where the row goes. Omit it and the row is an action, rendered as a button. */\n href?: string\n /** Which glyph the row carries. Omit it and the glyph lane stays, empty. */\n icon?: AccountMenuIcon\n}\n\n/**\n * A run of rows drawn together. The panel's hairline is drawn above every group,\n * which is what separates the first group from the identity blocks above it as\n * well as each group from the last.\n *\n * Grouped rather than flat because the drawing puts a rule between its three\n * navigations and its one way out, and grouped rather than named (`items` plus a\n * `footer`) because \"footer\" would be this package inventing a meaning for the\n * second group. A consumer that wants one group passes one.\n */\nexport interface AccountMenuGroup {\n /** Stable identity: the React key. */\n id: string\n /** The rows, in the order they are drawn. */\n items: AccountMenuItem[]\n}\n\nexport interface AccountMenuProps {\n /** The signed-in person. */\n person: AccountMenuPerson\n /**\n * The organisation this person is acting in. Omit it and no strip is drawn: a\n * person on no membership would otherwise get an empty dark tile where the\n * company goes, which reads as a company whose name failed to load (the Hub's\n * reasoning).\n */\n org?: AccountMenuOrg\n /** The row groups, in order. A hairline is drawn above each one. */\n groups: AccountMenuGroup[]\n /**\n * Every user-facing string that is not row data -- which is the panel's\n * accessible name, and today nothing else. Each field defaults to English, and\n * a field left out, or supplied as `undefined` (what a missed next-intl key\n * looks like), keeps its default.\n */\n labels?: Partial<AccountMenuLabels>\n /**\n * Draw the menu open on first render, without moving focus. For the specimen\n * and for screenshots; an app leaves it unset.\n */\n defaultOpen?: boolean\n /**\n * The element a row with an `href` renders through. Defaults to a plain `a`; a\n * router-owning consumer passes its own so the rows route client-side with the\n * locale prefix intact (ADR-0011, B33-847). Rows without an `href` are buttons\n * and ignore it.\n */\n linkComponent?: ElementType\n}\n\n/**\n * The account menu: the avatar button in the top bar's end group, and the menu it\n * opens -- who you are, which organisation you are in, the way into Profile,\n * Security and Settings, Feedback and Docs, and the way out (B33-493).\n *\n * **Stage B, and the referent is the Hub's working menu** (boost33-hub\n * src/app/account-menu.tsx), named by Jamal on 5 Nov 2026 so that every app's\n * avatar opens the same menu. The panel is Stage A's, which was drawn from the\n * same Paper artboard the Hub built from (103 · 11); the trigger and the placement\n * are the Hub's: a 26px avatar, the panel 10px below it and aligned to its right\n * edge.\n *\n * A menu button, not a disclosure: `role=\"menu\"` on the panel and\n * `role=\"menuitem\"` on every row, with the Hub's keyboard model (./use-menu.ts).\n * Choosing a row closes the menu, so a client-side navigation does not leave it\n * open over the page it just opened.\n *\n * Most apps want `StandardAccountMenu`, which builds the standard rows from an\n * identity URL, a Hub URL and the person's role. This is the lower level: any\n * rows, as groups.\n */\nexport function AccountMenu({ person, org, groups, labels, defaultOpen, linkComponent }: AccountMenuProps) {\n const t = resolveAccountMenuLabels(labels)\n const Link: ElementType = linkComponent ?? 'a'\n const menu = useMenu({ defaultOpen })\n const letters = initials(person.name ?? person.email)\n\n function renderRow(item: AccountMenuItem): ReactElement {\n const singleLine = item.description === undefined\n const glyph = item.icon === undefined ? null : ACCOUNT_MENU_ICONS[item.icon](styles.icon)\n\n // The lane is drawn whether or not the row has a glyph. A row that drops its\n // icon and lets the text slide left would break the column the names stand\n // in, which is the one alignment a panel of stacked rows has to hold. Same\n // reasoning as NavItem's trailing slot (CONTEXT.md, \"Trailing slot\").\n const content = (\n <>\n <span className={styles.lane} aria-hidden=\"true\">\n {glyph}\n </span>\n <span className={styles.rowText}>\n <span className={styles.rowLabel}>{item.label}</span>\n {item.description !== undefined && (\n <span className={styles.rowDescription}>{item.description}</span>\n )}\n </span>\n </>\n )\n\n // tabIndex -1: the arrow keys move through the rows and Tab leaves the menu\n // (./use-menu.ts). An action row is a real <button>; it carries no handler,\n // because a handler is a function and this component takes none from a\n // Server Component. A consumer with an action to wire renders its own row.\n return item.href === undefined ? (\n <button\n type=\"button\"\n role=\"menuitem\"\n tabIndex={-1}\n className={styles.row}\n data-item={item.id}\n data-single-line={singleLine ? 'true' : undefined}\n >\n {content}\n </button>\n ) : (\n <Link\n role=\"menuitem\"\n tabIndex={-1}\n className={styles.row}\n href={item.href}\n data-item={item.id}\n data-single-line={singleLine ? 'true' : undefined}\n >\n {content}\n </Link>\n )\n }\n\n return (\n <div className={styles.root} {...menu.rootProps}>\n <button {...menu.triggerProps} type=\"button\" className={styles.trigger} aria-label={t.trigger}>\n {person.avatarUrl === undefined ? (\n <span aria-hidden=\"true\">{letters}</span>\n ) : (\n <img className={styles.avatarImage} src={person.avatarUrl} alt=\"\" />\n )}\n </button>\n\n {menu.open && (\n <div\n {...menu.menuProps}\n className={styles.panel}\n aria-label={t.panel}\n // Choosing a row closes the menu. A client-side navigation keeps this\n // component mounted, and the menu would otherwise stay open over the\n // page the row just opened (the Hub's reasoning).\n onClick={(event) => {\n if ((event.target as HTMLElement).closest(\"[role='menuitem']\")) menu.close(false)\n }}\n >\n <div className={styles.person}>\n {person.avatarUrl === undefined ? (\n <span className={styles.personAvatar} aria-hidden=\"true\">\n {letters}\n </span>\n ) : (\n // Empty alt, not a name: the person's name is on the next line in.\n <img className={styles.personAvatar} src={person.avatarUrl} alt=\"\" />\n )}\n <span className={styles.personText}>\n <span className={styles.personName}>{person.name ?? t.unnamed}</span>\n <span className={styles.personEmail}>{person.email}</span>\n </span>\n </div>\n\n {org !== undefined && (\n <div className={styles.org}>\n <span className={styles.orgIdentity}>\n <span className={styles.orgTile} aria-hidden=\"true\">\n {org.initials ?? initials(org.name)}\n </span>\n <span className={styles.orgName}>{org.name}</span>\n </span>\n {org.role !== undefined && <span className={styles.orgRole}>{org.role}</span>}\n </div>\n )}\n\n {/* A Fragment rather than a wrapper element, so the hairline and the\n rows are direct children of the panel's flex column. The lists are\n role=\"none\": inside a menu the rows are menuitems, and list\n semantics between the two would break that ownership. */}\n {groups.map((group) => (\n <Fragment key={group.id}>\n <hr className={styles.divider} />\n <ul className={styles.rows} role=\"none\">\n {group.items.map((item) => (\n <li key={item.id} role=\"none\">\n {renderRow(item)}\n </li>\n ))}\n </ul>\n </Fragment>\n ))}\n </div>\n )}\n </div>\n )\n}\n"],"names":["AccountMenu","person","org","groups","labels","defaultOpen","linkComponent","t","resolveAccountMenuLabels","Link","menu","useMenu","letters","initials","renderRow","item","singleLine","glyph","ACCOUNT_MENU_ICONS","styles","content","jsxs","Fragment","jsx","event","group"],"mappings":"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"}
|
|
@@ -1,42 +1,48 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
const t = "_root_trt1y_61", r = "_trigger_trt1y_71", o = "_avatarImage_trt1y_95", _ = "_panel_trt1y_102", e = "_person_trt1y_124", n = "_personAvatar_trt1y_138", s = "_personText_trt1y_156", a = "_personName_trt1y_164", c = "_personEmail_trt1y_180", y = "_org_trt1y_205", g = "_orgIdentity_trt1y_219", i = "_orgTile_trt1y_234", p = "_orgName_trt1y_250", l = "_orgRole_trt1y_265", w = "_divider_trt1y_201", m = "_rows_trt1y_298", d = "_row_trt1y_48", v = "_rowDescription_trt1y_359", T = "_lane_trt1y_364", x = "_rowLabel_trt1y_366", I = "_rowText_trt1y_411", N = {
|
|
2
|
+
root: t,
|
|
3
|
+
trigger: r,
|
|
4
|
+
avatarImage: o,
|
|
5
|
+
panel: _,
|
|
6
|
+
person: e,
|
|
7
|
+
personAvatar: n,
|
|
8
|
+
personText: s,
|
|
9
|
+
personName: a,
|
|
10
|
+
personEmail: c,
|
|
11
|
+
org: y,
|
|
12
|
+
orgIdentity: g,
|
|
13
|
+
orgTile: i,
|
|
14
|
+
orgName: p,
|
|
15
|
+
orgRole: l,
|
|
16
|
+
divider: w,
|
|
17
|
+
rows: m,
|
|
18
|
+
row: d,
|
|
19
|
+
rowDescription: v,
|
|
20
|
+
lane: T,
|
|
21
|
+
rowLabel: x,
|
|
22
|
+
rowText: I
|
|
20
23
|
};
|
|
21
24
|
export {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
n as
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
25
|
+
o as avatarImage,
|
|
26
|
+
N as default,
|
|
27
|
+
w as divider,
|
|
28
|
+
T as lane,
|
|
29
|
+
y as org,
|
|
30
|
+
g as orgIdentity,
|
|
31
|
+
p as orgName,
|
|
32
|
+
l as orgRole,
|
|
33
|
+
i as orgTile,
|
|
34
|
+
_ as panel,
|
|
35
|
+
e as person,
|
|
36
|
+
n as personAvatar,
|
|
37
|
+
c as personEmail,
|
|
38
|
+
a as personName,
|
|
39
|
+
s as personText,
|
|
40
|
+
t as root,
|
|
41
|
+
d as row,
|
|
42
|
+
v as rowDescription,
|
|
43
|
+
x as rowLabel,
|
|
44
|
+
I as rowText,
|
|
45
|
+
m as rows,
|
|
46
|
+
r as trigger
|
|
41
47
|
};
|
|
42
48
|
//# sourceMappingURL=AccountMenu.module.css.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountMenu.module.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AccountMenu.module.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { AccountMenuGroup, AccountMenuPerson } from './AccountMenu';
|
|
2
|
+
import { AccountMenuLabels, StandardAccountMenuLabels } from './labels';
|
|
3
|
+
/** The person's role in the organisation they are acting in, as Identity issues it. */
|
|
4
|
+
export type AccountMenuRole = 'admin' | 'member';
|
|
5
|
+
/**
|
|
6
|
+
* Where the standard rows go. Every href is absolute, built from the two base
|
|
7
|
+
* URLs, so the same rows work from any app -- the Hub included, where they are a
|
|
8
|
+
* full page load to the Hub's own settings rather than a client-side route.
|
|
9
|
+
*/
|
|
10
|
+
export interface StandardAccountMenuRowsOptions {
|
|
11
|
+
/** The person's role. Picks Settings' line; `null` or omitted reads as a member's. */
|
|
12
|
+
role?: AccountMenuRole | null;
|
|
13
|
+
/**
|
|
14
|
+
* Identity's origin, e.g. `https://id.boost33.com`. Security, Switch account and
|
|
15
|
+
* Sign out go there: a passphrase is set against Identity's own session, only
|
|
16
|
+
* Identity can re-issue a token for another account, and a product cannot end a
|
|
17
|
+
* session (the Hub's reasoning, B33-1227).
|
|
18
|
+
*/
|
|
19
|
+
identityUrl: string;
|
|
20
|
+
/**
|
|
21
|
+
* The Hub's origin. In production `https://app.boost33.com` (Identity's
|
|
22
|
+
* `HUB_URL`, docs/DEPLOY.md). Profile goes to `{hubUrl}/settings/profile` and
|
|
23
|
+
* Settings to `{hubUrl}/settings/org/general`, the Hub's settings pages.
|
|
24
|
+
*/
|
|
25
|
+
hubUrl: string;
|
|
26
|
+
/**
|
|
27
|
+
* How many other accounts this person can switch to. Above 0, a Switch account
|
|
28
|
+
* row is drawn, described by `otherAccountNames` when every one is named, or by
|
|
29
|
+
* the count. 0 or omitted: no row.
|
|
30
|
+
*/
|
|
31
|
+
otherAccountsCount?: number;
|
|
32
|
+
/** The other accounts' names, in order. Used only when there is one per account. */
|
|
33
|
+
otherAccountNames?: string[];
|
|
34
|
+
/** The rows' words. Each field defaults to the Hub's English. */
|
|
35
|
+
labels?: Partial<StandardAccountMenuLabels>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The standard rows of the account menu, the Hub's, as plain data:
|
|
39
|
+
*
|
|
40
|
+
* Profile, Security, Settings, [Switch account]
|
|
41
|
+
* Feedback, Docs
|
|
42
|
+
* Sign out
|
|
43
|
+
*
|
|
44
|
+
* Pass the result to `AccountMenu` as `groups`, adding or reordering rows if an
|
|
45
|
+
* app ever has one the standard set cannot express -- or use
|
|
46
|
+
* `StandardAccountMenu`, which does this for you.
|
|
47
|
+
*/
|
|
48
|
+
export declare function standardAccountMenuGroups(options: StandardAccountMenuRowsOptions): AccountMenuGroup[];
|
|
49
|
+
export interface StandardAccountMenuProps extends StandardAccountMenuRowsOptions {
|
|
50
|
+
/** The signed-in person: email, and a name and photo when Identity has them. */
|
|
51
|
+
person: AccountMenuPerson;
|
|
52
|
+
/**
|
|
53
|
+
* The organisation this person is acting in. Omit it and no strip is drawn. The
|
|
54
|
+
* role pill comes from `role`, in `labels.roleAdmin` / `labels.roleMember`.
|
|
55
|
+
*/
|
|
56
|
+
org?: {
|
|
57
|
+
name: string;
|
|
58
|
+
initials?: string;
|
|
59
|
+
};
|
|
60
|
+
/** The menu's accessible names and the unnamed fallback. Each defaults to the Hub's English. */
|
|
61
|
+
menuLabels?: Partial<AccountMenuLabels>;
|
|
62
|
+
/** Draw the menu open on first render. For the specimen and screenshots. */
|
|
63
|
+
defaultOpen?: boolean;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The account menu every Boost33 app shows behind its avatar: the Hub's, with
|
|
67
|
+
* the standard rows. One element, plain data in, safe to render from a Server
|
|
68
|
+
* Component layout:
|
|
69
|
+
*
|
|
70
|
+
* ```tsx
|
|
71
|
+
* <AppShell
|
|
72
|
+
* topBar={{
|
|
73
|
+
* account: (
|
|
74
|
+
* <StandardAccountMenu
|
|
75
|
+
* person={{ name: claims.name, email: claims.email }}
|
|
76
|
+
* org={{ name: orgName }}
|
|
77
|
+
* role={role}
|
|
78
|
+
* identityUrl="https://id.boost33.com"
|
|
79
|
+
* hubUrl="https://app.boost33.com"
|
|
80
|
+
* otherAccountsCount={claims.accounts.length - 1}
|
|
81
|
+
* />
|
|
82
|
+
* ),
|
|
83
|
+
* }}
|
|
84
|
+
* />
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export declare function StandardAccountMenu({ person, org, role, labels, menuLabels, defaultOpen, ...rows }: StandardAccountMenuProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { jsx as a } from "react/jsx-runtime";
|
|
2
|
+
import { fillTemplate as m } from "../../fill-template.js";
|
|
3
|
+
import { AccountMenu as f } from "./AccountMenu.js";
|
|
4
|
+
import { resolveStandardAccountMenuLabels as u } from "./labels.js";
|
|
5
|
+
const p = "mailto:support@boost33.com", b = "https://docs.boost33.com";
|
|
6
|
+
function n(t, e) {
|
|
7
|
+
return `${t.replace(/\/+$/, "")}${e}`;
|
|
8
|
+
}
|
|
9
|
+
function h(t) {
|
|
10
|
+
const e = u(t.labels), i = t.otherAccountsCount ?? 0, c = t.otherAccountNames ?? [], o = {
|
|
11
|
+
id: "account",
|
|
12
|
+
items: [
|
|
13
|
+
{
|
|
14
|
+
id: "profile",
|
|
15
|
+
label: e.profile,
|
|
16
|
+
description: e.profileDescription,
|
|
17
|
+
href: n(t.hubUrl, "/settings/profile"),
|
|
18
|
+
icon: "person"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: "security",
|
|
22
|
+
label: e.security,
|
|
23
|
+
description: e.securityDescription,
|
|
24
|
+
href: n(t.identityUrl, "/account/security"),
|
|
25
|
+
icon: "security"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: "settings",
|
|
29
|
+
label: e.settings,
|
|
30
|
+
description: t.role === "admin" ? e.settingsDescriptionAdmin : e.settingsDescriptionMember,
|
|
31
|
+
href: n(t.hubUrl, "/settings/org/general"),
|
|
32
|
+
icon: "settings"
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
};
|
|
36
|
+
if (i > 0) {
|
|
37
|
+
const r = c.length === i ? c.join(" · ") : i === 1 ? e.otherAccountsOne : m(e.otherAccountsMany, { count: String(i) });
|
|
38
|
+
o.items.push({
|
|
39
|
+
id: "switch-account",
|
|
40
|
+
label: e.switchAccount,
|
|
41
|
+
description: r,
|
|
42
|
+
href: n(t.identityUrl, "/choose-account"),
|
|
43
|
+
icon: "switch"
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return [
|
|
47
|
+
o,
|
|
48
|
+
{
|
|
49
|
+
id: "help",
|
|
50
|
+
items: [
|
|
51
|
+
{ id: "feedback", label: e.feedback, description: e.feedbackDescription, href: p, icon: "feedback" },
|
|
52
|
+
{ id: "docs", label: e.docs, description: e.docsDescription, href: b, icon: "docs" }
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: "session",
|
|
57
|
+
items: [{ id: "sign-out", label: e.signOut, href: n(t.identityUrl, "/signout"), icon: "signOut" }]
|
|
58
|
+
}
|
|
59
|
+
];
|
|
60
|
+
}
|
|
61
|
+
function M({
|
|
62
|
+
person: t,
|
|
63
|
+
org: e,
|
|
64
|
+
role: i,
|
|
65
|
+
labels: c,
|
|
66
|
+
menuLabels: o,
|
|
67
|
+
defaultOpen: r,
|
|
68
|
+
...d
|
|
69
|
+
}) {
|
|
70
|
+
const s = u(c), l = i === "admin" ? s.roleAdmin : i === "member" ? s.roleMember : void 0;
|
|
71
|
+
return /* @__PURE__ */ a(
|
|
72
|
+
f,
|
|
73
|
+
{
|
|
74
|
+
person: t,
|
|
75
|
+
org: e === void 0 ? void 0 : { ...e, role: l },
|
|
76
|
+
groups: h({ ...d, role: i, labels: c }),
|
|
77
|
+
labels: o,
|
|
78
|
+
defaultOpen: r
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
export {
|
|
83
|
+
M as StandardAccountMenu,
|
|
84
|
+
h as standardAccountMenuGroups
|
|
85
|
+
};
|
|
86
|
+
//# sourceMappingURL=StandardAccountMenu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StandardAccountMenu.js","sources":["../../../src/components/AccountMenu/StandardAccountMenu.tsx"],"sourcesContent":["// No \"use client\": this builds plain data and renders the client AccountMenu with\n// it, so a Server Component layout can render it and pass no function across the\n// boundary. tests/guards.test.ts holds it to the server-component rules.\n\nimport { fillTemplate } from '../../fill-template'\nimport { AccountMenu, type AccountMenuGroup, type AccountMenuPerson } from './AccountMenu'\nimport {\n resolveStandardAccountMenuLabels,\n type AccountMenuLabels,\n type StandardAccountMenuLabels,\n} from './labels'\n\n/** The person's role in the organisation they are acting in, as Identity issues it. */\nexport type AccountMenuRole = 'admin' | 'member'\n\n/**\n * Where the standard rows go. Every href is absolute, built from the two base\n * URLs, so the same rows work from any app -- the Hub included, where they are a\n * full page load to the Hub's own settings rather than a client-side route.\n */\nexport interface StandardAccountMenuRowsOptions {\n /** The person's role. Picks Settings' line; `null` or omitted reads as a member's. */\n role?: AccountMenuRole | null\n /**\n * Identity's origin, e.g. `https://id.boost33.com`. Security, Switch account and\n * Sign out go there: a passphrase is set against Identity's own session, only\n * Identity can re-issue a token for another account, and a product cannot end a\n * session (the Hub's reasoning, B33-1227).\n */\n identityUrl: string\n /**\n * The Hub's origin. In production `https://app.boost33.com` (Identity's\n * `HUB_URL`, docs/DEPLOY.md). Profile goes to `{hubUrl}/settings/profile` and\n * Settings to `{hubUrl}/settings/org/general`, the Hub's settings pages.\n */\n hubUrl: string\n /**\n * How many other accounts this person can switch to. Above 0, a Switch account\n * row is drawn, described by `otherAccountNames` when every one is named, or by\n * the count. 0 or omitted: no row.\n */\n otherAccountsCount?: number\n /** The other accounts' names, in order. Used only when there is one per account. */\n otherAccountNames?: string[]\n /** The rows' words. Each field defaults to the Hub's English. */\n labels?: Partial<StandardAccountMenuLabels>\n}\n\nconst FEEDBACK_HREF = 'mailto:support@boost33.com'\nconst DOCS_HREF = 'https://docs.boost33.com'\n\n/** `https://x/` and `https://x` both join to `https://x/path`. */\nfunction join(base: string, path: string): string {\n return `${base.replace(/\\/+$/, '')}${path}`\n}\n\n/**\n * The standard rows of the account menu, the Hub's, as plain data:\n *\n * Profile, Security, Settings, [Switch account]\n * Feedback, Docs\n * Sign out\n *\n * Pass the result to `AccountMenu` as `groups`, adding or reordering rows if an\n * app ever has one the standard set cannot express -- or use\n * `StandardAccountMenu`, which does this for you.\n */\nexport function standardAccountMenuGroups(options: StandardAccountMenuRowsOptions): AccountMenuGroup[] {\n const t = resolveStandardAccountMenuLabels(options.labels)\n const count = options.otherAccountsCount ?? 0\n const names = options.otherAccountNames ?? []\n\n const account: AccountMenuGroup = {\n id: 'account',\n items: [\n {\n id: 'profile',\n label: t.profile,\n description: t.profileDescription,\n href: join(options.hubUrl, '/settings/profile'),\n icon: 'person',\n },\n {\n id: 'security',\n label: t.security,\n description: t.securityDescription,\n href: join(options.identityUrl, '/account/security'),\n icon: 'security',\n },\n {\n id: 'settings',\n label: t.settings,\n description: options.role === 'admin' ? t.settingsDescriptionAdmin : t.settingsDescriptionMember,\n href: join(options.hubUrl, '/settings/org/general'),\n icon: 'settings',\n },\n ],\n }\n\n if (count > 0) {\n // \"Nova Retail · Cortex Labs\" when every account is named; counted rather\n // than half-named otherwise (the Hub's describeOthers).\n const description =\n names.length === count\n ? names.join(' · ')\n : count === 1\n ? t.otherAccountsOne\n : fillTemplate(t.otherAccountsMany, { count: String(count) })\n account.items.push({\n id: 'switch-account',\n label: t.switchAccount,\n description,\n href: join(options.identityUrl, '/choose-account'),\n icon: 'switch',\n })\n }\n\n return [\n account,\n {\n id: 'help',\n items: [\n { id: 'feedback', label: t.feedback, description: t.feedbackDescription, href: FEEDBACK_HREF, icon: 'feedback' },\n { id: 'docs', label: t.docs, description: t.docsDescription, href: DOCS_HREF, icon: 'docs' },\n ],\n },\n {\n id: 'session',\n items: [{ id: 'sign-out', label: t.signOut, href: join(options.identityUrl, '/signout'), icon: 'signOut' }],\n },\n ]\n}\n\nexport interface StandardAccountMenuProps extends StandardAccountMenuRowsOptions {\n /** The signed-in person: email, and a name and photo when Identity has them. */\n person: AccountMenuPerson\n /**\n * The organisation this person is acting in. Omit it and no strip is drawn. The\n * role pill comes from `role`, in `labels.roleAdmin` / `labels.roleMember`.\n */\n org?: { name: string; initials?: string }\n /** The menu's accessible names and the unnamed fallback. Each defaults to the Hub's English. */\n menuLabels?: Partial<AccountMenuLabels>\n /** Draw the menu open on first render. For the specimen and screenshots. */\n defaultOpen?: boolean\n}\n\n/**\n * The account menu every Boost33 app shows behind its avatar: the Hub's, with\n * the standard rows. One element, plain data in, safe to render from a Server\n * Component layout:\n *\n * ```tsx\n * <AppShell\n * topBar={{\n * account: (\n * <StandardAccountMenu\n * person={{ name: claims.name, email: claims.email }}\n * org={{ name: orgName }}\n * role={role}\n * identityUrl=\"https://id.boost33.com\"\n * hubUrl=\"https://app.boost33.com\"\n * otherAccountsCount={claims.accounts.length - 1}\n * />\n * ),\n * }}\n * />\n * ```\n */\nexport function StandardAccountMenu({\n person,\n org,\n role,\n labels,\n menuLabels,\n defaultOpen,\n ...rows\n}: StandardAccountMenuProps) {\n const t = resolveStandardAccountMenuLabels(labels)\n const roleLabel = role === 'admin' ? t.roleAdmin : role === 'member' ? t.roleMember : undefined\n\n return (\n <AccountMenu\n person={person}\n org={org === undefined ? undefined : { ...org, role: roleLabel }}\n groups={standardAccountMenuGroups({ ...rows, role, labels })}\n labels={menuLabels}\n defaultOpen={defaultOpen}\n />\n )\n}\n"],"names":["FEEDBACK_HREF","DOCS_HREF","join","base","path","standardAccountMenuGroups","options","t","resolveStandardAccountMenuLabels","count","names","account","description","fillTemplate","StandardAccountMenu","person","org","role","labels","menuLabels","defaultOpen","rows","roleLabel","jsx","AccountMenu"],"mappings":";;;;AAgDA,MAAMA,IAAgB,8BAChBC,IAAY;AAGlB,SAASC,EAAKC,GAAcC,GAAsB;AAChD,SAAO,GAAGD,EAAK,QAAQ,QAAQ,EAAE,CAAC,GAAGC,CAAI;AAC3C;AAaO,SAASC,EAA0BC,GAA6D;AACrG,QAAMC,IAAIC,EAAiCF,EAAQ,MAAM,GACnDG,IAAQH,EAAQ,sBAAsB,GACtCI,IAAQJ,EAAQ,qBAAqB,CAAA,GAErCK,IAA4B;AAAA,IAChC,IAAI;AAAA,IACJ,OAAO;AAAA,MACL;AAAA,QACE,IAAI;AAAA,QACJ,OAAOJ,EAAE;AAAA,QACT,aAAaA,EAAE;AAAA,QACf,MAAML,EAAKI,EAAQ,QAAQ,mBAAmB;AAAA,QAC9C,MAAM;AAAA,MAAA;AAAA,MAER;AAAA,QACE,IAAI;AAAA,QACJ,OAAOC,EAAE;AAAA,QACT,aAAaA,EAAE;AAAA,QACf,MAAML,EAAKI,EAAQ,aAAa,mBAAmB;AAAA,QACnD,MAAM;AAAA,MAAA;AAAA,MAER;AAAA,QACE,IAAI;AAAA,QACJ,OAAOC,EAAE;AAAA,QACT,aAAaD,EAAQ,SAAS,UAAUC,EAAE,2BAA2BA,EAAE;AAAA,QACvE,MAAML,EAAKI,EAAQ,QAAQ,uBAAuB;AAAA,QAClD,MAAM;AAAA,MAAA;AAAA,IACR;AAAA,EACF;AAGF,MAAIG,IAAQ,GAAG;AAGb,UAAMG,IACJF,EAAM,WAAWD,IACbC,EAAM,KAAK,KAAK,IAChBD,MAAU,IACRF,EAAE,mBACFM,EAAaN,EAAE,mBAAmB,EAAE,OAAO,OAAOE,CAAK,GAAG;AAClE,IAAAE,EAAQ,MAAM,KAAK;AAAA,MACjB,IAAI;AAAA,MACJ,OAAOJ,EAAE;AAAA,MACT,aAAAK;AAAA,MACA,MAAMV,EAAKI,EAAQ,aAAa,iBAAiB;AAAA,MACjD,MAAM;AAAA,IAAA,CACP;AAAA,EACH;AAEA,SAAO;AAAA,IACLK;AAAA,IACA;AAAA,MACE,IAAI;AAAA,MACJ,OAAO;AAAA,QACL,EAAE,IAAI,YAAY,OAAOJ,EAAE,UAAU,aAAaA,EAAE,qBAAqB,MAAMP,GAAe,MAAM,WAAA;AAAA,QACpG,EAAE,IAAI,QAAQ,OAAOO,EAAE,MAAM,aAAaA,EAAE,iBAAiB,MAAMN,GAAW,MAAM,OAAA;AAAA,MAAO;AAAA,IAC7F;AAAA,IAEF;AAAA,MACE,IAAI;AAAA,MACJ,OAAO,CAAC,EAAE,IAAI,YAAY,OAAOM,EAAE,SAAS,MAAML,EAAKI,EAAQ,aAAa,UAAU,GAAG,MAAM,WAAW;AAAA,IAAA;AAAA,EAC5G;AAEJ;AAsCO,SAASQ,EAAoB;AAAA,EAClC,QAAAC;AAAA,EACA,KAAAC;AAAA,EACA,MAAAC;AAAA,EACA,QAAAC;AAAA,EACA,YAAAC;AAAA,EACA,aAAAC;AAAA,EACA,GAAGC;AACL,GAA6B;AAC3B,QAAMd,IAAIC,EAAiCU,CAAM,GAC3CI,IAAYL,MAAS,UAAUV,EAAE,YAAYU,MAAS,WAAWV,EAAE,aAAa;AAEtF,SACE,gBAAAgB;AAAA,IAACC;AAAA,IAAA;AAAA,MACC,QAAAT;AAAA,MACA,KAAKC,MAAQ,SAAY,SAAY,EAAE,GAAGA,GAAK,MAAMM,EAAA;AAAA,MACrD,QAAQjB,EAA0B,EAAE,GAAGgB,GAAM,MAAAJ,GAAM,QAAAC,GAAQ;AAAA,MAC3D,QAAQC;AAAA,MACR,aAAAC;AAAA,IAAA;AAAA,EAAA;AAGN;"}
|