Passed
Push — master ( aedd55...0c8c47 )
by Alexander
15:54 queued 14:03
created

Dropdown::dividerClass()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Bulma;
6
7
use Yiisoft\Arrays\ArrayHelper;
8
use Yiisoft\Html\Html;
9
use Yiisoft\Widget\Exception\InvalidConfigException;
10
11
final class Dropdown extends Widget
12
{
13
    private string $buttonLabel = '';
14
    private array $buttonLabelOptions = [];
15
    private array $buttonIcon = ['class' => 'fas fa-angle-down', 'aria-hidden' => 'true'];
16
    private array $buttonIconOptions = [];
17
    private string $dividerClass = 'dropdown-divider';
18
    private string $itemsClass = 'dropdown-menu';
19
    private string $itemClass = 'dropdown-item';
20
    private bool $encodeLabels = true;
21
    private bool $encloseByContainer = true;
22
    private array $items = [];
23
    private array $itemsOptions = [];
24
    private array $options = [];
25
    private array $buttonOptions = [];
26
    private array $linkOptions = ['aria-haspopup' => 'true', 'aria-expanded' => 'false'];
27
    private array $triggerOptions = [];
28
29 16
    protected function run(): string
30
    {
31 16
        $this->buildOptions();
32
33 16
        return $this->buildDropdown();
34
    }
35
36
    /**
37
     * Set label button dropdown.
38
     *
39
     * @param string $value
40
     *
41
     * @return self
42
     */
43 5
    public function buttonLabel(string $value): self
44
    {
45 5
        $this->buttonLabel = $value;
46 5
        return $this;
47
    }
48
49
    /**
50
     * The HTML attributes for the button dropdown.
51
     *
52
     * @param array $value
53
     *
54
     * @return self
55
     *
56
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
57
     */
58 1
    public function buttonLabelOptions(array $value): self
59
    {
60 1
        $this->buttonLabelOptions = $value;
61 1
        return $this;
62
    }
63
64
    /**
65
     * Set css class divider dropdown.
66
     *
67
     * @param $value
68
     *
69
     * @return self
70
     */
71 11
    public function dividerClass(string $value): self
72
    {
73 11
        $this->dividerClass  = $value;
74 11
        return $this;
75
    }
76
77
    /**
78
     * Set css class item dropdown.
79
     *
80
     * @param $value
81
     *
82
     * @return self
83
     */
84 11
    public function itemClass(string $value): self
85
    {
86 11
        $this->itemClass  = $value;
87 11
        return $this;
88
    }
89
90
    /**
91
     * Set css class items container dropdown.
92
     *
93
     * @param $value
94
     *
95
     * @return self
96
     */
97 11
    public function itemsClass(string $value): self
98
    {
99 11
        $this->itemsClass  = $value;
100 11
        return $this;
101
    }
102
103
    /**
104
     * Whether the labels for header items should be HTML-encoded.
105
     *
106
     * @param bool $value
107
     *
108
     * @return self
109
     */
110 11
    public function encodeLabels(bool $value): self
111
    {
112 11
        $this->encodeLabels = $value;
113 11
        return $this;
114
    }
115
116
    /**
117
     * Set enclosed by container dropdown.
118
     *
119
     * @param $value
120
     *
121
     * @return self
122
     */
123 11
    public function encloseByContainer(bool $value): self
124
    {
125 11
        $this->encloseByContainer = $value;
126 11
        return $this;
127
    }
128
129
    /**
130
     * List of menu items in the dropdown. Each array element can be either an HTML string, or an array representing a
131
     * single menu with the following structure:
132
     *
133
     * - label: string, required, the label of the item link.
134
     * - encode: bool, optional, whether to HTML-encode item label.
135
     * - url: string|array, optional, the URL of the item link. This will be processed by {@see currentPath}.
136
     *   If not set, the item will be treated as a menu header when the item has no sub-menu.
137
     * - visible: bool, optional, whether this menu item is visible. Defaults to true.
138
     * - linkOptions: array, optional, the HTML attributes of the item link.
139
     * - itemsOptions: array, optional, the HTML attributes of the item.
140
     * - items: array, optional, the submenu items. The structure is the same as this property.
141
     *
142
     * To insert divider use `-`.
143
     */
144 16
    public function items(array $value): self
145
    {
146 16
        $this->items = $value;
147 16
        return $this;
148
    }
149
150
    /**
151
     * The HTML attributes for the widget container tag.
152
     *
153
     * @param array $value
154
     *
155
     * @return self
156
     *
157
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
158
     */
159 1
    public function options(array $value): self
160
    {
161 1
        $this->options = $value;
162 1
        return $this;
163
    }
164
165
    /**
166
     * The HTML attributes for the widget button tag.
167
     *
168
     * @param array $value
169
     *
170
     * @return self
171
     *
172
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
173
     */
174 1
    public function buttonOptions(array $value): self
175
    {
176 1
        $this->buttonOptions = $value;
177 1
        return $this;
178
    }
179
180
    /**
181
     * The HTML attributes for the widget items.
182
     *
183
     * @param array $value
184
     *
185
     * @return self
186
     *
187
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
188
     */
189 11
    public function itemsOptions(array $value): self
190
    {
191 11
        $this->itemsOptions = $value;
192 11
        return $this;
193
    }
194
195
    /**
196
     * The HTML attributes for the widget container trigger.
197
     *
198
     * @param array $value
199
     *
200
     * @return self
201
     *
202
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
203
     */
204 1
    public function triggerOptions(array $value): self
205
    {
206 1
        $this->triggerOptions = $value;
207 1
        return $this;
208
    }
209
210 16
    private function buildDropdown(): string
211
    {
212 16
        if ($this->encloseByContainer) {
213 5
            $html = Html::beginTag('div', $this->options) . "\n";
214 5
            $html .= $this->buildDropdownTrigger();
215 5
            $html .= $this->renderItems($this->items, $this->itemsOptions) . "\n";
216 5
            $html .= Html::endTag('div');
217
        } else {
218 11
            $html = $this->renderItems($this->items, $this->itemsOptions);
219
        }
220
221 15
        return $html;
222
    }
223
224 5
    private function buildDropdownTrigger(): string
225
    {
226
        return
227 5
            Html::beginTag('div', $this->triggerOptions) . "\n" .
228 5
                Html::beginTag('button', $this->buttonOptions) . "\n" .
229 5
                    Html::tag('span', $this->buttonLabel, $this->buttonLabelOptions) . "\n" .
230 5
                    Html::beginTag('span', $this->buttonIconOptions) . "\n" .
231 5
                        Html::tag('i', '', $this->buttonIcon) . "\n" .
232 5
                    Html::endTag('span') . "\n" .
233 5
                Html::endTag('button') . "\n" .
234 5
            Html::endTag('div') . "\n";
235
    }
236
237 16
    private function buildOptions(): void
238
    {
239 16
        if ($this->encloseByContainer && (!isset($this->options['id']))) {
240 5
            $this->options['id'] = "{$this->getId()}-dropdown";
241 5
            $this->options = $this->addOptions($this->options, 'dropdown');
242 5
            $this->triggerOptions = $this->addOptions($this->triggerOptions, 'dropdown-trigger');
243 5
            $this->buttonOptions = $this->addOptions($this->buttonOptions, 'button');
244 5
            $this->buttonOptions = array_merge(
245 5
                ['aria-haspopup' => 'true', 'aria-controls' => 'dropdown-menu'],
246 5
                $this->buttonOptions
247
            );
248 5
            $this->buttonIconOptions = $this->addOptions($this->buttonIconOptions, 'icon is-small');
249 11
        } elseif (!isset($this->itemsOptions['id'])) {
250 10
            $this->itemsOptions['id'] = "{$this->getId()}-dropdown";
251
        }
252
253 16
        $this->itemsOptions = $this->addOptions($this->itemsOptions, $this->itemsClass);
254 16
    }
255
256
    /**
257
     * Renders menu items.
258
     *
259
     * @param array $items the menu items to be rendered
260
     * @param array $itemsOptions the container HTML attributes
261
     *
262
     * @return string the rendering result.
263
     *
264
     * @throws InvalidConfigException if the label option is not specified in one of the items.
265
     */
266 16
    private function renderItems(array $items, array $itemsOptions = []): string
267
    {
268 16
        $lines = [];
269
270 16
        foreach ($items as $item) {
271 15
            if ($item === '-') {
272 8
                $lines[] = Html::tag('div', '', ['class' => $this->dividerClass]);
273 8
                continue;
274
            }
275
276 15
            if (!isset($item['label']) && $item !== '-') {
277 1
                throw new InvalidConfigException("The 'label' option is required.");
278
            }
279
280 14
            $this->encodeLabels = $item['encode'] ?? $this->encodeLabels;
281
282 14
            if ($this->encodeLabels) {
283 12
                $label = Html::encode($item['label']);
284
            } else {
285 2
                $label = $item['label'];
286
            }
287
288 14
            $linkOptions = ArrayHelper::getValue($item, 'linkOptions', []);
289 14
            $active = ArrayHelper::getValue($item, 'active', false);
290 14
            $disabled = ArrayHelper::getValue($item, 'disabled', false);
291
292 14
            Html::addCssClass($linkOptions, $this->itemClass);
293
294 14
            if ($disabled) {
295 1
                Html::addCssStyle($linkOptions, 'opacity:.65; pointer-events:none;');
296
            }
297
298 14
            if ($active) {
299 3
                Html::addCssClass($linkOptions, 'is-active');
300
            }
301
302 14
            $url = $item['url'] ?? null;
303
304 14
            if (empty($item['items'])) {
305 14
                $lines[] = Html::a($label, $url, $linkOptions);
306
            } else {
307 1
                $lines[] = Html::a($label, $url, array_merge($this->linkOptions, $linkOptions));
308
309 1
                $lines[] = Dropdown::widget()
310 1
                    ->dividerClass($this->dividerClass)
0 ignored issues
show
Bug introduced by
The method dividerClass() does not exist on Yiisoft\Widget\Widget. It seems like you code against a sub-type of Yiisoft\Widget\Widget such as Yiisoft\Yii\Bulma\Dropdown. ( Ignorable by Annotation )

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

310
                    ->/** @scrutinizer ignore-call */ dividerClass($this->dividerClass)
Loading history...
311 1
                    ->itemClass($this->itemClass)
312 1
                    ->itemsClass($this->itemsClass)
313 1
                    ->encloseByContainer($this->encloseByContainer)
314 1
                    ->encodeLabels($this->encodeLabels)
315 1
                    ->items($item['items'])
316 1
                    ->render();
317
            }
318
        }
319
320
        return
321 15
            Html::beginTag('div', $itemsOptions) . "\n" .
322 15
                implode("\n", $lines) . "\n" .
323 15
            Html::endTag('div');
324
    }
325
}
326