btc_horse/btc-UI/src/app/components/touch-pad-menu/touch-pad-menu.component.ts

497 lines
16 KiB
TypeScript
Executable File

import { Component, Input, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SelectionService } from '../selection.service/selection.service';
@Component({
selector: 'app-touch-pad-menu',
standalone: true,
imports: [CommonModule],
templateUrl: './touch-pad-menu.component.html',
styleUrls: ['./touch-pad-menu.component.css']
})
export class TouchPadMenuComponent implements OnInit {
@Input() ticketingActive: boolean = false;
public twoGroupLabels = ['FOR', 'QUI'];
public multiLegLabels = ['TRE', 'MJP', 'JKP'];
public allowedFieldLabels = ['WIN', 'SHP', 'THP', 'PLC', 'SHW'];
labels: string[] = [
'WIN', 'SHP', 'THP', 'PLC', 'SHW', 'FOR',
'QUI', 'TAN', 'EXA', 'WSP', 'TRE', 'MJP',
'JKP', 'SJP', '.'
];
numbers: number[] = Array.from({ length: 30 }, (_, i) => i + 1);
labelRowsFlat: string[] = [];
numbersFlat: number[] = [];
selectedLabel: string | null = null;
selectedNumbers: (number | string)[] = [];
padValue: string = '';
canPrint = false;
calculatorOpen = false;
calcDisplay = '';
maxRowsReached: boolean = false;
disabledLabels: string[] = ['SHW', 'SJP', '.'];
// ✅ Original TAN logic
tanGroupStage = 0;
tanGroups: number[][] = [[], [], []];
// ✅ FOR/QUI logic
isFirstGroupComplete = false;
firstGroup: number[] = [];
secondGroup: number[] = [];
// ✅ Multi-leg logic (TRE, MJP, JKP)
multiLegStage = 0;
multiLegGroups: number[][] = [[], [], [], [], []];
isBoxed: boolean = false;
// FIELD modal
fieldModalOpen = false;
fieldInput: string = '';
fieldFEntered = false;
constructor(private selectionService: SelectionService) {}
ngOnInit() {
this.labelRowsFlat = this.labelRows.flat();
this.numbersFlat = this.numberRows.flat();
this.selectionService.selections$.subscribe(selections => {
this.maxRowsReached = selections.length >= 5;
});
}
get labelRows() {
return this.chunk(this.labels, 3);
}
get numberRows() {
return this.chunk(this.numbers, 6);
}
get numericPadEnabled() {
return this.selectedLabel !== null && (this.selectedNumbers.length > 0 || this.selectedNumbers.includes('F'));
}
get showShashEnter(): boolean {
const label = this.selectedLabel || '';
const isBoxed = this.isBoxed;
if (['FOR', 'QUI', 'TAN'].includes(label) && isBoxed) {
return false;
}
const specialLabels = ['FOR', 'QUI', 'TAN', 'EXA', 'WSP', 'TRE', 'MJP', 'JKP', '.'];
return specialLabels.includes(label);
}
get isShashEnterDisabled(): boolean {
if (this.selectedLabel === 'TAN') {
// In box mode, shash enter is always disabled
if (this.isBoxed) {
return true;
}
return this.tanGroupStage >= 2 || this.tanGroups[this.tanGroupStage].length === 0;
} else if (this.multiLegLabels.includes(this.selectedLabel || '')) {
const maxLegs = this.getMaxLegs(this.selectedLabel || '');
return this.multiLegStage >= maxLegs - 1 || this.multiLegGroups[this.multiLegStage].length === 0;
} else if (this.twoGroupLabels.includes(this.selectedLabel || '')) {
return this.isFirstGroupComplete || this.firstGroup.length === 0;
}
return false;
}
get showBackspace(): boolean {
return this.selectedLabel !== null &&
(this.selectedNumbers.length > 0 || this.selectedNumbers.includes('F')) &&
this.padValue.length === 0;
}
get isBoxToggleDisabled(): boolean {
return this.selectedLabel !== null && this.allowedFieldLabels.includes(this.selectedLabel);
}
private chunk<T>(array: T[], size: number): T[][] {
return Array.from({ length: Math.ceil(array.length / size) }, (_, i) =>
array.slice(i * size, i * size + size)
);
}
isLabelDisabled(label: string): boolean {
return this.disabledLabels.includes(label);
}
selectLabel(label: string) {
this.selectedLabel = label;
this.selectedNumbers = [];
this.padValue = '';
this.canPrint = false;
this.isBoxed = false;
// ✅ Reset TAN
this.tanGroupStage = 0;
this.tanGroups = [[], [], []];
// ✅ Reset FOR/QUI
this.isFirstGroupComplete = false;
this.firstGroup = [];
this.secondGroup = [];
// ✅ Reset Multi-leg
this.multiLegStage = 0;
this.multiLegGroups = [[], [], [], [], []];
this.selectionService.updatePartial({ label });
}
selectNumber(number: number) {
if (!this.selectedLabel) return;
// TAN Box mode: freestyle selection with dash-separated format
if (this.selectedLabel === 'TAN' && this.isBoxed) {
if (!this.selectedNumbers.includes(number)) {
// Extract current numbers (excluding dashes)
const currentNumbers = this.selectedNumbers.filter(n => typeof n === 'number') as number[];
const allBoxed = [...currentNumbers, number];
// Split into 3 roughly equal groups for display consistency
const groupSize = Math.ceil(allBoxed.length / 3);
const group1 = allBoxed.slice(0, groupSize);
const group2 = allBoxed.slice(group1.length, group1.length + groupSize);
const group3 = allBoxed.slice(group1.length + group2.length);
const combined: (number | string)[] = [...group1];
if (group2.length) combined.push('-', ...group2);
if (group3.length) combined.push('-', ...group3);
this.selectedNumbers = combined;
this.selectionService.updatePartial({
numbers: [...this.selectedNumbers],
isBoxed: true,
label: 'TAN'
});
}
return;
}
// Original TAN logic (unboxed)
if (this.selectedLabel === 'TAN') {
if (!this.tanGroups[this.tanGroupStage].includes(number)) {
this.tanGroups[this.tanGroupStage].push(number);
const combined: (number | string)[] = [...this.tanGroups[0]];
if (this.tanGroupStage > 0) combined.push('-', ...this.tanGroups[1]);
if (this.tanGroupStage > 1) combined.push('-', ...this.tanGroups[2]);
this.selectedNumbers = combined;
this.selectionService.updatePartial({ numbers: [...this.selectedNumbers] });
}
return;
}
// Multi-leg logic (TRE, MJP, JKP)
if (this.multiLegLabels.includes(this.selectedLabel)) {
if (!this.multiLegGroups[this.multiLegStage].includes(number)) {
this.multiLegGroups[this.multiLegStage].push(number);
this.updateMultiLegSelection();
}
return;
}
// FOR/QUI logic
if (this.twoGroupLabels.includes(this.selectedLabel || '')) {
if (!this.isFirstGroupComplete) {
if (!this.firstGroup.includes(number)) {
this.firstGroup.push(number);
this.selectedNumbers = [...this.firstGroup];
}
} else {
if (!this.secondGroup.includes(number)) {
this.secondGroup.push(number);
this.selectedNumbers = [...this.firstGroup, '-', ...this.secondGroup];
}
}
this.selectionService.updatePartial({ numbers: [...this.selectedNumbers] });
return;
}
// Default single-number selection (WIN, SHP, THP, etc.)
if (!this.selectedNumbers.includes(number)) {
this.selectedNumbers.push(number);
this.selectionService.updatePartial({ numbers: [...this.selectedNumbers] });
}
}
private updateMultiLegSelection() {
const combined: (number | string)[] = [];
for (let i = 0; i <= this.multiLegStage; i++) {
if (i > 0) combined.push('/');
combined.push(...this.multiLegGroups[i]);
}
this.selectedNumbers = combined;
this.selectionService.updatePartial({ numbers: [...this.selectedNumbers] });
}
isNumberDisabled(number: number): boolean {
// For TAN Box mode, allow all numbers to be selectable
if (this.selectedLabel === 'TAN' && this.isBoxed) {
return false;
}
if (this.selectedLabel === 'TAN' || this.multiLegLabels.includes(this.selectedLabel || '') || this.twoGroupLabels.includes(this.selectedLabel || '')) {
return false;
}
return this.selectedNumbers.includes(number);
}
onPadEnter() {
if (this.canPrint) {
this.print();
}
}
onShashEnter() {
// Disable shash enter for TAN Box mode
if (this.selectedLabel === 'TAN' && this.isBoxed) {
return;
}
if (this.selectedLabel === 'TAN') {
if (this.tanGroupStage < 2) {
this.tanGroupStage++;
const combined: (number | string)[] = [...this.tanGroups[0]];
if (this.tanGroupStage > 0) combined.push('-', ...this.tanGroups[1]);
if (this.tanGroupStage > 1) combined.push('-', ...this.tanGroups[2]);
this.selectedNumbers = combined;
this.selectionService.updatePartial({ numbers: [...this.selectedNumbers] });
}
return;
}
if (this.multiLegLabels.includes(this.selectedLabel || '')) {
const maxLegs = this.getMaxLegs(this.selectedLabel || '');
if (this.multiLegStage < maxLegs - 1) {
this.multiLegStage++;
this.updateMultiLegSelection();
}
return;
}
if (this.twoGroupLabels.includes(this.selectedLabel || '')) {
if (!this.isFirstGroupComplete && this.firstGroup.length > 0) {
this.isFirstGroupComplete = true;
this.secondGroup = [];
this.selectedNumbers = [...this.firstGroup, '-'];
this.selectionService.updatePartial({ numbers: [...this.selectedNumbers] });
}
}
}
enterPadVal(key: string) {
if (!this.numericPadEnabled) return;
if (key === 'X') {
this.padValue = '';
} else if (/[0-9]/.test(key)) {
this.padValue += key;
}
this.updateCanPrint();
const value = parseFloat(this.padValue) || 0;
this.selectionService.updatePartial({
value,
isBoxed: this.isBoxed,
label: this.selectedLabel || '',
numbers: [...this.selectedNumbers]
});
}
updateCanPrint() {
this.canPrint = this.padValue.trim().length > 0 && /^[0-9]+$/.test(this.padValue);
}
print() {
this.selectionService.finalizeCurrentRow();
this.resetSelections();
}
erase() {
this.selectionService.clearSelections();
this.resetSelections();
}
resetSelections() {
this.selectedLabel = null;
this.selectedNumbers = [];
this.padValue = '';
this.canPrint = false;
this.isBoxed = false;
this.tanGroupStage = 0;
this.tanGroups = [[], [], []];
this.isFirstGroupComplete = false;
this.firstGroup = [];
this.secondGroup = [];
this.multiLegStage = 0;
this.multiLegGroups = [[], [], [], [], []];
this.fieldModalOpen = false;
this.fieldInput = '';
this.fieldFEntered = false;
}
toggleBoxMode() {
this.isBoxed = !this.isBoxed;
const value = parseFloat(this.padValue) || 0;
// For TAN Box mode, reset to freestyle selection
if (this.selectedLabel === 'TAN' && this.isBoxed) {
this.tanGroupStage = 0;
this.tanGroups = [[], [], []];
this.selectedNumbers = [];
}
this.selectionService.updatePartial({
isBoxed: this.isBoxed,
label: this.selectedLabel || '',
numbers: [...this.selectedNumbers],
value
});
this.updateCanPrint();
}
removeLastNumber() {
if (!this.selectedLabel || (this.selectedNumbers.length === 0 && !this.selectedNumbers.includes('F'))) return;
if (this.selectedNumbers.includes('F') && this.allowedFieldLabels.includes(this.selectedLabel || '')) {
this.selectedNumbers = [];
this.selectionService.updatePartial({
numbers: [],
isBoxed: false,
label: this.selectedLabel || ''
});
return;
}
if (this.selectedLabel === 'TAN' && this.isBoxed) {
const currentNumbers = this.selectedNumbers.filter(n => typeof n === 'number') as number[];
if (currentNumbers.length > 0) {
currentNumbers.pop();
// Rebuild dash-separated structure
const groupSize = Math.ceil(currentNumbers.length / 3);
const group1 = currentNumbers.slice(0, groupSize);
const group2 = currentNumbers.slice(group1.length, group1.length + groupSize);
const group3 = currentNumbers.slice(group1.length + group2.length);
const combined: (number | string)[] = [...group1];
if (group2.length) combined.push('-', ...group2);
if (group3.length) combined.push('-', ...group3);
this.selectedNumbers = combined;
this.selectionService.updatePartial({
numbers: [...this.selectedNumbers],
isBoxed: true,
label: 'TAN'
});
}
return;
}
// Original TAN logic (unboxed)
if (this.selectedLabel === 'TAN') {
const currentGroup = this.tanGroups[this.tanGroupStage];
if (currentGroup.length > 0) {
currentGroup.pop();
let combined: (number | string)[] = [...this.tanGroups[0]];
if (this.tanGroupStage > 0) combined.push('-', ...this.tanGroups[1]);
if (this.tanGroupStage > 1) combined.push('-', ...this.tanGroups[2]);
this.selectedNumbers = combined;
this.selectionService.updatePartial({ numbers: [...this.selectedNumbers] });
}
return;
}
// Multi-leg logic
if (this.multiLegLabels.includes(this.selectedLabel)) {
const currentGroup = this.multiLegGroups[this.multiLegStage];
if (currentGroup.length > 0) {
currentGroup.pop();
this.updateMultiLegSelection();
}
return;
}
// FOR/QUI logic
if (this.twoGroupLabels.includes(this.selectedLabel)) {
if (!this.isFirstGroupComplete && this.firstGroup.length > 0) {
this.firstGroup.pop();
this.selectedNumbers = [...this.firstGroup];
} else if (this.secondGroup.length > 0) {
this.secondGroup.pop();
this.selectedNumbers = [...this.firstGroup, '-', ...this.secondGroup];
}
this.selectionService.updatePartial({ numbers: [...this.selectedNumbers] });
return;
}
// Default single-number removal
this.selectedNumbers.pop();
this.selectionService.updatePartial({ numbers: [...this.selectedNumbers] });
}
private getMaxLegs(label: string): number {
switch (label) {
case 'TRE': return 3;
case 'MJP': return 4;
case 'JKP': return 5;
default: return 3;
}
}
// Calculator and Field Modal methods (unchanged)
openCalculator() { this.calculatorOpen = true; this.calcDisplay = ''; }
closeCalculator() { this.calculatorOpen = false; }
press(val: string) { if (this.calcDisplay === 'Error') this.calcDisplay = ''; this.calcDisplay += val; }
clearDisplay() { this.calcDisplay = ''; }
backspace() { this.calcDisplay = this.calcDisplay === 'Error' ? '' : this.calcDisplay.slice(0, -1); }
calculate() {
try { this.calcDisplay = eval(this.calcDisplay).toString(); }
catch { this.calcDisplay = 'Error'; }
}
canUseField(): boolean {
return this.selectedLabel !== null && this.allowedFieldLabels.includes(this.selectedLabel) && this.selectedNumbers.length === 0;
}
openFieldModal() { this.fieldModalOpen = true; this.fieldInput = ''; this.fieldFEntered = false; }
closeFieldModal() { this.fieldModalOpen = false; }
handleFieldKey(key: string) {
if (key === 'BACK') {
this.fieldInput = this.fieldInput.slice(0, -1);
if (!this.fieldInput.includes('F')) this.fieldFEntered = false;
return;
}
if (key === 'F') {
if (!this.fieldFEntered) {
this.fieldFEntered = true;
this.fieldInput = 'F';
}
} else {
this.fieldInput += key;
}
}
confirmFieldEntry() {
if (this.fieldFEntered) {
this.selectedNumbers = ['F'];
this.selectionService.updatePartial({
label: this.selectedLabel!,
numbers: ['F'],
isBoxed: false,
value: 1
});
this.closeFieldModal();
}
}
}