Passed
Pull Request — master (#44)
by Wilmer
02:38
created

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