Skip to content

Commit

Permalink
Initial version of Recipe Converter Calculator
Browse files Browse the repository at this point in the history
  • Loading branch information
dpilafian committed May 8, 2024
1 parent 22c8418 commit 8a81ebe
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 41 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
"chokidar-cli": "~3.0",
"copy-file-util": "~1.2",
"copy-folder-util": "~1.1",
"dna-engine": "~3.1",
"dna-engine": "~3.2",
"jshint": "~2.13",
"less": "~4.2",
"replacer-util": "~1.3",
Expand Down
3 changes: 3 additions & 0 deletions src/templates/doc-begin.html
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@
<a href={{webRoot}}>Home</a>
<a href={{webRoot}}/products>Products</a>
<a href={{webRoot}}/do-not-learn-metric>Just Use It</a>
<!--
<a href={{webRoot}}/recipe-converter-calculator>Calculator</a>
-->
<a href={{webRoot}}/elsewhere>Elsewhere</a>
</nav>
<h1>Think Metric</h1>
Expand Down
2 changes: 1 addition & 1 deletion src/templates/to-do-checklist.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{% assign github = 'https://github.com/center-key/think-metric' %}
{% assign reddit = 'https://www.reddit.com/r/ThinkMetric' %}

<ol class=to-do-checklist data-on-click=app.saveChecklist>
<ol class=to-do-checklist data-on-click=app.checklist.save>
<li id=phone> <p>Set your mobile device to metric</p> <label><input type=checkbox><b></b></label></li>
<li id=car> <p>Set the display in your car to Celsius (&deg;C)</p> <label><input type=checkbox><b></b></label></li>
<li id=home> <p>Set your home thermostat to Celsius (&deg;C)</p> <label><input type=checkbox><b></b></label></li>
Expand Down
133 changes: 96 additions & 37 deletions src/website/app.js
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);
9 changes: 8 additions & 1 deletion src/website/article/rules/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,14 @@ <h2>Metric clock gibberish is prohibited</h2>
The base SI unit for time is the second, and the other units of time, like minute and hour, are
just convenient derivations of the second.&nbsp;
</p>
<figure>⚖️ ⚖️ ⚖️</figure>
<p>
Other prohibited gibberish includes pointless navel-gazing about
using Kelvin instead of Celsius,
changing metric to a base system other than base-10,
derisive comparisions to Myanmar and Liberia,
and lamenting about how Caribbean pirates stole America's official 1 kilogram weight.
</p>
<figure>🚔 🚔 🚔</figure>
</section>

</main>
Expand Down
2 changes: 1 addition & 1 deletion src/website/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ <h3>Go metric to boost your efficiency</h3>
</p>
</section>

<section id=checklist>
<section id=checklist data-on-load=app.checklist.restore>
<h2>Your Personal Metrication Checklist</h2>
{% render 'src/templates/to-do-checklist.html' %}
</section>
Expand Down
47 changes: 47 additions & 0 deletions src/website/recipe-converter-calculator/index.html
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' %}

0 comments on commit 8a81ebe

Please sign in to comment.