feat(match-report): implement floating keyboard for set input in MatchReportApiDialog

- Added a floating keyboard overlay for set input, allowing users to enter scores without using the system keyboard.
- Updated input fields to be read-only and disabled system keyboard interactions, enhancing user experience.
- Implemented methods to manage keyboard interactions, including key input, backspace, and confirmation actions.
- Improved styling for the floating keyboard to ensure clarity and usability during score entry.
This commit is contained in:
Torsten Schulz (local)
2026-02-26 16:18:55 +01:00
parent f031485bd4
commit 07370bfcef

View File

@@ -386,27 +386,13 @@
<div class="set-input-wrapper">
<input
class="set-input"
:class="{ 'gap-warning': shouldHighlightSetInput(idx, sIdx) }"
:class="{ 'gap-warning': shouldHighlightSetInput(idx, sIdx), 'keyboard-open': isSetKeyboardOpen(idx, sIdx) }"
v-model="m.sets[sIdx]"
placeholder=":"
:readonly="true"
:disabled="isSetInputDisabled(idx, sIdx)"
inputmode="numeric"
pattern="[0-9:\-]*"
@keyup.enter="processScoreInput(idx, sIdx)"
@blur="processScoreInput(idx, sIdx)"
@click="openSetKeyboard(idx, sIdx)"
/>
<div class="set-extra-buttons">
<button
type="button"
class="set-extra-btn"
@click="appendToSet(idx, sIdx, ':')"
>:</button>
<button
type="button"
class="set-extra-btn"
@click="appendToSet(idx, sIdx, '-')"
>-</button>
</div>
</div>
</td>
<td class="state-cell">
@@ -455,6 +441,23 @@
</div>
</div>
</div>
<!-- Schwebende Tastatur für Satzeingabe (unterdrückt System-Tastatur) -->
<div v-if="editingSetCell" class="set-keyboard-overlay" @click.self="closeSetKeyboard">
<div class="set-keyboard" @click.stop>
<div class="set-keyboard-value" :class="{ 'placeholder': !editingSetValue }">
{{ editingSetValue || 'Satz z.B. 11:9' }}
</div>
<div class="set-keyboard-keys">
<button type="button" v-for="n in 9" :key="n" class="set-key" @click="setKeyboardKey(String(n))">{{ n }}</button>
<button type="button" class="set-key" @click="setKeyboardKey(':')">:</button>
<button type="button" class="set-key" @click="setKeyboardKey('0')">0</button>
<button type="button" class="set-key" @click="setKeyboardKey('-')"></button>
<button type="button" class="set-key set-key-backspace" @click="setKeyboardBackspace"></button>
<button type="button" class="set-key set-key-ok" @click="setKeyboardOk">OK</button>
</div>
</div>
</div>
</div>
<div v-else-if="activeSection === 'completion'" class="completion-content">
@@ -713,6 +716,8 @@ export default {
results: [],
// Fehlermeldungen für Satzeingaben
errors: [],
// Aktive Zelle der schwebenden Satz-Tastatur: { matchIndex, setIndex } oder null
editingSetCell: null,
// Abschluss-Felder
protestText: '',
finalHomePin: '',
@@ -788,6 +793,11 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
.trim();
return norm(this.meetingData.league) === norm(this.meetingData.group);
},
editingSetValue() {
if (!this.editingSetCell || !this.results[this.editingSetCell.matchIndex]) return '';
const sets = this.results[this.editingSetCell.matchIndex].sets || [];
return sets[this.editingSetCell.setIndex] || '';
},
canOpenNextStages() {
// Wenn das Match bereits abgeschlossen ist, dürfen keine Änderungen mehr gemacht werden
if (this.isMatchCompleted) {
@@ -2144,6 +2154,38 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
}
this.results[matchIndex].sets[setIndex] = current + char;
},
isSetKeyboardOpen(matchIndex, setIndex) {
return this.editingSetCell && this.editingSetCell.matchIndex === matchIndex && this.editingSetCell.setIndex === setIndex;
},
openSetKeyboard(matchIndex, setIndex) {
if (this.isSetInputDisabled(matchIndex, setIndex)) return;
this.editingSetCell = { matchIndex, setIndex };
},
closeSetKeyboard() {
this.editingSetCell = null;
},
setKeyboardKey(char) {
if (!this.editingSetCell || !this.results[this.editingSetCell.matchIndex]) return;
const { matchIndex, setIndex } = this.editingSetCell;
const sets = this.results[matchIndex].sets || [];
const current = sets[setIndex] || '';
if (current.length >= 6) return;
this.results[matchIndex].sets[setIndex] = current + char;
},
setKeyboardBackspace() {
if (!this.editingSetCell || !this.results[this.editingSetCell.matchIndex]) return;
const { matchIndex, setIndex } = this.editingSetCell;
const sets = this.results[matchIndex].sets || [];
const current = sets[setIndex] || '';
this.results[matchIndex].sets[setIndex] = current.slice(0, -1);
},
setKeyboardOk() {
if (!this.editingSetCell) return;
const { matchIndex, setIndex } = this.editingSetCell;
this.processScoreInput(matchIndex, setIndex);
this.closeSetKeyboard();
},
reopenMatch(idx) {
const m = this.results[idx];
m.completed = false;
@@ -3762,6 +3804,78 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
background-color: #f0f0f0;
}
/* Schwebende Satz-Tastatur (ersetzt System-Tastatur) */
.set-keyboard-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.4);
display: flex;
align-items: flex-end;
justify-content: center;
z-index: 10000;
padding: 0;
}
.set-keyboard {
background: #f0f0f0;
border-radius: 12px 12px 0 0;
padding: 12px;
padding-bottom: max(12px, env(safe-area-inset-bottom));
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.2);
width: 100%;
max-width: 360px;
}
.set-keyboard-value {
text-align: center;
font-size: 1.25rem;
font-family: monospace;
padding: 10px 12px;
margin-bottom: 10px;
background: #fff;
border: 1px solid #ccc;
border-radius: 8px;
min-height: 44px;
line-height: 1.4;
}
.set-keyboard-value.placeholder {
color: #999;
}
.set-keyboard-keys {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 8px;
}
.set-key {
min-height: 48px;
font-size: 1.25rem;
border: 1px solid #bbb;
border-radius: 8px;
background: #fff;
cursor: pointer;
font-family: inherit;
-webkit-tap-highlight-color: transparent;
touch-action: manipulation;
}
.set-key:active {
background: #e0e0e0;
}
.set-key-backspace {
font-size: 1.1rem;
}
.set-key-ok {
grid-column: span 2;
background: var(--primary-color, #1976d2);
color: #fff;
border-color: var(--primary-hover, #1565c0);
font-weight: 600;
}
.set-key-ok:active {
opacity: 0.9;
}
.set-input.keyboard-open {
border-color: var(--primary-color);
box-shadow: 0 0 0 2px rgba(var(--primary-rgb, 25, 118, 210), 0.3);
}
/* Mobile-spezifische Optimierungen */
@media (max-width: 768px) {
.set-input {
@@ -3769,14 +3883,9 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
padding: 8px;
font-size: 16px; /* Verhindert Zoom auf iOS */
-webkit-appearance: none;
appearance: none;
border-radius: 6px;
}
.set-extra-buttons {
display: flex;
flex-direction: column;
gap: 2px;
}
}
.state-cell { text-align: center; }
.state-content { display: flex; flex-direction: row; align-items: center; gap: 8px; }