Label nested workflows and add in-recipe jump links
This commit is contained in:
@@ -2,6 +2,16 @@ import { useState } from "react";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import TermOutput from "./TermOutput";
|
import TermOutput from "./TermOutput";
|
||||||
|
|
||||||
|
const JUMP = [
|
||||||
|
{ id: "problem", label: "Problem" },
|
||||||
|
{ id: "actors", label: "Actors" },
|
||||||
|
{ id: "setup", label: "Setup" },
|
||||||
|
{ id: "safeguards", label: "Safeguards" },
|
||||||
|
{ id: "method", label: "Method" },
|
||||||
|
{ id: "evidence", label: "Evidence" },
|
||||||
|
{ id: "tryit", label: "Try-it" },
|
||||||
|
];
|
||||||
|
|
||||||
function ShieldCheck() {
|
function ShieldCheck() {
|
||||||
return (
|
return (
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||||
@@ -59,13 +69,25 @@ async function copyText(text) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Field({ anchor, name, label, children }) {
|
||||||
|
return (
|
||||||
|
<div className="row" id={`${anchor}-${name}`}>
|
||||||
|
<h4 className="k">{label}</h4>
|
||||||
|
<div className="v">{children}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function WorkflowBlock({
|
export default function WorkflowBlock({
|
||||||
recipe,
|
recipe,
|
||||||
complete = false,
|
complete = false,
|
||||||
onToggleComplete,
|
onToggleComplete,
|
||||||
focused = false,
|
focused = false,
|
||||||
|
index = 1,
|
||||||
|
total = 1,
|
||||||
}) {
|
}) {
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
|
const a = recipe.anchor;
|
||||||
|
|
||||||
const onShare = async () => {
|
const onShare = async () => {
|
||||||
const ok = await copyText(recipe.shareBlurb);
|
const ok = await copyText(recipe.shareBlurb);
|
||||||
@@ -76,140 +98,120 @@ export default function WorkflowBlock({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<article
|
<article
|
||||||
id={recipe.anchor}
|
id={a}
|
||||||
className={`recipe recipe-nested${focused ? " recipe-focus" : ""}`}
|
className={`recipe recipe-nested${focused ? " recipe-focus" : ""}`}
|
||||||
aria-labelledby={`${recipe.anchor}-title`}
|
aria-labelledby={`${a}-title`}
|
||||||
>
|
>
|
||||||
<div className="recipe-top">
|
<div className="recipe-top">
|
||||||
<span className="recipe-id">
|
<span className="recipe-id">
|
||||||
recipe <b>{recipe.id}</b> · v{recipe.version} · {recipe.time} · {recipe.cost}
|
Workflow {index} of {total} · <b>{recipe.id}</b> · v{recipe.version} · {recipe.time} · {recipe.cost}
|
||||||
</span>
|
</span>
|
||||||
<span className="recipe-vert">{recipe.vertical}</span>
|
<span className="recipe-vert">{recipe.vertical}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="recipe-body">
|
<div className="recipe-body">
|
||||||
<h3 className="recipe-title" id={`${recipe.anchor}-title`}>
|
<h3 className="recipe-title" id={`${a}-title`}>
|
||||||
{recipe.title}
|
{recipe.title}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<div className="row">
|
<nav className="recipe-jump" aria-label="In this workflow">
|
||||||
<div className="k">Problem</div>
|
{JUMP.map((j) => (
|
||||||
<div className="v">
|
<a key={j.id} href={`#${a}-${j.id}`}>
|
||||||
{recipe.problem}{" "}
|
{j.label}
|
||||||
<span className="sub">{recipe.problemJob}</span>
|
</a>
|
||||||
</div>
|
))}
|
||||||
</div>
|
</nav>
|
||||||
|
|
||||||
<div className="row">
|
<Field anchor={a} name="problem" label="Problem">
|
||||||
<div className="k">Actors</div>
|
{recipe.problem} <span className="sub">{recipe.problemJob}</span>
|
||||||
<div className="v">
|
</Field>
|
||||||
<ul className="plain-list">
|
|
||||||
{recipe.actors.map((line) => (
|
|
||||||
<li key={line}>{line}</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="row">
|
<Field anchor={a} name="actors" label="Actors">
|
||||||
<div className="k">Setup</div>
|
<ul className="plain-list">
|
||||||
<div className="v">
|
{recipe.actors.map((line) => (
|
||||||
<ul className="plain-list">
|
<li key={line}>{line}</li>
|
||||||
{recipe.setup.map((line) => (
|
|
||||||
<li key={line}>{line}</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="row">
|
|
||||||
<div className="k">Safeguards</div>
|
|
||||||
<div className="v">
|
|
||||||
{recipe.safeguards.map((line) => (
|
|
||||||
<div className="guard" key={line}>
|
|
||||||
<ShieldCheck />
|
|
||||||
<span>{line}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</ul>
|
||||||
</div>
|
</Field>
|
||||||
|
|
||||||
<div className="row">
|
<Field anchor={a} name="setup" label="Setup">
|
||||||
<div className="k">Method</div>
|
<ul className="plain-list">
|
||||||
<div className="v">
|
{recipe.setup.map((line) => (
|
||||||
<ol className="steps">
|
<li key={line}>{line}</li>
|
||||||
{recipe.method.map((step, i) => (
|
|
||||||
<li key={i}>
|
|
||||||
<div className="step-copy">
|
|
||||||
{step.text}
|
|
||||||
{step.term ? <TermOutput id={step.term} /> : null}
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="row">
|
|
||||||
<div className="k">Evidence</div>
|
|
||||||
<div className="v">
|
|
||||||
{recipe.evidence.map((line) => (
|
|
||||||
<p key={line} className="evidence-p">
|
|
||||||
{line}
|
|
||||||
</p>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</ul>
|
||||||
</div>
|
</Field>
|
||||||
|
|
||||||
<div className="row">
|
<Field anchor={a} name="safeguards" label="Safeguards">
|
||||||
<div className="k">Try it</div>
|
{recipe.safeguards.map((line) => (
|
||||||
<div className="v">
|
<div className="guard" key={line}>
|
||||||
<p>
|
<ShieldCheck />
|
||||||
About {recipe.tryit.minutes} minutes. {recipe.tryit.sample}{" "}
|
<span>{line}</span>
|
||||||
{recipe.tryit.produce}
|
</div>
|
||||||
</p>
|
))}
|
||||||
<p className="sub-line">
|
</Field>
|
||||||
{recipe.tryit.done}{" "}
|
|
||||||
<a className="inline-sample" href={recipe.tryit.href} download>
|
|
||||||
{recipe.tryit.hrefLabel}
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<span className="chip">◷ {recipe.tryit.chip}</span>
|
|
||||||
</p>
|
|
||||||
{onToggleComplete ? (
|
|
||||||
<div className="tryit-actions">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={`btn ${complete ? "btn-primary tryit-done" : "btn-ghost"}`}
|
|
||||||
aria-pressed={complete}
|
|
||||||
onClick={() => onToggleComplete(recipe.id)}
|
|
||||||
>
|
|
||||||
{complete ? <CheckMark /> : null}
|
|
||||||
{complete ? "Counted as activation (demo)" : "Mark complete"}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="row">
|
<Field anchor={a} name="method" label="Method">
|
||||||
<div className="k">Share</div>
|
<ol className="steps">
|
||||||
<div className="v">
|
{recipe.method.map((step, i) => (
|
||||||
<p>
|
<li key={i}>
|
||||||
Copy a short result line. Share the outcome, not the recipe text.
|
<div className="step-copy">
|
||||||
|
{step.text}
|
||||||
|
{step.term ? <TermOutput id={step.term} /> : null}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ol>
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Field anchor={a} name="evidence" label="Evidence">
|
||||||
|
{recipe.evidence.map((line) => (
|
||||||
|
<p key={line} className="evidence-p">
|
||||||
|
{line}
|
||||||
</p>
|
</p>
|
||||||
|
))}
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Field anchor={a} name="tryit" label="Try-it">
|
||||||
|
<p>
|
||||||
|
About {recipe.tryit.minutes} minutes. {recipe.tryit.sample}{" "}
|
||||||
|
{recipe.tryit.produce}
|
||||||
|
</p>
|
||||||
|
<p className="sub-line">
|
||||||
|
{recipe.tryit.done}{" "}
|
||||||
|
<a className="inline-sample" href={recipe.tryit.href} download>
|
||||||
|
{recipe.tryit.hrefLabel}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span className="chip">◷ {recipe.tryit.chip}</span>
|
||||||
|
</p>
|
||||||
|
{onToggleComplete ? (
|
||||||
<div className="tryit-actions">
|
<div className="tryit-actions">
|
||||||
<button type="button" className="btn btn-ghost" onClick={onShare}>
|
<button
|
||||||
{copied ? "Copied" : "Copy result line"}
|
type="button"
|
||||||
|
className={`btn ${complete ? "btn-primary tryit-done" : "btn-ghost"}`}
|
||||||
|
aria-pressed={complete}
|
||||||
|
onClick={() => onToggleComplete(recipe.id)}
|
||||||
|
>
|
||||||
|
{complete ? <CheckMark /> : null}
|
||||||
|
{complete ? "Counted as activation (demo)" : "Mark complete"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
) : null}
|
||||||
</div>
|
</Field>
|
||||||
|
|
||||||
<div className="row">
|
<Field anchor={a} name="share" label="Share">
|
||||||
<div className="k">Record</div>
|
<p>Copy a short result line. Share the outcome, not the recipe text.</p>
|
||||||
<div className="v recipe-record">
|
<div className="tryit-actions">
|
||||||
|
<button type="button" className="btn btn-ghost" onClick={onShare}>
|
||||||
|
{copied ? "Copied" : "Copy result line"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Field anchor={a} name="record" label="Record">
|
||||||
|
<div className="recipe-record">
|
||||||
<span>Author: {recipe.author}</span>
|
<span>Author: {recipe.author}</span>
|
||||||
<span>Sources: {recipe.sources}</span>
|
<span>Sources: {recipe.sources}</span>
|
||||||
<span>Last updated: {recipe.lastUpdated}</span>
|
<span>Last updated: {recipe.lastUpdated}</span>
|
||||||
@@ -225,7 +227,7 @@ export default function WorkflowBlock({
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Field>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user