@hotosm/hanko-auth 0.4.8 → 0.4.10
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 +20 -0
- package/dist/hanko-auth.esm.js +1266 -1272
- package/dist/hanko-auth.iife.js +49 -42
- package/dist/hanko-auth.umd.js +48 -41
- package/package.json +3 -2
- package/src/hanko-auth.styles.ts +8 -1
- package/src/hanko-auth.ts +59 -119
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hotosm/hanko-auth",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.10",
|
|
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",
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"dev": "vite",
|
|
22
22
|
"build": "vite build",
|
|
23
23
|
"watch": "vite build --watch",
|
|
24
|
-
"preview": "vite preview"
|
|
24
|
+
"preview": "vite preview",
|
|
25
|
+
"prepublishOnly": "pnpm build"
|
|
25
26
|
},
|
|
26
27
|
"keywords": [
|
|
27
28
|
"hotosm",
|
package/src/hanko-auth.styles.ts
CHANGED
|
@@ -25,8 +25,10 @@ export const styles = css`
|
|
|
25
25
|
display: flex;
|
|
26
26
|
flex-direction: column;
|
|
27
27
|
align-items: center;
|
|
28
|
+
width: 100%;
|
|
28
29
|
gap: var(--hot-spacing-small);
|
|
29
30
|
padding: var(--hot-spacing-large);
|
|
31
|
+
box-sizing: border-box;
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
.spinner {
|
|
@@ -147,15 +149,19 @@ export const styles = css`
|
|
|
147
149
|
font-weight: var(--hot-font-weight-bold);
|
|
148
150
|
color: var(--hot-color-gray-600);
|
|
149
151
|
overflow: hidden;
|
|
152
|
+
flex-shrink: 0;
|
|
150
153
|
}
|
|
151
154
|
|
|
152
155
|
.profile-info {
|
|
153
|
-
|
|
156
|
+
min-width: 0;
|
|
154
157
|
}
|
|
155
158
|
|
|
156
159
|
.profile-email {
|
|
157
160
|
font-size: var(--hot-font-size-small);
|
|
158
161
|
font-weight: var(--hot-font-weight-bold);
|
|
162
|
+
overflow: hidden;
|
|
163
|
+
text-overflow: ellipsis;
|
|
164
|
+
white-space: nowrap;
|
|
159
165
|
}
|
|
160
166
|
|
|
161
167
|
.osm-section {
|
|
@@ -260,6 +266,7 @@ export const styles = css`
|
|
|
260
266
|
overflow: hidden;
|
|
261
267
|
font-weight: var(--hot-font-weight-semibold);
|
|
262
268
|
color: white;
|
|
269
|
+
flex-shrink: 0;
|
|
263
270
|
}
|
|
264
271
|
|
|
265
272
|
.login-link {
|
package/src/hanko-auth.ts
CHANGED
|
@@ -1,15 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* Smart authentication component that handles:
|
|
5
|
-
* - Hanko SSO (Google, GitHub, Email)
|
|
6
|
-
* - Optional OSM connection
|
|
7
|
-
* - Session management
|
|
8
|
-
* - Event dispatching
|
|
9
|
-
* - URL fallback chain for production builds
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { LitElement, html, css } from "lit";
|
|
1
|
+
/** HOTOSM Hanko auth web component. */
|
|
2
|
+
|
|
3
|
+
import { LitElement, html } from "lit";
|
|
13
4
|
import { customElement, property, state } from "lit/decorators.js";
|
|
14
5
|
import { keyed } from "lit/directives/keyed.js";
|
|
15
6
|
import { register } from "@teamhanko/hanko-elements";
|
|
@@ -85,6 +76,7 @@ interface UserState {
|
|
|
85
76
|
email: string | null;
|
|
86
77
|
username: string | null;
|
|
87
78
|
emailVerified: boolean;
|
|
79
|
+
avatarUrl?: string;
|
|
88
80
|
}
|
|
89
81
|
|
|
90
82
|
interface OSMData {
|
|
@@ -202,12 +194,26 @@ export class HankoAuth extends LitElement {
|
|
|
202
194
|
}
|
|
203
195
|
|
|
204
196
|
// Private fields
|
|
205
|
-
private _trailingSlashCache: Record<string, boolean> = {};
|
|
206
197
|
private _debugMode = false;
|
|
207
198
|
private _lastSessionId: string | null = null;
|
|
208
199
|
private _hanko: any = null;
|
|
209
200
|
private _isPrimary = false; // Is this the primary instance?
|
|
210
201
|
|
|
202
|
+
constructor() {
|
|
203
|
+
super();
|
|
204
|
+
try {
|
|
205
|
+
const cached = localStorage.getItem("hotosm-auth-user");
|
|
206
|
+
if (cached) {
|
|
207
|
+
const cachedUser: UserState = JSON.parse(cached);
|
|
208
|
+
this.user = cachedUser;
|
|
209
|
+
this.loading = false;
|
|
210
|
+
if (cachedUser.avatarUrl) {
|
|
211
|
+
this.profilePictureUrl = cachedUser.avatarUrl;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
} catch {}
|
|
215
|
+
}
|
|
216
|
+
|
|
211
217
|
// Get computed hankoUrl (priority: attribute > meta tag > window.HANKO_URL > origin)
|
|
212
218
|
get hankoUrl(): string {
|
|
213
219
|
if (this.hankoUrlAttr) {
|
|
@@ -218,28 +224,28 @@ export class HankoAuth extends LitElement {
|
|
|
218
224
|
if (metaTag) {
|
|
219
225
|
const content = metaTag.getAttribute("content");
|
|
220
226
|
if (content) {
|
|
221
|
-
this.log("
|
|
227
|
+
this.log("hanko-url auto-detected from <meta> tag:", content);
|
|
222
228
|
return content;
|
|
223
229
|
}
|
|
224
230
|
}
|
|
225
231
|
|
|
226
232
|
if ((window as any).HANKO_URL) {
|
|
227
233
|
this.log(
|
|
228
|
-
"
|
|
234
|
+
"hanko-url auto-detected from window.HANKO_URL:",
|
|
229
235
|
(window as any).HANKO_URL,
|
|
230
236
|
);
|
|
231
237
|
return (window as any).HANKO_URL;
|
|
232
238
|
}
|
|
233
239
|
|
|
234
240
|
const origin = window.location.origin;
|
|
235
|
-
this.log("
|
|
241
|
+
this.log("hanko-url auto-detected from window.location.origin:", origin);
|
|
236
242
|
return origin;
|
|
237
243
|
}
|
|
238
244
|
|
|
239
245
|
connectedCallback() {
|
|
240
246
|
super.connectedCallback();
|
|
241
247
|
this._debugMode = this._checkDebugMode();
|
|
242
|
-
this.log("
|
|
248
|
+
this.log("hanko-auth connectedCallback called");
|
|
243
249
|
|
|
244
250
|
// Inject Hot styles early, before any Hanko elements render
|
|
245
251
|
this.injectHotStyles();
|
|
@@ -257,7 +263,7 @@ export class HankoAuth extends LitElement {
|
|
|
257
263
|
|
|
258
264
|
// Use firstUpdated instead of connectedCallback to ensure React props are set
|
|
259
265
|
firstUpdated() {
|
|
260
|
-
this.log("
|
|
266
|
+
this.log("hanko-auth firstUpdated called");
|
|
261
267
|
this.log(" hankoUrl:", this.hankoUrl);
|
|
262
268
|
this.log(" basePath:", this.basePath);
|
|
263
269
|
|
|
@@ -359,7 +365,7 @@ export class HankoAuth extends LitElement {
|
|
|
359
365
|
if (!this.showProfile && !this.user) {
|
|
360
366
|
// Window focused, we're in header mode, and no user is logged in
|
|
361
367
|
// Re-check session in case user logged in
|
|
362
|
-
this.log("
|
|
368
|
+
this.log("Window focused, re-checking session...");
|
|
363
369
|
this.checkSession();
|
|
364
370
|
}
|
|
365
371
|
};
|
|
@@ -371,7 +377,7 @@ export class HankoAuth extends LitElement {
|
|
|
371
377
|
const customEvent = event as CustomEvent;
|
|
372
378
|
if (!this.showProfile && !this.user && customEvent.detail?.user) {
|
|
373
379
|
// Another component (e.g., login page) logged in
|
|
374
|
-
this.log("
|
|
380
|
+
this.log("External login detected, updating user state...");
|
|
375
381
|
this.user = customEvent.detail.user;
|
|
376
382
|
this._broadcastState();
|
|
377
383
|
// Also re-check OSM connection (only if required)
|
|
@@ -410,10 +416,6 @@ export class HankoAuth extends LitElement {
|
|
|
410
416
|
return langTranslations[key] || translations.en[key] || key;
|
|
411
417
|
}
|
|
412
418
|
|
|
413
|
-
private warn(...args: any[]) {
|
|
414
|
-
console.warn(...args);
|
|
415
|
-
}
|
|
416
|
-
|
|
417
419
|
private logError(...args: any[]) {
|
|
418
420
|
console.error(...args);
|
|
419
421
|
}
|
|
@@ -421,24 +423,16 @@ export class HankoAuth extends LitElement {
|
|
|
421
423
|
private getBasePath(): string {
|
|
422
424
|
// Use basePath property directly (works with both attribute and React props)
|
|
423
425
|
if (this.basePath) {
|
|
424
|
-
this.log("
|
|
426
|
+
this.log("getBasePath() using basePath:", this.basePath);
|
|
425
427
|
return this.basePath;
|
|
426
428
|
}
|
|
427
429
|
|
|
428
430
|
// For single-page apps (like Portal), default to empty base path
|
|
429
431
|
// The authPath already contains the full API path
|
|
430
|
-
this.log("
|
|
432
|
+
this.log("getBasePath() using default: empty string");
|
|
431
433
|
return "";
|
|
432
434
|
}
|
|
433
435
|
|
|
434
|
-
private addTrailingSlash(path: string, basePath: string): string {
|
|
435
|
-
const needsSlash = this._trailingSlashCache[basePath];
|
|
436
|
-
if (needsSlash !== undefined && needsSlash && !path.endsWith("/")) {
|
|
437
|
-
return path + "/";
|
|
438
|
-
}
|
|
439
|
-
return path;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
436
|
// styles injected to ensure global availability
|
|
443
437
|
private injectHotStyles() {
|
|
444
438
|
const stylesheets = [
|
|
@@ -466,7 +460,7 @@ export class HankoAuth extends LitElement {
|
|
|
466
460
|
private async init() {
|
|
467
461
|
// Only primary instance should initialize
|
|
468
462
|
if (!this._isPrimary) {
|
|
469
|
-
this.log("
|
|
463
|
+
this.log("Not primary, skipping init...");
|
|
470
464
|
return;
|
|
471
465
|
}
|
|
472
466
|
|
|
@@ -505,12 +499,12 @@ export class HankoAuth extends LitElement {
|
|
|
505
499
|
|
|
506
500
|
// Set up session lifecycle event listeners (these persist across the component lifecycle)
|
|
507
501
|
this._hanko.onSessionExpired(() => {
|
|
508
|
-
this.log("
|
|
502
|
+
this.log("Hanko session expired event received");
|
|
509
503
|
this.handleSessionExpired();
|
|
510
504
|
});
|
|
511
505
|
|
|
512
506
|
this._hanko.onUserLoggedOut(() => {
|
|
513
|
-
this.log("
|
|
507
|
+
this.log("Hanko user logged out event received");
|
|
514
508
|
this.handleUserLoggedOut();
|
|
515
509
|
});
|
|
516
510
|
|
|
@@ -537,7 +531,7 @@ export class HankoAuth extends LitElement {
|
|
|
537
531
|
}
|
|
538
532
|
|
|
539
533
|
private async checkSession() {
|
|
540
|
-
this.log("
|
|
534
|
+
this.log("Checking for existing Hanko session...");
|
|
541
535
|
|
|
542
536
|
if (!this._hanko) {
|
|
543
537
|
this.log("Hanko instance not initialized yet");
|
|
@@ -569,6 +563,13 @@ export class HankoAuth extends LitElement {
|
|
|
569
563
|
this.log(
|
|
570
564
|
"Session validation returned is_valid:false - no valid session",
|
|
571
565
|
);
|
|
566
|
+
if (this.user) {
|
|
567
|
+
this.user = null;
|
|
568
|
+
this.profilePictureUrl = "";
|
|
569
|
+
this.dispatchEvent(
|
|
570
|
+
new CustomEvent("logout", { bubbles: true, composed: true }),
|
|
571
|
+
);
|
|
572
|
+
}
|
|
572
573
|
return;
|
|
573
574
|
}
|
|
574
575
|
|
|
@@ -657,6 +658,10 @@ export class HankoAuth extends LitElement {
|
|
|
657
658
|
// Redirect to onboarding in progress, don't proceed
|
|
658
659
|
return;
|
|
659
660
|
}
|
|
661
|
+
await this.fetchProfileDisplayName();
|
|
662
|
+
if (this.user && this.profilePictureUrl) {
|
|
663
|
+
this.user = { ...this.user, avatarUrl: this.profilePictureUrl };
|
|
664
|
+
}
|
|
660
665
|
|
|
661
666
|
this.dispatchEvent(
|
|
662
667
|
new CustomEvent("hanko-login", {
|
|
@@ -677,8 +682,6 @@ export class HankoAuth extends LitElement {
|
|
|
677
682
|
if (this.osmRequired) {
|
|
678
683
|
await this.checkOSMConnection();
|
|
679
684
|
}
|
|
680
|
-
// Fetch profile display name
|
|
681
|
-
await this.fetchProfileDisplayName();
|
|
682
685
|
if (this.osmRequired && this.autoConnect && !this.osmConnected) {
|
|
683
686
|
this.log("Auto-connecting to OSM (from existing session)...");
|
|
684
687
|
this.handleOSMConnect();
|
|
@@ -879,12 +882,9 @@ export class HankoAuth extends LitElement {
|
|
|
879
882
|
this.log("Display name set to:", this.profileDisplayName);
|
|
880
883
|
}
|
|
881
884
|
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
this.profilePictureUrl = picUrl;
|
|
886
|
-
this.log("Profile picture set to:", this.profilePictureUrl);
|
|
887
|
-
}
|
|
885
|
+
const picUrl = profile.osm_avatar_url || profile.picture_url;
|
|
886
|
+
this.profilePictureUrl = picUrl || "";
|
|
887
|
+
this.log("Profile picture set to:", this.profilePictureUrl);
|
|
888
888
|
|
|
889
889
|
// Set language from user profile if available
|
|
890
890
|
if (profile.language) {
|
|
@@ -909,7 +909,7 @@ export class HankoAuth extends LitElement {
|
|
|
909
909
|
this.user === null &&
|
|
910
910
|
this.showProfile
|
|
911
911
|
) {
|
|
912
|
-
this.log("
|
|
912
|
+
this.log("User logged out, re-attaching event listeners...");
|
|
913
913
|
this._currentHankoAuthElement = null;
|
|
914
914
|
this.setupEventListeners();
|
|
915
915
|
}
|
|
@@ -928,7 +928,7 @@ export class HankoAuth extends LitElement {
|
|
|
928
928
|
|
|
929
929
|
if (hankoAuth) {
|
|
930
930
|
this._currentHankoAuthElement = hankoAuth;
|
|
931
|
-
this.log("
|
|
931
|
+
this.log("Attaching event listeners to hanko-auth element");
|
|
932
932
|
|
|
933
933
|
hankoAuth.addEventListener("onSessionCreated", (e: any) => {
|
|
934
934
|
this.log(`Hanko event: onSessionCreated`, e.detail);
|
|
@@ -1058,6 +1058,10 @@ export class HankoAuth extends LitElement {
|
|
|
1058
1058
|
}
|
|
1059
1059
|
|
|
1060
1060
|
this.log("User state updated:", this.user);
|
|
1061
|
+
await this.fetchProfileDisplayName();
|
|
1062
|
+
if (this.user && this.profilePictureUrl) {
|
|
1063
|
+
this.user = { ...this.user, avatarUrl: this.profilePictureUrl };
|
|
1064
|
+
}
|
|
1061
1065
|
|
|
1062
1066
|
// Broadcast state changes to other instances
|
|
1063
1067
|
if (this._isPrimary) {
|
|
@@ -1076,8 +1080,6 @@ export class HankoAuth extends LitElement {
|
|
|
1076
1080
|
if (this.osmRequired) {
|
|
1077
1081
|
await this.checkOSMConnection();
|
|
1078
1082
|
}
|
|
1079
|
-
// Fetch profile display name (only works with login.hotosm.org backend)
|
|
1080
|
-
await this.fetchProfileDisplayName();
|
|
1081
1083
|
|
|
1082
1084
|
// Auto-connect to OSM if required and auto-connect is enabled
|
|
1083
1085
|
if (this.osmRequired && this.autoConnect && !this.osmConnected) {
|
|
@@ -1366,47 +1368,9 @@ export class HankoAuth extends LitElement {
|
|
|
1366
1368
|
// Close dropdown after selection
|
|
1367
1369
|
this.closeDropdown();
|
|
1368
1370
|
}
|
|
1369
|
-
private oldHandleDropdownSelect(event: CustomEvent) {
|
|
1370
|
-
const selectedValue = event.detail.item.value;
|
|
1371
|
-
this.log("🎯 Dropdown item selected:", selectedValue);
|
|
1372
|
-
|
|
1373
|
-
if (selectedValue === "profile") {
|
|
1374
|
-
// Profile page: standalone apps have their own, others use central login service
|
|
1375
|
-
// loginUrl already includes /app, hankoUrl doesn't
|
|
1376
|
-
const returnTo = this.redirectAfterLogin || window.location.origin;
|
|
1377
|
-
const profileUrl = this.loginUrl
|
|
1378
|
-
? `${this.loginUrl}/profile`
|
|
1379
|
-
: `${this.hankoUrl}/app/profile`;
|
|
1380
|
-
window.location.href = `${profileUrl}?return_to=${encodeURIComponent(returnTo)}`;
|
|
1381
|
-
} else if (selectedValue === "connect-osm") {
|
|
1382
|
-
// Smart return_to: if already on a login page, redirect to home instead
|
|
1383
|
-
const currentPath = window.location.pathname;
|
|
1384
|
-
const isOnLoginPage = currentPath.includes("/app");
|
|
1385
|
-
const returnTo = isOnLoginPage
|
|
1386
|
-
? window.location.origin
|
|
1387
|
-
: window.location.href;
|
|
1388
|
-
|
|
1389
|
-
// Use the getter which handles all fallbacks correctly
|
|
1390
|
-
const baseUrl = this.hankoUrl;
|
|
1391
|
-
window.location.href = `${baseUrl}/app?return_to=${encodeURIComponent(
|
|
1392
|
-
returnTo,
|
|
1393
|
-
)}&osm_required=true`;
|
|
1394
|
-
} else if (selectedValue === "logout") {
|
|
1395
|
-
this.handleLogout();
|
|
1396
|
-
}
|
|
1397
|
-
}
|
|
1398
|
-
|
|
1399
|
-
private handleSkipOSM() {
|
|
1400
|
-
this.dispatchEvent(new CustomEvent("osm-skipped"));
|
|
1401
|
-
this.dispatchEvent(new CustomEvent("auth-complete"));
|
|
1402
|
-
if (this.redirectAfterLogin) {
|
|
1403
|
-
window.location.href = this.redirectAfterLogin;
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
|
-
|
|
1407
1371
|
render() {
|
|
1408
1372
|
this.log(
|
|
1409
|
-
"
|
|
1373
|
+
"RENDER - showProfile:",
|
|
1410
1374
|
this.showProfile,
|
|
1411
1375
|
"user:",
|
|
1412
1376
|
!!this.user,
|
|
@@ -1444,8 +1408,6 @@ export class HankoAuth extends LitElement {
|
|
|
1444
1408
|
const initial = displayName ? displayName[0].toUpperCase() : "U";
|
|
1445
1409
|
|
|
1446
1410
|
if (this.showProfile) {
|
|
1447
|
-
// Show full profile view
|
|
1448
|
-
// TODO check use cases
|
|
1449
1411
|
return html`
|
|
1450
1412
|
<div class="container">
|
|
1451
1413
|
<div class="profile">
|
|
@@ -1652,13 +1614,12 @@ export class HankoAuth extends LitElement {
|
|
|
1652
1614
|
`;
|
|
1653
1615
|
}
|
|
1654
1616
|
} else {
|
|
1655
|
-
// Not logged in
|
|
1617
|
+
// Not logged in.
|
|
1656
1618
|
if (this.showProfile) {
|
|
1657
|
-
//
|
|
1658
|
-
// Don't render until Hanko is registered to prevent 404 errors
|
|
1619
|
+
// Login page mode: render full Hanko form after registration is ready.
|
|
1659
1620
|
if (!this.hankoReady) {
|
|
1660
1621
|
this.log(
|
|
1661
|
-
"
|
|
1622
|
+
"Waiting for Hanko registration before rendering form...",
|
|
1662
1623
|
);
|
|
1663
1624
|
return html`<span class="loading-placeholder"
|
|
1664
1625
|
><span class="loading-placeholder-text">${this.t("logIn")}</span
|
|
@@ -1697,9 +1658,7 @@ export class HankoAuth extends LitElement {
|
|
|
1697
1658
|
</div>
|
|
1698
1659
|
`;
|
|
1699
1660
|
} else {
|
|
1700
|
-
//
|
|
1701
|
-
// Use redirectAfterLogin if set, otherwise use current URL
|
|
1702
|
-
// Smart return_to: if already on a login page, redirect to home instead
|
|
1661
|
+
// Header mode: render login link with a safe return_to target.
|
|
1703
1662
|
const currentPath = window.location.pathname;
|
|
1704
1663
|
const isOnLoginPage = currentPath.includes("/app");
|
|
1705
1664
|
const returnTo =
|
|
@@ -1710,34 +1669,15 @@ export class HankoAuth extends LitElement {
|
|
|
1710
1669
|
const autoConnectParam =
|
|
1711
1670
|
urlParams.get("auto_connect") === "true" ? "&auto_connect=true" : "";
|
|
1712
1671
|
|
|
1713
|
-
// Use the getter which handles all fallbacks correctly
|
|
1714
1672
|
const baseUrl = this.hankoUrl;
|
|
1715
|
-
this.log("
|
|
1673
|
+
this.log("Login URL base:", baseUrl);
|
|
1716
1674
|
|
|
1717
|
-
// Use custom loginUrl
|
|
1675
|
+
// Use custom loginUrl when provided; fallback to {hankoUrl}/app.
|
|
1718
1676
|
const loginBase = this.loginUrl || `${baseUrl}/app`;
|
|
1719
1677
|
const loginUrl = `${loginBase}?return_to=${encodeURIComponent(
|
|
1720
1678
|
returnTo,
|
|
1721
1679
|
)}${this.osmRequired ? "&osm_required=true" : ""}${autoConnectParam}&lang=${this.lang}`;
|
|
1722
1680
|
|
|
1723
|
-
/* if (this.display === "bar") {
|
|
1724
|
-
return html`<a
|
|
1725
|
-
class="bar-trigger login-link ${this.buttonVariant} ${this.buttonColor}"
|
|
1726
|
-
href="${loginUrl}"
|
|
1727
|
-
@click=${(e: Event) => {
|
|
1728
|
-
e.preventDefault();
|
|
1729
|
-
window.location.href = loginUrl;
|
|
1730
|
-
}}
|
|
1731
|
-
>
|
|
1732
|
-
<span class="bar-email">${this.t("logIn")}</span>
|
|
1733
|
-
<img
|
|
1734
|
-
src="${chevronDownIcon}"
|
|
1735
|
-
class="bar-chevron"
|
|
1736
|
-
alt=""
|
|
1737
|
-
/>
|
|
1738
|
-
</a>`;
|
|
1739
|
-
} */
|
|
1740
|
-
|
|
1741
1681
|
return html`<a
|
|
1742
1682
|
class="login-link ${this.buttonVariant} ${this.buttonColor}"
|
|
1743
1683
|
href="${loginUrl}"
|