Passed
Push — master ( 7c2ef6...e80b4e )
by Alexander
02:16
created

Panel::renderTabs()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 7
c 1
b 0
f 0
nc 3
nop 0
dl 0
loc 14
ccs 8
cts 8
cp 1
crap 3
rs 10
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 string $template = '{panelBegin}{panelHeading}{panelTabs}{panelItems}{panelEnd}';
36
    private array $tabItems = [];
37
38 11
    protected function run(): string
39
    {
40 11
        $this->buildOptions();
41
42 11
        $tag = ArrayHelper::getValue($this->options, 'tag', 'nav');
43
44 11
        return strtr($this->template, [
45 11
            '{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

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