Browse Source

ui optimizations

master
Stephanie Gredell 3 weeks ago
parent
commit
846e8be592
  1. 106
      index.html
  2. 115
      index.js
  3. 112
      style.css

106
index.html

@ -40,21 +40,42 @@ @@ -40,21 +40,42 @@
<h2 class="section_title">Recipe</h2>
<p class="section_hint">Per cup of lemonade</p>
<ul class="game_list">
<li>Lemons: <span data-bind="recipe.lemons">0</span></li>
<li>Sugar: <span data-bind="recipe.sugar">0</span></li>
<li>Ice: <span data-bind="recipe.ice">0</span></li>
<ul class="game_list recipe_list">
<li>
<span>Lemons</span>
<div class="recipe_stepper">
<button class="stepper_btn stepper_minus" data-ingredient="lemons"></button>
<span class="stepper_value" data-bind="recipe.lemons">0</span>
<button class="stepper_btn stepper_plus" data-ingredient="lemons">+</button>
</div>
</li>
<li>
<span>Sugar</span>
<div class="recipe_stepper">
<button class="stepper_btn stepper_minus" data-ingredient="sugar"></button>
<span class="stepper_value" data-bind="recipe.sugar">0</span>
<button class="stepper_btn stepper_plus" data-ingredient="sugar">+</button>
</div>
</li>
<li>
<span>Ice</span>
<div class="recipe_stepper">
<button class="stepper_btn stepper_minus" data-ingredient="ice"></button>
<span class="stepper_value" data-bind="recipe.ice">0</span>
<button class="stepper_btn stepper_plus" data-ingredient="ice">+</button>
</div>
</li>
</ul>
<button class="change_recipe_btn">Change Recipe</button>
</section>
<section class="game_section">
<h2 class="section_title">Current Price</h2>
<p class="section_hint">Click to edit</p>
<p data-bind="price_per_cup" data-format="currency"></p>
<button class="change_price_button">Change Price</button>
<div class="price_editable">
<span class="price_currency">$</span>
<input type="text" class="price_inline_input" inputmode="decimal" value="0.00">
</div>
</section>
<section class="game_section day_section">
@ -127,73 +148,6 @@ @@ -127,73 +148,6 @@
</div>
</div>
<div class="price_change_modal">
<div class="price_change_modal_content">
<div class="price_change_modal_header">
<h2 class="price_change_modal_title">Set Your Price</h2>
<button class="price_change_modal_close">&times;</button>
</div>
<div class="price_change_modal_body">
<p class="price_change_hint">Choose how much to charge per cup of lemonade.</p>
<div class="price_input_group">
<span class="price_currency">$</span>
<input type="text" class="price_input" inputmode="decimal">
</div>
<button class="price_change_save_btn">Save Price</button>
</div>
</div>
</div>
<div class="recipe_modal">
<div class="recipe_modal_content">
<div class="recipe_modal_header">
<h2 class="recipe_modal_title">Set Your Recipe</h2>
<button class="recipe_modal_close">&times;</button>
</div>
<div class="recipe_modal_body">
<p class="recipe_modal_hint">Ingredients per cup of lemonade</p>
<div class="recipe_grid">
<div class="recipe_item">
<label class="recipe_label">Lemons</label>
<input type="number" class="recipe_input" data-recipe="lemons" min="0" max="10" value="1">
</div>
<div class="recipe_item">
<label class="recipe_label">Sugar</label>
<input type="number" class="recipe_input" data-recipe="sugar" min="0" max="10" value="1">
</div>
<div class="recipe_item">
<label class="recipe_label">Ice</label>
<input type="number" class="recipe_input" data-recipe="ice" min="0" max="10" value="3">
</div>
</div>
<div class="recipe_cost_breakdown">
<h3 class="cost_breakdown">Cost Breakdown</h3>
<div class="recipe_cost_row">
<span>Lemons</span>
<span class="recipe_cost_item" data-cost="lemons">$0.00</span>
</div>
<div class="recipe_cost_row">
<span>Sugar</span>
<span class="recipe_cost_item" data-cost="sugar">$0.00</span>
</div>
<div class="recipe_cost_row">
<span>Ice</span>
<span class="recipe_cost_item" data-cost="ice">$0.00</span>
</div>
<div class="recipe_cost_row">
<span>Cup</span>
<span class="recipe_cost_item" data-cost="cup">$0.01</span>
</div>
<div class="recipe_cost_row total">
<span>Total per cup</span>
<span class="recipe_cost_value">$0.00</span>
</div>
</div>
<button class="recipe_save_btn">Save Recipe</button>
</div>
</div>
</div>
<canvas id="scene" width="1200" height="500"></canvas>
<script type="module" src="index.js"></script>
</body>

115
index.js

@ -134,19 +134,8 @@ const goShoppingBtn = document.querySelector('.go_shopping_btn'); @@ -134,19 +134,8 @@ const goShoppingBtn = document.querySelector('.go_shopping_btn');
const shoppingModal = document.querySelector('.shopping_modal');
const shoppingModalClose = document.querySelector('.shopping_modal_close');
const changePriceBtn = document.querySelector('.change_price_button');
const priceModal = document.querySelector('.price_change_modal');
const priceModalClose = document.querySelector('.price_change_modal_close');
const priceInput = document.querySelector('.price_input');
const priceSaveBtn = document.querySelector('.price_change_save_btn');
const changeRecipeBtn = document.querySelector('.change_recipe_btn');
const recipeModal = document.querySelector('.recipe_modal');
const recipeModalClose = document.querySelector('.recipe_modal_close');
const priceInlineInput = document.querySelector('.price_inline_input');
const startDayBtn = document.querySelector('.start_day_btn');
const recipeSaveBtn = document.querySelector('.recipe_save_btn');
// Shopping modal - quantity inputs and dynamic pricing
const shopQtyInputs = document.querySelectorAll('.shop_qty_input');
@ -203,87 +192,53 @@ if (goShoppingBtn) { @@ -203,87 +192,53 @@ if (goShoppingBtn) {
})
}
if (changePriceBtn) {
changePriceBtn.addEventListener('click', () => {
priceModal.classList.add('open');
priceInput.focus();
const priceInputLength = priceInput.value.length;
// Inline price editing
if (priceInlineInput) {
// Save price on blur or Enter
function savePrice() {
const value = parseFloat(priceInlineInput.value) || 0;
const newState = set_price_per_cup(gameState, value);
setState(newState);
priceInlineInput.value = gameState.price_per_cup.toFixed(2);
}
priceInput.setSelectionRange(priceInputLength, priceInputLength);
priceInlineInput.addEventListener('blur', savePrice);
priceInlineInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
priceInlineInput.blur();
}
});
priceModalClose.addEventListener('click', () => {
priceModal.classList.remove('open');
// Select all text on focus for easy editing
priceInlineInput.addEventListener('focus', () => {
priceInlineInput.select();
});
priceSaveBtn.addEventListener('click', () => {
const newState = set_price_per_cup(gameState, Number(priceInput.value));
setState(newState);
priceModal.classList.remove('open');
})
// Initialize with current value
priceInlineInput.value = gameState.price_per_cup.toFixed(2);
}
// Recipe modal handlers
const recipeInputs = document.querySelectorAll('.recipe_input');
const recipeCostValue = document.querySelector('.recipe_cost_value');
// Base prices for cost breakdown (matches SupplyPricing tier 1 in game.js)
const basePrices = { lemons: 0.02, sugar: 0.01, ice: 0.01, cup: 0.01 };
function updateRecipeCost() {
const lemons = parseInt(document.querySelector('.recipe_input[data-recipe="lemons"]').value) || 0;
const sugar = parseInt(document.querySelector('.recipe_input[data-recipe="sugar"]').value) || 0;
const ice = parseInt(document.querySelector('.recipe_input[data-recipe="ice"]').value) || 0;
// Update breakdown rows
document.querySelector('.recipe_cost_item[data-cost="lemons"]').textContent = '$' + (lemons * basePrices.lemons).toFixed(2);
document.querySelector('.recipe_cost_item[data-cost="sugar"]').textContent = '$' + (sugar * basePrices.sugar).toFixed(2);
document.querySelector('.recipe_cost_item[data-cost="ice"]').textContent = '$' + (ice * basePrices.ice).toFixed(2);
document.querySelector('.recipe_cost_item[data-cost="cup"]').textContent = '$' + basePrices.cup.toFixed(2);
// Update total
const result = calculate_cost_per_cup(gameState, { lemons, sugar, ice });
recipeCostValue.textContent = '$' + result.cost_per_cup.toFixed(2);
// Start Day button
if (startDayBtn) {
startDayBtn.addEventListener('click', startDay);
}
// Update cost when recipe inputs change
recipeInputs.forEach(input => {
input.addEventListener('input', updateRecipeCost);
});
if (changeRecipeBtn) {
changeRecipeBtn.addEventListener('click', () => {
// Set inputs to current recipe values
document.querySelector('.recipe_input[data-recipe="lemons"]').value = gameState.recipe.lemons;
document.querySelector('.recipe_input[data-recipe="sugar"]').value = gameState.recipe.sugar;
document.querySelector('.recipe_input[data-recipe="ice"]').value = gameState.recipe.ice;
updateRecipeCost();
recipeModal.classList.add('open');
});
recipeModalClose.addEventListener('click', () => {
recipeModal.classList.remove('open');
});
recipeSaveBtn.addEventListener('click', () => {
const lemons = parseInt(document.querySelector('.recipe_input[data-recipe="lemons"]').value) || 0;
const sugar = parseInt(document.querySelector('.recipe_input[data-recipe="sugar"]').value) || 0;
const ice = parseInt(document.querySelector('.recipe_input[data-recipe="ice"]').value) || 0;
const result = calculate_cost_per_cup(gameState, { lemons, sugar, ice });
// Recipe stepper buttons
document.querySelectorAll('.stepper_btn').forEach(btn => {
btn.addEventListener('click', () => {
const ingredient = btn.dataset.ingredient;
const isPlus = btn.classList.contains('stepper_plus');
const currentValue = gameState.recipe[ingredient];
const newValue = isPlus ? currentValue + 1 : Math.max(0, currentValue - 1);
const newRecipe = { ...gameState.recipe, [ingredient]: newValue };
const result = calculate_cost_per_cup(gameState, newRecipe);
setState({
recipe: { lemons, sugar, ice },
recipe: newRecipe,
cost_per_cup: result.cost_per_cup
});
recipeModal.classList.remove('open');
});
}
// Start Day button
if (startDayBtn) {
startDayBtn.addEventListener('click', startDay);
}
});
// Export for debugging in console
window.gameState = gameState;

112
style.css

@ -11,7 +11,10 @@ body { @@ -11,7 +11,10 @@ body {
.game_header {
display: flex;
margin: 0 auto;
width: 500px;
max-width: 500px;
width: 100%;
padding: 0 16px;
box-sizing: border-box;
align-items: center;
}
@ -96,6 +99,94 @@ body { @@ -96,6 +99,94 @@ body {
margin-bottom: 0;
}
/* Recipe stepper buttons */
.recipe_stepper {
display: flex;
align-items: center;
gap: 8px;
}
.stepper_btn {
font-family: 'Fredoka', sans-serif;
font-size: 18px;
font-weight: 600;
width: 28px;
height: 28px;
border-radius: 6px;
border: 2px solid #c5e8a8;
background: linear-gradient(180deg, #fff 0%, #f5f5f0 100%);
color: #5C4632;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.1s ease;
padding: 0;
line-height: 1;
}
.stepper_btn:hover {
border-color: #8fd16a;
background: linear-gradient(180deg, #f0ffe0 0%, #e5f5d5 100%);
}
.stepper_btn:active {
transform: scale(0.95);
}
.stepper_value {
font-family: 'Fredoka', sans-serif;
font-size: 18px;
font-weight: 600;
color: #5C4632;
min-width: 24px;
text-align: center;
}
/* Inline editable price */
.price_editable {
display: flex;
align-items: center;
justify-content: center;
margin: 12px 0;
}
.price_editable .price_currency {
font-family: 'Fredoka', sans-serif;
font-size: 28px;
font-weight: 600;
color: #5C4632;
margin-right: 2px;
}
.price_inline_input {
font-family: 'Fredoka', sans-serif;
font-size: 28px;
font-weight: 600;
color: #5C4632;
background: transparent;
border: 2px solid transparent;
border-radius: 8px;
padding: 4px 8px;
width: 80px;
text-align: center;
cursor: pointer;
transition: all 0.15s ease;
}
.price_inline_input:hover {
background: rgba(143, 209, 106, 0.15);
border-color: #c5e8a8;
}
.price_inline_input:focus {
outline: none;
background: #fff;
border-color: #8fd16a;
cursor: text;
box-shadow: 0 0 0 3px rgba(143, 209, 106, 0.25);
}
.game_list li span {
font-family: 'Fredoka', sans-serif;
font-size: 16px;
@ -185,8 +276,8 @@ body { @@ -185,8 +276,8 @@ body {
}
canvas {
width: 1200px;
height: 500px;
max-width: 100%;
height: auto;
margin: 0 auto;
display: block;
pointer-events: none;
@ -796,3 +887,18 @@ canvas { @@ -796,3 +887,18 @@ canvas {
transform: translateY(2px);
box-shadow: 0 2px 0 #3f7a33;
}
/* Responsive adjustments for tablets */
@media (max-width: 768px) {
.game_header_title {
font-size: 36px;
}
.game_section > p {
font-size: 22px;
}
.dashboard {
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
}
}

Loading…
Cancel
Save