ngx-show-hide-password
Version:
> Add split input button to password or text input. Toggles input type between "text" and "password".
284 lines (275 loc) • 13.3 kB
JavaScript
import * as i0 from '@angular/core';
import { signal, Injectable, inject, input, Directive, HostListener, ElementRef, Renderer2, ErrorHandler, Injector, effect, Input, ChangeDetectionStrategy, Component } from '@angular/core';
import { faEye, faEyeSlash } from '@fortawesome/free-solid-svg-icons';
import * as i1 from '@fortawesome/angular-fontawesome';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { NgClass } from '@angular/common';
class ShowHideService {
constructor() {
this.states = [];
}
getIO(id) {
let io = this.states.find((o) => o.id === id);
if (!io) {
io = this.init(id);
}
return io;
}
init(id) {
const subject = signal(false, ...(ngDevMode ? [{ debugName: "subject" }] : []));
const io = {
id,
show: false,
subject,
};
this.states.push(io);
return io;
}
saveAndProadcast(io, show) {
io.show = show;
io.subject.set(io.show);
}
getSignal(id) {
return this.getIO(id).subject;
}
setShow(id, show) {
this.saveAndProadcast(this.getIO(id), show);
}
toggleShow(id) {
const io = this.getIO(id);
this.saveAndProadcast(io, !io.show);
}
static { this.ɵfac = function ShowHideService_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ShowHideService)(); }; }
static { this.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: ShowHideService, factory: ShowHideService.ɵfac, providedIn: 'root' }); }
}
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ShowHideService, [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}], null, null); })();
class ShowHideTriggerDirective {
constructor() {
this.service = inject(ShowHideService);
this.showHideTrigger = input.required(...(ngDevMode ? [{ debugName: "showHideTrigger" }] : []));
}
onClick() {
this.service.toggleShow(this.showHideTrigger());
}
static { this.ɵfac = function ShowHideTriggerDirective_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ShowHideTriggerDirective)(); }; }
static { this.ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({ type: ShowHideTriggerDirective, selectors: [["", "showHideTrigger", ""]], hostBindings: function ShowHideTriggerDirective_HostBindings(rf, ctx) { if (rf & 1) {
i0.ɵɵlistener("click", function ShowHideTriggerDirective_click_HostBindingHandler() { return ctx.onClick(); });
} }, inputs: { showHideTrigger: [1, "showHideTrigger"] } }); }
}
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ShowHideTriggerDirective, [{
type: Directive,
args: [{
// eslint-disable-next-line @angular-eslint/directive-selector
selector: '[showHideTrigger]',
standalone: true,
}]
}], null, { showHideTrigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "showHideTrigger", required: true }] }], onClick: [{
type: HostListener,
args: ['click']
}] }); })();
/* eslint-disable @angular-eslint/directive-selector */
const defaultConfig = {
show: 'visibility',
hide: 'visibility_off',
materialIcon: false,
};
// TODO: don't trigger on disabled element
class ShowHideStatusDirective {
constructor() {
this.service = inject(ShowHideService);
this.el = inject(ElementRef);
this.renderer = inject(Renderer2);
this.errorHandler = inject(ErrorHandler);
this.injector = inject(Injector);
this.config = defaultConfig;
}
// TODO: Skipped for migration because:
// Accessor inputs cannot be migrated as they are too complex.
set showHideStatus(config) {
this.init(config);
}
init(config) {
this.config = {
...defaultConfig,
...config,
};
if (this.config.id) {
effect(() => {
this.updateStatus(this.service.getSignal(this.config.id)());
}, { injector: this.injector });
}
else {
this.errorHandler.handleError(new Error(`Status can not be set without [id].`));
}
}
updateStatus(show) {
if (this.config.materialIcon) {
this.renderer.setProperty(this.el.nativeElement, 'innerHTML', show ? this.config.hide : this.config.show);
}
else {
this.renderer.removeClass(this.el.nativeElement, (!show ? this.config.hide : this.config.show) ?? '');
this.renderer.addClass(this.el.nativeElement, (show ? this.config.hide : this.config.show) ?? '');
}
}
static { this.ɵfac = function ShowHideStatusDirective_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ShowHideStatusDirective)(); }; }
static { this.ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({ type: ShowHideStatusDirective, selectors: [["", "showHideStatus", ""]], inputs: { showHideStatus: "showHideStatus" } }); }
}
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ShowHideStatusDirective, [{
type: Directive,
args: [{
selector: '[showHideStatus]',
standalone: true,
}]
}], null, { showHideStatus: [{
type: Input,
args: [{ required: true }]
}] }); })();
class ShowHideInputDirective {
constructor() {
this.service = inject(ShowHideService);
this.el = inject(ElementRef);
this.renderer = inject(Renderer2);
this.injector = inject(Injector);
this.id = input.required(...(ngDevMode ? [{ debugName: "id" }] : []));
this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", transform: (value) => (typeof value === 'string' ? value === '' : value) }] : [{
transform: (value) => (typeof value === 'string' ? value === '' : value),
}]));
}
ngOnInit() {
this.service.setShow(this.id(), this.el.nativeElement.type !== 'password');
effect(() => {
if (this.disabled())
return;
this.renderer.setAttribute(this.el.nativeElement, 'type', this.service.getSignal(this.id())() ? 'text' : 'password');
}, { injector: this.injector });
}
static { this.ɵfac = function ShowHideInputDirective_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ShowHideInputDirective)(); }; }
static { this.ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({ type: ShowHideInputDirective, selectors: [["input", "showHideInput", ""]], inputs: { id: [1, "id"], disabled: [1, "disabled"] } }); }
}
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ShowHideInputDirective, [{
type: Directive,
args: [{
// eslint-disable-next-line @angular-eslint/directive-selector
selector: 'input[showHideInput]',
standalone: true,
}]
}], null, { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: true }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] }); })();
/* eslint-disable no-bitwise */
const _c0 = ["*"];
const _c1 = a0 => ({ id: a0 });
var BtnStyle;
(function (BtnStyle) {
BtnStyle["Primary"] = "primary";
BtnStyle["Secondary"] = "secondary";
BtnStyle["Success"] = "success";
BtnStyle["Danger"] = "danger";
BtnStyle["Warning"] = "warning";
BtnStyle["Info"] = "info";
BtnStyle["Dark"] = "dark";
BtnStyle["Light"] = "light";
})(BtnStyle || (BtnStyle = {}));
// hail jed https://gist.github.com/jed/982883
const uuid = (a) => a
? (a ^ ((Math.random() * 16) >> (a / 4))).toString(16)
: ('' + 1e7 + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, uuid);
/**
* Add a split input button to password or text input. Toggles input type between "text" and "password".
*
* @example
* <show-hide-password size="sm|lg">
* <input type="password" name=... />
* </show-hide-password>
*/
class ShowHidePasswordComponent {
constructor() {
this.service = inject(ShowHideService);
this.elem = inject(ElementRef);
this.renderer = inject(Renderer2);
this.injector = inject(Injector);
this.btnStyle = input(BtnStyle.Secondary, ...(ngDevMode ? [{ debugName: "btnStyle" }] : []));
this.btnOutline = input(true, ...(ngDevMode ? [{ debugName: "btnOutline", transform: (value) => (typeof value === 'string' ? value === '' : value) }] : [{
transform: (value) => (typeof value === 'string' ? value === '' : value),
}]));
this.size = input('', ...(ngDevMode ? [{ debugName: "size" }] : []));
this.faEye = faEye;
this.faEyeSlash = faEyeSlash;
}
ngOnInit() {
this.input = this.elem.nativeElement.querySelector('input');
if (!this.input) {
throw new Error(`No input element found.`);
}
this.id = this.input.getAttribute('id');
if (!this.id) {
this.id = 'showHideInput_' + uuid();
this.renderer.setAttribute(this.input, 'id', this.id);
}
this.renderer.addClass(this.elem.nativeElement, 'input-group');
if (this.size() === 'sm') {
this.renderer.addClass(this.elem.nativeElement, 'input-group-sm');
}
else if (this.size() === 'lg') {
this.renderer.addClass(this.elem.nativeElement, 'input-group-lg');
}
this.isHidden = this.input.type === 'password';
this.renderer.addClass(this.input, 'form-control'); // just to be sure
this.service.setShow(this.id, this.input.type !== 'password');
effect(() => {
const show = this.service.getSignal(this.id)();
this.isHidden = !show;
this.renderer.setAttribute(this.input, 'type', show ? 'text' : 'password');
}, { injector: this.injector });
}
static { this.ɵfac = function ShowHidePasswordComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ShowHidePasswordComponent)(); }; }
static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ShowHidePasswordComponent, selectors: [["show-hide-password"]], inputs: { btnStyle: [1, "btnStyle"], btnOutline: [1, "btnOutline"], size: [1, "size"] }, ngContentSelectors: _c0, decls: 3, vars: 7, consts: [["type", "button", 1, "btn", "ngx-show-hide-password", 3, "ngClass", "showHideTrigger"], ["size", "lg", 3, "fixedWidth", "icon", "showHideStatus"]], template: function ShowHidePasswordComponent_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵprojectionDef();
i0.ɵɵprojection(0);
i0.ɵɵelementStart(1, "button", 0);
i0.ɵɵelement(2, "fa-icon", 1);
i0.ɵɵelementEnd();
} if (rf & 2) {
i0.ɵɵadvance();
i0.ɵɵproperty("ngClass", ctx.btnOutline() ? "btn-outline-" + ctx.btnStyle() : "btn-" + ctx.btnStyle())("showHideTrigger", ctx.id);
i0.ɵɵadvance();
i0.ɵɵproperty("fixedWidth", true)("icon", ctx.isHidden ? ctx.faEye : ctx.faEyeSlash)("showHideStatus", i0.ɵɵpureFunction1(5, _c1, ctx.id));
} }, dependencies: [NgClass, ShowHideTriggerDirective, FontAwesomeModule, i1.FaIconComponent, ShowHideStatusDirective], encapsulation: 2, changeDetection: 0 }); }
}
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ShowHidePasswordComponent, [{
type: Component,
args: [{
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'show-hide-password',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<ng-content />
<button
class="btn ngx-show-hide-password"
[ngClass]="btnOutline() ? 'btn-outline-' + btnStyle() : 'btn-' + btnStyle()"
type="button"
[showHideTrigger]="id"
>
<fa-icon
[fixedWidth]="true"
size="lg"
[icon]="isHidden ? faEye : faEyeSlash"
[showHideStatus]="{ id: id }"
></fa-icon>
</button>
`,
imports: [NgClass, ShowHideTriggerDirective, FontAwesomeModule, ShowHideStatusDirective],
}]
}], null, { btnStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "btnStyle", required: false }] }], btnOutline: [{ type: i0.Input, args: [{ isSignal: true, alias: "btnOutline", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] }); })();
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ShowHidePasswordComponent, { className: "ShowHidePasswordComponent", filePath: "lib/show-hide-password.component.ts", lineNumber: 68 }); })();
/*
* Public API Surface of ngx-show-hide-password
*/
/**
* Generated bundle index. Do not edit.
*/
export { BtnStyle, ShowHideInputDirective, ShowHidePasswordComponent, ShowHideService, ShowHideStatusDirective, ShowHideTriggerDirective };
//# sourceMappingURL=ngx-show-hide-password.mjs.map