Completed
Push — master ( 92c51b...1c2d7c )
by Wilmer
02:09 queued 02:09
created

Alert::initOptions()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 10
c 1
b 0
f 0
nc 4
nop 0
dl 0
loc 19
ccs 10
cts 10
cp 1
crap 3
rs 9.9332

1 Method

Rating   Name   Duplication   Size   Complexity  
A Alert::closeButtonTag() 0 6 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Bootstrap5;
6
7
use JsonException;
8
use Yiisoft\Arrays\ArrayHelper;
9
use Yiisoft\Html\Html;
10
11
use function array_merge;
12
13
/**
14
 * Alert renders an alert bootstrap component.
15
 *
16
 * For example,
17
 *
18
 * ```php
19
 * echo Alert::widget()
20
 *     ->options([
21
 *         'class' => 'alert-info',
22
 *     ])
23
 *     ->body('Say hello...');
24
 * ```
25
 *
26
 * @link https://getbootstrap.com/docs/5.0/components/alerts/
27
 */
28
final class Alert extends Widget
29
{
30
    private string $body = '';
31
    private ?string $header = null;
32
    private array $headerOptions = [];
33
    private string $headerTag = 'h4';
34
    private ?array $closeButton = [
35
        'class' => 'btn-close',
36
    ];
37
    private string $closeButtonTag = 'button';
38
    private bool $encode = false;
39
    private array $options = [];
40
    private array $classNames = [];
41
    private bool $fade = false;
42
43 17
    public function getId(?string $suffix = '-alert'): ?string
44
    {
45 17
        return $this->options['id'] ?? parent::getId($suffix);
46
    }
47
48 17
    protected function run(): string
49
    {
50 17
        $options = $this->prepareOptions();
51 17
        $tag = ArrayHelper::remove($options, 'tag', 'div');
52 17
        $closeButtonOutside = $this->closeButton['outside'] ?? false;
53
54 17
        $content = Html::openTag($tag, $options);
55 17
        $content .= $this->renderHeader();
56 17
        $content .= $this->encode ? Html::encode($this->body) : $this->body;
57
58 17
        if (!$closeButtonOutside) {
59 17
            $content .= $this->renderCloseButton(false);
60
        }
61
62 17
        $content .= Html::closeTag($tag);
63
64 17
        return $content;
65
    }
66
67
    /**
68
     * The body content in the alert component. Alert widget will also be treated as the body content, and will be
69
     * rendered before this.
70
     *
71
     * @param string $value
72
     *
73
     * @return self
74
     */
75 17
    public function body(string $value): self
76
    {
77 17
        $new = clone $this;
78 17
        $new->body = $value;
79
80 17
        return $new;
81
    }
82
83
    /**
84
     * The header content in alert component
85
     *
86
     * @param string|null $header
87
     *
88
     * @return self
89
     */
90 1
    public function header(?string $header): self
91
    {
92 1
        $new = clone $this;
93 1
        $new->header = $header;
94
95 1
        return $new;
96
    }
97
98
    /**
99
     * The HTML attributes for the widget header tag. The following special options are recognized.
100
     *
101
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
102
     *
103
     * @param array $options
104
     *
105
     * @return self
106
     */
107 1
    public function headerOptions(array $options): self
108
    {
109 1
        $new = clone $this;
110 1
        $new->headerOptions = $options;
111
112 1
        return $new;
113
    }
114
115
    /**
116
     * Set tag name for header
117
     *
118
     * @param string $tag
119
     *
120
     * @return self
121
     */
122 1
    public function headerTag(string $tag): self
123
    {
124 1
        $new = clone $this;
125 1
        $new->headerTag = $tag;
126
127 1
        return $new;
128
    }
129
130
    /**
131
     * The options for rendering the close button tag.
132
     *
133
     * The close button is displayed in the header of the modal window. Clicking on the button will hide the modal
134
     * window. If {@see closeButtonEnabled} is false, no close button will be rendered.
135
     *
136
     * The following special options are supported:
137
     *
138
     * - tag: string, the tag name of the button. Defaults to 'button'.
139
     * - label: string, the label of the button. Defaults to '&times;'.
140
     *
141
     * The rest of the options will be rendered as the HTML attributes of the button tag.
142
     *
143
     * Please refer to the [Alert documentation](http://getbootstrap.com/components/#alerts) for the supported HTML
144
     * attributes.
145
     *
146
     * @param array $value
147
     *
148
     * @return self
149
     */
150 1
    public function closeButton(array $value): self
151
    {
152 1
        $new = clone $this;
153 1
        $new->closeButton = $value;
154
155 1
        return $new;
156
    }
157
158
    /**
159
     * Disable close button.
160
     *
161
     * @return self
162
     */
163 10
    public function withoutCloseButton(): self
164
    {
165 10
        $new = clone $this;
166 10
        $new->closeButton = null;
167
168 10
        return $new;
169
    }
170
171
    /**
172
     * Set close button tag
173
     *
174
     * @param string $tag
175
     *
176
     * @return self
177
     */
178 1
    public function closeButtonTag(string $tag): self
179
    {
180 1
        $new = clone $this;
181 1
        $new->closeButtonTag = $tag;
182
183 1
        return $new;
184
    }
185
186
    /**
187
     * The HTML attributes for the widget container tag. The following special options are recognized.
188
     *
189
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
190
     *
191
     * @param array $value
192
     *
193
     * @return self
194
     */
195 3
    public function options(array $value): self
196
    {
197 3
        $new = clone $this;
198 3
        $new->options = $value;
199
200 3
        return $new;
201
    }
202
203
    /**
204
     * Enable/Disable encode body
205
     *
206
     * @param bool $encode
207
     *
208
     * @return self
209
     */
210
    public function encode(bool $encode = true): self
211
    {
212
        $new = clone $this;
213
        $new->encode = $encode;
214
215
        return $new;
216
    }
217
218
    /**
219
     * Enable/Disable dissmiss animation
220
     *
221
     * @param bool $fade
222
     *
223
     * @return self
224
     */
225 2
    public function fade(bool $fade = true): self
226
    {
227 2
        $new = clone $this;
228 2
        $new->fade = $fade;
229
230 2
        return $new;
231
    }
232
233
    /**
234
     * Set type of alert, 'alert-success', 'alert-danger', 'custom-alert' etc
235
     *
236
     * @param string $type
237
     *
238
     * @return self
239
     */
240 9
    public function addClassNames(string ...$classNames): self
241
    {
242 9
        $new = clone $this;
243 9
        $new->classNames = array_filter($classNames, static fn ($name) => $name !== '');
244
245 9
        return $new;
246
    }
247
248
    /**
249
     * Short method for primary alert type
250
     *
251
     * @return self
252
     */
253 1
    public function primary(): self
254
    {
255 1
        return $this->addClassNames('alert-primary');
256
    }
257
258
    /**
259
     * Short method for secondary alert type
260
     *
261
     * @return self
262
     */
263 1
    public function secondary(): self
264
    {
265 1
        return $this->addClassNames('alert-secondary');
266
    }
267
268
    /**
269
     * Short method for success alert type
270
     *
271
     * @return self
272
     */
273 1
    public function success(): self
274
    {
275 1
        return $this->addClassNames('alert-success');
276
    }
277
278
    /**
279
     * Short method for danger alert type
280
     *
281
     * @return self
282
     */
283 1
    public function danger(): self
284
    {
285 1
        return $this->addClassNames('alert-danger');
286
    }
287
288
    /**
289
     * Short method for warning alert type
290
     *
291
     * @return self
292
     */
293 1
    public function warning(): self
294
    {
295 1
        return $this->addClassNames('alert-warning');
296
    }
297
298
    /**
299
     * Short method for info alert type
300
     *
301
     * @return self
302
     */
303 1
    public function info(): self
304
    {
305 1
        return $this->addClassNames('alert-info');
306
    }
307
308
    /**
309
     * Short method for light alert type
310
     *
311
     * @return self
312
     */
313 1
    public function light(): self
314
    {
315 1
        return $this->addClassNames('alert-light');
316
    }
317
318
    /**
319
     * Short method for dark alert type
320
     *
321
     * @return self
322
     */
323 1
    public function dark(): self
324
    {
325 1
        return $this->addClassNames('alert-dark');
326
    }
327
328
    /**
329
     * Renders the close button.
330
     *
331
     * @throws JsonException
332
     *
333
     * @return string the rendering result
334
     */
335 17
    public function renderCloseButton(bool $outside = true): ?string
336
    {
337 17
        if ($this->closeButton === null) {
338 10
            return null;
339
        }
340
341 7
        $options = array_merge(
342 7
            $this->closeButton,
343
            [
344 7
                'aria-label' => 'Close',
345
                'data-bs-dismiss' => 'alert',
346
            ],
347
        );
348 7
        $label = ArrayHelper::remove($options, 'label', '');
349 7
        $encode = ArrayHelper::remove($options, 'encode', $this->encode);
350
351 7
        unset($options['outside']);
352
353 7
        if ($this->closeButtonTag === 'button' && !isset($options['type'])) {
354 7
            $options['type'] = 'button';
355
        }
356
357 7
        if ($outside) {
358
            $options['data-bs-target'] = '#' . $this->getId();
359
        }
360
361 7
        return Html::tag($this->closeButtonTag, $label, $options)->encode($encode)->render();
362
    }
363
364
    /**
365
     * Render header tag
366
     *
367
     * @return string|null
368
     */
369 17
    private function renderHeader(): ?string
370
    {
371 17
        if ($this->header === null) {
372 16
            return null;
373
        }
374
375 1
        $options = $this->headerOptions;
376 1
        $encode = ArrayHelper::remove($options, 'encode', true);
377
378 1
        Html::addCssClass($options, ['alert-heading']);
379
380 1
        return Html::tag($this->headerTag, $this->header, $options)->encode($encode)->render();
381
    }
382
383
    /**
384
     * Prepare the widget options.
385
     *
386
     * This method returns the default values for various options.
387
     *
388
     * @return array
389
     */
390 17
    private function prepareOptions(): array
391
    {
392 17
        $options = $this->options;
393 17
        $options['id'] = $this->getId();
394 17
        $classNames = array_merge(['alert'], $this->classNames);
395
396 17
        if ($this->closeButton !== null) {
397 7
            $classNames[] = 'alert-dismissible';
398
        }
399
400 17
        if ($this->fade) {
401 2
            $classNames[] = 'fade show';
402
        }
403
404 17
        Html::addCssClass($options, $classNames);
405
406 17
        if (!isset($options['role'])) {
407 17
            $options['role'] = 'alert';
408
        }
409
410 17
        return $options;
411
    }
412
}
413