@@ -246,11 +246,12 @@ defmodule Oban.Web.Components.Core do
246246 A status badge with icon that expands to show label on hover.
247247 """
248248 attr :icon , :string , required: true
249+ attr :id , :string , default: nil
249250 attr :label , :string , required: true
250251
251252 def status_badge ( assigns ) do
252253 ~H"""
253- < div class = "group flex items-center cursor-default select-none " >
254+ < div id = { @ id } class = "group flex items-center cursor-default select-none " >
254255 < span class = "inline-flex items-center justify-center h-9 pl-2.5 pr-2.5 group-hover:pr-4 rounded-full text-sm font-medium bg-violet-100 text-violet-700 dark:bg-violet-700/70 dark:text-violet-200 transition-all duration-200 " >
255256 < . badge_icon name = { @ icon } />
256257 < span class = "max-w-0 overflow-hidden group-hover:max-w-24 group-hover:ml-1.5 transition-all duration-200 whitespace-nowrap " >
@@ -287,6 +288,15 @@ defmodule Oban.Web.Components.Core do
287288 defp badge_icon ( % { name: "link" } = assigns ) ,
288289 do: ~H[ < Icons . link class = "h-4 w-4 shrink-0 " /> ]
289290
291+ defp badge_icon ( % { name: "power" } = assigns ) ,
292+ do: ~H[ < Icons . power class = "h-4 w-4 shrink-0 " /> ]
293+
294+ defp badge_icon ( % { name: "pause_circle" } = assigns ) ,
295+ do: ~H[ < Icons . pause_circle class = "h-4 w-4 shrink-0 " /> ]
296+
297+ defp badge_icon ( % { name: "play_pause_circle" } = assigns ) ,
298+ do: ~H[ < Icons . play_pause_circle class = "h-4 w-4 shrink-0 " /> ]
299+
290300 @ doc """
291301 An icon-only button that expands to show label on hover. Supports disabled state.
292302 """
@@ -369,4 +379,105 @@ defmodule Oban.Web.Components.Core do
369379 defp button_icon ( % { name: "play_circle" } = assigns ) , do: ~H[ < Icons . play_circle class = { @ class } /> ]
370380 defp button_icon ( % { name: "trash" } = assigns ) , do: ~H[ < Icons . trash class = { @ class } /> ]
371381 defp button_icon ( % { name: "x_circle" } = assigns ) , do: ~H[ < Icons . x_circle class = { @ class } /> ]
382+
383+ # Sparkline
384+
385+ attr :id , :string , required: true
386+ attr :history , :map , required: true
387+ attr :max_value , :integer , default: nil
388+ attr :bar_width , :integer , default: 4
389+ attr :count , :integer , default: 60
390+ attr :gap , :integer , default: 1
391+ attr :height , :integer , default: 16
392+ attr :class , :string , default: nil
393+
394+ def sparkline ( assigns ) do
395+ history = assigns . history
396+ count = assigns . count
397+ bar_width = assigns . bar_width
398+ gap = assigns . gap
399+ height = assigns . height
400+ max_index = count - 1
401+
402+ max_value =
403+ if assigns . max_value do
404+ max ( assigns . max_value , 1 )
405+ else
406+ history
407+ |> Map . values ( )
408+ |> Enum . reduce ( 1 , fn % { count: c } , acc -> max ( c , acc ) end )
409+ end
410+
411+ now = System . system_time ( :millisecond )
412+
413+ { bars , tooltip_data } =
414+ for slot <- 0 .. max_index , reduce: { [ ] , [ ] } do
415+ { bars_acc , tool_acc } ->
416+ index = max_index - slot
417+ timestamp = now - index * 5 * 1000
418+ x = slot * ( bar_width + gap )
419+
420+ case Map . get ( history , index ) do
421+ % { count: c } ->
422+ bar_height = min ( c / max_value , 1.0 ) * height
423+ bar = % { x: x , height: max ( bar_height , 0 ) }
424+ tooltip = % { timestamp: timestamp , count: c }
425+
426+ { [ bar | bars_acc ] , [ tooltip | tool_acc ] }
427+
428+ nil ->
429+ tooltip = % { timestamp: timestamp , count: 0 }
430+
431+ { bars_acc , [ tooltip | tool_acc ] }
432+ end
433+ end
434+
435+ bars = Enum . reverse ( bars )
436+ tooltip_data = Enum . reverse ( tooltip_data )
437+
438+ placeholders =
439+ for slot <- 0 .. max_index do
440+ % { x: slot * ( bar_width + gap ) }
441+ end
442+
443+ width = count * ( bar_width + gap )
444+
445+ assigns =
446+ assigns
447+ |> assign ( bars: bars , placeholders: placeholders , width: width )
448+ |> assign ( tooltip_data: tooltip_data )
449+
450+ ~H"""
451+ < svg
452+ id = { @ id }
453+ width = { @ width }
454+ height = { @ height }
455+ viewBox = { "0 0 #{ @ width } #{ @ height } " }
456+ class = { [ "flex-shrink-0" , @ class ] }
457+ phx-hook = "QueueSparkline "
458+ data-tooltip = { Oban.JSON . encode! ( @ tooltip_data ) }
459+ data-bar-width = { @ bar_width }
460+ >
461+ < rect
462+ :for = { placeholder <- @ placeholders }
463+ x = { placeholder . x }
464+ y = { @ height - 2 }
465+ width = { @ bar_width }
466+ height = "2 "
467+ fill = "#e5e7eb "
468+ class = "dark:fill-gray-700 "
469+ rx = "0.5 "
470+ />
471+ < rect
472+ :for = { bar <- @ bars }
473+ x = { bar . x }
474+ y = { @ height - bar . height }
475+ width = { @ bar_width }
476+ height = { bar . height }
477+ fill = "#22d3ee "
478+ rx = "1 "
479+ />
480+ </ svg >
481+ """
482+ end
372483end
0 commit comments