From e0e847784da1539a6644fcc711a3bda2240f57ee Mon Sep 17 00:00:00 2001
From: Galen Charlton <gmc@equinoxinitiative.org>
Date: Mon, 25 Mar 2019 15:21:46 -0400
Subject: [PATCH] LP#1831780: various improvements to the Angular date-select
 widget

Styling
-------
- the widget is now narrower
- the widget now enables form validation styles; in particular,
  entry of an incorrectly-formatted date is now highlighted
- the calendar drop-down is now allowed to overflow the containing
  element when expanded, making it easier to embed the date
  selector in other controls
- the calendar button (and any material icons button that's part of
  an input group) now has the same default font size as main text,
  making the overall date-select look cleaner

API
---
- add a reset() method
- hitting enter in the text box can now triggers emitting date
  change events
- a new onCleared event is emitted if the suer hits enter on an
  empty input
- onChangeAsYmd() now pads month and day to two digits apiece,
  making the result conform to ISO 8601 and thus more easily
  plugged into queries.
- adds the following methods to retrieve the current date; these
  are meant to be used via local template references in parent
  templates:

  currentAsYmd()
  currentAsIso()
  currentAsDate()

Sponsored-by: MassLNC
Sponsored-by: Georgia Public Library Service
Sponsored-by: Indiana State Library
Sponsored-by: CW MARS
Sponsored-by: King County Library System

Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>
Signed-off-by: Bill Erickson <berickxx@gmail.com>
---
 .../share/date-select/date-select.component.css    |  3 ++
 .../share/date-select/date-select.component.html   |  6 ++-
 .../app/share/date-select/date-select.component.ts | 51 +++++++++++++++++++++-
 Open-ILS/src/eg2/src/styles.css                    |  4 ++
 4 files changed, 60 insertions(+), 4 deletions(-)
 create mode 100644 Open-ILS/src/eg2/src/app/share/date-select/date-select.component.css

diff --git a/Open-ILS/src/eg2/src/app/share/date-select/date-select.component.css b/Open-ILS/src/eg2/src/app/share/date-select/date-select.component.css
new file mode 100644
index 0000000000..277f1314ce
--- /dev/null
+++ b/Open-ILS/src/eg2/src/app/share/date-select/date-select.component.css
@@ -0,0 +1,3 @@
+.eg-date-select {
+    max-width: 11em;
+}
diff --git a/Open-ILS/src/eg2/src/app/share/date-select/date-select.component.html b/Open-ILS/src/eg2/src/app/share/date-select/date-select.component.html
index 7e65f7628e..41425339a3 100644
--- a/Open-ILS/src/eg2/src/app/share/date-select/date-select.component.html
+++ b/Open-ILS/src/eg2/src/app/share/date-select/date-select.component.html
@@ -1,16 +1,18 @@
 
-<div class="input-group">
-  <input
+<div class="input-group eg-date-select form-validated">
+  <input 
     class="form-control"
     ngbDatepicker
     #datePicker="ngbDatepicker"
     [attr.id]="domId.length ? domId : null"
     placeholder="yyyy-mm-dd"
     class="form-control"
+    container="body"
     name="{{fieldName}}"
     [disabled]="_disabled"
     [required]="required"
     [(ngModel)]="current"
+    (keyup.enter)="onDateEnter()"
     (dateSelect)="onDateSelect($event)"/>
   <div class="input-group-append">
     <button class="btn btn-outline-secondary" [disabled]="_disabled"
diff --git a/Open-ILS/src/eg2/src/app/share/date-select/date-select.component.ts b/Open-ILS/src/eg2/src/app/share/date-select/date-select.component.ts
index 625629026f..077058a6e8 100644
--- a/Open-ILS/src/eg2/src/app/share/date-select/date-select.component.ts
+++ b/Open-ILS/src/eg2/src/app/share/date-select/date-select.component.ts
@@ -9,7 +9,8 @@ import {NgbDateStruct} from '@ng-bootstrap/ng-bootstrap';
 
 @Component({
   selector: 'eg-date-select',
-  templateUrl: './date-select.component.html'
+  templateUrl: './date-select.component.html',
+  styleUrls: ['date-select.component.css']
 })
 export class DateSelectComponent implements OnInit {
 
@@ -30,11 +31,35 @@ export class DateSelectComponent implements OnInit {
     @Output() onChangeAsDate: EventEmitter<Date>;
     @Output() onChangeAsIso: EventEmitter<string>;
     @Output() onChangeAsYmd: EventEmitter<string>;
+    @Output() onCleared: EventEmitter<string>;
+
+    // convenience methods to access current selected date
+    currentAsYmd(): string {
+        if (this.current == null) { return null; }
+        if (!this.isValidDate(this.current)) { return null; }
+        return `${this.current.year}-${String(this.current.month).padStart(2, '0')}-${String(this.current.day).padStart(2, '0')}`;
+    }
+    currentAsIso(): string {
+        if (this.current == null) { return null; }
+        if (!this.isValidDate(this.current)) { return null; }
+        const ymd = `${this.current.year}-${String(this.current.month).padStart(2, '0')}-${String(this.current.day).padStart(2, '0')}`;
+        const date = this.localDateFromYmd(ymd);
+        const iso = date.toISOString();
+        return iso;
+    }
+    currentAsDate(): Date {
+        if (this.current == null) { return null; }
+        if (!this.isValidDate(this.current)) { return null; }
+        const ymd = `${this.current.year}-${String(this.current.month).padStart(2, '0')}-${String(this.current.day).padStart(2, '0')}`;
+        const date = this.localDateFromYmd(ymd);
+        return date;
+    }
 
     constructor() {
         this.onChangeAsDate = new EventEmitter<Date>();
         this.onChangeAsIso = new EventEmitter<string>();
         this.onChangeAsYmd = new EventEmitter<string>();
+        this.onCleared = new EventEmitter<string>();
     }
 
     ngOnInit() {
@@ -55,8 +80,21 @@ export class DateSelectComponent implements OnInit {
         }
     }
 
+    isValidDate(dt: NgbDateStruct): dt is NgbDateStruct {
+        return (<NgbDateStruct>dt).year !== undefined;
+    }
+
+    onDateEnter() {
+        if (this.current === null) {
+            this.onCleared.emit('cleared');
+        } else if (this.isValidDate(this.current)) {
+            this.onDateSelect(this.current);
+        }
+        // ignoring invalid input for now
+    }
+
     onDateSelect(evt) {
-        const ymd = `${evt.year}-${evt.month}-${evt.day}`;
+        const ymd = `${evt.year}-${String(evt.month).padStart(2, '0')}-${String(evt.day).padStart(2, '0')}`;
         const date = this.localDateFromYmd(ymd);
         const iso = date.toISOString();
         this.onChangeAsDate.emit(date);
@@ -71,6 +109,15 @@ export class DateSelectComponent implements OnInit {
         return new Date(
             Number(parts[0]), Number(parts[1]) - 1, Number(parts[2]));
     }
+
+    reset() {
+        this.current = {
+            year: null,
+            month: null,
+            day: null
+        };
+    }
+
 }
 
 
diff --git a/Open-ILS/src/eg2/src/styles.css b/Open-ILS/src/eg2/src/styles.css
index 10424f2722..0ffd6b576a 100644
--- a/Open-ILS/src/eg2/src/styles.css
+++ b/Open-ILS/src/eg2/src/styles.css
@@ -89,6 +89,10 @@ h5 {font-size: .95rem}
     line-height: inherit;
 }
 
+.input-group .mat-icon-in-button {
+    font-size: .88rem !important; /* useful for buttons that cuddle up with inputs */
+}
+
 .material-icons {
   /** default is 24px which is pretty chunky */
   font-size: 22px;
-- 
2.11.0