Passed
Push — master ( e80b4e...741461 )
by Alexander
02:19
created

ModalCard::withoutToggleButton()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 6
ccs 4
cts 4
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\Bulma;
6
7
use InvalidArgumentException;
8
use JsonException;
9
use Yiisoft\Html\Html;
10
11
/**
12
 * ModalCard renders a modal window that can be toggled by clicking on a button.
13
 *
14
 * The following example will show the content enclosed between the {@see begin()} and {@see end()} calls within the
15
 * modal window:
16
 *
17
 * ```php
18
 * echo ModalCard::widget()
19
 *     ->title('Modal title')
20
 *     ->footer(
21
 *         Html::button('Cancel', ['class' => 'button'])
22
 *     )
23
 *     ->begin();
24
 *
25
 * echo 'Say hello...';
26
 *
27
 * echo ModalCard::end();
28
 * ```
29
 *
30
 * @link https://bulma.io/documentation/components/modal/
31
 */
32
final class ModalCard extends Widget
33
{
34
    public const SIZE_SMALL = 'is-small';
35
    public const SIZE_MEDIUM = 'is-medium';
36
    public const SIZE_LARGE = 'is-large';
37
    private const SIZE_ALL = [
38
        self::SIZE_SMALL,
39
        self::SIZE_MEDIUM,
40
        self::SIZE_LARGE,
41
    ];
42
43
    public const COLOR_PRIMARY = 'is-primary';
44
    public const COLOR_LINK = 'is-link';
45
    public const COLOR_INFO = 'is-info';
46
    public const COLOR_SUCCESS = 'is-success';
47
    public const COLOR_WARNING = 'is-warning';
48
    public const COLOR_DANGER = 'is-danger';
49
    private const COLOR_ALL = [
50
        self::COLOR_PRIMARY,
51
        self::COLOR_LINK,
52
        self::COLOR_INFO,
53
        self::COLOR_SUCCESS,
54
        self::COLOR_WARNING,
55
        self::COLOR_DANGER,
56
    ];
57
58
    private array $options = [];
59
    private array $contentOptions = [];
60
    private array $closeButtonOptions = [];
61
    private string $closeButtonSize = '';
62
    private bool $withoutCloseButton = true;
63
    private string $toggleButtonLabel = 'Toggle button';
64
    private string $toggleButtonSize = '';
65
    private string $toggleButtonColor = '';
66
    private array $toggleButtonOptions = [];
67
    private bool $withoutToggleButton = true;
68
    private string $title = '';
69
    private string $footer = '';
70
    private array $titleOptions = [];
71
    private array $headerOptions = [];
72
    private array $bodyOptions = [];
73
    private array $footerOptions = [];
74
75 17
    public function begin(): ?string
76
    {
77 17
        parent::begin();
78
79 17
        $this->buildOptions();
80
81 17
        $html = '';
82 17
        $html .= $this->renderToggleButton() . "\n";
83 17
        $html .= Html::beginTag('div', $this->options) . "\n"; // .modal
84 17
        $html .= $this->renderBackgroundTransparentOverlay() . "\n"; // .modal-background
85 17
        $html .= Html::beginTag('div', $this->contentOptions) . "\n"; // .modal-card
86 17
        $html .= $this->renderHeader();
87 17
        $html .= $this->renderBodyBegin() . "\n";
88
89 17
        return $html;
90
    }
91
92 17
    protected function run(): string
93
    {
94 17
        $html = '';
95 17
        $html .= $this->renderBodyEnd() . "\n";
96 17
        $html .= $this->renderFooter() . "\n";
97 17
        $html .= Html::endTag('div') . "\n"; // .modal-card
98 17
        $html .= Html::endTag('div'); // .modal
99
100 17
        return $html;
101
    }
102
103
    /**
104
     * Main container options.
105
     *
106
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
107
     *
108
     * @param array $value
109
     *
110
     * @return self
111
     */
112 1
    public function options(array $value): self
113
    {
114 1
        $new = clone $this;
115 1
        $new->options = $value;
116
117 1
        return $new;
118
    }
119
120
    /**
121
     * Main content container options.
122
     *
123
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
124
     *
125
     * @param array $value
126
     *
127
     * @return self
128
     */
129 1
    public function contentOptions(array $value): self
130
    {
131 1
        $new = clone $this;
132 1
        $new->contentOptions = $value;
133
134 1
        return $new;
135
    }
136
137
    /**
138
     * Toggle button label.
139
     *
140
     * @param string $value
141
     *
142
     * @return self
143
     */
144 1
    public function toggleButtonLabel(string $value): self
145
    {
146 1
        $new = clone $this;
147 1
        $new->toggleButtonLabel = $value;
148
149 1
        return $new;
150
    }
151
152
    /**
153
     * Toggle button options.
154
     *
155
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
156
     *
157
     * @param array $value
158
     *
159
     * @return self
160
     */
161 1
    public function toggleButtonOptions(array $value): self
162
    {
163 1
        $new = clone $this;
164 1
        $new->toggleButtonOptions = $value;
165
166 1
        return $new;
167
    }
168
169
    /**
170
     * Toggle button size.
171
     *
172
     * @param string $value
173
     *
174
     * @return self
175
     */
176 2
    public function toggleButtonSize(string $value): self
177
    {
178 2
        if (!in_array($value, self::SIZE_ALL)) {
179 1
            $values = implode('", "', self::SIZE_ALL);
180 1
            throw new InvalidArgumentException("Invalid size. Valid values are: \"$values\".");
181
        }
182
183 1
        $new = clone $this;
184 1
        $new->toggleButtonSize = $value;
185
186 1
        return $new;
187
    }
188
189
    /**
190
     * Toggle button color.
191
     *
192
     * @param string $value
193
     *
194
     * @return self
195
     */
196 2
    public function toggleButtonColor(string $value): self
197
    {
198 2
        if (!in_array($value, self::COLOR_ALL)) {
199 1
            $values = implode('", "', self::COLOR_ALL);
200 1
            throw new InvalidArgumentException("Invalid color. Valid values are: \"$values\".");
201
        }
202
203 1
        $new = clone $this;
204 1
        $new->toggleButtonColor = $value;
205
206 1
        return $new;
207
    }
208
209
    /**
210
     * Disable toggle button.
211
     *
212
     * @param bool $value
213
     *
214
     * @return self
215
     */
216 1
    public function withoutToggleButton(): self
217
    {
218 1
        $new = clone $this;
219 1
        $new->withoutToggleButton = false;
220
221 1
        return $new;
222
    }
223
224
    /**
225
     * Close button size.
226
     *
227
     * @param string $value
228
     *
229
     * @return self
230
     */
231 2
    public function closeButtonSize(string $value): self
232
    {
233 2
        if (!in_array($value, self::SIZE_ALL)) {
234 1
            $values = implode('"', self::SIZE_ALL);
235 1
            throw new InvalidArgumentException("Invalid size. Valid values are: \"$values\".");
236
        }
237
238 1
        $new = clone $this;
239 1
        $new->closeButtonSize = $value;
240
241 1
        return $new;
242
    }
243
244
    /**
245
     * Close button options
246
     *
247
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
248
     *
249
     * @param array $value
250
     *
251
     * @return self
252
     */
253 1
    public function closeButtonOptions(array $value): self
254
    {
255 1
        $new = clone $this;
256 1
        $new->closeButtonOptions = $value;
257
258 1
        return $new;
259
    }
260
261
    /**
262
     * Disable close button.
263
     *
264
     * @param bool $value
265
     *
266
     * @return self
267
     */
268 1
    public function withoutCloseButton(): self
269
    {
270 1
        $new = clone $this;
271 1
        $new->withoutCloseButton = false;
272
273 1
        return $new;
274
    }
275
276
    /**
277
     * Header options.
278
     *
279
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
280
     *
281
     * @param array $value
282
     *
283
     * @return self
284
     */
285 1
    public function headerOptions(array $value): self
286
    {
287 1
        $new = clone $this;
288 1
        $new->headerOptions = $value;
289
290 1
        return $new;
291
    }
292
293
    /**
294
     * Body options.
295
     *
296
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
297
     *
298
     * @param array $value
299
     *
300
     * @return self
301
     */
302 1
    public function bodyOptions(array $value): self
303
    {
304 1
        $new = clone $this;
305 1
        $new->bodyOptions = $value;
306
307 1
        return $new;
308
    }
309
310
    /**
311
     * Footer options
312
     *
313
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
314
     *
315
     * @param array $value
316
     *
317
     * @return self
318
     */
319 1
    public function footerOptions(array $value): self
320
    {
321 1
        $new = clone $this;
322 1
        $new->footerOptions = $value;
323
324 1
        return $new;
325
    }
326
327
    /**
328
     * The footer content in the modal window.
329
     *
330
     * @param string $value
331
     *
332
     * @return self
333
     */
334 1
    public function footer(string $value): self
335
    {
336 1
        $new = clone $this;
337 1
        $new->footer = $value;
338
339 1
        return $new;
340
    }
341
342
    /**
343
     * Title options.
344
     *
345
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
346
     *
347
     * @param array $value
348
     *
349
     * @return self
350
     */
351 1
    public function titleOptions(array $value): self
352
    {
353 1
        $new = clone $this;
354 1
        $new->titleOptions = $value;
355
356 1
        return $new;
357
    }
358
359
    /**
360
     * The title content in the modal window.
361
     *
362
     * @param string $value
363
     *
364
     * @return self
365
     */
366 1
    public function title(string $value): self
367
    {
368 1
        $new = clone $this;
369 1
        $new->title = $value;
370
371 1
        return $new;
372
    }
373
374
    /**
375
     * Renders the toggle button.
376
     *
377
     * @throws JsonException
378
     *
379
     * @return string
380
     */
381 17
    private function renderToggleButton(): string
382
    {
383 17
        if ($this->withoutToggleButton) {
384 16
            return Html::button($this->toggleButtonLabel, $this->toggleButtonOptions);
385
        }
386
387 1
        return '';
388
    }
389
390
    /**
391
     * Renders the close button.
392
     *
393
     * @throws JsonException
394
     *
395
     * @return string
396
     */
397 17
    private function renderCloseButton(): string
398
    {
399 17
        if ($this->withoutCloseButton) {
400 16
            return Html::button('', $this->closeButtonOptions);
401
        }
402
403 1
        return '';
404
    }
405
406
    /**
407
     * Renders header.
408
     *
409
     * @throws JsonException
410
     *
411
     * @return string
412
     */
413 17
    private function renderHeader(): string
414
    {
415 17
        $html = '';
416 17
        $html .= Html::beginTag('header', $this->headerOptions) . "\n";
417 17
        $html .= Html::tag('p', $this->title, $this->titleOptions) . "\n";
418 17
        $html .= $this->renderCloseButton() . "\n";
419 17
        $html .= Html::endTag('header') . "\n";
420
421 17
        return $html;
422
    }
423
424
    /**
425
     * Renders begin body tag.
426
     *
427
     * @throws JsonException
428
     *
429
     * @return string
430
     */
431 17
    private function renderBodyBegin(): string
432
    {
433 17
        return Html::beginTag('section', $this->bodyOptions);
434
    }
435
436
    /**
437
     * Renders end body tag.
438
     *
439
     * @throws JsonException
440
     *
441
     * @return string
442
     */
443 17
    private function renderBodyEnd(): string
444
    {
445 17
        return Html::endTag('section');
446
    }
447
448
    /**
449
     * Renders the footer.
450
     *
451
     * @throws JsonException
452
     *
453
     * @return string
454
     */
455 17
    private function renderFooter(): string
456
    {
457 17
        return Html::tag('footer', $this->footer, $this->footerOptions);
458
    }
459
460
    /**
461
     * Renders the background transparent overlay.
462
     *
463
     * @throws JsonException
464
     *
465
     * @return string
466
     */
467 17
    private function renderBackgroundTransparentOverlay(): string
468
    {
469 17
        return Html::tag('div', '', ['class' => 'modal-background']);
470
    }
471
472 17
    private function buildOptions(): void
473
    {
474 17
        $this->options['id'] ??= "{$this->getId()}-modal";
475 17
        $this->options = $this->addOptions($this->options, 'modal');
476
477 17
        $this->closeButtonOptions = $this->addOptions($this->closeButtonOptions, 'delete');
478 17
        $this->closeButtonOptions['aria-label'] = 'close';
479
480 17
        if ($this->closeButtonSize !== '') {
481 1
            Html::addCssClass($this->closeButtonOptions, $this->closeButtonSize);
482
        }
483
484 17
        $this->toggleButtonOptions = $this->addOptions($this->toggleButtonOptions, 'button');
485 17
        $this->toggleButtonOptions['data-target'] = '#' . $this->options['id'];
486 17
        $this->toggleButtonOptions['aria-haspopup'] = 'true';
487
488 17
        if ($this->toggleButtonSize !== '') {
489 1
            Html::addCssClass($this->toggleButtonOptions, $this->toggleButtonSize);
490
        }
491
492 17
        if ($this->toggleButtonColor !== '') {
493 1
            Html::addCssClass($this->toggleButtonOptions, $this->toggleButtonColor);
494
        }
495
496 17
        $this->contentOptions = $this->addOptions($this->contentOptions, 'modal-card');
497 17
        $this->headerOptions = $this->addOptions($this->headerOptions, 'modal-card-head');
498 17
        $this->titleOptions = $this->addOptions($this->titleOptions, 'modal-card-title');
499 17
        $this->bodyOptions = $this->addOptions($this->bodyOptions, 'modal-card-body');
500 17
        $this->footerOptions = $this->addOptions($this->footerOptions, 'modal-card-foot');
501 17
    }
502
}
503