-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial version of Recipe Converter Calculator
- Loading branch information
Showing
7 changed files
with
157 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,50 +1,109 @@ | ||
// Think Metric | ||
|
||
const app = { | ||
restoreChecklist(listElem) { | ||
// Set checklist task items according to previously saved values. | ||
const checklist = JSON.parse(globalThis.localStorage.getItem('checklist')); | ||
const setCheckbox = (li) => | ||
li.querySelector('input[type=checkbox]').checked = checklist[li.id]; | ||
if (checklist) | ||
[...listElem.children].forEach(setCheckbox); | ||
|
||
checklist: { | ||
restore(checklistSection) { | ||
// Set checklist task items according to previously saved values. | ||
const listElem = checklistSection.querySelector('ol.to-do-checklist'); | ||
const checklist = JSON.parse(globalThis.localStorage.getItem('checklist')); | ||
const getCheckbox = (li) => li.querySelector('input[type=checkbox]'); | ||
const setCheckbox = (li) => getCheckbox(li).checked = checklist[li.id]; | ||
if (checklist) | ||
[...listElem.children].forEach(setCheckbox); | ||
}, | ||
save(checklistElem) { | ||
// Record current status of checklist tasks to Local Storage. | ||
const tasks = [...checklistElem.querySelectorAll('li')]; | ||
const isChecked = (li) => li.querySelector('input[type=checkbox]').checked; | ||
const checklist = Object.fromEntries(tasks.map(li => [li.id, isChecked(li)])); | ||
globalThis.localStorage.setItem('checklist', JSON.stringify(checklist)); | ||
}, | ||
}, | ||
saveChecklist(checklistElem) { | ||
// Record current status of checklist tasks to Local Storage. | ||
const tasks = [...checklistElem.querySelectorAll('li')]; | ||
const isChecked = (li) => li.querySelector('input[type=checkbox]').checked; | ||
const checklist = Object.fromEntries(tasks.map(li => [li.id, isChecked(li)])); | ||
globalThis.localStorage.setItem('checklist', JSON.stringify(checklist)); | ||
|
||
calculator: { | ||
// <form class=calculator> | ||
// <label> | ||
// <input name=quantity> | ||
// <label> | ||
// <select name=units> | ||
// <option value=teaspoon data-type=volume data-per-cup=48> | ||
// <option value=lb data-type=weight data-grams=453.592> | ||
// <label> | ||
// <select name=ingredient> | ||
// <option id=input-ingredient class=dna-template>~~name~~</option> | ||
// <output> | ||
// <span id=metric-ingredient class=dna-template> | ||
// <b>~~grams~~</b> grams <b>~~form~~</b> <b>~~name~~</b> | ||
ingredients: [ | ||
{ name: 'Almonds', form: 'Sliced', gramsPerCup: 110 }, | ||
{ name: 'Almonds', form: 'Raw', gramsPerCup: 130 }, | ||
{ name: 'Almonds', form: 'Roasted', gramsPerCup: 120 }, | ||
{ name: 'Butter', form: null, gramsPerCup: 227 }, | ||
{ name: 'Honey', form: null, gramsPerCup: 340 }, | ||
], | ||
convertToGrams(elem) { | ||
const calculatorForm = elem.closest('form'); | ||
const elemMap = { | ||
quantity: calculatorForm.querySelector('input[name=quantity]'), | ||
units: calculatorForm.querySelector('select[name=units]'), | ||
ingredient: calculatorForm.querySelector('select[name=ingredient]'), | ||
}; | ||
const quantityText = elemMap.quantity.value.trim().replace(/[^0-9.\/\s]/g, '').replace(/\s+/g, ' '); | ||
const quantity = Number(quantityText); | ||
const unitsOption = elemMap.units.options[elemMap.units.selectedIndex]; | ||
const unitType = unitsOption.dataset.type; | ||
const unitsPerCup = Number(unitsOption.dataset.perCup); | ||
const unitsGrams = Number(unitsOption.dataset.grams); | ||
const ingredientName = elemMap.ingredient.value; | ||
const ingredients = app.calculator.ingredients.filter(ingredient => ingredient.name === ingredientName); | ||
ingredients.forEach(ingredient => | ||
ingredient.grams = quantity * (unitType === 'volume' ? | ||
ingredient.gramsPerCup / unitsPerCup : unitsGrams) | ||
); | ||
if (!isNaN(quantity)) | ||
dna.clone('metric-ingredient', ingredients, {empty: true}); | ||
}, | ||
init() { | ||
const allNames = app.calculator.ingredients.map(ingredient => ingredient.name); | ||
const ingredientNames = [...new Set(allNames)]; | ||
dna.clone('input-ingredient', ingredientNames); | ||
}, | ||
}, | ||
setupArticlePage() { | ||
// <div id=article-nav> | ||
// <i data-icon=circle-left></i> | ||
// <i data-icon=circle-right></i> | ||
// <ul><li><a href=../../article/go-metric>Go Metric<a><li>... | ||
// </div> | ||
const container = globalThis.document.getElementById('article-nav'); | ||
const articles = [...container.querySelectorAll('ul >li >a')]; | ||
const header = 'main >section:first-child >h2'; | ||
const title = globalThis.document.querySelector(header).textContent; | ||
const index = articles.findIndex(article => article.textContent === title); | ||
const configure = (button, index) => { | ||
button.setAttribute('data-href', articles[index]?.getAttribute('href')); | ||
button.setAttribute('title', articles[index]?.textContent); | ||
button.classList.add(index > -1 && index < articles.length ? 'show' : 'hide'); | ||
}; | ||
configure(container.children[0], index - 1); //previous article | ||
configure(container.children[1], index + 1); //next article | ||
container.classList.add('show'); | ||
|
||
article: { | ||
setupPage() { | ||
// <div id=article-nav> | ||
// <i data-icon=circle-left></i> | ||
// <i data-icon=circle-right></i> | ||
// <ul><li><a href=../../article/go-metric>Go Metric<a><li>... | ||
// </div> | ||
const container = globalThis.document.getElementById('article-nav'); | ||
const articles = [...container.querySelectorAll('ul >li >a')]; | ||
const header = 'main >section:first-child >h2'; | ||
const title = globalThis.document.querySelector(header).textContent; | ||
const index = articles.findIndex(article => article.textContent === title); | ||
const configure = (button, index) => { | ||
button.setAttribute('data-href', articles[index]?.getAttribute('href')); | ||
button.setAttribute('title', articles[index]?.textContent); | ||
button.classList.add(index > -1 && index < articles.length ? 'show' : 'hide'); | ||
}; | ||
configure(container.children[0], index - 1); //previous article | ||
configure(container.children[1], index + 1); //next article | ||
container.classList.add('show'); | ||
}, | ||
init() { | ||
if (globalThis.location.href.includes('/article/')) | ||
app.article.setupPage(); | ||
}, | ||
}, | ||
|
||
start() { | ||
console.log('Think Metric'); | ||
console.log('🇺🇸 Americans for Metrication 🇺🇸'); | ||
if (globalThis.location.href.includes('/article/')) | ||
app.setupArticlePage(); | ||
const checklistElem = globalThis.document.querySelector('ol.to-do-checklist'); | ||
if (checklistElem) | ||
app.restoreChecklist(checklistElem); | ||
app.article.init(); | ||
}, | ||
|
||
}; | ||
|
||
dna.dom.onReady(app.start); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
{% assign articleTitle = 'Recipe Converter Calculator' %} | ||
{% render 'src/templates/doc-begin.html', articleTitle: articleTitle %} | ||
|
||
<main> | ||
|
||
<section> | ||
<h2>{{articleTitle}}</h2> | ||
<h3>Enter imperial ingredient measurements to get metric recipe grams</h3> | ||
<form class=calculator data-on-load=app.calculator.init> | ||
<label> | ||
<span>Quantity (such as "3 1/2"):</span> | ||
<input name=quantity type=text autofocus data-on-input=app.calculator.convertToGrams> | ||
</label> | ||
<label> | ||
<span>Units:</span> | ||
<select name=units data-on-change=app.calculator.convertToGrams> | ||
<option value=teaspoon data-type=volume data-per-cup=48> Teaspoons</option> | ||
<option value=tablespoon data-type=volume data-per-cup=16> Tablespoons</option> | ||
<option value=floz data-type=volume data-per-cup=8> Fluid Ounces (fl oz)</option> | ||
<option value=stick data-type=volume data-per-cup=2> Sticks (4 oz)</option> | ||
<option value=cup data-type=volume data-per-cup=1 selected>Cups</option> | ||
<option value=block data-type=volume data-per-cup=0.5> Blocks (4 sticks)</option> | ||
<option value=pint data-type=volume data-per-cup=0.5> Pints</option> | ||
<option value=quart data-type=volume data-per-cup=0.25> Quarts</option> | ||
<option value=gallon data-type=volume data-per-cup=0.0625> Gallons</option> | ||
<option value=oz data-type=weight data-grams=28.3495> Ounces (oz weight)</option> | ||
<option value=lb data-type=weight data-grams=453.592> Pounds (lb weight)</option> | ||
</select> | ||
</label> | ||
<label> | ||
<span>Ingredient:</span> | ||
<select name=ingredient data-on-change=app.calculator.convertToGrams> | ||
<option id=input-ingredient class=dna-template>~~[value]~~</option> | ||
</select> | ||
</label> | ||
<output> | ||
<span id=metric-ingredient class=dna-template> | ||
<b data-precision=2 data-format-number=#>~~grams~~</b> <b>grams</b> | ||
<b>~~form~~</b> <b>~~name~~</b> | ||
</span> | ||
</output> | ||
</form> | ||
</section> | ||
|
||
</main> | ||
|
||
{% render 'src/templates/doc-end.html' %} |