Passed
Push — master ( 9e997b...1490b2 )
by Rustam
02:32
created

Dropdown   F

Complexity

Total Complexity 68

Size/Duplication

Total Lines 721
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 228
c 0
b 0
f 0
dl 0
loc 721
rs 2.96
wmc 68

43 Methods

Rating   Name   Duplication   Size   Complexity  
A renderItemsContainer() 0 15 3
A id() 0 6 1
A renderItemContainer() 0 13 3
A itemsContainerTag() 0 6 1
A dividerClass() 0 6 1
A toggleAttributes() 0 6 1
A renderItemLink() 0 17 2
A itemClass() 0 6 1
A splitButtonSpanClass() 0 6 1
B renderItem() 0 47 10
A containerTag() 0 5 1
A splitButtonAttributes() 0 6 1
A renderToggleLink() 0 3 1
A itemContainerTag() 0 6 1
A itemContainerAttributes() 0 6 1
A container() 0 6 1
A headerClass() 0 6 1
A disabledClass() 0 6 1
A dividerTag() 0 6 1
A renderItems() 0 16 3
A renderToggleButton() 0 3 1
A toggleType() 0 6 1
A renderDropdown() 0 16 1
A itemContainer() 0 6 1
A renderDivider() 0 14 3
A renderToggleSplit() 0 7 1
A renderToggle() 0 14 3
A splitButtonClass() 0 6 1
A dividerAttributes() 0 6 1
A containerAttributes() 0 6 1
A renderToggleSplitButton() 0 3 1
A itemsContainerAttributes() 0 6 1
A itemsContainerClass() 0 6 1
A headerTag() 0 6 1
A toggleClass() 0 6 1
A renderItemContent() 0 13 1
A items() 0 6 1
A containerClass() 0 6 1
A itemTag() 0 6 1
A itemContainerClass() 0 6 1
A activeClass() 0 6 1
A renderHeader() 0 12 3
A render() 0 42 4

How to fix   Complexity   

Complex Class

Complex classes like Dropdown often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Dropdown, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Widgets;
6
7
use InvalidArgumentException;
8
use Yiisoft\Definitions\Exception\CircularReferenceException;
9
use Yiisoft\Definitions\Exception\InvalidConfigException;
10
use Yiisoft\Definitions\Exception\NotInstantiableException;
11
use Yiisoft\Factory\NotFoundException;
12
use Yiisoft\Html\Html;
13
use Yiisoft\Html\Tag\A;
14
use Yiisoft\Html\Tag\Button;
15
use Yiisoft\Html\Tag\Span;
16
use Yiisoft\Widget\Widget;
17
18
use function array_merge;
19
use function gettype;
20
use function implode;
21
use function str_contains;
22
use function trim;
23
24
final class Dropdown extends Widget
25
{
26
    private string $activeClass = 'active';
27
    private bool $container = true;
28
    private array $containerAttributes = [];
29
    private string $containerClass = '';
30
    private string $containerTag = 'div';
31
    private string $disabledClass = 'disabled';
32
    private array $dividerAttributes = [];
33
    private string $dividerClass = 'dropdown-divider';
34
    private string $dividerTag = 'hr';
35
    private string $headerClass = '';
36
    private string $headerTag = 'span';
37
    private string $id = '';
38
    private string $itemClass = '';
39
    private string $itemTag = 'a';
40
    private bool $itemContainer = true;
41
    private array $itemContainerAttributes = [];
42
    private string $itemContainerTag = 'li';
43
    private array $items = [];
44
    private array $itemsContainerAttributes = [];
45
    private string $itemsContainerTag = 'ul';
46
    private array $splitButtonAttributes = [];
47
    private array $splitButtonSpanAttributes = [];
48
    private array $toggleAttributes = [];
49
    private string $toggleType = 'button';
50
51
    /**
52
     * Returns a new instance with the specified active class.
53
     *
54
     * @param string $value The active class.
55
     */
56
    public function activeClass(string $value): self
57
    {
58
        $new = clone $this;
59
        $new->activeClass = $value;
60
61
        return $new;
62
    }
63
64
    /**
65
     * Returns a new instance with the specified if the container is enabled, or not. Default is true.
66
     *
67
     * @param bool $value The container enabled.
68
     */
69
    public function container(bool $value): self
70
    {
71
        $new = clone $this;
72
        $new->container = $value;
73
74
        return $new;
75
    }
76
77
    /**
78
     * Returns a new instance with the specified container HTML attributes.
79
     *
80
     * @param array $valuesMap Attribute values indexed by attribute names.
81
     */
82
    public function containerAttributes(array $valuesMap): self
83
    {
84
        $new = clone $this;
85
        $new->containerAttributes = $valuesMap;
86
87
        return $new;
88
    }
89
90
    /**
91
     * Returns a new instance with the specified container class.
92
     *
93
     * @param string $value The container class.
94
     */
95
    public function containerClass(string $value): self
96
    {
97
        $new = clone $this;
98
        $new->containerClass = $value;
99
100
        return $new;
101
    }
102
103
    /**
104
     * Returns a new instance with the specified container tag.
105
     *
106
     * @param string $value The container tag.
107
     */
108
    public function containerTag(string $value): self
109
    {
110
        $new = clone $this;
111
        $new->containerTag = $value;
112
        return $new;
113
    }
114
115
    /**
116
     * Returns a new instance with the specified disabled class.
117
     *
118
     * @param string $value The disabled class.
119
     */
120
    public function disabledClass(string $value): self
121
    {
122
        $new = clone $this;
123
        $new->disabledClass = $value;
124
125
        return $new;
126
    }
127
128
    /**
129
     * Returns a new instance with the specified divider HTML attributes.
130
     *
131
     * @param array $valuesMap Attribute values indexed by attribute names.
132
     */
133
    public function dividerAttributes(array $valuesMap): self
134
    {
135
        $new = clone $this;
136
        $new->dividerAttributes = $valuesMap;
137
138
        return $new;
139
    }
140
141
    /**
142
     * Returns a new instance with the specified divider class.
143
     *
144
     * @param string $value The divider class.
145
     */
146
    public function dividerClass(string $value): self
147
    {
148
        $new = clone $this;
149
        $new->dividerClass = $value;
150
151
        return $new;
152
    }
153
154
    /**
155
     * Returns a new instance with the specified divider tag.
156
     *
157
     * @param string $value The divider tag.
158
     */
159
    public function dividerTag(string $value): self
160
    {
161
        $new = clone $this;
162
        $new->dividerTag = $value;
163
164
        return $new;
165
    }
166
167
    /**
168
     * Returns a new instance with the specified header class.
169
     *
170
     * @param string $value The header class.
171
     */
172
    public function headerClass(string $value): self
173
    {
174
        $new = clone $this;
175
        $new->headerClass = $value;
176
177
        return $new;
178
    }
179
180
    /**
181
     * Returns a new instance with the specified header tag.
182
     *
183
     * @param string $value The header tag.
184
     */
185
    public function headerTag(string $value): self
186
    {
187
        $new = clone $this;
188
        $new->headerTag = $value;
189
190
        return $new;
191
    }
192
193
    /**
194
     * Returns a new instance with the specified Widget ID.
195
     *
196
     * @param string $value The id of the widget.
197
     */
198
    public function id(string $value): self
199
    {
200
        $new = clone $this;
201
        $new->id = $value;
202
203
        return $new;
204
    }
205
206
    /**
207
     * Returns a new instance with the specified item class.
208
     *
209
     * @param string $value The item class.
210
     */
211
    public function itemClass(string $value): self
212
    {
213
        $new = clone $this;
214
        $new->itemClass = $value;
215
216
        return $new;
217
    }
218
219
    /**
220
     * Returns a new instance with the specified item container, if false, the item container will not be rendered.
221
     *
222
     * @param bool $value The item container.
223
     */
224
    public function itemContainer(bool $value): self
225
    {
226
        $new = clone $this;
227
        $new->itemContainer = $value;
228
229
        return $new;
230
    }
231
232
    /**
233
     * Returns a new instance with the specified item container HTML attributes.
234
     *
235
     * @param array $valuesMap Attribute values indexed by attribute names.
236
     */
237
    public function itemContainerAttributes(array $valuesMap): self
238
    {
239
        $new = clone $this;
240
        $new->itemContainerAttributes = $valuesMap;
241
242
        return $new;
243
    }
244
245
    /**
246
     * Returns a new instance with the specified item container class.
247
     *
248
     * @param string $value The item container class.
249
     */
250
    public function itemContainerClass(string $value): self
251
    {
252
        $new = clone $this;
253
        Html::addCssClass($new->itemContainerAttributes, $value);
254
255
        return $new;
256
    }
257
258
    /**
259
     * Returns a new instance with the specified item container tag.
260
     *
261
     * @param string $value The item container tag.
262
     */
263
    public function itemContainerTag(string $value): self
264
    {
265
        $new = clone $this;
266
        $new->itemContainerTag = $value;
267
268
        return $new;
269
    }
270
271
    /**
272
     * Returns a new instance with the specified item tag.
273
     *
274
     * @param string $value The item tag.
275
     */
276
    public function itemTag(string $value): self
277
    {
278
        $new = clone $this;
279
        $new->itemTag = $value;
280
281
        return $new;
282
    }
283
284
    /**
285
     * List of menu items in the dropdown. Each array element can be either an HTML string, or an array representing a
286
     * single menu with the following structure:
287
     *
288
     * - label: string, required, the nav item label.
289
     * - active: bool, whether the item should be on active state or not.
290
     * - disabled: bool, whether the item should be on disabled state or not. For default `disabled` is false.
291
     * - enclose: bool, whether the item should be enclosed by a `<li>` tag or not. For default `enclose` is true.
292
     * - encode: bool, whether the label should be HTML encoded or not. For default `encodeLabel` is true.
293
     * - headerAttributes: array, HTML attributes to be rendered in the item header.
294
     * - link: string, the item's href. Defaults to "#". For default `link` is "#".
295
     * - linkAttributes: array, the HTML attributes of the item's link. For default `linkAttributes` is `[]`.
296
     * - icon: string, the item's icon. For default `icon` is ``.
297
     * - iconAttributes: array, the HTML attributes of the item's icon. For default `iconAttributes` is `[]`.
298
     * - visible: bool, optional, whether this menu item is visible. Defaults to true.
299
     * - items: array, optional, the submenu items. The structure is the same as this property.
300
     *   Note that Bootstrap doesn't support dropdown submenu. You have to add your own CSS styles to support it.
301
     * - itemsContainerAttributes: array, optional, the HTML attributes for tag `<li>`.
302
     *
303
     * To insert dropdown divider use `-`.
304
     */
305
    public function items(array $value): self
306
    {
307
        $new = clone $this;
308
        $new->items = $value;
309
310
        return $new;
311
    }
312
313
    /**
314
     * Returns a new instance with the specified items' container HTML attributes.
315
     *
316
     * @param array $valuesMap Attribute values indexed by attribute names.
317
     */
318
    public function itemsContainerAttributes(array $valuesMap): self
319
    {
320
        $new = clone $this;
321
        $new->itemsContainerAttributes = $valuesMap;
322
323
        return $new;
324
    }
325
326
    /**
327
     * Returns a new instance with the specified item container class.
328
     *
329
     * @param string $value The item container class.
330
     */
331
    public function itemsContainerClass(string $value): self
332
    {
333
        $new = clone $this;
334
        Html::addCssClass($new->itemsContainerAttributes, $value);
335
336
        return $new;
337
    }
338
339
    /**
340
     * Returns a new instance with the specified items' container tag.
341
     *
342
     * @param string $value The items' container tag.
343
     */
344
    public function itemsContainerTag(string $value): self
345
    {
346
        $new = clone $this;
347
        $new->itemsContainerTag = $value;
348
349
        return $new;
350
    }
351
352
    /**
353
     * Returns a new instance with the specified split button attributes.
354
     *
355
     * @param array $valuesMap Attribute values indexed by attribute names.
356
     */
357
    public function splitButtonAttributes(array $valuesMap): self
358
    {
359
        $new = clone $this;
360
        $new->splitButtonAttributes = $valuesMap;
361
362
        return $new;
363
    }
364
365
    /**
366
     * Returns a new instance with the specified split button class.
367
     *
368
     * @param string $value The split button class.
369
     */
370
    public function splitButtonClass(string $value): self
371
    {
372
        $new = clone $this;
373
        Html::addCssClass($new->splitButtonAttributes, $value);
374
375
        return $new;
376
    }
377
378
    /**
379
     * Returns a new instance with the specified split button span class.
380
     *
381
     * @param string $value The split button span class.
382
     */
383
    public function splitButtonSpanClass(string $value): self
384
    {
385
        $new = clone $this;
386
        Html::addCssClass($new->splitButtonSpanAttributes, $value);
387
388
        return $new;
389
    }
390
391
    /**
392
     * Returns a new instance with the specified toggle HTML attributes.
393
     *
394
     * @param array $valuesMap Attribute values indexed by attribute names.
395
     */
396
    public function toggleAttributes(array $valuesMap): self
397
    {
398
        $new = clone $this;
399
        $new->toggleAttributes = $valuesMap;
400
401
        return $new;
402
    }
403
404
    /**
405
     * Returns a new instance with the specified toggle class.
406
     *
407
     * @param string $value The toggle class.
408
     */
409
    public function toggleClass(string $value): self
410
    {
411
        $new = clone $this;
412
        Html::addCssClass($new->toggleAttributes, $value);
413
414
        return $new;
415
    }
416
417
    /**
418
     * Returns a new instance with the specified toggle type, if `button` the toggle will be a button, otherwise a
419
     * `a` tag will be used.
420
     *
421
     * @param string $value The toggle tag.
422
     */
423
    public function toggleType(string $value): self
424
    {
425
        $new = clone $this;
426
        $new->toggleType = $value;
427
428
        return $new;
429
    }
430
431
    /**
432
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
433
     */
434
    public function render(): string
435
    {
436
        /**
437
         * @psalm-var array<
438
         *   array-key,
439
         *   array{
440
         *     label: string,
441
         *     link: string,
442
         *     linkAttributes: array,
443
         *     items?: array,
444
         *     active: bool,
445
         *     disabled: bool,
446
         *     enclose: bool,
447
         *     headerAttributes: array,
448
         *     itemContainerAttributes: array,
449
         *     toggleAttributes: array,
450
         *     visible: bool,
451
         *     items: array,
452
         *   }|string
453
         * > $normalizedItems
454
         */
455
        $normalizedItems = Helper\Normalizer::dropdown($this->items);
456
457
        $containerAttributes = $this->containerAttributes;
458
459
        $items = $this->renderItems($normalizedItems) . PHP_EOL;
460
461
        if (trim($items) === '') {
462
            return '';
463
        }
464
465
        if ($this->containerClass !== '') {
466
            Html::addCssClass($containerAttributes, $this->containerClass);
467
        }
468
469
        if ($this->containerTag === '') {
470
            throw new InvalidArgumentException('Tag name must be a string and cannot be empty.');
471
        }
472
473
        return match ($this->container) {
474
            true => Html::normalTag($this->containerTag, $items, $containerAttributes)->encode(false)->render(),
475
            false => $items,
476
        };
477
    }
478
479
    private function renderDivider(): string
480
    {
481
        $dividerAttributes = $this->dividerAttributes;
482
483
        if ($this->dividerClass !== '') {
484
            Html::addCssClass($dividerAttributes, $this->dividerClass);
485
        }
486
487
        if ($this->dividerTag === '') {
488
            throw new InvalidArgumentException('Tag name must be a string and cannot be empty.');
489
        }
490
491
        return $this->renderItemContainer(
492
            Html::tag($this->dividerTag, '', $dividerAttributes)->encode(false)->render(),
493
        );
494
    }
495
496
    /**
497
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
498
     */
499
    private function renderDropdown(array $items): string
500
    {
501
        return self::widget()
502
            ->container(false)
0 ignored issues
show
Bug introduced by
The method container() does not exist on Yiisoft\Widget\Widget. It seems like you code against a sub-type of Yiisoft\Widget\Widget such as Yiisoft\Yii\Widgets\Menu or Yiisoft\Yii\Widgets\Dropdown. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

502
            ->/** @scrutinizer ignore-call */ container(false)
Loading history...
503
            ->dividerAttributes($this->dividerAttributes)
504
            ->headerClass($this->headerClass)
505
            ->headerTag($this->headerTag)
506
            ->itemClass($this->itemClass)
507
            ->itemContainerAttributes($this->itemContainerAttributes)
508
            ->itemContainerTag($this->itemContainerTag)
509
            ->items($items)
510
            ->itemsContainerAttributes(array_merge($this->itemsContainerAttributes))
511
            ->itemTag($this->itemTag)
512
            ->toggleAttributes($this->toggleAttributes)
513
            ->toggleType($this->toggleType)
514
            ->render();
515
    }
516
517
    private function renderHeader(string $label, array $headerAttributes = []): string
518
    {
519
        if ($this->headerClass !== '') {
520
            Html::addCssClass($headerAttributes, $this->headerClass);
521
        }
522
523
        if ($this->headerTag === '') {
524
            throw new InvalidArgumentException('Tag name must be a string and cannot be empty.');
525
        }
526
527
        return $this->renderItemContainer(
528
            Html::normalTag($this->headerTag, $label, $headerAttributes)->encode(false)->render(),
529
        );
530
    }
531
532
    /**
533
     * @param array $item The item to be rendered.
534
     *
535
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
536
     *
537
     * @psalm-param array{
538
     *   label: string,
539
     *   link: string,
540
     *   linkAttributes: array,
541
     *   active: bool,
542
     *   disabled: bool,
543
     *   enclose: bool,
544
     *   headerAttributes: array,
545
     *   itemContainerAttributes: array,
546
     *   toggleAttributes: array,
547
     *   visible: bool,
548
     *   items: array,
549
     * } $item
550
     */
551
    private function renderItem(array $item): string
552
    {
553
        if ($item['visible'] === false) {
554
            return '';
555
        }
556
557
        $lines = [];
558
        $linkAttributes = $item['linkAttributes'];
559
560
        if ($this->itemClass !== '') {
561
            Html::addCssClass($linkAttributes, $this->itemClass);
562
        }
563
564
        if ($item['active']) {
565
            $linkAttributes['aria-current'] = 'true';
566
            Html::addCssClass($linkAttributes, [$this->activeClass]);
567
        }
568
569
        if ($item['disabled']) {
570
            Html::addCssClass($linkAttributes, $this->disabledClass);
571
        }
572
573
        if ($item['items'] === []) {
574
            $lines[] = $this->renderItemContent(
575
                $item['label'],
576
                $item['link'],
577
                $item['enclose'],
578
                $linkAttributes,
579
                $item['headerAttributes'],
580
                $item['itemContainerAttributes'],
581
            );
582
        } else {
583
            $itemContainer = $this->renderItemsContainer($this->renderDropdown($item['items']));
584
            $toggle = $this->renderToggle($item['label'], $item['link'], $item['toggleAttributes']);
585
            $toggleSplitButton = $this->renderToggleSplitButton($item['label']);
586
587
            if ($this->toggleType === 'split' && !str_contains($this->containerClass, 'dropstart')) {
588
                $lines[] = $toggleSplitButton . PHP_EOL . $toggle . PHP_EOL . $itemContainer;
589
            } elseif ($this->toggleType === 'split' && str_contains($this->containerClass, 'dropstart')) {
590
                $lines[] = $toggle . PHP_EOL . $itemContainer . PHP_EOL . $toggleSplitButton;
591
            } else {
592
                $lines[] = $toggle . PHP_EOL . $itemContainer;
593
            }
594
        }
595
596
        /** @psalm-var string[] $lines */
597
        return implode(PHP_EOL, $lines);
598
    }
599
600
    private function renderItemContainer(string $content, array $itemContainerAttributes = []): string
601
    {
602
        if ($this->itemContainerTag === '') {
603
            throw new InvalidArgumentException('Tag name must be a string and cannot be empty.');
604
        }
605
606
        if ($itemContainerAttributes === []) {
607
            $itemContainerAttributes = $this->itemContainerAttributes;
608
        }
609
610
        return Html::normalTag($this->itemContainerTag, $content, $itemContainerAttributes)
611
            ->encode(false)
612
            ->render();
613
    }
614
615
    private function renderItemsContainer(string $content): string
616
    {
617
        $itemsContainerAttributes = $this->itemsContainerAttributes;
618
619
        if ($this->id !== '') {
620
            $itemsContainerAttributes['aria-labelledby'] = $this->id;
621
        }
622
623
        if ($this->itemsContainerTag === '') {
624
            throw new InvalidArgumentException('Tag name must be a string and cannot be empty.');
625
        }
626
627
        return Html::normalTag($this->itemsContainerTag, $content, $itemsContainerAttributes)
628
            ->encode(false)
629
            ->render();
630
    }
631
632
    private function renderItemContent(
633
        string $label,
634
        string $link,
635
        bool $enclose,
636
        array $linkAttributes = [],
637
        array $headerAttributes = [],
638
        array $itemContainerAttributes = [],
639
    ): string {
640
        return match (true) {
641
            $label === '-' => $this->renderDivider(),
642
            $enclose === false => $label,
643
            $link === '' => $this->renderHeader($label, $headerAttributes),
644
            default => $this->renderItemLink($label, $link, $linkAttributes, $itemContainerAttributes),
645
        };
646
    }
647
648
    /**
649
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
650
     *
651
     * @psalm-param array<
652
     *   array-key,
653
     *   array{
654
     *     label: string,
655
     *     link: string,
656
     *     linkAttributes: array,
657
     *     active: bool,
658
     *     disabled: bool,
659
     *     enclose: bool,
660
     *     headerAttributes: array,
661
     *     itemContainerAttributes: array,
662
     *     toggleAttributes: array,
663
     *     visible: bool,
664
     *     items: array,
665
     *   }|string
666
     * > $items
667
     */
668
    private function renderItems(array $items = []): string
669
    {
670
        $lines = [];
671
672
        foreach ($items as $item) {
673
            $line = match (gettype($item)) {
674
                'array' => $this->renderItem($item),
675
                'string' => $this->renderDivider(),
676
            };
677
678
            if ($line !== '') {
679
                $lines[] = $line;
680
            }
681
        }
682
683
        return PHP_EOL . implode(PHP_EOL, $lines);
684
    }
685
686
    private function renderItemLink(
687
        string $label,
688
        string $link,
689
        array $linkAttributes = [],
690
        array $itemContainerAttributes = []
691
    ): string {
692
        $linkAttributes['href'] = $link;
693
694
        if ($this->itemTag === '') {
695
            throw new InvalidArgumentException('Tag name must be a string and cannot be empty.');
696
        }
697
698
        $linkTag = Html::normalTag($this->itemTag, $label, $linkAttributes)->encode(false)->render();
699
700
        return match ($this->itemContainer) {
701
            true => $this->renderItemContainer($linkTag, $itemContainerAttributes),
702
            default => $linkTag,
703
        };
704
    }
705
706
    private function renderToggle(string $label, string $link, array $toggleAttributes = []): string
707
    {
708
        if ($toggleAttributes === []) {
709
            $toggleAttributes = $this->toggleAttributes;
710
        }
711
712
        if ($this->id !== '') {
713
            $toggleAttributes['id'] = $this->id;
714
        }
715
716
        return match ($this->toggleType) {
717
            'link' => $this->renderToggleLink($label, $link, $toggleAttributes),
718
            'split' => $this->renderToggleSplit($label, $toggleAttributes),
719
            default => $this->renderToggleButton($label, $toggleAttributes),
720
        };
721
    }
722
723
    private function renderToggleButton(string $label, array $toggleAttributes = []): string
724
    {
725
        return Button::tag()->attributes($toggleAttributes)->content($label)->type('button')->render();
726
    }
727
728
    private function renderToggleLink(string $label, string $link, array $toggleAttributes = []): string
729
    {
730
        return A::tag()->attributes($toggleAttributes)->content($label)->href($link)->render();
731
    }
732
733
    private function renderToggleSplit(string $label, array $toggleAttributes = []): string
734
    {
735
        return Button::tag()
736
            ->attributes($toggleAttributes)
737
            ->content(Span::tag()->attributes($this->splitButtonSpanAttributes)->content($label))
738
            ->type('button')
739
            ->render();
740
    }
741
742
    private function renderToggleSplitButton(string $label): string
743
    {
744
        return Button::tag()->attributes($this->splitButtonAttributes)->content($label)->type('button')->render();
745
    }
746
}
747