Passed
Push — master ( ba2057...8d3da4 )
by Alexander
02:54
created

Modal::renderBodyEnd()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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