Progress   A
last analyzed

Complexity

Total Complexity 12

Size/Duplication

Total Lines 151
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 12
eloc 47
c 1
b 0
f 0
dl 0
loc 151
ccs 51
cts 51
cp 1
rs 10

8 Methods

Rating   Name   Duplication   Size   Complexity  
A render() 0 10 2
A label() 0 6 1
A percent() 0 6 1
A options() 0 6 1
A bars() 0 6 1
A renderBar() 0 16 1
A barOptions() 0 6 1
A renderProgress() 0 22 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Bootstrap5;
6
7
use JsonException;
8
use RuntimeException;
9
use Yiisoft\Arrays\ArrayHelper;
10
use Yiisoft\Html\Html;
11
12
use function array_merge;
13
use function implode;
14
use function rtrim;
15
use function trim;
16
17
/**
18
 * Progress renders a bootstrap progress bar component.
19
 *
20
 * For example,
21
 *
22
 * ```php
23
 * // default with label
24
 * echo Progress::widget()
25
 *     ->percent('60')
26
 *     ->label('test');
27
 *
28
 * // styled
29
 * echo Progress::widget()
30
 *     ->bars([
31
 *         ['percent' => '65', 'options' => ['class' => 'bg-danger']]
32
 *     ]);
33
 *
34
 * // striped
35
 * echo Progress::widget()
36
 *     ->bars([
37
 *         ['percent' => '70', 'options' => ['class' => 'bg-warning progress-bar-striped']]
38
 *     ]);
39
 *
40
 * // striped animated
41
 * echo Progress::widget()
42
 *     ->percent('70')
43
 *     ->barOptions(['class' => 'bg-success progress-bar-animated progress-bar-striped']);
44
 *
45
 * // stacked bars
46
 * echo Progress::widget()
47
 *     bars => ([
48
 *         ['percent' => '30', 'options' => ['class' => 'bg-danger']],
49
 *         ['percent' => '30', 'label' => 'test', 'options' => ['class' => 'bg-success']],
50
 *         ['percent' => '35', 'options' => ['class' => 'bg-warning']],
51
 *     ]);
52
 * ```
53
 */
54
final class Progress extends Widget
55
{
56
    private string $label = '';
57
    private string $percent = '';
58
    private array $bars = [];
59
    private array $options = [];
60
    private array $barOptions = [];
61
    private bool $encodeTags = false;
62
63 6
    public function render(): string
64
    {
65 6
        if (!isset($this->options['id'])) {
66 6
            $this->options['id'] = "{$this->getId()}-progress";
67
        }
68
69
        /** @psalm-suppress InvalidArgument */
70 6
        Html::addCssClass($this->options, ['widget' => 'progress']);
71
72 6
        return $this->renderProgress();
73
    }
74
75
    /**
76
     * Set of bars that are stacked together to form a single progress bar.
77
     *
78
     * Each bar is an array of the following structure:
79
     *
80
     * ```php
81
     * [
82
     *     // required, the amount of progress as a percentage.
83
     *     'percent' => '30',
84
     *     // optional, the label to be displayed on the bar
85
     *     'label' => '30%',
86
     *     // optional, array, additional HTML attributes for the bar tag
87
     *     'options' => [],
88
     * ]
89
     * ```
90
     */
91 4
    public function bars(array $value): self
92
    {
93 4
        $new = clone $this;
94 4
        $new->bars = $value;
95
96 4
        return $new;
97
    }
98
99
    /**
100
     * The HTML attributes of the bar. This property will only be considered if {@see bars} is empty.
101
     *
102
     * {@see Html::renderTagAttributes() for details on how attributes are being rendered}
103
     */
104 1
    public function barOptions(array $value): self
105
    {
106 1
        $new = clone $this;
107 1
        $new->barOptions = $value;
108
109 1
        return $new;
110
    }
111
112
    /**
113
     * The button label.
114
     */
115 1
    public function label(string $value): self
116
    {
117 1
        $new = clone $this;
118 1
        $new->label = $value;
119
120 1
        return $new;
121
    }
122
123
    /**
124
     * The HTML attributes for the widget container tag. The following special options are recognized.
125
     *
126
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
127
     */
128 1
    public function options(array $value): self
129
    {
130 1
        $new = clone $this;
131 1
        $new->options = $value;
132
133 1
        return $new;
134
    }
135
136
    /**
137
     * The amount of progress as a percentage.
138
     */
139 2
    public function percent(string $value): self
140
    {
141 2
        $new = clone $this;
142 2
        $new->percent = $value;
143
144 2
        return $new;
145
    }
146
147
    /**
148
     * Renders the progress.
149
     *
150
     * @throws JsonException|RuntimeException if the "percent" option is not set in a stacked progress bar.
151
     *
152
     * @return string the rendering result.
153
     */
154 6
    private function renderProgress(): string
155
    {
156 6
        if (empty($this->bars)) {
157 2
            $this->bars = [
158 2
                ['label' => $this->label, 'percent' => $this->percent, 'options' => $this->barOptions],
159 2
            ];
160
        }
161
162 6
        $bars = [];
163
164 6
        foreach ($this->bars as $bar) {
165 6
            $label = ArrayHelper::getValue($bar, 'label', '');
166 6
            if (!isset($bar['percent'])) {
167 1
                throw new RuntimeException('The "percent" option is required.');
168
            }
169 5
            $options = ArrayHelper::getValue($bar, 'options', []);
170 5
            $bars[] = $this->renderBar($bar['percent'], $label, $options);
171
        }
172
173 5
        return Html::div(implode("\n", $bars), $this->options)
174 5
            ->encode($this->encodeTags)
175 5
            ->render();
176
    }
177
178
    /**
179
     * Generates a bar.
180
     *
181
     * @param string $percent the percentage of the bar
182
     * @param string $label , optional, the label to display at the bar
183
     * @param array $options the HTML attributes of the bar
184
     *
185
     * @throws JsonException
186
     *
187
     * @return string the rendering result.
188
     */
189 5
    private function renderBar(string $percent, string $label = '', array $options = []): string
190
    {
191 5
        $valuePercent = (float)trim(rtrim($percent, '%'));
192
193 5
        $options = array_merge($options, [
194 5
            'role' => 'progressbar',
195 5
            'aria-valuenow' => $percent,
196 5
            'aria-valuemin' => 0,
197 5
            'aria-valuemax' => 100,
198 5
        ]);
199
200
        /** @psalm-suppress InvalidArgument */
201 5
        Html::addCssClass($options, ['widget' => 'progress-bar']);
202 5
        Html::addCssStyle($options, ['width' => $valuePercent . '%'], true);
203
204 5
        return Html::div($label, $options)->render();
205
    }
206
}
207