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

NavBar::renderToggleButton()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

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