diff --git a/script.js b/script.js index 4149f15..1baf46e 100755 --- a/script.js +++ b/script.js @@ -23,6 +23,9 @@ class Dice { } fromText(value) { + if (!value) { + return; + } const result = value.match(localeData && localeData['regex'] || dice_regex); if (result) { if (result[1]) { @@ -51,7 +54,7 @@ class DiceHistoryEntry { } const default_sides = [4, 6, 8, 10, 12, 20, 100]; -const default_colors = ["#de324c", "#f4895f", "#f8e16f", "#95cf92", "#369acc", "#9656a2", "#6c584c"]; +const default_colors = ["#de324c", "#f4895f", "#d8b400ff", "#95cf92", "#369acc", "#9656a2", "#6c584c"]; const dice_regex = new RegExp("(\\d+)?[D|d](\\d+)([\\+|\\-]\\d+)?(\\[(.+)\\])?"); @@ -209,7 +212,6 @@ function renderHistory() { } if (redoHistory.length) { - console.log(redoHistory); document.getElementById('history-redo-button').classList.remove("disabled"); } else { document.getElementById('history-redo-button').classList.add("disabled"); @@ -217,13 +219,42 @@ function renderHistory() { history.forEach((entry) => { if (entry.result) { + // Create main history entry container + const historyEntry = document.createElement("div"); + historyEntry.classList.add("history-entry"); + + // Create header section + const entryHeader = document.createElement("div"); + entryHeader.classList.add("history-entry-header"); + + // Create dice label section const diceLabelContainer = document.createElement("div"); diceLabelContainer.classList.add("label"); if (entry.dices) { entry.dices.forEach((diceData, i) => { const dice = new Dice(diceData.sides, diceData.addition, diceData.count, diceData.color); + + // Add dice icon + const diceIcon = document.createElement("span"); + diceIcon.classList.add("dice-icon"); + if (default_sides.indexOf(dice.sides) != -1) { + diceIcon.style.maskImage = `url(./assets/dices/${dice.sides}.svg)`; + } else { + diceIcon.style.maskImage = "url(./assets/dices/custom.svg)"; + } + diceIcon.style.color = dice.color; + // revert b/w on dark-mode + if (darkMode) { + if (["#000", "#000000", "black", "rgb(0,0,0)", "rgb(0, 0, 0)"].indexOf(dice.color) != -1) { + diceIcon.style.color = "#fff"; + } else if (["#fff", "#ffffff", "white", "rgb(255,255,255)", "rgb(255, 255, 255)"].indexOf(dice.color) != -1) { + diceIcon.style.color = "#000"; + } + } + diceLabelContainer.appendChild(diceIcon); + const diceLabel = document.createElement("label"); - diceLabel.innerHTML = dice.toText() + (i == entry.dices.length - 1 ? ": " : ""); + diceLabel.innerHTML = dice.toText(); diceLabel.style.color = dice.color; // revert b/w on dark-mode if (darkMode) { @@ -234,6 +265,7 @@ function renderHistory() { } } diceLabelContainer.appendChild(diceLabel); + if (i < entry.dices.length - 1) { const diceLabelAdd = document.createElement("span"); diceLabelAdd.innerHTML = " + "; @@ -241,48 +273,130 @@ function renderHistory() { } }) } - historyContainer.appendChild(diceLabelContainer); - - const diceResult = document.createElement("span"); + // Create result display + const diceResult = document.createElement("div"); diceResult.classList.add("result"); diceResult.innerText = entry.result; - historyContainer.appendChild(diceResult); - const diceFormulaContainer = document.createElement("span"); - diceFormulaContainer.classList.add("formula-container"); - historyContainer.appendChild(diceFormulaContainer); + entryHeader.appendChild(diceLabelContainer); - if (entry.formula) { - const diceFormulaEqual = document.createElement("span"); - diceFormulaEqual.innerText = " = "; - diceFormulaContainer.appendChild(diceFormulaEqual); - const diceFormula = document.createElement("span"); - diceFormula.classList.add("formula"); - diceFormula.innerText = entry.formula; - diceFormulaContainer.appendChild(diceFormula); + // Only show result in header if there's no formula + if (!entry.formula) { + entryHeader.appendChild(diceResult); } - const diceTime = document.createElement("span"); + // Create time display + const diceTime = document.createElement("div"); diceTime.classList.add("time"); - historyContainer.appendChild(diceTime); - if (entry.time) { entry.time.locale(locale); diceTime.innerText = entry.time.fromNow(); diceTime.title = entry.time.format("LLLL"); } + entryHeader.appendChild(diceTime); + + historyEntry.appendChild(entryHeader); + + // Create details section if formula exists + if (entry.formula) { + const entryDetails = document.createElement("div"); + entryDetails.classList.add("history-entry-details"); + + const diceFormulaContainer = document.createElement("div"); + diceFormulaContainer.classList.add("formula-container"); + + const diceFormulaResult = document.createElement("span"); + diceFormulaResult.classList.add("result"); + diceFormulaResult.innerText = entry.result; + + const diceFormulaEqual = document.createElement("span"); + diceFormulaEqual.innerText = " = "; + + diceFormulaContainer.appendChild(diceFormulaResult); + diceFormulaContainer.appendChild(diceFormulaEqual); + + const diceFormula = document.createElement("span"); + diceFormula.classList.add("formula"); + + // If we have dices, show colored breakdown + if (entry.dices && entry.dices.length >= 1) { + // Parse the formula to show which dice contributed what + const formulaParts = entry.formula.split(" + "); + let partIndex = 0; + + // Create a mapping of which dice contributes which parts + let dicePartMapping = []; + entry.dices.forEach(dice => { + for (let i = 0; i < dice.count; i++) { + if (partIndex < formulaParts.length && !isNaN(parseInt(formulaParts[partIndex]))) { + dicePartMapping.push({ + partIndex: partIndex, + dice: dice + }); + partIndex++; + } + } + }); + + formulaParts.forEach((part, index) => { + const mapping = dicePartMapping.find(m => m.partIndex === index); + + if (mapping) { + // This part comes from a dice roll + const partSpan = document.createElement("span"); + partSpan.innerText = part; + partSpan.style.color = mapping.dice.color; + // revert b/w on dark-mode + if (darkMode) { + if (["#000", "#000000", "black", "rgb(0,0,0)", "rgb(0, 0, 0)"].indexOf(mapping.dice.color) != -1) { + partSpan.style.color = "#fff"; + } else if (["#fff", "#ffffff", "white", "rgb(255,255,255)", "rgb(255, 255, 255)"].indexOf(mapping.dice.color) != -1) { + partSpan.style.color = "#000"; + } + } + diceFormula.appendChild(partSpan); + } else { + // This is an addition value (modifier) + const partSpan = document.createElement("span"); + partSpan.innerText = part; + diceFormula.appendChild(partSpan); + } + + if (index < formulaParts.length - 1) { + const plusSpan = document.createElement("span"); + plusSpan.innerText = " + "; + diceFormula.appendChild(plusSpan); + } + }); + } else { + // Fallback to plain text + diceFormula.innerText = entry.formula; + } + + diceFormulaContainer.appendChild(diceFormula); + entryDetails.appendChild(diceFormulaContainer); + historyEntry.appendChild(entryDetails); + } + + historyContainer.appendChild(historyEntry); } else { + // Add separator for different roll sessions historyContainer.appendChild(document.createElement("hr")); } }) } function formula(dice) { + let rolls = []; + for (let index = 0; index < dice.count; index++) { + rolls.push(Math.floor(Math.random() * dice.sides + 1)); + } + rolls.sort((a, b) => b - a); let formula = ""; for (let index = 0; index < dice.count; index++) { - formula += Math.floor(Math.random() * dice.sides + 1); + formula += rolls[index]; if (index < dice.count - 1) { formula += " + "; } diff --git a/style.css b/style.css index f83dae5..8f52cf6 100755 --- a/style.css +++ b/style.css @@ -210,6 +210,9 @@ body { align-items: center; row-gap: 1em; grid-template-columns: auto; + width: 100%; + max-width: 100%; + box-sizing: border-box; } .form-container .actions { @@ -261,45 +264,114 @@ body { } .history { - display: grid; - grid-template-columns: repeat(4, auto); width: 100%; margin-top: 1.5em; margin-bottom: 0.5em; - row-gap: 0.3em; - column-gap: 0.3em; + display: flex; + flex-direction: column; + gap: 0.8em; } .history hr { width: 100%; - grid-column: span 4; + margin: 0.5em 0; + border: none; + border-top: 1px solid #ccc; +} + +.history-entry { + background: #fff; + border: 0.15em solid #000; + padding: 1em; + transition: all 0.2s ease; + position: relative; + overflow: hidden; +} + +.history-entry:hover { + background: #f1f3f4; +} + +.history-entry-header { + display: flex; + align-items: flex-start; + margin-bottom: 0.5em; + flex-wrap: wrap; + gap: 0.5em; +} + +.history-entry-header .label { + flex: 1; + min-width: 0; +} + +.history-entry-header .result { + flex: 0 0 auto; + margin: 0 1em; +} + +.history-entry-header .time { + flex: 0 0 auto; + margin-left: auto; + min-width: 8em; + text-align: right; } .history .label { - font-size: 1.2em; + font-size: 1.1em; display: flex; flex-wrap: wrap; + align-items: center; + gap: 0.3em; } .history .result { - text-align: center; - font-size: 1.2em; + font-size: 1.8em; font-weight: bold; + color: #2c3e50; + text-align: center; + min-width: 3em; +} + +.history .time { + font-size: 0.9em; + color: #6c757d; + text-align: right; + opacity: 0.8; +} + +.history-entry-details { + margin-top: 0.5em; + padding-top: 0.5em; + border-top: 0.1em solid #000; } .history .formula-container { display: flex; - margin: 0 0.5em; + align-items: center; + font-size: 0.95em; + color: #495057; } - .history .formula-container .formula { - display: flex; - margin: 0 0.5em; + margin-left: 0.5em; + font-family: 'Courier New', Courier, monospace; + font-size: 1.3em; + background: #f1f3f4; + padding: 0.2em 0.5em; + border: 0.1em solid #000; } -.history .time { - text-align: right; +.dice-icon { + display: inline-block; + width: 1.2em; + height: 1.2em; + background-color: currentColor; + mask-size: contain; + mask-repeat: no-repeat; + mask-position: center; + margin-right: 0.2em; + vertical-align: middle; } button { @@ -366,6 +438,37 @@ body.dark .form select { border-color: #fff; } +body.dark .history-entry { + background: #000; + border-color: #fff; +} + +body.dark .history .result { + color: #e9ecef; +} + +body.dark .history .time { + color: #adb5bd; +} + +body.dark .history .formula-container { + color: #adb5bd; +} + +body.dark .history .formula-container .formula { + background: #2d3436; + color: #ddd; + border-color: #fff; +} + +body.dark .history hr { + border-top-color: #fff; +} + +body.dark .history-entry-details { + border-top-color: #fff; +} + @media screen and (max-width: 767px) { .dices { font-size: 0.65em; @@ -381,4 +484,50 @@ body.dark .form select { min-width: auto; width: 100%; } + + .history-entry { + padding: 0.8em; + } + + .history-entry-header { + flex-wrap: wrap; + align-items: flex-start; + gap: 0.5em; + } + + .history-entry-header .label { + flex: 0 1 auto; + min-width: 0; + } + + .history-entry-header .result { + flex: 0 0 auto; + margin: 0; + } + + .history-entry-header .time { + flex: 0 0 auto; + min-width: 6em; + margin-left: auto; + text-align: right; + } + + .history .result { + font-size: 1.5em; + align-self: center; + } + + .history .time { + align-self: center; + font-size: 0.8em; + } + + .history .label { + font-size: 1em; + } + + .dice-icon { + width: 1em; + height: 1em; + } } \ No newline at end of file