Passed
Push — master ( 566566...d0f53d )
by Alexander
02:18
created

ButtonDropdown::renderButton()   B

Complexity

Conditions 6
Paths 16

Size

Total Lines 49
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 30
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 30
nc 16
nop 0
dl 0
loc 49
ccs 30
cts 30
cp 1
crap 6
rs 8.8177
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Bootstrap5;
6
7
use Yiisoft\Arrays\ArrayHelper;
8
use Yiisoft\Factory\Exceptions\InvalidConfigException;
9
use Yiisoft\Html\Html;
10
11
/**
12
 * ButtonDropdown renders a group or split button dropdown bootstrap component.
13
 *
14
 * For example,
15
 *
16
 * ```php
17
 * // a button group using Dropdown widget
18
 * echo ButtonDropdown::widget()
19
 *     ->label('Action')
20
 *     ->dropdown'([
21
 *         'items' => [
22
 *             ['label' => 'DropdownA', 'url' => '/'],
23
 *             ['label' => 'DropdownB', 'url' => '#'],
24
 *         ],
25
 *     ]);
26
 * ```
27
 */
28
final class ButtonDropdown extends Widget
29
{
30
    /**
31
     * The css class part of dropdown
32
     */
33
    public const DIRECTION_DOWN = 'down';
34
35
    /**
36
     * The css class part of dropleft
37
     */
38
    public const DIRECTION_LEFT = 'left';
39
40
    /**
41
     * The css class part of dropright
42
     */
43
    public const DIRECTION_RIGHT = 'right';
44
45
    /**
46
     * The css class part of dropup
47
     */
48
    public const DIRECTION_UP = 'up';
49
50
    private string $label = 'Button';
51
    private array $options = [];
52
    private array $buttonOptions = [];
53
    private array $dropdown = [];
54
    private string $direction = self::DIRECTION_DOWN;
55
    private bool $split = false;
56
    private string $tagName = 'button';
57
    private bool $encodeLabels = true;
58
    private bool $encodeTags = false;
59
    private string $dropdownClass = Dropdown::class;
60
    private bool $renderContainer = true;
61
62 11
    protected function run(): string
63
    {
64 11
        if (!isset($this->dropdown['items'])) {
65 1
            return '';
66
        }
67
68
        /** Set options id to button options id to ensure correct css selector in plugin initialisation */
69 10
        if (empty($this->options['id'])) {
70 10
            $id = $this->getId();
71
72 10
            $this->options['id'] = "{$id}-button-dropdown";
73 10
            $this->buttonOptions['id'] = "{$id}-button";
74
        }
75
76 10
        if ($this->encodeTags === false) {
77 9
            $this->options['encode'] = false;
78
        }
79
80 10
        $html = $this->renderButton() . "\n" . $this->renderDropdown();
81
82 10
        if ($this->renderContainer) {
83
            /** @psalm-suppress InvalidArgument */
84 9
            Html::addCssClass($this->options, ['widget' => 'drop' . $this->direction, 'btn-group']);
85
86 9
            $options = $this->options;
87 9
            $tag = ArrayHelper::remove($options, 'tag', 'div');
88 9
            $html = Html::tag($tag, $html, $options);
89
        }
90
91 10
        return $html;
92
    }
93
94
    /**
95
     * The HTML attributes of the button.
96
     *
97
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
98
     *
99
     * @param array $value
100
     *
101
     * @return $this
102
     */
103 1
    public function buttonOptions(array $value): self
104
    {
105 1
        $new = clone $this;
106 1
        $new->buttonOptions = $value;
107
108 1
        return $new;
109
    }
110
111
    /**
112
     * The drop-direction of the widget.
113
     *
114
     * Possible values are 'left', 'right', 'up', or 'down' (default)
115
     *
116
     * @param string $value
117
     *
118
     * @return $this
119
     */
120 2
    public function direction(string $value): self
121
    {
122 2
        $new = clone $this;
123 2
        $new->direction = $value;
124
125 2
        return $new;
126
    }
127
128
    /**
129
     * The configuration array for example:
130
     *
131
     * ```php
132
     *    [
133
     *        'items' => [
134
     *            ['label' => 'DropdownA', 'url' => '/'],
135
     *            ['label' => 'DropdownB', 'url' => '#'],
136
     *        ],
137
     *    ]
138
     * ```
139
     *
140
     * {@see Dropdown}
141
     *
142
     * @param array $value
143
     *
144
     * @return $this
145
     */
146 10
    public function dropdown(array $value): self
147
    {
148 10
        $new = clone $this;
149 10
        $new->dropdown = $value;
150
151 10
        return $new;
152
    }
153
154
    /**
155
     * Name of a class to use for rendering dropdowns withing this widget. Defaults to {@see Dropdown}.
156
     *
157
     * @param string $value
158
     *
159
     * @return $this
160
     */
161 1
    public function dropdownClass(string $value): self
162
    {
163 1
        $new = clone $this;
164 1
        $new->dropdownClass = $value;
165
166 1
        return $new;
167
    }
168
169
    /**
170
     * When tags Labels HTML should not be encoded.
171
     *
172
     * @return $this
173
     */
174 1
    public function withoutEncodeLabels(): self
175
    {
176 1
        $new = clone $this;
177 1
        $new->encodeLabels = false;
178
179 1
        return $new;
180
    }
181
182
    /**
183
     * The button label.
184
     *
185
     * @param string $value
186
     *
187
     * @return $this
188
     */
189 2
    public function label(string $value): self
190
    {
191 2
        $new = clone $this;
192 2
        $new->label = $value;
193
194 2
        return $new;
195
    }
196
197
    /**
198
     * The HTML attributes for the container tag. The following special options are recognized.
199
     *
200
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
201
     *
202
     * @param array $value
203
     *
204
     * @return $this
205
     */
206 1
    public function options(array $value): self
207
    {
208 1
        $new = clone $this;
209 1
        $new->options = $value;
210
211 1
        return $new;
212
    }
213
214
    /**
215
     * Whether to render the container using the {@see options} as HTML attributes. If set to `false`, the container
216
     * element enclosing the button and dropdown will NOT be rendered.
217
     *
218
     * @return $this
219
     */
220 1
    public function withoutRenderContainer(): self
221
    {
222 1
        $new = clone $this;
223 1
        $new->renderContainer = false;
224
225 1
        return $new;
226
    }
227
228
    /**
229
     * Whether to display a group of split-styled button group.
230
     *
231
     * @return $this
232
     */
233 1
    public function split(): self
234
    {
235 1
        $new = clone $this;
236 1
        $new->split = true;
237
238 1
        return $new;
239
    }
240
241
    /**
242
     * The tag to use to render the button.
243
     *
244
     * @param string $value
245
     *
246
     * @return $this
247
     */
248 1
    public function tagName(string $value): self
249
    {
250 1
        $new = clone $this;
251 1
        $new->tagName = $value;
252
253 1
        return $new;
254
    }
255
256
    /**
257
     * Allows you to enable the encoding tags html.
258
     *
259
     * @return self
260
     */
261 1
    public function encodeTags(): self
262
    {
263 1
        $new = clone $this;
264 1
        $new->encodeTags = true;
265
266 1
        return $new;
267
    }
268
269
    /**
270
     * Generates the button dropdown.
271
     *
272
     * @throws InvalidConfigException
273
     *
274
     * @return string the rendering result.
275
     */
276 10
    private function renderButton(): string
277
    {
278 10
        Html::addCssClass($this->buttonOptions, ['buttonOptions' => 'btn']);
279
280 10
        $label = $this->label;
281
282 10
        if ($this->encodeLabels !== false) {
283 10
            $label = Html::encode($label);
284
        }
285
286 10
        if ($this->split) {
287 1
            $buttonOptions = $this->buttonOptions;
288
289 1
            $this->buttonOptions['data-bs-toggle'] = 'dropdown';
290 1
            $this->buttonOptions['aria-haspopup'] = 'true';
291 1
            $this->buttonOptions['aria-expanded'] = 'false';
292
293 1
            Html::addCssClass($this->buttonOptions, ['toggle' => 'dropdown-toggle dropdown-toggle-split']);
294
295 1
            unset($buttonOptions['id']);
296
297 1
            $splitButton = Button::widget()
298 1
                ->label('<span class="sr-only">Toggle Dropdown</span>')
0 ignored issues
show
Bug introduced by
The method label() 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\Button or Yiisoft\Yii\Bootstrap5\Progress or Yiisoft\Yii\Bootstrap5\ButtonDropdown. ( Ignorable by Annotation )

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

298
                ->/** @scrutinizer ignore-call */ label('<span class="sr-only">Toggle Dropdown</span>')
Loading history...
299 1
                ->options($this->buttonOptions)
300 1
                ->withoutEncodeLabels()
301 1
                ->render();
302
        } else {
303 9
            $buttonOptions = $this->buttonOptions;
304
305 9
            Html::addCssClass($buttonOptions, ['toggle' => 'dropdown-toggle']);
306
307 9
            $buttonOptions['data-bs-toggle'] = 'dropdown';
308 9
            $buttonOptions['aria-haspopup'] = 'true';
309 9
            $buttonOptions['aria-expanded'] = 'false';
310 9
            $splitButton = '';
311
        }
312
313 10
        if (!isset($buttonOptions['href']) && ($this->tagName === 'a')) {
314 1
            $buttonOptions['href'] = '#';
315 1
            $buttonOptions['role'] = 'button';
316
        }
317
318 10
        $button = Button::widget()->label($label)->options($buttonOptions)->tagName($this->tagName);
319
320 10
        if ($this->encodeLabels === false) {
321 1
            $button = $button->withoutEncodeLabels();
322
        }
323
324 10
        return $button->render() . "\n" . $splitButton;
325
    }
326
327
    /**
328
     * Generates the dropdown menu.
329
     *
330
     * @return string the rendering result.
331
     */
332 10
    private function renderDropdown(): string
333
    {
334 10
        $dropdownClass = $this->dropdownClass;
335
336 10
        $dropdown = $dropdownClass::widget()->items($this->dropdown['items']);
337
338 10
        if ($this->encodeLabels === false) {
339 1
            $dropdown = $dropdown->withoutEncodeLabels();
340
        }
341
342 10
        return $dropdown->render();
343
    }
344
}
345