diff --git a/cypress/integration/clipboard-events.spec.ts b/cypress/integration/clipboard-events.spec.ts index dbba8d5..745db78 100644 --- a/cypress/integration/clipboard-events.spec.ts +++ b/cypress/integration/clipboard-events.spec.ts @@ -135,7 +135,7 @@ describe('Copy & Paste', () => { cy.get('#digit-only-decimal').clear(); }); - it.only('s', ()=> { + it('s', ()=> { const dt = new DataTransfer(); dt.setData('text/plain', 'abc1.0s.1'); const pasteEvent = new ClipboardEvent('paste', { @@ -264,4 +264,28 @@ describe('Copy & Paste', () => { cy.get('#decimal-number').clear(); }); + + it('should accept negative sign (only once)', () => { + const dt = new DataTransfer(); + dt.setData('text/plain', '-123'); + const pasteEvent = new ClipboardEvent('paste', { + clipboardData: dt, + bubbles: true, + cancelable: true, + }); + + cy.get('#negative-digit-only').clear(); + + cy.get('#negative-digit-only').then(($el) => { + $el[0].dispatchEvent(pasteEvent); + cy.get('#negative-digit-only').should('have.value', '-123'); + }); + + cy.get('#negative-digit-only').then(($el) => { + $el[0].dispatchEvent(pasteEvent); + cy.get('#negative-digit-only').should('have.value', '-123123'); + }); + + cy.get('#negative-digit-only').clear(); + }); }); diff --git a/projects/uiowa/digit-only/src/lib/digit-only.directive.ts b/projects/uiowa/digit-only/src/lib/digit-only.directive.ts index edba777..256ab65 100644 --- a/projects/uiowa/digit-only/src/lib/digit-only.directive.ts +++ b/projects/uiowa/digit-only/src/lib/digit-only.directive.ts @@ -12,6 +12,7 @@ import { }) export class DigitOnlyDirective implements OnChanges { private hasDecimalPoint = false; + private hasNegativeSign = false; private navigationKeys = [ 'Backspace', 'Delete', @@ -29,6 +30,8 @@ export class DigitOnlyDirective implements OnChanges { @Input() decimal = false; @Input() decimalSeparator = '.'; + @Input() allowNegatives= false; + @Input() negativeSign = '-'; @Input() min = -Infinity; @Input() max = Infinity; @Input() pattern?: string | RegExp; @@ -58,7 +61,8 @@ export class DigitOnlyDirective implements OnChanges { @HostListener('beforeinput', ['$event']) onBeforeInput(e: InputEvent): any { if (isNaN(Number(e.data))) { - if (e.data === this.decimalSeparator) { + if (e.data === this.decimalSeparator + || (e.data === this.negativeSign && this.allowNegatives)) { return; // go on } e.preventDefault(); @@ -97,6 +101,17 @@ export class DigitOnlyDirective implements OnChanges { } } + if (e.key === this.negativeSign && this.allowNegatives) { + newValue = this.forecastValue(e.key); + if (newValue.charAt(0) !== this.negativeSign || newValue.split(this.negativeSign).length > 2) { + e.preventDefault(); + return; + } else { + this.hasNegativeSign = newValue.split(this.negativeSign).length > -1; + return; + } + } + // Ensure that it is a number and stop the keypress if (e.key === ' ' || isNaN(Number(e.key))) { e.preventDefault(); @@ -145,6 +160,11 @@ export class DigitOnlyDirective implements OnChanges { private pasteData(pastedContent: string): void { const sanitizedContent = this.sanitizeInput(pastedContent); + if (sanitizedContent.includes(this.negativeSign) + && this.hasNegativeSign + && !this.getSelection().includes(this.negativeSign)) { + return; + } const pasted = document.execCommand('insertText', false, sanitizedContent); if (!pasted) { if (this.inputElement.setRangeText) { @@ -174,6 +194,7 @@ export class DigitOnlyDirective implements OnChanges { this.hasDecimalPoint = this.inputElement.value.indexOf(this.decimalSeparator) > -1; } + this.hasNegativeSign = this.inputElement.value.indexOf(this.negativeSign) > -1; } // The following 2 methods were added from the below article for browsers that do not support setRangeText @@ -206,22 +227,27 @@ export class DigitOnlyDirective implements OnChanges { private sanitizeInput(input: string): string { let result = ''; + let regex; if (this.decimal && this.isValidDecimal(input)) { - const regex = new RegExp(`[^0-9${this.decimalSeparator}]`, 'g'); - result = input.replace(regex, ''); + regex = new RegExp(`${this.getNegativeSignRegExp()}[^0-9${this.decimalSeparator}]`, 'g'); } else { - result = input.replace(/[^0-9]/g, ''); + regex = new RegExp(`${this.getNegativeSignRegExp()}[^0-9]`, 'g'); } + result = input.replace(regex, ''); const maxLength = this.inputElement.maxLength; if (maxLength > 0) { // the input element has maxLength limit - const allowedLength = maxLength - this.inputElement.value.length; + const allowedLength = maxLength - this.inputElement.value.length + (result.includes(`${this.negativeSign}`) ? 1 : 0); result = allowedLength > 0 ? result.substring(0, allowedLength) : ''; } return result; } + private getNegativeSignRegExp() : string { + return this.allowNegatives && (!this.hasNegativeSign || this.getSelection().includes(this.negativeSign)) ? `(?!^${this.negativeSign})` : ''; + } + private isValidDecimal(string: string): boolean { if (!this.hasDecimalPoint) { return string.split(this.decimalSeparator).length <= 2; @@ -247,11 +273,8 @@ export class DigitOnlyDirective implements OnChanges { const selectionStart = this.inputElement.selectionStart ?? 0; const selectionEnd = this.inputElement.selectionEnd ?? 0; const oldValue = this.inputElement.value; - const selection = oldValue.substring(selectionStart, selectionEnd); - return selection - ? oldValue.replace(selection, key) - : oldValue.substring(0, selectionStart) + - key + - oldValue.substring(selectionStart); + return oldValue.substring(0, selectionStart) + + key + + oldValue.substring(selectionEnd); } } diff --git a/src/app/digit-only-demos/digit-only-demos.component.html b/src/app/digit-only-demos/digit-only-demos.component.html index 61df59f..7f21088 100644 --- a/src/app/digit-only-demos/digit-only-demos.component.html +++ b/src/app/digit-only-demos/digit-only-demos.component.html @@ -16,6 +16,9 @@
digitOnly
directive