Add qr codes to recipes

This commit is contained in:
Nicholas Jitkoff 2022-06-15 07:50:03 -07:00
parent 946f0b57fc
commit 597a0482af
3 changed files with 157 additions and 83 deletions

View file

@ -39,3 +39,11 @@ function renderScriptContent(data, origin) {
window.addEventListener("message", function(e) {
renderScriptContent(e.data, e.origin);
}, false);
function QRCodeURL(url, options) {
let size = options?.size ?? 547;
let errorCorrection = options?.correction ?? 'L';
let margin = options?.margin?.toString() || "1";
console.log("margin", options?.margin);
return `https://chart.googleapis.com/chart?cht=qr&chs=${size}x${size}&chld=${errorCorrection}|${margin}&choe=UTF-8&chl=${encodeURIComponent(url || location.href)}`;
}

View file

@ -7,23 +7,27 @@ body {
background-color: var(--background-color);
color: var(--text-color);
--text-color:rgba(0,0,0,0.87);
--text-color:
rgba(0,0,0,0.87);
--opaque-text-color:black;
--disabled-text-color:rgba(16, 12, 12, 0.54);
--disabled-text-color:
rgba(16, 12, 12, 0.54);
--hover-background-color:
rgba(16, 12, 12, 0.07);
--background-color:white;
}
.thumbnail {
width: 100%;
padding-top:28.125%;
padding-top: 77%;
max-height:1vh;
background-color: gray;
background-size: cover;
background-position: center;
margin-bottom:2em;
box-sizing: border-box;
/* margin: 0 -2em; */}
/* margin: 0 -2em; */
}
.ingredients:hover .noun {
/* text-decoration:underline; */
@ -96,7 +100,8 @@ ul.step {
span.number {
margin-left: -27px;
float:left;
float:
left;
border: 1.5px solid var(--text-color);
font-weight: 800;
border-radius:10em;
@ -105,9 +110,9 @@ span.number {
text-align: center;
font-size: 10px;
line-height: 16px;
opacity:0.7;
opacity: 0.7;
user-select: none;
/* background: black; */
/* background: black; */
}
.complete span.number,
@ -184,16 +189,27 @@ li:hover {
}
.ingredients {
font-weight: 500;
font-size: 90%;
font-weight: 600;
font-size: 80%;
/* position:sticky; */
/* top: 0px; */
/* padding-top: 1em; */
flex: 0 1 35%;
/* color: rgba(0,0,0,0.8); */}
/* color: rgba(0,0,0,0.8); */
/* color:rgb(0, 118, 173); */
}
.noun {
font-weight: 700;
font-weight: 600;
color: var(--opaque-text-color);
}
.complete .noun {
color:var(--disabled-text-color);
}
li:hover .noun {
text-decoration: 1px dotted underline;
/* background-color: var(--hover-background-color); */
}
.instructions {
@ -257,11 +273,12 @@ text-decoration: rgba(0,0,0,0.75) solid 0.5px line-through; /* Ignored in CSS1/C
}
.instructions hr {
width: 100px;
width: 5em;
height: 0.5px;
background-color: currentColor;
opacity: .3;
border:none;
opacity: 1;
border:
none;
margin: 0.25em 0;
}
@ -276,30 +293,37 @@ hr {
opacity: 0.54; */
}
a.action {
/* border: none;
background: none;
text-transform: uppercase;
text-decoration: none;
color: currentColor;
opacity: 0.;
cursor: pointer;
border: 1px solid currentColor;
padding: 0 0.6em;
border-radius: 1em;
/* line-height: 16px;
font-size: 14px;
box-sizing: border-box;
line-height: 17px; */
border-radius:1em;
cursor:pointer;
.actions {
display: flex;
gap: 0.5em 1em;
line-height:24px;
margin-left: -4px;
}
a.action {
border-radius:50%;
cursor: pointer;
width: 1.5em;
height: 1.5em;
/* display: flex;
align-items: center;
justify-content: center; */
text-align: center;
line-height: 1.5em;
background-color:
var(--hover-background-color);
}
a.action:hover {
background-color:var(--hover-background-color);
}
a.action:hover:not(:active) .icon {
opacity:1.0;
/* text-decoration: none;
box-shadow: 0 1px 1px currentColor;
margin-top: -1px; */
}
a.action .icon {
margin-right: 0;
opacity: 1.0;
}
@media screen and (max-width: 480px) {
@ -335,22 +359,21 @@ a.action:hover:not(:active) .icon {
}
.metadata .icon {
font-size: 15px;
vertical-align: middle;
margin-top: -4px;
margin-right: 4px;
opacity:0.6;
font-size: 15px;vertical-align: middle;margin-top: -4px;margin-right: 4px;opacity: .6;
color:var(--text-color);
user-select: none;
}
@media (prefers-color-scheme: dark) {
body {
--text-color:white;
--hover-background-color:rgba(255,255,255, 0.14);
--background-color:#222;
}
}
@media print {
body {
font-size: 14px;
@ -360,6 +383,40 @@ color:var(--text-color);
}
}
.instructions.numbered hr {margin: 0.7em 0;height: 1.5px;opacity: 10;}
@media not print {
.print-only {
display:none;
}
}
.instructions.numbered {/* margin-top: -1.6em; */}
.instructions.numbered hr {margin: 0.7em -1px;height: 1.5px;opacity: 0.2;width: auto;}
.instructions.numbered {/* margin-top: -1.6em; */}
img.qr {
width:100%;
}
@media screen and (min-width: 1000px) {
body {
width:auto;
margin:0;
}
.recipe {
flex-direction: row;
display: flex;
max-width: unset;
}
#thumbnail-container {width: 100%;height: 100vh;}
.thumbnail {
max-height: 100vh;
height: 100vh;
}
.recipe article {
max-width:50em;
padding: 4em;
}
}

View file

@ -25,8 +25,8 @@ FRACTION_MAP = {
}
let ignoredTerms = [
"teaspoon", "teaspoons", "tablespoon", "tablespoons", "cup", "cups", "taste", "more", "melted", "into", "wide", "pound", "pounds", "gram", "grams", "you", "ounce", "ounces", "thinly", "sliced",
"pan", "finely", "ground", "garnish", "about", "cut", "and", "smashed", "each", "the", "medium", "large", "small", "for", "chopped", "minced", "grated", "box", "softened", "directed", "shredded", "cooked", "from", "frozen", "thawed"
"note", "teaspoon", "teaspoons", "tablespoon", "tablespoons", "cup", "cups", "taste", "more", "melted", "into", "wide", "pound", "pounds", "gram", "grams", "you", "ounce", "ounces", "thinly", "sliced",
"pan", "cube", "cubes", "finely", "ground", "garnish", "about", "cut", "and", "smashed", "each", "the", "medium", "large", "small", "for", "chopped", "minced", "grated", "box", "softened", "directed", "shredded", "cooked", "from", "frozen", "thawed"
]
const replacements = {
@ -142,7 +142,7 @@ function highlightStep(e) {
}
const ingredientMatch = /^(?:A )?([\/0-9 \u00BC-\u00BE\u2153-\u215E\u2009]*) (.*)/
const ingredientMatch = /^(?:A )?([\/0-9 \u00BC-\u00BE\u2153-\u215E\u2009]*)\s(.*)/
function ingredientEl(string, terms) {
@ -164,6 +164,10 @@ function highlightTerms(string, terms) {
return string.replace(pattern, match => `<span class="noun" id="term-${match}">${match}</span>`);
}
function share() {
parent.postMessage({share:{}}, "*");
}
function render() {
delete document.documentElement.style.display;
document.body.childNodes.forEach((c) => document.body.removeChild(c))
@ -286,50 +290,55 @@ function render() {
document.body.appendChild(
m("article.recipe", {},
m(".recipe", {},
image ? m("#thumbnail-container", m("#thumbnail.thumbnail.noprint", { style: "background-image:url(" + ");", onload: imgload })) : null,
m("header",
m("img.publisher", { src: json.publisher?.image ?.[0]?.url ?? json.publisher ?.logo ?.url }),
m("h1", title),
m(".metadata",
m("div", m("span.yield", m(".icon.material-icons-outlined", "restaurant"), yield)),
m(".time",
m(".icon.material-icons-outlined", "timer"),
m("article",
m("header",
m("img.publisher", { src: json.publisher?.image ?.[0]?.url ?? json.publisher ?.logo ?.url }),
m("h1", title),
m(".metadata",
m("div", m("span.yield", m(".icon.material-icons-outlined", "restaurant"), yield)),
m(".time",
m(".icon.material-icons-outlined", "timer"),
json.totalTime ? m("span", formatTime(json.totalTime)) : undefined,
// " (",
// json.prepTime ? m("span", formatTime(json.prepTime), " prep") : undefined,
// json.cookTime ? m("span", ", ", formatTime(json.cookTime), " cook") : undefined,
// ")"
json.totalTime ? m("span", formatTime(json.totalTime)) : undefined,
// " (",
// json.prepTime ? m("span", formatTime(json.prepTime), " prep") : undefined,
// json.cookTime ? m("span", ", ", formatTime(json.cookTime), " cook") : undefined,
// ")"
),
(rating) ? m("div.rating",
m(".icon.material-icons-outlined", "grade"),
parseFloat(rating.ratingValue).toFixed(1), " ",
// ratingCount ? m("span.count", ratingCount.toString()) : null
)
: null,
json.nutrition?.calories ?
m("div", m(".icon.material-icons-outlined", "info"),
(json.nutrition?.calories.toString().replace("calories", "Cal").replace("kcal", "Cal")) + (isNaN(json.nutrition?.calories) ? '' : ' Cal')) : null,
m("div.spacer"),
m(".actions",
m("a.action.noprint", { title:"Open original", href: json.mainEntityOfPage || json.url, target:"_blank"}, m(".icon.material-icons-outlined", "public")),
m("a.action.noprint", { title:"Share", onclick: share}, m(".icon.material-icons-outlined", "share")),
m("a.action.noprint", { title:"Show steps as list", href: "#", onclick: () => {reformat = !reformat; render(); return false;}}, m(".icon.material-icons-outlined", "notes")),
m("a.action.noprint", { title:"Print", href: "#", onclick: () => window.print() }, m(".icon.material-icons-outlined", "print")),
)
),
(rating) ? m("div.rating",
m(".icon.material-icons-outlined", "grade"),
parseFloat(rating.ratingValue).toFixed(1), " ",
// ratingCount ? m("span.count", ratingCount.toString()) : null
)
: null,
json.nutrition?.calories ? m("div", m(".icon.material-icons-outlined", "info"), (json.nutrition?.calories) + (parseFloat(json.nutrition?.calories) != NaN ? " calories" : "")) : null,
json.description ? m(".description",
clean(json.description),
json.author?.name ? m("span.author", (" —⁠" + json.author?.name)) : null,
m("div.spacer"),
m("a.action.noprint", { title:"Open original", href: json.mainEntityOfPage || json.url, target:"_blank"}, m(".icon.material-icons-outlined", "link")),
m("a.action.noprint", { title:"Show steps as list", href: "#", onclick: () => {reformat = !reformat; render(); return false;}}, m(".icon.material-icons-outlined", "format_list_numbered")),
m("a.action.noprint", { title:"Print", href: "#", onclick: () => window.print() }, m(".icon.material-icons-outlined", "print")),
m("p"),
) : null,
),
json.description ? m(".description",
clean(json.description),
json.author?.name ? m("span.author", (" —⁠" + json.author?.name)) : null,
m("p"),
) : null,
),
m(".columns",
m(".ingredients", ingredients),
m(reformat ? ".instructions.numbered" : ".instructions", instructions)
),
m("main.columns",
m("section.ingredients", ingredients, m("img.qr.print-only", {src:QRCodeURL(params.originalURL, {margin:0})})),
m(reformat ? "section.instructions.numbered" : "section.instructions", instructions)
),
)
)
)