Passed
Pull Request — master (#44)
by Wilmer
07:14
created

Alert::bodyTag()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 9
ccs 6
cts 6
cp 1
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Widgets;
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\Widget\Widget;
13
14
/**
15
 * Alert renders an alert component.
16
 *
17
 * @link https://getbootstrap.com/docs/5.0/components/alerts/
18
 * @link https://bulma.io/documentation/elements/notification/
19
 * @link https://tailwindui.com/components/application-ui/feedback/alerts
20
 */
21
final class Alert extends Widget
22
{
23
    private array $attributes = [];
24
    private array $buttonAttributes = [];
25
    private string $buttonClass = '';
26
    private string $buttonLabel = '&times;';
27
    private string $buttonOnClick = '';
28
    private string $body = '';
29
    private array $bodyAttributes = [];
30
    private string $bodyClass = '';
31
    private bool $bodyContainer = false;
32
    private array $bodyContainerAttributes = [];
33
    private string $bodyContainerClass = '';
34
    /** @var non-empty-string */
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string.
Loading history...
35
    private string $bodyTag = 'span';
36
    private string $class = '';
37
    private ?string $id = null;
38
    private string $header = '';
39
    private array $headerAttributes = [];
40
    private string $headerClass = '';
41
    private bool $headerContainer = false;
42
    private array $headerContainerAttributes = [];
43
    private string $headerContainerClass = '';
44
    /** @var non-empty-string */
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string.
Loading history...
45
    private string $headerTag = 'span';
46
    private array $iconAttributes = [];
47
    private array $iconContainerAttributes = [];
48
    private string $iconContainerClass = '';
49
    private string $iconClass = '';
50
    private string $iconText = '';
51
    private string $layoutHeader = '';
52
    private string $layoutBody = '{body}{button}';
53
    private array $parts = [];
54
55
    /**
56
     * The HTML attributes for widget. The following special options are recognized.
57
     *
58
     * @param array $value
59
     *
60
     * @return static
61
     */
62
    public function attributes(array $value): self
63
    {
64
        $new = clone $this;
65
        $new->attributes = $value;
66
        return $new;
67
    }
68
69
    /**
70
     * The message content in the alert component. Alert widget will also be treated as the message content, and will be
71
     * rendered before this.
72
     *
73
     * @param string $value
74
     *
75
     * @return static
76
     */
77 19
    public function body(string $value): self
78
    {
79 19
        $new = clone $this;
80 19
        $new->body = $value;
81 19
        return $new;
82
    }
83
84
    /**
85
     * The attributes for rendering message content in the alert component.
86
     *
87
     * @param array $value
88
     *
89
     * @return static
90
     *
91
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
92
     */
93 1
    public function bodyAttributes(array $value): self
94
    {
95 1
        $new = clone $this;
96 1
        $new->bodyAttributes = $value;
97 1
        return $new;
98
    }
99
100
    /**
101
     * The CSS class for the alert panel body.
102
     *
103
     * @param string $value
104
     *
105
     * @return static
106
     */
107 7
    public function bodyClass(string $value): self
108
    {
109 7
        $new = clone $this;
110 7
        $new->bodyClass = $value;
111 7
        return $new;
112
    }
113
114
    /**
115
     * Allows you to add a div tag to the alert panel body.
116
     *
117
     * @param bool $value
118
     *
119
     * @return static
120
     */
121 6
    public function bodyContainer(bool $value = true): self
122
    {
123 6
        $new = clone $this;
124 6
        $new->bodyContainer = $value;
125 6
        return $new;
126
    }
127
128
    /**
129
     * The attributes for rendering the panel alert body.
130
     *
131
     * @param array $value
132
     *
133
     * @return static
134
     *
135
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
136
     */
137 1
    public function bodyContainerAttributes(array $value): self
138
    {
139 1
        $new = clone $this;
140 1
        $new->bodyContainerAttributes = $value;
141 1
        return $new;
142
    }
143
144
    /**
145
     * The CSS class for the alert panel body.
146
     *
147
     * @param string $value
148
     *
149
     * @return static
150
     */
151 6
    public function bodyContainerClass(string $value): self
152
    {
153 6
        $new = clone $this;
154 6
        $new->bodyContainerClass = $value;
155 6
        return $new;
156
    }
157
158
    /**
159
     * Set tag name for the alert panel body.
160
     *
161
     * @param string $value
162
     *
163
     * @return static
164
     * @throws InvalidArgumentException
165
     *
166
     */
167 2
    public function bodyTag(string $value): self
168
    {
169 2
        if (empty($value)) {
170 1
            throw new InvalidArgumentException('Body tag must be a string and cannot be empty.');
171
        }
172
173 1
        $new = clone $this;
174 1
        $new->bodyTag = $value;
175 1
        return $new;
176
    }
177
178
    /**
179
     * The attributes for rendering the close button tag.
180
     *
181
     * The close button is displayed in the header of the modal window. Clicking on the button will hide the modal
182
     * window.
183
     *
184
     * If {@see closeButtonEnabled} is false, no close button will be rendered.
185
     *
186
     * The rest of the options will be rendered as the HTML attributes of the button tag.
187
     *
188
     * @param array $value
189
     *
190
     * @return static
191
     *
192
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
193
     */
194 4
    public function buttonAttributes(array $value): self
195
    {
196 4
        $new = clone $this;
197 4
        $new->buttonAttributes = $value;
198 4
        return $new;
199
    }
200
201
    /**
202
     * The CSS class for the close button.
203
     *
204
     * @param string $value
205
     *
206
     * @return static
207
     */
208 13
    public function buttonClass(string $value): self
209
    {
210 13
        $new = clone $this;
211 13
        $new->buttonClass = $value;
212 13
        return $new;
213
    }
214
215
    /**
216
     * The label for the close button.
217
     *
218
     * @param string $value
219
     *
220
     * @return static
221
     */
222 4
    public function buttonLabel(string $value = ''): self
223
    {
224 4
        $new = clone $this;
225 4
        $new->buttonLabel = $value;
226 4
        return $new;
227
    }
228
229
    /**
230
     * The onclick JavaScript for the close button.
231
     *
232
     * @param string $value
233
     *
234
     * @return static
235
     */
236 11
    public function buttonOnClick(string $value = ''): self
237
    {
238 11
        $new = clone $this;
239 11
        $new->buttonOnClick = $value;
240 11
        return $new;
241
    }
242
243
    /**
244
     * Set attribute class for widget.
245
     *
246
     * @param string $value
247
     *
248
     * @return static
249
     */
250 12
    public function class(string $value): self
251
    {
252 12
        $new = clone $this;
253 12
        $new->class = $value;
254 12
        return $new;
255
    }
256
257 19
    public function id(?string $value): self
258
    {
259 19
        $new = clone $this;
260 19
        $new->id = $value;
261 19
        return $new;
262
    }
263
    /**
264
     * The header content in the alert component. Alert widget will also be treated as the header content, and will be
265
     * rendered before this.
266
     *
267
     * @param string $value
268
     *
269
     * @return static
270
     */
271 4
    public function header(string $value): self
272
    {
273 4
        $new = clone $this;
274 4
        $new->header = $value;
275 4
        return $new;
276
    }
277
278
    /**
279
     * The attributes for rendering the header content in the alert component.
280
     *
281
     * @param array $value
282
     *
283
     * @return static
284
     *
285
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
286
     */
287 1
    public function headerAttributes(array $value): self
288
    {
289 1
        $new = clone $this;
290 1
        $new->headerAttributes = $value;
291 1
        return $new;
292
    }
293
294
    /**
295
     * The CSS class for the alert panel header.
296
     *
297
     * @param string $value
298
     *
299
     * @return static
300
     */
301 2
    public function headerClass(string $value): self
302
    {
303 2
        $new = clone $this;
304 2
        $new->headerClass = $value;
305 2
        return $new;
306
    }
307
308
    /**
309
     * Allows you to add a div tag to the alert panel header.
310
     *
311
     * @param bool $value
312
     *
313
     * @return static
314
     */
315 2
    public function headerContainer(bool $value = true): self
316
    {
317 2
        $new = clone $this;
318 2
        $new->headerContainer = $value;
319 2
        return $new;
320
    }
321
322
    /**
323
     * The CSS class for the alert panel header.
324
     *
325
     * @param string $value
326
     *
327
     * @return static
328
     */
329 1
    public function headerContainerClass(string $value): self
330
    {
331 1
        $new = clone $this;
332 1
        $new->headerContainerClass = $value;
333 1
        return $new;
334
    }
335
336
    /**
337
     * The attributes for rendering the panel alert header.
338
     *
339
     * @param array $value
340
     *
341
     * @return static
342
     *
343
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
344
     */
345 1
    public function headerContainerAttributes(array $value): self
346
    {
347 1
        $new = clone $this;
348 1
        $new->headerContainerAttributes = $value;
349 1
        return $new;
350
    }
351
352
    /**
353
     * Set tag name for the alert panel header.
354
     *
355
     * @param string $value
356
     *
357
     * @return static
358
     * @throws InvalidArgumentException
359
     *
360
     */
361 2
    public function headerTag(string $value): self
362
    {
363 2
        if (empty($value)) {
364 1
            throw new InvalidArgumentException('Header tag must be a string and cannot be empty.');
365
        }
366
367 1
        $new = clone $this;
368 1
        $new->headerTag = $value;
369 1
        return $new;
370
    }
371
372
    /**
373
     * The attributes for rendering the `<i>` tag for icons alerts.
374
     *
375
     * @param array $value
376
     *
377
     * @return static
378
     *
379
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
380
     */
381 2
    public function iconAttributes(array $value): self
382
    {
383 2
        $new = clone $this;
384 2
        $new->iconAttributes = $value;
385 2
        return $new;
386
    }
387
388
    /**
389
     * Set icon css class in the alert component.
390
     *
391
     * @param string $value
392
     *
393
     * @return static
394
     */
395 5
    public function iconClass(string $value): self
396
    {
397 5
        $new = clone $this;
398 5
        $new->iconClass = $value;
399 5
        return $new;
400
    }
401
402
    /**
403
     * The attributes for rendering the container `<i>` tag.
404
     *
405
     * The rest of the options will be rendered as the HTML attributes of the `<i>` tag.
406
     *
407
     * @param array $value
408
     *
409
     * @return static
410
     *
411
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
412
     */
413 1
    public function iconContainerAttributes(array $value): self
414
    {
415 1
        $new = clone $this;
416 1
        $new->iconContainerAttributes = $value;
417 1
        return $new;
418
    }
419
420
    /**
421
     * The CSS class for the container `<i>` tag.
422
     *
423
     * @param string $value
424
     *
425
     * @return static
426
     */
427 2
    public function iconContainerClass(string $value): self
428
    {
429 2
        $new = clone $this;
430 2
        $new->iconContainerClass = $value;
431 2
        return $new;
432
    }
433
434
    /**
435
     * Set icon text in the alert component.
436
     *
437
     * @param string $value
438
     *
439
     * @return static
440
     */
441 3
    public function iconText(string $value): self
442
    {
443 3
        $new = clone $this;
444 3
        $new->iconText = $value;
445 3
        return $new;
446
    }
447
448
    /**
449
     * Set layout the alert panel body.
450
     *
451
     * @param string $value
452
     *
453
     * @return static
454
     */
455 8
    public function layoutBody(string $value): self
456
    {
457 8
        $new = clone $this;
458 8
        $new->layoutBody = $value;
459 8
        return $new;
460
    }
461
462
    /**
463
     * Set layout the alert panel header.
464
     *
465
     * @param string $value
466
     *
467
     * @return static
468
     */
469 6
    public function layoutHeader(string $value): self
470
    {
471 6
        $new = clone $this;
472 6
        $new->layoutHeader = $value;
473 6
        return $new;
474
    }
475
476 19
    protected function run(): string
477
    {
478 19
        $new = clone $this;
479 19
        return $new->renderAlert($new);
480
    }
481
482
    /**
483
     * Render Alert.
484
     */
485 19
    private function renderAlert(self $new): string
486
    {
487 19
        $new->attributes['role'] = 'alert';
488 19
        $new->attributes['id'] ??= $new->id ?? Html::generateId('alert-');
489
490 19
        if (!isset($new->parts['{button}'])) {
491 19
            $new->renderCloseButton($new);
492
        }
493
494 19
        if (!isset($new->parts['{icon}'])) {
495 19
            $new->renderIcon($new);
496
        }
497
498 19
        if (!isset($new->parts['{body}'])) {
499 19
            $new->renderBody($new);
500
        }
501
502 19
        if (!isset($new->parts['{header}'])) {
503 19
            $new->renderHeader($new);
504
        }
505
506 19
        $contentAlert = $new->renderPanelHeader($new) . PHP_EOL . $new->renderPanelBody($new);
507
508 19
        if ($new->class !== '') {
509 12
            Html::addCssClass($new->attributes, $new->class);
510
        }
511
512 19
        return $new->body !== ''
513 19
            ? Div::tag()
514 19
                ->attributes($new->attributes)
515 19
                ->content(PHP_EOL . trim($contentAlert) . PHP_EOL)
516 19
                ->encode(false)
517 19
                ->render()
518 19
            : '';
519
    }
520
521
    /**
522
     * Renders close button.
523
     */
524 19
    private function renderCloseButton(self $new): void
525
    {
526 19
        $new->parts['{button}'] = '';
527
528 19
        if ($new->buttonOnClick !== '') {
529 7
            $new->buttonAttributes['onclick'] = $new->buttonOnClick;
530
        }
531
532 19
        if ($new->buttonClass !== '') {
533 13
            Html::addCssClass($new->buttonAttributes, $new->buttonClass);
534
        }
535
536 19
        $new->parts['{button}'] = PHP_EOL .
537 19
            Button::tag()
538 19
                ->attributes($new->buttonAttributes)
539 19
                ->content($new->buttonLabel)
540 19
                ->encode(false)
541 19
                ->type('button')
542 19
                ->render();
543 19
    }
544
545
    /**
546
     * Render icon.
547
     */
548 19
    private function renderIcon(self $new): void
549
    {
550 19
        if ($new->iconClass !== '') {
551 5
            Html::addCssClass($new->iconAttributes, $new->iconClass);
552
        }
553
554 19
        if ($new->iconContainerClass !== '') {
555 2
            Html::addCssClass($new->iconContainerAttributes, $new->iconContainerClass);
556
        }
557
558 19
        $icon = CustomTag::name('i')->attributes($new->iconAttributes)->content($new->iconText)->render();
559
560 19
        $new->parts['{icon}'] = PHP_EOL .
561 19
            Div::tag()
562 19
                ->attributes($new->iconContainerAttributes)
563 19
                ->content($icon)
564 19
                ->encode(false)
565 19
                ->render() . PHP_EOL;
566 19
    }
567
568
    /**
569
     * Render the alert message.
570
     */
571 19
    private function renderBody(self $new): void
572
    {
573 19
        if ($new->bodyClass !== '') {
574 7
            Html::addCssClass($new->bodyAttributes, $new->bodyClass);
575
        }
576
577 19
        $new->parts['{body}'] = CustomTag::name($new->bodyTag)
578 19
            ->attributes($new->bodyAttributes)
579 19
            ->content($new->body)
580 19
            ->encode(false)
581 19
            ->render();
582 19
    }
583
584
    /**
585
     * Render the alert message header.
586
     */
587 19
    private function renderHeader(self $new): void
588
    {
589 19
        if ($new->headerClass !== '') {
590 2
            Html::addCssClass($new->headerAttributes, $new->headerClass);
591
        }
592
593 19
        $new->parts['{header}'] = CustomTag::name($new->headerTag)
594 19
            ->attributes($new->headerAttributes)
595 19
            ->content($new->header)
596 19
            ->encode(false)
597 19
            ->render();
598 19
    }
599
600
    /**
601
     * Render the alert panel header.
602
     */
603 19
    private function renderPanelHeader(self $new): string
604
    {
605 19
        $headerHtml = trim(strtr($new->layoutHeader, $new->parts));
606
607 19
        if ($new->headerContainerClass !== '') {
608 1
            Html::addCssClass($new->headerContainerAttributes, $new->headerContainerClass);
609
        }
610
611 19
        if ($new->headerContainer && $headerHtml !== '') {
612 2
            $headerHtml = Div::tag()
613 2
                ->attributes($new->headerContainerAttributes)
614 2
                ->content(PHP_EOL . $headerHtml . PHP_EOL)
615 2
                ->encode(false)
616 2
                ->render();
617
        }
618
619 19
        return $headerHtml;
620
    }
621
622
    /**
623
     * Render the alert panel body.
624
     */
625 19
    private function renderPanelBody(self $new): string
626
    {
627 19
        $bodyHtml = trim(strtr($new->layoutBody, $new->parts));
628
629 19
        if ($new->bodyContainerClass !== '') {
630 6
            Html::addCssClass($new->bodyContainerAttributes, $new->bodyContainerClass);
631
        }
632
633 19
        if ($new->bodyContainer) {
634 6
            $bodyHtml = Div::tag()
635 6
                ->attributes($new->bodyContainerAttributes)
636 6
                ->content(PHP_EOL . $bodyHtml . PHP_EOL)
637 6
                ->encode(false)
638 6
                ->render();
639
        }
640
641 19
        return $bodyHtml;
642
    }
643
}
644