UNPKG

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
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