Passed
Push — master ( 2301b1...386ec7 )
by Alexander
14:22 queued 11:36
created

Progress::render()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 10
ccs 5
cts 5
cp 1
crap 2
rs 10
c 0
b 0
f 0
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
     * @param array $value
92
     */
93 4
    public function bars(array $value): self
94
    {
95 4
        $new = clone $this;
96 4
        $new->bars = $value;
97
98 4
        return $new;
99
    }
100
101
    /**
102
     * The HTML attributes of the bar. This property will only be considered if {@see bars} is empty.
103
     *
104
     * @param array $value
105
     *
106
     * {@see Html::renderTagAttributes() for details on how attributes are being rendered}
107
     */
108 1
    public function barOptions(array $value): self
109
    {
110 1
        $new = clone $this;
111 1
        $new->barOptions = $value;
112
113 1
        return $new;
114
    }
115
116
    /**
117
     * The button label.
118
     */
119 1
    public function label(string $value): self
120
    {
121 1
        $new = clone $this;
122 1
        $new->label = $value;
123
124 1
        return $new;
125
    }
126
127
    /**
128
     * The HTML attributes for the widget container tag. The following special options are recognized.
129
     *
130
     * @param array $value
131
     *
132
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
133
     */
134 1
    public function options(array $value): self
135
    {
136 1
        $new = clone $this;
137 1
        $new->options = $value;
138
139 1
        return $new;
140
    }
141
142
    /**
143
     * The amount of progress as a percentage.
144
     */
145 2
    public function percent(string $value): self
146
    {
147 2
        $new = clone $this;
148 2
        $new->percent = $value;
149
150 2
        return $new;
151
    }
152
153
    /**
154
     * Renders the progress.
155
     *
156
     * @throws JsonException|RuntimeException if the "percent" option is not set in a stacked progress bar.
157
     *
158
     * @return string the rendering result.
159
     */
160 6
    private function renderProgress(): string
161
    {
162 6
        if (empty($this->bars)) {
163 2
            $this->bars = [
164 2
                ['label' => $this->label, 'percent' => $this->percent, 'options' => $this->barOptions],
165 2
            ];
166
        }
167
168 6
        $bars = [];
169
170 6
        foreach ($this->bars as $bar) {
171 6
            $label = ArrayHelper::getValue($bar, 'label', '');
172 6
            if (!isset($bar['percent'])) {
173 1
                throw new RuntimeException('The "percent" option is required.');
174
            }
175 5
            $options = ArrayHelper::getValue($bar, 'options', []);
176 5
            $bars[] = $this->renderBar($bar['percent'], $label, $options);
177
        }
178
179 5
        return Html::div(implode("\n", $bars), $this->options)
180 5
            ->encode($this->encodeTags)
181 5
            ->render();
182
    }
183
184
    /**
185
     * Generates a bar.
186
     *
187
     * @param string $percent the percentage of the bar
188
     * @param string $label , optional, the label to display at the bar
189
     * @param array $options the HTML attributes of the bar
190
     *
191
     * @throws JsonException
192
     *
193
     * @return string the rendering result.
194
     */
195 5
    private function renderBar(string $percent, string $label = '', array $options = []): string
196
    {
197 5
        $valuePercent = (float)trim(rtrim($percent, '%'));
198
199 5
        $options = array_merge($options, [
200 5
            'role' => 'progressbar',
201 5
            'aria-valuenow' => $percent,
202 5
            'aria-valuemin' => 0,
203 5
            'aria-valuemax' => 100,
204 5
        ]);
205
206
        /** @psalm-suppress InvalidArgument */
207 5
        Html::addCssClass($options, ['widget' => 'progress-bar']);
208 5
        Html::addCssStyle($options, ['width' => $valuePercent . '%'], true);
209
210 5
        return Html::div($label, $options)->render();
211
    }
212
}
213