Fl4m3Ph03n1x
Radial progress bar using TailwinUI
Background
I have a small Phoenix 1.7 app where I am trying to add a radial progress bar, using the default TailwindUI components: Tailwind CSS Components - Tailwind Plus
Unfortunately for me, I was only able to find normal progress bars:
Namely:
<div class="w-full bg-gray-200 rounded-full h-2.5 dark:bg-gray-700">
<div class="bg-blue-600 h-2.5 rounded-full" style="width: 45%"></div>
</div>
The only radial progress bar I found was one using DaisyUI:
However, I want to avoid installing anything extra, I really just want to use the default tailwindUI and tailwindCSS that come with Phoenix 1.7
Questions
- Does someone know how to create a a radial progress bar component using the default setup that comes with Phoenix LiveView 1.7?
- Am I missing some component that already does this?
Marked As Solved
Fl4m3Ph03n1x
No need to apologize !
Even though the link and code you shared used alpine, it was still very educative. I was able to analyze the code and create a component that does exactly what I want. Its also quite pretty, so I am very proud. This code uses only Phoniex LV vanilla, no extra CSS no extra anything !
core_components:
@doc """
Renders a progress bar for an ongoing operation.
## Examples
<.progress_bar hidden=false progress=15 />
<.progress_bar hidden=false progress=20 message="Activating system ..." />
<.progress_bar hidden=false class="cool-bar" />
"""
attr :hidden, :boolean, default: true, doc: "whether or not to show the progress bar"
attr :progress, :integer, default: 0, doc: "the current progress of the bar"
attr :message, :string, default: "Operation in progress ...", doc: "the message to show while the bar is progressing"
attr :class, :string, default: nil
def progress_bar(assigns) do
assigns = assign(assigns, :circumference, 2 * 22 / 7 * 120)
assigns = assign(assigns, :offset, assigns.circumference - assigns.progress / 100 * assigns.circumference)
~H"""
<div class={@class} hidden={@hidden}>
<div class="flex items-center justify-center">
<p class="text-lg font-semibold"><%= @message %></p>
</div>
<div class="flex items-center justify-center">
<svg class="transform -rotate-90 w-72 h-72">
<circle cx="145" cy="145" r="120" stroke-width="30" fill="transparent" class="stroke-gray-700" />
<circle cx="145" cy="145" r="120" stroke-width="30" fill="transparent"
stroke-dasharray={@circumference}
stroke-dashoffset={@offset}
class="stroke-indigo-500" />
</svg>
<span class="absolute text-5xl stroke-black"><%= @progress %></span>
</div>
</div>
"""
end
Usage in my_app_live.heex.html:
<div class="min-h-full max-w-full mx-auto py-6 sm:px-6 lg:px-8">
<.progress_bar hidden={false} progress={40} message="Activating system ..." />
</div>
Will produce the following:
Possible improvements
Currently i am not sure if there is a more elixir way of updating the assigns:
assigns = assign(assigns, :circumference, 2 * 22 / 7 * 120)
assigns = assign(assigns, :offset, assigns.circumference - assigns.progress / 100 * assigns.circumference)
But other than that, I am pretty happy with it.
Additional info
For those of you curious, you can also check this other solution someone else provided in SO:
I hope this helps!
Also Liked
dfalling
You can compute this circumference outside of the function since it’s never changing.
Eg
# 2 * 22 / 7 * 120
@circumference 754.285714285714286
(not sure how important precision is for that variable)
LostKobrakai
I mean sure if you’re looking at the LV docs and not the Alpine docs. :attr is a short form for x-bind:attr in alpine.
LostKobrakai
No. Not sure where you got that html you posted, but it’s using alpine.
outlog
apologies for posting a link that depends on alpine… I believe it’s best avoided nowadays with all the liveviewjs improvements…
think you need to go with this underlying example Building a Progress Ring, Quickly | CSS-Tricks - CSS-Tricks - and then maybe take some tailwind styling from the alpine dependent one…








