Skip to content

Instantly share code, notes, and snippets.

@FFKL
Last active May 5, 2020 19:13
Show Gist options
  • Select an option

  • Save FFKL/0a15b5421393171426af166437a943b5 to your computer and use it in GitHub Desktop.

Select an option

Save FFKL/0a15b5421393171426af166437a943b5 to your computer and use it in GitHub Desktop.
Numbers range component
<sv-numbers-range>
<input svThreshold placeholder="Min price" formControlName="min" type="text">
<input svThreshold placeholder="Max price" formControlName="max" type="text">
</sv-numbers-range>
import {
AfterContentInit,
ChangeDetectionStrategy,
Component,
ContentChildren,
OnDestroy,
QueryList
} from '@angular/core';
import { ThresholdDirective } from './threshold.directive';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'sv-numbers-range',
template: '<ng-content></ng-content>',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class NumbersRangeComponent implements AfterContentInit, OnDestroy {
@ContentChildren(ThresholdDirective) thresholds!: QueryList<ThresholdDirective>;
private unsubscribe$ = new Subject<void>();
ngAfterContentInit() {
if (this.thresholds.length !== 2) throw new Error('Numbers range should include 2 inputs thresholds inside');
const [minInput, maxInput] = this.thresholds.toArray();
this.subscribeOnInputValue(minInput, maxInput, (min, max) => min <= max);
this.subscribeOnInputValue(maxInput, minInput, (max, min) => max >= min);
}
ngOnDestroy() {
this.unsubscribe$.next();
this.unsubscribe$.complete();
}
private subscribeOnInputValue(target: ThresholdDirective, other: ThresholdDirective, isValidFn: (t: number, o: number) => boolean) {
target.valueChanged$.pipe(
takeUntil(this.unsubscribe$)
).subscribe(targetVal => {
const otherVal = other.getValue();
if (otherVal && !isValidFn(targetVal, otherVal)) {
target.clearValue();
}
});
}
}
import { Directive, ElementRef, Renderer2, HostListener, Inject } from '@angular/core';
import { Subject } from 'rxjs';
import { DOCUMENT } from '@angular/common';
@Directive({
selector: 'input[svThreshold]'
})
export class ThresholdDirective {
constructor(private ref: ElementRef, private renderer: Renderer2, @Inject(DOCUMENT) private document: Document) {}
valueChanged$: Subject<any> = new Subject();
private navigationKeys = [
'Backspace',
'Delete',
'Tab',
'Escape',
'Enter',
'Home',
'End',
'ArrowLeft',
'ArrowRight',
'Clear',
'Copy',
'Paste'
];
@HostListener('keydown', ['$event'])
onInput(e: KeyboardEvent) {
if (this.isAllowedNavigationKey(e) || this.isAllowedShortcut(e)) return;
if (e.key === ' ' || isNaN(Number(e.key))) {
e.preventDefault();
}
}
@HostListener('blur', ['$event.target.value'])
onBlur(val: string) {
this.valueChanged$.next(Number(val));
}
@HostListener('paste', ['$event'])
onPaste(e: ClipboardEvent) {
e.preventDefault();
const pastedInput: string = e.clipboardData!
.getData('text/plain')
.replace(/\D/g, '');
this.insertText(pastedInput);
}
@HostListener('drop', ['$event'])
onDrop(event: DragEvent) {
event.preventDefault();
this.ref.nativeElement.focus();
const textData = event.dataTransfer!
.getData('text/plain').replace(/\D/g, '');
this.insertText(textData);
}
clearValue() {
this.renderer.setProperty(this.ref.nativeElement, 'value', null);
}
getValue(): number {
return Number(this.ref.nativeElement.value);
}
private insertText(data: string) {
this.document.execCommand('insertText', false, data);
}
private isAllowedNavigationKey(e: KeyboardEvent) {
return this.navigationKeys.includes(e.key);
}
private isAllowedShortcut(e: KeyboardEvent) {
return (e.key === 'a' && e.ctrlKey) || // Allow: Ctrl+A
(e.key === 'c' && e.ctrlKey) || // Allow: Ctrl+C
(e.key === 'v' && e.ctrlKey) || // Allow: Ctrl+V
(e.key === 'x' && e.ctrlKey) || // Allow: Ctrl+X
(e.key === 'a' && e.metaKey) || // Allow: Cmd+A (Mac)
(e.key === 'c' && e.metaKey) || // Allow: Cmd+C (Mac)
(e.key === 'v' && e.metaKey) || // Allow: Cmd+V (Mac)
(e.key === 'x' && e.metaKey) // Allow: Cmd+X (Mac)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment