ModalCard   B
last analyzed

Complexity

Total Complexity 48

Size/Duplication

Total Lines 601
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 48
eloc 218
c 1
b 0
f 0
dl 0
loc 601
ccs 207
cts 207
cp 1
rs 8.5599

37 Methods

Rating   Name   Duplication   Size   Complexity  
A footerClass() 0 6 1
A toggleButtonLabel() 0 6 1
A closeButtonAttributes() 0 5 1
A bodyAttributes() 0 5 1
A renderFooter() 0 16 2
A attributes() 0 5 1
A bodyClass() 0 5 1
A autoIdPrefix() 0 5 1
A headerAttributes() 0 5 1
A footer() 0 5 1
A headerClass() 0 6 1
A renderBodyBegin() 0 7 1
A id() 0 5 1
A withoutToggleButton() 0 5 1
A renderHeader() 0 23 2
A toggleButtonSize() 0 11 2
A renderToggleButton() 0 25 4
A closeButtonSize() 0 10 2
A toggleButtonAttributes() 0 6 1
A cardAttributes() 0 5 1
A footerAttributes() 0 5 1
A render() 0 8 1
A title() 0 6 1
A titleAttributes() 0 6 1
A contentClass() 0 6 1
A backgroundClass() 0 5 1
A withoutCloseButton() 0 5 1
A begin() 0 29 3
A renderCloseButton() 0 14 2
A buttonClass() 0 5 1
A toggleButtonId() 0 5 1
A closeButtonCssClass() 0 5 1
A toggleButtonColor() 0 11 2
A titleClass() 0 6 1
A renderBackgroundTransparentOverlay() 0 5 1
A renderBodyEnd() 0 3 1
A cardClass() 0 6 1

How to fix   Complexity   

Complex Class

Complex classes like ModalCard 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 ModalCard, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Bulma;
6
7
use InvalidArgumentException;
8
use Yiisoft\Html\Html;
9
use Yiisoft\Html\Tag\Button;
10
use Yiisoft\Html\Tag\CustomTag;
11
use Yiisoft\Html\Tag\Div;
12
use Yiisoft\Html\Tag\P;
13
use Yiisoft\Widget\Widget;
14
15
use function array_key_exists;
16
use function implode;
17
use function in_array;
18
19
/**
20
 * ModalCard renders a modal window that can be toggled by clicking on a button.
21
 *
22
 * The following example will show the content enclosed between the {@see Widget::begin()} and {@see Widget::end()}
23
 * calls within the modal window:
24
 *
25
 * ```php
26
 * echo ModalCard::widget()
27
 *     ->title('Modal title')
28
 *     ->footer(
29
 *         Html::button('Cancel', ['class' => 'button'])
30
 *     )
31
 *     ->begin();
32
 *
33
 * echo 'Say hello...';
34
 *
35
 * echo ModalCard::end();
36
 * ```
37
 *
38
 * @link https://bulma.io/documentation/components/modal/
39
 */
40
final class ModalCard extends Widget
41
{
42
    public const SIZE_SMALL = 'is-small';
43
    public const SIZE_MEDIUM = 'is-medium';
44
    public const SIZE_LARGE = 'is-large';
45
    private const SIZE_ALL = [
46
        self::SIZE_SMALL,
47
        self::SIZE_MEDIUM,
48
        self::SIZE_LARGE,
49
    ];
50
    public const COLOR_PRIMARY = 'is-primary';
51
    public const COLOR_LINK = 'is-link';
52
    public const COLOR_INFO = 'is-info';
53
    public const COLOR_SUCCESS = 'is-success';
54
    public const COLOR_WARNING = 'is-warning';
55
    public const COLOR_DANGER = 'is-danger';
56
    public const COLOR_DARK = 'is-dark';
57
    private const COLOR_ALL = [
58
        self::COLOR_PRIMARY,
59
        self::COLOR_LINK,
60
        self::COLOR_INFO,
61
        self::COLOR_SUCCESS,
62
        self::COLOR_WARNING,
63
        self::COLOR_DANGER,
64
        self::COLOR_DARK,
65
    ];
66
    private array $attributes = [];
67
    private string $autoIdPrefix = 'w';
68
    private string $backgroundClass = 'modal-background';
69
    private array $bodyAttributes = [];
70
    private string $bodyClass = 'modal-card-body';
71
    private string $buttonClass = 'button modal-button';
72
    private array $cardAttributes = [];
73
    private string $cardClass = 'modal';
74
    private array $closeButtonAttributes = [];
75
    private string $closeButtonCssClass = 'button delete';
76
    private string $closeButtonSize = '';
77
    private string $contentClass = 'modal-card';
78
    private string $footer = '';
79
    private array $headerAttributes = [];
80
    private string $headerClass = 'modal-card-head';
81
    private array $footerAttributes = [];
82
    private string $footerClass = 'modal-card-foot';
83
    private string $title = '';
84
    private string $titleClass = 'modal-card-title';
85
    private array $titleAttributes = [];
86
    private array $toggleButtonAttributes = [];
87
    private string $toggleButtonColor = '';
88
    private string $toggleButtonLabel = 'Toggle button';
89
    private string $toggleButtonSize = '';
90
    private bool $withoutCloseButton = false;
91
    private bool $withoutToggleButton = false;
92
93
    /**
94
     * Returns a new instance with the specified HTML attributes for widget.
95
     *
96
     * @param array $values Attribute values indexed by attribute names.
97
     *
98
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} For details on how attributes are being rendered.
99
     */
100 2
    public function attributes(array $values): self
101
    {
102 2
        $new = clone $this;
103 2
        $new->attributes = $values;
104 2
        return $new;
105
    }
106
107
    /**
108
     * Returns a new instance with the specified prefix to the automatically generated widget IDs.
109
     *
110
     * @param string $value The prefix to the automatically generated widget IDs.
111
     */
112 1
    public function autoIdPrefix(string $value): self
113
    {
114 1
        $new = clone $this;
115 1
        $new->autoIdPrefix = $value;
116 1
        return $new;
117
    }
118
119
    /**
120
     * Returns a new instance with the specified modal card background class.
121
     *
122
     * @param string $value The modal card background class.
123
     */
124 1
    public function backgroundClass(string $value): self
125
    {
126 1
        $new = clone $this;
127 1
        $new->backgroundClass = $value;
128 1
        return $new;
129
    }
130
131
    /**
132
     * Returns a new instance with the specified body attributes.
133
     *
134
     * @param array $value The body attributes.
135
     *
136
     * {@see Html::renderTagAttributes()} For details on how attributes are being rendered.
137
     */
138 3
    public function bodyAttributes(array $value): self
139
    {
140 3
        $new = clone $this;
141 3
        $new->bodyAttributes = $value;
142 3
        return $new;
143
    }
144
145
    /**
146
     * Returns a new instance with the specified modal card body class.
147
     *
148
     * @param string $value The modal card body class.
149
     */
150 1
    public function bodyClass(string $value): self
151
    {
152 1
        $new = clone $this;
153 1
        $new->bodyClass = $value;
154 1
        return $new;
155
    }
156
157
    /**
158
     * Returns a new instance with the specified modal card button class.
159
     *
160
     * @param string $value The modal card button class.
161
     */
162 1
    public function buttonClass(string $value): self
163
    {
164 1
        $new = clone $this;
165 1
        $new->buttonClass = $value;
166 1
        return $new;
167
    }
168
169
    /**
170
     * Returns a new instance with the specified card attributes.
171
     *
172
     * @param array $value The content attributes.
173
     *
174
     * {@see Html::renderTagAttributes()} For details on how attributes are being rendered.
175
     */
176 2
    public function cardAttributes(array $value): self
177
    {
178 2
        $new = clone $this;
179 2
        $new->cardAttributes = $value;
180 2
        return $new;
181
    }
182
183
    /**
184
     * Returns a new instance with the specified modal card class.
185
     *
186
     * @param string $value The modal card class.
187
     */
188 1
    public function cardClass(string $value): self
189
    {
190 1
        $new = clone $this;
191 1
        $new->cardClass = $value;
192
193 1
        return $new;
194
    }
195
196
    /**
197
     * Returns a new instance with the specified close button attributes.
198
     *
199
     * @param array $value The close button attributes.
200
     *
201
     * {@see Html::renderTagAttributes()} For details on how attributes are being rendered.
202
     */
203 3
    public function closeButtonAttributes(array $value): self
204
    {
205 3
        $new = clone $this;
206 3
        $new->closeButtonAttributes = $value;
207 3
        return $new;
208
    }
209
210
    /**
211
     * Returns a new instance with the specified close button class.
212
     *
213
     * @param string $value The close button class.
214
     */
215 1
    public function closeButtonCssClass(string $value): self
216
    {
217 1
        $new = clone $this;
218 1
        $new->closeButtonCssClass = $value;
219 1
        return $new;
220
    }
221
222
    /**
223
     * Returns a new instance with the specified close button size.
224
     *
225
     * @param string $value The close button size. Default setting is "normal".
226
     * Possible values are: ModalCard::SIZE_SMALL, ModalCard::SIZE_MEDIUM, ModalCard::SIZE_LARGE.
227
     */
228 3
    public function closeButtonSize(string $value): self
229
    {
230 3
        if (!in_array($value, self::SIZE_ALL, true)) {
231 1
            $values = implode('", "', self::SIZE_ALL);
232 1
            throw new InvalidArgumentException("Invalid size. Valid values are: \"$values\".");
233
        }
234
235 2
        $new = clone $this;
236 2
        $new->closeButtonSize = $value;
237 2
        return $new;
238
    }
239
240
    /**
241
     * Returns a new instance with the specified modal card content class.
242
     *
243
     * @param string $value The modal card content class.
244
     */
245 1
    public function contentClass(string $value): self
246
    {
247 1
        $new = clone $this;
248 1
        $new->contentClass = $value;
249
250 1
        return $new;
251
    }
252
253
    /**
254
     * Returns a new instance with the specified footer content.
255
     *
256
     * @param string $value The footer content in the modal window.
257
     */
258 17
    public function footer(string $value): self
259
    {
260 17
        $new = clone $this;
261 17
        $new->footer = $value;
262 17
        return $new;
263
    }
264
265
    /**
266
     * Returns a new instance with the specified footer attributes.
267
     *
268
     * @param array $value The footer attributes.
269
     *
270
     * {@see Html::renderTagAttributes()} For details on how attributes are being rendered.
271
     */
272 2
    public function footerAttributes(array $value): self
273
    {
274 2
        $new = clone $this;
275 2
        $new->footerAttributes = $value;
276 2
        return $new;
277
    }
278
279
    /**
280
     * Returns a new instance with the specified header attributes.
281
     *
282
     * @param array $value The header attributes.
283
     *
284
     * {@see Html::renderTagAttributes()} For details on how attributes are being rendered.
285
     */
286 2
    public function headerAttributes(array $value): self
287
    {
288 2
        $new = clone $this;
289 2
        $new->headerAttributes = $value;
290 2
        return $new;
291
    }
292
293
    /**
294
     * Returns a new instance with the specified ID of the widget.
295
     *
296
     * @param string|null $value The ID of the widget.
297
     */
298 1
    public function id(?string $value): self
299
    {
300 1
        $new = clone $this;
301 1
        $new->attributes['id'] = $value;
302 1
        return $new;
303
    }
304
305
    /**
306
     * Returns a new instance with the specified modal card footer class.
307
     *
308
     * @param string $value The modal card footer class.
309
     */
310 1
    public function footerClass(string $value): self
311
    {
312 1
        $new = clone $this;
313 1
        $new->footerClass = $value;
314
315 1
        return $new;
316
    }
317
318
    /**
319
     * Returns a new instance with the specified modal card header class.
320
     *
321
     * @param string $value The modal card head class.
322
     */
323 1
    public function headerClass(string $value): self
324
    {
325 1
        $new = clone $this;
326 1
        $new->headerClass = $value;
327
328 1
        return $new;
329
    }
330
331
    /**
332
     * Returns a new instance with the specified modal card title class.
333
     *
334
     * @param string $value The modal card title class.
335
     */
336 1
    public function titleClass(string $value): self
337
    {
338 1
        $new = clone $this;
339 1
        $new->titleClass = $value;
340
341 1
        return $new;
342
    }
343
344
    /**
345
     * Returns a new instance with the specified title content.
346
     *
347
     * @param string $value The title content in the modal window.
348
     */
349 17
    public function title(string $value): self
350
    {
351 17
        $new = clone $this;
352 17
        $new->title = $value;
353
354 17
        return $new;
355
    }
356
357
    /**
358
     * Returns a new instance with the specified title attributes.
359
     *
360
     * @param array $value The title attributes.
361
     *
362
     * {@see Html::renderTagAttributes()} For details on how attributes are being rendered.
363
     */
364 2
    public function titleAttributes(array $value): self
365
    {
366 2
        $new = clone $this;
367 2
        $new->titleAttributes = $value;
368
369 2
        return $new;
370
    }
371
372
    /**
373
     * Returns a new instance with the specified toggle button attributes.
374
     *
375
     * @param array $value The toggle button attributes.
376
     *
377
     * {@see Html::renderTagAttributes()} For details on how attributes are being rendered.
378
     */
379 2
    public function toggleButtonAttributes(array $value): self
380
    {
381 2
        $new = clone $this;
382 2
        $new->toggleButtonAttributes = $value;
383
384 2
        return $new;
385
    }
386
387
    /**
388
     * Returns a new instance with the specified toggle button color.
389
     *
390
     * @param string $value The toggle button color. By default, there's no color set.
391
     * Possible values are: ModalCard::COLOR_PRIMARY, ModalCard::COLOR_INFO, ModalCard::COLOR_SUCCESS,
392
     * ModalCard::COLOR_WARNING, ModalCard::COLOR_DANGER, ModalCard::COLOR_DARK
393
     */
394 3
    public function toggleButtonColor(string $value): self
395
    {
396 3
        if (!in_array($value, self::COLOR_ALL, true)) {
397 1
            $values = implode('", "', self::COLOR_ALL);
398 1
            throw new InvalidArgumentException("Invalid color. Valid values are: \"$values\".");
399
        }
400
401 2
        $new = clone $this;
402 2
        $new->toggleButtonColor = $value;
403
404 2
        return $new;
405
    }
406
407
    /**
408
     * Returns a new instance with the specified ID of the toggle button.
409
     *
410
     * @param string|null $value The ID of the widget.
411
     */
412 1
    public function toggleButtonId(?string $value): self
413
    {
414 1
        $new = clone $this;
415 1
        $new->toggleButtonAttributes['id'] = $value;
416 1
        return $new;
417
    }
418
419
    /**
420
     * Returns a new instance with the specified toggle button label.
421
     *
422
     * @param string $value The toggle button label.
423
     */
424 2
    public function toggleButtonLabel(string $value): self
425
    {
426 2
        $new = clone $this;
427 2
        $new->toggleButtonLabel = $value;
428
429 2
        return $new;
430
    }
431
432
    /**
433
     * Returns a new instance with the specified toggle button size.
434
     *
435
     * @param string $value The toggle button size.
436
     */
437 3
    public function toggleButtonSize(string $value): self
438
    {
439 3
        if (!in_array($value, self::SIZE_ALL, true)) {
440 1
            $values = implode('", "', self::SIZE_ALL);
441 1
            throw new InvalidArgumentException("Invalid size. Valid values are: \"$values\".");
442
        }
443
444 2
        $new = clone $this;
445 2
        $new->toggleButtonSize = $value;
446
447 2
        return $new;
448
    }
449
450
    /**
451
     * Returns a new instance with the specified options for rendering the close button tag.
452
     *
453
     * @param bool $value Whether the close button is disabled.
454
     */
455 2
    public function withoutCloseButton(bool $value): self
456
    {
457 2
        $new = clone $this;
458 2
        $new->withoutCloseButton = $value;
459 2
        return $new;
460
    }
461
462
    /**
463
     * Returns a new instance with the disabled toggle button.
464
     *
465
     * @param bool $value Whether the toggle button is disabled.
466
     */
467 2
    public function withoutToggleButton(bool $value): self
468
    {
469 2
        $new = clone $this;
470 2
        $new->withoutToggleButton = $value;
471 2
        return $new;
472
    }
473
474 16
    public function begin(): ?string
475
    {
476 16
        parent::begin();
477
478 16
        $attributes = $this->attributes;
479 16
        $cardAttributes = $this->cardAttributes;
480 16
        $html = '';
481
482 16
        if (!array_key_exists('id', $attributes)) {
483 16
            $attributes['id'] = Html::generateId($this->autoIdPrefix) . '-modal';
484
        }
485
486
        /** @var string */
487 16
        $id = $attributes['id'];
488
489 16
        if ($this->withoutToggleButton === false) {
490 15
            $html .= $this->renderToggleButton($id) . "\n";
491
        }
492
493 16
        Html::addCssClass($attributes, $this->cardClass);
494 16
        Html::addCssClass($cardAttributes, $this->contentClass);
495
496 16
        $html .= Html::openTag('div', $attributes) . "\n"; // .modal
497 16
        $html .= $this->renderBackgroundTransparentOverlay() . "\n"; // .modal-background
498 16
        $html .= Html::openTag('div', $cardAttributes) . "\n"; // .modal-card
499 16
        $html .= $this->renderHeader();
500 16
        $html .= $this->renderBodyBegin() . "\n";
501
502 16
        return $html;
503
    }
504
505 16
    public function render(): string
506
    {
507 16
        $html = $this->renderBodyEnd() . "\n";
508 16
        $html .= $this->renderFooter() . "\n";
509 16
        $html .= Html::closeTag('div') . "\n"; // .modal-card
510 16
        $html .= Html::closeTag('div'); // .modal
511
512 16
        return $html;
513
    }
514
515
    /**
516
     * Renders the background transparent overlay.
517
     */
518 16
    private function renderBackgroundTransparentOverlay(): string
519
    {
520 16
        return Div::tag()
521 16
            ->class($this->backgroundClass)
522 16
            ->render();
523
    }
524
525
    /**
526
     * Renders begin body tag.
527
     */
528 16
    private function renderBodyBegin(): string
529
    {
530 16
        $bodyAttributes = $this->bodyAttributes;
531
532 16
        Html::addCssClass($bodyAttributes, $this->bodyClass);
533
534 16
        return Html::openTag('section', $bodyAttributes);
535
    }
536
537
    /**
538
     * Renders end body tag.
539
     */
540 16
    private function renderBodyEnd(): string
541
    {
542 16
        return Html::closeTag('section');
543
    }
544
545
    /**
546
     * Renders the close button.
547
     */
548 15
    private function renderCloseButton(): string
549
    {
550 15
        $closeButtonAttributes = $this->closeButtonAttributes;
551 15
        $closeButtonAttributes['aria-label'] = 'close';
552
553 15
        Html::addCssClass($closeButtonAttributes, $this->closeButtonCssClass);
554
555 15
        if ($this->closeButtonSize !== '') {
556 1
            Html::addCssClass($closeButtonAttributes, $this->closeButtonSize);
557
        }
558
559 15
        return Button::tag()
560 15
            ->attributes($closeButtonAttributes)
561 15
            ->render() . PHP_EOL;
562
    }
563
564
    /**
565
     * Renders the footer.
566
     */
567 16
    private function renderFooter(): string
568
    {
569 16
        $footer = $this->footer;
570 16
        $footerAttributes = $this->footerAttributes;
571
572 16
        if ($footer !== '') {
573 16
            $footer = PHP_EOL . $footer . PHP_EOL;
574
        }
575
576 16
        Html::addCssClass($footerAttributes, $this->footerClass);
577
578 16
        return CustomTag::name('footer')
579 16
            ->attributes($footerAttributes)
580 16
            ->content($footer)
581 16
            ->encode(false)
582 16
            ->render();
583
    }
584
585
    /**
586
     * Renders header.
587
     */
588 16
    private function renderHeader(): string
589
    {
590 16
        $content = '';
591 16
        $headerAttributes = $this->headerAttributes;
592 16
        $titleAttributes = $this->titleAttributes;
593
594 16
        Html::addCssClass($headerAttributes, $this->headerClass);
595 16
        Html::addCssClass($titleAttributes, $this->titleClass);
596
597 16
        $content .= P::tag()
598 16
            ->attributes($titleAttributes)
599 16
            ->content($this->title)
600 16
            ->render() . PHP_EOL;
601
602 16
        if ($this->withoutCloseButton === false) {
603 15
            $content .= $this->renderCloseButton();
604
        }
605
606 16
        return CustomTag::name('header')
607 16
            ->attributes($headerAttributes)
608 16
            ->content(PHP_EOL . $content)
609 16
            ->encode(false)
610 16
            ->render() . PHP_EOL;
611
    }
612
613
    /**
614
     * Renders the toggle button.
615
     */
616 15
    private function renderToggleButton(string $id): string
617
    {
618 15
        $toggleButtonAttributes = $this->toggleButtonAttributes;
619
620 15
        if (!array_key_exists('id', $toggleButtonAttributes)) {
621 15
            $toggleButtonAttributes['id'] = Html::generateId($this->autoIdPrefix) . '-button';
622
        }
623
624 15
        $toggleButtonAttributes['data-target'] = '#' . $id;
625 15
        $toggleButtonAttributes['aria-haspopup'] = 'true';
626
627 15
        if ($this->toggleButtonSize !== '') {
628 1
            Html::addCssClass($toggleButtonAttributes, $this->toggleButtonSize);
629
        }
630
631 15
        if ($this->toggleButtonColor !== '') {
632 1
            Html::addCssClass($toggleButtonAttributes, $this->toggleButtonColor);
633
        }
634
635 15
        Html::addCssClass($toggleButtonAttributes, $this->buttonClass);
636
637 15
        return Button::tag()
638 15
            ->attributes($toggleButtonAttributes)
639 15
            ->content($this->toggleButtonLabel)
640 15
            ->render();
641
    }
642
}
643