Create a new scounting component to manage a counter.

Much simplifies the entry component by moving common logic into a
subcomponent.

Change-Id: I260c895a149698a4404bdfa558ecd5d2f5f6f730
Signed-off-by: Alex Perry <alex.perry96@gmail.com>
diff --git a/scouting/www/counter_button/BUILD b/scouting/www/counter_button/BUILD
new file mode 100644
index 0000000..1dbcdfc
--- /dev/null
+++ b/scouting/www/counter_button/BUILD
@@ -0,0 +1,21 @@
+load("@npm//@bazel/typescript:index.bzl", "ts_library")
+
+ts_library(
+    name = "counter_button",
+    srcs = [
+        "counter_button.component.ts",
+        "counter_button.module.ts",
+    ],
+    angular_assets = [
+        "counter_button.component.css",
+        "counter_button.ng.html",
+    ],
+    compiler = "//tools:tsc_wrapped_with_angular",
+    target_compatible_with = ["@platforms//cpu:x86_64"],
+    use_angular_plugin = True,
+    visibility = ["//visibility:public"],
+    deps = [
+        "@npm//@angular/common",
+        "@npm//@angular/core",
+    ],
+)
diff --git a/scouting/www/counter_button/counter_button.component.css b/scouting/www/counter_button/counter_button.component.css
new file mode 100644
index 0000000..39faea8
--- /dev/null
+++ b/scouting/www/counter_button/counter_button.component.css
@@ -0,0 +1,11 @@
+:host {
+  display: flex;
+  align-items: stretch;
+  flex-direction: column;
+  text-align: center;
+}
+
+* {
+    padding: 10px;
+}
+
diff --git a/scouting/www/counter_button/counter_button.component.ts b/scouting/www/counter_button/counter_button.component.ts
new file mode 100644
index 0000000..fa84dc6
--- /dev/null
+++ b/scouting/www/counter_button/counter_button.component.ts
@@ -0,0 +1,17 @@
+import {Component, Input, Output, EventEmitter} from '@angular/core';
+
+@Component({
+  selector: 'frc971-counter-button',
+  templateUrl: './counter_button.ng.html',
+  styleUrls: ['./counter_button.component.css'],
+})
+export class CounterButton {
+  @Input() value: number = 0;
+  @Output() valueChange = new EventEmitter<number>();
+
+  update(delta: number) {
+    this.value = Math.max(this.value + delta, 0);
+
+    this.valueChange.emit(this.value);
+  }
+}
diff --git a/scouting/www/counter_button/counter_button.module.ts b/scouting/www/counter_button/counter_button.module.ts
new file mode 100644
index 0000000..25bafcb
--- /dev/null
+++ b/scouting/www/counter_button/counter_button.module.ts
@@ -0,0 +1,10 @@
+import {NgModule} from '@angular/core';
+
+import {CounterButton} from './counter_button.component';
+
+@NgModule({
+  declarations: [CounterButton],
+  exports: [CounterButton],
+})
+export class CounterButtonModule {
+}
diff --git a/scouting/www/counter_button/counter_button.ng.html b/scouting/www/counter_button/counter_button.ng.html
new file mode 100644
index 0000000..4abf415
--- /dev/null
+++ b/scouting/www/counter_button/counter_button.ng.html
@@ -0,0 +1,4 @@
+<h4><ng-content></ng-content></h4>
+<button (click)="update(1)" class="btn btn-secondary btn-block">+</button>
+<h3>{{value}}</h3>
+<button (click)="update(-1)" class="btn btn-secondary btn-block">-</button>
diff --git a/scouting/www/entry/BUILD b/scouting/www/entry/BUILD
index 2c394de..17eed23 100644
--- a/scouting/www/entry/BUILD
+++ b/scouting/www/entry/BUILD
@@ -19,6 +19,7 @@
         "//scouting/webserver/requests/messages:error_response_ts_fbs",
         "//scouting/webserver/requests/messages:submit_data_scouting_response_ts_fbs",
         "//scouting/webserver/requests/messages:submit_data_scouting_ts_fbs",
+        "//scouting/www/counter_button",
         "@com_github_google_flatbuffers//ts:flatbuffers_ts",
         "@npm//@angular/common",
         "@npm//@angular/core",
diff --git a/scouting/www/entry/entry.component.css b/scouting/www/entry/entry.component.css
index 76a3c29..e6f81b3 100644
--- a/scouting/www/entry/entry.component.css
+++ b/scouting/www/entry/entry.component.css
@@ -2,13 +2,6 @@
     padding: 10px;
 }
 
-.center-column {
-  display: flex;
-  align-items: stretch;
-  flex-direction: column;
-  text-align: center;
-}
-
 .buttons {
   display: flex;
   justify-content: space-between;
diff --git a/scouting/www/entry/entry.component.ts b/scouting/www/entry/entry.component.ts
index fb4a1b1..af0ce97 100644
--- a/scouting/www/entry/entry.component.ts
+++ b/scouting/www/entry/entry.component.ts
@@ -100,30 +100,6 @@
       }
     }
 
-    adjustAutoUpper(by: number) {
-        this.autoUpperShotsMade = Math.max(0, this.autoUpperShotsMade + by);
-    }
-
-    adjustAutoLower(by: number) {
-        this.autoLowerShotsMade = Math.max(0, this.autoLowerShotsMade + by);
-    }
-
-    adjustAutoMissed(by: number) {
-        this.autoShotsMissed = Math.max(0, this.autoShotsMissed + by);
-    }
-
-    adjustTeleUpper(by: number) {
-        this.teleUpperShotsMade = Math.max(0, this.teleUpperShotsMade + by);
-    }
-
-    adjustTeleLower(by: number) {
-        this.teleLowerShotsMade = Math.max(0, this.teleLowerShotsMade + by);
-    }
-
-    adjustTeleMissed(by: number) {
-        this.teleShotsMissed = Math.max(0, this.teleShotsMissed + by);
-    }
-
     async submitDataScouting() {
         const builder = new flatbuffer_builder.Builder() as unknown as flatbuffers.Builder;
         SubmitDataScouting.startSubmitDataScouting(builder);
diff --git a/scouting/www/entry/entry.module.ts b/scouting/www/entry/entry.module.ts
index 35ecd26..b4d81c0 100644
--- a/scouting/www/entry/entry.module.ts
+++ b/scouting/www/entry/entry.module.ts
@@ -1,12 +1,14 @@
 import {NgModule} from '@angular/core';
 import {CommonModule} from '@angular/common';
-import {EntryComponent} from './entry.component';
 import {FormsModule} from '@angular/forms';
 
+import {CounterButtonModule} from '../counter_button/counter_button.module';
+import {EntryComponent} from './entry.component';
+
 @NgModule({
   declarations: [EntryComponent],
   exports: [EntryComponent],
-  imports: [CommonModule, FormsModule],
+  imports: [CommonModule, FormsModule, CounterButtonModule],
 })
 export class EntryModule {
 }
diff --git a/scouting/www/entry/entry.ng.html b/scouting/www/entry/entry.ng.html
index b5783d2..bb4c85a 100644
--- a/scouting/www/entry/entry.ng.html
+++ b/scouting/www/entry/entry.ng.html
@@ -44,26 +44,9 @@
             </form>
         </div>
         <div class="row justify-content-center">
-            <span class="col-4 center-column">
-                <h4>Upper</h4>
-                <button (click)="adjustAutoUpper(1)" class="btn btn-secondary btn-block">+</button>
-                <h3>{{autoUpperShotsMade}}</h3>
-                <button (click)="adjustAutoUpper(-1)" class="btn btn-secondary btn-block">-</button>
-            </span>
-
-            <span class="col-4 center-column">
-                <h4>Lower</h4>
-                <button (click)="adjustAutoLower(1)" class="btn btn-secondary btn-block">+</button>
-                <h3>{{autoLowerShotsMade}}</h3>
-                <button (click)="adjustAutoLower(-1)" class="btn btn-secondary btn-block">-</button>
-            </span>
-
-            <span class="col-4 center-column">
-                <h4>Missed</h4>
-                <button (click)="adjustAutoMissed(1)" class="btn btn-secondary btn-block">+</button>
-                <h3>{{autoShotsMissed}}</h3>
-                <button (click)="adjustAutoMissed(-1)" class="btn btn-secondary btn-block">-</button>
-            </span>
+            <frc971-counter-button class="col-4" [(value)]="autoUpperShotsMade">Upper</frc971-counter-button>
+            <frc971-counter-button class="col-4" [(value)]="autoLowerShotsMade">Lower</frc971-counter-button>
+            <frc971-counter-button class="col-4" [(value)]="autoShotsMissed">Missed</frc971-counter-button>
         </div>
         <div class="buttons">
           <!-- hack to right align the next button -->
@@ -74,26 +57,9 @@
 
     <div *ngSwitchCase="'TeleOp'" id="teleop" class="container-fluid">
         <div class="row justify-content-center">
-            <span class="col-4 center-column">
-                <h4>Upper</h4>
-                <button (click)="adjustTeleUpper(1)" class="btn btn-secondary btn-block">+</button>
-                <h3>{{teleUpperShotsMade}}</h3>
-                <button (click)="adjustTeleUpper(-1)" class="btn btn-secondary btn-block">-</button>
-            </span>
-
-            <span class="col-4 center-column">
-                <h4>Lower</h4>
-                <button (click)="adjustTeleLower(1)" class="btn btn-secondary btn-block">+</button>
-                <h3>{{teleLowerShotsMade}}</h3>
-                <button (click)="adjustTeleLower(-1)" class="btn btn-secondary btn-block">-</button>
-            </span>
-
-            <span class="col-4 center-column">
-                <h4>Missed</h4>
-                <button (click)="adjustTeleMissed(1)" class="btn btn-secondary btn-block">+</button>
-                <h3>{{teleShotsMissed}}</h3>
-                <button (click)="adjustTeleMissed(-1)" class="btn btn-secondary btn-block">-</button>
-            </span>
+            <frc971-counter-button class="col-4" [(value)]="teleUpperShotsMade">Upper</frc971-counter-button>
+            <frc971-counter-button class="col-4" [(value)]="teleLowerShotsMade">Lower</frc971-counter-button>
+            <frc971-counter-button class="col-4" [(value)]="teleShotsMissed">Missed</frc971-counter-button>
         </div>
         <div class="buttons">
           <button class="btn btn-primary" (click)="prevSection()">Back</button>