mirror of
https://github.com/arfct/itty-bitty.git
synced 2026-03-11 08:54:33 +00:00
Add timers
This commit is contained in:
parent
c97711ccd8
commit
03a071f04c
5 changed files with 181 additions and 46 deletions
|
|
@ -79,6 +79,7 @@ window.addEventListener("message", function(e) {
|
|||
}, false);
|
||||
|
||||
function QRCodeURL(url, options) {
|
||||
if (url.length > 2953) return undefined;
|
||||
let size = options?.size ?? 547;
|
||||
let errorCorrection = options?.correction ?? 'L';
|
||||
let margin = options?.margin?.toString() || "1";
|
||||
|
|
|
|||
|
|
@ -14,8 +14,11 @@ body {
|
|||
rgba(16, 12, 12, 0.54);
|
||||
--hover-background-color:
|
||||
rgba(16, 12, 12, 0.07);
|
||||
--image-placeholder-color: var(--hover-background-color);
|
||||
--image-placeholder-color:
|
||||
var(--hover-background-color);
|
||||
--background-color:white;
|
||||
--alt-text-color:
|
||||
rgb(0, 118, 173);
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
|
|
@ -39,18 +42,62 @@ body {
|
|||
/* text-decoration:underline; */
|
||||
}
|
||||
|
||||
.time-button {
|
||||
/* background-color:cyan; */
|
||||
white-space: nowrap;
|
||||
border-radius:1em;
|
||||
/* padding:0 0.3em; */
|
||||
/* border:1px solid currentColor; */
|
||||
text-decoration:underline;
|
||||
text-decoration-style: dotted;
|
||||
|
||||
@media screen {
|
||||
.timer {
|
||||
/* background-color:cyan; */
|
||||
white-space: nowrap;
|
||||
border-radius: 2px;
|
||||
padding: 0 0.1em;
|
||||
margin: 0 -0.1em;
|
||||
/* text-decoration:underline; */
|
||||
text-decoration-style: dotted;
|
||||
box-sizing: border-box;
|
||||
/* color: var(--background-color); */
|
||||
|
||||
border-bottom:1px solid transparent;
|
||||
border-bottom-color: currentColor;
|
||||
/* background-color: var(--text-color); */
|
||||
}
|
||||
|
||||
.timer:hover,
|
||||
.timer.active {
|
||||
color: var(--background-color);
|
||||
background-color:var(--text-color);
|
||||
z-index:100;
|
||||
/* border:1px solid currentColor; */
|
||||
position:relative;
|
||||
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
|
||||
.timer.active .label{
|
||||
opacity:0.0;
|
||||
}
|
||||
.timer .countdown{
|
||||
position:
|
||||
absolute;
|
||||
display:
|
||||
none;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
left: 0;
|
||||
}
|
||||
.timer.active .countdown {
|
||||
display:inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media print {
|
||||
.timer {
|
||||
font-weight:800;
|
||||
}
|
||||
}
|
||||
|
||||
img.publisher {
|
||||
max-width: 5em;
|
||||
max-height: 2em;
|
||||
/* margin-top: -0.1em; */
|
||||
/* transform:scale(0.7); */
|
||||
/* float:
|
||||
|
|
@ -65,11 +112,11 @@ img.publisher {
|
|||
|
||||
.columns {
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
gap: 3em;
|
||||
margin-bottom: 4em;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
flex-direction: row;
|
||||
flex-direction: row-reverse;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
|
|
@ -136,15 +183,29 @@ span.number {
|
|||
/* background: black; */
|
||||
}
|
||||
|
||||
.complete span.number,
|
||||
li:hover span.number {
|
||||
color:transparent;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='48' width='48'%3E%3Cpath d='M18.9 36.9 6.5 24.5 9.9 21.1 18.9 30.1 38.05 10.95 41.45 14.35Z'/%3E%3C/svg%3E"); background-size:cover;
|
||||
@media screen {
|
||||
.complete span.number, li:hover span.number {
|
||||
color:transparent;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='48' width='48'%3E%3Cpath d='M18.9 36.9 6.5 24.5 9.9 21.1 18.9 30.1 38.05 10.95 41.45 14.35Z'/%3E%3C/svg%3E"); background-size:cover;
|
||||
|
||||
}
|
||||
.complete span.number {
|
||||
border-color:transparent;
|
||||
}
|
||||
.complete span.number {
|
||||
border-color:transparent;
|
||||
|
||||
}
|
||||
|
||||
.complete .noun {
|
||||
color:var(--disabled-text-color);
|
||||
}
|
||||
.ingredient.complete,
|
||||
li.complete {
|
||||
text-decoration: line-through;
|
||||
color:var(--disabled-text-color);
|
||||
|
||||
text-decoration: line-through;
|
||||
/*noinspection CssOverwrittenProperties*/
|
||||
text-decoration: rgba(0,0,0,0.75) solid 0.5px line-through; /* Ignored in CSS1/CSS2 UAs */
|
||||
}
|
||||
}
|
||||
|
||||
.number.big {
|
||||
|
|
@ -212,22 +273,20 @@ li:hover {
|
|||
.ingredients {
|
||||
font-weight: 600;
|
||||
font-size: 88%;
|
||||
/* position:
|
||||
sticky; */
|
||||
position: sticky;
|
||||
/* top: 0px; */
|
||||
/* padding-top: 1.5em; */
|
||||
flex: 0 1 35%;
|
||||
/* color: rgba(0,0,0,0.8); */
|
||||
/* color:rgb(0, 118, 173); */
|
||||
/* color:
|
||||
rgb(0, 118, 173); */
|
||||
}
|
||||
|
||||
.noun {
|
||||
font-weight: 700;
|
||||
color: var(--opaque-text-color);
|
||||
}
|
||||
.complete .noun {
|
||||
color:var(--disabled-text-color);
|
||||
color: var(--alt-text-color);
|
||||
}
|
||||
|
||||
li:hover .noun {
|
||||
text-decoration: 1px dotted underline;
|
||||
/* background-color: var(--hover-background-color); */
|
||||
|
|
@ -274,16 +333,6 @@ li:hover .noun {
|
|||
right;
|
||||
}
|
||||
|
||||
.ingredient.complete,
|
||||
li.complete {
|
||||
text-decoration: line-through;
|
||||
color:var(--disabled-text-color);
|
||||
|
||||
text-decoration: line-through;
|
||||
/*noinspection CssOverwrittenProperties*/
|
||||
text-decoration: rgba(0,0,0,0.75) solid 0.5px line-through; /* Ignored in CSS1/CSS2 UAs */
|
||||
}
|
||||
|
||||
.ingredient:hover {
|
||||
opacity: 1.0;
|
||||
background-color: rgba(128, 128, 128, 0.1)
|
||||
|
|
@ -291,7 +340,7 @@ text-decoration: rgba(0,0,0,0.75) solid 0.5px line-through; /* Ignored in CSS1/C
|
|||
|
||||
.step {
|
||||
max-width: 80em;
|
||||
cursor: pointer;
|
||||
cursor: default;
|
||||
/* border:1px solid red; */
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
|
@ -422,6 +471,7 @@ a.action .icon {
|
|||
user-select: none;
|
||||
/* float: left; */
|
||||
display: inline-block;
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -431,7 +481,7 @@ a.action .icon {
|
|||
min-height:100vh;
|
||||
}
|
||||
.print-hide {
|
||||
display: none;
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@page {
|
||||
|
|
@ -544,7 +594,7 @@ img.qr {
|
|||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.icon.timer {
|
||||
.icon.time {
|
||||
background-position: -72px -16px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
|
|
@ -647,4 +697,12 @@ a.publisherlink {
|
|||
/* order: -2; */
|
||||
/* width: 100%; */
|
||||
float: left;
|
||||
}
|
||||
|
||||
.headerleft h1 {
|
||||
max-width: 16em;
|
||||
}
|
||||
|
||||
span.timer.active.expired {
|
||||
background-color: rgb(221, 40, 0);
|
||||
}
|
||||
|
|
@ -265,6 +265,7 @@ function highlightStep(e) {
|
|||
// el.classList.toggle("complete", )
|
||||
// }
|
||||
if (e.target.classList.contains("noun")) return;
|
||||
if (e.target.closest(".timer")) return;
|
||||
if (e.target.tagName == "A") return;
|
||||
e.target.closest("li").classList.toggle("complete")
|
||||
|
||||
|
|
@ -293,12 +294,67 @@ function highlightTerms(string, terms) {
|
|||
}
|
||||
|
||||
function highlightTimes(string) {
|
||||
const pattern = /([0-9]+) ?(minutes|hours)/g;
|
||||
return string.replace(pattern, match => {
|
||||
return `<span class="time-button" onclick="setTimer(${match})">${match}</span>`
|
||||
const pattern = /([0-9]+)\s*(?:to|-|–)?\s*([0-9]+)?\s?(minutes?|hours?)/g;
|
||||
return string.replace(pattern, (match, t1, t2, tt, offset, groups) => {
|
||||
// console.log("match", t1, t2, tt)
|
||||
|
||||
return `<span class="timer" onclick="startTimer(this, ${t1}, ${t2})"><span class="countdown"></span><span class="label">${match}</span></span>`
|
||||
});
|
||||
}
|
||||
|
||||
function dismissTimer(e) {
|
||||
console.log(e.target);
|
||||
}
|
||||
|
||||
function playSound(loc) {
|
||||
var audio = new Audio(loc);
|
||||
// audio.play();
|
||||
}
|
||||
|
||||
function startTimer(e, t1, t2) {
|
||||
if (!e.startTime) {
|
||||
e.startTime = new Date();
|
||||
let duration = t1 * 60
|
||||
e.endTime = new Date(e.startTime.getTime() + duration * 1000);
|
||||
|
||||
console.log("end", e.endTime);
|
||||
let countdownEl = e.querySelector(".countdown");
|
||||
|
||||
console.log(e, t1, t2);
|
||||
e.classList.add("active")
|
||||
let update = () => {
|
||||
let now = new Date();
|
||||
let percent = (now - e.startTime) / 1000 / duration * 100;
|
||||
// console.log("perc", (now - e.startTime)/1000 , percent, duration)
|
||||
e.style.backgroundImage = `linear-gradient(to right, transparent ${percent}%, rgba(255,255,255,0.4) ${percent}%)`
|
||||
let remaining = Math.round((e.endTime - now)/1000);
|
||||
let expired = percent > 100;
|
||||
if (expired) {
|
||||
playSound("recipe/beep.mp4")
|
||||
delete e.style.backgroundImage;
|
||||
// clearInterval(e.interval);
|
||||
remaining = -remaining;
|
||||
e.classList.add("expired")
|
||||
}
|
||||
|
||||
let countdownString = `${expired ? "-": ""}${Math.floor(remaining / 60)}m ${remaining % 60}s`
|
||||
countdownEl.innerText = countdownString;
|
||||
|
||||
}
|
||||
e.interval = setInterval(update, 1000)
|
||||
update();
|
||||
} else {
|
||||
e.interval = clearInterval(e.interval)
|
||||
e.classList.remove("active");
|
||||
e.classList.remove("expired");
|
||||
|
||||
e.style.backgroundImage = '';
|
||||
delete e.startTime;
|
||||
}
|
||||
}
|
||||
|
||||
window.startTimer = startTimer;
|
||||
|
||||
function share() {
|
||||
parent.postMessage({share:{}}, "*");
|
||||
}
|
||||
|
|
@ -342,7 +398,6 @@ function render() {
|
|||
let favicon = faviconForTitle(title) || "🍴";
|
||||
parent.postMessage({title:title, favicon:favicon, image:image, description:description, wakeLock:true, updateURL:true}, "*");
|
||||
description = description.split("\n").join("<p>")
|
||||
console.log(description)
|
||||
|
||||
// let text = instructions.join(" ");
|
||||
let ingredients = json.recipeIngredient;
|
||||
|
|
@ -386,6 +441,24 @@ function render() {
|
|||
m("span.substep",{innerHTML:highlightTimes(highlightTerms(FRACTION_MAP.replace(text.trim()), terms))}))
|
||||
}
|
||||
|
||||
function stepsFromText(text) {
|
||||
let steps = []
|
||||
let components = text.split(/(?<=[\.\!\?]|[\.\!\?]\)|\))\s+(?!\()/);
|
||||
|
||||
let append = false;
|
||||
while (components.length) {
|
||||
let step = components.shift()
|
||||
if (append) {
|
||||
steps.push(steps.pop() + " " + step)
|
||||
} else {
|
||||
steps.push(step);
|
||||
}
|
||||
if (step.indexOf("(") >= 0) { append = true; }
|
||||
if (step.includes(")")) { append = false; }
|
||||
}
|
||||
|
||||
return steps;
|
||||
}
|
||||
function flattenInstructions(instruction) {
|
||||
if (instruction.itemListElement) {
|
||||
return ["= " + instruction.name].concat(flattenInstructions(instruction.itemListElement).flat());
|
||||
|
|
@ -400,7 +473,7 @@ function render() {
|
|||
|
||||
console.log()
|
||||
if (reformat) {
|
||||
text = text.replace(/(\.\)? )+/g,"$1\n").split("\n")///text.match( /[0-9\. ]{0,6}[^\.!\?]+[\.!\?]+/g );
|
||||
text = stepsFromText(text);
|
||||
} else {
|
||||
text = text.match( /[^\n]+/g );
|
||||
}
|
||||
|
|
@ -436,13 +509,16 @@ function render() {
|
|||
bgImg.onload = function(){
|
||||
let thumbnail = document.querySelector("#thumbnail");
|
||||
let thumbnailContainer = document.querySelector("#thumbnail-container");
|
||||
thumbnail.style.backgroundImage = 'url(' + bgImg.src + ')';
|
||||
thumbnail.style.backgroundImage = 'url("' + bgImg.src + '")';
|
||||
thumbnail.style.filter = `blur(${thumbnailContainer.offsetHeight / bgImg.naturalHeight * 1}px)`;
|
||||
thumbnail.style.transform = `scale(1.1)`;
|
||||
setTimeout(() => thumbnail.style.opacity = 1.0, 100);
|
||||
};
|
||||
bgImg.src = image;
|
||||
|
||||
console.log("image", bgImg.src)
|
||||
|
||||
let qrImage = QRCodeURL(params.originalURL, {margin:0});
|
||||
let originalURL = json.mainEntityOfPage?.["@id"] || json.mainEntityOfPage || json.url;
|
||||
document.body.appendChild(
|
||||
m(".recipe", {},
|
||||
|
|
@ -458,7 +534,7 @@ function render() {
|
|||
m(".metadata",
|
||||
(recipeYield) ? m("div", m("span.yield", m(".icon.servings"), recipeYield)) : null,
|
||||
json.totalTime ? m(".time",
|
||||
m(".icon.timer"),
|
||||
m(".icon.time"),
|
||||
|
||||
json.totalTime ? m("span", formatTime(json.totalTime)) : undefined,
|
||||
// " (",
|
||||
|
|
@ -496,7 +572,7 @@ function render() {
|
|||
m("section.ingredients",
|
||||
m("caption.ingredients-title", "Ingredients"),
|
||||
ingredients,
|
||||
m("img.qr.print-show", {src:QRCodeURL(params.originalURL, {margin:0})})
|
||||
qrImage ? m("img.qr.print-show", {src:qrImage}) : null
|
||||
),
|
||||
m(reformat ? "section.instructions.numbered" : "section.instructions",
|
||||
m("caption.ingredients-title", {onclick:() => {reformat = !reformat; render(); return false;}},
|
||||
|
|
|
|||
BIN
docs/render/recipe/beep.mp4
Normal file
BIN
docs/render/recipe/beep.mp4
Normal file
Binary file not shown.
BIN
docs/render/recipe/beep2.mp4
Normal file
BIN
docs/render/recipe/beep2.mp4
Normal file
Binary file not shown.
Loading…
Reference in a new issue