web-ui-pack
Version:
Web package with UI elements
138 lines (137 loc) • 5.29 kB
JavaScript
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);