Passed
Pull Request — master (#85)
by Albert
13:00 queued 10:33
created

Offcanvas::withoutBackdrop()   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
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Bootstrap5;
6
7
use Yiisoft\Arrays\ArrayHelper;
8
use Yiisoft\Html\Html;
9
10
final class Offcanvas extends Widget
11
{
12
    public const PLACEMENT_TOP = 'offcanvas-top';
13
    public const PLACEMENT_END = 'offcanvas-end';
14
    public const PLACEMENT_BOTTOM = 'offcanvas-bottom';
15
    public const PLACEMENT_START = 'offcanvas-start';
16
17
    private array $options = [];
18
    private array $headerOptions = [];
19
    private array $titleOptions = [];
20
    private array $bodyOptions = [];
21
    private array $closeButtonOptions = [
22
        'class' => 'btn-close',
23
    ];
24
    private ?string $title = null;
25
    private string $placement = self::PLACEMENT_START;
26
    private bool $scroll = false;
27
    private bool $withoutBackdrop = false;
28
29 11
    public function getId(?string $suffix = '-offcanvas'): ?string
30
    {
31 11
        return $this->options['id'] ?? parent::getId($suffix);
32
    }
33
34
    /**
35
     * Enable/disable body scroll when offcanvas show
36
     *
37
     * @return self
38
     *
39
     * @link https://getbootstrap.com/docs/5.1/components/offcanvas/#backdrop
40
     */
41 1
    public function scroll(bool $scroll = true): self
42
    {
43 1
        $new = clone $this;
44 1
        $new->scroll = $scroll;
45
46 1
        return $new;
47
    }
48
49
    /**
50
     * Enable/disable offcanvas backdrop
51
     *
52
     * @return self
53
     *
54
     * @link https://getbootstrap.com/docs/5.1/components/offcanvas/#backdrop
55
     */
56 1
    public function withoutBackdrop(bool $withoutBackdrop = true): self
57
    {
58 1
        $new = clone $this;
59 1
        $new->withoutBackdrop = $withoutBackdrop;
60
61 1
        return $new;
62
    }
63
64
    /**
65
     * Set placement for opened offcanvas
66
     *
67
     * @param string $placement
68
     *
69
     * @return self
70
     *
71
     * @link https://getbootstrap.com/docs/5.1/components/offcanvas/#placement
72
     */
73 1
    public function placement(string $placement): self
74
    {
75 1
        $new = clone $this;
76 1
        $new->placement = $placement;
77
78 1
        return $new;
79
    }
80
81
    /**
82
     * The HTML attributes for the widget container tag. The following special options are recognized.
83
     *
84
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
85
     *
86
     * @param array $options
87
     *
88
     * @return self
89
     */
90 2
    public function options(array $options): self
91
    {
92 2
        $new = clone $this;
93 2
        $new->options = $options;
94
95 2
        return $new;
96
    }
97
98
    /**
99
     * The HTML attributes for the widget header tag. The following special options are recognized.
100
     *
101
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
102
     *
103
     * @param array $options
104
     *
105
     * @return self
106
     */
107 1
    public function headerOptions(array $options): self
108
    {
109 1
        $new = clone $this;
110 1
        $new->headerOptions = $options;
111
112 1
        return $new;
113
    }
114
115
    /**
116
     * Set/remove offcanvas title
117
     *
118
     * @param string|null $title
119
     *
120
     * @return self
121
     */
122 10
    public function title(?string $title): self
123
    {
124 10
        $new = clone $this;
125 10
        $new->title = $title;
126
127 10
        return $new;
128
    }
129
130
    /**
131
     * The HTML attributes for the widget title tag. The following special options are recognized.
132
     *
133
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
134
     *
135
     * @param array $options
136
     *
137
     * @return self
138
     */
139 1
    public function titleOptions(array $options): self
140
    {
141 1
        $new = clone $this;
142 1
        $new->titleOptions = $options;
143
144 1
        return $new;
145
    }
146
147
    /**
148
     * The HTML attributes for the widget body tag. The following special options are recognized.
149
     *
150
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
151
     *
152
     * @param array $options
153
     *
154
     * @return self
155
     */
156 1
    public function bodyOptions(array $options): self
157
    {
158 1
        $new = clone $this;
159 1
        $new->bodyOptions = $options;
160
161 1
        return $new;
162
    }
163
164
    /**
165
     * The HTML attributes for the widget close button tag. The following special options are recognized.
166
     *
167
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
168
     *
169
     * @param array $options
170
     *
171
     * @return self
172
     */
173
    public function closeButtonOptions(array $options): self
174
    {
175
        $new = clone $this;
176
        $new->closeButtonOptions = $options;
177
178
        return $new;
179
    }
180
181 11
    public function begin(): string
182
    {
183 11
        parent::begin();
184
185 11
        $options = $this->options;
186 11
        $bodyOptions = $this->bodyOptions;
187 11
        $tag = ArrayHelper::remove($options, 'tag', 'div');
188 11
        $bodyTag = ArrayHelper::remove($bodyOptions, 'tag', 'div');
189
190 11
        Html::addCssClass($options, ['widget' => 'offcanvas', 'placement' => $this->placement]);
191 11
        Html::addCssClass($bodyOptions, ['widget' => 'offcanvas-body']);
192
193 11
        $options['id'] = $this->getId();
194 11
        $options['tabindex'] = -1;
195
196 11
        if (!empty($this->title)) {
197 9
            if (isset($this->titleOptions['id'])) {
198
                $options['aria-labelledby'] = $this->titleOptions['id'];
199 9
            } elseif ($options['id']) {
200 9
                $options['aria-labelledby'] = $options['id'] . '-title';
201
            }
202
        }
203
204 11
        if ($this->scroll) {
205 1
            $options['data-bs-scroll'] = 'true';
206
        }
207
208 11
        if ($this->withoutBackdrop) {
209 1
            $options['data-bs-backdrop'] = 'false';
210
        }
211
212 11
        $html = Html::openTag($tag, $options);
213 11
        $html .= $this->renderHeader();
214 11
        $html .= Html::openTag($bodyTag, $bodyOptions);
215
216 11
        return $html;
217
    }
218
219 11
    protected function run(): string
220
    {
221 11
        $tag = $this->options['tag'] ?? 'div';
222 11
        $bodyTag = $this->bodyOptions['tag'] ?? 'div';
223
224 11
        return Html::closeTag($bodyTag) . Html::closeTag($tag);
225
    }
226
227
    /**
228
     * Renders offcanvas header.
229
     *
230
     * @return string the rendering header.
231
     */
232 11
    private function renderHeader(): string
233
    {
234 11
        $options = $this->headerOptions;
235 11
        $tag = ArrayHelper::remove($options, 'tag', 'header');
236
237 11
        Html::addCssClass($options, ['widget' => 'offcanvas-header']);
238
239 11
        $title = (string) $this->renderTitle();
240 11
        $closeButton = $this->renderCloseButton();
241
242 11
        return Html::tag($tag, $title . $closeButton, $options)->encode(false)->render();
243
    }
244
245
    /**
246
     * Renders offcanvas title.
247
     *
248
     * @return string|null the rendering header.
249
     */
250 11
    private function renderTitle(): ?string
251
    {
252 11
        if ($this->title === null) {
253 2
            return null;
254
        }
255
256 10
        $options = $this->titleOptions;
257 10
        $tag = ArrayHelper::remove($options, 'tag', 'h5');
258 10
        $encode = ArrayHelper::remove($options, 'encode');
259
260 10
        Html::addCssClass($options, ['offcanvas-title']);
261
262 10
        if (!isset($options['id']) && $id = $this->getId()) {
263 10
            $options['id'] = $id . '-title';
264
        }
265
266 10
        return Html::tag($tag, $this->title, $options)->encode($encode)->render();
267
    }
268
269
    /**
270
     * Renders offcanvas close button.
271
     *
272
     * @return string the rendering close button.
273
     */
274 11
    private function renderCloseButton(): string
275
    {
276 11
        $options = $this->closeButtonOptions;
277 11
        $label = ArrayHelper::remove($options, 'label', '');
278 11
        $encode = ArrayHelper::remove($options, 'encode');
279
280 11
        $options['type'] = 'button';
281 11
        $options['aria-label'] = 'Close';
282 11
        $options['data-bs-dismiss'] = 'offcanvas';
283
284 11
        return Html::button($label, $options)->encode($encode)->render();
285
    }
286
}
287