Passed
Pull Request — master (#31)
by Wilmer
02:15
created

Panel::getId()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
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 bool $encodeTags = false;
36
    private string $template = '{panelBegin}{panelHeading}{panelTabs}{panelItems}{panelEnd}';
37
    private array $tabItems = [];
38
39 14
    protected function run(): string
40
    {
41 14
        $this->buildOptions();
42
43 14
        $tag = ArrayHelper::getValue($this->options, 'tag', 'nav');
44
45 14
        return strtr($this->template, [
46 14
            '{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 14
            '{panelHeading}' => $this->renderHeading(),
48 14
            '{panelTabs}' => $this->renderTabs(),
49 12
            '{panelItems}' => implode("\n", $this->tabItems),
50 12
            '{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 withTemplate(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 withOptions(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 withHeading(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 withHeadingOptions(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 withColor(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 9
    public function withTabs(array $value): self
140
    {
141 9
        $new = clone $this;
142 9
        $new->tabs = $value;
143
144 9
        return $new;
145
    }
146
147
    /**
148
     * @param array $value
149
     *
150
     * @return self
151
     */
152 1
    public function withTabsOptions(array $value): self
153
    {
154 1
        $new = clone $this;
155 1
        $new->tabsOptions = $value;
156
157 1
        return $new;
158
    }
159
160
    /**
161
     * Allows you to enable the encoding tags html.
162
     *
163
     * @return self
164
     */
165 1
    public function withEncodeTags(): self
166
    {
167 1
        $new = clone $this;
168 1
        $new->encodeTags = true;
169
170 1
        return $new;
171
    }
172
173
    /**
174
     * @throws \JsonException
175
     *
176
     * @return string
177
     */
178 14
    private function renderHeading(): string
179
    {
180 14
        if ($this->heading !== null) {
181 3
            Html::addCssClass($this->headingOptions, 'panel-heading');
182 3
            return "\n" . Html::tag('p', $this->heading, $this->headingOptions) . "\n";
183
        }
184
185 11
        return '';
186
    }
187
188
    /**
189
     * @throws \JsonException
190
     *
191
     * @return string
192
     */
193 14
    private function renderTabs(): string
194
    {
195 14
        if (!empty($this->tabs)) {
196 9
            $tabs = '';
197 9
            foreach ($this->tabs as $index => $item) {
198 9
                $tabs .= $this->renderTab($index, $item) . "\n";
199
            }
200
201 7
            Html::addCssClass($this->tabsOptions, 'panel-tabs');
202
203 7
            return "\n" . Html::tag('p', "\n" . $tabs, $this->tabsOptions) . "\n";
204
        }
205
206 5
        return '';
207
    }
208
209 14
    private function buildOptions(): void
210
    {
211 14
        Html::addCssClass($this->options, 'panel');
212
213 14
        $this->options['id'] ??= $this->getId() . '-panel';
214
215 14
        if ($this->encodeTags === false) {
216 13
            $this->tabsOptions = array_merge($this->tabsOptions, ['encode' => false]);
217
        }
218
219 14
        if ($this->color !== '') {
220 1
            Html::addCssClass($this->options, $this->color);
221
        }
222 14
    }
223
224
    /**
225
     * @param int $index
226
     * @param array $item
227
     *
228
     * @throws \JsonException
229
     *
230
     * @return string
231
     */
232 9
    private function renderTab(int $index, array $item): string
233
    {
234 9
        $id = $this->getId() . '-panel-c' . $index;
235 9
        $url = ArrayHelper::getValue($item, 'url', '');
236 9
        $label = ArrayHelper::getValue($item, 'label', '');
237 9
        $encode = ArrayHelper::getValue($item, 'encode', $this->encodeLabels);
238 9
        $linkOptions = ArrayHelper::getValue($item, 'linkOptions', []);
239 9
        $tabItems = ArrayHelper::getValue($item, 'items', []);
240 9
        $tabItemsContainerOptions = ArrayHelper::getValue($item, 'itemsContainerOptions', []);
241
242 9
        if ($url !== '') {
243 2
            $linkOptions['href'] = $url;
244
        }
245
246 9
        if ($label === '') {
247 1
            throw new InvalidArgumentException('The "label" option is required.');
248
        }
249
250 8
        if ($encode === true) {
251 8
            $label = Html::encode($label);
252
        }
253
254 8
        if ($this->isActive($item)) {
255 5
            Html::addCssClass($linkOptions, 'is-active');
256
        }
257
258 8
        if (is_array($tabItems) && !empty($tabItems)) {
259 5
            $linkOptions['href'] ??= '#' . $id;
260 5
            $tabItemsContainerOptions['id'] ??= $id;
261
262 5
            $tabItemsContainer = Html::beginTag('div', $tabItemsContainerOptions) . "\n";
263
264 5
            foreach ($tabItems as $tabItem) {
265 5
                $tabItemsContainer .= $this->renderItem($tabItem) . "\n";
266
            }
267
268 4
            $tabItemsContainer .= Html::endTag('div');
269
270 4
            $this->tabItems[$index] = $tabItemsContainer;
271
        }
272
273 7
        return Html::tag('a', $label, $linkOptions);
274
    }
275
276 5
    private function renderItem(array $item): string
277
    {
278 5
        $options = ArrayHelper::getValue($item, 'options', []);
279 5
        $label = ArrayHelper::getValue($item, 'label', '');
280 5
        $icon = ArrayHelper::getValue($item, 'icon', '');
281 5
        $encode = ArrayHelper::getValue($item, 'encode', $this->encodeLabels);
282
283 5
        if ($label === '') {
284 1
            throw new InvalidArgumentException('The "label" option is required.');
285
        }
286
287 4
        if ($encode === true) {
288 4
            $label = Html::encode($label);
289
        }
290
291 4
        Html::addCssClass($options, 'panel-block');
292
293 4
        if ($this->isActive($item)) {
294 3
            Html::addCssClass($options, 'is-active');
295
        }
296
297 4
        $labelOptions = ['class' => 'panel-icon'];
298
299 4
        if ($this->encodeTags === false) {
300 3
            $labelOptions['encode'] = false;
301 3
            $options['encode'] = false;
302
        }
303
304 4
        if ($icon !== '') {
305 4
            $icon = "\n" . Html::tag('i', '', ['class' => $icon, 'aria-hidden' => 'true']) . "\n";
306 4
            $label = "\n" . Html::tag('span', $icon, $labelOptions) . "\n" . $label . "\n";
307
        }
308
309 4
        return Html::tag('a', $label, $options);
310
    }
311
312
    /**
313
     * Checking if active item.
314
     *
315
     * @param array $item
316
     *
317
     * @return bool
318
     */
319 8
    private function isActive(array $item): bool
320
    {
321 8
        return (bool) ArrayHelper::getValue($item, 'active', false);
322
    }
323
}
324