Passed
Pull Request — master (#20)
by Mr.
02:25
created

Dropdown::renderItems()   C

Complexity

Conditions 14
Paths 77

Size

Total Lines 82
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 51
CRAP Score 14.0013

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 14
eloc 56
c 1
b 0
f 0
nc 77
nop 2
dl 0
loc 82
ccs 51
cts 52
cp 0.9808
crap 14.0013
rs 6.2666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Bootstrap5;
6
7
use JsonException;
8
use RuntimeException;
9
use Yiisoft\Arrays\ArrayHelper;
10
use Yiisoft\Html\Html;
11
12
use function array_key_exists;
13
use function array_merge;
14
use function array_merge_recursive;
15
use function implode;
16
use function is_string;
17
18
/**
19
 * Dropdown renders a Bootstrap dropdown menu component.
20
 *
21
 * For example,
22
 *
23
 * ```php
24
 * <div class="dropdown">
25
 *     <?php
26
 *         echo Dropdown::widget()
27
 *             ->items([
28
 *                 ['label' => 'DropdownA', 'url' => '/'],
29
 *                 ['label' => 'DropdownB', 'url' => '#'],
30
 *             ]);
31
 *     ?>
32
 * </div>
33
 * ```
34
 */
35
class Dropdown extends Widget
36
{
37
    private array $items = [];
38
    private bool $encodeLabels = true;
39
    private array $submenuOptions = [];
40
    private array $options = [];
41
42 16
    protected function run(): string
43
    {
44 16
        if (!isset($this->options['id'])) {
45 15
            $this->options['id'] = "{$this->getId()}-dropdown";
46
        }
47
48 16
        Html::addCssClass($this->options, ['widget' => 'dropdown-menu']);
49
50 16
        $this->registerClientEvents($this->options['id']);
51
52 16
        return $this->renderItems($this->items, $this->options);
53
    }
54
55
    /**
56
     * Renders menu items.
57
     *
58
     * @param array $items the menu items to be rendered
59
     * @param array $options the container HTML attributes
60
     *
61
     * @throws JsonException|RuntimeException if the label option is not specified in one of the items.
62
     *
63
     * @return string the rendering result.
64
     */
65 16
    protected function renderItems(array $items, array $options = []): string
66
    {
67 16
        $lines = [];
68
69 16
        foreach ($items as $item) {
70 16
            if (is_string($item)) {
71 3
                $item = ['label' => $item, 'encode' => false, 'enclose' => false];
72
            }
73
74 16
            if (isset($item['visible']) && !$item['visible']) {
75 3
                continue;
76
            }
77
78 16
            if (!array_key_exists('label', $item)) {
79
                throw new RuntimeException("The 'label' option is required.");
80
            }
81
82 16
            $encodeLabel = $item['encode'] ?? $this->encodeLabels;
83 16
            $label = $encodeLabel ? Html::encode($item['label']) : $item['label'];
84 16
            $itemOptions = ArrayHelper::getValue($item, 'options', []);
85 16
            $linkOptions = ArrayHelper::getValue($item, 'linkOptions', []);
86 16
            $active = ArrayHelper::getValue($item, 'active', false);
87 16
            $disabled = ArrayHelper::getValue($item, 'disabled', false);
88 16
            $enclose = ArrayHelper::getValue($item, 'enclose', true);
89
90 16
            Html::addCssClass($linkOptions, 'dropdown-item');
91
92 16
            if ($disabled) {
93 1
                ArrayHelper::setValue($linkOptions, 'tabindex', '-1');
94 1
                ArrayHelper::setValue($linkOptions, 'aria-disabled', 'true');
95 1
                Html::addCssClass($linkOptions, 'disabled');
96 16
            } elseif ($active) {
97 3
                Html::addCssClass($linkOptions, 'active');
98
            }
99
100 16
            $url = $item['url'] ?? null;
101
102 16
            if (empty($item['items'])) {
103 16
                if ($label === '-') {
104 3
                    $content = Html::div('', ['class' => 'dropdown-divider']);
105 16
                } elseif ($enclose === false) {
106 1
                    $content = $label;
107 16
                } elseif ($url === null) {
108 8
                    $content = Html::tag('h6', $label, ['class' => 'dropdown-header']);
109
                } else {
110 10
                    $content = Html::a($label, $url, $linkOptions);
111
                }
112
113 16
                $lines[] = $content;
114
            } else {
115 3
                $submenuOptions = $this->submenuOptions;
116
117 3
                if (isset($item['submenuOptions'])) {
118 1
                    $submenuOptions = array_merge($submenuOptions, $item['submenuOptions']);
119
                }
120
121 3
                Html::addCssClass($submenuOptions, ['dropdown-submenu']);
122 3
                Html::addCssClass($linkOptions, ['dropdown-toggle']);
123
124 3
                $lines[] = Html::beginTag(
125 3
                    'div',
126 3
                    array_merge_recursive(['class' => ['dropdown'], 'aria-expanded' => 'false'], $itemOptions)
127
                );
128
129 3
                $lines[] = Html::a($label, $url, array_merge([
130 3
                    'data-toggle' => 'dropdown',
131
                    'aria-haspopup' => 'true',
132
                    'aria-expanded' => 'false',
133
                    'role' => 'button',
134 3
                ], $linkOptions));
135
136 3
                $lines[] = self::widget()
137 3
                    ->items($item['items'])
0 ignored issues
show
Bug introduced by
The method items() does not exist on Yiisoft\Widget\Widget. It seems like you code against a sub-type of Yiisoft\Widget\Widget such as Yiisoft\Yii\Bootstrap5\Nav or Yiisoft\Yii\Bootstrap5\Carousel or Yiisoft\Yii\Bootstrap5\Dropdown or Yiisoft\Yii\Bootstrap5\Tabs or Yiisoft\Yii\Bootstrap5\Accordion. ( Ignorable by Annotation )

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

137
                    ->/** @scrutinizer ignore-call */ items($item['items'])
Loading history...
138 3
                    ->options($submenuOptions)
139 3
                    ->submenuOptions($submenuOptions)
140 3
                    ->encodeLabels($this->encodeLabels)
141 3
                    ->render();
142 3
                $lines[] = Html::endTag('div');
143
            }
144
        }
145
146 16
        return Html::tag('div', implode("\n", $lines), $options);
147
    }
148
149
    /**
150
     * List of menu items in the dropdown. Each array element can be either an HTML string, or an array representing a
151
     * single menu with the following structure:
152
     *
153
     * - label: string, required, the label of the item link.
154
     * - encode: bool, optional, whether to HTML-encode item label.
155
     * - url: string|array, optional, the URL of the item link. This will be processed by {@see currentPath}.
156
     *   If not set, the item will be treated as a menu header when the item has no sub-menu.
157
     * - visible: bool, optional, whether this menu item is visible. Defaults to true.
158
     * - linkOptions: array, optional, the HTML attributes of the item link.
159
     * - options: array, optional, the HTML attributes of the item.
160
     * - items: array, optional, the submenu items. The structure is the same as this property.
161
     *   Note that Bootstrap doesn't support dropdown submenu. You have to add your own CSS styles to support it.
162
     * - submenuOptions: array, optional, the HTML attributes for sub-menu container tag. If specified it will be
163
     *   merged with {@see submenuOptions}.
164
     *
165
     * To insert divider use `-`.
166
     *
167
     * @param array $value
168
     *
169
     * @return $this
170
     */
171 16
    public function items(array $value): self
172
    {
173 16
        $this->items = $value;
174
175 16
        return $this;
176
    }
177
178
    /**
179
     * Whether the labels for header items should be HTML-encoded.
180
     *
181
     * @param bool $value
182
     *
183
     * @return $this
184
     */
185 12
    public function encodeLabels(bool $value): self
186
    {
187 12
        $this->encodeLabels = $value;
188
189 12
        return $this;
190
    }
191
192
    /**
193
     * The HTML attributes for sub-menu container tags.
194
     *
195
     * @param array $value
196
     *
197
     * @return $this
198
     */
199 3
    public function submenuOptions(array $value): self
200
    {
201 3
        $this->submenuOptions = $value;
202
203 3
        return $this;
204
    }
205
206
    /**
207
     * @param array $value the HTML attributes for the widget container tag. The following special options are
208
     * recognized.
209
     *
210
     * @return $this
211
     *
212
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
213
     */
214 12
    public function options(array $value): self
215
    {
216 12
        $this->options = $value;
217
218 12
        return $this;
219
    }
220
}
221