From 5e4650f2dfcbfc10e6efa37f19704eeb6ea1794c Mon Sep 17 00:00:00 2001 From: Natxo <1172351+natxocc@users.noreply.github.com> Date: Tue, 31 Mar 2026 12:18:44 +0200 Subject: [PATCH] Create Timeline.js --- src/components/Timeline.js | 52 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/components/Timeline.js diff --git a/src/components/Timeline.js b/src/components/Timeline.js new file mode 100644 index 0000000..0d6bab5 --- /dev/null +++ b/src/components/Timeline.js @@ -0,0 +1,52 @@ +import { $html, $for } from "sigpro"; +import { val } from "../core/utils.js"; +import { iconInfo, iconSuccess, iconWarning, iconError } from "../core/icons.js"; + +/** TIMELINE */ +export const Timeline = (props) => { + const { items = [], vertical = true, compact = false, ...rest } = props; + + const icons = { + info: iconInfo, + success: iconSuccess, + warning: iconWarning, + error: iconError, + }; + + return $html( + "ul", + { + ...rest, + class: () => + `timeline ${val(vertical) ? "timeline-vertical" : "timeline-horizontal"} ${ + val(compact) ? "timeline-compact" : "" + } ${props.class || ""}`, + }, + [ + $for( + items, + (item, i) => { + const isFirst = i === 0; + const isLast = i === val(items).length - 1; + const itemType = item.type || "success"; + const renderSlot = (content) => (typeof content === "function" ? content() : content); + + return $html("li", { class: "flex-1" }, [ + !isFirst ? $html("hr", { class: item.completed ? "bg-primary" : "" }) : null, + $html("div", { class: "timeline-start" }, [renderSlot(item.title)]), + $html("div", { class: "timeline-middle" }, [ + $html("img", { + src: icons[itemType] || item.icon || icons.success, + class: "w-4 h-4 object-contain mx-1", + alt: itemType, + }), + ]), + $html("div", { class: "timeline-end timeline-box shadow-sm" }, [renderSlot(item.detail)]), + !isLast ? $html("hr", { class: item.completed ? "bg-primary" : "" }) : null, + ]); + }, + (item, i) => item.id || i, + ), + ], + ); +};