Test Setup Failed
Pull Request — master (#61)
by Wilmer
02:18
created

ModalCard::closeButtonClass()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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