Passed
Push — master ( 46d6df...8617c9 )
by Alexander
02:21
created

NavBar::begin()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4.0092

Importance

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