Allowed renaming of teams in Finska score card
All checks were successful
/ publish (push) Successful in 1m40s
All checks were successful
/ publish (push) Successful in 1m40s
This commit is contained in:
parent
c7ff8597aa
commit
3953adedd3
|
|
@ -20,8 +20,18 @@
|
||||||
<table class="score-table">
|
<table class="score-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="2">Team A</td>
|
<td colspan="2" data-finska-scorecard-target="team1Header">
|
||||||
<td colspan="2">Team B</td>
|
<span class="team-header">
|
||||||
|
<span class="team-name">Team A</span>
|
||||||
|
<button class="edit-btn" data-action="finska-scorecard#editTeam1" aria-label="Edit Team A name">✎</button>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td colspan="2" data-finska-scorecard-target="team2Header">
|
||||||
|
<span class="team-header">
|
||||||
|
<span class="team-name">Team B</span>
|
||||||
|
<button class="edit-btn" data-action="finska-scorecard#editTeam2" aria-label="Edit Team B name">✎</button>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody data-finska-scorecard-target="scoreTable">
|
<tbody data-finska-scorecard-target="scoreTable">
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { Scorecard, getStoreDAO } from "./models.js";
|
||||||
const storeDao = getStoreDAO();
|
const storeDao = getStoreDAO();
|
||||||
|
|
||||||
export class FinskaScorecardController extends Controller {
|
export class FinskaScorecardController extends Controller {
|
||||||
static targets = ["score1Input", "score2Input", "scoreTable"];
|
static targets = ["score1Input", "score2Input", "scoreTable", "team1Header", "team2Header"];
|
||||||
static values = {
|
static values = {
|
||||||
maxScore: Number,
|
maxScore: Number,
|
||||||
overflowScoreTo: Number
|
overflowScoreTo: Number
|
||||||
|
|
@ -18,8 +18,95 @@ export class FinskaScorecardController extends Controller {
|
||||||
|
|
||||||
this._undoStack = [];
|
this._undoStack = [];
|
||||||
this._scorecard = storeDao.loadOrCreate(rules);
|
this._scorecard = storeDao.loadOrCreate(rules);
|
||||||
|
this._loadTeamNames();
|
||||||
this.updateTable();
|
this.updateTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_loadTeamNames() {
|
||||||
|
const stored1 = localStorage.getItem('finska-team1-name');
|
||||||
|
const stored2 = localStorage.getItem('finska-team2-name');
|
||||||
|
this._team1Name = stored1 || 'Team A';
|
||||||
|
this._team2Name = stored2 || 'Team B';
|
||||||
|
this._renderTeamHeader(this.team1HeaderTarget, this._team1Name, 'editTeam1');
|
||||||
|
this._renderTeamHeader(this.team2HeaderTarget, this._team2Name, 'editTeam2');
|
||||||
|
}
|
||||||
|
|
||||||
|
_renderTeamHeader(td, name, editAction) {
|
||||||
|
td.innerHTML = `<span class="team-header">
|
||||||
|
<span class="team-name">${this._escapeHtml(name)}</span>
|
||||||
|
<button class="edit-btn" data-action="finska-scorecard#${editAction}" aria-label="Edit ${this._escapeHtml(name)} name">✎</button>
|
||||||
|
</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
_renderEditHeader(td, currentName, teamNum) {
|
||||||
|
td.innerHTML = `<span class="team-header-edit">
|
||||||
|
<input type="text" value="${this._escapeAttr(currentName)}" data-action="keydown->finska-scorecard#editKeyDown${teamNum}">
|
||||||
|
<button class="confirm-btn" data-action="finska-scorecard#confirmTeam${teamNum}" aria-label="Confirm">✓</button>
|
||||||
|
<button class="cancel-btn" data-action="finska-scorecard#cancelTeam${teamNum}" aria-label="Cancel">✗</button>
|
||||||
|
</span>`;
|
||||||
|
td.querySelector('input').focus();
|
||||||
|
td.querySelector('input').select();
|
||||||
|
}
|
||||||
|
|
||||||
|
editTeam1() {
|
||||||
|
this._renderEditHeader(this.team1HeaderTarget, this._team1Name, '1');
|
||||||
|
}
|
||||||
|
|
||||||
|
editTeam2() {
|
||||||
|
this._renderEditHeader(this.team2HeaderTarget, this._team2Name, '2');
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmTeam1() {
|
||||||
|
const input = this.team1HeaderTarget.querySelector('input');
|
||||||
|
const newName = input.value.trim() || 'Team A';
|
||||||
|
this._team1Name = newName;
|
||||||
|
localStorage.setItem('finska-team1-name', newName);
|
||||||
|
this._renderTeamHeader(this.team1HeaderTarget, this._team1Name, 'editTeam1');
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmTeam2() {
|
||||||
|
const input = this.team2HeaderTarget.querySelector('input');
|
||||||
|
const newName = input.value.trim() || 'Team B';
|
||||||
|
this._team2Name = newName;
|
||||||
|
localStorage.setItem('finska-team2-name', newName);
|
||||||
|
this._renderTeamHeader(this.team2HeaderTarget, this._team2Name, 'editTeam2');
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelTeam1() {
|
||||||
|
this._renderTeamHeader(this.team1HeaderTarget, this._team1Name, 'editTeam1');
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelTeam2() {
|
||||||
|
this._renderTeamHeader(this.team2HeaderTarget, this._team2Name, 'editTeam2');
|
||||||
|
}
|
||||||
|
|
||||||
|
editKeyDown1(e) {
|
||||||
|
this._editKeyDown(e, this.confirmTeam1.bind(this), this.cancelTeam1.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
editKeyDown2(e) {
|
||||||
|
this._editKeyDown(e, this.confirmTeam2.bind(this), this.cancelTeam2.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
_editKeyDown(e, confirmFn, cancelFn) {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
confirmFn();
|
||||||
|
} else if (e.key === 'Escape') {
|
||||||
|
e.preventDefault();
|
||||||
|
cancelFn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_escapeHtml(str) {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.textContent = str;
|
||||||
|
return div.innerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
_escapeAttr(str) {
|
||||||
|
return str.replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
|
||||||
|
}
|
||||||
|
|
||||||
updateTable() {
|
updateTable() {
|
||||||
let tableBody = this.scoreTableTarget;
|
let tableBody = this.scoreTableTarget;
|
||||||
|
|
@ -155,6 +242,12 @@ export class FinskaScorecardController extends Controller {
|
||||||
this._scorecard.reset();
|
this._scorecard.reset();
|
||||||
storeDao.clear();
|
storeDao.clear();
|
||||||
this._undoStack = [];
|
this._undoStack = [];
|
||||||
|
this._team1Name = 'Team A';
|
||||||
|
this._team2Name = 'Team B';
|
||||||
|
localStorage.removeItem('finska-team1-name');
|
||||||
|
localStorage.removeItem('finska-team2-name');
|
||||||
|
this._renderTeamHeader(this.team1HeaderTarget, this._team1Name, 'editTeam1');
|
||||||
|
this._renderTeamHeader(this.team2HeaderTarget, this._team2Name, 'editTeam2');
|
||||||
this.updateTable();
|
this.updateTable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,4 +18,67 @@ tfoot input {
|
||||||
color: #394D00;
|
color: #394D00;
|
||||||
background-color: #DEFC85;
|
background-color: #DEFC85;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.team-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-header .team-name {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-header .edit-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.1rem 0.3rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin: 0;
|
||||||
|
width: auto;
|
||||||
|
line-height: 1;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-header .edit-btn:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-header-edit {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-header-edit input {
|
||||||
|
border-width: 1px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0.2rem 0.4rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
width: 6rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-header-edit .confirm-btn,
|
||||||
|
.team-header-edit .cancel-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.1rem 0.3rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
margin: 0;
|
||||||
|
width: auto;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-header-edit .confirm-btn {
|
||||||
|
color: #2e7d32;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-header-edit .cancel-btn {
|
||||||
|
color: #c62828;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue