Passed
Push — master ( 2301b1...386ec7 )
by Alexander
14:22 queued 11:36
created

Toast::run()   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 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
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
 * Toasts renders a toast bootstrap widget.
15
 *
16
 * For example,
17
 *
18
 * ```php
19
 * echo Toast::widget()
20
 *     ->title('Hello world!')
21
 *     ->dateTime('a minute ago')
22
 *     ->body('Say hello...')
23
 *     ->begin();
24
 * ```
25
 *
26
 * The following example will show the content enclosed between the {@see begin()}
27
 * and {@see end()} calls within the toast box:
28
 *
29
 * ```php
30
 * echo Toast::widget()
31
 *     ->title('Hello world!')
32
 *     ->dateTime('a minute ago')
33
 *     ->begin();
34
 *
35
 * echo 'Say hello...';
36
 *
37
 * echo Toast::end();
38
 * ```
39
 *
40
 * @see https://getbootstrap.com/docs/5.0/components/toasts/
41
 */
42
final class Toast extends Widget
43
{
44
    private string $body = '';
45
    private string $title = '';
46
    private string $dateTime = '';
47
    private array $closeButton = [];
48
    private array $titleOptions = [];
49
    private array $dateTimeOptions = [];
50
    private array $headerOptions = [];
51
    private array $bodyOptions = [];
52
    private array $options = [];
53
    private bool $encodeTags = false;
54
55 7
    public function begin(): string
56
    {
57 7
        parent::begin();
58
59 7
        if (!isset($this->options['id'])) {
60 7
            $this->options['id'] = "{$this->getId()}-toast";
61
        }
62
63 7
        $this->initOptions();
64
65 7
        return
66 7
            Html::openTag('div', $this->options) . "\n" .
67 7
            $this->renderHeader() . "\n" .
68 7
            $this->renderBodyBegin();
69
    }
70
71 7
    public function render(): string
72
    {
73 7
        return $this->renderBodyEnd() . Html::closeTag('div');
74
    }
75
76
    /**
77
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
78
     *
79
     * @param array $value Body options.
80
     */
81 1
    public function bodyOptions(array $value): self
82
    {
83 1
        $new = clone $this;
84 1
        $new->bodyOptions = $value;
85
86 1
        return $new;
87
    }
88
89
    /**
90
     * The options for rendering the close button tag.
91
     *
92
     * The close button is displayed in the header of the toast window. Clicking on the button will hide the toast
93
     * window. If {@see closeButtonEnabled} is false, no close button will be rendered.
94
     *
95
     * The following special options are supported:
96
     *
97
     * - tag: string, the tag name of the button. Defaults to 'button'.
98
     * - label: string, the label of the button. Defaults to '&times;'.
99
     *
100
     * The rest of the options will be rendered as the HTML attributes of the button tag. Please refer to the
101
     * [Toast plugin help](https://getbootstrap.com/docs/5.0/components/toasts/) for the supported HTML attributes.
102
     *
103
     * @param array $value
104
     */
105 1
    public function closeButton(array $value): self
106
    {
107 1
        $new = clone $this;
108 1
        $new->closeButton = $value;
109
110 1
        return $new;
111
    }
112
113
    /**
114
     * The date/time content in the toast window.
115
     */
116 2
    public function dateTime(string $value): self
117
    {
118 2
        $new = clone $this;
119 2
        $new->dateTime = $value;
120
121 2
        return $new;
122
    }
123
124
    /**
125
     * Additional DateTime options.
126
     *
127
     * @param array $value
128
     *
129
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
130
     */
131 1
    public function dateTimeOptions(array $value): self
132
    {
133 1
        $new = clone $this;
134 1
        $new->dateTimeOptions = $value;
135
136 1
        return $new;
137
    }
138
139
    /**
140
     * Additional header options.
141
     *
142
     * @param array $value
143
     *
144
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
145
     */
146 2
    public function headerOptions(array $value): self
147
    {
148 2
        $new = clone $this;
149 2
        $new->headerOptions = $value;
150
151 2
        return $new;
152
    }
153
154
    /**
155
     * @param array $value the HTML attributes for the widget container tag. The following special options are
156
     * recognized.
157
     *
158
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
159
     */
160 1
    public function options(array $value): self
161
    {
162 1
        $new = clone $this;
163 1
        $new->options = $value;
164
165 1
        return $new;
166
    }
167
168
    /**
169
     * The title content in the toast window.
170
     */
171 6
    public function title(string $value): self
172
    {
173 6
        $new = clone $this;
174 6
        $new->title = $value;
175
176 6
        return $new;
177
    }
178
179
    /**
180
     * Additional title options.
181
     *
182
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
183
     *
184
     * @param array $value
185
     */
186 3
    public function titleOptions(array $value): self
187
    {
188 3
        $new = clone $this;
189 3
        $new->titleOptions = $value;
190
191 3
        return $new;
192
    }
193
194
    /**
195
     * Renders the header HTML markup of the toast.
196
     *
197
     * @throws JsonException
198
     *
199
     * @return string the rendering result
200
     */
201 7
    private function renderHeader(): string
202
    {
203 7
        $button = $this->renderCloseButton();
204 7
        $tag = ArrayHelper::remove($this->titleOptions, 'tag', 'strong');
205 7
        Html::addCssClass($this->titleOptions, ['widget' => 'me-auto']);
206 7
        $title = Html::tag($tag, $this->title, $this->titleOptions)
207 7
            ->encode($this->encodeTags)
208 7
            ->render();
209
210 7
        if ($this->dateTime !== '') {
211 2
            $tag = ArrayHelper::remove($this->dateTimeOptions, 'tag', 'small');
212 2
            $title .= "\n" . Html::tag($tag, $this->dateTime, $this->dateTimeOptions)->encode($this->encodeTags);
213
        }
214
215 7
        $title .= "\n" . $button;
216
217 7
        Html::addCssClass($this->headerOptions, ['widget' => 'toast-header']);
218 7
        return Html::div("\n" . $title . "\n", $this->headerOptions)
219 7
            ->encode($this->encodeTags)
220 7
            ->render();
221
    }
222
223
    /**
224
     * Renders the opening tag of the toast body.
225
     *
226
     * @throws JsonException
227
     *
228
     * @return string the rendering result
229
     */
230 7
    private function renderBodyBegin(): string
231
    {
232 7
        Html::addCssClass($this->bodyOptions, ['widget' => 'toast-body']);
233 7
        return Html::openTag('div', $this->bodyOptions);
234
    }
235
236
    /**
237
     * Renders the closing tag of the toast body.
238
     *
239
     * @return string the rendering result
240
     */
241 7
    private function renderBodyEnd(): string
242
    {
243 7
        return $this->body . "\n" . Html::closeTag('div');
244
    }
245
246
    /**
247
     * Renders the close button.
248
     *
249
     * @throws JsonException
250
     *
251
     * @return string the rendering result
252
     */
253 7
    private function renderCloseButton(): string
254
    {
255 7
        $tag = ArrayHelper::remove($this->closeButton, 'tag', 'button');
256 7
        $label = ArrayHelper::remove(
257 7
            $this->closeButton,
258 7
            'label',
259 7
            Html::tag('span', '&times;', ['aria-hidden' => 'true'])
260 7
                ->encode(false)
261 7
                ->render()
262 7
        );
263
264 7
        return Html::tag($tag, "\n" . $label . "\n", $this->closeButton)
265 7
            ->encode($this->encodeTags)
266 7
            ->render();
267
    }
268
269
    /**
270
     * Initializes the widget options.
271
     *
272
     * This method sets the default values for various options.
273
     */
274 7
    private function initOptions(): void
275
    {
276 7
        Html::addCssClass($this->options, ['widget' => 'toast']);
277
278 7
        $this->closeButton = array_merge([
279 7
            'aria' => ['label' => 'Close'],
280 7
            'data' => ['bs-dismiss' => 'toast'],
281 7
            'class' => ['widget' => 'btn-close'],
282 7
            'type' => 'button',
283 7
        ], $this->closeButton);
284
285 7
        if (!isset($this->options['role'])) {
286 7
            $this->options['role'] = 'alert';
287
        }
288
289 7
        if (!isset($this->options['aria']) && !isset($this->options['aria-live'])) {
290 7
            $this->options['aria'] = [
291 7
                'live' => 'assertive',
292 7
                'atomic' => 'true',
293 7
            ];
294
        }
295
    }
296
}
297