Passed
Pull Request — master (#59)
by Wilmer
02:07
created

ModalCard::withoutToggleButton()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 6
ccs 4
cts 4
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 InvalidArgumentException;
8
use JsonException;
9
use Yiisoft\Html\Html;
10
use Yiisoft\Html\Tag\Button;
11
use Yiisoft\Html\Tag\CustomTag;
12
use Yiisoft\Html\Tag\Div;
13
use Yiisoft\Html\Tag\P;
14
use Yiisoft\Widget\Widget;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Yiisoft\Yii\Bulma\Widget. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
15
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 array $bodyAttributes = [];
69
    private array $cardAttributes = [];
70
    private array $closeButtonAttributes = [];
71
    private string $closeButtonClass = 'button delete';
72
    private string $closeButtonSize = '';
73
    private string $footer = '';
74
    private array $headerAttributes = [];
75
    private array $footerAttributes = [];
76
    private string $modalCardBackgroundClass = 'modal-background';
77
    private string $modalCardBodyClass = 'modal-card-body';
78
    private string $modalCardButtonClass = 'button modal-button';
79
    private string $modalCardClass = 'modal';
80
    private string $modalCardContentClass = 'modal-card';
81
    private string $modalCardFootClass = 'modal-card-foot';
82
    private string $modalCardHeadClass = 'modal-card-head';
83
    private string $modalCardTitleClass = 'modal-card-title';
84
    private string $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
     * The HTML attributes.
95
     *
96
     * @param array $values Attribute values indexed by attribute names.
97
     *
98
     * @return self
99
     *
100
     * See {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
101
     */
102 2
    public function attributes(array $values): self
103
    {
104 2
        $new = clone $this;
105 2
        $new->attributes = $values;
106 2
        return $new;
107
    }
108
109
    /**
110
     * Returns a new instance with the specified prefix to the automatically generated widget IDs.
111
     *
112
     * @param string $value The prefix to the automatically generated widget IDs.
113
     *
114
     * @return self
115
     */
116 1
    public function autoIdPrefix(string $value): self
117
    {
118 1
        $new = clone $this;
119 1
        $new->autoIdPrefix = $value;
120 1
        return $new;
121
    }
122
123
    /**
124
     * Returns a new instance with the specified body attributes.
125
     *
126
     * @param array $value The body attributes.
127
     *
128
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
129
     *
130
     * @return self
131
     */
132 3
    public function bodyAttributes(array $value): self
133
    {
134 3
        $new = clone $this;
135 3
        $new->bodyAttributes = $value;
136
137 3
        return $new;
138
    }
139
140
    /**
141
     * Returns a new instance with the specified close button attributes.
142
     *
143
     * @param array $value The close button attributes.
144
     *
145
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
146
     *
147
     * @return self
148
     */
149 2
    public function closeButtonAttributes(array $value): self
150
    {
151 2
        $new = clone $this;
152 2
        $new->closeButtonAttributes = $value;
153
154 2
        return $new;
155
    }
156
157
    /**
158
     * Returns a new instance with the specified close button class.
159
     *
160
     * @param string $value The close button class.
161
     *
162
     * @return self
163
     */
164
    public function closeButtonClass(string $value): self
165
    {
166
        $new = clone $this;
167
        $new->closeButtonClass = $value;
168
169
        return $new;
170
    }
171
172
    /**
173
     * Returns a new instance with the specified close button size.
174
     *
175
     * @param string $value The close button size. Default setting empty normal.
176
     * Possible values are: ModalCard::SIZE_SMALL, ModalCard::SIZE_MEDIUM, ModalCard::SIZE_LARGE.
177
     *
178
     * @return self
179
     */
180 3
    public function closeButtonSize(string $value): self
181
    {
182 3
        if (!in_array($value, self::SIZE_ALL, true)) {
183 1
            $values = implode(' ', self::SIZE_ALL);
184 1
            throw new InvalidArgumentException("Invalid size. Valid values are: \"$values\".");
185
        }
186
187 2
        $new = clone $this;
188 2
        $new->closeButtonSize = $value;
189
190 2
        return $new;
191
    }
192
193
    /**
194
     * Returns a new instance with the specified card attributes.
195
     *
196
     * @param array $value The content attributes.
197
     *
198
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
199
     *
200
     * @return self
201
     */
202 2
    public function cardAttributes(array $value): self
203
    {
204 2
        $new = clone $this;
205 2
        $new->cardAttributes = $value;
206
207 2
        return $new;
208
    }
209
210
    /**
211
     * Returns a new instance with the specified footer content.
212
     *
213
     * @param string $value The footer content in the modal window.
214
     *
215
     * @return self
216
     */
217 16
    public function footer(string $value): self
218
    {
219 16
        $new = clone $this;
220 16
        $new->footer = $value;
221
222 16
        return $new;
223
    }
224
225
    /**
226
     * Returns a new instance with the specified footer attributes.
227
     *
228
     * @param array $value The footer attributes.
229
     *
230
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
231
     *
232
     * @return self
233
     */
234 2
    public function footerAttributes(array $value): self
235
    {
236 2
        $new = clone $this;
237 2
        $new->footerAttributes = $value;
238
239 2
        return $new;
240
    }
241
242
    /**
243
     * Returns a new instance with the specified header attributes.
244
     *
245
     * @param array $value The header attributes.
246
     *
247
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
248
     *
249
     * @return self
250
     */
251 2
    public function headerAttributes(array $value): self
252
    {
253 2
        $new = clone $this;
254 2
        $new->headerAttributes = $value;
255
256 2
        return $new;
257
    }
258
259
    /**
260
     * Returns a new instance with the specified ID of the widget.
261
     *
262
     * @param string|null $value The ID of the widget.
263
     *
264
     * @return self
265
     */
266 1
    public function id(?string $value): self
267
    {
268 1
        $new = clone $this;
269 1
        $new->attributes['id'] = $value;
270 1
        return $new;
271
    }
272
273
    /**
274
     * Returns a new instance with the specified modal card background class.
275
     *
276
     * @param string $value The modal card background class.
277
     *
278
     * @return self
279
     */
280 1
    public function modalCardBackgroundClass(string $value): self
281
    {
282 1
        $new = clone $this;
283 1
        $new->modalCardBackgroundClass = $value;
284
285 1
        return $new;
286
    }
287
288
    /**
289
     * Returns a new instance with the specified modal card body class.
290
     *
291
     * @param string $value The modal card body class.
292
     *
293
     * @return self
294
     */
295 1
    public function modalCardBodyClass(string $value): self
296
    {
297 1
        $new = clone $this;
298 1
        $new->modalCardBodyClass = $value;
299
300 1
        return $new;
301
    }
302
303
    /**
304
     * Returns a new instance with the specified modal card button class.
305
     *
306
     * @param string $value The modal card button class.
307
     *
308
     * @return self
309
     */
310 1
    public function modalCardButtonClass(string $value): self
311
    {
312 1
        $new = clone $this;
313 1
        $new->modalCardButtonClass = $value;
314
315 1
        return $new;
316
    }
317
318
    /**
319
     * Returns a new instance with the specified modal card class.
320
     *
321
     * @param string $value The modal card class.
322
     *
323
     * @return self
324
     */
325 1
    public function modalCardClass(string $value): self
326
    {
327 1
        $new = clone $this;
328 1
        $new->modalCardClass = $value;
329
330 1
        return $new;
331
    }
332
333
    /**
334
     * Returns a new instance with the specified modal card content class.
335
     *
336
     * @param string $value The modal card content class.
337
     *
338
     * @return self
339
     */
340 1
    public function modalCardContentClass(string $value): self
341
    {
342 1
        $new = clone $this;
343 1
        $new->modalCardContentClass = $value;
344
345 1
        return $new;
346
    }
347
348
    /**
349
     * Returns a new instance with the specified modal card footer class.
350
     *
351
     * @param string $value The modal card footer class.
352
     *
353
     * @return self
354
     */
355 1
    public function modalCardFootClass(string $value): self
356
    {
357 1
        $new = clone $this;
358 1
        $new->modalCardFootClass = $value;
359
360 1
        return $new;
361
    }
362
363
    /**
364
     * Returns a new instance with the specified modal card head class.
365
     *
366
     * @param string $value The modal card head class.
367
     *
368
     * @return self
369
     */
370 1
    public function modalCardHeadClass(string $value): self
371
    {
372 1
        $new = clone $this;
373 1
        $new->modalCardHeadClass = $value;
374
375 1
        return $new;
376
    }
377
378
    /**
379
     * Returns a new instance with the specified modal card title class.
380
     *
381
     * @param string $value The modal card title class.
382
     *
383
     * @return self
384
     */
385 1
    public function modalCardTitleClass(string $value): self
386
    {
387 1
        $new = clone $this;
388 1
        $new->modalCardTitleClass = $value;
389
390 1
        return $new;
391
    }
392
393
    /**
394
     * Returns a new instance with the specified title content.
395
     *
396
     * @param string $value The title content in the modal window.
397
     *
398
     * @return self
399
     */
400 16
    public function title(string $value): self
401
    {
402 16
        $new = clone $this;
403 16
        $new->title = $value;
404
405 16
        return $new;
406
    }
407
408
    /**
409
     * Returns a new instance with the specified title attributes.
410
     *
411
     * @param array $value The title attributes.
412
     *
413
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
414
     *
415
     * @return self
416
     */
417 2
    public function titleAttributes(array $value): self
418
    {
419 2
        $new = clone $this;
420 2
        $new->titleAttributes = $value;
421
422 2
        return $new;
423
    }
424
425
    /**
426
     * Returns a new instance with the specified toggle button attributes.
427
     *
428
     * @param array $value The toggle button attributes.
429
     *
430
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
431
     *
432
     * @return self
433
     */
434 2
    public function toggleButtonAttributes(array $value): self
435
    {
436 2
        $new = clone $this;
437 2
        $new->toggleButtonAttributes = $value;
438
439 2
        return $new;
440
    }
441
442
    /**
443
     * Returns a new instance with the specified toggle button color.
444
     *
445
     * @param string $value The toggle button color. Default seeting emtpy without any color.
446
     * Possible values are: ModalCard::COLOR_PRIMARY, ModalCard::COLOR_INFO, ModalCard::COLOR_SUCCESS,
447
     * ModalCard::COLOR_WARNING, ModalCard::COLOR_DANGER, ModalCard::COLOR_DARK
448
     *
449
     * @return self
450
     */
451 3
    public function toggleButtonColor(string $value): self
452
    {
453 3
        if (!in_array($value, self::COLOR_ALL, true)) {
454 1
            $values = implode(' ', self::COLOR_ALL);
455 1
            throw new InvalidArgumentException("Invalid color. Valid values are: \"$values\".");
456
        }
457
458 2
        $new = clone $this;
459 2
        $new->toggleButtonColor = $value;
460
461 2
        return $new;
462
    }
463
464
    /**
465
     * Returns a new instance with the specified ID of the toggle button.
466
     *
467
     * @param string|null $value The ID of the widget.
468
     *
469
     * @return self
470
     */
471 1
    public function toggleButtonId(?string $value): self
472
    {
473 1
        $new = clone $this;
474 1
        $new->toggleButtonAttributes['id'] = $value;
475 1
        return $new;
476
    }
477
478
    /**
479
     * Returns a new instance with the specified toggle button label.
480
     *
481
     * @param string $value The toggle button label.
482
     *
483
     * @return self
484
     */
485 2
    public function toggleButtonLabel(string $value): self
486
    {
487 2
        $new = clone $this;
488 2
        $new->toggleButtonLabel = $value;
489
490 2
        return $new;
491
    }
492
493
    /**
494
     * Returns a new instance with the specified toggle button size.
495
     *
496
     * @param string $value The toggle button size.
497
     *
498
     * @return self
499
     */
500 3
    public function toggleButtonSize(string $value): self
501
    {
502 3
        if (!in_array($value, self::SIZE_ALL, true)) {
503 1
            $values = implode('', self::SIZE_ALL);
504 1
            throw new InvalidArgumentException("Invalid size. Valid values are: \"$values\".");
505
        }
506
507 2
        $new = clone $this;
508 2
        $new->toggleButtonSize = $value;
509
510 2
        return $new;
511
    }
512
513
    /**
514
     * Returns a new instance with the specified options for rendering the close button tag.
515
     *
516
     * @param bool $value Whether the close button is disabled.
517
     *
518
     * @return self
519
     */
520 2
    public function withoutCloseButton(bool $value): self
521
    {
522 2
        $new = clone $this;
523 2
        $new->withoutCloseButton = $value;
524
525 2
        return $new;
526
    }
527
528
    /**
529
     * Returns a new instance with the disabled toggle button.
530
     *
531
     * @param bool $value Whether the toggle button is disabled.
532
     *
533
     * @return self
534
     */
535 2
    public function withoutToggleButton(bool $value): self
536
    {
537 2
        $new = clone $this;
538 2
        $new->withoutToggleButton = $value;
539
540 2
        return $new;
541
    }
542
543 15
    public function begin(): ?string
544
    {
545 15
        parent::begin();
546
547 15
        $attributes = $this->attributes;
548 15
        $cardAttributes = $this->cardAttributes;
549 15
        $html = '';
550
551 15
        if (!array_key_exists('id', $attributes)) {
552 15
            $attributes['id'] = Html::generateId($this->autoIdPrefix) . '-modal';
553
        }
554
555
        /** @var string */
556 15
        $id = $attributes['id'];
557
558 15
        if ($this->withoutToggleButton === false) {
559 14
            $html .= $this->renderToggleButton($id) . "\n";
560
        }
561
562 15
        Html::addCssClass($attributes, $this->modalCardClass);
563 15
        Html::addCssClass($cardAttributes, $this->modalCardContentClass);
564
565 15
        $html .= Html::openTag('div', $attributes) . "\n"; // .modal
566 15
        $html .= $this->renderBackgroundTransparentOverlay() . "\n"; // .modal-background
567 15
        $html .= Html::openTag('div', $cardAttributes) . "\n"; // .modal-card
568 15
        $html .= $this->renderHeader();
569 15
        $html .= $this->renderBodyBegin() . "\n";
570
571 15
        return $html;
572
    }
573
574 15
    protected function run(): string
575
    {
576 15
        $html = $this->renderBodyEnd() . "\n";
577 15
        $html .= $this->renderFooter() . "\n";
578 15
        $html .= Html::closeTag('div') . "\n"; // .modal-card
579 15
        $html .= Html::closeTag('div'); // .modal
580
581 15
        return $html;
582
    }
583
584
    /**
585
     * Renders the background transparent overlay.
586
     *
587
     * @throws JsonException
588
     *
589
     * @return string
590
     */
591 15
    private function renderBackgroundTransparentOverlay(): string
592
    {
593 15
        return Div::tag()->class($this->modalCardBackgroundClass)->render();
594
    }
595
596
    /**
597
     * Renders begin body tag.
598
     *
599
     * @throws JsonException
600
     *
601
     * @return string
602
     */
603 15
    private function renderBodyBegin(): string
604
    {
605 15
        $bodyAttributes = $this->bodyAttributes;
606
607 15
        Html::addCssClass($bodyAttributes, $this->modalCardBodyClass);
608
609 15
        return Html::openTag('section', $bodyAttributes);
610
    }
611
612
    /**
613
     * Renders end body tag.
614
     *
615
     * @throws JsonException
616
     *
617
     * @return string
618
     */
619 15
    private function renderBodyEnd(): string
620
    {
621 15
        return Html::closeTag('section');
622
    }
623
624
    /**
625
     * Renders the close button.
626
     *
627
     * @throws JsonException
628
     *
629
     * @return string
630
     */
631 14
    private function renderCloseButton(): string
632
    {
633 14
        $closeButtonAttributes = $this->closeButtonAttributes;
634 14
        $closeButtonAttributes['aria-label'] = 'close';
635
636 14
        Html::addCssClass($closeButtonAttributes, $this->closeButtonClass);
637
638 14
        if ($this->closeButtonSize !== '') {
639 1
            Html::addCssClass($closeButtonAttributes, $this->closeButtonSize);
640
        }
641
642 14
        return Button::tag()->attributes($closeButtonAttributes)->render() . PHP_EOL;
643
    }
644
645
    /**
646
     * Renders the footer.
647
     *
648
     * @throws JsonException
649
     *
650
     * @return string
651
     */
652 15
    private function renderFooter(): string
653
    {
654 15
        $footer = $this->footer;
655 15
        $footerAttributes = $this->footerAttributes;
656
657 15
        if ($footer !== '') {
658 15
            $footer = PHP_EOL . $footer . PHP_EOL;
659
        }
660
661 15
        Html::addCssClass($footerAttributes, $this->modalCardFootClass);
662
663 15
        return CustomTag::name('footer')->attributes($footerAttributes)->content($footer)->encode(false)->render();
664
    }
665
666
    /**
667
     * Renders header.
668
     *
669
     * @throws JsonException
670
     *
671
     * @return string
672
     */
673 15
    private function renderHeader(): string
674
    {
675 15
        $content = '';
676 15
        $headerAttributes = $this->headerAttributes;
677 15
        $titleAttributes = $this->titleAttributes;
678
679 15
        Html::addCssClass($headerAttributes, $this->modalCardHeadClass);
680 15
        Html::addCssClass($titleAttributes, $this->modalCardTitleClass);
681
682 15
        $content .= P::tag()->attributes($titleAttributes)->content($this->title)->render() . PHP_EOL;
683
684 15
        if ($this->withoutCloseButton === false) {
685 14
            $content .= $this->renderCloseButton();
686
        }
687
688 15
        return CustomTag::name('header')
689 15
            ->attributes($headerAttributes)
690 15
            ->content(PHP_EOL . $content)
691 15
            ->encode(false)
692 15
            ->render() . PHP_EOL;
693
    }
694
695
    /**
696
     * Renders the toggle button.
697
     *
698
     * @throws JsonException
699
     *
700
     * @return string
701
     */
702 14
    private function renderToggleButton(string $id): string
703
    {
704 14
        $toggleButtonAttributes = $this->toggleButtonAttributes;
705
706 14
        if (!array_key_exists('id', $toggleButtonAttributes)) {
707 14
            $toggleButtonAttributes['id'] = Html::generateId($this->autoIdPrefix) . '-button';
708
        }
709
710 14
        $toggleButtonAttributes['data-target'] = '#' . $id;
711 14
        $toggleButtonAttributes['aria-haspopup'] = 'true';
712
713 14
        if ($this->toggleButtonSize !== '') {
714 1
            Html::addCssClass($toggleButtonAttributes, $this->toggleButtonSize);
715
        }
716
717 14
        if ($this->toggleButtonColor !== '') {
718 1
            Html::addCssClass($toggleButtonAttributes, $this->toggleButtonColor);
719
        }
720
721 14
        Html::addCssClass($toggleButtonAttributes, $this->modalCardButtonClass);
722
723 14
        return Button::tag()->attributes($toggleButtonAttributes)->content($this->toggleButtonLabel)->render();
724
    }
725
}
726