UNPKG

web-ui-pack

Version:
138 lines (137 loc) 5.29 kB
import onEvent from "../helpers/onEvent"; import { stringLowerCount, stringUpperCount } from "../helpers/string"; import { WUPcssIcon } from "../styles"; import WUPTextControl from "./text"; const tagName = "wup-pwd"; export default class WUPPasswordControl extends WUPTextControl { #ctr = this.constructor; static $ariaDescription = __wupln("press Alt + V to show/hide password", "aria"); static get $style() { return `${super.$style} :host { --ctrl-icon-img: var(--wup-icon-eye); } :host[w-reverse] { --ctrl-icon-img: var(--wup-icon-eye-off); } :host input[type=password] { font-family: Verdana, sans-serif; letter-spacing: 0.125em; } :host button[eye] { ${WUPcssIcon} cursor: pointer; margin-right: -0.5em; -webkit-mask-size: calc(var(--ctrl-icon-size) * 1.3); mask-size: calc(var(--ctrl-icon-size) * 1.3); } :host button[eye=off] { --ctrl-icon-img: var(--wup-icon-eye-off); } :host[w-reverse] button[eye="off"] { --ctrl-icon-img: var(--wup-icon-eye); } @media (hover: hover) and (pointer: fine) { :host button[eye]:hover { box-shadow: none; background-color: var(--ctrl-focus-label); } } :host button[clear] { margin: 0; }`; } static $defaults = { ...WUPTextControl.$defaults, validationRules: { ...WUPTextControl.$defaults.validationRules, minNumber: (v, setV) => (!v || (v.match(/[0-9]/g)?.length ?? 0) < setV) && __wupln(`Must contain at least ${setV} number${setV === 1 ? "" : "s"}`, "validation"), minUpper: (v, setV) => (!v || stringUpperCount(v, setV) < setV) && __wupln(`Must contain at least ${setV} upper case`, "validation"), minLower: (v, setV) => (!v || stringLowerCount(v, setV) < setV) && __wupln(`Must contain at least ${setV} lower case`, "validation"), special: (v, setV) => (!v || ![...setV.chars].reduce((prev, c) => (v.includes(c) ? ++prev : prev), 0)) && __wupln(`Must contain at least ${setV.min} special character${setV.min === 1 ? "" : "s"}: ${setV.chars}`, "validation"), confirm: (v, setV, c) => { if (!setV) { return false; } const selector = c.tagName.toLowerCase(); const all = document.querySelectorAll(selector); let i = -1; for (const el of all.values()) { if (c === el) { const found = all[i]; if (!found) { i = -2; } else if (found.$value === v) { return false; } break; } ++i; } if (i === -2) { return `Previous "${selector}" not found`; } return __wupln("Passwords must be equal", "validation"); }, }, reverse: false, }; $refBtnEye = document.createElement("button"); renderControl() { super.renderControl(); this.changeInputType(false); this.$ariaDetails(this.#ctr.$ariaDescription); this.renderBtnEye(); } renderBtnEye() { const b = this.$refBtnEye; b.setAttribute("eye", ""); b.setAttribute("aria-hidden", true); b.setAttribute("type", "button"); b.tabIndex = -1; onEvent(b, "click", (e) => { e.preventDefault(); this.toggleVisibility(); }, { passive: false }); this.$refLabel.appendChild(b); } toggleVisibility() { const start = this.$refInput.selectionStart; const end = this.$refInput.selectionEnd; const isOff = this.$refBtnEye.getAttribute("eye") !== "off"; this.$refBtnEye.setAttribute("eye", isOff ? "off" : ""); this.changeInputType(isOff); window.requestAnimationFrame(() => { this.$refInput.setSelectionRange(start, end); }); } changeInputType(isVisible) { const h = this.$refInput.offsetHeight; this.$refInput.type = isVisible ? "text" : "password"; const isHchanged = h !== this.$refInput.offsetHeight; if (isHchanged && h) { this.$refInput.style.height = `${h}px`; } } gotChanges(propsChanged) { super.gotChanges(propsChanged); this.$refInput.autocomplete = this.$autoComplete || "new-password"; this.setAttr("w-reverse", this._opts.reverse, true); } gotKeyDown(e) { if (e.altKey && e.key === "v") { this.toggleVisibility(); } super.gotKeyDown(e); } storageSet() { console.error("Saving not allowed for password. Remove $options.storageKey"); } } let rr = ["mask", "maskholder", "storageKey", "storage"]; rr.forEach((k) => delete WUPPasswordControl.$defaults[k]); rr = undefined; customElements.define(tagName, WUPPasswordControl);