Basic
Basic timeline.
2015-09-01
Create a services site 2015-09-01
2015-09-01
Solve initial network problems 2015-09-01
2015-09-01
Technical testing 2015-09-01
2015-09-01
Network problems being solved 2015-09-01
Code
<%= render Hakumi::Timeline::Component.new do |timeline| %>
<% timeline.with_item(title: "2015-09-01") do %>
Create a services site 2015-09-01
<% end %>
<% timeline.with_item(title: "2015-09-01") do %>
Solve initial network problems 2015-09-01
<% end %>
<% timeline.with_item(title: "2015-09-01") do %>
Technical testing 2015-09-01
<% end %>
<% timeline.with_item(title: "2015-09-01") do %>
Network problems being solved 2015-09-01
<% end %>
<% end %>
Variant
Use the variant to set the style of the timeline.
2015-09-01
Filled timeline
2015-09-02
Default dots
2015-09-03
Resolved alerts
2015-09-01
Outlined timeline
2015-09-02
Neutral states
2015-09-03
Investigations
Code
<%= render Hakumi::Space::Component.new(direction: :vertical, size: :large) do |space| %>
<% space.with_item do %>
<%= render Hakumi::Timeline::Component.new do |timeline| %>
<% timeline.with_item(title: "2015-09-01", color: :green) { "Filled timeline" } %>
<% timeline.with_item(title: "2015-09-02", color: :blue) { "Default dots" } %>
<% timeline.with_item(title: "2015-09-03", color: :red) { "Resolved alerts" } %>
<% end %>
<% end %>
<% space.with_item do %>
<%= render Hakumi::Timeline::Component.new(variant: :outlined) do |timeline| %>
<% timeline.with_item(title: "2015-09-01", color: :green) { "Outlined timeline" } %>
<% timeline.with_item(title: "2015-09-02", color: :blue) { "Neutral states" } %>
<% timeline.with_item(title: "2015-09-03", color: :red) { "Investigations" } %>
<% end %>
<% end %>
<% end %>
Loading and Reversing
Node supports loading to indicate loading, and reverse property to control the order of nodes.
Recording...
2015-09-03
Technical testing
2015-09-02
Solve initial network problems
2015-09-01
Create a services site
Code
<%= render Hakumi::Timeline::Component.new(
reverse: true,
pending: "Recording...",
pending_dot: Hakumi::Icon::Component.new(name: :loading, spin: true, size: 12)
) do |timeline| %>
<% timeline.with_item(title: "2015-09-01") do %>
Create a services site
<% end %>
<% timeline.with_item(title: "2015-09-02") do %>
Solve initial network problems
<% end %>
<% timeline.with_item(title: "2015-09-03") do %>
Technical testing
<% end %>
<% end %>
Alternate
Alternate timeline.
2015-09-01
Create a services site
2015-09-02
Solve initial network problems
2015-09-03
Technical testing
2015-09-03
Network problems being solved
Code
<%= render Hakumi::Timeline::Component.new(mode: :alternate) do |timeline| %>
<% timeline.with_item(title: "2015-09-01") do %>
Create a services site
<% end %>
<% timeline.with_item(title: "2015-09-02") do %>
Solve initial network problems
<% end %>
<% timeline.with_item(title: "2015-09-03") do %>
Technical testing
<% end %>
<% timeline.with_item(title: "2015-09-03") do %>
Network problems being solved
<% end %>
<% end %>
End alternate
End alternate timeline.
2015-09-01
Create a services site
2015-09-02
Solve initial network problems
2015-09-03
Technical testing
2015-09-04
Final deployment
Code
<%= render Hakumi::Timeline::Component.new(mode: :alternate) do |timeline| %>
<% timeline.with_item(title: "2015-09-01") do %>
Create a services site
<% end %>
<% timeline.with_item(title: "2015-09-02") do %>
Solve initial network problems
<% end %>
<% timeline.with_item(title: "2015-09-03") do %>
Technical testing
<% end %>
<% timeline.with_item(title: "2015-09-04", position: :left) do %>
Final deployment
<% end %>
<% end %>
Horizontal
Horizontal layout.
09:00
Kickoff
11:30
Planning
14:00
QA checks
18:00
Launch
Code
<%= render Hakumi::Timeline::Component.new(direction: :horizontal) do |timeline| %>
<% timeline.with_item(title: "09:00") do %>
Kickoff
<% end %>
<% timeline.with_item(title: "11:30", color: :green) do %>
Planning
<% end %>
<% timeline.with_item(title: "14:00", color: :red) do %>
QA checks
<% end %>
<% timeline.with_item(title: "18:00") do %>
Launch
<% end %>
<% end %>
Custom
Set a node as an icon or other custom element.
2015-09-01
Custom icon node
2015-09-02
Document review
2015-09-03
Approved deliverables
Code
<%= render Hakumi::Timeline::Component.new do |timeline| %>
<% timeline.with_item(title: "2015-09-01", dot: Hakumi::Icon::Component.new(name: :clock_circle, size: 12), color: :blue) do %>
Custom icon node
<% end %>
<% timeline.with_item(title: "2015-09-02", color: :green) do %>
Document review
<% end %>
<% timeline.with_item(title: "2015-09-03", dot: Hakumi::Icon::Component.new(name: :check, size: 12), color: :green) do %>
Approved deliverables
<% end %>
<% end %>
Title
Use title show time alone.
09:00
11:30
14:00
18:00
Code
<%= render Hakumi::Timeline::Component.new do |timeline| %>
<% timeline.with_item(title: "09:00") %>
<% timeline.with_item(title: "11:30") %>
<% timeline.with_item(title: "14:00") %>
<% timeline.with_item(title: "18:00") %>
<% end %>
Title Offset
Use titleSpan to set the title span space.
2015-09-01 09:00
Start deployment
2015-09-01 12:00
Verification and QA
2015-09-01 18:00
Release completed
Code
<%= render Hakumi::Timeline::Component.new(title_span: "160px") do |timeline| %>
<% timeline.with_item(title: "2015-09-01 09:00") do %>
Start deployment
<% end %>
<% timeline.with_item(title: "2015-09-01 12:00") do %>
Verification and QA
<% end %>
<% timeline.with_item(title: "2015-09-01 18:00") do %>
Release completed
<% end %>
<% end %>
Semantic Sample
Achieve richer custom styles by using semantic structure.
Sprint 01
Design review
Tickets: 12 · Lead: Sofía
Sprint 02
Implementation
API, UI, and QA in parallel
Sprint 03
Stabilization
Regression suite + launch plan
Code
<%= render Hakumi::Timeline::Component.new(mode: :alternate) do |timeline| %>
<% timeline.with_item(title: "Sprint 01", color: :green) do %>
<%= render Hakumi::Space::Component.new(direction: :vertical, size: :small) do |space| %>
<% space.with_item do %>
<%= render(Hakumi::Typography::Title::Component.new(level: 5)) { "Design review" } %>
<% end %>
<% space.with_item do %>
<%= render(Hakumi::Typography::Text::Component.new(type: :secondary)) { "Tickets: 12 · Lead: Sofía" } %>
<% end %>
<% end %>
<% end %>
<% timeline.with_item(title: "Sprint 02", color: :blue) do %>
<%= render Hakumi::Space::Component.new(direction: :vertical, size: :small) do |space| %>
<% space.with_item do %>
<%= render(Hakumi::Typography::Title::Component.new(level: 5)) { "Implementation" } %>
<% end %>
<% space.with_item do %>
<%= render(Hakumi::Typography::Text::Component.new(type: :secondary)) { "API, UI, and QA in parallel" } %>
<% end %>
<% end %>
<% end %>
<% timeline.with_item(title: "Sprint 03", color: :red) do %>
<%= render Hakumi::Space::Component.new(direction: :vertical, size: :small) do |space| %>
<% space.with_item do %>
<%= render(Hakumi::Typography::Title::Component.new(level: 5)) { "Stabilization" } %>
<% end %>
<% space.with_item do %>
<%= render(Hakumi::Typography::Text::Component.new(type: :secondary)) { "Regression suite + launch plan" } %>
<% end %>
<% end %>
<% end %>
<% end %>
Programmatic
Render a timeline with HakumiComponents.renderComponent.
Code
<%= render Hakumi::Space::Component.new(size: :middle) do %>
<%= render(Hakumi::Button::Component.new(id: "timeline-programmatic-render", type: :primary)) { "Render timeline" } %>
<%= render(Hakumi::Button::Component.new(id: "timeline-programmatic-reverse")) { "Toggle reverse" } %>
<% end %>
<%= render Hakumi::Container::Component.new(id: "timeline-programmatic-target", padded: false, style: "min-height: 180px;") %>
<script>
(() => {
let instance = null
const wire = () => {
if (!window.HakumiComponents?.renderComponent) return false
const renderButton = document.getElementById("timeline-programmatic-render")
const reverseButton = document.getElementById("timeline-programmatic-reverse")
renderButton?.addEventListener("click", async () => {
const result = await window.HakumiComponents.renderComponent("timeline", {
target: "#timeline-programmatic-target",
mode: "replace",
params: {
id: "timeline-programmatic",
pending: "Syncing...",
items: JSON.stringify([
{ title: "09:00", body: "Check-in" },
{ title: "12:00", body: "Deploy" },
{ title: "15:00", body: "Verification" }
])
}
})
instance = result.instance
})
reverseButton?.addEventListener("click", () => instance?.setReverse?.(!instance?.getReverse?.()))
return true
}
if (wire()) return
const onReady = () => {
if (wire()) {
document.removeEventListener("turbo:load", onReady)
window.removeEventListener("load", onReady)
}
}
document.addEventListener("turbo:load", onReady)
window.addEventListener("load", onReady)
})()
</script>
Timeline API
| Prop | Type | Default | Description |
|---|---|---|---|
direction |
Symbol |
:vertical |
Layout direction. Options: :vertical, :horizontal. |
mode |
Symbol |
:left |
Item placement for vertical layout. Options: :left, :right, :alternate. |
reverse |
Boolean |
false |
Reverse the display order of items. |
pending |
String or Boolean |
nil |
Show a pending node with optional text. |
pending_dot |
Component or String |
nil |
Custom dot for the pending node. |
title_span |
String |
nil |
CSS width for the title column (e.g., '160px'). |
variant |
Symbol |
:filled |
Visual style. Options: :filled, :outlined. |
Timeline.Item API
| Prop | Type | Default | Description |
|---|---|---|---|
title |
String |
nil |
Title or time label for the item. |
content |
String |
nil |
Item content when no block is supplied. |
color |
Symbol or String |
:blue |
Dot color token (:blue, :green, :red, :gray) or custom CSS color. |
dot |
Component or String |
nil |
Custom dot element or icon component. |
position |
Symbol |
nil |
Override item position. Options: :left, :right. |
loading |
Boolean |
false |
Show loading indicator inside the dot. |
JavaScript API (element.hakumiTimeline)
| Prop | Type | Default | Description |
|---|---|---|---|
getItems() |
Function |
- |
Return item metadata in display order. |
getReverse() |
Function |
- |
Returns current reverse state. |
setReverse(value) |
Function |
- |
Set reverse state and update DOM order. |
toggleReverse() |
Function |
- |
Toggle reverse state. |