/** * Show/hide password toggle button injected next to password inputs. */ import { resolveHost } from '../core/app-dom.js'; export function initPasswordToggles(root = document, options = {}) { const selector = String(options.selector || 'input[type="password"]').trim() || 'input[type="password"]'; const host = resolveHost(root); const inputs = Array.from(host.querySelectorAll(selector)); if (!inputs.length) { return { destroy: () => {} }; } const showLabel = (document.documentElement.dataset.passwordToggleShowLabel || '').trim(); const hideLabel = (document.documentElement.dataset.passwordToggleHideLabel || '').trim(); if (!showLabel || !hideLabel) { return { destroy: () => {} }; } const cleanupFns = []; inputs.forEach((input) => { if (!(input instanceof HTMLInputElement)) {return;} if (input.dataset.passwordToggleBound === '1') {return;} const label = input.closest('label'); if (!label) {return;} const parent = input.parentElement; if (parent instanceof HTMLElement && parent.classList.contains('login-password-wrapper')) { const existingToggle = parent.querySelector('.login-password-toggle'); if (existingToggle instanceof HTMLButtonElement) { input.dataset.passwordToggleBound = '1'; existingToggle.dataset.passwordToggleBound = '1'; existingToggle.setAttribute('aria-label', input.type === 'password' ? showLabel : hideLabel); cleanupFns.push(() => { delete input.dataset.passwordToggleBound; delete existingToggle.dataset.passwordToggleBound; }); return; } } const wrapper = document.createElement('div'); wrapper.className = 'login-password-wrapper'; const toggle = document.createElement('button'); toggle.type = 'button'; toggle.className = 'login-password-toggle'; toggle.setAttribute('aria-label', showLabel); const icon = document.createElement('i'); icon.className = 'bi bi-eye'; icon.setAttribute('aria-hidden', 'true'); toggle.appendChild(icon); input.replaceWith(wrapper); wrapper.appendChild(input); wrapper.appendChild(toggle); input.dataset.passwordToggleBound = '1'; toggle.dataset.passwordToggleBound = '1'; const onToggleClick = () => { const isPassword = input.type === 'password'; input.type = isPassword ? 'text' : 'password'; icon.className = isPassword ? 'bi bi-eye-slash' : 'bi bi-eye'; toggle.setAttribute('aria-label', isPassword ? hideLabel : showLabel); input.focus(); }; toggle.addEventListener('click', onToggleClick); cleanupFns.push(() => { toggle.removeEventListener('click', onToggleClick); delete input.dataset.passwordToggleBound; delete toggle.dataset.passwordToggleBound; }); }); const destroy = () => { cleanupFns.forEach((cleanup) => cleanup()); }; return { destroy }; }