Passed
Pull Request — master (#60)
by Wilmer
12:52
created

Panel::tabClass()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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