Test Failed
Pull Request — master (#127)
by
unknown
13:20
created

Offcanvas::bsToggle()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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