Passed
Push — master ( 261b33...7249d3 )
by Alexander
02:44
created

Modal   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 411
Duplicated Lines 0 %

Test Coverage

Coverage 83.04%

Importance

Changes 0
Metric Value
eloc 114
dl 0
loc 411
ccs 93
cts 112
cp 0.8304
rs 9.6
c 0
b 0
f 0
wmc 35

21 Methods

Rating   Name   Duplication   Size   Complexity  
A renderBodyEnd() 0 3 1
A headerOptions() 0 5 1
A footer() 0 5 1
A renderFooter() 0 8 2
A footerOptions() 0 5 1
A bodyOptions() 0 5 1
A closeButtonEnabled() 0 5 1
A title() 0 5 1
A toggleButton() 0 5 1
A titleOptions() 0 5 1
A options() 0 5 1
A closeButton() 0 5 1
A renderBodyBegin() 0 5 1
A toggleButtonEnabled() 0 5 1
A renderCloseButton() 0 12 2
A renderHeader() 0 20 4
A renderToggleButton() 0 10 2
B initOptions() 0 38 8
A size() 0 5 1
A run() 0 10 1
A begin() 0 17 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Bootstrap4;
6
7
use JsonException;
8
use Yiisoft\Arrays\ArrayHelper;
9
use Yiisoft\Html\Html;
10
11
use function array_merge;
12
13
/**
14
 * Modal renders a modal window that can be toggled by clicking on a button.
15
 *
16
 * The following example will show the content enclosed between the {@see begin()} and {@see end()} calls within the
17
 * modal window:
18
 *
19
 * ```php
20
 * Modal::widget()
21
 *     ->title('<h2>Hello world</h2>')
22
 *     ->toggleButton(['label' => 'click me'])
23
 *     ->begin();
24
 *
25
 * echo 'Say hello...';
26
 *
27
 * echo Modal::end();
28
 * ```
29
 */
30
class Modal extends Widget
31
{
32
    /**
33
     * The additional css class of large modal
34
     */
35
    public const SIZE_LARGE = 'modal-lg';
36
37
    /**
38
     * The additional css class of small modal
39
     */
40
    public const SIZE_SMALL = 'modal-sm';
41
42
    /**
43
     * The additional css class of default modal
44
     */
45
    public const SIZE_DEFAULT = '';
46
47
    private ?string $title = null;
48
    private array $titleOptions = [];
49
    private array $headerOptions = [];
50
    private array $bodyOptions = [];
51
    private ?string $footer = null;
52
    private array $footerOptions = [];
53
    private ?string $size = null;
54
    private array $closeButton = [];
55
    private bool $closeButtonEnabled = true;
56
    private array $toggleButton = [];
57
    private bool $toggleButtonEnabled = true;
58
    private array $options = [];
59
60 3
    public function begin(): ?string
61
    {
62 3
        parent::begin();
63
64 3
        if (!isset($this->options['id'])) {
65 3
            $this->options['id'] = "{$this->getId()}-modal";
66
        }
67
68 3
        $this->initOptions();
69
70
        return
71 3
            $this->renderToggleButton() . "\n" .
72 3
            Html::beginTag('div', $this->options) . "\n" .
73 3
            Html::beginTag('div', ['class' => 'modal-dialog ' . $this->size]) . "\n" .
74 3
            Html::beginTag('div', ['class' => 'modal-content']) . "\n" .
75 3
            $this->renderHeader() . "\n" .
76 3
            $this->renderBodyBegin() . "\n";
77
    }
78
79 3
    protected function run(): string
80
    {
81 3
        $this->registerPlugin('modal', $this->options);
82
83
        return
84 3
            "\n" . $this->renderBodyEnd() .
85 3
            "\n" . $this->renderFooter() .
86 3
            "\n" . Html::endTag('div') . // modal-content
87 3
            "\n" . Html::endTag('div') . // modal-dialog
88 3
            "\n" . Html::endTag('div');
89
    }
90
91
    /**
92
     * Renders the header HTML markup of the modal.
93
     *
94
     * @throws JsonException
95
     *
96
     * @return string the rendering result
97
     */
98 3
    protected function renderHeader(): string
99
    {
100 3
        $button = $this->renderCloseButton();
101
102 3
        if ($this->title !== null) {
103 2
            Html::addCssClass($this->titleOptions, ['widget' => 'modal-title']);
104 2
            $header = Html::tag('h5', $this->title, $this->titleOptions);
105
        } else {
106 1
            $header = '';
107
        }
108
109 3
        if ($button !== null) {
110 2
            $header .= "\n" . $button;
111 1
        } elseif ($header === '') {
112 1
            return '';
113
        }
114
115 2
        Html::addCssClass($this->headerOptions, ['widget' => 'modal-header']);
116
117 2
        return Html::tag('div', "\n" . $header . "\n", $this->headerOptions);
118
    }
119
120
    /**
121
     * Renders the opening tag of the modal body.
122
     *
123
     * @throws JsonException
124
     *
125
     * @return string the rendering result
126
     */
127 3
    protected function renderBodyBegin(): string
128
    {
129 3
        Html::addCssClass($this->bodyOptions, ['widget' => 'modal-body']);
130
131 3
        return Html::beginTag('div', $this->bodyOptions);
132
    }
133
134
    /**
135
     * Renders the closing tag of the modal body.
136
     *
137
     * @return string the rendering result
138
     */
139 3
    protected function renderBodyEnd(): string
140
    {
141 3
        return Html::endTag('div');
142
    }
143
144
    /**
145
     * Renders the HTML markup for the footer of the modal.
146
     *
147
     * @throws JsonException
148
     *
149
     * @return string the rendering result
150
     */
151 3
    protected function renderFooter(): ?string
152
    {
153 3
        if ($this->footer === null) {
154 1
            return null;
155
        }
156
157 2
        Html::addCssClass($this->footerOptions, ['widget' => 'modal-footer']);
158 2
        return Html::tag('div', "\n" . $this->footer . "\n", $this->footerOptions);
159
    }
160
161
    /**
162
     * Renders the toggle button.
163
     *
164
     * @throws JsonException
165
     *
166
     * @return string the rendering result
167
     */
168 3
    protected function renderToggleButton(): ?string
169
    {
170 3
        if ($this->toggleButtonEnabled === false) {
171 2
            return null;
172
        }
173
174 1
        $tag = ArrayHelper::remove($this->toggleButton, 'tag', 'button');
175 1
        $label = ArrayHelper::remove($this->toggleButton, 'label', 'Show');
176
177 1
        return Html::tag($tag, $label, $this->toggleButton);
178
    }
179
180
    /**
181
     * Renders the close button.
182
     *
183
     * @throws JsonException
184
     *
185
     * @return string the rendering result.
186
     */
187 3
    protected function renderCloseButton(): ?string
188
    {
189 3
        if ($this->closeButtonEnabled === false) {
190 1
            return null;
191
        }
192
193 2
        $tag = ArrayHelper::remove($this->closeButton, 'tag', 'button');
194 2
        $label = ArrayHelper::remove($this->closeButton, 'label', Html::tag('span', '&times;', [
195 2
            'aria-hidden' => 'true',
196
        ]));
197
198 2
        return Html::tag($tag, $label, $this->closeButton);
199
    }
200
201
    /**
202
     * Initializes the widget options.
203
     *
204
     * This method sets the default values for various options.
205
     */
206 3
    protected function initOptions(): void
207
    {
208 3
        $this->options = array_merge([
209 3
            'class' => 'fade',
210
            'role' => 'dialog',
211
            'tabindex' => -1,
212
            'aria-hidden' => 'true',
213 3
        ], $this->options);
214
215 3
        Html::addCssClass($this->options, ['widget' => 'modal']);
216
217 3
        if ($this->getEnableClientOptions() !== false) {
218
            $this->clientOptions(array_merge(['show' => false], $this->getClientOptions()));
219
        }
220
221 3
        $this->titleOptions = array_merge([
222 3
            'id' => $this->options['id'] . '-label',
223 3
        ], $this->titleOptions);
224
225 3
        if (!isset($this->options['aria-label'], $this->options['aria-labelledby']) && $this->title !== null) {
226 2
            $this->options['aria-labelledby'] = $this->titleOptions['id'];
227
        }
228
229 3
        if ($this->closeButtonEnabled !== false) {
230 2
            $this->closeButton = array_merge([
231 2
                'data-dismiss' => 'modal',
232
                'class' => 'close',
233
                'type' => 'button',
234 2
            ], $this->closeButton);
235
        }
236
237 3
        if ($this->toggleButton !== []) {
238 1
            $this->toggleButton = array_merge([
239 1
                'data-toggle' => 'modal',
240
                'type' => 'button',
241 1
            ], $this->toggleButton);
242 1
            if (!isset($this->toggleButton['data-target']) && !isset($this->toggleButton['href'])) {
243 1
                $this->toggleButton['data-target'] = '#' . $this->options['id'];
244
            }
245
        }
246 3
    }
247
248
    /**
249
     * Body options.
250
     *
251
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
252
     *
253
     * @param array $value
254
     *
255
     * @return $this
256
     */
257 1
    public function bodyOptions(array $value): self
258
    {
259 1
        $this->bodyOptions = $value;
260
261 1
        return $this;
262
    }
263
264
    /**
265
     * The options for rendering the close button tag.
266
     *
267
     * The close button is displayed in the header of the modal window. Clicking on the button will hide the modal
268
     * window. If {@see closeButtonEnabled} is false, no close button will be rendered.
269
     *
270
     * The following special options are supported:
271
     *
272
     * - tag: string, the tag name of the button. Defaults to 'button'.
273
     * - label: string, the label of the button. Defaults to '&times;'.
274
     *
275
     * The rest of the options will be rendered as the HTML attributes of the button tag. Please refer to the
276
     * [Modal plugin help](http://getbootstrap.com/javascript/#modals) for the supported HTML attributes.
277
     *
278
     * @param array $value
279
     *
280
     * @return $this
281
     */
282
    public function closeButton(array $value): self
283
    {
284
        $this->closeButton = $value;
285
286
        return $this;
287
    }
288
289
    /**
290
     * Enable/Disable close button.
291
     *
292
     * @param bool $value
293
     *
294
     * @return $this
295
     */
296 1
    public function closeButtonEnabled(bool $value): self
297
    {
298 1
        $this->closeButtonEnabled = $value;
299
300 1
        return $this;
301
    }
302
303
    /**
304
     * The footer content in the modal window.
305
     *
306
     * @param string|null $value
307
     *
308
     * @return $this
309
     */
310 2
    public function footer(?string $value): self
311
    {
312 2
        $this->footer = $value;
313
314 2
        return $this;
315
    }
316
317
    /**
318
     * Additional footer options.
319
     *
320
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
321
     */
322
    public function footerOptions(array $value): self
323
    {
324
        $this->footerOptions = $value;
325
326
        return $this;
327
    }
328
329
    /**
330
     * Additional header options.
331
     *
332
     * @param array $value
333
     *
334
     * @return $this
335
     *
336
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
337
     */
338
    public function headerOptions(array $value): self
339
    {
340
        $this->headerOptions = $value;
341
342
        return $this;
343
    }
344
345
    /**
346
     * @param array $value the HTML attributes for the widget container tag. The following special options are
347
     * recognized.
348
     *
349
     * @return $this
350
     *
351
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
352
     */
353
    public function options(array $value): self
354
    {
355
        $this->options = $value;
356
357
        return $this;
358
    }
359
360
    /**
361
     * The title content in the modal window.
362
     *
363
     * @param string|null $value
364
     *
365
     * @return $this
366
     */
367 2
    public function title(?string $value): self
368
    {
369 2
        $this->title = $value;
370
371 2
        return $this;
372
    }
373
374
    /**
375
     * Additional title options.
376
     *
377
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
378
     *
379
     * @param array $value
380
     *
381
     * @return $this
382
     */
383
    public function titleOptions(array $value): self
384
    {
385
        $this->titleOptions = $value;
386
387
        return $this;
388
    }
389
390
    /**
391
     * The options for rendering the toggle button tag.
392
     *
393
     * The toggle button is used to toggle the visibility of the modal window. If {@see toggleButtonEnabled} is false,
394
     * no toggle button will be rendered.
395
     *
396
     * The following special options are supported:
397
     *
398
     * - tag: string, the tag name of the button. Defaults to 'button'.
399
     * - label: string, the label of the button. Defaults to 'Show'.
400
     *
401
     * The rest of the options will be rendered as the HTML attributes of the button tag. Please refer to the
402
     * [Modal plugin help](http://getbootstrap.com/javascript/#modals) for the supported HTML attributes.
403
     *
404
     * @param array $value
405
     *
406
     * @return $this
407
     */
408 1
    public function toggleButton(array $value): self
409
    {
410 1
        $this->toggleButton = $value;
411
412 1
        return $this;
413
    }
414
415
    /**
416
     * Enable/Disable toggle button.
417
     *
418
     * @param bool $value
419
     *
420
     * @return $this
421
     */
422 2
    public function toggleButtonEnabled(bool $value): self
423
    {
424 2
        $this->toggleButtonEnabled = $value;
425
426 2
        return $this;
427
    }
428
429
    /**
430
     * The modal size. Can be {@see SIZE_LARGE} or {@see SIZE_SMALL}, or null for default.
431
     *
432
     * @param string|null $value
433
     *
434
     * @return $this
435
     */
436
    public function size(?string $value): self
437
    {
438
        $this->size = $value;
439
440
        return $this;
441
    }
442
}
443