Passed
Push — master ( e80b4e...741461 )
by Alexander
02:19
created

Panel::renderTab()   B

Complexity

Conditions 8
Paths 18

Size

Total Lines 42
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 8

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
eloc 24
nc 18
nop 2
dl 0
loc 42
ccs 25
cts 25
cp 1
crap 8
rs 8.4444
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Bulma;
6
7
use InvalidArgumentException;
8
use Yiisoft\Arrays\ArrayHelper;
9
use Yiisoft\Html\Html;
10
11
final class Panel extends Widget
12
{
13
    public const COLOR_PRIMARY = 'is-primary';
14
    public const COLOR_LINK = 'is-link';
15
    public const COLOR_INFO = 'is-info';
16
    public const COLOR_SUCCESS = 'is-success';
17
    public const COLOR_WARNING = 'is-warning';
18
    public const COLOR_DANGER = 'is-danger';
19
    private const COLOR_ALL = [
20
        self::COLOR_PRIMARY,
21
        self::COLOR_LINK,
22
        self::COLOR_INFO,
23
        self::COLOR_SUCCESS,
24
        self::COLOR_WARNING,
25
        self::COLOR_DANGER,
26
    ];
27
28
    private array $options = [];
29
    private array $headingOptions = [];
30
    private ?string $heading = null;
31
    private string $color = '';
32
    private array $tabs = [];
33
    private array $tabsOptions = [];
34
    private bool $encodeLabels = true;
35
    private bool $encodeTags = false;
36
    private string $template = '{panelBegin}{panelHeading}{panelTabs}{panelItems}{panelEnd}';
37
    private array $tabItems = [];
38
39 13
    protected function run(): string
40
    {
41 13
        $this->buildOptions();
42
43 13
        $tag = ArrayHelper::getValue($this->options, 'tag', 'nav');
44
45 13
        return strtr($this->template, [
46 13
            '{panelBegin}' => Html::beginTag($tag, $this->options),
0 ignored issues
show
Bug introduced by
It seems like $this->options can also be of type object; however, parameter $options of Yiisoft\Html\Html::beginTag() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

46
            '{panelBegin}' => Html::beginTag($tag, /** @scrutinizer ignore-type */ $this->options),
Loading history...
47 13
            '{panelHeading}' => $this->renderHeading(),
48 13
            '{panelTabs}' => $this->renderTabs(),
49 11
            '{panelItems}' => implode("\n", $this->tabItems),
50 11
            '{panelEnd}' => Html::endTag($tag),
51
        ]);
52
    }
53
54
    /**
55
     * String the template for rendering panel.
56
     *
57
     * - `{panelBegin}`: _string_, which will render the panel container begin.
58
     * - `{panelHeading}`: _string_, which will render the panel heading.
59
     * - `{panelTabs}`: _string_, which will render the panel tabs.
60
     * - `{panelItems}`: _string_, which will render the panel items.
61
     * - `{panelEnd}`: _string_, which will render the panel container end.
62
     *
63
     * @param string $value
64
     *
65
     * @return self
66
     */
67 1
    public function template(string $value): self
68
    {
69 1
        $new = clone $this;
70 1
        $new->template = $value;
71
72 1
        return $new;
73
    }
74
75
    /**
76
     * @param array $value
77
     *
78
     * @return self
79
     */
80 1
    public function options(array $value): self
81
    {
82 1
        $new = clone $this;
83 1
        $new->options = $value;
84
85 1
        return $new;
86
    }
87
88
    /**
89
     * @param string $value
90
     *
91
     * @return self
92
     */
93 3
    public function heading(string $value): self
94
    {
95 3
        $new = clone $this;
96 3
        $new->heading = $value;
97
98 3
        return $new;
99
    }
100
101
    /**
102
     * @param array $value
103
     *
104
     * @return self
105
     */
106 1
    public function headingOptions(array $value): self
107
    {
108 1
        $new = clone $this;
109 1
        $new->headingOptions = $value;
110
111 1
        return $new;
112
    }
113
114
    /**
115
     * Set progress bar color.
116
     *
117
     * @var string $value Color class.
118
     *
119
     * @return self
120
     */
121 2
    public function color(string $value): self
122
    {
123 2
        if (!in_array($value, self::COLOR_ALL, true)) {
124 1
            $values = implode('", "', self::COLOR_ALL);
125 1
            throw new InvalidArgumentException("Invalid color. Valid values are: \"$values\".");
126
        }
127
128 1
        $new = clone $this;
129 1
        $new->color = $value;
130
131 1
        return $new;
132
    }
133
134
    /**
135
     * @param array $value
136
     *
137
     * @return self
138
     */
139 8
    public function tabs(array $value): self
140
    {
141 8
        $new = clone $this;
142 8
        $new->tabs = $value;
143
144 8
        return $new;
145
    }
146
147
    /**
148
     * @param array $value
149
     *
150
     * @return self
151
     */
152 1
    public function tabsOptions(array $value): self
153
    {
154 1
        $new = clone $this;
155 1
        $new->tabsOptions = $value;
156
157 1
        return $new;
158
    }
159
160
    /**
161
     * @throws \JsonException
162
     *
163
     * @return string
164
     */
165 13
    private function renderHeading(): string
166
    {
167 13
        if ($this->heading !== null) {
168 3
            Html::addCssClass($this->headingOptions, 'panel-heading');
169 3
            return "\n" . Html::tag('p', $this->heading, $this->headingOptions) . "\n";
170
        }
171
172 10
        return '';
173
    }
174
175
    /**
176
     * @throws \JsonException
177
     *
178
     * @return string
179
     */
180 13
    private function renderTabs(): string
181
    {
182 13
        if (!empty($this->tabs)) {
183 8
            $tabs = '';
184 8
            foreach ($this->tabs as $index => $item) {
185 8
                $tabs .= $this->renderTab($index, $item) . "\n";
186
            }
187
188 6
            Html::addCssClass($this->tabsOptions, 'panel-tabs');
189
190 6
            return "\n" . Html::tag('p', "\n" . $tabs, $this->tabsOptions) . "\n";
191
        }
192
193 5
        return '';
194
    }
195
196 13
    private function buildOptions(): void
197
    {
198 13
        Html::addCssClass($this->options, 'panel');
199
200 13
        $this->options['id'] ??= $this->getId() . '-panel';
201
202 13
        if ($this->encodeTags === false) {
203 13
            $this->tabsOptions = array_merge($this->tabsOptions, ['encode' => false]);
204
        }
205
206 13
        if ($this->color !== '') {
207 1
            Html::addCssClass($this->options, $this->color);
208
        }
209 13
    }
210
211
    /**
212
     * @param int $index
213
     * @param array $item
214
     *
215
     * @throws \JsonException
216
     *
217
     * @return string
218
     */
219 8
    private function renderTab(int $index, array $item): string
220
    {
221 8
        $id = $this->getId() . '-panel-c' . $index;
222 8
        $url = ArrayHelper::getValue($item, 'url', '');
223 8
        $label = ArrayHelper::getValue($item, 'label', '');
224 8
        $encode = ArrayHelper::getValue($item, 'encode', $this->encodeLabels);
225 8
        $linkOptions = ArrayHelper::getValue($item, 'linkOptions', []);
226 8
        $tabItems = ArrayHelper::getValue($item, 'items', []);
227 8
        $tabItemsContainerOptions = ArrayHelper::getValue($item, 'itemsContainerOptions', []);
228
229 8
        if ($url !== '') {
230 1
            $linkOptions['href'] = $url;
231
        }
232
233 8
        if ($label === '') {
234 1
            throw new InvalidArgumentException('The "label" option is required.');
235
        }
236
237 7
        if ($encode === true) {
238 7
            $label = Html::encode($label);
239
        }
240
241 7
        if ($this->isActive($item)) {
242 4
            Html::addCssClass($linkOptions, ['active' => 'is-active']);
243
        }
244
245 7
        if (is_array($tabItems) && !empty($tabItems)) {
246 4
            $linkOptions['href'] ??= '#' . $id;
247 4
            $tabItemsContainerOptions['id'] ??= $id;
248
249 4
            $tabItemsContainer = Html::beginTag('div', $tabItemsContainerOptions) . "\n";
250
251 4
            foreach ($tabItems as $tabItem) {
252 4
                $tabItemsContainer .= $this->renderItem($tabItem) . "\n";
253
            }
254
255 3
            $tabItemsContainer .= Html::endTag('div');
256
257 3
            $this->tabItems[$index] = $tabItemsContainer;
258
        }
259
260 6
        return Html::tag('a', $label, $linkOptions);
261
    }
262
263 4
    private function renderItem(array $item): string
264
    {
265 4
        $options = ArrayHelper::getValue($item, 'options', []);
266 4
        $label = ArrayHelper::getValue($item, 'label', '');
267 4
        $icon = ArrayHelper::getValue($item, 'icon', '');
268 4
        $encode = ArrayHelper::getValue($item, 'encode', $this->encodeLabels);
269
270 4
        if ($label === '') {
271 1
            throw new InvalidArgumentException('The "label" option is required.');
272
        }
273
274 3
        if ($encode === true) {
275 3
            $label = Html::encode($label);
276
        }
277
278 3
        Html::addCssClass($options, 'panel-block');
279
280 3
        if ($this->isActive($item)) {
281 2
            Html::addCssClass($options, ['active' => 'is-active']);
282
        }
283
284 3
        $labelOptions = ['class' => 'panel-icon'];
285
286 3
        if ($this->encodeTags === false) {
287 3
            $labelOptions['encode'] = false;
288 3
            $options['encode'] = false;
289
        }
290
291 3
        if ($icon !== '') {
292 3
            $icon = "\n" . Html::tag('i', '', ['class' => $icon, 'aria-hidden' => 'true']) . "\n";
293 3
            $label = "\n" . Html::tag('span', $icon, $labelOptions) . "\n" . $label . "\n";
294
        }
295
296 3
        return Html::tag('a', $label, $options);
297
    }
298
299
    /**
300
     * Checking if active item.
301
     *
302
     * @param array $item
303
     *
304
     * @return bool
305
     */
306 7
    private function isActive(array $item): bool
307
    {
308 7
        return (bool) ArrayHelper::getValue($item, 'active', false);
309
    }
310
}
311