* Setup PIN input with visual boxes
(container)
| 451 | const toggleBtn = container.querySelector('.toggle-pin-visibility'); |
| 452 | const randomBtn = container.querySelector('.random-pin-btn'); |
| 453 | if (!pinInput || !boxes.length) return; |
| 454 | |
| 455 | let isRevealed = true; // Show PIN by default (low-stakes sharing) |
| 456 | let cursorPos = 0; |
| 457 | |
| 458 | const updateBoxes = () => { |
| 459 | const value = pinInput.value; |
| 460 | // Highlight the box that backspace would delete (cursorPos - 1), |
| 461 | // or box 0 if cursor is at start |
| 462 | const activeIndex = Math.min(Math.max(cursorPos - 1, 0), 5); |
| 463 | boxes.forEach((box, i) => { |
| 464 | const char = value[i] || ''; |
| 465 | box.textContent = char ? (isRevealed ? char : '•') : ''; |
| 466 | box.classList.toggle('filled', !!char); |
| 467 | box.classList.toggle('active', i === activeIndex); |
| 468 | }); |
| 469 | }; |
| 470 | |
| 471 | const setCursor = (pos) => { |
| 472 | cursorPos = Math.max(0, Math.min(pos, pinInput.value.length, 6)); |
| 473 | pinInput.setSelectionRange(cursorPos, cursorPos); |
| 474 | updateBoxes(); |
| 475 | }; |
| 476 | |
| 477 | // Handle input |
| 478 | pinInput.addEventListener('input', () => { |
| 479 | pinInput.value = sanitizePinInput(pinInput.value); |
| 480 | cursorPos = pinInput.selectionStart ?? pinInput.value.length; |
| 481 | updateBoxes(); |
| 482 | }); |
| 483 | |
| 484 | // Handle paste |
| 485 | pinInput.addEventListener('paste', (e) => { |
| 486 | e.preventDefault(); |
| 487 | const pasted = (e.clipboardData || window.clipboardData).getData('text'); |
| 488 | pinInput.value = sanitizePinInput(pasted); |
| 489 | cursorPos = pinInput.value.length; |
| 490 | updateBoxes(); |
| 491 | }); |
| 492 | |
| 493 | // Handle arrow keys |
| 494 | pinInput.addEventListener('keydown', (e) => { |
| 495 | if (e.key === 'ArrowLeft') { |
| 496 | e.preventDefault(); |
| 497 | setCursor(cursorPos - 1); |
| 498 | } else if (e.key === 'ArrowRight') { |
| 499 | e.preventDefault(); |
| 500 | setCursor(cursorPos + 1); |
| 501 | } else if (e.key === 'Home') { |
| 502 | e.preventDefault(); |
| 503 | setCursor(0); |
| 504 | } else if (e.key === 'End') { |
| 505 | e.preventDefault(); |
| 506 | setCursor(pinInput.value.length); |
| 507 | } |
| 508 | }); |
| 509 | |
| 510 | // Click on individual box positions cursor after it (so backspace deletes that box) |
no test coverage detected