@wix/astro 0.3.3 → 1.0.1

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.
Files changed (71) hide show
  1. package/build/index.d.ts +5 -0
  2. package/build/index.js +124854 -0
  3. package/build/index.js.map +1 -0
  4. package/build/xdg-open +1267 -0
  5. package/build/yoga.wasm +0 -0
  6. package/build-runtime/auth.d.ts +5 -0
  7. package/build-runtime/auth.js +3557 -0
  8. package/package.json +37 -47
  9. package/runtime/entry.astro +26 -0
  10. package/src/directories.ts +10 -0
  11. package/src/env.d.ts +3 -0
  12. package/src/index.ts +217 -0
  13. package/src/middleware/auth.ts +115 -0
  14. package/src/plugins/patchGlobal.ts +20 -0
  15. package/src/utils/authStrategyAsyncLocalStorage.ts +6 -0
  16. package/src/utils/createProjectModel.ts +77 -0
  17. package/src/utils/generateAppManifest.ts +32 -0
  18. package/src/utils/isValidBackofficeComponent.ts +15 -0
  19. package/src/utils/loadExtension.ts +59 -0
  20. package/src/utils/sessionCookieJson.ts +9 -0
  21. package/src/utils/writeVirtualExtensionFiles.ts +54 -0
  22. package/tsconfig.json +8 -0
  23. package/tsup.config.mjs +28 -0
  24. package/README.md +0 -121
  25. package/dist/auth-context.d.ts +0 -5
  26. package/dist/auth-context.js +0 -2
  27. package/dist/client.d.ts +0 -15
  28. package/dist/client.js +0 -16
  29. package/dist/components/login-helpers/bi-header-generator.d.ts +0 -11
  30. package/dist/components/login-helpers/bi-header-generator.js +0 -17
  31. package/dist/components/login-helpers/iframeUtils.d.ts +0 -4
  32. package/dist/components/login-helpers/iframeUtils.js +0 -43
  33. package/dist/components/login-helpers/login-helpers.d.ts +0 -42
  34. package/dist/components/login-helpers/login-helpers.js +0 -119
  35. package/dist/components/login.astro +0 -873
  36. package/dist/entrypoints/server.d.ts +0 -13
  37. package/dist/entrypoints/server.js +0 -36
  38. package/dist/helpers/index.d.ts +0 -1
  39. package/dist/helpers/index.js +0 -1
  40. package/dist/index.d.ts +0 -5
  41. package/dist/index.js +0 -4
  42. package/dist/integration.d.ts +0 -22
  43. package/dist/integration.js +0 -233
  44. package/dist/loaders/blog.d.ts +0 -2
  45. package/dist/loaders/blog.js +0 -48
  46. package/dist/loaders/index.d.ts +0 -2
  47. package/dist/loaders/index.js +0 -2
  48. package/dist/middleware.d.ts +0 -2
  49. package/dist/middleware.js +0 -89
  50. package/dist/routes/auth/callback.d.ts +0 -3
  51. package/dist/routes/auth/callback.js +0 -52
  52. package/dist/routes/auth/constants.d.ts +0 -5
  53. package/dist/routes/auth/constants.js +0 -5
  54. package/dist/routes/auth/login.d.ts +0 -3
  55. package/dist/routes/auth/login.js +0 -26
  56. package/dist/routes/auth/logout-callback.d.ts +0 -3
  57. package/dist/routes/auth/logout-callback.js +0 -9
  58. package/dist/routes/auth/logout.d.ts +0 -3
  59. package/dist/routes/auth/logout.js +0 -10
  60. package/dist/routes/auth/runtime.d.ts +0 -5
  61. package/dist/routes/auth/runtime.js +0 -7
  62. package/dist/runtime-client.d.ts +0 -3
  63. package/dist/runtime-client.js +0 -4
  64. package/dist/runtime.client.d.ts +0 -2
  65. package/dist/runtime.client.js +0 -4
  66. package/dist/runtime.d.ts +0 -5
  67. package/dist/runtime.js +0 -12
  68. package/dist/runtime.server.d.ts +0 -2
  69. package/dist/runtime.server.js +0 -8
  70. package/dist/vite-plugins/sdk-context.d.ts +0 -4
  71. package/dist/vite-plugins/sdk-context.js +0 -67
@@ -1,873 +0,0 @@
1
- ---
2
- ---
3
-
4
- <div class="login-form-wrapper">
5
- <login-form>
6
- <form id="loginForm">
7
- <div class="form-container">
8
- <h3 id="formTitle">
9
- <span id="title-login">
10
- <slot name="title-login">Log In</slot>
11
- </span>
12
- <span id="title-signup" style="display: none;">
13
- <slot name="title-signup">Sign Up</slot>
14
- </span>
15
- <span id="title-reset" style="display: none;">
16
- <slot name="title-reset">Reset Password</slot>
17
- </span>
18
- <span id="title-verify" style="display: none;">
19
- <slot name="title-verify">Email Verification</slot>
20
- </span>
21
- <span id="title-success" style="display: none;">
22
- <slot name="title-success">Success</slot>
23
- </span>
24
- </h3>
25
-
26
- <!-- Success message with animation -->
27
- <div id="successMessage" style="display: none;">
28
- <div class="success-animation">
29
- <svg class="checkmark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52">
30
- <circle class="checkmark__circle" cx="26" cy="26" r="25" fill="none"/>
31
- <path class="checkmark__check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8"/>
32
- </svg>
33
- </div>
34
- <p class="success-title">
35
- <slot name="success-title">Login Successful!</slot>
36
- </p>
37
- <p id="successText">
38
- <slot name="success-message">You are now logged in.</slot>
39
- </p>
40
- </div>
41
-
42
- <div id="pendingMessage" style="display: none;">
43
- <p id="pendingText"></p>
44
- <button id="okButton" type="button">
45
- <slot name="ok-button">OK</slot>
46
- </button>
47
- </div>
48
-
49
- <!-- General Error Message -->
50
- <div id="errorMessage" style="display: none;">
51
- <div class="error-container">
52
- <svg class="error-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
53
- <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
54
- </svg>
55
- <p id="errorText">
56
- <slot name="error-message">Authentication error. Please try again.</slot>
57
- </p>
58
- </div>
59
- </div>
60
-
61
- <div id="formFields">
62
- <!-- Username field (sign up only) -->
63
- <div id="usernameField" style="display: none;">
64
- <div class="field-label">
65
- <label for="username">
66
- <slot name="username-label">Username</slot>
67
- </label>
68
- </div>
69
- <input
70
- class={Astro.props["input-class"]}
71
- id="username"
72
- name="username"
73
- type="text"
74
- required
75
- />
76
- </div>
77
-
78
- <!-- Email field -->
79
- <div id="emailField">
80
- <div class="field-label">
81
- <label for="email">
82
- <slot name="email-label">Email</slot>
83
- </label>
84
- </div>
85
- <input
86
- class={Astro.props["input-class"]}
87
- id="email"
88
- name="email"
89
- type="email"
90
- required
91
- />
92
- </div>
93
-
94
- <!-- Verification code field -->
95
- <div id="codeField" style="display: none;">
96
- <div class="field-label">
97
- <label for="code">
98
- <slot name="code-label">Code</slot>
99
- </label>
100
- </div>
101
- <input
102
- class={Astro.props["input-class"]}
103
- id="code"
104
- name="code"
105
- type="number"
106
- required
107
- />
108
- </div>
109
-
110
- <!-- Password field -->
111
- <div id="passwordField">
112
- <div class="field-label">
113
- <label for="password">
114
- <slot name="password-label">Password</slot>
115
- </label>
116
- </div>
117
- <input
118
- class={Astro.props["input-class"]}
119
- id="password"
120
- name="password"
121
- type="password"
122
- required
123
- />
124
- </div>
125
-
126
- <!-- Forgot password link -->
127
- <div id="forgotPasswordLink">
128
- <a id="forgotPassword" href="#">
129
- <slot name="forgot-password">Forgot password?</slot>
130
- </a>
131
- </div>
132
-
133
- <!-- reCAPTCHA container -->
134
- <div id="recaptcha-container" style="display: none;"></div>
135
-
136
- <!-- Submit button -->
137
- <div class="submit-button-container">
138
- <button id="submitButton" type="submit" class={Astro.props["button-class"]}>
139
- <span class="login-text">
140
- <slot name="button-login">Log In</slot>
141
- </span>
142
- <span class="signup-text" style="display: none;">
143
- <slot name="button-signup">Sign Up</slot>
144
- </span>
145
- <span class="reset-text" style="display: none;">
146
- <slot name="button-reset">Reset</slot>
147
- </span>
148
- <span class="verify-text" style="display: none;">
149
- <slot name="button-verify">Submit</slot>
150
- </span>
151
- <span class="loading-text loading-bounce" style="display: none;">
152
- <slot name="button-loading">Loading</slot><span class="dot">.</span><span class="dot">.</span><span class="dot">.</span>
153
- </span>
154
- </button>
155
- </div>
156
-
157
- <!-- Toggle login/signup -->
158
- <div id="toggleStateContainer">
159
- <span class="login-toggle">
160
- <slot name="toggle-login-text">Not registered?</slot>
161
- <a id="toggleState" href="#">
162
- <slot name="toggle-login-link">Sign up</slot>
163
- </a>
164
- </span>
165
- <span class="signup-toggle" style="display: none;">
166
- <slot name="toggle-signup-text">Already registered?</slot>
167
- <a id="toggleState-signup" href="#">
168
- <slot name="toggle-signup-link">Log in</slot>
169
- </a>
170
- </span>
171
- </div>
172
- </div>
173
- </div>
174
- </form>
175
- </login-form>
176
- </div>
177
-
178
- <style>
179
- button:disabled {
180
- opacity: 0.5;
181
- cursor: not-allowed;
182
- }
183
-
184
- /* Success animation styles */
185
- .success-animation {
186
- display: flex;
187
- justify-content: center;
188
- margin: 20px 0;
189
- }
190
-
191
- .checkmark {
192
- width: 80px;
193
- height: 80px;
194
- border-radius: 50%;
195
- display: block;
196
- stroke-width: 2;
197
- stroke: #4BB71B;
198
- stroke-miterlimit: 10;
199
- box-shadow: inset 0px 0px 0px #4BB71B;
200
- animation: fill .4s ease-in-out .4s forwards, scale .3s ease-in-out .9s both;
201
- }
202
-
203
- .checkmark__circle {
204
- stroke-dasharray: 166;
205
- stroke-dashoffset: 166;
206
- stroke-width: 2;
207
- stroke-miterlimit: 10;
208
- stroke: #4BB71B;
209
- fill: none;
210
- animation: stroke 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards;
211
- }
212
-
213
- .checkmark__check {
214
- transform-origin: 50% 50%;
215
- stroke-dasharray: 48;
216
- stroke-dashoffset: 48;
217
- animation: stroke 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.8s forwards;
218
- }
219
-
220
- @keyframes stroke {
221
- 100% {
222
- stroke-dashoffset: 0;
223
- }
224
- }
225
-
226
- @keyframes scale {
227
- 0%, 100% {
228
- transform: none;
229
- }
230
- 50% {
231
- transform: scale3d(1.1, 1.1, 1);
232
- }
233
- }
234
-
235
- @keyframes fill {
236
- 100% {
237
- box-shadow: inset 0px 0px 0px 30px rgba(75, 183, 27, 0.2);
238
- }
239
- }
240
-
241
-
242
- /* RTL support for Hebrew and other RTL languages */
243
- :global([dir="rtl"]) .form-container {
244
- text-align: right;
245
- }
246
-
247
- :global([dir="rtl"]) input {
248
- text-align: right;
249
- }
250
-
251
-
252
- /* Loading text on submit button animation */
253
- .loading-bounce .dot {
254
- display: inline-block; /* Needed for transform */
255
- position: relative; /* Or just rely on inline-block default */
256
- animation: bounce 0.6s infinite ease-in-out alternate;
257
- }
258
-
259
- /* Add delays to each dot */
260
- .loading-bounce .dot:nth-child(1) {
261
- animation-delay: 0.1s;
262
- }
263
-
264
- .loading-bounce .dot:nth-child(2) {
265
- animation-delay: 0.2s;
266
- }
267
-
268
- .loading-bounce .dot:nth-child(3) {
269
- animation-delay: 0.3s;
270
- }
271
-
272
- @keyframes bounce {
273
- from {
274
- transform: translateY(0);
275
- }
276
- to {
277
- transform: translateY(-5px); /* Adjust bounce height */
278
- }
279
- }
280
- </style>
281
-
282
- <script>
283
- import { authentication, verification, recovery } from '@wix/identity';
284
- import { getMemberTokensForDirectLogin } from './login-helpers/login-helpers.js';
285
- import { wixContext } from '@wix/sdk-context';
286
- import { getAuth } from '@wix/astro/runtime/client';
287
-
288
- import type { WixClient } from '@wix/sdk';
289
- import type { SiteSessionAuth } from "@wix/sdk/auth/site-session";
290
-
291
- class LoginForm extends HTMLElement {
292
- constructor() {
293
- super();
294
- // States enum
295
- this.State = {
296
- LOGIN: 'LOGIN',
297
- SIGNUP: 'SIGNUP',
298
- RESET_PASSWORD: 'RESET_PASSWORD',
299
- EMAIL_VERIFICATION: 'EMAIL_VERIFICATION',
300
- SUCCESS: 'SUCCESS'
301
- };
302
-
303
- // Initialize state variables
304
- this.state = this.State.LOGIN;
305
- this.loading = false;
306
- this.email = '';
307
- this.code = '';
308
- this.username = '';
309
- this.password = '';
310
- this.pending = { state: false, message: '' };
311
- this.passwordInvalid = false;
312
- this.emailInvalid = false;
313
- this.captcha = '';
314
- this.error = { state: false, message: '' };
315
-
316
- // Bind methods to this
317
- this.resetState = this.resetState.bind(this);
318
- this.submit = this.submit.bind(this);
319
- this.updateUI = this.updateUI.bind(this);
320
- this.handleInputChange = this.handleInputChange.bind(this);
321
- this.showSuccessMessage = this.showSuccessMessage.bind(this);
322
- this.showErrorMessage = this.showErrorMessage.bind(this);
323
- }
324
-
325
- connectedCallback() {
326
- // Initial setup
327
- this.setupEventListenersForStaticElements();
328
- this.updateUI(); // This will handle dynamic element setup
329
- }
330
-
331
- setupEventListenersForStaticElements() {
332
- // Form submission
333
- this.querySelector('#loginForm').addEventListener('submit', this.submit);
334
-
335
- // OK button for messages
336
- this.querySelector('#okButton').addEventListener('click', () => {
337
- this.resetState();
338
- });
339
-
340
- // Forgot password link
341
- const forgotPasswordLink = this.querySelector('#forgotPassword');
342
- if (forgotPasswordLink) {
343
- forgotPasswordLink.addEventListener('click', (e) => {
344
- e.preventDefault();
345
- this.state = this.State.RESET_PASSWORD;
346
- this.resetState();
347
- });
348
- }
349
-
350
- // Input event listeners for all form fields
351
- const inputs = this.querySelectorAll('input');
352
- inputs.forEach(input => {
353
- input.addEventListener('input', this.handleInputChange);
354
- });
355
- }
356
-
357
- async setupCaptcha() {
358
- try {
359
- // Set up recaptcha
360
- if (!window.grecaptcha && this.state === this.State.SIGNUP) {
361
- const script = document.createElement('script');
362
- script.src = "https://www.google.com/recaptcha/enterprise.js";
363
- document.head.appendChild(script);
364
-
365
- script.onload = () => {
366
- window.grecaptcha.enterprise.ready(() => {
367
- window.grecaptcha.enterprise.render('recaptcha-container', {
368
- 'sitekey': '6Ld0J8IcAAAAANyrnxzrRlX1xrrdXsOmsepUYosy', // Replace with your actual site key
369
- 'size': 'normal', // or 'compact' for mobile-friendly
370
- 'callback': this.onCaptchaChange.bind(this),
371
- 'expired-callback': () => {
372
- this.captcha = '';
373
- this.updateInputValues(); // Update UI without full render
374
- },
375
- });
376
- });
377
- };
378
- }
379
- } catch (error) {
380
- console.error("Error setting up captcha:", error);
381
- }
382
- }
383
-
384
- resetState() {
385
- this.loading = false;
386
- this.pending = { state: false, message: '' };
387
- this.email = '';
388
- this.code = '';
389
- this.passwordInvalid = false;
390
- this.emailInvalid = false;
391
- this.username = '';
392
- this.password = '';
393
- this.error = { state: false, message: '' };
394
-
395
- // Reset recaptcha if it exists
396
- if (this.querySelector('.g-recaptcha') && window.grecaptcha) {
397
- window.grecaptcha.enterprise.reset();
398
- }
399
-
400
- this.updateUI();
401
- }
402
-
403
- handleInputChange(event) {
404
- const { id, value } = event.target;
405
-
406
- // Clear error message when user starts typing
407
- if (this.error.state) {
408
- this.error = { state: false, message: '' };
409
- this.updateUI();
410
- }
411
-
412
- if (id === 'email') {
413
- this.email = value;
414
- this.emailInvalid = false;
415
- } else if (id === 'password') {
416
- this.password = value;
417
- this.passwordInvalid = false;
418
- } else if (id === 'username') {
419
- this.username = value;
420
- } else if (id === 'code') {
421
- this.code = value;
422
- }
423
-
424
- // Update UI state without full re-render
425
- this.updateInputValues();
426
- }
427
-
428
- async submit(event) {
429
- event.preventDefault();
430
- this.loading = true;
431
- this.error = { state: false, message: '' }; // Clear any previous errors
432
- this.updateInputValues(); // Update UI without full render
433
- this.updateButtonText();
434
-
435
- let response;
436
-
437
- try {
438
- if (this.state === this.State.RESET_PASSWORD) {
439
- await recovery.sendRecoveryEmail(
440
- this.email,
441
- { redirect: { url: window.location.origin, clientId: wixContext['clientId'] as string } }
442
- );
443
- this.pending = { message: 'Password reset email sent', state: true };
444
- this.updateUI(); // Update UI for modal state change
445
- return;
446
- }
447
-
448
- if (this.state === this.State.EMAIL_VERIFICATION) {
449
- response = await verification.verifyDuringAuthentication(
450
- this.code,
451
- { stateToken: this.emailVerificationStateToken },
452
- );
453
- } else if (this.state === this.State.LOGIN) {
454
- response = await authentication.loginV2(
455
- {
456
- email: this.email,
457
- },
458
- {
459
- password: this.password,
460
- }
461
- );
462
- } else {
463
- response = await authentication.registerV2({
464
- email: this.email,
465
- },
466
- {
467
- password: this.password,
468
- profile: {
469
- nickname: this.username,
470
- },
471
- captchaTokens: [
472
- {
473
- Recaptcha: this.captcha,
474
- },
475
- ],
476
- });
477
- }
478
-
479
- // Handle login success
480
- if (response.state === 'SUCCESS') {
481
- const tokens = await getMemberTokensForDirectLogin(
482
- response.sessionToken!
483
- );
484
-
485
- // Update context
486
- (wixContext['client'] as WixClient<undefined, ReturnType<typeof SiteSessionAuth>>).auth.setTokens(tokens.tokens);
487
-
488
- localStorage.removeItem('wixRedirectSessionLastPreWarm');
489
- await getAuth().syncToWixPages();
490
-
491
- // Set cookie
492
- this.setCookie('wixSession', JSON.stringify(tokens), 2);
493
-
494
- // Reset loading state before exiting
495
- this.loading = false;
496
-
497
- // Show success animation
498
- this.showSuccessMessage();
499
-
500
- // Dispatch a success event that consumers can listen for
501
- this.dispatchEvent(new CustomEvent('login-success'));
502
- return;
503
- }
504
-
505
- // Handle various states
506
- if (response.state === 'OWNER_APPROVAL_REQUIRED') {
507
- this.pending = { message: 'Your account is pending approval', state: true };
508
- this.updateUI(); // Update UI for state change
509
- } else if (response.state === 'REQUIRE_EMAIL_VERIFICATION') {
510
- this.state = this.State.EMAIL_VERIFICATION;
511
- this.emailVerificationStateToken = response.stateToken;
512
- this.updateUI(); // Update UI for state change
513
- } else if (response.state === 'FAILURE') {
514
- if (response.errorCode === 'invalidPassword') {
515
- this.passwordInvalid = true;
516
- this.updateInputValues(); // Update without full render
517
- } else if (
518
- response.errorCode === 'invalidEmail' ||
519
- response.errorCode === 'emailAlreadyExists'
520
- ) {
521
- this.emailInvalid = true;
522
- this.updateInputValues(); // Update without full render
523
- } else if (response.errorCode === 'resetPassword') {
524
- this.pending = {
525
- message: 'Your password requires reset',
526
- state: true,
527
- };
528
- this.updateUI(); // Update UI for state change
529
- } else {
530
- // Handle other error codes with a generic message
531
- this.showErrorMessage();
532
- const errorElem = this.querySelector('#errorText');
533
- if (errorElem) {
534
- errorElem.setAttribute('data-error-code', response.errorCode || 'unknown');
535
- }
536
- }
537
- }
538
- } catch (error) {
539
- console.error('Authentication error:', error);
540
- // Show user-friendly error message
541
- this.showErrorMessage();
542
- }
543
-
544
- // Reset recaptcha
545
- if (this.querySelector('.g-recaptcha') && window.grecaptcha) {
546
- window.grecaptcha.reset();
547
- }
548
-
549
- this.loading = false;
550
- this.updateInputValues(); // Update UI without full render
551
- }
552
-
553
- // New method to show error message
554
- showErrorMessage() {
555
- this.error = { state: true, message: '' };
556
- this.updateUI();
557
- }
558
-
559
- setCookie(name, value, days) {
560
- let expires = '';
561
- if (days) {
562
- const date = new Date();
563
- date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
564
- expires = "; expires=" + date.toUTCString();
565
- }
566
- const encodedName = encodeURIComponent(name);
567
- const encodedValue = encodeURIComponent(value || '');
568
- document.cookie = `${encodedName}=${encodedValue}${expires}; path=/`;
569
- }
570
-
571
- onCaptchaChange(token) {
572
- this.captcha = token;
573
- this.updateInputValues(); // Update UI without full render
574
- }
575
-
576
- // New method to show success message with animation
577
- showSuccessMessage() {
578
- // Update state
579
- this.state = this.State.SUCCESS;
580
-
581
- // Update UI to show success message
582
- this.updateUI();
583
- }
584
-
585
- updateUI() {
586
- // Handle success message visibility first
587
- const successMessage = this.querySelector('#successMessage');
588
- if (this.state === this.State.SUCCESS) {
589
- this.updateTitleVisibility();
590
-
591
- successMessage.style.display = 'block';
592
- this.querySelector('#formFields').style.display = 'none';
593
- this.querySelector('#pendingMessage').style.display = 'none';
594
- this.querySelector('#errorMessage').style.display = 'none';
595
- return; // Exit early as we don't need to update other UI parts
596
- } else {
597
- successMessage.style.display = 'none';
598
- }
599
-
600
- // Update title visibility
601
- this.updateTitleVisibility();
602
-
603
- // Handle error message visibility
604
- const errorMessage = this.querySelector('#errorMessage');
605
-
606
- if (this.error.state) {
607
- errorMessage.style.display = 'block';
608
- } else {
609
- errorMessage.style.display = 'none';
610
- }
611
-
612
- // Handle pending message visibility
613
- this.updatePendingMessageVisibility();
614
-
615
- // Update field visibility based on current state
616
- this.updateFieldVisibility();
617
-
618
- // Update toggle state link
619
- this.updateToggleStateLink();
620
-
621
- // Set up captcha if needed
622
- if (this.state === this.State.SIGNUP) {
623
- this.setupCaptcha();
624
- }
625
-
626
- // Update input values
627
- this.updateInputValues();
628
-
629
- // Update button text
630
- this.updateButtonText();
631
- }
632
-
633
- updateTitleVisibility() {
634
- // First create a map of all title elements
635
- const titleMap = {
636
- [this.State.LOGIN]: this.querySelector('#title-login'),
637
- [this.State.SIGNUP]: this.querySelector('#title-signup'),
638
- [this.State.RESET_PASSWORD]: this.querySelector('#title-reset'),
639
- [this.State.EMAIL_VERIFICATION]: this.querySelector('#title-verify'),
640
- [this.State.SUCCESS]: this.querySelector('#title-success')
641
- };
642
-
643
- // Hide all title elements first
644
- Object.values(titleMap).forEach(elem => {
645
- if (elem) elem.style.display = 'none';
646
- });
647
-
648
- // Show only the current state's title
649
- const currentTitleElem = titleMap[this.state];
650
- if (currentTitleElem) {
651
- currentTitleElem.style.display = '';
652
- }
653
- }
654
-
655
- updateButtonText() {
656
- // Hide all button text elements
657
- const buttonTextElements = {
658
- login: this.querySelector('.login-text'),
659
- signup: this.querySelector('.signup-text'),
660
- reset: this.querySelector('.reset-text'),
661
- verify: this.querySelector('.verify-text'),
662
- loading: this.querySelector('.loading-text')
663
- };
664
-
665
- // Hide all text elements first
666
- Object.values(buttonTextElements).forEach(elem => {
667
- if (elem) elem.style.display = 'none';
668
- });
669
-
670
- // Determine which text to show based on state
671
- const textToShow = this.loading ? 'loading' :
672
- {
673
- [this.State.LOGIN]: 'login',
674
- [this.State.SIGNUP]: 'signup',
675
- [this.State.RESET_PASSWORD]: 'reset',
676
- [this.State.EMAIL_VERIFICATION]: 'verify'
677
- }[this.state];
678
-
679
- // Show the selected text
680
- const selectedElement = buttonTextElements[textToShow];
681
- if (selectedElement) {
682
- selectedElement.style.display = '';
683
- }
684
- }
685
-
686
- updatePendingMessageVisibility() {
687
- const pendingMessage = this.querySelector('#pendingMessage');
688
- const formFields = this.querySelector('#formFields');
689
-
690
- if (this.pending.state) {
691
- pendingMessage.style.display = 'block';
692
- formFields.style.display = 'none';
693
- this.querySelector('#pendingText').textContent = this.pending.message;
694
- } else {
695
- pendingMessage.style.display = 'none';
696
- formFields.style.display = 'block';
697
- }
698
- }
699
-
700
- updateFieldVisibility() {
701
- // Username field (only visible during signup)
702
- const usernameField = this.querySelector('#usernameField');
703
- const usernameInput = this.querySelector('#username');
704
- if (this.state === this.State.SIGNUP) {
705
- usernameField.style.display = 'block';
706
- usernameInput.setAttribute('required', '');
707
- } else {
708
- usernameField.style.display = 'none';
709
- usernameInput.removeAttribute('required');
710
- }
711
-
712
- // Email vs Code field visibility
713
- const emailField = this.querySelector('#emailField');
714
- const emailInput = this.querySelector('#email');
715
- const codeField = this.querySelector('#codeField');
716
- const codeInput = this.querySelector('#code');
717
-
718
- if (this.state === this.State.EMAIL_VERIFICATION) {
719
- emailField.style.display = 'none';
720
- emailInput.removeAttribute('required');
721
- codeField.style.display = 'block';
722
- codeInput.setAttribute('required', '');
723
- } else {
724
- emailField.style.display = 'block';
725
- emailInput.setAttribute('required', '');
726
- codeField.style.display = 'none';
727
- codeInput.removeAttribute('required');
728
- }
729
-
730
- // Password field visibility
731
- const passwordField = this.querySelector('#passwordField');
732
- const passwordInput = this.querySelector('#password');
733
- if (this.state === this.State.RESET_PASSWORD || this.state === this.State.EMAIL_VERIFICATION) {
734
- passwordField.style.display = 'none';
735
- passwordInput.removeAttribute('required');
736
- } else {
737
- passwordField.style.display = 'block';
738
- passwordInput.setAttribute('required', '');
739
- }
740
-
741
- // Forgot password link visibility
742
- const forgotPasswordLink = this.querySelector('#forgotPasswordLink');
743
- if (this.state === this.State.LOGIN) {
744
- forgotPasswordLink.style.display = 'block';
745
- } else {
746
- forgotPasswordLink.style.display = 'none';
747
- }
748
-
749
- // Recaptcha visibility
750
- const recaptchaContainer = this.querySelector('#recaptcha-container');
751
- if (this.state === this.State.SIGNUP) {
752
- recaptchaContainer.style.display = 'block';
753
- } else {
754
- recaptchaContainer.style.display = 'none';
755
- }
756
- }
757
-
758
- updateToggleStateLink() {
759
- const toggleStateContainer = this.querySelector('#toggleStateContainer');
760
- const loginToggle = this.querySelector('.login-toggle');
761
- const signupToggle = this.querySelector('.signup-toggle');
762
-
763
- if (this.state === this.State.RESET_PASSWORD || this.state === this.State.EMAIL_VERIFICATION) {
764
- toggleStateContainer.style.display = 'none';
765
- } else {
766
- toggleStateContainer.style.display = 'block';
767
-
768
- // Show the appropriate toggle based on state
769
- if (this.state === this.State.LOGIN) {
770
- loginToggle.style.display = '';
771
- signupToggle.style.display = 'none';
772
-
773
- // Add event listener to login toggle
774
- const toggleLink = loginToggle.querySelector('#toggleState');
775
- if (toggleLink) {
776
- toggleLink.addEventListener('click', (e) => {
777
- e.preventDefault();
778
- this.state = this.State.SIGNUP;
779
- this.updateUI();
780
- });
781
- }
782
- } else {
783
- loginToggle.style.display = 'none';
784
- signupToggle.style.display = '';
785
-
786
- // Add event listener to signup toggle
787
- const toggleLink = signupToggle.querySelector('#toggleState-signup');
788
- if (toggleLink) {
789
- toggleLink.addEventListener('click', (e) => {
790
- e.preventDefault();
791
- this.state = this.State.LOGIN;
792
- this.updateUI();
793
- });
794
- }
795
- }
796
- }
797
- }
798
-
799
- updateInputValues() {
800
- // Update email input
801
- const emailInput = this.querySelector('#email');
802
- if (emailInput) {
803
- emailInput.value = this.email;
804
- if (this.emailInvalid) {
805
- emailInput.classList.add('error');
806
- } else {
807
- emailInput.classList.remove('error');
808
- }
809
- }
810
-
811
- // Update password input
812
- const passwordInput = this.querySelector('#password');
813
- if (passwordInput) {
814
- passwordInput.value = this.password;
815
- if (this.passwordInvalid) {
816
- passwordInput.classList.add('error');
817
- } else {
818
- passwordInput.classList.remove('error');
819
- }
820
- }
821
-
822
- // Update username input
823
- const usernameInput = this.querySelector('#username');
824
- if (usernameInput) {
825
- usernameInput.value = this.username;
826
- }
827
-
828
- // Update code input
829
- const codeInput = this.querySelector('#code');
830
- if (codeInput) {
831
- codeInput.value = this.code;
832
- }
833
-
834
- // Update submit button state
835
- const submitButton = this.querySelector('#submitButton');
836
- if (submitButton) {
837
- let shouldDisable =
838
- (!this.email && this.state !== this.State.EMAIL_VERIFICATION) ||
839
- (!this.password &&
840
- this.state !== this.State.RESET_PASSWORD &&
841
- this.state !== this.State.EMAIL_VERIFICATION) ||
842
- this.loading;
843
-
844
- // Add captcha check for signup
845
- if (this.state === this.State.SIGNUP) {
846
- // For signup, require username and captcha as well
847
- shouldDisable = shouldDisable ||
848
- !this.username ||
849
- !this.captcha; // Make button disabled if captcha is not completed
850
- }
851
-
852
- submitButton.disabled = shouldDisable;
853
- }
854
- }
855
-
856
- restoreFocus(activeElementId) {
857
- if (activeElementId) {
858
- const elementToFocus = this.querySelector(`#${activeElementId}`);
859
- if (elementToFocus) {
860
- elementToFocus.focus();
861
-
862
- // If it's an input, place cursor at the end
863
- if (elementToFocus.tagName === 'INPUT' && (elementToFocus.type === 'text' || elementToFocus.type === 'email')) {
864
- elementToFocus.selectionStart = elementToFocus.selectionEnd = elementToFocus.value.length;
865
- }
866
- }
867
- }
868
- }
869
- }
870
-
871
- // Define the custom element
872
- customElements.define('login-form', LoginForm);
873
- </script>