mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 16:23:44 +00:00
Split jslib into multiple modules (#363)
* Split jslib into multiple modules
This commit is contained in:
28
angular/src/directives/a11y-title.directive.ts
Normal file
28
angular/src/directives/a11y-title.directive.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import {
|
||||
Directive,
|
||||
ElementRef,
|
||||
Input,
|
||||
Renderer2,
|
||||
} from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[appA11yTitle]',
|
||||
})
|
||||
export class A11yTitleDirective {
|
||||
@Input() set appA11yTitle(title: string) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
private title: string;
|
||||
|
||||
constructor(private el: ElementRef, private renderer: Renderer2) { }
|
||||
|
||||
ngOnInit() {
|
||||
if (!this.el.nativeElement.hasAttribute('title')) {
|
||||
this.renderer.setAttribute(this.el.nativeElement, 'title', this.title);
|
||||
}
|
||||
if (!this.el.nativeElement.hasAttribute('aria-label')) {
|
||||
this.renderer.setAttribute(this.el.nativeElement, 'aria-label', this.title);
|
||||
}
|
||||
}
|
||||
}
|
||||
32
angular/src/directives/api-action.directive.ts
Normal file
32
angular/src/directives/api-action.directive.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import {
|
||||
Directive,
|
||||
ElementRef,
|
||||
Input,
|
||||
OnChanges,
|
||||
} from '@angular/core';
|
||||
|
||||
import { ValidationService } from '../services/validation.service';
|
||||
|
||||
@Directive({
|
||||
selector: '[appApiAction]',
|
||||
})
|
||||
export class ApiActionDirective implements OnChanges {
|
||||
@Input() appApiAction: Promise<any>;
|
||||
|
||||
constructor(private el: ElementRef, private validationService: ValidationService) { }
|
||||
|
||||
ngOnChanges(changes: any) {
|
||||
if (this.appApiAction == null || this.appApiAction.then == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.el.nativeElement.loading = true;
|
||||
|
||||
this.appApiAction.then((response: any) => {
|
||||
this.el.nativeElement.loading = false;
|
||||
}, (e: any) => {
|
||||
this.el.nativeElement.loading = false;
|
||||
this.validationService.showError(e);
|
||||
});
|
||||
}
|
||||
}
|
||||
26
angular/src/directives/autofocus.directive.ts
Normal file
26
angular/src/directives/autofocus.directive.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import {
|
||||
Directive,
|
||||
ElementRef,
|
||||
Input,
|
||||
} from '@angular/core';
|
||||
|
||||
import { Utils } from 'jslib-common/misc/utils';
|
||||
|
||||
@Directive({
|
||||
selector: '[appAutofocus]',
|
||||
})
|
||||
export class AutofocusDirective {
|
||||
@Input() set appAutofocus(condition: boolean | string) {
|
||||
this.autofocus = condition === '' || condition === true;
|
||||
}
|
||||
|
||||
private autofocus: boolean;
|
||||
|
||||
constructor(private el: ElementRef) { }
|
||||
|
||||
ngOnInit() {
|
||||
if (!Utils.isMobileBrowser && this.autofocus) {
|
||||
this.el.nativeElement.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
17
angular/src/directives/blur-click.directive.ts
Normal file
17
angular/src/directives/blur-click.directive.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import {
|
||||
Directive,
|
||||
ElementRef,
|
||||
HostListener,
|
||||
} from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[appBlurClick]',
|
||||
})
|
||||
export class BlurClickDirective {
|
||||
constructor(private el: ElementRef) {
|
||||
}
|
||||
|
||||
@HostListener('click') onClick() {
|
||||
this.el.nativeElement.blur();
|
||||
}
|
||||
}
|
||||
51
angular/src/directives/box-row.directive.ts
Normal file
51
angular/src/directives/box-row.directive.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import {
|
||||
Directive,
|
||||
ElementRef,
|
||||
HostListener,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[appBoxRow]',
|
||||
})
|
||||
export class BoxRowDirective implements OnInit {
|
||||
el: HTMLElement = null;
|
||||
formEls: Element[];
|
||||
|
||||
constructor(private elRef: ElementRef) {
|
||||
this.el = elRef.nativeElement;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.formEls = Array.from(this.el.querySelectorAll('input:not([type="hidden"]), select, textarea'));
|
||||
this.formEls.forEach(formEl => {
|
||||
formEl.addEventListener('focus', (event: Event) => {
|
||||
this.el.classList.add('active');
|
||||
}, false);
|
||||
|
||||
formEl.addEventListener('blur', (event: Event) => {
|
||||
this.el.classList.remove('active');
|
||||
}, false);
|
||||
});
|
||||
}
|
||||
|
||||
@HostListener('click', ['$event']) onClick(event: Event) {
|
||||
const target = event.target as HTMLElement;
|
||||
if (target !== this.el && !target.classList.contains('progress') &&
|
||||
!target.classList.contains('progress-bar')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.formEls.length > 0) {
|
||||
const formEl = (this.formEls[0] as HTMLElement);
|
||||
if (formEl.tagName.toLowerCase() === 'input') {
|
||||
const inputEl = (formEl as HTMLInputElement);
|
||||
if (inputEl.type != null && inputEl.type.toLowerCase() === 'checkbox') {
|
||||
inputEl.click();
|
||||
return;
|
||||
}
|
||||
}
|
||||
formEl.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
20
angular/src/directives/fallback-src.directive.ts
Normal file
20
angular/src/directives/fallback-src.directive.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import {
|
||||
Directive,
|
||||
ElementRef,
|
||||
HostListener,
|
||||
Input,
|
||||
} from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[appFallbackSrc]',
|
||||
})
|
||||
export class FallbackSrcDirective {
|
||||
@Input('appFallbackSrc') appFallbackSrc: string;
|
||||
|
||||
constructor(private el: ElementRef) {
|
||||
}
|
||||
|
||||
@HostListener('error') onError() {
|
||||
this.el.nativeElement.src = this.appFallbackSrc;
|
||||
}
|
||||
}
|
||||
37
angular/src/directives/input-verbatim.directive.ts
Normal file
37
angular/src/directives/input-verbatim.directive.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import {
|
||||
Directive,
|
||||
ElementRef,
|
||||
Input,
|
||||
Renderer2,
|
||||
} from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[appInputVerbatim]',
|
||||
})
|
||||
export class InputVerbatimDirective {
|
||||
@Input() set appInputVerbatim(condition: boolean | string) {
|
||||
this.disableComplete = condition === '' || condition === true;
|
||||
}
|
||||
|
||||
private disableComplete: boolean;
|
||||
|
||||
constructor(private el: ElementRef, private renderer: Renderer2) { }
|
||||
|
||||
ngOnInit() {
|
||||
if (this.disableComplete && !this.el.nativeElement.hasAttribute('autocomplete')) {
|
||||
this.renderer.setAttribute(this.el.nativeElement, 'autocomplete', 'off');
|
||||
}
|
||||
if (!this.el.nativeElement.hasAttribute('autocapitalize')) {
|
||||
this.renderer.setAttribute(this.el.nativeElement, 'autocapitalize', 'none');
|
||||
}
|
||||
if (!this.el.nativeElement.hasAttribute('autocorrect')) {
|
||||
this.renderer.setAttribute(this.el.nativeElement, 'autocorrect', 'none');
|
||||
}
|
||||
if (!this.el.nativeElement.hasAttribute('spellcheck')) {
|
||||
this.renderer.setAttribute(this.el.nativeElement, 'spellcheck', 'false');
|
||||
}
|
||||
if (!this.el.nativeElement.hasAttribute('inputmode')) {
|
||||
this.renderer.setAttribute(this.el.nativeElement, 'inputmode', 'verbatim');
|
||||
}
|
||||
}
|
||||
}
|
||||
41
angular/src/directives/select-copy.directive.ts
Normal file
41
angular/src/directives/select-copy.directive.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import {
|
||||
Directive,
|
||||
ElementRef,
|
||||
HostListener,
|
||||
} from '@angular/core';
|
||||
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
|
||||
@Directive({
|
||||
selector: '[appSelectCopy]',
|
||||
})
|
||||
export class SelectCopyDirective {
|
||||
constructor(private el: ElementRef, private platformUtilsService: PlatformUtilsService) { }
|
||||
|
||||
@HostListener('copy') onCopy() {
|
||||
if (window == null) {
|
||||
return;
|
||||
}
|
||||
let copyText = '';
|
||||
const selection = window.getSelection();
|
||||
for (let i = 0; i < selection.rangeCount; i++) {
|
||||
const range = selection.getRangeAt(i);
|
||||
const text = range.toString();
|
||||
|
||||
// The selection should only contain one line of text. In some cases however, the
|
||||
// selection contains newlines and space characters from the indentation of following
|
||||
// sibling nodes. To avoid copying passwords containing trailing newlines and spaces
|
||||
// that aren't part of the password, the selection has to be trimmed.
|
||||
let stringEndPos = text.length;
|
||||
const newLinePos = text.search(/(?:\r\n|\r|\n)/);
|
||||
if (newLinePos > -1) {
|
||||
const otherPart = text.substr(newLinePos).trim();
|
||||
if (otherPart === '') {
|
||||
stringEndPos = newLinePos;
|
||||
}
|
||||
}
|
||||
copyText += text.substring(0, stringEndPos);
|
||||
}
|
||||
this.platformUtilsService.copyToClipboard(copyText, { window: window });
|
||||
}
|
||||
}
|
||||
13
angular/src/directives/stop-click.directive.ts
Normal file
13
angular/src/directives/stop-click.directive.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import {
|
||||
Directive,
|
||||
HostListener,
|
||||
} from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[appStopClick]',
|
||||
})
|
||||
export class StopClickDirective {
|
||||
@HostListener('click', ['$event']) onClick($event: MouseEvent) {
|
||||
$event.preventDefault();
|
||||
}
|
||||
}
|
||||
13
angular/src/directives/stop-prop.directive.ts
Normal file
13
angular/src/directives/stop-prop.directive.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import {
|
||||
Directive,
|
||||
HostListener,
|
||||
} from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[appStopProp]',
|
||||
})
|
||||
export class StopPropDirective {
|
||||
@HostListener('click', ['$event']) onClick($event: MouseEvent) {
|
||||
$event.stopPropagation();
|
||||
}
|
||||
}
|
||||
54
angular/src/directives/true-false-value.directive.ts
Normal file
54
angular/src/directives/true-false-value.directive.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import {
|
||||
Directive,
|
||||
ElementRef,
|
||||
forwardRef,
|
||||
HostListener,
|
||||
Input,
|
||||
Renderer2,
|
||||
} from '@angular/core';
|
||||
import {
|
||||
ControlValueAccessor,
|
||||
NgControl,
|
||||
NG_VALUE_ACCESSOR,
|
||||
} from '@angular/forms';
|
||||
|
||||
// ref: https://juristr.com/blog/2018/02/ng-true-value-directive/
|
||||
@Directive({
|
||||
selector: 'input[type=checkbox][appTrueFalseValue]',
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => TrueFalseValueDirective),
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class TrueFalseValueDirective implements ControlValueAccessor {
|
||||
@Input() trueValue = true;
|
||||
@Input() falseValue = false;
|
||||
|
||||
constructor(private elementRef: ElementRef, private renderer: Renderer2) { }
|
||||
|
||||
@HostListener('change', ['$event'])
|
||||
onHostChange(ev: any) {
|
||||
this.propagateChange(ev.target.checked ? this.trueValue : this.falseValue);
|
||||
}
|
||||
|
||||
writeValue(obj: any): void {
|
||||
if (obj === this.trueValue) {
|
||||
this.renderer.setProperty(this.elementRef.nativeElement, 'checked', true);
|
||||
} else {
|
||||
this.renderer.setProperty(this.elementRef.nativeElement, 'checked', false);
|
||||
}
|
||||
}
|
||||
|
||||
registerOnChange(fn: any): void {
|
||||
this.propagateChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: any): void { /* nothing */ }
|
||||
|
||||
setDisabledState?(isDisabled: boolean): void { /* nothing */ }
|
||||
|
||||
private propagateChange = (_: any) => { /* nothing */ };
|
||||
}
|
||||
Reference in New Issue
Block a user