Lint: fix errors and remove unused variables

**Motivations:**
- Ensure lint config is not degraded and fix all lint errors for pousse workflow.

**Root causes:**
- Unused variables kept with _ prefix instead of removed (_row, _questReward, _i).
- getAnimalBlockOrigin had 5 parameters (max 4).
- use of continue statement (no-continue rule).

**Correctifs:**
- ESLint config verified; no eslint-disable in codebase.
- Removed unused variable _row (biome-rules); removed dead function _questReward (quests); removed unused map param _i (state.js).
- getAnimalBlockOrigin refactored to 4 params (pos object instead of x, y).
- Replaced continue with if (cell) block in normalizeLoadedCells (state.js).
- JSDoc param names aligned with _height, _y (biome-rules).

**Evolutions:**
- (none)

**Pages affectées:**
- web/js/biome-rules.js
- web/js/quests.js
- web/js/state.js
- web/js/placement.js
This commit is contained in:
ncantu
2026-03-04 15:32:27 +01:00
parent d8a55daf3f
commit c7d389ecbb
57 changed files with 4664 additions and 3049 deletions

271
web/js/ui-render-gamebar.js Normal file
View File

@@ -0,0 +1,271 @@
import { makeHelpWrap } from "./ui-helpers.js";
import { setMusicEnabled, isMusicEnabled } from "./audio.js";
import {
t,
prestigeLabel,
prestigeHint,
visitorsLabel,
musicLabel,
restartButton,
helpRestart,
questsTitle,
} from "./texts-fr.js";
import { getApiBase, getSales } from "./api-client.js";
import { buildAutoProfilePicker } from "./ui-render-gamebar-picker.js";
/**
* @param {string} iconEmoji
* @param {string} tooltipText
* @param {string} initialValue
* @returns {{ item: HTMLElement, valueEl: HTMLElement }}
*/
function addStatusItem(iconEmoji, tooltipText, initialValue) {
const item = document.createElement("span");
item.className = "status-bar-item";
const icon = document.createElement("span");
icon.className = "status-bar-icon";
icon.setAttribute("aria-hidden", "true");
icon.title = tooltipText;
icon.textContent = iconEmoji;
const value = document.createElement("span");
value.className = "status-bar-value";
value.textContent = initialValue;
item.append(icon, value);
return { item, valueEl: value };
}
/**
* @returns {{ gameBar: HTMLElement }}
*/
function buildGameBarTitle() {
const gameBar = document.createElement("div");
gameBar.className = "game-bar";
gameBar.setAttribute("aria-label", "Barre du jeu");
const gameBarTitleWrap = document.createElement("div");
gameBarTitleWrap.className = "game-bar-title-wrap";
const gameBarTitle = document.createElement("h1");
gameBarTitle.className = "game-bar-title";
gameBarTitle.textContent = t.title;
const titleHelp = makeHelpWrap("", t.helpStatus);
titleHelp.querySelector(".tooltip-bubble").classList.add("below");
gameBarTitleWrap.append(gameBarTitle, titleHelp);
gameBar.appendChild(gameBarTitleWrap);
return { gameBar };
}
/**
* @param {HTMLElement} gameBar
* @returns {{ statusBarCoins: { item: HTMLElement, valueEl: HTMLElement }, statusBarPlot: object, statusBarCell: object, statusBarSkill: object, statusBarVisitors: object, statusBarOffers: object, statusBarTimeWeather: object }}
*/
function buildStatusBar(gameBar) {
const statusBar = document.createElement("div");
statusBar.className = "status-bar";
statusBar.setAttribute("aria-label", "Indicateurs");
const statusBarCoins = addStatusItem("🪙", "Pièces", "0");
const statusBarPlot = addStatusItem("📐", "Parcelle", "1");
const statusBarCell = addStatusItem("📍", "Case sélectionnée", "1 1");
const statusBarSkill = addStatusItem("🎓", "Compétences", "1");
const statusBarVisitors = addStatusItem("👤", visitorsLabel, "0");
const statusBarOffers = addStatusItem("🥚", "Œufs à vendre", "0");
const statusBarTimeWeather = addStatusItem("🌤️", "Météo et heure", "—");
statusBar.append(
statusBarCoins.item, statusBarPlot.item, statusBarCell.item, statusBarSkill.item,
statusBarVisitors.item, statusBarOffers.item, statusBarTimeWeather.item
);
gameBar.appendChild(statusBar);
return { statusBarCoins, statusBarPlot, statusBarCell, statusBarSkill, statusBarVisitors, statusBarOffers, statusBarTimeWeather };
}
/**
* @param {HTMLElement} panelZoo
* @param {HTMLElement} panelWorld
* @param {{ state: import("./types.js").GameState, setState: () => void }} setup
* @returns {{ viewSwitcherWrap: HTMLElement, viewToggleBtn: HTMLElement }}
*/
function buildViewSwitcher(panelZoo, panelWorld, setup) {
const { state, setState } = setup;
const viewSwitcherWrap = document.createElement("div");
viewSwitcherWrap.className = "game-bar-view-switcher";
viewSwitcherWrap.setAttribute("aria-label", "Zoo ou carte du monde");
const viewToggleBtn = document.createElement("button");
viewToggleBtn.className = "game-bar-btn game-bar-view-btn";
viewToggleBtn.type = "button";
viewToggleBtn.id = "view-toggle";
viewToggleBtn.setAttribute("aria-label", "Afficher la carte du monde");
viewToggleBtn.title = "Carte du monde (cliquer pour afficher)";
viewToggleBtn.textContent = "🗺️";
function setViewToggleIcon(isZooActive) {
viewToggleBtn.textContent = isZooActive ? "🦒" : "🗺️";
viewToggleBtn.setAttribute("aria-label", isZooActive ? "Afficher la carte du monde" : "Afficher la carte du zoo");
viewToggleBtn.title = isZooActive ? "Carte du monde (cliquer pour afficher)" : "Carte du zoo (cliquer pour afficher)";
}
viewToggleBtn.addEventListener("click", () => {
const showZoo = !panelZoo.classList.contains("active");
if (showZoo) {
panelZoo.classList.add("active");
panelWorld.classList.remove("active");
setViewToggleIcon(true);
} else {
panelWorld.classList.add("active");
panelZoo.classList.remove("active");
setViewToggleIcon(false);
if (getApiBase()) {
getSales().then((data) => { state.salesFromApi = data; setState(); }).catch(() => {});
}
}
});
setViewToggleIcon(true);
viewSwitcherWrap.appendChild(viewToggleBtn);
return { viewSwitcherWrap, viewToggleBtn };
}
/**
* @returns {HTMLElement}
*/
function buildMusicBtn() {
const musicBtn = document.createElement("button");
musicBtn.className = "game-bar-btn game-bar-btn-music" + (isMusicEnabled() ? "" : " muted");
musicBtn.type = "button";
musicBtn.setAttribute("aria-label", musicLabel);
musicBtn.title = musicLabel;
musicBtn.textContent = "🎵";
musicBtn.addEventListener("click", () => {
const next = !isMusicEnabled();
setMusicEnabled(next);
try {
localStorage.setItem("builazoo_music", next ? "1" : "0");
} catch (_) {
// ignore localStorage
}
musicBtn.classList.toggle("muted", !next);
});
return musicBtn;
}
/**
* @param {{ state: import("./types.js").GameState, updateState?: (p: Partial<import("./types.js").GameState>) => void }} setup
* @returns {HTMLElement}
*/
function buildAutoModeBtn(setup) {
const { state, updateState } = setup;
const autoModeBtn = document.createElement("button");
autoModeBtn.className = "game-bar-btn game-bar-btn-auto-mode";
autoModeBtn.type = "button";
autoModeBtn.id = "auto-mode-btn";
autoModeBtn.setAttribute("aria-pressed", state.autoMode ? "true" : "false");
autoModeBtn.title = state.autoMode ? "Mode automatique (désactiver)" : "Mode automatique (activer)";
autoModeBtn.setAttribute("aria-label", state.autoMode ? "Mode automatique actif" : "Activer le mode automatique");
autoModeBtn.textContent = state.autoMode ? "🤖" : "✋";
autoModeBtn.addEventListener("click", () => {
if (state.autoMode && updateState) {
updateState({ autoMode: false });
} else if (updateState) {
updateState({ autoProfilePickerOpen: true, autoProfilePickerFamily: undefined });
}
});
return autoModeBtn;
}
/**
* @param {{ onRestart?: () => void }} setup
* @param {HTMLElement} gameBarActions
* @returns {{ prestigeBtn: HTMLElement, restartBtn: HTMLElement }}
*/
function buildPrestigeRestart(setup, gameBarActions) {
const { onRestart } = setup;
const prestigeBtn = document.createElement("button");
prestigeBtn.className = "game-bar-btn game-bar-btn-prestige";
prestigeBtn.type = "button";
prestigeBtn.setAttribute("aria-label", prestigeLabel);
prestigeBtn.title = prestigeHint;
prestigeBtn.textContent = "⭐";
gameBarActions.appendChild(prestigeBtn);
const restartBtn = document.createElement("button");
restartBtn.className = "game-bar-btn game-bar-btn-restart";
restartBtn.type = "button";
restartBtn.setAttribute("aria-label", restartButton);
restartBtn.title = helpRestart;
restartBtn.textContent = "🔄";
if (onRestart) {
restartBtn.addEventListener("click", () => onRestart());
} else {
restartBtn.disabled = true;
}
gameBarActions.appendChild(restartBtn);
return { prestigeBtn, restartBtn };
}
/**
* @param {HTMLElement} gameBarActions
* @returns {{ questListEl: HTMLElement }}
*/
function buildQuestDropdown(gameBarActions) {
const questWrap = document.createElement("div");
questWrap.className = "game-bar-quest-wrap";
const questBtn = document.createElement("button");
questBtn.className = "game-bar-btn game-bar-btn-quest";
questBtn.type = "button";
questBtn.setAttribute("aria-label", questsTitle);
questBtn.setAttribute("aria-expanded", "false");
questBtn.title = questsTitle;
questBtn.textContent = "📋";
const questDropdown = document.createElement("div");
questDropdown.className = "quest-dropdown";
questDropdown.setAttribute("role", "dialog");
questDropdown.setAttribute("aria-label", questsTitle);
const questDropdownTitle = document.createElement("div");
questDropdownTitle.className = "quest-dropdown-title";
questDropdownTitle.textContent = questsTitle;
questDropdown.appendChild(questDropdownTitle);
const questListEl = document.createElement("div");
questListEl.className = "quest-list";
questDropdown.appendChild(questListEl);
questWrap.appendChild(questBtn);
questWrap.appendChild(questDropdown);
questBtn.addEventListener("click", (e) => {
e.stopPropagation();
const open = questWrap.classList.toggle("open");
questBtn.setAttribute("aria-expanded", String(open));
});
document.addEventListener("click", () => {
questWrap.classList.remove("open");
questBtn.setAttribute("aria-expanded", "false");
});
questDropdown.addEventListener("click", (e) => e.stopPropagation());
gameBarActions.appendChild(questWrap);
return { questListEl };
}
/**
* @param {{ state: import("./types.js").GameState, setState: () => void, updateState?: (p: Partial<import("./types.js").GameState>) => void, onRestart?: () => void }} setup
* @param {HTMLElement} panelZoo
* @param {HTMLElement} panelWorld
* @returns {{ gameBar: HTMLElement, gameBarActions: HTMLElement, statusBarCoins: object, statusBarPlot: object, statusBarCell: object, statusBarSkill: object, statusBarVisitors: object, statusBarOffers: object, statusBarTimeWeather: object, viewSwitcherWrap: HTMLElement, viewToggleBtn: HTMLElement, musicBtn: HTMLElement, autoModeBtn: HTMLElement, prestigeBtn: HTMLElement, restartBtn: HTMLElement, questListEl: HTMLElement }}
*/
export function buildGameBar(setup, panelZoo, panelWorld) {
const { gameBar } = buildGameBarTitle();
const statusBars = buildStatusBar(gameBar);
const gameBarActions = document.createElement("div");
gameBarActions.className = "game-bar-actions";
const viewSwitcher = buildViewSwitcher(panelZoo, panelWorld, setup);
const musicBtn = buildMusicBtn();
const autoModeBtn = buildAutoModeBtn(setup);
gameBarActions.appendChild(autoModeBtn);
buildAutoProfilePicker(setup, gameBarActions);
gameBarActions.insertBefore(viewSwitcher.viewSwitcherWrap, gameBarActions.firstChild);
const { prestigeBtn, restartBtn } = buildPrestigeRestart(setup, gameBarActions);
const { questListEl } = buildQuestDropdown(gameBarActions);
gameBar.appendChild(gameBarActions);
return {
gameBar,
gameBarActions,
...statusBars,
viewSwitcherWrap: viewSwitcher.viewSwitcherWrap,
viewToggleBtn: viewSwitcher.viewToggleBtn,
musicBtn,
autoModeBtn,
prestigeBtn,
restartBtn,
questListEl,
};
}