From 5b8370aa20f7109dda996f4d9fc1bed191213844 Mon Sep 17 00:00:00 2001 From: Jan Bader Date: Sat, 27 Jun 2026 15:38:15 +0200 Subject: [PATCH] Support compact numeric input suffixes --- app.js | 76 +++++++++++++++++++++++++++++++++++++++++------------- index.html | 4 +-- 2 files changed, 60 insertions(+), 20 deletions(-) diff --git a/app.js b/app.js index 38330b0..b9d48de 100644 --- a/app.js +++ b/app.js @@ -61,7 +61,29 @@ const slots = [ ]; const $ = (selector) => document.querySelector(selector); -const fmt = (value) => Number(value || 0).toLocaleString(undefined, { maximumFractionDigits: 1 }); +const NUMBER_SUFFIXES = Object.freeze({ k: 1_000, m: 1_000_000, b: 1_000_000_000 }); + +function parseNumberInput(value, fallback = 0) { + if (typeof value === "number") return Number.isFinite(value) ? value : fallback; + const text = String(value ?? "").trim().replace(/,/g, "").toLowerCase(); + if (!text) return fallback; + const match = /^([+-]?(?:\d+(?:\.\d*)?|\.\d+))\s*([kmb])?$/.exec(text); + if (!match) return fallback; + const parsed = Number(match[1]) * (NUMBER_SUFFIXES[match[2]] || 1); + return Number.isFinite(parsed) ? parsed : fallback; +} + +function formatCompactNumber(value) { + const number = Number(value || 0); + if (!Number.isFinite(number)) return "0"; + const abs = Math.abs(number); + const suffix = abs >= NUMBER_SUFFIXES.b ? "b" : abs >= NUMBER_SUFFIXES.m ? "m" : abs >= NUMBER_SUFFIXES.k ? "k" : ""; + const divisor = suffix ? NUMBER_SUFFIXES[suffix] : 1; + const scaled = number / divisor; + return `${Number(scaled.toPrecision(3))}${suffix}`; +} + +const fmt = formatCompactNumber; const effectById = (id) => EFFECTS.find((effect) => effect.id === id); let state = loadDraft() || createDefaultState(); @@ -212,7 +234,7 @@ function renderControls() { $("#profileSelect").value = state.profile; $("#combatStyle").value = state.combatStyle; $("#optimizationTarget").value = state.optimizationTarget; - $("#recommendationCount").value = state.recommendationCount; + $("#recommendationCount").value = fmt(state.recommendationCount); } function createEffectRow(effect, effectIndex, typeAttribute, valueAttribute, showInitialHint = true) { @@ -226,7 +248,7 @@ function createEffectRow(effect, effectIndex, typeAttribute, valueAttribute, sho `; row.querySelector("select").value = effect.type; @@ -258,20 +280,20 @@ function renderGear() { `; baseFields.querySelector('[data-field="baseType"]').value = piece.baseType; - baseFields.querySelector('[data-field="baseValue"]').value = piece.baseValue; + baseFields.querySelector('[data-field="baseValue"]').value = fmt(piece.baseValue); } else { baseFields.innerHTML = ` `; - baseFields.querySelector('[data-field="baseDamage"]').value = piece.baseDamage || 0; - baseFields.querySelector('[data-field="baseHealth"]').value = piece.baseHealth || 0; + baseFields.querySelector('[data-field="baseDamage"]').value = fmt(piece.baseDamage || 0); + baseFields.querySelector('[data-field="baseHealth"]').value = fmt(piece.baseHealth || 0); } const effects = node.querySelector(".effects"); @@ -304,15 +326,15 @@ function renderNewItemComparison() { `; } else { baseFields.innerHTML = ` `; } @@ -970,7 +992,13 @@ function bindEvents() { updateAnalysis(); }); $("#recommendationCount").addEventListener("input", (event) => { - state.recommendationCount = Math.min(20, Math.max(3, Number(event.target.value || 8))); + state.recommendationCount = Math.min(20, Math.max(3, Math.round(parseNumberInput(event.target.value, 8)))); + formatChangedInput(event, state.recommendationCount); + updateAnalysis(); + }); + $("#recommendationCount").addEventListener("change", (event) => { + state.recommendationCount = Math.min(20, Math.max(3, Math.round(parseNumberInput(event.target.value, 8)))); + formatChangedInput(event, state.recommendationCount); updateAnalysis(); }); $("#resetBuild").addEventListener("click", () => { @@ -1001,7 +1029,8 @@ function bindEvents() { function handleNewItemInput(event) { if (event.target.id === "newItemTarget") return; if (event.target.dataset.newItemField) { - state.newItem[event.target.dataset.newItemField] = Math.max(0, Number(event.target.value || 0)); + state.newItem[event.target.dataset.newItemField] = Math.max(0, parseNumberInput(event.target.value, 0)); + formatChangedInput(event, state.newItem[event.target.dataset.newItemField]); } if (event.target.dataset.newItemEffectType !== undefined) { const index = Number(event.target.dataset.newItemEffectType); @@ -1011,7 +1040,8 @@ function handleNewItemInput(event) { } if (event.target.dataset.newItemEffectValue !== undefined) { const index = Number(event.target.dataset.newItemEffectValue); - state.newItem.effects[index].value = clampEffectValue(state.newItem.effects[index].type, Number(event.target.value || 1)); + state.newItem.effects[index].value = clampEffectValue(state.newItem.effects[index].type, parseNumberInput(event.target.value, 1)); + formatChangedInput(event, state.newItem.effects[index].value); } updateAnalysis(); } @@ -1022,16 +1052,21 @@ function handleGearInput(event) { const piece = state.pieces[Number(card.dataset.index)]; if (event.target.dataset.field) { - if (piece.fixedBase && ["name", "baseType", "baseValue"].includes(event.target.dataset.field)) { + if (piece.fixedBase && ["name", "baseType"].includes(event.target.dataset.field)) { event.target.value = piece[event.target.dataset.field]; return; } const field = event.target.dataset.field; - piece[field] = ["baseValue", "baseDamage", "baseHealth"].includes(field) ? Math.max(0, Number(event.target.value || 0)) : event.target.value; + piece[field] = ["baseValue", "baseDamage", "baseHealth"].includes(field) ? Math.max(0, parseNumberInput(event.target.value, 0)) : event.target.value; + if (field === "baseValue") { + if (piece.baseType === "health") piece.baseHealth = piece.baseValue; + else piece.baseDamage = piece.baseValue; + } if (field === "baseDamage" || field === "baseHealth") { piece.baseType = Number(piece.baseHealth || 0) > Number(piece.baseDamage || 0) ? "health" : "damage"; piece.baseValue = Math.max(Number(piece.baseDamage || 0), Number(piece.baseHealth || 0)); } + if (["baseValue", "baseDamage", "baseHealth"].includes(field)) formatChangedInput(event, piece[field]); } if (event.target.dataset.effectType !== undefined) { const index = Number(event.target.dataset.effectType); @@ -1041,11 +1076,16 @@ function handleGearInput(event) { } if (event.target.dataset.effectValue !== undefined) { const index = Number(event.target.dataset.effectValue); - piece.effects[index].value = clampEffectValue(piece.effects[index].type, Number(event.target.value || 1)); + piece.effects[index].value = clampEffectValue(piece.effects[index].type, parseNumberInput(event.target.value, 1)); + formatChangedInput(event, piece.effects[index].value); } updateAnalysis(); } +function formatChangedInput(event, value) { + if (event.type === "change") event.target.value = fmt(value); +} + function saveNamedSlot() { const name = $("#slotName").value.trim(); if (!name) return setStatus("Enter a slot name first.", true); diff --git a/index.html b/index.html index aaadd9b..f38d4ea 100644 --- a/index.html +++ b/index.html @@ -42,7 +42,7 @@ @@ -175,7 +175,7 @@