Passed
Pull Request — master (#90)
by
unknown
13:09
created

Toast::dateTime()   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 1
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 RedCatGirl\YiiBootstrap386;
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
        return
66 7
            Html::openTag('div', $this->options) . "\n" .
67 7
            $this->renderHeader() . "\n" .
68 7
            $this->renderBodyBegin();
69
    }
70
71 7
    protected function run(): 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
     * @return self
82
     */
83 1
    public function bodyOptions(array $value): self
84
    {
85 1
        $new = clone $this;
86 1
        $new->bodyOptions = $value;
87
88 1
        return $new;
89
    }
90
91
    /**
92
     * The options for rendering the close button tag.
93
     *
94
     * The close button is displayed in the header of the toast window. Clicking on the button will hide the toast
95
     * window. If {@see closeButtonEnabled} is false, no close button will be rendered.
96
     *
97
     * The following special options are supported:
98
     *
99
     * - tag: string, the tag name of the button. Defaults to 'button'.
100
     * - label: string, the label of the button. Defaults to '&times;'.
101
     *
102
     * The rest of the options will be rendered as the HTML attributes of the button tag. Please refer to the
103
     * [Toast plugin help](https://getbootstrap.com/docs/5.0/components/toasts/) for the supported HTML attributes.
104
     *
105
     * @param array $value
106
     *
107
     * @return self
108
     */
109 1
    public function closeButton(array $value): self
110
    {
111 1
        $new = clone $this;
112 1
        $new->closeButton = $value;
113
114 1
        return $new;
115
    }
116
117
    /**
118
     * The date/time content in the toast window.
119
     *
120
     * @param string $value
121
     *
122
     * @return self
123
     */
124 2
    public function dateTime(string $value): self
125
    {
126 2
        $new = clone $this;
127 2
        $new->dateTime = $value;
128
129 2
        return $new;
130
    }
131
132
    /**
133
     * Additional DateTime options.
134
     *
135
     * @param array $value
136
     *
137
     * @return self
138
     *
139
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
140
     */
141 1
    public function dateTimeOptions(array $value): self
142
    {
143 1
        $new = clone $this;
144 1
        $new->dateTimeOptions = $value;
145
146 1
        return $new;
147
    }
148
149
    /**
150
     * Additional header options.
151
     *
152
     * @param array $value
153
     *
154
     * @return self
155
     *
156
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
157
     */
158 2
    public function headerOptions(array $value): self
159
    {
160 2
        $new = clone $this;
161 2
        $new->headerOptions = $value;
162
163 2
        return $new;
164
    }
165
166
    /**
167
     * @param array $value the HTML attributes for the widget container tag. The following special options are
168
     * recognized.
169
     *
170
     * @return self
171
     *
172
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
173
     */
174 1
    public function options(array $value): self
175
    {
176 1
        $new = clone $this;
177 1
        $new->options = $value;
178
179 1
        return $new;
180
    }
181
182
    /**
183
     * The title content in the toast window.
184
     *
185
     * @param string $value
186
     *
187
     * @return self
188
     */
189 6
    public function title(string $value): self
190
    {
191 6
        $new = clone $this;
192 6
        $new->title = $value;
193
194 6
        return $new;
195
    }
196
197
    /**
198
     * Additional title options.
199
     *
200
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
201
     *
202
     * @param array $value
203
     *
204
     * @return self
205
     */
206 3
    public function titleOptions(array $value): self
207
    {
208 3
        $new = clone $this;
209 3
        $new->titleOptions = $value;
210
211 3
        return $new;
212
    }
213
214
    /**
215
     * Renders the header HTML markup of the toast.
216
     *
217
     * @throws JsonException
218
     *
219
     * @return string the rendering result
220
     */
221 7
    private function renderHeader(): string
222
    {
223 7
        $button = $this->renderCloseButton();
224 7
        $tag = ArrayHelper::remove($this->titleOptions, 'tag', 'strong');
225 7
        Html::addCssClass($this->titleOptions, ['widget' => 'me-auto']);
226 7
        $title = Html::tag($tag, $this->title, $this->titleOptions)
227 7
            ->encode($this->encodeTags)
228 7
            ->render();
229
230 7
        if ($this->dateTime !== '') {
231 2
            $tag = ArrayHelper::remove($this->dateTimeOptions, 'tag', 'small');
232 2
            $title .= "\n" . Html::tag($tag, $this->dateTime, $this->dateTimeOptions)->encode($this->encodeTags);
233
        }
234
235 7
        $title .= "\n" . $button;
236
237 7
        Html::addCssClass($this->headerOptions, ['widget' => 'toast-header']);
238 7
        return Html::div("\n" . $title . "\n", $this->headerOptions)
239 7
            ->encode($this->encodeTags)
240 7
            ->render();
241
    }
242
243
    /**
244
     * Renders the opening tag of the toast body.
245
     *
246
     * @throws JsonException
247
     *
248
     * @return string the rendering result
249
     */
250 7
    private function renderBodyBegin(): string
251
    {
252 7
        Html::addCssClass($this->bodyOptions, ['widget' => 'toast-body']);
253 7
        return Html::openTag('div', $this->bodyOptions);
254
    }
255
256
    /**
257
     * Renders the closing tag of the toast body.
258
     *
259
     * @return string the rendering result
260
     */
261 7
    private function renderBodyEnd(): string
262
    {
263 7
        return $this->body . "\n" . Html::closeTag('div');
264
    }
265
266
    /**
267
     * Renders the close button.
268
     *
269
     * @throws JsonException
270
     *
271
     * @return string the rendering result
272
     */
273 7
    private function renderCloseButton(): string
274
    {
275 7
        $tag = ArrayHelper::remove($this->closeButton, 'tag', 'button');
276 7
        $label = ArrayHelper::remove(
277 7
            $this->closeButton,
278
            'label',
279 7
            Html::tag('span', '&times;', ['aria-hidden' => 'true'])
280 7
                ->encode(false)
281 7
                ->render()
282
        );
283
284 7
        return Html::tag($tag, "\n" . $label . "\n", $this->closeButton)
285 7
            ->encode($this->encodeTags)
286 7
            ->render();
287
    }
288
289
    /**
290
     * Initializes the widget options.
291
     *
292
     * This method sets the default values for various options.
293
     */
294 7
    private function initOptions(): void
295
    {
296 7
        Html::addCssClass($this->options, ['widget' => 'toast']);
297
298 7
        $this->closeButton = array_merge([
299 7
            'aria' => ['label' => 'Close'],
300
            'data' => ['bs-dismiss' => 'toast'],
301
            'class' => ['widget' => 'btn-close'],
302
            'type' => 'button',
303 7
        ], $this->closeButton);
304
305 7
        if (!isset($this->options['role'])) {
306 7
            $this->options['role'] = 'alert';
307
        }
308
309 7
        if (!isset($this->options['aria']) && !isset($this->options['aria-live'])) {
310 7
            $this->options['aria'] = [
311
                'live' => 'assertive',
312
                'atomic' => 'true',
313
            ];
314
        }
315
    }
316
}
317