Allowed renaming of teams in Finska score card
All checks were successful
/ publish (push) Successful in 1m40s

This commit is contained in:
exe.dev user 2026-04-04 03:47:57 +00:00
parent c7ff8597aa
commit 3953adedd3
3 changed files with 170 additions and 4 deletions

View file

@ -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">&#9998;</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">&#9998;</button>
</span>
</td>
</tr> </tr>
</thead> </thead>
<tbody data-finska-scorecard-target="scoreTable"> <tbody data-finska-scorecard-target="scoreTable">

View file

@ -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,9 +18,96 @@ 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">&#9998;</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">&#10003;</button>
<button class="cancel-btn" data-action="finska-scorecard#cancelTeam${teamNum}" aria-label="Cancel">&#10007;</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, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
updateTable() { updateTable() {
let tableBody = this.scoreTableTarget; let tableBody = this.scoreTableTarget;
let tableRows = tableBody.querySelectorAll("tr"); let tableRows = tableBody.querySelectorAll("tr");
@ -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();
} }
} }

View file

@ -19,3 +19,66 @@ tfoot input {
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;
}