Test Failed
Pull Request — master (#78)
by
unknown
02:39
created

Alert::encode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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