• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaMathias / moolah / 7813c63a0ba77d9fa77ed722befbe42759951cb7

01 Sep 2025 08:23PM UTC coverage: 0.687% (-0.04%) from 0.723%
7813c63a0ba77d9fa77ed722befbe42759951cb7

push

github

web-flow
Merge pull request #11 from JuliaMathias/handle-beacon

Handle beacon

26 of 1621 new or added lines in 68 files covered. (1.6%)

119 existing lines in 40 files now uncovered.

72 of 10486 relevant lines covered (0.69%)

0.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/lib/moolah_web/components/progress.ex
1
defmodule MoolahWeb.Components.Progress do
2
  @moduledoc """
3
  The `MoolahWeb.Components.Progress` module provides a customizable progress bar component for
4
  Phoenix LiveView applications.
5

6
  It offers a range of styling options, including different sizes, colors, and variants,
7
  allowing developers to create both horizontal and vertical progress bars tailored to
8
  their design requirements.
9

10
  This component supports a variety of visual configurations, such as gradient backgrounds
11
  and rounded corners, and can be used in diverse use cases, from displaying loading states
12
  to indicating progress in forms and surveys.
13

14
  The module's flexibility is further enhanced by its use of `slots`, enabling developers
15
  to include custom label within the progress bar, making it a versatile choice for building
16
  interactive and dynamic UIs.
17
  """
18
  use Phoenix.Component
19
  alias Phoenix.LiveView.JS
20
  import Phoenix.LiveView.Utils, only: [random_id: 0]
21

22
  @doc """
23
  Renders a `progress` bar component that visually represents the completion status of a task.
24

25
  It supports both horizontal and vertical orientations and can be customized with various colors and styles.
26

27
  ## Examples
28

29
  ```elixir
30
  <.progress value={10} />
31
  <.progress color="primary" value={20} />
32
  <.progress color="secondary" value={30} />
33
  <.progress variation="vertical" color="primary" value={20} />
34

35
  <.progress>
36
    <.progress_section color="primary" value={10} />
37
    <.progress_section color="secondary" value={15} />
38
    <.progress_section color="misc" value={10} />
39
    <.progress_section color="danger" value={5} />
40
    <.progress_section color="warning" value={10} />
41
    <.progress_section color="success" value={10} />
42
    <.progress_section color="info" value={5} />
43
  </.progress>
44

45
  <.progress variation="horizontal" size="large" value={70}>
46
    <div class="absolute inset-y-0 left-0 flex items-center pl-3 text-white">
47
      70%
48
    </div>
49
  </.progress>
50

51
  <.progress variation="vertical" size="extra_large" value={80}>
52
    <div class="absolute bottom-0 left-0 flex items-center text-white">
53
      80%
54
    </div>
55
  </.progress>
56
  ```
57
  """
58
  @doc type: :component
59
  attr :id, :string,
60
    default: nil,
61
    doc: "A unique identifier is used to manage state and interaction"
62

63
  attr :value, :integer, default: nil, doc: "Value of inout"
64

65
  attr :variation, :string,
66
    values: ["horizontal", "vertical"],
67
    default: "horizontal",
68
    doc: "Defines the layout orientation of the component"
69

70
  attr :color, :string, default: "natural", doc: "Determines color theme"
71
  attr :rounded, :string, default: "full", doc: "Determines the border radius"
72
  attr :variant, :string, default: "base", doc: "Determines the style"
73

74
  attr :size, :string,
75
    default: "small",
76
    doc:
77
      "Determines the overall size of the elements, including padding, font size, and other items"
78

79
  attr :class, :string, default: nil, doc: "Custom CSS class for additional styling"
80
  attr :csp_nonce, :string, default: nil, doc: "csp nonce"
81

82
  attr :rest, :global,
83
    doc:
84
      "Global attributes can define defaults which are merged with attributes provided by the caller"
85

86
  slot :inner_block, doc: "Inner block that renders HEEx label"
87

88
  def progress(assigns) do
89
    ~H"""
×
90
    <div
×
91
      role="progressbar"
NEW
92
      aria-valuenow={@value}
×
93
      class={[
94
        "bg-[#F4F4F4] dark:bg-[#B6B6B6] overflow-hidden",
NEW
95
        @variation == "vertical" && "flex items-end vertical-progress overflow-y-hidden",
×
96
        size_class(@size, @variation),
×
97
        rounded_size(@rounded)
×
98
      ]}
99
      {@rest}
×
100
    >
101
      <.progress_section :if={@value} {assigns} />
×
102
      <div
103
        :if={msg = render_slot(@inner_block)}
×
104
        class={[
105
          "flex",
NEW
106
          (@variation == "horizontal" && "flex-row") || "flex-col flex-col-reverse w-full h-full"
×
107
        ]}
108
      >
109
        {msg}
110
      </div>
111
    </div>
112
    """
113
  end
114

115
  @doc """
116
  Displays a semicircular progress indicator.
117

118
  ## Attributes
119
    - value: The progress value (0 to 100)
120
    - size: The SVG size (width), default is 100
121
    - thickness: The stroke width of the circle, default is 10
122
    - color: Determines the color theme; default is "natural"
123
  """
124
  attr :id, :string, default: nil, doc: "HTML ID for the container"
125
  attr :value, :integer, required: true, doc: "Progress value (0 to 100)"
126
  attr :size, :integer, default: 200, doc: "Diameter of the circle in px"
127
  attr :thickness, :integer, default: 12, doc: "Stroke width"
128
  attr :orientation, :string, default: "up", doc: "'up' or 'down'"
129
  attr :fill_direction, :string, default: "left-to-right", doc: "Direction of fill"
130
  attr :transition_duration, :integer, default: 300, doc: "Transition duration in ms"
131
  attr :class, :string, default: nil, doc: "Custom CSS class for additional styling"
132
  attr :linecap, :string, default: nil, doc: "add radius to progress"
133
  attr :color, :string, default: "natural", doc: "Determines color theme"
134

135
  attr :label, :string, default: nil, doc: "Optional label"
136

137
  attr :rest, :global,
138
    doc:
139
      "Global attributes can define defaults which are merged with attributes provided by the caller"
140

141
  def semi_circle_progress(assigns) do
NEW
142
    coordinate = assigns.size / 2
×
NEW
143
    radius = (assigns.size - 2 * assigns.thickness) / 2
×
NEW
144
    circumference = :math.pi() * radius
×
NEW
145
    progress = clamp(assigns.value, 0, 100) * (circumference / 100)
×
146

NEW
147
    assigns =
×
148
      assigns
149
      |> assign(:coordinate, coordinate)
150
      |> assign(:radius, radius)
151
      |> assign(:circumference, circumference)
152
      |> assign(:progress, progress)
153

NEW
154
    ~H"""
×
NEW
155
    <div
×
NEW
156
      id={@id}
×
157
      role="progressbar"
NEW
158
      aria-valuenow={@value}
×
159
      class={[
160
        "relative overflow-hidden w-fit",
NEW
161
        color_variant(nil, @color),
×
NEW
162
        @class
×
163
      ]}
NEW
164
      {@rest}
×
165
    >
NEW
166
      <svg
×
NEW
167
        width={@size}
×
NEW
168
        height={@size / 2}
×
NEW
169
        viewBox={"0 0 #{@size} #{@size / 2}"}
×
NEW
170
        class={["block", progress_rotation_class(@orientation, @fill_direction)]}
×
171
      >
NEW
172
        <circle
×
NEW
173
          cx={@coordinate}
×
NEW
174
          cy={@coordinate}
×
NEW
175
          r={@radius}
×
176
          fill="none"
177
          class="semi-circle-progress-base stroke-[#f4f4f4] dark:stroke-[#b6b6b6]"
NEW
178
          stroke-width={@thickness}
×
NEW
179
          stroke-dasharray={@circumference}
×
NEW
180
          stroke-dashoffset={@circumference}
×
181
        />
182

NEW
183
        <circle
×
NEW
184
          cx={@coordinate}
×
NEW
185
          cy={@coordinate}
×
NEW
186
          r={@radius}
×
187
          fill="none"
NEW
188
          stroke-linecap={@linecap}
×
NEW
189
          stroke-width={@thickness}
×
NEW
190
          stroke-dasharray={@circumference}
×
NEW
191
          stroke-dashoffset={@progress}
×
192
          class={[
193
            "semi-circle-progress-bar transition-all ease-in-out",
NEW
194
            "duration-[#{@transition_duration}ms]",
×
NEW
195
            @color
×
196
          ]}
197
        />
198
      </svg>
199

200
      <div
NEW
201
        :if={@label || @value}
×
202
        class={[
203
          "z-10 absolute left-1/2 transform -translate-x-1/2 font-medium",
NEW
204
          @orientation == "up" && "top-1/2",
×
NEW
205
          @orientation == "down" && "top-0  translate-y-1/2"
×
206
        ]}
207
      >
NEW
208
        {@label || "#{@value}%"}
×
209
      </div>
210
    </div>
211
    """
212
  end
213

214
  @doc """
215
  A function component that displays a ring progress bar.
216

217
  Usage:
218
      <.ring_progress id="example" value={50} />
219

220
  Attributes:
221
    - id: unique identifier (required)
222
    - value: current progress value (required)
223
    - max: maximum progress value (default is 100)
224
    - size: size of the SVG (width and height, default is 120)
225
    - thickness: width of the circle stroke (default is 10)
226
    - progress_color: stroke color for the progress circle (default is "#00aaff")
227
  """
228
  @doc type: :component
229
  attr :id, :string,
230
    required: true,
231
    doc: "A unique identifier used to manage state and interaction."
232

233
  attr :color, :string, default: "natural", doc: "Determines color theme"
234

235
  attr :value, :integer,
236
    required: true,
237
    doc: "The current value representing the progress completion (e.g., between 0 and max)."
238

239
  attr :max, :integer,
240
    default: 100,
241
    doc: "The maximum value the progress can reach. Default is 100."
242

243
  attr :size, :integer,
244
    default: 120,
245
    doc: "The overall size of the progress element, typically in pixels. Default is 120."
246

247
  attr :thickness, :integer,
248
    default: 10,
249
    doc: "The thickness of the progress stroke in pixels. Default is 10."
250

251
  attr :label, :string,
252
    default: nil,
253
    doc: "Optional label to be displayed along with the progress element."
254

255
  attr :class, :string,
256
    default: nil,
257
    doc: "Custom CSS class for additional styling."
258

259
  attr :linecap, :string,
260
    default: nil,
261
    doc: "Controls the shape of the stroke ends. Use 'round' for rounded corners."
262

263
  attr :rest, :global,
264
    doc:
265
      "Global attributes can define defaults which are merged with attributes provided by the caller"
266

267
  def ring_progress(assigns) do
NEW
268
    radius = (assigns.size - assigns.thickness) / 2
×
NEW
269
    circumference = 2 * :math.pi() * radius
×
NEW
270
    progress_fraction = (assigns.max > 0 && assigns.value / assigns.max) || 0
×
NEW
271
    dash_offset = circumference * (1 - progress_fraction)
×
272

NEW
273
    assigns =
×
274
      assigns
275
      |> assign(:radius, radius)
276
      |> assign(:circumference, circumference)
277
      |> assign(:dash_offset, dash_offset)
278

NEW
279
    ~H"""
×
NEW
280
    <div
×
NEW
281
      id={@id}
×
282
      role="progressbar"
NEW
283
      aria-valuenow={@value}
×
NEW
284
      class={["circular-progress", color_variant(nil, @color), @class]}
×
NEW
285
      {@rest}
×
286
    >
NEW
287
      <svg width={@size} height={@size} viewBox={"0 0 #{@size} #{@size}"}>
×
NEW
288
        <circle
×
NEW
289
          cx={@size / 2}
×
NEW
290
          cy={@size / 2}
×
NEW
291
          r={@radius}
×
292
          class="semi-circle-progress-base stroke-[#F4F4F4] dark:stroke-[#B6B6B6]"
NEW
293
          stroke-width={@thickness}
×
294
          fill="none"
295
        />
NEW
296
        <circle
×
NEW
297
          cx={@size / 2}
×
NEW
298
          cy={@size / 2}
×
NEW
299
          r={@radius}
×
NEW
300
          stroke-width={@thickness}
×
301
          class="semi-circle-progress-bar"
302
          fill="none"
NEW
303
          stroke-linecap={@linecap}
×
NEW
304
          stroke-dasharray={@circumference}
×
NEW
305
          stroke-dashoffset={@dash_offset}
×
NEW
306
          transform={"rotate(-90, #{@size / 2}, #{@size / 2})"}
×
307
        />
308
        <text
309
          x="50%"
310
          y="50%"
311
          dominant-baseline="central"
312
          text-anchor="middle"
313
          class="ring-progress-text font-semibold fill-current"
314
        >
NEW
315
          {@label || "#{@value}%"}
×
316
        </text>
317
      </svg>
318
    </div>
319
    """
320
  end
321

322
  @doc """
323
  Renders a section of a progress bar component (`progress_section`).
324

325
  Each section represents a part of the progress with its own value and color, allowing for
326
  segmented progress bars.
327

328
  ## Examples
329

330
  ```elixir
331
  <.progress>
332
    <.progress_section color="primary" value={10} />
333
    <.progress_section color="secondary" value={15} />
334
    <.progress_section color="misc" value={10} />
335
    <.progress_section color="danger" value={5} />
336
    <.progress_section color="warning" value={10} />
337
    <.progress_section color="success" value={10} />
338
    <.progress_section color="info" value={5} />
339
  </.progress>
340
  ```
341
  """
342
  @doc type: :component
343
  attr :value, :integer, default: 0, doc: ""
344
  attr :class, :string, default: nil, doc: "Custom CSS class for additional styling"
345

346
  attr :variation, :string,
347
    values: ["horizontal", "vertical"],
348
    default: "horizontal",
349
    doc: "Defines the layout orientation of the component"
350

351
  attr :color, :string, default: "natural", doc: "Determines color theme"
352
  attr :variant, :string, default: "base", doc: "Determines the style"
353
  attr :csp_nonce, :string, default: nil, doc: "csp nonce"
354

355
  attr :rest, :global,
356
    doc:
357
      "Global attributes can define defaults which are merged with attributes provided by the caller"
358

359
  slot :label, required: false do
360
    attr :class, :string, doc: "Custom CSS class for additional styling"
361
  end
362

363
  slot :tooltip, required: false do
364
    attr :label, :string, doc: "Determines element's text"
365
    attr :position, :string, doc: "Determines element's position"
366
    attr :clickable, :boolean, doc: "Determines element's click"
367
    attr :class, :string, doc: "Custom CSS class for additional styling"
368
  end
369

370
  def progress_section(assigns) do
371
    assigns =
×
372
      assigns
NEW
373
      |> assign_new(:tooltip, fn -> [] end)
×
374
      |> assign(:value, (is_integer(assigns.value) && assigns.value) || 0)
×
375
      |> assign_new(:id, fn -> random_id() end)
×
376

NEW
377
    ~H"""
×
NEW
378
    <.progress_section_with_tooltip :if={is_list(@tooltip) and @tooltip != []} {assigns} />
×
NEW
379
    <.progress_section_simple :if={@tooltip == [] or @tooltip == nil} {assigns} />
×
380
    """
381
  end
382

383
  defp progress_section_with_tooltip(assigns) do
384
    ~H"""
×
385
    <style :if={@csp_nonce} nonce={@csp_nonce}>
×
386
      #<%= @id %> {
×
387
        <%= if @variation == "horizontal" do %>
×
388
          width: <%= @value %>%;
×
389
        <% else %>
390
          height: <%= @value %>%;
×
391
        <% end %>
392
      }
393
    </style>
394

395
    <div
×
396
      phx-mounted={
397
        is_nil(@csp_nonce) &&
×
NEW
398
          JS.set_attribute({"style", dimension_style(@variation, @value)})
×
399
      }
400
      id={@id}
×
401
      role="presentation"
402
      aria-hidden="true"
403
      class={[
404
        "progress-section cursor-pointer",
NEW
405
        @variation == "vertical" && "progress-vertical",
×
NEW
406
        @variation == "horizontal" && "flex justify-center items-center",
×
NEW
407
        color_variant(@variant, @color),
×
NEW
408
        @class
×
409
      ]}
NEW
410
      {@rest}
×
411
    >
NEW
412
      <div
×
NEW
413
        :for={tooltip <- @tooltip}
×
NEW
414
        id={"tooltip-wrapper-#{@id}"}
×
415
        phx-hook="Floating"
416
        data-floating-type="tooltip"
417
        data-position={Map.get(tooltip, :position, "top")}
418
        data-smart-position="false"
NEW
419
        data-clickable={to_string(tooltip[:clickable])}
×
NEW
420
        aria-describedby={"#{@id}-tooltip"}
×
421
        class={[
422
          "w-full h-full",
423
          tooltip[:class]
424
        ]}
425
      >
426
        <div
427
          data-floating-trigger="true"
NEW
428
          aria-describedby={"#{@id}-tooltip"}
×
429
          class={[
430
            "w-full h-full flex items-center justify-center",
NEW
431
            @variation == "vertical" && "w-full"
×
432
          ]}
433
        >
NEW
434
          {tooltip[:label]}
×
435
        </div>
436

437
        <div
NEW
438
          id={"#{@id}-tooltip"}
×
439
          role="tooltip"
440
          data-floating-content="true"
441
          aria-hidden="false"
442
          role="tooltip"
443
          tabindex="0"
444
          aria-live="polite"
445
          hidden
NEW
446
          id={"#{@id}-tooltip"}
×
447
          class={[
448
            "absolute z-50 transition-all ease-in-out delay-100 duration-200 w-fit max-w-52",
449
            "progress-tooltip p-1 text-center bg-[#4B4B4B] text-white dark:bg-[#DDDDDD] dark:text-black rounded",
450
            tooltip[:class]
451
          ]}
452
        >
453
          <span class={[
454
            "block absolute size-[8px] bg-inherit rotate-45 -z-[1] tooltip-arrow",
455
            position_class(tooltip[:position])
456
          ]}>
457
          </span>
NEW
458
          {render_slot(tooltip)}
×
459
        </div>
460
      </div>
461
    </div>
462
    """
463
  end
464

465
  defp progress_section_simple(assigns) do
NEW
466
    ~H"""
×
NEW
467
    <style :if={@csp_nonce} nonce={@csp_nonce}>
×
NEW
468
      #<%= @id %> {
×
NEW
469
        <%= if @variation == "horizontal" do %>
×
NEW
470
          width: <%= @value %>%;
×
471
        <% else %>
NEW
472
          height: <%= @value %>%;
×
473
        <% end %>
474
      }
475
    </style>
476

NEW
477
    <div
×
478
      phx-mounted={
NEW
479
        is_nil(@csp_nonce) &&
×
NEW
480
          JS.set_attribute({"style", dimension_style(@variation, @value)})
×
481
      }
NEW
482
      id={@id}
×
483
      role="presentation"
484
      aria-hidden="true"
485
      class={[
486
        "w-full progress-section",
487
        if(@variation == "vertical", do: "progress-vertical"),
×
488
        if(@variation == "horizontal" && !is_nil(@label),
×
489
          do: "flex justify-center items-center [&_span]:text-[11px]"
490
        ),
491
        color_variant(@variant, @color),
×
492
        @class
×
493
      ]}
NEW
494
      {@rest}
×
495
    >
NEW
496
      <span :for={label <- @label} class={label[:class]} aria-hidden="false">
×
497
        {render_slot(label)}
×
498
      </span>
499
    </div>
500
    """
501
  end
502

NEW
503
  defp dimension_style("horizontal", value), do: "width: #{value}%;"
×
NEW
504
  defp dimension_style("vertical", value), do: "height: #{value}%;"
×
505

NEW
506
  defp position_class("top") do
×
507
    [
508
      "-bottom-[3px] -translate-x-1/2 left-1/2"
509
    ]
510
  end
511

NEW
512
  defp position_class("bottom") do
×
513
    [
514
      "-top-[3px] -translate-x-1/2 left-1/2"
515
    ]
516
  end
517

NEW
518
  defp position_class("left") do
×
519
    [
520
      "-right-[3px] -translate-y-1/2 top-1/2"
521
    ]
522
  end
523

NEW
524
  defp position_class("right") do
×
525
    [
526
      "-left-[3px] -translate-y-1/2 top-1/2"
527
    ]
528
  end
529

NEW
530
  defp position_class(_), do: position_class("top")
×
531

UNCOV
532
  defp rounded_size("extra_small") do
×
533
    "rounded-sm [&:not(.vertical-progress)_.progress-section:last-of-type]:rounded-e-sm"
534
  end
535

536
  defp rounded_size("small") do
×
537
    "rounded [&:not(.vertical-progress)_.progress-section:last-of-type]:rounded-e"
538
  end
539

540
  defp rounded_size("medium") do
×
541
    "rounded-md [&:not(.vertical-progress)_.progress-section:last-of-type]:rounded-e-md"
542
  end
543

544
  defp rounded_size("large") do
×
545
    "rounded-lg [&:not(.vertical-progress)_.progress-section:last-of-type]:rounded-e-lg"
546
  end
547

548
  defp rounded_size("extra_large") do
×
549
    "rounded-xl [&:not(.vertical-progress)_.progress-section:last-of-type]:rounded-e-xl"
550
  end
551

552
  defp rounded_size("full") do
×
553
    "rounded-full [&:not(.vertical-progress)_.progress-section:last-of-type]:rounded-e-full"
554
  end
555

556
  defp rounded_size(params) when is_binary(params), do: params
×
557

558
  defp size_class("extra_small", "horizontal"), do: "text-xs h-1.5 [&>*]:h-1.5"
×
559

560
  defp size_class("small", "horizontal"), do: "text-sm h-2 [&>*]:h-2"
×
561

562
  defp size_class("medium", "horizontal"), do: "text-base h-3 [&>*]:h-3"
×
563

564
  defp size_class("large", "horizontal"), do: "text-lg h-4 [&>*]:h-4"
×
565

566
  defp size_class("extra_large", "horizontal"), do: "text-xl h-5 [&>*]:h-5"
×
567

568
  defp size_class("double_large", "horizontal"), do: "text-xl h-6 [&>*]:h-6"
×
569

570
  defp size_class("triple_large", "horizontal"), do: "text-xl h-7 [&>*]:h-7"
×
571

572
  defp size_class("quadruple_large", "horizontal"), do: "text-xl h-8 [&>*]:h-8"
×
573

574
  defp size_class("extra_small", "vertical"), do: "text-xs w-1 h-[5rem]"
×
575

576
  defp size_class("small", "vertical"), do: "text-sm w-2 h-[6rem]"
×
577

578
  defp size_class("medium", "vertical"), do: "text-base w-3 h-[7rem]"
×
579

580
  defp size_class("large", "vertical"), do: "text-lg w-4 h-[8rem]"
×
581

582
  defp size_class("extra_large", "vertical"), do: "text-xl w-5 h-[9rem]"
×
583

584
  defp size_class("double_large", "vertical"), do: "text-xl w-6 h-[10rem]"
×
585

586
  defp size_class("triple_large", "vertical"), do: "text-xl w-7 h-[11rem]"
×
587

588
  defp size_class("quadruple_large", "vertical"), do: "text-xl w-8  h-[12rem]"
×
589

590
  defp size_class(params, _) when is_binary(params), do: params
×
591

592
  defp color_variant("base", _) do
×
593
    [
594
      "text-[#09090b] bg-[#e4e4e7] dark:text-[#FAFAFA] dark:bg-[#27272a]"
595
    ]
596
  end
597

598
  defp color_variant("default", "white") do
×
599
    [
600
      "bg-white text-black"
601
    ]
602
  end
603

604
  defp color_variant("default", "dark") do
×
605
    [
606
      "bg-[#282828] text-white"
607
    ]
608
  end
609

610
  defp color_variant("default", "natural") do
×
611
    [
612
      "bg-[#4B4B4B] text-white dark:bg-[#DDDDDD] dark:text-black"
613
    ]
614
  end
615

616
  defp color_variant("default", "primary") do
×
617
    [
618
      "bg-[#007F8C] text-white dark:bg-[#01B8CA] dark:text-black"
619
    ]
620
  end
621

622
  defp color_variant("default", "secondary") do
×
623
    [
624
      "bg-[#266EF1] text-white dark:bg-[#6DAAFB] dark:text-black"
625
    ]
626
  end
627

628
  defp color_variant("default", "success") do
×
629
    [
630
      "bg-[#0E8345] text-white dark:bg-[#06C167] dark:text-black"
631
    ]
632
  end
633

634
  defp color_variant("default", "warning") do
×
635
    [
636
      "bg-[#CA8D01] text-white dark:bg-[#FDC034] dark:text-black"
637
    ]
638
  end
639

640
  defp color_variant("default", "danger") do
×
641
    [
642
      "bg-[#DE1135] text-white dark:bg-[#FC7F79] dark:text-black"
643
    ]
644
  end
645

646
  defp color_variant("default", "info") do
×
647
    [
648
      "bg-[#0B84BA] text-white dark:bg-[#3EB7ED] dark:text-black"
649
    ]
650
  end
651

652
  defp color_variant("default", "misc") do
×
653
    [
654
      "bg-[#8750C5] text-white dark:bg-[#BA83F9] dark:text-black"
655
    ]
656
  end
657

658
  defp color_variant("default", "dawn") do
×
659
    [
660
      "bg-[#A86438] text-white dark:bg-[#DB976B] dark:text-black"
661
    ]
662
  end
663

664
  defp color_variant("default", "silver") do
×
665
    [
666
      "bg-[#868686] text-white dark:bg-[#A6A6A6] dark:text-black"
667
    ]
668
  end
669

670
  defp color_variant("gradient", "natural") do
×
671
    [
672
      "[&:not(.progress-vertical)]:bg-gradient-to-r rtl:[&:not(.progress-vertical)]:bg-gradient-to-l",
673
      "[&:not(.progress-vertical)]:from-[#282828] [&:not(.progress-vertical)]:via-[#727272] [&:not(.progress-vertical)]:to-[#e9ecef] text-white",
674
      "[&.progress-vertical]:bg-gradient-to-b [&.progress-vertical]:from-[#e9ecef] [&.progress-vertical]:to-[#727272] [&.progress-vertical]:via-[#727272]",
675
      "dark:[&:not(.progress-vertical)]:from-[#A6A6A6] dark:[&:not(.progress-vertical)]:via-[#FFFFFF] dark:[&:not(.progress-vertical)]:to-[#e9ecef] text-black",
676
      "dark:[&.progress-vertical]:to-[#A6A6A6] dark:[&.progress-vertical]:via-[#A6A6A6] dark:[&.progress-vertical]:from-[#e9ecef]"
677
    ]
678
  end
679

680
  defp color_variant("gradient", "primary") do
×
681
    [
682
      "[&:not(.progress-vertical)]:bg-gradient-to-r rtl:[&:not(.progress-vertical)]:bg-gradient-to-l",
683
      "[&:not(.progress-vertical)]:from-[#016974] [&:not(.progress-vertical)]:via-[#01B8CA] [&:not(.progress-vertical)]:to-[#e9ecef] text-white",
684
      "[&.progress-vertical]:bg-gradient-to-b [&.progress-vertical]:from-[#e9ecef] [&.progress-vertical]:to-[#01B8CA] [&.progress-vertical]:via-[#01B8CA]",
685
      "dark:[&:not(.progress-vertical)]:from-[#01B8CA] dark:[&:not(.progress-vertical)]:via-[#B0E7EF] dark:[&:not(.progress-vertical)]:to-[#e9ecef] text-black",
686
      "dark:[&.progress-vertical]:to-[#B0E7EF] dark:[&.progress-vertical]:via-[#B0E7EF] dark:[&.progress-vertical]:from-[#e9ecef]"
687
    ]
688
  end
689

690
  defp color_variant("gradient", "secondary") do
×
691
    [
692
      "[&:not(.progress-vertical)]:bg-gradient-to-r rtl:[&:not(.progress-vertical)]:bg-gradient-to-l",
693
      "[&:not(.progress-vertical)]:from-[#175BCC] [&:not(.progress-vertical)]:via-[#6DAAFB] [&:not(.progress-vertical)]:to-[#e9ecef] text-white",
694
      "[&.progress-vertical]:bg-gradient-to-b [&.progress-vertical]:from-[#e9ecef] [&.progress-vertical]:to-[#6DAAFB] [&.progress-vertical]:via-[#6DAAFB]",
695
      "dark:[&:not(.progress-vertical)]:from-[#6DAAFB] dark:[&:not(.progress-vertical)]:via-[#CDDEFF] dark:[&:not(.progress-vertical)]:to-[#e9ecef] text-black",
696
      "dark:[&.progress-vertical]:to-[#CDDEFF] dark:[&.progress-vertical]:via-[#CDDEFF] dark:[&.progress-vertical]:from-[#e9ecef]"
697
    ]
698
  end
699

700
  defp color_variant("gradient", "success") do
×
701
    [
702
      "[&:not(.progress-vertical)]:bg-gradient-to-r rtl:[&:not(.progress-vertical)]:bg-gradient-to-l",
703
      "[&:not(.progress-vertical)]:from-[#166C3B] [&:not(.progress-vertical)]:via-[#06C167] [&:not(.progress-vertical)]:to-[#e9ecef] text-white",
704
      "[&.progress-vertical]:bg-gradient-to-b [&.progress-vertical]:from-[#e9ecef] [&.progress-vertical]:to-[#06C167] [&.progress-vertical]:via-[#06C167]",
705
      "dark:[&:not(.progress-vertical)]:from-[#06C167] dark:[&:not(.progress-vertical)]:via-[#B1EAC2] dark:[&:not(.progress-vertical)]:to-[#e9ecef] text-black",
706
      "dark:[&.progress-vertical]:to-[#B1EAC2] dark:[&.progress-vertical]:via-[#B1EAC2] dark:[&.progress-vertical]:from-[#e9ecef]"
707
    ]
708
  end
709

710
  defp color_variant("gradient", "warning") do
×
711
    [
712
      "[&:not(.progress-vertical)]:bg-gradient-to-r rtl:[&:not(.progress-vertical)]:bg-gradient-to-l",
713
      "[&:not(.progress-vertical)]:from-[#976A01] [&:not(.progress-vertical)]:via-[#FDC034] [&:not(.progress-vertical)]:to-[#e9ecef] text-white",
714
      "[&.progress-vertical]:bg-gradient-to-b [&.progress-vertical]:from-[#e9ecef] [&.progress-vertical]:to-[#976A01] [&.progress-vertical]:via-[#976A01]",
715
      "dark:[&:not(.progress-vertical)]:from-[#FDC034] dark:[&:not(.progress-vertical)]:via-[#FEDF99] dark:[&:not(.progress-vertical)]:to-[#e9ecef] text-black",
716
      "dark:[&.progress-vertical]:to-[#FEDF99] dark:[&.progress-vertical]:via-[#FEDF99] dark:[&.progress-vertical]:from-[#e9ecef]"
717
    ]
718
  end
719

720
  defp color_variant("gradient", "danger") do
×
721
    [
722
      "[&:not(.progress-vertical)]:bg-gradient-to-r rtl:[&:not(.progress-vertical)]:bg-gradient-to-l",
723
      "[&:not(.progress-vertical)]:from-[#BB032A] [&:not(.progress-vertical)]:via-[#FC7F79] [&:not(.progress-vertical)]:to-[#e9ecef] text-white",
724
      "[&.progress-vertical]:bg-gradient-to-b [&.progress-vertical]:from-[#e9ecef] [&.progress-vertical]:to-[#FC7F79] [&.progress-vertical]:via-[#FC7F79]",
725
      "dark:[&:not(.progress-vertical)]:from-[#FC7F79] dark:[&:not(.progress-vertical)]:via-[#FFD2CD] dark:[&:not(.progress-vertical)]:to-[#e9ecef] text-black",
726
      "dark:[&.progress-vertical]:to-[#FFD2CD] dark:[&.progress-vertical]:via-[#FFD2CD] dark:[&.progress-vertical]:from-[#e9ecef]"
727
    ]
728
  end
729

730
  defp color_variant("gradient", "info") do
×
731
    [
732
      "[&:not(.progress-vertical)]:bg-gradient-to-r rtl:[&:not(.progress-vertical)]:bg-gradient-to-l",
733
      "[&:not(.progress-vertical)]:from-[#08638C] [&:not(.progress-vertical)]:via-[#3EB7ED] [&:not(.progress-vertical)]:to-[#e9ecef] text-white",
734
      "[&.progress-vertical]:bg-gradient-to-b [&.progress-vertical]:from-[#e9ecef] [&.progress-vertical]:to-[#3EB7ED] [&.progress-vertical]:via-[#3EB7ED]",
735
      "dark:[&:not(.progress-vertical)]:from-[#3EB7ED] dark:[&:not(.progress-vertical)]:via-[#9FDBF6] dark:[&:not(.progress-vertical)]:to-[#e9ecef] text-black",
736
      "dark:[&.progress-vertical]:to-[#9FDBF6] dark:[&.progress-vertical]:via-[#9FDBF6] dark:[&.progress-vertical]:from-[#e9ecef]"
737
    ]
738
  end
739

740
  defp color_variant("gradient", "misc") do
×
741
    [
742
      "[&:not(.progress-vertical)]:bg-gradient-to-r rtl:[&:not(.progress-vertical)]:bg-gradient-to-l",
743
      "[&:not(.progress-vertical)]:from-[#653C94] [&:not(.progress-vertical)]:via-[#BA83F9] [&:not(.progress-vertical)]:to-[#e9ecef] text-white",
744
      "[&.progress-vertical]:bg-gradient-to-b [&.progress-vertical]:from-[#e9ecef] [&.progress-vertical]:to-[#BA83F9] [&.progress-vertical]:via-[#BA83F9]",
745
      "dark:[&:not(.progress-vertical)]:from-[#BA83F9] dark:[&:not(.progress-vertical)]:via-[#DDC1FC] dark:[&:not(.progress-vertical)]:to-[#e9ecef] text-black",
746
      "dark:[&.progress-vertical]:to-[#DDC1FC] dark:[&.progress-vertical]:via-[#DDC1FC] dark:[&.progress-vertical]:from-[#e9ecef]"
747
    ]
748
  end
749

750
  defp color_variant("gradient", "dawn") do
×
751
    [
752
      "[&:not(.progress-vertical)]:bg-gradient-to-r rtl:[&:not(.progress-vertical)]:bg-gradient-to-l",
753
      "[&:not(.progress-vertical)]:from-[#7E4B2A] [&:not(.progress-vertical)]:via-[#DB976B] [&:not(.progress-vertical)]:to-[#e9ecef] text-white",
754
      "[&.progress-vertical]:bg-gradient-to-b [&.progress-vertical]:from-[#e9ecef] [&.progress-vertical]:to-[#DB976B] [&.progress-vertical]:via-[#DB976B]",
755
      "dark:[&:not(.progress-vertical)]:from-[#DB976B] dark:[&:not(.progress-vertical)]:via-[#EDCBB5] dark:[&:not(.progress-vertical)]:to-[#e9ecef] text-black",
756
      "dark:[&.progress-vertical]:to-[#EDCBB5] dark:[&.progress-vertical]:via-[#EDCBB5] dark:[&.progress-vertical]:from-[#e9ecef]"
757
    ]
758
  end
759

760
  defp color_variant("gradient", "silver") do
×
761
    [
762
      "[&:not(.progress-vertical)]:bg-gradient-to-r rtl:[&:not(.progress-vertical)]:bg-gradient-to-l",
763
      "[&:not(.progress-vertical)]:from-[#5E5E5E] [&:not(.progress-vertical)]:via-[#A6A6A6] [&:not(.progress-vertical)]:to-[#e9ecef] text-white",
764
      "[&.progress-vertical]:bg-gradient-to-b [&.progress-vertical]:from-[#e9ecef] [&.progress-vertical]:to-[#5E5E5E] [&.progress-vertical]:via-[#5E5E5E]",
765
      "dark:[&:not(.progress-vertical)]:from-[#868686] dark:[&:not(.progress-vertical)]:via-[#BBBBBB] dark:[&:not(.progress-vertical)]:to-[#e9ecef] text-black",
766
      "dark:[&.progress-vertical]:to-[#BBBBBB] dark:[&.progress-vertical]:via-[#BBBBBB] dark:[&.progress-vertical]:from-[#e9ecef]"
767
    ]
768
  end
769

NEW
770
  defp color_variant(nil, "natural") do
×
771
    [
772
      "[&_.semi-circle-progress-bar]:stroke-[#4B4B4B] dark:[&_.semi-circle-progress-bar]:stroke-[#DDDDDD]"
773
    ]
774
  end
775

NEW
776
  defp color_variant(nil, "primary") do
×
777
    [
778
      "[&_.semi-circle-progress-bar]:stroke-[#007F8C] dark:[&_.semi-circle-progress-bar]:stroke-[#01B8CA]"
779
    ]
780
  end
781

NEW
782
  defp color_variant(nil, "secondary") do
×
783
    [
784
      "[&_.semi-circle-progress-bar]:stroke-[#266EF1] dark:[&_.semi-circle-progress-bar]:stroke-[#6DAAFB]"
785
    ]
786
  end
787

NEW
788
  defp color_variant(nil, "success") do
×
789
    [
790
      "[&_.semi-circle-progress-bar]:stroke-[#0E8345] dark:[&_.semi-circle-progress-bar]:stroke-[#06C167]"
791
    ]
792
  end
793

NEW
794
  defp color_variant(nil, "warning") do
×
795
    [
796
      "[&_.semi-circle-progress-bar]:stroke-[#CA8D01] dark:[&_.semi-circle-progress-bar]:stroke-[#FDC034]"
797
    ]
798
  end
799

NEW
800
  defp color_variant(nil, "danger") do
×
801
    [
802
      "[&_.semi-circle-progress-bar]:stroke-[#DE1135] dark:[&_.semi-circle-progress-bar]:stroke-[#FC7F79]"
803
    ]
804
  end
805

NEW
806
  defp color_variant(nil, "info") do
×
807
    [
808
      "[&_.semi-circle-progress-bar]:stroke-[#0B84BA] dark:[&_.semi-circle-progress-bar]:stroke-[#3EB7ED]"
809
    ]
810
  end
811

NEW
812
  defp color_variant(nil, "misc") do
×
813
    [
814
      "[&_.semi-circle-progress-bar]:stroke-[#8750C5] dark:[&_.semi-circle-progress-bar]:stroke-[#BA83F9]"
815
    ]
816
  end
817

NEW
818
  defp color_variant(nil, "dawn") do
×
819
    [
820
      "[&_.semi-circle-progress-bar]:stroke-[#A86438] dark:[&_.semi-circle-progress-bar]:stroke-[#DB976B]"
821
    ]
822
  end
823

NEW
824
  defp color_variant(nil, "silver") do
×
825
    [
826
      "[&_.semi-circle-progress-bar]:stroke-[#868686] dark:[&_.semi-circle-progress-bar]:stroke-[#A6A6A6]"
827
    ]
828
  end
829

UNCOV
830
  defp color_variant(params, _) when is_binary(params), do: params
×
831

NEW
832
  defp progress_rotation_class("down", "right-to-left"), do: "rotate-180 scale-x-[-1]"
×
NEW
833
  defp progress_rotation_class("down", _), do: "rotate-180"
×
NEW
834
  defp progress_rotation_class("up", "left-to-right"), do: "scale-x-[-1]"
×
NEW
835
  defp progress_rotation_class(_, _), do: ""
×
836

NEW
837
  defp clamp(val, min, max), do: max(min, min(val, max))
×
838
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc