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

Alert::iconClass()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

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