@ghub/identity-chrome 0.2.0 → 0.3.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 +28 -2
- package/dist/elements/account.d.ts +36 -3
- package/dist/elements/account.js +271 -31
- package/dist/elements/apps.js +1 -0
- package/dist/elements/index.d.ts +4 -2
- package/dist/elements/index.js +2 -1
- package/dist/index.d.ts +1 -1
- package/dist/react.d.ts +11 -3
- package/dist/react.js +8 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -76,7 +76,7 @@ const account = document.querySelector("gp-account")!;
|
|
|
76
76
|
account.core = chrome;
|
|
77
77
|
account.product = "GAdvisory"; // your own name, from your catalogue entry
|
|
78
78
|
account.person = { name: user.name, email: user.email, id: user.id, avatarUrl: user.picture };
|
|
79
|
-
account.organisations = [{ id: "org_1", name: "Hafen Labs", role: "
|
|
79
|
+
account.organisations = [{ id: "org_1", name: "Hafen Labs", role: "Admin", active: true }];
|
|
80
80
|
account.onSwitch = async (id) => switchOrganisation(id); // reject when it did not switch
|
|
81
81
|
account.onSignOut = async () => {
|
|
82
82
|
await signOut(); // reject when it failed: the popup says they are still signed in
|
|
@@ -89,7 +89,8 @@ set before the element is defined. Neither control renders anything while the co
|
|
|
89
89
|
and the account popup renders nothing without a person.
|
|
90
90
|
|
|
91
91
|
**The account popup shows mostly what the product passes**: the name and address from your
|
|
92
|
-
session, your organisations with
|
|
92
|
+
session, your organisations with the role your product gives them there (its own label, already in the
|
|
93
|
+
person's language and shown as given; none, and the row has no second line) and your two actions.
|
|
93
94
|
Identity adds the picture, the colour of the initial, the links to its own account and set up
|
|
94
95
|
screens and the footer, so the popup still signs somebody out while Identity is down. The
|
|
95
96
|
picture is Identity's if the person has one, else yours, else a coloured initial whose tint
|
|
@@ -99,6 +100,31 @@ Identity, and Sign out says it ends both. Sign out leaves this product only.
|
|
|
99
100
|
|
|
100
101
|
Every word can be replaced through `labels`; the rest stay English.
|
|
101
102
|
|
|
103
|
+
### At the bottom of a sidebar, with your own items
|
|
104
|
+
|
|
105
|
+
```html
|
|
106
|
+
<div style="display: flex; align-items: center">
|
|
107
|
+
<gp-account layout="row" placement="top" align="start" style="flex: 1"></gp-account>
|
|
108
|
+
<gp-apps placement="top" align="start"></gp-apps>
|
|
109
|
+
</div>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
account.labels = { organisations: "Groups" };
|
|
114
|
+
account.items = [
|
|
115
|
+
{ id: "profile", label: "Profile", href: "/settings/profile", icon: userSvg },
|
|
116
|
+
{ id: "account", label: "Account", href: "/settings/account", icon: settingsSvg },
|
|
117
|
+
{ id: "replay", label: "Session recording", onSelect: () => openReplayChoice(), icon: videoSvg },
|
|
118
|
+
];
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
`layout="row"` draws the trigger as a full width row, the picture with the name over the
|
|
122
|
+
address; `placement="top"` opens both popups upward. Your items follow the organisations
|
|
123
|
+
under your product's name, as links or as buttons; on a link, `onSelect` may call
|
|
124
|
+
`preventDefault()` and navigate with your own router. An icon is SVG markup on a 24 grid and
|
|
125
|
+
is kept to its shapes. Until you sign in through Identity, Sign out is the last row of your
|
|
126
|
+
items' group; with `viaIdentity` it ends both sessions and stands on its own.
|
|
127
|
+
|
|
102
128
|
### In React
|
|
103
129
|
|
|
104
130
|
```tsx
|
|
@@ -32,13 +32,22 @@
|
|
|
32
32
|
* sentence that the person is still signed in, and turns the row into Try
|
|
33
33
|
* again. It never claims to have signed out.
|
|
34
34
|
*
|
|
35
|
+
* **In a sidebar** (`layout="row"`, `placement="top"`): the trigger is a full
|
|
36
|
+
* width row, the picture with the name over the address, and the popup opens
|
|
37
|
+
* upward from it. A product with a sidebar puts `<gp-apps>` at the row's end.
|
|
38
|
+
*
|
|
39
|
+
* **The product's own items** (`items`) sit after the organisations, as a group
|
|
40
|
+
* headed with the product's name. Until the product signs in through Identity,
|
|
41
|
+
* Sign out is the last row of that same group, because it is one of the
|
|
42
|
+
* product's own actions; once it does (`viaIdentity`), signing out ends both
|
|
43
|
+
* sessions and stands on its own again.
|
|
44
|
+
*
|
|
35
45
|
* **Hidden for now (D20):** the other accounts and "Add another account" are
|
|
36
46
|
* not drawn until Identity holds several sessions in one browser and products
|
|
37
47
|
* sign in through it, and no product can switch them on.
|
|
38
48
|
*
|
|
39
49
|
* The words are English by default and every one can be replaced (`labels`).
|
|
40
50
|
*/
|
|
41
|
-
export type OrganisationRole = "owner" | "admin" | "member";
|
|
42
51
|
/** Who is signed in, from the product's own session. */
|
|
43
52
|
export interface AccountPerson {
|
|
44
53
|
name: string;
|
|
@@ -52,9 +61,29 @@ export interface AccountPerson {
|
|
|
52
61
|
export interface AccountOrganisation {
|
|
53
62
|
id: string;
|
|
54
63
|
name: string;
|
|
55
|
-
|
|
64
|
+
/**
|
|
65
|
+
* The product's own role for this person here, already in the person's
|
|
66
|
+
* language and shown as given. Roles differ per product and can be custom,
|
|
67
|
+
* so the element has no list of its own. None, and the row has no second line.
|
|
68
|
+
*/
|
|
69
|
+
role?: string | null;
|
|
56
70
|
active: boolean;
|
|
57
71
|
}
|
|
72
|
+
/** One of the product's own items in the popup, such as its profile or its settings. */
|
|
73
|
+
export interface AccountItem {
|
|
74
|
+
/** Stable across renders, so focus stays on the item when the list is set again. */
|
|
75
|
+
id: string;
|
|
76
|
+
label: string;
|
|
77
|
+
/** A link, so middle click and opening in a new tab work as on any link. Relative to the page. */
|
|
78
|
+
href?: string;
|
|
79
|
+
/**
|
|
80
|
+
* Called when it is chosen. On a link, `event.preventDefault()` hands the
|
|
81
|
+
* navigation to the product, such as a client side router.
|
|
82
|
+
*/
|
|
83
|
+
onSelect?: (event: MouseEvent) => void;
|
|
84
|
+
/** SVG markup on a 24 grid, drawn at 18 in the row's ink. Only shapes are kept. */
|
|
85
|
+
icon?: string;
|
|
86
|
+
}
|
|
58
87
|
/** Signs the person out of this product. Rejecting means they are still signed in. */
|
|
59
88
|
export type SignOutHandler = () => unknown;
|
|
60
89
|
/** Switches the product to this organisation. Rejecting means it did not. */
|
|
@@ -68,7 +97,6 @@ export interface AccountLabels {
|
|
|
68
97
|
manage: string;
|
|
69
98
|
setUp: string;
|
|
70
99
|
organisations: string;
|
|
71
|
-
roles: Record<OrganisationRole, string>;
|
|
72
100
|
active: string;
|
|
73
101
|
showAll: (count: number) => string;
|
|
74
102
|
switched: (organisation: string) => string;
|
|
@@ -86,6 +114,11 @@ export interface AccountLabels {
|
|
|
86
114
|
}
|
|
87
115
|
export declare const ACCOUNT_LABELS: AccountLabels;
|
|
88
116
|
export declare const ACCOUNT_CSS: string;
|
|
117
|
+
/**
|
|
118
|
+
* A product's icon, kept to shapes: no script, no link, no style and no reference
|
|
119
|
+
* to anything outside it, because it is drawn inside every page the element is on.
|
|
120
|
+
*/
|
|
121
|
+
export declare function iconOf(markup: string): SVGSVGElement | null;
|
|
89
122
|
/**
|
|
90
123
|
* Defines `<gp-account>` once, where custom elements exist. Safe to import on a
|
|
91
124
|
* server: nothing touches the DOM until this is called in a browser.
|
package/dist/elements/account.js
CHANGED
|
@@ -33,6 +33,16 @@
|
|
|
33
33
|
* sentence that the person is still signed in, and turns the row into Try
|
|
34
34
|
* again. It never claims to have signed out.
|
|
35
35
|
*
|
|
36
|
+
* **In a sidebar** (`layout="row"`, `placement="top"`): the trigger is a full
|
|
37
|
+
* width row, the picture with the name over the address, and the popup opens
|
|
38
|
+
* upward from it. A product with a sidebar puts `<gp-apps>` at the row's end.
|
|
39
|
+
*
|
|
40
|
+
* **The product's own items** (`items`) sit after the organisations, as a group
|
|
41
|
+
* headed with the product's name. Until the product signs in through Identity,
|
|
42
|
+
* Sign out is the last row of that same group, because it is one of the
|
|
43
|
+
* product's own actions; once it does (`viaIdentity`), signing out ends both
|
|
44
|
+
* sessions and stands on its own again.
|
|
45
|
+
*
|
|
36
46
|
* **Hidden for now (D20):** the other accounts and "Add another account" are
|
|
37
47
|
* not drawn until Identity holds several sessions in one browser and products
|
|
38
48
|
* sign in through it, and no product can switch them on.
|
|
@@ -41,6 +51,7 @@
|
|
|
41
51
|
*/
|
|
42
52
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
53
|
exports.ACCOUNT_CSS = exports.ACCOUNT_LABELS = void 0;
|
|
54
|
+
exports.iconOf = iconOf;
|
|
44
55
|
exports.defineAccountElement = defineAccountElement;
|
|
45
56
|
exports.createAccountElementClass = createAccountElementClass;
|
|
46
57
|
const tint_js_1 = require("../tint.js");
|
|
@@ -56,7 +67,6 @@ exports.ACCOUNT_LABELS = {
|
|
|
56
67
|
manage: "Manage your Identity account",
|
|
57
68
|
setUp: "Set up your Identity",
|
|
58
69
|
organisations: "Organisations",
|
|
59
|
-
roles: { owner: "Owner", admin: "Admin", member: "Member" },
|
|
60
70
|
active: "Active",
|
|
61
71
|
showAll: (count) => `Show all ${String(count)}`,
|
|
62
72
|
switched: (organisation) => `Switched to ${organisation}`,
|
|
@@ -78,7 +88,6 @@ const SHOWN = 5;
|
|
|
78
88
|
const TIP_DELAY_MS = 500;
|
|
79
89
|
/** Long enough to cross from the trigger to its tooltip without losing it. */
|
|
80
90
|
const TIP_LINGER_MS = 150;
|
|
81
|
-
const ROLES = ["owner", "admin", "member"];
|
|
82
91
|
const tintRules = tint_js_1.AVATAR_TINTS.map((tint, index) => `.t${String(index)} { background: ${tint}; }`).join("\n ");
|
|
83
92
|
exports.ACCOUNT_CSS = `${contract_js_1.CONTRACT_CSS}
|
|
84
93
|
.trigger {
|
|
@@ -89,7 +98,17 @@ exports.ACCOUNT_CSS = `${contract_js_1.CONTRACT_CSS}
|
|
|
89
98
|
.trigger:active { background: var(--_active); }
|
|
90
99
|
.trigger[aria-expanded="true"] { box-shadow: 0 0 0 2px var(--_accent); }
|
|
91
100
|
.trigger:focus-visible { outline: 2px solid var(--_ring); outline-offset: 2px; }
|
|
92
|
-
@media (pointer: coarse) { .trigger { box-sizing: content-box; padding: 2px; } }
|
|
101
|
+
@media (pointer: coarse) { :host(:not([layout="row"])) .trigger { box-sizing: content-box; padding: 2px; } }
|
|
102
|
+
:host([layout="row"]) { display: block; }
|
|
103
|
+
:host([layout="row"]) .trigger {
|
|
104
|
+
box-sizing: border-box; width: 100%; height: auto; min-height: 44px; padding: 6px 8px; border-radius: 10px;
|
|
105
|
+
display: flex; align-items: center; gap: 10px; text-align: left; font: inherit;
|
|
106
|
+
}
|
|
107
|
+
:host([layout="row"]) .trigger[aria-expanded="true"] { background: var(--_hover); box-shadow: inset 0 0 0 1px var(--_accent); }
|
|
108
|
+
:host([layout="row"]) .trigger:focus-visible { outline-offset: 0; }
|
|
109
|
+
.who { flex: 1; min-width: 0; display: flex; flex-direction: column; }
|
|
110
|
+
.who-name { font-size: 14px; line-height: 19px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
111
|
+
.who-mail { font-size: 12px; line-height: 16px; color: var(--_ink-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
93
112
|
|
|
94
113
|
.avatar {
|
|
95
114
|
position: relative; display: grid; place-items: center; flex-shrink: 0; overflow: hidden;
|
|
@@ -117,6 +136,8 @@ exports.ACCOUNT_CSS = `${contract_js_1.CONTRACT_CSS}
|
|
|
117
136
|
box-shadow: var(--_shadow); color: var(--_ink); animation: rise 120ms ease-out;
|
|
118
137
|
}
|
|
119
138
|
:host([align="start"]) .popup, :host([align="start"]) .tip { right: auto; left: 0; }
|
|
139
|
+
:host([placement="top"]) .popup:not(.sheet) { top: auto; bottom: calc(100% + 8px); }
|
|
140
|
+
:host([placement="top"]) .tip { top: auto; bottom: calc(100% + 6px); }
|
|
120
141
|
.popup.sheet {
|
|
121
142
|
position: fixed; top: auto; right: 0; bottom: 0; left: 0; width: 100%; max-width: none;
|
|
122
143
|
max-height: calc(100dvh - 48px); border-radius: 20px 20px 0 0;
|
|
@@ -175,6 +196,20 @@ exports.ACCOUNT_CSS = `${contract_js_1.CONTRACT_CSS}
|
|
|
175
196
|
|
|
176
197
|
.section { display: flex; flex-direction: column; gap: 2px; margin-top: 6px; }
|
|
177
198
|
.heading { margin: 0; padding: 8px 16px 4px; font-size: 13px; line-height: 18px; font-weight: 500; color: var(--_ink-2); }
|
|
199
|
+
.heading.with-mark { display: flex; align-items: center; gap: 8px; }
|
|
200
|
+
.heading img { display: block; width: 16px; height: 16px; flex-shrink: 0; }
|
|
201
|
+
.stack { display: flex; flex-direction: column; gap: 2px; }
|
|
202
|
+
.stack > * { border-radius: 4px; }
|
|
203
|
+
.stack > :first-child { border-top-left-radius: 18px; border-top-right-radius: 18px; }
|
|
204
|
+
.stack > :last-child { border-bottom-left-radius: 18px; border-bottom-right-radius: 18px; }
|
|
205
|
+
.item {
|
|
206
|
+
display: flex; align-items: center; gap: 12px; box-sizing: border-box; width: 100%; min-height: 48px; padding: 0 16px;
|
|
207
|
+
border: 0; color: var(--_ink); font: inherit; font-size: 14px; line-height: 20px; font-weight: 500;
|
|
208
|
+
text-align: left; text-decoration: none; cursor: pointer;
|
|
209
|
+
}
|
|
210
|
+
.item svg { flex-shrink: 0; color: var(--_ink-2); }
|
|
211
|
+
.item:focus-visible { outline: 2px solid var(--_ring); outline-offset: -2px; }
|
|
212
|
+
a.item:hover, button.item:hover { background-image: linear-gradient(var(--_hover), var(--_hover)); }
|
|
178
213
|
.wrap, .radios { display: flex; flex-direction: column; gap: 2px; }
|
|
179
214
|
.radios.all {
|
|
180
215
|
max-height: 300px; overflow-y: auto; overscroll-behavior: contain;
|
|
@@ -206,7 +241,7 @@ exports.ACCOUNT_CSS = `${contract_js_1.CONTRACT_CSS}
|
|
|
206
241
|
.more:focus-visible, .signout:focus-visible { outline: 2px solid var(--_ring); outline-offset: -2px; }
|
|
207
242
|
.signout {
|
|
208
243
|
display: flex; align-items: center; gap: 12px; box-sizing: border-box; width: 100%; min-height: 52px; padding: 0 16px;
|
|
209
|
-
border: 0;
|
|
244
|
+
border: 0; color: var(--_ink); font: inherit; font-size: 14px; font-weight: 500;
|
|
210
245
|
text-align: left; cursor: pointer;
|
|
211
246
|
}
|
|
212
247
|
.signout[aria-busy="true"] { color: var(--_ink-2); cursor: progress; }
|
|
@@ -231,7 +266,7 @@ exports.ACCOUNT_CSS = `${contract_js_1.CONTRACT_CSS}
|
|
|
231
266
|
.popup { background: var(--_tint-solid); -webkit-backdrop-filter: none; backdrop-filter: none; }
|
|
232
267
|
}
|
|
233
268
|
@media (forced-colors: active) {
|
|
234
|
-
.popup, .panel, .org { border: 1px solid CanvasText; background: Canvas; }
|
|
269
|
+
.popup, .panel, .org, .item { border: 1px solid CanvasText; background: Canvas; }
|
|
235
270
|
.org[aria-checked="true"] { outline: 2px solid Highlight; outline-offset: -2px; }
|
|
236
271
|
.avatar, .org-mark { border: 1px solid CanvasText; }
|
|
237
272
|
.pill, .legal a { color: LinkText; }
|
|
@@ -281,8 +316,8 @@ function emailLine(email) {
|
|
|
281
316
|
}
|
|
282
317
|
return line;
|
|
283
318
|
}
|
|
284
|
-
/**
|
|
285
|
-
function
|
|
319
|
+
/** An address the product gave, absolute or relative to the page, and only ever http or https. */
|
|
320
|
+
function productAddress(value) {
|
|
286
321
|
if (typeof value !== "string" || value === "")
|
|
287
322
|
return null;
|
|
288
323
|
try {
|
|
@@ -292,6 +327,108 @@ function productPicture(value) {
|
|
|
292
327
|
return null;
|
|
293
328
|
}
|
|
294
329
|
}
|
|
330
|
+
const ICON_TAGS = new Set([
|
|
331
|
+
"svg",
|
|
332
|
+
"g",
|
|
333
|
+
"path",
|
|
334
|
+
"circle",
|
|
335
|
+
"ellipse",
|
|
336
|
+
"line",
|
|
337
|
+
"polyline",
|
|
338
|
+
"polygon",
|
|
339
|
+
"rect",
|
|
340
|
+
]);
|
|
341
|
+
const ICON_ATTRIBUTES = new Set([
|
|
342
|
+
"viewBox",
|
|
343
|
+
"d",
|
|
344
|
+
"cx",
|
|
345
|
+
"cy",
|
|
346
|
+
"r",
|
|
347
|
+
"rx",
|
|
348
|
+
"ry",
|
|
349
|
+
"x",
|
|
350
|
+
"y",
|
|
351
|
+
"x1",
|
|
352
|
+
"y1",
|
|
353
|
+
"x2",
|
|
354
|
+
"y2",
|
|
355
|
+
"width",
|
|
356
|
+
"height",
|
|
357
|
+
"points",
|
|
358
|
+
"fill",
|
|
359
|
+
"stroke",
|
|
360
|
+
"stroke-width",
|
|
361
|
+
"stroke-linecap",
|
|
362
|
+
"stroke-linejoin",
|
|
363
|
+
"fill-rule",
|
|
364
|
+
"clip-rule",
|
|
365
|
+
"transform",
|
|
366
|
+
"opacity",
|
|
367
|
+
]);
|
|
368
|
+
/**
|
|
369
|
+
* A product's icon, kept to shapes: no script, no link, no style and no reference
|
|
370
|
+
* to anything outside it, because it is drawn inside every page the element is on.
|
|
371
|
+
*/
|
|
372
|
+
function iconOf(markup) {
|
|
373
|
+
let parsed;
|
|
374
|
+
try {
|
|
375
|
+
parsed = new DOMParser().parseFromString(markup, "image/svg+xml");
|
|
376
|
+
}
|
|
377
|
+
catch {
|
|
378
|
+
return null;
|
|
379
|
+
}
|
|
380
|
+
const root = parsed.documentElement;
|
|
381
|
+
if (root.localName !== "svg" || parsed.getElementsByTagName("parsererror").length > 0)
|
|
382
|
+
return null;
|
|
383
|
+
const copy = (source) => {
|
|
384
|
+
if (!ICON_TAGS.has(source.localName))
|
|
385
|
+
return null;
|
|
386
|
+
const made = document.createElementNS(dom_js_1.SVG_NS, source.localName);
|
|
387
|
+
for (const attribute of [...source.attributes]) {
|
|
388
|
+
if (ICON_ATTRIBUTES.has(attribute.name) && !/url\(|javascript:|&/iu.test(attribute.value)) {
|
|
389
|
+
made.setAttribute(attribute.name, attribute.value);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
for (const child of [...source.children]) {
|
|
393
|
+
const kept = copy(child);
|
|
394
|
+
if (kept !== null)
|
|
395
|
+
made.append(kept);
|
|
396
|
+
}
|
|
397
|
+
return made;
|
|
398
|
+
};
|
|
399
|
+
const svg = copy(root);
|
|
400
|
+
if (svg === null)
|
|
401
|
+
return null;
|
|
402
|
+
svg.setAttribute("width", "18");
|
|
403
|
+
svg.setAttribute("height", "18");
|
|
404
|
+
svg.setAttribute("aria-hidden", "true");
|
|
405
|
+
svg.setAttribute("focusable", "false");
|
|
406
|
+
return svg;
|
|
407
|
+
}
|
|
408
|
+
function itemsOf(value) {
|
|
409
|
+
if (!Array.isArray(value))
|
|
410
|
+
return [];
|
|
411
|
+
const out = [];
|
|
412
|
+
for (const one of value) {
|
|
413
|
+
if (typeof one !== "object" || one === null)
|
|
414
|
+
continue;
|
|
415
|
+
const { id, label, href, onSelect, icon } = one;
|
|
416
|
+
if (typeof id !== "string" || id === "" || typeof label !== "string" || label.trim() === "")
|
|
417
|
+
continue;
|
|
418
|
+
const link = typeof href === "string" ? productAddress(href) : null;
|
|
419
|
+
const select = typeof onSelect === "function" ? onSelect : null;
|
|
420
|
+
if (link === null && select === null)
|
|
421
|
+
continue;
|
|
422
|
+
out.push({
|
|
423
|
+
id,
|
|
424
|
+
label: label.trim(),
|
|
425
|
+
href: link,
|
|
426
|
+
onSelect: select,
|
|
427
|
+
icon: typeof icon === "string" ? iconOf(icon) : null,
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
return out;
|
|
431
|
+
}
|
|
295
432
|
function personOf(value) {
|
|
296
433
|
if (typeof value !== "object" || value === null)
|
|
297
434
|
return null;
|
|
@@ -316,9 +453,8 @@ function organisationsOf(value) {
|
|
|
316
453
|
const { id, name, role, active } = one;
|
|
317
454
|
if (typeof id !== "string" || id === "" || typeof name !== "string" || name.trim() === "")
|
|
318
455
|
continue;
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
out.push({ id, name: name.trim(), role: role, active: active === true });
|
|
456
|
+
const label = typeof role === "string" && role.trim() !== "" ? role.trim() : null;
|
|
457
|
+
out.push({ id, name: name.trim(), role: label, active: active === true });
|
|
322
458
|
}
|
|
323
459
|
// The active one first: the row says which one you are acting in.
|
|
324
460
|
const active = out.findIndex((one) => one.active);
|
|
@@ -347,6 +483,8 @@ function createAccountElementClass() {
|
|
|
347
483
|
#viaIdentity = false;
|
|
348
484
|
#onSignOut = null;
|
|
349
485
|
#onSwitch = null;
|
|
486
|
+
#items = [];
|
|
487
|
+
#given = [];
|
|
350
488
|
#open = false;
|
|
351
489
|
#sheet = false;
|
|
352
490
|
#allShown = false;
|
|
@@ -487,16 +625,28 @@ function createAccountElementClass() {
|
|
|
487
625
|
this.#onSwitch = typeof next === "function" ? next : null;
|
|
488
626
|
this.#render();
|
|
489
627
|
}
|
|
628
|
+
static get observedAttributes() {
|
|
629
|
+
return ["layout"];
|
|
630
|
+
}
|
|
631
|
+
attributeChangedCallback() {
|
|
632
|
+
this.#face = "";
|
|
633
|
+
this.#render();
|
|
634
|
+
}
|
|
635
|
+
get items() {
|
|
636
|
+
return this.#given;
|
|
637
|
+
}
|
|
638
|
+
/** The product's own items. None, and the group is absent. */
|
|
639
|
+
set items(next) {
|
|
640
|
+
this.#given = Array.isArray(next) ? next : [];
|
|
641
|
+
this.#items = itemsOf(next);
|
|
642
|
+
this.#render();
|
|
643
|
+
}
|
|
490
644
|
get labels() {
|
|
491
645
|
return this.#labels;
|
|
492
646
|
}
|
|
493
647
|
/** Any of the words, replaced; the rest stay English. */
|
|
494
648
|
set labels(next) {
|
|
495
|
-
this.#labels = {
|
|
496
|
-
...exports.ACCOUNT_LABELS,
|
|
497
|
-
...next,
|
|
498
|
-
roles: { ...exports.ACCOUNT_LABELS.roles, ...next.roles },
|
|
499
|
-
};
|
|
649
|
+
this.#labels = { ...exports.ACCOUNT_LABELS, ...next };
|
|
500
650
|
this.#render();
|
|
501
651
|
}
|
|
502
652
|
get open() {
|
|
@@ -512,6 +662,7 @@ function createAccountElementClass() {
|
|
|
512
662
|
"viaIdentity",
|
|
513
663
|
"onSignOut",
|
|
514
664
|
"onSwitch",
|
|
665
|
+
"items",
|
|
515
666
|
"labels",
|
|
516
667
|
]) {
|
|
517
668
|
if (Object.prototype.hasOwnProperty.call(this, key)) {
|
|
@@ -725,6 +876,9 @@ function createAccountElementClass() {
|
|
|
725
876
|
void this.#choose(id);
|
|
726
877
|
break;
|
|
727
878
|
}
|
|
879
|
+
case "item":
|
|
880
|
+
this.#chosen(target, event);
|
|
881
|
+
break;
|
|
728
882
|
default:
|
|
729
883
|
break;
|
|
730
884
|
}
|
|
@@ -757,6 +911,25 @@ function createAccountElementClass() {
|
|
|
757
911
|
if (moved !== undefined)
|
|
758
912
|
this.#byKey(`org:${moved.id}`)?.focus();
|
|
759
913
|
}
|
|
914
|
+
/** One of the product's items: its handler runs, a link goes where it goes, and the popup closes. */
|
|
915
|
+
#chosen(target, event) {
|
|
916
|
+
const item = this.#items.find((one) => one.id === target.dataset["id"]);
|
|
917
|
+
if (item === undefined)
|
|
918
|
+
return;
|
|
919
|
+
try {
|
|
920
|
+
item.onSelect?.(event);
|
|
921
|
+
}
|
|
922
|
+
catch {
|
|
923
|
+
// The product's fault, and not a reason to leave the popup open over the page.
|
|
924
|
+
}
|
|
925
|
+
this.close(false);
|
|
926
|
+
// Back to the trigger unless the item took focus somewhere, as a dialog it opened would.
|
|
927
|
+
setTimeout(() => {
|
|
928
|
+
if (document.activeElement === document.body || document.activeElement === null) {
|
|
929
|
+
this.#returnFocus();
|
|
930
|
+
}
|
|
931
|
+
}, 0);
|
|
932
|
+
}
|
|
760
933
|
async #choose(id) {
|
|
761
934
|
const handler = this.#onSwitch;
|
|
762
935
|
const organisation = this.#organisations.find((one) => one.id === id);
|
|
@@ -806,7 +979,7 @@ function createAccountElementClass() {
|
|
|
806
979
|
#faceOf(view, person) {
|
|
807
980
|
const own = view?.person.hasIdentity === true ? view.person : null;
|
|
808
981
|
return {
|
|
809
|
-
picture: own?.avatarUrl ??
|
|
982
|
+
picture: own?.avatarUrl ?? productAddress(person.avatarUrl),
|
|
810
983
|
tint: own?.tint ?? (0, tint_js_1.avatarTint)(person.id ?? person.email.toLowerCase()),
|
|
811
984
|
initial: initialOf(person.name),
|
|
812
985
|
};
|
|
@@ -823,13 +996,20 @@ function createAccountElementClass() {
|
|
|
823
996
|
const labels = this.#labels;
|
|
824
997
|
const view = state.view;
|
|
825
998
|
const name = labels.trigger(person.name);
|
|
826
|
-
this
|
|
999
|
+
const row = this.getAttribute("layout") === "row";
|
|
1000
|
+
// In a row the address is on screen too, so the name says it as well (2.5.3, label in name).
|
|
1001
|
+
this.#trigger.setAttribute("aria-label", row ? `${name}, ${person.email}` : name);
|
|
827
1002
|
this.#tip.textContent = name;
|
|
828
1003
|
const face = this.#faceOf(view, person);
|
|
829
|
-
const signature = `${face.picture ?? ""} ${String(face.tint)} ${face.initial}`;
|
|
1004
|
+
const signature = `${face.picture ?? ""} ${String(face.tint)} ${face.initial} ${row ? `${person.name} ${person.email}` : ""}`;
|
|
830
1005
|
if (signature !== this.#face) {
|
|
831
1006
|
this.#face = signature;
|
|
832
1007
|
this.#trigger.replaceChildren(avatar(face, "s32"));
|
|
1008
|
+
if (row) {
|
|
1009
|
+
const who = (0, dom_js_1.element)("span", { class: "who", "aria-hidden": "true" });
|
|
1010
|
+
who.append((0, dom_js_1.element)("span", { class: "who-name" }, person.name), (0, dom_js_1.element)("span", { class: "who-mail" }, person.email));
|
|
1011
|
+
this.#trigger.append(who);
|
|
1012
|
+
}
|
|
833
1013
|
}
|
|
834
1014
|
if (!this.#open)
|
|
835
1015
|
return;
|
|
@@ -910,8 +1090,7 @@ function createAccountElementClass() {
|
|
|
910
1090
|
out.append(card);
|
|
911
1091
|
if (this.#organisations.length > 0)
|
|
912
1092
|
out.append(this.#organisationsSection());
|
|
913
|
-
|
|
914
|
-
out.append(this.#signOutSection(product));
|
|
1093
|
+
out.append(...this.#actionSections(view, product));
|
|
915
1094
|
if (view !== null && view.legal !== null) {
|
|
916
1095
|
const footer = (0, dom_js_1.element)("div", { class: "legal" });
|
|
917
1096
|
footer.append((0, dom_js_1.element)("a", { href: view.legal.termsUrl, "data-key": "terms" }, labels.terms), (0, dom_js_1.element)("span", { "aria-hidden": "true" }, "·"), (0, dom_js_1.element)("a", { href: view.legal.privacyUrl, "data-key": "privacy" }, labels.privacy));
|
|
@@ -938,19 +1117,20 @@ function createAccountElementClass() {
|
|
|
938
1117
|
if (this.#switching !== null)
|
|
939
1118
|
radios.setAttribute("aria-busy", "true");
|
|
940
1119
|
for (const one of shown) {
|
|
941
|
-
const role = labels.roles[one.role];
|
|
942
1120
|
const row = (0, dom_js_1.element)("div", {
|
|
943
1121
|
class: "org panel",
|
|
944
1122
|
role: "radio",
|
|
945
1123
|
"aria-checked": String(one.active),
|
|
946
|
-
"aria-label": `${one.name}, ${role}
|
|
1124
|
+
"aria-label": one.role ? `${one.name}, ${one.role}` : one.name,
|
|
947
1125
|
tabindex: one === stop ? "0" : "-1",
|
|
948
1126
|
"data-action": "org",
|
|
949
1127
|
"data-id": one.id,
|
|
950
1128
|
"data-key": `org:${one.id}`,
|
|
951
1129
|
});
|
|
952
1130
|
const text = (0, dom_js_1.element)("span", { class: "org-text", "aria-hidden": "true" });
|
|
953
|
-
text.append((0, dom_js_1.element)("span", { class: "org-name", title: one.name }, one.name)
|
|
1131
|
+
text.append((0, dom_js_1.element)("span", { class: "org-name", title: one.name }, one.name));
|
|
1132
|
+
if (one.role)
|
|
1133
|
+
text.append((0, dom_js_1.element)("span", { class: "org-role" }, one.role));
|
|
954
1134
|
row.append((0, dom_js_1.element)("span", { class: `org-mark t${String((0, tint_js_1.avatarTint)(one.id))}`, "aria-hidden": "true" }, initialOf(one.name)), text);
|
|
955
1135
|
if (one.active) {
|
|
956
1136
|
const tag = (0, dom_js_1.element)("span", { class: "active", "aria-hidden": "true" });
|
|
@@ -970,12 +1150,77 @@ function createAccountElementClass() {
|
|
|
970
1150
|
}
|
|
971
1151
|
return section;
|
|
972
1152
|
}
|
|
973
|
-
|
|
1153
|
+
/**
|
|
1154
|
+
* The product's items and Sign out. Until the product signs in through
|
|
1155
|
+
* Identity, Sign out is the last row of the items' group, one of the
|
|
1156
|
+
* product's own actions; once it does, it ends both sessions and stands apart.
|
|
1157
|
+
*/
|
|
1158
|
+
#actionSections(view, product) {
|
|
974
1159
|
const labels = this.#labels;
|
|
975
1160
|
const name = product ?? labels.thisProduct;
|
|
1161
|
+
const sections = [];
|
|
1162
|
+
const joined = !this.#viaIdentity;
|
|
1163
|
+
let stack = null;
|
|
1164
|
+
let section = null;
|
|
1165
|
+
if (this.#items.length > 0) {
|
|
1166
|
+
section = (0, dom_js_1.element)("div", { class: "section" });
|
|
1167
|
+
if (product !== null) {
|
|
1168
|
+
const heading = (0, dom_js_1.element)("p", { class: "heading with-mark" });
|
|
1169
|
+
const mark = view?.yours.find((tile) => tile.current)?.iconUrl ?? null;
|
|
1170
|
+
if (mark !== null) {
|
|
1171
|
+
heading.append((0, dom_js_1.element)("img", {
|
|
1172
|
+
src: mark,
|
|
1173
|
+
alt: "",
|
|
1174
|
+
width: "16",
|
|
1175
|
+
height: "16",
|
|
1176
|
+
referrerpolicy: "no-referrer",
|
|
1177
|
+
}));
|
|
1178
|
+
}
|
|
1179
|
+
heading.append(product);
|
|
1180
|
+
section.append(heading);
|
|
1181
|
+
}
|
|
1182
|
+
stack = (0, dom_js_1.element)("div", { class: "stack" });
|
|
1183
|
+
for (const item of this.#items)
|
|
1184
|
+
stack.append(this.#itemRow(item));
|
|
1185
|
+
section.append(stack);
|
|
1186
|
+
sections.push(section);
|
|
1187
|
+
}
|
|
1188
|
+
if (this.#onSignOut === null)
|
|
1189
|
+
return sections;
|
|
1190
|
+
const button = this.#signOutButton(name);
|
|
1191
|
+
if (joined && stack !== null && section !== null) {
|
|
1192
|
+
stack.append(button);
|
|
1193
|
+
}
|
|
1194
|
+
else {
|
|
1195
|
+
section = (0, dom_js_1.element)("div", { class: "section" });
|
|
1196
|
+
stack = (0, dom_js_1.element)("div", { class: "stack" });
|
|
1197
|
+
stack.append(button);
|
|
1198
|
+
section.append(stack);
|
|
1199
|
+
sections.push(section);
|
|
1200
|
+
}
|
|
1201
|
+
if (this.#signOut === "failed") {
|
|
1202
|
+
button.setAttribute("aria-describedby", "signout-failed");
|
|
1203
|
+
section.append((0, dom_js_1.element)("p", { id: "signout-failed", class: "error", role: "alert" }, labels.signOutFailed(name)));
|
|
1204
|
+
}
|
|
1205
|
+
return sections;
|
|
1206
|
+
}
|
|
1207
|
+
#itemRow(item) {
|
|
1208
|
+
const row = item.href === null
|
|
1209
|
+
? (0, dom_js_1.element)("button", { type: "button" })
|
|
1210
|
+
: (0, dom_js_1.element)("a", { href: item.href });
|
|
1211
|
+
row.className = "item panel";
|
|
1212
|
+
row.dataset["action"] = "item";
|
|
1213
|
+
row.dataset["id"] = item.id;
|
|
1214
|
+
row.dataset["key"] = `item:${item.id}`;
|
|
1215
|
+
if (item.icon !== null)
|
|
1216
|
+
row.append(item.icon.cloneNode(true));
|
|
1217
|
+
row.append((0, dom_js_1.element)("span", {}, item.label));
|
|
1218
|
+
return row;
|
|
1219
|
+
}
|
|
1220
|
+
#signOutButton(name) {
|
|
1221
|
+
const labels = this.#labels;
|
|
976
1222
|
const busy = this.#signOut === "busy";
|
|
977
1223
|
const failed = this.#signOut === "failed";
|
|
978
|
-
const section = (0, dom_js_1.element)("div", { class: "section" });
|
|
979
1224
|
const button = (0, dom_js_1.element)("button", {
|
|
980
1225
|
type: "button",
|
|
981
1226
|
class: "signout panel",
|
|
@@ -996,12 +1241,7 @@ function createAccountElementClass() {
|
|
|
996
1241
|
: this.#viaIdentity
|
|
997
1242
|
? labels.signOutBoth(name)
|
|
998
1243
|
: labels.signOut(name)));
|
|
999
|
-
|
|
1000
|
-
if (failed) {
|
|
1001
|
-
button.setAttribute("aria-describedby", "signout-failed");
|
|
1002
|
-
section.append((0, dom_js_1.element)("p", { id: "signout-failed", class: "error", role: "alert" }, labels.signOutFailed(name)));
|
|
1003
|
-
}
|
|
1004
|
-
return section;
|
|
1244
|
+
return button;
|
|
1005
1245
|
}
|
|
1006
1246
|
};
|
|
1007
1247
|
}
|
package/dist/elements/apps.js
CHANGED
|
@@ -79,6 +79,7 @@ exports.APPS_CSS = `${contract_js_1.CONTRACT_CSS}
|
|
|
79
79
|
animation: rise 120ms ease-out;
|
|
80
80
|
}
|
|
81
81
|
:host([align="start"]) .popup { right: auto; left: 0; }
|
|
82
|
+
:host([placement="top"]) .popup:not(.sheet) { top: auto; bottom: calc(100% + 8px); }
|
|
82
83
|
.popup[hidden], .scrim[hidden] { display: none; }
|
|
83
84
|
.scroller { overflow-y: auto; overscroll-behavior: contain; display: flex; flex-direction: column; gap: 4px; }
|
|
84
85
|
.popup.sheet {
|
package/dist/elements/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* in a browser.
|
|
6
6
|
*/
|
|
7
7
|
import type { ChromeCore } from "../core.js";
|
|
8
|
-
import { type AccountLabels, type AccountOrganisation, type AccountPerson, type SignOutHandler, type SwitchHandler } from "./account.js";
|
|
8
|
+
import { type AccountItem, type AccountLabels, type AccountOrganisation, type AccountPerson, type SignOutHandler, type SwitchHandler } from "./account.js";
|
|
9
9
|
import { type AppsLabels } from "./apps.js";
|
|
10
10
|
/** What `<gp-apps>` offers its page. */
|
|
11
11
|
export interface AppsElement extends HTMLElement {
|
|
@@ -29,6 +29,8 @@ export interface AccountElement extends HTMLElement {
|
|
|
29
29
|
viaIdentity: boolean;
|
|
30
30
|
onSignOut: SignOutHandler | null;
|
|
31
31
|
onSwitch: SwitchHandler | null;
|
|
32
|
+
get items(): AccountItem[];
|
|
33
|
+
set items(next: AccountItem[] | null);
|
|
32
34
|
get labels(): AccountLabels;
|
|
33
35
|
set labels(next: Partial<AccountLabels>);
|
|
34
36
|
readonly open: boolean;
|
|
@@ -43,6 +45,6 @@ declare global {
|
|
|
43
45
|
}
|
|
44
46
|
/** Defines `<gp-apps>` and `<gp-account>`, each once. */
|
|
45
47
|
export declare function defineChromeElements(): void;
|
|
46
|
-
export { ACCOUNT_LABELS, createAccountElementClass, defineAccountElement, type
|
|
48
|
+
export { ACCOUNT_LABELS, createAccountElementClass, defineAccountElement, iconOf, type AccountItem, type AccountLabels, type AccountOrganisation, type AccountPerson, type SignOutHandler, type SwitchHandler, } from "./account.js";
|
|
47
49
|
export { APPS_LABELS, createAppsElementClass, defineAppsElement, type AppsLabels } from "./apps.js";
|
|
48
50
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/elements/index.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* in a browser.
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.defineAppsElement = exports.createAppsElementClass = exports.APPS_LABELS = exports.defineAccountElement = exports.createAccountElementClass = exports.ACCOUNT_LABELS = void 0;
|
|
9
|
+
exports.defineAppsElement = exports.createAppsElementClass = exports.APPS_LABELS = exports.iconOf = exports.defineAccountElement = exports.createAccountElementClass = exports.ACCOUNT_LABELS = void 0;
|
|
10
10
|
exports.defineChromeElements = defineChromeElements;
|
|
11
11
|
const account_js_1 = require("./account.js");
|
|
12
12
|
const apps_js_1 = require("./apps.js");
|
|
@@ -19,6 +19,7 @@ var account_js_2 = require("./account.js");
|
|
|
19
19
|
Object.defineProperty(exports, "ACCOUNT_LABELS", { enumerable: true, get: function () { return account_js_2.ACCOUNT_LABELS; } });
|
|
20
20
|
Object.defineProperty(exports, "createAccountElementClass", { enumerable: true, get: function () { return account_js_2.createAccountElementClass; } });
|
|
21
21
|
Object.defineProperty(exports, "defineAccountElement", { enumerable: true, get: function () { return account_js_2.defineAccountElement; } });
|
|
22
|
+
Object.defineProperty(exports, "iconOf", { enumerable: true, get: function () { return account_js_2.iconOf; } });
|
|
22
23
|
var apps_js_2 = require("./apps.js");
|
|
23
24
|
Object.defineProperty(exports, "APPS_LABELS", { enumerable: true, get: function () { return apps_js_2.APPS_LABELS; } });
|
|
24
25
|
Object.defineProperty(exports, "createAppsElementClass", { enumerable: true, get: function () { return apps_js_2.createAppsElementClass; } });
|
package/dist/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export { CACHE_KEY, CACHE_MAX_AGE_MS, DEFAULT_TIMEOUT_MS, createChromeCore, type ChromeCore, type ChromeCoreOptions, type ChromeState, type ChromeStatus, type StorageLike, } from "./core.js";
|
|
11
11
|
export { catalogueMarkUrl, createCatalogue, type Catalogue, type CatalogueOptions, } from "./catalogue.js";
|
|
12
|
-
export { ACCOUNT_LABELS, APPS_LABELS, createAccountElementClass, createAppsElementClass, defineAccountElement, defineAppsElement, defineChromeElements, type AccountElement, type AccountLabels, type AccountOrganisation, type AccountPerson, type AppsElement, type AppsLabels, type
|
|
12
|
+
export { ACCOUNT_LABELS, APPS_LABELS, createAccountElementClass, createAppsElementClass, defineAccountElement, defineAppsElement, defineChromeElements, type AccountElement, type AccountItem, type AccountLabels, type AccountOrganisation, type AccountPerson, type AppsElement, type AppsLabels, type SignOutHandler, type SwitchHandler, } from "./elements/index.js";
|
|
13
13
|
export { AVATAR_INK, AVATAR_TINTS, avatarTint } from "./tint.js";
|
|
14
14
|
export type { CatalogueEntry, ChromeIdentityLinks, ChromeLegal, ChromePerson, ChromePublicView, ChromeTile, ChromeView, } from "./types.js";
|
|
15
15
|
export { safeUrl } from "./validate.js";
|
package/dist/react.d.ts
CHANGED
|
@@ -9,17 +9,20 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { type ReactElement } from "react";
|
|
11
11
|
import type { ChromeCore } from "./core.js";
|
|
12
|
-
import type { AccountLabels, AccountOrganisation, AccountPerson, AppsLabels, SignOutHandler, SwitchHandler } from "./elements/index.js";
|
|
12
|
+
import type { AccountItem, AccountLabels, AccountOrganisation, AccountPerson, AppsLabels, SignOutHandler, SwitchHandler } from "./elements/index.js";
|
|
13
13
|
/** Which edge of the trigger the popup lines up with. The end, by default. */
|
|
14
14
|
type Align = "start" | "end";
|
|
15
|
+
/** Whether the popup opens below the trigger (a top bar) or above it (the bottom of a sidebar). Below, by default. */
|
|
16
|
+
type Placement = "bottom" | "top";
|
|
15
17
|
export interface GpAppsProps {
|
|
16
18
|
core: ChromeCore | null;
|
|
17
19
|
labels?: Partial<AppsLabels>;
|
|
18
20
|
align?: Align;
|
|
21
|
+
placement?: Placement;
|
|
19
22
|
className?: string;
|
|
20
23
|
}
|
|
21
24
|
/** `<gp-apps>`, the apps picker. */
|
|
22
|
-
export declare function GpApps({ core, labels, align, className }: GpAppsProps): ReactElement;
|
|
25
|
+
export declare function GpApps({ core, labels, align, placement, className }: GpAppsProps): ReactElement;
|
|
23
26
|
export interface GpAccountProps {
|
|
24
27
|
core: ChromeCore | null;
|
|
25
28
|
/** Who is signed in, from the product's own session. Nothing renders without one. */
|
|
@@ -31,11 +34,16 @@ export interface GpAccountProps {
|
|
|
31
34
|
viaIdentity?: boolean;
|
|
32
35
|
onSignOut?: SignOutHandler | null;
|
|
33
36
|
onSwitch?: SwitchHandler | null;
|
|
37
|
+
/** The product's own items, after the organisations. */
|
|
38
|
+
items?: AccountItem[] | null;
|
|
34
39
|
labels?: Partial<AccountLabels>;
|
|
40
|
+
/** `row` for the bottom of a sidebar: the picture with the name over the address, full width. */
|
|
41
|
+
layout?: "button" | "row";
|
|
35
42
|
align?: Align;
|
|
43
|
+
placement?: Placement;
|
|
36
44
|
className?: string;
|
|
37
45
|
}
|
|
38
46
|
/** `<gp-account>`, the account popup. */
|
|
39
|
-
export declare function GpAccount({ core, person, organisations, product, viaIdentity, onSignOut, onSwitch, labels, align, className, }: GpAccountProps): ReactElement;
|
|
47
|
+
export declare function GpAccount({ core, person, organisations, product, viaIdentity, onSignOut, onSwitch, items, labels, layout, align, placement, className, }: GpAccountProps): ReactElement;
|
|
40
48
|
export {};
|
|
41
49
|
//# sourceMappingURL=react.d.ts.map
|
package/dist/react.js
CHANGED
|
@@ -15,7 +15,7 @@ exports.GpAccount = GpAccount;
|
|
|
15
15
|
const react_1 = require("react");
|
|
16
16
|
const index_js_1 = require("./elements/index.js");
|
|
17
17
|
/** `<gp-apps>`, the apps picker. */
|
|
18
|
-
function GpApps({ core, labels, align, className }) {
|
|
18
|
+
function GpApps({ core, labels, align, placement, className }) {
|
|
19
19
|
const ref = (0, react_1.useRef)(null);
|
|
20
20
|
(0, react_1.useEffect)(() => {
|
|
21
21
|
(0, index_js_1.defineChromeElements)();
|
|
@@ -28,10 +28,10 @@ function GpApps({ core, labels, align, className }) {
|
|
|
28
28
|
if (ref.current !== null && labels !== undefined)
|
|
29
29
|
ref.current.labels = labels;
|
|
30
30
|
}, [labels]);
|
|
31
|
-
return (0, react_1.createElement)("gp-apps", { ref, className, align });
|
|
31
|
+
return (0, react_1.createElement)("gp-apps", { ref, className, align, placement });
|
|
32
32
|
}
|
|
33
33
|
/** `<gp-account>`, the account popup. */
|
|
34
|
-
function GpAccount({ core, person, organisations = null, product = null, viaIdentity = false, onSignOut = null, onSwitch = null, labels, align, className, }) {
|
|
34
|
+
function GpAccount({ core, person, organisations = null, product = null, viaIdentity = false, onSignOut = null, onSwitch = null, items = null, labels, layout, align, placement, className, }) {
|
|
35
35
|
const ref = (0, react_1.useRef)(null);
|
|
36
36
|
(0, react_1.useEffect)(() => {
|
|
37
37
|
(0, index_js_1.defineChromeElements)();
|
|
@@ -64,10 +64,14 @@ function GpAccount({ core, person, organisations = null, product = null, viaIden
|
|
|
64
64
|
if (ref.current !== null)
|
|
65
65
|
ref.current.onSwitch = onSwitch;
|
|
66
66
|
}, [onSwitch]);
|
|
67
|
+
(0, react_1.useEffect)(() => {
|
|
68
|
+
if (ref.current !== null)
|
|
69
|
+
ref.current.items = items;
|
|
70
|
+
}, [items]);
|
|
67
71
|
(0, react_1.useEffect)(() => {
|
|
68
72
|
if (ref.current !== null && labels !== undefined)
|
|
69
73
|
ref.current.labels = labels;
|
|
70
74
|
}, [labels]);
|
|
71
|
-
return (0, react_1.createElement)("gp-account", { ref, className, align });
|
|
75
|
+
return (0, react_1.createElement)("gp-account", { ref, className, layout, align, placement });
|
|
72
76
|
}
|
|
73
77
|
//# sourceMappingURL=react.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ghub/identity-chrome",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "The shared header controls of the Gelhaus Solutions products: the apps picker and the account popup, as custom elements with shadow DOM and as React wrappers. The browser half (state, cache, the last list held for an outage) and the server half (the token calls and a route handler). No dependencies.",
|
|
5
5
|
"author": "Gelhaus Solutions (Enno Gelhaus) <egelhaus@ennogelhaus.de> (https://ennogelhaus.de)",
|
|
6
6
|
"homepage": "https://ennogelhaus.de",
|