@ghub/identity-chrome 0.4.0 → 0.5.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 CHANGED
@@ -76,7 +76,9 @@ 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: "Admin", active: true }];
79
+ account.organisations = [
80
+ { id: "org_1", name: "Hafen Labs", role: "Admin", iconUrl: "/orgs/org_1/avatar", active: true },
81
+ ];
80
82
  account.onSwitch = async (id) => switchOrganisation(id); // reject when it did not switch
81
83
  account.onSignOut = async () => {
82
84
  await signOut(); // reject when it failed: the popup says they are still signed in
@@ -90,7 +92,8 @@ and the account popup renders nothing without a person.
90
92
 
91
93
  **The account popup shows mostly what the product passes**: the name and address from your
92
94
  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.
95
+ person's language and shown as given; none, and the row has no second line) and their own picture
96
+ where they have one (`iconUrl`; the initial otherwise, and if it does not load), and your two actions.
94
97
  Identity adds the picture, the colour of the initial, the links to its own account and set up
95
98
  screens and the footer, so the popup still signs somebody out while Identity is down. The
96
99
  picture is Identity's if the person has one, else yours, else a coloured initial whose tint
@@ -69,6 +69,12 @@ export interface AccountOrganisation {
69
69
  * so the element has no list of its own. None, and the row has no second line.
70
70
  */
71
71
  role?: string | null;
72
+ /**
73
+ * The organisation's own picture, absolute or relative to the page, http or
74
+ * https only. Drawn in place of the initial, which comes back if it does not
75
+ * load.
76
+ */
77
+ iconUrl?: string | null;
72
78
  active: boolean;
73
79
  }
74
80
  /** One of the product's own items in the popup, such as its profile or its settings. */
@@ -229,8 +229,9 @@ exports.ACCOUNT_CSS = `${contract_js_1.CONTRACT_CSS}
229
229
  .wrap > .radios:last-child > .org:last-child { border-radius: 4px 4px 18px 18px; }
230
230
  .org-mark {
231
231
  display: grid; place-items: center; flex-shrink: 0; width: 32px; height: 32px; border-radius: 9px;
232
- color: ${tint_js_1.AVATAR_INK}; font-size: 14px; font-weight: 600;
232
+ overflow: hidden; color: ${tint_js_1.AVATAR_INK}; font-size: 14px; font-weight: 600;
233
233
  }
234
+ .org-mark img { display: block; width: 100%; height: 100%; object-fit: cover; }
234
235
  .org-text { flex: 1; min-width: 0; display: flex; flex-direction: column; }
235
236
  .org-name { font-size: 14px; line-height: 20px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
236
237
  .org-role { font-size: 12.5px; line-height: 17px; color: var(--_ink-2); }
@@ -352,6 +353,30 @@ function emailLine(email) {
352
353
  }
353
354
  return line;
354
355
  }
356
+ /** An organisation's picture where it has one that loads, else its initial on its tint. */
357
+ function orgMark(one) {
358
+ const initial = initialOf(one.name);
359
+ const mark = (0, dom_js_1.element)("span", {
360
+ class: `org-mark t${String((0, tint_js_1.avatarTint)(one.id))}`,
361
+ "aria-hidden": "true",
362
+ });
363
+ const picture = productAddress(one.iconUrl);
364
+ if (picture === null) {
365
+ mark.textContent = initial;
366
+ return mark;
367
+ }
368
+ const image = (0, dom_js_1.element)("img", {
369
+ src: picture,
370
+ alt: "",
371
+ decoding: "async",
372
+ referrerpolicy: "no-referrer",
373
+ });
374
+ image.addEventListener("error", () => {
375
+ mark.textContent = initial;
376
+ }, { once: true });
377
+ mark.append(image);
378
+ return mark;
379
+ }
355
380
  /** An address the product gave, absolute or relative to the page, and only ever http or https. */
356
381
  function productAddress(value) {
357
382
  if (typeof value !== "string" || value === "")
@@ -486,11 +511,17 @@ function organisationsOf(value) {
486
511
  for (const one of value) {
487
512
  if (typeof one !== "object" || one === null)
488
513
  continue;
489
- const { id, name, role, active } = one;
514
+ const { id, name, role, iconUrl, active } = one;
490
515
  if (typeof id !== "string" || id === "" || typeof name !== "string" || name.trim() === "")
491
516
  continue;
492
517
  const label = typeof role === "string" && role.trim() !== "" ? role.trim() : null;
493
- out.push({ id, name: name.trim(), role: label, active: active === true });
518
+ out.push({
519
+ id,
520
+ name: name.trim(),
521
+ role: label,
522
+ iconUrl: typeof iconUrl === "string" && iconUrl !== "" ? iconUrl : null,
523
+ active: active === true,
524
+ });
494
525
  }
495
526
  // The active one first: the row says which one you are acting in.
496
527
  const active = out.findIndex((one) => one.active);
@@ -1167,7 +1198,7 @@ function createAccountElementClass() {
1167
1198
  text.append((0, dom_js_1.element)("span", { class: "org-name", title: one.name }, one.name));
1168
1199
  if (one.role)
1169
1200
  text.append((0, dom_js_1.element)("span", { class: "org-role" }, one.role));
1170
- 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);
1201
+ row.append(orgMark(one), text);
1171
1202
  if (one.active) {
1172
1203
  const tag = (0, dom_js_1.element)("span", { class: "active", "aria-hidden": "true" });
1173
1204
  tag.append((0, dom_js_1.element)("span", { class: "check" }), labels.active);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghub/identity-chrome",
3
- "version": "0.4.0",
3
+ "version": "0.5.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",