Passed
Pull Request — master (#35)
by Wilmer
02:31
created

Progress::run()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 14
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 6
c 1
b 0
f 0
nc 4
nop 0
dl 0
loc 14
ccs 7
cts 7
cp 1
crap 3
rs 10
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
 *     ->withPercent('60')
26
 *     ->withLabel('test');
27
 *
28
 * // styled
29
 * echo Progress::widget()
30
 *     ->withBars([
31
 *         ['percent' => '65', 'options' => ['class' => 'bg-danger']]
32
 *     ]);
33
 *
34
 * // striped
35
 * echo Progress::widget()
36
 *     ->withBars([
37
 *         ['percent' => '70', 'options' => ['class' => 'bg-warning progress-bar-striped']]
38
 *     ]);
39
 *
40
 * // striped animated
41
 * echo Progress::widget()
42
 *     ->withPercent('70')
43
 *     ->withBarOptions(['class' => 'bg-success progress-bar-animated progress-bar-striped']);
44
 *
45
 * // stacked bars
46
 * echo Progress::widget()
47
 *     withBars => ([
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 7
    protected function run(): string
64
    {
65 7
        if (!isset($this->options['id'])) {
66 7
            $this->options['id'] = "{$this->getId()}-progress";
67
        }
68
69
        /** @psalm-suppress InvalidArgument */
70 7
        Html::addCssClass($this->options, ['widget' => 'progress']);
71
72 7
        if ($this->encodeTags === false) {
73 6
            $this->options = array_merge($this->options, ['encode' => false]);
74
        }
75
76 7
        return $this->renderProgress();
77
    }
78
79
    /**
80
     * Set of bars that are stacked together to form a single progress bar.
81
     *
82
     * Each bar is an array of the following structure:
83
     *
84
     * ```php
85
     * [
86
     *     // required, the amount of progress as a percentage.
87
     *     'percent' => '30',
88
     *     // optional, the label to be displayed on the bar
89
     *     'label' => '30%',
90
     *     // optional, array, additional HTML attributes for the bar tag
91
     *     'options' => [],
92
     * ]
93
     * ```
94
     *
95
     * @param array $value
96
     *
97
     * @return $this
98
     */
99 5
    public function withBars(array $value): self
100
    {
101 5
        $new = clone $this;
102 5
        $new->bars = $value;
103
104 5
        return $new;
105
    }
106
107
    /**
108
     * The HTML attributes of the bar. This property will only be considered if {@see bars} is empty.
109
     *
110
     * @param array $value
111
     *
112
     * @return $this
113
     *
114
     * {@see Html::renderTagAttributes() for details on how attributes are being rendered}
115
     */
116 1
    public function withBarOptions(array $value): self
117
    {
118 1
        $new = clone $this;
119 1
        $new->barOptions = $value;
120
121 1
        return $new;
122
    }
123
124
    /**
125
     * The button label.
126
     *
127
     * @param string $value
128
     *
129
     * @return $this
130
     */
131 1
    public function withLabel(string $value): self
132
    {
133 1
        $new = clone $this;
134 1
        $new->label = $value;
135
136 1
        return $new;
137
    }
138
139
    /**
140
     * The HTML attributes for the widget container tag. The following special options are recognized.
141
     *
142
     * @param array $value
143
     *
144
     * @return $this
145
     *
146
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
147
     */
148 1
    public function withOptions(array $value): self
149
    {
150 1
        $new = clone $this;
151 1
        $new->options = $value;
152
153 1
        return $new;
154
    }
155
156
    /**
157
     * The amount of progress as a percentage.
158
     *
159
     * @param string $value
160
     *
161
     * @return $this
162
     */
163 2
    public function withPercent(string $value): self
164
    {
165 2
        $new = clone $this;
166 2
        $new->percent = $value;
167
168 2
        return $new;
169
    }
170
171
    /**
172
     * Allows you to enable or disable the encoding tags html.
173
     *
174
     * @param bool $value
175
     *
176
     * @return self
177
     */
178 1
    public function withEncodeTags(bool $value = true): self
179
    {
180 1
        $new = clone $this;
181 1
        $new->encodeTags = $value;
182
183 1
        return $new;
184
    }
185
186
    /**
187
     * Renders the progress.
188
     *
189
     * @throws JsonException|RuntimeException if the "percent" option is not set in a stacked progress bar.
190
     *
191
     * @return string the rendering result.
192
     */
193 7
    private function renderProgress(): string
194
    {
195 7
        if (empty($this->bars)) {
196 2
            $this->bars = [
197 2
                ['label' => $this->label, 'percent' => $this->percent, 'options' => $this->barOptions],
198
            ];
199
        }
200
201 7
        $bars = [];
202
203 7
        foreach ($this->bars as $bar) {
204 7
            $label = ArrayHelper::getValue($bar, 'label', '');
205 7
            if (!isset($bar['percent'])) {
206 1
                throw new RuntimeException('The "percent" option is required.');
207
            }
208 6
            $options = ArrayHelper::getValue($bar, 'options', []);
209 6
            $bars[] = $this->renderBar($bar['percent'], $label, $options);
210
        }
211
212 6
        return Html::div(implode("\n", $bars), $this->options);
213
    }
214
215
    /**
216
     * Generates a bar.
217
     *
218
     * @param string $percent the percentage of the bar
219
     * @param string $label , optional, the label to display at the bar
220
     * @param array $options the HTML attributes of the bar
221
     *
222
     * @throws JsonException
223
     *
224
     * @return string the rendering result.
225
     */
226 6
    private function renderBar(string $percent, string $label = '', array $options = []): string
227
    {
228 6
        $valuePercent = (float) trim(rtrim($percent, '%'));
229
230 6
        $options = array_merge($options, [
231 6
            'role' => 'progressbar',
232 6
            'aria-valuenow' => $percent,
233 6
            'aria-valuemin' => 0,
234 6
            'aria-valuemax' => 100,
235
        ]);
236
237
        /** @psalm-suppress InvalidArgument */
238 6
        Html::addCssClass($options, ['widget' => 'progress-bar']);
239 6
        Html::addCssStyle($options, ['width' => $valuePercent . '%'], true);
240
241 6
        return Html::div($label, $options);
242
    }
243
}
244