@wix/astro 0.3.4 → 1.0.2

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 (78) hide show
  1. package/build/index.d.ts +5 -0
  2. package/build/index.js +124997 -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/middleware/auth.d.ts +5 -0
  7. package/build-runtime/middleware/auth.js +8027 -0
  8. package/build-runtime/routes/webhooks.d.ts +5 -0
  9. package/build-runtime/routes/webhooks.js +11 -0
  10. package/package.json +37 -47
  11. package/runtime/entry.astro +26 -0
  12. package/src/directories.ts +14 -0
  13. package/src/env.d.ts +9 -0
  14. package/src/index.ts +283 -0
  15. package/src/middleware/auth.ts +86 -0
  16. package/src/plugins/patchGlobal.ts +20 -0
  17. package/src/routes/webhooks.ts +10 -0
  18. package/src/utils/authStrategyAsyncLocalStorage.ts +7 -0
  19. package/src/utils/checkIsDynamicPageRequest.ts +17 -0
  20. package/src/utils/createProjectModel.ts +87 -0
  21. package/src/utils/generateAppManifest.ts +49 -0
  22. package/src/utils/getSessionTokensFromCookie.ts +38 -0
  23. package/src/utils/isValidBackofficeComponent.ts +15 -0
  24. package/src/utils/isValidWebhookComponent.ts +9 -0
  25. package/src/utils/loadExtension.ts +59 -0
  26. package/src/utils/saveSessionTokensToCookie.ts +17 -0
  27. package/src/utils/writeVirtualBackofficeExtensionFiles.ts +56 -0
  28. package/src/utils/writeVirtualWebhookExtensionFiles.ts +70 -0
  29. package/tsconfig.json +8 -0
  30. package/tsup.config.mjs +28 -0
  31. package/README.md +0 -121
  32. package/dist/auth-context.d.ts +0 -5
  33. package/dist/auth-context.js +0 -2
  34. package/dist/client.d.ts +0 -15
  35. package/dist/client.js +0 -16
  36. package/dist/components/login-helpers/bi-header-generator.d.ts +0 -11
  37. package/dist/components/login-helpers/bi-header-generator.js +0 -17
  38. package/dist/components/login-helpers/iframeUtils.d.ts +0 -4
  39. package/dist/components/login-helpers/iframeUtils.js +0 -43
  40. package/dist/components/login-helpers/login-helpers.d.ts +0 -42
  41. package/dist/components/login-helpers/login-helpers.js +0 -119
  42. package/dist/components/login.astro +0 -872
  43. package/dist/entrypoints/server.d.ts +0 -13
  44. package/dist/entrypoints/server.js +0 -36
  45. package/dist/helpers/index.d.ts +0 -1
  46. package/dist/helpers/index.js +0 -1
  47. package/dist/index.d.ts +0 -5
  48. package/dist/index.js +0 -4
  49. package/dist/integration.d.ts +0 -22
  50. package/dist/integration.js +0 -233
  51. package/dist/loaders/blog.d.ts +0 -2
  52. package/dist/loaders/blog.js +0 -48
  53. package/dist/loaders/index.d.ts +0 -2
  54. package/dist/loaders/index.js +0 -2
  55. package/dist/middleware.d.ts +0 -2
  56. package/dist/middleware.js +0 -89
  57. package/dist/routes/auth/callback.d.ts +0 -3
  58. package/dist/routes/auth/callback.js +0 -52
  59. package/dist/routes/auth/constants.d.ts +0 -5
  60. package/dist/routes/auth/constants.js +0 -5
  61. package/dist/routes/auth/login.d.ts +0 -3
  62. package/dist/routes/auth/login.js +0 -26
  63. package/dist/routes/auth/logout-callback.d.ts +0 -3
  64. package/dist/routes/auth/logout-callback.js +0 -9
  65. package/dist/routes/auth/logout.d.ts +0 -3
  66. package/dist/routes/auth/logout.js +0 -10
  67. package/dist/routes/auth/runtime.d.ts +0 -5
  68. package/dist/routes/auth/runtime.js +0 -7
  69. package/dist/runtime-client.d.ts +0 -3
  70. package/dist/runtime-client.js +0 -4
  71. package/dist/runtime.client.d.ts +0 -2
  72. package/dist/runtime.client.js +0 -4
  73. package/dist/runtime.d.ts +0 -5
  74. package/dist/runtime.js +0 -12
  75. package/dist/runtime.server.d.ts +0 -2
  76. package/dist/runtime.server.js +0 -8
  77. package/dist/vite-plugins/sdk-context.d.ts +0 -4
  78. package/dist/vite-plugins/sdk-context.js +0 -67
@@ -1,872 +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
- await getAuth().syncToWixPages({ force: true });
489
-
490
- // Set cookie
491
- this.setCookie('wixSession', JSON.stringify(tokens), 2);
492
-
493
- // Reset loading state before exiting
494
- this.loading = false;
495
-
496
- // Show success animation
497
- this.showSuccessMessage();
498
-
499
- // Dispatch a success event that consumers can listen for
500
- this.dispatchEvent(new CustomEvent('login-success'));
501
- return;
502
- }
503
-
504
- // Handle various states
505
- if (response.state === 'OWNER_APPROVAL_REQUIRED') {
506
- this.pending = { message: 'Your account is pending approval', state: true };
507
- this.updateUI(); // Update UI for state change
508
- } else if (response.state === 'REQUIRE_EMAIL_VERIFICATION') {
509
- this.state = this.State.EMAIL_VERIFICATION;
510
- this.emailVerificationStateToken = response.stateToken;
511
- this.updateUI(); // Update UI for state change
512
- } else if (response.state === 'FAILURE') {
513
- if (response.errorCode === 'invalidPassword') {
514
- this.passwordInvalid = true;
515
- this.updateInputValues(); // Update without full render
516
- } else if (
517
- response.errorCode === 'invalidEmail' ||
518
- response.errorCode === 'emailAlreadyExists'
519
- ) {
520
- this.emailInvalid = true;
521
- this.updateInputValues(); // Update without full render
522
- } else if (response.errorCode === 'resetPassword') {
523
- this.pending = {
524
- message: 'Your password requires reset',
525
- state: true,
526
- };
527
- this.updateUI(); // Update UI for state change
528
- } else {
529
- // Handle other error codes with a generic message
530
- this.showErrorMessage();
531
- const errorElem = this.querySelector('#errorText');
532
- if (errorElem) {
533
- errorElem.setAttribute('data-error-code', response.errorCode || 'unknown');
534
- }
535
- }
536
- }
537
- } catch (error) {
538
- console.error('Authentication error:', error);
539
- // Show user-friendly error message
540
- this.showErrorMessage();
541
- }
542
-
543
- // Reset recaptcha
544
- if (this.querySelector('.g-recaptcha') && window.grecaptcha) {
545
- window.grecaptcha.reset();
546
- }
547
-
548
- this.loading = false;
549
- this.updateInputValues(); // Update UI without full render
550
- }
551
-
552
- // New method to show error message
553
- showErrorMessage() {
554
- this.error = { state: true, message: '' };
555
- this.updateUI();
556
- }
557
-
558
- setCookie(name, value, days) {
559
- let expires = '';
560
- if (days) {
561
- const date = new Date();
562
- date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
563
- expires = "; expires=" + date.toUTCString();
564
- }
565
- const encodedName = encodeURIComponent(name);
566
- const encodedValue = encodeURIComponent(value || '');
567
- document.cookie = `${encodedName}=${encodedValue}${expires}; path=/`;
568
- }
569
-
570
- onCaptchaChange(token) {
571
- this.captcha = token;
572
- this.updateInputValues(); // Update UI without full render
573
- }
574
-
575
- // New method to show success message with animation
576
- showSuccessMessage() {
577
- // Update state
578
- this.state = this.State.SUCCESS;
579
-
580
- // Update UI to show success message
581
- this.updateUI();
582
- }
583
-
584
- updateUI() {
585
- // Handle success message visibility first
586
- const successMessage = this.querySelector('#successMessage');
587
- if (this.state === this.State.SUCCESS) {
588
- this.updateTitleVisibility();
589
-
590
- successMessage.style.display = 'block';
591
- this.querySelector('#formFields').style.display = 'none';
592
- this.querySelector('#pendingMessage').style.display = 'none';
593
- this.querySelector('#errorMessage').style.display = 'none';
594
- return; // Exit early as we don't need to update other UI parts
595
- } else {
596
- successMessage.style.display = 'none';
597
- }
598
-
599
- // Update title visibility
600
- this.updateTitleVisibility();
601
-
602
- // Handle error message visibility
603
- const errorMessage = this.querySelector('#errorMessage');
604
-
605
- if (this.error.state) {
606
- errorMessage.style.display = 'block';
607
- } else {
608
- errorMessage.style.display = 'none';
609
- }
610
-
611
- // Handle pending message visibility
612
- this.updatePendingMessageVisibility();
613
-
614
- // Update field visibility based on current state
615
- this.updateFieldVisibility();
616
-
617
- // Update toggle state link
618
- this.updateToggleStateLink();
619
-
620
- // Set up captcha if needed
621
- if (this.state === this.State.SIGNUP) {
622
- this.setupCaptcha();
623
- }
624
-
625
- // Update input values
626
- this.updateInputValues();
627
-
628
- // Update button text
629
- this.updateButtonText();
630
- }
631
-
632
- updateTitleVisibility() {
633
- // First create a map of all title elements
634
- const titleMap = {
635
- [this.State.LOGIN]: this.querySelector('#title-login'),
636
- [this.State.SIGNUP]: this.querySelector('#title-signup'),
637
- [this.State.RESET_PASSWORD]: this.querySelector('#title-reset'),
638
- [this.State.EMAIL_VERIFICATION]: this.querySelector('#title-verify'),
639
- [this.State.SUCCESS]: this.querySelector('#title-success')
640
- };
641
-
642
- // Hide all title elements first
643
- Object.values(titleMap).forEach(elem => {
644
- if (elem) elem.style.display = 'none';
645
- });
646
-
647
- // Show only the current state's title
648
- const currentTitleElem = titleMap[this.state];
649
- if (currentTitleElem) {
650
- currentTitleElem.style.display = '';
651
- }
652
- }
653
-
654
- updateButtonText() {
655
- // Hide all button text elements
656
- const buttonTextElements = {
657
- login: this.querySelector('.login-text'),
658
- signup: this.querySelector('.signup-text'),
659
- reset: this.querySelector('.reset-text'),
660
- verify: this.querySelector('.verify-text'),
661
- loading: this.querySelector('.loading-text')
662
- };
663
-
664
- // Hide all text elements first
665
- Object.values(buttonTextElements).forEach(elem => {
666
- if (elem) elem.style.display = 'none';
667
- });
668
-
669
- // Determine which text to show based on state
670
- const textToShow = this.loading ? 'loading' :
671
- {
672
- [this.State.LOGIN]: 'login',
673
- [this.State.SIGNUP]: 'signup',
674
- [this.State.RESET_PASSWORD]: 'reset',
675
- [this.State.EMAIL_VERIFICATION]: 'verify'
676
- }[this.state];
677
-
678
- // Show the selected text
679
- const selectedElement = buttonTextElements[textToShow];
680
- if (selectedElement) {
681
- selectedElement.style.display = '';
682
- }
683
- }
684
-
685
- updatePendingMessageVisibility() {
686
- const pendingMessage = this.querySelector('#pendingMessage');
687
- const formFields = this.querySelector('#formFields');
688
-
689
- if (this.pending.state) {
690
- pendingMessage.style.display = 'block';
691
- formFields.style.display = 'none';
692
- this.querySelector('#pendingText').textContent = this.pending.message;
693
- } else {
694
- pendingMessage.style.display = 'none';
695
- formFields.style.display = 'block';
696
- }
697
- }
698
-
699
- updateFieldVisibility() {
700
- // Username field (only visible during signup)
701
- const usernameField = this.querySelector('#usernameField');
702
- const usernameInput = this.querySelector('#username');
703
- if (this.state === this.State.SIGNUP) {
704
- usernameField.style.display = 'block';
705
- usernameInput.setAttribute('required', '');
706
- } else {
707
- usernameField.style.display = 'none';
708
- usernameInput.removeAttribute('required');
709
- }
710
-
711
- // Email vs Code field visibility
712
- const emailField = this.querySelector('#emailField');
713
- const emailInput = this.querySelector('#email');
714
- const codeField = this.querySelector('#codeField');
715
- const codeInput = this.querySelector('#code');
716
-
717
- if (this.state === this.State.EMAIL_VERIFICATION) {
718
- emailField.style.display = 'none';
719
- emailInput.removeAttribute('required');
720
- codeField.style.display = 'block';
721
- codeInput.setAttribute('required', '');
722
- } else {
723
- emailField.style.display = 'block';
724
- emailInput.setAttribute('required', '');
725
- codeField.style.display = 'none';
726
- codeInput.removeAttribute('required');
727
- }
728
-
729
- // Password field visibility
730
- const passwordField = this.querySelector('#passwordField');
731
- const passwordInput = this.querySelector('#password');
732
- if (this.state === this.State.RESET_PASSWORD || this.state === this.State.EMAIL_VERIFICATION) {
733
- passwordField.style.display = 'none';
734
- passwordInput.removeAttribute('required');
735
- } else {
736
- passwordField.style.display = 'block';
737
- passwordInput.setAttribute('required', '');
738
- }
739
-
740
- // Forgot password link visibility
741
- const forgotPasswordLink = this.querySelector('#forgotPasswordLink');
742
- if (this.state === this.State.LOGIN) {
743
- forgotPasswordLink.style.display = 'block';
744
- } else {
745
- forgotPasswordLink.style.display = 'none';
746
- }
747
-
748
- // Recaptcha visibility
749
- const recaptchaContainer = this.querySelector('#recaptcha-container');
750
- if (this.state === this.State.SIGNUP) {
751
- recaptchaContainer.style.display = 'block';
752
- } else {
753
- recaptchaContainer.style.display = 'none';
754
- }
755
- }
756
-
757
- updateToggleStateLink() {
758
- const toggleStateContainer = this.querySelector('#toggleStateContainer');
759
- const loginToggle = this.querySelector('.login-toggle');
760
- const signupToggle = this.querySelector('.signup-toggle');
761
-
762
- if (this.state === this.State.RESET_PASSWORD || this.state === this.State.EMAIL_VERIFICATION) {
763
- toggleStateContainer.style.display = 'none';
764
- } else {
765
- toggleStateContainer.style.display = 'block';
766
-
767
- // Show the appropriate toggle based on state
768
- if (this.state === this.State.LOGIN) {
769
- loginToggle.style.display = '';
770
- signupToggle.style.display = 'none';
771
-
772
- // Add event listener to login toggle
773
- const toggleLink = loginToggle.querySelector('#toggleState');
774
- if (toggleLink) {
775
- toggleLink.addEventListener('click', (e) => {
776
- e.preventDefault();
777
- this.state = this.State.SIGNUP;
778
- this.updateUI();
779
- });
780
- }
781
- } else {
782
- loginToggle.style.display = 'none';
783
- signupToggle.style.display = '';
784
-
785
- // Add event listener to signup toggle
786
- const toggleLink = signupToggle.querySelector('#toggleState-signup');
787
- if (toggleLink) {
788
- toggleLink.addEventListener('click', (e) => {
789
- e.preventDefault();
790
- this.state = this.State.LOGIN;
791
- this.updateUI();
792
- });
793
- }
794
- }
795
- }
796
- }
797
-
798
- updateInputValues() {
799
- // Update email input
800
- const emailInput = this.querySelector('#email');
801
- if (emailInput) {
802
- emailInput.value = this.email;
803
- if (this.emailInvalid) {
804
- emailInput.classList.add('error');
805
- } else {
806
- emailInput.classList.remove('error');
807
- }
808
- }
809
-
810
- // Update password input
811
- const passwordInput = this.querySelector('#password');
812
- if (passwordInput) {
813
- passwordInput.value = this.password;
814
- if (this.passwordInvalid) {
815
- passwordInput.classList.add('error');
816
- } else {
817
- passwordInput.classList.remove('error');
818
- }
819
- }
820
-
821
- // Update username input
822
- const usernameInput = this.querySelector('#username');
823
- if (usernameInput) {
824
- usernameInput.value = this.username;
825
- }
826
-
827
- // Update code input
828
- const codeInput = this.querySelector('#code');
829
- if (codeInput) {
830
- codeInput.value = this.code;
831
- }
832
-
833
- // Update submit button state
834
- const submitButton = this.querySelector('#submitButton');
835
- if (submitButton) {
836
- let shouldDisable =
837
- (!this.email && this.state !== this.State.EMAIL_VERIFICATION) ||
838
- (!this.password &&
839
- this.state !== this.State.RESET_PASSWORD &&
840
- this.state !== this.State.EMAIL_VERIFICATION) ||
841
- this.loading;
842
-
843
- // Add captcha check for signup
844
- if (this.state === this.State.SIGNUP) {
845
- // For signup, require username and captcha as well
846
- shouldDisable = shouldDisable ||
847
- !this.username ||
848
- !this.captcha; // Make button disabled if captcha is not completed
849
- }
850
-
851
- submitButton.disabled = shouldDisable;
852
- }
853
- }
854
-
855
- restoreFocus(activeElementId) {
856
- if (activeElementId) {
857
- const elementToFocus = this.querySelector(`#${activeElementId}`);
858
- if (elementToFocus) {
859
- elementToFocus.focus();
860
-
861
- // If it's an input, place cursor at the end
862
- if (elementToFocus.tagName === 'INPUT' && (elementToFocus.type === 'text' || elementToFocus.type === 'email')) {
863
- elementToFocus.selectionStart = elementToFocus.selectionEnd = elementToFocus.value.length;
864
- }
865
- }
866
- }
867
- }
868
- }
869
-
870
- // Define the custom element
871
- customElements.define('login-form', LoginForm);
872
- </script>