diff --git a/README.md b/README.md index a5a2705d..4d60a6bb 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,9 @@ String or Null, default `'PT0S'`. Value to be used when the duration is 0. Since [the specification](https://en.wikipedia.org/wiki/ISO_8601#Durations) says that `'PT0S'` or `'P0D'` are both valid, you are allowed to change this value. You can also pass just `null`. +#### showNegative +Boolean, default `false`. Sets up the option for negative and positive durations. + #### showButtons Boolean, default `true`. Shows the up and down buttons. diff --git a/package-lock.json b/package-lock.json index 6dee1062..bfe6ea22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "ngx-duration-picker", - "version": "1.2.1", + "version": "1.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/src/app/app.component.html b/src/app/app.component.html index 8e015113..e0432c8b 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -50,6 +50,30 @@ + +
+ +
+
+
Negative Duration
+ + + +
+
+
+ {{ myNegativeValue }} +
+ +
+ +
+ +
+
diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 6dfea6d8..c35f85d4 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -9,6 +9,8 @@ import { FormControl, FormGroup } from '@angular/forms'; export class AppComponent implements OnInit { myValue; + myNegativeValue; + showNegative = true; disabled = false; form: FormGroup; diff --git a/src/app/duration-picker/duration-picker.component.css b/src/app/duration-picker/duration-picker.component.css index 7bf5cda1..585bd2a4 100644 --- a/src/app/duration-picker/duration-picker.component.css +++ b/src/app/duration-picker/duration-picker.component.css @@ -18,6 +18,14 @@ input { box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); } +input[type="checkbox"] { + display: none; +} + +.status-container { + margin-right: 5px; +} + a.btn { padding: 1px; } diff --git a/src/app/duration-picker/duration-picker.component.html b/src/app/duration-picker/duration-picker.component.html index ac4ab610..b39b20ce 100644 --- a/src/app/duration-picker/duration-picker.component.html +++ b/src/app/duration-picker/duration-picker.component.html @@ -2,6 +2,7 @@ + Y M W @@ -12,6 +13,8 @@ + + @@ -56,6 +59,13 @@ + + + + + + + @@ -90,6 +100,8 @@ + + diff --git a/src/app/duration-picker/duration-picker.component.spec.ts b/src/app/duration-picker/duration-picker.component.spec.ts index 1a214f12..bd80c068 100644 --- a/src/app/duration-picker/duration-picker.component.spec.ts +++ b/src/app/duration-picker/duration-picker.component.spec.ts @@ -59,11 +59,47 @@ describe('DurationPickerComponent', () => { expect(component.generate()).toBe('P9MT10M'); }); + it('generate() should correctly generate the negative duration values', () => { + set(component, 1, 2, 3, 4, 5, 6, 7, true); + expect(component.generate()).toBe('-P1Y2M3W4DT5H6M7S'); + + set(component, '1', '2', '3', '4', '5', '6', '7', true); + expect(component.generate()).toBe('-P1Y2M3W4DT5H6M7S'); + + set(component, 1, 0, 0, 0, 0, 0, 0, true); + expect(component.generate()).toBe('-P1Y'); + + set(component, 0, 123, 0, 0, 0, 0, 0, true); + expect(component.generate()).toBe('-P123M'); + + set(component, 0, 0, 3, 0, 0, 0, 0, true); + expect(component.generate()).toBe('-P3W'); + + set(component, 0, 0, 0, 2, 0, 0, 0, true); + expect(component.generate()).toBe('-P2D'); + + set(component, 0, 0, 0, 0, 5, 0, 0, true); + expect(component.generate()).toBe('-PT5H'); + + set(component, 0, 0, 0, 0, 0, 9, 0, true); + expect(component.generate()).toBe('-PT9M'); + + set(component, 0, 0, 0, 0, 0, 0, 10, true); + expect(component.generate()).toBe('-PT10S'); + + set(component, 0, 9, 0, 0, 0, 10, 0, true); + expect(component.generate()).toBe('-P9MT10M'); + }); + it('generate() should correctly set the zero value according to the configuration', () => { set(component, 0, 0, 0, 0, 0, 0, 0); expect(component.generate()).toBe('PT0S'); + set(component, 0, 0, 0, 0, 0, 0, 0, true); + + expect(component.generate()).toBe('PT0S'); + component.config.zeroValue = null; expect(component.generate()).toBe(null); @@ -90,6 +126,20 @@ describe('DurationPickerComponent', () => { expect(component.hours).toBe(5); expect(component.minutes).toBe(6); expect(component.seconds).toBe(7); + expect(component.negative).toBe(false); + + component.value = '-P1Y2M3W4DT5H6M7S'; + + component.parse(); + + expect(component.years).toBe(1); + expect(component.months).toBe(2); + expect(component.weeks).toBe(3); + expect(component.days).toBe(4); + expect(component.hours).toBe(5); + expect(component.minutes).toBe(6); + expect(component.seconds).toBe(7); + expect(component.negative).toBe(true); }); it('parse() should do nothing if the value is null', () => { @@ -173,6 +223,7 @@ function set( hours, minutes, seconds, + negative = false ) { component.years = years; component.months = months; @@ -181,4 +232,6 @@ function set( component.hours = hours; component.minutes = minutes; component.seconds = seconds; + component.config.showNegative = negative; + component.negative = negative; } diff --git a/src/app/duration-picker/duration-picker.component.ts b/src/app/duration-picker/duration-picker.component.ts index 90f2dfdf..e72be244 100644 --- a/src/app/duration-picker/duration-picker.component.ts +++ b/src/app/duration-picker/duration-picker.component.ts @@ -46,8 +46,9 @@ export class DurationPickerComponent implements OnInit, ControlValueAccessor { this._disabled = disabled; } - regex: RegExp = /^P(?!$)(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(?=\d+[HMS])(\d+H)?(\d+M)?(\d+S)?)?$/; + regex: RegExp = /^[\+\-]?P(?!$)(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(?=\d+[HMS])(\d+H)?(\d+M)?(\d+S)?)?$/; + private _negative = false; private _years = 0; private _months = 0; private _weeks = 0; @@ -57,6 +58,7 @@ export class DurationPickerComponent implements OnInit, ControlValueAccessor { private _seconds = 0; config: DurationPickerOptions = { + showNegative: false, showButtons : true, showPreview : true, showLetters : true, @@ -70,6 +72,12 @@ export class DurationPickerComponent implements OnInit, ControlValueAccessor { zeroValue : 'PT0S', }; + get negative() { return this._negative; } + set negative(value) { + this._negative = value; + this.emitNewValue(); + } + get years() { return this._years; } set years(value) { value = this.parseNumber(value) > 0 ? value : 0; @@ -159,6 +167,7 @@ export class DurationPickerComponent implements OnInit, ControlValueAccessor { return; } + this._negative = match[0].startsWith('-'); this._years = this.parseNumber(match[1]); this._months = this.parseNumber(match[2]); this._weeks = this.parseNumber(match[3]); @@ -175,6 +184,10 @@ export class DurationPickerComponent implements OnInit, ControlValueAccessor { generate(): string { let output = 'P'; + if (this.config.showNegative && this.negative) { + output = '-' + output; + } + if (this.config.showYears && this.years) { output += `${this.years}Y`; } @@ -206,7 +219,7 @@ export class DurationPickerComponent implements OnInit, ControlValueAccessor { } // if all values are empty, just output null - if (output === 'P') { + if (output === 'P' || output === '-P') { output = this.config.zeroValue; } diff --git a/src/app/duration-picker/duration-picker.d.ts b/src/app/duration-picker/duration-picker.d.ts index a885a6d5..1b8df31f 100644 --- a/src/app/duration-picker/duration-picker.d.ts +++ b/src/app/duration-picker/duration-picker.d.ts @@ -1,4 +1,5 @@ export interface DurationPickerOptions { + showNegative: boolean; showButtons: boolean; showPreview: boolean; showLetters: boolean;