Sequence of reversible editing operations.
<div class="uhd uhd--timeline" data-layout="timeline" style="--accent:#D9C8F0;">
<div class="uhd__progress"><span class="uhd__bar" style="width:calc(20% * 3);"></span></div>
<ol class="uhd__list">
<li class="uhd__item uhd__item--done">Typed opening paragraph </li>
<li class="uhd__item uhd__item--done">Bolded heading </li>
<li class="uhd__item uhd__item--current">Inserted image <span class="uhd__here">You are here</span></li>
<li class="uhd__item uhd__item--pending">Deleted stray sentence</li>
<li class="uhd__item uhd__item--pending">Reordered sections</li>
</ol>
<div class="uhd__actions">
<button type="button" class="uhd__btn">Undo</button>
<button type="button" class="uhd__btn" aria-disabled="false">Redo</button>
</div>
</div>
<style>
.uhd { font-family: system-ui, sans-serif; color: #241430; background: #FAF7F2; padding: 18px; max-width: 320px; }
.uhd__progress { height: 4px; background: #24143014; border-radius: 2px; overflow: hidden; margin-bottom: 14px; }
.uhd__bar { display: block; height: 100%; background: var(--accent); }
.uhd__list { list-style: none; margin: 0 0 14px; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.uhd--timeline .uhd__item { border-left: 2px solid #24143020; padding-left: 12px; }
.uhd--list .uhd__item { border-bottom: 1px solid #24143014; padding-bottom: 8px; }
.uhd__item { font-size: 13px; }
.uhd__item--pending { color: #241430aa; text-decoration: line-through; }
.uhd__item--current { font-weight: 700; }
.uhd--timeline .uhd__item--current { border-left-color: var(--accent); }
.uhd__time { margin-left: 6px; font-size: 11px; color: #241430aa; }
.uhd__here { margin-left: 6px; font-size: 10px; font-weight: 700; text-transform: uppercase; color: var(--accent); }
.uhd__actions { display: flex; gap: 8px; }
.uhd__btn { border: 1.5px solid #24143026; background: #fff; padding: 6px 14px; border-radius: 8px; font: 600 13px system-ui, sans-serif; cursor: pointer; }
.uhd__btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.uhd__btn[disabled] { opacity: .45; cursor: not-allowed; }
</style><div class="uhd-tw uhd-tw--timeline max-w-[320px] bg-[#FAF7F2] text-[#241430] font-sans p-[18px]" data-layout="timeline" style="--accent:#D9C8F0;">
<div class="h-1 bg-[#24143014] rounded-full overflow-hidden mb-3.5"><span class="block h-full" style="width:calc(20% * 3);background:#D9C8F0"></span></div>
<ol class="list-none m-0 mb-3.5 p-0 flex flex-col gap-2">
<li class="uhd-tw__item text-[13px]">Typed opening paragraph </li>
<li class="uhd-tw__item text-[13px]">Bolded heading </li>
<li class="uhd-tw__item uhd-tw__item--current text-[13px] font-bold">Inserted image <span class="ml-1.5 text-[10px] font-bold uppercase" style="color:#D9C8F0">You are here</span></li>
<li class="uhd-tw__item text-[13px] text-[#241430]/65 line-through">Deleted stray sentence</li>
<li class="uhd-tw__item text-[13px] text-[#241430]/65 line-through">Reordered sections</li>
</ol>
<div class="flex gap-2">
<button type="button" class="border-[1.5px] border-[#24143026] bg-white px-3.5 py-1.5 rounded-lg text-[13px] font-semibold cursor-pointer">Undo</button>
<button type="button" class="border-[1.5px] border-[#24143026] bg-white px-3.5 py-1.5 rounded-lg text-[13px] font-semibold cursor-pointer disabled:opacity-45" aria-disabled="false">Redo</button>
</div>
</div>
<style>
.uhd-tw--timeline .uhd-tw__item { border-left: 2px solid #24143020; padding-left: 12px; }
.uhd-tw--list .uhd-tw__item { border-bottom: 1px solid #24143014; padding-bottom: 8px; }
</style>export function UndoHistory({
currentStep = 3,
accent = "#D9C8F0",
layout = "timeline",
canRedo = true,
showTimestamps = false,
}) {
const timeline = layout === "timeline";
const items = [
{ text: "Typed opening paragraph", state: "done", time: "2m ago" },
{ text: "Bolded heading", state: "done", time: "90s ago" },
{ text: "Inserted image", state: "current", time: null },
{ text: "Deleted stray sentence", state: "pending", time: null },
{ text: "Reordered sections", state: "pending", time: null },
];
return (
<div style={{ fontFamily: "system-ui, sans-serif", color: "#241430", background: "#FAF7F2", padding: 18, maxWidth: 320 }}>
<div style={{ height: 4, background: "#24143014", borderRadius: 2, overflow: "hidden", marginBottom: 14 }}>
<span style={{ display: "block", height: "100%", width: `calc(20% * ${currentStep})`, background: accent }} />
</div>
<ol style={{ listStyle: "none", margin: "0 0 14px", padding: 0, display: "flex", flexDirection: "column", gap: 8 }}>
{items.map((it) => (
<li key={it.text} style={{
fontSize: 13,
fontWeight: it.state === "current" ? 700 : 400,
color: it.state === "pending" ? "#241430aa" : "#241430",
textDecoration: it.state === "pending" ? "line-through" : "none",
borderLeft: timeline ? `2px solid ${it.state === "current" ? accent : "#24143020"}` : "none",
borderBottom: timeline ? "none" : "1px solid #24143014",
paddingLeft: timeline ? 12 : 0,
paddingBottom: timeline ? 0 : 8,
}}>
{it.text}
{showTimestamps && it.time && <span style={{ marginLeft: 6, fontSize: 11, color: "#241430aa" }}>{it.time}</span>}
{it.state === "current" && <span style={{ marginLeft: 6, fontSize: 10, fontWeight: 700, textTransform: "uppercase", color: accent }}>You are here</span>}
</li>
))}
</ol>
<div style={{ display: "flex", gap: 8 }}>
<button type="button" style={{ border: "1.5px solid #24143026", background: "#fff", padding: "6px 14px", borderRadius: 8, fontSize: 13, fontWeight: 600, cursor: "pointer" }}>Undo</button>
<button type="button" disabled={!canRedo} aria-disabled={!canRedo} style={{ border: "1.5px solid #24143026", background: "#fff", padding: "6px 14px", borderRadius: 8, fontSize: 13, fontWeight: 600,
cursor: canRedo ? "pointer" : "not-allowed", opacity: canRedo ? 1 : 0.45 }}>Redo</button>
</div>
</div>
);
}