Panel   A
last analyzed

Complexity

Total Complexity 36

Size/Duplication

Total Lines 411
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 36
eloc 163
c 1
b 0
f 0
dl 0
loc 411
ccs 155
cts 155
cp 1
rs 9.52

21 Methods

Rating   Name   Duplication   Size   Complexity  
B renderTab() 0 52 7
A id() 0 5 1
A heading() 0 5 1
A render() 0 24 2
A renderTabs() 0 21 3
A iconClass() 0 5 1
A isActiveClass() 0 5 1
A headingClass() 0 5 1
A template() 0 5 1
A tabsAttributes() 0 5 1
A renderHeading() 0 14 2
A attributes() 0 5 1
A tabs() 0 5 1
A isActive() 0 3 1
A tabClass() 0 5 1
A renderItem() 0 49 5
A color() 0 10 2
A cssClass() 0 5 1
A autoIdPrefix() 0 5 1
A blockClass() 0 5 1
A headingAttributes() 0 5 1
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
use Yiisoft\Html\Tag\A;
11
use Yiisoft\Html\Tag\I;
12
use Yiisoft\Html\Tag\P;
13
use Yiisoft\Html\Tag\Span;
14
use Yiisoft\Widget\Widget;
15
16
use function implode;
17
use function in_array;
18
19
/**
20
 * Panel renders a panel component.
21
 *
22
 * @see https://bulma.io/documentation/components/panel/
23
 */
24
final class Panel extends Widget
25
{
26
    public const COLOR_PRIMARY = 'is-primary';
27
    public const COLOR_LINK = 'is-link';
28
    public const COLOR_INFO = 'is-info';
29
    public const COLOR_SUCCESS = 'is-success';
30
    public const COLOR_WARNING = 'is-warning';
31
    public const COLOR_DANGER = 'is-danger';
32
    public const COLOR_DARK = 'is-dark';
33
    private const COLOR_ALL = [
34
        self::COLOR_PRIMARY,
35
        self::COLOR_LINK,
36
        self::COLOR_INFO,
37
        self::COLOR_SUCCESS,
38
        self::COLOR_WARNING,
39
        self::COLOR_DANGER,
40
        self::COLOR_DARK,
41
    ];
42
43
    private array $attributes = [];
44
    private string $autoIdPrefix = 'w';
45
    private string $blockClass = 'panel-block';
46
    private string $color = '';
47
    private ?string $heading = null;
48
    private array $headingAttributes = [];
49
    private string $headingClass = 'panel-heading';
50
    private string $iconClass = 'panel-icon';
51
    private string $isActiveClass = 'is-active';
52
    private string $panelClass = 'panel';
53
    /** @psalm-var array<int, array> */
54
    private array $tabs = [];
55
    private array $tabsAttributes = [];
56
    private string $tabClass = 'panel-tabs';
57
    /** @psalm-var array<int, string> */
58
    private array $tabItems = [];
59
    private string $template = '{panelBegin}{panelHeading}{panelTabs}{panelItems}{panelEnd}';
60
61
    /**
62
     * Returns a new instance with the specified HTML attributes for widget.
63
     *
64
     * @param array $values Attribute values indexed by attribute names.
65
     *
66
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} For details on how attributes are being rendered.
67
     */
68 2
    public function attributes(array $values): self
69
    {
70 2
        $new = clone $this;
71 2
        $new->attributes = $values;
72 2
        return $new;
73
    }
74
75
    /**
76
     * Returns a new instance with the specified prefix to the automatically generated widget IDs.
77
     *
78
     * @param string $value The prefix to the automatically generated widget IDs.
79
     */
80 1
    public function autoIdPrefix(string $value): self
81
    {
82 1
        $new = clone $this;
83 1
        $new->autoIdPrefix = $value;
84 1
        return $new;
85
    }
86
87
    /**
88
     * Returns a new instance with the specified the panel block class.
89
     *
90
     * @param string $value The block class.
91
     */
92 2
    public function blockClass(string $value): self
93
    {
94 2
        $new = clone $this;
95 2
        $new->blockClass = $value;
96 2
        return $new;
97
    }
98
99
    /**
100
     * Returns a new instance with the specified panel color class.
101
     *
102
     * @param string $value The panel color class. Default any color.
103
     * Possible values are: Panel::COLOR_PRIMARY, Panel::COLOR_INFO, Panel::COLOR_SUCCESS, Panel::COLOR_WARNING,
104
     * Panel::COLOR_DANGER, Panel::COLOR_DARK
105
     */
106 5
    public function color(string $value): self
107
    {
108 5
        if (!in_array($value, self::COLOR_ALL, true)) {
109 1
            $values = implode(' ', self::COLOR_ALL);
110 1
            throw new InvalidArgumentException("Invalid color. Valid values are: \"$values\".");
111
        }
112
113 4
        $new = clone $this;
114 4
        $new->color = $value;
115 4
        return $new;
116
    }
117
118
    /**
119
     * Returns a new instance with the specified the panel class.
120
     *
121
     * @param string $value The panel class.
122
     */
123 2
    public function cssClass(string $value): self
124
    {
125 2
        $new = clone $this;
126 2
        $new->panelClass = $value;
127 2
        return $new;
128
    }
129
130
    /**
131
     * Returns a new instance with the specified panel heading.
132
     *
133
     * @param string $value The panel heading.
134
     */
135 7
    public function heading(string $value): self
136
    {
137 7
        $new = clone $this;
138 7
        $new->heading = $value;
139 7
        return $new;
140
    }
141
142
    /**
143
     * Returns a new instance with the specified panel heading attributes.
144
     *
145
     * @param array $values Attribute values indexed by attribute names.
146
     */
147 2
    public function headingAttributes(array $values): self
148
    {
149 2
        $new = clone $this;
150 2
        $new->headingAttributes = $values;
151 2
        return $new;
152
    }
153
154
    /**
155
     * Returns a new instance with the specified the panel heading class.
156
     *
157
     * @param string $value The heading class.
158
     */
159 2
    public function headingClass(string $value): self
160
    {
161 2
        $new = clone $this;
162 2
        $new->headingClass = $value;
163 2
        return $new;
164
    }
165
166
    /**
167
     * Returns a new instance with the specified the panel icon class.
168
     *
169
     * @param string $value The icon class.
170
     */
171 1
    public function iconClass(string $value): self
172
    {
173 1
        $new = clone $this;
174 1
        $new->iconClass = $value;
175 1
        return $new;
176
    }
177
178
    /**
179
     * Returns a new instance with the specified ID of the widget.
180
     *
181
     * @param string $value The ID of the widget.
182
     */
183 1
    public function id(string $value): self
184
    {
185 1
        $new = clone $this;
186 1
        $new->attributes['id'] = $value;
187 1
        return $new;
188
    }
189
190
    /**
191
     * Returns a new instance with the specified active tab class.
192
     *
193
     * @param string $value The active tab class.
194
     */
195 1
    public function isActiveClass(string $value): self
196
    {
197 1
        $new = clone $this;
198 1
        $new->isActiveClass = $value;
199 1
        return $new;
200
    }
201
202
    /**
203
     * Returns a new instance with the specified panel tabs.
204
     *
205
     * @param array $value The panel tabs.
206
     *
207
     * @psalm-param array<int, array> $value
208
     */
209 12
    public function tabs(array $value): self
210
    {
211 12
        $new = clone $this;
212 12
        $new->tabs = $value;
213 12
        return $new;
214
    }
215
216
    /**
217
     * Returns a new instance with the specified the panel tab class.
218
     *
219
     * @param string $value The tab class.
220
     */
221 2
    public function tabClass(string $value): self
222
    {
223 2
        $new = clone $this;
224 2
        $new->tabClass = $value;
225 2
        return $new;
226
    }
227
228
    /**
229
     * Returns a new instance with the specified panel tabs attributes.
230
     *
231
     * @param array $values Attribute values indexed by attribute names.
232
     */
233 2
    public function tabsAttributes(array $values): self
234
    {
235 2
        $new = clone $this;
236 2
        $new->tabsAttributes = $values;
237 2
        return $new;
238
    }
239
240
    /**
241
     * Returns a new instance with the specified template.
242
     *
243
     * @param string $value The string the template for rendering panel:
244
     *
245
     * - `{panelBegin}`: _string_, which will render the panel container begin.
246
     * - `{panelHeading}`: _string_, which will render the panel heading.
247
     * - `{panelTabs}`: _string_, which will render the panel tabs.
248
     * - `{panelItems}`: _string_, which will render the panel items.
249
     * - `{panelEnd}`: _string_, which will render the panel container end.
250
     */
251 2
    public function template(string $value): self
252
    {
253 2
        $new = clone $this;
254 2
        $new->template = $value;
255 2
        return $new;
256
    }
257
258 17
    public function render(): string
259
    {
260 17
        $attributes = $this->attributes;
261
262
        /** @var string */
263 17
        $attributes['id'] ??= (Html::generateId($this->autoIdPrefix) . '-panel');
264
265 17
        $id = $attributes['id'];
266
267
        /** @var string */
268 17
        $tag = $attributes['tag'] ?? 'nav';
269
270 17
        Html::addCssClass($attributes, $this->panelClass);
271
272 17
        if ($this->color !== '') {
273 3
            Html::addCssClass($attributes, $this->color);
274
        }
275
276 17
        return strtr($this->template, [
277 17
            '{panelBegin}' => Html::openTag($tag, $attributes) . PHP_EOL,
278 17
            '{panelHeading}' => $this->renderHeading(),
279 17
            '{panelTabs}' => $this->renderTabs($id),
280 17
            '{panelItems}' => implode('', $this->tabItems),
281 17
            '{panelEnd}' => Html::closeTag($tag),
282 17
        ]);
283
    }
284
285 17
    private function renderHeading(): string
286
    {
287 17
        $headingAttributes = $this->headingAttributes;
288
289 17
        if (!empty($this->heading)) {
290 6
            Html::addCssClass($headingAttributes, $this->headingClass);
291
292 6
            return P::tag()
293 6
                ->attributes($headingAttributes)
294 6
                ->content($this->heading)
295 6
                ->render() . PHP_EOL;
296
        }
297
298 11
        return '';
299
    }
300
301 17
    private function renderTabs(string $id): string
302
    {
303 17
        $tabsAttributes = $this->tabsAttributes;
304
305 17
        if (!empty($this->tabs)) {
306 11
            $tabs = '';
307
308 11
            foreach ($this->tabs as $index => $item) {
309 11
                $tabs .= $this->renderTab($index, $item, $id . '-' . $index) . PHP_EOL;
310
            }
311
312 9
            Html::addCssClass($tabsAttributes, $this->tabClass);
313
314 9
            return P::tag()
315 9
                ->attributes($tabsAttributes)
316 9
                ->content(PHP_EOL . $tabs)
317 9
                ->encode(false)
318 9
                ->render() . PHP_EOL;
319
        }
320
321 6
        return '';
322
    }
323
324 11
    private function renderTab(int $index, array $item, string $id): string
325
    {
326
        /** @var string */
327 11
        $url = $item['url'] ?? '';
328
329
        /** @var string */
330 11
        $label = $item['label'] ?? '';
331
332
        /** @var bool */
333 11
        $encode = $item['encode'] ?? true;
334
335
        /** @var array */
336 11
        $urlAttributes = $item['urlAttributes'] ?? [];
337
338
        /** @var array */
339 11
        $tabItems = $item['items'] ?? [];
340
341 11
        if ($url !== '') {
342 1
            $urlAttributes['href'] = $url;
343
        }
344
345 11
        if ($label === '') {
346 1
            throw new InvalidArgumentException('The "label" option is required.');
347
        }
348
349 10
        if ($encode === true) {
350 10
            $label = Html::encode($label);
351
        }
352
353 10
        if ($this->isActive($item)) {
354 3
            Html::addCssClass($urlAttributes, $this->isActiveClass);
355
        }
356
357 10
        if (!empty($tabItems)) {
358
            /** @var string */
359 5
            $urlAttributes['href'] ??= '#' . $id;
360
361 5
            $tabsItems = '';
362
363
            /** @psalm-var array[] */
364 5
            foreach ($tabItems as $tabItem) {
365 5
                $tabsItems .= $this->renderItem($tabItem) . PHP_EOL;
366
            }
367
368 4
            $this->tabItems[$index] = $tabsItems;
369
        }
370
371 9
        return A::tag()
372 9
            ->attributes($urlAttributes)
373 9
            ->content($label)
374 9
            ->encode(false)
375 9
            ->render();
376
    }
377
378 5
    private function renderItem(array $item): string
379
    {
380
        /** @var array */
381 5
        $urlAttributes = $item['urlAttributes'] ?? [];
382
383
        /** @var string */
384 5
        $label = $item['label'] ?? '';
385
386
        /** @var string */
387 5
        $icon = $item['icon'] ?? '';
388
389
        /** @var bool */
390 5
        $encode = $item['encode'] ?? true;
391
392 5
        if ($label === '') {
393 1
            throw new InvalidArgumentException('The "label" option is required.');
394
        }
395
396
        /** @var array */
397 4
        $labelAttributes = $item['labelAttributes'] ?? [];
398
399 4
        if ($encode) {
400 4
            $label = Html::encode($label);
401
        }
402
403 4
        Html::addCssClass($urlAttributes, $this->blockClass);
404
405 4
        if ($this->isActive($item)) {
406 1
            Html::addCssClass($urlAttributes, $this->isActiveClass);
407
        }
408
409 4
        Html::addCssClass($labelAttributes, $this->iconClass);
410
411 4
        if ($icon !== '') {
412 3
            $icon = PHP_EOL . I::tag()
413 3
                ->attributes(['aria-hidden' => 'true'])
414 3
                ->class($icon) . PHP_EOL;
415 3
            $label = PHP_EOL . Span::tag()
416 3
                ->attributes($labelAttributes)
417 3
                ->content($icon)
418 3
                ->encode(false) . PHP_EOL .
419 3
                $label . PHP_EOL;
420
        }
421
422 4
        return A::tag()
423 4
            ->attributes($urlAttributes)
424 4
            ->content($label)
425 4
            ->encode(false)
426 4
            ->render();
427
    }
428
429
    /**
430
     * Checking if active item.
431
     */
432 10
    private function isActive(array $item): bool
433
    {
434 10
        return (bool) ArrayHelper::getValue($item, 'active', false);
435
    }
436
}
437