@hotosm/hanko-auth 0.4.5 → 0.4.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotosm/hanko-auth",
3
- "version": "0.4.5",
3
+ "version": "0.4.6",
4
4
  "description": "Web component for HOTOSM SSO authentication with Hanko and OSM integration",
5
5
  "type": "module",
6
6
  "main": "dist/hanko-auth.umd.js",
@@ -43,11 +43,17 @@ export const styles = css`
43
43
  display: inline-grid;
44
44
  place-items: center;
45
45
  /* Use same styling as login-link button */
46
- padding: var(--login-btn-padding, var(--hot-spacing-x-small) var(--hot-spacing-medium));
46
+ padding: var(
47
+ --login-btn-padding,
48
+ var(--hot-spacing-x-small) var(--hot-spacing-medium)
49
+ );
47
50
  margin: var(--login-btn-margin, 0);
48
51
  font-size: var(--login-btn-text-size, var(--hot-font-size-medium));
49
52
  font-family: var(--login-btn-font-family, inherit);
50
- border-radius: var(--login-btn-border-radius, var(--hot-border-radius-medium));
53
+ border-radius: var(
54
+ --login-btn-border-radius,
55
+ var(--hot-border-radius-medium)
56
+ );
51
57
  }
52
58
 
53
59
  /* Invisible text to reserve button width */
@@ -255,9 +261,15 @@ export const styles = css`
255
261
  .login-link {
256
262
  color: var(--login-btn-text-color, white);
257
263
  font-size: var(--login-btn-text-size, var(--hot-font-size-medium));
258
- border-radius: var(--login-btn-border-radius, var(--hot-border-radius-medium));
264
+ border-radius: var(
265
+ --login-btn-border-radius,
266
+ var(--hot-border-radius-medium)
267
+ );
259
268
  text-decoration: none;
260
- padding: var(--login-btn-padding, var(--hot-spacing-x-small) var(--hot-spacing-medium));
269
+ padding: var(
270
+ --login-btn-padding,
271
+ var(--hot-spacing-x-small) var(--hot-spacing-medium)
272
+ );
261
273
  margin: var(--login-btn-margin, 0);
262
274
  display: inline-block;
263
275
  cursor: pointer;
@@ -365,7 +377,6 @@ export const styles = css`
365
377
  position: absolute;
366
378
  right: 0;
367
379
  background: white;
368
- border: 1px solid var(--hot-color-gray-100);
369
380
  border-radius: var(--hot-border-radius-medium);
370
381
  z-index: 1000;
371
382
  opacity: 0;
@@ -386,7 +397,7 @@ export const styles = css`
386
397
  .dropdown-content.open {
387
398
  opacity: 1;
388
399
  visibility: visible;
389
- transform: translateY(0);
400
+ transform: translateY(-1);
390
401
  }
391
402
 
392
403
  .dropdown-content button {
@@ -427,4 +438,64 @@ export const styles = css`
427
438
  width: 20px;
428
439
  height: 20px;
429
440
  }
441
+
442
+ /* Bar display mode */
443
+
444
+ :host([display="bar"]) {
445
+ width: 100%;
446
+ }
447
+
448
+ :host([display="bar"]) .dropdown {
449
+ display: block;
450
+ width: 100%;
451
+ }
452
+
453
+ .bar-trigger {
454
+ display: flex;
455
+ align-items: center;
456
+ width: 100%;
457
+ padding: var(--hot-spacing-small) var(--hot-spacing-medium);
458
+ background: none;
459
+ border: none;
460
+ cursor: pointer;
461
+ gap: var(--hot-spacing-small);
462
+ }
463
+
464
+ .bar-trigger:hover,
465
+ .bar-trigger:active,
466
+ .bar-trigger:focus {
467
+ background: none;
468
+ outline: none;
469
+ }
470
+
471
+ .bar-info {
472
+ display: flex;
473
+ align-items: center;
474
+ gap: var(--hot-spacing-small);
475
+ flex: 1;
476
+ min-width: 0;
477
+ }
478
+
479
+ .bar-email {
480
+ font-size: var(--hot-font-size-medium);
481
+ color: var(--hot-color-gray-900);
482
+ overflow: hidden;
483
+ text-overflow: ellipsis;
484
+ white-space: nowrap;
485
+ }
486
+
487
+ .bar-chevron {
488
+ width: 16px;
489
+ height: 16px;
490
+ flex-shrink: 0;
491
+ color: var(--hot-color-gray-900);
492
+ }
493
+
494
+ /* When bar-trigger is used as a login-link, override width behavior */
495
+ a.bar-trigger.login-link {
496
+ display: flex;
497
+ width: 100%;
498
+ box-sizing: border-box;
499
+ text-decoration: none;
500
+ }
430
501
  `;
package/src/hanko-auth.ts CHANGED
@@ -27,6 +27,8 @@ import accountIcon from "../assets/icon-account.svg";
27
27
  import logoutIcon from "../assets/icon-logout.svg";
28
28
  import mapIcon from "../assets/icon-map.svg";
29
29
  import checkIcon from "../assets/icon-check.svg";
30
+ import chevronDownIcon from "../assets/chevron-down.svg";
31
+ import chevronUpIcon from "../assets/chevron-up.svg";
30
32
 
31
33
  // Track if Hanko has been registered globally
32
34
  let hankoRegistered = false;
@@ -129,6 +131,9 @@ export class HankoAuth extends LitElement {
129
131
  | "primary"
130
132
  | "neutral"
131
133
  | "danger" = "primary";
134
+ // Display mode: "default" (compact avatar button) or "bar" (full-width bar with avatar + email + chevron)
135
+ @property({ type: String, reflect: true }) display: "default" | "bar" =
136
+ "default";
132
137
 
133
138
  // Internal state
134
139
  @state() private user: UserState | null = null;
@@ -167,6 +172,40 @@ export class HankoAuth extends LitElement {
167
172
  }
168
173
  };
169
174
 
175
+ /** Dropdown menu content for bar display mode (no email, only action links) */
176
+ private renderBarDropdownContent() {
177
+ return html`
178
+ <div class="dropdown-content ${this.isOpen ? "open" : ""}">
179
+ <button data-action="profile" @click=${this.handleDropdownSelect}>
180
+ <img src="${accountIcon}" class="icon" alt="Account icon" />
181
+ ${this.t("myHotAccount")}
182
+ </button>
183
+ ${this.osmRequired
184
+ ? this.osmConnected
185
+ ? html`
186
+ <button class="osm-connected" disabled>
187
+ <img src="${checkIcon}" alt="Check icon" class="icon" />
188
+ ${this.t("connectedToOsm")} (@${this.osmData?.osm_username})
189
+ </button>
190
+ `
191
+ : html`
192
+ <button
193
+ data-action="connect-osm"
194
+ @click=${this.handleDropdownSelect}
195
+ >
196
+ <img src="${mapIcon}" alt="Check icon" class="icon" />
197
+ ${this.t("connectToOsm")}
198
+ </button>
199
+ `
200
+ : ""}
201
+ <button data-action="logout" @click=${this.handleDropdownSelect}>
202
+ <img src="${logoutIcon}" alt="Log out icon" class="icon" />
203
+ ${this.t("logOut")}
204
+ </button>
205
+ </div>
206
+ `;
207
+ }
208
+
170
209
  // Private fields
171
210
  private _trailingSlashCache: Record<string, boolean> = {};
172
211
  private _debugMode = false;
@@ -865,7 +904,11 @@ export class HankoAuth extends LitElement {
865
904
  super.updated(changedProperties);
866
905
  // Re-attach event listeners when user becomes null (after logout)
867
906
  // because a new <hanko-auth> element is created
868
- if (changedProperties.has("user") && this.user === null && this.showProfile) {
907
+ if (
908
+ changedProperties.has("user") &&
909
+ this.user === null &&
910
+ this.showProfile
911
+ ) {
869
912
  this.log("🔄 User logged out, re-attaching event listeners...");
870
913
  this._currentHankoAuthElement = null;
871
914
  this.setupEventListeners();
@@ -1483,8 +1526,34 @@ export class HankoAuth extends LitElement {
1483
1526
  </div>
1484
1527
  </div>
1485
1528
  `;
1529
+ } else if (this.display === "bar") {
1530
+ // Logged in, show-profile=false, display=bar: render full-width bar trigger
1531
+ return html`
1532
+ <div class="dropdown">
1533
+ <button
1534
+ @click=${this.toggleDropdown}
1535
+ aria-label="${this.t("openAccountMenu")}"
1536
+ aria-expanded=${this.isOpen}
1537
+ aria-haspopup="true"
1538
+ class="bar-trigger"
1539
+ >
1540
+ <div class="bar-info">
1541
+ <span class="header-avatar">${initial}</span>
1542
+ <span class="bar-email"
1543
+ >${this.user.email || this.user.id}</span
1544
+ >
1545
+ </div>
1546
+ <img
1547
+ src="${this.isOpen ? chevronUpIcon : chevronDownIcon}"
1548
+ class="bar-chevron"
1549
+ alt=""
1550
+ />
1551
+ </button>
1552
+ ${this.renderBarDropdownContent()}
1553
+ </div>
1554
+ `;
1486
1555
  } else {
1487
- // Logged in, show-profile=false: render dropdown
1556
+ // Logged in, show-profile=false, display=default: render compact dropdown
1488
1557
  return html`
1489
1558
  <div class="dropdown">
1490
1559
  <button
@@ -1621,6 +1690,24 @@ export class HankoAuth extends LitElement {
1621
1690
  returnTo,
1622
1691
  )}${this.osmRequired ? "&osm_required=true" : ""}${autoConnectParam}&lang=${this.lang}`;
1623
1692
 
1693
+ /* if (this.display === "bar") {
1694
+ return html`<a
1695
+ class="bar-trigger login-link ${this.buttonVariant} ${this.buttonColor}"
1696
+ href="${loginUrl}"
1697
+ @click=${(e: Event) => {
1698
+ e.preventDefault();
1699
+ window.location.href = loginUrl;
1700
+ }}
1701
+ >
1702
+ <span class="bar-email">${this.t("logIn")}</span>
1703
+ <img
1704
+ src="${chevronDownIcon}"
1705
+ class="bar-chevron"
1706
+ alt=""
1707
+ />
1708
+ </a>`;
1709
+ } */
1710
+
1624
1711
  return html`<a
1625
1712
  class="login-link ${this.buttonVariant} ${this.buttonColor}"
1626
1713
  href="${loginUrl}"