Passed
Push — master ( 741461...e00202 )
by Alexander
02:34
created

NavBar::checkNavTag()   A

Complexity

Conditions 5
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 5
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 8
ccs 5
cts 5
cp 1
crap 5
rs 9.6111
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
12
use function strpos;
13
14
/**
15
 * The navbar component is a responsive and versatile horizontal navigation bar.
16
 *
17
 * @link https://bulma.io/documentation/components/navbar/
18
 */
19
final class NavBar extends Widget
20
{
21
    private string $brand = '';
22
    private string $brandLabel = '';
23
    private string $brandImage = '';
24
    private string $brandUrl = '/';
25
    private string $toggleIcon = '';
26
    private array $options = [];
27
    private array $brandOptions = [];
28
    private array $brandLabelOptions = [];
29
    private array $brandImageOptions = [];
30
    private array $itemsOptions = [];
31
    private array $menuOptions = [];
32
    private array $toggleOptions = [
33
        'aria-expanded' => 'false',
34
        'aria-label' => 'menu',
35
        'class' => 'navbar-burger',
36
        'role' => 'button',
37
    ];
38
    private bool $encodeTags = false;
39
40 19
    public function begin(): ?string
41
    {
42 19
        parent::begin();
43
44 19
        $this->buildOptions();
45 19
        $this->renderBrand();
46
47 19
        $navOptions = $this->options;
48 19
        $navTag = ArrayHelper::remove($navOptions, 'tag', 'nav');
49 19
        $this->checkNavTag($navTag);
50
51
        return
52 16
            (is_string($navTag) ? Html::openTag($navTag, $navOptions) : '') . "\n" .
53 16
            $this->brand . "\n" .
54 16
            Html::openTag('div', $this->menuOptions) .
55 16
            Html::openTag('div', $this->itemsOptions);
56
    }
57
58 19
    protected function run(): string
59
    {
60 19
        $tag = ArrayHelper::remove($this->options, 'tag', 'nav');
61 19
        $this->checkNavTag($tag);
62
63
        return
64 16
            Html::closeTag('div') . "\n" .
65 16
            Html::closeTag('div') . "\n" .
66 16
            (is_string($tag) ? Html::closeTag($tag) : '');
67
    }
68
69
    /**
70
     * @param mixed $navTag
71
     */
72 22
    private function checkNavTag($navTag): void
73
    {
74
        if (
75 22
            (!is_string($navTag) || $navTag === '') &&
76 22
            !is_bool($navTag) &&
77 22
            $navTag !== null
78
        ) {
79 6
            throw new InvalidArgumentException('Tag should be either non empty string, bool or null.');
80
        }
81 16
    }
82
83
    /**
84
     * Set render brand custom, {@see brandLabel} and {@see brandImage} are not generated.
85
     *
86
     * @param string $value
87
     *
88
     * @return self
89
     */
90 1
    public function brand(string $value): self
91
    {
92 1
        $new = clone $this;
93 1
        $new->brand = $value;
94 1
        return $new;
95
    }
96
97
    /**
98
     * The text of the brand label or empty if it's not used. Note that this is not HTML-encoded.
99
     *
100
     * @param string $value
101
     *
102
     * @return self
103
     */
104 15
    public function brandLabel(string $value): self
105
    {
106 15
        $new = clone $this;
107 15
        $new->brandLabel = $value;
108 15
        return $new;
109
    }
110
111
    /**
112
     * The image of the brand or empty if it's not used.
113
     *
114
     * @param string $value
115
     *
116
     * @return self
117
     */
118 14
    public function brandImage(string $value): self
119
    {
120 14
        $new = clone $this;
121 14
        $new->brandImage = $value;
122 14
        return $new;
123
    }
124
125
    /**
126
     * The URL for the brand's hyperlink tag and will be used for the "href" attribute of the brand link. Default value
127
     * is '/' will be used. You may set it to `null` if you want to have no link at all.
128
     *
129
     * @param string $value
130
     *
131
     * @return self
132
     */
133 13
    public function brandUrl(string $value): self
134
    {
135 13
        $new = clone $this;
136 13
        $new->brandUrl = $value;
137 13
        return $new;
138
    }
139
140
    /**
141
     * Set toggle icon.
142
     *
143
     * @param string $value .
144
     *
145
     * @return self
146
     */
147 1
    public function toggleIcon(string $value): self
148
    {
149 1
        $new = clone $this;
150 1
        $new->toggleIcon = $value;
151 1
        return $new;
152
    }
153
154
    /**
155
     * Options HTML attributes for the tag nav.
156
     *
157
     * @param array $value
158
     *
159
     * @return self
160
     *
161
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
162
     */
163 9
    public function options(array $value): self
164
    {
165 9
        $new = clone $this;
166 9
        $new->options = $value;
167 9
        return $new;
168
    }
169
170
    /**
171
     * Options HTML attributes of the tag div brand.
172
     *
173
     * @param array $value default value `navbar-item`.
174
     *
175
     * @return self
176
     *
177
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
178
     */
179 1
    public function brandOptions(array $value): self
180
    {
181 1
        $new = clone $this;
182 1
        $new->brandOptions = $value;
183 1
        return $new;
184
    }
185
186
    /**
187
     * Options HTML attributes of the tag div brand label.
188
     *
189
     * @param array $value default value `navbar-item`.
190
     *
191
     * @return self
192
     *
193
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
194
     */
195 1
    public function brandLabelOptions(array $value): self
196
    {
197 1
        $new = clone $this;
198 1
        $new->brandLabelOptions = $value;
199 1
        return $new;
200
    }
201
202
    /**
203
     * Options HTML attributes of the tag div brand link.
204
     *
205
     * @param array $value default value `navbar-item`.
206
     *
207
     * @return self
208
     *
209
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
210
     */
211 1
    public function brandImageOptions(array $value): self
212
    {
213 1
        $new = clone $this;
214 1
        $new->brandImageOptions = $value;
215 1
        return $new;
216
    }
217
218
    /**
219
     * Options HTML attributes of the tag div items nav, values `navbar-start`, `navbar-end`.
220
     *
221
     * @param array $value default value `navbar-start`.
222
     *
223
     * @return self
224
     *
225
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
226
     */
227 3
    public function itemsOptions(array $value): self
228
    {
229 3
        $new = clone $this;
230 3
        $new->itemsOptions = $value;
231 3
        return $new;
232
    }
233
234
    /**
235
     * Options HTML attributes of the tag div nav menu.
236
     *
237
     * @param array $value default value `navbar-menu`.
238
     *
239
     * @return self
240
     *
241
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
242
     */
243 1
    public function menuOptions(array $value): self
244
    {
245 1
        $new = clone $this;
246 1
        $new->menuOptions = $value;
247 1
        return $new;
248
    }
249
250
    /**
251
     * The HTML attributes of the navbar toggler button.
252
     *
253
     * @param array $value
254
     *
255
     * @return self
256
     *
257
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
258
     */
259 1
    public function toggleOptions(array $value): self
260
    {
261 1
        $new = clone $this;
262 1
        $new->toggleOptions = $value;
263 1
        return $new;
264
    }
265
266 19
    private function buildOptions(): void
267
    {
268 19
        $id = '';
269
270 19
        if (!isset($this->options['id'])) {
271 19
            $id = $this->getId();
272 19
            $this->options['id'] = "{$id}-navbar";
273
        }
274
275 19
        $this->options = $this->addOptions($this->options, 'navbar');
276 19
        $this->brandOptions = $this->addOptions($this->brandOptions, 'navbar-brand');
277 19
        $this->brandLabelOptions = $this->addOptions($this->brandLabelOptions, 'navbar-item');
278 19
        $this->brandImageOptions = $this->addOptions($this->brandImageOptions, 'navbar-item');
279 19
        $this->menuOptions = $this->addOptions($this->menuOptions, 'navbar-menu');
280
281 19
        $this->menuOptions['id'] = "{$id}-navbar-Menu";
282
283 19
        $this->initItemsOptions();
284 19
    }
285
286 19
    private function initItemsOptions(): void
287
    {
288 19
        $itemsClass = '';
289
290 19
        if (isset($this->itemsOptions['class'])) {
291 3
            $itemsClass = $this->itemsOptions['class'];
292 3
            unset($this->itemsOptions['class']);
293
294 3
            if (is_array($itemsClass)) {
295 1
                $itemsClass = implode(' ', $itemsClass);
296
            }
297
        }
298
299
        /** @var string $itemsClass */
300 19
        if (strpos($itemsClass, 'navbar-end') === false) {
301 18
            Html::addCssClass($this->itemsOptions, 'navbar-start');
302
        }
303
304 19
        if (!empty($itemsClass)) {
305 3
            Html::addCssClass($this->itemsOptions, $itemsClass);
306
        }
307 19
    }
308
309 19
    private function renderBrand(): void
310
    {
311 19
        if ($this->brand === '') {
312 18
            $this->brand = Html::openTag('div', $this->brandOptions);
313
314 18
            if ($this->brandImage !== '' && $this->brandLabel !== '') {
315 13
                $this->brand .= Html::tag(
316 13
                    'span',
317 13
                    Html::img($this->brandImage)->render(),
318 13
                    $this->brandImageOptions
319 13
                )->encode($this->encodeTags);
320
            }
321
322 18
            if ($this->brandImage !== '' && $this->brandLabel === '') {
323 1
                $this->brand .= Html::a(
324 1
                    Html::img($this->brandImage)->render(),
325 1
                    $this->brandUrl,
326 1
                    $this->brandImageOptions
327 1
                )->encode($this->encodeTags);
328
            }
329
330 18
            if ($this->brandLabel !== '') {
331 14
                $this->brand .= Html::a($this->brandLabel, $this->brandUrl, $this->brandLabelOptions);
332
            }
333
334 18
            $this->brand .= $this->renderToggleButton();
335 18
            $this->brand .= Html::closeTag('div');
336
        }
337 19
    }
338
339
    /**
340
     * Renders collapsible toggle button.
341
     *
342
     * @throws JsonException
343
     *
344
     * @return string the rendering toggle button.
345
     */
346 18
    private function renderToggleButton(): string
347
    {
348
        return
349 18
            Html::openTag('a', $this->toggleOptions) .
350 18
            $this->renderToggleIcon() .
351
352 18
            Html::closeTag('a');
353
    }
354
355
    /**
356
     * Render icon toggle.
357
     *
358
     * @throws JsonException
359
     *
360
     * @return string
361
     */
362 18
    private function renderToggleIcon(): string
363
    {
364 18
        if ($this->toggleIcon === '') {
365 17
            $this->toggleIcon = Html::tag('span', '', ['aria-hidden' => 'true']) .
366 17
                Html::tag('span', '', ['aria-hidden' => 'true']) .
367 17
                Html::tag('span', '', ['aria-hidden' => 'true']);
368
        }
369
370 18
        return $this->toggleIcon;
371
    }
372
}
373